Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .npmrc
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
engine-strict=true


2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- **Smart Operators**: Reusable AI personas with drag-and-drop assignment
- **Project Memory System**: Automatic context management with Curator
- **MCP Server Integration**: Filesystem, Shell, Git, HTTP Fetch, and third-party tools
- **Local Agent Support**: Antigravity and Claude-Code integrations
- **Local Agent Support**: Claude-Code integrations
- **Marketplace**: Share and discover workflows, operators, and templates
- **Four Task Types**: AI tasks, Script tasks (JavaScript), Input tasks, Output tasks
- **Webhooks**: Task completion notifications
Expand Down
12 changes: 10 additions & 2 deletions electron/main/config/provider-registry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,13 @@ export const PROVIDER_REGISTRY: Record<string, ProviderConfig> = {
executionStrategy: 'local-session',
agentCommand: 'codex',
},
'gemini-cli': {
id: 'gemini-cli',
name: 'Gemini CLI',
type: 'local-agent',
executionStrategy: 'local-session',
agentCommand: 'gemini',
},
};

/**
Expand Down Expand Up @@ -104,14 +111,15 @@ export function isApiProvider(providerId: string): boolean {
/**
* Get local agent type for provider
*/
export function getLocalAgentType(providerId: string): 'claude' | 'codex' | null {
export function getLocalAgentType(providerId: string): 'claude' | 'codex' | 'gemini-cli' | null {
const config = getProviderConfig(providerId);
if (config?.type !== 'local-agent') return null;

// Map provider ID to agent type
const agentMap: Record<string, 'claude' | 'codex'> = {
const agentMap: Record<string, 'claude' | 'codex' | 'gemini-cli'> = {
'claude-code': 'claude',
codex: 'codex',
'gemini-cli': 'gemini-cli',
};

return agentMap[providerId] || null;
Expand Down
14 changes: 14 additions & 0 deletions electron/main/database/repositories/task-history-repository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,20 @@ export class TaskHistoryRepository {
return result[0]!;
}

async findByProject(projectId: number, limit?: number): Promise<TaskHistory[]> {
let query = db
.select()
.from(taskHistory)
.where(eq(taskHistory.taskProjectId, projectId))
.orderBy(desc(taskHistory.createdAt), desc(taskHistory.id));

if (limit) {
query = query.limit(limit) as typeof query;
}

return await query;
}

/**
* Find all history entries for a task
*/
Expand Down
134 changes: 120 additions & 14 deletions electron/main/database/repositories/task-repository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,15 +79,25 @@
const [result] = await db
.select()
.from(tasks)
.where(
and(
eq(tasks.projectId, projectId),
eq(tasks.projectSequence, projectSequence),
isNull(tasks.deletedAt)
)
)
.where(and(eq(tasks.projectId, projectId), eq(tasks.projectSequence, projectSequence)))
.limit(1);

if (result?.executionResult?.content) {
const safeResult = {
...result,
executionResult: { ...result.executionResult, content: '(truncated)' },
};
console.log(

Check warning on line 90 in electron/main/database/repositories/task-repository.ts

View workflow job for this annotation

GitHub Actions / Lint Check

Unexpected console statement

Check warning on line 90 in electron/main/database/repositories/task-repository.ts

View workflow job for this annotation

GitHub Actions / Lint Check

Unexpected console statement
`[TaskRepo] findByKey(${projectId}, ${projectSequence}) raw result:`,
safeResult
);
} else {
console.log(

Check warning on line 95 in electron/main/database/repositories/task-repository.ts

View workflow job for this annotation

GitHub Actions / Lint Check

Unexpected console statement

Check warning on line 95 in electron/main/database/repositories/task-repository.ts

View workflow job for this annotation

GitHub Actions / Lint Check

Unexpected console statement
`[TaskRepo] findByKey(${projectId}, ${projectSequence}) raw result:`,
result
);
}

return result;
}

Expand Down Expand Up @@ -244,14 +254,110 @@
/**
* Soft delete task by composite key
*/
/**
* Soft delete task by composite key and clean up dependencies
*/
async deleteByKey(projectId: number, projectSequence: number): Promise<void> {
await db
.update(tasks)
.set({
deletedAt: new Date(),
updatedAt: new Date(),
})
.where(and(eq(tasks.projectId, projectId), eq(tasks.projectSequence, projectSequence)));
// 1. Find tasks that depend on this task
const dependentTasks = await db
.select()
.from(tasks)
.where(
and(
eq(tasks.projectId, projectId),
isNull(tasks.deletedAt)
// We can't easily filter JSON in SQLite efficiently for all cases,
// so we'll fetch project tasks and filter in memory or rely on broader fetch.
// Given findByProject usage pattern, fetching all active tasks for project is safe/standard here.
)
);

const tasksToUpdate_TriggerConfig: Task[] = [];
const tasksToUpdate_Dependencies: Task[] = [];

for (const task of dependentTasks) {
// Check triggerConfig
if (
task.triggerConfig &&
task.triggerConfig.dependsOn &&
Array.isArray(task.triggerConfig.dependsOn.taskIds)
) {
if (task.triggerConfig.dependsOn.taskIds.includes(projectSequence)) {
tasksToUpdate_TriggerConfig.push(task);
}
}

// Check dependencies (legacy or simple array)
if (Array.isArray(task.dependencies) && task.dependencies.includes(projectSequence)) {
tasksToUpdate_Dependencies.push(task);
}
}

// 2. Update dependent tasks
await db.transaction(async (tx) => {
// Update triggerConfigs
for (const task of tasksToUpdate_TriggerConfig) {
if (!task.triggerConfig?.dependsOn?.taskIds) continue;

const newTaskIds = task.triggerConfig.dependsOn.taskIds.filter(
(id) => id !== projectSequence

Check failure on line 303 in electron/main/database/repositories/task-repository.ts

View workflow job for this annotation

GitHub Actions / Type Check

Parameter 'id' implicitly has an 'any' type.

Check failure on line 303 in electron/main/database/repositories/task-repository.ts

View workflow job for this annotation

GitHub Actions / Type Check

Parameter 'id' implicitly has an 'any' type.
);
const newTriggerConfig = {
...task.triggerConfig,
dependsOn: {
...task.triggerConfig.dependsOn,
taskIds: newTaskIds,
},
};

// If no dependencies left, remove dependsOn entirely?
// Or keep empty array? Keeping empty array might be safer or user might want to add more.
// User request says "remove from dependency".
// If taskIds becomes empty, the task might auto-trigger or never trigger depending on logic.
// Usually empty dependency means "no dependency".

await tx
.update(tasks)
.set({
triggerConfig: newTriggerConfig,
updatedAt: new Date(),
})
.where(
and(
eq(tasks.projectId, projectId),
eq(tasks.projectSequence, task.projectSequence)
)
);
}

// Update dependencies column
for (const task of tasksToUpdate_Dependencies) {
const newDeps = (task.dependencies || []).filter((id) => id !== projectSequence);

Check failure on line 335 in electron/main/database/repositories/task-repository.ts

View workflow job for this annotation

GitHub Actions / Type Check

Parameter 'id' implicitly has an 'any' type.

Check failure on line 335 in electron/main/database/repositories/task-repository.ts

View workflow job for this annotation

GitHub Actions / Type Check

Parameter 'id' implicitly has an 'any' type.
await tx
.update(tasks)
.set({
dependencies: newDeps,
updatedAt: new Date(),
})
.where(
and(
eq(tasks.projectId, projectId),
eq(tasks.projectSequence, task.projectSequence)
)
);
}

// 3. Perform the soft delete
await tx
.update(tasks)
.set({
deletedAt: new Date(),
updatedAt: new Date(),
})
.where(
and(eq(tasks.projectId, projectId), eq(tasks.projectSequence, projectSequence))
);
});
}

/**
Expand Down
Loading
Loading