gsd-pi 2.69.0-dev.232905b → 2.69.0-dev.87298d1

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 (85) hide show
  1. package/dist/resources/extensions/gsd/bootstrap/system-context.js +2 -6
  2. package/dist/resources/extensions/gsd/commands-cmux.js +1 -30
  3. package/dist/resources/extensions/gsd/workflow-mcp.js +6 -53
  4. package/dist/web/standalone/.next/BUILD_ID +1 -1
  5. package/dist/web/standalone/.next/app-path-routes-manifest.json +9 -9
  6. package/dist/web/standalone/.next/build-manifest.json +2 -2
  7. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  8. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  9. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  10. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  11. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  12. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  13. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  14. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  15. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  16. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  17. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  18. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  19. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  20. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  21. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  22. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  23. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  24. package/dist/web/standalone/.next/server/app/api/onboarding/route.js +1 -1
  25. package/dist/web/standalone/.next/server/app/index.html +1 -1
  26. package/dist/web/standalone/.next/server/app/index.rsc +1 -1
  27. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  28. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
  29. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  30. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  31. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  32. package/dist/web/standalone/.next/server/app-paths-manifest.json +9 -9
  33. package/dist/web/standalone/.next/server/chunks/6897.js +3 -3
  34. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  35. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  36. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  37. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  38. package/package.json +1 -1
  39. package/packages/daemon/src/orchestrator.ts +84 -9
  40. package/packages/mcp-server/README.md +3 -25
  41. package/packages/mcp-server/dist/cli.d.ts +1 -0
  42. package/packages/mcp-server/dist/cli.d.ts.map +1 -1
  43. package/packages/mcp-server/dist/cli.js +2 -4
  44. package/packages/mcp-server/dist/cli.js.map +1 -1
  45. package/packages/mcp-server/dist/server.d.ts +1 -32
  46. package/packages/mcp-server/dist/server.d.ts.map +1 -1
  47. package/packages/mcp-server/dist/server.js +1 -118
  48. package/packages/mcp-server/dist/server.js.map +1 -1
  49. package/packages/mcp-server/dist/workflow-tools.d.ts +0 -1
  50. package/packages/mcp-server/dist/workflow-tools.d.ts.map +1 -1
  51. package/packages/mcp-server/dist/workflow-tools.js +2 -274
  52. package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
  53. package/packages/mcp-server/src/cli.ts +3 -5
  54. package/packages/mcp-server/src/mcp-server.test.ts +1 -85
  55. package/packages/mcp-server/src/server.ts +1 -188
  56. package/packages/mcp-server/src/workflow-tools.test.ts +25 -32
  57. package/packages/mcp-server/src/workflow-tools.ts +2 -365
  58. package/packages/pi-ai/dist/providers/anthropic.d.ts.map +1 -1
  59. package/packages/pi-ai/dist/providers/anthropic.js +23 -1
  60. package/packages/pi-ai/dist/providers/anthropic.js.map +1 -1
  61. package/packages/pi-ai/dist/utils/oauth/anthropic.d.ts +17 -0
  62. package/packages/pi-ai/dist/utils/oauth/anthropic.d.ts.map +1 -0
  63. package/packages/pi-ai/dist/utils/oauth/anthropic.js +106 -0
  64. package/packages/pi-ai/dist/utils/oauth/anthropic.js.map +1 -0
  65. package/packages/pi-ai/dist/utils/oauth/index.d.ts +2 -3
  66. package/packages/pi-ai/dist/utils/oauth/index.d.ts.map +1 -1
  67. package/packages/pi-ai/dist/utils/oauth/index.js +5 -3
  68. package/packages/pi-ai/dist/utils/oauth/index.js.map +1 -1
  69. package/packages/pi-ai/src/providers/anthropic.ts +31 -1
  70. package/packages/pi-ai/src/utils/oauth/anthropic.ts +140 -0
  71. package/packages/pi-ai/src/utils/oauth/index.ts +5 -3
  72. package/src/resources/extensions/gsd/bootstrap/system-context.ts +5 -9
  73. package/src/resources/extensions/gsd/commands-cmux.ts +1 -32
  74. package/src/resources/extensions/gsd/tests/cmux.test.ts +1 -67
  75. package/src/resources/extensions/gsd/tests/mcp-project-config.test.ts +2 -6
  76. package/src/resources/extensions/gsd/tests/workflow-mcp.test.ts +7 -23
  77. package/src/resources/extensions/gsd/workflow-mcp.ts +5 -59
  78. package/packages/mcp-server/dist/tool-credentials.d.ts +0 -6
  79. package/packages/mcp-server/dist/tool-credentials.d.ts.map +0 -1
  80. package/packages/mcp-server/dist/tool-credentials.js +0 -90
  81. package/packages/mcp-server/dist/tool-credentials.js.map +0 -1
  82. package/packages/mcp-server/src/tool-credentials.test.ts +0 -95
  83. package/packages/mcp-server/src/tool-credentials.ts +0 -97
  84. /package/dist/web/standalone/.next/static/{CHeIRZw-E97oO51g7zDvv → PYwFks7YuKWnl48wsvjo4}/_buildManifest.js +0 -0
  85. /package/dist/web/standalone/.next/static/{CHeIRZw-E97oO51g7zDvv → PYwFks7YuKWnl48wsvjo4}/_ssgManifest.js +0 -0
@@ -2,9 +2,8 @@
2
2
  * MCP Server — registers GSD orchestration, project-state, and workflow tools.
3
3
  *
4
4
  * Session tools (6): gsd_execute, gsd_status, gsd_result, gsd_cancel, gsd_query, gsd_resolve_blocker
5
- * Interactive tools (1): ask_user_questions via MCP form elicitation
6
5
  * Read-only tools (6): gsd_progress, gsd_roadmap, gsd_history, gsd_doctor, gsd_captures, gsd_knowledge
7
- * Workflow tools (29): headless-safe planning, metadata persistence, replanning, completion, validation, reassessment, gate result, status, and journal tools
6
+ * Workflow tools (17): planning, replanning, completion, validation, reassessment, gate result, and milestone status tools
8
7
  *
9
8
  * Uses dynamic imports for @modelcontextprotocol/sdk because TS Node16
10
9
  * cannot resolve the SDK's subpath exports statically (same pattern as
@@ -45,11 +44,6 @@ function errorContent(message: string): { isError: true; content: Array<{ type:
45
44
  return { isError: true, content: [{ type: 'text' as const, text: message }] };
46
45
  }
47
46
 
48
- /** Return raw text content without JSON wrapping. */
49
- function textContent(text: string): { content: Array<{ type: 'text'; text: string }> } {
50
- return { content: [{ type: 'text' as const, text }] };
51
- }
52
-
53
47
  // ---------------------------------------------------------------------------
54
48
  // gsd_query filesystem reader
55
49
  // ---------------------------------------------------------------------------
@@ -114,155 +108,10 @@ async function fileExists(path: string): Promise<boolean> {
114
108
 
115
109
  interface McpServerInstance {
116
110
  tool(name: string, description: string, params: Record<string, unknown>, handler: (args: Record<string, unknown>) => Promise<unknown>): unknown;
117
- server: {
118
- elicitInput(
119
- params: AskUserQuestionsElicitRequest,
120
- options?: unknown,
121
- ): Promise<AskUserQuestionsElicitResult>;
122
- };
123
111
  connect(transport: unknown): Promise<void>;
124
112
  close(): Promise<void>;
125
113
  }
126
114
 
127
- interface AskUserQuestionOption {
128
- label: string;
129
- description: string;
130
- }
131
-
132
- interface AskUserQuestion {
133
- id: string;
134
- header: string;
135
- question: string;
136
- options: AskUserQuestionOption[];
137
- allowMultiple?: boolean;
138
- }
139
-
140
- interface AskUserQuestionsParams {
141
- questions: AskUserQuestion[];
142
- }
143
-
144
- type AskUserQuestionsContentValue = string | number | boolean | string[];
145
-
146
- interface AskUserQuestionsElicitResult {
147
- action: 'accept' | 'decline' | 'cancel';
148
- content?: Record<string, AskUserQuestionsContentValue>;
149
- }
150
-
151
- interface AskUserQuestionsElicitRequest {
152
- mode: 'form';
153
- message: string;
154
- requestedSchema: {
155
- type: 'object';
156
- properties: Record<string, Record<string, unknown>>;
157
- required?: string[];
158
- };
159
- }
160
-
161
- const OTHER_OPTION_LABEL = 'None of the above';
162
-
163
- function normalizeAskUserQuestionsNote(value: AskUserQuestionsContentValue | undefined): string {
164
- return typeof value === 'string' ? value.trim() : '';
165
- }
166
-
167
- function normalizeAskUserQuestionsAnswers(
168
- value: AskUserQuestionsContentValue | undefined,
169
- allowMultiple: boolean,
170
- ): string[] {
171
- if (allowMultiple) {
172
- return Array.isArray(value) ? value.filter((item): item is string => typeof item === 'string') : [];
173
- }
174
-
175
- return typeof value === 'string' && value.length > 0 ? [value] : [];
176
- }
177
-
178
- function validateAskUserQuestionsPayload(questions: AskUserQuestion[]): string | null {
179
- if (questions.length === 0 || questions.length > 3) {
180
- return 'Error: questions must contain 1-3 items';
181
- }
182
-
183
- for (const question of questions) {
184
- if (!question.options || question.options.length === 0) {
185
- return `Error: ask_user_questions requires non-empty options for every question (question "${question.id}" has none)`;
186
- }
187
- }
188
-
189
- return null;
190
- }
191
-
192
- export function buildAskUserQuestionsElicitRequest(questions: AskUserQuestion[]): AskUserQuestionsElicitRequest {
193
- const properties: Record<string, Record<string, unknown>> = {};
194
- const required = questions.map((question) => question.id);
195
-
196
- for (const question of questions) {
197
- if (question.allowMultiple) {
198
- properties[question.id] = {
199
- type: 'array',
200
- title: question.header,
201
- description: question.question,
202
- minItems: 1,
203
- maxItems: question.options.length,
204
- items: {
205
- anyOf: question.options.map((option) => ({
206
- const: option.label,
207
- title: option.label,
208
- })),
209
- },
210
- };
211
- continue;
212
- }
213
-
214
- properties[question.id] = {
215
- type: 'string',
216
- title: question.header,
217
- description: question.question,
218
- oneOf: [...question.options, { label: OTHER_OPTION_LABEL, description: 'Choose this when the listed options do not fit.' }].map((option) => ({
219
- const: option.label,
220
- title: option.label,
221
- })),
222
- };
223
-
224
- properties[`${question.id}__note`] = {
225
- type: 'string',
226
- title: `${question.header} Note`,
227
- description: `Optional note for "${OTHER_OPTION_LABEL}".`,
228
- maxLength: 500,
229
- };
230
- }
231
-
232
- return {
233
- mode: 'form',
234
- message: 'Please answer the following question(s). For single-select questions, choose "None of the above" and add a note if the provided options do not fit.',
235
- requestedSchema: {
236
- type: 'object',
237
- properties,
238
- required,
239
- },
240
- };
241
- }
242
-
243
- export function formatAskUserQuestionsElicitResult(
244
- questions: AskUserQuestion[],
245
- result: AskUserQuestionsElicitResult,
246
- ): string {
247
- const answers: Record<string, { answers: string[] }> = {};
248
- const content = result.content ?? {};
249
-
250
- for (const question of questions) {
251
- const answerList = normalizeAskUserQuestionsAnswers(content[question.id], !!question.allowMultiple);
252
-
253
- if (!question.allowMultiple && answerList[0] === OTHER_OPTION_LABEL) {
254
- const note = normalizeAskUserQuestionsNote(content[`${question.id}__note`]);
255
- if (note) {
256
- answerList.push(`user_note: ${note}`);
257
- }
258
- }
259
-
260
- answers[question.id] = { answers: answerList };
261
- }
262
-
263
- return JSON.stringify({ answers });
264
- }
265
-
266
115
  // ---------------------------------------------------------------------------
267
116
  // createMcpServer
268
117
  // ---------------------------------------------------------------------------
@@ -436,42 +285,6 @@ export async function createMcpServer(sessionManager: SessionManager): Promise<{
436
285
  },
437
286
  );
438
287
 
439
- // -----------------------------------------------------------------------
440
- // ask_user_questions — structured user input via MCP form elicitation
441
- // -----------------------------------------------------------------------
442
- server.tool(
443
- 'ask_user_questions',
444
- 'Request user input for one to three short questions and wait for the response. Single-select questions include a free-form "None of the above" path. Multi-select questions allow multiple choices.',
445
- {
446
- questions: z.array(z.object({
447
- id: z.string().describe('Stable identifier for mapping answers (snake_case)'),
448
- header: z.string().describe('Short header label shown in the UI (12 or fewer chars)'),
449
- question: z.string().describe('Single-sentence prompt shown to the user'),
450
- options: z.array(z.object({
451
- label: z.string().describe('User-facing label (1-5 words)'),
452
- description: z.string().describe('One short sentence explaining impact/tradeoff if selected'),
453
- })).describe('Provide 2-3 mutually exclusive choices. Put the recommended option first and suffix its label with "(Recommended)". Do not include an "Other" option for single-select questions.'),
454
- allowMultiple: z.boolean().optional().describe('If true, the user can select multiple options. No "None of the above" option is added.'),
455
- })).describe('Questions to show the user. Prefer 1 and do not exceed 3.'),
456
- },
457
- async (args: Record<string, unknown>) => {
458
- const { questions } = args as unknown as AskUserQuestionsParams;
459
- try {
460
- const validationError = validateAskUserQuestionsPayload(questions);
461
- if (validationError) return errorContent(validationError);
462
-
463
- const elicitation = await server.server.elicitInput(buildAskUserQuestionsElicitRequest(questions));
464
- if (elicitation.action !== 'accept' || !elicitation.content) {
465
- return textContent('ask_user_questions was cancelled before receiving a response');
466
- }
467
-
468
- return textContent(formatAskUserQuestionsElicitResult(questions, elicitation));
469
- } catch (err) {
470
- return errorContent(err instanceof Error ? err.message : String(err));
471
- }
472
- },
473
- );
474
-
475
288
  // =======================================================================
476
289
  // READ-ONLY TOOLS — no session required, pure filesystem reads
477
290
  // =======================================================================
@@ -6,7 +6,7 @@ import { tmpdir } from "node:os";
6
6
  import { randomUUID } from "node:crypto";
7
7
 
8
8
  import { _getAdapter, closeDatabase } from "../../../src/resources/extensions/gsd/gsd-db.ts";
9
- import { registerWorkflowTools, WORKFLOW_TOOL_NAMES } from "./workflow-tools.ts";
9
+ import { registerWorkflowTools } from "./workflow-tools.ts";
10
10
 
11
11
  function makeTmpBase(): string {
12
12
  const base = join(tmpdir(), `gsd-mcp-workflow-${randomUUID()}`);
@@ -68,12 +68,33 @@ function makeMockServer() {
68
68
  }
69
69
 
70
70
  describe("workflow MCP tools", () => {
71
- it("registers the full headless-safe workflow tool surface", () => {
71
+ it("registers the seventeen workflow tools", () => {
72
72
  const server = makeMockServer();
73
73
  registerWorkflowTools(server as any);
74
74
 
75
- assert.equal(server.tools.length, WORKFLOW_TOOL_NAMES.length);
76
- assert.deepEqual(server.tools.map((t) => t.name), [...WORKFLOW_TOOL_NAMES]);
75
+ assert.equal(server.tools.length, 17);
76
+ assert.deepEqual(
77
+ server.tools.map((t) => t.name),
78
+ [
79
+ "gsd_plan_milestone",
80
+ "gsd_plan_slice",
81
+ "gsd_replan_slice",
82
+ "gsd_slice_replan",
83
+ "gsd_slice_complete",
84
+ "gsd_complete_slice",
85
+ "gsd_complete_milestone",
86
+ "gsd_milestone_complete",
87
+ "gsd_validate_milestone",
88
+ "gsd_milestone_validate",
89
+ "gsd_reassess_roadmap",
90
+ "gsd_roadmap_reassess",
91
+ "gsd_save_gate_result",
92
+ "gsd_summary_save",
93
+ "gsd_task_complete",
94
+ "gsd_complete_task",
95
+ "gsd_milestone_status",
96
+ ],
97
+ );
77
98
  });
78
99
 
79
100
  it("gsd_summary_save writes artifact through the shared executor", async () => {
@@ -953,31 +974,3 @@ describe("workflow MCP tools", () => {
953
974
  }
954
975
  });
955
976
  });
956
-
957
- describe("URL scheme regex — Windows drive letter safety", () => {
958
- // This is the regex used in getWriteGateModuleCandidates() and
959
- // getWorkflowExecutorModuleCandidates() to reject non-file URL schemes.
960
- // It must NOT match single-letter Windows drive prefixes (C:, D:, etc.).
961
- const urlSchemeRegex = /^[a-z]{2,}:/i;
962
-
963
- it("rejects multi-letter URL schemes", () => {
964
- assert.ok(urlSchemeRegex.test("http://example.com"), "http: should match");
965
- assert.ok(urlSchemeRegex.test("https://example.com"), "https: should match");
966
- assert.ok(urlSchemeRegex.test("ftp://files.example.com"), "ftp: should match");
967
- assert.ok(urlSchemeRegex.test("file:///C:/Users"), "file: should match");
968
- assert.ok(urlSchemeRegex.test("node:fs"), "node: should match");
969
- });
970
-
971
- it("allows single-letter Windows drive prefixes", () => {
972
- assert.ok(!urlSchemeRegex.test("C:\\Users\\user\\project"), "C:\\ should not match");
973
- assert.ok(!urlSchemeRegex.test("D:\\other\\path"), "D:\\ should not match");
974
- assert.ok(!urlSchemeRegex.test("c:\\lowercase\\drive"), "c:\\ should not match");
975
- assert.ok(!urlSchemeRegex.test("E:/forward/slash/path"), "E:/ should not match");
976
- });
977
-
978
- it("allows bare filesystem paths", () => {
979
- assert.ok(!urlSchemeRegex.test("/usr/local/lib/module.js"), "unix absolute path should not match");
980
- assert.ok(!urlSchemeRegex.test("./relative/path.js"), "relative path should not match");
981
- assert.ok(!urlSchemeRegex.test("../parent/path.js"), "parent relative path should not match");
982
- });
983
- });