@praneeth_54/agentdoctor 2.0.1 → 3.0.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 (192) hide show
  1. package/CHANGELOG.md +123 -1
  2. package/README.md +510 -260
  3. package/dist/agent/approvals.d.ts +24 -0
  4. package/dist/agent/approvals.js +64 -0
  5. package/dist/agent/chat/deterministic.d.ts +6 -0
  6. package/dist/agent/chat/deterministic.js +180 -0
  7. package/dist/agent/chat/index.d.ts +7 -0
  8. package/dist/agent/chat/index.js +5 -0
  9. package/dist/agent/chat/memory.d.ts +44 -0
  10. package/dist/agent/chat/memory.js +103 -0
  11. package/dist/agent/chat/project-summary.d.ts +16 -0
  12. package/dist/agent/chat/project-summary.js +70 -0
  13. package/dist/agent/chat/prompts.d.ts +6 -0
  14. package/dist/agent/chat/prompts.js +39 -0
  15. package/dist/agent/chat/response.d.ts +19 -0
  16. package/dist/agent/chat/response.js +109 -0
  17. package/dist/agent/chat/service.d.ts +42 -0
  18. package/dist/agent/chat/service.js +246 -0
  19. package/dist/agent/chat/types.d.ts +48 -0
  20. package/dist/agent/chat/types.js +1 -0
  21. package/dist/agent/context/retrieve.d.ts +21 -0
  22. package/dist/agent/context/retrieve.js +117 -0
  23. package/dist/agent/context/truth.d.ts +6 -0
  24. package/dist/agent/context/truth.js +19 -0
  25. package/dist/agent/context/types.d.ts +23 -0
  26. package/dist/agent/context/types.js +4 -0
  27. package/dist/agent/index.d.ts +28 -0
  28. package/dist/agent/index.js +15 -0
  29. package/dist/agent/loop.d.ts +53 -0
  30. package/dist/agent/loop.js +253 -0
  31. package/dist/agent/modes.d.ts +18 -0
  32. package/dist/agent/modes.js +102 -0
  33. package/dist/agent/plan.d.ts +30 -0
  34. package/dist/agent/plan.js +121 -0
  35. package/dist/agent/roles.d.ts +12 -0
  36. package/dist/agent/roles.js +138 -0
  37. package/dist/agent/runtime.d.ts +83 -0
  38. package/dist/agent/runtime.js +293 -0
  39. package/dist/agent/state.d.ts +30 -0
  40. package/dist/agent/state.js +95 -0
  41. package/dist/agent/student.d.ts +56 -0
  42. package/dist/agent/student.js +230 -0
  43. package/dist/agent/tools/execute.d.ts +20 -0
  44. package/dist/agent/tools/execute.js +365 -0
  45. package/dist/agent/tools/index.d.ts +6 -0
  46. package/dist/agent/tools/index.js +5 -0
  47. package/dist/agent/tools/registry.d.ts +7 -0
  48. package/dist/agent/tools/registry.js +212 -0
  49. package/dist/agent/tools/run.d.ts +24 -0
  50. package/dist/agent/tools/run.js +44 -0
  51. package/dist/agent/tools/types.d.ts +32 -0
  52. package/dist/agent/tools/types.js +10 -0
  53. package/dist/agent/tools/write.d.ts +24 -0
  54. package/dist/agent/tools/write.js +121 -0
  55. package/dist/agent/verify.d.ts +29 -0
  56. package/dist/agent/verify.js +210 -0
  57. package/dist/ai/config.d.ts +22 -0
  58. package/dist/ai/config.js +69 -0
  59. package/dist/ai/index.d.ts +17 -0
  60. package/dist/ai/index.js +52 -0
  61. package/dist/ai/providers/mock.d.ts +20 -0
  62. package/dist/ai/providers/mock.js +84 -0
  63. package/dist/ai/providers/none.d.ts +6 -0
  64. package/dist/ai/providers/none.js +23 -0
  65. package/dist/ai/providers/openai-compatible.d.ts +21 -0
  66. package/dist/ai/providers/openai-compatible.js +151 -0
  67. package/dist/ai/redact.d.ts +9 -0
  68. package/dist/ai/redact.js +61 -0
  69. package/dist/ai/types.d.ts +71 -0
  70. package/dist/ai/types.js +6 -0
  71. package/dist/cli/commands/agent.d.ts +29 -0
  72. package/dist/cli/commands/agent.js +164 -0
  73. package/dist/cli/commands/chat.d.ts +14 -0
  74. package/dist/cli/commands/chat.js +153 -0
  75. package/dist/cli/commands/learn.d.ts +12 -0
  76. package/dist/cli/commands/learn.js +107 -0
  77. package/dist/cli/commands/product.d.ts +19 -0
  78. package/dist/cli/commands/product.js +147 -0
  79. package/dist/cli/commands/start.d.ts +15 -0
  80. package/dist/cli/commands/start.js +80 -0
  81. package/dist/cli/program.js +233 -0
  82. package/dist/constants.d.ts +1 -1
  83. package/dist/constants.js +1 -1
  84. package/dist/dashboard/server.d.ts +6 -0
  85. package/dist/dashboard/server.js +333 -43
  86. package/dist/enforcement/runner.d.ts +5 -0
  87. package/dist/enforcement/runner.js +51 -4
  88. package/dist/index.d.ts +17 -0
  89. package/dist/index.js +10 -0
  90. package/dist/intelligence/graph/build.js +36 -8
  91. package/dist/intelligence/resolve/imports.js +1 -1
  92. package/dist/languages/dart.d.ts +10 -0
  93. package/dist/languages/dart.js +99 -0
  94. package/dist/languages/go.d.ts +13 -7
  95. package/dist/languages/go.js +91 -26
  96. package/dist/languages/index.d.ts +5 -1
  97. package/dist/languages/index.js +13 -36
  98. package/dist/languages/java.d.ts +10 -0
  99. package/dist/languages/java.js +80 -0
  100. package/dist/languages/kotlin.d.ts +10 -0
  101. package/dist/languages/kotlin.js +85 -0
  102. package/dist/languages/php.d.ts +1 -0
  103. package/dist/languages/php.js +11 -2
  104. package/dist/languages/python.d.ts +6 -2
  105. package/dist/languages/python.js +39 -9
  106. package/dist/languages/rust.d.ts +10 -0
  107. package/dist/languages/rust.js +93 -0
  108. package/dist/languages/types.d.ts +4 -3
  109. package/dist/mcp/agent/registry.d.ts +13 -0
  110. package/dist/mcp/agent/registry.js +320 -0
  111. package/dist/mcp/agentdoctor/server.js +8 -1
  112. package/dist/mcp/intelligence/handlers.d.ts +3 -0
  113. package/dist/mcp/intelligence/handlers.js +28 -0
  114. package/dist/mcp/intelligence/registry.d.ts +1 -1
  115. package/dist/mcp/intelligence/registry.js +30 -1
  116. package/dist/product/api/doctor.d.ts +14 -0
  117. package/dist/product/api/doctor.js +185 -0
  118. package/dist/product/api/openapi.d.ts +7 -0
  119. package/dist/product/api/openapi.js +122 -0
  120. package/dist/product/approval/model.d.ts +30 -0
  121. package/dist/product/approval/model.js +64 -0
  122. package/dist/product/approval/session.d.ts +49 -0
  123. package/dist/product/approval/session.js +134 -0
  124. package/dist/product/database/doctor.d.ts +30 -0
  125. package/dist/product/database/doctor.js +184 -0
  126. package/dist/product/decisions/ledger.d.ts +24 -0
  127. package/dist/product/decisions/ledger.js +110 -0
  128. package/dist/product/deps/analyze.d.ts +46 -0
  129. package/dist/product/deps/analyze.js +137 -0
  130. package/dist/product/deps/lockfiles.d.ts +25 -0
  131. package/dist/product/deps/lockfiles.js +200 -0
  132. package/dist/product/discovery/roots.d.ts +42 -0
  133. package/dist/product/discovery/roots.js +215 -0
  134. package/dist/product/dna/build.d.ts +50 -0
  135. package/dist/product/dna/build.js +255 -0
  136. package/dist/product/eval/lab.d.ts +17 -0
  137. package/dist/product/eval/lab.js +218 -0
  138. package/dist/product/events/doctor.d.ts +21 -0
  139. package/dist/product/events/doctor.js +147 -0
  140. package/dist/product/evidence-scan.d.ts +12 -0
  141. package/dist/product/evidence-scan.js +46 -0
  142. package/dist/product/evolution/timeline.d.ts +29 -0
  143. package/dist/product/evolution/timeline.js +123 -0
  144. package/dist/product/features/intelligence.d.ts +23 -0
  145. package/dist/product/features/intelligence.js +158 -0
  146. package/dist/product/forensic/mode.d.ts +25 -0
  147. package/dist/product/forensic/mode.js +70 -0
  148. package/dist/product/graph/enrich-languages.d.ts +20 -0
  149. package/dist/product/graph/enrich-languages.js +193 -0
  150. package/dist/product/health/code-health.d.ts +20 -0
  151. package/dist/product/health/code-health.js +149 -0
  152. package/dist/product/index.d.ts +69 -0
  153. package/dist/product/index.js +35 -0
  154. package/dist/product/ledger/change-ledger.d.ts +23 -0
  155. package/dist/product/ledger/change-ledger.js +64 -0
  156. package/dist/product/map/software-map.d.ts +17 -0
  157. package/dist/product/map/software-map.js +95 -0
  158. package/dist/product/memory/institutional.d.ts +16 -0
  159. package/dist/product/memory/institutional.js +87 -0
  160. package/dist/product/ops/incident.d.ts +20 -0
  161. package/dist/product/ops/incident.js +61 -0
  162. package/dist/product/ops/infra.d.ts +15 -0
  163. package/dist/product/ops/infra.js +112 -0
  164. package/dist/product/org/model.d.ts +34 -0
  165. package/dist/product/org/model.js +195 -0
  166. package/dist/product/privacy/doctor.d.ts +13 -0
  167. package/dist/product/privacy/doctor.js +90 -0
  168. package/dist/product/requirements/trace.d.ts +21 -0
  169. package/dist/product/requirements/trace.js +166 -0
  170. package/dist/product/search/index.d.ts +29 -0
  171. package/dist/product/search/index.js +116 -0
  172. package/dist/product/search/software-search.d.ts +19 -0
  173. package/dist/product/search/software-search.js +100 -0
  174. package/dist/product/security/doctor.d.ts +23 -0
  175. package/dist/product/security/doctor.js +124 -0
  176. package/dist/product/self/diagnose.d.ts +15 -0
  177. package/dist/product/self/diagnose.js +82 -0
  178. package/dist/product/techdebt/roadmap.d.ts +20 -0
  179. package/dist/product/techdebt/roadmap.js +118 -0
  180. package/dist/product/testbrain/analyze.d.ts +29 -0
  181. package/dist/product/testbrain/analyze.js +129 -0
  182. package/dist/product/truth.d.ts +12 -0
  183. package/dist/product/truth.js +30 -0
  184. package/dist/product/twin/digital-twin.d.ts +23 -0
  185. package/dist/product/twin/digital-twin.js +52 -0
  186. package/dist/product/twin/store.d.ts +21 -0
  187. package/dist/product/twin/store.js +70 -0
  188. package/dist/product/whatif/engine.d.ts +28 -0
  189. package/dist/product/whatif/engine.js +70 -0
  190. package/dist/security/paths.js +43 -15
  191. package/dist/utils/fs.js +10 -2
  192. package/package.json +1 -1
@@ -0,0 +1,212 @@
1
+ const READ_SPECS = [
2
+ {
3
+ name: "read_file",
4
+ description: "READ: Read a UTF-8 text file inside the workspace (path-safe).",
5
+ risk: "LOW",
6
+ category: "read",
7
+ parameters: {
8
+ type: "object",
9
+ properties: { path: { type: "string" }, maxBytes: { type: "number" } },
10
+ required: ["path"],
11
+ },
12
+ },
13
+ {
14
+ name: "list_files",
15
+ description: "READ: List repository files (bounded discovery).",
16
+ risk: "LOW",
17
+ category: "read",
18
+ parameters: {
19
+ type: "object",
20
+ properties: { limit: { type: "number" } },
21
+ },
22
+ },
23
+ {
24
+ name: "search_code",
25
+ description: "READ: Search graph nodes by label/path substring.",
26
+ risk: "LOW",
27
+ category: "read",
28
+ parameters: {
29
+ type: "object",
30
+ properties: { query: { type: "string" } },
31
+ required: ["query"],
32
+ },
33
+ },
34
+ {
35
+ name: "find_symbol",
36
+ description: "READ: Lookup symbols by name.",
37
+ risk: "LOW",
38
+ category: "read",
39
+ parameters: {
40
+ type: "object",
41
+ properties: { name: { type: "string" } },
42
+ required: ["name"],
43
+ },
44
+ },
45
+ {
46
+ name: "find_references",
47
+ description: "READ: Refactor/rename impact for a symbol (best-effort references).",
48
+ risk: "LOW",
49
+ category: "read",
50
+ parameters: {
51
+ type: "object",
52
+ properties: { symbol: { type: "string" } },
53
+ required: ["symbol"],
54
+ },
55
+ },
56
+ {
57
+ name: "find_callers",
58
+ description: "READ: Call-graph callers for a symbol/path.",
59
+ risk: "LOW",
60
+ category: "read",
61
+ parameters: {
62
+ type: "object",
63
+ properties: { target: { type: "string" } },
64
+ required: ["target"],
65
+ },
66
+ },
67
+ {
68
+ name: "find_callees",
69
+ description: "READ: Call-graph callees for a symbol/path.",
70
+ risk: "LOW",
71
+ category: "read",
72
+ parameters: {
73
+ type: "object",
74
+ properties: { target: { type: "string" } },
75
+ required: ["target"],
76
+ },
77
+ },
78
+ {
79
+ name: "inspect_project",
80
+ description: "READ: Project fingerprint summary.",
81
+ risk: "LOW",
82
+ category: "read",
83
+ parameters: { type: "object", properties: {} },
84
+ },
85
+ {
86
+ name: "inspect_architecture",
87
+ description: "READ: Architecture contract check + C4 overview.",
88
+ risk: "LOW",
89
+ category: "read",
90
+ parameters: { type: "object", properties: {} },
91
+ },
92
+ {
93
+ name: "inspect_dependencies",
94
+ description: "READ: Dependency edges for a target path/symbol.",
95
+ risk: "LOW",
96
+ category: "read",
97
+ parameters: {
98
+ type: "object",
99
+ properties: { target: { type: "string" } },
100
+ required: ["target"],
101
+ },
102
+ },
103
+ {
104
+ name: "inspect_tests",
105
+ description: "READ: Test-impact analysis for current changes (heuristic).",
106
+ risk: "LOW",
107
+ category: "read",
108
+ parameters: { type: "object", properties: {} },
109
+ },
110
+ {
111
+ name: "inspect_findings",
112
+ description: "READ: AgentDoctor safety scan findings.",
113
+ risk: "LOW",
114
+ category: "read",
115
+ parameters: { type: "object", properties: {} },
116
+ },
117
+ {
118
+ name: "inspect_git_status",
119
+ description: "READ: Git change status (staged/unstaged/untracked).",
120
+ risk: "LOW",
121
+ category: "read",
122
+ parameters: { type: "object", properties: {} },
123
+ },
124
+ {
125
+ name: "inspect_git_diff",
126
+ description: "READ: Change set summary (not full patch dump).",
127
+ risk: "LOW",
128
+ category: "read",
129
+ parameters: { type: "object", properties: { since: { type: "string" } } },
130
+ },
131
+ ];
132
+ const WRITE_SPECS = [
133
+ {
134
+ name: "create_file",
135
+ description: "WRITE: Create a new file inside the workspace (requires approval).",
136
+ risk: "MEDIUM",
137
+ category: "write",
138
+ parameters: {
139
+ type: "object",
140
+ properties: { path: { type: "string" }, content: { type: "string" } },
141
+ required: ["path", "content"],
142
+ },
143
+ },
144
+ {
145
+ name: "edit_file",
146
+ description: "WRITE: Edit a file (exact replace, line range, or full content) with diff.",
147
+ risk: "MEDIUM",
148
+ category: "write",
149
+ parameters: {
150
+ type: "object",
151
+ properties: {
152
+ path: { type: "string" },
153
+ content: { type: "string" },
154
+ oldContent: { type: "string" },
155
+ startLine: { type: "number" },
156
+ endLine: { type: "number" },
157
+ replacement: { type: "string" },
158
+ },
159
+ required: ["path"],
160
+ },
161
+ },
162
+ {
163
+ name: "delete_file",
164
+ description: "WRITE: Delete a file inside the workspace (HIGH risk).",
165
+ risk: "HIGH",
166
+ category: "write",
167
+ parameters: {
168
+ type: "object",
169
+ properties: { path: { type: "string" } },
170
+ required: ["path"],
171
+ },
172
+ },
173
+ ];
174
+ const EXEC_SPECS = [
175
+ {
176
+ name: "run_command",
177
+ description: "EXECUTE: Run a controlled argv command (policy + shell=false).",
178
+ risk: "MEDIUM",
179
+ category: "execute",
180
+ parameters: {
181
+ type: "object",
182
+ properties: {
183
+ command: { type: "string" },
184
+ argv: { type: "array", items: { type: "string" } },
185
+ },
186
+ },
187
+ },
188
+ {
189
+ name: "run_tests",
190
+ description: "EXECUTE: Run project tests via controlled runner.",
191
+ risk: "MEDIUM",
192
+ category: "execute",
193
+ parameters: {
194
+ type: "object",
195
+ properties: { command: { type: "string" } },
196
+ },
197
+ },
198
+ ];
199
+ export function listAgentToolSpecs(options) {
200
+ const out = [...READ_SPECS];
201
+ if (options?.includeWrite)
202
+ out.push(...WRITE_SPECS);
203
+ if (options?.includeExecute)
204
+ out.push(...EXEC_SPECS);
205
+ return out;
206
+ }
207
+ export function getToolSpec(name) {
208
+ return listAgentToolSpecs({ includeWrite: true, includeExecute: true }).find((t) => t.name === name);
209
+ }
210
+ export function riskForTool(name) {
211
+ return getToolSpec(name)?.risk ?? "CRITICAL";
212
+ }
@@ -0,0 +1,24 @@
1
+ export interface AgentCommandResult {
2
+ ok: boolean;
3
+ blocked: boolean;
4
+ command: string;
5
+ notice: string;
6
+ stdout?: string;
7
+ stderr?: string;
8
+ exitCode?: number | null;
9
+ durationMs?: number;
10
+ decision: string;
11
+ reason: string;
12
+ }
13
+ /**
14
+ * Execute a project command only via the existing controlled runner (shell=false by default).
15
+ */
16
+ export declare function runAgentCommand(options: {
17
+ root: string;
18
+ command?: string;
19
+ argv?: string[];
20
+ execute?: boolean;
21
+ timeoutMs?: number;
22
+ }): Promise<AgentCommandResult>;
23
+ /** Detect common project test runners from package manifests / markers (best-effort). */
24
+ export declare function inferTestArgv(packageJson: unknown): string[] | null;
@@ -0,0 +1,44 @@
1
+ import { runControlledCommand } from "../../enforcement/runner.js";
2
+ import { resolveRepoRoot } from "../../utils/path.js";
3
+ /**
4
+ * Execute a project command only via the existing controlled runner (shell=false by default).
5
+ */
6
+ export async function runAgentCommand(options) {
7
+ const root = resolveRepoRoot(options.root);
8
+ const result = await runControlledCommand({
9
+ root,
10
+ ...(options.command !== undefined ? { command: options.command } : {}),
11
+ ...(options.argv !== undefined ? { argv: options.argv } : {}),
12
+ executeIfAllowed: options.execute !== false,
13
+ useShell: false,
14
+ ...(options.timeoutMs !== undefined ? { timeoutMs: options.timeoutMs } : {}),
15
+ });
16
+ const decision = result.decision.decision;
17
+ const blocked = decision === "block" || decision === "deny-network";
18
+ const exitOk = result.exitCode === 0;
19
+ const executed = result.decision.executionStatus === "executed";
20
+ const allowed = decision === "allow";
21
+ return {
22
+ ok: allowed && executed && exitOk,
23
+ blocked,
24
+ command: options.command ?? (options.argv ?? []).join(" "),
25
+ notice: result.notice,
26
+ ...(result.stdout !== undefined ? { stdout: result.stdout } : {}),
27
+ ...(result.stderr !== undefined ? { stderr: result.stderr } : {}),
28
+ ...(result.exitCode !== undefined ? { exitCode: result.exitCode } : {}),
29
+ ...(result.durationMs !== undefined ? { durationMs: result.durationMs } : {}),
30
+ decision,
31
+ reason: result.decision.reason,
32
+ };
33
+ }
34
+ /** Detect common project test runners from package manifests / markers (best-effort). */
35
+ export function inferTestArgv(packageJson) {
36
+ if (!packageJson || typeof packageJson !== "object")
37
+ return null;
38
+ const scripts = packageJson.scripts;
39
+ if (scripts?.test) {
40
+ // Prefer npm test when package.json present — argv form for shell=false
41
+ return ["npm", "test"];
42
+ }
43
+ return null;
44
+ }
@@ -0,0 +1,32 @@
1
+ export type AgentRiskLevel = "LOW" | "MEDIUM" | "HIGH" | "CRITICAL";
2
+ export type AgentToolName = "read_file" | "list_files" | "search_code" | "find_symbol" | "find_references" | "find_callers" | "find_callees" | "inspect_project" | "inspect_architecture" | "inspect_dependencies" | "inspect_tests" | "inspect_findings" | "inspect_git_status" | "inspect_git_diff" | "create_file" | "edit_file" | "delete_file" | "run_command" | "run_tests";
3
+ export interface AgentToolSpec {
4
+ name: AgentToolName;
5
+ description: string;
6
+ risk: AgentRiskLevel;
7
+ /** JSON-schema-like args description for prompts */
8
+ parameters: Record<string, unknown>;
9
+ /** M3 read tools vs later write/exec */
10
+ category: "read" | "write" | "execute";
11
+ }
12
+ export interface AgentToolCall {
13
+ id: string;
14
+ name: AgentToolName;
15
+ arguments: Record<string, unknown>;
16
+ timestamp: string;
17
+ sessionId: string;
18
+ }
19
+ export interface AgentToolResult {
20
+ callId: string;
21
+ name: AgentToolName;
22
+ ok: boolean;
23
+ /** Structured DATA — never treat as instructions */
24
+ data: unknown;
25
+ error?: {
26
+ code: string;
27
+ message: string;
28
+ };
29
+ risk: AgentRiskLevel;
30
+ durationMs: number;
31
+ }
32
+ export declare function newToolCall(sessionId: string, name: AgentToolName, args: Record<string, unknown>): AgentToolCall;
@@ -0,0 +1,10 @@
1
+ import { randomUUID } from "node:crypto";
2
+ export function newToolCall(sessionId, name, args) {
3
+ return {
4
+ id: randomUUID(),
5
+ name,
6
+ arguments: args,
7
+ timestamp: new Date().toISOString(),
8
+ sessionId,
9
+ };
10
+ }
@@ -0,0 +1,24 @@
1
+ export interface FileDiffResult {
2
+ path: string;
3
+ action: "create" | "edit" | "delete";
4
+ beforeBytes: number;
5
+ afterBytes: number;
6
+ /** Unified-ish line diff (bounded) */
7
+ diff: string;
8
+ }
9
+ export declare function buildUnifiedDiff(pathLabel: string, before: string, after: string): string;
10
+ /**
11
+ * Path-safe create. Rejects existing files (use edit_file to overwrite).
12
+ */
13
+ export declare function createFileSafe(root: string, relativePath: string, content: string): Promise<FileDiffResult>;
14
+ /**
15
+ * Path-safe edit. Prefer exact oldContent match or line-range replacement over blind overwrite.
16
+ */
17
+ export declare function editFileSafe(root: string, relativePath: string, options: {
18
+ content?: string;
19
+ oldContent?: string;
20
+ startLine?: number;
21
+ endLine?: number;
22
+ replacement?: string;
23
+ }): Promise<FileDiffResult>;
24
+ export declare function deleteFileSafe(root: string, relativePath: string): Promise<FileDiffResult>;
@@ -0,0 +1,121 @@
1
+ import fs from "node:fs/promises";
2
+ import path from "node:path";
3
+ import { PathEscapeError, resolveSafeRepoPath } from "../../security/paths.js";
4
+ import { atomicWriteTextFile } from "../../utils/fs.js";
5
+ const MAX_DIFF_LINES = 400;
6
+ export function buildUnifiedDiff(pathLabel, before, after) {
7
+ const a = before.split(/\r?\n/);
8
+ const b = after.split(/\r?\n/);
9
+ const lines = [`--- a/${pathLabel}`, `+++ b/${pathLabel}`];
10
+ const max = Math.max(a.length, b.length);
11
+ let shown = 0;
12
+ for (let i = 0; i < max && shown < MAX_DIFF_LINES; i += 1) {
13
+ const left = a[i];
14
+ const right = b[i];
15
+ if (left === right) {
16
+ if (left !== undefined) {
17
+ lines.push(` ${left}`);
18
+ shown += 1;
19
+ }
20
+ continue;
21
+ }
22
+ if (left !== undefined) {
23
+ lines.push(`-${left}`);
24
+ shown += 1;
25
+ }
26
+ if (right !== undefined) {
27
+ lines.push(`+${right}`);
28
+ shown += 1;
29
+ }
30
+ }
31
+ if (shown >= MAX_DIFF_LINES) {
32
+ lines.push("... diff truncated ...");
33
+ }
34
+ return lines.join("\n");
35
+ }
36
+ function normalizeRel(rel) {
37
+ return rel.split(path.sep).join("/");
38
+ }
39
+ /**
40
+ * Path-safe create. Rejects existing files (use edit_file to overwrite).
41
+ */
42
+ export async function createFileSafe(root, relativePath, content) {
43
+ const abs = resolveSafeRepoPath(root, relativePath);
44
+ const rel = normalizeRel(relativePath);
45
+ try {
46
+ await fs.access(abs);
47
+ throw new Error(`file_exists: ${rel}`);
48
+ }
49
+ catch (error) {
50
+ if (error instanceof PathEscapeError)
51
+ throw error;
52
+ if (error instanceof Error && error.message.startsWith("file_exists:"))
53
+ throw error;
54
+ // ENOENT — ok to create
55
+ }
56
+ await fs.mkdir(path.dirname(abs), { recursive: true });
57
+ await atomicWriteTextFile(abs, content);
58
+ return {
59
+ path: rel,
60
+ action: "create",
61
+ beforeBytes: 0,
62
+ afterBytes: Buffer.byteLength(content, "utf8"),
63
+ diff: buildUnifiedDiff(rel, "", content),
64
+ };
65
+ }
66
+ /**
67
+ * Path-safe edit. Prefer exact oldContent match or line-range replacement over blind overwrite.
68
+ */
69
+ export async function editFileSafe(root, relativePath, options) {
70
+ const abs = resolveSafeRepoPath(root, relativePath);
71
+ const rel = normalizeRel(relativePath);
72
+ const before = await fs.readFile(abs, "utf8");
73
+ let after;
74
+ if (typeof options.startLine === "number" &&
75
+ typeof options.endLine === "number" &&
76
+ typeof options.replacement === "string") {
77
+ const lines = before.split(/\r?\n/);
78
+ const start = Math.max(1, options.startLine);
79
+ const end = Math.max(start, options.endLine);
80
+ if (start > lines.length) {
81
+ throw new Error(`invalid_range: startLine ${start} beyond file length ${lines.length}`);
82
+ }
83
+ const head = lines.slice(0, start - 1);
84
+ const tail = lines.slice(end);
85
+ const mid = options.replacement.split(/\r?\n/);
86
+ after = [...head, ...mid, ...tail].join("\n");
87
+ }
88
+ else if (typeof options.oldContent === "string" && typeof options.content === "string") {
89
+ if (!before.includes(options.oldContent)) {
90
+ throw new Error("old_content_mismatch: exact oldContent not found in file");
91
+ }
92
+ after = before.replace(options.oldContent, options.content);
93
+ }
94
+ else if (typeof options.content === "string") {
95
+ after = options.content;
96
+ }
97
+ else {
98
+ throw new Error("invalid_argument: provide content, or oldContent+content, or line range");
99
+ }
100
+ await atomicWriteTextFile(abs, after);
101
+ return {
102
+ path: rel,
103
+ action: "edit",
104
+ beforeBytes: Buffer.byteLength(before, "utf8"),
105
+ afterBytes: Buffer.byteLength(after, "utf8"),
106
+ diff: buildUnifiedDiff(rel, before, after),
107
+ };
108
+ }
109
+ export async function deleteFileSafe(root, relativePath) {
110
+ const abs = resolveSafeRepoPath(root, relativePath);
111
+ const rel = normalizeRel(relativePath);
112
+ const before = await fs.readFile(abs, "utf8");
113
+ await fs.unlink(abs);
114
+ return {
115
+ path: rel,
116
+ action: "delete",
117
+ beforeBytes: Buffer.byteLength(before, "utf8"),
118
+ afterBytes: 0,
119
+ diff: buildUnifiedDiff(rel, before, ""),
120
+ };
121
+ }
@@ -0,0 +1,29 @@
1
+ export interface VerificationCheck {
2
+ id: string;
3
+ label: string;
4
+ status: "passed" | "failed" | "skipped" | "not-run";
5
+ detail: string;
6
+ }
7
+ export interface AgentVerificationReport {
8
+ root: string;
9
+ changeId: string | null;
10
+ proofId: string | null;
11
+ filesChanged: string[];
12
+ checks: VerificationCheck[];
13
+ verified: string[];
14
+ notVerified: string[];
15
+ correctnessStatus: "ENGINEERING_CORRECTNESS_NOT_CLAIMED";
16
+ summaryText: string;
17
+ }
18
+ /**
19
+ * Post-change verification for the coding agent (M5).
20
+ * Reuses analyzeChange / verifyChange / proof / architecture — does not invent results.
21
+ */
22
+ export declare function verifyAgentWork(options: {
23
+ root: string;
24
+ filesChanged?: string[];
25
+ /** When true, attempt controlled test run (requires approval flags on tool exec) */
26
+ runTests?: boolean;
27
+ approvedByHuman?: boolean;
28
+ }): Promise<AgentVerificationReport>;
29
+ export declare function formatVerificationReport(report: AgentVerificationReport): string;