gsd-pi 2.29.0-dev.77f06e2 → 2.29.0-dev.f08b4fe

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 (251) hide show
  1. package/dist/extension-registry.d.ts +63 -0
  2. package/dist/extension-registry.js +166 -0
  3. package/dist/headless.js +4 -0
  4. package/dist/loader.js +10 -1
  5. package/dist/resource-loader.js +11 -1
  6. package/dist/resources/extensions/async-jobs/extension-manifest.json +13 -0
  7. package/dist/resources/extensions/bg-shell/extension-manifest.json +14 -0
  8. package/dist/resources/extensions/browser-tools/extension-manifest.json +37 -0
  9. package/dist/resources/extensions/context7/extension-manifest.json +12 -0
  10. package/dist/resources/extensions/google-search/extension-manifest.json +12 -0
  11. package/dist/resources/extensions/gsd/auto-dashboard.ts +31 -0
  12. package/dist/resources/extensions/gsd/auto-dispatch.ts +32 -3
  13. package/dist/resources/extensions/gsd/auto-post-unit.ts +39 -10
  14. package/dist/resources/extensions/gsd/auto-prompts.ts +40 -17
  15. package/dist/resources/extensions/gsd/auto-recovery.ts +2 -1
  16. package/dist/resources/extensions/gsd/auto-start.ts +18 -32
  17. package/dist/resources/extensions/gsd/auto-worktree.ts +21 -182
  18. package/dist/resources/extensions/gsd/auto.ts +2 -9
  19. package/dist/resources/extensions/gsd/captures.ts +4 -10
  20. package/dist/resources/extensions/gsd/commands-extensions.ts +328 -0
  21. package/dist/resources/extensions/gsd/commands-handlers.ts +2 -1
  22. package/dist/resources/extensions/gsd/commands.ts +53 -2
  23. package/dist/resources/extensions/gsd/detection.ts +2 -1
  24. package/dist/resources/extensions/gsd/doctor-checks.ts +49 -1
  25. package/dist/resources/extensions/gsd/doctor-types.ts +3 -1
  26. package/dist/resources/extensions/gsd/extension-manifest.json +18 -0
  27. package/dist/resources/extensions/gsd/forensics.ts +2 -2
  28. package/dist/resources/extensions/gsd/git-service.ts +3 -2
  29. package/dist/resources/extensions/gsd/gitignore.ts +9 -63
  30. package/dist/resources/extensions/gsd/gsd-db.ts +1 -165
  31. package/dist/resources/extensions/gsd/guided-flow.ts +8 -5
  32. package/dist/resources/extensions/gsd/index.ts +3 -3
  33. package/dist/resources/extensions/gsd/md-importer.ts +3 -2
  34. package/dist/resources/extensions/gsd/mechanical-completion.ts +430 -0
  35. package/dist/resources/extensions/gsd/migrate/command.ts +3 -2
  36. package/dist/resources/extensions/gsd/migrate/writer.ts +2 -1
  37. package/dist/resources/extensions/gsd/migrate-external.ts +123 -0
  38. package/dist/resources/extensions/gsd/paths.ts +24 -2
  39. package/dist/resources/extensions/gsd/post-unit-hooks.ts +6 -5
  40. package/dist/resources/extensions/gsd/preferences-models.ts +7 -1
  41. package/dist/resources/extensions/gsd/preferences-validation.ts +2 -1
  42. package/dist/resources/extensions/gsd/preferences.ts +10 -5
  43. package/dist/resources/extensions/gsd/prompts/discuss-headless.md +4 -2
  44. package/dist/resources/extensions/gsd/prompts/guided-discuss-milestone.md +1 -1
  45. package/dist/resources/extensions/gsd/prompts/plan-milestone.md +26 -2
  46. package/dist/resources/extensions/gsd/prompts/plan-slice.md +15 -1
  47. package/dist/resources/extensions/gsd/repo-identity.ts +148 -0
  48. package/dist/resources/extensions/gsd/resource-version.ts +99 -0
  49. package/dist/resources/extensions/gsd/session-forensics.ts +4 -3
  50. package/dist/resources/extensions/gsd/tests/activity-log.test.ts +2 -2
  51. package/dist/resources/extensions/gsd/tests/auto-recovery.test.ts +3 -3
  52. package/dist/resources/extensions/gsd/tests/auto-worktree.test.ts +0 -58
  53. package/dist/resources/extensions/gsd/tests/doctor-runtime.test.ts +3 -4
  54. package/dist/resources/extensions/gsd/tests/feature-branch-lifecycle-integration.test.ts +5 -18
  55. package/dist/resources/extensions/gsd/tests/git-service.test.ts +10 -37
  56. package/dist/resources/extensions/gsd/tests/knowledge.test.ts +4 -4
  57. package/dist/resources/extensions/gsd/tests/mechanical-completion.test.ts +356 -0
  58. package/dist/resources/extensions/gsd/tests/plan-slice-prompt.test.ts +1 -0
  59. package/dist/resources/extensions/gsd/tests/token-profile.test.ts +14 -16
  60. package/dist/resources/extensions/gsd/triage-resolution.ts +2 -1
  61. package/dist/resources/extensions/gsd/types.ts +2 -0
  62. package/dist/resources/extensions/gsd/worktree-command.ts +1 -11
  63. package/dist/resources/extensions/gsd/worktree-manager.ts +3 -2
  64. package/dist/resources/extensions/gsd/worktree.ts +42 -5
  65. package/dist/resources/extensions/mac-tools/extension-manifest.json +16 -0
  66. package/dist/resources/extensions/mcporter/extension-manifest.json +12 -0
  67. package/dist/resources/extensions/remote-questions/extension-manifest.json +11 -0
  68. package/dist/resources/extensions/search-the-web/extension-manifest.json +13 -0
  69. package/dist/resources/extensions/slash-commands/extension-manifest.json +11 -0
  70. package/dist/resources/extensions/subagent/extension-manifest.json +13 -0
  71. package/dist/resources/extensions/ttsr/extension-manifest.json +11 -0
  72. package/dist/resources/extensions/universal-config/extension-manifest.json +13 -0
  73. package/dist/resources/extensions/voice/extension-manifest.json +12 -0
  74. package/dist/resources/skills/create-gsd-extension/SKILL.md +87 -0
  75. package/dist/resources/skills/create-gsd-extension/references/compaction-session-control.md +77 -0
  76. package/dist/resources/skills/create-gsd-extension/references/custom-commands.md +139 -0
  77. package/dist/resources/skills/create-gsd-extension/references/custom-rendering.md +108 -0
  78. package/dist/resources/skills/create-gsd-extension/references/custom-tools.md +183 -0
  79. package/dist/resources/skills/create-gsd-extension/references/custom-ui.md +490 -0
  80. package/dist/resources/skills/create-gsd-extension/references/events-reference.md +126 -0
  81. package/dist/resources/skills/create-gsd-extension/references/extension-lifecycle.md +64 -0
  82. package/dist/resources/skills/create-gsd-extension/references/extensionapi-reference.md +75 -0
  83. package/dist/resources/skills/create-gsd-extension/references/extensioncontext-reference.md +53 -0
  84. package/dist/resources/skills/create-gsd-extension/references/key-rules-gotchas.md +36 -0
  85. package/dist/resources/skills/create-gsd-extension/references/mode-behavior.md +32 -0
  86. package/dist/resources/skills/create-gsd-extension/references/model-provider-management.md +89 -0
  87. package/dist/resources/skills/create-gsd-extension/references/packaging-distribution.md +55 -0
  88. package/dist/resources/skills/create-gsd-extension/references/remote-execution-overrides.md +90 -0
  89. package/dist/resources/skills/create-gsd-extension/references/state-management.md +70 -0
  90. package/dist/resources/skills/create-gsd-extension/references/system-prompt-modification.md +52 -0
  91. package/dist/resources/skills/create-gsd-extension/templates/extension-skeleton.ts +51 -0
  92. package/dist/resources/skills/create-gsd-extension/templates/stateful-tool-skeleton.ts +143 -0
  93. package/dist/resources/skills/create-gsd-extension/workflows/add-capability.md +57 -0
  94. package/dist/resources/skills/create-gsd-extension/workflows/create-extension.md +156 -0
  95. package/dist/resources/skills/create-gsd-extension/workflows/debug-extension.md +74 -0
  96. package/dist/resources/skills/create-skill/SKILL.md +184 -0
  97. package/dist/resources/skills/create-skill/references/api-security.md +226 -0
  98. package/dist/resources/skills/create-skill/references/be-clear-and-direct.md +531 -0
  99. package/dist/resources/skills/create-skill/references/common-patterns.md +595 -0
  100. package/dist/resources/skills/create-skill/references/core-principles.md +437 -0
  101. package/dist/resources/skills/create-skill/references/executable-code.md +175 -0
  102. package/dist/resources/skills/create-skill/references/gsd-skill-ecosystem.md +68 -0
  103. package/dist/resources/skills/create-skill/references/iteration-and-testing.md +474 -0
  104. package/dist/resources/skills/create-skill/references/recommended-structure.md +168 -0
  105. package/dist/resources/skills/create-skill/references/skill-structure.md +372 -0
  106. package/dist/resources/skills/create-skill/references/use-xml-tags.md +466 -0
  107. package/dist/resources/skills/create-skill/references/using-scripts.md +113 -0
  108. package/dist/resources/skills/create-skill/references/using-templates.md +112 -0
  109. package/dist/resources/skills/create-skill/references/workflows-and-validation.md +510 -0
  110. package/dist/resources/skills/create-skill/templates/router-skill.md +73 -0
  111. package/dist/resources/skills/create-skill/templates/simple-skill.md +33 -0
  112. package/dist/resources/skills/create-skill/workflows/add-reference.md +96 -0
  113. package/dist/resources/skills/create-skill/workflows/add-script.md +93 -0
  114. package/dist/resources/skills/create-skill/workflows/add-template.md +74 -0
  115. package/dist/resources/skills/create-skill/workflows/add-workflow.md +120 -0
  116. package/dist/resources/skills/create-skill/workflows/audit-skill.md +148 -0
  117. package/dist/resources/skills/create-skill/workflows/create-new-skill.md +196 -0
  118. package/dist/resources/skills/create-skill/workflows/get-guidance.md +121 -0
  119. package/dist/resources/skills/create-skill/workflows/upgrade-to-router.md +161 -0
  120. package/dist/resources/skills/create-skill/workflows/verify-skill.md +204 -0
  121. package/dist/resources/skills/react-best-practices/SKILL.md +1 -1
  122. package/package.json +1 -1
  123. package/packages/native/dist/native.d.ts +2 -0
  124. package/packages/native/dist/native.js +19 -5
  125. package/packages/native/src/native.ts +23 -9
  126. package/packages/pi-coding-agent/dist/core/lsp/client.d.ts.map +1 -1
  127. package/packages/pi-coding-agent/dist/core/lsp/client.js +3 -0
  128. package/packages/pi-coding-agent/dist/core/lsp/client.js.map +1 -1
  129. package/packages/pi-coding-agent/src/core/lsp/client.ts +3 -0
  130. package/src/resources/extensions/async-jobs/extension-manifest.json +13 -0
  131. package/src/resources/extensions/bg-shell/extension-manifest.json +14 -0
  132. package/src/resources/extensions/browser-tools/extension-manifest.json +37 -0
  133. package/src/resources/extensions/context7/extension-manifest.json +12 -0
  134. package/src/resources/extensions/google-search/extension-manifest.json +12 -0
  135. package/src/resources/extensions/gsd/auto-dashboard.ts +31 -0
  136. package/src/resources/extensions/gsd/auto-dispatch.ts +32 -3
  137. package/src/resources/extensions/gsd/auto-post-unit.ts +39 -10
  138. package/src/resources/extensions/gsd/auto-prompts.ts +40 -17
  139. package/src/resources/extensions/gsd/auto-recovery.ts +2 -1
  140. package/src/resources/extensions/gsd/auto-start.ts +18 -32
  141. package/src/resources/extensions/gsd/auto-worktree.ts +21 -182
  142. package/src/resources/extensions/gsd/auto.ts +2 -9
  143. package/src/resources/extensions/gsd/captures.ts +4 -10
  144. package/src/resources/extensions/gsd/commands-extensions.ts +328 -0
  145. package/src/resources/extensions/gsd/commands-handlers.ts +2 -1
  146. package/src/resources/extensions/gsd/commands.ts +53 -2
  147. package/src/resources/extensions/gsd/detection.ts +2 -1
  148. package/src/resources/extensions/gsd/doctor-checks.ts +49 -1
  149. package/src/resources/extensions/gsd/doctor-types.ts +3 -1
  150. package/src/resources/extensions/gsd/extension-manifest.json +18 -0
  151. package/src/resources/extensions/gsd/forensics.ts +2 -2
  152. package/src/resources/extensions/gsd/git-service.ts +3 -2
  153. package/src/resources/extensions/gsd/gitignore.ts +9 -63
  154. package/src/resources/extensions/gsd/gsd-db.ts +1 -165
  155. package/src/resources/extensions/gsd/guided-flow.ts +8 -5
  156. package/src/resources/extensions/gsd/index.ts +3 -3
  157. package/src/resources/extensions/gsd/md-importer.ts +3 -2
  158. package/src/resources/extensions/gsd/mechanical-completion.ts +430 -0
  159. package/src/resources/extensions/gsd/migrate/command.ts +3 -2
  160. package/src/resources/extensions/gsd/migrate/writer.ts +2 -1
  161. package/src/resources/extensions/gsd/migrate-external.ts +123 -0
  162. package/src/resources/extensions/gsd/paths.ts +24 -2
  163. package/src/resources/extensions/gsd/post-unit-hooks.ts +6 -5
  164. package/src/resources/extensions/gsd/preferences-models.ts +7 -1
  165. package/src/resources/extensions/gsd/preferences-validation.ts +2 -1
  166. package/src/resources/extensions/gsd/preferences.ts +10 -5
  167. package/src/resources/extensions/gsd/prompts/discuss-headless.md +4 -2
  168. package/src/resources/extensions/gsd/prompts/guided-discuss-milestone.md +1 -1
  169. package/src/resources/extensions/gsd/prompts/plan-milestone.md +26 -2
  170. package/src/resources/extensions/gsd/prompts/plan-slice.md +15 -1
  171. package/src/resources/extensions/gsd/repo-identity.ts +148 -0
  172. package/src/resources/extensions/gsd/resource-version.ts +99 -0
  173. package/src/resources/extensions/gsd/session-forensics.ts +4 -3
  174. package/src/resources/extensions/gsd/tests/activity-log.test.ts +2 -2
  175. package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +3 -3
  176. package/src/resources/extensions/gsd/tests/auto-worktree.test.ts +0 -58
  177. package/src/resources/extensions/gsd/tests/doctor-runtime.test.ts +3 -4
  178. package/src/resources/extensions/gsd/tests/feature-branch-lifecycle-integration.test.ts +5 -18
  179. package/src/resources/extensions/gsd/tests/git-service.test.ts +10 -37
  180. package/src/resources/extensions/gsd/tests/knowledge.test.ts +4 -4
  181. package/src/resources/extensions/gsd/tests/mechanical-completion.test.ts +356 -0
  182. package/src/resources/extensions/gsd/tests/plan-slice-prompt.test.ts +1 -0
  183. package/src/resources/extensions/gsd/tests/token-profile.test.ts +14 -16
  184. package/src/resources/extensions/gsd/triage-resolution.ts +2 -1
  185. package/src/resources/extensions/gsd/types.ts +2 -0
  186. package/src/resources/extensions/gsd/worktree-command.ts +1 -11
  187. package/src/resources/extensions/gsd/worktree-manager.ts +3 -2
  188. package/src/resources/extensions/gsd/worktree.ts +42 -5
  189. package/src/resources/extensions/mac-tools/extension-manifest.json +16 -0
  190. package/src/resources/extensions/mcporter/extension-manifest.json +12 -0
  191. package/src/resources/extensions/remote-questions/extension-manifest.json +11 -0
  192. package/src/resources/extensions/search-the-web/extension-manifest.json +13 -0
  193. package/src/resources/extensions/slash-commands/extension-manifest.json +11 -0
  194. package/src/resources/extensions/subagent/extension-manifest.json +13 -0
  195. package/src/resources/extensions/ttsr/extension-manifest.json +11 -0
  196. package/src/resources/extensions/universal-config/extension-manifest.json +13 -0
  197. package/src/resources/extensions/voice/extension-manifest.json +12 -0
  198. package/src/resources/skills/create-gsd-extension/SKILL.md +87 -0
  199. package/src/resources/skills/create-gsd-extension/references/compaction-session-control.md +77 -0
  200. package/src/resources/skills/create-gsd-extension/references/custom-commands.md +139 -0
  201. package/src/resources/skills/create-gsd-extension/references/custom-rendering.md +108 -0
  202. package/src/resources/skills/create-gsd-extension/references/custom-tools.md +183 -0
  203. package/src/resources/skills/create-gsd-extension/references/custom-ui.md +490 -0
  204. package/src/resources/skills/create-gsd-extension/references/events-reference.md +126 -0
  205. package/src/resources/skills/create-gsd-extension/references/extension-lifecycle.md +64 -0
  206. package/src/resources/skills/create-gsd-extension/references/extensionapi-reference.md +75 -0
  207. package/src/resources/skills/create-gsd-extension/references/extensioncontext-reference.md +53 -0
  208. package/src/resources/skills/create-gsd-extension/references/key-rules-gotchas.md +36 -0
  209. package/src/resources/skills/create-gsd-extension/references/mode-behavior.md +32 -0
  210. package/src/resources/skills/create-gsd-extension/references/model-provider-management.md +89 -0
  211. package/src/resources/skills/create-gsd-extension/references/packaging-distribution.md +55 -0
  212. package/src/resources/skills/create-gsd-extension/references/remote-execution-overrides.md +90 -0
  213. package/src/resources/skills/create-gsd-extension/references/state-management.md +70 -0
  214. package/src/resources/skills/create-gsd-extension/references/system-prompt-modification.md +52 -0
  215. package/src/resources/skills/create-gsd-extension/templates/extension-skeleton.ts +51 -0
  216. package/src/resources/skills/create-gsd-extension/templates/stateful-tool-skeleton.ts +143 -0
  217. package/src/resources/skills/create-gsd-extension/workflows/add-capability.md +57 -0
  218. package/src/resources/skills/create-gsd-extension/workflows/create-extension.md +156 -0
  219. package/src/resources/skills/create-gsd-extension/workflows/debug-extension.md +74 -0
  220. package/src/resources/skills/create-skill/SKILL.md +184 -0
  221. package/src/resources/skills/create-skill/references/api-security.md +226 -0
  222. package/src/resources/skills/create-skill/references/be-clear-and-direct.md +531 -0
  223. package/src/resources/skills/create-skill/references/common-patterns.md +595 -0
  224. package/src/resources/skills/create-skill/references/core-principles.md +437 -0
  225. package/src/resources/skills/create-skill/references/executable-code.md +175 -0
  226. package/src/resources/skills/create-skill/references/gsd-skill-ecosystem.md +68 -0
  227. package/src/resources/skills/create-skill/references/iteration-and-testing.md +474 -0
  228. package/src/resources/skills/create-skill/references/recommended-structure.md +168 -0
  229. package/src/resources/skills/create-skill/references/skill-structure.md +372 -0
  230. package/src/resources/skills/create-skill/references/use-xml-tags.md +466 -0
  231. package/src/resources/skills/create-skill/references/using-scripts.md +113 -0
  232. package/src/resources/skills/create-skill/references/using-templates.md +112 -0
  233. package/src/resources/skills/create-skill/references/workflows-and-validation.md +510 -0
  234. package/src/resources/skills/create-skill/templates/router-skill.md +73 -0
  235. package/src/resources/skills/create-skill/templates/simple-skill.md +33 -0
  236. package/src/resources/skills/create-skill/workflows/add-reference.md +96 -0
  237. package/src/resources/skills/create-skill/workflows/add-script.md +93 -0
  238. package/src/resources/skills/create-skill/workflows/add-template.md +74 -0
  239. package/src/resources/skills/create-skill/workflows/add-workflow.md +120 -0
  240. package/src/resources/skills/create-skill/workflows/audit-skill.md +148 -0
  241. package/src/resources/skills/create-skill/workflows/create-new-skill.md +196 -0
  242. package/src/resources/skills/create-skill/workflows/get-guidance.md +121 -0
  243. package/src/resources/skills/create-skill/workflows/upgrade-to-router.md +161 -0
  244. package/src/resources/skills/create-skill/workflows/verify-skill.md +204 -0
  245. package/src/resources/skills/react-best-practices/SKILL.md +1 -1
  246. package/dist/resources/extensions/gsd/auto-worktree-sync.ts +0 -199
  247. package/dist/resources/extensions/gsd/tests/worktree-db-integration.test.ts +0 -205
  248. package/dist/resources/extensions/gsd/tests/worktree-db.test.ts +0 -442
  249. package/src/resources/extensions/gsd/auto-worktree-sync.ts +0 -199
  250. package/src/resources/extensions/gsd/tests/worktree-db-integration.test.ts +0 -205
  251. package/src/resources/extensions/gsd/tests/worktree-db.test.ts +0 -442
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Extension Registry — manages manifest reading, registry persistence, and enable/disable state.
3
+ *
4
+ * Extensions without manifests always load (backwards compatible).
5
+ * A fresh install has an empty registry — all extensions enabled by default.
6
+ * The only way an extension stops loading is an explicit `gsd extensions disable <id>`.
7
+ */
8
+ export interface ExtensionManifest {
9
+ id: string;
10
+ name: string;
11
+ version: string;
12
+ description: string;
13
+ tier: "core" | "bundled" | "community";
14
+ requires: {
15
+ platform: string;
16
+ };
17
+ provides?: {
18
+ tools?: string[];
19
+ commands?: string[];
20
+ hooks?: string[];
21
+ shortcuts?: string[];
22
+ };
23
+ dependencies?: {
24
+ extensions?: string[];
25
+ runtime?: string[];
26
+ };
27
+ }
28
+ export interface ExtensionRegistryEntry {
29
+ id: string;
30
+ enabled: boolean;
31
+ source: "bundled" | "user" | "project";
32
+ disabledAt?: string;
33
+ disabledReason?: string;
34
+ }
35
+ export interface ExtensionRegistry {
36
+ version: 1;
37
+ entries: Record<string, ExtensionRegistryEntry>;
38
+ }
39
+ export declare function getRegistryPath(): string;
40
+ export declare function loadRegistry(): ExtensionRegistry;
41
+ export declare function saveRegistry(registry: ExtensionRegistry): void;
42
+ /** Returns true if the extension is enabled (missing entries default to enabled). */
43
+ export declare function isExtensionEnabled(registry: ExtensionRegistry, id: string): boolean;
44
+ export declare function enableExtension(registry: ExtensionRegistry, id: string): void;
45
+ /**
46
+ * Disable an extension. Returns an error string if the extension is core (cannot disable),
47
+ * or null on success.
48
+ */
49
+ export declare function disableExtension(registry: ExtensionRegistry, id: string, manifest: ExtensionManifest | null, reason?: string): string | null;
50
+ /** Read extension-manifest.json from a directory. Returns null if missing or invalid. */
51
+ export declare function readManifest(extensionDir: string): ExtensionManifest | null;
52
+ /**
53
+ * Given an entry path (e.g. `.../extensions/browser-tools/index.ts`),
54
+ * resolve the parent directory and read its manifest.
55
+ */
56
+ export declare function readManifestFromEntryPath(entryPath: string): ExtensionManifest | null;
57
+ /** Scan all subdirectories of extensionsDir for manifests. Returns a Map<id, manifest>. */
58
+ export declare function discoverAllManifests(extensionsDir: string): Map<string, ExtensionManifest>;
59
+ /**
60
+ * Auto-populate registry entries for newly discovered extensions.
61
+ * Extensions already in the registry are left untouched.
62
+ */
63
+ export declare function ensureRegistryEntries(extensionsDir: string): void;
@@ -0,0 +1,166 @@
1
+ /**
2
+ * Extension Registry — manages manifest reading, registry persistence, and enable/disable state.
3
+ *
4
+ * Extensions without manifests always load (backwards compatible).
5
+ * A fresh install has an empty registry — all extensions enabled by default.
6
+ * The only way an extension stops loading is an explicit `gsd extensions disable <id>`.
7
+ */
8
+ import { existsSync, mkdirSync, readFileSync, readdirSync, renameSync, writeFileSync } from "node:fs";
9
+ import { homedir } from "node:os";
10
+ import { dirname, join } from "node:path";
11
+ // ─── Validation ─────────────────────────────────────────────────────────────
12
+ function isRegistry(data) {
13
+ if (typeof data !== "object" || data === null)
14
+ return false;
15
+ const obj = data;
16
+ return obj.version === 1 && typeof obj.entries === "object" && obj.entries !== null;
17
+ }
18
+ function isManifest(data) {
19
+ if (typeof data !== "object" || data === null)
20
+ return false;
21
+ const obj = data;
22
+ return (typeof obj.id === "string" &&
23
+ typeof obj.name === "string" &&
24
+ typeof obj.version === "string" &&
25
+ typeof obj.tier === "string");
26
+ }
27
+ // ─── Registry Path ──────────────────────────────────────────────────────────
28
+ export function getRegistryPath() {
29
+ return join(homedir(), ".gsd", "extensions", "registry.json");
30
+ }
31
+ // ─── Registry I/O ───────────────────────────────────────────────────────────
32
+ function defaultRegistry() {
33
+ return { version: 1, entries: {} };
34
+ }
35
+ export function loadRegistry() {
36
+ const filePath = getRegistryPath();
37
+ try {
38
+ if (!existsSync(filePath))
39
+ return defaultRegistry();
40
+ const raw = readFileSync(filePath, "utf-8");
41
+ const parsed = JSON.parse(raw);
42
+ return isRegistry(parsed) ? parsed : defaultRegistry();
43
+ }
44
+ catch {
45
+ return defaultRegistry();
46
+ }
47
+ }
48
+ export function saveRegistry(registry) {
49
+ const filePath = getRegistryPath();
50
+ try {
51
+ mkdirSync(dirname(filePath), { recursive: true });
52
+ const tmp = filePath + ".tmp";
53
+ writeFileSync(tmp, JSON.stringify(registry, null, 2), "utf-8");
54
+ renameSync(tmp, filePath);
55
+ }
56
+ catch {
57
+ // Non-fatal — don't let persistence failures break operation
58
+ }
59
+ }
60
+ // ─── Query ──────────────────────────────────────────────────────────────────
61
+ /** Returns true if the extension is enabled (missing entries default to enabled). */
62
+ export function isExtensionEnabled(registry, id) {
63
+ const entry = registry.entries[id];
64
+ if (!entry)
65
+ return true;
66
+ return entry.enabled;
67
+ }
68
+ // ─── Mutations ──────────────────────────────────────────────────────────────
69
+ export function enableExtension(registry, id) {
70
+ const entry = registry.entries[id];
71
+ if (entry) {
72
+ entry.enabled = true;
73
+ delete entry.disabledAt;
74
+ delete entry.disabledReason;
75
+ }
76
+ else {
77
+ registry.entries[id] = { id, enabled: true, source: "bundled" };
78
+ }
79
+ }
80
+ /**
81
+ * Disable an extension. Returns an error string if the extension is core (cannot disable),
82
+ * or null on success.
83
+ */
84
+ export function disableExtension(registry, id, manifest, reason) {
85
+ if (manifest?.tier === "core") {
86
+ return `Cannot disable "${id}" — it is a core extension.`;
87
+ }
88
+ const entry = registry.entries[id];
89
+ if (entry) {
90
+ entry.enabled = false;
91
+ entry.disabledAt = new Date().toISOString();
92
+ entry.disabledReason = reason;
93
+ }
94
+ else {
95
+ registry.entries[id] = {
96
+ id,
97
+ enabled: false,
98
+ source: "bundled",
99
+ disabledAt: new Date().toISOString(),
100
+ disabledReason: reason,
101
+ };
102
+ }
103
+ return null;
104
+ }
105
+ // ─── Manifest Reading ───────────────────────────────────────────────────────
106
+ /** Read extension-manifest.json from a directory. Returns null if missing or invalid. */
107
+ export function readManifest(extensionDir) {
108
+ const manifestPath = join(extensionDir, "extension-manifest.json");
109
+ if (!existsSync(manifestPath))
110
+ return null;
111
+ try {
112
+ const raw = JSON.parse(readFileSync(manifestPath, "utf-8"));
113
+ return isManifest(raw) ? raw : null;
114
+ }
115
+ catch {
116
+ return null;
117
+ }
118
+ }
119
+ /**
120
+ * Given an entry path (e.g. `.../extensions/browser-tools/index.ts`),
121
+ * resolve the parent directory and read its manifest.
122
+ */
123
+ export function readManifestFromEntryPath(entryPath) {
124
+ const dir = dirname(entryPath);
125
+ return readManifest(dir);
126
+ }
127
+ // ─── Discovery ──────────────────────────────────────────────────────────────
128
+ /** Scan all subdirectories of extensionsDir for manifests. Returns a Map<id, manifest>. */
129
+ export function discoverAllManifests(extensionsDir) {
130
+ const manifests = new Map();
131
+ if (!existsSync(extensionsDir))
132
+ return manifests;
133
+ for (const entry of readdirSync(extensionsDir, { withFileTypes: true })) {
134
+ if (!entry.isDirectory())
135
+ continue;
136
+ const manifest = readManifest(join(extensionsDir, entry.name));
137
+ if (manifest) {
138
+ manifests.set(manifest.id, manifest);
139
+ }
140
+ }
141
+ return manifests;
142
+ }
143
+ /**
144
+ * Auto-populate registry entries for newly discovered extensions.
145
+ * Extensions already in the registry are left untouched.
146
+ */
147
+ export function ensureRegistryEntries(extensionsDir) {
148
+ const manifests = discoverAllManifests(extensionsDir);
149
+ if (manifests.size === 0)
150
+ return;
151
+ const registry = loadRegistry();
152
+ let changed = false;
153
+ for (const [id, manifest] of manifests) {
154
+ if (!registry.entries[id]) {
155
+ registry.entries[id] = {
156
+ id,
157
+ enabled: true,
158
+ source: "bundled",
159
+ };
160
+ changed = true;
161
+ }
162
+ }
163
+ if (changed) {
164
+ saveRegistry(registry);
165
+ }
166
+ }
package/dist/headless.js CHANGED
@@ -128,6 +128,10 @@ async function runHeadlessOnce(options, restartCount) {
128
128
  let interrupted = false;
129
129
  const startTime = Date.now();
130
130
  const isNewMilestone = options.command === 'new-milestone';
131
+ // new-milestone involves codebase investigation + artifact writing — needs more time
132
+ if (isNewMilestone && options.timeout === 300_000) {
133
+ options.timeout = 600_000; // 10 minutes
134
+ }
131
135
  // Supervised mode cannot share stdin with --context -
132
136
  if (options.supervised && options.context === '-') {
133
137
  process.stderr.write('[headless] Error: --supervised cannot be used with --context - (both require stdin)\n');
package/dist/loader.js CHANGED
@@ -29,6 +29,7 @@ if (firstArg === '--help' || firstArg === '-h') {
29
29
  import { agentDir, appRoot } from './app-paths.js';
30
30
  import { serializeBundledExtensionPaths } from './bundled-extension-paths.js';
31
31
  import { discoverExtensionEntryPaths } from './extension-discovery.js';
32
+ import { loadRegistry, readManifestFromEntryPath, isExtensionEnabled } from './extension-registry.js';
32
33
  import { renderLogo } from './logo.js';
33
34
  // pkg/ is a shim directory: contains gsd's piConfig (package.json) and pi's
34
35
  // theme assets (dist/modes/interactive/theme/) without a src/ directory.
@@ -86,7 +87,15 @@ process.env.GSD_WORKFLOW_PATH = join(resourcesDir, 'GSD-WORKFLOW.md');
86
87
  // where initResources() will sync them.
87
88
  const bundledExtDir = join(resourcesDir, 'extensions');
88
89
  const agentExtDir = join(agentDir, 'extensions');
89
- const discoveredExtensionPaths = discoverExtensionEntryPaths(bundledExtDir).map((entryPath) => join(agentExtDir, relative(bundledExtDir, entryPath)));
90
+ const registry = loadRegistry();
91
+ const discoveredExtensionPaths = discoverExtensionEntryPaths(bundledExtDir)
92
+ .map((entryPath) => join(agentExtDir, relative(bundledExtDir, entryPath)))
93
+ .filter((entryPath) => {
94
+ const manifest = readManifestFromEntryPath(entryPath);
95
+ if (!manifest)
96
+ return true; // no manifest = always load
97
+ return isExtensionEnabled(registry, manifest.id);
98
+ });
90
99
  process.env.GSD_BUNDLED_EXTENSION_PATHS = serializeBundledExtensionPaths(discoveredExtensionPaths);
91
100
  // Respect HTTP_PROXY / HTTPS_PROXY / NO_PROXY env vars for all outbound requests.
92
101
  // pi-coding-agent's cli.ts sets this, but GSD bypasses that entry point — so we
@@ -6,6 +6,7 @@ import { dirname, join, relative, resolve } from 'node:path';
6
6
  import { fileURLToPath } from 'node:url';
7
7
  import { compareSemver } from './update-check.js';
8
8
  import { discoverExtensionEntryPaths } from './extension-discovery.js';
9
+ import { loadRegistry, readManifestFromEntryPath, isExtensionEnabled, ensureRegistryEntries } from './extension-registry.js';
9
10
  // Resolve resources directory — prefer dist/resources/ (stable, set at build time)
10
11
  // over src/resources/ (live working tree, changes with git branch).
11
12
  //
@@ -218,6 +219,7 @@ export function initResources(agentDir) {
218
219
  // overwrite them (covers extensions, agents, and skills in one walk).
219
220
  makeTreeWritable(agentDir);
220
221
  writeManagedResourceManifest(agentDir);
222
+ ensureRegistryEntries(join(agentDir, 'extensions'));
221
223
  }
222
224
  /**
223
225
  * Constructs a DefaultResourceLoader that loads extensions from both
@@ -234,10 +236,18 @@ function getBundledExtensionKeys() {
234
236
  return _bundledExtensionKeys;
235
237
  }
236
238
  export function buildResourceLoader(agentDir) {
239
+ const registry = loadRegistry();
237
240
  const piAgentDir = join(homedir(), '.pi', 'agent');
238
241
  const piExtensionsDir = join(piAgentDir, 'extensions');
239
242
  const bundledKeys = getBundledExtensionKeys();
240
- const piExtensionPaths = discoverExtensionEntryPaths(piExtensionsDir).filter((entryPath) => !bundledKeys.has(getExtensionKey(entryPath, piExtensionsDir)));
243
+ const piExtensionPaths = discoverExtensionEntryPaths(piExtensionsDir)
244
+ .filter((entryPath) => !bundledKeys.has(getExtensionKey(entryPath, piExtensionsDir)))
245
+ .filter((entryPath) => {
246
+ const manifest = readManifestFromEntryPath(entryPath);
247
+ if (!manifest)
248
+ return true;
249
+ return isExtensionEnabled(registry, manifest.id);
250
+ });
241
251
  return new DefaultResourceLoader({
242
252
  agentDir,
243
253
  additionalExtensionPaths: piExtensionPaths,
@@ -0,0 +1,13 @@
1
+ {
2
+ "id": "async-jobs",
3
+ "name": "Async Jobs",
4
+ "version": "1.0.0",
5
+ "description": "Run bash commands in the background with job tracking and cancellation",
6
+ "tier": "bundled",
7
+ "requires": { "platform": ">=2.29.0" },
8
+ "provides": {
9
+ "tools": ["async_bash", "await_job", "cancel_job"],
10
+ "commands": ["jobs"],
11
+ "hooks": ["session_start"]
12
+ }
13
+ }
@@ -0,0 +1,14 @@
1
+ {
2
+ "id": "bg-shell",
3
+ "name": "Background Shell",
4
+ "version": "1.0.0",
5
+ "description": "Run and manage background shell processes with interactive monitoring",
6
+ "tier": "bundled",
7
+ "requires": { "platform": ">=2.29.0" },
8
+ "provides": {
9
+ "tools": ["bg_shell"],
10
+ "commands": ["bg"],
11
+ "hooks": ["session_shutdown"],
12
+ "shortcuts": ["Ctrl+Alt+B"]
13
+ }
14
+ }
@@ -0,0 +1,37 @@
1
+ {
2
+ "id": "browser-tools",
3
+ "name": "Browser Tools",
4
+ "version": "1.0.0",
5
+ "description": "Playwright-based web automation, screenshots, and analysis",
6
+ "tier": "bundled",
7
+ "requires": { "platform": ">=2.29.0" },
8
+ "provides": {
9
+ "tools": [
10
+ "browser_navigate", "browser_go_back", "browser_go_forward", "browser_reload",
11
+ "browser_click", "browser_drag", "browser_type", "browser_upload_file",
12
+ "browser_scroll", "browser_hover", "browser_key_press", "browser_select_option",
13
+ "browser_set_checked", "browser_screenshot", "browser_wait_for",
14
+ "browser_get_console_logs", "browser_get_network_logs", "browser_get_dialog_logs",
15
+ "browser_evaluate", "browser_get_accessibility_tree", "browser_find",
16
+ "browser_get_page_source", "browser_close",
17
+ "browser_trace_start", "browser_trace_stop", "browser_export_har",
18
+ "browser_timeline", "browser_session_summary", "browser_debug_bundle",
19
+ "browser_assert", "browser_diff", "browser_batch",
20
+ "browser_snapshot_refs", "browser_get_ref", "browser_click_ref",
21
+ "browser_hover_ref", "browser_fill_ref",
22
+ "browser_list_pages", "browser_switch_page", "browser_close_page",
23
+ "browser_list_frames", "browser_select_frame",
24
+ "browser_analyze_form", "browser_fill_form",
25
+ "browser_find_best", "browser_act",
26
+ "browser_save_pdf", "browser_save_state", "browser_restore_state",
27
+ "browser_mock_route", "browser_block_urls", "browser_clear_routes",
28
+ "browser_emulate_device", "browser_extract",
29
+ "browser_visual_diff", "browser_zoom_region",
30
+ "browser_generate_test", "browser_action_cache", "browser_check_injection"
31
+ ],
32
+ "hooks": ["session_shutdown"]
33
+ },
34
+ "dependencies": {
35
+ "runtime": ["playwright"]
36
+ }
37
+ }
@@ -0,0 +1,12 @@
1
+ {
2
+ "id": "context7",
3
+ "name": "Context7",
4
+ "version": "1.0.0",
5
+ "description": "Fetch up-to-date library documentation and code examples from Context7",
6
+ "tier": "bundled",
7
+ "requires": { "platform": ">=2.29.0" },
8
+ "provides": {
9
+ "tools": ["resolve_library", "get_library_docs"],
10
+ "hooks": ["session_start"]
11
+ }
12
+ }
@@ -0,0 +1,12 @@
1
+ {
2
+ "id": "google-search",
3
+ "name": "Google Search",
4
+ "version": "1.0.0",
5
+ "description": "Web search via Google with AI-synthesized answers and source citations",
6
+ "tier": "bundled",
7
+ "requires": { "platform": ">=2.29.0" },
8
+ "provides": {
9
+ "tools": ["google_search"],
10
+ "hooks": ["session_start"]
11
+ }
12
+ }
@@ -11,6 +11,7 @@ import type { GSDState } from "./types.js";
11
11
  import { getCurrentBranch } from "./worktree.js";
12
12
  import { getActiveHook } from "./post-unit-hooks.js";
13
13
  import { getLedger, getProjectTotals, formatCost, formatTokenCount, formatTierSavings } from "./metrics.js";
14
+ import { getHealthTrend, getConsecutiveErrorUnits } from "./doctor-proactive.js";
14
15
  import {
15
16
  resolveMilestoneFile,
16
17
  resolveSliceFile,
@@ -649,6 +650,31 @@ export function updateProgressWidget(
649
650
  * Build a compact string-array representation of the progress widget.
650
651
  * Used as a fallback when the factory-based widget cannot render (RPC mode).
651
652
  */
653
+ // ─── Model Health Indicator ───────────────────────────────────────────────────
654
+
655
+ /**
656
+ * Compute a traffic-light health indicator from observable signals.
657
+ * 🟢 progressing well — no errors, trend stable/improving
658
+ * 🟡 struggling — some errors or degrading trend
659
+ * 🔴 stuck — consecutive errors, likely needs attention
660
+ */
661
+ export function getModelHealthIndicator(): { emoji: string; label: string } {
662
+ const trend = getHealthTrend();
663
+ const consecutiveErrors = getConsecutiveErrorUnits();
664
+
665
+ if (consecutiveErrors >= 3) {
666
+ return { emoji: "🔴", label: "stuck" };
667
+ }
668
+ if (consecutiveErrors >= 1 || trend === "degrading") {
669
+ return { emoji: "🟡", label: "struggling" };
670
+ }
671
+ if (trend === "improving") {
672
+ return { emoji: "🟢", label: "progressing well" };
673
+ }
674
+ // stable or unknown
675
+ return { emoji: "🟢", label: "progressing" };
676
+ }
677
+
652
678
  function buildProgressTextLines(
653
679
  verb: string,
654
680
  phaseLabel: string,
@@ -697,6 +723,11 @@ function buildProgressTextLines(
697
723
  }
698
724
 
699
725
  if (next) lines.push(` Next: ${next}`);
726
+
727
+ // Model health indicator
728
+ const health = getModelHealthIndicator();
729
+ lines.push(` Health: ${health.emoji} ${health.label}`);
730
+
700
731
  lines.push(` ${widgetPwd}`);
701
732
 
702
733
  return lines;
@@ -12,7 +12,7 @@
12
12
  import type { GSDState } from "./types.js";
13
13
  import type { GSDPreferences } from "./preferences.js";
14
14
  import type { UatType } from "./files.js";
15
- import { loadFile, extractUatType, loadActiveOverrides } from "./files.js";
15
+ import { loadFile, extractUatType, loadActiveOverrides, parseRoadmap } from "./files.js";
16
16
  import {
17
17
  resolveMilestoneFile, resolveMilestonePath, resolveSliceFile, resolveTaskFile,
18
18
  relSliceFile, buildMilestoneFileName,
@@ -123,11 +123,40 @@ const DISPATCH_RULES: DispatchRule[] = [
123
123
  };
124
124
  },
125
125
  },
126
+ {
127
+ name: "uat-verdict-gate (non-PASS blocks progression)",
128
+ match: async ({ mid, basePath, prefs }) => {
129
+ // Only applies when UAT dispatch is enabled
130
+ if (!prefs?.uat_dispatch) return null;
131
+
132
+ const roadmapFile = resolveMilestoneFile(basePath, mid, "ROADMAP");
133
+ const roadmapContent = roadmapFile ? await loadFile(roadmapFile) : null;
134
+ if (!roadmapContent) return null;
135
+
136
+ const roadmap = parseRoadmap(roadmapContent);
137
+ for (const slice of roadmap.slices.filter(s => s.done)) {
138
+ const resultFile = resolveSliceFile(basePath, mid, slice.id, "UAT-RESULT");
139
+ if (!resultFile) continue;
140
+ const content = await loadFile(resultFile);
141
+ if (!content) continue;
142
+ const verdictMatch = content.match(/verdict:\s*([\w-]+)/i);
143
+ const verdict = verdictMatch?.[1]?.toLowerCase();
144
+ if (verdict && verdict !== "pass" && verdict !== "passed") {
145
+ return {
146
+ action: "stop" as const,
147
+ reason: `UAT verdict for ${slice.id} is "${verdict}" — blocking progression until resolved.\nReview the UAT result and update the verdict to PASS, or re-run /gsd auto after fixing.`,
148
+ level: "warning" as const,
149
+ };
150
+ }
151
+ }
152
+ return null;
153
+ },
154
+ },
126
155
  {
127
156
  name: "reassess-roadmap (post-completion)",
128
157
  match: async ({ state, mid, midTitle, basePath, prefs }) => {
129
- // Phase skip: skip reassess when preference or profile says so
130
- if (prefs?.phases?.skip_reassess) return null;
158
+ // Reassess is opt-in: only fire when explicitly enabled
159
+ if (!prefs?.phases?.reassess_after_slice) return null;
131
160
  const needsReassess = await checkNeedsReassessment(basePath, mid, state);
132
161
  if (!needsReassess) return null;
133
162
  return {
@@ -37,7 +37,6 @@ import { resolveAutoSupervisorConfig, loadEffectiveGSDPreferences } from "./pref
37
37
  import { runGSDDoctor, rebuildState, summarizeDoctorIssues } from "./doctor.js";
38
38
  import { COMPLETION_TRANSITION_CODES } from "./doctor-types.js";
39
39
  import { recordHealthSnapshot, checkHealEscalation } from "./doctor-proactive.js";
40
- import { syncStateToProjectRoot } from "./auto-worktree-sync.js";
41
40
  import { resetRewriteCircuitBreaker } from "./auto-dispatch.js";
42
41
  import { isDbAvailable } from "./gsd-db.js";
43
42
  import { consumeSignal } from "./session-status-io.js";
@@ -213,15 +212,6 @@ export async function postUnitPreVerification(pctx: PostUnitContext): Promise<"d
213
212
  // Non-fatal
214
213
  }
215
214
 
216
- // Sync worktree state back to project root
217
- if (s.originalBasePath && s.originalBasePath !== s.basePath) {
218
- try {
219
- syncStateToProjectRoot(s.basePath, s.originalBasePath, s.currentMilestoneId);
220
- } catch {
221
- // Non-fatal
222
- }
223
- }
224
-
225
215
  // Rewrite-docs completion
226
216
  if (s.currentUnit.type === "rewrite-docs") {
227
217
  try {
@@ -331,6 +321,45 @@ export async function postUnitPostVerification(pctx: PostUnitContext): Promise<"
331
321
  }
332
322
  }
333
323
 
324
+ // ── Mechanical completion (ADR-003) ──
325
+ // After task execution, attempt mechanical slice and milestone completion
326
+ // instead of dispatching LLM sessions for complete-slice / validate-milestone.
327
+ if (s.currentUnit?.type === "execute-task" && !s.stepMode) {
328
+ try {
329
+ const [mid, sid] = s.currentUnit.id.split("/");
330
+ if (mid && sid) {
331
+ const state = await deriveState(s.basePath);
332
+ if (state.phase === "summarizing" && state.activeSlice?.id === sid) {
333
+ const { mechanicalSliceCompletion } = await import("./mechanical-completion.js");
334
+ const ok = await mechanicalSliceCompletion(s.basePath, mid, sid);
335
+ if (ok) {
336
+ invalidateAllCaches();
337
+ autoCommitCurrentBranch(s.basePath, "mechanical-completion", `${mid}/${sid}`);
338
+ ctx.ui.notify(`Mechanical completion: ${sid} summary + roadmap updated.`, "info");
339
+
340
+ // Re-derive state — check if milestone is now ready for validation
341
+ invalidateAllCaches();
342
+ const postSliceState = await deriveState(s.basePath);
343
+ if (postSliceState.phase === "validating-milestone" || postSliceState.phase === "completing-milestone") {
344
+ const { aggregateMilestoneVerification, generateMilestoneSummary } = await import("./mechanical-completion.js");
345
+ const validation = await aggregateMilestoneVerification(s.basePath, mid);
346
+ if (validation.verdict !== "failed") {
347
+ await generateMilestoneSummary(s.basePath, mid);
348
+ invalidateAllCaches();
349
+ autoCommitCurrentBranch(s.basePath, "mechanical-milestone-completion", mid);
350
+ ctx.ui.notify(`Mechanical completion: ${mid} validation + summary written.`, "info");
351
+ }
352
+ }
353
+ }
354
+ // If !ok, summarizing phase persists → dispatch rule fires as LLM fallback
355
+ }
356
+ }
357
+ } catch (err) {
358
+ process.stderr.write(`gsd-mechanical: completion failed: ${(err as Error).message}\n`);
359
+ // Non-fatal — fall through to normal dispatch
360
+ }
361
+ }
362
+
334
363
  // ── Post-unit hooks ──
335
364
  if (s.currentUnit && !s.stepMode) {
336
365
  const hookUnit = checkPostUnitHooks(s.currentUnit.type, s.currentUnit.id, s.basePath);