gsd-pi 2.44.0-dev.73f2fd5 → 2.44.0-dev.8894d5b

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 (164) hide show
  1. package/dist/resources/extensions/gsd/auto/infra-errors.js +0 -3
  2. package/dist/resources/extensions/gsd/auto/phases.js +36 -36
  3. package/dist/resources/extensions/gsd/auto-prompts.js +1 -24
  4. package/dist/resources/extensions/gsd/auto-timers.js +3 -57
  5. package/dist/resources/extensions/gsd/auto-worktree-sync.js +0 -4
  6. package/dist/resources/extensions/gsd/auto-worktree.js +6 -9
  7. package/dist/resources/extensions/gsd/auto.js +3 -30
  8. package/dist/resources/extensions/gsd/bootstrap/db-tools.js +0 -136
  9. package/dist/resources/extensions/gsd/commands/catalog.js +1 -6
  10. package/dist/resources/extensions/gsd/commands/handlers/core.js +0 -1
  11. package/dist/resources/extensions/gsd/commands/handlers/ops.js +0 -5
  12. package/dist/resources/extensions/gsd/db-writer.js +16 -34
  13. package/dist/resources/extensions/gsd/doctor.js +0 -8
  14. package/dist/resources/extensions/gsd/git-service.js +3 -8
  15. package/dist/resources/extensions/gsd/gsd-db.js +1 -12
  16. package/dist/resources/extensions/gsd/markdown-renderer.js +1 -1
  17. package/dist/resources/extensions/gsd/prompts/complete-milestone.md +4 -2
  18. package/dist/resources/extensions/gsd/prompts/plan-slice.md +1 -1
  19. package/dist/resources/extensions/gsd/prompts/reassess-roadmap.md +6 -6
  20. package/dist/resources/extensions/gsd/prompts/replan-slice.md +14 -3
  21. package/dist/resources/extensions/gsd/prompts/validate-milestone.md +37 -7
  22. package/dist/resources/extensions/gsd/provider-error-pause.js +0 -7
  23. package/dist/resources/extensions/gsd/tools/plan-slice.js +0 -1
  24. package/dist/resources/extensions/gsd/tools/plan-task.js +0 -1
  25. package/dist/resources/extensions/gsd/tools/replan-slice.js +0 -2
  26. package/dist/resources/extensions/gsd/worktree-resolver.js +0 -6
  27. package/dist/resources/extensions/mcp-client/index.js +0 -14
  28. package/dist/web/standalone/.next/BUILD_ID +1 -1
  29. package/dist/web/standalone/.next/app-path-routes-manifest.json +20 -20
  30. package/dist/web/standalone/.next/build-manifest.json +2 -2
  31. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  32. package/dist/web/standalone/.next/server/app/_global-error.html +2 -2
  33. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  34. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  35. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  36. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  37. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  38. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  39. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  40. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  41. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  42. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  43. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  44. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  45. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  46. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  47. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  48. package/dist/web/standalone/.next/server/app/index.html +1 -1
  49. package/dist/web/standalone/.next/server/app/index.rsc +1 -1
  50. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  51. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
  52. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  53. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  54. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  55. package/dist/web/standalone/.next/server/app-paths-manifest.json +20 -20
  56. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  57. package/dist/web/standalone/.next/server/pages/500.html +2 -2
  58. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  59. package/package.json +1 -1
  60. package/packages/pi-coding-agent/dist/core/auth-storage.d.ts +1 -3
  61. package/packages/pi-coding-agent/dist/core/auth-storage.d.ts.map +1 -1
  62. package/packages/pi-coding-agent/dist/core/auth-storage.js +1 -15
  63. package/packages/pi-coding-agent/dist/core/auth-storage.js.map +1 -1
  64. package/packages/pi-coding-agent/dist/core/model-registry.d.ts +0 -11
  65. package/packages/pi-coding-agent/dist/core/model-registry.d.ts.map +1 -1
  66. package/packages/pi-coding-agent/dist/core/model-registry.js +1 -20
  67. package/packages/pi-coding-agent/dist/core/model-registry.js.map +1 -1
  68. package/packages/pi-coding-agent/dist/core/settings-manager.d.ts +0 -3
  69. package/packages/pi-coding-agent/dist/core/settings-manager.d.ts.map +1 -1
  70. package/packages/pi-coding-agent/dist/core/settings-manager.js +0 -6
  71. package/packages/pi-coding-agent/dist/core/settings-manager.js.map +1 -1
  72. package/packages/pi-coding-agent/dist/main.d.ts.map +1 -1
  73. package/packages/pi-coding-agent/dist/main.js +0 -17
  74. package/packages/pi-coding-agent/dist/main.js.map +1 -1
  75. package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.d.ts +1 -3
  76. package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
  77. package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.js +1 -8
  78. package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.js.map +1 -1
  79. package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.d.ts +0 -2
  80. package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  81. package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.js +0 -12
  82. package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.js.map +1 -1
  83. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  84. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js +1 -4
  85. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js.map +1 -1
  86. package/packages/pi-coding-agent/dist/modes/interactive/components/user-message.d.ts +2 -5
  87. package/packages/pi-coding-agent/dist/modes/interactive/components/user-message.d.ts.map +1 -1
  88. package/packages/pi-coding-agent/dist/modes/interactive/components/user-message.js +2 -13
  89. package/packages/pi-coding-agent/dist/modes/interactive/components/user-message.js.map +1 -1
  90. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
  91. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js +8 -17
  92. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
  93. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  94. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +3 -7
  95. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
  96. package/packages/pi-coding-agent/src/core/auth-storage.ts +1 -15
  97. package/packages/pi-coding-agent/src/core/model-registry.ts +1 -21
  98. package/packages/pi-coding-agent/src/core/settings-manager.ts +0 -9
  99. package/packages/pi-coding-agent/src/main.ts +0 -19
  100. package/packages/pi-coding-agent/src/modes/interactive/components/assistant-message.ts +0 -10
  101. package/packages/pi-coding-agent/src/modes/interactive/components/settings-selector.ts +0 -15
  102. package/packages/pi-coding-agent/src/modes/interactive/components/tool-execution.ts +1 -3
  103. package/packages/pi-coding-agent/src/modes/interactive/components/user-message.ts +3 -18
  104. package/packages/pi-coding-agent/src/modes/interactive/controllers/chat-controller.ts +7 -16
  105. package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +1 -8
  106. package/src/resources/extensions/gsd/auto/infra-errors.ts +0 -3
  107. package/src/resources/extensions/gsd/auto/phases.ts +48 -45
  108. package/src/resources/extensions/gsd/auto-prompts.ts +1 -24
  109. package/src/resources/extensions/gsd/auto-timers.ts +3 -64
  110. package/src/resources/extensions/gsd/auto-worktree-sync.ts +0 -5
  111. package/src/resources/extensions/gsd/auto-worktree.ts +6 -9
  112. package/src/resources/extensions/gsd/auto.ts +3 -37
  113. package/src/resources/extensions/gsd/bootstrap/db-tools.ts +0 -129
  114. package/src/resources/extensions/gsd/commands/catalog.ts +1 -6
  115. package/src/resources/extensions/gsd/commands/handlers/core.ts +0 -1
  116. package/src/resources/extensions/gsd/commands/handlers/ops.ts +0 -5
  117. package/src/resources/extensions/gsd/db-writer.ts +17 -39
  118. package/src/resources/extensions/gsd/doctor.ts +1 -7
  119. package/src/resources/extensions/gsd/git-service.ts +2 -6
  120. package/src/resources/extensions/gsd/gsd-db.ts +1 -16
  121. package/src/resources/extensions/gsd/markdown-renderer.ts +1 -1
  122. package/src/resources/extensions/gsd/prompts/complete-milestone.md +4 -2
  123. package/src/resources/extensions/gsd/prompts/plan-slice.md +1 -1
  124. package/src/resources/extensions/gsd/prompts/reassess-roadmap.md +6 -6
  125. package/src/resources/extensions/gsd/prompts/replan-slice.md +14 -3
  126. package/src/resources/extensions/gsd/prompts/validate-milestone.md +37 -7
  127. package/src/resources/extensions/gsd/provider-error-pause.ts +0 -9
  128. package/src/resources/extensions/gsd/tests/db-writer.test.ts +0 -79
  129. package/src/resources/extensions/gsd/tests/infra-error.test.ts +2 -20
  130. package/src/resources/extensions/gsd/tests/prompt-contracts.test.ts +7 -11
  131. package/src/resources/extensions/gsd/tests/tool-naming.test.ts +1 -2
  132. package/src/resources/extensions/gsd/tools/plan-slice.ts +0 -2
  133. package/src/resources/extensions/gsd/tools/plan-task.ts +0 -2
  134. package/src/resources/extensions/gsd/tools/replan-slice.ts +0 -3
  135. package/src/resources/extensions/gsd/worktree-resolver.ts +0 -7
  136. package/src/resources/extensions/mcp-client/index.ts +0 -20
  137. package/dist/resources/extensions/gsd/commands-mcp-status.js +0 -187
  138. package/dist/resources/extensions/gsd/tools/validate-milestone.js +0 -88
  139. package/packages/pi-coding-agent/dist/core/local-model-check.d.ts +0 -15
  140. package/packages/pi-coding-agent/dist/core/local-model-check.d.ts.map +0 -1
  141. package/packages/pi-coding-agent/dist/core/local-model-check.js +0 -41
  142. package/packages/pi-coding-agent/dist/core/local-model-check.js.map +0 -1
  143. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/timestamp.test.d.ts +0 -2
  144. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/timestamp.test.d.ts.map +0 -1
  145. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/timestamp.test.js +0 -32
  146. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/timestamp.test.js.map +0 -1
  147. package/packages/pi-coding-agent/dist/modes/interactive/components/timestamp.d.ts +0 -15
  148. package/packages/pi-coding-agent/dist/modes/interactive/components/timestamp.d.ts.map +0 -1
  149. package/packages/pi-coding-agent/dist/modes/interactive/components/timestamp.js +0 -40
  150. package/packages/pi-coding-agent/dist/modes/interactive/components/timestamp.js.map +0 -1
  151. package/packages/pi-coding-agent/src/core/local-model-check.ts +0 -45
  152. package/packages/pi-coding-agent/src/modes/interactive/components/__tests__/timestamp.test.ts +0 -38
  153. package/packages/pi-coding-agent/src/modes/interactive/components/timestamp.ts +0 -48
  154. package/src/resources/extensions/gsd/commands-mcp-status.ts +0 -247
  155. package/src/resources/extensions/gsd/tests/auto-pr-bugs.test.ts +0 -88
  156. package/src/resources/extensions/gsd/tests/completed-units-metrics-sync.test.ts +0 -114
  157. package/src/resources/extensions/gsd/tests/est-annotation-timeout.test.ts +0 -120
  158. package/src/resources/extensions/gsd/tests/mcp-status.test.ts +0 -103
  159. package/src/resources/extensions/gsd/tests/merge-conflict-stops-loop.test.ts +0 -66
  160. package/src/resources/extensions/gsd/tests/stop-auto-merge-back.test.ts +0 -67
  161. package/src/resources/extensions/gsd/tests/terminated-transient.test.ts +0 -49
  162. package/src/resources/extensions/gsd/tools/validate-milestone.ts +0 -127
  163. /package/dist/web/standalone/.next/static/{kxxAA66bah_yhPYqLBHE2 → oZMtyM-zfu6Inx-S59cOl}/_buildManifest.js +0 -0
  164. /package/dist/web/standalone/.next/static/{kxxAA66bah_yhPYqLBHE2 → oZMtyM-zfu6Inx-S59cOl}/_ssgManifest.js +0 -0
@@ -744,21 +744,7 @@ export class AuthStorage {
744
744
  * @param providerId - The provider to get an API key for
745
745
  * @param sessionId - Optional session ID for sticky credential selection
746
746
  */
747
- async getApiKey(providerId: string, sessionId?: string, options?: { baseUrl?: string }): Promise<string | undefined> {
748
- // If the model has a local baseUrl, return a dummy key to avoid auth blocking
749
- if (options?.baseUrl) {
750
- try {
751
- const hostname = new URL(options.baseUrl).hostname;
752
- if (hostname === "localhost" || hostname === "127.0.0.1" || hostname === "0.0.0.0" || hostname === "::1") {
753
- return "local-no-key-needed";
754
- }
755
- } catch {
756
- if (options.baseUrl.startsWith("unix:")) {
757
- return "local-no-key-needed";
758
- }
759
- }
760
- }
761
-
747
+ async getApiKey(providerId: string, sessionId?: string): Promise<string | undefined> {
762
748
  // Runtime override takes highest priority
763
749
  const runtimeKey = this.runtimeOverrides.get(providerId);
764
750
  if (runtimeKey) {
@@ -28,7 +28,6 @@ import { ModelDiscoveryCache } from "./discovery-cache.js";
28
28
  import type { DiscoveredModel, DiscoveryResult } from "./model-discovery.js";
29
29
  import { getDefaultTTL, getDiscoverableProviders, getDiscoveryAdapter } from "./model-discovery.js";
30
30
  import { clearConfigValueCache, resolveConfigValue, resolveHeaders } from "./resolve-config-value.js";
31
- import { isLocalModel } from "./local-model-check.js";
32
31
 
33
32
  const Ajv = (AjvModule as any).default || AjvModule;
34
33
  const ajv = new Ajv();
@@ -558,7 +557,7 @@ export class ModelRegistry {
558
557
  async getApiKey(model: Model<Api>, sessionId?: string): Promise<string | undefined> {
559
558
  const authMode = this.getProviderAuthMode(model.provider);
560
559
  if (authMode === "externalCli" || authMode === "none") return undefined;
561
- return this.authStorage.getApiKey(model.provider, sessionId, { baseUrl: model.baseUrl });
560
+ return this.authStorage.getApiKey(model.provider, sessionId);
562
561
  }
563
562
 
564
563
  /**
@@ -808,25 +807,6 @@ export class ModelRegistry {
808
807
  }
809
808
  return converted;
810
809
  }
811
-
812
- /**
813
- * Check if a model's baseUrl points to a local endpoint.
814
- * Delegates to standalone isLocalModel() function.
815
- */
816
- static isLocalModel(model: Model<Api>): boolean {
817
- return isLocalModel(model);
818
- }
819
-
820
- /**
821
- * Check if all models in the registry are local.
822
- * Returns true only if every model passes isLocalModel().
823
- * Returns false if there are no models.
824
- */
825
- isAllLocalChain(): boolean {
826
- const models = this.getAll();
827
- if (models.length === 0) return false;
828
- return models.every((m) => isLocalModel(m));
829
- }
830
810
  }
831
811
 
832
812
  /**
@@ -151,7 +151,6 @@ export interface Settings {
151
151
  fallback?: FallbackSettings;
152
152
  modelDiscovery?: ModelDiscoverySettings;
153
153
  editMode?: "standard" | "hashline"; // Edit tool mode: "standard" (text match) or "hashline" (LINE#ID anchors). Default: "standard"
154
- timestampFormat?: "date-time-iso" | "date-time-us"; // Timestamp display format for messages. Default: "date-time-iso"
155
154
  }
156
155
 
157
156
  /** Deep merge settings: project/overrides take precedence, nested objects merge recursively */
@@ -1088,12 +1087,4 @@ export class SettingsManager {
1088
1087
  setEditMode(mode: "standard" | "hashline"): void {
1089
1088
  this.setGlobalSetting("editMode", mode);
1090
1089
  }
1091
-
1092
- getTimestampFormat(): "date-time-iso" | "date-time-us" {
1093
- return this.settings.timestampFormat ?? "date-time-iso";
1094
- }
1095
-
1096
- setTimestampFormat(format: "date-time-iso" | "date-time-us"): void {
1097
- this.setGlobalSetting("timestampFormat", format);
1098
- }
1099
1090
  }
@@ -391,25 +391,6 @@ export async function main(args: string[]) {
391
391
  const authStorage = AuthStorage.create();
392
392
  const modelRegistry = new ModelRegistry(authStorage, getModelsPath());
393
393
 
394
- // Offline mode validation / auto-detection
395
- if (offlineMode) {
396
- // --offline flag: validate all models are local
397
- if (!modelRegistry.isAllLocalChain()) {
398
- const remoteModel = modelRegistry.getAll().find((m) => !ModelRegistry.isLocalModel(m));
399
- if (remoteModel) {
400
- console.error(
401
- `Error: --offline requires all configured models to be local. Found remote model: ${remoteModel.name} (${remoteModel.baseUrl || "cloud API"})`,
402
- );
403
- process.exit(1);
404
- }
405
- }
406
- } else if (modelRegistry.isAllLocalChain() && modelRegistry.getAll().length > 0) {
407
- // Auto-detect: all models are local, enable offline mode
408
- process.env.PI_OFFLINE = "1";
409
- process.env.PI_SKIP_VERSION_CHECK = "1";
410
- console.log("[gsd] All configured models are local \u2014 enabling offline mode automatically.");
411
- }
412
-
413
394
  const resourceLoader = new DefaultResourceLoader({
414
395
  cwd,
415
396
  agentDir,
@@ -1,7 +1,6 @@
1
1
  import type { AssistantMessage } from "@gsd/pi-ai";
2
2
  import { Container, Markdown, type MarkdownTheme, Spacer, Text } from "@gsd/pi-tui";
3
3
  import { getMarkdownTheme, theme } from "../theme/theme.js";
4
- import { formatTimestamp, type TimestampFormat } from "./timestamp.js";
5
4
 
6
5
  /**
7
6
  * Component that renders a complete assistant message
@@ -11,19 +10,16 @@ export class AssistantMessageComponent extends Container {
11
10
  private hideThinkingBlock: boolean;
12
11
  private markdownTheme: MarkdownTheme;
13
12
  private lastMessage?: AssistantMessage;
14
- private timestampFormat: TimestampFormat;
15
13
 
16
14
  constructor(
17
15
  message?: AssistantMessage,
18
16
  hideThinkingBlock = false,
19
17
  markdownTheme: MarkdownTheme = getMarkdownTheme(),
20
- timestampFormat: TimestampFormat = "date-time-iso",
21
18
  ) {
22
19
  super();
23
20
 
24
21
  this.hideThinkingBlock = hideThinkingBlock;
25
22
  this.markdownTheme = markdownTheme;
26
- this.timestampFormat = timestampFormat;
27
23
 
28
24
  // Container for text/thinking content
29
25
  this.contentContainer = new Container();
@@ -115,11 +111,5 @@ export class AssistantMessageComponent extends Container {
115
111
  this.contentContainer.addChild(new Text(theme.fg("error", `Error: ${errorMsg}`), 1, 0));
116
112
  }
117
113
  }
118
-
119
- // Show timestamp when the message is complete (has a stop reason)
120
- if (message.stopReason && message.timestamp) {
121
- const timeStr = formatTimestamp(message.timestamp, this.timestampFormat);
122
- this.contentContainer.addChild(new Text(theme.fg("dim", timeStr), 1, 0));
123
- }
124
114
  }
125
115
  }
@@ -45,7 +45,6 @@ export interface SettingsConfig {
45
45
  respectGitignoreInPicker: boolean;
46
46
  quietStartup: boolean;
47
47
  clearOnShrink: boolean;
48
- timestampFormat: "date-time-iso" | "date-time-us";
49
48
  }
50
49
 
51
50
  export interface SettingsCallbacks {
@@ -70,7 +69,6 @@ export interface SettingsCallbacks {
70
69
  onRespectGitignoreInPickerChange: (enabled: boolean) => void;
71
70
  onQuietStartupChange: (enabled: boolean) => void;
72
71
  onClearOnShrinkChange: (enabled: boolean) => void;
73
- onTimestampFormatChange: (format: "date-time-iso" | "date-time-us") => void;
74
72
  onCancel: () => void;
75
73
  }
76
74
 
@@ -357,16 +355,6 @@ export class SettingsSelectorComponent extends Container {
357
355
  values: ["true", "false"],
358
356
  });
359
357
 
360
- // Timestamp format (insert after respect-gitignore-in-picker)
361
- const gitignoreIndex = items.findIndex((item) => item.id === "respect-gitignore-in-picker");
362
- items.splice(gitignoreIndex + 1, 0, {
363
- id: "timestamp-format",
364
- label: "Timestamp format",
365
- description: "Date/time format for message timestamps",
366
- currentValue: config.timestampFormat,
367
- values: ["date-time-iso", "date-time-us"],
368
- });
369
-
370
358
  // Add borders
371
359
  this.addChild(new DynamicBorder());
372
360
 
@@ -432,9 +420,6 @@ export class SettingsSelectorComponent extends Container {
432
420
  case "respect-gitignore-in-picker":
433
421
  callbacks.onRespectGitignoreInPickerChange(newValue === "true");
434
422
  break;
435
- case "timestamp-format":
436
- callbacks.onTimestampFormatChange(newValue as "date-time-iso" | "date-time-us");
437
- break;
438
423
  }
439
424
  },
440
425
  callbacks.onCancel,
@@ -895,9 +895,7 @@ export class ToolExecutionComponent extends Container {
895
895
  // Server-side Anthropic web search
896
896
  text = theme.fg("toolTitle", theme.bold("web search"));
897
897
 
898
- if (process.env.PI_OFFLINE === "1") {
899
- text += "\n\n" + theme.fg("muted", "\u{1F50C} Offline \u{2014} web search unavailable");
900
- } else if (this.result) {
898
+ if (this.result) {
901
899
  const output = this.getTextOutput().trim();
902
900
  if (output) {
903
901
  const lines = output.split("\n");
@@ -1,21 +1,15 @@
1
- import { Container, Markdown, type MarkdownTheme, Spacer, Text } from "@gsd/pi-tui";
1
+ import { Container, Markdown, type MarkdownTheme, Spacer } from "@gsd/pi-tui";
2
2
  import { getMarkdownTheme, theme } from "../theme/theme.js";
3
- import { formatTimestamp, type TimestampFormat } from "./timestamp.js";
4
3
 
5
4
  const OSC133_ZONE_START = "\x1b]133;A\x07";
6
5
  const OSC133_ZONE_END = "\x1b]133;B\x07";
7
6
 
8
7
  /**
9
- * Component that renders a user message with a right-aligned timestamp.
8
+ * Component that renders a user message
10
9
  */
11
10
  export class UserMessageComponent extends Container {
12
- private timestamp: number | undefined;
13
- private timestampFormat: TimestampFormat;
14
-
15
- constructor(text: string, markdownTheme: MarkdownTheme = getMarkdownTheme(), timestamp?: number, timestampFormat: TimestampFormat = "date-time-iso") {
11
+ constructor(text: string, markdownTheme: MarkdownTheme = getMarkdownTheme()) {
16
12
  super();
17
- this.timestamp = timestamp;
18
- this.timestampFormat = timestampFormat;
19
13
  this.addChild(new Spacer(1));
20
14
  this.addChild(
21
15
  new Markdown(text, 1, 1, markdownTheme, {
@@ -31,15 +25,6 @@ export class UserMessageComponent extends Container {
31
25
  return lines;
32
26
  }
33
27
 
34
- // Insert right-aligned timestamp above the message content
35
- if (this.timestamp) {
36
- const timeStr = formatTimestamp(this.timestamp, this.timestampFormat);
37
- const label = theme.fg("dim", timeStr);
38
- const padding = Math.max(0, width - timeStr.length - 1);
39
- const timestampLine = " ".repeat(padding) + label;
40
- lines.splice(0, 0, timestampLine);
41
- }
42
-
43
28
  lines[0] = OSC133_ZONE_START + lines[0];
44
29
  lines[lines.length - 1] = lines[lines.length - 1] + OSC133_ZONE_END;
45
30
  return lines;
@@ -100,7 +100,6 @@ export async function handleAgentEvent(host: InteractiveModeStateHost & {
100
100
  undefined,
101
101
  host.hideThinkingBlock,
102
102
  host.getMarkdownThemeWithSettings(),
103
- host.settingsManager.getTimestampFormat(),
104
103
  );
105
104
  host.streamingMessage = event.message;
106
105
  host.chatContainer.addChild(host.streamingComponent);
@@ -145,21 +144,13 @@ export async function handleAgentEvent(host: InteractiveModeStateHost & {
145
144
  } else if (content.type === "webSearchResult") {
146
145
  const component = host.pendingTools.get(content.toolUseId);
147
146
  if (component) {
148
- if (process.env.PI_OFFLINE === "1") {
149
- component.updateResult({
150
- content: [{ type: "text", text: "Web search disabled (offline mode)" }],
151
- isError: false,
152
- });
153
- host.pendingTools.delete(content.toolUseId);
154
- } else {
155
- const searchContent = content.content;
156
- const isError = searchContent && typeof searchContent === "object" && "type" in (searchContent as any) && (searchContent as any).type === "web_search_tool_result_error";
157
- component.updateResult({
158
- content: [{ type: "text", text: host.formatWebSearchResult(searchContent) }],
159
- isError: !!isError,
160
- });
161
- host.pendingTools.delete(content.toolUseId);
162
- }
147
+ const searchContent = content.content;
148
+ const isError = searchContent && typeof searchContent === "object" && "type" in (searchContent as any) && (searchContent as any).type === "web_search_tool_result_error";
149
+ component.updateResult({
150
+ content: [{ type: "text", text: host.formatWebSearchResult(searchContent) }],
151
+ isError: !!isError,
152
+ });
153
+ host.pendingTools.delete(content.toolUseId);
163
154
  }
164
155
  }
165
156
  }
@@ -2099,13 +2099,11 @@ export class InteractiveMode {
2099
2099
  const userComponent = new UserMessageComponent(
2100
2100
  skillBlock.userMessage,
2101
2101
  this.getMarkdownThemeWithSettings(),
2102
- message.timestamp,
2103
- this.settingsManager.getTimestampFormat(),
2104
2102
  );
2105
2103
  this.chatContainer.addChild(userComponent);
2106
2104
  }
2107
2105
  } else {
2108
- const userComponent = new UserMessageComponent(textContent, this.getMarkdownThemeWithSettings(), message.timestamp, this.settingsManager.getTimestampFormat());
2106
+ const userComponent = new UserMessageComponent(textContent, this.getMarkdownThemeWithSettings());
2109
2107
  this.chatContainer.addChild(userComponent);
2110
2108
  }
2111
2109
  if (options?.populateHistory) {
@@ -2119,7 +2117,6 @@ export class InteractiveMode {
2119
2117
  message,
2120
2118
  this.hideThinkingBlock,
2121
2119
  this.getMarkdownThemeWithSettings(),
2122
- this.settingsManager.getTimestampFormat(),
2123
2120
  );
2124
2121
  this.chatContainer.addChild(assistantComponent);
2125
2122
  break;
@@ -2798,7 +2795,6 @@ export class InteractiveMode {
2798
2795
  respectGitignoreInPicker: this.settingsManager.getRespectGitignoreInPicker(),
2799
2796
  quietStartup: this.settingsManager.getQuietStartup(),
2800
2797
  clearOnShrink: this.settingsManager.getClearOnShrink(),
2801
- timestampFormat: this.settingsManager.getTimestampFormat(),
2802
2798
  },
2803
2799
  {
2804
2800
  onAutoCompactChange: (enabled) => {
@@ -2902,9 +2898,6 @@ export class InteractiveMode {
2902
2898
  this.settingsManager.setRespectGitignoreInPicker(enabled);
2903
2899
  this.autocompleteProvider?.setRespectGitignore(enabled);
2904
2900
  },
2905
- onTimestampFormatChange: (format) => {
2906
- this.settingsManager.setTimestampFormat(format);
2907
- },
2908
2901
  onCancel: () => {
2909
2902
  done();
2910
2903
  this.ui.requestRender();
@@ -18,9 +18,6 @@ export const INFRA_ERROR_CODES: ReadonlySet<string> = new Set([
18
18
  "EDQUOT", // disk quota exceeded
19
19
  "EMFILE", // too many open files (process)
20
20
  "ENFILE", // too many open files (system)
21
- "ECONNREFUSED", // connection refused (offline / local server down)
22
- "ENOTFOUND", // DNS lookup failed (offline / no network)
23
- "ENETUNREACH", // network unreachable (offline / no route)
24
21
  ]);
25
22
 
26
23
  /**
@@ -27,9 +27,7 @@ import { debugLog } from "../debug-logger.js";
27
27
  import { gsdRoot } from "../paths.js";
28
28
  import { atomicWriteSync } from "../atomic-write.js";
29
29
  import { PROJECT_FILES } from "../detection.js";
30
- import { MergeConflictError } from "../git-service.js";
31
30
  import { join } from "node:path";
32
- import { existsSync, cpSync } from "node:fs";
33
31
 
34
32
  // ─── generateMilestoneReport ──────────────────────────────────────────────────
35
33
 
@@ -235,23 +233,26 @@ export async function runPreDispatch(
235
233
  loopState.stuckRecoveryAttempts = 0;
236
234
 
237
235
  // Worktree lifecycle on milestone transition — merge current, enter next
238
- try {
239
- deps.resolver.mergeAndExit(s.currentMilestoneId!, ctx.ui);
240
- } catch (mergeErr) {
241
- if (mergeErr instanceof MergeConflictError) {
242
- // Real code conflicts — stop the loop instead of retrying forever (#2330)
243
- ctx.ui.notify(
244
- `Merge conflict: ${mergeErr.conflictedFiles.join(", ")}. Resolve conflicts manually and run /gsd auto to resume.`,
245
- "error",
236
+ deps.resolver.mergeAndExit(s.currentMilestoneId!, ctx.ui);
237
+
238
+ // Opt-in: create draft PR on milestone completion
239
+ if (prefs?.git?.auto_pr) {
240
+ try {
241
+ const { createDraftPR } = await import("../git-service.js");
242
+ const prUrl = createDraftPR(
243
+ s.basePath,
244
+ s.currentMilestoneId!,
245
+ `[GSD] ${s.currentMilestoneId} complete`,
246
+ `Milestone ${s.currentMilestoneId} completed by GSD auto-mode.\n\nSee .gsd/${s.currentMilestoneId}/ for details.`,
246
247
  );
247
- await deps.stopAuto(ctx, pi, `Merge conflict on milestone ${s.currentMilestoneId}`);
248
- return { action: "break", reason: "merge-conflict" };
248
+ if (prUrl) {
249
+ ctx.ui.notify(`Draft PR created: ${prUrl}`, "info");
250
+ }
251
+ } catch {
252
+ // Non-fatal — PR creation is best-effort
249
253
  }
250
- // Non-conflict errors — log and continue
251
254
  }
252
255
 
253
- // PR creation (auto_pr) is handled inside mergeMilestoneToMain (#2302)
254
-
255
256
  deps.invalidateAllCaches();
256
257
 
257
258
  state = await deps.deriveState(s.basePath);
@@ -278,17 +279,9 @@ export async function runPreDispatch(
278
279
  // Reset completed-units tracking for the new milestone — stale entries
279
280
  // from the previous milestone cause the dispatch loop to skip units
280
281
  // that haven't actually been completed in the new milestone's context.
281
- // Archive the old completed-units.json instead of wiping it (#2313).
282
282
  s.completedUnits = [];
283
283
  try {
284
284
  const completedKeysPath = join(gsdRoot(s.basePath), "completed-units.json");
285
- if (existsSync(completedKeysPath) && s.currentMilestoneId) {
286
- const archivePath = join(
287
- gsdRoot(s.basePath),
288
- `completed-units-${s.currentMilestoneId}.json`,
289
- );
290
- cpSync(completedKeysPath, archivePath);
291
- }
292
285
  atomicWriteSync(completedKeysPath, JSON.stringify([], null, 2));
293
286
  } catch { /* non-fatal */ }
294
287
 
@@ -329,20 +322,25 @@ export async function runPreDispatch(
329
322
  if (incomplete.length === 0 && state.registry.length > 0) {
330
323
  // All milestones complete — merge milestone branch before stopping
331
324
  if (s.currentMilestoneId) {
332
- try {
333
- deps.resolver.mergeAndExit(s.currentMilestoneId, ctx.ui);
334
- } catch (mergeErr) {
335
- if (mergeErr instanceof MergeConflictError) {
336
- ctx.ui.notify(
337
- `Merge conflict: ${mergeErr.conflictedFiles.join(", ")}. Resolve conflicts manually and run /gsd auto to resume.`,
338
- "error",
325
+ deps.resolver.mergeAndExit(s.currentMilestoneId, ctx.ui);
326
+
327
+ // Opt-in: create draft PR on milestone completion
328
+ if (prefs?.git?.auto_pr) {
329
+ try {
330
+ const { createDraftPR } = await import("../git-service.js");
331
+ const prUrl = createDraftPR(
332
+ s.basePath,
333
+ s.currentMilestoneId,
334
+ `[GSD] ${s.currentMilestoneId} complete`,
335
+ `Milestone ${s.currentMilestoneId} completed by GSD auto-mode.\n\nSee .gsd/${s.currentMilestoneId}/ for details.`,
339
336
  );
340
- await deps.stopAuto(ctx, pi, `Merge conflict on milestone ${s.currentMilestoneId}`);
341
- return { action: "break", reason: "merge-conflict" };
337
+ if (prUrl) {
338
+ ctx.ui.notify(`Draft PR created: ${prUrl}`, "info");
339
+ }
340
+ } catch {
341
+ // Non-fatal — PR creation is best-effort
342
342
  }
343
343
  }
344
-
345
- // PR creation (auto_pr) is handled inside mergeMilestoneToMain (#2302)
346
344
  }
347
345
  deps.sendDesktopNotification(
348
346
  "GSD",
@@ -424,20 +422,25 @@ export async function runPreDispatch(
424
422
  if (state.phase === "complete") {
425
423
  // Milestone merge on complete (before closeout so branch state is clean)
426
424
  if (s.currentMilestoneId) {
427
- try {
428
- deps.resolver.mergeAndExit(s.currentMilestoneId, ctx.ui);
429
- } catch (mergeErr) {
430
- if (mergeErr instanceof MergeConflictError) {
431
- ctx.ui.notify(
432
- `Merge conflict: ${mergeErr.conflictedFiles.join(", ")}. Resolve conflicts manually and run /gsd auto to resume.`,
433
- "error",
425
+ deps.resolver.mergeAndExit(s.currentMilestoneId, ctx.ui);
426
+
427
+ // Opt-in: create draft PR on milestone completion
428
+ if (prefs?.git?.auto_pr) {
429
+ try {
430
+ const { createDraftPR } = await import("../git-service.js");
431
+ const prUrl = createDraftPR(
432
+ s.basePath,
433
+ s.currentMilestoneId,
434
+ `[GSD] ${s.currentMilestoneId} complete`,
435
+ `Milestone ${s.currentMilestoneId} completed by GSD auto-mode.\n\nSee .gsd/${s.currentMilestoneId}/ for details.`,
434
436
  );
435
- await deps.stopAuto(ctx, pi, `Merge conflict on milestone ${s.currentMilestoneId}`);
436
- return { action: "break", reason: "merge-conflict" };
437
+ if (prUrl) {
438
+ ctx.ui.notify(`Draft PR created: ${prUrl}`, "info");
439
+ }
440
+ } catch {
441
+ // Non-fatal — PR creation is best-effort
437
442
  }
438
443
  }
439
-
440
- // PR creation (auto_pr) is handled inside mergeMilestoneToMain (#2302)
441
444
  }
442
445
  deps.sendDesktopNotification(
443
446
  "GSD",
@@ -1307,12 +1307,6 @@ export async function buildCompleteMilestonePrompt(
1307
1307
  roadmapPath: roadmapRel,
1308
1308
  inlinedContext,
1309
1309
  milestoneSummaryPath,
1310
- skillActivation: buildSkillActivationBlock({
1311
- base,
1312
- milestoneId: mid,
1313
- milestoneTitle: midTitle,
1314
- extraContext: [inlinedContext],
1315
- }),
1316
1310
  });
1317
1311
  }
1318
1312
 
@@ -1396,12 +1390,6 @@ export async function buildValidateMilestonePrompt(
1396
1390
  inlinedContext,
1397
1391
  validationPath: validationOutputPath,
1398
1392
  remediationRound: String(remediationRound),
1399
- skillActivation: buildSkillActivationBlock({
1400
- base,
1401
- milestoneId: mid,
1402
- milestoneTitle: midTitle,
1403
- extraContext: [inlinedContext],
1404
- }),
1405
1393
  });
1406
1394
  }
1407
1395
 
@@ -1512,12 +1500,6 @@ export async function buildRunUatPrompt(
1512
1500
  uatResultPath,
1513
1501
  uatType,
1514
1502
  inlinedContext,
1515
- skillActivation: buildSkillActivationBlock({
1516
- base,
1517
- milestoneId: mid,
1518
- sliceId,
1519
- extraContext: [inlinedContext],
1520
- }),
1521
1503
  });
1522
1504
  }
1523
1505
 
@@ -1570,16 +1552,11 @@ export async function buildReassessRoadmapPrompt(
1570
1552
  milestoneTitle: midTitle,
1571
1553
  completedSliceId,
1572
1554
  roadmapPath: roadmapRel,
1555
+ completedSliceSummaryPath: summaryRel,
1573
1556
  assessmentPath,
1574
1557
  inlinedContext,
1575
1558
  deferredCaptures,
1576
1559
  commitInstruction: reassessCommitInstruction,
1577
- skillActivation: buildSkillActivationBlock({
1578
- base,
1579
- milestoneId: mid,
1580
- milestoneTitle: midTitle,
1581
- extraContext: [inlinedContext, deferredCaptures],
1582
- }),
1583
1560
  });
1584
1561
  }
1585
1562
 
@@ -8,7 +8,6 @@
8
8
 
9
9
  import type { ExtensionAPI, ExtensionContext } from "@gsd/pi-coding-agent";
10
10
  import { readUnitRuntimeRecord, writeUnitRuntimeRecord } from "./unit-runtime.js";
11
- import { isDbAvailable, getMilestoneSlices, getSliceTasks } from "./gsd-db.js";
12
11
  import { resolveAutoSupervisorConfig } from "./preferences.js";
13
12
  import type { GSDPreferences } from "./preferences.js";
14
13
  import { computeBudgets, resolveExecutorContextWindow } from "./context-budget.js";
@@ -33,8 +32,6 @@ export interface SupervisionContext {
33
32
  buildSnapshotOpts: () => CloseoutOptions & Record<string, unknown>;
34
33
  buildRecoveryContext: () => RecoveryContext;
35
34
  pauseAuto: (ctx?: ExtensionContext, pi?: ExtensionAPI) => Promise<void>;
36
- /** Optional task estimate string (e.g. "30m", "2h") for timeout scaling (#2243). */
37
- taskEstimate?: string;
38
35
  }
39
36
 
40
37
  /**
@@ -44,71 +41,13 @@ export interface SupervisionContext {
44
41
  * 3. Hard timeout (pause + recovery)
45
42
  * 4. Context-pressure monitor (continue-here)
46
43
  */
47
-
48
- /**
49
- * Parse a task estimate string (e.g. "30m", "2h", "1h30m") into minutes.
50
- * Returns null if the string cannot be parsed.
51
- */
52
- export function parseEstimateMinutes(estimate: string): number | null {
53
- if (!estimate || typeof estimate !== "string") return null;
54
- const trimmed = estimate.trim();
55
- if (!trimmed) return null;
56
-
57
- let totalMinutes = 0;
58
- let matched = false;
59
-
60
- // Match hours component
61
- const hoursMatch = trimmed.match(/(\d+)\s*h/i);
62
- if (hoursMatch) {
63
- totalMinutes += Number(hoursMatch[1]) * 60;
64
- matched = true;
65
- }
66
-
67
- // Match minutes component
68
- const minutesMatch = trimmed.match(/(\d+)\s*m/i);
69
- if (minutesMatch) {
70
- totalMinutes += Number(minutesMatch[1]);
71
- matched = true;
72
- }
73
-
74
- return matched ? totalMinutes : null;
75
- }
76
-
77
44
  export function startUnitSupervision(sctx: SupervisionContext): void {
78
45
  const { s, ctx, pi, unitType, unitId, prefs, buildSnapshotOpts, buildRecoveryContext, pauseAuto } = sctx;
79
46
 
80
47
  const supervisor = resolveAutoSupervisorConfig();
81
-
82
- // Scale timeouts based on task estimate annotations (#2243).
83
- // If the task has an est: annotation, use it to extend the hard and soft timeouts
84
- // so longer tasks don't get prematurely timed out.
85
- let taskEstimate = sctx.taskEstimate;
86
- if (!taskEstimate && unitType === "task" && isDbAvailable()) {
87
- // Look up the task estimate from the DB (#2243).
88
- try {
89
- if (s.currentMilestoneId) {
90
- const slices = getMilestoneSlices(s.currentMilestoneId);
91
- for (const slice of slices) {
92
- const tasks = getSliceTasks(s.currentMilestoneId, slice.id);
93
- const task = tasks.find(t => t.id === unitId);
94
- if (task?.estimate) {
95
- taskEstimate = task.estimate;
96
- break;
97
- }
98
- }
99
- }
100
- } catch {
101
- // Non-fatal — fall through with no estimate
102
- }
103
- }
104
- const estimateMinutes = taskEstimate ? parseEstimateMinutes(taskEstimate) : null;
105
- const timeoutScale = estimateMinutes && estimateMinutes > 0
106
- ? Math.max(1, estimateMinutes / 10) // 10min task = 1x, 30min = 3x, 2h = 12x
107
- : 1;
108
-
109
- const softTimeoutMs = (supervisor.soft_timeout_minutes ?? 0) * 60 * 1000 * timeoutScale;
110
- const idleTimeoutMs = (supervisor.idle_timeout_minutes ?? 0) * 60 * 1000; // idle not scaled — idle is idle
111
- const hardTimeoutMs = (supervisor.hard_timeout_minutes ?? 0) * 60 * 1000 * timeoutScale;
48
+ const softTimeoutMs = (supervisor.soft_timeout_minutes ?? 0) * 60 * 1000;
49
+ const idleTimeoutMs = (supervisor.idle_timeout_minutes ?? 0) * 60 * 1000;
50
+ const hardTimeoutMs = (supervisor.hard_timeout_minutes ?? 0) * 60 * 1000;
112
51
 
113
52
  // ── 1. Soft timeout warning ──
114
53
  s.wrapupWarningHandle = setTimeout(() => {
@@ -93,11 +93,6 @@ export function syncStateToProjectRoot(
93
93
  { force: true },
94
94
  );
95
95
 
96
- // 3. metrics.json — session cost/token tracking (#2313).
97
- // Without this, metrics accumulated in the worktree are invisible from the
98
- // project root and never appear in the dashboard or skill-health reports.
99
- safeCopy(join(wtGsd, "metrics.json"), join(prGsd, "metrics.json"), { force: true });
100
-
101
96
  // 4. Runtime records — unit dispatch state used by selfHealRuntimeRecords().
102
97
  // Without this, a crash during a unit leaves the runtime record only in the
103
98
  // worktree. If the next session resolves basePath before worktree re-entry,