gsd-pi 2.58.0-dev.778d6ac → 2.58.0-dev.e002a57

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 (212) hide show
  1. package/dist/cli.js +11 -0
  2. package/dist/resources/extensions/gsd/auto-worktree.js +11 -8
  3. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +9 -16
  4. package/dist/resources/extensions/gsd/bootstrap/system-context.js +22 -1
  5. package/dist/resources/extensions/gsd/codebase-generator.js +279 -0
  6. package/dist/resources/extensions/gsd/commands/catalog.js +10 -1
  7. package/dist/resources/extensions/gsd/commands/handlers/ops.js +5 -0
  8. package/dist/resources/extensions/gsd/commands-codebase.js +115 -0
  9. package/dist/resources/extensions/gsd/commands-prefs-wizard.js +41 -4
  10. package/dist/resources/extensions/gsd/complexity-classifier.js +8 -6
  11. package/dist/resources/extensions/gsd/doctor-git-checks.js +48 -1
  12. package/dist/resources/extensions/gsd/doctor-proactive.js +34 -1
  13. package/dist/resources/extensions/gsd/error-classifier.js +3 -4
  14. package/dist/resources/extensions/gsd/git-service.js +82 -1
  15. package/dist/resources/extensions/gsd/native-git-bridge.js +22 -0
  16. package/dist/resources/extensions/gsd/paths.js +2 -0
  17. package/dist/resources/extensions/gsd/preferences-types.js +1 -0
  18. package/dist/resources/extensions/gsd/watch/header-renderer.js +241 -0
  19. package/dist/resources/extensions/search-the-web/url-utils.js +17 -0
  20. package/dist/security-overrides.d.ts +11 -0
  21. package/dist/security-overrides.js +41 -0
  22. package/dist/web/standalone/.next/BUILD_ID +1 -1
  23. package/dist/web/standalone/.next/app-path-routes-manifest.json +16 -16
  24. package/dist/web/standalone/.next/build-manifest.json +2 -2
  25. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  26. package/dist/web/standalone/.next/server/app/_global-error.html +2 -2
  27. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  28. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  29. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  30. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  31. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  32. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  33. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  34. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  35. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  36. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  37. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  38. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  39. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  40. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  41. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  42. package/dist/web/standalone/.next/server/app/index.html +1 -1
  43. package/dist/web/standalone/.next/server/app/index.rsc +1 -1
  44. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  45. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
  46. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  47. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  48. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  49. package/dist/web/standalone/.next/server/app-paths-manifest.json +16 -16
  50. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  51. package/dist/web/standalone/.next/server/pages/500.html +2 -2
  52. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  53. package/dist/welcome-screen.d.ts +1 -0
  54. package/dist/welcome-screen.js +32 -6
  55. package/package.json +1 -1
  56. package/packages/pi-coding-agent/dist/core/resolve-config-value.d.ts +8 -0
  57. package/packages/pi-coding-agent/dist/core/resolve-config-value.d.ts.map +1 -1
  58. package/packages/pi-coding-agent/dist/core/resolve-config-value.js +23 -2
  59. package/packages/pi-coding-agent/dist/core/resolve-config-value.js.map +1 -1
  60. package/packages/pi-coding-agent/dist/core/resolve-config-value.test.js +89 -2
  61. package/packages/pi-coding-agent/dist/core/resolve-config-value.test.js.map +1 -1
  62. package/packages/pi-coding-agent/dist/core/settings-manager-security.test.d.ts +2 -0
  63. package/packages/pi-coding-agent/dist/core/settings-manager-security.test.d.ts.map +1 -0
  64. package/packages/pi-coding-agent/dist/core/settings-manager-security.test.js +83 -0
  65. package/packages/pi-coding-agent/dist/core/settings-manager-security.test.js.map +1 -0
  66. package/packages/pi-coding-agent/dist/core/settings-manager.d.ts +14 -0
  67. package/packages/pi-coding-agent/dist/core/settings-manager.d.ts.map +1 -1
  68. package/packages/pi-coding-agent/dist/core/settings-manager.js +36 -3
  69. package/packages/pi-coding-agent/dist/core/settings-manager.js.map +1 -1
  70. package/packages/pi-coding-agent/dist/index.d.ts +1 -0
  71. package/packages/pi-coding-agent/dist/index.d.ts.map +1 -1
  72. package/packages/pi-coding-agent/dist/index.js +1 -0
  73. package/packages/pi-coding-agent/dist/index.js.map +1 -1
  74. package/packages/pi-coding-agent/dist/modes/interactive/components/armin.d.ts +1 -1
  75. package/packages/pi-coding-agent/dist/modes/interactive/components/armin.d.ts.map +1 -1
  76. package/packages/pi-coding-agent/dist/modes/interactive/components/armin.js +9 -8
  77. package/packages/pi-coding-agent/dist/modes/interactive/components/armin.js.map +1 -1
  78. package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
  79. package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.js +0 -3
  80. package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.js.map +1 -1
  81. package/packages/pi-coding-agent/dist/modes/interactive/components/bash-execution.d.ts +1 -0
  82. package/packages/pi-coding-agent/dist/modes/interactive/components/bash-execution.d.ts.map +1 -1
  83. package/packages/pi-coding-agent/dist/modes/interactive/components/bash-execution.js +2 -1
  84. package/packages/pi-coding-agent/dist/modes/interactive/components/bash-execution.js.map +1 -1
  85. package/packages/pi-coding-agent/dist/modes/interactive/components/bordered-loader.js +1 -1
  86. package/packages/pi-coding-agent/dist/modes/interactive/components/bordered-loader.js.map +1 -1
  87. package/packages/pi-coding-agent/dist/modes/interactive/components/branch-summary-message.js +1 -1
  88. package/packages/pi-coding-agent/dist/modes/interactive/components/branch-summary-message.js.map +1 -1
  89. package/packages/pi-coding-agent/dist/modes/interactive/components/compaction-summary-message.js +1 -1
  90. package/packages/pi-coding-agent/dist/modes/interactive/components/compaction-summary-message.js.map +1 -1
  91. package/packages/pi-coding-agent/dist/modes/interactive/components/config-selector.d.ts.map +1 -1
  92. package/packages/pi-coding-agent/dist/modes/interactive/components/config-selector.js +5 -2
  93. package/packages/pi-coding-agent/dist/modes/interactive/components/config-selector.js.map +1 -1
  94. package/packages/pi-coding-agent/dist/modes/interactive/components/countdown-timer.d.ts +1 -0
  95. package/packages/pi-coding-agent/dist/modes/interactive/components/countdown-timer.d.ts.map +1 -1
  96. package/packages/pi-coding-agent/dist/modes/interactive/components/countdown-timer.js +4 -0
  97. package/packages/pi-coding-agent/dist/modes/interactive/components/countdown-timer.js.map +1 -1
  98. package/packages/pi-coding-agent/dist/modes/interactive/components/custom-message.js +1 -1
  99. package/packages/pi-coding-agent/dist/modes/interactive/components/custom-message.js.map +1 -1
  100. package/packages/pi-coding-agent/dist/modes/interactive/components/daxnuts.d.ts +1 -1
  101. package/packages/pi-coding-agent/dist/modes/interactive/components/daxnuts.d.ts.map +1 -1
  102. package/packages/pi-coding-agent/dist/modes/interactive/components/daxnuts.js +4 -2
  103. package/packages/pi-coding-agent/dist/modes/interactive/components/daxnuts.js.map +1 -1
  104. package/packages/pi-coding-agent/dist/modes/interactive/components/diff.js +2 -2
  105. package/packages/pi-coding-agent/dist/modes/interactive/components/diff.js.map +1 -1
  106. package/packages/pi-coding-agent/dist/modes/interactive/components/dynamic-border.d.ts.map +1 -1
  107. package/packages/pi-coding-agent/dist/modes/interactive/components/dynamic-border.js +8 -1
  108. package/packages/pi-coding-agent/dist/modes/interactive/components/dynamic-border.js.map +1 -1
  109. package/packages/pi-coding-agent/dist/modes/interactive/components/extension-input.d.ts.map +1 -1
  110. package/packages/pi-coding-agent/dist/modes/interactive/components/extension-input.js +2 -0
  111. package/packages/pi-coding-agent/dist/modes/interactive/components/extension-input.js.map +1 -1
  112. package/packages/pi-coding-agent/dist/modes/interactive/components/extension-selector.d.ts.map +1 -1
  113. package/packages/pi-coding-agent/dist/modes/interactive/components/extension-selector.js +4 -0
  114. package/packages/pi-coding-agent/dist/modes/interactive/components/extension-selector.js.map +1 -1
  115. package/packages/pi-coding-agent/dist/modes/interactive/components/footer.d.ts.map +1 -1
  116. package/packages/pi-coding-agent/dist/modes/interactive/components/footer.js +26 -12
  117. package/packages/pi-coding-agent/dist/modes/interactive/components/footer.js.map +1 -1
  118. package/packages/pi-coding-agent/dist/modes/interactive/components/oauth-selector.js +4 -4
  119. package/packages/pi-coding-agent/dist/modes/interactive/components/oauth-selector.js.map +1 -1
  120. package/packages/pi-coding-agent/dist/modes/interactive/components/provider-manager.d.ts +3 -0
  121. package/packages/pi-coding-agent/dist/modes/interactive/components/provider-manager.d.ts.map +1 -1
  122. package/packages/pi-coding-agent/dist/modes/interactive/components/provider-manager.js +46 -14
  123. package/packages/pi-coding-agent/dist/modes/interactive/components/provider-manager.js.map +1 -1
  124. package/packages/pi-coding-agent/dist/modes/interactive/components/scoped-models-selector.d.ts.map +1 -1
  125. package/packages/pi-coding-agent/dist/modes/interactive/components/scoped-models-selector.js +2 -8
  126. package/packages/pi-coding-agent/dist/modes/interactive/components/scoped-models-selector.js.map +1 -1
  127. package/packages/pi-coding-agent/dist/modes/interactive/components/session-selector.js +4 -4
  128. package/packages/pi-coding-agent/dist/modes/interactive/components/session-selector.js.map +1 -1
  129. package/packages/pi-coding-agent/dist/modes/interactive/components/skill-invocation-message.js +2 -2
  130. package/packages/pi-coding-agent/dist/modes/interactive/components/skill-invocation-message.js.map +1 -1
  131. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  132. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js +8 -3
  133. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js.map +1 -1
  134. package/packages/pi-coding-agent/dist/modes/interactive/components/user-message-selector.d.ts.map +1 -1
  135. package/packages/pi-coding-agent/dist/modes/interactive/components/user-message-selector.js +3 -2
  136. package/packages/pi-coding-agent/dist/modes/interactive/components/user-message-selector.js.map +1 -1
  137. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
  138. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js +15 -1
  139. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
  140. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.d.ts.map +1 -1
  141. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.js +16 -1
  142. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.js.map +1 -1
  143. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts +1 -0
  144. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  145. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +27 -4
  146. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
  147. package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.d.ts.map +1 -1
  148. package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.js +6 -0
  149. package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.js.map +1 -1
  150. package/packages/pi-coding-agent/dist/modes/interactive/theme/themes.js +1 -1
  151. package/packages/pi-coding-agent/dist/modes/interactive/theme/themes.js.map +1 -1
  152. package/packages/pi-coding-agent/src/core/resolve-config-value.test.ts +111 -1
  153. package/packages/pi-coding-agent/src/core/resolve-config-value.ts +26 -2
  154. package/packages/pi-coding-agent/src/core/settings-manager-security.test.ts +102 -0
  155. package/packages/pi-coding-agent/src/core/settings-manager.ts +44 -3
  156. package/packages/pi-coding-agent/src/index.ts +5 -0
  157. package/packages/pi-coding-agent/src/modes/interactive/components/armin.ts +9 -9
  158. package/packages/pi-coding-agent/src/modes/interactive/components/assistant-message.ts +0 -2
  159. package/packages/pi-coding-agent/src/modes/interactive/components/bash-execution.ts +3 -1
  160. package/packages/pi-coding-agent/src/modes/interactive/components/bordered-loader.ts +1 -1
  161. package/packages/pi-coding-agent/src/modes/interactive/components/branch-summary-message.ts +1 -1
  162. package/packages/pi-coding-agent/src/modes/interactive/components/compaction-summary-message.ts +1 -1
  163. package/packages/pi-coding-agent/src/modes/interactive/components/config-selector.ts +7 -2
  164. package/packages/pi-coding-agent/src/modes/interactive/components/countdown-timer.ts +3 -0
  165. package/packages/pi-coding-agent/src/modes/interactive/components/custom-message.ts +1 -1
  166. package/packages/pi-coding-agent/src/modes/interactive/components/daxnuts.ts +4 -3
  167. package/packages/pi-coding-agent/src/modes/interactive/components/diff.ts +2 -2
  168. package/packages/pi-coding-agent/src/modes/interactive/components/dynamic-border.ts +3 -1
  169. package/packages/pi-coding-agent/src/modes/interactive/components/extension-input.ts +1 -0
  170. package/packages/pi-coding-agent/src/modes/interactive/components/extension-selector.ts +4 -0
  171. package/packages/pi-coding-agent/src/modes/interactive/components/footer.ts +27 -13
  172. package/packages/pi-coding-agent/src/modes/interactive/components/oauth-selector.ts +4 -4
  173. package/packages/pi-coding-agent/src/modes/interactive/components/provider-manager.ts +45 -14
  174. package/packages/pi-coding-agent/src/modes/interactive/components/scoped-models-selector.ts +2 -7
  175. package/packages/pi-coding-agent/src/modes/interactive/components/session-selector.ts +4 -4
  176. package/packages/pi-coding-agent/src/modes/interactive/components/skill-invocation-message.ts +2 -2
  177. package/packages/pi-coding-agent/src/modes/interactive/components/tool-execution.ts +8 -3
  178. package/packages/pi-coding-agent/src/modes/interactive/components/user-message-selector.ts +3 -2
  179. package/packages/pi-coding-agent/src/modes/interactive/controllers/chat-controller.ts +17 -1
  180. package/packages/pi-coding-agent/src/modes/interactive/controllers/input-controller.ts +14 -1
  181. package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +35 -3
  182. package/packages/pi-coding-agent/src/modes/interactive/slash-command-handlers.ts +7 -0
  183. package/packages/pi-coding-agent/src/modes/interactive/theme/themes.ts +1 -1
  184. package/pkg/dist/modes/interactive/theme/themes.js +1 -1
  185. package/pkg/dist/modes/interactive/theme/themes.js.map +1 -1
  186. package/src/resources/extensions/gsd/auto-worktree.ts +10 -7
  187. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +10 -16
  188. package/src/resources/extensions/gsd/bootstrap/system-context.ts +22 -1
  189. package/src/resources/extensions/gsd/codebase-generator.ts +351 -0
  190. package/src/resources/extensions/gsd/commands/catalog.ts +10 -1
  191. package/src/resources/extensions/gsd/commands/handlers/ops.ts +5 -0
  192. package/src/resources/extensions/gsd/commands-codebase.ts +164 -0
  193. package/src/resources/extensions/gsd/commands-prefs-wizard.ts +46 -4
  194. package/src/resources/extensions/gsd/complexity-classifier.ts +8 -6
  195. package/src/resources/extensions/gsd/doctor-git-checks.ts +49 -1
  196. package/src/resources/extensions/gsd/doctor-proactive.ts +35 -1
  197. package/src/resources/extensions/gsd/doctor-types.ts +2 -0
  198. package/src/resources/extensions/gsd/error-classifier.ts +3 -4
  199. package/src/resources/extensions/gsd/git-service.ts +93 -0
  200. package/src/resources/extensions/gsd/native-git-bridge.ts +24 -0
  201. package/src/resources/extensions/gsd/paths.ts +2 -0
  202. package/src/resources/extensions/gsd/preferences-types.ts +8 -0
  203. package/src/resources/extensions/gsd/tests/codebase-generator.test.ts +488 -0
  204. package/src/resources/extensions/gsd/tests/complexity-classifier.test.ts +4 -4
  205. package/src/resources/extensions/gsd/tests/integration/auto-worktree-milestone-merge.test.ts +33 -0
  206. package/src/resources/extensions/gsd/tests/integration/doctor-git.test.ts +72 -0
  207. package/src/resources/extensions/gsd/tests/integration/git-service.test.ts +68 -0
  208. package/src/resources/extensions/gsd/tests/terminated-transient.test.ts +44 -0
  209. package/src/resources/extensions/gsd/watch/header-renderer.ts +275 -0
  210. package/src/resources/extensions/search-the-web/url-utils.ts +19 -0
  211. /package/dist/web/standalone/.next/static/{R0D4xaIPl5kg93edN7Oo0 → nUA6d2OJrDSVq9RNb-c8b}/_buildManifest.js +0 -0
  212. /package/dist/web/standalone/.next/static/{R0D4xaIPl5kg93edN7Oo0 → nUA6d2OJrDSVq9RNb-c8b}/_ssgManifest.js +0 -0
@@ -46,7 +46,12 @@ export function setupEditorSubmitHandler(host: InteractiveModeStateHost & {
46
46
  if (host.isExtensionCommand(text)) {
47
47
  host.editor.addToHistory?.(text);
48
48
  host.editor.setText("");
49
- await host.session.prompt(text);
49
+ try {
50
+ await host.session.prompt(text);
51
+ } catch (error: unknown) {
52
+ const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
53
+ host.showError(errorMessage);
54
+ }
50
55
  } else {
51
56
  host.queueCompactionMessage(text, "steer");
52
57
  }
@@ -82,5 +87,13 @@ export function setupEditorSubmitHandler(host: InteractiveModeStateHost & {
82
87
  }
83
88
 
84
89
  host.editor.addToHistory?.(text);
90
+ // submitPromptsDirectly is false — still dispatch via session.prompt so user input
91
+ // is not silently discarded.
92
+ try {
93
+ await host.session.prompt(text);
94
+ } catch (error: unknown) {
95
+ const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
96
+ host.showError(errorMessage);
97
+ }
85
98
  };
86
99
  }
@@ -107,6 +107,7 @@ import {
107
107
  getThemeByName,
108
108
  initTheme,
109
109
  onThemeChange,
110
+ stopThemeWatcher,
110
111
  setRegisteredThemes,
111
112
  setTheme,
112
113
  setThemeInstance,
@@ -202,6 +203,9 @@ export class InteractiveMode {
202
203
  // Agent subscription unsubscribe function
203
204
  private unsubscribe?: () => void;
204
205
 
206
+ // Branch change listener unsubscribe function
207
+ private _branchChangeUnsub?: () => void;
208
+
205
209
  // Track if editor is in bash mode (text starts with !)
206
210
  private isBashMode = false;
207
211
 
@@ -511,7 +515,7 @@ export class InteractiveMode {
511
515
  });
512
516
 
513
517
  // Set up git branch watcher (uses provider instead of footer)
514
- this.footerDataProvider.onBranchChange(() => {
518
+ this._branchChangeUnsub = this.footerDataProvider.onBranchChange(() => {
515
519
  this.ui.requestRender();
516
520
  });
517
521
 
@@ -1998,8 +2002,9 @@ export class InteractiveMode {
1998
2002
  }
1999
2003
 
2000
2004
  private subscribeToAgent(): void {
2001
- this.unsubscribe = this.session.subscribe(async (event) => {
2002
- await this.handleEvent(event);
2005
+ let eventQueue: Promise<void> = Promise.resolve();
2006
+ this.unsubscribe = this.session.subscribe((event) => {
2007
+ eventQueue = eventQueue.then(() => this.handleEvent(event)).catch(() => {});
2003
2008
  });
2004
2009
  }
2005
2010
 
@@ -3805,6 +3810,33 @@ export class InteractiveMode {
3805
3810
  this.loadingAnimation = undefined;
3806
3811
  }
3807
3812
  this.clearExtensionTerminalInputListeners();
3813
+
3814
+ // Clean up branch change listener (Fix 1)
3815
+ this._branchChangeUnsub?.();
3816
+ this._branchChangeUnsub = undefined;
3817
+
3818
+ // Clean up theme change listener and watcher (Fix 2)
3819
+ onThemeChange(() => {});
3820
+ stopThemeWatcher();
3821
+
3822
+ // Resolve any pending getUserInput promise so the run() loop can exit (Fix 3)
3823
+ if (this.onInputCallback) {
3824
+ this.onInputCallback("");
3825
+ this.onInputCallback = undefined;
3826
+ }
3827
+
3828
+ // Dispose extension widgets, custom footer, and custom header (Fix 4)
3829
+ this.clearExtensionWidgets();
3830
+ if (this.customFooter?.dispose) {
3831
+ this.customFooter.dispose();
3832
+ }
3833
+ this.customFooter = undefined;
3834
+ if (this.customHeader?.dispose) {
3835
+ this.customHeader.dispose();
3836
+ }
3837
+ this.customHeader = undefined;
3838
+ this.autocompleteProvider = undefined;
3839
+
3808
3840
  this.footer.dispose();
3809
3841
  this.footerDataProvider.dispose();
3810
3842
  if (this.unsubscribe) {
@@ -236,6 +236,13 @@ export async function dispatchSlashCommand(
236
236
  return true;
237
237
  }
238
238
 
239
+ // If input starts with "/" but no command matched, show unknown command feedback
240
+ if (text.startsWith("/")) {
241
+ const command = text.split(/\s/)[0];
242
+ ctx.showError(`Unknown command: ${command}. Type /help for available commands.`);
243
+ return true;
244
+ }
245
+
239
246
  return false;
240
247
  }
241
248
 
@@ -23,7 +23,7 @@ const dark: ThemeJson = {
23
23
  blue: "#5f87ff",
24
24
  green: "#b5bd68",
25
25
  red: "#cc6666",
26
- yellow: "#ffff00",
26
+ yellow: "#e6b800",
27
27
  gray: "#808080",
28
28
  dimGray: "#666666",
29
29
  darkGray: "#505050",
@@ -17,7 +17,7 @@ const dark = {
17
17
  blue: "#5f87ff",
18
18
  green: "#b5bd68",
19
19
  red: "#cc6666",
20
- yellow: "#ffff00",
20
+ yellow: "#e6b800",
21
21
  gray: "#808080",
22
22
  dimGray: "#666666",
23
23
  darkGray: "#505050",
@@ -1 +1 @@
1
- {"version":3,"file":"themes.js","sourceRoot":"","sources":["../../../../src/modes/interactive/theme/themes.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Built-in theme definitions.\n *\n * Each theme is a self-contained record of color values. Variable references\n * (e.g. \"accent\") are resolved against the `vars` map at load time by the\n * theme engine in theme.ts.\n *\n * To add a new built-in theme, add an entry to `builtinThemes` below.\n */\n\n// Re-use the ThemeJson type from the schema defined in theme.ts.\n// We import only the type to avoid circular runtime dependencies.\nimport type { ThemeJson } from \"./theme.js\";\n\n// ---------------------------------------------------------------------------\n// Dark theme\n// ---------------------------------------------------------------------------\n\nconst dark: ThemeJson = {\n\tname: \"dark\",\n\tvars: {\n\t\tcyan: \"#00d7ff\",\n\t\tblue: \"#5f87ff\",\n\t\tgreen: \"#b5bd68\",\n\t\tred: \"#cc6666\",\n\t\tyellow: \"#ffff00\",\n\t\tgray: \"#808080\",\n\t\tdimGray: \"#666666\",\n\t\tdarkGray: \"#505050\",\n\t\taccent: \"#8abeb7\",\n\t\tselectedBg: \"#3a3a4a\",\n\t\tuserMsgBg: \"#343541\",\n\t\ttoolPendingBg: \"#282832\",\n\t\ttoolSuccessBg: \"#283228\",\n\t\ttoolErrorBg: \"#3c2828\",\n\t\tcustomMsgBg: \"#2d2838\",\n\t},\n\tcolors: {\n\t\taccent: \"accent\",\n\t\tborder: \"blue\",\n\t\tborderAccent: \"cyan\",\n\t\tborderMuted: \"darkGray\",\n\t\tsuccess: \"green\",\n\t\terror: \"red\",\n\t\twarning: \"yellow\",\n\t\tmuted: \"gray\",\n\t\tdim: \"dimGray\",\n\t\ttext: \"\",\n\t\tthinkingText: \"gray\",\n\n\t\tselectedBg: \"selectedBg\",\n\t\tuserMessageBg: \"userMsgBg\",\n\t\tuserMessageText: \"\",\n\t\tcustomMessageBg: \"customMsgBg\",\n\t\tcustomMessageText: \"\",\n\t\tcustomMessageLabel: \"#9575cd\",\n\t\ttoolPendingBg: \"toolPendingBg\",\n\t\ttoolSuccessBg: \"toolSuccessBg\",\n\t\ttoolErrorBg: \"toolErrorBg\",\n\t\ttoolTitle: \"\",\n\t\ttoolOutput: \"gray\",\n\n\t\tmdHeading: \"#f0c674\",\n\t\tmdLink: \"#81a2be\",\n\t\tmdLinkUrl: \"dimGray\",\n\t\tmdCode: \"accent\",\n\t\tmdCodeBlock: \"green\",\n\t\tmdCodeBlockBorder: \"gray\",\n\t\tmdQuote: \"gray\",\n\t\tmdQuoteBorder: \"gray\",\n\t\tmdHr: \"gray\",\n\t\tmdListBullet: \"accent\",\n\n\t\ttoolDiffAdded: \"green\",\n\t\ttoolDiffRemoved: \"red\",\n\t\ttoolDiffContext: \"gray\",\n\n\t\tsyntaxComment: \"#6A9955\",\n\t\tsyntaxKeyword: \"#569CD6\",\n\t\tsyntaxFunction: \"#DCDCAA\",\n\t\tsyntaxVariable: \"#9CDCFE\",\n\t\tsyntaxString: \"#CE9178\",\n\t\tsyntaxNumber: \"#B5CEA8\",\n\t\tsyntaxType: \"#4EC9B0\",\n\t\tsyntaxOperator: \"#D4D4D4\",\n\t\tsyntaxPunctuation: \"#D4D4D4\",\n\n\t\tthinkingOff: \"darkGray\",\n\t\tthinkingMinimal: \"#6e6e6e\",\n\t\tthinkingLow: \"#5f87af\",\n\t\tthinkingMedium: \"#81a2be\",\n\t\tthinkingHigh: \"#b294bb\",\n\t\tthinkingXhigh: \"#d183e8\",\n\n\t\tbashMode: \"green\",\n\t},\n\texport: {\n\t\tpageBg: \"#18181e\",\n\t\tcardBg: \"#1e1e24\",\n\t\tinfoBg: \"#3c3728\",\n\t},\n};\n\n// ---------------------------------------------------------------------------\n// Light theme\n// ---------------------------------------------------------------------------\n\nconst light: ThemeJson = {\n\tname: \"light\",\n\tvars: {\n\t\tteal: \"#5a8080\",\n\t\tblue: \"#547da7\",\n\t\tgreen: \"#588458\",\n\t\tred: \"#aa5555\",\n\t\tyellow: \"#9a7326\",\n\t\twarning: \"#7a5a00\",\n\t\tmediumGray: \"#6c6c6c\",\n\t\tdimGray: \"#767676\",\n\t\tlightGray: \"#b0b0b0\",\n\t\tselectedBg: \"#d0d0e0\",\n\t\tuserMsgBg: \"#e8e8e8\",\n\t\ttoolPendingBg: \"#e8e8f0\",\n\t\ttoolSuccessBg: \"#e8f0e8\",\n\t\ttoolErrorBg: \"#f0e8e8\",\n\t\tcustomMsgBg: \"#ede7f6\",\n\t},\n\tcolors: {\n\t\taccent: \"teal\",\n\t\tborder: \"blue\",\n\t\tborderAccent: \"teal\",\n\t\tborderMuted: \"lightGray\",\n\t\tsuccess: \"green\",\n\t\terror: \"red\",\n\t\twarning: \"warning\",\n\t\tmuted: \"mediumGray\",\n\t\tdim: \"dimGray\",\n\t\ttext: \"\",\n\t\tthinkingText: \"mediumGray\",\n\n\t\tselectedBg: \"selectedBg\",\n\t\tuserMessageBg: \"userMsgBg\",\n\t\tuserMessageText: \"\",\n\t\tcustomMessageBg: \"customMsgBg\",\n\t\tcustomMessageText: \"\",\n\t\tcustomMessageLabel: \"#7e57c2\",\n\t\ttoolPendingBg: \"toolPendingBg\",\n\t\ttoolSuccessBg: \"toolSuccessBg\",\n\t\ttoolErrorBg: \"toolErrorBg\",\n\t\ttoolTitle: \"\",\n\t\ttoolOutput: \"mediumGray\",\n\n\t\tmdHeading: \"yellow\",\n\t\tmdLink: \"blue\",\n\t\tmdLinkUrl: \"dimGray\",\n\t\tmdCode: \"teal\",\n\t\tmdCodeBlock: \"green\",\n\t\tmdCodeBlockBorder: \"mediumGray\",\n\t\tmdQuote: \"mediumGray\",\n\t\tmdQuoteBorder: \"mediumGray\",\n\t\tmdHr: \"mediumGray\",\n\t\tmdListBullet: \"green\",\n\n\t\ttoolDiffAdded: \"green\",\n\t\ttoolDiffRemoved: \"red\",\n\t\ttoolDiffContext: \"mediumGray\",\n\n\t\tsyntaxComment: \"#008000\",\n\t\tsyntaxKeyword: \"#0000FF\",\n\t\tsyntaxFunction: \"#795E26\",\n\t\tsyntaxVariable: \"#001080\",\n\t\tsyntaxString: \"#A31515\",\n\t\tsyntaxNumber: \"#098658\",\n\t\tsyntaxType: \"#267F99\",\n\t\tsyntaxOperator: \"#000000\",\n\t\tsyntaxPunctuation: \"#000000\",\n\n\t\tthinkingOff: \"lightGray\",\n\t\tthinkingMinimal: \"#767676\",\n\t\tthinkingLow: \"blue\",\n\t\tthinkingMedium: \"teal\",\n\t\tthinkingHigh: \"#875f87\",\n\t\tthinkingXhigh: \"#8b008b\",\n\n\t\tbashMode: \"green\",\n\t},\n\texport: {\n\t\tpageBg: \"#f8f8f8\",\n\t\tcardBg: \"#ffffff\",\n\t\tinfoBg: \"#fffae6\",\n\t},\n};\n\n// ---------------------------------------------------------------------------\n// Export\n// ---------------------------------------------------------------------------\n\nexport const builtinThemes: Record<string, ThemeJson> = { dark, light };\n"]}
1
+ {"version":3,"file":"themes.js","sourceRoot":"","sources":["../../../../src/modes/interactive/theme/themes.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Built-in theme definitions.\n *\n * Each theme is a self-contained record of color values. Variable references\n * (e.g. \"accent\") are resolved against the `vars` map at load time by the\n * theme engine in theme.ts.\n *\n * To add a new built-in theme, add an entry to `builtinThemes` below.\n */\n\n// Re-use the ThemeJson type from the schema defined in theme.ts.\n// We import only the type to avoid circular runtime dependencies.\nimport type { ThemeJson } from \"./theme.js\";\n\n// ---------------------------------------------------------------------------\n// Dark theme\n// ---------------------------------------------------------------------------\n\nconst dark: ThemeJson = {\n\tname: \"dark\",\n\tvars: {\n\t\tcyan: \"#00d7ff\",\n\t\tblue: \"#5f87ff\",\n\t\tgreen: \"#b5bd68\",\n\t\tred: \"#cc6666\",\n\t\tyellow: \"#e6b800\",\n\t\tgray: \"#808080\",\n\t\tdimGray: \"#666666\",\n\t\tdarkGray: \"#505050\",\n\t\taccent: \"#8abeb7\",\n\t\tselectedBg: \"#3a3a4a\",\n\t\tuserMsgBg: \"#343541\",\n\t\ttoolPendingBg: \"#282832\",\n\t\ttoolSuccessBg: \"#283228\",\n\t\ttoolErrorBg: \"#3c2828\",\n\t\tcustomMsgBg: \"#2d2838\",\n\t},\n\tcolors: {\n\t\taccent: \"accent\",\n\t\tborder: \"blue\",\n\t\tborderAccent: \"cyan\",\n\t\tborderMuted: \"darkGray\",\n\t\tsuccess: \"green\",\n\t\terror: \"red\",\n\t\twarning: \"yellow\",\n\t\tmuted: \"gray\",\n\t\tdim: \"dimGray\",\n\t\ttext: \"\",\n\t\tthinkingText: \"gray\",\n\n\t\tselectedBg: \"selectedBg\",\n\t\tuserMessageBg: \"userMsgBg\",\n\t\tuserMessageText: \"\",\n\t\tcustomMessageBg: \"customMsgBg\",\n\t\tcustomMessageText: \"\",\n\t\tcustomMessageLabel: \"#9575cd\",\n\t\ttoolPendingBg: \"toolPendingBg\",\n\t\ttoolSuccessBg: \"toolSuccessBg\",\n\t\ttoolErrorBg: \"toolErrorBg\",\n\t\ttoolTitle: \"\",\n\t\ttoolOutput: \"gray\",\n\n\t\tmdHeading: \"#f0c674\",\n\t\tmdLink: \"#81a2be\",\n\t\tmdLinkUrl: \"dimGray\",\n\t\tmdCode: \"accent\",\n\t\tmdCodeBlock: \"green\",\n\t\tmdCodeBlockBorder: \"gray\",\n\t\tmdQuote: \"gray\",\n\t\tmdQuoteBorder: \"gray\",\n\t\tmdHr: \"gray\",\n\t\tmdListBullet: \"accent\",\n\n\t\ttoolDiffAdded: \"green\",\n\t\ttoolDiffRemoved: \"red\",\n\t\ttoolDiffContext: \"gray\",\n\n\t\tsyntaxComment: \"#6A9955\",\n\t\tsyntaxKeyword: \"#569CD6\",\n\t\tsyntaxFunction: \"#DCDCAA\",\n\t\tsyntaxVariable: \"#9CDCFE\",\n\t\tsyntaxString: \"#CE9178\",\n\t\tsyntaxNumber: \"#B5CEA8\",\n\t\tsyntaxType: \"#4EC9B0\",\n\t\tsyntaxOperator: \"#D4D4D4\",\n\t\tsyntaxPunctuation: \"#D4D4D4\",\n\n\t\tthinkingOff: \"darkGray\",\n\t\tthinkingMinimal: \"#6e6e6e\",\n\t\tthinkingLow: \"#5f87af\",\n\t\tthinkingMedium: \"#81a2be\",\n\t\tthinkingHigh: \"#b294bb\",\n\t\tthinkingXhigh: \"#d183e8\",\n\n\t\tbashMode: \"green\",\n\t},\n\texport: {\n\t\tpageBg: \"#18181e\",\n\t\tcardBg: \"#1e1e24\",\n\t\tinfoBg: \"#3c3728\",\n\t},\n};\n\n// ---------------------------------------------------------------------------\n// Light theme\n// ---------------------------------------------------------------------------\n\nconst light: ThemeJson = {\n\tname: \"light\",\n\tvars: {\n\t\tteal: \"#5a8080\",\n\t\tblue: \"#547da7\",\n\t\tgreen: \"#588458\",\n\t\tred: \"#aa5555\",\n\t\tyellow: \"#9a7326\",\n\t\twarning: \"#7a5a00\",\n\t\tmediumGray: \"#6c6c6c\",\n\t\tdimGray: \"#767676\",\n\t\tlightGray: \"#b0b0b0\",\n\t\tselectedBg: \"#d0d0e0\",\n\t\tuserMsgBg: \"#e8e8e8\",\n\t\ttoolPendingBg: \"#e8e8f0\",\n\t\ttoolSuccessBg: \"#e8f0e8\",\n\t\ttoolErrorBg: \"#f0e8e8\",\n\t\tcustomMsgBg: \"#ede7f6\",\n\t},\n\tcolors: {\n\t\taccent: \"teal\",\n\t\tborder: \"blue\",\n\t\tborderAccent: \"teal\",\n\t\tborderMuted: \"lightGray\",\n\t\tsuccess: \"green\",\n\t\terror: \"red\",\n\t\twarning: \"warning\",\n\t\tmuted: \"mediumGray\",\n\t\tdim: \"dimGray\",\n\t\ttext: \"\",\n\t\tthinkingText: \"mediumGray\",\n\n\t\tselectedBg: \"selectedBg\",\n\t\tuserMessageBg: \"userMsgBg\",\n\t\tuserMessageText: \"\",\n\t\tcustomMessageBg: \"customMsgBg\",\n\t\tcustomMessageText: \"\",\n\t\tcustomMessageLabel: \"#7e57c2\",\n\t\ttoolPendingBg: \"toolPendingBg\",\n\t\ttoolSuccessBg: \"toolSuccessBg\",\n\t\ttoolErrorBg: \"toolErrorBg\",\n\t\ttoolTitle: \"\",\n\t\ttoolOutput: \"mediumGray\",\n\n\t\tmdHeading: \"yellow\",\n\t\tmdLink: \"blue\",\n\t\tmdLinkUrl: \"dimGray\",\n\t\tmdCode: \"teal\",\n\t\tmdCodeBlock: \"green\",\n\t\tmdCodeBlockBorder: \"mediumGray\",\n\t\tmdQuote: \"mediumGray\",\n\t\tmdQuoteBorder: \"mediumGray\",\n\t\tmdHr: \"mediumGray\",\n\t\tmdListBullet: \"green\",\n\n\t\ttoolDiffAdded: \"green\",\n\t\ttoolDiffRemoved: \"red\",\n\t\ttoolDiffContext: \"mediumGray\",\n\n\t\tsyntaxComment: \"#008000\",\n\t\tsyntaxKeyword: \"#0000FF\",\n\t\tsyntaxFunction: \"#795E26\",\n\t\tsyntaxVariable: \"#001080\",\n\t\tsyntaxString: \"#A31515\",\n\t\tsyntaxNumber: \"#098658\",\n\t\tsyntaxType: \"#267F99\",\n\t\tsyntaxOperator: \"#000000\",\n\t\tsyntaxPunctuation: \"#000000\",\n\n\t\tthinkingOff: \"lightGray\",\n\t\tthinkingMinimal: \"#767676\",\n\t\tthinkingLow: \"blue\",\n\t\tthinkingMedium: \"teal\",\n\t\tthinkingHigh: \"#875f87\",\n\t\tthinkingXhigh: \"#8b008b\",\n\n\t\tbashMode: \"green\",\n\t},\n\texport: {\n\t\tpageBg: \"#f8f8f8\",\n\t\tcardBg: \"#ffffff\",\n\t\tinfoBg: \"#fffae6\",\n\t},\n};\n\n// ---------------------------------------------------------------------------\n// Export\n// ---------------------------------------------------------------------------\n\nexport const builtinThemes: Record<string, ThemeJson> = { dark, light };\n"]}
@@ -1566,14 +1566,17 @@ export function mergeMilestoneToMain(
1566
1566
  // Non-fatal — proceed with merge; untracked files may block it
1567
1567
  }
1568
1568
 
1569
- // 7c. Clean stale MERGE_HEAD before the squash merge (#2912).
1570
- // The native (libgit2) merge path or a prior interrupted merge may leave
1571
- // MERGE_HEAD in the git dir. `git merge --squash` refuses to run when
1572
- // MERGE_HEAD exists, so remove it preemptively.
1569
+ // 7b. Clean up stale merge state before attempting squash merge (#2912).
1570
+ // A leftover MERGE_HEAD (from a previous failed merge, libgit2 native path,
1571
+ // or interrupted operation) causes `git merge --squash` to refuse with
1572
+ // "fatal: You have not concluded your merge (MERGE_HEAD exists)".
1573
+ // Defensively remove merge artifacts before starting.
1573
1574
  try {
1574
- const gitDirPre = resolveGitDir(originalBasePath_);
1575
- const mergeHeadPre = join(gitDirPre, "MERGE_HEAD");
1576
- if (existsSync(mergeHeadPre)) unlinkSync(mergeHeadPre);
1575
+ const gitDir_ = resolveGitDir(originalBasePath_);
1576
+ for (const f of ["SQUASH_MSG", "MERGE_MSG", "MERGE_HEAD"]) {
1577
+ const p = join(gitDir_, f);
1578
+ if (existsSync(p)) unlinkSync(p);
1579
+ }
1577
1580
  } catch { /* best-effort */ }
1578
1581
 
1579
1582
  // 8. Squash merge — auto-resolve .gsd/ state file conflicts (#530)
@@ -48,26 +48,20 @@ export function registerHooks(pi: ExtensionAPI): void {
48
48
  const { dirname } = await import("node:path");
49
49
  const { printWelcomeScreen } = await import(
50
50
  join(dirname(gsdBinPath), "welcome-screen.js")
51
- ) as { printWelcomeScreen: (opts: { version: string; modelName?: string; provider?: string }) => void };
52
- printWelcomeScreen({ version: process.env.GSD_VERSION || "0.0.0" });
51
+ ) as { printWelcomeScreen: (opts: { version: string; modelName?: string; provider?: string; remoteChannel?: string }) => void };
52
+
53
+ let remoteChannel: string | undefined;
54
+ try {
55
+ const { resolveRemoteConfig } = await import("../../remote-questions/config.js");
56
+ const rc = resolveRemoteConfig();
57
+ if (rc) remoteChannel = rc.channel;
58
+ } catch { /* non-fatal */ }
59
+
60
+ printWelcomeScreen({ version: process.env.GSD_VERSION || "0.0.0", remoteChannel });
53
61
  }
54
62
  } catch { /* non-fatal */ }
55
63
  }
56
64
  loadToolApiKeys();
57
- try {
58
- const [{ getRemoteConfigStatus }, { getLatestPromptSummary }] = await Promise.all([
59
- import("../../remote-questions/config.js"),
60
- import("../../remote-questions/status.js"),
61
- ]);
62
- const status = getRemoteConfigStatus();
63
- const latest = getLatestPromptSummary();
64
- if (!status.includes("not configured")) {
65
- const suffix = latest ? `\nLast remote prompt: ${latest.id} (${latest.status})` : "";
66
- ctx.ui.notify(`${status}${suffix}`, status.includes("disabled") ? "warning" : "info");
67
- }
68
- } catch {
69
- // ignore
70
- }
71
65
  });
72
66
 
73
67
  pi.on("session_switch", async (_event, ctx) => {
@@ -95,6 +95,27 @@ export async function buildBeforeAgentStartResult(
95
95
  }
96
96
  }
97
97
 
98
+ let codebaseBlock = "";
99
+ const codebasePath = resolveGsdRootFile(process.cwd(), "CODEBASE");
100
+ if (existsSync(codebasePath)) {
101
+ try {
102
+ const rawContent = readFileSync(codebasePath, "utf-8").trim();
103
+ if (rawContent) {
104
+ // Cap injection size to ~2 000 tokens to avoid bloating every request.
105
+ // Full map is always available at .gsd/CODEBASE.md.
106
+ const MAX_CODEBASE_CHARS = 8_000;
107
+ const generatedMatch = rawContent.match(/Generated: (\S+)/);
108
+ const generatedAt = generatedMatch?.[1] ?? "unknown";
109
+ const content = rawContent.length > MAX_CODEBASE_CHARS
110
+ ? rawContent.slice(0, MAX_CODEBASE_CHARS) + "\n\n*(truncated — see .gsd/CODEBASE.md for full map)*"
111
+ : rawContent;
112
+ codebaseBlock = `\n\n[PROJECT CODEBASE — File structure and descriptions (generated ${generatedAt}, may be stale — run /gsd codebase update to refresh)]\n\n${content}`;
113
+ }
114
+ } catch {
115
+ // skip
116
+ }
117
+ }
118
+
98
119
  warnDeprecatedAgentInstructions();
99
120
 
100
121
  const injection = await buildGuidedExecuteContextInjection(event.prompt, process.cwd());
@@ -103,7 +124,7 @@ export async function buildBeforeAgentStartResult(
103
124
  const forensicsInjection = !injection ? buildForensicsContextInjection(process.cwd()) : null;
104
125
 
105
126
  const worktreeBlock = buildWorktreeContextBlock();
106
- const fullSystem = `${event.systemPrompt}\n\n[SYSTEM CONTEXT — GSD]\n\n${systemContent}${preferenceBlock}${knowledgeBlock}${memoryBlock}${newSkillsBlock}${worktreeBlock}`;
127
+ const fullSystem = `${event.systemPrompt}\n\n[SYSTEM CONTEXT — GSD]\n\n${systemContent}${preferenceBlock}${knowledgeBlock}${codebaseBlock}${memoryBlock}${newSkillsBlock}${worktreeBlock}`;
107
128
 
108
129
  stopContextTimer({
109
130
  systemPromptSize: fullSystem.length,
@@ -0,0 +1,351 @@
1
+ /**
2
+ * GSD Codebase Map Generator
3
+ *
4
+ * Produces .gsd/CODEBASE.md — a structural table of contents for the project.
5
+ * Gives fresh agent contexts instant orientation without filesystem exploration.
6
+ *
7
+ * Generation: walk `git ls-files`, group by directory, output with descriptions.
8
+ * Maintenance: agent updates descriptions as it works; incremental update preserves them.
9
+ */
10
+
11
+ import { existsSync, readFileSync, writeFileSync, mkdirSync } from "node:fs";
12
+ import { join, dirname, extname } from "node:path";
13
+
14
+ import { execSync } from "node:child_process";
15
+ import { gsdRoot } from "./paths.js";
16
+
17
+ // ─── Types ───────────────────────────────────────────────────────────────────
18
+
19
+ export interface CodebaseMapOptions {
20
+ excludePatterns?: string[];
21
+ maxFiles?: number;
22
+ collapseThreshold?: number;
23
+ }
24
+
25
+ interface FileEntry {
26
+ path: string;
27
+ description: string;
28
+ }
29
+
30
+ interface DirectoryGroup {
31
+ path: string;
32
+ files: FileEntry[];
33
+ collapsed: boolean;
34
+ }
35
+
36
+ // ─── Defaults ────────────────────────────────────────────────────────────────
37
+
38
+ const DEFAULT_EXCLUDES = [
39
+ ".gsd/",
40
+ ".planning/",
41
+ ".git/",
42
+ "node_modules/",
43
+ "dist/",
44
+ "build/",
45
+ ".next/",
46
+ "coverage/",
47
+ "__pycache__/",
48
+ ".venv/",
49
+ "vendor/",
50
+ ];
51
+
52
+ const DEFAULT_MAX_FILES = 500;
53
+ const DEFAULT_COLLAPSE_THRESHOLD = 20;
54
+
55
+ // ─── Parsing ─────────────────────────────────────────────────────────────────
56
+
57
+ /**
58
+ * Parse an existing CODEBASE.md to extract file → description mappings.
59
+ * Also scans <!-- gsd:collapsed-descriptions --> comment blocks to preserve
60
+ * descriptions for files in collapsed directories across incremental updates.
61
+ */
62
+ export function parseCodebaseMap(content: string): Map<string, string> {
63
+ const descriptions = new Map<string, string>();
64
+ let inCollapsedBlock = false;
65
+
66
+ for (const line of content.split("\n")) {
67
+ // Track collapsed-description comment blocks
68
+ if (line.trimStart().startsWith("<!-- gsd:collapsed-descriptions")) {
69
+ inCollapsedBlock = true;
70
+ continue;
71
+ }
72
+ if (inCollapsedBlock && line.trimStart().startsWith("-->")) {
73
+ inCollapsedBlock = false;
74
+ continue;
75
+ }
76
+
77
+ // Match: - `path/to/file.ts` — Description here
78
+ const match = line.match(/^- `(.+?)` — (.+)$/);
79
+ if (match) {
80
+ descriptions.set(match[1], match[2]);
81
+ continue;
82
+ }
83
+
84
+ // Match: - `path/to/file.ts` (no description) — only outside collapsed blocks
85
+ if (!inCollapsedBlock) {
86
+ const bareMatch = line.match(/^- `(.+?)`\s*$/);
87
+ if (bareMatch) {
88
+ descriptions.set(bareMatch[1], "");
89
+ }
90
+ }
91
+ }
92
+ return descriptions;
93
+ }
94
+
95
+ // ─── File Enumeration ────────────────────────────────────────────────────────
96
+
97
+ function shouldExclude(filePath: string, excludes: string[]): boolean {
98
+ for (const pattern of excludes) {
99
+ if (pattern.endsWith("/")) {
100
+ if (filePath.startsWith(pattern) || filePath.includes(`/${pattern}`)) return true;
101
+ } else if (filePath === pattern || filePath.endsWith(`/${pattern}`)) {
102
+ return true;
103
+ }
104
+ }
105
+ // Skip binary/lock files
106
+ const ext = extname(filePath).toLowerCase();
107
+ if ([".lock", ".png", ".jpg", ".jpeg", ".gif", ".ico", ".woff", ".woff2", ".ttf", ".eot", ".svg"].includes(ext)) {
108
+ return true;
109
+ }
110
+ return false;
111
+ }
112
+
113
+ function lsFiles(basePath: string): string[] {
114
+ try {
115
+ const result = execSync("git ls-files", { cwd: basePath, encoding: "utf-8", timeout: 10000 });
116
+ return result.split("\n").filter(Boolean);
117
+ } catch {
118
+ return [];
119
+ }
120
+ }
121
+
122
+ /**
123
+ * Enumerate tracked files, applying exclusions and the maxFiles cap.
124
+ * Returns both the file list and whether truncation occurred.
125
+ */
126
+ function enumerateFiles(basePath: string, excludes: string[], maxFiles: number): { files: string[]; truncated: boolean } {
127
+ const allFiles = lsFiles(basePath);
128
+ const filtered = allFiles.filter((f) => !shouldExclude(f, excludes));
129
+ const truncated = filtered.length > maxFiles;
130
+ return { files: truncated ? filtered.slice(0, maxFiles) : filtered, truncated };
131
+ }
132
+
133
+ // ─── Grouping ────────────────────────────────────────────────────────────────
134
+
135
+ function groupByDirectory(
136
+ files: string[],
137
+ descriptions: Map<string, string>,
138
+ collapseThreshold: number,
139
+ ): DirectoryGroup[] {
140
+ const dirMap = new Map<string, FileEntry[]>();
141
+
142
+ for (const file of files) {
143
+ const dir = dirname(file);
144
+ const dirKey = dir === "." ? "" : dir;
145
+ if (!dirMap.has(dirKey)) {
146
+ dirMap.set(dirKey, []);
147
+ }
148
+ dirMap.get(dirKey)!.push({
149
+ path: file,
150
+ description: descriptions.get(file) ?? "",
151
+ });
152
+ }
153
+
154
+ const groups: DirectoryGroup[] = [];
155
+ const sortedDirs = [...dirMap.keys()].sort();
156
+
157
+ for (const dir of sortedDirs) {
158
+ const dirFiles = dirMap.get(dir)!;
159
+ dirFiles.sort((a, b) => a.path.localeCompare(b.path));
160
+
161
+ groups.push({
162
+ path: dir,
163
+ files: dirFiles,
164
+ collapsed: dirFiles.length > collapseThreshold,
165
+ });
166
+ }
167
+
168
+ return groups;
169
+ }
170
+
171
+ // ─── Rendering ───────────────────────────────────────────────────────────────
172
+
173
+ function renderCodebaseMap(groups: DirectoryGroup[], totalFiles: number, truncated: boolean): string {
174
+ const lines: string[] = [];
175
+ const now = new Date().toISOString().split(".")[0] + "Z";
176
+ const described = groups.reduce((sum, g) => sum + g.files.filter((f) => f.description).length, 0);
177
+
178
+ lines.push("# Codebase Map");
179
+ lines.push("");
180
+ lines.push(`Generated: ${now} | Files: ${totalFiles} | Described: ${described}/${totalFiles}`);
181
+ if (truncated) {
182
+ lines.push(`Note: Truncated to first ${totalFiles} files. Run with higher --max-files to include all.`);
183
+ }
184
+ lines.push("");
185
+
186
+ for (const group of groups) {
187
+ const heading = group.path || "(root)";
188
+ lines.push(`### ${heading}/`);
189
+
190
+ if (group.collapsed) {
191
+ // Summarize collapsed directories
192
+ const extensions = new Map<string, number>();
193
+ for (const f of group.files) {
194
+ const ext = extname(f.path) || "(no ext)";
195
+ extensions.set(ext, (extensions.get(ext) ?? 0) + 1);
196
+ }
197
+ const extSummary = [...extensions.entries()]
198
+ .sort((a, b) => b[1] - a[1])
199
+ .map(([ext, count]) => `${count} ${ext}`)
200
+ .join(", ");
201
+ lines.push(`- *(${group.files.length} files: ${extSummary})*`);
202
+
203
+ // Preserve any existing descriptions in a hidden comment block so
204
+ // incremental updates can recover them via parseCodebaseMap.
205
+ const descLines = group.files
206
+ .filter((f) => f.description)
207
+ .map((f) => `- \`${f.path}\` — ${f.description}`);
208
+ if (descLines.length > 0) {
209
+ lines.push("<!-- gsd:collapsed-descriptions");
210
+ lines.push(...descLines);
211
+ lines.push("-->");
212
+ }
213
+ } else {
214
+ for (const file of group.files) {
215
+ if (file.description) {
216
+ lines.push(`- \`${file.path}\` — ${file.description}`);
217
+ } else {
218
+ lines.push(`- \`${file.path}\``);
219
+ }
220
+ }
221
+ }
222
+ lines.push("");
223
+ }
224
+
225
+ return lines.join("\n");
226
+ }
227
+
228
+ // ─── Public API ──────────────────────────────────────────────────────────────
229
+
230
+ /**
231
+ * Generate a fresh CODEBASE.md from scratch.
232
+ * Preserves existing descriptions if `existingDescriptions` is provided.
233
+ */
234
+ export function generateCodebaseMap(
235
+ basePath: string,
236
+ options?: CodebaseMapOptions,
237
+ existingDescriptions?: Map<string, string>,
238
+ ): { content: string; fileCount: number; truncated: boolean; files: string[] } {
239
+ const excludes = [...DEFAULT_EXCLUDES, ...(options?.excludePatterns ?? [])];
240
+ const maxFiles = options?.maxFiles ?? DEFAULT_MAX_FILES;
241
+ const collapseThreshold = options?.collapseThreshold ?? DEFAULT_COLLAPSE_THRESHOLD;
242
+
243
+ const { files, truncated } = enumerateFiles(basePath, excludes, maxFiles);
244
+ const descriptions = existingDescriptions ?? new Map<string, string>();
245
+ const groups = groupByDirectory(files, descriptions, collapseThreshold);
246
+ const content = renderCodebaseMap(groups, files.length, truncated);
247
+
248
+ return { content, fileCount: files.length, truncated, files };
249
+ }
250
+
251
+ /**
252
+ * Incremental update: re-scan files, preserve existing descriptions,
253
+ * add new files, remove deleted files.
254
+ */
255
+ export function updateCodebaseMap(
256
+ basePath: string,
257
+ options?: CodebaseMapOptions,
258
+ ): { content: string; added: number; removed: number; unchanged: number; fileCount: number; truncated: boolean } {
259
+ const codebasePath = join(gsdRoot(basePath), "CODEBASE.md");
260
+
261
+ // Load existing descriptions
262
+ let existingDescriptions = new Map<string, string>();
263
+ if (existsSync(codebasePath)) {
264
+ const existing = readFileSync(codebasePath, "utf-8");
265
+ existingDescriptions = parseCodebaseMap(existing);
266
+ }
267
+
268
+ const existingFiles = new Set(existingDescriptions.keys());
269
+
270
+ // Generate new map preserving descriptions — reuse the returned file list
271
+ // to avoid a second enumeration (prevents race between content and stats).
272
+ const result = generateCodebaseMap(basePath, options, existingDescriptions);
273
+ const currentSet = new Set(result.files);
274
+
275
+ // Count changes
276
+ let added = 0;
277
+ let removed = 0;
278
+
279
+ for (const f of result.files) {
280
+ if (!existingFiles.has(f)) added++;
281
+ }
282
+ for (const f of existingFiles) {
283
+ if (!currentSet.has(f)) removed++;
284
+ }
285
+
286
+ return {
287
+ content: result.content,
288
+ added,
289
+ removed,
290
+ unchanged: result.files.length - added,
291
+ fileCount: result.fileCount,
292
+ truncated: result.truncated,
293
+ };
294
+ }
295
+
296
+ /**
297
+ * Write CODEBASE.md to .gsd/ directory.
298
+ */
299
+ export function writeCodebaseMap(basePath: string, content: string): string {
300
+ const root = gsdRoot(basePath);
301
+ mkdirSync(root, { recursive: true });
302
+ const outPath = join(root, "CODEBASE.md");
303
+ writeFileSync(outPath, content, "utf-8");
304
+ return outPath;
305
+ }
306
+
307
+ /**
308
+ * Read existing CODEBASE.md, or return null if it doesn't exist.
309
+ */
310
+ export function readCodebaseMap(basePath: string): string | null {
311
+ const codebasePath = join(gsdRoot(basePath), "CODEBASE.md");
312
+ if (!existsSync(codebasePath)) return null;
313
+ try {
314
+ return readFileSync(codebasePath, "utf-8");
315
+ } catch {
316
+ return null;
317
+ }
318
+ }
319
+
320
+ /**
321
+ * Get stats about the codebase map.
322
+ */
323
+ export function getCodebaseMapStats(basePath: string): {
324
+ exists: boolean;
325
+ fileCount: number;
326
+ describedCount: number;
327
+ undescribedCount: number;
328
+ generatedAt: string | null;
329
+ } {
330
+ const content = readCodebaseMap(basePath);
331
+ if (!content) {
332
+ return { exists: false, fileCount: 0, describedCount: 0, undescribedCount: 0, generatedAt: null };
333
+ }
334
+
335
+ // Parse total file count from the header line (accurate even for collapsed dirs)
336
+ const fileCountMatch = content.match(/Files:\s*(\d+)/);
337
+ const totalFiles = fileCountMatch ? parseInt(fileCountMatch[1], 10) : 0;
338
+
339
+ // Use parseCodebaseMap to count described files (includes collapsed-description blocks)
340
+ const descriptions = parseCodebaseMap(content);
341
+ const described = [...descriptions.values()].filter((d) => d.length > 0).length;
342
+ const dateMatch = content.match(/Generated: (\S+)/);
343
+
344
+ return {
345
+ exists: true,
346
+ fileCount: totalFiles,
347
+ describedCount: described,
348
+ undescribedCount: totalFiles - described,
349
+ generatedAt: dateMatch?.[1] ?? null,
350
+ };
351
+ }
@@ -15,7 +15,7 @@ export interface GsdCommandDefinition {
15
15
  type CompletionMap = Record<string, readonly GsdCommandDefinition[]>;
16
16
 
17
17
  export const GSD_COMMAND_DESCRIPTION =
18
- "GSD — Get Shit Done: /gsd help|start|templates|next|auto|stop|pause|status|widget|visualize|queue|quick|discuss|capture|triage|dispatch|history|undo|undo-task|reset-slice|rate|skip|export|cleanup|mode|prefs|config|keys|hooks|run-hook|skill-health|doctor|logs|forensics|changelog|migrate|remote|steer|knowledge|new-milestone|parallel|cmux|park|unpark|init|setup|inspect|extensions|update|fast|mcp|rethink";
18
+ "GSD — Get Shit Done: /gsd help|start|templates|next|auto|stop|pause|status|widget|visualize|queue|quick|discuss|capture|triage|dispatch|history|undo|undo-task|reset-slice|rate|skip|export|cleanup|mode|prefs|config|keys|hooks|run-hook|skill-health|doctor|logs|forensics|changelog|migrate|remote|steer|knowledge|new-milestone|parallel|cmux|park|unpark|init|setup|inspect|extensions|update|fast|mcp|rethink|codebase";
19
19
 
20
20
  export const TOP_LEVEL_SUBCOMMANDS: readonly GsdCommandDefinition[] = [
21
21
  { cmd: "help", desc: "Categorized command reference with descriptions" },
@@ -71,6 +71,7 @@ export const TOP_LEVEL_SUBCOMMANDS: readonly GsdCommandDefinition[] = [
71
71
  { cmd: "mcp", desc: "MCP server status and connectivity check (status, check <server>)" },
72
72
  { cmd: "rethink", desc: "Conversational project reorganization — reorder, park, discard, add milestones" },
73
73
  { cmd: "workflow", desc: "Custom workflow lifecycle (new, run, list, validate, pause, resume)" },
74
+ { cmd: "codebase", desc: "Generate and manage codebase map (.gsd/CODEBASE.md)" },
74
75
  ];
75
76
 
76
77
  const NESTED_COMPLETIONS: CompletionMap = {
@@ -225,6 +226,14 @@ const NESTED_COMPLETIONS: CompletionMap = {
225
226
  { cmd: "pause", desc: "Pause custom workflow auto-mode" },
226
227
  { cmd: "resume", desc: "Resume paused custom workflow auto-mode" },
227
228
  ],
229
+ codebase: [
230
+ { cmd: "generate", desc: "Generate or regenerate CODEBASE.md" },
231
+ { cmd: "generate --max-files", desc: "Generate with custom file limit (default: 500)" },
232
+ { cmd: "update", desc: "Incremental update (preserves descriptions)" },
233
+ { cmd: "update --max-files", desc: "Update with custom file limit" },
234
+ { cmd: "stats", desc: "Show file count, description coverage, and generation time" },
235
+ { cmd: "help", desc: "Show usage and available subcommands" },
236
+ ],
228
237
  };
229
238
 
230
239
  function filterOptions(