@tienne/gestalt 0.84.2 → 0.86.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/CLAUDE.md +4 -4
- package/README.ko.md +72 -22
- package/README.md +73 -23
- package/dist/benchmarks/runners/passthrough-benchmark-runner.d.ts.map +1 -1
- package/dist/benchmarks/runners/passthrough-benchmark-runner.js +8 -7
- package/dist/benchmarks/runners/passthrough-benchmark-runner.js.map +1 -1
- package/dist/bin/gestalt.js +2 -2
- package/dist/package.json +4 -3
- package/dist/plugin/review-agents/harness-reviewer/AGENT.md +21 -1
- package/dist/plugin/skills/dispatch/SKILL.md +14 -2
- package/dist/plugin/skills/execute/SKILL.md +219 -52
- package/dist/plugin/skills/interview/SKILL.md +15 -2
- package/dist/plugin/skills/solve/SKILL.md +20 -5
- package/dist/plugin/skills/spec/SKILL.md +2 -0
- package/dist/src/cli/commands/interview.d.ts.map +1 -1
- package/dist/src/cli/commands/interview.js +16 -4
- package/dist/src/cli/commands/interview.js.map +1 -1
- package/dist/src/cli/commands/memory-merge.d.ts +2 -0
- package/dist/src/cli/commands/memory-merge.d.ts.map +1 -0
- package/dist/src/cli/commands/memory-merge.js +13 -0
- package/dist/src/cli/commands/memory-merge.js.map +1 -0
- package/dist/src/cli/index.d.ts.map +1 -1
- package/dist/src/cli/index.js +7 -0
- package/dist/src/cli/index.js.map +1 -1
- package/dist/src/core/constants.d.ts +4 -0
- package/dist/src/core/constants.d.ts.map +1 -1
- package/dist/src/core/constants.js +8 -0
- package/dist/src/core/constants.js.map +1 -1
- package/dist/src/core/errors.d.ts +3 -0
- package/dist/src/core/errors.d.ts.map +1 -1
- package/dist/src/core/errors.js +7 -0
- package/dist/src/core/errors.js.map +1 -1
- package/dist/src/core/file-lock.d.ts +37 -0
- package/dist/src/core/file-lock.d.ts.map +1 -0
- package/dist/src/core/file-lock.js +143 -0
- package/dist/src/core/file-lock.js.map +1 -0
- package/dist/src/core/json-file.d.ts +40 -0
- package/dist/src/core/json-file.d.ts.map +1 -0
- package/dist/src/core/json-file.js +82 -0
- package/dist/src/core/json-file.js.map +1 -0
- package/dist/src/core/types.d.ts +86 -9
- package/dist/src/core/types.d.ts.map +1 -1
- package/dist/src/events/store.d.ts +6 -0
- package/dist/src/events/store.d.ts.map +1 -1
- package/dist/src/events/store.js +12 -0
- package/dist/src/events/store.js.map +1 -1
- package/dist/src/events/types.d.ts +7 -0
- package/dist/src/events/types.d.ts.map +1 -1
- package/dist/src/events/types.js +7 -0
- package/dist/src/events/types.js.map +1 -1
- package/dist/src/execute/artifact-verifier.d.ts +12 -0
- package/dist/src/execute/artifact-verifier.d.ts.map +1 -0
- package/dist/src/execute/artifact-verifier.js +211 -0
- package/dist/src/execute/artifact-verifier.js.map +1 -0
- package/dist/src/execute/drift-detector.d.ts +2 -0
- package/dist/src/execute/drift-detector.d.ts.map +1 -1
- package/dist/src/execute/drift-detector.js +9 -13
- package/dist/src/execute/drift-detector.js.map +1 -1
- package/dist/src/execute/orchestrators/evaluation.d.ts +5 -1
- package/dist/src/execute/orchestrators/evaluation.d.ts.map +1 -1
- package/dist/src/execute/orchestrators/evaluation.js +48 -41
- package/dist/src/execute/orchestrators/evaluation.js.map +1 -1
- package/dist/src/execute/orchestrators/evolution.d.ts +17 -3
- package/dist/src/execute/orchestrators/evolution.d.ts.map +1 -1
- package/dist/src/execute/orchestrators/evolution.js +102 -53
- package/dist/src/execute/orchestrators/evolution.js.map +1 -1
- package/dist/src/execute/orchestrators/execution.d.ts +17 -2
- package/dist/src/execute/orchestrators/execution.d.ts.map +1 -1
- package/dist/src/execute/orchestrators/execution.js +99 -3
- package/dist/src/execute/orchestrators/execution.js.map +1 -1
- package/dist/src/execute/orchestrators/planning.d.ts +18 -4
- package/dist/src/execute/orchestrators/planning.d.ts.map +1 -1
- package/dist/src/execute/orchestrators/planning.js +62 -21
- package/dist/src/execute/orchestrators/planning.js.map +1 -1
- package/dist/src/execute/orchestrators/types.d.ts +12 -1
- package/dist/src/execute/orchestrators/types.d.ts.map +1 -1
- package/dist/src/execute/passthrough-engine.d.ts +12 -5
- package/dist/src/execute/passthrough-engine.d.ts.map +1 -1
- package/dist/src/execute/passthrough-engine.js +10 -4
- package/dist/src/execute/passthrough-engine.js.map +1 -1
- package/dist/src/execute/repository.d.ts +11 -1
- package/dist/src/execute/repository.d.ts.map +1 -1
- package/dist/src/execute/repository.js +138 -19
- package/dist/src/execute/repository.js.map +1 -1
- package/dist/src/execute/session.d.ts +26 -4
- package/dist/src/execute/session.d.ts.map +1 -1
- package/dist/src/execute/session.js +165 -48
- package/dist/src/execute/session.js.map +1 -1
- package/dist/src/execute/structural-commands.d.ts +23 -0
- package/dist/src/execute/structural-commands.d.ts.map +1 -0
- package/dist/src/execute/structural-commands.js +139 -0
- package/dist/src/execute/structural-commands.js.map +1 -0
- package/dist/src/harness-review/collect.d.ts +1 -1
- package/dist/src/harness-review/collect.d.ts.map +1 -1
- package/dist/src/harness-review/collect.js +6 -1
- package/dist/src/harness-review/collect.js.map +1 -1
- package/dist/src/harness-review/rule-overlap.d.ts +3 -0
- package/dist/src/harness-review/rule-overlap.d.ts.map +1 -0
- package/dist/src/harness-review/rule-overlap.js +78 -0
- package/dist/src/harness-review/rule-overlap.js.map +1 -0
- package/dist/src/harness-review/types.d.ts +1 -1
- package/dist/src/harness-review/types.d.ts.map +1 -1
- package/dist/src/harness-review/types.js +1 -0
- package/dist/src/harness-review/types.js.map +1 -1
- package/dist/src/humanize/overlap.d.ts +116 -0
- package/dist/src/humanize/overlap.d.ts.map +1 -0
- package/dist/src/humanize/overlap.js +680 -0
- package/dist/src/humanize/overlap.js.map +1 -0
- package/dist/src/interview/engine.d.ts +1 -1
- package/dist/src/interview/engine.d.ts.map +1 -1
- package/dist/src/interview/engine.js +3 -3
- package/dist/src/interview/engine.js.map +1 -1
- package/dist/src/interview/passthrough-engine.d.ts +1 -1
- package/dist/src/interview/passthrough-engine.d.ts.map +1 -1
- package/dist/src/interview/passthrough-engine.js +20 -3
- package/dist/src/interview/passthrough-engine.js.map +1 -1
- package/dist/src/interview/repository.d.ts +8 -0
- package/dist/src/interview/repository.d.ts.map +1 -1
- package/dist/src/interview/repository.js +16 -1
- package/dist/src/interview/repository.js.map +1 -1
- package/dist/src/interview/session.d.ts +15 -2
- package/dist/src/interview/session.d.ts.map +1 -1
- package/dist/src/interview/session.js +67 -23
- package/dist/src/interview/session.js.map +1 -1
- package/dist/src/knowledge-base/embedding.d.ts +6 -1
- package/dist/src/knowledge-base/embedding.d.ts.map +1 -1
- package/dist/src/knowledge-base/embedding.js +19 -6
- package/dist/src/knowledge-base/embedding.js.map +1 -1
- package/dist/src/local-pr/registry.d.ts +1 -21
- package/dist/src/local-pr/registry.d.ts.map +1 -1
- package/dist/src/local-pr/registry.js +7 -113
- package/dist/src/local-pr/registry.js.map +1 -1
- package/dist/src/mcp/input-guard.d.ts +17 -0
- package/dist/src/mcp/input-guard.d.ts.map +1 -1
- package/dist/src/mcp/input-guard.js +1 -1
- package/dist/src/mcp/input-guard.js.map +1 -1
- package/dist/src/mcp/schemas.d.ts +62 -7
- package/dist/src/mcp/schemas.d.ts.map +1 -1
- package/dist/src/mcp/schemas.js +28 -1
- package/dist/src/mcp/schemas.js.map +1 -1
- package/dist/src/mcp/server.d.ts.map +1 -1
- package/dist/src/mcp/server.js +26 -40
- package/dist/src/mcp/server.js.map +1 -1
- package/dist/src/mcp/tools/execute/evaluate.d.ts.map +1 -1
- package/dist/src/mcp/tools/execute/evaluate.js +15 -9
- package/dist/src/mcp/tools/execute/evaluate.js.map +1 -1
- package/dist/src/mcp/tools/execute/evolve.d.ts +1 -0
- package/dist/src/mcp/tools/execute/evolve.d.ts.map +1 -1
- package/dist/src/mcp/tools/execute/evolve.js +102 -37
- package/dist/src/mcp/tools/execute/evolve.js.map +1 -1
- package/dist/src/mcp/tools/execute/execution.d.ts.map +1 -1
- package/dist/src/mcp/tools/execute/execution.js +66 -7
- package/dist/src/mcp/tools/execute/execution.js.map +1 -1
- package/dist/src/mcp/tools/execute/planning.d.ts.map +1 -1
- package/dist/src/mcp/tools/execute/planning.js +34 -2
- package/dist/src/mcp/tools/execute/planning.js.map +1 -1
- package/dist/src/mcp/tools/execute/roles.d.ts.map +1 -1
- package/dist/src/mcp/tools/execute/roles.js +13 -5
- package/dist/src/mcp/tools/execute/roles.js.map +1 -1
- package/dist/src/mcp/tools/execute/utility.d.ts.map +1 -1
- package/dist/src/mcp/tools/execute/utility.js +26 -0
- package/dist/src/mcp/tools/execute/utility.js.map +1 -1
- package/dist/src/mcp/tools/execute/utils.d.ts +1 -0
- package/dist/src/mcp/tools/execute/utils.d.ts.map +1 -1
- package/dist/src/mcp/tools/execute/utils.js +15 -0
- package/dist/src/mcp/tools/execute/utils.js.map +1 -1
- package/dist/src/mcp/tools/execute-passthrough.d.ts.map +1 -1
- package/dist/src/mcp/tools/execute-passthrough.js +21 -1
- package/dist/src/mcp/tools/execute-passthrough.js.map +1 -1
- package/dist/src/mcp/tools/interview-passthrough.d.ts +2 -1
- package/dist/src/mcp/tools/interview-passthrough.d.ts.map +1 -1
- package/dist/src/mcp/tools/interview-passthrough.js +4 -3
- package/dist/src/mcp/tools/interview-passthrough.js.map +1 -1
- package/dist/src/mcp/tools/interview.d.ts.map +1 -1
- package/dist/src/mcp/tools/interview.js +2 -1
- package/dist/src/mcp/tools/interview.js.map +1 -1
- package/dist/src/mcp/tools/review-passthrough.js +15 -19
- package/dist/src/mcp/tools/review-passthrough.js.map +1 -1
- package/dist/src/mcp/tools/spec-passthrough.d.ts.map +1 -1
- package/dist/src/mcp/tools/spec-passthrough.js +16 -8
- package/dist/src/mcp/tools/spec-passthrough.js.map +1 -1
- package/dist/src/mcp/tools/status.d.ts.map +1 -1
- package/dist/src/mcp/tools/status.js +4 -0
- package/dist/src/mcp/tools/status.js.map +1 -1
- package/dist/src/memory/memory-context-injector.d.ts +22 -8
- package/dist/src/memory/memory-context-injector.d.ts.map +1 -1
- package/dist/src/memory/memory-context-injector.js +85 -13
- package/dist/src/memory/memory-context-injector.js.map +1 -1
- package/dist/src/memory/memory-merge.d.ts +19 -0
- package/dist/src/memory/memory-merge.d.ts.map +1 -0
- package/dist/src/memory/memory-merge.js +120 -0
- package/dist/src/memory/memory-merge.js.map +1 -0
- package/dist/src/memory/project-memory-store.d.ts +15 -11
- package/dist/src/memory/project-memory-store.d.ts.map +1 -1
- package/dist/src/memory/project-memory-store.js +97 -120
- package/dist/src/memory/project-memory-store.js.map +1 -1
- package/dist/src/memory/semantic-search.d.ts +4 -1
- package/dist/src/memory/semantic-search.d.ts.map +1 -1
- package/dist/src/memory/semantic-search.js +66 -19
- package/dist/src/memory/semantic-search.js.map +1 -1
- package/dist/src/resilience/lateral.d.ts +8 -1
- package/dist/src/resilience/lateral.d.ts.map +1 -1
- package/dist/src/resilience/lateral.js +52 -4
- package/dist/src/resilience/lateral.js.map +1 -1
- package/package.json +4 -3
- package/plugin/.codex-plugin/plugin.json +1 -1
- package/plugin/.mcp.json +1 -1
- package/plugin/mcp.json +1 -1
- package/plugin/review-agents/harness-reviewer/AGENT.md +21 -1
- package/plugin/skills/dispatch/SKILL.md +14 -2
- package/plugin/skills/execute/SKILL.md +219 -52
- package/plugin/skills/interview/SKILL.md +15 -2
- package/plugin/skills/solve/SKILL.md +20 -5
- package/plugin/skills/spec/SKILL.md +2 -0
- package/dist/src/interview/mini-interview-engine.d.ts +0 -33
- package/dist/src/interview/mini-interview-engine.d.ts.map +0 -1
- package/dist/src/interview/mini-interview-engine.js +0 -102
- package/dist/src/interview/mini-interview-engine.js.map +0 -1
- package/dist/src/memory/user-profile-store.d.ts +0 -13
- package/dist/src/memory/user-profile-store.d.ts.map +0 -1
- package/dist/src/memory/user-profile-store.js +0 -85
- package/dist/src/memory/user-profile-store.js.map +0 -1
|
@@ -1,11 +1,13 @@
|
|
|
1
|
-
import type { ProjectMemory, ArchitectureDecision } from '../core/types.js';
|
|
1
|
+
import type { ProjectMemory, ArchitectureDecision, SpecHistoryEntry } from '../core/types.js';
|
|
2
|
+
interface SpecSummary {
|
|
3
|
+
goal: string;
|
|
4
|
+
specId: string;
|
|
5
|
+
createdAt: string;
|
|
6
|
+
sourceType: string;
|
|
7
|
+
}
|
|
2
8
|
export interface MemoryContext {
|
|
3
|
-
recentSpecs:
|
|
4
|
-
|
|
5
|
-
specId: string;
|
|
6
|
-
createdAt: string;
|
|
7
|
-
sourceType: string;
|
|
8
|
-
}>;
|
|
9
|
+
recentSpecs: SpecSummary[];
|
|
10
|
+
relatedSpecs: SpecSummary[];
|
|
9
11
|
architectureDecisions: ArchitectureDecision[];
|
|
10
12
|
recentExecutions: Array<{
|
|
11
13
|
specId: string;
|
|
@@ -15,12 +17,24 @@ export interface MemoryContext {
|
|
|
15
17
|
}>;
|
|
16
18
|
hasContext: boolean;
|
|
17
19
|
}
|
|
18
|
-
export
|
|
20
|
+
export type SpecSearchFn = (query: string, memory: ProjectMemory, topK: number, minScore: number) => Promise<SpecHistoryEntry[]>;
|
|
21
|
+
export interface RelatedSpecOptions {
|
|
22
|
+
search?: SpecSearchFn;
|
|
23
|
+
timeoutMs?: number;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* 최근 창 밖으로 밀려난 스펙 중 주제와 비슷한 것을 찾는다.
|
|
27
|
+
* 임베딩 모델을 못 불러오거나 제한 시간을 넘기면 빈 배열을 돌려준다.
|
|
28
|
+
*/
|
|
29
|
+
export declare function findRelatedSpecs(memory: ProjectMemory, topic: string, options?: RelatedSpecOptions): Promise<SpecSummary[]>;
|
|
30
|
+
export declare function buildMemoryContext(memory: ProjectMemory, relatedSpecs?: SpecSummary[]): MemoryContext;
|
|
19
31
|
export declare function formatMemoryContextForPrompt(context: MemoryContext): string;
|
|
20
32
|
export declare class MemoryContextInjector {
|
|
21
33
|
private store;
|
|
22
34
|
constructor(cwd?: string);
|
|
23
35
|
getContext(): MemoryContext;
|
|
36
|
+
getContextForTopic(topic: string, options?: RelatedSpecOptions): Promise<MemoryContext>;
|
|
24
37
|
formatForPrompt(): string;
|
|
25
38
|
}
|
|
39
|
+
export {};
|
|
26
40
|
//# sourceMappingURL=memory-context-injector.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"memory-context-injector.d.ts","sourceRoot":"","sources":["../../../src/memory/memory-context-injector.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"memory-context-injector.d.ts","sourceRoot":"","sources":["../../../src/memory/memory-context-injector.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,oBAAoB,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAY9F,UAAU,WAAW;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,aAAa;IAC5B,WAAW,EAAE,WAAW,EAAE,CAAC;IAC3B,YAAY,EAAE,WAAW,EAAE,CAAC;IAC5B,qBAAqB,EAAE,oBAAoB,EAAE,CAAC;IAC9C,gBAAgB,EAAE,KAAK,CAAC;QACtB,MAAM,EAAE,MAAM,CAAC;QACf,cAAc,EAAE,MAAM,CAAC;QACvB,WAAW,EAAE,MAAM,CAAC;QACpB,WAAW,EAAE,MAAM,CAAC;KACrB,CAAC,CAAC;IACH,UAAU,EAAE,OAAO,CAAC;CACrB;AAED,MAAM,MAAM,YAAY,GAAG,CACzB,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,aAAa,EACrB,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,KACb,OAAO,CAAC,gBAAgB,EAAE,CAAC,CAAC;AAEjC,MAAM,WAAW,kBAAkB;IACjC,MAAM,CAAC,EAAE,YAAY,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAWD;;;GAGG;AACH,wBAAsB,gBAAgB,CACpC,MAAM,EAAE,aAAa,EACrB,KAAK,EAAE,MAAM,EACb,OAAO,GAAE,kBAAuB,GAC/B,OAAO,CAAC,WAAW,EAAE,CAAC,CA4BxB;AAED,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,aAAa,EACrB,YAAY,GAAE,WAAW,EAAO,GAC/B,aAAa,CAuBf;AAiBD,wBAAgB,4BAA4B,CAAC,OAAO,EAAE,aAAa,GAAG,MAAM,CAsC3E;AAED,qBAAa,qBAAqB;IAChC,OAAO,CAAC,KAAK,CAAqB;gBAEtB,GAAG,CAAC,EAAE,MAAM;IAIxB,UAAU,IAAI,aAAa;IAKrB,kBAAkB,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,kBAAkB,GAAG,OAAO,CAAC,aAAa,CAAC;IAM7F,eAAe,IAAI,MAAM;CAI1B"}
|
|
@@ -1,27 +1,87 @@
|
|
|
1
|
+
import { log } from '../core/log.js';
|
|
1
2
|
import { ProjectMemoryStore } from './project-memory-store.js';
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
3
|
+
const RECENT_SPEC_WINDOW = 5;
|
|
4
|
+
const RELATED_SPEC_TOP_K = 3;
|
|
5
|
+
// all-MiniLM-L6-v2에서 주제가 겹치는 문장은 0.5 위로, 무관한 문장은 0.2 아래로 갈린다
|
|
6
|
+
const RELATED_SPEC_MIN_SCORE = 0.4;
|
|
7
|
+
// 모델을 처음 받는 환경에서는 수십 초가 걸린다. 인터뷰 시작을 그만큼 붙잡지 않고
|
|
8
|
+
// 이번엔 최근 스펙만 넣는다. 로딩은 서버 프로세스 안에서 계속 돌고 끝난 뒤의 start부터 쓰인다
|
|
9
|
+
const RELATED_SPEC_TIMEOUT_MS = 3000;
|
|
10
|
+
function toSummary(s) {
|
|
11
|
+
return { goal: s.goal, specId: s.specId, createdAt: s.createdAt, sourceType: s.sourceType };
|
|
12
|
+
}
|
|
13
|
+
async function defaultSearch(...args) {
|
|
14
|
+
const { searchSimilarSpecs } = await import('./semantic-search.js');
|
|
15
|
+
return searchSimilarSpecs(...args);
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* 최근 창 밖으로 밀려난 스펙 중 주제와 비슷한 것을 찾는다.
|
|
19
|
+
* 임베딩 모델을 못 불러오거나 제한 시간을 넘기면 빈 배열을 돌려준다.
|
|
20
|
+
*/
|
|
21
|
+
export async function findRelatedSpecs(memory, topic, options = {}) {
|
|
22
|
+
// 최근 창 안의 스펙은 recentSpecs로 이미 들어가므로 그 앞쪽만 뒤진다
|
|
23
|
+
const older = memory.specHistory.slice(0, -RECENT_SPEC_WINDOW);
|
|
24
|
+
if (older.length === 0)
|
|
25
|
+
return [];
|
|
26
|
+
const search = options.search ?? defaultSearch;
|
|
27
|
+
const timeoutMs = options.timeoutMs ?? RELATED_SPEC_TIMEOUT_MS;
|
|
28
|
+
let timer;
|
|
29
|
+
const timeout = new Promise((resolve) => {
|
|
30
|
+
timer = setTimeout(() => resolve(null), timeoutMs);
|
|
31
|
+
});
|
|
32
|
+
try {
|
|
33
|
+
const found = await Promise.race([
|
|
34
|
+
search(topic, { ...memory, specHistory: older }, RELATED_SPEC_TOP_K, RELATED_SPEC_MIN_SCORE),
|
|
35
|
+
timeout,
|
|
36
|
+
]);
|
|
37
|
+
if (found === null) {
|
|
38
|
+
log(`memory: related spec search timed out after ${timeoutMs}ms, using recent specs only`);
|
|
39
|
+
return [];
|
|
40
|
+
}
|
|
41
|
+
return found.map(toSummary);
|
|
42
|
+
}
|
|
43
|
+
catch (e) {
|
|
44
|
+
log(`memory: related spec search unavailable (${e instanceof Error ? e.message : String(e)})`);
|
|
45
|
+
return [];
|
|
46
|
+
}
|
|
47
|
+
finally {
|
|
48
|
+
clearTimeout(timer);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
export function buildMemoryContext(memory, relatedSpecs = []) {
|
|
52
|
+
const recentSpecs = memory.specHistory.slice(-RECENT_SPEC_WINDOW).map(toSummary);
|
|
9
53
|
const recentExecutions = memory.executionHistory.slice(-3).map((e) => ({
|
|
10
54
|
specId: e.specId,
|
|
11
|
-
completedTasks: e.completedTasks.length,
|
|
12
|
-
failedTasks: e.failedTasks.length,
|
|
55
|
+
completedTasks: Array.isArray(e.completedTasks) ? e.completedTasks.length : 0,
|
|
56
|
+
failedTasks: Array.isArray(e.failedTasks) ? e.failedTasks.length : 0,
|
|
13
57
|
completedAt: e.completedAt,
|
|
14
58
|
}));
|
|
15
59
|
const hasContext = recentSpecs.length > 0 ||
|
|
60
|
+
relatedSpecs.length > 0 ||
|
|
16
61
|
memory.architectureDecisions.length > 0 ||
|
|
17
62
|
recentExecutions.length > 0;
|
|
18
63
|
return {
|
|
19
64
|
recentSpecs,
|
|
65
|
+
relatedSpecs,
|
|
20
66
|
architectureDecisions: memory.architectureDecisions,
|
|
21
67
|
recentExecutions,
|
|
22
68
|
hasContext,
|
|
23
69
|
};
|
|
24
70
|
}
|
|
71
|
+
// memory.json은 팀이 커밋해 공유하고 merge driver가 남의 브랜치 항목을 그대로 합친다.
|
|
72
|
+
// 값 안의 개행이나 헤더가 시스템 프롬프트의 새 섹션으로 읽히지 않게 한 줄로 접는다
|
|
73
|
+
const MAX_FIELD_LENGTH = 300;
|
|
74
|
+
// 구조 검증은 merge driver에만 있어 fast-forward로 들어온 파일은 그대로 읽힌다.
|
|
75
|
+
// 문자열이 아닌 값이 와도 인터뷰 시작이 죽지 않게 여기서 문자열로 바꾼다.
|
|
76
|
+
// 먼저 잘라야 긴 공백 입력에서도 정규식 비용이 길이 상한 안에 머문다
|
|
77
|
+
function oneLine(value, maxLength = MAX_FIELD_LENGTH) {
|
|
78
|
+
return String(value ?? '')
|
|
79
|
+
.slice(0, maxLength * 2)
|
|
80
|
+
.replace(/\s+/g, ' ')
|
|
81
|
+
.replace(/^ ?#+ ?/, '')
|
|
82
|
+
.trim()
|
|
83
|
+
.slice(0, maxLength);
|
|
84
|
+
}
|
|
25
85
|
export function formatMemoryContextForPrompt(context) {
|
|
26
86
|
if (!context.hasContext)
|
|
27
87
|
return '';
|
|
@@ -29,20 +89,27 @@ export function formatMemoryContextForPrompt(context) {
|
|
|
29
89
|
if (context.recentSpecs.length > 0) {
|
|
30
90
|
lines.push('\n### Recent Specs');
|
|
31
91
|
for (const s of context.recentSpecs) {
|
|
32
|
-
lines.push(`- [${s.createdAt
|
|
92
|
+
lines.push(`- [${oneLine(s.createdAt, 10)}] ${oneLine(s.goal)}`);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
if (context.relatedSpecs.length > 0) {
|
|
96
|
+
lines.push('\n### Related Past Specs');
|
|
97
|
+
for (const s of context.relatedSpecs) {
|
|
98
|
+
lines.push(`- [${oneLine(s.createdAt, 10)}] ${oneLine(s.goal)}`);
|
|
33
99
|
}
|
|
34
100
|
}
|
|
35
101
|
if (context.architectureDecisions.length > 0) {
|
|
36
102
|
lines.push('\n### Architecture Decisions');
|
|
37
103
|
for (const d of context.architectureDecisions) {
|
|
38
|
-
const rationale = d.rationale ? ` (${d.rationale})` : '';
|
|
39
|
-
|
|
104
|
+
const rationale = d.rationale ? ` (${oneLine(d.rationale)})` : '';
|
|
105
|
+
const outcome = d.outcome ? ` → Outcome: ${oneLine(d.outcome)}` : '';
|
|
106
|
+
lines.push(`- ${oneLine(d.decision)}${rationale}${outcome}`);
|
|
40
107
|
}
|
|
41
108
|
}
|
|
42
109
|
if (context.recentExecutions.length > 0) {
|
|
43
110
|
lines.push('\n### Recent Execution History');
|
|
44
111
|
for (const e of context.recentExecutions) {
|
|
45
|
-
lines.push(`- Spec ${e.specId
|
|
112
|
+
lines.push(`- Spec ${oneLine(e.specId, 8)}: ${e.completedTasks} tasks completed, ${e.failedTasks} failed (${oneLine(e.completedAt, 10)})`);
|
|
46
113
|
}
|
|
47
114
|
}
|
|
48
115
|
return lines.join('\n');
|
|
@@ -56,6 +123,11 @@ export class MemoryContextInjector {
|
|
|
56
123
|
const memory = this.store.read();
|
|
57
124
|
return buildMemoryContext(memory);
|
|
58
125
|
}
|
|
126
|
+
async getContextForTopic(topic, options) {
|
|
127
|
+
const memory = this.store.read();
|
|
128
|
+
const related = await findRelatedSpecs(memory, topic, options);
|
|
129
|
+
return buildMemoryContext(memory, related);
|
|
130
|
+
}
|
|
59
131
|
formatForPrompt() {
|
|
60
132
|
const context = this.getContext();
|
|
61
133
|
return formatMemoryContextForPrompt(context);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"memory-context-injector.js","sourceRoot":"","sources":["../../../src/memory/memory-context-injector.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;
|
|
1
|
+
{"version":3,"file":"memory-context-injector.js","sourceRoot":"","sources":["../../../src/memory/memory-context-injector.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,GAAG,EAAE,MAAM,gBAAgB,CAAC;AACrC,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAE/D,MAAM,kBAAkB,GAAG,CAAC,CAAC;AAC7B,MAAM,kBAAkB,GAAG,CAAC,CAAC;AAC7B,6DAA6D;AAC7D,MAAM,sBAAsB,GAAG,GAAG,CAAC;AACnC,gDAAgD;AAChD,0DAA0D;AAC1D,MAAM,uBAAuB,GAAG,IAAI,CAAC;AAkCrC,SAAS,SAAS,CAAC,CAAmB;IACpC,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,SAAS,EAAE,CAAC,CAAC,SAAS,EAAE,UAAU,EAAE,CAAC,CAAC,UAAU,EAAE,CAAC;AAC9F,CAAC;AAED,KAAK,UAAU,aAAa,CAAC,GAAG,IAA8B;IAC5D,MAAM,EAAE,kBAAkB,EAAE,GAAG,MAAM,MAAM,CAAC,sBAAsB,CAAC,CAAC;IACpE,OAAO,kBAAkB,CAAC,GAAG,IAAI,CAAC,CAAC;AACrC,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB,CACpC,MAAqB,EACrB,KAAa,EACb,UAA8B,EAAE;IAEhC,8CAA8C;IAC9C,MAAM,KAAK,GAAG,MAAM,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,kBAAkB,CAAC,CAAC;IAC/D,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IAElC,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,IAAI,aAAa,CAAC;IAC/C,MAAM,SAAS,GAAG,OAAO,CAAC,SAAS,IAAI,uBAAuB,CAAC;IAC/D,IAAI,KAAiC,CAAC;IACtC,MAAM,OAAO,GAAG,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE;QAC5C,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,SAAS,CAAC,CAAC;IACrD,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC;QACH,MAAM,KAAK,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC;YAC/B,MAAM,CAAC,KAAK,EAAE,EAAE,GAAG,MAAM,EAAE,WAAW,EAAE,KAAK,EAAE,EAAE,kBAAkB,EAAE,sBAAsB,CAAC;YAC5F,OAAO;SACR,CAAC,CAAC;QACH,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;YACnB,GAAG,CAAC,+CAA+C,SAAS,6BAA6B,CAAC,CAAC;YAC3F,OAAO,EAAE,CAAC;QACZ,CAAC;QACD,OAAO,KAAK,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;IAC9B,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,GAAG,CAAC,4CAA4C,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;QAC/F,OAAO,EAAE,CAAC;IACZ,CAAC;YAAS,CAAC;QACT,YAAY,CAAC,KAAK,CAAC,CAAC;IACtB,CAAC;AACH,CAAC;AAED,MAAM,UAAU,kBAAkB,CAChC,MAAqB,EACrB,eAA8B,EAAE;IAEhC,MAAM,WAAW,GAAG,MAAM,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC,kBAAkB,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;IAEjF,MAAM,gBAAgB,GAAG,MAAM,CAAC,gBAAgB,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QACrE,MAAM,EAAE,CAAC,CAAC,MAAM;QAChB,cAAc,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;QAC7E,WAAW,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;QACpE,WAAW,EAAE,CAAC,CAAC,WAAW;KAC3B,CAAC,CAAC,CAAC;IAEJ,MAAM,UAAU,GACd,WAAW,CAAC,MAAM,GAAG,CAAC;QACtB,YAAY,CAAC,MAAM,GAAG,CAAC;QACvB,MAAM,CAAC,qBAAqB,CAAC,MAAM,GAAG,CAAC;QACvC,gBAAgB,CAAC,MAAM,GAAG,CAAC,CAAC;IAE9B,OAAO;QACL,WAAW;QACX,YAAY;QACZ,qBAAqB,EAAE,MAAM,CAAC,qBAAqB;QACnD,gBAAgB;QAChB,UAAU;KACX,CAAC;AACJ,CAAC;AAED,6DAA6D;AAC7D,iDAAiD;AACjD,MAAM,gBAAgB,GAAG,GAAG,CAAC;AAC7B,0DAA0D;AAC1D,4CAA4C;AAC5C,yCAAyC;AACzC,SAAS,OAAO,CAAC,KAAc,EAAE,SAAS,GAAG,gBAAgB;IAC3D,OAAO,MAAM,CAAC,KAAK,IAAI,EAAE,CAAC;SACvB,KAAK,CAAC,CAAC,EAAE,SAAS,GAAG,CAAC,CAAC;SACvB,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC;SACpB,OAAO,CAAC,SAAS,EAAE,EAAE,CAAC;SACtB,IAAI,EAAE;SACN,KAAK,CAAC,CAAC,EAAE,SAAS,CAAC,CAAC;AACzB,CAAC;AAED,MAAM,UAAU,4BAA4B,CAAC,OAAsB;IACjE,IAAI,CAAC,OAAO,CAAC,UAAU;QAAE,OAAO,EAAE,CAAC;IAEnC,MAAM,KAAK,GAAa,CAAC,0BAA0B,CAAC,CAAC;IAErD,IAAI,OAAO,CAAC,WAAW,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACnC,KAAK,CAAC,IAAI,CAAC,oBAAoB,CAAC,CAAC;QACjC,KAAK,MAAM,CAAC,IAAI,OAAO,CAAC,WAAW,EAAE,CAAC;YACpC,KAAK,CAAC,IAAI,CAAC,MAAM,OAAO,CAAC,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACnE,CAAC;IACH,CAAC;IAED,IAAI,OAAO,CAAC,YAAY,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACpC,KAAK,CAAC,IAAI,CAAC,0BAA0B,CAAC,CAAC;QACvC,KAAK,MAAM,CAAC,IAAI,OAAO,CAAC,YAAY,EAAE,CAAC;YACrC,KAAK,CAAC,IAAI,CAAC,MAAM,OAAO,CAAC,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACnE,CAAC;IACH,CAAC;IAED,IAAI,OAAO,CAAC,qBAAqB,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC7C,KAAK,CAAC,IAAI,CAAC,8BAA8B,CAAC,CAAC;QAC3C,KAAK,MAAM,CAAC,IAAI,OAAO,CAAC,qBAAqB,EAAE,CAAC;YAC9C,MAAM,SAAS,GAAG,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;YAClE,MAAM,OAAO,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,eAAe,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YACrE,KAAK,CAAC,IAAI,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,QAAQ,CAAC,GAAG,SAAS,GAAG,OAAO,EAAE,CAAC,CAAC;QAC/D,CAAC;IACH,CAAC;IAED,IAAI,OAAO,CAAC,gBAAgB,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACxC,KAAK,CAAC,IAAI,CAAC,gCAAgC,CAAC,CAAC;QAC7C,KAAK,MAAM,CAAC,IAAI,OAAO,CAAC,gBAAgB,EAAE,CAAC;YACzC,KAAK,CAAC,IAAI,CACR,UAAU,OAAO,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,cAAc,qBAAqB,CAAC,CAAC,WAAW,YAAY,OAAO,CAAC,CAAC,CAAC,WAAW,EAAE,EAAE,CAAC,GAAG,CAC/H,CAAC;QACJ,CAAC;IACH,CAAC;IAED,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC;AAED,MAAM,OAAO,qBAAqB;IACxB,KAAK,CAAqB;IAElC,YAAY,GAAY;QACtB,IAAI,CAAC,KAAK,GAAG,IAAI,kBAAkB,CAAC,GAAG,CAAC,CAAC;IAC3C,CAAC;IAED,UAAU;QACR,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC;QACjC,OAAO,kBAAkB,CAAC,MAAM,CAAC,CAAC;IACpC,CAAC;IAED,KAAK,CAAC,kBAAkB,CAAC,KAAa,EAAE,OAA4B;QAClE,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC;QACjC,MAAM,OAAO,GAAG,MAAM,gBAAgB,CAAC,MAAM,EAAE,KAAK,EAAE,OAAO,CAAC,CAAC;QAC/D,OAAO,kBAAkB,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC7C,CAAC;IAED,eAAe;QACb,MAAM,OAAO,GAAG,IAAI,CAAC,UAAU,EAAE,CAAC;QAClC,OAAO,4BAA4B,CAAC,OAAO,CAAC,CAAC;IAC/C,CAAC;CACF"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { ProjectMemory } from '../core/types.js';
|
|
2
|
+
/**
|
|
3
|
+
* local과 remote ProjectMemory를 합친다. specHistory, executionHistory,
|
|
4
|
+
* architectureDecisions는 쌓기만 하는 기록이라 공통 조상 없이 합집합으로 충분하다.
|
|
5
|
+
* compressedContexts는 같은 세션 요약을 덮어써 갱신하므로 더 최신 쪽을 고른다.
|
|
6
|
+
*
|
|
7
|
+
* - specHistory: specId 기준, executionHistory: executeSessionId 기준
|
|
8
|
+
* - architectureDecisions: decision 기준. 한쪽에만 outcome이 있으면 그쪽을 남긴다
|
|
9
|
+
* - compressedContexts: sessionId 기준. compressedAt이 더 최신인 쪽을 남긴다
|
|
10
|
+
* - 그 밖에 같은 키가 양쪽에 있으면 remote가 이긴다. lastUpdated는 더 최신 값
|
|
11
|
+
*/
|
|
12
|
+
export declare function mergeMemory(local: ProjectMemory, remote: ProjectMemory): ProjectMemory;
|
|
13
|
+
/**
|
|
14
|
+
* git merge driver 진입점. `%A`(현재 브랜치) 자리에 합친 결과를 쓴다.
|
|
15
|
+
* 어느 쪽이든 JSON으로 못 읽거나 구조가 맞지 않으면 쓰기 전에 던진다 — 호출부가
|
|
16
|
+
* 0이 아닌 코드로 끝내야 git이 그 경로를 충돌로 남기고 사람에게 넘긴다.
|
|
17
|
+
*/
|
|
18
|
+
export declare function runMemoryMergeDriver(oursPath: string, theirsPath: string): ProjectMemory;
|
|
19
|
+
//# sourceMappingURL=memory-merge.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"memory-merge.d.ts","sourceRoot":"","sources":["../../../src/memory/memory-merge.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,aAAa,EAId,MAAM,kBAAkB,CAAC;AAO1B;;;;;;;;;GASG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,aAAa,EAAE,MAAM,EAAE,aAAa,GAAG,aAAa,CAgDtF;AAyED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,aAAa,CAOxF"}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { readFileSync, renameSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
function unionBy(remote, local, key) {
|
|
4
|
+
const seen = new Set(remote.map(key));
|
|
5
|
+
return [...remote, ...local.filter((item) => !seen.has(key(item)))];
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* local과 remote ProjectMemory를 합친다. specHistory, executionHistory,
|
|
9
|
+
* architectureDecisions는 쌓기만 하는 기록이라 공통 조상 없이 합집합으로 충분하다.
|
|
10
|
+
* compressedContexts는 같은 세션 요약을 덮어써 갱신하므로 더 최신 쪽을 고른다.
|
|
11
|
+
*
|
|
12
|
+
* - specHistory: specId 기준, executionHistory: executeSessionId 기준
|
|
13
|
+
* - architectureDecisions: decision 기준. 한쪽에만 outcome이 있으면 그쪽을 남긴다
|
|
14
|
+
* - compressedContexts: sessionId 기준. compressedAt이 더 최신인 쪽을 남긴다
|
|
15
|
+
* - 그 밖에 같은 키가 양쪽에 있으면 remote가 이긴다. lastUpdated는 더 최신 값
|
|
16
|
+
*/
|
|
17
|
+
export function mergeMemory(local, remote) {
|
|
18
|
+
const specHistory = unionBy(remote.specHistory, local.specHistory, (s) => s.specId);
|
|
19
|
+
const executionHistory = unionBy(remote.executionHistory, local.executionHistory, (e) => e.executeSessionId);
|
|
20
|
+
// addArchitectureDecision이 decision 문자열로 중복을 막으므로 여기서도 같은 키를 쓴다.
|
|
21
|
+
// timestamp까지 키에 넣으면 양쪽에서 같은 결정을 따로 기록했을 때 둘 다 남는다
|
|
22
|
+
const localDecisions = new Map(local.architectureDecisions.map((d) => [d.decision, d]));
|
|
23
|
+
const architectureDecisions = unionBy(remote.architectureDecisions, local.architectureDecisions, (d) => d.decision).map((d) => {
|
|
24
|
+
const other = localDecisions.get(d.decision);
|
|
25
|
+
return !d.outcome && other?.outcome ? other : d;
|
|
26
|
+
});
|
|
27
|
+
const localContexts = new Map((local.compressedContexts ?? []).map((c) => [c.sessionId, c]));
|
|
28
|
+
const compressedContexts = unionBy(remote.compressedContexts ?? [], local.compressedContexts ?? [], (c) => c.sessionId).map((c) => {
|
|
29
|
+
const other = localContexts.get(c.sessionId);
|
|
30
|
+
return other && other.compressedAt > c.compressedAt ? other : c;
|
|
31
|
+
});
|
|
32
|
+
// 스키마가 lastUpdated를 빠뜨린 파일도 받으므로 빈 값은 더 오래된 것으로 친다
|
|
33
|
+
const lastUpdated = (local.lastUpdated ?? '') > (remote.lastUpdated ?? '') ? local.lastUpdated : remote.lastUpdated;
|
|
34
|
+
return {
|
|
35
|
+
version: local.version,
|
|
36
|
+
repoRoot: local.repoRoot,
|
|
37
|
+
specHistory,
|
|
38
|
+
executionHistory,
|
|
39
|
+
architectureDecisions,
|
|
40
|
+
compressedContexts: compressedContexts.length > 0 ? compressedContexts : undefined,
|
|
41
|
+
lastUpdated,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
// 합친 결과는 사람 검토 없이 커밋된다. 머지 키와 날짜 비교에 쓰는 필드, 그리고 인터뷰를 시작할 때
|
|
45
|
+
// 프롬프트에 싣는 필드는 여기서 형태를 확인한다. 그 밖의 필드는 손대지 않고 그대로 넘긴다
|
|
46
|
+
const memoryFileSchema = z
|
|
47
|
+
.object({
|
|
48
|
+
specHistory: z.array(z.object({ specId: z.string(), goal: z.string(), createdAt: z.string() }).passthrough()),
|
|
49
|
+
executionHistory: z.array(z
|
|
50
|
+
.object({
|
|
51
|
+
executeSessionId: z.string(),
|
|
52
|
+
specId: z.string(),
|
|
53
|
+
completedAt: z.string(),
|
|
54
|
+
completedTasks: z.array(z.unknown()),
|
|
55
|
+
failedTasks: z.array(z.unknown()),
|
|
56
|
+
})
|
|
57
|
+
.passthrough()),
|
|
58
|
+
architectureDecisions: z.array(z
|
|
59
|
+
.object({
|
|
60
|
+
decision: z.string(),
|
|
61
|
+
rationale: z.string().optional(),
|
|
62
|
+
outcome: z.string().optional(),
|
|
63
|
+
})
|
|
64
|
+
.passthrough()),
|
|
65
|
+
compressedContexts: z
|
|
66
|
+
.array(z.object({ sessionId: z.string(), compressedAt: z.string() }).passthrough())
|
|
67
|
+
.optional(),
|
|
68
|
+
lastUpdated: z.string().optional(),
|
|
69
|
+
})
|
|
70
|
+
.passthrough();
|
|
71
|
+
function shapeError(path, at) {
|
|
72
|
+
const where = at.length > 0 ? at.map(String).join('.') : '(root)';
|
|
73
|
+
return new Error(`${path}: unexpected memory.json shape at ${where}`);
|
|
74
|
+
}
|
|
75
|
+
// ProjectMemoryStore.load()의 v1 마이그레이션과 같은 일을 하되 두 군데가 다르다.
|
|
76
|
+
// timestamp를 실행 시각 대신 파일의 lastUpdated로 채워 같은 입력이면 같은 머지 결과가 나오게 한다.
|
|
77
|
+
// store는 배열이 아닌 필드를 빈 배열로 바꿔 읽지만 여기서는 거부한다. 합친 결과가 그대로 커밋되므로
|
|
78
|
+
// 망가진 값을 조용히 지우면 사람이 알아챌 자리가 없다. 드라이버는 git이 넘겨준 임시 파일을 읽으므로 store를 거치지 못한다
|
|
79
|
+
function readMemoryFile(path) {
|
|
80
|
+
let raw;
|
|
81
|
+
try {
|
|
82
|
+
raw = JSON.parse(readFileSync(path, 'utf-8'));
|
|
83
|
+
}
|
|
84
|
+
catch (e) {
|
|
85
|
+
throw new Error(`${path}: ${e instanceof Error ? e.message : String(e)}`, { cause: e });
|
|
86
|
+
}
|
|
87
|
+
// 마이그레이션이 필드를 건드리기 전에 막아야 원시 TypeError 대신 어디가 틀렸는지가 남는다
|
|
88
|
+
if (typeof raw !== 'object' || raw === null || Array.isArray(raw))
|
|
89
|
+
throw shapeError(path, []);
|
|
90
|
+
const parsed = raw;
|
|
91
|
+
if (parsed.architectureDecisions !== undefined && !Array.isArray(parsed.architectureDecisions)) {
|
|
92
|
+
throw shapeError(path, ['architectureDecisions']);
|
|
93
|
+
}
|
|
94
|
+
parsed.architectureDecisions = (parsed.architectureDecisions ?? []).map((item) => typeof item === 'string'
|
|
95
|
+
? { decision: item, rationale: '', specId: '', timestamp: parsed.lastUpdated ?? '' }
|
|
96
|
+
: item);
|
|
97
|
+
// null은 채우지 않고 스키마에 걸리게 둔다. 빠진 필드만 빈 배열로 본다
|
|
98
|
+
if (parsed.specHistory === undefined)
|
|
99
|
+
parsed.specHistory = [];
|
|
100
|
+
if (parsed.executionHistory === undefined)
|
|
101
|
+
parsed.executionHistory = [];
|
|
102
|
+
const checked = memoryFileSchema.safeParse(parsed);
|
|
103
|
+
if (!checked.success)
|
|
104
|
+
throw shapeError(path, checked.error.issues[0]?.path ?? []);
|
|
105
|
+
return parsed;
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* git merge driver 진입점. `%A`(현재 브랜치) 자리에 합친 결과를 쓴다.
|
|
109
|
+
* 어느 쪽이든 JSON으로 못 읽거나 구조가 맞지 않으면 쓰기 전에 던진다 — 호출부가
|
|
110
|
+
* 0이 아닌 코드로 끝내야 git이 그 경로를 충돌로 남기고 사람에게 넘긴다.
|
|
111
|
+
*/
|
|
112
|
+
export function runMemoryMergeDriver(oursPath, theirsPath) {
|
|
113
|
+
const merged = mergeMemory(readMemoryFile(oursPath), readMemoryFile(theirsPath));
|
|
114
|
+
// 쓰다 끊겨도 ours가 반쯤 쓰인 채 남지 않게 같은 디렉토리에 쓰고 바꿔 끼운다
|
|
115
|
+
const tmpPath = `${oursPath}.${process.pid}.tmp`;
|
|
116
|
+
writeFileSync(tmpPath, JSON.stringify(merged, null, 2), 'utf-8');
|
|
117
|
+
renameSync(tmpPath, oursPath);
|
|
118
|
+
return merged;
|
|
119
|
+
}
|
|
120
|
+
//# sourceMappingURL=memory-merge.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"memory-merge.js","sourceRoot":"","sources":["../../../src/memory/memory-merge.ts"],"names":[],"mappings":"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"}
|
|
@@ -3,22 +3,26 @@ export declare class ProjectMemoryStore {
|
|
|
3
3
|
private memoryPath;
|
|
4
4
|
private repoRoot;
|
|
5
5
|
constructor(cwd?: string);
|
|
6
|
+
/**
|
|
7
|
+
* 메모리를 읽는다. 읽기 자체가 실패하면 빈 메모리를 돌려준다.
|
|
8
|
+
*
|
|
9
|
+
* 인터뷰에 맥락을 끼워 넣는 자리라 여기서 던지면 인터뷰가 멈춘다. 고쳐 쓰는 경로는
|
|
10
|
+
* 이 메서드 대신 `load()`를 직접 불러, 못 읽은 채로 빈 메모리를 덮어쓰지 않는다.
|
|
11
|
+
* 잠금 없이 읽으므로 깨진 파일을 옮기지 않는다. 옮기는 건 잠금을 쥔 `update()`만 한다.
|
|
12
|
+
*/
|
|
6
13
|
read(): ProjectMemory;
|
|
7
|
-
private
|
|
14
|
+
private load;
|
|
15
|
+
/**
|
|
16
|
+
* 잠금을 쥔 채 읽고 고쳐 쓴다.
|
|
17
|
+
*
|
|
18
|
+
* 워크트리나 프로세스 여럿이 같은 메모리에 동시에 기록하면, 잠금 없이는 늦게 쓴 쪽이
|
|
19
|
+
* 먼저 쓴 쪽의 항목을 덮는다.
|
|
20
|
+
*/
|
|
21
|
+
private update;
|
|
8
22
|
addSpec(entry: SpecHistoryEntry): ProjectMemory;
|
|
9
23
|
addExecution(record: MemoryExecutionRecord): ProjectMemory;
|
|
10
24
|
addArchitectureDecision(decision: ArchitectureDecision): ProjectMemory;
|
|
11
25
|
addCompressedContext(sessionId: string, summary: string): ProjectMemory;
|
|
12
|
-
searchSimilarSpecs(query: string, topK?: number): Promise<SpecHistoryEntry[]>;
|
|
13
|
-
/**
|
|
14
|
-
* local과 remote ProjectMemory를 머지한다.
|
|
15
|
-
*
|
|
16
|
-
* - specHistory: specId 기준 dedupe (remote에 없는 local 항목 추가)
|
|
17
|
-
* - executionHistory: executeSessionId 기준 dedupe (동일 방식)
|
|
18
|
-
* - architectureDecisions: timestamp+decision 기준 dedupe
|
|
19
|
-
* - 그 외 스칼라 필드: local 값 우선, lastUpdated는 최신값
|
|
20
|
-
*/
|
|
21
|
-
mergeMemory(local: ProjectMemory, remote: ProjectMemory): ProjectMemory;
|
|
22
26
|
getRepoRoot(): string;
|
|
23
27
|
}
|
|
24
28
|
//# sourceMappingURL=project-memory-store.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"project-memory-store.d.ts","sourceRoot":"","sources":["../../../src/memory/project-memory-store.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"project-memory-store.d.ts","sourceRoot":"","sources":["../../../src/memory/project-memory-store.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EACV,aAAa,EACb,gBAAgB,EAChB,qBAAqB,EACrB,oBAAoB,EACrB,MAAM,kBAAkB,CAAC;AA+B1B,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,QAAQ,CAAS;gBAEb,GAAG,CAAC,EAAE,MAAM;IAKxB;;;;;;OAMG;IACH,IAAI,IAAI,aAAa;IASrB,OAAO,CAAC,IAAI;IA+BZ;;;;;OAKG;IACH,OAAO,CAAC,MAAM;IAiBd,OAAO,CAAC,KAAK,EAAE,gBAAgB,GAAG,aAAa;IAU/C,YAAY,CAAC,MAAM,EAAE,qBAAqB,GAAG,aAAa;IAY1D,uBAAuB,CAAC,QAAQ,EAAE,oBAAoB,GAAG,aAAa;IAUtE,oBAAoB,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,aAAa;IAgBvE,WAAW,IAAI,MAAM;CAGtB"}
|