localptp 0.1.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/LICENSE +23 -0
- package/README.md +159 -0
- package/dist/cli.d.ts +24 -0
- package/dist/cli.js +344 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/config.d.ts +14 -0
- package/dist/commands/config.js +65 -0
- package/dist/commands/config.js.map +1 -0
- package/dist/commands/context.d.ts +12 -0
- package/dist/commands/context.js +176 -0
- package/dist/commands/context.js.map +1 -0
- package/dist/commands/doctor.d.ts +16 -0
- package/dist/commands/doctor.js +54 -0
- package/dist/commands/doctor.js.map +1 -0
- package/dist/commands/index.d.ts +13 -0
- package/dist/commands/index.js +70 -0
- package/dist/commands/index.js.map +1 -0
- package/dist/commands/init.d.ts +15 -0
- package/dist/commands/init.js +46 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/plan.d.ts +17 -0
- package/dist/commands/plan.js +170 -0
- package/dist/commands/plan.js.map +1 -0
- package/dist/commands/resume.d.ts +17 -0
- package/dist/commands/resume.js +75 -0
- package/dist/commands/resume.js.map +1 -0
- package/dist/commands/review.d.ts +17 -0
- package/dist/commands/review.js +67 -0
- package/dist/commands/review.js.map +1 -0
- package/dist/commands/run.d.ts +24 -0
- package/dist/commands/run.js +65 -0
- package/dist/commands/run.js.map +1 -0
- package/dist/commands/step.d.ts +44 -0
- package/dist/commands/step.js +50 -0
- package/dist/commands/step.js.map +1 -0
- package/dist/commands/summarize.d.ts +17 -0
- package/dist/commands/summarize.js +276 -0
- package/dist/commands/summarize.js.map +1 -0
- package/dist/commands/task.d.ts +13 -0
- package/dist/commands/task.js +53 -0
- package/dist/commands/task.js.map +1 -0
- package/dist/core/activePointer.d.ts +28 -0
- package/dist/core/activePointer.js +84 -0
- package/dist/core/activePointer.js.map +1 -0
- package/dist/core/approval.d.ts +12 -0
- package/dist/core/approval.js +34 -0
- package/dist/core/approval.js.map +1 -0
- package/dist/core/configManager.d.ts +32 -0
- package/dist/core/configManager.js +177 -0
- package/dist/core/configManager.js.map +1 -0
- package/dist/core/contextBuilder.d.ts +40 -0
- package/dist/core/contextBuilder.js +406 -0
- package/dist/core/contextBuilder.js.map +1 -0
- package/dist/core/memoryLoader.d.ts +4 -0
- package/dist/core/memoryLoader.js +35 -0
- package/dist/core/memoryLoader.js.map +1 -0
- package/dist/core/memoryManager.d.ts +41 -0
- package/dist/core/memoryManager.js +181 -0
- package/dist/core/memoryManager.js.map +1 -0
- package/dist/core/memoryPolicy.d.ts +23 -0
- package/dist/core/memoryPolicy.js +73 -0
- package/dist/core/memoryPolicy.js.map +1 -0
- package/dist/core/modelClient.d.ts +37 -0
- package/dist/core/modelClient.js +160 -0
- package/dist/core/modelClient.js.map +1 -0
- package/dist/core/patchManager.d.ts +89 -0
- package/dist/core/patchManager.js +801 -0
- package/dist/core/patchManager.js.map +1 -0
- package/dist/core/plannerJson.d.ts +23 -0
- package/dist/core/plannerJson.js +118 -0
- package/dist/core/plannerJson.js.map +1 -0
- package/dist/core/promptManager.d.ts +16 -0
- package/dist/core/promptManager.js +35 -0
- package/dist/core/promptManager.js.map +1 -0
- package/dist/core/prompts.d.ts +8 -0
- package/dist/core/prompts.js +18 -0
- package/dist/core/prompts.js.map +1 -0
- package/dist/core/repoIndexer.d.ts +21 -0
- package/dist/core/repoIndexer.js +557 -0
- package/dist/core/repoIndexer.js.map +1 -0
- package/dist/core/reviewEngine.d.ts +53 -0
- package/dist/core/reviewEngine.js +229 -0
- package/dist/core/reviewEngine.js.map +1 -0
- package/dist/core/runLoop.d.ts +26 -0
- package/dist/core/runLoop.js +103 -0
- package/dist/core/runLoop.js.map +1 -0
- package/dist/core/runStep.d.ts +42 -0
- package/dist/core/runStep.js +586 -0
- package/dist/core/runStep.js.map +1 -0
- package/dist/core/safetyManager.d.ts +7 -0
- package/dist/core/safetyManager.js +202 -0
- package/dist/core/safetyManager.js.map +1 -0
- package/dist/core/sessionManager.d.ts +35 -0
- package/dist/core/sessionManager.js +265 -0
- package/dist/core/sessionManager.js.map +1 -0
- package/dist/core/stopConditions.d.ts +24 -0
- package/dist/core/stopConditions.js +18 -0
- package/dist/core/stopConditions.js.map +1 -0
- package/dist/core/taskManager.d.ts +26 -0
- package/dist/core/taskManager.js +312 -0
- package/dist/core/taskManager.js.map +1 -0
- package/dist/core/testRunner.d.ts +27 -0
- package/dist/core/testRunner.js +71 -0
- package/dist/core/testRunner.js.map +1 -0
- package/dist/prompts/coder.d.ts +3 -0
- package/dist/prompts/coder.js +46 -0
- package/dist/prompts/coder.js.map +1 -0
- package/dist/prompts/planner.d.ts +3 -0
- package/dist/prompts/planner.js +44 -0
- package/dist/prompts/planner.js.map +1 -0
- package/dist/prompts/reviewer.d.ts +3 -0
- package/dist/prompts/reviewer.js +41 -0
- package/dist/prompts/reviewer.js.map +1 -0
- package/dist/prompts/summarizer.d.ts +3 -0
- package/dist/prompts/summarizer.js +65 -0
- package/dist/prompts/summarizer.js.map +1 -0
- package/dist/prompts/testFixer.d.ts +3 -0
- package/dist/prompts/testFixer.js +33 -0
- package/dist/prompts/testFixer.js.map +1 -0
- package/dist/repl/approver.d.ts +15 -0
- package/dist/repl/approver.js +21 -0
- package/dist/repl/approver.js.map +1 -0
- package/dist/repl/dispatch.d.ts +48 -0
- package/dist/repl/dispatch.js +164 -0
- package/dist/repl/dispatch.js.map +1 -0
- package/dist/repl/loop.d.ts +14 -0
- package/dist/repl/loop.js +124 -0
- package/dist/repl/loop.js.map +1 -0
- package/dist/repl/tokenize.d.ts +13 -0
- package/dist/repl/tokenize.js +56 -0
- package/dist/repl/tokenize.js.map +1 -0
- package/dist/templates/memory.d.ts +13 -0
- package/dist/templates/memory.js +32 -0
- package/dist/templates/memory.js.map +1 -0
- package/dist/types/config.d.ts +235 -0
- package/dist/types/config.js +66 -0
- package/dist/types/config.js.map +1 -0
- package/dist/types/context.d.ts +78 -0
- package/dist/types/context.js +141 -0
- package/dist/types/context.js.map +1 -0
- package/dist/types/index.d.ts +117 -0
- package/dist/types/index.js +24 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/model.d.ts +35 -0
- package/dist/types/model.js +16 -0
- package/dist/types/model.js.map +1 -0
- package/dist/types/patch.d.ts +38 -0
- package/dist/types/patch.js +11 -0
- package/dist/types/patch.js.map +1 -0
- package/dist/types/plan.d.ts +86 -0
- package/dist/types/plan.js +27 -0
- package/dist/types/plan.js.map +1 -0
- package/dist/types/review.d.ts +33 -0
- package/dist/types/review.js +24 -0
- package/dist/types/review.js.map +1 -0
- package/dist/types/run.d.ts +34 -0
- package/dist/types/run.js +2 -0
- package/dist/types/run.js.map +1 -0
- package/dist/types/session.d.ts +21 -0
- package/dist/types/session.js +2 -0
- package/dist/types/session.js.map +1 -0
- package/dist/types/summary.d.ts +65 -0
- package/dist/types/summary.js +26 -0
- package/dist/types/summary.js.map +1 -0
- package/dist/types/task.d.ts +31 -0
- package/dist/types/task.js +10 -0
- package/dist/types/task.js.map +1 -0
- package/dist/types/test.d.ts +16 -0
- package/dist/types/test.js +2 -0
- package/dist/types/test.js.map +1 -0
- package/dist/utils/fs.d.ts +13 -0
- package/dist/utils/fs.js +65 -0
- package/dist/utils/fs.js.map +1 -0
- package/dist/utils/gitRoot.d.ts +5 -0
- package/dist/utils/gitRoot.js +21 -0
- package/dist/utils/gitRoot.js.map +1 -0
- package/dist/utils/logger.d.ts +15 -0
- package/dist/utils/logger.js +60 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/paths.d.ts +13 -0
- package/dist/utils/paths.js +24 -0
- package/dist/utils/paths.js.map +1 -0
- package/dist/utils/tokenEstimate.d.ts +5 -0
- package/dist/utils/tokenEstimate.js +8 -0
- package/dist/utils/tokenEstimate.js.map +1 -0
- package/package.json +44 -0
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Memory Manager (HLD-SRD §3.4).
|
|
3
|
+
*
|
|
4
|
+
* `scaffold()` ensures the `/ai`, `/ai/tasks`, `/ai/sessions` and
|
|
5
|
+
* `.ai-orchestrator` dirs exist, then writes each of the 14 starter templates
|
|
6
|
+
* ONLY if absent (preserving user edits). Returns a created/preserved report.
|
|
7
|
+
*
|
|
8
|
+
* `updateMarkerSection(filePath, markerId, body)` — edit-preserving marker-
|
|
9
|
+
* delimited section update for tool-owned regions (e.g. `<!-- BEGIN
|
|
10
|
+
* localcoder:index -->`). Appends a fresh block when markers are absent.
|
|
11
|
+
*
|
|
12
|
+
* `appendMemoryEntry(filePath, sectionHeading, rawEntry)` — accumulating
|
|
13
|
+
* append-only writer (0001_07). Adds a dated, length-capped entry under the
|
|
14
|
+
* named `## ` section, preserving all existing content and de-duplicating an
|
|
15
|
+
* identical same-day entry in the same section. Distinct from the marker-
|
|
16
|
+
* section regeneration approach used by the indexer.
|
|
17
|
+
*/
|
|
18
|
+
import { promises as fs } from "node:fs";
|
|
19
|
+
import path from "node:path";
|
|
20
|
+
import { ensureDir, writeIfAbsent, readIfExists } from "../utils/fs.js";
|
|
21
|
+
import { MEMORY_TEMPLATES } from "../templates/memory.js";
|
|
22
|
+
/**
|
|
23
|
+
* Per-entry character cap for appended memory entries (design §3, assumption 3).
|
|
24
|
+
* Content longer than this cap is truncated before writing.
|
|
25
|
+
*/
|
|
26
|
+
export const MAX_ENTRY_CHARS = 280;
|
|
27
|
+
function dateStamp(d = new Date()) {
|
|
28
|
+
const y = d.getFullYear();
|
|
29
|
+
const m = String(d.getMonth() + 1).padStart(2, "0");
|
|
30
|
+
const day = String(d.getDate()).padStart(2, "0");
|
|
31
|
+
return `${y}-${m}-${day}`;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Replace or append a marker-delimited section in a file.
|
|
35
|
+
*
|
|
36
|
+
* Markers: `<!-- BEGIN localcoder:<markerId> -->` / `<!-- END localcoder:<markerId> -->`
|
|
37
|
+
*
|
|
38
|
+
* - If both markers exist: replaces the inner region with `\n${body}\n`, preserving
|
|
39
|
+
* all content outside.
|
|
40
|
+
* - If markers are absent: appends a fresh BEGIN/body/END block.
|
|
41
|
+
* - If duplicate BEGIN markers: replaces the first complete block, warns.
|
|
42
|
+
* - If file doesn't exist: creates it with just the marker block.
|
|
43
|
+
*/
|
|
44
|
+
export async function updateMarkerSection(filePath, markerId, body) {
|
|
45
|
+
const begin = `<!-- BEGIN localcoder:${markerId} -->`;
|
|
46
|
+
const end = `<!-- END localcoder:${markerId} -->`;
|
|
47
|
+
const existing = (await readIfExists(filePath)) ?? "";
|
|
48
|
+
const beginIdx = existing.indexOf(begin);
|
|
49
|
+
const endIdx = existing.indexOf(end, beginIdx >= 0 ? beginIdx : 0);
|
|
50
|
+
let next;
|
|
51
|
+
if (beginIdx >= 0 && endIdx >= 0) {
|
|
52
|
+
// Check for duplicate BEGIN markers after the first END
|
|
53
|
+
const secondBeginIdx = existing.indexOf(begin, beginIdx + 1);
|
|
54
|
+
if (secondBeginIdx >= 0) {
|
|
55
|
+
process.stderr.write(`warning: updateMarkerSection: duplicate BEGIN localcoder:${markerId} markers found in ${filePath}; replacing the first complete block.\n`);
|
|
56
|
+
}
|
|
57
|
+
// Replace the inner content between begin and end markers
|
|
58
|
+
const before = existing.slice(0, beginIdx + begin.length);
|
|
59
|
+
const after = existing.slice(endIdx);
|
|
60
|
+
next = `${before}\n${body}\n${after}`;
|
|
61
|
+
}
|
|
62
|
+
else {
|
|
63
|
+
// Append a fresh block
|
|
64
|
+
const sep = existing.length > 0 && !existing.endsWith("\n") ? "\n" : "";
|
|
65
|
+
next = `${existing}${sep}\n${begin}\n${body}\n${end}\n`;
|
|
66
|
+
}
|
|
67
|
+
await ensureDir(path.dirname(filePath));
|
|
68
|
+
await fs.writeFile(filePath, next, "utf8");
|
|
69
|
+
}
|
|
70
|
+
// ---------------------------------------------------------------------------
|
|
71
|
+
// appendMemoryEntry (accumulating append-only writer, 0001_07)
|
|
72
|
+
// ---------------------------------------------------------------------------
|
|
73
|
+
function todayStamp(d = new Date()) {
|
|
74
|
+
const y = d.getFullYear();
|
|
75
|
+
const m = String(d.getMonth() + 1).padStart(2, "0");
|
|
76
|
+
const day = String(d.getDate()).padStart(2, "0");
|
|
77
|
+
return `${y}-${m}-${day}`;
|
|
78
|
+
}
|
|
79
|
+
function titleFromFile(filePath) {
|
|
80
|
+
const base = path.basename(filePath, path.extname(filePath));
|
|
81
|
+
// Convert kebab-case to Title Case
|
|
82
|
+
return base
|
|
83
|
+
.split("-")
|
|
84
|
+
.map((w) => w.charAt(0).toUpperCase() + w.slice(1))
|
|
85
|
+
.join(" ");
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Append a dated, length-capped entry under `sectionHeading` in `filePath`.
|
|
89
|
+
*
|
|
90
|
+
* Algorithm:
|
|
91
|
+
* 1. Read the file (or default to a title heading if absent).
|
|
92
|
+
* 2. Build the entry line: `- YYYY-MM-DD — <truncated content>`.
|
|
93
|
+
* 3. If the file already contains that exact line anywhere inside
|
|
94
|
+
* `## sectionHeading` → return (de-dupe same-day identical entry).
|
|
95
|
+
* 4. If `## sectionHeading` exists: insert the entry at the END of that
|
|
96
|
+
* section (just before the next `## ` of same/lower level or EOF).
|
|
97
|
+
* 5. If not: append a fresh `\n## sectionHeading\n<entry>\n` block.
|
|
98
|
+
* 6. Write file — only the inserted line differs; all prior prose is intact.
|
|
99
|
+
*/
|
|
100
|
+
export async function appendMemoryEntry(filePath, sectionHeading, rawEntry) {
|
|
101
|
+
const today = todayStamp();
|
|
102
|
+
const truncated = rawEntry.length > MAX_ENTRY_CHARS
|
|
103
|
+
? rawEntry.slice(0, MAX_ENTRY_CHARS)
|
|
104
|
+
: rawEntry;
|
|
105
|
+
const entryLine = `- ${today} — ${truncated}`;
|
|
106
|
+
// 1. Read or initialize the file body.
|
|
107
|
+
let body = (await readIfExists(filePath)) ?? `# ${titleFromFile(filePath)}\n`;
|
|
108
|
+
// 2. Check for de-duplication: is this exact entry already in the section?
|
|
109
|
+
// We find the section's content range first and check within it.
|
|
110
|
+
const lines = body.split("\n");
|
|
111
|
+
const headingPattern = /^(#{1,6})\s+(.*?)\s*$/;
|
|
112
|
+
let sectionStart = -1;
|
|
113
|
+
let sectionLevel = 0;
|
|
114
|
+
for (let i = 0; i < lines.length; i++) {
|
|
115
|
+
const m = headingPattern.exec(lines[i]);
|
|
116
|
+
if (m && m[2] === sectionHeading) {
|
|
117
|
+
sectionStart = i;
|
|
118
|
+
sectionLevel = m[1].length;
|
|
119
|
+
break;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
if (sectionStart >= 0) {
|
|
123
|
+
// Find the end of the section.
|
|
124
|
+
let sectionEnd = lines.length;
|
|
125
|
+
for (let i = sectionStart + 1; i < lines.length; i++) {
|
|
126
|
+
const m = headingPattern.exec(lines[i]);
|
|
127
|
+
if (m && m[1].length <= sectionLevel) {
|
|
128
|
+
sectionEnd = i;
|
|
129
|
+
break;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
// Check for duplicate in this section.
|
|
133
|
+
const sectionLines = lines.slice(sectionStart + 1, sectionEnd);
|
|
134
|
+
if (sectionLines.includes(entryLine)) {
|
|
135
|
+
return; // Already present — de-dupe.
|
|
136
|
+
}
|
|
137
|
+
// Insert the entry at the end of this section (before sectionEnd).
|
|
138
|
+
// Find the last non-blank line in the section and insert after it.
|
|
139
|
+
let insertAt = sectionEnd;
|
|
140
|
+
// Insert before sectionEnd, but we need to add it in lines[] then rejoin.
|
|
141
|
+
const before = lines.slice(0, insertAt);
|
|
142
|
+
const after = lines.slice(insertAt);
|
|
143
|
+
const merged = [...before, entryLine, ...after];
|
|
144
|
+
body = merged.join("\n");
|
|
145
|
+
}
|
|
146
|
+
else {
|
|
147
|
+
// Section does not exist — append it.
|
|
148
|
+
const sep = body.length > 0 && !body.endsWith("\n") ? "\n" : "";
|
|
149
|
+
body = `${body}${sep}\n## ${sectionHeading}\n${entryLine}\n`;
|
|
150
|
+
}
|
|
151
|
+
// Write the result.
|
|
152
|
+
await ensureDir(path.dirname(filePath));
|
|
153
|
+
await fs.writeFile(filePath, body, "utf8");
|
|
154
|
+
}
|
|
155
|
+
export class MemoryManager {
|
|
156
|
+
layout;
|
|
157
|
+
constructor(layout) {
|
|
158
|
+
this.layout = layout;
|
|
159
|
+
}
|
|
160
|
+
async scaffold() {
|
|
161
|
+
await ensureDir(this.layout.orchestratorDir);
|
|
162
|
+
await ensureDir(this.layout.aiDir);
|
|
163
|
+
await ensureDir(this.layout.tasksDir);
|
|
164
|
+
await ensureDir(this.layout.sessionsDir);
|
|
165
|
+
const stamp = dateStamp();
|
|
166
|
+
const created = [];
|
|
167
|
+
const preserved = [];
|
|
168
|
+
for (const template of MEMORY_TEMPLATES) {
|
|
169
|
+
const target = this.layout.memoryFile(template.name);
|
|
170
|
+
const wasCreated = await writeIfAbsent(target, template.render(stamp));
|
|
171
|
+
if (wasCreated) {
|
|
172
|
+
created.push(template.name);
|
|
173
|
+
}
|
|
174
|
+
else {
|
|
175
|
+
preserved.push(template.name);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
return { created, preserved };
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
//# sourceMappingURL=memoryManager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"memoryManager.js","sourceRoot":"","sources":["../../src/core/memoryManager.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Memory update policy (HLD-SRD §3.4, 0001_07).
|
|
3
|
+
*
|
|
4
|
+
* The §3.4 policy table maps canonical change types to their single target file.
|
|
5
|
+
* The model proposes {changeType, content} only — the code picks the file via
|
|
6
|
+
* POLICY. Out-of-table change types are dropped + warned (blocks .env/secret
|
|
7
|
+
* writes, §13).
|
|
8
|
+
*
|
|
9
|
+
* `normalize(loose)` maps loose model strings to canonical keys.
|
|
10
|
+
* `headingFor(key)` returns the section heading to append under.
|
|
11
|
+
*/
|
|
12
|
+
/** The §3.4 update-policy table: changeType → target memory file. */
|
|
13
|
+
export declare const POLICY: Readonly<Record<string, string>>;
|
|
14
|
+
/**
|
|
15
|
+
* Map a loose model-provided change type string to its canonical key.
|
|
16
|
+
* Returns `undefined` for an unknown/unmapped change type.
|
|
17
|
+
*/
|
|
18
|
+
export declare function normalize(loose: string): string | undefined;
|
|
19
|
+
/**
|
|
20
|
+
* Return the section heading for a canonical change-type key.
|
|
21
|
+
* Returns `undefined` for an unknown key.
|
|
22
|
+
*/
|
|
23
|
+
export declare function headingFor(key: string): string | undefined;
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Memory update policy (HLD-SRD §3.4, 0001_07).
|
|
3
|
+
*
|
|
4
|
+
* The §3.4 policy table maps canonical change types to their single target file.
|
|
5
|
+
* The model proposes {changeType, content} only — the code picks the file via
|
|
6
|
+
* POLICY. Out-of-table change types are dropped + warned (blocks .env/secret
|
|
7
|
+
* writes, §13).
|
|
8
|
+
*
|
|
9
|
+
* `normalize(loose)` maps loose model strings to canonical keys.
|
|
10
|
+
* `headingFor(key)` returns the section heading to append under.
|
|
11
|
+
*/
|
|
12
|
+
/** The §3.4 update-policy table: changeType → target memory file. */
|
|
13
|
+
export const POLICY = {
|
|
14
|
+
"file-responsibility": "file-index.md",
|
|
15
|
+
"api-behavior": "api-map.md",
|
|
16
|
+
"data-model": "data-model.md",
|
|
17
|
+
"architectural-decision": "decisions.md",
|
|
18
|
+
"external-integration": "external-integrations.md",
|
|
19
|
+
"testing-process": "test-plan.md",
|
|
20
|
+
risk: "known-issues.md",
|
|
21
|
+
};
|
|
22
|
+
/** Human-readable section heading for each canonical change type. */
|
|
23
|
+
const HEADINGS = {
|
|
24
|
+
"file-responsibility": "File Responsibility Changes",
|
|
25
|
+
"api-behavior": "API Behavior Changes",
|
|
26
|
+
"data-model": "Data Model Changes",
|
|
27
|
+
"architectural-decision": "Architectural Decisions",
|
|
28
|
+
"external-integration": "External Integration Changes",
|
|
29
|
+
"testing-process": "Testing Process Changes",
|
|
30
|
+
risk: "Risks / Known Issues",
|
|
31
|
+
};
|
|
32
|
+
// Ordered normalization rules: each entry is [pattern, canonical key].
|
|
33
|
+
// Evaluated in order — first match wins.
|
|
34
|
+
const NORMALIZE_RULES = [
|
|
35
|
+
// Exact canonical key first (identity pass)
|
|
36
|
+
[/^file-responsibility$/i, "file-responsibility"],
|
|
37
|
+
[/^api-behavior$/i, "api-behavior"],
|
|
38
|
+
[/^data-model$/i, "data-model"],
|
|
39
|
+
[/^architectural-decision$/i, "architectural-decision"],
|
|
40
|
+
[/^external-integration$/i, "external-integration"],
|
|
41
|
+
[/^testing-process$/i, "testing-process"],
|
|
42
|
+
[/^risk$/i, "risk"],
|
|
43
|
+
// Loose matches
|
|
44
|
+
[/\bapi\b|\bendpoint\b/i, "api-behavior"],
|
|
45
|
+
[/\barchitectural\b|\bdecision\b/i, "architectural-decision"],
|
|
46
|
+
[/\brisk\b|\bbug\b/i, "risk"],
|
|
47
|
+
[/\btest(ing)?\b/i, "testing-process"],
|
|
48
|
+
[/\bfile\b|\bmodule\b/i, "file-responsibility"],
|
|
49
|
+
[/\bdata\b|\bschema\b/i, "data-model"],
|
|
50
|
+
[/\bintegration\b|\bexternal\b/i, "external-integration"],
|
|
51
|
+
];
|
|
52
|
+
/**
|
|
53
|
+
* Map a loose model-provided change type string to its canonical key.
|
|
54
|
+
* Returns `undefined` for an unknown/unmapped change type.
|
|
55
|
+
*/
|
|
56
|
+
export function normalize(loose) {
|
|
57
|
+
const trimmed = loose.trim();
|
|
58
|
+
if (trimmed.length === 0)
|
|
59
|
+
return undefined;
|
|
60
|
+
for (const [pattern, key] of NORMALIZE_RULES) {
|
|
61
|
+
if (pattern.test(trimmed))
|
|
62
|
+
return key;
|
|
63
|
+
}
|
|
64
|
+
return undefined;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Return the section heading for a canonical change-type key.
|
|
68
|
+
* Returns `undefined` for an unknown key.
|
|
69
|
+
*/
|
|
70
|
+
export function headingFor(key) {
|
|
71
|
+
return HEADINGS[key];
|
|
72
|
+
}
|
|
73
|
+
//# sourceMappingURL=memoryPolicy.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"memoryPolicy.js","sourceRoot":"","sources":["../../src/core/memoryPolicy.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,qEAAqE;AACrE,MAAM,CAAC,MAAM,MAAM,GAAqC;IACtD,qBAAqB,EAAE,eAAe;IACtC,cAAc,EAAE,YAAY;IAC5B,YAAY,EAAE,eAAe;IAC7B,wBAAwB,EAAE,cAAc;IACxC,sBAAsB,EAAE,0BAA0B;IAClD,iBAAiB,EAAE,cAAc;IACjC,IAAI,EAAE,iBAAiB;CACxB,CAAC;AAEF,qEAAqE;AACrE,MAAM,QAAQ,GAAqC;IACjD,qBAAqB,EAAE,6BAA6B;IACpD,cAAc,EAAE,sBAAsB;IACtC,YAAY,EAAE,oBAAoB;IAClC,wBAAwB,EAAE,yBAAyB;IACnD,sBAAsB,EAAE,8BAA8B;IACtD,iBAAiB,EAAE,yBAAyB;IAC5C,IAAI,EAAE,sBAAsB;CAC7B,CAAC;AAEF,uEAAuE;AACvE,yCAAyC;AACzC,MAAM,eAAe,GAA4B;IAC/C,4CAA4C;IAC5C,CAAC,wBAAwB,EAAE,qBAAqB,CAAC;IACjD,CAAC,iBAAiB,EAAE,cAAc,CAAC;IACnC,CAAC,eAAe,EAAE,YAAY,CAAC;IAC/B,CAAC,2BAA2B,EAAE,wBAAwB,CAAC;IACvD,CAAC,yBAAyB,EAAE,sBAAsB,CAAC;IACnD,CAAC,oBAAoB,EAAE,iBAAiB,CAAC;IACzC,CAAC,SAAS,EAAE,MAAM,CAAC;IACnB,gBAAgB;IAChB,CAAC,uBAAuB,EAAE,cAAc,CAAC;IACzC,CAAC,iCAAiC,EAAE,wBAAwB,CAAC;IAC7D,CAAC,mBAAmB,EAAE,MAAM,CAAC;IAC7B,CAAC,iBAAiB,EAAE,iBAAiB,CAAC;IACtC,CAAC,sBAAsB,EAAE,qBAAqB,CAAC;IAC/C,CAAC,sBAAsB,EAAE,YAAY,CAAC;IACtC,CAAC,+BAA+B,EAAE,sBAAsB,CAAC;CAC1D,CAAC;AAEF;;;GAGG;AACH,MAAM,UAAU,SAAS,CAAC,KAAa;IACrC,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC;IAC7B,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,SAAS,CAAC;IAC3C,KAAK,MAAM,CAAC,OAAO,EAAE,GAAG,CAAC,IAAI,eAAe,EAAE,CAAC;QAC7C,IAAI,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC;YAAE,OAAO,GAAG,CAAC;IACxC,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,UAAU,CAAC,GAAW;IACpC,OAAO,QAAQ,CAAC,GAAG,CAAC,CAAC;AACvB,CAAC"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LM Studio model client (HLD-SRD §3.8, §12).
|
|
3
|
+
*
|
|
4
|
+
* Speaks the OpenAI-compatible REST API behind the single `ModelClient` seam,
|
|
5
|
+
* using the Node 20 built-in `fetch` + `AbortController` for timeouts. All
|
|
6
|
+
* transport/protocol failures are mapped to a typed `ModelClientError` whose
|
|
7
|
+
* `kind` callers branch on without string-matching.
|
|
8
|
+
*/
|
|
9
|
+
import { type ModelClient, type ModelRequest, type ModelResponse, type HealthResult } from "../types/model.js";
|
|
10
|
+
export interface LmStudioClientOptions {
|
|
11
|
+
baseUrl: string;
|
|
12
|
+
model: string;
|
|
13
|
+
apiKey?: string;
|
|
14
|
+
temperature?: number;
|
|
15
|
+
timeoutMs?: number;
|
|
16
|
+
}
|
|
17
|
+
export declare class LmStudioClient implements ModelClient {
|
|
18
|
+
private readonly baseUrl;
|
|
19
|
+
private readonly model;
|
|
20
|
+
private readonly apiKey;
|
|
21
|
+
private readonly temperature;
|
|
22
|
+
private readonly timeoutMs;
|
|
23
|
+
constructor(opts: LmStudioClientOptions);
|
|
24
|
+
private headers;
|
|
25
|
+
/**
|
|
26
|
+
* Run a fetch with a timeout, translating transport failures (refused/timeout)
|
|
27
|
+
* into typed errors. The timeout covers BOTH the headers AND the body read:
|
|
28
|
+
* the abort timer stays armed until the body has been consumed, so a server
|
|
29
|
+
* that sends headers and then stalls the body cannot hang past `timeoutMs`.
|
|
30
|
+
*/
|
|
31
|
+
private request;
|
|
32
|
+
complete(req: ModelRequest): Promise<ModelResponse>;
|
|
33
|
+
health(): Promise<HealthResult>;
|
|
34
|
+
private looksLikeModelNotLoaded;
|
|
35
|
+
private extractContent;
|
|
36
|
+
private extractModels;
|
|
37
|
+
}
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LM Studio model client (HLD-SRD §3.8, §12).
|
|
3
|
+
*
|
|
4
|
+
* Speaks the OpenAI-compatible REST API behind the single `ModelClient` seam,
|
|
5
|
+
* using the Node 20 built-in `fetch` + `AbortController` for timeouts. All
|
|
6
|
+
* transport/protocol failures are mapped to a typed `ModelClientError` whose
|
|
7
|
+
* `kind` callers branch on without string-matching.
|
|
8
|
+
*/
|
|
9
|
+
import { ModelClientError, } from "../types/model.js";
|
|
10
|
+
function connectionMessage(baseUrl) {
|
|
11
|
+
return (`Cannot connect to LM Studio at ${baseUrl}. ` +
|
|
12
|
+
`Make sure LM Studio Local Server is running and the model is loaded.`);
|
|
13
|
+
}
|
|
14
|
+
/** Join the base (which ends in /v1) with an endpoint path. */
|
|
15
|
+
function urlFor(baseUrl, endpoint) {
|
|
16
|
+
return `${baseUrl.replace(/\/+$/, "")}/${endpoint.replace(/^\/+/, "")}`;
|
|
17
|
+
}
|
|
18
|
+
export class LmStudioClient {
|
|
19
|
+
baseUrl;
|
|
20
|
+
model;
|
|
21
|
+
apiKey;
|
|
22
|
+
temperature;
|
|
23
|
+
timeoutMs;
|
|
24
|
+
constructor(opts) {
|
|
25
|
+
this.baseUrl = opts.baseUrl;
|
|
26
|
+
this.model = opts.model;
|
|
27
|
+
this.apiKey = opts.apiKey ?? "lm-studio";
|
|
28
|
+
this.temperature = opts.temperature ?? 0.2;
|
|
29
|
+
this.timeoutMs = opts.timeoutMs ?? 60000;
|
|
30
|
+
}
|
|
31
|
+
headers() {
|
|
32
|
+
return {
|
|
33
|
+
"content-type": "application/json",
|
|
34
|
+
authorization: `Bearer ${this.apiKey}`,
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Run a fetch with a timeout, translating transport failures (refused/timeout)
|
|
39
|
+
* into typed errors. The timeout covers BOTH the headers AND the body read:
|
|
40
|
+
* the abort timer stays armed until the body has been consumed, so a server
|
|
41
|
+
* that sends headers and then stalls the body cannot hang past `timeoutMs`.
|
|
42
|
+
*/
|
|
43
|
+
async request(url, init) {
|
|
44
|
+
const controller = new AbortController();
|
|
45
|
+
const timer = setTimeout(() => controller.abort(), this.timeoutMs);
|
|
46
|
+
try {
|
|
47
|
+
const res = await fetch(url, { ...init, signal: controller.signal });
|
|
48
|
+
// Read the body within the same abortable window so a stalled body still
|
|
49
|
+
// trips the timeout rather than hanging indefinitely.
|
|
50
|
+
const text = await res.text();
|
|
51
|
+
return { status: res.status, ok: res.ok, text };
|
|
52
|
+
}
|
|
53
|
+
catch (err) {
|
|
54
|
+
if (err.name === "AbortError") {
|
|
55
|
+
throw new ModelClientError("timeout", `Request to LM Studio at ${this.baseUrl} timed out after ${this.timeoutMs}ms. ` +
|
|
56
|
+
connectionMessage(this.baseUrl), this.baseUrl, err);
|
|
57
|
+
}
|
|
58
|
+
throw new ModelClientError("refused", connectionMessage(this.baseUrl), this.baseUrl, err);
|
|
59
|
+
}
|
|
60
|
+
finally {
|
|
61
|
+
clearTimeout(timer);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
async complete(req) {
|
|
65
|
+
const url = urlFor(this.baseUrl, "chat/completions");
|
|
66
|
+
const body = {
|
|
67
|
+
model: this.model,
|
|
68
|
+
messages: [
|
|
69
|
+
{ role: "system", content: req.systemPrompt },
|
|
70
|
+
{ role: "user", content: req.userPrompt },
|
|
71
|
+
],
|
|
72
|
+
temperature: req.temperature ?? this.temperature,
|
|
73
|
+
...(req.maxTokens !== undefined ? { max_tokens: req.maxTokens } : {}),
|
|
74
|
+
};
|
|
75
|
+
const res = await this.request(url, {
|
|
76
|
+
method: "POST",
|
|
77
|
+
headers: this.headers(),
|
|
78
|
+
body: JSON.stringify(body),
|
|
79
|
+
});
|
|
80
|
+
const text = res.text;
|
|
81
|
+
if (!res.ok) {
|
|
82
|
+
// Non-2xx: distinguish model-not-loaded from a generic failure.
|
|
83
|
+
if (this.looksLikeModelNotLoaded(res.status, text)) {
|
|
84
|
+
throw new ModelClientError("model-not-loaded", `LM Studio at ${this.baseUrl} has no model loaded (HTTP ${res.status}). ` +
|
|
85
|
+
connectionMessage(this.baseUrl), this.baseUrl);
|
|
86
|
+
}
|
|
87
|
+
throw new ModelClientError("refused", `LM Studio at ${this.baseUrl} returned HTTP ${res.status}. ` +
|
|
88
|
+
connectionMessage(this.baseUrl), this.baseUrl);
|
|
89
|
+
}
|
|
90
|
+
let parsed;
|
|
91
|
+
try {
|
|
92
|
+
parsed = JSON.parse(text);
|
|
93
|
+
}
|
|
94
|
+
catch (err) {
|
|
95
|
+
throw new ModelClientError("malformed", "Model returned an empty/invalid response.", this.baseUrl, err);
|
|
96
|
+
}
|
|
97
|
+
const content = this.extractContent(parsed);
|
|
98
|
+
if (content === undefined || content.length === 0) {
|
|
99
|
+
throw new ModelClientError("empty", "Model returned an empty/invalid response.", this.baseUrl);
|
|
100
|
+
}
|
|
101
|
+
return { content, raw: parsed };
|
|
102
|
+
}
|
|
103
|
+
async health() {
|
|
104
|
+
const url = urlFor(this.baseUrl, "models");
|
|
105
|
+
const res = await this.request(url, {
|
|
106
|
+
method: "GET",
|
|
107
|
+
headers: this.headers(),
|
|
108
|
+
});
|
|
109
|
+
if (!res.ok) {
|
|
110
|
+
throw new ModelClientError("model-not-loaded", `LM Studio at ${this.baseUrl} returned HTTP ${res.status} for /models. ` +
|
|
111
|
+
connectionMessage(this.baseUrl), this.baseUrl);
|
|
112
|
+
}
|
|
113
|
+
const text = res.text;
|
|
114
|
+
let parsed;
|
|
115
|
+
try {
|
|
116
|
+
parsed = JSON.parse(text);
|
|
117
|
+
}
|
|
118
|
+
catch (err) {
|
|
119
|
+
throw new ModelClientError("malformed", "Model returned an empty/invalid response.", this.baseUrl, err);
|
|
120
|
+
}
|
|
121
|
+
const models = this.extractModels(parsed);
|
|
122
|
+
return { reachable: true, models };
|
|
123
|
+
}
|
|
124
|
+
looksLikeModelNotLoaded(status, body) {
|
|
125
|
+
if (status === 404)
|
|
126
|
+
return true;
|
|
127
|
+
const lower = body.toLowerCase();
|
|
128
|
+
return (lower.includes("model_not_found") ||
|
|
129
|
+
lower.includes("not found") ||
|
|
130
|
+
lower.includes("no model"));
|
|
131
|
+
}
|
|
132
|
+
extractContent(parsed) {
|
|
133
|
+
if (typeof parsed !== "object" || parsed === null)
|
|
134
|
+
return undefined;
|
|
135
|
+
const choices = parsed.choices;
|
|
136
|
+
if (!Array.isArray(choices) || choices.length === 0)
|
|
137
|
+
return undefined;
|
|
138
|
+
const first = choices[0];
|
|
139
|
+
if (typeof first !== "object" || first === null)
|
|
140
|
+
return undefined;
|
|
141
|
+
const message = first.message;
|
|
142
|
+
if (typeof message !== "object" || message === null)
|
|
143
|
+
return undefined;
|
|
144
|
+
const content = message.content;
|
|
145
|
+
return typeof content === "string" ? content : undefined;
|
|
146
|
+
}
|
|
147
|
+
extractModels(parsed) {
|
|
148
|
+
if (typeof parsed !== "object" || parsed === null)
|
|
149
|
+
return [];
|
|
150
|
+
const data = parsed.data;
|
|
151
|
+
if (!Array.isArray(data))
|
|
152
|
+
return [];
|
|
153
|
+
return data
|
|
154
|
+
.map((d) => typeof d === "object" && d !== null
|
|
155
|
+
? d.id
|
|
156
|
+
: undefined)
|
|
157
|
+
.filter((id) => typeof id === "string");
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
//# sourceMappingURL=modelClient.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"modelClient.js","sourceRoot":"","sources":["../../src/core/modelClient.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import type { AppConfig } from "../types/config.js";
|
|
2
|
+
import type { PatchPlan } from "../types/patch.js";
|
|
3
|
+
/** A path/target/ignore validation failure (§3.10, §13). Carries an exit code. */
|
|
4
|
+
export declare class PatchValidationError extends Error {
|
|
5
|
+
readonly exitCode = 1;
|
|
6
|
+
constructor(message: string);
|
|
7
|
+
}
|
|
8
|
+
/** A patch that does not cleanly apply, or an apply mechanism failure (§12.3). */
|
|
9
|
+
export declare class PatchApplyError extends Error {
|
|
10
|
+
readonly exitCode = 1;
|
|
11
|
+
constructor(message: string);
|
|
12
|
+
}
|
|
13
|
+
/** The working tree is not safe to apply onto (e.g. mid-merge/rebase) (§11.2). */
|
|
14
|
+
export declare class WorkingTreeUnsafeError extends Error {
|
|
15
|
+
readonly exitCode = 1;
|
|
16
|
+
constructor(message: string);
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Tolerantly extract a unified diff from raw model output. Strips code fences
|
|
20
|
+
* and surrounding prose, returning the diff block (from its first `diff --git`
|
|
21
|
+
* or `---` line through its last diff-shaped line). Returns null when there is
|
|
22
|
+
* no diff — empty/whitespace output, a `needs_context` JSON response, or plain
|
|
23
|
+
* prose.
|
|
24
|
+
*/
|
|
25
|
+
export declare function extractUnifiedDiff(raw: string): string | null;
|
|
26
|
+
/**
|
|
27
|
+
* Parse a unified diff into a classified `PatchPlan`. Each `diff --git` (or, for
|
|
28
|
+
* a header-less diff, each `---`/`+++` pair) is one file section. Classification:
|
|
29
|
+
* - `new file mode` / `--- /dev/null` → add
|
|
30
|
+
* - `deleted file mode` / `+++ /dev/null` → delete
|
|
31
|
+
* - otherwise → modify
|
|
32
|
+
* `GIT binary patch` in any section sets `isBinary`.
|
|
33
|
+
*/
|
|
34
|
+
export declare function parsePatch(diff: string): PatchPlan;
|
|
35
|
+
/**
|
|
36
|
+
* Normalize an ALREADY-prefix-stripped touched path (the paths in a `PatchPlan`
|
|
37
|
+
* have had their `a/`/`b/` diff prefix removed by `parsePatch`) to repo-relative
|
|
38
|
+
* POSIX form: convert Windows `\` separators and drop a `./` prefix. It does NOT
|
|
39
|
+
* re-strip an `a/`/`b/` prefix — doing so would corrupt a legitimate path whose
|
|
40
|
+
* first segment is literally `a` or `b` (e.g. `a/secure.txt`), causing the
|
|
41
|
+
* ignore / risky-path checks to inspect the wrong path. Absolute markers are
|
|
42
|
+
* preserved for the escape check.
|
|
43
|
+
*/
|
|
44
|
+
export declare function normalizeTouchedPath(p: string): string;
|
|
45
|
+
/**
|
|
46
|
+
* Validate a parsed plan against the repo root + config. Throws
|
|
47
|
+
* `PatchValidationError` on the first violation:
|
|
48
|
+
* - any touched path escaping the root (absolute, `../`, or symlink escape);
|
|
49
|
+
* - any touched path matching the ignore list;
|
|
50
|
+
* - a modify/delete whose target does not exist;
|
|
51
|
+
* - an add whose target already exists (never overwrite).
|
|
52
|
+
* Resolves (undefined) when the plan is structurally safe to apply.
|
|
53
|
+
*/
|
|
54
|
+
export declare function validate(plan: PatchPlan, config: AppConfig, root: string): Promise<void>;
|
|
55
|
+
/**
|
|
56
|
+
* Refuse to apply onto an unsafe working tree (§11.2). Unsafe = a repo in the
|
|
57
|
+
* middle of a merge / rebase / cherry-pick / revert / bisect (detected by the
|
|
58
|
+
* marker files / dirs git writes). Unrelated dirty files are tolerated — the
|
|
59
|
+
* `git apply --check` pre-flight gates the exact apply. A non-git dir has no
|
|
60
|
+
* merge state, so this is a no-op there.
|
|
61
|
+
*/
|
|
62
|
+
export declare function assertWorkingTreeSafe(root: string): Promise<void>;
|
|
63
|
+
/**
|
|
64
|
+
* Pre-flight a diff with `git apply --check` (no mutation). Throws
|
|
65
|
+
* `PatchApplyError` when the patch would not apply cleanly. Git repos only —
|
|
66
|
+
* the command skips this for the non-git add-only path.
|
|
67
|
+
*/
|
|
68
|
+
export declare function gitApplyCheck(plan: PatchPlan, root: string): Promise<void>;
|
|
69
|
+
/**
|
|
70
|
+
* Apply a parsed plan to the working tree, never partially.
|
|
71
|
+
*
|
|
72
|
+
* Git repo: `git apply --check` then `git apply` (atomic per call). A failing
|
|
73
|
+
* check leaves the tree untouched.
|
|
74
|
+
*
|
|
75
|
+
* Non-git dir: only an add-only patch is applied, via a controlled write of the
|
|
76
|
+
* new files (the Git pre-check is skipped). Any existing-file modify/delete in a
|
|
77
|
+
* non-git dir is refused with `PatchApplyError` — there is no rollback layer.
|
|
78
|
+
*/
|
|
79
|
+
export declare function apply(plan: PatchPlan, root: string): Promise<void>;
|
|
80
|
+
export interface SavePatchOptions {
|
|
81
|
+
/** Injectable clock for deterministic tests. */
|
|
82
|
+
now?: Date;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Save the raw diff as a patch artifact under
|
|
86
|
+
* `<orchestratorDir>/patches/YYYY-MM-DD_HHMM_<step-id>.patch`. Returns the
|
|
87
|
+
* absolute path written. Never overwrites: a name collision appends `-2`, `-3`…
|
|
88
|
+
*/
|
|
89
|
+
export declare function savePatch(diff: string, stepId: string, orchestratorDir: string, opts?: SavePatchOptions): Promise<string>;
|