gsd-pi 2.23.0 → 2.25.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 +2 -1
- package/dist/cli.js +12 -3
- package/dist/headless.d.ts +4 -0
- package/dist/headless.js +118 -10
- package/dist/help-text.js +22 -7
- package/dist/models-resolver.d.ts +0 -11
- package/dist/models-resolver.js +0 -15
- package/dist/resource-loader.d.ts +0 -1
- package/dist/resource-loader.js +64 -18
- package/dist/resources/GSD-WORKFLOW.md +12 -9
- package/dist/resources/extensions/bg-shell/overlay.ts +18 -17
- package/dist/resources/extensions/get-secrets-from-user.ts +5 -23
- package/dist/resources/extensions/gsd/activity-log.ts +5 -3
- package/dist/resources/extensions/gsd/auto-dispatch.ts +51 -2
- package/dist/resources/extensions/gsd/auto-prompts.ts +87 -0
- package/dist/resources/extensions/gsd/auto-recovery.ts +41 -2
- package/dist/resources/extensions/gsd/auto-worktree.ts +134 -4
- package/dist/resources/extensions/gsd/auto.ts +307 -77
- package/dist/resources/extensions/gsd/cache.ts +3 -1
- package/dist/resources/extensions/gsd/commands.ts +176 -10
- package/dist/resources/extensions/gsd/complexity.ts +1 -0
- package/dist/resources/extensions/gsd/dashboard-overlay.ts +38 -0
- package/dist/resources/extensions/gsd/doctor.ts +58 -11
- package/dist/resources/extensions/gsd/exit-command.ts +2 -2
- package/dist/resources/extensions/gsd/git-service.ts +74 -14
- package/dist/resources/extensions/gsd/gitignore.ts +1 -0
- package/dist/resources/extensions/gsd/gsd-db.ts +78 -1
- package/dist/resources/extensions/gsd/guided-flow.ts +109 -12
- package/dist/resources/extensions/gsd/index.ts +48 -2
- package/dist/resources/extensions/gsd/memory-extractor.ts +352 -0
- package/dist/resources/extensions/gsd/memory-store.ts +441 -0
- package/dist/resources/extensions/gsd/migrate/command.ts +2 -2
- package/dist/resources/extensions/gsd/parallel-eligibility.ts +233 -0
- package/dist/resources/extensions/gsd/parallel-merge.ts +156 -0
- package/dist/resources/extensions/gsd/parallel-orchestrator.ts +496 -0
- package/dist/resources/extensions/gsd/preferences.ts +65 -1
- package/dist/resources/extensions/gsd/prompts/complete-slice.md +1 -1
- package/dist/resources/extensions/gsd/prompts/discuss-headless.md +86 -0
- package/dist/resources/extensions/gsd/prompts/discuss.md +4 -4
- package/dist/resources/extensions/gsd/prompts/execute-task.md +1 -1
- package/dist/resources/extensions/gsd/prompts/guided-discuss-milestone.md +1 -1
- package/dist/resources/extensions/gsd/prompts/guided-discuss-slice.md +1 -1
- package/dist/resources/extensions/gsd/prompts/plan-slice.md +1 -1
- package/dist/resources/extensions/gsd/prompts/queue.md +1 -1
- package/dist/resources/extensions/gsd/prompts/reassess-roadmap.md +1 -1
- package/dist/resources/extensions/gsd/prompts/research-slice.md +1 -1
- package/dist/resources/extensions/gsd/prompts/validate-milestone.md +40 -61
- package/dist/resources/extensions/gsd/provider-error-pause.ts +29 -2
- package/dist/resources/extensions/gsd/session-status-io.ts +197 -0
- package/dist/resources/extensions/gsd/state.ts +72 -30
- package/dist/resources/extensions/gsd/tests/agent-end-provider-error.test.ts +81 -0
- package/dist/resources/extensions/gsd/tests/auto-budget-alerts.test.ts +20 -3
- package/dist/resources/extensions/gsd/tests/auto-preflight.test.ts +1 -0
- package/dist/resources/extensions/gsd/tests/auto-recovery.test.ts +256 -2
- package/dist/resources/extensions/gsd/tests/auto-worktree-milestone-merge.test.ts +34 -0
- package/dist/resources/extensions/gsd/tests/auto-worktree.test.ts +58 -0
- package/dist/resources/extensions/gsd/tests/complete-milestone.test.ts +8 -1
- package/dist/resources/extensions/gsd/tests/derive-state-db.test.ts +9 -15
- package/dist/resources/extensions/gsd/tests/derive-state-deps.test.ts +9 -0
- package/dist/resources/extensions/gsd/tests/derive-state-draft.test.ts +8 -0
- package/dist/resources/extensions/gsd/tests/derive-state.test.ts +14 -0
- package/dist/resources/extensions/gsd/tests/git-service.test.ts +70 -4
- package/dist/resources/extensions/gsd/tests/gsd-db.test.ts +2 -2
- package/dist/resources/extensions/gsd/tests/integration-mixed-milestones.test.ts +8 -0
- package/dist/resources/extensions/gsd/tests/md-importer.test.ts +2 -3
- package/dist/resources/extensions/gsd/tests/memory-extractor.test.ts +180 -0
- package/dist/resources/extensions/gsd/tests/memory-store.test.ts +345 -0
- package/dist/resources/extensions/gsd/tests/migrate-writer-integration.test.ts +5 -5
- package/dist/resources/extensions/gsd/tests/parallel-orchestration.test.ts +656 -0
- package/dist/resources/extensions/gsd/tests/parallel-workers-multi-milestone-e2e.test.ts +354 -0
- package/dist/resources/extensions/gsd/tests/queue-reorder-e2e.test.ts +1 -0
- package/dist/resources/extensions/gsd/tests/smart-entry-draft.test.ts +1 -1
- package/dist/resources/extensions/gsd/tests/validate-milestone.test.ts +316 -0
- package/dist/resources/extensions/gsd/tests/visualizer-data.test.ts +147 -2
- package/dist/resources/extensions/gsd/tests/visualizer-overlay.test.ts +88 -10
- package/dist/resources/extensions/gsd/tests/visualizer-views.test.ts +314 -87
- package/dist/resources/extensions/gsd/tests/worker-registry.test.ts +148 -0
- package/dist/resources/extensions/gsd/triage-ui.ts +1 -1
- package/dist/resources/extensions/gsd/types.ts +15 -1
- package/dist/resources/extensions/gsd/visualizer-data.ts +291 -10
- package/dist/resources/extensions/gsd/visualizer-overlay.ts +237 -28
- package/dist/resources/extensions/gsd/visualizer-views.ts +462 -48
- package/dist/resources/extensions/gsd/worktree.ts +9 -2
- package/dist/resources/extensions/search-the-web/native-search.ts +15 -5
- package/dist/resources/extensions/subagent/index.ts +5 -0
- package/dist/resources/extensions/subagent/worker-registry.ts +99 -0
- package/dist/update-check.d.ts +9 -0
- package/dist/update-check.js +97 -0
- package/package.json +6 -1
- package/packages/pi-agent-core/dist/agent-loop.js +2 -0
- package/packages/pi-agent-core/dist/agent-loop.js.map +1 -1
- package/packages/pi-agent-core/src/agent-loop.ts +2 -0
- package/packages/pi-ai/dist/providers/anthropic.d.ts.map +1 -1
- package/packages/pi-ai/dist/providers/anthropic.js +55 -7
- package/packages/pi-ai/dist/providers/anthropic.js.map +1 -1
- package/packages/pi-ai/dist/providers/azure-openai-responses.d.ts.map +1 -1
- package/packages/pi-ai/dist/providers/azure-openai-responses.js +12 -4
- package/packages/pi-ai/dist/providers/azure-openai-responses.js.map +1 -1
- package/packages/pi-ai/dist/providers/google-vertex.d.ts.map +1 -1
- package/packages/pi-ai/dist/providers/google-vertex.js +21 -9
- package/packages/pi-ai/dist/providers/google-vertex.js.map +1 -1
- package/packages/pi-ai/dist/providers/mistral.js +3 -0
- package/packages/pi-ai/dist/providers/mistral.js.map +1 -1
- package/packages/pi-ai/dist/providers/openai-completions.d.ts.map +1 -1
- package/packages/pi-ai/dist/providers/openai-completions.js +12 -4
- package/packages/pi-ai/dist/providers/openai-completions.js.map +1 -1
- package/packages/pi-ai/dist/providers/openai-responses.d.ts.map +1 -1
- package/packages/pi-ai/dist/providers/openai-responses.js +12 -4
- package/packages/pi-ai/dist/providers/openai-responses.js.map +1 -1
- package/packages/pi-ai/dist/types.d.ts +23 -1
- package/packages/pi-ai/dist/types.d.ts.map +1 -1
- package/packages/pi-ai/dist/types.js.map +1 -1
- package/packages/pi-ai/src/providers/anthropic.ts +59 -9
- package/packages/pi-ai/src/providers/azure-openai-responses.ts +16 -4
- package/packages/pi-ai/src/providers/google-vertex.ts +32 -17
- package/packages/pi-ai/src/providers/mistral.ts +3 -0
- package/packages/pi-ai/src/providers/openai-completions.ts +16 -4
- package/packages/pi-ai/src/providers/openai-responses.ts +16 -4
- package/packages/pi-ai/src/types.ts +19 -1
- package/packages/pi-coding-agent/dist/core/agent-session.js +1 -1
- package/packages/pi-coding-agent/dist/core/agent-session.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/settings-manager.js +1 -1
- package/packages/pi-coding-agent/dist/core/settings-manager.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js +17 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts +4 -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 +72 -0
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/packages/pi-coding-agent/src/core/agent-session.ts +1 -1
- package/packages/pi-coding-agent/src/core/settings-manager.ts +2 -2
- package/packages/pi-coding-agent/src/modes/interactive/components/tool-execution.ts +18 -0
- package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +84 -0
- package/scripts/postinstall.js +7 -109
- package/src/resources/GSD-WORKFLOW.md +12 -9
- package/src/resources/extensions/bg-shell/overlay.ts +18 -17
- package/src/resources/extensions/get-secrets-from-user.ts +5 -23
- package/src/resources/extensions/gsd/activity-log.ts +5 -3
- package/src/resources/extensions/gsd/auto-dispatch.ts +51 -2
- package/src/resources/extensions/gsd/auto-prompts.ts +87 -0
- package/src/resources/extensions/gsd/auto-recovery.ts +41 -2
- package/src/resources/extensions/gsd/auto-worktree.ts +134 -4
- package/src/resources/extensions/gsd/auto.ts +307 -77
- package/src/resources/extensions/gsd/cache.ts +3 -1
- package/src/resources/extensions/gsd/commands.ts +176 -10
- package/src/resources/extensions/gsd/complexity.ts +1 -0
- package/src/resources/extensions/gsd/dashboard-overlay.ts +38 -0
- package/src/resources/extensions/gsd/doctor.ts +58 -11
- package/src/resources/extensions/gsd/exit-command.ts +2 -2
- package/src/resources/extensions/gsd/git-service.ts +74 -14
- package/src/resources/extensions/gsd/gitignore.ts +1 -0
- package/src/resources/extensions/gsd/gsd-db.ts +78 -1
- package/src/resources/extensions/gsd/guided-flow.ts +109 -12
- package/src/resources/extensions/gsd/index.ts +48 -2
- package/src/resources/extensions/gsd/memory-extractor.ts +352 -0
- package/src/resources/extensions/gsd/memory-store.ts +441 -0
- package/src/resources/extensions/gsd/migrate/command.ts +2 -2
- package/src/resources/extensions/gsd/parallel-eligibility.ts +233 -0
- package/src/resources/extensions/gsd/parallel-merge.ts +156 -0
- package/src/resources/extensions/gsd/parallel-orchestrator.ts +496 -0
- package/src/resources/extensions/gsd/preferences.ts +65 -1
- package/src/resources/extensions/gsd/prompts/complete-slice.md +1 -1
- package/src/resources/extensions/gsd/prompts/discuss-headless.md +86 -0
- package/src/resources/extensions/gsd/prompts/discuss.md +4 -4
- package/src/resources/extensions/gsd/prompts/execute-task.md +1 -1
- package/src/resources/extensions/gsd/prompts/guided-discuss-milestone.md +1 -1
- package/src/resources/extensions/gsd/prompts/guided-discuss-slice.md +1 -1
- package/src/resources/extensions/gsd/prompts/plan-slice.md +1 -1
- package/src/resources/extensions/gsd/prompts/queue.md +1 -1
- package/src/resources/extensions/gsd/prompts/reassess-roadmap.md +1 -1
- package/src/resources/extensions/gsd/prompts/research-slice.md +1 -1
- package/src/resources/extensions/gsd/prompts/validate-milestone.md +40 -61
- package/src/resources/extensions/gsd/provider-error-pause.ts +29 -2
- package/src/resources/extensions/gsd/session-status-io.ts +197 -0
- package/src/resources/extensions/gsd/state.ts +72 -30
- package/src/resources/extensions/gsd/tests/agent-end-provider-error.test.ts +81 -0
- package/src/resources/extensions/gsd/tests/auto-budget-alerts.test.ts +20 -3
- package/src/resources/extensions/gsd/tests/auto-preflight.test.ts +1 -0
- package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +256 -2
- package/src/resources/extensions/gsd/tests/auto-worktree-milestone-merge.test.ts +34 -0
- package/src/resources/extensions/gsd/tests/auto-worktree.test.ts +58 -0
- package/src/resources/extensions/gsd/tests/complete-milestone.test.ts +8 -1
- package/src/resources/extensions/gsd/tests/derive-state-db.test.ts +9 -15
- package/src/resources/extensions/gsd/tests/derive-state-deps.test.ts +9 -0
- package/src/resources/extensions/gsd/tests/derive-state-draft.test.ts +8 -0
- package/src/resources/extensions/gsd/tests/derive-state.test.ts +14 -0
- package/src/resources/extensions/gsd/tests/git-service.test.ts +70 -4
- package/src/resources/extensions/gsd/tests/gsd-db.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/integration-mixed-milestones.test.ts +8 -0
- package/src/resources/extensions/gsd/tests/md-importer.test.ts +2 -3
- package/src/resources/extensions/gsd/tests/memory-extractor.test.ts +180 -0
- package/src/resources/extensions/gsd/tests/memory-store.test.ts +345 -0
- package/src/resources/extensions/gsd/tests/migrate-writer-integration.test.ts +5 -5
- package/src/resources/extensions/gsd/tests/parallel-orchestration.test.ts +656 -0
- package/src/resources/extensions/gsd/tests/parallel-workers-multi-milestone-e2e.test.ts +354 -0
- package/src/resources/extensions/gsd/tests/queue-reorder-e2e.test.ts +1 -0
- package/src/resources/extensions/gsd/tests/smart-entry-draft.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/validate-milestone.test.ts +316 -0
- package/src/resources/extensions/gsd/tests/visualizer-data.test.ts +147 -2
- package/src/resources/extensions/gsd/tests/visualizer-overlay.test.ts +88 -10
- package/src/resources/extensions/gsd/tests/visualizer-views.test.ts +314 -87
- package/src/resources/extensions/gsd/tests/worker-registry.test.ts +148 -0
- package/src/resources/extensions/gsd/triage-ui.ts +1 -1
- package/src/resources/extensions/gsd/types.ts +15 -1
- package/src/resources/extensions/gsd/visualizer-data.ts +291 -10
- package/src/resources/extensions/gsd/visualizer-overlay.ts +237 -28
- package/src/resources/extensions/gsd/visualizer-views.ts +462 -48
- package/src/resources/extensions/gsd/worktree.ts +9 -2
- package/src/resources/extensions/search-the-web/native-search.ts +15 -5
- package/src/resources/extensions/subagent/index.ts +5 -0
- package/src/resources/extensions/subagent/worker-registry.ts +99 -0
|
@@ -5,6 +5,7 @@ import { execSync } from "node:child_process";
|
|
|
5
5
|
|
|
6
6
|
import {
|
|
7
7
|
inferCommitType,
|
|
8
|
+
buildTaskCommitMessage,
|
|
8
9
|
GitServiceImpl,
|
|
9
10
|
RUNTIME_EXCLUSION_PATHS,
|
|
10
11
|
VALID_BRANCH_NAME,
|
|
@@ -14,6 +15,7 @@ import {
|
|
|
14
15
|
type GitPreferences,
|
|
15
16
|
type CommitOptions,
|
|
16
17
|
type PreMergeCheckResult,
|
|
18
|
+
type TaskCommitContext,
|
|
17
19
|
} from "../git-service.ts";
|
|
18
20
|
import { createTestContext } from './test-helpers.ts';
|
|
19
21
|
|
|
@@ -188,6 +190,58 @@ async function main(): Promise<void> {
|
|
|
188
190
|
"'prefix' does not match 'fix' — word boundary prevents partial match"
|
|
189
191
|
);
|
|
190
192
|
|
|
193
|
+
// ─── inferCommitType with oneLiner ──────────────────────────────────────
|
|
194
|
+
|
|
195
|
+
console.log("\n=== inferCommitType with oneLiner ===");
|
|
196
|
+
|
|
197
|
+
assertEq(
|
|
198
|
+
inferCommitType("implement dashboard", "Fixed rendering bug in sidebar"),
|
|
199
|
+
"fix",
|
|
200
|
+
"one-liner with 'fixed' overrides generic title → fix"
|
|
201
|
+
);
|
|
202
|
+
|
|
203
|
+
assertEq(
|
|
204
|
+
inferCommitType("add search", "Optimized query performance with caching"),
|
|
205
|
+
"perf",
|
|
206
|
+
"one-liner with 'performance' and 'caching' → perf"
|
|
207
|
+
);
|
|
208
|
+
|
|
209
|
+
// ─── buildTaskCommitMessage ─────────────────────────────────────────────
|
|
210
|
+
|
|
211
|
+
console.log("\n=== buildTaskCommitMessage ===");
|
|
212
|
+
|
|
213
|
+
{
|
|
214
|
+
const msg = buildTaskCommitMessage({
|
|
215
|
+
taskId: "S01/T02",
|
|
216
|
+
taskTitle: "implement user authentication",
|
|
217
|
+
oneLiner: "Added JWT-based auth with refresh token rotation",
|
|
218
|
+
keyFiles: ["src/auth.ts", "src/middleware/jwt.ts"],
|
|
219
|
+
});
|
|
220
|
+
assertTrue(msg.startsWith("feat(S01/T02):"), "message starts with type(scope)");
|
|
221
|
+
assertTrue(msg.includes("JWT-based auth"), "message includes one-liner content");
|
|
222
|
+
assertTrue(msg.includes("- src/auth.ts"), "message body includes key files");
|
|
223
|
+
assertTrue(msg.includes("- src/middleware/jwt.ts"), "message body includes second key file");
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
{
|
|
227
|
+
const msg = buildTaskCommitMessage({
|
|
228
|
+
taskId: "S02/T01",
|
|
229
|
+
taskTitle: "fix login redirect bug",
|
|
230
|
+
});
|
|
231
|
+
assertTrue(msg.startsWith("fix(S02/T01):"), "infers fix type from title");
|
|
232
|
+
assertTrue(msg.includes("fix login redirect bug"), "uses task title when no one-liner");
|
|
233
|
+
assertTrue(!msg.includes("\n"), "no body when no key files");
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
{
|
|
237
|
+
const msg = buildTaskCommitMessage({
|
|
238
|
+
taskId: "S01/T03",
|
|
239
|
+
taskTitle: "add tests",
|
|
240
|
+
oneLiner: "Unit tests for auth module with coverage",
|
|
241
|
+
});
|
|
242
|
+
assertTrue(msg.startsWith("test(S01/T03):"), "infers test type");
|
|
243
|
+
}
|
|
244
|
+
|
|
191
245
|
// ─── RUNTIME_EXCLUSION_PATHS ───────────────────────────────────────────
|
|
192
246
|
|
|
193
247
|
console.log("\n=== RUNTIME_EXCLUSION_PATHS ===");
|
|
@@ -430,13 +484,25 @@ async function main(): Promise<void> {
|
|
|
430
484
|
const svc = new GitServiceImpl(repo);
|
|
431
485
|
|
|
432
486
|
createFile(repo, "src/new-feature.ts", "export const x = 1;");
|
|
433
|
-
const msg = svc.autoCommit("task", "T01");
|
|
434
487
|
|
|
435
|
-
|
|
488
|
+
// Without task context, autoCommit uses generic chore message
|
|
489
|
+
const msg = svc.autoCommit("task", "T01");
|
|
490
|
+
assertEq(msg, "chore(T01): auto-commit after task", "autoCommit returns generic format without task context");
|
|
436
491
|
|
|
437
|
-
// Verify the commit exists
|
|
438
492
|
const log = run("git log --oneline -1", repo);
|
|
439
|
-
assertTrue(log.includes("chore(T01): auto-commit after task"), "commit message is in git log");
|
|
493
|
+
assertTrue(log.includes("chore(T01): auto-commit after task"), "generic commit message is in git log");
|
|
494
|
+
|
|
495
|
+
// With task context, autoCommit uses meaningful message
|
|
496
|
+
createFile(repo, "src/auth.ts", "export function login() {}");
|
|
497
|
+
const msg2 = svc.autoCommit("task", "S01/T02", [], {
|
|
498
|
+
taskId: "S01/T02",
|
|
499
|
+
taskTitle: "implement user authentication endpoint",
|
|
500
|
+
oneLiner: "Added JWT-based auth with refresh token rotation",
|
|
501
|
+
keyFiles: ["src/auth.ts"],
|
|
502
|
+
});
|
|
503
|
+
assertTrue(msg2 !== null, "autoCommit with task context returns a message");
|
|
504
|
+
assertTrue(msg2!.startsWith("feat(S01/T02):"), "meaningful commit uses feat type and scope");
|
|
505
|
+
assertTrue(msg2!.includes("JWT-based auth"), "meaningful commit includes one-liner content");
|
|
440
506
|
|
|
441
507
|
rmSync(repo, { recursive: true, force: true });
|
|
442
508
|
}
|
|
@@ -65,8 +65,8 @@ console.log('\n=== gsd-db: fresh DB schema init (memory) ===');
|
|
|
65
65
|
|
|
66
66
|
// Check schema_version table
|
|
67
67
|
const adapter = _getAdapter()!;
|
|
68
|
-
const version = adapter.prepare('SELECT version FROM schema_version').get();
|
|
69
|
-
assertEq(version?.['version'],
|
|
68
|
+
const version = adapter.prepare('SELECT MAX(version) as version FROM schema_version').get();
|
|
69
|
+
assertEq(version?.['version'], 3, 'schema version should be 3');
|
|
70
70
|
|
|
71
71
|
// Check tables exist by querying them
|
|
72
72
|
const dRows = adapter.prepare('SELECT count(*) as cnt FROM decisions').get();
|
|
@@ -51,6 +51,12 @@ function writeMilestoneSummary(base: string, mid: string, content: string): void
|
|
|
51
51
|
writeFileSync(join(dir, `${mid}-SUMMARY.md`), content);
|
|
52
52
|
}
|
|
53
53
|
|
|
54
|
+
function writeMilestoneValidation(base: string, mid: string): void {
|
|
55
|
+
const dir = join(base, '.gsd', 'milestones', mid);
|
|
56
|
+
mkdirSync(dir, { recursive: true });
|
|
57
|
+
writeFileSync(join(dir, `${mid}-VALIDATION.md`), `---\nverdict: pass\nremediation_round: 0\n---\n\n# Validation\nPassed.`);
|
|
58
|
+
}
|
|
59
|
+
|
|
54
60
|
function cleanup(base: string): void {
|
|
55
61
|
rmSync(base, { recursive: true, force: true });
|
|
56
62
|
}
|
|
@@ -166,6 +172,7 @@ async function main(): Promise<void> {
|
|
|
166
172
|
Did it.
|
|
167
173
|
`);
|
|
168
174
|
|
|
175
|
+
writeMilestoneValidation(base, 'M001');
|
|
169
176
|
writeMilestoneSummary(base, 'M001', `# M001: Legacy Feature Summary
|
|
170
177
|
|
|
171
178
|
**One-liner summary**
|
|
@@ -265,6 +272,7 @@ Everything worked.
|
|
|
265
272
|
Did it.
|
|
266
273
|
`);
|
|
267
274
|
|
|
275
|
+
writeMilestoneValidation(base, 'M001');
|
|
268
276
|
writeMilestoneSummary(base, 'M001', `# M001: Legacy Feature Summary
|
|
269
277
|
|
|
270
278
|
**One-liner summary**
|
|
@@ -350,12 +350,11 @@ console.log('=== md-importer: missing file handling ===');
|
|
|
350
350
|
console.log('=== md-importer: schema v1→v2 migration ===');
|
|
351
351
|
|
|
352
352
|
{
|
|
353
|
-
// This test verifies that opening a
|
|
354
|
-
// (The actual migration is tested via the gsd-db.test.ts schema version assertion = 2)
|
|
353
|
+
// This test verifies that opening a fresh DB auto-migrates to current schema version
|
|
355
354
|
openDatabase(':memory:');
|
|
356
355
|
const adapter = _getAdapter();
|
|
357
356
|
const version = adapter?.prepare('SELECT MAX(version) as v FROM schema_version').get();
|
|
358
|
-
assertEq(version?.v,
|
|
357
|
+
assertEq(version?.v, 3, 'new DB should be at schema version 3');
|
|
359
358
|
|
|
360
359
|
// Artifacts table should exist
|
|
361
360
|
const tableCheck = adapter?.prepare("SELECT count(*) as c FROM sqlite_master WHERE type='table' AND name='artifacts'").get();
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
import { createTestContext } from './test-helpers.ts';
|
|
2
|
+
import { parseMemoryResponse, _resetExtractionState } from '../memory-extractor.ts';
|
|
3
|
+
import {
|
|
4
|
+
openDatabase,
|
|
5
|
+
closeDatabase,
|
|
6
|
+
} from '../gsd-db.ts';
|
|
7
|
+
import {
|
|
8
|
+
getActiveMemories,
|
|
9
|
+
applyMemoryActions,
|
|
10
|
+
getActiveMemoriesRanked,
|
|
11
|
+
} from '../memory-store.ts';
|
|
12
|
+
import type { MemoryAction } from '../memory-store.ts';
|
|
13
|
+
|
|
14
|
+
const { assertEq, assertTrue, report } = createTestContext();
|
|
15
|
+
|
|
16
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
17
|
+
// memory-extractor: parse valid JSON response
|
|
18
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
19
|
+
|
|
20
|
+
console.log('\n=== memory-extractor: parse valid JSON ===');
|
|
21
|
+
{
|
|
22
|
+
const response = JSON.stringify([
|
|
23
|
+
{ action: 'CREATE', category: 'gotcha', content: 'esbuild drops binaries', confidence: 0.85 },
|
|
24
|
+
{ action: 'REINFORCE', id: 'MEM001' },
|
|
25
|
+
{ action: 'UPDATE', id: 'MEM002', content: 'revised content' },
|
|
26
|
+
{ action: 'SUPERSEDE', id: 'MEM003', superseded_by: 'MEM004' },
|
|
27
|
+
]);
|
|
28
|
+
|
|
29
|
+
const actions = parseMemoryResponse(response);
|
|
30
|
+
assertEq(actions.length, 4, 'should parse 4 actions');
|
|
31
|
+
assertEq(actions[0].action, 'CREATE', 'first action should be CREATE');
|
|
32
|
+
assertEq((actions[0] as any).category, 'gotcha', 'CREATE category');
|
|
33
|
+
assertEq((actions[0] as any).confidence, 0.85, 'CREATE confidence');
|
|
34
|
+
assertEq(actions[1].action, 'REINFORCE', 'second action should be REINFORCE');
|
|
35
|
+
assertEq(actions[2].action, 'UPDATE', 'third action should be UPDATE');
|
|
36
|
+
assertEq(actions[3].action, 'SUPERSEDE', 'fourth action should be SUPERSEDE');
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
40
|
+
// memory-extractor: parse fenced JSON response
|
|
41
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
42
|
+
|
|
43
|
+
console.log('\n=== memory-extractor: parse fenced JSON ===');
|
|
44
|
+
{
|
|
45
|
+
const response = '```json\n[\n {"action": "CREATE", "category": "convention", "content": "test memory"}\n]\n```';
|
|
46
|
+
|
|
47
|
+
const actions = parseMemoryResponse(response);
|
|
48
|
+
assertEq(actions.length, 1, 'should parse 1 action from fenced JSON');
|
|
49
|
+
assertEq(actions[0].action, 'CREATE', 'action should be CREATE');
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
53
|
+
// memory-extractor: parse empty array response
|
|
54
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
55
|
+
|
|
56
|
+
console.log('\n=== memory-extractor: parse empty array ===');
|
|
57
|
+
{
|
|
58
|
+
const actions = parseMemoryResponse('[]');
|
|
59
|
+
assertEq(actions.length, 0, 'empty array should parse to empty actions');
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
63
|
+
// memory-extractor: parse malformed response
|
|
64
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
65
|
+
|
|
66
|
+
console.log('\n=== memory-extractor: malformed responses ===');
|
|
67
|
+
{
|
|
68
|
+
assertEq(parseMemoryResponse('not json at all'), [], 'garbage text should return []');
|
|
69
|
+
assertEq(parseMemoryResponse('{"action": "CREATE"}'), [], 'non-array should return []');
|
|
70
|
+
assertEq(parseMemoryResponse(''), [], 'empty string should return []');
|
|
71
|
+
assertEq(parseMemoryResponse('```\nbroken\n```'), [], 'fenced non-JSON should return []');
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
75
|
+
// memory-extractor: validation of required fields
|
|
76
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
77
|
+
|
|
78
|
+
console.log('\n=== memory-extractor: field validation ===');
|
|
79
|
+
{
|
|
80
|
+
const response = JSON.stringify([
|
|
81
|
+
// Valid CREATE
|
|
82
|
+
{ action: 'CREATE', category: 'gotcha', content: 'valid' },
|
|
83
|
+
// Invalid CREATE — missing content
|
|
84
|
+
{ action: 'CREATE', category: 'gotcha' },
|
|
85
|
+
// Invalid CREATE — missing category
|
|
86
|
+
{ action: 'CREATE', content: 'no category' },
|
|
87
|
+
// Valid REINFORCE
|
|
88
|
+
{ action: 'REINFORCE', id: 'MEM001' },
|
|
89
|
+
// Invalid REINFORCE — missing id
|
|
90
|
+
{ action: 'REINFORCE' },
|
|
91
|
+
// Valid UPDATE
|
|
92
|
+
{ action: 'UPDATE', id: 'MEM002', content: 'new content' },
|
|
93
|
+
// Invalid UPDATE — missing content
|
|
94
|
+
{ action: 'UPDATE', id: 'MEM002' },
|
|
95
|
+
// Valid SUPERSEDE
|
|
96
|
+
{ action: 'SUPERSEDE', id: 'MEM001', superseded_by: 'MEM002' },
|
|
97
|
+
// Invalid SUPERSEDE — missing superseded_by
|
|
98
|
+
{ action: 'SUPERSEDE', id: 'MEM001' },
|
|
99
|
+
// Unknown action
|
|
100
|
+
{ action: 'DELETE', id: 'MEM001' },
|
|
101
|
+
// Null entry
|
|
102
|
+
null,
|
|
103
|
+
]);
|
|
104
|
+
|
|
105
|
+
const actions = parseMemoryResponse(response);
|
|
106
|
+
assertEq(actions.length, 4, 'should only accept 4 valid actions');
|
|
107
|
+
assertEq(actions[0].action, 'CREATE', 'first valid is CREATE');
|
|
108
|
+
assertEq(actions[1].action, 'REINFORCE', 'second valid is REINFORCE');
|
|
109
|
+
assertEq(actions[2].action, 'UPDATE', 'third valid is UPDATE');
|
|
110
|
+
assertEq(actions[3].action, 'SUPERSEDE', 'fourth valid is SUPERSEDE');
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
114
|
+
// Integration: applyMemoryActions with mixed actions
|
|
115
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
116
|
+
|
|
117
|
+
console.log('\n=== integration: mixed action lifecycle ===');
|
|
118
|
+
{
|
|
119
|
+
openDatabase(':memory:');
|
|
120
|
+
|
|
121
|
+
// Phase 1: Create initial memories
|
|
122
|
+
applyMemoryActions([
|
|
123
|
+
{ action: 'CREATE', category: 'gotcha', content: 'npm run build needs tsc first', confidence: 0.7 },
|
|
124
|
+
{ action: 'CREATE', category: 'convention', content: 'all DB queries use named params', confidence: 0.8 },
|
|
125
|
+
{ action: 'CREATE', category: 'architecture', content: 'extensions loaded from two paths', confidence: 0.85 },
|
|
126
|
+
], 'plan-slice', 'M001/S01');
|
|
127
|
+
|
|
128
|
+
let active = getActiveMemoriesRanked(30);
|
|
129
|
+
assertEq(active.length, 3, 'phase 1: 3 active memories');
|
|
130
|
+
|
|
131
|
+
// Phase 2: Reinforce one, update another, create new
|
|
132
|
+
applyMemoryActions([
|
|
133
|
+
{ action: 'REINFORCE', id: 'MEM002' },
|
|
134
|
+
{ action: 'UPDATE', id: 'MEM001', content: 'npm run build requires tsc --noEmit first' },
|
|
135
|
+
{ action: 'CREATE', category: 'pattern', content: 'use INSERT OR IGNORE for idempotency', confidence: 0.75 },
|
|
136
|
+
], 'execute-task', 'M001/S01/T01');
|
|
137
|
+
|
|
138
|
+
active = getActiveMemoriesRanked(30);
|
|
139
|
+
assertEq(active.length, 4, 'phase 2: 4 active memories');
|
|
140
|
+
assertEq(
|
|
141
|
+
active.find(m => m.id === 'MEM001')?.content,
|
|
142
|
+
'npm run build requires tsc --noEmit first',
|
|
143
|
+
'MEM001 content should be updated',
|
|
144
|
+
);
|
|
145
|
+
assertEq(active.find(m => m.id === 'MEM002')?.hit_count, 1, 'MEM002 should be reinforced');
|
|
146
|
+
|
|
147
|
+
// Phase 3: Supersede MEM001 with MEM005
|
|
148
|
+
applyMemoryActions([
|
|
149
|
+
{ action: 'CREATE', category: 'gotcha', content: 'build script handles tsc automatically now', confidence: 0.9 },
|
|
150
|
+
{ action: 'SUPERSEDE', id: 'MEM001', superseded_by: 'MEM005' },
|
|
151
|
+
], 'execute-task', 'M001/S01/T02');
|
|
152
|
+
|
|
153
|
+
active = getActiveMemoriesRanked(30);
|
|
154
|
+
assertEq(active.length, 4, 'phase 3: 4 active (1 superseded, 1 created)');
|
|
155
|
+
assertTrue(!active.find(m => m.id === 'MEM001'), 'MEM001 should be superseded');
|
|
156
|
+
assertTrue(!!active.find(m => m.id === 'MEM005'), 'MEM005 should be active');
|
|
157
|
+
|
|
158
|
+
// Verify ranking: MEM003 (0.85) > MEM005 (0.9) but MEM002 has 1 hit
|
|
159
|
+
// MEM002: 0.8 * (1 + 1*0.1) = 0.88
|
|
160
|
+
// MEM003: 0.85 * 1.0 = 0.85
|
|
161
|
+
// MEM005: 0.9 * 1.0 = 0.9
|
|
162
|
+
// MEM004: 0.75 * 1.0 = 0.75
|
|
163
|
+
assertEq(active[0].id, 'MEM005', 'MEM005 should rank first (0.9)');
|
|
164
|
+
assertEq(active[1].id, 'MEM002', 'MEM002 should rank second (0.88)');
|
|
165
|
+
|
|
166
|
+
closeDatabase();
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
170
|
+
// memory-extractor: _resetExtractionState
|
|
171
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
172
|
+
|
|
173
|
+
console.log('\n=== memory-extractor: reset extraction state ===');
|
|
174
|
+
{
|
|
175
|
+
// Just verify it doesn't throw
|
|
176
|
+
_resetExtractionState();
|
|
177
|
+
assertTrue(true, '_resetExtractionState should not throw');
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
report();
|