frontend-harness 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.
Files changed (77) hide show
  1. package/AGENTS.md +48 -0
  2. package/CLAUDE.md +48 -0
  3. package/README.md +262 -0
  4. package/dist/cli/index.d.ts +2 -0
  5. package/dist/cli/index.js +380 -0
  6. package/dist/cli/index.js.map +1 -0
  7. package/dist/runtime/builtin-skills.d.ts +6 -0
  8. package/dist/runtime/builtin-skills.js +269 -0
  9. package/dist/runtime/builtin-skills.js.map +1 -0
  10. package/dist/runtime/clean.d.ts +11 -0
  11. package/dist/runtime/clean.js +85 -0
  12. package/dist/runtime/clean.js.map +1 -0
  13. package/dist/runtime/command-taxonomy.d.ts +12 -0
  14. package/dist/runtime/command-taxonomy.js +72 -0
  15. package/dist/runtime/command-taxonomy.js.map +1 -0
  16. package/dist/runtime/context.d.ts +71 -0
  17. package/dist/runtime/context.js +153 -0
  18. package/dist/runtime/context.js.map +1 -0
  19. package/dist/runtime/graph.d.ts +12 -0
  20. package/dist/runtime/graph.js +211 -0
  21. package/dist/runtime/graph.js.map +1 -0
  22. package/dist/runtime/knowledge.d.ts +48 -0
  23. package/dist/runtime/knowledge.js +383 -0
  24. package/dist/runtime/knowledge.js.map +1 -0
  25. package/dist/runtime/plan.d.ts +18 -0
  26. package/dist/runtime/plan.js +571 -0
  27. package/dist/runtime/plan.js.map +1 -0
  28. package/dist/runtime/policy-provenance.d.ts +17 -0
  29. package/dist/runtime/policy-provenance.js +195 -0
  30. package/dist/runtime/policy-provenance.js.map +1 -0
  31. package/dist/runtime/project-discovery.d.ts +17 -0
  32. package/dist/runtime/project-discovery.js +166 -0
  33. package/dist/runtime/project-discovery.js.map +1 -0
  34. package/dist/runtime/project-paths.d.ts +6 -0
  35. package/dist/runtime/project-paths.js +47 -0
  36. package/dist/runtime/project-paths.js.map +1 -0
  37. package/dist/runtime/protocol-init.d.ts +50 -0
  38. package/dist/runtime/protocol-init.js +256 -0
  39. package/dist/runtime/protocol-init.js.map +1 -0
  40. package/dist/runtime/repair-decision.d.ts +3 -0
  41. package/dist/runtime/repair-decision.js +195 -0
  42. package/dist/runtime/repair-decision.js.map +1 -0
  43. package/dist/runtime/repair-packet.d.ts +7 -0
  44. package/dist/runtime/repair-packet.js +159 -0
  45. package/dist/runtime/repair-packet.js.map +1 -0
  46. package/dist/runtime/skills.d.ts +19 -0
  47. package/dist/runtime/skills.js +230 -0
  48. package/dist/runtime/skills.js.map +1 -0
  49. package/dist/runtime/state-explain.d.ts +2 -0
  50. package/dist/runtime/state-explain.js +106 -0
  51. package/dist/runtime/state-explain.js.map +1 -0
  52. package/dist/runtime/state.d.ts +10 -0
  53. package/dist/runtime/state.js +237 -0
  54. package/dist/runtime/state.js.map +1 -0
  55. package/dist/runtime/units.d.ts +10 -0
  56. package/dist/runtime/units.js +181 -0
  57. package/dist/runtime/units.js.map +1 -0
  58. package/dist/runtime/verification-commands.d.ts +11 -0
  59. package/dist/runtime/verification-commands.js +89 -0
  60. package/dist/runtime/verification-commands.js.map +1 -0
  61. package/dist/runtime/verify.d.ts +7 -0
  62. package/dist/runtime/verify.js +192 -0
  63. package/dist/runtime/verify.js.map +1 -0
  64. package/dist/schemas/types.d.ts +244 -0
  65. package/dist/schemas/types.js +2 -0
  66. package/dist/schemas/types.js.map +1 -0
  67. package/dist/schemas/validation.d.ts +2 -0
  68. package/dist/schemas/validation.js +21 -0
  69. package/dist/schemas/validation.js.map +1 -0
  70. package/dist/storage/json.d.ts +5 -0
  71. package/dist/storage/json.js +24 -0
  72. package/dist/storage/json.js.map +1 -0
  73. package/dist/storage/paths.d.ts +3 -0
  74. package/dist/storage/paths.js +9 -0
  75. package/dist/storage/paths.js.map +1 -0
  76. package/docs/DIRECTION.md +67 -0
  77. package/package.json +35 -0
@@ -0,0 +1,256 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import { createHash } from "node:crypto";
4
+ import { harnessPath, relativeHarnessPath } from "../storage/paths.js";
5
+ import { readJson, writeJson } from "../storage/json.js";
6
+ import { builtinProjectSkills } from "./builtin-skills.js";
7
+ import { protocolUsefulCommandLines } from "./command-taxonomy.js";
8
+ const blockStart = "<!-- frontend-harness:managed:start -->";
9
+ const blockEnd = "<!-- frontend-harness:managed:end -->";
10
+ export function initializeProtocol(projectRoot, options = {}) {
11
+ const createdAt = new Date().toISOString();
12
+ const dryRun = Boolean(options.dryRun);
13
+ const files = [
14
+ upsertProtocolFile(projectRoot, "AGENTS.md", { dryRun }),
15
+ upsertProtocolFile(projectRoot, "CLAUDE.md", { dryRun })
16
+ ];
17
+ const skills = upsertBuiltinProjectSkills(projectRoot, { dryRun });
18
+ const protocolStatus = checkProtocol(projectRoot);
19
+ const needsUpdate = files.some((file) => file.action !== "unchanged")
20
+ || skills.some((skill) => skill.action !== "unchanged")
21
+ || protocolStatus.status !== "passed"
22
+ || !protocolStatus.provenance.current;
23
+ const result = {
24
+ contractVersion: 1,
25
+ createdAt,
26
+ projectRoot,
27
+ dryRun,
28
+ needsUpdate,
29
+ files,
30
+ skills,
31
+ artifacts: {
32
+ protocol: relativeHarnessPath("protocol", "latest.json"),
33
+ skillsRoot: relativeHarnessPath("skills")
34
+ }
35
+ };
36
+ if (!dryRun) {
37
+ writeJson(harnessPath(projectRoot, "protocol", "latest.json"), result);
38
+ }
39
+ return result;
40
+ }
41
+ function upsertBuiltinProjectSkills(projectRoot, options) {
42
+ const skillsRoot = harnessPath(projectRoot, "skills");
43
+ return builtinProjectSkills.map((skill) => {
44
+ const relativePath = relativeHarnessPath("skills", skill.fileName);
45
+ const fullPath = path.join(skillsRoot, skill.fileName);
46
+ const exists = fs.existsSync(fullPath);
47
+ const content = exists ? fs.readFileSync(fullPath, "utf8") : skill.content;
48
+ const action = exists ? "unchanged" : "created";
49
+ if (!exists && !options.dryRun) {
50
+ fs.mkdirSync(path.dirname(fullPath), { recursive: true });
51
+ fs.writeFileSync(fullPath, skill.content, "utf8");
52
+ }
53
+ return {
54
+ name: skill.name,
55
+ path: relativePath,
56
+ action,
57
+ sha256: sha256(content)
58
+ };
59
+ });
60
+ }
61
+ export function checkProtocol(projectRoot) {
62
+ const artifactPath = relativeHarnessPath("protocol", "latest.json");
63
+ const fullArtifactPath = harnessPath(projectRoot, "protocol", "latest.json");
64
+ const files = [
65
+ checkProtocolFile(projectRoot, "AGENTS.md"),
66
+ checkProtocolFile(projectRoot, "CLAUDE.md")
67
+ ];
68
+ const artifactExists = fs.existsSync(fullArtifactPath);
69
+ const initialized = artifactExists
70
+ || files.some((file) => file.exists || file.hasManagedBlock);
71
+ const errors = [];
72
+ const warnings = [];
73
+ let provenanceCurrent = false;
74
+ if (!initialized) {
75
+ return {
76
+ status: "not_initialized",
77
+ artifactPath,
78
+ provenance: {
79
+ exists: false,
80
+ current: false
81
+ },
82
+ files,
83
+ errors,
84
+ warnings: ["Run frontend-harness init --json to inject managed AGENTS.md and CLAUDE.md protocol blocks."]
85
+ };
86
+ }
87
+ if (!artifactExists) {
88
+ errors.push("Protocol provenance artifact is missing; rerun frontend-harness init --json.");
89
+ }
90
+ else {
91
+ const provenance = readProtocolArtifact(fullArtifactPath, errors);
92
+ provenanceCurrent = provenance !== null && checkProtocolProvenance(provenance, files, errors);
93
+ }
94
+ for (const file of files) {
95
+ if (!file.exists) {
96
+ errors.push(`${file.path} is missing.`);
97
+ continue;
98
+ }
99
+ if (!file.hasManagedBlock) {
100
+ errors.push(`${file.path} is missing the frontend-harness managed block.`);
101
+ continue;
102
+ }
103
+ if (!file.managedBlockCurrent) {
104
+ errors.push(`${file.path} managed block is stale; rerun frontend-harness init --json.`);
105
+ }
106
+ }
107
+ return {
108
+ status: errors.length > 0 ? "failed" : "passed",
109
+ artifactPath,
110
+ provenance: {
111
+ exists: artifactExists,
112
+ current: provenanceCurrent
113
+ },
114
+ files,
115
+ errors,
116
+ warnings
117
+ };
118
+ }
119
+ function upsertProtocolFile(projectRoot, fileName, options) {
120
+ const fullPath = path.join(projectRoot, fileName);
121
+ const existing = fs.existsSync(fullPath) ? fs.readFileSync(fullPath, "utf8") : "";
122
+ const nextContent = replaceManagedBlock(existing, renderManagedBlock(fileName));
123
+ const action = !fs.existsSync(fullPath)
124
+ ? "created"
125
+ : existing === nextContent
126
+ ? "unchanged"
127
+ : "updated";
128
+ if (action !== "unchanged" && !options.dryRun) {
129
+ fs.writeFileSync(fullPath, nextContent, "utf8");
130
+ }
131
+ const persisted = action === "unchanged" ? existing : nextContent;
132
+ const managedBlock = renderManagedBlock(fileName);
133
+ return {
134
+ path: fileName,
135
+ action,
136
+ sha256: createHash("sha256").update(persisted).digest("hex"),
137
+ managedBlockSha256: sha256(managedBlock),
138
+ managedBlockStart: blockStart,
139
+ managedBlockEnd: blockEnd
140
+ };
141
+ }
142
+ function checkProtocolFile(projectRoot, fileName) {
143
+ const fullPath = path.join(projectRoot, fileName);
144
+ if (!fs.existsSync(fullPath)) {
145
+ return {
146
+ path: fileName,
147
+ exists: false,
148
+ hasManagedBlock: false,
149
+ managedBlockCurrent: false,
150
+ sha256: null,
151
+ managedBlockSha256: null
152
+ };
153
+ }
154
+ const content = fs.readFileSync(fullPath, "utf8");
155
+ const managedBlock = extractManagedBlock(content);
156
+ return {
157
+ path: fileName,
158
+ exists: true,
159
+ hasManagedBlock: managedBlock !== null,
160
+ managedBlockCurrent: managedBlock === renderManagedBlock(fileName),
161
+ sha256: sha256(content),
162
+ managedBlockSha256: managedBlock === null ? null : sha256(managedBlock)
163
+ };
164
+ }
165
+ function readProtocolArtifact(filePath, errors) {
166
+ try {
167
+ return readJson(filePath);
168
+ }
169
+ catch (error) {
170
+ const message = error instanceof Error ? error.message : String(error);
171
+ errors.push(`Protocol provenance artifact is malformed: ${message}`);
172
+ return null;
173
+ }
174
+ }
175
+ function checkProtocolProvenance(provenance, files, errors) {
176
+ if (!provenance || provenance.contractVersion !== 1 || !Array.isArray(provenance.files)) {
177
+ errors.push("Protocol provenance artifact has an unsupported shape; rerun frontend-harness init --json.");
178
+ return false;
179
+ }
180
+ let current = true;
181
+ for (const file of files) {
182
+ const record = provenance.files.find((item) => item.path === file.path);
183
+ if (!record) {
184
+ errors.push(`Protocol provenance artifact is missing ${file.path}; rerun frontend-harness init --json.`);
185
+ current = false;
186
+ continue;
187
+ }
188
+ if (!file.managedBlockSha256 || record.managedBlockSha256 !== file.managedBlockSha256) {
189
+ errors.push(`${file.path} managed block hash does not match protocol provenance; rerun frontend-harness init --json.`);
190
+ current = false;
191
+ }
192
+ }
193
+ return current;
194
+ }
195
+ function replaceManagedBlock(content, block) {
196
+ if (!content.trim()) {
197
+ return `${block}\n`;
198
+ }
199
+ const pattern = new RegExp(`${escapeRegExp(blockStart)}[\\s\\S]*?${escapeRegExp(blockEnd)}`);
200
+ if (pattern.test(content)) {
201
+ return ensureTrailingNewline(content.replace(pattern, block));
202
+ }
203
+ return `${ensureTrailingNewline(content)}\n${block}\n`;
204
+ }
205
+ function renderManagedBlock(fileName) {
206
+ const owner = fileName === "AGENTS.md" ? "Codex" : "Claude Code";
207
+ return [
208
+ blockStart,
209
+ `# Frontend Harness Execution Policy for ${owner}`,
210
+ "",
211
+ "This project uses `frontend-harness` as a project-level execution policy layer.",
212
+ "The coding agent runtime remains responsible for its own agent loop, tools, context management, permissions, retries, subagents, and skill loading.",
213
+ "`frontend-harness` owns only deterministic project facts, execution contracts, validation evidence, project-local knowledge, and repair artifacts.",
214
+ "",
215
+ "Required loop:",
216
+ "1. Run `frontend-harness context --json` before planning edits.",
217
+ "2. If agent planning input is useful, write only bounded hints (`intentSuggestion`, `constraintHints`, `componentHints`) to an agent decision JSON file and validate it with `frontend-harness decision check --json --file <agent-decision.json>`.",
218
+ "3. Run `frontend-harness plan --json \"<task>\"` with optional `--agent-decision <agent-decision.json>`, or consume the existing `.frontend-harness/plans/latest.json` before editing.",
219
+ "4. Edit only project files required by the system-owned plan and current task.",
220
+ "5. Record every changed project file with `frontend-harness state record-change <file>`.",
221
+ "6. Run `frontend-harness verify --json` and inspect the JSON result.",
222
+ "7. If verification fails, run `frontend-harness repair packet --json`, inspect `.frontend-harness/repair/latest.json`, patch the relevant project files, record changes, and verify again.",
223
+ "8. Before committing, inspect relevant project-local skills under `.frontend-harness/skills/` and follow the repository commit protocol.",
224
+ "9. Stop only when `frontend-harness state next --json` returns `done` for the unchanged task scope.",
225
+ "",
226
+ "Commit protocol:",
227
+ "- Use commit header format: `<type>(<scope>): <short description>`.",
228
+ "- Allowed commit types: `feat`, `fix`, `docs`, `style`, `refactor`, `perf`, `test`, `chore`.",
229
+ "- Keep the header concise and include verification evidence in the commit body or trailers when available.",
230
+ "",
231
+ "Boundaries:",
232
+ "- Do not treat `frontend-harness` as an agent launcher or runtime wrapper.",
233
+ "- Do not edit `.frontend-harness/` generated artifacts manually; let harness commands write them.",
234
+ "- Do not interpret planning or repair decision JSON as shell commands, patch content, scheduler directives, graph execution, skill routing, or RAG instructions.",
235
+ "- Do not put workflow type, execution units, file paths, dependency graph, execution order, or verification commands into agent planning input; the system plan always owns those structures.",
236
+ "- Keep durable project knowledge under `.frontend-harness/knowledge/` and project-local skills under `.frontend-harness/skills/` when intentionally authored.",
237
+ "",
238
+ "Useful commands:",
239
+ ...protocolUsefulCommandLines(),
240
+ blockEnd
241
+ ].join("\n");
242
+ }
243
+ function extractManagedBlock(content) {
244
+ const pattern = new RegExp(`${escapeRegExp(blockStart)}[\\s\\S]*?${escapeRegExp(blockEnd)}`);
245
+ return content.match(pattern)?.[0] ?? null;
246
+ }
247
+ function ensureTrailingNewline(content) {
248
+ return content.endsWith("\n") ? content : `${content}\n`;
249
+ }
250
+ function escapeRegExp(value) {
251
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
252
+ }
253
+ function sha256(value) {
254
+ return createHash("sha256").update(value).digest("hex");
255
+ }
256
+ //# sourceMappingURL=protocol-init.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"protocol-init.js","sourceRoot":"","sources":["../../src/runtime/protocol-init.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import type { RepairDecisionCheckResult, RepairDecisionSummary } from "../schemas/types.js";
2
+ export declare function checkRepairDecision(projectRoot: string, file: string | undefined): RepairDecisionCheckResult;
3
+ export declare function readAcceptedRepairDecision(projectRoot: string): RepairDecisionSummary | null;
@@ -0,0 +1,195 @@
1
+ import fs from "node:fs";
2
+ import { createHash } from "node:crypto";
3
+ import path from "node:path";
4
+ import { harnessPath, relativeHarnessPath } from "../storage/paths.js";
5
+ import { readJson, writeJson } from "../storage/json.js";
6
+ import { resolveProjectRelativePath } from "./project-paths.js";
7
+ const MAX_LIST_ITEMS = 20;
8
+ const MAX_STRING_LENGTH = 500;
9
+ export function checkRepairDecision(projectRoot, file) {
10
+ if (!file) {
11
+ throw new Error("repair decision check requires --file <repair-decision.json>");
12
+ }
13
+ const artifactPath = file;
14
+ const acceptedArtifactPath = relativeHarnessPath("repair-decision", "latest.json");
15
+ try {
16
+ const relativePath = validateReferencedFile(projectRoot, file);
17
+ const decision = readRepairDecision(projectRoot, relativePath);
18
+ const summary = {
19
+ artifactPath: acceptedArtifactPath,
20
+ sourceArtifactPath: relativePath,
21
+ ...readRepairProvenance(projectRoot),
22
+ contractVersion: 1,
23
+ intent: decision.intent,
24
+ hypothesis: decision.hypothesis,
25
+ targetFiles: decision.targetFiles,
26
+ verificationFocus: decision.verificationFocus
27
+ };
28
+ writeJson(harnessPath(projectRoot, "repair-decision", "latest.json"), summary);
29
+ return {
30
+ status: "passed",
31
+ artifactPath: relativePath,
32
+ acceptedArtifactPath,
33
+ contractVersion: 1,
34
+ decision,
35
+ errors: []
36
+ };
37
+ }
38
+ catch (error) {
39
+ return {
40
+ status: "failed",
41
+ artifactPath,
42
+ acceptedArtifactPath: null,
43
+ contractVersion: null,
44
+ decision: null,
45
+ errors: [error instanceof Error ? error.message : String(error)]
46
+ };
47
+ }
48
+ }
49
+ export function readAcceptedRepairDecision(projectRoot) {
50
+ const summary = readJson(harnessPath(projectRoot, "repair-decision", "latest.json"));
51
+ if (!summary) {
52
+ return null;
53
+ }
54
+ return {
55
+ ...summary,
56
+ artifactPath: relativeHarnessPath("repair-decision", "latest.json"),
57
+ repairPacketArtifactPath: summary.repairPacketArtifactPath ?? null,
58
+ repairPacketSha256: summary.repairPacketSha256 ?? null,
59
+ verificationArtifactPath: summary.verificationArtifactPath ?? null,
60
+ verificationSha256: summary.verificationSha256 ?? null
61
+ };
62
+ }
63
+ function readRepairProvenance(projectRoot) {
64
+ const repairPacket = describeExistingHarnessArtifact(projectRoot, relativeHarnessPath("repair", "latest.json"));
65
+ const verification = describeExistingHarnessArtifact(projectRoot, relativeHarnessPath("verification", "latest.json"));
66
+ return {
67
+ repairPacketArtifactPath: repairPacket.path,
68
+ repairPacketSha256: repairPacket.sha256,
69
+ verificationArtifactPath: verification.path,
70
+ verificationSha256: verification.sha256
71
+ };
72
+ }
73
+ function describeExistingHarnessArtifact(projectRoot, artifactPath) {
74
+ const fullPath = path.resolve(projectRoot, artifactPath);
75
+ const relativePath = path.relative(projectRoot, fullPath);
76
+ if (relativePath.startsWith("..") ||
77
+ path.isAbsolute(relativePath) ||
78
+ !fs.existsSync(fullPath) ||
79
+ !fs.statSync(fullPath).isFile()) {
80
+ return { path: null, sha256: null };
81
+ }
82
+ return {
83
+ path: artifactPath,
84
+ sha256: createHash("sha256").update(fs.readFileSync(fullPath)).digest("hex")
85
+ };
86
+ }
87
+ function validateReferencedFile(projectRoot, file) {
88
+ const { relativePath, fullPath } = resolveProjectRelativePath(projectRoot, file, "repair decision file");
89
+ if (!fs.existsSync(fullPath)) {
90
+ throw new Error(`repair decision file cannot be read: ${file}`);
91
+ }
92
+ return relativePath;
93
+ }
94
+ function readRepairDecision(projectRoot, file) {
95
+ const fullPath = path.resolve(projectRoot, file);
96
+ let parsed;
97
+ try {
98
+ parsed = JSON.parse(fs.readFileSync(fullPath, "utf8"));
99
+ }
100
+ catch (error) {
101
+ const detail = error instanceof Error ? error.message : String(error);
102
+ throw new Error(`repair decision file must contain valid JSON: ${file}: ${detail}`);
103
+ }
104
+ return parseRepairDecisionEnvelope(parsed, file);
105
+ }
106
+ function parseRepairDecisionEnvelope(value, file) {
107
+ if (!isRecord(value) || Array.isArray(value)) {
108
+ throw new Error(`repair decision file must contain a JSON object: ${file}`);
109
+ }
110
+ const allowedEnvelopeKeys = new Set(["contractVersion", "decision"]);
111
+ for (const key of Object.keys(value)) {
112
+ if (!allowedEnvelopeKeys.has(key)) {
113
+ throw new Error(`repair decision file contains unsupported field: ${key}`);
114
+ }
115
+ }
116
+ if (value["contractVersion"] !== 1) {
117
+ throw new Error("repair decision contractVersion must be 1");
118
+ }
119
+ if (!isRecord(value["decision"]) || Array.isArray(value["decision"])) {
120
+ throw new Error(`repair decision file decision must be a JSON object: ${file}`);
121
+ }
122
+ return parseRepairDecisionObject(value["decision"]);
123
+ }
124
+ function parseRepairDecisionObject(value) {
125
+ const allowedDecisionKeys = new Set(["intent", "hypothesis", "targetFiles", "verificationFocus"]);
126
+ for (const key of Object.keys(value)) {
127
+ if (!allowedDecisionKeys.has(key)) {
128
+ throw new Error(`repair decision file decision contains unsupported field: ${key}`);
129
+ }
130
+ }
131
+ return {
132
+ intent: parseBoundedString(value["intent"], "intent"),
133
+ hypothesis: parseBoundedString(value["hypothesis"], "hypothesis"),
134
+ targetFiles: parseTargetFiles(value["targetFiles"]),
135
+ verificationFocus: parseStringArray(value["verificationFocus"], "verificationFocus", 10)
136
+ };
137
+ }
138
+ function parseStringArray(value, label, maxItems) {
139
+ if (!Array.isArray(value)) {
140
+ throw new Error(`repair decision ${label} must be an array of strings`);
141
+ }
142
+ if (value.length === 0) {
143
+ throw new Error(`repair decision ${label} must contain at least 1 item`);
144
+ }
145
+ if (value.length > maxItems) {
146
+ throw new Error(`repair decision ${label} cannot contain more than ${maxItems} items`);
147
+ }
148
+ return value.map((item, index) => parseBoundedString(item, `${label}[${index}]`));
149
+ }
150
+ function parseTargetFiles(value) {
151
+ const files = parseStringArray(value, "targetFiles", MAX_LIST_ITEMS);
152
+ return files.map((file, index) => parseSafeRelativeProjectFile(file, `targetFiles[${index}]`));
153
+ }
154
+ function parseSafeRelativeProjectFile(value, label) {
155
+ if (value.includes("\0")) {
156
+ throw new Error(`repair decision ${label} cannot contain null bytes`);
157
+ }
158
+ if (value.includes("\\")) {
159
+ throw new Error(`repair decision ${label} must use POSIX-style relative paths`);
160
+ }
161
+ if (path.isAbsolute(value)) {
162
+ throw new Error(`repair decision ${label} must be a relative project file path`);
163
+ }
164
+ const normalized = path.posix.normalize(value);
165
+ if (normalized === "." ||
166
+ normalized.startsWith("../") ||
167
+ normalized === ".." ||
168
+ normalized.endsWith("/")) {
169
+ throw new Error(`repair decision ${label} must be a safe relative project file path`);
170
+ }
171
+ if (normalized === ".frontend-harness" ||
172
+ normalized === ".omx" ||
173
+ normalized.startsWith(".frontend-harness/") ||
174
+ normalized.startsWith(".omx/")) {
175
+ throw new Error(`repair decision ${label} must target project source files, not harness state`);
176
+ }
177
+ return normalized;
178
+ }
179
+ function parseBoundedString(value, label) {
180
+ if (typeof value !== "string") {
181
+ throw new Error(`repair decision ${label} must be a string`);
182
+ }
183
+ const trimmed = value.trim();
184
+ if (!trimmed) {
185
+ throw new Error(`repair decision ${label} cannot be empty`);
186
+ }
187
+ if (trimmed.length > MAX_STRING_LENGTH) {
188
+ throw new Error(`repair decision ${label} cannot exceed ${MAX_STRING_LENGTH} characters`);
189
+ }
190
+ return trimmed;
191
+ }
192
+ function isRecord(value) {
193
+ return typeof value === "object" && value !== null;
194
+ }
195
+ //# sourceMappingURL=repair-decision.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"repair-decision.js","sourceRoot":"","sources":["../../src/runtime/repair-decision.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ import type { RepairPacketArtifact, StateNextResult, VerificationResult } from "../schemas/types.js";
2
+ interface RepairPacketOptions {
3
+ verification?: VerificationResult | null;
4
+ nextAction?: StateNextResult | null;
5
+ }
6
+ export declare function createRepairPacket(projectRoot: string, options?: RepairPacketOptions): RepairPacketArtifact;
7
+ export {};
@@ -0,0 +1,159 @@
1
+ import fs from "node:fs";
2
+ import { createHash } from "node:crypto";
3
+ import path from "node:path";
4
+ import { harnessPath, relativeHarnessPath } from "../storage/paths.js";
5
+ import { readJson, writeJson, writeText } from "../storage/json.js";
6
+ import { getNextAction, loadState } from "./state.js";
7
+ import { isProjectRelativeSubpath } from "./project-paths.js";
8
+ const MAX_LOG_EXCERPT_CHARS = 4000;
9
+ export function createRepairPacket(projectRoot, options = {}) {
10
+ const verification = options.verification ?? readLatestVerification(projectRoot);
11
+ const nextAction = options.nextAction ?? getNextAction(projectRoot);
12
+ const state = loadState(projectRoot);
13
+ const failedResults = verification?.results.filter((result) => result.status === "failed" || result.status === "error") ?? [];
14
+ const packet = {
15
+ contractVersion: 1,
16
+ createdAt: new Date().toISOString(),
17
+ status: failedResults.length > 0 ? "ready" : "not_applicable",
18
+ stateStatus: state.status,
19
+ verificationStatus: verification?.status ?? state.verification.status,
20
+ nextAction,
21
+ artifacts: {
22
+ verification: verificationArtifactPath(),
23
+ repairPacket: repairPacketArtifactPath(),
24
+ repairPacketMarkdown: repairPacketMarkdownPath()
25
+ },
26
+ changedFiles: [...state.changedFiles].sort(),
27
+ failedCommands: failedResults.map((result) => {
28
+ const logEvidence = readLogEvidence(projectRoot, result.logPath);
29
+ return {
30
+ name: result.name,
31
+ command: result.command,
32
+ status: result.status,
33
+ exitCode: result.exitCode,
34
+ logPath: result.logPath,
35
+ logExcerpt: logEvidence.excerpt,
36
+ logSha256: logEvidence.sha256,
37
+ logSizeBytes: logEvidence.sizeBytes,
38
+ excerptStartByte: logEvidence.excerptStartByte,
39
+ excerptEndByte: logEvidence.excerptEndByte,
40
+ excerptTruncated: logEvidence.excerptTruncated
41
+ };
42
+ }),
43
+ retryGuidance: verification?.retryGuidance ?? null,
44
+ instructions: buildRepairInstructions(failedResults.length)
45
+ };
46
+ writeJson(harnessPath(projectRoot, "repair", "latest.json"), packet);
47
+ writeText(harnessPath(projectRoot, "repair", "latest.md"), renderRepairPacket(packet));
48
+ return packet;
49
+ }
50
+ function readLatestVerification(projectRoot) {
51
+ return readJson(harnessPath(projectRoot, "verification", "latest.json"));
52
+ }
53
+ function readLogEvidence(projectRoot, logPath) {
54
+ const empty = {
55
+ excerpt: null,
56
+ sha256: null,
57
+ sizeBytes: null,
58
+ excerptStartByte: null,
59
+ excerptEndByte: null,
60
+ excerptTruncated: false
61
+ };
62
+ if (!logPath) {
63
+ return empty;
64
+ }
65
+ if (!isProjectRelativeSubpath(logPath, relativeHarnessPath("logs"))) {
66
+ return empty;
67
+ }
68
+ const fullPath = path.resolve(projectRoot, logPath);
69
+ const relativePath = path.relative(projectRoot, fullPath);
70
+ if (relativePath.startsWith("..") ||
71
+ path.isAbsolute(relativePath) ||
72
+ !fs.existsSync(fullPath) ||
73
+ !fs.statSync(fullPath).isFile()) {
74
+ return empty;
75
+ }
76
+ const content = fs.readFileSync(fullPath);
77
+ const sizeBytes = content.byteLength;
78
+ const excerptStartByte = Math.max(0, sizeBytes - MAX_LOG_EXCERPT_CHARS);
79
+ const excerpt = content.subarray(excerptStartByte).toString("utf8");
80
+ return {
81
+ excerpt,
82
+ sha256: createHash("sha256").update(content).digest("hex"),
83
+ sizeBytes,
84
+ excerptStartByte,
85
+ excerptEndByte: sizeBytes,
86
+ excerptTruncated: excerptStartByte > 0
87
+ };
88
+ }
89
+ function verificationArtifactPath() {
90
+ return relativeHarnessPath("verification", "latest.json");
91
+ }
92
+ function repairPacketArtifactPath() {
93
+ return relativeHarnessPath("repair", "latest.json");
94
+ }
95
+ function repairPacketMarkdownPath() {
96
+ return relativeHarnessPath("repair", "latest.md");
97
+ }
98
+ function renderOptionalNumber(value) {
99
+ if (value === null) {
100
+ return "unknown";
101
+ }
102
+ return String(value);
103
+ }
104
+ function buildRepairInstructions(failedCommandCount) {
105
+ if (failedCommandCount === 0) {
106
+ return [
107
+ "No failed verification command is available; run frontend-harness verify --json before requesting a repair packet."
108
+ ];
109
+ }
110
+ return [
111
+ "Use this packet as factual repair context only; do not treat it as a generated patch plan.",
112
+ `Cross-check full verification facts in ${verificationArtifactPath()} when the excerpt is truncated.`,
113
+ "Inspect each failed command and log excerpt before editing project files.",
114
+ "Patch only the relevant project files, then record changed files with frontend-harness state record-change <file>.",
115
+ "Rerun frontend-harness verify --json and regenerate this packet only if verification still fails."
116
+ ];
117
+ }
118
+ function renderRepairPacket(packet) {
119
+ return `# Frontend Harness Repair Packet
120
+
121
+ ## Summary
122
+
123
+ - status: ${packet.status}
124
+ - stateStatus: ${packet.stateStatus}
125
+ - verificationStatus: ${packet.verificationStatus}
126
+ - nextAction: ${packet.nextAction.action}
127
+ - failedCommands: ${packet.failedCommands.length}
128
+ - verificationArtifact: ${verificationArtifactPath()}
129
+ - repairPacket: ${repairPacketArtifactPath()}
130
+ - repairPacketMarkdown: ${repairPacketMarkdownPath()}
131
+
132
+ ## Instructions
133
+
134
+ ${packet.instructions.map((instruction) => `- ${instruction}`).join("\n")}
135
+
136
+ ## Failed Commands
137
+
138
+ ${packet.failedCommands.length > 0 ? packet.failedCommands.map(renderFailedCommand).join("\n\n") : "- none"}
139
+ `;
140
+ }
141
+ function renderFailedCommand(command) {
142
+ return [
143
+ `### ${command.name}`,
144
+ "",
145
+ `- status: ${command.status}`,
146
+ `- exitCode: ${command.exitCode ?? "unknown"}`,
147
+ `- command: ${command.command ?? "unknown"}`,
148
+ `- logPath: ${command.logPath ?? "none"}`,
149
+ `- logSha256: ${command.logSha256 ?? "unknown"}`,
150
+ `- logSizeBytes: ${renderOptionalNumber(command.logSizeBytes)}`,
151
+ `- excerptRange: ${renderOptionalNumber(command.excerptStartByte)}..${renderOptionalNumber(command.excerptEndByte)}`,
152
+ `- excerptTruncated: ${String(command.excerptTruncated)}`,
153
+ "",
154
+ "```text",
155
+ command.logExcerpt ?? "",
156
+ "```"
157
+ ].join("\n");
158
+ }
159
+ //# sourceMappingURL=repair-packet.js.map