gsd-pi 2.34.0-dev.7d38042 → 2.34.0-dev.e6d9bed
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.
- package/dist/resources/extensions/gsd/commands-prefs-wizard.js +5 -1
- package/dist/resources/extensions/gsd/docs/preferences-reference.md +10 -0
- package/dist/resources/extensions/gsd/doctor-checks.js +113 -5
- package/dist/resources/extensions/gsd/doctor-proactive.js +22 -0
- package/dist/resources/extensions/gsd/doctor.js +36 -0
- package/dist/resources/extensions/gsd/guided-flow.js +4 -2
- package/dist/resources/extensions/gsd/preferences-validation.js +38 -0
- package/dist/resources/extensions/gsd/preferences.js +2 -0
- package/dist/resources/skills/create-gsd-extension/references/events-reference.md +4 -4
- package/package.json +1 -1
- package/packages/pi-agent-core/dist/agent-loop.d.ts +14 -0
- package/packages/pi-agent-core/dist/agent-loop.d.ts.map +1 -1
- package/packages/pi-agent-core/dist/agent-loop.js +24 -27
- package/packages/pi-agent-core/dist/agent-loop.js.map +1 -1
- package/packages/pi-agent-core/dist/agent.d.ts +1 -0
- package/packages/pi-agent-core/dist/agent.d.ts.map +1 -1
- package/packages/pi-agent-core/dist/agent.js +11 -22
- package/packages/pi-agent-core/dist/agent.js.map +1 -1
- package/packages/pi-agent-core/dist/proxy.d.ts.map +1 -1
- package/packages/pi-agent-core/dist/proxy.js +2 -8
- package/packages/pi-agent-core/dist/proxy.js.map +1 -1
- package/packages/pi-agent-core/src/agent-loop.ts +30 -27
- package/packages/pi-agent-core/src/agent.ts +12 -23
- package/packages/pi-agent-core/src/proxy.ts +2 -8
- package/packages/pi-ai/dist/providers/azure-openai-responses.d.ts.map +1 -1
- package/packages/pi-ai/dist/providers/azure-openai-responses.js +5 -41
- package/packages/pi-ai/dist/providers/azure-openai-responses.js.map +1 -1
- package/packages/pi-ai/dist/providers/openai-completions.d.ts.map +1 -1
- package/packages/pi-ai/dist/providers/openai-completions.js +10 -73
- package/packages/pi-ai/dist/providers/openai-completions.js.map +1 -1
- package/packages/pi-ai/dist/providers/openai-responses.d.ts.map +1 -1
- package/packages/pi-ai/dist/providers/openai-responses.js +8 -79
- package/packages/pi-ai/dist/providers/openai-responses.js.map +1 -1
- package/packages/pi-ai/dist/providers/openai-shared.d.ts +65 -0
- package/packages/pi-ai/dist/providers/openai-shared.d.ts.map +1 -0
- package/packages/pi-ai/dist/providers/openai-shared.js +146 -0
- package/packages/pi-ai/dist/providers/openai-shared.js.map +1 -0
- package/packages/pi-ai/dist/utils/oauth/google-antigravity.d.ts.map +1 -1
- package/packages/pi-ai/dist/utils/oauth/google-antigravity.js +7 -135
- package/packages/pi-ai/dist/utils/oauth/google-antigravity.js.map +1 -1
- package/packages/pi-ai/dist/utils/oauth/google-gemini-cli.d.ts.map +1 -1
- package/packages/pi-ai/dist/utils/oauth/google-gemini-cli.js +7 -135
- package/packages/pi-ai/dist/utils/oauth/google-gemini-cli.js.map +1 -1
- package/packages/pi-ai/dist/utils/oauth/google-oauth-utils.d.ts +46 -0
- package/packages/pi-ai/dist/utils/oauth/google-oauth-utils.d.ts.map +1 -0
- package/packages/pi-ai/dist/utils/oauth/google-oauth-utils.js +160 -0
- package/packages/pi-ai/dist/utils/oauth/google-oauth-utils.js.map +1 -0
- package/packages/pi-ai/src/providers/azure-openai-responses.ts +11 -45
- package/packages/pi-ai/src/providers/openai-completions.ts +16 -86
- package/packages/pi-ai/src/providers/openai-responses.ts +15 -95
- package/packages/pi-ai/src/providers/openai-shared.ts +193 -0
- package/packages/pi-ai/src/utils/oauth/google-antigravity.ts +14 -162
- package/packages/pi-ai/src/utils/oauth/google-gemini-cli.ts +13 -161
- package/packages/pi-ai/src/utils/oauth/google-oauth-utils.ts +201 -0
- package/packages/pi-coding-agent/dist/core/agent-session.d.ts +16 -63
- package/packages/pi-coding-agent/dist/core/agent-session.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/agent-session.js +104 -641
- package/packages/pi-coding-agent/dist/core/agent-session.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/auth-storage.d.ts +0 -1
- package/packages/pi-coding-agent/dist/core/auth-storage.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/auth-storage.js +4 -35
- package/packages/pi-coding-agent/dist/core/auth-storage.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/compaction/branch-summarization.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/compaction/branch-summarization.js +5 -43
- package/packages/pi-coding-agent/dist/core/compaction/branch-summarization.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/compaction/compaction.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/compaction/compaction.js +11 -69
- package/packages/pi-coding-agent/dist/core/compaction/compaction.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/compaction/utils.d.ts +40 -0
- package/packages/pi-coding-agent/dist/core/compaction/utils.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/compaction/utils.js +78 -0
- package/packages/pi-coding-agent/dist/core/compaction/utils.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/compaction-orchestrator.d.ts +77 -0
- package/packages/pi-coding-agent/dist/core/compaction-orchestrator.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/compaction-orchestrator.js +331 -0
- package/packages/pi-coding-agent/dist/core/compaction-orchestrator.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/extensions/index.d.ts +2 -2
- package/packages/pi-coding-agent/dist/core/extensions/index.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/index.js +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/index.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/runner.d.ts +15 -0
- package/packages/pi-coding-agent/dist/core/extensions/runner.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/runner.js +129 -243
- package/packages/pi-coding-agent/dist/core/extensions/runner.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/types.d.ts +49 -42
- package/packages/pi-coding-agent/dist/core/extensions/types.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/types.js +2 -21
- package/packages/pi-coding-agent/dist/core/extensions/types.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/lock-utils.d.ts +39 -0
- package/packages/pi-coding-agent/dist/core/lock-utils.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/lock-utils.js +89 -0
- package/packages/pi-coding-agent/dist/core/lock-utils.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/lsp/config.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/lsp/config.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/lsp/config.js +4 -1
- package/packages/pi-coding-agent/dist/core/lsp/config.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/lsp/index.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/lsp/index.js +52 -107
- package/packages/pi-coding-agent/dist/core/lsp/index.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/lsp/lspmux.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/lsp/lspmux.js +2 -21
- package/packages/pi-coding-agent/dist/core/lsp/lspmux.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/lsp/types.d.ts +0 -1
- package/packages/pi-coding-agent/dist/core/lsp/types.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/lsp/types.js +0 -28
- package/packages/pi-coding-agent/dist/core/lsp/types.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/package-manager.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/package-manager.js +2 -4
- package/packages/pi-coding-agent/dist/core/package-manager.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/resource-loader.d.ts +2 -4
- package/packages/pi-coding-agent/dist/core/resource-loader.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/resource-loader.js +33 -58
- package/packages/pi-coding-agent/dist/core/resource-loader.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/retry-handler.d.ts +87 -0
- package/packages/pi-coding-agent/dist/core/retry-handler.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/retry-handler.js +295 -0
- package/packages/pi-coding-agent/dist/core/retry-handler.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/session-manager.d.ts +0 -1
- package/packages/pi-coding-agent/dist/core/session-manager.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/session-manager.js +3 -28
- package/packages/pi-coding-agent/dist/core/session-manager.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/skills.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/skills.js +1 -3
- package/packages/pi-coding-agent/dist/core/skills.js.map +1 -1
- package/packages/pi-coding-agent/dist/index.d.ts +1 -1
- package/packages/pi-coding-agent/dist/index.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/index.js +1 -1
- package/packages/pi-coding-agent/dist/index.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/session-selector.d.ts +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/session-selector.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/session-selector.js +9 -26
- package/packages/pi-coding-agent/dist/modes/interactive/components/session-selector.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js +1 -13
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/tree-render-utils.d.ts +44 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/tree-render-utils.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/tree-render-utils.js +61 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/tree-render-utils.js.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/tree-selector.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/tree-selector.js +6 -9
- package/packages/pi-coding-agent/dist/modes/interactive/components/tree-selector.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/utils/shorten-path.d.ts +6 -0
- package/packages/pi-coding-agent/dist/modes/interactive/utils/shorten-path.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/utils/shorten-path.js +15 -0
- package/packages/pi-coding-agent/dist/modes/interactive/utils/shorten-path.js.map +1 -0
- package/packages/pi-coding-agent/dist/modes/print-mode.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/print-mode.js +2 -30
- package/packages/pi-coding-agent/dist/modes/print-mode.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/rpc/rpc-mode.js +2 -28
- package/packages/pi-coding-agent/dist/modes/rpc/rpc-mode.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/shared/command-context-actions.d.ts +19 -0
- package/packages/pi-coding-agent/dist/modes/shared/command-context-actions.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/modes/shared/command-context-actions.js +45 -0
- package/packages/pi-coding-agent/dist/modes/shared/command-context-actions.js.map +1 -0
- package/packages/pi-coding-agent/dist/utils/error.d.ts +5 -0
- package/packages/pi-coding-agent/dist/utils/error.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/utils/error.js +7 -0
- package/packages/pi-coding-agent/dist/utils/error.js.map +1 -0
- package/packages/pi-coding-agent/src/core/agent-session.ts +117 -745
- package/packages/pi-coding-agent/src/core/auth-storage.ts +4 -38
- package/packages/pi-coding-agent/src/core/compaction/branch-summarization.ts +7 -53
- package/packages/pi-coding-agent/src/core/compaction/compaction.ts +14 -74
- package/packages/pi-coding-agent/src/core/compaction/utils.ts +100 -0
- package/packages/pi-coding-agent/src/core/compaction-orchestrator.ts +424 -0
- package/packages/pi-coding-agent/src/core/extensions/index.ts +1 -21
- package/packages/pi-coding-agent/src/core/extensions/runner.ts +119 -243
- package/packages/pi-coding-agent/src/core/extensions/types.ts +50 -69
- package/packages/pi-coding-agent/src/core/lock-utils.ts +113 -0
- package/packages/pi-coding-agent/src/core/lsp/config.ts +4 -1
- package/packages/pi-coding-agent/src/core/lsp/index.ts +83 -152
- package/packages/pi-coding-agent/src/core/lsp/lspmux.ts +2 -22
- package/packages/pi-coding-agent/src/core/lsp/types.ts +0 -29
- package/packages/pi-coding-agent/src/core/package-manager.ts +1 -4
- package/packages/pi-coding-agent/src/core/resource-loader.ts +43 -67
- package/packages/pi-coding-agent/src/core/retry-handler.ts +359 -0
- package/packages/pi-coding-agent/src/core/session-manager.ts +3 -30
- package/packages/pi-coding-agent/src/core/skills.ts +1 -4
- package/packages/pi-coding-agent/src/index.ts +1 -7
- package/packages/pi-coding-agent/src/modes/interactive/components/session-selector.ts +17 -29
- package/packages/pi-coding-agent/src/modes/interactive/components/tool-execution.ts +1 -13
- package/packages/pi-coding-agent/src/modes/interactive/components/tree-render-utils.ts +81 -0
- package/packages/pi-coding-agent/src/modes/interactive/components/tree-selector.ts +14 -19
- package/packages/pi-coding-agent/src/modes/interactive/utils/shorten-path.ts +14 -0
- package/packages/pi-coding-agent/src/modes/print-mode.ts +2 -30
- package/packages/pi-coding-agent/src/modes/rpc/rpc-mode.ts +2 -28
- package/packages/pi-coding-agent/src/modes/shared/command-context-actions.ts +53 -0
- package/packages/pi-coding-agent/src/utils/error.ts +6 -0
- package/packages/pi-tui/dist/components/markdown.d.ts +5 -0
- package/packages/pi-tui/dist/components/markdown.d.ts.map +1 -1
- package/packages/pi-tui/dist/components/markdown.js +25 -31
- package/packages/pi-tui/dist/components/markdown.js.map +1 -1
- package/packages/pi-tui/dist/keys.d.ts +0 -4
- package/packages/pi-tui/dist/keys.d.ts.map +1 -1
- package/packages/pi-tui/dist/keys.js +94 -162
- package/packages/pi-tui/dist/keys.js.map +1 -1
- package/packages/pi-tui/src/components/markdown.ts +25 -29
- package/packages/pi-tui/src/keys.ts +94 -173
- package/src/resources/extensions/gsd/commands-prefs-wizard.ts +5 -1
- package/src/resources/extensions/gsd/docs/preferences-reference.md +10 -0
- package/src/resources/extensions/gsd/doctor-checks.ts +107 -5
- package/src/resources/extensions/gsd/doctor-proactive.ts +24 -0
- package/src/resources/extensions/gsd/doctor-types.ts +9 -1
- package/src/resources/extensions/gsd/doctor.ts +35 -0
- package/src/resources/extensions/gsd/guided-flow.ts +4 -2
- package/src/resources/extensions/gsd/preferences-validation.ts +38 -0
- package/src/resources/extensions/gsd/preferences.ts +2 -0
- package/src/resources/extensions/gsd/tests/doctor-git.test.ts +98 -2
- package/src/resources/extensions/gsd/tests/doctor-runtime.test.ts +59 -3
- package/src/resources/extensions/gsd/tests/preferences.test.ts +28 -0
- package/src/resources/skills/create-gsd-extension/references/events-reference.md +4 -4
|
@@ -325,131 +325,74 @@ const FUNCTIONAL_CODEPOINTS = {
|
|
|
325
325
|
end: -15,
|
|
326
326
|
} as const;
|
|
327
327
|
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
left: ["\x1b[d"],
|
|
359
|
-
clear: ["\x1b[e"],
|
|
360
|
-
insert: ["\x1b[2$"],
|
|
361
|
-
delete: ["\x1b[3$"],
|
|
362
|
-
pageUp: ["\x1b[5$"],
|
|
363
|
-
pageDown: ["\x1b[6$"],
|
|
364
|
-
home: ["\x1b[7$"],
|
|
365
|
-
end: ["\x1b[8$"],
|
|
366
|
-
} as const;
|
|
367
|
-
|
|
368
|
-
const LEGACY_CTRL_SEQUENCES = {
|
|
369
|
-
up: ["\x1bOa"],
|
|
370
|
-
down: ["\x1bOb"],
|
|
371
|
-
right: ["\x1bOc"],
|
|
372
|
-
left: ["\x1bOd"],
|
|
373
|
-
clear: ["\x1bOe"],
|
|
374
|
-
insert: ["\x1b[2^"],
|
|
375
|
-
delete: ["\x1b[3^"],
|
|
376
|
-
pageUp: ["\x1b[5^"],
|
|
377
|
-
pageDown: ["\x1b[6^"],
|
|
378
|
-
home: ["\x1b[7^"],
|
|
379
|
-
end: ["\x1b[8^"],
|
|
380
|
-
} as const;
|
|
381
|
-
|
|
382
|
-
const LEGACY_SEQUENCE_KEY_IDS: Record<string, KeyId> = {
|
|
383
|
-
"\x1bOA": "up",
|
|
384
|
-
"\x1bOB": "down",
|
|
385
|
-
"\x1bOC": "right",
|
|
386
|
-
"\x1bOD": "left",
|
|
387
|
-
"\x1bOH": "home",
|
|
388
|
-
"\x1bOF": "end",
|
|
389
|
-
"\x1b[E": "clear",
|
|
390
|
-
"\x1bOE": "clear",
|
|
391
|
-
"\x1bOe": "ctrl+clear",
|
|
392
|
-
"\x1b[e": "shift+clear",
|
|
393
|
-
"\x1b[2~": "insert",
|
|
394
|
-
"\x1b[2$": "shift+insert",
|
|
395
|
-
"\x1b[2^": "ctrl+insert",
|
|
396
|
-
"\x1b[3$": "shift+delete",
|
|
397
|
-
"\x1b[3^": "ctrl+delete",
|
|
398
|
-
"\x1b[[5~": "pageUp",
|
|
399
|
-
"\x1b[[6~": "pageDown",
|
|
400
|
-
"\x1b[a": "shift+up",
|
|
401
|
-
"\x1b[b": "shift+down",
|
|
402
|
-
"\x1b[c": "shift+right",
|
|
403
|
-
"\x1b[d": "shift+left",
|
|
404
|
-
"\x1bOa": "ctrl+up",
|
|
405
|
-
"\x1bOb": "ctrl+down",
|
|
406
|
-
"\x1bOc": "ctrl+right",
|
|
407
|
-
"\x1bOd": "ctrl+left",
|
|
408
|
-
"\x1b[5$": "shift+pageUp",
|
|
409
|
-
"\x1b[6$": "shift+pageDown",
|
|
410
|
-
"\x1b[7$": "shift+home",
|
|
411
|
-
"\x1b[8$": "shift+end",
|
|
412
|
-
"\x1b[5^": "ctrl+pageUp",
|
|
413
|
-
"\x1b[6^": "ctrl+pageDown",
|
|
414
|
-
"\x1b[7^": "ctrl+home",
|
|
415
|
-
"\x1b[8^": "ctrl+end",
|
|
416
|
-
"\x1bOP": "f1",
|
|
417
|
-
"\x1bOQ": "f2",
|
|
418
|
-
"\x1bOR": "f3",
|
|
419
|
-
"\x1bOS": "f4",
|
|
420
|
-
"\x1b[11~": "f1",
|
|
421
|
-
"\x1b[12~": "f2",
|
|
422
|
-
"\x1b[13~": "f3",
|
|
423
|
-
"\x1b[14~": "f4",
|
|
424
|
-
"\x1b[[A": "f1",
|
|
425
|
-
"\x1b[[B": "f2",
|
|
426
|
-
"\x1b[[C": "f3",
|
|
427
|
-
"\x1b[[D": "f4",
|
|
428
|
-
"\x1b[[E": "f5",
|
|
429
|
-
"\x1b[15~": "f5",
|
|
430
|
-
"\x1b[17~": "f6",
|
|
431
|
-
"\x1b[18~": "f7",
|
|
432
|
-
"\x1b[19~": "f8",
|
|
433
|
-
"\x1b[20~": "f9",
|
|
434
|
-
"\x1b[21~": "f10",
|
|
435
|
-
"\x1b[23~": "f11",
|
|
436
|
-
"\x1b[24~": "f12",
|
|
437
|
-
"\x1bb": "alt+left",
|
|
438
|
-
"\x1bf": "alt+right",
|
|
439
|
-
"\x1bp": "alt+up",
|
|
440
|
-
"\x1bn": "alt+down",
|
|
328
|
+
/**
|
|
329
|
+
* Consolidated legacy terminal key sequences.
|
|
330
|
+
* Each key maps to its sequences for unmodified, shift-modified, and ctrl-modified variants.
|
|
331
|
+
* This single structure replaces three separate maps (LEGACY_KEY_SEQUENCES,
|
|
332
|
+
* LEGACY_SHIFT_SEQUENCES, LEGACY_CTRL_SEQUENCES) that shared the same key sets.
|
|
333
|
+
*/
|
|
334
|
+
const LEGACY_SEQUENCES: Record<string, { plain?: readonly string[]; shift?: readonly string[]; ctrl?: readonly string[] }> = {
|
|
335
|
+
up: { plain: ["\x1b[A", "\x1bOA"], shift: ["\x1b[a"], ctrl: ["\x1bOa"] },
|
|
336
|
+
down: { plain: ["\x1b[B", "\x1bOB"], shift: ["\x1b[b"], ctrl: ["\x1bOb"] },
|
|
337
|
+
right: { plain: ["\x1b[C", "\x1bOC"], shift: ["\x1b[c"], ctrl: ["\x1bOc"] },
|
|
338
|
+
left: { plain: ["\x1b[D", "\x1bOD"], shift: ["\x1b[d"], ctrl: ["\x1bOd"] },
|
|
339
|
+
home: { plain: ["\x1b[H", "\x1bOH", "\x1b[1~", "\x1b[7~"], shift: ["\x1b[7$"], ctrl: ["\x1b[7^"] },
|
|
340
|
+
end: { plain: ["\x1b[F", "\x1bOF", "\x1b[4~", "\x1b[8~"], shift: ["\x1b[8$"], ctrl: ["\x1b[8^"] },
|
|
341
|
+
insert: { plain: ["\x1b[2~"], shift: ["\x1b[2$"], ctrl: ["\x1b[2^"] },
|
|
342
|
+
delete: { plain: ["\x1b[3~"], shift: ["\x1b[3$"], ctrl: ["\x1b[3^"] },
|
|
343
|
+
pageUp: { plain: ["\x1b[5~", "\x1b[[5~"], shift: ["\x1b[5$"], ctrl: ["\x1b[5^"] },
|
|
344
|
+
pageDown: { plain: ["\x1b[6~", "\x1b[[6~"], shift: ["\x1b[6$"], ctrl: ["\x1b[6^"] },
|
|
345
|
+
clear: { plain: ["\x1b[E", "\x1bOE"], shift: ["\x1b[e"], ctrl: ["\x1bOe"] },
|
|
346
|
+
f1: { plain: ["\x1bOP", "\x1b[11~", "\x1b[[A"] },
|
|
347
|
+
f2: { plain: ["\x1bOQ", "\x1b[12~", "\x1b[[B"] },
|
|
348
|
+
f3: { plain: ["\x1bOR", "\x1b[13~", "\x1b[[C"] },
|
|
349
|
+
f4: { plain: ["\x1bOS", "\x1b[14~", "\x1b[[D"] },
|
|
350
|
+
f5: { plain: ["\x1b[15~", "\x1b[[E"] },
|
|
351
|
+
f6: { plain: ["\x1b[17~"] },
|
|
352
|
+
f7: { plain: ["\x1b[18~"] },
|
|
353
|
+
f8: { plain: ["\x1b[19~"] },
|
|
354
|
+
f9: { plain: ["\x1b[20~"] },
|
|
355
|
+
f10: { plain: ["\x1b[21~"] },
|
|
356
|
+
f11: { plain: ["\x1b[23~"] },
|
|
357
|
+
f12: { plain: ["\x1b[24~"] },
|
|
441
358
|
} as const;
|
|
442
359
|
|
|
443
|
-
|
|
360
|
+
/**
|
|
361
|
+
* Reverse lookup from escape sequence to key identifier, auto-generated from LEGACY_SEQUENCES.
|
|
362
|
+
* Additional non-standard sequences (alt+arrow aliases) are appended after generation.
|
|
363
|
+
*/
|
|
364
|
+
const LEGACY_SEQUENCE_KEY_IDS: Record<string, KeyId> = (() => {
|
|
365
|
+
const map: Record<string, KeyId> = {};
|
|
366
|
+
for (const [key, entry] of Object.entries(LEGACY_SEQUENCES)) {
|
|
367
|
+
const keyId = key as KeyId;
|
|
368
|
+
if (entry.plain) {
|
|
369
|
+
for (const seq of entry.plain) map[seq] = keyId;
|
|
370
|
+
}
|
|
371
|
+
if (entry.shift) {
|
|
372
|
+
for (const seq of entry.shift) map[seq] = `shift+${keyId}` as KeyId;
|
|
373
|
+
}
|
|
374
|
+
if (entry.ctrl) {
|
|
375
|
+
for (const seq of entry.ctrl) map[seq] = `ctrl+${keyId}` as KeyId;
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
// Non-standard alt+arrow aliases not derivable from the table
|
|
379
|
+
map["\x1bb"] = "alt+left";
|
|
380
|
+
map["\x1bf"] = "alt+right";
|
|
381
|
+
map["\x1bp"] = "alt+up";
|
|
382
|
+
map["\x1bn"] = "alt+down";
|
|
383
|
+
return map;
|
|
384
|
+
})();
|
|
444
385
|
|
|
445
386
|
const matchesLegacySequence = (data: string, sequences: readonly string[]): boolean => sequences.includes(data);
|
|
446
387
|
|
|
447
|
-
const matchesLegacyModifierSequence = (data: string, key:
|
|
448
|
-
|
|
449
|
-
|
|
388
|
+
const matchesLegacyModifierSequence = (data: string, key: string, modifier: number): boolean => {
|
|
389
|
+
const entry = LEGACY_SEQUENCES[key];
|
|
390
|
+
if (!entry) return false;
|
|
391
|
+
if (modifier === MODIFIERS.shift && entry.shift) {
|
|
392
|
+
return matchesLegacySequence(data, entry.shift);
|
|
450
393
|
}
|
|
451
|
-
if (modifier === MODIFIERS.ctrl) {
|
|
452
|
-
return matchesLegacySequence(data,
|
|
394
|
+
if (modifier === MODIFIERS.ctrl && entry.ctrl) {
|
|
395
|
+
return matchesLegacySequence(data, entry.ctrl);
|
|
453
396
|
}
|
|
454
397
|
return false;
|
|
455
398
|
};
|
|
@@ -481,33 +424,29 @@ interface ParsedModifyOtherKeysSequence {
|
|
|
481
424
|
let _lastEventType: KeyEventType = "press";
|
|
482
425
|
|
|
483
426
|
/**
|
|
484
|
-
* Check if
|
|
485
|
-
*
|
|
427
|
+
* Check if input data contains a Kitty event type marker.
|
|
428
|
+
* Event type markers appear as ":<eventType>" followed by a sequence terminator (u, ~, A-D, H, F).
|
|
429
|
+
* Ignores bracketed paste content which may contain similar patterns.
|
|
486
430
|
*/
|
|
487
|
-
|
|
488
|
-
// Don't treat bracketed paste content as key release, even if it contains
|
|
489
|
-
// patterns like ":3F" (e.g., bluetooth MAC addresses like "90:62:3F:A5").
|
|
490
|
-
// Terminal.ts re-wraps paste content with bracketed paste markers before
|
|
491
|
-
// passing to TUI, so pasted data will always contain \x1b[200~.
|
|
431
|
+
function hasKittyEventType(data: string, eventType: number): boolean {
|
|
492
432
|
if (data.includes("\x1b[200~")) {
|
|
493
433
|
return false;
|
|
494
434
|
}
|
|
435
|
+
const marker = `:${eventType}`;
|
|
436
|
+
return (
|
|
437
|
+
data.includes(`${marker}u`) ||
|
|
438
|
+
data.includes(`${marker}~`) ||
|
|
439
|
+
data.includes(`${marker}A`) ||
|
|
440
|
+
data.includes(`${marker}B`) ||
|
|
441
|
+
data.includes(`${marker}C`) ||
|
|
442
|
+
data.includes(`${marker}D`) ||
|
|
443
|
+
data.includes(`${marker}H`) ||
|
|
444
|
+
data.includes(`${marker}F`)
|
|
445
|
+
);
|
|
446
|
+
}
|
|
495
447
|
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
if (
|
|
499
|
-
data.includes(":3u") ||
|
|
500
|
-
data.includes(":3~") ||
|
|
501
|
-
data.includes(":3A") ||
|
|
502
|
-
data.includes(":3B") ||
|
|
503
|
-
data.includes(":3C") ||
|
|
504
|
-
data.includes(":3D") ||
|
|
505
|
-
data.includes(":3H") ||
|
|
506
|
-
data.includes(":3F")
|
|
507
|
-
) {
|
|
508
|
-
return true;
|
|
509
|
-
}
|
|
510
|
-
return false;
|
|
448
|
+
export function isKeyRelease(data: string): boolean {
|
|
449
|
+
return hasKittyEventType(data, 3);
|
|
511
450
|
}
|
|
512
451
|
|
|
513
452
|
/**
|
|
@@ -515,25 +454,7 @@ export function isKeyRelease(data: string): boolean {
|
|
|
515
454
|
* Only meaningful when Kitty keyboard protocol with flag 2 is active.
|
|
516
455
|
*/
|
|
517
456
|
export function isKeyRepeat(data: string): boolean {
|
|
518
|
-
|
|
519
|
-
// patterns like ":2F". See isKeyRelease() for details.
|
|
520
|
-
if (data.includes("\x1b[200~")) {
|
|
521
|
-
return false;
|
|
522
|
-
}
|
|
523
|
-
|
|
524
|
-
if (
|
|
525
|
-
data.includes(":2u") ||
|
|
526
|
-
data.includes(":2~") ||
|
|
527
|
-
data.includes(":2A") ||
|
|
528
|
-
data.includes(":2B") ||
|
|
529
|
-
data.includes(":2C") ||
|
|
530
|
-
data.includes(":2D") ||
|
|
531
|
-
data.includes(":2H") ||
|
|
532
|
-
data.includes(":2F")
|
|
533
|
-
) {
|
|
534
|
-
return true;
|
|
535
|
-
}
|
|
536
|
-
return false;
|
|
457
|
+
return hasKittyEventType(data, 2);
|
|
537
458
|
}
|
|
538
459
|
|
|
539
460
|
function parseEventType(eventTypeStr: string | undefined): KeyEventType {
|
|
@@ -847,7 +768,7 @@ export function matchesKey(data: string, keyId: KeyId): boolean {
|
|
|
847
768
|
case "insert":
|
|
848
769
|
if (modifier === 0) {
|
|
849
770
|
return (
|
|
850
|
-
matchesLegacySequence(data,
|
|
771
|
+
matchesLegacySequence(data, LEGACY_SEQUENCES.insert.plain!) ||
|
|
851
772
|
matchesKittySequence(data, FUNCTIONAL_CODEPOINTS.insert, 0)
|
|
852
773
|
);
|
|
853
774
|
}
|
|
@@ -859,7 +780,7 @@ export function matchesKey(data: string, keyId: KeyId): boolean {
|
|
|
859
780
|
case "delete":
|
|
860
781
|
if (modifier === 0) {
|
|
861
782
|
return (
|
|
862
|
-
matchesLegacySequence(data,
|
|
783
|
+
matchesLegacySequence(data, LEGACY_SEQUENCES.delete.plain!) ||
|
|
863
784
|
matchesKittySequence(data, FUNCTIONAL_CODEPOINTS.delete, 0)
|
|
864
785
|
);
|
|
865
786
|
}
|
|
@@ -870,14 +791,14 @@ export function matchesKey(data: string, keyId: KeyId): boolean {
|
|
|
870
791
|
|
|
871
792
|
case "clear":
|
|
872
793
|
if (modifier === 0) {
|
|
873
|
-
return matchesLegacySequence(data,
|
|
794
|
+
return matchesLegacySequence(data, LEGACY_SEQUENCES.clear.plain!);
|
|
874
795
|
}
|
|
875
796
|
return matchesLegacyModifierSequence(data, "clear", modifier);
|
|
876
797
|
|
|
877
798
|
case "home":
|
|
878
799
|
if (modifier === 0) {
|
|
879
800
|
return (
|
|
880
|
-
matchesLegacySequence(data,
|
|
801
|
+
matchesLegacySequence(data, LEGACY_SEQUENCES.home.plain!) ||
|
|
881
802
|
matchesKittySequence(data, FUNCTIONAL_CODEPOINTS.home, 0)
|
|
882
803
|
);
|
|
883
804
|
}
|
|
@@ -889,7 +810,7 @@ export function matchesKey(data: string, keyId: KeyId): boolean {
|
|
|
889
810
|
case "end":
|
|
890
811
|
if (modifier === 0) {
|
|
891
812
|
return (
|
|
892
|
-
matchesLegacySequence(data,
|
|
813
|
+
matchesLegacySequence(data, LEGACY_SEQUENCES.end.plain!) ||
|
|
893
814
|
matchesKittySequence(data, FUNCTIONAL_CODEPOINTS.end, 0)
|
|
894
815
|
);
|
|
895
816
|
}
|
|
@@ -901,7 +822,7 @@ export function matchesKey(data: string, keyId: KeyId): boolean {
|
|
|
901
822
|
case "pageup":
|
|
902
823
|
if (modifier === 0) {
|
|
903
824
|
return (
|
|
904
|
-
matchesLegacySequence(data,
|
|
825
|
+
matchesLegacySequence(data, LEGACY_SEQUENCES.pageUp.plain!) ||
|
|
905
826
|
matchesKittySequence(data, FUNCTIONAL_CODEPOINTS.pageUp, 0)
|
|
906
827
|
);
|
|
907
828
|
}
|
|
@@ -913,7 +834,7 @@ export function matchesKey(data: string, keyId: KeyId): boolean {
|
|
|
913
834
|
case "pagedown":
|
|
914
835
|
if (modifier === 0) {
|
|
915
836
|
return (
|
|
916
|
-
matchesLegacySequence(data,
|
|
837
|
+
matchesLegacySequence(data, LEGACY_SEQUENCES.pageDown.plain!) ||
|
|
917
838
|
matchesKittySequence(data, FUNCTIONAL_CODEPOINTS.pageDown, 0)
|
|
918
839
|
);
|
|
919
840
|
}
|
|
@@ -928,7 +849,7 @@ export function matchesKey(data: string, keyId: KeyId): boolean {
|
|
|
928
849
|
}
|
|
929
850
|
if (modifier === 0) {
|
|
930
851
|
return (
|
|
931
|
-
matchesLegacySequence(data,
|
|
852
|
+
matchesLegacySequence(data, LEGACY_SEQUENCES.up.plain!) ||
|
|
932
853
|
matchesKittySequence(data, ARROW_CODEPOINTS.up, 0)
|
|
933
854
|
);
|
|
934
855
|
}
|
|
@@ -943,7 +864,7 @@ export function matchesKey(data: string, keyId: KeyId): boolean {
|
|
|
943
864
|
}
|
|
944
865
|
if (modifier === 0) {
|
|
945
866
|
return (
|
|
946
|
-
matchesLegacySequence(data,
|
|
867
|
+
matchesLegacySequence(data, LEGACY_SEQUENCES.down.plain!) ||
|
|
947
868
|
matchesKittySequence(data, ARROW_CODEPOINTS.down, 0)
|
|
948
869
|
);
|
|
949
870
|
}
|
|
@@ -970,7 +891,7 @@ export function matchesKey(data: string, keyId: KeyId): boolean {
|
|
|
970
891
|
}
|
|
971
892
|
if (modifier === 0) {
|
|
972
893
|
return (
|
|
973
|
-
matchesLegacySequence(data,
|
|
894
|
+
matchesLegacySequence(data, LEGACY_SEQUENCES.left.plain!) ||
|
|
974
895
|
matchesKittySequence(data, ARROW_CODEPOINTS.left, 0)
|
|
975
896
|
);
|
|
976
897
|
}
|
|
@@ -997,7 +918,7 @@ export function matchesKey(data: string, keyId: KeyId): boolean {
|
|
|
997
918
|
}
|
|
998
919
|
if (modifier === 0) {
|
|
999
920
|
return (
|
|
1000
|
-
matchesLegacySequence(data,
|
|
921
|
+
matchesLegacySequence(data, LEGACY_SEQUENCES.right.plain!) ||
|
|
1001
922
|
matchesKittySequence(data, ARROW_CODEPOINTS.right, 0)
|
|
1002
923
|
);
|
|
1003
924
|
}
|
|
@@ -1021,8 +942,8 @@ export function matchesKey(data: string, keyId: KeyId): boolean {
|
|
|
1021
942
|
if (modifier !== 0) {
|
|
1022
943
|
return false;
|
|
1023
944
|
}
|
|
1024
|
-
const functionKey = key as keyof typeof
|
|
1025
|
-
return matchesLegacySequence(data,
|
|
945
|
+
const functionKey = key as keyof typeof LEGACY_SEQUENCES;
|
|
946
|
+
return matchesLegacySequence(data, LEGACY_SEQUENCES[functionKey]!.plain!);
|
|
1026
947
|
}
|
|
1027
948
|
}
|
|
1028
949
|
|
|
@@ -738,10 +738,14 @@ export function serializePreferencesToFrontmatter(prefs: Record<string, unknown>
|
|
|
738
738
|
const orderedKeys = [
|
|
739
739
|
"version", "mode", "always_use_skills", "prefer_skills", "avoid_skills",
|
|
740
740
|
"skill_rules", "custom_instructions", "models", "skill_discovery",
|
|
741
|
-
"auto_supervisor", "uat_dispatch", "unique_milestone_ids",
|
|
741
|
+
"skill_staleness_days", "auto_supervisor", "uat_dispatch", "unique_milestone_ids",
|
|
742
742
|
"budget_ceiling", "budget_enforcement", "context_pause_threshold",
|
|
743
743
|
"notifications", "remote_questions", "git",
|
|
744
744
|
"post_unit_hooks", "pre_dispatch_hooks",
|
|
745
|
+
"dynamic_routing", "token_profile", "phases", "parallel",
|
|
746
|
+
"auto_visualize", "auto_report",
|
|
747
|
+
"verification_commands", "verification_auto_fix", "verification_max_retries",
|
|
748
|
+
"search_provider", "compression_strategy", "context_selection",
|
|
745
749
|
];
|
|
746
750
|
|
|
747
751
|
const seen = new Set<string>();
|
|
@@ -134,6 +134,10 @@ Setting `prefer_skills: []` does **not** disable skill discovery — it just mea
|
|
|
134
134
|
- `isolation`: `"worktree"`, `"branch"`, or `"none"` — controls auto-mode git isolation strategy. `"worktree"` creates a milestone worktree for isolated work; `"branch"` works directly in the project root but creates a milestone branch (useful for submodule-heavy repos); `"none"` works directly on the current branch with no worktree or milestone branch (ideal for step-mode with hot reloads). Default: `"worktree"`.
|
|
135
135
|
- `manage_gitignore`: boolean — when `false`, GSD will not touch `.gitignore` at all. Useful when your project has a strictly managed `.gitignore` and you don't want GSD adding entries. Default: `true`.
|
|
136
136
|
- `worktree_post_create`: string — script to run after a worktree is created (both auto-mode and manual `/worktree`). Receives `SOURCE_DIR` and `WORKTREE_DIR` as environment variables. Can be absolute or relative to project root. Runs with 30-second timeout. Failure is non-fatal (logged as warning). Default: none.
|
|
137
|
+
- `auto_pr`: boolean — automatically create a GitHub pull request after a milestone branch is merged. Requires `gh` CLI to be installed. Default: `false`.
|
|
138
|
+
- `pr_target_branch`: string — branch to target when `auto_pr` is enabled. Defaults to `main_branch` when omitted.
|
|
139
|
+
- **Deprecated:** `commit_docs` — no longer valid; `.gsd/` is always gitignored. Remove this setting.
|
|
140
|
+
- **Deprecated:** `merge_to_main` — no longer valid; milestone-level merge is always used. Remove this setting.
|
|
137
141
|
|
|
138
142
|
- `unique_milestone_ids`: boolean — when `true`, generates milestone IDs in `M{seq}-{rand6}` format (e.g. `M001-eh88as`) instead of plain sequential `M001`. Prevents ID collisions in team workflows where multiple contributors create milestones concurrently. Both formats coexist — existing `M001`-style milestones remain valid. Default: `false`.
|
|
139
143
|
|
|
@@ -181,6 +185,12 @@ Setting `prefer_skills: []` does **not** disable skill discovery — it just mea
|
|
|
181
185
|
|
|
182
186
|
- `auto_report`: boolean — generate an HTML report snapshot after each milestone completion. Default: `true`.
|
|
183
187
|
|
|
188
|
+
- `search_provider`: `"brave"`, `"tavily"`, `"ollama"`, `"native"`, or `"auto"` — selects the search backend for research phases. `"native"` forces Anthropic's built-in web search only; provider values force that backend and disable native search; `"auto"` uses the default heuristic. Default: `"auto"`.
|
|
189
|
+
|
|
190
|
+
- `compression_strategy`: `"truncate"` or `"compress"` — controls how context that exceeds the budget is reduced. `"truncate"` (default) drops sections from the end. `"compress"` applies heuristic compression before truncating, preserving more content at the cost of some fidelity. Default: `"truncate"`.
|
|
191
|
+
|
|
192
|
+
- `context_selection`: `"full"` or `"smart"` — controls how files are inlined into context. `"full"` inlines entire files; `"smart"` uses semantic chunking to include only the most relevant sections. Default is derived from `token_profile`.
|
|
193
|
+
|
|
184
194
|
- `parallel`: configures parallel orchestration for running multiple slices concurrently. Keys:
|
|
185
195
|
- `enabled`: boolean — enable parallel execution. Default: `false`.
|
|
186
196
|
- `max_workers`: number — maximum concurrent workers (1-4). Default: `2`.
|
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
import { existsSync, lstatSync, readdirSync, readFileSync, realpathSync, statSync } from "node:fs";
|
|
2
|
-
import { join, sep } from "node:path";
|
|
1
|
+
import { existsSync, lstatSync, readdirSync, readFileSync, realpathSync, rmSync, statSync } from "node:fs";
|
|
2
|
+
import { basename, dirname, join, sep } from "node:path";
|
|
3
3
|
|
|
4
4
|
import type { DoctorIssue, DoctorIssueCode } from "./doctor-types.js";
|
|
5
5
|
import { loadFile, parseRoadmap } from "./files.js";
|
|
6
6
|
import { resolveMilestoneFile, milestonesDir, gsdRoot, resolveGsdRootFile, relGsdRootFile } from "./paths.js";
|
|
7
7
|
import { deriveState, isMilestoneComplete } from "./state.js";
|
|
8
8
|
import { saveFile } from "./files.js";
|
|
9
|
-
import { listWorktrees, resolveGitDir } from "./worktree-manager.js";
|
|
9
|
+
import { listWorktrees, resolveGitDir, worktreesDir } from "./worktree-manager.js";
|
|
10
10
|
import { abortAndReset } from "./git-self-heal.js";
|
|
11
|
-
import { RUNTIME_EXCLUSION_PATHS } from "./git-service.js";
|
|
12
|
-
import { nativeIsRepo, nativeWorktreeRemove, nativeBranchList, nativeBranchDelete, nativeLsFiles, nativeRmCached } from "./native-git-bridge.js";
|
|
11
|
+
import { RUNTIME_EXCLUSION_PATHS, readIntegrationBranch } from "./git-service.js";
|
|
12
|
+
import { nativeIsRepo, nativeBranchExists, nativeWorktreeList, nativeWorktreeRemove, nativeBranchList, nativeBranchDelete, nativeLsFiles, nativeRmCached } from "./native-git-bridge.js";
|
|
13
13
|
import { readCrashLock, isLockProcessAlive, clearLock } from "./crash-recovery.js";
|
|
14
14
|
import { ensureGitignore } from "./gitignore.js";
|
|
15
15
|
import { readAllSessionStatuses, isSessionStale, removeSessionStatus } from "./session-status-io.js";
|
|
@@ -215,6 +215,70 @@ export async function checkGitHealth(
|
|
|
215
215
|
} catch {
|
|
216
216
|
// git branch list failed — skip
|
|
217
217
|
}
|
|
218
|
+
|
|
219
|
+
// ── Integration branch existence ──────────────────────────────────────
|
|
220
|
+
// For each active (non-complete) milestone, verify the stored integration
|
|
221
|
+
// branch still exists in git. A missing integration branch blocks merge-back
|
|
222
|
+
// and causes the next merge operation to fail silently.
|
|
223
|
+
try {
|
|
224
|
+
const state = await deriveState(basePath);
|
|
225
|
+
for (const milestone of state.registry) {
|
|
226
|
+
if (milestone.status === "complete") continue;
|
|
227
|
+
const integrationBranch = readIntegrationBranch(basePath, milestone.id);
|
|
228
|
+
if (!integrationBranch) continue; // No stored branch — skip (not yet set)
|
|
229
|
+
if (!nativeBranchExists(basePath, integrationBranch)) {
|
|
230
|
+
issues.push({
|
|
231
|
+
severity: "error",
|
|
232
|
+
code: "integration_branch_missing",
|
|
233
|
+
scope: "milestone",
|
|
234
|
+
unitId: milestone.id,
|
|
235
|
+
message: `Milestone ${milestone.id} recorded integration branch "${integrationBranch}" but that branch no longer exists in git. Merge-back will fail.`,
|
|
236
|
+
fixable: false,
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
} catch {
|
|
241
|
+
// Non-fatal — integration branch check failed
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
// ── Orphaned worktree directories ────────────────────────────────────
|
|
245
|
+
// Worktree removal can fail after a branch delete, leaving a directory
|
|
246
|
+
// that is no longer registered with git. These orphaned dirs cause
|
|
247
|
+
// "already exists" errors when re-creating the same worktree name.
|
|
248
|
+
try {
|
|
249
|
+
const wtDir = worktreesDir(basePath);
|
|
250
|
+
if (existsSync(wtDir)) {
|
|
251
|
+
const registeredPaths = new Set(
|
|
252
|
+
nativeWorktreeList(basePath).map(entry => entry.path),
|
|
253
|
+
);
|
|
254
|
+
for (const entry of readdirSync(wtDir)) {
|
|
255
|
+
const fullPath = join(wtDir, entry);
|
|
256
|
+
try {
|
|
257
|
+
if (!statSync(fullPath).isDirectory()) continue;
|
|
258
|
+
} catch { continue; }
|
|
259
|
+
if (!registeredPaths.has(fullPath)) {
|
|
260
|
+
issues.push({
|
|
261
|
+
severity: "warning",
|
|
262
|
+
code: "worktree_directory_orphaned",
|
|
263
|
+
scope: "project",
|
|
264
|
+
unitId: entry,
|
|
265
|
+
message: `Worktree directory ${fullPath} exists on disk but is not registered with git. Run "git worktree prune" or doctor --fix to remove it.`,
|
|
266
|
+
fixable: true,
|
|
267
|
+
});
|
|
268
|
+
if (shouldFix("worktree_directory_orphaned")) {
|
|
269
|
+
try {
|
|
270
|
+
rmSync(fullPath, { recursive: true, force: true });
|
|
271
|
+
fixesApplied.push(`removed orphaned worktree directory ${fullPath}`);
|
|
272
|
+
} catch {
|
|
273
|
+
fixesApplied.push(`failed to remove orphaned worktree directory ${fullPath}`);
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
} catch {
|
|
280
|
+
// Non-fatal — orphaned worktree directory check failed
|
|
281
|
+
}
|
|
218
282
|
}
|
|
219
283
|
|
|
220
284
|
// ── Runtime Health Checks ──────────────────────────────────────────────────
|
|
@@ -255,6 +319,44 @@ export async function checkRuntimeHealth(
|
|
|
255
319
|
// Non-fatal — crash lock check failed
|
|
256
320
|
}
|
|
257
321
|
|
|
322
|
+
// ── Stranded lock directory ────────────────────────────────────────────
|
|
323
|
+
// proper-lockfile creates a `.gsd.lock/` directory as the OS-level lock
|
|
324
|
+
// mechanism. If the process was SIGKILLed or crashed hard, this directory
|
|
325
|
+
// can remain on disk without any live process holding it. The next session
|
|
326
|
+
// fails to acquire the lock until the directory is removed (#1245).
|
|
327
|
+
try {
|
|
328
|
+
const lockDir = join(dirname(root), `${basename(root)}.lock`);
|
|
329
|
+
if (existsSync(lockDir)) {
|
|
330
|
+
const statRes = statSync(lockDir);
|
|
331
|
+
if (statRes.isDirectory()) {
|
|
332
|
+
// Check if any live process actually holds this lock
|
|
333
|
+
const lock = readCrashLock(basePath);
|
|
334
|
+
const lockHolderAlive = lock ? isLockProcessAlive(lock) : false;
|
|
335
|
+
if (!lockHolderAlive) {
|
|
336
|
+
issues.push({
|
|
337
|
+
severity: "error",
|
|
338
|
+
code: "stranded_lock_directory",
|
|
339
|
+
scope: "project",
|
|
340
|
+
unitId: "project",
|
|
341
|
+
message: `Stranded lock directory "${lockDir}" exists but no live process holds the session lock. This blocks new auto-mode sessions from starting.`,
|
|
342
|
+
file: lockDir,
|
|
343
|
+
fixable: true,
|
|
344
|
+
});
|
|
345
|
+
if (shouldFix("stranded_lock_directory")) {
|
|
346
|
+
try {
|
|
347
|
+
rmSync(lockDir, { recursive: true, force: true });
|
|
348
|
+
fixesApplied.push(`removed stranded lock directory ${lockDir}`);
|
|
349
|
+
} catch {
|
|
350
|
+
fixesApplied.push(`failed to remove stranded lock directory ${lockDir}`);
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
} catch {
|
|
357
|
+
// Non-fatal — stranded lock directory check failed
|
|
358
|
+
}
|
|
359
|
+
|
|
258
360
|
// ── Stale parallel sessions ────────────────────────────────────────────
|
|
259
361
|
try {
|
|
260
362
|
const parallelStatuses = readAllSessionStatuses(basePath);
|
|
@@ -20,6 +20,9 @@ import { gsdRoot, resolveGsdRootFile } from "./paths.js";
|
|
|
20
20
|
import { readCrashLock, isLockProcessAlive, clearLock } from "./crash-recovery.js";
|
|
21
21
|
import { abortAndReset } from "./git-self-heal.js";
|
|
22
22
|
import { rebuildState } from "./doctor.js";
|
|
23
|
+
import { deriveState } from "./state.js";
|
|
24
|
+
import { readIntegrationBranch } from "./git-service.js";
|
|
25
|
+
import { nativeBranchExists, nativeIsRepo } from "./native-git-bridge.js";
|
|
23
26
|
|
|
24
27
|
// ── Health Score Tracking ──────────────────────────────────────────────────
|
|
25
28
|
|
|
@@ -191,6 +194,27 @@ export async function preDispatchHealthGate(basePath: string): Promise<PreDispat
|
|
|
191
194
|
// Non-fatal — dispatch continues without STATE.md if rebuild fails
|
|
192
195
|
}
|
|
193
196
|
|
|
197
|
+
// ── Integration branch existence check ──
|
|
198
|
+
// If the active milestone's recorded integration branch no longer exists in
|
|
199
|
+
// git, the merge-back at the end of the milestone will fail. Block dispatch
|
|
200
|
+
// now to surface this before work is lost.
|
|
201
|
+
try {
|
|
202
|
+
if (nativeIsRepo(basePath)) {
|
|
203
|
+
const state = await deriveState(basePath);
|
|
204
|
+
if (state.activeMilestone) {
|
|
205
|
+
const integrationBranch = readIntegrationBranch(basePath, state.activeMilestone.id);
|
|
206
|
+
if (integrationBranch && !nativeBranchExists(basePath, integrationBranch)) {
|
|
207
|
+
issues.push(
|
|
208
|
+
`Integration branch "${integrationBranch}" for milestone ${state.activeMilestone.id} no longer exists in git. ` +
|
|
209
|
+
`Restore the branch or update the integration branch before dispatching. Run /gsd doctor for details.`,
|
|
210
|
+
);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
} catch {
|
|
215
|
+
// Non-fatal — dispatch continues if state/branch check fails
|
|
216
|
+
}
|
|
217
|
+
|
|
194
218
|
// If we had critical issues that couldn't be auto-healed, block dispatch
|
|
195
219
|
if (issues.length > 0) {
|
|
196
220
|
return {
|
|
@@ -45,7 +45,15 @@ export type DoctorIssueCode =
|
|
|
45
45
|
| "env_python"
|
|
46
46
|
| "env_cargo"
|
|
47
47
|
| "env_go"
|
|
48
|
-
| "env_git_remote"
|
|
48
|
+
| "env_git_remote"
|
|
49
|
+
// Provider / auth checks
|
|
50
|
+
| "provider_key_missing"
|
|
51
|
+
| "provider_key_backedoff"
|
|
52
|
+
// Lock infrastructure checks
|
|
53
|
+
| "stranded_lock_directory"
|
|
54
|
+
// Git / worktree integrity checks
|
|
55
|
+
| "integration_branch_missing"
|
|
56
|
+
| "worktree_directory_orphaned";
|
|
49
57
|
|
|
50
58
|
/**
|
|
51
59
|
* Issue codes that represent expected completion-transition states.
|
|
@@ -11,6 +11,7 @@ import type { DoctorIssue, DoctorIssueCode } from "./doctor-types.js";
|
|
|
11
11
|
import { COMPLETION_TRANSITION_CODES } from "./doctor-types.js";
|
|
12
12
|
import { checkGitHealth, checkRuntimeHealth } from "./doctor-checks.js";
|
|
13
13
|
import { checkEnvironmentHealth } from "./doctor-environment.js";
|
|
14
|
+
import { runProviderChecks } from "./doctor-providers.js";
|
|
14
15
|
|
|
15
16
|
// ── Re-exports ─────────────────────────────────────────────────────────────
|
|
16
17
|
// All public types and functions from extracted modules are re-exported here
|
|
@@ -406,6 +407,40 @@ export async function runGSDDoctor(basePath: string, options?: { fix?: boolean;
|
|
|
406
407
|
issues.push(...auditRequirements(requirementsContent));
|
|
407
408
|
|
|
408
409
|
const state = await deriveState(basePath);
|
|
410
|
+
|
|
411
|
+
// Provider / auth health checks — only relevant when there is active work to dispatch.
|
|
412
|
+
// Skipped for idle projects (no active milestone) to avoid noise in environments
|
|
413
|
+
// where CI/test runners have no API key configured.
|
|
414
|
+
if (state.activeMilestone) {
|
|
415
|
+
try {
|
|
416
|
+
const providerResults = runProviderChecks();
|
|
417
|
+
for (const result of providerResults) {
|
|
418
|
+
if (!result.required) continue;
|
|
419
|
+
if (result.status === "error") {
|
|
420
|
+
issues.push({
|
|
421
|
+
severity: "warning",
|
|
422
|
+
code: "provider_key_missing",
|
|
423
|
+
scope: "project",
|
|
424
|
+
unitId: "project",
|
|
425
|
+
message: result.message + (result.detail ? ` — ${result.detail}` : ""),
|
|
426
|
+
fixable: false,
|
|
427
|
+
});
|
|
428
|
+
} else if (result.status === "warning") {
|
|
429
|
+
issues.push({
|
|
430
|
+
severity: "warning",
|
|
431
|
+
code: "provider_key_backedoff",
|
|
432
|
+
scope: "project",
|
|
433
|
+
unitId: "project",
|
|
434
|
+
message: result.message + (result.detail ? ` — ${result.detail}` : ""),
|
|
435
|
+
fixable: false,
|
|
436
|
+
});
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
} catch {
|
|
440
|
+
// Non-fatal — provider check failure should not block other checks
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
|
|
409
444
|
for (const milestone of state.registry) {
|
|
410
445
|
const milestoneId = milestone.id;
|
|
411
446
|
const milestonePath = resolveMilestonePath(basePath, milestoneId);
|