patchwarden 1.1.0 → 1.5.1

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 (258) hide show
  1. package/README.en.md +83 -8
  2. package/README.md +78 -10
  3. package/dist/assessments/agentAssessor.d.ts +1 -1
  4. package/dist/assessments/agentAssessor.js +3 -3
  5. package/dist/assessments/assessmentStore.d.ts +1 -1
  6. package/dist/assessments/assessmentStore.js +2 -2
  7. package/dist/assessments/confirmCli.js +5 -4
  8. package/dist/config.d.ts +7 -0
  9. package/dist/config.js +34 -0
  10. package/dist/control/middleware/auth.d.ts +10 -0
  11. package/dist/control/middleware/auth.js +8 -0
  12. package/dist/control/middleware/static.d.ts +3 -0
  13. package/dist/control/middleware/static.js +65 -0
  14. package/dist/control/routes/audit.d.ts +15 -0
  15. package/dist/control/routes/audit.js +277 -0
  16. package/dist/control/routes/evidence.d.ts +4 -0
  17. package/dist/control/routes/evidence.js +96 -0
  18. package/dist/control/routes/lineage.d.ts +3 -0
  19. package/dist/control/routes/lineage.js +71 -0
  20. package/dist/control/routes/policy.d.ts +3 -0
  21. package/dist/control/routes/policy.js +81 -0
  22. package/dist/control/routes/process.d.ts +5 -0
  23. package/dist/control/routes/process.js +200 -0
  24. package/dist/control/routes/sessions.d.ts +22 -0
  25. package/dist/control/routes/sessions.js +224 -0
  26. package/dist/control/routes/status.d.ts +6 -0
  27. package/dist/control/routes/status.js +250 -0
  28. package/dist/control/routes/taskActions.d.ts +21 -0
  29. package/dist/control/routes/taskActions.js +233 -0
  30. package/dist/control/routes/tasks.d.ts +20 -0
  31. package/dist/control/routes/tasks.js +310 -0
  32. package/dist/control/routes/workspace.d.ts +15 -0
  33. package/dist/control/routes/workspace.js +193 -0
  34. package/dist/control/runtime.d.ts +91 -0
  35. package/dist/control/runtime.js +392 -0
  36. package/dist/control/server.d.ts +13 -0
  37. package/dist/control/server.js +479 -0
  38. package/dist/control/shared.d.ts +35 -0
  39. package/dist/control/shared.js +288 -0
  40. package/dist/controlCenter.d.ts +6 -0
  41. package/dist/controlCenter.js +7 -2001
  42. package/dist/direct/directGuards.js +30 -8
  43. package/dist/direct/directSessionStore.d.ts +2 -0
  44. package/dist/direct/directVerification.js +7 -0
  45. package/dist/doctor.d.ts +18 -1
  46. package/dist/doctor.js +579 -348
  47. package/dist/goal/goalReport.d.ts +54 -0
  48. package/dist/goal/goalReport.js +204 -0
  49. package/dist/goal/goalStatus.d.ts +6 -0
  50. package/dist/goal/specKitImport.d.ts +63 -0
  51. package/dist/goal/specKitImport.js +220 -0
  52. package/dist/goal/subgoalSync.js +2 -1
  53. package/dist/httpServer.js +15 -12
  54. package/dist/index.js +7 -4
  55. package/dist/logging.d.ts +7 -1
  56. package/dist/logging.js +8 -0
  57. package/dist/policy/projectPolicy.d.ts +55 -0
  58. package/dist/policy/projectPolicy.js +286 -0
  59. package/dist/runner/changeCapture.d.ts +3 -3
  60. package/dist/runner/changeCapture.js +63 -39
  61. package/dist/runner/cli.js +7 -6
  62. package/dist/runner/postTaskCleanup.js +26 -2
  63. package/dist/runner/runTask.js +245 -221
  64. package/dist/runner/simpleProcess.js +4 -4
  65. package/dist/runner/watch.js +17 -14
  66. package/dist/security/contentRedaction.d.ts +6 -0
  67. package/dist/security/contentRedaction.js +22 -0
  68. package/dist/smoke-test.js +261 -255
  69. package/dist/test/unit/apply-patch.test.d.ts +1 -0
  70. package/dist/test/unit/apply-patch.test.js +225 -0
  71. package/dist/test/unit/create-task.test.d.ts +1 -0
  72. package/dist/test/unit/create-task.test.js +197 -0
  73. package/dist/test/unit/direct-guards.test.js +124 -9
  74. package/dist/test/unit/evidence-pack.test.d.ts +1 -0
  75. package/dist/test/unit/evidence-pack.test.js +224 -0
  76. package/dist/test/unit/get-task-status.test.d.ts +1 -0
  77. package/dist/test/unit/get-task-status.test.js +174 -0
  78. package/dist/test/unit/get-task-summary.test.d.ts +1 -0
  79. package/dist/test/unit/get-task-summary.test.js +146 -0
  80. package/dist/test/unit/goal-report.test.d.ts +1 -0
  81. package/dist/test/unit/goal-report.test.js +159 -0
  82. package/dist/test/unit/goal-subgoal-task.test.js +6 -6
  83. package/dist/test/unit/goal-tools-registry.test.js +6 -4
  84. package/dist/test/unit/path-guard.test.js +24 -0
  85. package/dist/test/unit/project-policy-release-mode.test.d.ts +1 -0
  86. package/dist/test/unit/project-policy-release-mode.test.js +125 -0
  87. package/dist/test/unit/run-task-loop.test.d.ts +1 -0
  88. package/dist/test/unit/run-task-loop.test.js +380 -0
  89. package/dist/test/unit/schema-drift-check.test.js +10 -9
  90. package/dist/test/unit/spec-kit-import.test.d.ts +1 -0
  91. package/dist/test/unit/spec-kit-import.test.js +341 -0
  92. package/dist/test/unit/wait-for-task.test.d.ts +1 -0
  93. package/dist/test/unit/wait-for-task.test.js +144 -0
  94. package/dist/tools/auditTask.d.ts +8 -63
  95. package/dist/tools/auditTask.js +12 -8
  96. package/dist/tools/createDirectSession.d.ts +1 -1
  97. package/dist/tools/createDirectSession.js +2 -2
  98. package/dist/tools/createTask.d.ts +2 -2
  99. package/dist/tools/createTask.js +4 -4
  100. package/dist/tools/dispatch/coreDispatch.d.ts +9 -0
  101. package/dist/tools/dispatch/coreDispatch.js +282 -0
  102. package/dist/tools/dispatch/diagnosticDispatch.d.ts +14 -0
  103. package/dist/tools/dispatch/diagnosticDispatch.js +78 -0
  104. package/dist/tools/dispatch/directDispatch.d.ts +8 -0
  105. package/dist/tools/dispatch/directDispatch.js +115 -0
  106. package/dist/tools/dispatch/goalDispatch.d.ts +8 -0
  107. package/dist/tools/dispatch/goalDispatch.js +91 -0
  108. package/dist/tools/dispatch/releaseDispatch.d.ts +8 -0
  109. package/dist/tools/dispatch/releaseDispatch.js +45 -0
  110. package/dist/tools/dispatch/types.d.ts +23 -0
  111. package/dist/tools/dispatch/types.js +15 -0
  112. package/dist/tools/evidencePack.d.ts +45 -0
  113. package/dist/tools/evidencePack.js +375 -0
  114. package/dist/tools/finalizeDirectSession.d.ts +1 -1
  115. package/dist/tools/finalizeDirectSession.js +3 -3
  116. package/dist/tools/goalSubgoalTask.d.ts +1 -1
  117. package/dist/tools/goalSubgoalTask.js +2 -2
  118. package/dist/tools/healthCheck.js +3 -3
  119. package/dist/tools/recommendAgentForTask.d.ts +19 -0
  120. package/dist/tools/recommendAgentForTask.js +56 -0
  121. package/dist/tools/registry.d.ts +3 -3
  122. package/dist/tools/registry.js +333 -402
  123. package/dist/tools/releaseMode.d.ts +50 -0
  124. package/dist/tools/releaseMode.js +370 -0
  125. package/dist/tools/retryTask.d.ts +2 -2
  126. package/dist/tools/retryTask.js +2 -2
  127. package/dist/tools/runDirectVerificationBundle.d.ts +26 -0
  128. package/dist/tools/runDirectVerificationBundle.js +64 -0
  129. package/dist/tools/runTaskLoop.d.ts +57 -0
  130. package/dist/tools/runTaskLoop.js +417 -0
  131. package/dist/tools/runVerification.d.ts +4 -0
  132. package/dist/tools/runVerification.js +4 -0
  133. package/dist/tools/safeViews.d.ts +8 -2
  134. package/dist/tools/safeViews.js +4 -2
  135. package/dist/tools/taskLineage.d.ts +91 -0
  136. package/dist/tools/taskLineage.js +175 -0
  137. package/dist/tools/toolCatalog.d.ts +2 -2
  138. package/dist/tools/toolCatalog.js +6 -0
  139. package/dist/tools/toolRegistry.js +132 -0
  140. package/dist/version.d.ts +2 -2
  141. package/dist/version.js +2 -2
  142. package/docs/CODE_WIKI.md +983 -0
  143. package/docs/agentseal-integration.md +150 -0
  144. package/docs/architecture.md +63 -0
  145. package/docs/assets/patchwarden-oss-demo.gif +0 -0
  146. package/docs/chatgpt-usage.md +31 -0
  147. package/docs/control-center/README.md +9 -0
  148. package/docs/dashboard-overview.md +86 -0
  149. package/docs/demo.md +8 -0
  150. package/docs/direct-session-workflow.md +98 -0
  151. package/docs/evidence-pack-schema.md +215 -0
  152. package/docs/execution-plan-2026-07-09.md +315 -0
  153. package/docs/lineage-evidence-pack-workflow.md +127 -0
  154. package/docs/mcp-inspector-testing.md +200 -0
  155. package/docs/open-source-application.md +162 -0
  156. package/docs/opencode-worker.md +151 -0
  157. package/docs/openhands-worker.md +181 -0
  158. package/docs/release-evidence.md +72 -0
  159. package/docs/roadmap-execution-and-acceptance.md +365 -0
  160. package/docs/spec-kit-integration.md +131 -0
  161. package/docs/task-safe-review-workflow.md +98 -0
  162. package/docs/threat-model.md +79 -0
  163. package/docs/user-feedback.md +40 -0
  164. package/docs/why-patchwarden.md +110 -0
  165. package/package.json +3 -3
  166. package/scripts/checks/control-center-smoke.js +443 -0
  167. package/scripts/checks/control-smoke.js +2 -2
  168. package/scripts/checks/lifecycle-smoke.js +29 -23
  169. package/scripts/checks/mcp-manifest-check.js +12 -0
  170. package/scripts/checks/mcp-smoke.js +33 -7
  171. package/scripts/checks/watcher-supervisor-smoke.js +1 -1
  172. package/scripts/generate-demo-gif.py +320 -0
  173. package/src/assessments/agentAssessor.ts +3 -3
  174. package/src/assessments/assessmentStore.ts +2 -2
  175. package/src/assessments/confirmCli.ts +5 -4
  176. package/src/config.ts +37 -0
  177. package/src/control/middleware/auth.ts +17 -0
  178. package/src/control/middleware/static.ts +71 -0
  179. package/src/control/routes/audit.ts +321 -0
  180. package/src/control/routes/evidence.ts +107 -0
  181. package/src/control/routes/lineage.ts +92 -0
  182. package/src/control/routes/policy.ts +81 -0
  183. package/src/control/routes/process.ts +204 -0
  184. package/src/control/routes/sessions.ts +251 -0
  185. package/src/control/routes/status.ts +325 -0
  186. package/src/control/routes/taskActions.ts +248 -0
  187. package/src/control/routes/tasks.ts +323 -0
  188. package/src/control/routes/workspace.ts +203 -0
  189. package/src/control/runtime.ts +472 -0
  190. package/src/control/server.ts +471 -0
  191. package/src/control/shared.ts +294 -0
  192. package/src/controlCenter.ts +7 -2150
  193. package/src/direct/directGuards.ts +28 -7
  194. package/src/direct/directSessionStore.ts +2 -0
  195. package/src/direct/directVerification.ts +7 -0
  196. package/src/doctor.ts +741 -481
  197. package/src/goal/goalReport.ts +271 -0
  198. package/src/goal/goalStatus.ts +6 -0
  199. package/src/goal/specKitImport.ts +355 -0
  200. package/src/goal/subgoalSync.ts +4 -2
  201. package/src/httpServer.ts +17 -14
  202. package/src/index.ts +7 -4
  203. package/src/logging.ts +10 -1
  204. package/src/policy/projectPolicy.ts +344 -0
  205. package/src/runner/changeCapture.ts +70 -42
  206. package/src/runner/cli.ts +7 -6
  207. package/src/runner/postTaskCleanup.ts +26 -2
  208. package/src/runner/runTask.ts +325 -223
  209. package/src/runner/simpleProcess.ts +4 -4
  210. package/src/runner/watch.ts +17 -14
  211. package/src/security/contentRedaction.ts +29 -0
  212. package/src/smoke-test.ts +254 -252
  213. package/src/test/unit/apply-patch.test.ts +293 -0
  214. package/src/test/unit/create-task.test.ts +255 -0
  215. package/src/test/unit/direct-guards.test.ts +178 -8
  216. package/src/test/unit/evidence-pack.test.ts +251 -0
  217. package/src/test/unit/get-task-status.test.ts +203 -0
  218. package/src/test/unit/get-task-summary.test.ts +173 -0
  219. package/src/test/unit/goal-report.test.ts +189 -0
  220. package/src/test/unit/goal-subgoal-task.test.ts +6 -6
  221. package/src/test/unit/goal-tools-registry.test.ts +7 -5
  222. package/src/test/unit/path-guard.test.ts +30 -0
  223. package/src/test/unit/project-policy-release-mode.test.ts +156 -0
  224. package/src/test/unit/run-task-loop.test.ts +425 -0
  225. package/src/test/unit/schema-drift-check.test.ts +11 -9
  226. package/src/test/unit/spec-kit-import.test.ts +429 -0
  227. package/src/test/unit/wait-for-task.test.ts +176 -0
  228. package/src/tools/auditTask.ts +99 -59
  229. package/src/tools/createDirectSession.ts +3 -3
  230. package/src/tools/createTask.ts +7 -7
  231. package/src/tools/dispatch/coreDispatch.ts +374 -0
  232. package/src/tools/dispatch/diagnosticDispatch.ts +101 -0
  233. package/src/tools/dispatch/directDispatch.ts +167 -0
  234. package/src/tools/dispatch/goalDispatch.ts +127 -0
  235. package/src/tools/dispatch/releaseDispatch.ts +65 -0
  236. package/src/tools/dispatch/types.ts +24 -0
  237. package/src/tools/evidencePack.ts +490 -0
  238. package/src/tools/finalizeDirectSession.ts +4 -4
  239. package/src/tools/goalSubgoalTask.ts +2 -2
  240. package/src/tools/healthCheck.ts +3 -3
  241. package/src/tools/listWorkspace.ts +71 -71
  242. package/src/tools/recommendAgentForTask.ts +79 -0
  243. package/src/tools/registry.ts +352 -509
  244. package/src/tools/releaseMode.ts +450 -0
  245. package/src/tools/retryTask.ts +2 -2
  246. package/src/tools/runDirectVerificationBundle.ts +98 -0
  247. package/src/tools/runTaskLoop.ts +526 -0
  248. package/src/tools/runVerification.ts +8 -0
  249. package/src/tools/safeViews.ts +4 -2
  250. package/src/tools/taskLineage.ts +300 -0
  251. package/src/tools/toolCatalog.ts +6 -0
  252. package/src/tools/toolRegistry.ts +132 -0
  253. package/src/version.ts +2 -2
  254. package/ui/pages/audit.html +192 -3
  255. package/ui/pages/dashboard.html +1070 -14
  256. package/ui/pages/direct-sessions.html +505 -53
  257. package/ui/pages/task-detail.html +456 -438
  258. package/ui/pages/tasks.html +598 -61
@@ -1,5 +1,3 @@
1
- // @ts-nocheck
2
-
3
1
  interface AuditCheck {
4
2
  name: string;
5
3
  result: "pass" | "warn" | "fail";
@@ -40,6 +38,43 @@ import { guardReadPath, guardWorkspacePath } from "../security/pathGuard.js";
40
38
  import { guardSensitivePath, isSensitivePath } from "../security/sensitiveGuard.js";
41
39
  import { evaluateAcceptance } from "../goal/acceptanceEngine.js";
42
40
  import { renderAcceptanceMarkdown } from "../goal/acceptanceTemplate.js";
41
+ import type { ChangedFile } from "../runner/changeCapture.js";
42
+
43
+ interface ExtractedCommand {
44
+ type: "npm-run" | "npm-bare" | "node" | "npx" | "python";
45
+ name: string;
46
+ }
47
+
48
+ interface TaskStatusData {
49
+ status?: string;
50
+ resolved_repo_path?: string;
51
+ repo_path?: string;
52
+ new_out_of_scope_changes?: Array<{ path: string; change: string }>;
53
+ out_of_scope_changes?: Array<{ path: string; change: string }>;
54
+ test_command?: string;
55
+ verify_commands?: string[];
56
+ forbidden?: string[];
57
+ done_evidence?: string[];
58
+ scope?: string[];
59
+ verification?: string[];
60
+ goal?: string;
61
+ artifact_status?: string;
62
+ acceptance_status?: string;
63
+ updated_at?: string;
64
+ }
65
+
66
+ interface ChangedFilesEvidence {
67
+ changed_files?: ChangedFile[];
68
+ artifact_hygiene?: {
69
+ counts?: {
70
+ tracked_build_artifacts?: number | string;
71
+ ignored_untracked_artifacts?: number | string;
72
+ runtime_generated_files?: number | string;
73
+ suspicious_changes?: number | string;
74
+ };
75
+ };
76
+ }
77
+
43
78
  // Release claim patterns — anything that claims remote publish/release/deploy
44
79
  const RELEASE_PATTERNS = [
45
80
  /npm\s+package\s+version\s+published/i,
@@ -51,8 +86,8 @@ const RELEASE_PATTERNS = [
51
86
  /npm\s+publish\s+completed/i,
52
87
  /deploy(ed|ment)?\s+(to|on)\s+(npm|registry|github)/i,
53
88
  ];
54
- function scanForReleaseClaims(text) {
55
- const found = [];
89
+ function scanForReleaseClaims(text: string): string[] {
90
+ const found: string[] = [];
56
91
  for (const pattern of RELEASE_PATTERNS) {
57
92
  const match = text.match(pattern);
58
93
  if (match)
@@ -60,8 +95,8 @@ function scanForReleaseClaims(text) {
60
95
  }
61
96
  return found;
62
97
  }
63
- function findMdFiles(dir, maxDepth = 3) {
64
- const results = [];
98
+ function findMdFiles(dir: string, maxDepth = 3): string[] {
99
+ const results: string[] = [];
65
100
  if (!existsSync(dir) || maxDepth <= 0)
66
101
  return results;
67
102
  try {
@@ -86,7 +121,7 @@ function findMdFiles(dir, maxDepth = 3) {
86
121
  * `*` matches a single path segment (no `/`).
87
122
  * Path separators in the pattern are expected to be `/`.
88
123
  */
89
- function globToRegExp(pattern) {
124
+ function globToRegExp(pattern: string): RegExp {
90
125
  // Normalize backslashes to forward slashes for matching consistency.
91
126
  const normalized = pattern.replace(/\\/g, "/");
92
127
  let re = "^";
@@ -127,7 +162,7 @@ function globToRegExp(pattern) {
127
162
  * Returns pass when changed files do not match any forbidden pattern.
128
163
  * Returns fail when any changed file path (or old_path) matches a forbidden glob.
129
164
  */
130
- export function checkForbiddenScope(changedFiles, forbidden) {
165
+ export function checkForbiddenScope(changedFiles: ChangedFile[], forbidden: string[] | null): AuditCheck | null {
131
166
  if (!forbidden || forbidden.length === 0)
132
167
  return null;
133
168
  if (changedFiles.length === 0) {
@@ -161,7 +196,7 @@ export function checkForbiddenScope(changedFiles, forbidden) {
161
196
  * Returns pass when all listed files exist in the task directory.
162
197
  * Returns warn when any listed file is missing.
163
198
  */
164
- export function checkDoneEvidenceMissing(taskDir, doneEvidence) {
199
+ export function checkDoneEvidenceMissing(taskDir: string, doneEvidence: string[] | null): AuditCheck | null {
165
200
  if (!doneEvidence || doneEvidence.length === 0)
166
201
  return null;
167
202
  const missing = [];
@@ -189,7 +224,7 @@ const CODE_EXTENSIONS = new Set([
189
224
  * Returns pass when README.md or CHANGELOG.md (basename, case-insensitive) is also changed.
190
225
  * Returns warn when code changed but neither documentation file was updated.
191
226
  */
192
- export function checkReadmeChangelogSync(changedFiles) {
227
+ export function checkReadmeChangelogSync(changedFiles: ChangedFile[]): AuditCheck | null {
193
228
  const paths = changedFiles.map((f) => f.path);
194
229
  const hasCodeChange = paths.some((p) => {
195
230
  const lower = p.toLowerCase();
@@ -223,14 +258,14 @@ export function checkReadmeChangelogSync(changedFiles) {
223
258
  * Returns pass when package.json parses as JSON and has non-empty name and version strings.
224
259
  * Returns warn otherwise.
225
260
  */
226
- export function checkPackageManifestConsistency(changedFiles, repoPathSafe) {
261
+ export function checkPackageManifestConsistency(changedFiles: ChangedFile[], repoPathSafe: string): AuditCheck | null {
227
262
  const includesPackageJson = changedFiles.some((f) => basename(f.path).toLowerCase() === "package.json");
228
263
  if (!includesPackageJson)
229
264
  return null;
230
265
  try {
231
266
  const pkgPath = join(repoPathSafe, "package.json");
232
267
  const raw = readFileSync(pkgPath, "utf-8");
233
- const parsed = JSON.parse(raw);
268
+ const parsed = JSON.parse(raw) as { name?: unknown; version?: unknown };
234
269
  const hasName = typeof parsed.name === "string" && parsed.name.length > 0;
235
270
  const hasVersion = typeof parsed.version === "string" && parsed.version.length > 0;
236
271
  if (hasName && hasVersion) {
@@ -256,7 +291,7 @@ export function checkPackageManifestConsistency(changedFiles, repoPathSafe) {
256
291
  * Returns pass when no changed file path (or old_path) is sensitive.
257
292
  * Returns fail when any changed file path is flagged by isSensitivePath.
258
293
  */
259
- export function checkSensitivePathAccess(changedFiles) {
294
+ export function checkSensitivePathAccess(changedFiles: ChangedFile[]): AuditCheck | null {
260
295
  if (changedFiles.length === 0)
261
296
  return null;
262
297
  const hits = [];
@@ -287,8 +322,8 @@ const HIGH_RISK_COMMAND_PATTERNS = [
287
322
  /\bRemove-Item\b[\s\S]{0,80}\b-Recurse\b/i,
288
323
  /\brm\s+-rf\b/i,
289
324
  ];
290
- function extractCommands(text) {
291
- const found = [];
325
+ function extractCommands(text: string): ExtractedCommand[] {
326
+ const found: ExtractedCommand[] = [];
292
327
  const runRe = /npm(?:\.cmd)?\s+run\s+([a-zA-Z0-9:_-]+)/gi;
293
328
  let m;
294
329
  while ((m = runRe.exec(text)) !== null) found.push({ type: "npm-run", name: m[1] });
@@ -302,31 +337,34 @@ function extractCommands(text) {
302
337
  while ((m = pythonRe.exec(text)) !== null) found.push({ type: "python", name: normalizeCommandName(m[1]) });
303
338
  return found;
304
339
  }
305
- function normalizeCommandName(value) {
340
+ function normalizeCommandName(value: string): string {
306
341
  return value.replace(/\\/g, "/").replace(/^\.\//, "");
307
342
  }
308
- function commandKey(command) {
343
+ function commandKey(command: ExtractedCommand): string {
309
344
  return command.name;
310
345
  }
311
- function readPackageScripts(repoPath) {
346
+ function readPackageScripts(repoPath: string | null): Record<string, string> {
312
347
  if (!repoPath) return {};
313
348
  const pkgJsonPath = join(repoPath, "package.json");
314
349
  if (!existsSync(pkgJsonPath)) return {};
315
350
  try {
316
351
  guardSensitivePath(pkgJsonPath);
317
- const parsed = JSON.parse(readFileSync(pkgJsonPath, "utf-8"));
318
- if (!parsed.scripts || typeof parsed.scripts !== "object" || Array.isArray(parsed.scripts)) return {};
319
- return Object.fromEntries(Object.entries(parsed.scripts).filter(([, value]) => typeof value === "string"));
352
+ const parsed = JSON.parse(readFileSync(pkgJsonPath, "utf-8")) as { scripts?: Record<string, unknown> };
353
+ const scripts = parsed.scripts;
354
+ if (!scripts || typeof scripts !== "object" || Array.isArray(scripts)) return {};
355
+ return Object.fromEntries(
356
+ Object.entries(scripts).filter(([, value]) => typeof value === "string")
357
+ ) as Record<string, string>;
320
358
  }
321
359
  catch {
322
360
  return {};
323
361
  }
324
362
  }
325
- function buildCommandWhitelist(verifyCommands, testCommand, repoPath) {
363
+ function buildCommandWhitelist(verifyCommands: string[], testCommand: string | null, repoPath: string | null): { allowed: Set<string>; transitive: Set<string> } {
326
364
  const scripts = readPackageScripts(repoPath);
327
- const allowed = new Set();
328
- const transitive = new Set();
329
- const queue = [];
365
+ const allowed = new Set<string>();
366
+ const transitive = new Set<string>();
367
+ const queue: Array<{ command: ExtractedCommand; depth: number }> = [];
330
368
  const sources = [...verifyCommands];
331
369
  if (testCommand) sources.push(testCommand);
332
370
  for (const src of sources) {
@@ -336,9 +374,9 @@ function buildCommandWhitelist(verifyCommands, testCommand, repoPath) {
336
374
  }
337
375
  }
338
376
  let inspectedFiles = 0;
339
- const seen = new Set();
377
+ const seen = new Set<string>();
340
378
  while (queue.length > 0) {
341
- const { command, depth } = queue.shift();
379
+ const { command, depth } = queue.shift()!;
342
380
  if (depth > 4) continue;
343
381
  const key = `${command.type}:${command.name}`;
344
382
  if (seen.has(key)) continue;
@@ -365,7 +403,7 @@ function buildCommandWhitelist(verifyCommands, testCommand, repoPath) {
365
403
  }
366
404
  return { allowed, transitive };
367
405
  }
368
- function readLocalScript(repoPath, scriptPath) {
406
+ function readLocalScript(repoPath: string, scriptPath: string): string | null {
369
407
  const resolvedRepo = resolve(repoPath);
370
408
  const resolvedScript = resolve(resolvedRepo, scriptPath);
371
409
  const rel = relative(resolvedRepo, resolvedScript);
@@ -380,17 +418,17 @@ function readLocalScript(repoPath, scriptPath) {
380
418
  return null;
381
419
  }
382
420
  }
383
- function findHighRiskCommandEvidence(text) {
384
- const found = [];
421
+ function findHighRiskCommandEvidence(text: string): string[] {
422
+ const found: string[] = [];
385
423
  for (const pattern of HIGH_RISK_COMMAND_PATTERNS) {
386
424
  const match = text.match(pattern);
387
425
  if (match) found.push(match[0]);
388
426
  }
389
427
  return [...new Set(found)];
390
428
  }
391
- export function checkUnrecordedCommandExecution(testLogContent: string | null, resultMdContent: string | null, verifyCommands: string[], testCommand: string | null, repoPath: string | null = null) {
392
- const hasLog = testLogContent && testLogContent.length > 0;
393
- const hasResult = resultMdContent && resultMdContent.length > 0;
429
+ export function checkUnrecordedCommandExecution(testLogContent: string | null, resultMdContent: string | null, verifyCommands: string[], testCommand: string | null, repoPath: string | null = null): AuditCheck | null {
430
+ const hasLog = !!testLogContent && testLogContent.length > 0;
431
+ const hasResult = !!resultMdContent && resultMdContent.length > 0;
394
432
  if (!hasLog && !hasResult) return null;
395
433
  const combined = `${testLogContent || ""}\n${resultMdContent || ""}`;
396
434
  const highRisk = findHighRiskCommandEvidence(combined);
@@ -398,10 +436,10 @@ export function checkUnrecordedCommandExecution(testLogContent: string | null, r
398
436
  return { name: "unrecorded_command_execution", result: "fail", detail: `High-risk command evidence: ${highRisk.join(", ")}` };
399
437
  }
400
438
  const whitelist = buildCommandWhitelist(verifyCommands, testCommand, repoPath);
401
- const discovered = [];
402
- if (hasLog) discovered.push(...extractCommands(testLogContent));
403
- if (hasResult) discovered.push(...extractCommands(resultMdContent));
404
- const unrecorded = new Set();
439
+ const discovered: ExtractedCommand[] = [];
440
+ if (hasLog && testLogContent) discovered.push(...extractCommands(testLogContent));
441
+ if (hasResult && resultMdContent) discovered.push(...extractCommands(resultMdContent));
442
+ const unrecorded = new Set<string>();
405
443
  for (const cmd of discovered) {
406
444
  if (!whitelist.allowed.has(commandKey(cmd))) unrecorded.add(commandKey(cmd));
407
445
  }
@@ -415,7 +453,7 @@ export function checkUnrecordedCommandExecution(testLogContent: string | null, r
415
453
  detail: transitive.length > 0 ? `All commands are whitelisted; transitive_verified_command: ${transitive.join(", ")}` : "All commands in whitelist.",
416
454
  };
417
455
  }
418
- export function auditTask(taskId) {
456
+ export function auditTask(taskId: string): AuditTaskOutput {
419
457
  const config = getConfig();
420
458
  const tasksDir = getTasksDir(config);
421
459
  const taskDir = join(tasksDir, taskId);
@@ -424,17 +462,17 @@ export function auditTask(taskId) {
424
462
  if (!existsSync(statusFile)) {
425
463
  throw new Error(`Task not found: "${taskId}"`);
426
464
  }
427
- const statusData = JSON.parse(readFileSync(statusFile, "utf-8"));
428
- const checks = [];
429
- const risks = [];
430
- const actions = [];
431
- const possibleFalsePositives = [];
432
- const manualVerificationItems = [];
433
- const addManualVerification = (item) => {
465
+ const statusData = JSON.parse(readFileSync(statusFile, "utf-8")) as TaskStatusData;
466
+ const checks: AuditCheck[] = [];
467
+ const risks: AuditRisk[] = [];
468
+ const actions: string[] = [];
469
+ const possibleFalsePositives: Array<{ check: string; reason: string }> = [];
470
+ const manualVerificationItems: string[] = [];
471
+ const addManualVerification = (item: string) => {
434
472
  if (!manualVerificationItems.includes(item))
435
473
  manualVerificationItems.push(item);
436
474
  };
437
- const addPossibleFalsePositive = (check, reason) => {
475
+ const addPossibleFalsePositive = (check: string, reason: string) => {
438
476
  if (!possibleFalsePositives.some((item) => item.check === check && item.reason === reason)) {
439
477
  possibleFalsePositives.push({ check, reason });
440
478
  }
@@ -478,7 +516,7 @@ export function auditTask(taskId) {
478
516
  }
479
517
  if (existsSync(verifyJsonFile)) {
480
518
  try {
481
- const verify = JSON.parse(readFileSync(verifyJsonFile, "utf-8"));
519
+ const verify = JSON.parse(readFileSync(verifyJsonFile, "utf-8")) as { status?: string };
482
520
  checks.push({
483
521
  name: "verify_status",
484
522
  result: verify.status === "passed" ? "pass" : verify.status === "failed" ? "fail" : "warn",
@@ -504,11 +542,11 @@ export function auditTask(taskId) {
504
542
  : "No new out-of-scope changes recorded.",
505
543
  });
506
544
  // Extract changed_files from change evidence (used by several v0.7.2 checks).
507
- let changedFiles = [];
545
+ let changedFiles: ChangedFile[] = [];
508
546
  const changedFilesFile = join(taskDir, "changed-files.json");
509
547
  if (existsSync(changedFilesFile)) {
510
548
  try {
511
- const changeEvidence = JSON.parse(readFileSync(changedFilesFile, "utf-8"));
549
+ const changeEvidence = JSON.parse(readFileSync(changedFilesFile, "utf-8")) as ChangedFilesEvidence;
512
550
  if (Array.isArray(changeEvidence.changed_files)) {
513
551
  changedFiles = changeEvidence.changed_files;
514
552
  }
@@ -596,11 +634,11 @@ export function auditTask(taskId) {
596
634
  risks.push({ severity: "high", description: "repo_path inconsistent with workspace." });
597
635
  // ── 6. package.json scripts ──
598
636
  const pkgJsonPath = join(repoPathSafe, "package.json");
599
- let pkgScripts = [];
637
+ let pkgScripts: string[] = [];
600
638
  if (existsSync(pkgJsonPath)) {
601
639
  try {
602
640
  guardSensitivePath(pkgJsonPath);
603
- const pkgData = JSON.parse(readFileSync(pkgJsonPath, "utf-8"));
641
+ const pkgData = JSON.parse(readFileSync(pkgJsonPath, "utf-8")) as { scripts?: Record<string, unknown> };
604
642
  pkgScripts = Object.keys(pkgData.scripts || {});
605
643
  checks.push({
606
644
  name: "package_json_scripts",
@@ -625,10 +663,10 @@ export function auditTask(taskId) {
625
663
  docsToScan.push(...findMdFiles(docsDir));
626
664
  }
627
665
  // Collect all npm run references from all docs
628
- const allNpmRunRefs = new Set();
629
- const allReleaseClaims = [];
666
+ const allNpmRunRefs = new Set<string>();
667
+ const allReleaseClaims: string[] = [];
630
668
  for (const docPath of docsToScan) {
631
- let content;
669
+ let content: string;
632
670
  try {
633
671
  if (docPath !== resultFile) {
634
672
  guardReadPath(docPath, config.workspaceRoot);
@@ -693,9 +731,9 @@ export function auditTask(taskId) {
693
731
  addManualVerification("Verify remote npm, GitHub Release, and Git tag claims against authoritative remote services.");
694
732
  }
695
733
  // ── 9. test.log Exit code check ──
696
- let testLogContent = null;
734
+ let testLogContent: string | null = null;
697
735
  if (hasTestLog) {
698
- let logContent;
736
+ let logContent: string;
699
737
  try {
700
738
  logContent = readFileSync(testLogFile, "utf-8");
701
739
  }
@@ -732,7 +770,7 @@ export function auditTask(taskId) {
732
770
  }
733
771
  // ── v0.7.2: New audit checks ──
734
772
  // Read result.md content (guarded) for the unrecorded_command_execution check.
735
- let resultMdContent = null;
773
+ let resultMdContent: string | null = null;
736
774
  if (hasResult) {
737
775
  try {
738
776
  resultMdContent = readFileSync(resultFile, "utf-8");
@@ -818,8 +856,10 @@ export function auditTask(taskId) {
818
856
  if (!existsSync(verifyJsonFile))
819
857
  return null;
820
858
  try {
821
- const v = JSON.parse(readFileSync(verifyJsonFile, "utf-8"));
822
- return v.status === "passed" ? "passed" : v.status === "failed" ? "failed" : "skipped";
859
+ const v = JSON.parse(readFileSync(verifyJsonFile, "utf-8")) as { status?: string };
860
+ if (v.status === "passed") return "passed" as const;
861
+ if (v.status === "failed") return "failed" as const;
862
+ return "skipped" as const;
823
863
  }
824
864
  catch {
825
865
  return null;
@@ -27,9 +27,9 @@ export interface CreateDirectSessionOutput {
27
27
 
28
28
  // ── Tool implementation ────────────────────────────────────────────
29
29
 
30
- export function createDirectSession(
30
+ export async function createDirectSession(
31
31
  input: CreateDirectSessionInput
32
- ): CreateDirectSessionOutput {
32
+ ): Promise<CreateDirectSessionOutput> {
33
33
  const config = getConfig();
34
34
 
35
35
  // ── Validate repo_path ───────────────────────────────────────────
@@ -88,7 +88,7 @@ export function createDirectSession(
88
88
 
89
89
  // ── Capture repo snapshot ────────────────────────────────────────
90
90
 
91
- const snapshot = captureRepoSnapshot(resolvedRepoPath);
91
+ const snapshot = await captureRepoSnapshot(resolvedRepoPath);
92
92
 
93
93
  // ── Create session record ────────────────────────────────────────
94
94
 
@@ -181,9 +181,9 @@ export interface CreateTaskOutput {
181
181
  };
182
182
  }
183
183
 
184
- export function createTask(input: CreateTaskInput & { execution_mode: "assess_only" }): AssessOnlyOutput;
185
- export function createTask(input: CreateTaskInput): CreateTaskOutput;
186
- export function createTask(input: CreateTaskInput): CreateTaskResult {
184
+ export function createTask(input: CreateTaskInput & { execution_mode: "assess_only" }): Promise<AssessOnlyOutput>;
185
+ export function createTask(input: CreateTaskInput): Promise<CreateTaskOutput>;
186
+ export async function createTask(input: CreateTaskInput): Promise<CreateTaskResult> {
187
187
  const config = getConfig();
188
188
  const tasksDir = getTasksDir(config);
189
189
  const plansDir = getPlansDir(config);
@@ -243,7 +243,7 @@ export function createTask(input: CreateTaskInput): CreateTaskResult {
243
243
 
244
244
  // Resolve repo alias if configured
245
245
  let resolvedRepoPath = effectiveInput.repo_path?.trim() || "";
246
- const aliases = (config as any).repoAliases as Record<string, string> | undefined;
246
+ const aliases = config.repoAliases;
247
247
  if (aliases && resolvedRepoPath && aliases[resolvedRepoPath]) {
248
248
  resolvedRepoPath = aliases[resolvedRepoPath];
249
249
  }
@@ -382,7 +382,7 @@ export function createTask(input: CreateTaskInput): CreateTaskResult {
382
382
  planBlockReason = e instanceof PatchWardenError ? e.reason : "plan_content_blocked";
383
383
  }
384
384
 
385
- const snapshot = captureRepoSnapshot(safeRepoPath);
385
+ const snapshot = await captureRepoSnapshot(safeRepoPath);
386
386
  const snapshotTruncated = snapshot.warnings.some((w) => w.includes("snapshot limited"));
387
387
 
388
388
  let riskResult;
@@ -441,7 +441,7 @@ export function createTask(input: CreateTaskInput): CreateTaskResult {
441
441
  preGeneratedAssessmentDir = createAssessmentDir(preGeneratedAssessmentId);
442
442
 
443
443
  const assessorAgentName = config.agentAssessmentAgentName || effectiveInput.agent;
444
- agentAssessmentSummary = runAgentAssessment({
444
+ agentAssessmentSummary = await runAgentAssessment({
445
445
  assessmentId: preGeneratedAssessmentId,
446
446
  assessmentDir: preGeneratedAssessmentDir,
447
447
  agentName: assessorAgentName,
@@ -572,7 +572,7 @@ export function createTask(input: CreateTaskInput): CreateTaskResult {
572
572
 
573
573
  // ── execute mode with assessment_id: validate freshness ──
574
574
  if (assessmentRecord) {
575
- const snapshot = captureRepoSnapshot(safeRepoPath);
575
+ const snapshot = await captureRepoSnapshot(safeRepoPath);
576
576
  const validation = validateAssessmentFreshness(input.assessment_id!, snapshot);
577
577
  if (!validation.valid) {
578
578
  throw new PatchWardenError(