@grackle-ai/server 0.46.0 → 0.47.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/dist/db.d.ts.map +1 -1
  2. package/dist/db.js +49 -21
  3. package/dist/db.js.map +1 -1
  4. package/dist/event-bus.d.ts +1 -1
  5. package/dist/event-bus.d.ts.map +1 -1
  6. package/dist/event-processor.d.ts +1 -1
  7. package/dist/event-processor.d.ts.map +1 -1
  8. package/dist/event-processor.js +16 -16
  9. package/dist/event-processor.js.map +1 -1
  10. package/dist/finding-store.d.ts +5 -5
  11. package/dist/finding-store.d.ts.map +1 -1
  12. package/dist/finding-store.js +10 -10
  13. package/dist/finding-store.js.map +1 -1
  14. package/dist/github-import.d.ts +2 -2
  15. package/dist/github-import.d.ts.map +1 -1
  16. package/dist/github-import.js +15 -15
  17. package/dist/github-import.js.map +1 -1
  18. package/dist/grpc-service.js +70 -70
  19. package/dist/grpc-service.js.map +1 -1
  20. package/dist/processor-registry.d.ts +3 -3
  21. package/dist/processor-registry.d.ts.map +1 -1
  22. package/dist/processor-registry.js +4 -4
  23. package/dist/processor-registry.js.map +1 -1
  24. package/dist/reanimate-agent.js +3 -3
  25. package/dist/reanimate-agent.js.map +1 -1
  26. package/dist/resolve-persona.d.ts +2 -2
  27. package/dist/resolve-persona.d.ts.map +1 -1
  28. package/dist/resolve-persona.js +4 -4
  29. package/dist/resolve-persona.js.map +1 -1
  30. package/dist/schema.d.ts +21 -21
  31. package/dist/schema.d.ts.map +1 -1
  32. package/dist/schema.js +6 -6
  33. package/dist/schema.js.map +1 -1
  34. package/dist/task-store.d.ts +6 -6
  35. package/dist/task-store.d.ts.map +1 -1
  36. package/dist/task-store.js +15 -15
  37. package/dist/task-store.js.map +1 -1
  38. package/dist/utils/slugify.d.ts +1 -1
  39. package/dist/utils/slugify.js +1 -1
  40. package/dist/utils/system-context.js +4 -4
  41. package/dist/utils/system-context.js.map +1 -1
  42. package/dist/workspace-store.d.ts +26 -0
  43. package/dist/workspace-store.d.ts.map +1 -0
  44. package/dist/{project-store.js → workspace-store.js} +21 -21
  45. package/dist/workspace-store.js.map +1 -0
  46. package/dist/ws-bridge.js +76 -76
  47. package/dist/ws-bridge.js.map +1 -1
  48. package/package.json +6 -6
  49. package/dist/project-store.d.ts +0 -26
  50. package/dist/project-store.d.ts.map +0 -1
  51. package/dist/project-store.js.map +0 -1
@@ -6,13 +6,13 @@ export function buildTaskSystemContext(title, description, notes, canDecompose)
6
6
  notes ? `## Notes (from previous attempt or user feedback)\n${notes}` : "",
7
7
  `## Grackle Tools (MCP)`,
8
8
  `You have a "grackle" MCP server with tools for coordinating with other agents:`,
9
- `- **mcp__grackle__finding_post**: Share discoveries (architecture decisions, bugs, patterns) with other agents working on this project. Parameters: projectId (string — injected automatically), title (string, required), content (string, optional), category (optional: architecture|api|bug|decision|dependency|pattern|general), tags (optional: string[]).`,
10
- `- **mcp__grackle__finding_list**: Query findings posted by other agents. Parameters: projectId (string — injected automatically), category (optional), tag (optional), limit (optional). Findings from previous tasks are also in your system context above.`,
9
+ `- **mcp__grackle__finding_post**: Share discoveries (architecture decisions, bugs, patterns) with other agents working on this workspace. Parameters: workspaceId (string — injected automatically), title (string, required), content (string, optional), category (optional: architecture|api|bug|decision|dependency|pattern|general), tags (optional: string[]).`,
10
+ `- **mcp__grackle__finding_list**: Query findings posted by other agents. Parameters: workspaceId (string — injected automatically), category (optional), tag (optional), limit (optional). Findings from previous tasks are also in your system context above.`,
11
11
  ];
12
12
  if (canDecompose) {
13
- sections.push(`- **mcp__grackle__task_create**: Create a new task in the project. Use this when work is too large or complex for you to complete alone. Parameters: projectId (string — injected automatically), title (string, required), description (string, optional — be specific about what to do and what "done" looks like).`);
13
+ sections.push(`- **mcp__grackle__task_create**: Create a new task in the workspace. Use this when work is too large or complex for you to complete alone. Parameters: workspaceId (string — injected automatically), title (string, required), description (string, optional — be specific about what to do and what "done" looks like).`);
14
14
  }
15
- sections.push(`## Completion Checklist`, `When you have finished implementing the task, you MUST complete ALL steps below in order. Do NOT stop early or go to "waiting for input" until every step is done.`, ``, `### Phase 1: Implement & Test`, `1. **Implement** the task requirements.`, `2. **Write tests**: Write unit tests, integration tests, or E2E specs as appropriate. Every implementation MUST include tests unless the change is purely cosmetic or untestable (state why if skipping).`, `3. **Build**: Run the project's build command and fix any errors.`, `4. **Run tests**: Run relevant tests and ensure they pass.`, `5. **Manual test**: If the change affects UI, visually verify. If it affects CLI or API, run the commands manually. State explicitly if skipping and why.`, ``, `### Phase 2: Create PR`, `6. **Sync with main**: Fetch and merge the main branch. If merge conflicts arise, resolve them, stage, and commit the merge. NEVER rebase.`, `7. **Rebuild after merge**: If the merge brought in new commits, rebuild to catch integration conflicts.`, `8. **Commit**: Stage your changed files and create a descriptive git commit. Use a conventional commit message (e.g., \`fix: ...\`, \`feat: ...\`).`, `9. **Push**: Push your branch to the remote.`, `10. **Create PR**: Create a pull request that links back to the issue (e.g., "Closes #ISSUE").`, ``, `### Phase 3: PR Readiness (you MUST complete this — do NOT skip)`, `After creating the PR, you must ensure it is ready to merge.`, ``, `11. **Check for merge conflicts**: Verify the PR has no merge conflicts. If it does, fetch and merge the main branch, resolve conflicts, rebuild, commit, and push.`, `12. **Wait for CI**: Wait for all CI checks to complete. If any check fails, read the logs, fix the issue, commit, push, and repeat.`, `13. **Address code review comments**: Check for automated code review comments. For each unresolved comment: read the suggestion, fix the code or dismiss with an explanation, reply to the comment, and resolve the thread. After fixing, commit, push, and check again. Repeat until all review threads are resolved.`, `14. **Post finding**: Use mcp__grackle__post_finding to summarize what you did and any key decisions.`, ``, `IMPORTANT: The PR is the deliverable, but a PR with failing CI or unresolved review comments is NOT done. You MUST complete Phase 3. Do NOT go to "waiting for input" until CI is green AND all review threads are resolved.`);
15
+ sections.push(`## Completion Checklist`, `When you have finished implementing the task, you MUST complete ALL steps below in order. Do NOT stop early or go to "waiting for input" until every step is done.`, ``, `### Phase 1: Implement & Test`, `1. **Implement** the task requirements.`, `2. **Write tests**: Write unit tests, integration tests, or E2E specs as appropriate. Every implementation MUST include tests unless the change is purely cosmetic or untestable (state why if skipping).`, `3. **Build**: Run the repository's build command and fix any errors.`, `4. **Run tests**: Run relevant tests and ensure they pass.`, `5. **Manual test**: If the change affects UI, visually verify. If it affects CLI or API, run the commands manually. State explicitly if skipping and why.`, ``, `### Phase 2: Create PR`, `6. **Sync with main**: Fetch and merge the main branch. If merge conflicts arise, resolve them, stage, and commit the merge. NEVER rebase.`, `7. **Rebuild after merge**: If the merge brought in new commits, rebuild to catch integration conflicts.`, `8. **Commit**: Stage your changed files and create a descriptive git commit. Use a conventional commit message (e.g., \`fix: ...\`, \`feat: ...\`).`, `9. **Push**: Push your branch to the remote.`, `10. **Create PR**: Create a pull request that links back to the issue (e.g., "Closes #ISSUE").`, ``, `### Phase 3: PR Readiness (you MUST complete this — do NOT skip)`, `After creating the PR, you must ensure it is ready to merge.`, ``, `11. **Check for merge conflicts**: Verify the PR has no merge conflicts. If it does, fetch and merge the main branch, resolve conflicts, rebuild, commit, and push.`, `12. **Wait for CI**: Wait for all CI checks to complete. If any check fails, read the logs, fix the issue, commit, push, and repeat.`, `13. **Address code review comments**: Check for automated code review comments. For each unresolved comment: read the suggestion, fix the code or dismiss with an explanation, reply to the comment, and resolve the thread. After fixing, commit, push, and check again. Repeat until all review threads are resolved.`, `14. **Post finding**: Use mcp__grackle__post_finding to summarize what you did and any key decisions.`, ``, `IMPORTANT: The PR is the deliverable, but a PR with failing CI or unresolved review comments is NOT done. You MUST complete Phase 3. Do NOT go to "waiting for input" until CI is green AND all review threads are resolved.`);
16
16
  return sections.filter(Boolean).join("\n\n");
17
17
  }
18
18
  //# sourceMappingURL=system-context.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"system-context.js","sourceRoot":"","sources":["../../src/utils/system-context.ts"],"names":[],"mappings":"AAAA,iFAAiF;AACjF,MAAM,UAAU,sBAAsB,CAAC,KAAa,EAAE,WAAmB,EAAE,KAAa,EAAE,YAAsB;IAC9G,MAAM,QAAQ,GAAa;QACzB,YAAY,KAAK,EAAE;QACnB,WAAW;QACX,KAAK,CAAC,CAAC,CAAC,sDAAsD,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE;QAC1E,wBAAwB;QACxB,gFAAgF;QAChF,kWAAkW;QAClW,8PAA8P;KAC/P,CAAC;IAEF,IAAI,YAAY,EAAE,CAAC;QACjB,QAAQ,CAAC,IAAI,CACX,uTAAuT,CACxT,CAAC;IACJ,CAAC;IAED,QAAQ,CAAC,IAAI,CACX,yBAAyB,EACzB,oKAAoK,EACpK,EAAE,EACF,+BAA+B,EAC/B,yCAAyC,EACzC,2MAA2M,EAC3M,mEAAmE,EACnE,4DAA4D,EAC5D,2JAA2J,EAC3J,EAAE,EACF,wBAAwB,EACxB,4IAA4I,EAC5I,0GAA0G,EAC1G,qJAAqJ,EACrJ,8CAA8C,EAC9C,gGAAgG,EAChG,EAAE,EACF,kEAAkE,EAClE,8DAA8D,EAC9D,EAAE,EACF,qKAAqK,EACrK,sIAAsI,EACtI,yTAAyT,EACzT,uGAAuG,EACvG,EAAE,EACF,8NAA8N,CAC/N,CAAC;IAEF,OAAO,QAAQ,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;AAC/C,CAAC"}
1
+ {"version":3,"file":"system-context.js","sourceRoot":"","sources":["../../src/utils/system-context.ts"],"names":[],"mappings":"AAAA,iFAAiF;AACjF,MAAM,UAAU,sBAAsB,CAAC,KAAa,EAAE,WAAmB,EAAE,KAAa,EAAE,YAAsB;IAC9G,MAAM,QAAQ,GAAa;QACzB,YAAY,KAAK,EAAE;QACnB,WAAW;QACX,KAAK,CAAC,CAAC,CAAC,sDAAsD,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE;QAC1E,wBAAwB;QACxB,gFAAgF;QAChF,sWAAsW;QACtW,gQAAgQ;KACjQ,CAAC;IAEF,IAAI,YAAY,EAAE,CAAC;QACjB,QAAQ,CAAC,IAAI,CACX,2TAA2T,CAC5T,CAAC;IACJ,CAAC;IAED,QAAQ,CAAC,IAAI,CACX,yBAAyB,EACzB,oKAAoK,EACpK,EAAE,EACF,+BAA+B,EAC/B,yCAAyC,EACzC,2MAA2M,EAC3M,sEAAsE,EACtE,4DAA4D,EAC5D,2JAA2J,EAC3J,EAAE,EACF,wBAAwB,EACxB,4IAA4I,EAC5I,0GAA0G,EAC1G,qJAAqJ,EACrJ,8CAA8C,EAC9C,gGAAgG,EAChG,EAAE,EACF,kEAAkE,EAClE,8DAA8D,EAC9D,EAAE,EACF,qKAAqK,EACrK,sIAAsI,EACtI,yTAAyT,EACzT,uGAAuG,EACvG,EAAE,EACF,8NAA8N,CAC/N,CAAC;IAEF,OAAO,QAAQ,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;AAC/C,CAAC"}
@@ -0,0 +1,26 @@
1
+ import { type WorkspaceRow } from "./schema.js";
2
+ export type { WorkspaceRow };
3
+ /** Insert a new workspace record. */
4
+ export declare function createWorkspace(id: string, name: string, description: string, repoUrl: string, defaultEnvironmentId: string, useWorktrees?: boolean, worktreeBasePath?: string, defaultPersonaId?: string): void;
5
+ /** Retrieve a single workspace by ID. */
6
+ export declare function getWorkspace(id: string): WorkspaceRow | undefined;
7
+ /** Return all active workspaces, newest first. */
8
+ export declare function listWorkspaces(): WorkspaceRow[];
9
+ /** Mark a workspace as archived. */
10
+ export declare function archiveWorkspace(id: string): void;
11
+ /** Partial-update fields for a workspace. Undefined means "no change"; empty string means "clear". */
12
+ export interface UpdateWorkspaceFields {
13
+ name?: string;
14
+ description?: string;
15
+ repoUrl?: string;
16
+ defaultEnvironmentId?: string;
17
+ /** When false, agents work directly in the main checkout instead of creating a worktree. */
18
+ useWorktrees?: boolean;
19
+ /** Custom base path for worktrees (e.g. /workspaces/my-repo). Empty means use default. */
20
+ worktreeBasePath?: string;
21
+ /** Default persona for tasks in this workspace. */
22
+ defaultPersonaId?: string;
23
+ }
24
+ /** Update one or more fields on an existing workspace. Returns the updated row, or undefined if not found. */
25
+ export declare function updateWorkspace(id: string, fields: UpdateWorkspaceFields): WorkspaceRow | undefined;
26
+ //# sourceMappingURL=workspace-store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"workspace-store.d.ts","sourceRoot":"","sources":["../src/workspace-store.ts"],"names":[],"mappings":"AACA,OAAO,EAAc,KAAK,YAAY,EAAE,MAAM,aAAa,CAAC;AAG5D,YAAY,EAAE,YAAY,EAAE,CAAC;AAE7B,qCAAqC;AACrC,wBAAgB,eAAe,CAC7B,EAAE,EAAE,MAAM,EACV,IAAI,EAAE,MAAM,EACZ,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE,MAAM,EACf,oBAAoB,EAAE,MAAM,EAC5B,YAAY,GAAE,OAAc,EAC5B,gBAAgB,GAAE,MAAW,EAC7B,gBAAgB,GAAE,MAAW,GAC5B,IAAI,CAWN;AAED,yCAAyC;AACzC,wBAAgB,YAAY,CAAC,EAAE,EAAE,MAAM,GAAG,YAAY,GAAG,SAAS,CAEjE;AAED,kDAAkD;AAClD,wBAAgB,cAAc,IAAI,YAAY,EAAE,CAK/C;AAED,oCAAoC;AACpC,wBAAgB,gBAAgB,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,CAKjD;AAED,sGAAsG;AACtG,MAAM,WAAW,qBAAqB;IACpC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,4FAA4F;IAC5F,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,0FAA0F;IAC1F,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,mDAAmD;IACnD,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,8GAA8G;AAC9G,wBAAgB,eAAe,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,qBAAqB,GAAG,YAAY,GAAG,SAAS,CAyBnG"}
@@ -1,9 +1,9 @@
1
1
  import db from "./db.js";
2
- import { projects } from "./schema.js";
2
+ import { workspaces } from "./schema.js";
3
3
  import { eq, desc, sql } from "drizzle-orm";
4
- /** Insert a new project record. */
5
- export function createProject(id, name, description, repoUrl, defaultEnvironmentId, useWorktrees = true, worktreeBasePath = "", defaultPersonaId = "") {
6
- db.insert(projects).values({
4
+ /** Insert a new workspace record. */
5
+ export function createWorkspace(id, name, description, repoUrl, defaultEnvironmentId, useWorktrees = true, worktreeBasePath = "", defaultPersonaId = "") {
6
+ db.insert(workspaces).values({
7
7
  id,
8
8
  name,
9
9
  description,
@@ -14,26 +14,26 @@ export function createProject(id, name, description, repoUrl, defaultEnvironment
14
14
  defaultPersonaId,
15
15
  }).run();
16
16
  }
17
- /** Retrieve a single project by ID. */
18
- export function getProject(id) {
19
- return db.select().from(projects).where(eq(projects.id, id)).get();
17
+ /** Retrieve a single workspace by ID. */
18
+ export function getWorkspace(id) {
19
+ return db.select().from(workspaces).where(eq(workspaces.id, id)).get();
20
20
  }
21
- /** Return all active projects, newest first. */
22
- export function listProjects() {
23
- return db.select().from(projects)
24
- .where(eq(projects.status, "active"))
25
- .orderBy(desc(projects.createdAt))
21
+ /** Return all active workspaces, newest first. */
22
+ export function listWorkspaces() {
23
+ return db.select().from(workspaces)
24
+ .where(eq(workspaces.status, "active"))
25
+ .orderBy(desc(workspaces.createdAt))
26
26
  .all();
27
27
  }
28
- /** Mark a project as archived. */
29
- export function archiveProject(id) {
30
- db.update(projects)
28
+ /** Mark a workspace as archived. */
29
+ export function archiveWorkspace(id) {
30
+ db.update(workspaces)
31
31
  .set({ status: "archived", updatedAt: sql `datetime('now')` })
32
- .where(eq(projects.id, id))
32
+ .where(eq(workspaces.id, id))
33
33
  .run();
34
34
  }
35
- /** Update one or more fields on an existing project. Returns the updated row, or undefined if not found. */
36
- export function updateProject(id, fields) {
35
+ /** Update one or more fields on an existing workspace. Returns the updated row, or undefined if not found. */
36
+ export function updateWorkspace(id, fields) {
37
37
  const sets = { updatedAt: sql `datetime('now')` };
38
38
  if (fields.name !== undefined) {
39
39
  sets.name = fields.name;
@@ -56,7 +56,7 @@ export function updateProject(id, fields) {
56
56
  if (fields.defaultPersonaId !== undefined) {
57
57
  sets.defaultPersonaId = fields.defaultPersonaId;
58
58
  }
59
- db.update(projects).set(sets).where(eq(projects.id, id)).run();
60
- return getProject(id);
59
+ db.update(workspaces).set(sets).where(eq(workspaces.id, id)).run();
60
+ return getWorkspace(id);
61
61
  }
62
- //# sourceMappingURL=project-store.js.map
62
+ //# sourceMappingURL=workspace-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"workspace-store.js","sourceRoot":"","sources":["../src/workspace-store.ts"],"names":[],"mappings":"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"}
package/dist/ws-bridge.js CHANGED
@@ -8,7 +8,7 @@ import { reconnectOrProvision, } from "@grackle-ai/adapter-sdk";
8
8
  import * as streamHub from "./stream-hub.js";
9
9
  import * as tokenBroker from "./token-broker.js";
10
10
  import * as credentialProviders from "./credential-providers.js";
11
- import * as projectStore from "./project-store.js";
11
+ import * as workspaceStore from "./workspace-store.js";
12
12
  import * as taskStore from "./task-store.js";
13
13
  import * as findingStore from "./finding-store.js";
14
14
  import * as personaStore from "./persona-store.js";
@@ -174,12 +174,12 @@ async function autoProvisionEnvironment(ws, environmentId, env, logContext) {
174
174
  * string for failures that need the caller to surface to the client.
175
175
  */
176
176
  async function startTaskSession(ws, task, options) {
177
- const project = task.projectId ? projectStore.getProject(task.projectId) : undefined;
178
- if (task.projectId && !project) {
179
- logger.warn({ taskId: task.id }, "startTaskSession failed: project not found");
180
- return `Project not found: ${task.projectId}`;
177
+ const workspace = task.workspaceId ? workspaceStore.getWorkspace(task.workspaceId) : undefined;
178
+ if (task.workspaceId && !workspace) {
179
+ logger.warn({ taskId: task.id }, "startTaskSession failed: workspace not found");
180
+ return `Workspace not found: ${task.workspaceId}`;
181
181
  }
182
- const environmentId = options?.environmentId || project?.defaultEnvironmentId || "";
182
+ const environmentId = options?.environmentId || workspace?.defaultEnvironmentId || "";
183
183
  const env = envRegistry.getEnvironment(environmentId);
184
184
  if (!env) {
185
185
  logger.warn({ taskId: task.id, environmentId }, "startTaskSession failed: environment not found");
@@ -191,10 +191,10 @@ async function startTaskSession(ws, task, options) {
191
191
  if (!conn) {
192
192
  return undefined;
193
193
  }
194
- // Resolve persona via cascade (request → task → project → app default)
194
+ // Resolve persona via cascade (request → task → workspace → app default)
195
195
  let resolved;
196
196
  try {
197
- resolved = resolvePersona(options?.personaId || "", task.defaultPersonaId, project?.defaultPersonaId || "");
197
+ resolved = resolvePersona(options?.personaId || "", task.defaultPersonaId, workspace?.defaultPersonaId || "");
198
198
  }
199
199
  catch (err) {
200
200
  return err.message;
@@ -211,7 +211,7 @@ async function startTaskSession(ws, task, options) {
211
211
  emit("task.started", {
212
212
  taskId: freshTask.id,
213
213
  sessionId,
214
- projectId: freshTask.projectId || "",
214
+ workspaceId: freshTask.workspaceId || "",
215
215
  });
216
216
  // Re-push stored tokens + provider credentials (scoped to runtime) so they're fresh for this session.
217
217
  // For local envs, skip file tokens — the PowerLine is on the same machine.
@@ -233,8 +233,8 @@ async function startTaskSession(ws, task, options) {
233
233
  const mcpPort = parseInt(process.env.GRACKLE_MCP_PORT || String(DEFAULT_MCP_PORT), 10);
234
234
  const mcpDialHost = toDialableHost(process.env.GRACKLE_HOST || "127.0.0.1");
235
235
  const mcpUrl = `http://${mcpDialHost}:${mcpPort}/mcp`;
236
- const mcpToken = createScopedToken({ sub: freshTask.id, pid: freshTask.projectId || "", per: resolved.personaId, sid: sessionId }, loadOrCreateApiKey());
237
- const useWorktrees = project?.useWorktrees ?? false;
236
+ const mcpToken = createScopedToken({ sub: freshTask.id, pid: freshTask.workspaceId || "", per: resolved.personaId, sid: sessionId }, loadOrCreateApiKey());
237
+ const useWorktrees = workspace?.useWorktrees ?? false;
238
238
  const powerlineReq = create(powerline.SpawnRequestSchema, {
239
239
  sessionId,
240
240
  runtime,
@@ -243,10 +243,10 @@ async function startTaskSession(ws, task, options) {
243
243
  maxTurns,
244
244
  branch: freshTask.branch,
245
245
  worktreeBasePath: freshTask.branch && useWorktrees
246
- ? (project?.worktreeBasePath || process.env.GRACKLE_WORKTREE_BASE || "/workspace")
246
+ ? (workspace?.worktreeBasePath || process.env.GRACKLE_WORKTREE_BASE || "/workspace")
247
247
  : "",
248
248
  systemContext,
249
- projectId: freshTask.projectId ?? undefined,
249
+ workspaceId: freshTask.workspaceId ?? undefined,
250
250
  taskId: freshTask.id,
251
251
  mcpServersJson,
252
252
  mcpUrl,
@@ -255,7 +255,7 @@ async function startTaskSession(ws, task, options) {
255
255
  processEventStream(conn.client.spawn(powerlineReq), {
256
256
  sessionId,
257
257
  logPath,
258
- projectId: freshTask.projectId ?? undefined,
258
+ workspaceId: freshTask.workspaceId ?? undefined,
259
259
  taskId: freshTask.id,
260
260
  });
261
261
  return undefined;
@@ -530,7 +530,7 @@ async function handleMessage(ws, msg, subscriptions) {
530
530
  if (session.taskId) {
531
531
  const task = taskStore.getTask(session.taskId);
532
532
  if (task) {
533
- emit("task.updated", { taskId: task.id, projectId: task.projectId || "" });
533
+ emit("task.updated", { taskId: task.id, workspaceId: task.workspaceId || "" });
534
534
  }
535
535
  }
536
536
  break;
@@ -563,7 +563,7 @@ async function handleMessage(ws, msg, subscriptions) {
563
563
  // Mark task complete (same as "complete_task" handler)
564
564
  taskStore.markTaskComplete(taskId, TASK_STATUS.COMPLETE);
565
565
  const stoppedTask = taskStore.getTask(taskId);
566
- const unblocked = stoppedTask?.projectId ? taskStore.checkAndUnblock(stoppedTask.projectId) : [];
566
+ const unblocked = stoppedTask?.workspaceId ? taskStore.checkAndUnblock(stoppedTask.workspaceId) : [];
567
567
  sendWs(ws, {
568
568
  type: "task_completed",
569
569
  payload: {
@@ -572,7 +572,7 @@ async function handleMessage(ws, msg, subscriptions) {
572
572
  },
573
573
  });
574
574
  if (stoppedTask) {
575
- emit("task.completed", { taskId, projectId: stoppedTask.projectId || "" });
575
+ emit("task.completed", { taskId, workspaceId: stoppedTask.workspaceId || "" });
576
576
  }
577
577
  break;
578
578
  }
@@ -592,13 +592,13 @@ async function handleMessage(ws, msg, subscriptions) {
592
592
  }
593
593
  break;
594
594
  }
595
- // ─── Projects ──────────────────────────────────────────
596
- case "list_projects": {
597
- const rows = projectStore.listProjects();
595
+ // ─── Workspaces ────────────────────────────────────────
596
+ case "list_workspaces": {
597
+ const rows = workspaceStore.listWorkspaces();
598
598
  sendWs(ws, {
599
- type: "projects",
599
+ type: "workspaces",
600
600
  payload: {
601
- projects: rows.map((r) => ({
601
+ workspaces: rows.map((r) => ({
602
602
  id: r.id,
603
603
  name: r.name,
604
604
  description: r.description,
@@ -615,47 +615,47 @@ async function handleMessage(ws, msg, subscriptions) {
615
615
  });
616
616
  break;
617
617
  }
618
- case "create_project": {
618
+ case "create_workspace": {
619
619
  const name = msg.payload?.name;
620
620
  if (!name) {
621
621
  sendWs(ws, { type: "error", payload: { message: "name required" } });
622
622
  return;
623
623
  }
624
- const baseProjectId = slugify(name) || uuid().slice(0, 8);
625
- let id = baseProjectId;
626
- for (let attempt = 0; attempt < 10 && projectStore.getProject(id); attempt++) {
627
- id = `${baseProjectId}-${uuid().slice(0, 4)}`;
624
+ const baseWorkspaceId = slugify(name) || uuid().slice(0, 8);
625
+ let id = baseWorkspaceId;
626
+ for (let attempt = 0; attempt < 10 && workspaceStore.getWorkspace(id); attempt++) {
627
+ id = `${baseWorkspaceId}-${uuid().slice(0, 4)}`;
628
628
  }
629
- if (projectStore.getProject(id)) {
629
+ if (workspaceStore.getWorkspace(id)) {
630
630
  id = uuid();
631
631
  }
632
632
  // useWorktrees defaults to true when not specified
633
633
  const createUseWorktrees = msg.payload?.useWorktrees ?? true;
634
- projectStore.createProject(id, name, msg.payload?.description || "", msg.payload?.repoUrl || "", msg.payload?.defaultEnvironmentId || "", createUseWorktrees, typeof msg.payload?.worktreeBasePath === "string" ? msg.payload.worktreeBasePath.trim() : "", msg.payload?.defaultPersonaId || "");
635
- emit("project.created", { projectId: id });
634
+ workspaceStore.createWorkspace(id, name, msg.payload?.description || "", msg.payload?.repoUrl || "", msg.payload?.defaultEnvironmentId || "", createUseWorktrees, typeof msg.payload?.worktreeBasePath === "string" ? msg.payload.worktreeBasePath.trim() : "", msg.payload?.defaultPersonaId || "");
635
+ emit("workspace.created", { workspaceId: id });
636
636
  break;
637
637
  }
638
- case "archive_project": {
639
- const projectId = msg.payload?.projectId;
640
- if (projectId)
641
- projectStore.archiveProject(projectId);
642
- emit("project.archived", { projectId });
638
+ case "archive_workspace": {
639
+ const workspaceId = msg.payload?.workspaceId;
640
+ if (workspaceId)
641
+ workspaceStore.archiveWorkspace(workspaceId);
642
+ emit("workspace.archived", { workspaceId });
643
643
  break;
644
644
  }
645
- case "update_project": {
646
- const projectId = msg.payload?.projectId;
647
- if (!projectId) {
648
- sendWs(ws, { type: "error", payload: { message: "projectId required" } });
645
+ case "update_workspace": {
646
+ const workspaceId = msg.payload?.workspaceId;
647
+ if (!workspaceId) {
648
+ sendWs(ws, { type: "error", payload: { message: "workspaceId required" } });
649
649
  return;
650
650
  }
651
- const existing = projectStore.getProject(projectId);
651
+ const existing = workspaceStore.getWorkspace(workspaceId);
652
652
  if (!existing) {
653
- sendWs(ws, { type: "error", payload: { message: `Project not found: ${projectId}` } });
653
+ sendWs(ws, { type: "error", payload: { message: `Workspace not found: ${workspaceId}` } });
654
654
  return;
655
655
  }
656
656
  const nameVal = typeof msg.payload?.name === "string" ? msg.payload.name : undefined;
657
657
  if (nameVal?.trim() === "") {
658
- sendWs(ws, { type: "error", payload: { message: "Project name cannot be empty" } });
658
+ sendWs(ws, { type: "error", payload: { message: "Workspace name cannot be empty" } });
659
659
  return;
660
660
  }
661
661
  const descVal = typeof msg.payload?.description === "string" ? msg.payload.description : undefined;
@@ -668,7 +668,7 @@ async function handleMessage(ws, msg, subscriptions) {
668
668
  const worktreesVal = typeof msg.payload?.useWorktrees === "boolean" ? msg.payload.useWorktrees : undefined;
669
669
  const worktreeBasePathVal = typeof msg.payload?.worktreeBasePath === "string" ? msg.payload.worktreeBasePath : undefined;
670
670
  const defaultPersonaIdVal = typeof msg.payload?.defaultPersonaId === "string" ? msg.payload.defaultPersonaId : undefined;
671
- projectStore.updateProject(projectId, {
671
+ workspaceStore.updateWorkspace(workspaceId, {
672
672
  name: nameVal !== undefined ? nameVal.trim() : undefined,
673
673
  description: descVal,
674
674
  repoUrl: repoVal,
@@ -677,7 +677,7 @@ async function handleMessage(ws, msg, subscriptions) {
677
677
  worktreeBasePath: worktreeBasePathVal,
678
678
  defaultPersonaId: defaultPersonaIdVal,
679
679
  });
680
- emit("project.updated", { projectId });
680
+ emit("workspace.updated", { workspaceId });
681
681
  break;
682
682
  }
683
683
  // ─── Personas ──────────────────────────────────────────
@@ -823,8 +823,8 @@ async function handleMessage(ws, msg, subscriptions) {
823
823
  }
824
824
  // ─── Tasks ─────────────────────────────────────────────
825
825
  case "list_tasks": {
826
- const projectId = msg.payload?.projectId || undefined;
827
- const rows = taskStore.listTasks(projectId, {
826
+ const workspaceId = msg.payload?.workspaceId || undefined;
827
+ const rows = taskStore.listTasks(workspaceId, {
828
828
  search: msg.payload?.search || undefined,
829
829
  status: msg.payload?.status || undefined,
830
830
  });
@@ -841,13 +841,13 @@ async function handleMessage(ws, msg, subscriptions) {
841
841
  sendWs(ws, {
842
842
  type: "tasks",
843
843
  payload: {
844
- projectId,
844
+ workspaceId,
845
845
  tasks: rows.map((r) => {
846
846
  const taskSessions = sessionsByTask.get(r.id) ?? [];
847
847
  const computed = computeTaskStatus(r.status, taskSessions);
848
848
  return {
849
849
  id: r.id,
850
- projectId: r.projectId ?? undefined,
850
+ workspaceId: r.workspaceId ?? undefined,
851
851
  title: r.title,
852
852
  description: r.description,
853
853
  status: computed.status,
@@ -868,7 +868,7 @@ async function handleMessage(ws, msg, subscriptions) {
868
868
  break;
869
869
  }
870
870
  case "create_task": {
871
- const projectId = msg.payload?.projectId || undefined;
871
+ const workspaceId = msg.payload?.workspaceId || undefined;
872
872
  const title = msg.payload?.title;
873
873
  const requestId = typeof msg.payload?.requestId === "string"
874
874
  ? msg.payload.requestId
@@ -880,13 +880,13 @@ async function handleMessage(ws, msg, subscriptions) {
880
880
  });
881
881
  return;
882
882
  }
883
- let project;
884
- if (projectId) {
885
- project = projectStore.getProject(projectId);
886
- if (!project) {
883
+ let workspace;
884
+ if (workspaceId) {
885
+ workspace = workspaceStore.getWorkspace(workspaceId);
886
+ if (!workspace) {
887
887
  sendWs(ws, {
888
888
  type: "create_task_error",
889
- payload: { message: `Project not found: ${projectId}`, requestId },
889
+ payload: { message: `Workspace not found: ${workspaceId}`, requestId },
890
890
  });
891
891
  return;
892
892
  }
@@ -898,8 +898,8 @@ async function handleMessage(ws, msg, subscriptions) {
898
898
  const canDecompose = typeof rawCanDecompose === "boolean" ? rawCanDecompose : false;
899
899
  try {
900
900
  const id = uuid().slice(0, 8);
901
- taskStore.createTask(id, projectId, title, msg.payload?.description || "", msg.payload?.dependsOn || [], project ? slugify(project.name) : "", parentTaskId, canDecompose, msg.payload?.defaultPersonaId || "");
902
- emit("task.created", { taskId: id, projectId, requestId });
901
+ taskStore.createTask(id, workspaceId, title, msg.payload?.description || "", msg.payload?.dependsOn || [], workspace ? slugify(workspace.name) : "", parentTaskId, canDecompose, msg.payload?.defaultPersonaId || "");
902
+ emit("task.created", { taskId: id, workspaceId, requestId });
903
903
  }
904
904
  catch (error) {
905
905
  const message = error instanceof Error ? error.message : "Failed to create task";
@@ -947,13 +947,13 @@ async function handleMessage(ws, msg, subscriptions) {
947
947
  }
948
948
  sessionStore.setSessionTask(lateBindSessionId, updateTaskId);
949
949
  try {
950
- processorRegistry.lateBind(lateBindSessionId, updateTaskId, existingTask.projectId || undefined);
950
+ processorRegistry.lateBind(lateBindSessionId, updateTaskId, existingTask.workspaceId || undefined);
951
951
  }
952
952
  catch (err) {
953
953
  sendWs(ws, { type: "error", payload: { message: String(err) } });
954
954
  return;
955
955
  }
956
- emit("task.started", { taskId: updateTaskId, sessionId: lateBindSessionId, projectId: existingTask.projectId || "" });
956
+ emit("task.started", { taskId: updateTaskId, sessionId: lateBindSessionId, workspaceId: existingTask.workspaceId || "" });
957
957
  break;
958
958
  }
959
959
  // Only allow editing not_started tasks (non-late-bind path)
@@ -982,7 +982,7 @@ async function handleMessage(ws, msg, subscriptions) {
982
982
  ? msg.payload.defaultPersonaId
983
983
  : undefined;
984
984
  taskStore.updateTask(updateTaskId, updatedTitle, updatedDescription, existingTask.status, updatedDependsOn, updatedDefaultPersonaId);
985
- emit("task.updated", { taskId: updateTaskId, projectId: existingTask.projectId || "" });
985
+ emit("task.updated", { taskId: updateTaskId, workspaceId: existingTask.workspaceId || "" });
986
986
  break;
987
987
  }
988
988
  case "start_task": {
@@ -1033,7 +1033,7 @@ async function handleMessage(ws, msg, subscriptions) {
1033
1033
  return;
1034
1034
  taskStore.markTaskComplete(taskId, TASK_STATUS.COMPLETE);
1035
1035
  const task = taskStore.getTask(taskId);
1036
- const unblocked = task?.projectId ? taskStore.checkAndUnblock(task.projectId) : [];
1036
+ const unblocked = task?.workspaceId ? taskStore.checkAndUnblock(task.workspaceId) : [];
1037
1037
  sendWs(ws, {
1038
1038
  type: "task_completed",
1039
1039
  payload: {
@@ -1042,7 +1042,7 @@ async function handleMessage(ws, msg, subscriptions) {
1042
1042
  },
1043
1043
  });
1044
1044
  if (task) {
1045
- emit("task.completed", { taskId, projectId: task.projectId || "" });
1045
+ emit("task.completed", { taskId, workspaceId: task.workspaceId || "" });
1046
1046
  }
1047
1047
  break;
1048
1048
  }
@@ -1092,10 +1092,10 @@ async function handleMessage(ws, msg, subscriptions) {
1092
1092
  processEventStream(conn.client.resume(powerlineReq), {
1093
1093
  sessionId: latestSession.id,
1094
1094
  logPath,
1095
- projectId: task.projectId ?? undefined,
1095
+ workspaceId: task.workspaceId ?? undefined,
1096
1096
  taskId: task.id,
1097
1097
  });
1098
- emit("task.started", { taskId: task.id, sessionId: latestSession.id, projectId: task.projectId || "" });
1098
+ emit("task.started", { taskId: task.id, sessionId: latestSession.id, workspaceId: task.workspaceId || "" });
1099
1099
  break;
1100
1100
  }
1101
1101
  case "delete_task": {
@@ -1144,7 +1144,7 @@ async function handleMessage(ws, msg, subscriptions) {
1144
1144
  sendWs(ws, { type: "error", payload: { message: `Failed to delete task ${taskId}: no rows affected` } });
1145
1145
  return;
1146
1146
  }
1147
- emit("task.deleted", { taskId, projectId: deletedTask.projectId || "" });
1147
+ emit("task.deleted", { taskId, workspaceId: deletedTask.workspaceId || "" });
1148
1148
  break;
1149
1149
  }
1150
1150
  // ─── Task Sessions ─────────────────────────────────────
@@ -1175,17 +1175,17 @@ async function handleMessage(ws, msg, subscriptions) {
1175
1175
  }
1176
1176
  // ─── Findings ──────────────────────────────────────────
1177
1177
  case "list_findings": {
1178
- const projectId = msg.payload?.projectId;
1179
- if (!projectId)
1178
+ const workspaceId = msg.payload?.workspaceId;
1179
+ if (!workspaceId)
1180
1180
  return;
1181
- const rows = findingStore.queryFindings(projectId, msg.payload?.categories || undefined, msg.payload?.tags || undefined, msg.payload?.limit || undefined);
1181
+ const rows = findingStore.queryFindings(workspaceId, msg.payload?.categories || undefined, msg.payload?.tags || undefined, msg.payload?.limit || undefined);
1182
1182
  sendWs(ws, {
1183
1183
  type: "findings",
1184
1184
  payload: {
1185
- projectId,
1185
+ workspaceId,
1186
1186
  findings: rows.map((r) => ({
1187
1187
  id: r.id,
1188
- projectId: r.projectId,
1188
+ workspaceId: r.workspaceId,
1189
1189
  taskId: r.taskId,
1190
1190
  sessionId: r.sessionId,
1191
1191
  category: r.category,
@@ -1199,18 +1199,18 @@ async function handleMessage(ws, msg, subscriptions) {
1199
1199
  break;
1200
1200
  }
1201
1201
  case "post_finding": {
1202
- const projectId = msg.payload?.projectId;
1202
+ const workspaceId = msg.payload?.workspaceId;
1203
1203
  const title = msg.payload?.title;
1204
- if (!projectId || !title) {
1204
+ if (!workspaceId || !title) {
1205
1205
  sendWs(ws, {
1206
1206
  type: "error",
1207
- payload: { message: "projectId and title required" },
1207
+ payload: { message: "workspaceId and title required" },
1208
1208
  });
1209
1209
  return;
1210
1210
  }
1211
1211
  const id = uuid().slice(0, 8);
1212
- findingStore.postFinding(id, projectId, msg.payload?.taskId || "", msg.payload?.sessionId || "", msg.payload?.category || "general", title, msg.payload?.content || "", msg.payload?.tags || []);
1213
- sendWs(ws, { type: "finding_posted", payload: { id, projectId } });
1212
+ findingStore.postFinding(id, workspaceId, msg.payload?.taskId || "", msg.payload?.sessionId || "", msg.payload?.category || "general", title, msg.payload?.content || "", msg.payload?.tags || []);
1213
+ sendWs(ws, { type: "finding_posted", payload: { id, workspaceId } });
1214
1214
  break;
1215
1215
  }
1216
1216
  // ─── Diff ──────────────────────────────────────────────
@@ -1226,8 +1226,8 @@ async function handleMessage(ws, msg, subscriptions) {
1226
1226
  });
1227
1227
  return;
1228
1228
  }
1229
- const environmentId = task.projectId
1230
- ? projectStore.getProject(task.projectId)?.defaultEnvironmentId
1229
+ const environmentId = task.workspaceId
1230
+ ? workspaceStore.getWorkspace(task.workspaceId)?.defaultEnvironmentId
1231
1231
  : undefined;
1232
1232
  if (!environmentId) {
1233
1233
  sendWs(ws, {