@aperant/framework 0.13.0 → 0.16.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 (245) hide show
  1. package/CHANGELOG.md +106 -0
  2. package/README.md +1 -1
  3. package/agents/apt-pr-review-crossplatform.md +189 -0
  4. package/agents/apt-pr-review-i18n.md +190 -0
  5. package/dist/cli/commands/audit-branch-current.d.mts +27 -5
  6. package/dist/cli/commands/audit-branch-current.d.mts.map +1 -1
  7. package/dist/cli/commands/audit-branch-current.mjs +3 -3
  8. package/dist/cli/commands/audit-branch-current.mjs.map +1 -1
  9. package/dist/cli/commands/event.d.mts.map +1 -1
  10. package/dist/cli/commands/event.mjs +6 -4
  11. package/dist/cli/commands/event.mjs.map +1 -1
  12. package/dist/cli/commands/fan-out.d.mts.map +1 -1
  13. package/dist/cli/commands/fan-out.mjs +3 -1
  14. package/dist/cli/commands/fan-out.mjs.map +1 -1
  15. package/dist/cli/commands/features-audit.d.mts.map +1 -1
  16. package/dist/cli/commands/features-audit.mjs +6 -1
  17. package/dist/cli/commands/features-audit.mjs.map +1 -1
  18. package/dist/cli/commands/followups.d.mts +5 -0
  19. package/dist/cli/commands/followups.d.mts.map +1 -0
  20. package/dist/cli/commands/followups.mjs +266 -0
  21. package/dist/cli/commands/followups.mjs.map +1 -0
  22. package/dist/cli/commands/health-check.d.mts.map +1 -1
  23. package/dist/cli/commands/health-check.mjs +3 -1
  24. package/dist/cli/commands/health-check.mjs.map +1 -1
  25. package/dist/cli/commands/init.d.mts.map +1 -1
  26. package/dist/cli/commands/init.mjs +30 -1
  27. package/dist/cli/commands/init.mjs.map +1 -1
  28. package/dist/cli/commands/merge-detect.d.mts +29 -0
  29. package/dist/cli/commands/merge-detect.d.mts.map +1 -0
  30. package/dist/cli/commands/merge-detect.mjs +196 -0
  31. package/dist/cli/commands/merge-detect.mjs.map +1 -0
  32. package/dist/cli/commands/merge-integrate.d.mts +28 -0
  33. package/dist/cli/commands/merge-integrate.d.mts.map +1 -0
  34. package/dist/cli/commands/merge-integrate.mjs +383 -0
  35. package/dist/cli/commands/merge-integrate.mjs.map +1 -0
  36. package/dist/cli/commands/pr-review/detect-specialists.d.mts +58 -0
  37. package/dist/cli/commands/pr-review/detect-specialists.d.mts.map +1 -0
  38. package/dist/cli/commands/pr-review/detect-specialists.mjs +984 -0
  39. package/dist/cli/commands/pr-review/detect-specialists.mjs.map +1 -0
  40. package/dist/cli/commands/pr-review.d.mts.map +1 -1
  41. package/dist/cli/commands/pr-review.mjs +11 -2
  42. package/dist/cli/commands/pr-review.mjs.map +1 -1
  43. package/dist/cli/commands/precompact-hook.d.mts.map +1 -1
  44. package/dist/cli/commands/precompact-hook.mjs +10 -6
  45. package/dist/cli/commands/precompact-hook.mjs.map +1 -1
  46. package/dist/cli/commands/roadmap.d.mts.map +1 -1
  47. package/dist/cli/commands/roadmap.mjs +24 -11
  48. package/dist/cli/commands/roadmap.mjs.map +1 -1
  49. package/dist/cli/commands/seed.d.mts.map +1 -1
  50. package/dist/cli/commands/seed.mjs +3 -1
  51. package/dist/cli/commands/seed.mjs.map +1 -1
  52. package/dist/cli/commands/state.d.mts.map +1 -1
  53. package/dist/cli/commands/state.mjs +9 -3
  54. package/dist/cli/commands/state.mjs.map +1 -1
  55. package/dist/cli/commands/statusline.d.mts.map +1 -1
  56. package/dist/cli/commands/statusline.mjs +5 -1
  57. package/dist/cli/commands/statusline.mjs.map +1 -1
  58. package/dist/cli/commands/task.d.mts.map +1 -1
  59. package/dist/cli/commands/task.mjs +126 -23
  60. package/dist/cli/commands/task.mjs.map +1 -1
  61. package/dist/cli/commands/worktree.d.mts +1 -1
  62. package/dist/cli/commands/worktree.d.mts.map +1 -1
  63. package/dist/cli/commands/worktree.mjs +282 -83
  64. package/dist/cli/commands/worktree.mjs.map +1 -1
  65. package/dist/cli/config/post-merge-sweep.d.mts.map +1 -1
  66. package/dist/cli/config/post-merge-sweep.mjs +3 -2
  67. package/dist/cli/config/post-merge-sweep.mjs.map +1 -1
  68. package/dist/cli/config/share-policy.d.mts +3 -1
  69. package/dist/cli/config/share-policy.d.mts.map +1 -1
  70. package/dist/cli/config/share-policy.mjs +16 -3
  71. package/dist/cli/config/share-policy.mjs.map +1 -1
  72. package/dist/cli/config/upgrade-gitignore.d.mts.map +1 -1
  73. package/dist/cli/config/upgrade-gitignore.mjs +27 -5
  74. package/dist/cli/config/upgrade-gitignore.mjs.map +1 -1
  75. package/dist/cli/consistency/parse-qa.mjs +12 -1
  76. package/dist/cli/consistency/parse-qa.mjs.map +1 -1
  77. package/dist/cli/consistency/parse-review.d.mts +23 -0
  78. package/dist/cli/consistency/parse-review.d.mts.map +1 -1
  79. package/dist/cli/consistency/parse-review.mjs +36 -0
  80. package/dist/cli/consistency/parse-review.mjs.map +1 -1
  81. package/dist/cli/consistency/parse-spec.d.mts +16 -0
  82. package/dist/cli/consistency/parse-spec.d.mts.map +1 -1
  83. package/dist/cli/consistency/parse-spec.mjs +44 -0
  84. package/dist/cli/consistency/parse-spec.mjs.map +1 -1
  85. package/dist/cli/coordination/agent-identity.d.mts +5 -2
  86. package/dist/cli/coordination/agent-identity.d.mts.map +1 -1
  87. package/dist/cli/coordination/agent-identity.mjs +6 -4
  88. package/dist/cli/coordination/agent-identity.mjs.map +1 -1
  89. package/dist/cli/coordination/store.d.mts +1 -1
  90. package/dist/cli/dispatch.d.mts.map +1 -1
  91. package/dist/cli/dispatch.mjs +4 -0
  92. package/dist/cli/dispatch.mjs.map +1 -1
  93. package/dist/cli/followups/derive.d.mts +63 -0
  94. package/dist/cli/followups/derive.d.mts.map +1 -0
  95. package/dist/cli/followups/derive.mjs +115 -0
  96. package/dist/cli/followups/derive.mjs.map +1 -0
  97. package/dist/cli/followups/schema.d.mts +165 -0
  98. package/dist/cli/followups/schema.d.mts.map +1 -0
  99. package/dist/cli/followups/schema.mjs +251 -0
  100. package/dist/cli/followups/schema.mjs.map +1 -0
  101. package/dist/cli/gate/context.d.mts +1 -1
  102. package/dist/cli/gate/gates/followups-adjudicated.d.mts +69 -0
  103. package/dist/cli/gate/gates/followups-adjudicated.d.mts.map +1 -0
  104. package/dist/cli/gate/gates/followups-adjudicated.mjs +141 -0
  105. package/dist/cli/gate/gates/followups-adjudicated.mjs.map +1 -0
  106. package/dist/cli/gate/gates/index.mjs +1 -0
  107. package/dist/cli/gate/gates/index.mjs.map +1 -1
  108. package/dist/cli/git/materialize-env.d.mts +39 -0
  109. package/dist/cli/git/materialize-env.d.mts.map +1 -0
  110. package/dist/cli/git/materialize-env.mjs +217 -0
  111. package/dist/cli/git/materialize-env.mjs.map +1 -0
  112. package/dist/cli/help.d.mts.map +1 -1
  113. package/dist/cli/help.mjs +9 -1
  114. package/dist/cli/help.mjs.map +1 -1
  115. package/dist/cli/install/legacy-paths.d.mts.map +1 -1
  116. package/dist/cli/install/legacy-paths.mjs +5 -0
  117. package/dist/cli/install/legacy-paths.mjs.map +1 -1
  118. package/dist/cli/roadmap/lifecycle.d.mts.map +1 -1
  119. package/dist/cli/roadmap/lifecycle.mjs +4 -1
  120. package/dist/cli/roadmap/lifecycle.mjs.map +1 -1
  121. package/dist/cli/task/ids.d.mts +9 -0
  122. package/dist/cli/task/ids.d.mts.map +1 -1
  123. package/dist/cli/task/ids.mjs +9 -0
  124. package/dist/cli/task/ids.mjs.map +1 -1
  125. package/dist/cli/task/index-md.d.mts +5 -0
  126. package/dist/cli/task/index-md.d.mts.map +1 -1
  127. package/dist/cli/task/index-md.mjs +12 -4
  128. package/dist/cli/task/index-md.mjs.map +1 -1
  129. package/dist/cli/util/io.d.mts +6 -0
  130. package/dist/cli/util/io.d.mts.map +1 -1
  131. package/dist/cli/util/io.mjs +16 -1
  132. package/dist/cli/util/io.mjs.map +1 -1
  133. package/dist/cli/util/project.d.mts +7 -2
  134. package/dist/cli/util/project.d.mts.map +1 -1
  135. package/dist/cli/util/project.mjs +11 -14
  136. package/dist/cli/util/project.mjs.map +1 -1
  137. package/dist/cli/util/state-paths.d.mts +19 -0
  138. package/dist/cli/util/state-paths.d.mts.map +1 -0
  139. package/dist/cli/util/state-paths.mjs +45 -0
  140. package/dist/cli/util/state-paths.mjs.map +1 -0
  141. package/dist/plugin/.claude-plugin/plugin.json +4 -1
  142. package/dist/plugin/agents/apt-pr-review-crossplatform.md +189 -0
  143. package/dist/plugin/agents/apt-pr-review-i18n.md +190 -0
  144. package/dist/plugin/skills/apt/SKILL.md +9 -3
  145. package/dist/plugin/skills/apt-close-task/SKILL.md +48 -0
  146. package/dist/plugin/skills/apt-debug/SKILL.md +19 -9
  147. package/dist/plugin/skills/apt-execute/SKILL.md +3 -1
  148. package/dist/plugin/skills/apt-fan-out/SKILL.md +65 -1
  149. package/dist/plugin/skills/apt-merge-conflict-resolution/SKILL.md +227 -0
  150. package/dist/plugin/skills/apt-pr-review/SCHEMA.md +4 -2
  151. package/dist/plugin/skills/apt-pr-review/SKILL.md +117 -29
  152. package/dist/plugin/skills/apt-quick/SKILL.md +24 -1
  153. package/dist/plugin/skills/apt-review/SKILL.md +6 -0
  154. package/dist/plugin/skills/apt-run/SKILL.md +1 -1
  155. package/dist/plugin/skills/apt-setup/SKILL.md +21 -3
  156. package/dist/plugin/skills/apt-ship/SKILL.md +32 -11
  157. package/dist/plugin/skills/apt-terminal/SKILL.md +8 -0
  158. package/dist/plugin/skills/apt-triage/SKILL.md +4 -0
  159. package/dist/plugin/skills/apt-verify/SKILL.md +24 -0
  160. package/dist/plugin/skills/apt-watch-ci/SKILL.md +3 -1
  161. package/dist/types/config.d.ts +33 -0
  162. package/dist/types/config.d.ts.map +1 -1
  163. package/package.json +1 -1
  164. package/prompts/conductor-framework-context.md +1 -1
  165. package/prompts/github/pr_crossplatform_agent.md +283 -0
  166. package/prompts/github/pr_i18n_agent.md +279 -0
  167. package/prompts/inbox-clarification.md +18 -11
  168. package/skills/apt/SKILL.md +9 -3
  169. package/skills/apt-close-task/SKILL.md +48 -0
  170. package/skills/apt-debug/SKILL.md +19 -9
  171. package/skills/apt-execute/SKILL.md +3 -1
  172. package/skills/apt-fan-out/SKILL.md +65 -1
  173. package/skills/apt-merge-conflict-resolution/SKILL.md +227 -0
  174. package/skills/apt-pr-review/SCHEMA.md +4 -2
  175. package/skills/apt-pr-review/SKILL.md +117 -29
  176. package/skills/apt-quick/SKILL.md +24 -1
  177. package/skills/apt-review/SKILL.md +6 -0
  178. package/skills/apt-run/SKILL.md +1 -1
  179. package/skills/apt-setup/SKILL.md +21 -3
  180. package/skills/apt-ship/SKILL.md +32 -11
  181. package/skills/apt-terminal/SKILL.md +8 -0
  182. package/skills/apt-triage/SKILL.md +4 -0
  183. package/skills/apt-verify/SKILL.md +24 -0
  184. package/skills/apt-watch-ci/SKILL.md +3 -1
  185. package/src/cli/commands/audit-branch-current.mjs +3 -3
  186. package/src/cli/commands/event.mjs +6 -4
  187. package/src/cli/commands/fan-out.mjs +3 -1
  188. package/src/cli/commands/features-audit.mjs +6 -1
  189. package/src/cli/commands/followups.mjs +303 -0
  190. package/src/cli/commands/health-check.mjs +3 -1
  191. package/src/cli/commands/init.mjs +29 -0
  192. package/src/cli/commands/merge-detect.mjs +203 -0
  193. package/src/cli/commands/merge-integrate.mjs +392 -0
  194. package/src/cli/commands/pr-review/__fixtures__/android-strings/app/src/main/res/values-fr/strings.xml +3 -0
  195. package/src/cli/commands/pr-review/__fixtures__/babel-js/package.json +4 -0
  196. package/src/cli/commands/pr-review/__fixtures__/c-ifdef/src/main.c +4 -0
  197. package/src/cli/commands/pr-review/__fixtures__/ci-matrix/.github/workflows/ci.yml +10 -0
  198. package/src/cli/commands/pr-review/__fixtures__/cmake-win/CMakeLists.txt +5 -0
  199. package/src/cli/commands/pr-review/__fixtures__/diffs/add-arb.patch +9 -0
  200. package/src/cli/commands/pr-review/__fixtures__/diffs/ci-single-os-dep.patch +13 -0
  201. package/src/cli/commands/pr-review/__fixtures__/diffs/ci-single-os-list-item-dep.patch +17 -0
  202. package/src/cli/commands/pr-review/__fixtures__/diffs/lone-weak-t.patch +8 -0
  203. package/src/cli/commands/pr-review/__fixtures__/diffs/monorepo-b.patch +7 -0
  204. package/src/cli/commands/pr-review/__fixtures__/diffs/two-weak-xpl.patch +12 -0
  205. package/src/cli/commands/pr-review/__fixtures__/dropped-underscore/src/app.js +2 -0
  206. package/src/cli/commands/pr-review/__fixtures__/electron-app/package.json +4 -0
  207. package/src/cli/commands/pr-review/__fixtures__/i18n-locale-file/src/locales/fr.json +1 -0
  208. package/src/cli/commands/pr-review/__fixtures__/monorepo/packages/a/locales/fr.json +1 -0
  209. package/src/cli/commands/pr-review/__fixtures__/monorepo/packages/b/src/index.ts +1 -0
  210. package/src/cli/commands/pr-review/__fixtures__/py-babel/pyproject.toml +3 -0
  211. package/src/cli/commands/pr-review/__fixtures__/qt-i18n-dir/i18n/app_fr.ts +11 -0
  212. package/src/cli/commands/pr-review/__fixtures__/rust-cli/Cargo.toml +7 -0
  213. package/src/cli/commands/pr-review/__fixtures__/rust-cli/src/main.rs +1 -0
  214. package/src/cli/commands/pr-review/__fixtures__/ts-dir/ts/foo.ts +3 -0
  215. package/src/cli/commands/pr-review/detect-specialists.mjs +1029 -0
  216. package/src/cli/commands/pr-review.mjs +12 -2
  217. package/src/cli/commands/precompact-hook.mjs +10 -6
  218. package/src/cli/commands/roadmap.mjs +24 -11
  219. package/src/cli/commands/seed.mjs +3 -1
  220. package/src/cli/commands/state.mjs +9 -3
  221. package/src/cli/commands/statusline.mjs +5 -1
  222. package/src/cli/commands/task.mjs +135 -23
  223. package/src/cli/commands/worktree.mjs +298 -81
  224. package/src/cli/config/post-merge-sweep.mjs +3 -2
  225. package/src/cli/config/share-policy.mjs +16 -3
  226. package/src/cli/config/upgrade-gitignore.mjs +27 -5
  227. package/src/cli/consistency/parse-qa.mjs +11 -1
  228. package/src/cli/consistency/parse-review.mjs +36 -0
  229. package/src/cli/consistency/parse-spec.mjs +43 -0
  230. package/src/cli/coordination/agent-identity.mjs +6 -4
  231. package/src/cli/dispatch.mjs +4 -0
  232. package/src/cli/followups/derive.mjs +123 -0
  233. package/src/cli/followups/schema.mjs +264 -0
  234. package/src/cli/gate/gates/followups-adjudicated.mjs +158 -0
  235. package/src/cli/gate/gates/index.mjs +1 -0
  236. package/src/cli/git/materialize-env.mjs +212 -0
  237. package/src/cli/help.mjs +9 -1
  238. package/src/cli/install/legacy-paths.mjs +5 -0
  239. package/src/cli/roadmap/lifecycle.mjs +4 -1
  240. package/src/cli/task/ids.mjs +9 -0
  241. package/src/cli/task/index-md.mjs +12 -4
  242. package/src/cli/util/io.mjs +15 -1
  243. package/src/cli/util/project.mjs +11 -14
  244. package/src/cli/util/state-paths.mjs +45 -0
  245. package/prompts/followup_planner.md +0 -399
@@ -0,0 +1,383 @@
1
+ /**
2
+ * commands/merge-integrate.mjs — `apt-tools merge integrate` (GitHub issue #397).
3
+ *
4
+ * The fan-out PR-integration phase. After a fan-out run, N workers each opened
5
+ * an independent PR against the same base; nothing ordered or reconciled them.
6
+ * This command SERIALIZES that batch: it resolves the task → branch/PR set,
7
+ * computes a deterministic merge order (topological when `depends_on` edges
8
+ * exist, else stable by task id), and probes each branch's mergeability against
9
+ * the CURRENT base via the EXACT same `git merge-tree` detection the `detect`
10
+ * subcommand uses (#395). It emits a structured integration report the
11
+ * conductor renders and the `apt:merge-conflict-resolution` flow consumes for
12
+ * any conflicted branch.
13
+ *
14
+ * READ-ONLY, like `detect`/`markers`: NO `git merge`, NO `git rebase`, NO index
15
+ * mutation, NO `gh pr merge`. The SKILL.md prose owns every state change; this
16
+ * primitive only OBSERVES and ORDERS. Conflict resolution stays agent-inline —
17
+ * the framework CLI is self-contained ESM and MUST NOT import @aperant/core
18
+ * (see GitHub issue #390 for the architecture decision). When a branch probes
19
+ * `mergeable: false`, this command surfaces the conflict envelope (the worker's
20
+ * worktree + base ref + conflicts_preview) and the agent resolves it inline via
21
+ * `/apt:merge-conflict-resolution`, then re-runs `integrate` to re-probe.
22
+ *
23
+ * Reuse, not reinvent (#397):
24
+ * - detection → `gitTry` / `parseConflictPaths` / `isGitVersionTooOld`
25
+ * promoted in audit-branch-current.mjs (#395).
26
+ * - batch resolution → the same `--tasks`/`--batch` shape fan-out.mjs uses;
27
+ * task records read from the shared `.aperant/state.json`
28
+ * board (resolveMainRepoRoot — state-fork-fix).
29
+ * - dependency graph → `buildDependsOnMap` (graph/depends-on.mjs), which
30
+ * already normalizes `depends_on` edges to bare ids.
31
+ *
32
+ * apt-tools merge integrate <project-dir> --tasks <id,id,...> [--base <ref>]
33
+ * apt-tools merge integrate <project-dir> --batch <batch-id> [--base <ref>]
34
+ *
35
+ * --tasks Explicit canonical task-id list (what `fan-out resolve` returns and
36
+ * the conductor already holds). Primary form.
37
+ * --batch Recover the task-id list from the batch's worker rows
38
+ * `.aperant/team-status/fanout-<batch>-<task>.json` (the agent-id
39
+ * format fan-out.mjs spawns workers with: `fanout-${batch}-${task}`).
40
+ * --base Override the base ref every branch is probed against. When omitted,
41
+ * each task is probed against `origin/<its base_branch>` (falling back
42
+ * to `origin/main` when the record carries no base_branch).
43
+ *
44
+ * @internal
45
+ */
46
+ import { existsSync, readdirSync, readFileSync } from 'node:fs';
47
+ import { join, resolve } from 'node:path';
48
+ import { buildDependsOnMap } from '../graph/depends-on.mjs';
49
+ import { parseFlags } from '../util/args.mjs';
50
+ import { err, ok } from '../util/result.mjs';
51
+ import { resolveMainRepoRoot } from '../util/state-paths.mjs';
52
+ import { gitTry, isGitVersionTooOld, parseConflictPaths } from './audit-branch-current.mjs';
53
+ /**
54
+ * Parse a PR number from a `pr_url` string (full GitHub URL or `#123` short
55
+ * form). Returns null if not parseable. Incidental metadata for the report —
56
+ * the integration order/probe never depends on it.
57
+ *
58
+ * @param {unknown} url
59
+ * @returns {number | null}
60
+ */
61
+ function extractPrNumber(url) {
62
+ if (typeof url !== 'string')
63
+ return null;
64
+ const m1 = url.match(/\/pull\/(\d+)(?:[?#/]|$)/);
65
+ if (m1)
66
+ return Number.parseInt(m1[1], 10);
67
+ const m2 = url.match(/#(\d+)$/);
68
+ if (m2)
69
+ return Number.parseInt(m2[1], 10);
70
+ const m3 = url.match(/^(\d+)$/);
71
+ if (m3)
72
+ return Number.parseInt(m3[1], 10);
73
+ return null;
74
+ }
75
+ /**
76
+ * Read the shared ephemeral board (`state.json`). Anchors to the MAIN repo so
77
+ * it resolves identically from any worktree (state-fork-fix), matching how
78
+ * fan-out.mjs reads it.
79
+ *
80
+ * @param {string} targetDir
81
+ * @returns {object | null}
82
+ */
83
+ function readState(targetDir) {
84
+ const statePath = join(resolveMainRepoRoot(targetDir), '.aperant', 'state.json');
85
+ if (!existsSync(statePath))
86
+ return null;
87
+ try {
88
+ return JSON.parse(readFileSync(statePath, 'utf-8'));
89
+ }
90
+ catch {
91
+ return null;
92
+ }
93
+ }
94
+ /**
95
+ * Recover a batch's task-id list from its worker rows. Fan-out spawns each
96
+ * worker with `APT_AGENT_ID=fanout-<batch>-<task>`, which materializes one
97
+ * `.aperant/team-status/fanout-<batch>-<task>.json` per worker. We list that
98
+ * directory and strip the shared prefix to recover the task ids.
99
+ *
100
+ * @param {string} targetDir
101
+ * @param {string} batchId
102
+ * @returns {string[]}
103
+ */
104
+ function recoverBatchTaskIds(targetDir, batchId) {
105
+ const statusDir = join(resolveMainRepoRoot(targetDir), '.aperant', 'team-status');
106
+ if (!existsSync(statusDir))
107
+ return [];
108
+ const prefix = `fanout-${batchId}-`;
109
+ const ids = [];
110
+ let entries;
111
+ try {
112
+ entries = readdirSync(statusDir);
113
+ }
114
+ catch {
115
+ return [];
116
+ }
117
+ for (const name of entries) {
118
+ if (!name.endsWith('.json'))
119
+ continue;
120
+ const base = name.slice(0, -'.json'.length);
121
+ if (base.startsWith(prefix)) {
122
+ const taskId = base.slice(prefix.length);
123
+ if (taskId)
124
+ ids.push(taskId);
125
+ }
126
+ }
127
+ return ids;
128
+ }
129
+ /**
130
+ * Order the batch's task ids. Topological (Kahn) over the batch SUBSET when any
131
+ * member has a `depends_on` edge to another member, else a stable alphabetical
132
+ * sort by task id (deterministic). A dependency cycle (or any unresolvable
133
+ * ordering) falls back to the stable order with a warning rather than throwing.
134
+ *
135
+ * Reuses `buildDependsOnMap` so the edge normalization matches the rest of the
136
+ * framework's graph handling exactly.
137
+ *
138
+ * @param {string[]} taskIds
139
+ * @param {object | null} state
140
+ * @returns {{ order: string[], strategy: 'topological' | 'stable', warnings: string[] }}
141
+ */
142
+ function orderTasks(taskIds, state) {
143
+ const stable = [...taskIds].sort();
144
+ const inBatch = new Set(taskIds);
145
+ const warnings = [];
146
+ const depMap = buildDependsOnMap(state || {});
147
+ // Restrict edges to within-batch dependencies — cross-batch deps don't gate
148
+ // the integration order (those PRs aren't part of this serialization).
149
+ const deps = new Map();
150
+ let edgeCount = 0;
151
+ for (const id of taskIds) {
152
+ // buildDependsOnMap already normalizes deps to bare task_ids, so the
153
+ // entries are consumed directly (no re-parse — a re-parse would split a
154
+ // colon-bearing id and drop the constraint). De-dupe so a task that lists
155
+ // the same dependency twice doesn't overcount its Kahn indegree (which
156
+ // would strand it below zero-indegree and mis-flag a valid graph as a
157
+ // cycle).
158
+ const within = [];
159
+ const seen = new Set();
160
+ for (const depId of depMap.get(id) || []) {
161
+ if (inBatch.has(depId) && depId !== id && !seen.has(depId)) {
162
+ seen.add(depId);
163
+ within.push(depId);
164
+ edgeCount++;
165
+ }
166
+ }
167
+ deps.set(id, within);
168
+ }
169
+ if (edgeCount === 0) {
170
+ return { order: stable, strategy: 'stable', warnings };
171
+ }
172
+ // Kahn's algorithm — a task lands AFTER its dependencies (deps merge first).
173
+ const indegree = new Map(taskIds.map((id) => [id, 0]));
174
+ for (const id of taskIds) {
175
+ for (const _dep of deps.get(id) || []) {
176
+ indegree.set(id, (indegree.get(id) || 0) + 1);
177
+ }
178
+ }
179
+ // Ready = zero remaining unmet deps. Process in stable order for determinism.
180
+ const ready = taskIds.filter((id) => (indegree.get(id) || 0) === 0).sort();
181
+ const order = [];
182
+ while (ready.length > 0) {
183
+ const id = ready.shift();
184
+ order.push(id);
185
+ // Decrement indegree of every task that depends on `id`.
186
+ const newlyReady = [];
187
+ for (const other of taskIds) {
188
+ if ((deps.get(other) || []).includes(id)) {
189
+ const d = (indegree.get(other) || 0) - 1;
190
+ indegree.set(other, d);
191
+ if (d === 0)
192
+ newlyReady.push(other);
193
+ }
194
+ }
195
+ for (const r of newlyReady)
196
+ ready.push(r);
197
+ // Re-sort the whole queue (not just the newly-ready tail) so ties across
198
+ // rounds resolve in strict alphabetical order — fully deterministic.
199
+ ready.sort();
200
+ }
201
+ if (order.length !== taskIds.length) {
202
+ // Cycle or unresolvable ordering — fall back to stable, deterministic.
203
+ warnings.push('dependency cycle detected among batch tasks — falling back to stable task-id order');
204
+ return { order: stable, strategy: 'stable', warnings };
205
+ }
206
+ return { order, strategy: 'topological', warnings };
207
+ }
208
+ /**
209
+ * Probe one task's branch against the base. Mirrors the `detect` subcommand's
210
+ * merge-tree semantics exactly (#395) — same gitTry call, same exit-code
211
+ * branching — so the integrate report and a standalone `merge detect` agree.
212
+ *
213
+ * @param {string} worktreeDir
214
+ * @param {string} base
215
+ * @param {string} head
216
+ * @returns {{ mergeable: boolean | null, conflicts_preview?: string[], audit_warning?: string }}
217
+ */
218
+ function probeBranch(worktreeDir, base, head) {
219
+ const probe = gitTry(worktreeDir, ['merge-tree', '--write-tree', '--no-messages', head, base]);
220
+ if (probe.ok)
221
+ return { mergeable: true };
222
+ if (probe.code === 1 && probe.stdout.trim().length > 0) {
223
+ return { mergeable: false, conflicts_preview: parseConflictPaths(probe.stdout) };
224
+ }
225
+ if (isGitVersionTooOld(probe.stderr)) {
226
+ return { mergeable: null, audit_warning: 'git-version-too-old' };
227
+ }
228
+ return { mergeable: null, audit_warning: 'merge-tree-probe-failed' };
229
+ }
230
+ /**
231
+ * `apt-tools merge integrate` — serialize a fan-out batch into base.
232
+ *
233
+ * @param {string} targetDir
234
+ * @param {string[]} extraArgs
235
+ * @returns {import('../util/result.mjs').CommandResult}
236
+ */
237
+ export function mergeIntegrate(targetDir, extraArgs) {
238
+ const flags = parseFlags(extraArgs);
239
+ const hasTasks = flags.has('tasks');
240
+ const hasBatch = flags.has('batch');
241
+ if (!hasTasks && !hasBatch) {
242
+ return err('One of --tasks <id,id,...> or --batch <batch-id> is required for merge integrate');
243
+ }
244
+ if (hasTasks && hasBatch) {
245
+ return err('Pass only one of --tasks or --batch to merge integrate');
246
+ }
247
+ const state = readState(targetDir);
248
+ let taskIds = [];
249
+ let source = '';
250
+ if (hasTasks) {
251
+ taskIds = String(flags.get('tasks') || '')
252
+ .split(',')
253
+ .map((s) => s.trim())
254
+ .filter(Boolean);
255
+ source = 'tasks';
256
+ if (taskIds.length === 0) {
257
+ return err('--tasks resolved to an empty list');
258
+ }
259
+ }
260
+ else {
261
+ const batchId = String(flags.get('batch') || '').trim();
262
+ if (!batchId)
263
+ return err('--batch requires a batch id');
264
+ taskIds = recoverBatchTaskIds(targetDir, batchId);
265
+ source = 'batch';
266
+ if (taskIds.length === 0) {
267
+ return err(`No worker rows found for batch "${batchId}" — nothing to integrate`);
268
+ }
269
+ }
270
+ // parseFlags yields `true` for a bare `--base` with no following value; reject
271
+ // it rather than silently probing against the ref literal "true".
272
+ let baseOverride = null;
273
+ if (flags.has('base')) {
274
+ const raw = flags.get('base');
275
+ if (raw === true || String(raw).trim() === '') {
276
+ return err('--base requires a ref value (e.g. --base origin/main)');
277
+ }
278
+ baseOverride = String(raw).trim();
279
+ }
280
+ const { order, strategy, warnings } = orderTasks(taskIds, state);
281
+ const tasks = [];
282
+ let merged = 0;
283
+ let conflicted = 0;
284
+ let needsHuman = 0;
285
+ for (const id of order) {
286
+ const task = state?.active_tasks?.[id] || null;
287
+ // Tasks with no shipped branch can't be integrated — record them so the
288
+ // conductor sees why, but never block the rest of the batch.
289
+ if (!task) {
290
+ needsHuman++;
291
+ tasks.push({ task_id: id, status: 'unknown', reason: 'task not found in state.json' });
292
+ continue;
293
+ }
294
+ const branch = typeof task.branch === 'string' ? task.branch : null;
295
+ const prUrl = typeof task.pr_url === 'string' ? task.pr_url : null;
296
+ const worktree = typeof task.worktree_path === 'string' && existsSync(task.worktree_path)
297
+ ? resolve(task.worktree_path)
298
+ : null;
299
+ if (!prUrl) {
300
+ needsHuman++;
301
+ tasks.push({
302
+ task_id: id,
303
+ branch,
304
+ status: 'skipped',
305
+ reason: 'no pr_url on task record (not shipped) — nothing to integrate',
306
+ });
307
+ continue;
308
+ }
309
+ if (!worktree) {
310
+ needsHuman++;
311
+ tasks.push({
312
+ task_id: id,
313
+ branch,
314
+ pr_url: prUrl,
315
+ pr_number: extractPrNumber(prUrl),
316
+ status: 'needs-human',
317
+ reason: 'worktree_path missing or no longer on disk — cannot probe mergeability',
318
+ });
319
+ continue;
320
+ }
321
+ const taskBaseBranch = typeof task.base_branch === 'string' && task.base_branch ? task.base_branch : 'main';
322
+ const base = baseOverride || `origin/${taskBaseBranch}`;
323
+ const probe = probeBranch(worktree, base, 'HEAD');
324
+ const entry = {
325
+ task_id: id,
326
+ branch,
327
+ pr_url: prUrl,
328
+ pr_number: extractPrNumber(prUrl),
329
+ worktree_path: worktree,
330
+ base,
331
+ mergeable: probe.mergeable,
332
+ };
333
+ if (probe.conflicts_preview && probe.conflicts_preview.length > 0) {
334
+ entry.conflicts_preview = probe.conflicts_preview;
335
+ }
336
+ if (probe.audit_warning)
337
+ entry.audit_warning = probe.audit_warning;
338
+ if (probe.mergeable === true) {
339
+ merged++;
340
+ entry.status = 'mergeable';
341
+ entry.action = 'advance';
342
+ }
343
+ else if (probe.mergeable === false) {
344
+ conflicted++;
345
+ entry.status = 'conflicted';
346
+ // Hand-off envelope for /apt:merge-conflict-resolution — the agent
347
+ // resolves these markers inline then re-runs `integrate` to re-probe.
348
+ entry.action = 'resolve-conflict';
349
+ entry.resolution = {
350
+ skill: 'apt:merge-conflict-resolution',
351
+ worktree_path: worktree,
352
+ base,
353
+ };
354
+ }
355
+ else {
356
+ needsHuman++;
357
+ entry.status = 'needs-human';
358
+ entry.action = 'probe-failed';
359
+ }
360
+ tasks.push(entry);
361
+ }
362
+ const envelope = {
363
+ status: 'ok',
364
+ command: 'merge-integrate',
365
+ projectDir: targetDir,
366
+ source,
367
+ order_strategy: strategy,
368
+ order,
369
+ summary: {
370
+ total: tasks.length,
371
+ mergeable: merged,
372
+ conflicted,
373
+ needs_human: needsHuman,
374
+ },
375
+ tasks,
376
+ };
377
+ if (warnings.length > 0)
378
+ envelope.warnings = warnings;
379
+ return ok(envelope);
380
+ }
381
+ // Test-only export for unit-level coverage of the ordering core.
382
+ export { orderTasks as __orderTasksForTests };
383
+ //# sourceMappingURL=merge-integrate.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"merge-integrate.mjs","sourceRoot":"","sources":["../../../src/cli/commands/merge-integrate.mjs"],"names":[],"mappings":"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"}
@@ -0,0 +1,58 @@
1
+ /**
2
+ * Parse a unified diff into { touchedPaths: string[], addedText: string }.
3
+ * `touchedPaths` are POSIX-normalized repo-relative paths from the `+++ b/...`
4
+ * (and `--- a/...` for deletions) headers; `addedText` concatenates added
5
+ * lines (those starting with `+` but not `+++`).
6
+ *
7
+ * @param {string} patch
8
+ * @returns {{ touchedPaths: string[], addedText: string }}
9
+ */
10
+ export function parseUnifiedDiff(patch: string): {
11
+ touchedPaths: string[];
12
+ addedText: string;
13
+ };
14
+ /**
15
+ * The core detector. Pure over its inputs (reads the filesystem at `root` and
16
+ * the supplied `diffText`); returns the full
17
+ * `{ i18n, crossplatform }` decision object.
18
+ *
19
+ * @param {object} opts
20
+ * @param {string} opts.root absolute project dir
21
+ * @param {string} [opts.diffText] unified-diff text (optional)
22
+ * @param {'repo'|'diff'} [opts.scope] default 'repo'
23
+ * @param {{i18n?:string, crossplatform?:string}} [opts.config] override config
24
+ * (read from .aperant/config.json when omitted)
25
+ * @returns {{ i18n: SpecialistDecision, crossplatform: SpecialistDecision }}
26
+ */
27
+ export function detectSpecialists(opts: {
28
+ root: string;
29
+ diffText?: string | undefined;
30
+ scope?: "diff" | "repo" | undefined;
31
+ config?: {
32
+ i18n?: string;
33
+ crossplatform?: string;
34
+ } | undefined;
35
+ }): {
36
+ i18n: SpecialistDecision;
37
+ crossplatform: SpecialistDecision;
38
+ };
39
+ /**
40
+ * CLI subcommand handler: `apt-tools pr-review detect-specialists <dir>
41
+ * [--diff <patch>] [--scope diff|repo]`.
42
+ *
43
+ * @param {string} projectDir
44
+ * @param {string[]} extraArgs
45
+ * @returns {import('../../util/result.mjs').CommandResult}
46
+ */
47
+ export function cmdDetectSpecialists(projectDir: string, extraArgs?: string[]): import("../../util/result.mjs").CommandResult;
48
+ export type Signal = {
49
+ kind: string;
50
+ detail: string;
51
+ tier: "strong" | "weak";
52
+ };
53
+ export type SpecialistDecision = {
54
+ engage: boolean;
55
+ signals: Signal[];
56
+ reason: string;
57
+ };
58
+ //# sourceMappingURL=detect-specialists.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"detect-specialists.d.mts","sourceRoot":"","sources":["../../../../src/cli/commands/pr-review/detect-specialists.mjs"],"names":[],"mappings":"AAijBA;;;;;;;;GAQG;AACH,wCAHW,MAAM,GACJ;IAAE,YAAY,EAAE,MAAM,EAAE,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,CAwBzD;AAwKD;;;;;;;;;;;;GAYG;AACH,wCAPG;IAAqB,IAAI,EAAjB,MAAM;IACQ,QAAQ;IACD,KAAK;IACmB,MAAM;eAA5C,MAAM;wBAAiB,MAAM;;CAE5C,GAAU;IAAE,IAAI,EAAE,kBAAkB,CAAC;IAAC,aAAa,EAAE,kBAAkB,CAAA;CAAE,CAmC3E;AA0LD;;;;;;;GAOG;AACH,iDAJW,MAAM,cACN,MAAM,EAAE,GACN,OAAO,uBAAuB,EAAE,aAAa,CA8BzD;qBA39BY;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,QAAQ,GAAG,MAAM,CAAA;CAAE;iCAEzD;IAAE,MAAM,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,MAAM,EAAE,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE"}