@grackle-ai/server 0.46.0 → 0.47.1

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 (52) 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.d.ts.map +1 -1
  19. package/dist/grpc-service.js +72 -74
  20. package/dist/grpc-service.js.map +1 -1
  21. package/dist/processor-registry.d.ts +3 -3
  22. package/dist/processor-registry.d.ts.map +1 -1
  23. package/dist/processor-registry.js +4 -4
  24. package/dist/processor-registry.js.map +1 -1
  25. package/dist/reanimate-agent.js +3 -3
  26. package/dist/reanimate-agent.js.map +1 -1
  27. package/dist/resolve-persona.d.ts +2 -2
  28. package/dist/resolve-persona.d.ts.map +1 -1
  29. package/dist/resolve-persona.js +4 -4
  30. package/dist/resolve-persona.js.map +1 -1
  31. package/dist/schema.d.ts +21 -21
  32. package/dist/schema.d.ts.map +1 -1
  33. package/dist/schema.js +6 -6
  34. package/dist/schema.js.map +1 -1
  35. package/dist/task-store.d.ts +6 -6
  36. package/dist/task-store.d.ts.map +1 -1
  37. package/dist/task-store.js +15 -15
  38. package/dist/task-store.js.map +1 -1
  39. package/dist/utils/slugify.d.ts +1 -1
  40. package/dist/utils/slugify.js +1 -1
  41. package/dist/utils/system-context.js +4 -4
  42. package/dist/utils/system-context.js.map +1 -1
  43. package/dist/workspace-store.d.ts +26 -0
  44. package/dist/workspace-store.d.ts.map +1 -0
  45. package/dist/{project-store.js → workspace-store.js} +21 -21
  46. package/dist/workspace-store.js.map +1 -0
  47. package/dist/ws-bridge.js +78 -77
  48. package/dist/ws-bridge.js.map +1 -1
  49. package/package.json +6 -6
  50. package/dist/project-store.d.ts +0 -26
  51. package/dist/project-store.d.ts.map +0 -1
  52. 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,
@@ -242,11 +242,12 @@ async function startTaskSession(ws, task, options) {
242
242
  model,
243
243
  maxTurns,
244
244
  branch: freshTask.branch,
245
- worktreeBasePath: freshTask.branch && useWorktrees
246
- ? (project?.worktreeBasePath || process.env.GRACKLE_WORKTREE_BASE || "/workspace")
245
+ worktreeBasePath: freshTask.branch
246
+ ? (workspace?.worktreeBasePath || process.env.GRACKLE_WORKTREE_BASE || "/workspace")
247
247
  : "",
248
+ useWorktrees,
248
249
  systemContext,
249
- projectId: freshTask.projectId ?? undefined,
250
+ workspaceId: freshTask.workspaceId ?? undefined,
250
251
  taskId: freshTask.id,
251
252
  mcpServersJson,
252
253
  mcpUrl,
@@ -255,7 +256,7 @@ async function startTaskSession(ws, task, options) {
255
256
  processEventStream(conn.client.spawn(powerlineReq), {
256
257
  sessionId,
257
258
  logPath,
258
- projectId: freshTask.projectId ?? undefined,
259
+ workspaceId: freshTask.workspaceId ?? undefined,
259
260
  taskId: freshTask.id,
260
261
  });
261
262
  return undefined;
@@ -530,7 +531,7 @@ async function handleMessage(ws, msg, subscriptions) {
530
531
  if (session.taskId) {
531
532
  const task = taskStore.getTask(session.taskId);
532
533
  if (task) {
533
- emit("task.updated", { taskId: task.id, projectId: task.projectId || "" });
534
+ emit("task.updated", { taskId: task.id, workspaceId: task.workspaceId || "" });
534
535
  }
535
536
  }
536
537
  break;
@@ -563,7 +564,7 @@ async function handleMessage(ws, msg, subscriptions) {
563
564
  // Mark task complete (same as "complete_task" handler)
564
565
  taskStore.markTaskComplete(taskId, TASK_STATUS.COMPLETE);
565
566
  const stoppedTask = taskStore.getTask(taskId);
566
- const unblocked = stoppedTask?.projectId ? taskStore.checkAndUnblock(stoppedTask.projectId) : [];
567
+ const unblocked = stoppedTask?.workspaceId ? taskStore.checkAndUnblock(stoppedTask.workspaceId) : [];
567
568
  sendWs(ws, {
568
569
  type: "task_completed",
569
570
  payload: {
@@ -572,7 +573,7 @@ async function handleMessage(ws, msg, subscriptions) {
572
573
  },
573
574
  });
574
575
  if (stoppedTask) {
575
- emit("task.completed", { taskId, projectId: stoppedTask.projectId || "" });
576
+ emit("task.completed", { taskId, workspaceId: stoppedTask.workspaceId || "" });
576
577
  }
577
578
  break;
578
579
  }
@@ -592,13 +593,13 @@ async function handleMessage(ws, msg, subscriptions) {
592
593
  }
593
594
  break;
594
595
  }
595
- // ─── Projects ──────────────────────────────────────────
596
- case "list_projects": {
597
- const rows = projectStore.listProjects();
596
+ // ─── Workspaces ────────────────────────────────────────
597
+ case "list_workspaces": {
598
+ const rows = workspaceStore.listWorkspaces();
598
599
  sendWs(ws, {
599
- type: "projects",
600
+ type: "workspaces",
600
601
  payload: {
601
- projects: rows.map((r) => ({
602
+ workspaces: rows.map((r) => ({
602
603
  id: r.id,
603
604
  name: r.name,
604
605
  description: r.description,
@@ -615,47 +616,47 @@ async function handleMessage(ws, msg, subscriptions) {
615
616
  });
616
617
  break;
617
618
  }
618
- case "create_project": {
619
+ case "create_workspace": {
619
620
  const name = msg.payload?.name;
620
621
  if (!name) {
621
622
  sendWs(ws, { type: "error", payload: { message: "name required" } });
622
623
  return;
623
624
  }
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)}`;
625
+ const baseWorkspaceId = slugify(name) || uuid().slice(0, 8);
626
+ let id = baseWorkspaceId;
627
+ for (let attempt = 0; attempt < 10 && workspaceStore.getWorkspace(id); attempt++) {
628
+ id = `${baseWorkspaceId}-${uuid().slice(0, 4)}`;
628
629
  }
629
- if (projectStore.getProject(id)) {
630
+ if (workspaceStore.getWorkspace(id)) {
630
631
  id = uuid();
631
632
  }
632
633
  // useWorktrees defaults to true when not specified
633
634
  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 });
635
+ 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 || "");
636
+ emit("workspace.created", { workspaceId: id });
636
637
  break;
637
638
  }
638
- case "archive_project": {
639
- const projectId = msg.payload?.projectId;
640
- if (projectId)
641
- projectStore.archiveProject(projectId);
642
- emit("project.archived", { projectId });
639
+ case "archive_workspace": {
640
+ const workspaceId = msg.payload?.workspaceId;
641
+ if (workspaceId)
642
+ workspaceStore.archiveWorkspace(workspaceId);
643
+ emit("workspace.archived", { workspaceId });
643
644
  break;
644
645
  }
645
- case "update_project": {
646
- const projectId = msg.payload?.projectId;
647
- if (!projectId) {
648
- sendWs(ws, { type: "error", payload: { message: "projectId required" } });
646
+ case "update_workspace": {
647
+ const workspaceId = msg.payload?.workspaceId;
648
+ if (!workspaceId) {
649
+ sendWs(ws, { type: "error", payload: { message: "workspaceId required" } });
649
650
  return;
650
651
  }
651
- const existing = projectStore.getProject(projectId);
652
+ const existing = workspaceStore.getWorkspace(workspaceId);
652
653
  if (!existing) {
653
- sendWs(ws, { type: "error", payload: { message: `Project not found: ${projectId}` } });
654
+ sendWs(ws, { type: "error", payload: { message: `Workspace not found: ${workspaceId}` } });
654
655
  return;
655
656
  }
656
657
  const nameVal = typeof msg.payload?.name === "string" ? msg.payload.name : undefined;
657
658
  if (nameVal?.trim() === "") {
658
- sendWs(ws, { type: "error", payload: { message: "Project name cannot be empty" } });
659
+ sendWs(ws, { type: "error", payload: { message: "Workspace name cannot be empty" } });
659
660
  return;
660
661
  }
661
662
  const descVal = typeof msg.payload?.description === "string" ? msg.payload.description : undefined;
@@ -668,7 +669,7 @@ async function handleMessage(ws, msg, subscriptions) {
668
669
  const worktreesVal = typeof msg.payload?.useWorktrees === "boolean" ? msg.payload.useWorktrees : undefined;
669
670
  const worktreeBasePathVal = typeof msg.payload?.worktreeBasePath === "string" ? msg.payload.worktreeBasePath : undefined;
670
671
  const defaultPersonaIdVal = typeof msg.payload?.defaultPersonaId === "string" ? msg.payload.defaultPersonaId : undefined;
671
- projectStore.updateProject(projectId, {
672
+ workspaceStore.updateWorkspace(workspaceId, {
672
673
  name: nameVal !== undefined ? nameVal.trim() : undefined,
673
674
  description: descVal,
674
675
  repoUrl: repoVal,
@@ -677,7 +678,7 @@ async function handleMessage(ws, msg, subscriptions) {
677
678
  worktreeBasePath: worktreeBasePathVal,
678
679
  defaultPersonaId: defaultPersonaIdVal,
679
680
  });
680
- emit("project.updated", { projectId });
681
+ emit("workspace.updated", { workspaceId });
681
682
  break;
682
683
  }
683
684
  // ─── Personas ──────────────────────────────────────────
@@ -823,8 +824,8 @@ async function handleMessage(ws, msg, subscriptions) {
823
824
  }
824
825
  // ─── Tasks ─────────────────────────────────────────────
825
826
  case "list_tasks": {
826
- const projectId = msg.payload?.projectId || undefined;
827
- const rows = taskStore.listTasks(projectId, {
827
+ const workspaceId = msg.payload?.workspaceId || undefined;
828
+ const rows = taskStore.listTasks(workspaceId, {
828
829
  search: msg.payload?.search || undefined,
829
830
  status: msg.payload?.status || undefined,
830
831
  });
@@ -841,13 +842,13 @@ async function handleMessage(ws, msg, subscriptions) {
841
842
  sendWs(ws, {
842
843
  type: "tasks",
843
844
  payload: {
844
- projectId,
845
+ workspaceId,
845
846
  tasks: rows.map((r) => {
846
847
  const taskSessions = sessionsByTask.get(r.id) ?? [];
847
848
  const computed = computeTaskStatus(r.status, taskSessions);
848
849
  return {
849
850
  id: r.id,
850
- projectId: r.projectId ?? undefined,
851
+ workspaceId: r.workspaceId ?? undefined,
851
852
  title: r.title,
852
853
  description: r.description,
853
854
  status: computed.status,
@@ -868,7 +869,7 @@ async function handleMessage(ws, msg, subscriptions) {
868
869
  break;
869
870
  }
870
871
  case "create_task": {
871
- const projectId = msg.payload?.projectId || undefined;
872
+ const workspaceId = msg.payload?.workspaceId || undefined;
872
873
  const title = msg.payload?.title;
873
874
  const requestId = typeof msg.payload?.requestId === "string"
874
875
  ? msg.payload.requestId
@@ -880,13 +881,13 @@ async function handleMessage(ws, msg, subscriptions) {
880
881
  });
881
882
  return;
882
883
  }
883
- let project;
884
- if (projectId) {
885
- project = projectStore.getProject(projectId);
886
- if (!project) {
884
+ let workspace;
885
+ if (workspaceId) {
886
+ workspace = workspaceStore.getWorkspace(workspaceId);
887
+ if (!workspace) {
887
888
  sendWs(ws, {
888
889
  type: "create_task_error",
889
- payload: { message: `Project not found: ${projectId}`, requestId },
890
+ payload: { message: `Workspace not found: ${workspaceId}`, requestId },
890
891
  });
891
892
  return;
892
893
  }
@@ -898,8 +899,8 @@ async function handleMessage(ws, msg, subscriptions) {
898
899
  const canDecompose = typeof rawCanDecompose === "boolean" ? rawCanDecompose : false;
899
900
  try {
900
901
  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 });
902
+ taskStore.createTask(id, workspaceId, title, msg.payload?.description || "", msg.payload?.dependsOn || [], workspace ? slugify(workspace.name) : "", parentTaskId, canDecompose, msg.payload?.defaultPersonaId || "");
903
+ emit("task.created", { taskId: id, workspaceId, requestId });
903
904
  }
904
905
  catch (error) {
905
906
  const message = error instanceof Error ? error.message : "Failed to create task";
@@ -947,13 +948,13 @@ async function handleMessage(ws, msg, subscriptions) {
947
948
  }
948
949
  sessionStore.setSessionTask(lateBindSessionId, updateTaskId);
949
950
  try {
950
- processorRegistry.lateBind(lateBindSessionId, updateTaskId, existingTask.projectId || undefined);
951
+ processorRegistry.lateBind(lateBindSessionId, updateTaskId, existingTask.workspaceId || undefined);
951
952
  }
952
953
  catch (err) {
953
954
  sendWs(ws, { type: "error", payload: { message: String(err) } });
954
955
  return;
955
956
  }
956
- emit("task.started", { taskId: updateTaskId, sessionId: lateBindSessionId, projectId: existingTask.projectId || "" });
957
+ emit("task.started", { taskId: updateTaskId, sessionId: lateBindSessionId, workspaceId: existingTask.workspaceId || "" });
957
958
  break;
958
959
  }
959
960
  // Only allow editing not_started tasks (non-late-bind path)
@@ -982,7 +983,7 @@ async function handleMessage(ws, msg, subscriptions) {
982
983
  ? msg.payload.defaultPersonaId
983
984
  : undefined;
984
985
  taskStore.updateTask(updateTaskId, updatedTitle, updatedDescription, existingTask.status, updatedDependsOn, updatedDefaultPersonaId);
985
- emit("task.updated", { taskId: updateTaskId, projectId: existingTask.projectId || "" });
986
+ emit("task.updated", { taskId: updateTaskId, workspaceId: existingTask.workspaceId || "" });
986
987
  break;
987
988
  }
988
989
  case "start_task": {
@@ -1033,7 +1034,7 @@ async function handleMessage(ws, msg, subscriptions) {
1033
1034
  return;
1034
1035
  taskStore.markTaskComplete(taskId, TASK_STATUS.COMPLETE);
1035
1036
  const task = taskStore.getTask(taskId);
1036
- const unblocked = task?.projectId ? taskStore.checkAndUnblock(task.projectId) : [];
1037
+ const unblocked = task?.workspaceId ? taskStore.checkAndUnblock(task.workspaceId) : [];
1037
1038
  sendWs(ws, {
1038
1039
  type: "task_completed",
1039
1040
  payload: {
@@ -1042,7 +1043,7 @@ async function handleMessage(ws, msg, subscriptions) {
1042
1043
  },
1043
1044
  });
1044
1045
  if (task) {
1045
- emit("task.completed", { taskId, projectId: task.projectId || "" });
1046
+ emit("task.completed", { taskId, workspaceId: task.workspaceId || "" });
1046
1047
  }
1047
1048
  break;
1048
1049
  }
@@ -1092,10 +1093,10 @@ async function handleMessage(ws, msg, subscriptions) {
1092
1093
  processEventStream(conn.client.resume(powerlineReq), {
1093
1094
  sessionId: latestSession.id,
1094
1095
  logPath,
1095
- projectId: task.projectId ?? undefined,
1096
+ workspaceId: task.workspaceId ?? undefined,
1096
1097
  taskId: task.id,
1097
1098
  });
1098
- emit("task.started", { taskId: task.id, sessionId: latestSession.id, projectId: task.projectId || "" });
1099
+ emit("task.started", { taskId: task.id, sessionId: latestSession.id, workspaceId: task.workspaceId || "" });
1099
1100
  break;
1100
1101
  }
1101
1102
  case "delete_task": {
@@ -1144,7 +1145,7 @@ async function handleMessage(ws, msg, subscriptions) {
1144
1145
  sendWs(ws, { type: "error", payload: { message: `Failed to delete task ${taskId}: no rows affected` } });
1145
1146
  return;
1146
1147
  }
1147
- emit("task.deleted", { taskId, projectId: deletedTask.projectId || "" });
1148
+ emit("task.deleted", { taskId, workspaceId: deletedTask.workspaceId || "" });
1148
1149
  break;
1149
1150
  }
1150
1151
  // ─── Task Sessions ─────────────────────────────────────
@@ -1175,17 +1176,17 @@ async function handleMessage(ws, msg, subscriptions) {
1175
1176
  }
1176
1177
  // ─── Findings ──────────────────────────────────────────
1177
1178
  case "list_findings": {
1178
- const projectId = msg.payload?.projectId;
1179
- if (!projectId)
1179
+ const workspaceId = msg.payload?.workspaceId;
1180
+ if (!workspaceId)
1180
1181
  return;
1181
- const rows = findingStore.queryFindings(projectId, msg.payload?.categories || undefined, msg.payload?.tags || undefined, msg.payload?.limit || undefined);
1182
+ const rows = findingStore.queryFindings(workspaceId, msg.payload?.categories || undefined, msg.payload?.tags || undefined, msg.payload?.limit || undefined);
1182
1183
  sendWs(ws, {
1183
1184
  type: "findings",
1184
1185
  payload: {
1185
- projectId,
1186
+ workspaceId,
1186
1187
  findings: rows.map((r) => ({
1187
1188
  id: r.id,
1188
- projectId: r.projectId,
1189
+ workspaceId: r.workspaceId,
1189
1190
  taskId: r.taskId,
1190
1191
  sessionId: r.sessionId,
1191
1192
  category: r.category,
@@ -1199,18 +1200,18 @@ async function handleMessage(ws, msg, subscriptions) {
1199
1200
  break;
1200
1201
  }
1201
1202
  case "post_finding": {
1202
- const projectId = msg.payload?.projectId;
1203
+ const workspaceId = msg.payload?.workspaceId;
1203
1204
  const title = msg.payload?.title;
1204
- if (!projectId || !title) {
1205
+ if (!workspaceId || !title) {
1205
1206
  sendWs(ws, {
1206
1207
  type: "error",
1207
- payload: { message: "projectId and title required" },
1208
+ payload: { message: "workspaceId and title required" },
1208
1209
  });
1209
1210
  return;
1210
1211
  }
1211
1212
  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 } });
1213
+ findingStore.postFinding(id, workspaceId, msg.payload?.taskId || "", msg.payload?.sessionId || "", msg.payload?.category || "general", title, msg.payload?.content || "", msg.payload?.tags || []);
1214
+ sendWs(ws, { type: "finding_posted", payload: { id, workspaceId } });
1214
1215
  break;
1215
1216
  }
1216
1217
  // ─── Diff ──────────────────────────────────────────────
@@ -1226,8 +1227,8 @@ async function handleMessage(ws, msg, subscriptions) {
1226
1227
  });
1227
1228
  return;
1228
1229
  }
1229
- const environmentId = task.projectId
1230
- ? projectStore.getProject(task.projectId)?.defaultEnvironmentId
1230
+ const environmentId = task.workspaceId
1231
+ ? workspaceStore.getWorkspace(task.workspaceId)?.defaultEnvironmentId
1231
1232
  : undefined;
1232
1233
  if (!environmentId) {
1233
1234
  sendWs(ws, {