agentplane 0.3.13 → 0.3.14

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 (276) hide show
  1. package/assets/RUNNER.md +1 -1
  2. package/assets/agents/ORCHESTRATOR.json +1 -1
  3. package/assets/agents/SKILL_EXTRACTOR.json +31 -0
  4. package/assets/framework.manifest.json +7 -0
  5. package/assets/policy/incidents.md +5 -3
  6. package/assets/policy/workflow.branch_pr.md +10 -5
  7. package/dist/.build-manifest.json +278 -178
  8. package/dist/cli/output.d.ts +29 -0
  9. package/dist/cli/output.d.ts.map +1 -1
  10. package/dist/cli/output.js +33 -0
  11. package/dist/cli/run-cli/command-catalog/core.d.ts.map +1 -1
  12. package/dist/cli/run-cli/command-catalog/core.js +29 -87
  13. package/dist/cli/run-cli/command-catalog/lifecycle.d.ts.map +1 -1
  14. package/dist/cli/run-cli/command-catalog/lifecycle.js +4 -12
  15. package/dist/cli/run-cli/command-catalog/project.d.ts +1 -1
  16. package/dist/cli/run-cli/command-catalog/project.d.ts.map +1 -1
  17. package/dist/cli/run-cli/command-catalog/project.js +16 -38
  18. package/dist/cli/run-cli/command-catalog/shared.d.ts +9 -6
  19. package/dist/cli/run-cli/command-catalog/shared.d.ts.map +1 -1
  20. package/dist/cli/run-cli/command-catalog/shared.js +23 -6
  21. package/dist/cli/run-cli/command-catalog/task.d.ts.map +1 -1
  22. package/dist/cli/run-cli/command-catalog/task.js +6 -18
  23. package/dist/cli/run-cli/command-catalog.d.ts +1 -1
  24. package/dist/cli/run-cli/command-catalog.d.ts.map +1 -1
  25. package/dist/cli/run-cli/commands/init/recipes.d.ts.map +1 -1
  26. package/dist/cli/run-cli/commands/init/recipes.js +1 -0
  27. package/dist/cli/run-cli.js +1 -1
  28. package/dist/cli/run-cli.test-helpers.d.ts +1 -74
  29. package/dist/cli/run-cli.test-helpers.d.ts.map +1 -1
  30. package/dist/cli/run-cli.test-helpers.js +1 -769
  31. package/dist/commands/branch/cleanup-merged.d.ts.map +1 -1
  32. package/dist/commands/branch/cleanup-merged.js +5 -9
  33. package/dist/commands/branch/work-start.command.d.ts.map +1 -1
  34. package/dist/commands/branch/work-start.command.js +1 -0
  35. package/dist/commands/commit.spec.d.ts.map +1 -1
  36. package/dist/commands/commit.spec.js +2 -0
  37. package/dist/commands/doctor/branch-pr.d.ts +1 -1
  38. package/dist/commands/doctor/branch-pr.d.ts.map +1 -1
  39. package/dist/commands/doctor/branch-pr.js +5 -2
  40. package/dist/commands/guard/impl/commands.d.ts.map +1 -1
  41. package/dist/commands/guard/impl/commands.js +4 -1
  42. package/dist/commands/guard/impl/comment-commit.d.ts.map +1 -1
  43. package/dist/commands/guard/impl/comment-commit.js +2 -1
  44. package/dist/commands/guard/impl/env.d.ts +6 -0
  45. package/dist/commands/guard/impl/env.d.ts.map +1 -1
  46. package/dist/commands/guard/impl/env.js +41 -0
  47. package/dist/commands/pr/internal/auto-commit.d.ts.map +1 -1
  48. package/dist/commands/pr/internal/auto-commit.js +2 -1
  49. package/dist/commands/pr/internal/sync-branch.d.ts +36 -0
  50. package/dist/commands/pr/internal/sync-branch.d.ts.map +1 -0
  51. package/dist/commands/pr/internal/sync-branch.js +113 -0
  52. package/dist/commands/pr/internal/sync-github.d.ts +28 -0
  53. package/dist/commands/pr/internal/sync-github.d.ts.map +1 -0
  54. package/dist/commands/pr/internal/sync-github.js +178 -0
  55. package/dist/commands/pr/internal/sync-model.d.ts +36 -0
  56. package/dist/commands/pr/internal/sync-model.d.ts.map +1 -0
  57. package/dist/commands/pr/internal/sync-model.js +1 -0
  58. package/dist/commands/pr/internal/sync-open-step.d.ts +10 -0
  59. package/dist/commands/pr/internal/sync-open-step.d.ts.map +1 -0
  60. package/dist/commands/pr/internal/sync-open-step.js +128 -0
  61. package/dist/commands/pr/internal/sync-support.d.ts +7 -0
  62. package/dist/commands/pr/internal/sync-support.d.ts.map +1 -0
  63. package/dist/commands/pr/internal/sync-support.js +29 -0
  64. package/dist/commands/pr/internal/sync-update-step.d.ts +6 -0
  65. package/dist/commands/pr/internal/sync-update-step.d.ts.map +1 -0
  66. package/dist/commands/pr/internal/sync-update-step.js +68 -0
  67. package/dist/commands/pr/internal/sync.d.ts +2 -6
  68. package/dist/commands/pr/internal/sync.d.ts.map +1 -1
  69. package/dist/commands/pr/internal/sync.js +83 -529
  70. package/dist/commands/pr/open.d.ts.map +1 -1
  71. package/dist/commands/pr/open.js +25 -8
  72. package/dist/commands/pr/pr.command.d.ts.map +1 -1
  73. package/dist/commands/pr/pr.command.js +7 -2
  74. package/dist/commands/recipes/impl/apply.d.ts +1 -1
  75. package/dist/commands/recipes/impl/apply.d.ts.map +1 -1
  76. package/dist/commands/recipes/impl/apply.js +1 -2
  77. package/dist/commands/recipes/impl/commands/active.d.ts.map +1 -1
  78. package/dist/commands/recipes/impl/commands/active.js +6 -5
  79. package/dist/commands/recipes/impl/commands/add.d.ts +1 -0
  80. package/dist/commands/recipes/impl/commands/add.d.ts.map +1 -1
  81. package/dist/commands/recipes/impl/commands/add.js +32 -27
  82. package/dist/commands/recipes/impl/commands/detach.d.ts.map +1 -1
  83. package/dist/commands/recipes/impl/commands/detach.js +35 -21
  84. package/dist/commands/recipes/impl/commands/disable.d.ts.map +1 -1
  85. package/dist/commands/recipes/impl/commands/disable.js +5 -3
  86. package/dist/commands/recipes/impl/commands/enable.d.ts.map +1 -1
  87. package/dist/commands/recipes/impl/commands/enable.js +5 -3
  88. package/dist/commands/recipes/impl/commands/explain.d.ts.map +1 -1
  89. package/dist/commands/recipes/impl/commands/explain.js +57 -47
  90. package/dist/commands/recipes/impl/commands/info.d.ts.map +1 -1
  91. package/dist/commands/recipes/impl/commands/info.js +25 -21
  92. package/dist/commands/recipes/impl/commands/install.d.ts +1 -1
  93. package/dist/commands/recipes/impl/commands/install.d.ts.map +1 -1
  94. package/dist/commands/recipes/impl/commands/install.js +3 -13
  95. package/dist/commands/recipes/impl/commands/list-remote.d.ts.map +1 -1
  96. package/dist/commands/recipes/impl/commands/list-remote.js +2 -3
  97. package/dist/commands/recipes/impl/commands/list.d.ts.map +1 -1
  98. package/dist/commands/recipes/impl/commands/list.js +7 -6
  99. package/dist/commands/recipes/impl/commands/remove.d.ts.map +1 -1
  100. package/dist/commands/recipes/impl/commands/remove.js +12 -7
  101. package/dist/commands/recipes/impl/commands/update.d.ts.map +1 -1
  102. package/dist/commands/recipes/impl/commands/update.js +38 -24
  103. package/dist/commands/recipes/impl/index.d.ts +1 -1
  104. package/dist/commands/recipes/impl/index.d.ts.map +1 -1
  105. package/dist/commands/recipes/impl/installed-recipes.d.ts +1 -1
  106. package/dist/commands/recipes/impl/installed-recipes.d.ts.map +1 -1
  107. package/dist/commands/recipes/impl/installed-recipes.js +1 -2
  108. package/dist/commands/recipes/impl/mutation-transaction.d.ts +7 -0
  109. package/dist/commands/recipes/impl/mutation-transaction.d.ts.map +1 -0
  110. package/dist/commands/recipes/impl/mutation-transaction.js +47 -0
  111. package/dist/commands/recipes/impl/overlay-project.d.ts +19 -3
  112. package/dist/commands/recipes/impl/overlay-project.d.ts.map +1 -1
  113. package/dist/commands/recipes/impl/overlay-project.js +76 -38
  114. package/dist/commands/recipes/impl/paths.d.ts +0 -3
  115. package/dist/commands/recipes/impl/paths.d.ts.map +1 -1
  116. package/dist/commands/recipes/impl/paths.js +0 -3
  117. package/dist/commands/recipes/impl/project-installed-recipes.d.ts +4 -1
  118. package/dist/commands/recipes/impl/project-installed-recipes.d.ts.map +1 -1
  119. package/dist/commands/recipes/impl/project-installed-recipes.js +6 -4
  120. package/dist/commands/recipes/impl/project-recipe-state.d.ts +1 -1
  121. package/dist/commands/recipes/impl/project-recipe-state.d.ts.map +1 -1
  122. package/dist/commands/recipes/impl/project-registry.d.ts +5 -1
  123. package/dist/commands/recipes/impl/project-registry.d.ts.map +1 -1
  124. package/dist/commands/recipes/impl/project-registry.js +34 -14
  125. package/dist/commands/recipes/impl/resolver.d.ts +1 -1
  126. package/dist/commands/recipes/impl/resolver.d.ts.map +1 -1
  127. package/dist/commands/recipes/impl/resolver.js +1 -1
  128. package/dist/commands/recipes/impl/types.d.ts +1 -1
  129. package/dist/commands/recipes/impl/types.d.ts.map +1 -1
  130. package/dist/commands/recipes/impl/version.d.ts +5 -0
  131. package/dist/commands/recipes/impl/version.d.ts.map +1 -0
  132. package/dist/commands/recipes/impl/version.js +9 -0
  133. package/dist/commands/recipes.d.ts +5 -4
  134. package/dist/commands/recipes.d.ts.map +1 -1
  135. package/dist/commands/recipes.js +3 -3
  136. package/dist/commands/release/apply.command.d.ts +1 -1
  137. package/dist/commands/release/apply.command.d.ts.map +1 -1
  138. package/dist/commands/release/apply.command.js +15 -379
  139. package/dist/commands/release/apply.mutation.d.ts +1 -0
  140. package/dist/commands/release/apply.mutation.d.ts.map +1 -1
  141. package/dist/commands/release/apply.mutation.js +24 -1
  142. package/dist/commands/release/apply.pipeline.d.ts +22 -0
  143. package/dist/commands/release/apply.pipeline.d.ts.map +1 -0
  144. package/dist/commands/release/apply.pipeline.js +371 -0
  145. package/dist/commands/release/apply.preflight.d.ts +2 -0
  146. package/dist/commands/release/apply.preflight.d.ts.map +1 -1
  147. package/dist/commands/release/apply.preflight.js +13 -4
  148. package/dist/commands/release/apply.types.d.ts +27 -0
  149. package/dist/commands/release/apply.types.d.ts.map +1 -1
  150. package/dist/commands/release.test-helpers.d.ts +4 -0
  151. package/dist/commands/release.test-helpers.d.ts.map +1 -1
  152. package/dist/commands/release.test-helpers.js +7 -0
  153. package/dist/commands/shared/reconcile-check.d.ts.map +1 -1
  154. package/dist/commands/shared/reconcile-check.js +2 -2
  155. package/dist/commands/shared/task-backend.d.ts +6 -1
  156. package/dist/commands/shared/task-backend.d.ts.map +1 -1
  157. package/dist/commands/shared/task-backend.js +34 -2
  158. package/dist/commands/shared/task-mutation.d.ts.map +1 -1
  159. package/dist/commands/shared/task-mutation.js +4 -4
  160. package/dist/commands/shared/task-store/intents.d.ts +34 -0
  161. package/dist/commands/shared/task-store/intents.d.ts.map +1 -0
  162. package/dist/commands/shared/task-store/intents.js +265 -0
  163. package/dist/commands/shared/task-store/readme.d.ts +28 -0
  164. package/dist/commands/shared/task-store/readme.d.ts.map +1 -0
  165. package/dist/commands/shared/task-store/readme.js +125 -0
  166. package/dist/commands/shared/task-store/store.d.ts +26 -0
  167. package/dist/commands/shared/task-store/store.d.ts.map +1 -0
  168. package/dist/commands/shared/task-store/store.js +105 -0
  169. package/dist/commands/shared/task-store/types.d.ts +94 -0
  170. package/dist/commands/shared/task-store/types.d.ts.map +1 -0
  171. package/dist/commands/shared/task-store/types.js +1 -0
  172. package/dist/commands/shared/task-store.d.ts +3 -109
  173. package/dist/commands/shared/task-store.d.ts.map +1 -1
  174. package/dist/commands/shared/task-store.js +2 -493
  175. package/dist/commands/task/block.d.ts.map +1 -1
  176. package/dist/commands/task/block.js +7 -2
  177. package/dist/commands/task/comment.d.ts.map +1 -1
  178. package/dist/commands/task/comment.js +7 -2
  179. package/dist/commands/task/finish-shared.d.ts.map +1 -1
  180. package/dist/commands/task/finish-shared.js +3 -3
  181. package/dist/commands/task/finish.d.ts.map +1 -1
  182. package/dist/commands/task/finish.js +102 -15
  183. package/dist/commands/task/hosted-merge-sync.d.ts.map +1 -1
  184. package/dist/commands/task/hosted-merge-sync.js +9 -4
  185. package/dist/commands/task/list.run.d.ts.map +1 -1
  186. package/dist/commands/task/list.run.js +14 -4
  187. package/dist/commands/task/new.command.d.ts.map +1 -1
  188. package/dist/commands/task/new.command.js +16 -2
  189. package/dist/commands/task/new.js +2 -2
  190. package/dist/commands/task/show.d.ts.map +1 -1
  191. package/dist/commands/task/show.js +3 -3
  192. package/dist/commands/task/update.d.ts.map +1 -1
  193. package/dist/commands/task/update.js +11 -3
  194. package/dist/runner/adapters/codex.d.ts.map +1 -1
  195. package/dist/runner/adapters/codex.js +3 -33
  196. package/dist/runner/adapters/custom.d.ts.map +1 -1
  197. package/dist/runner/adapters/custom.js +3 -30
  198. package/dist/runner/adapters/runtime-shared.d.ts +14 -0
  199. package/dist/runner/adapters/runtime-shared.d.ts.map +1 -0
  200. package/dist/runner/adapters/runtime-shared.js +36 -0
  201. package/dist/runner/context/base-prompt-sources.d.ts +30 -0
  202. package/dist/runner/context/base-prompt-sources.d.ts.map +1 -0
  203. package/dist/runner/context/base-prompt-sources.js +144 -0
  204. package/dist/runner/context/base-prompts.d.ts +3 -22
  205. package/dist/runner/context/base-prompts.d.ts.map +1 -1
  206. package/dist/runner/context/base-prompts.js +6 -450
  207. package/dist/runner/context/overlay-prompt-blocks.d.ts +7 -0
  208. package/dist/runner/context/overlay-prompt-blocks.d.ts.map +1 -0
  209. package/dist/runner/context/overlay-prompt-blocks.js +72 -0
  210. package/dist/runner/context/prompt-block-shared.d.ts +54 -0
  211. package/dist/runner/context/prompt-block-shared.d.ts.map +1 -0
  212. package/dist/runner/context/prompt-block-shared.js +106 -0
  213. package/dist/runner/context/recipe-context.d.ts +2 -1
  214. package/dist/runner/context/recipe-context.d.ts.map +1 -1
  215. package/dist/runner/context/recipe-context.js +2 -1
  216. package/dist/runner/context/recipe-prompt-blocks.d.ts +6 -0
  217. package/dist/runner/context/recipe-prompt-blocks.d.ts.map +1 -0
  218. package/dist/runner/context/recipe-prompt-blocks.js +143 -0
  219. package/dist/runner/usecases/scenario-materialize-task.js +2 -2
  220. package/dist/runner/usecases/task-run-inspect.js +2 -2
  221. package/dist/runner/usecases/task-run-lifecycle-shared.js +2 -2
  222. package/dist/runner/usecases/task-run.d.ts.map +1 -1
  223. package/dist/runner/usecases/task-run.js +4 -2
  224. package/dist/runtime/capabilities/recipe.d.ts +1 -1
  225. package/dist/runtime/capabilities/recipe.d.ts.map +1 -1
  226. package/dist/runtime/execution-context.d.ts +63 -0
  227. package/dist/runtime/execution-context.d.ts.map +1 -0
  228. package/dist/{usecases/context/resolve-context.js → runtime/execution-context.js} +23 -26
  229. package/dist/runtime/incidents/advice-strategy.d.ts +15 -0
  230. package/dist/runtime/incidents/advice-strategy.d.ts.map +1 -0
  231. package/dist/runtime/incidents/advice-strategy.js +54 -0
  232. package/dist/runtime/incidents/plan-strategy.d.ts +9 -0
  233. package/dist/runtime/incidents/plan-strategy.d.ts.map +1 -0
  234. package/dist/runtime/incidents/plan-strategy.js +205 -0
  235. package/dist/runtime/incidents/registry-strategy.d.ts +6 -0
  236. package/dist/runtime/incidents/registry-strategy.d.ts.map +1 -0
  237. package/dist/runtime/incidents/registry-strategy.js +280 -0
  238. package/dist/runtime/incidents/resolve.d.ts +3 -25
  239. package/dist/runtime/incidents/resolve.d.ts.map +1 -1
  240. package/dist/runtime/incidents/resolve.js +3 -683
  241. package/dist/runtime/incidents/shared.d.ts +34 -0
  242. package/dist/runtime/incidents/shared.d.ts.map +1 -0
  243. package/dist/runtime/incidents/shared.js +171 -0
  244. package/dist/testing/cli-harness/recipe-archives.d.ts +28 -0
  245. package/dist/testing/cli-harness/recipe-archives.d.ts.map +1 -0
  246. package/dist/testing/cli-harness/recipe-archives.js +374 -0
  247. package/dist/testing/cli-harness/stdio.d.ts +26 -0
  248. package/dist/testing/cli-harness/stdio.d.ts.map +1 -0
  249. package/dist/testing/cli-harness/stdio.js +84 -0
  250. package/dist/testing/cli-harness.d.ts +25 -0
  251. package/dist/testing/cli-harness.d.ts.map +1 -0
  252. package/dist/testing/cli-harness.js +313 -0
  253. package/dist/testing/index.d.ts +2 -0
  254. package/dist/testing/index.d.ts.map +1 -0
  255. package/dist/testing/index.js +1 -0
  256. package/package.json +7 -4
  257. package/dist/commands/recipes/impl/manifest.d.ts +0 -4
  258. package/dist/commands/recipes/impl/manifest.d.ts.map +0 -1
  259. package/dist/commands/recipes/impl/manifest.js +0 -7
  260. package/dist/commands/recipes/impl/normalize.d.ts +0 -8
  261. package/dist/commands/recipes/impl/normalize.d.ts.map +0 -1
  262. package/dist/commands/recipes/impl/normalize.js +0 -54
  263. package/dist/commands/recipes/impl/scenario.d.ts +0 -16
  264. package/dist/commands/recipes/impl/scenario.d.ts.map +0 -1
  265. package/dist/commands/recipes/impl/scenario.js +0 -262
  266. package/dist/recipes/bundled-recipes.d.ts +0 -17
  267. package/dist/recipes/bundled-recipes.d.ts.map +0 -1
  268. package/dist/recipes/bundled-recipes.js +0 -15
  269. package/dist/usecases/context/resolve-context.d.ts +0 -68
  270. package/dist/usecases/context/resolve-context.d.ts.map +0 -1
  271. package/dist/usecases/task/task-list-usecase.d.ts +0 -9
  272. package/dist/usecases/task/task-list-usecase.d.ts.map +0 -1
  273. package/dist/usecases/task/task-list-usecase.js +0 -17
  274. package/dist/usecases/task/task-new-usecase.d.ts +0 -9
  275. package/dist/usecases/task/task-new-usecase.d.ts.map +0 -1
  276. package/dist/usecases/task/task-new-usecase.js +0 -17
@@ -1,15 +1,15 @@
1
- import { buildAdapters } from "../../adapters/index.js";
2
- import { loadCommandContext } from "../../commands/shared/task-backend.js";
3
- import { PolicyEngine } from "../../policy/engine.js";
4
- import { createApprovalRuntime } from "../../runtime/approvals/index.js";
5
- import { resolveTaskBackendCapabilityRegistry, } from "../../runtime/capabilities/index.js";
6
- import { resolveExecutionProfileRuntime, } from "../../runtime/execution-profile/index.js";
7
- import { buildFrameworkExplainPayload, } from "../../runtime/explain/index.js";
8
- import { resolveHarnessFromCommandContext, } from "../../runtime/harness/index.js";
9
- import { buildFrameworkProtocolSurface, } from "../../runtime/protocol/index.js";
10
- import { createTaskIntakeRuntime, } from "../../runtime/task-intake/index.js";
1
+ import { buildAdapters } from "../adapters/index.js";
2
+ import { loadCommandContext } from "../commands/shared/task-backend.js";
3
+ import { PolicyEngine } from "../policy/engine.js";
4
+ import { createApprovalRuntime } from "./approvals/index.js";
5
+ import { resolveTaskBackendCapabilityRegistry, } from "./capabilities/index.js";
6
+ import { resolveExecutionProfileRuntime, } from "./execution-profile/index.js";
7
+ import { buildFrameworkExplainPayload } from "./explain/index.js";
8
+ import { resolveHarnessFromCommandContext } from "./harness/index.js";
9
+ import { buildFrameworkProtocolSurface } from "./protocol/index.js";
10
+ import { createTaskIntakeRuntime } from "./task-intake/index.js";
11
11
  const READ_ONLY_CONTEXT_CACHE = new WeakMap();
12
- const USECASE_CONTEXT_CACHE = new WeakMap();
12
+ const CONTEXT_CACHE = new WeakMap();
13
13
  export async function resolveCommandContext(opts) {
14
14
  return await loadCommandContext({
15
15
  cwd: opts.cwd,
@@ -18,34 +18,31 @@ export async function resolveCommandContext(opts) {
18
18
  config: opts.config,
19
19
  });
20
20
  }
21
- export async function resolveContext(opts) {
21
+ export async function resolveReadOnlyExecutionContext(opts) {
22
22
  const command = await resolveCommandContext(opts);
23
- return await makeReadOnlyUsecaseContext(command);
23
+ return await makeReadOnlyExecutionContext(command);
24
24
  }
25
- export const resolveReadOnlyExecutionContext = resolveContext;
26
25
  export async function resolveExecutionContext(opts) {
27
26
  const command = await resolveCommandContext(opts);
28
- return await makeUsecaseContext(command);
27
+ return await makeExecutionContext(command);
29
28
  }
30
- export async function makeReadOnlyUsecaseContext(command) {
29
+ export async function makeReadOnlyExecutionContext(command) {
31
30
  let cached = READ_ONLY_CONTEXT_CACHE.get(command);
32
31
  if (!cached) {
33
- cached = buildReadOnlyUsecaseContext(command);
32
+ cached = buildReadOnlyExecutionContext(command);
34
33
  READ_ONLY_CONTEXT_CACHE.set(command, cached);
35
34
  }
36
35
  return await cached;
37
36
  }
38
- export async function makeUsecaseContext(command) {
39
- let cached = USECASE_CONTEXT_CACHE.get(command);
37
+ export async function makeExecutionContext(command) {
38
+ let cached = CONTEXT_CACHE.get(command);
40
39
  if (!cached) {
41
- cached = buildUsecaseContext(command);
42
- USECASE_CONTEXT_CACHE.set(command, cached);
40
+ cached = buildExecutionContext(command);
41
+ CONTEXT_CACHE.set(command, cached);
43
42
  }
44
43
  return await cached;
45
44
  }
46
- export const makeReadOnlyExecutionContext = makeReadOnlyUsecaseContext;
47
- export const makeExecutionContext = makeUsecaseContext;
48
- async function buildReadOnlyUsecaseContext(command) {
45
+ async function buildReadOnlyExecutionContext(command) {
49
46
  const harness = await resolveHarnessFromCommandContext(command);
50
47
  const policy = new PolicyEngine();
51
48
  const executionProfile = resolveExecutionProfileRuntime(command.config);
@@ -103,9 +100,9 @@ async function buildReadOnlyUsecaseContext(command) {
103
100
  approvalRuntime: createApprovalRuntime({ config: command.config, policy }),
104
101
  };
105
102
  }
106
- async function buildUsecaseContext(command) {
103
+ async function buildExecutionContext(command) {
107
104
  return {
108
- ...(await makeReadOnlyUsecaseContext(command)),
105
+ ...(await makeReadOnlyExecutionContext(command)),
109
106
  adapters: buildAdapters(command),
110
107
  };
111
108
  }
@@ -0,0 +1,15 @@
1
+ import type { IncidentAdviceMatch, IncidentAdviceQuery, IncidentRegistry } from "./types.js";
2
+ export declare function buildIncidentAdviceQueryFromTask(opts: {
3
+ taskId: string;
4
+ title: string;
5
+ description: string;
6
+ scope?: string | null;
7
+ tags: readonly string[];
8
+ }): IncidentAdviceQuery;
9
+ export declare function resolveIncidentAdviceMatches(opts: {
10
+ query: IncidentAdviceQuery;
11
+ registry: IncidentRegistry;
12
+ limit?: number;
13
+ }): IncidentAdviceMatch[];
14
+ export declare function renderIncidentAdvice(matches: readonly IncidentAdviceMatch[]): string;
15
+ //# sourceMappingURL=advice-strategy.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"advice-strategy.d.ts","sourceRoot":"","sources":["../../../src/runtime/incidents/advice-strategy.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAS7F,wBAAgB,gCAAgC,CAAC,IAAI,EAAE;IACrD,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,IAAI,EAAE,SAAS,MAAM,EAAE,CAAC;CACzB,GAAG,mBAAmB,CAQtB;AAED,wBAAgB,4BAA4B,CAAC,IAAI,EAAE;IACjD,KAAK,EAAE,mBAAmB,CAAC;IAC3B,QAAQ,EAAE,gBAAgB,CAAC;IAC3B,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,GAAG,mBAAmB,EAAE,CA8BxB;AAED,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,SAAS,mBAAmB,EAAE,GAAG,MAAM,CAcpF"}
@@ -0,0 +1,54 @@
1
+ import { buildIncidentSignature, compareIncidentAdviceMatch, dedupeCaseInsensitive, normalizeSearchText, tokenize, } from "./shared.js";
2
+ export function buildIncidentAdviceQueryFromTask(opts) {
3
+ return {
4
+ taskId: opts.taskId,
5
+ title: opts.title,
6
+ description: opts.description,
7
+ scope: opts.scope ?? null,
8
+ tags: dedupeCaseInsensitive(opts.tags),
9
+ };
10
+ }
11
+ export function resolveIncidentAdviceMatches(opts) {
12
+ const limit = Math.max(1, opts.limit ?? 5);
13
+ const tagSet = new Set(opts.query.tags.map((tag) => tag.trim().toLowerCase()).filter(Boolean));
14
+ const haystack = [opts.query.title, opts.query.description, opts.query.scope ?? ""].join(" ");
15
+ const normalizedHaystack = normalizeSearchText(haystack);
16
+ const queryTokens = new Set([...tokenize(haystack), ...tagSet]);
17
+ const matchesBySignature = new Map();
18
+ for (const entry of opts.registry.entries) {
19
+ const matchedTags = entry.tags.filter((tag) => tagSet.has(tag.trim().toLowerCase()));
20
+ const matchedTerms = entry.match.filter((term) => queryTokens.has(term.trim().toLowerCase()) ||
21
+ normalizedHaystack.includes(normalizeSearchText(term)));
22
+ const normalizedScope = normalizeSearchText(entry.scope);
23
+ const scopeMatched = normalizedScope.length > 0 && normalizedHaystack.includes(normalizedScope);
24
+ const score = matchedTags.length * 5 + matchedTerms.length * 2 + (scopeMatched ? 3 : 0);
25
+ if (score <= 0)
26
+ continue;
27
+ const match = { entry, score, matchedTags, matchedTerms, scopeMatched };
28
+ const signature = buildIncidentSignature(entry);
29
+ const existing = matchesBySignature.get(signature);
30
+ if (!existing || compareIncidentAdviceMatch(match, existing) < 0) {
31
+ matchesBySignature.set(signature, match);
32
+ }
33
+ }
34
+ const matches = [...matchesBySignature.values()];
35
+ matches.sort((left, right) => compareIncidentAdviceMatch(left, right));
36
+ return matches.slice(0, limit);
37
+ }
38
+ export function renderIncidentAdvice(matches) {
39
+ if (matches.length === 0)
40
+ return "No matching incident advice.";
41
+ return matches
42
+ .map((match) => {
43
+ const lines = [
44
+ `- ${match.entry.id} | scope: ${match.entry.scope}`,
45
+ ` failure: ${match.entry.failure}`,
46
+ ` advice: ${match.entry.advice ?? match.entry.rule}`,
47
+ ` rule: ${match.entry.rule}`,
48
+ ];
49
+ if (match.entry.evidence)
50
+ lines.push(` evidence: ${match.entry.evidence}`);
51
+ return lines.join("\n");
52
+ })
53
+ .join("\n");
54
+ }
@@ -0,0 +1,9 @@
1
+ import type { IncidentCollectionPlan, IncidentFindingCandidate, IncidentPromotionTaskContext, IncidentRegistry } from "./types.js";
2
+ export declare function extractIncidentCandidatesFromFindings(findings: string): IncidentFindingCandidate[];
3
+ export declare function planIncidentCollection(opts: {
4
+ task: IncidentPromotionTaskContext;
5
+ findings: string;
6
+ registry: IncidentRegistry;
7
+ now?: Date;
8
+ }): IncidentCollectionPlan;
9
+ //# sourceMappingURL=plan-strategy.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plan-strategy.d.ts","sourceRoot":"","sources":["../../../src/runtime/incidents/plan-strategy.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,sBAAsB,EACtB,wBAAwB,EAGxB,4BAA4B,EAC5B,gBAAgB,EAGjB,MAAM,YAAY,CAAC;AAqBpB,wBAAgB,qCAAqC,CACnD,QAAQ,EAAE,MAAM,GACf,wBAAwB,EAAE,CAI5B;AAwKD,wBAAgB,sBAAsB,CAAC,IAAI,EAAE;IAC3C,IAAI,EAAE,4BAA4B,CAAC;IACnC,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,gBAAgB,CAAC;IAC3B,GAAG,CAAC,EAAE,IAAI,CAAC;CACZ,GAAG,sBAAsB,CAyDzB"}
@@ -0,0 +1,205 @@
1
+ import { buildDerivedIncidentRule, buildIncidentFingerprint, buildMatchTerms, dedupeCaseInsensitive, normalizeLines, parseBoolean, parseCsvList, parseFixability, resolveIncidentState, summarizeTaskScope, appendFieldValue, normalizeKey, } from "./shared.js";
2
+ import { appendIncidentRegistryEntries, createIncidentRegistrySkeleton, parseIncidentRegistry, } from "./registry-strategy.js";
3
+ export function extractIncidentCandidatesFromFindings(findings) {
4
+ return parseIncidentFindingBlocks(findings)
5
+ .filter((candidate) => candidate.shouldPromote)
6
+ .map(({ shouldPromote: _shouldPromote, ...candidate }) => candidate);
7
+ }
8
+ function parseIncidentFindingBlocks(findings) {
9
+ const lines = normalizeLines(findings);
10
+ const candidates = [];
11
+ let currentFields = null;
12
+ let currentLine = 0;
13
+ let currentKey = null;
14
+ const flush = () => {
15
+ if (!currentFields)
16
+ return;
17
+ const promotion = currentFields.promotion?.trim() || null;
18
+ const fixability = parseFixability(currentFields.fixability);
19
+ const incidentExternal = parseBoolean(currentFields.incidentexternal) || fixability === "external";
20
+ const incidentInternal = parseBoolean(currentFields.incidentinternal) || fixability === "repo-fixable";
21
+ const shouldPromote = promotion?.toLowerCase() === "incident-candidate" || incidentExternal || incidentInternal;
22
+ const observation = currentFields.observation?.trim() ?? "";
23
+ if (!observation) {
24
+ currentFields = null;
25
+ currentKey = null;
26
+ currentLine = 0;
27
+ return;
28
+ }
29
+ candidates.push({
30
+ observation,
31
+ impact: currentFields.impact?.trim() || null,
32
+ resolution: currentFields.resolution?.trim() || null,
33
+ promotion,
34
+ incidentScope: currentFields.incidentscope?.trim() || null,
35
+ incidentRule: currentFields.incidentrule?.trim() || null,
36
+ incidentAdvice: currentFields.incidentadvice?.trim() || null,
37
+ incidentTags: parseCsvList(currentFields.incidenttags),
38
+ incidentMatch: parseCsvList(currentFields.incidentmatch),
39
+ incidentExternal,
40
+ incidentInternal,
41
+ fixability,
42
+ shouldPromote,
43
+ line: currentLine,
44
+ rawFields: { ...currentFields },
45
+ });
46
+ currentFields = null;
47
+ currentKey = null;
48
+ currentLine = 0;
49
+ };
50
+ for (const [index, line] of lines.entries()) {
51
+ const observationMatch = /^\s*-\s+Observation:\s*(.*?)\s*$/.exec(line);
52
+ if (observationMatch) {
53
+ flush();
54
+ currentFields = { observation: observationMatch[1] ?? "" };
55
+ currentKey = "observation";
56
+ currentLine = index + 1;
57
+ continue;
58
+ }
59
+ if (!currentFields)
60
+ continue;
61
+ if (/^\s*-\s+/.test(line)) {
62
+ flush();
63
+ const nestedObservationMatch = /^\s*-\s+Observation:\s*(.*?)\s*$/.exec(line);
64
+ if (nestedObservationMatch) {
65
+ currentFields = { observation: nestedObservationMatch[1] ?? "" };
66
+ currentKey = "observation";
67
+ currentLine = index + 1;
68
+ }
69
+ continue;
70
+ }
71
+ const fieldMatch = /^\s{2,}([A-Za-z][A-Za-z0-9 _-]*):\s*(.*?)\s*$/.exec(line);
72
+ if (fieldMatch) {
73
+ currentKey = normalizeKey(fieldMatch[1] ?? "");
74
+ currentFields[currentKey] = fieldMatch[2] ?? "";
75
+ continue;
76
+ }
77
+ if (currentKey && /^\s{2,}\S/.test(line)) {
78
+ appendFieldValue(currentFields, currentKey, line.trim(), "\n");
79
+ continue;
80
+ }
81
+ if (!line.trim())
82
+ continue;
83
+ if (currentKey)
84
+ appendFieldValue(currentFields, currentKey, line.trim());
85
+ }
86
+ flush();
87
+ return candidates;
88
+ }
89
+ function nextIncidentId(entries, now) {
90
+ const dateStamp = `${now.getUTCFullYear()}${String(now.getUTCMonth() + 1).padStart(2, "0")}${String(now.getUTCDate()).padStart(2, "0")}`;
91
+ const prefix = `INC-${dateStamp}-`;
92
+ let max = 0;
93
+ for (const entry of entries) {
94
+ if (!entry.id.startsWith(prefix))
95
+ continue;
96
+ const num = Number.parseInt(entry.id.slice(prefix.length), 10);
97
+ if (Number.isInteger(num) && num > max)
98
+ max = num;
99
+ }
100
+ return `${prefix}${String(max + 1).padStart(2, "0")}`;
101
+ }
102
+ function buildPromotionIssues(candidate) {
103
+ const missingFields = [];
104
+ if (!candidate.incidentExternal && !candidate.incidentInternal && candidate.fixability === null) {
105
+ missingFields.push("Fixability: external or IncidentExternal: true");
106
+ }
107
+ if (!candidate.incidentAdvice && !candidate.resolution) {
108
+ missingFields.push("Resolution or IncidentAdvice");
109
+ }
110
+ return missingFields.length > 0 ? { candidate, missingFields } : null;
111
+ }
112
+ function buildIncidentRegistryEntry(opts) {
113
+ const date = opts.now.toISOString().slice(0, 10);
114
+ const scope = opts.candidate.incidentScope ?? summarizeTaskScope(opts.task.scope, opts.task.title);
115
+ const tags = dedupeCaseInsensitive([
116
+ ...opts.candidate.incidentTags,
117
+ ...opts.task.tags.map((tag) => tag.trim()),
118
+ ]);
119
+ const advice = opts.candidate.incidentAdvice ?? opts.candidate.resolution ?? opts.candidate.observation;
120
+ const rule = opts.candidate.incidentRule ?? buildDerivedIncidentRule(scope);
121
+ const state = resolveIncidentState({
122
+ registry: opts.registry,
123
+ entry: {
124
+ scope,
125
+ failure: opts.candidate.observation,
126
+ rule,
127
+ },
128
+ now: opts.now,
129
+ });
130
+ const match = buildMatchTerms({
131
+ scope,
132
+ tags,
133
+ explicitMatch: opts.candidate.incidentMatch,
134
+ extraText: [opts.task.title, opts.task.description, advice],
135
+ });
136
+ return {
137
+ id: nextIncidentId(opts.registry.entries, opts.now),
138
+ date,
139
+ scope,
140
+ failure: opts.candidate.observation,
141
+ rule,
142
+ evidence: `task ${opts.task.id}${opts.task.commitHash ? `; commit ${opts.task.commitHash.slice(0, 12)}` : ""}`,
143
+ enforcement: "manual",
144
+ state,
145
+ tags,
146
+ match,
147
+ advice,
148
+ sourceTask: opts.task.id,
149
+ fixability: opts.candidate.fixability ?? (opts.candidate.incidentInternal ? "repo-fixable" : "external"),
150
+ rawFields: {},
151
+ line: 0,
152
+ };
153
+ }
154
+ export function planIncidentCollection(opts) {
155
+ const parsed = parseIncidentFindingBlocks(opts.findings);
156
+ const candidates = parsed
157
+ .filter((candidate) => candidate.shouldPromote)
158
+ .map(({ shouldPromote: _shouldPromote, ...candidate }) => candidate);
159
+ const skipped = parsed
160
+ .filter((candidate) => !candidate.shouldPromote)
161
+ .map(({ observation, line, rawFields }) => ({
162
+ observation,
163
+ line,
164
+ reason: "not_marked_external_or_promotable",
165
+ rawFields,
166
+ }));
167
+ const issues = [];
168
+ const promotable = [];
169
+ const duplicates = [];
170
+ const now = opts.now ?? new Date();
171
+ const seenFingerprints = new Set(opts.registry.entries.map((entry) => buildIncidentFingerprint(entry)));
172
+ for (const candidate of candidates) {
173
+ const issue = buildPromotionIssues(candidate);
174
+ if (issue) {
175
+ issues.push(issue);
176
+ continue;
177
+ }
178
+ const entry = buildIncidentRegistryEntry({
179
+ task: opts.task,
180
+ candidate,
181
+ now,
182
+ registry: parseIncidentRegistry(appendIncidentRegistryEntries(createIncidentRegistrySkeleton(), [
183
+ ...opts.registry.entries,
184
+ ...promotable.map((item) => item.entry),
185
+ ])),
186
+ });
187
+ const fingerprint = buildIncidentFingerprint(entry);
188
+ const draft = { candidate, entry, fingerprint };
189
+ if (seenFingerprints.has(fingerprint)) {
190
+ duplicates.push(draft);
191
+ continue;
192
+ }
193
+ seenFingerprints.add(fingerprint);
194
+ promotable.push(draft);
195
+ }
196
+ return {
197
+ candidates,
198
+ skipped,
199
+ promotable,
200
+ duplicates,
201
+ issues,
202
+ findingsTextPresent: opts.findings.trim().length > 0,
203
+ structuredFindingCount: parsed.length,
204
+ };
205
+ }
@@ -0,0 +1,6 @@
1
+ import type { IncidentRegistry, IncidentRegistryEntry } from "./types.js";
2
+ export declare function createIncidentRegistrySkeleton(): string;
3
+ export declare function parseIncidentRegistry(text: string): IncidentRegistry;
4
+ export declare function formatIncidentRegistryEntry(entry: IncidentRegistryEntry): string;
5
+ export declare function appendIncidentRegistryEntries(currentText: string, entries: readonly IncidentRegistryEntry[]): string;
6
+ //# sourceMappingURL=registry-strategy.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registry-strategy.d.ts","sourceRoot":"","sources":["../../../src/runtime/incidents/registry-strategy.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AAe1E,wBAAgB,8BAA8B,IAAI,MAAM,CAkCvD;AAED,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,GAAG,gBAAgB,CAkHpE;AAED,wBAAgB,2BAA2B,CAAC,KAAK,EAAE,qBAAqB,GAAG,MAAM,CAEhF;AAiJD,wBAAgB,6BAA6B,CAC3C,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE,SAAS,qBAAqB,EAAE,GACxC,MAAM,CAOR"}
@@ -0,0 +1,280 @@
1
+ import { appendFieldValue, buildIncidentFingerprint, COMPACT_INCIDENTS_HEADER, deriveSourceTask, incidentField, normalizeKey, normalizeLines, parseCsvList, parseEntryState, parseFixability, STRUCTURED_INCIDENTS_HEADER, } from "./shared.js";
2
+ export function createIncidentRegistrySkeleton() {
3
+ return [
4
+ STRUCTURED_INCIDENTS_HEADER,
5
+ "",
6
+ "## Entry contract",
7
+ "",
8
+ "- Add entries append-only.",
9
+ "- Every entry MUST include: `id`, `date`, `scope`, `failure`, `rule`, `evidence`, `enforcement`, `state`.",
10
+ "- New machine-matched entries SHOULD also include: `tags`, `match`, `advice`, `source_task`, `fixability`.",
11
+ "- `rule` MUST be concrete and testable (`MUST` / `MUST NOT`).",
12
+ "- `fixability: external` means the issue cannot be removed by changing only repository code and should stay as reusable operational advice.",
13
+ "- `fixability: repo-fixable` means the issue can be removed by repository code changes and should still be captured as reusable incident advice when explicitly marked.",
14
+ "- First auto-promoted reusable incidents normally enter as `open` and still participate in targeted advice lookup; recurring equivalent incidents can append later `stabilized` entries.",
15
+ "- `state` values: `open`, `stabilized`, `promoted`.",
16
+ "",
17
+ "## Entry template",
18
+ "",
19
+ "- id: `INC-YYYYMMDD-NN`",
20
+ "- date: `YYYY-MM-DD`",
21
+ "- scope: `<affected scope>`",
22
+ "- tags: `<comma-separated matching tags>`",
23
+ "- match: `<comma-separated lookup keywords>`",
24
+ "- failure: `<observed failure mode>`",
25
+ "- advice: `<reusable recovery or prevention guidance>`",
26
+ "- rule: `<new or refined MUST/MUST NOT>`",
27
+ "- evidence: `<task ids / logs / links>`",
28
+ "- enforcement: `<CI|test|lint|script|manual>`",
29
+ "- source_task: `<task id>`",
30
+ "- fixability: `<external|repo-fixable>`",
31
+ "- state: `<open|stabilized|promoted>`",
32
+ "",
33
+ "## Entries",
34
+ "",
35
+ ].join("\n");
36
+ }
37
+ export function parseIncidentRegistry(text) {
38
+ const lines = normalizeLines(text);
39
+ const entries = [];
40
+ let currentFields = null;
41
+ let currentLine = 0;
42
+ let currentKey = null;
43
+ const flush = () => {
44
+ if (!currentFields)
45
+ return;
46
+ const id = currentFields.id?.trim();
47
+ if (!id || !/^INC-\d{8}-\d+$/u.test(id)) {
48
+ currentFields = null;
49
+ currentKey = null;
50
+ currentLine = 0;
51
+ return;
52
+ }
53
+ const scope = currentFields.scope?.trim() ?? "";
54
+ const failure = currentFields.failure?.trim() ?? "";
55
+ const rule = currentFields.rule?.trim() ?? "";
56
+ const evidence = currentFields.evidence?.trim() ?? "";
57
+ const enforcement = currentFields.enforcement?.trim() ?? "manual";
58
+ if (!scope || !failure || !rule || !evidence) {
59
+ currentFields = null;
60
+ currentKey = null;
61
+ currentLine = 0;
62
+ return;
63
+ }
64
+ const sourceTask = deriveSourceTask(currentFields.source_task ?? currentFields.sourcetask, evidence);
65
+ entries.push({
66
+ id,
67
+ date: currentFields.date?.trim() ?? "",
68
+ scope,
69
+ tags: parseCsvList(currentFields.tags),
70
+ match: parseCsvList(currentFields.match),
71
+ failure,
72
+ advice: currentFields.advice?.trim() || null,
73
+ rule,
74
+ evidence,
75
+ enforcement,
76
+ sourceTask,
77
+ fixability: parseFixability(currentFields.fixability),
78
+ state: parseEntryState(currentFields.state),
79
+ rawFields: { ...currentFields },
80
+ line: currentLine,
81
+ });
82
+ currentFields = null;
83
+ currentKey = null;
84
+ currentLine = 0;
85
+ };
86
+ for (const [index, line] of lines.entries()) {
87
+ const trimmed = line.trim();
88
+ const inlineFields = parseInlineIncidentEntry(trimmed);
89
+ if (inlineFields) {
90
+ flush();
91
+ currentFields = { ...inlineFields };
92
+ const keys = Object.keys(inlineFields);
93
+ currentKey = keys.at(-1) ?? "id";
94
+ currentLine = index + 1;
95
+ continue;
96
+ }
97
+ const idMatch = /^\s*-\s+id:\s*(.*?)\s*$/u.exec(line);
98
+ if (idMatch) {
99
+ flush();
100
+ currentFields = { id: idMatch[1] ?? "" };
101
+ currentKey = "id";
102
+ currentLine = index + 1;
103
+ continue;
104
+ }
105
+ if (!currentFields)
106
+ continue;
107
+ if (/^##\s+/.test(trimmed)) {
108
+ flush();
109
+ continue;
110
+ }
111
+ if (/^\s*-\s+/.test(line)) {
112
+ flush();
113
+ const nestedIdMatch = /^\s*-\s+id:\s*(.*?)\s*$/u.exec(line);
114
+ if (nestedIdMatch) {
115
+ currentFields = { id: nestedIdMatch[1] ?? "" };
116
+ currentKey = "id";
117
+ currentLine = index + 1;
118
+ }
119
+ continue;
120
+ }
121
+ const fieldMatch = /^\s{2,}([A-Za-z][A-Za-z0-9 _-]*):\s*(.*?)\s*$/u.exec(line);
122
+ if (fieldMatch) {
123
+ currentKey = normalizeKey(fieldMatch[1] ?? "");
124
+ currentFields[currentKey] = fieldMatch[2] ?? "";
125
+ continue;
126
+ }
127
+ if (currentKey && /^\s{2,}\S/.test(line)) {
128
+ appendFieldValue(currentFields, currentKey, line.trim(), "\n");
129
+ continue;
130
+ }
131
+ if (!trimmed) {
132
+ flush();
133
+ continue;
134
+ }
135
+ if (currentKey)
136
+ appendFieldValue(currentFields, currentKey, line.trim());
137
+ }
138
+ flush();
139
+ return { entries };
140
+ }
141
+ export function formatIncidentRegistryEntry(entry) {
142
+ return formatIncidentRegistryEntryForStyle(entry, "structured");
143
+ }
144
+ function parseInlineIncidentEntry(trimmedLine) {
145
+ if (!trimmedLine.startsWith("- "))
146
+ return null;
147
+ const body = trimmedLine.slice(2).trim();
148
+ if (!body)
149
+ return null;
150
+ const segments = body.split(/\s+\|\s+(?=[a-z_]+:\s*)/u);
151
+ const fields = {};
152
+ for (const segment of segments) {
153
+ const match = /^([a-z_]+):\s*(.*?)\s*$/u.exec(segment.trim());
154
+ if (!match)
155
+ return null;
156
+ const key = normalizeKey(match[1] ?? "");
157
+ if (!key)
158
+ return null;
159
+ fields[key] = match[2] ?? "";
160
+ }
161
+ return fields.id ? fields : null;
162
+ }
163
+ function formatIncidentRegistryEntryForStyle(entry, style) {
164
+ const compactFields = [
165
+ incidentField("id", entry.id),
166
+ incidentField("date", entry.date),
167
+ incidentField("scope", entry.scope),
168
+ ...(entry.tags.length > 0 ? [incidentField("tags", entry.tags.join(", "))] : []),
169
+ ...(entry.match.length > 0 ? [incidentField("match", entry.match.join(", "))] : []),
170
+ incidentField("failure", entry.failure),
171
+ ...(entry.advice ? [incidentField("advice", entry.advice)] : []),
172
+ incidentField("rule", entry.rule),
173
+ incidentField("evidence", entry.evidence),
174
+ incidentField("enforcement", entry.enforcement),
175
+ ...(entry.fixability ? [incidentField("fixability", entry.fixability)] : []),
176
+ incidentField("state", entry.state),
177
+ ];
178
+ if (style === "compact") {
179
+ return `- ${compactFields.map(([key, value]) => `${key}: ${value}`).join(" | ")}`;
180
+ }
181
+ const structuredFields = [
182
+ incidentField("id", entry.id),
183
+ incidentField("date", entry.date),
184
+ incidentField("scope", entry.scope),
185
+ ...(entry.tags.length > 0 ? [incidentField("tags", entry.tags.join(", "))] : []),
186
+ ...(entry.match.length > 0 ? [incidentField("match", entry.match.join(", "))] : []),
187
+ incidentField("failure", entry.failure),
188
+ ...(entry.advice ? [incidentField("advice", entry.advice)] : []),
189
+ incidentField("rule", entry.rule),
190
+ incidentField("evidence", entry.evidence),
191
+ incidentField("enforcement", entry.enforcement),
192
+ ...(entry.sourceTask ? [incidentField("source_task", entry.sourceTask)] : []),
193
+ ...(entry.fixability ? [incidentField("fixability", entry.fixability)] : []),
194
+ incidentField("state", entry.state),
195
+ ];
196
+ return [
197
+ `- ${structuredFields[0]?.[0]}: ${structuredFields[0]?.[1] ?? ""}`,
198
+ ...structuredFields.slice(1).map(([key, value]) => ` ${key}: ${value}`),
199
+ ].join("\n");
200
+ }
201
+ function detectRegistryStyle(text) {
202
+ return /(^|\n)## Entries\s*$/mu.test(text) || /(^|\n)## Entry contract\s*$/mu.test(text)
203
+ ? "structured"
204
+ : "compact";
205
+ }
206
+ function entryRichness(entry) {
207
+ return [
208
+ entry.sourceTask ? 4 : 0,
209
+ entry.advice ? 3 : 0,
210
+ entry.tags.length,
211
+ entry.match.length,
212
+ entry.fixability ? 1 : 0,
213
+ entry.evidence.length > 0 ? 1 : 0,
214
+ ].reduce((sum, item) => sum + item, 0);
215
+ }
216
+ function nextIncidentIdForDate(dateStamp, usedIds, nextByDate) {
217
+ let next = nextByDate.get(dateStamp) ?? 0;
218
+ do {
219
+ next += 1;
220
+ } while (usedIds.has(`INC-${dateStamp}-${String(next).padStart(2, "0")}`));
221
+ nextByDate.set(dateStamp, next);
222
+ return `INC-${dateStamp}-${String(next).padStart(2, "0")}`;
223
+ }
224
+ function normalizeIncidentRegistryEntries(entries) {
225
+ const byFingerprint = new Map();
226
+ const orderedFingerprints = [];
227
+ for (const entry of entries) {
228
+ const fingerprint = buildIncidentFingerprint(entry);
229
+ const existing = byFingerprint.get(fingerprint);
230
+ if (!existing) {
231
+ byFingerprint.set(fingerprint, { ...entry });
232
+ orderedFingerprints.push(fingerprint);
233
+ continue;
234
+ }
235
+ if (entryRichness(entry) >= entryRichness(existing)) {
236
+ byFingerprint.set(fingerprint, { ...entry });
237
+ }
238
+ }
239
+ const normalized = orderedFingerprints.map((fingerprint) => byFingerprint.get(fingerprint));
240
+ const usedIds = new Set();
241
+ const nextByDate = new Map();
242
+ for (const entry of normalized) {
243
+ const match = /^INC-(\d{8})-(\d+)$/u.exec(entry.id);
244
+ if (!match)
245
+ continue;
246
+ const [, dateStamp, seqRaw] = match;
247
+ const seq = Number.parseInt(seqRaw ?? "", 10);
248
+ if (!Number.isInteger(seq))
249
+ continue;
250
+ const existing = nextByDate.get(dateStamp) ?? 0;
251
+ if (seq > existing)
252
+ nextByDate.set(dateStamp, seq);
253
+ }
254
+ return normalized.map((entry) => {
255
+ const dateStamp = /^\d{4}-\d{2}-\d{2}$/u.test(entry.date)
256
+ ? entry.date.replaceAll("-", "")
257
+ : "00000000";
258
+ const nextId = usedIds.has(entry.id)
259
+ ? nextIncidentIdForDate(dateStamp, usedIds, nextByDate)
260
+ : entry.id;
261
+ usedIds.add(nextId);
262
+ return nextId === entry.id ? entry : { ...entry, id: nextId };
263
+ });
264
+ }
265
+ function renderIncidentRegistryDocument(entries, style) {
266
+ const header = style === "structured" ? createIncidentRegistrySkeleton().trimEnd() : COMPACT_INCIDENTS_HEADER;
267
+ if (entries.length === 0)
268
+ return `${header}\n`;
269
+ const separator = style === "structured" ? "\n\n" : "\n";
270
+ return `${header}\n${entries.map((entry) => formatIncidentRegistryEntryForStyle(entry, style)).join(separator)}\n`;
271
+ }
272
+ export function appendIncidentRegistryEntries(currentText, entries) {
273
+ if (entries.length === 0)
274
+ return currentText;
275
+ const baseText = currentText.trim().length > 0 ? currentText : createIncidentRegistrySkeleton();
276
+ const style = detectRegistryStyle(baseText);
277
+ const existing = parseIncidentRegistry(baseText);
278
+ const merged = normalizeIncidentRegistryEntries([...existing.entries, ...entries]);
279
+ return renderIncidentRegistryDocument(merged, style);
280
+ }