gsd-pi 2.16.0 → 2.18.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +39 -0
- package/dist/onboarding.js +2 -2
- package/dist/remote-questions-config.d.ts +10 -0
- package/dist/remote-questions-config.js +36 -0
- package/dist/resources/extensions/gsd/activity-log.ts +37 -7
- package/dist/resources/extensions/gsd/auto-dashboard.ts +4 -0
- package/dist/resources/extensions/gsd/auto-dispatch.ts +9 -3
- package/dist/resources/extensions/gsd/auto-prompts.ts +91 -42
- package/dist/resources/extensions/gsd/auto-recovery.ts +7 -2
- package/dist/resources/extensions/gsd/auto-worktree.ts +33 -4
- package/dist/resources/extensions/gsd/auto.ts +177 -25
- package/dist/resources/extensions/gsd/commands.ts +264 -23
- package/dist/resources/extensions/gsd/complexity.ts +236 -0
- package/dist/resources/extensions/gsd/dispatch-guard.ts +7 -19
- package/dist/resources/extensions/gsd/docs/preferences-reference.md +202 -2
- package/dist/resources/extensions/gsd/files.ts +129 -3
- package/dist/resources/extensions/gsd/git-service.ts +19 -8
- package/dist/resources/extensions/gsd/gitignore.ts +41 -2
- package/dist/resources/extensions/gsd/guided-flow.ts +247 -10
- package/dist/resources/extensions/gsd/index.ts +47 -3
- package/dist/resources/extensions/gsd/metrics.ts +44 -0
- package/dist/resources/extensions/gsd/native-git-bridge.ts +5 -0
- package/dist/resources/extensions/gsd/native-parser-bridge.ts +5 -0
- package/dist/resources/extensions/gsd/paths.ts +9 -0
- package/dist/resources/extensions/gsd/preferences.ts +181 -2
- package/dist/resources/extensions/gsd/prompts/execute-task.md +6 -5
- package/dist/resources/extensions/gsd/prompts/system.md +2 -0
- package/dist/resources/extensions/gsd/queue-order.ts +231 -0
- package/dist/resources/extensions/gsd/queue-reorder-ui.ts +263 -0
- package/dist/resources/extensions/gsd/routing-history.ts +290 -0
- package/dist/resources/extensions/gsd/state.ts +15 -3
- package/dist/resources/extensions/gsd/templates/knowledge.md +19 -0
- package/dist/resources/extensions/gsd/templates/preferences.md +14 -0
- package/dist/resources/extensions/gsd/tests/auto-recovery.test.ts +50 -0
- package/dist/resources/extensions/gsd/tests/auto-worktree.test.ts +20 -0
- package/dist/resources/extensions/gsd/tests/budget-prediction.test.ts +220 -0
- package/dist/resources/extensions/gsd/tests/complexity-routing.test.ts +294 -0
- package/dist/resources/extensions/gsd/tests/context-compression.test.ts +180 -0
- package/dist/resources/extensions/gsd/tests/derive-state-deps.test.ts +99 -0
- package/dist/resources/extensions/gsd/tests/git-service.test.ts +132 -0
- package/dist/resources/extensions/gsd/tests/in-flight-tool-tracking.test.ts +79 -0
- package/dist/resources/extensions/gsd/tests/knowledge.test.ts +161 -0
- package/dist/resources/extensions/gsd/tests/memory-leak-guards.test.ts +87 -0
- package/dist/resources/extensions/gsd/tests/preferences-git.test.ts +28 -0
- package/dist/resources/extensions/gsd/tests/preferences-wizard-fields.test.ts +168 -0
- package/dist/resources/extensions/gsd/tests/queue-order.test.ts +204 -0
- package/dist/resources/extensions/gsd/tests/queue-reorder-e2e.test.ts +281 -0
- package/dist/resources/extensions/gsd/tests/routing-history.test.ts +87 -0
- package/dist/resources/extensions/gsd/tests/stale-worktree-cwd.test.ts +139 -0
- package/dist/resources/extensions/gsd/tests/stop-auto-remote.test.ts +130 -0
- package/dist/resources/extensions/gsd/tests/token-profile.test.ts +263 -0
- package/dist/resources/extensions/gsd/types.ts +28 -0
- package/dist/resources/extensions/gsd/worktree-manager.ts +8 -5
- package/dist/resources/extensions/gsd/worktree.ts +24 -2
- package/dist/resources/extensions/shared/next-action-ui.ts +16 -1
- package/package.json +1 -1
- package/packages/pi-ai/dist/models.generated.d.ts +493 -13
- package/packages/pi-ai/dist/models.generated.d.ts.map +1 -1
- package/packages/pi-ai/dist/models.generated.js +422 -62
- package/packages/pi-ai/dist/models.generated.js.map +1 -1
- package/packages/pi-ai/dist/providers/google-shared.d.ts +12 -0
- package/packages/pi-ai/dist/providers/google-shared.d.ts.map +1 -1
- package/packages/pi-ai/dist/providers/google-shared.js +9 -22
- package/packages/pi-ai/dist/providers/google-shared.js.map +1 -1
- package/packages/pi-ai/dist/providers/google-shared.test.d.ts +2 -0
- package/packages/pi-ai/dist/providers/google-shared.test.d.ts.map +1 -0
- package/packages/pi-ai/dist/providers/google-shared.test.js +125 -0
- package/packages/pi-ai/dist/providers/google-shared.test.js.map +1 -0
- package/packages/pi-ai/src/models.generated.ts +422 -62
- package/packages/pi-ai/src/providers/google-shared.test.ts +137 -0
- package/packages/pi-ai/src/providers/google-shared.ts +10 -19
- package/packages/pi-coding-agent/dist/cli/args.d.ts +5 -0
- package/packages/pi-coding-agent/dist/cli/args.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/cli/args.js +21 -0
- package/packages/pi-coding-agent/dist/cli/args.js.map +1 -1
- package/packages/pi-coding-agent/dist/cli/list-models.d.ts +14 -3
- package/packages/pi-coding-agent/dist/cli/list-models.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/cli/list-models.js +52 -17
- package/packages/pi-coding-agent/dist/cli/list-models.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/discovery-cache.d.ts +27 -0
- package/packages/pi-coding-agent/dist/core/discovery-cache.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/discovery-cache.js +79 -0
- package/packages/pi-coding-agent/dist/core/discovery-cache.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/discovery-cache.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/discovery-cache.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/discovery-cache.test.js +140 -0
- package/packages/pi-coding-agent/dist/core/discovery-cache.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/model-discovery.d.ts +35 -0
- package/packages/pi-coding-agent/dist/core/model-discovery.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/model-discovery.js +162 -0
- package/packages/pi-coding-agent/dist/core/model-discovery.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/model-discovery.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/model-discovery.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/model-discovery.test.js +100 -0
- package/packages/pi-coding-agent/dist/core/model-discovery.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/model-registry-discovery.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/model-registry-discovery.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/model-registry-discovery.test.js +113 -0
- package/packages/pi-coding-agent/dist/core/model-registry-discovery.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/model-registry.d.ts +26 -0
- package/packages/pi-coding-agent/dist/core/model-registry.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/model-registry.js +98 -0
- package/packages/pi-coding-agent/dist/core/model-registry.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/models-json-writer.d.ts +62 -0
- package/packages/pi-coding-agent/dist/core/models-json-writer.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/models-json-writer.js +145 -0
- package/packages/pi-coding-agent/dist/core/models-json-writer.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/models-json-writer.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/models-json-writer.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/models-json-writer.test.js +118 -0
- package/packages/pi-coding-agent/dist/core/models-json-writer.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/settings-manager.d.ts +9 -0
- package/packages/pi-coding-agent/dist/core/settings-manager.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/settings-manager.js +11 -0
- package/packages/pi-coding-agent/dist/core/settings-manager.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/slash-commands.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/slash-commands.js +1 -0
- package/packages/pi-coding-agent/dist/core/slash-commands.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/tools/edit-diff.d.ts +7 -7
- package/packages/pi-coding-agent/dist/core/tools/edit-diff.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/tools/edit-diff.js +209 -13
- package/packages/pi-coding-agent/dist/core/tools/edit-diff.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/tools/edit-diff.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/tools/edit-diff.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/tools/edit-diff.test.js +67 -0
- package/packages/pi-coding-agent/dist/core/tools/edit-diff.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/index.d.ts +5 -1
- package/packages/pi-coding-agent/dist/index.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/index.js +4 -1
- package/packages/pi-coding-agent/dist/index.js.map +1 -1
- package/packages/pi-coding-agent/dist/main.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/main.js +17 -2
- package/packages/pi-coding-agent/dist/main.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/index.d.ts +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/index.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/index.js +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/index.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/model-selector.js +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/model-selector.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/provider-manager.d.ts +25 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/provider-manager.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/provider-manager.js +121 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/provider-manager.js.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +32 -0
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/theme/theme.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/theme/theme.js +10 -0
- package/packages/pi-coding-agent/dist/modes/interactive/theme/theme.js.map +1 -1
- package/packages/pi-coding-agent/src/cli/args.ts +21 -0
- package/packages/pi-coding-agent/src/cli/list-models.ts +70 -17
- package/packages/pi-coding-agent/src/core/discovery-cache.test.ts +170 -0
- package/packages/pi-coding-agent/src/core/discovery-cache.ts +97 -0
- package/packages/pi-coding-agent/src/core/model-discovery.test.ts +125 -0
- package/packages/pi-coding-agent/src/core/model-discovery.ts +231 -0
- package/packages/pi-coding-agent/src/core/model-registry-discovery.test.ts +135 -0
- package/packages/pi-coding-agent/src/core/model-registry.ts +107 -0
- package/packages/pi-coding-agent/src/core/models-json-writer.test.ts +145 -0
- package/packages/pi-coding-agent/src/core/models-json-writer.ts +188 -0
- package/packages/pi-coding-agent/src/core/settings-manager.ts +21 -0
- package/packages/pi-coding-agent/src/core/slash-commands.ts +1 -0
- package/packages/pi-coding-agent/src/core/tools/edit-diff.test.ts +85 -0
- package/packages/pi-coding-agent/src/core/tools/edit-diff.ts +245 -17
- package/packages/pi-coding-agent/src/index.ts +5 -0
- package/packages/pi-coding-agent/src/main.ts +19 -2
- package/packages/pi-coding-agent/src/modes/interactive/components/index.ts +1 -0
- package/packages/pi-coding-agent/src/modes/interactive/components/model-selector.ts +1 -1
- package/packages/pi-coding-agent/src/modes/interactive/components/provider-manager.ts +163 -0
- package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +37 -0
- package/packages/pi-coding-agent/src/modes/interactive/theme/theme.ts +13 -0
- package/pkg/dist/modes/interactive/theme/theme.d.ts.map +1 -1
- package/pkg/dist/modes/interactive/theme/theme.js +10 -0
- package/pkg/dist/modes/interactive/theme/theme.js.map +1 -1
- package/src/resources/extensions/gsd/activity-log.ts +37 -7
- package/src/resources/extensions/gsd/auto-dashboard.ts +4 -0
- package/src/resources/extensions/gsd/auto-dispatch.ts +9 -3
- package/src/resources/extensions/gsd/auto-prompts.ts +91 -42
- package/src/resources/extensions/gsd/auto-recovery.ts +7 -2
- package/src/resources/extensions/gsd/auto-worktree.ts +33 -4
- package/src/resources/extensions/gsd/auto.ts +177 -25
- package/src/resources/extensions/gsd/commands.ts +264 -23
- package/src/resources/extensions/gsd/complexity.ts +236 -0
- package/src/resources/extensions/gsd/dispatch-guard.ts +7 -19
- package/src/resources/extensions/gsd/docs/preferences-reference.md +202 -2
- package/src/resources/extensions/gsd/files.ts +129 -3
- package/src/resources/extensions/gsd/git-service.ts +19 -8
- package/src/resources/extensions/gsd/gitignore.ts +41 -2
- package/src/resources/extensions/gsd/guided-flow.ts +247 -10
- package/src/resources/extensions/gsd/index.ts +47 -3
- package/src/resources/extensions/gsd/metrics.ts +44 -0
- package/src/resources/extensions/gsd/native-git-bridge.ts +5 -0
- package/src/resources/extensions/gsd/native-parser-bridge.ts +5 -0
- package/src/resources/extensions/gsd/paths.ts +9 -0
- package/src/resources/extensions/gsd/preferences.ts +181 -2
- package/src/resources/extensions/gsd/prompts/execute-task.md +6 -5
- package/src/resources/extensions/gsd/prompts/system.md +2 -0
- package/src/resources/extensions/gsd/queue-order.ts +231 -0
- package/src/resources/extensions/gsd/queue-reorder-ui.ts +263 -0
- package/src/resources/extensions/gsd/routing-history.ts +290 -0
- package/src/resources/extensions/gsd/state.ts +15 -3
- package/src/resources/extensions/gsd/templates/knowledge.md +19 -0
- package/src/resources/extensions/gsd/templates/preferences.md +14 -0
- package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +50 -0
- package/src/resources/extensions/gsd/tests/auto-worktree.test.ts +20 -0
- package/src/resources/extensions/gsd/tests/budget-prediction.test.ts +220 -0
- package/src/resources/extensions/gsd/tests/complexity-routing.test.ts +294 -0
- package/src/resources/extensions/gsd/tests/context-compression.test.ts +180 -0
- package/src/resources/extensions/gsd/tests/derive-state-deps.test.ts +99 -0
- package/src/resources/extensions/gsd/tests/git-service.test.ts +132 -0
- package/src/resources/extensions/gsd/tests/in-flight-tool-tracking.test.ts +79 -0
- package/src/resources/extensions/gsd/tests/knowledge.test.ts +161 -0
- package/src/resources/extensions/gsd/tests/memory-leak-guards.test.ts +87 -0
- package/src/resources/extensions/gsd/tests/preferences-git.test.ts +28 -0
- package/src/resources/extensions/gsd/tests/preferences-wizard-fields.test.ts +168 -0
- package/src/resources/extensions/gsd/tests/queue-order.test.ts +204 -0
- package/src/resources/extensions/gsd/tests/queue-reorder-e2e.test.ts +281 -0
- package/src/resources/extensions/gsd/tests/routing-history.test.ts +87 -0
- package/src/resources/extensions/gsd/tests/stale-worktree-cwd.test.ts +139 -0
- package/src/resources/extensions/gsd/tests/stop-auto-remote.test.ts +130 -0
- package/src/resources/extensions/gsd/tests/token-profile.test.ts +263 -0
- package/src/resources/extensions/gsd/types.ts +28 -0
- package/src/resources/extensions/gsd/worktree-manager.ts +8 -5
- package/src/resources/extensions/gsd/worktree.ts +24 -2
- package/src/resources/extensions/shared/next-action-ui.ts +16 -1
|
@@ -22,11 +22,12 @@ import {
|
|
|
22
22
|
} from "./paths.js";
|
|
23
23
|
import { randomInt } from "node:crypto";
|
|
24
24
|
import { join } from "node:path";
|
|
25
|
-
import { readFileSync, existsSync, mkdirSync, readdirSync, rmSync, unlinkSync } from "node:fs";
|
|
25
|
+
import { readFileSync, writeFileSync, existsSync, mkdirSync, readdirSync, rmSync, unlinkSync } from "node:fs";
|
|
26
26
|
import { nativeIsRepo, nativeInit, nativeAddPaths, nativeCommit } from "./native-git-bridge.js";
|
|
27
27
|
import { ensureGitignore, ensurePreferences, untrackRuntimeFiles } from "./gitignore.js";
|
|
28
28
|
import { loadEffectiveGSDPreferences } from "./preferences.js";
|
|
29
29
|
import { showConfirm } from "../shared/confirm-ui.js";
|
|
30
|
+
import { loadQueueOrder, sortByQueueOrder, saveQueueOrder } from "./queue-order.js";
|
|
30
31
|
|
|
31
32
|
// ─── Auto-start after discuss ─────────────────────────────────────────────────
|
|
32
33
|
|
|
@@ -203,13 +204,16 @@ function buildDiscussPrompt(nextId: string, preamble: string, _basePath: string)
|
|
|
203
204
|
export function findMilestoneIds(basePath: string): string[] {
|
|
204
205
|
const dir = milestonesDir(basePath);
|
|
205
206
|
try {
|
|
206
|
-
|
|
207
|
+
const ids = readdirSync(dir, { withFileTypes: true })
|
|
207
208
|
.filter((d) => d.isDirectory())
|
|
208
209
|
.map((d) => {
|
|
209
210
|
const match = d.name.match(/^(M\d+(?:-[a-z0-9]{6})?)/);
|
|
210
211
|
return match ? match[1] : d.name;
|
|
211
|
-
})
|
|
212
|
-
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
// Apply custom queue order if available, else fall back to numeric sort
|
|
215
|
+
const customOrder = loadQueueOrder(basePath);
|
|
216
|
+
return sortByQueueOrder(ids, customOrder);
|
|
213
217
|
} catch {
|
|
214
218
|
return [];
|
|
215
219
|
}
|
|
@@ -305,6 +309,235 @@ export async function showQueue(
|
|
|
305
309
|
return;
|
|
306
310
|
}
|
|
307
311
|
|
|
312
|
+
// ── Count pending milestones ────────────────────────────────────────
|
|
313
|
+
const pendingMilestones = state.registry.filter(
|
|
314
|
+
m => m.status === "pending" || m.status === "active",
|
|
315
|
+
);
|
|
316
|
+
const completeCount = state.registry.filter(m => m.status === "complete").length;
|
|
317
|
+
|
|
318
|
+
// ── If multiple pending milestones, show queue management hub ──────
|
|
319
|
+
if (pendingMilestones.length > 1) {
|
|
320
|
+
const choice = await showNextAction(ctx, {
|
|
321
|
+
title: "GSD — Queue Management",
|
|
322
|
+
summary: [
|
|
323
|
+
`${completeCount} complete, ${pendingMilestones.length} pending.`,
|
|
324
|
+
],
|
|
325
|
+
actions: [
|
|
326
|
+
{
|
|
327
|
+
id: "reorder",
|
|
328
|
+
label: "Reorder queue",
|
|
329
|
+
description: `Change execution order of ${pendingMilestones.length} pending milestones.`,
|
|
330
|
+
recommended: true,
|
|
331
|
+
},
|
|
332
|
+
{
|
|
333
|
+
id: "add",
|
|
334
|
+
label: "Add new work",
|
|
335
|
+
description: "Queue new milestones via discussion.",
|
|
336
|
+
},
|
|
337
|
+
],
|
|
338
|
+
notYetMessage: "Run /gsd queue when ready.",
|
|
339
|
+
});
|
|
340
|
+
|
|
341
|
+
if (choice === "reorder") {
|
|
342
|
+
await handleQueueReorder(ctx, basePath, state);
|
|
343
|
+
return;
|
|
344
|
+
}
|
|
345
|
+
if (choice === "not_yet") return;
|
|
346
|
+
// "add" falls through to existing queue-add logic below
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
// ── Existing queue-add flow ─────────────────────────────────────────
|
|
350
|
+
await showQueueAdd(ctx, pi, basePath, state);
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
async function handleQueueReorder(
|
|
354
|
+
ctx: ExtensionCommandContext,
|
|
355
|
+
basePath: string,
|
|
356
|
+
state: Awaited<ReturnType<typeof deriveState>>,
|
|
357
|
+
): Promise<void> {
|
|
358
|
+
const { showQueueReorder: showReorderUI } = await import("./queue-reorder-ui.js");
|
|
359
|
+
const { invalidateStateCache } = await import("./state.js");
|
|
360
|
+
|
|
361
|
+
const completed = state.registry
|
|
362
|
+
.filter(m => m.status === "complete")
|
|
363
|
+
.map(m => ({ id: m.id, title: m.title, dependsOn: m.dependsOn }));
|
|
364
|
+
|
|
365
|
+
const pending = state.registry
|
|
366
|
+
.filter(m => m.status !== "complete")
|
|
367
|
+
.map(m => ({ id: m.id, title: m.title, dependsOn: m.dependsOn }));
|
|
368
|
+
|
|
369
|
+
const result = await showReorderUI(ctx, completed, pending);
|
|
370
|
+
if (!result) {
|
|
371
|
+
ctx.ui.notify("Queue reorder cancelled.", "info");
|
|
372
|
+
return;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
// Save the new order
|
|
376
|
+
saveQueueOrder(basePath, result.order);
|
|
377
|
+
invalidateStateCache();
|
|
378
|
+
|
|
379
|
+
// Remove conflicting depends_on entries from CONTEXT.md files
|
|
380
|
+
if (result.depsToRemove.length > 0) {
|
|
381
|
+
removeDependsOnFromContextFiles(basePath, result.depsToRemove);
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
// Sync PROJECT.md milestone sequence table
|
|
385
|
+
syncProjectMdSequence(basePath, state.registry, result.order);
|
|
386
|
+
|
|
387
|
+
// Commit the change
|
|
388
|
+
const filesToAdd = [".gsd/QUEUE-ORDER.json", ".gsd/PROJECT.md"];
|
|
389
|
+
for (const r of result.depsToRemove) {
|
|
390
|
+
filesToAdd.push(`.gsd/milestones/${r.milestone}/${r.milestone}-CONTEXT.md`);
|
|
391
|
+
}
|
|
392
|
+
try {
|
|
393
|
+
nativeAddPaths(basePath, filesToAdd);
|
|
394
|
+
nativeCommit(basePath, "docs: reorder queue");
|
|
395
|
+
} catch {
|
|
396
|
+
// Commit may fail if nothing changed or git hooks block — non-fatal
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
const depInfo = result.depsToRemove.length > 0
|
|
400
|
+
? ` (removed ${result.depsToRemove.length} depends_on)`
|
|
401
|
+
: "";
|
|
402
|
+
ctx.ui.notify(`Queue reordered: ${result.order.join(" → ")}${depInfo}`, "info");
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
/**
|
|
406
|
+
* Remove specific depends_on entries from milestone CONTEXT.md frontmatter.
|
|
407
|
+
*/
|
|
408
|
+
function removeDependsOnFromContextFiles(
|
|
409
|
+
basePath: string,
|
|
410
|
+
depsToRemove: Array<{ milestone: string; dep: string }>,
|
|
411
|
+
): void {
|
|
412
|
+
// Group removals by milestone
|
|
413
|
+
const byMilestone = new Map<string, string[]>();
|
|
414
|
+
for (const { milestone, dep } of depsToRemove) {
|
|
415
|
+
const existing = byMilestone.get(milestone) ?? [];
|
|
416
|
+
existing.push(dep);
|
|
417
|
+
byMilestone.set(milestone, existing);
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
for (const [mid, depsToRemoveForMid] of byMilestone) {
|
|
421
|
+
const contextFile = resolveMilestoneFile(basePath, mid, "CONTEXT");
|
|
422
|
+
if (!contextFile || !existsSync(contextFile)) continue;
|
|
423
|
+
|
|
424
|
+
const content = readFileSync(contextFile, "utf-8");
|
|
425
|
+
|
|
426
|
+
// Parse frontmatter
|
|
427
|
+
const trimmed = content.trimStart();
|
|
428
|
+
if (!trimmed.startsWith("---")) continue;
|
|
429
|
+
const afterFirst = trimmed.indexOf("\n");
|
|
430
|
+
if (afterFirst === -1) continue;
|
|
431
|
+
const rest = trimmed.slice(afterFirst + 1);
|
|
432
|
+
const endIdx = rest.indexOf("\n---");
|
|
433
|
+
if (endIdx === -1) continue;
|
|
434
|
+
|
|
435
|
+
const fmText = rest.slice(0, endIdx);
|
|
436
|
+
const body = rest.slice(endIdx + 4);
|
|
437
|
+
|
|
438
|
+
// Parse depends_on line(s)
|
|
439
|
+
const fmLines = fmText.split("\n");
|
|
440
|
+
const removeSet = new Set(depsToRemoveForMid.map(d => d.toUpperCase()));
|
|
441
|
+
|
|
442
|
+
// Handle inline format: depends_on: [M009, M010]
|
|
443
|
+
const inlineMatch = fmLines.findIndex(l => /^depends_on:\s*\[/.test(l));
|
|
444
|
+
if (inlineMatch >= 0) {
|
|
445
|
+
const line = fmLines[inlineMatch];
|
|
446
|
+
const inner = line.match(/\[([^\]]*)\]/);
|
|
447
|
+
if (inner) {
|
|
448
|
+
const remaining = inner[1]
|
|
449
|
+
.split(",")
|
|
450
|
+
.map(s => s.trim())
|
|
451
|
+
.filter(s => s && !removeSet.has(s.toUpperCase()));
|
|
452
|
+
if (remaining.length === 0) {
|
|
453
|
+
fmLines.splice(inlineMatch, 1);
|
|
454
|
+
} else {
|
|
455
|
+
fmLines[inlineMatch] = `depends_on: [${remaining.join(", ")}]`;
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
} else {
|
|
459
|
+
// Handle multi-line format
|
|
460
|
+
const keyIdx = fmLines.findIndex(l => /^depends_on:\s*$/.test(l));
|
|
461
|
+
if (keyIdx >= 0) {
|
|
462
|
+
let end = keyIdx + 1;
|
|
463
|
+
while (end < fmLines.length && /^\s+-\s/.test(fmLines[end])) {
|
|
464
|
+
const val = fmLines[end].replace(/^\s+-\s*/, "").trim().toUpperCase();
|
|
465
|
+
if (removeSet.has(val)) {
|
|
466
|
+
fmLines.splice(end, 1);
|
|
467
|
+
} else {
|
|
468
|
+
end++;
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
if (end === keyIdx + 1 || (end <= fmLines.length && !/^\s+-\s/.test(fmLines[keyIdx + 1] ?? ""))) {
|
|
472
|
+
fmLines.splice(keyIdx, 1);
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
// Rebuild file
|
|
478
|
+
const newFm = fmLines.filter(l => l !== undefined).join("\n");
|
|
479
|
+
const newContent = newFm.trim()
|
|
480
|
+
? `---\n${newFm}\n---${body}`
|
|
481
|
+
: body.replace(/^\n+/, "");
|
|
482
|
+
writeFileSync(contextFile, newContent, "utf-8");
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
function syncProjectMdSequence(
|
|
487
|
+
basePath: string,
|
|
488
|
+
registry: Array<{ id: string; title: string; status: string }>,
|
|
489
|
+
newOrder: string[],
|
|
490
|
+
): void {
|
|
491
|
+
const projectPath = resolveGsdRootFile(basePath, "PROJECT");
|
|
492
|
+
if (!projectPath || !existsSync(projectPath)) return;
|
|
493
|
+
|
|
494
|
+
const content = readFileSync(projectPath, "utf-8");
|
|
495
|
+
const lines = content.split("\n");
|
|
496
|
+
|
|
497
|
+
const headerIdx = lines.findIndex(l => /^##\s+Milestone Sequence/.test(l));
|
|
498
|
+
if (headerIdx < 0) return;
|
|
499
|
+
|
|
500
|
+
let tableStart = headerIdx + 1;
|
|
501
|
+
while (tableStart < lines.length && !lines[tableStart].startsWith("|")) tableStart++;
|
|
502
|
+
if (tableStart >= lines.length) return;
|
|
503
|
+
|
|
504
|
+
let tableEnd = tableStart + 1;
|
|
505
|
+
while (tableEnd < lines.length && lines[tableEnd].startsWith("|")) tableEnd++;
|
|
506
|
+
|
|
507
|
+
const registryMap = new Map(registry.map(m => [m.id, m]));
|
|
508
|
+
const completedSet = new Set(registry.filter(m => m.status === "complete").map(m => m.id));
|
|
509
|
+
|
|
510
|
+
const newRows: string[] = [];
|
|
511
|
+
for (const m of registry) {
|
|
512
|
+
if (m.status === "complete") {
|
|
513
|
+
newRows.push(`| ${m.id} | ${m.title} | ✅ Complete |`);
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
let isFirst = true;
|
|
517
|
+
for (const id of newOrder) {
|
|
518
|
+
if (completedSet.has(id)) continue;
|
|
519
|
+
const m = registryMap.get(id);
|
|
520
|
+
if (!m) continue;
|
|
521
|
+
const status = isFirst ? "📋 Next" : "📋 Queued";
|
|
522
|
+
newRows.push(`| ${m.id} | ${m.title} | ${status} |`);
|
|
523
|
+
isFirst = false;
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
const headerLine = lines[tableStart];
|
|
527
|
+
const separatorLine = lines[tableStart + 1];
|
|
528
|
+
const newTable = [headerLine, separatorLine, ...newRows];
|
|
529
|
+
lines.splice(tableStart, tableEnd - tableStart, ...newTable);
|
|
530
|
+
writeFileSync(projectPath, lines.join("\n"), "utf-8");
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
async function showQueueAdd(
|
|
534
|
+
ctx: ExtensionCommandContext,
|
|
535
|
+
pi: ExtensionAPI,
|
|
536
|
+
basePath: string,
|
|
537
|
+
state: Awaited<ReturnType<typeof deriveState>>,
|
|
538
|
+
): Promise<void> {
|
|
539
|
+
const milestoneIds = findMilestoneIds(basePath);
|
|
540
|
+
|
|
308
541
|
// ── Build existing milestones context for the prompt ────────────────
|
|
309
542
|
const existingContext = await buildExistingMilestonesContext(basePath, milestoneIds, state);
|
|
310
543
|
|
|
@@ -712,7 +945,8 @@ export async function showSmartEntry(
|
|
|
712
945
|
}
|
|
713
946
|
|
|
714
947
|
// ── Ensure .gitignore has baseline patterns ──────────────────────────
|
|
715
|
-
|
|
948
|
+
const commitDocs = loadEffectiveGSDPreferences()?.preferences?.git?.commit_docs;
|
|
949
|
+
ensureGitignore(basePath, { commitDocs });
|
|
716
950
|
untrackRuntimeFiles(basePath);
|
|
717
951
|
|
|
718
952
|
// ── No GSD project OR no milestone → Create first/next milestone ────
|
|
@@ -723,11 +957,14 @@ export async function showSmartEntry(
|
|
|
723
957
|
|
|
724
958
|
// ── Create PREFERENCES.md template ────────────────────────────────
|
|
725
959
|
ensurePreferences(basePath);
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
960
|
+
// Only commit .gsd/ init when commit_docs is not explicitly false
|
|
961
|
+
if (commitDocs !== false) {
|
|
962
|
+
try {
|
|
963
|
+
nativeAddPaths(basePath, [".gsd", ".gitignore"]);
|
|
964
|
+
nativeCommit(basePath, "chore: init gsd");
|
|
965
|
+
} catch {
|
|
966
|
+
// nothing to commit — that's fine
|
|
967
|
+
}
|
|
731
968
|
}
|
|
732
969
|
}
|
|
733
970
|
|
|
@@ -28,10 +28,11 @@ import { createBashTool, createWriteTool, createReadTool, createEditTool, isTool
|
|
|
28
28
|
import { registerGSDCommand, loadToolApiKeys } from "./commands.js";
|
|
29
29
|
import { registerExitCommand } from "./exit-command.js";
|
|
30
30
|
import { registerWorktreeCommand, getWorktreeOriginalCwd, getActiveWorktreeName } from "./worktree-command.js";
|
|
31
|
+
import { getActiveAutoWorktreeContext } from "./auto-worktree.js";
|
|
31
32
|
import { saveFile, formatContinue, loadFile, parseContinue, parseSummary, loadActiveOverrides, formatOverridesSection } from "./files.js";
|
|
32
33
|
import { loadPrompt } from "./prompt-loader.js";
|
|
33
34
|
import { deriveState } from "./state.js";
|
|
34
|
-
import { isAutoActive, isAutoPaused, handleAgentEnd, pauseAuto, getAutoDashboardData } from "./auto.js";
|
|
35
|
+
import { isAutoActive, isAutoPaused, handleAgentEnd, pauseAuto, getAutoDashboardData, markToolStart, markToolEnd } from "./auto.js";
|
|
35
36
|
import { saveActivityLog } from "./activity-log.js";
|
|
36
37
|
import { checkAutoStartAfterDiscuss, getDiscussionMilestoneId } from "./guided-flow.js";
|
|
37
38
|
import { GSDDashboardOverlay } from "./dashboard-overlay.js";
|
|
@@ -47,10 +48,11 @@ import {
|
|
|
47
48
|
resolveSlicePath, resolveSliceFile, resolveTaskFile, resolveTaskFiles, resolveTasksDir,
|
|
48
49
|
relSliceFile, relSlicePath, relTaskFile,
|
|
49
50
|
buildSliceFileName, buildMilestoneFileName, gsdRoot, resolveMilestonePath,
|
|
51
|
+
resolveGsdRootFile,
|
|
50
52
|
} from "./paths.js";
|
|
51
53
|
import { Key } from "@gsd/pi-tui";
|
|
52
54
|
import { join } from "node:path";
|
|
53
|
-
import { existsSync } from "node:fs";
|
|
55
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
54
56
|
import { shortcutDesc } from "../shared/terminal.js";
|
|
55
57
|
import { Text } from "@gsd/pi-tui";
|
|
56
58
|
import { pauseAutoForProviderError } from "./provider-error-pause.js";
|
|
@@ -272,6 +274,20 @@ export default function (pi: ExtensionAPI) {
|
|
|
272
274
|
}
|
|
273
275
|
}
|
|
274
276
|
|
|
277
|
+
// Load project knowledge if available
|
|
278
|
+
let knowledgeBlock = "";
|
|
279
|
+
const knowledgePath = resolveGsdRootFile(process.cwd(), "KNOWLEDGE");
|
|
280
|
+
if (existsSync(knowledgePath)) {
|
|
281
|
+
try {
|
|
282
|
+
const content = readFileSync(knowledgePath, "utf-8").trim();
|
|
283
|
+
if (content) {
|
|
284
|
+
knowledgeBlock = `\n\n[PROJECT KNOWLEDGE — Rules, patterns, and lessons learned]\n\n${content}`;
|
|
285
|
+
}
|
|
286
|
+
} catch {
|
|
287
|
+
// File read error — skip knowledge injection
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
|
|
275
291
|
// Detect skills installed during this auto-mode session
|
|
276
292
|
let newSkillsBlock = "";
|
|
277
293
|
if (hasSkillSnapshot()) {
|
|
@@ -287,6 +303,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
287
303
|
let worktreeBlock = "";
|
|
288
304
|
const worktreeName = getActiveWorktreeName();
|
|
289
305
|
const worktreeMainCwd = getWorktreeOriginalCwd();
|
|
306
|
+
const autoWorktree = getActiveAutoWorktreeContext();
|
|
290
307
|
if (worktreeName && worktreeMainCwd) {
|
|
291
308
|
worktreeBlock = [
|
|
292
309
|
"",
|
|
@@ -304,10 +321,27 @@ export default function (pi: ExtensionAPI) {
|
|
|
304
321
|
"All file operations, bash commands, and GSD state resolve against the worktree path above.",
|
|
305
322
|
"Use /worktree merge to merge changes back. Use /worktree return to switch back to the main tree.",
|
|
306
323
|
].join("\n");
|
|
324
|
+
} else if (autoWorktree) {
|
|
325
|
+
worktreeBlock = [
|
|
326
|
+
"",
|
|
327
|
+
"",
|
|
328
|
+
"[WORKTREE CONTEXT — OVERRIDES CURRENT WORKING DIRECTORY ABOVE]",
|
|
329
|
+
`IMPORTANT: Ignore the "Current working directory" shown earlier in this prompt.`,
|
|
330
|
+
`The actual current working directory is: ${process.cwd()}`,
|
|
331
|
+
"",
|
|
332
|
+
"You are working inside a GSD auto-worktree.",
|
|
333
|
+
`- Milestone worktree: ${autoWorktree.worktreeName}`,
|
|
334
|
+
`- Worktree path (this is the real cwd): ${process.cwd()}`,
|
|
335
|
+
`- Main project: ${autoWorktree.originalBase}`,
|
|
336
|
+
`- Branch: ${autoWorktree.branch}`,
|
|
337
|
+
"",
|
|
338
|
+
"All file operations, bash commands, and GSD state resolve against the worktree path above.",
|
|
339
|
+
"Write every .gsd artifact in the worktree path above, never in the main project tree.",
|
|
340
|
+
].join("\n");
|
|
307
341
|
}
|
|
308
342
|
|
|
309
343
|
return {
|
|
310
|
-
systemPrompt: `${event.systemPrompt}\n\n[SYSTEM CONTEXT — GSD]\n\n${systemContent}${preferenceBlock}${newSkillsBlock}${worktreeBlock}`,
|
|
344
|
+
systemPrompt: `${event.systemPrompt}\n\n[SYSTEM CONTEXT — GSD]\n\n${systemContent}${preferenceBlock}${knowledgeBlock}${newSkillsBlock}${worktreeBlock}`,
|
|
311
345
|
...(injection
|
|
312
346
|
? {
|
|
313
347
|
message: {
|
|
@@ -542,6 +576,16 @@ export default function (pi: ExtensionAPI) {
|
|
|
542
576
|
const existing = await loadFile(discussionPath) ?? `# ${milestoneId} Discussion Log\n\n`;
|
|
543
577
|
await saveFile(discussionPath, existing + newBlock);
|
|
544
578
|
});
|
|
579
|
+
|
|
580
|
+
// ── tool_execution_start/end: track in-flight tools for idle detection ──
|
|
581
|
+
pi.on("tool_execution_start", async (event) => {
|
|
582
|
+
if (!isAutoActive()) return;
|
|
583
|
+
markToolStart(event.toolCallId);
|
|
584
|
+
});
|
|
585
|
+
|
|
586
|
+
pi.on("tool_execution_end", async (event) => {
|
|
587
|
+
markToolEnd(event.toolCallId);
|
|
588
|
+
});
|
|
545
589
|
}
|
|
546
590
|
|
|
547
591
|
async function buildGuidedExecuteContextInjection(prompt: string, basePath: string): Promise<string | null> {
|
|
@@ -303,6 +303,50 @@ export function formatCost(cost: number): string {
|
|
|
303
303
|
return `$${n.toFixed(2)}`;
|
|
304
304
|
}
|
|
305
305
|
|
|
306
|
+
// ─── Budget Prediction ────────────────────────────────────────────────────────
|
|
307
|
+
|
|
308
|
+
/**
|
|
309
|
+
* Calculate average cost per unit type from completed units.
|
|
310
|
+
* Returns a Map from unit type to average cost in USD.
|
|
311
|
+
*/
|
|
312
|
+
export function getAverageCostPerUnitType(units: UnitMetrics[]): Map<string, number> {
|
|
313
|
+
const sums = new Map<string, { total: number; count: number }>();
|
|
314
|
+
for (const u of units) {
|
|
315
|
+
const entry = sums.get(u.type) ?? { total: 0, count: 0 };
|
|
316
|
+
entry.total += u.cost;
|
|
317
|
+
entry.count += 1;
|
|
318
|
+
sums.set(u.type, entry);
|
|
319
|
+
}
|
|
320
|
+
const avgs = new Map<string, number>();
|
|
321
|
+
for (const [type, { total, count }] of sums) {
|
|
322
|
+
avgs.set(type, total / count);
|
|
323
|
+
}
|
|
324
|
+
return avgs;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
/**
|
|
328
|
+
* Estimate remaining cost given average costs and remaining unit counts.
|
|
329
|
+
* @param avgCosts - Average cost per unit type
|
|
330
|
+
* @param remainingUnits - Array of unit types still to dispatch
|
|
331
|
+
* @param fallbackAvg - Fallback average if unit type not seen before
|
|
332
|
+
* @returns Estimated remaining cost in USD
|
|
333
|
+
*/
|
|
334
|
+
export function predictRemainingCost(
|
|
335
|
+
avgCosts: Map<string, number>,
|
|
336
|
+
remainingUnits: string[],
|
|
337
|
+
fallbackAvg?: number,
|
|
338
|
+
): number {
|
|
339
|
+
// If no averages available, use overall average as fallback
|
|
340
|
+
const allAvgs = [...avgCosts.values()];
|
|
341
|
+
const overallAvg = fallbackAvg ?? (allAvgs.length > 0 ? allAvgs.reduce((a, b) => a + b, 0) / allAvgs.length : 0);
|
|
342
|
+
|
|
343
|
+
let total = 0;
|
|
344
|
+
for (const unitType of remainingUnits) {
|
|
345
|
+
total += avgCosts.get(unitType) ?? overallAvg;
|
|
346
|
+
}
|
|
347
|
+
return total;
|
|
348
|
+
}
|
|
349
|
+
|
|
306
350
|
/**
|
|
307
351
|
* Compute a projected remaining cost based on completed slice averages.
|
|
308
352
|
*
|
|
@@ -17,6 +17,10 @@ const GIT_NO_PROMPT_ENV = {
|
|
|
17
17
|
GIT_SVN_ID: "",
|
|
18
18
|
};
|
|
19
19
|
|
|
20
|
+
// Issue #453: keep auto-mode bookkeeping on the stable git CLI path unless a
|
|
21
|
+
// caller explicitly opts into the native helper.
|
|
22
|
+
const NATIVE_GSD_GIT_ENABLED = process.env.GSD_ENABLE_NATIVE_GSD_GIT === "1";
|
|
23
|
+
|
|
20
24
|
// ─── Native Module Types ──────────────────────────────────────────────────
|
|
21
25
|
|
|
22
26
|
interface GitDiffStat {
|
|
@@ -116,6 +120,7 @@ let loadAttempted = false;
|
|
|
116
120
|
function loadNative(): typeof nativeModule {
|
|
117
121
|
if (loadAttempted) return nativeModule;
|
|
118
122
|
loadAttempted = true;
|
|
123
|
+
if (!NATIVE_GSD_GIT_ENABLED) return nativeModule;
|
|
119
124
|
|
|
120
125
|
try {
|
|
121
126
|
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
@@ -6,6 +6,10 @@
|
|
|
6
6
|
|
|
7
7
|
import type { Roadmap, BoundaryMapEntry, RoadmapSliceEntry, RiskLevel } from './types.js';
|
|
8
8
|
|
|
9
|
+
// Issue #453: auto-mode post-turn reconciliation must stay on the stable JS path
|
|
10
|
+
// unless the native parser is explicitly requested.
|
|
11
|
+
const NATIVE_GSD_PARSER_ENABLED = process.env.GSD_ENABLE_NATIVE_GSD_PARSER === "1";
|
|
12
|
+
|
|
9
13
|
let nativeModule: {
|
|
10
14
|
parseFrontmatter: (content: string) => { metadata: string; body: string };
|
|
11
15
|
extractSection: (content: string, heading: string, level?: number) => { content: string; found: boolean };
|
|
@@ -29,6 +33,7 @@ let loadAttempted = false;
|
|
|
29
33
|
function loadNative(): typeof nativeModule {
|
|
30
34
|
if (loadAttempted) return nativeModule;
|
|
31
35
|
loadAttempted = true;
|
|
36
|
+
if (!NATIVE_GSD_PARSER_ENABLED) return nativeModule;
|
|
32
37
|
|
|
33
38
|
try {
|
|
34
39
|
// Dynamic import to avoid hard dependency - fails gracefully if native module not built
|
|
@@ -15,6 +15,9 @@ import { nativeScanGsdTree, type GsdTreeEntry } from "./native-parser-bridge.js"
|
|
|
15
15
|
|
|
16
16
|
// ─── Directory Listing Cache ──────────────────────────────────────────────────
|
|
17
17
|
|
|
18
|
+
/** Max entries before eviction. Prevents unbounded growth in long sessions (#611). */
|
|
19
|
+
const DIR_CACHE_MAX = 200;
|
|
20
|
+
|
|
18
21
|
const dirEntryCache = new Map<string, Dirent[]>();
|
|
19
22
|
const dirListCache = new Map<string, string[]>();
|
|
20
23
|
|
|
@@ -85,6 +88,7 @@ function cachedReaddirWithTypes(dirPath: string): Dirent[] {
|
|
|
85
88
|
d.isSocket = () => false;
|
|
86
89
|
return d;
|
|
87
90
|
});
|
|
91
|
+
if (dirEntryCache.size >= DIR_CACHE_MAX) dirEntryCache.clear();
|
|
88
92
|
dirEntryCache.set(dirPath, dirents);
|
|
89
93
|
return dirents;
|
|
90
94
|
}
|
|
@@ -92,6 +96,7 @@ function cachedReaddirWithTypes(dirPath: string): Dirent[] {
|
|
|
92
96
|
}
|
|
93
97
|
|
|
94
98
|
const entries = readdirSync(dirPath, { withFileTypes: true });
|
|
99
|
+
if (dirEntryCache.size >= DIR_CACHE_MAX) dirEntryCache.clear();
|
|
95
100
|
dirEntryCache.set(dirPath, entries);
|
|
96
101
|
return entries;
|
|
97
102
|
}
|
|
@@ -107,6 +112,7 @@ function cachedReaddir(dirPath: string): string[] {
|
|
|
107
112
|
const treeEntries = nativeTreeCache.get(key);
|
|
108
113
|
if (treeEntries) {
|
|
109
114
|
const names = treeEntries.map(e => e.name);
|
|
115
|
+
if (dirListCache.size >= DIR_CACHE_MAX) dirListCache.clear();
|
|
110
116
|
dirListCache.set(dirPath, names);
|
|
111
117
|
return names;
|
|
112
118
|
}
|
|
@@ -114,6 +120,7 @@ function cachedReaddir(dirPath: string): string[] {
|
|
|
114
120
|
}
|
|
115
121
|
|
|
116
122
|
const entries = readdirSync(dirPath);
|
|
123
|
+
if (dirListCache.size >= DIR_CACHE_MAX) dirListCache.clear();
|
|
117
124
|
dirListCache.set(dirPath, entries);
|
|
118
125
|
return entries;
|
|
119
126
|
}
|
|
@@ -248,6 +255,7 @@ export const GSD_ROOT_FILES = {
|
|
|
248
255
|
STATE: "STATE.md",
|
|
249
256
|
REQUIREMENTS: "REQUIREMENTS.md",
|
|
250
257
|
OVERRIDES: "OVERRIDES.md",
|
|
258
|
+
KNOWLEDGE: "KNOWLEDGE.md",
|
|
251
259
|
} as const;
|
|
252
260
|
|
|
253
261
|
export type GSDRootFileKey = keyof typeof GSD_ROOT_FILES;
|
|
@@ -259,6 +267,7 @@ const LEGACY_GSD_ROOT_FILES: Record<GSDRootFileKey, string> = {
|
|
|
259
267
|
STATE: "state.md",
|
|
260
268
|
REQUIREMENTS: "requirements.md",
|
|
261
269
|
OVERRIDES: "overrides.md",
|
|
270
|
+
KNOWLEDGE: "knowledge.md",
|
|
262
271
|
};
|
|
263
272
|
|
|
264
273
|
export function gsdRoot(basePath: string): string {
|