@developerz.ai/aitm 0.0.2 → 0.0.3

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 (83) hide show
  1. package/dist/agent-config/agent-config-detector.js +0 -3
  2. package/dist/cli/args.js +0 -10
  3. package/dist/cli/cli.js +0 -8
  4. package/dist/cli/commands.js +0 -46
  5. package/dist/compaction/compactor.js +0 -24
  6. package/dist/config/config-loader.js +0 -29
  7. package/dist/config/config-writer.js +0 -5
  8. package/dist/config/schema.js +0 -12
  9. package/dist/credentials/credentials.js +0 -14
  10. package/dist/credentials/defaults.js +0 -15
  11. package/dist/fs/atomic-write.js +0 -7
  12. package/dist/github/errors.js +0 -2
  13. package/dist/github/github-client.js +0 -21
  14. package/dist/github/schema.js +0 -2
  15. package/dist/index.js +0 -2
  16. package/dist/logger/logger.js +0 -6
  17. package/dist/loop/run-loop-adapter.js +0 -45
  18. package/dist/loop/take-over-flow.js +0 -37
  19. package/dist/loop/work-loop.js +0 -36
  20. package/dist/mcp/mcp-client.js +0 -13
  21. package/dist/mcp/schema.js +0 -15
  22. package/dist/openrouter/client.js +0 -4
  23. package/dist/openrouter/model-limits.js +0 -4
  24. package/dist/openrouter/server-tools.js +0 -16
  25. package/dist/orchestrator/orchestrator.js +0 -26
  26. package/dist/orchestrator/subagent-tools.js +0 -22
  27. package/dist/orchestrator/system-prompts.js +0 -3
  28. package/dist/plan/plan-graph.js +0 -9
  29. package/dist/plan/schema.js +0 -8
  30. package/dist/state/schema.js +0 -5
  31. package/dist/state/state-store.js +0 -8
  32. package/dist/subagents/factory.js +0 -9
  33. package/dist/subagents/planner.js +0 -9
  34. package/dist/subagents/reviewer.js +0 -25
  35. package/dist/subagents/worker.js +0 -27
  36. package/dist/testing/temp-repo.js +0 -4
  37. package/dist/tools/datetime.js +0 -9
  38. package/dist/tools/fetch-html.js +0 -24
  39. package/dist/tools/github-thread-tool.js +0 -15
  40. package/dist/tools/web-fetch.js +0 -32
  41. package/dist/workspace/worktree-pool.js +0 -21
  42. package/package.json +2 -2
  43. package/dist/agent-config/agent-config-detector.js.map +0 -1
  44. package/dist/cli/args.js.map +0 -1
  45. package/dist/cli/cli.js.map +0 -1
  46. package/dist/cli/commands.js.map +0 -1
  47. package/dist/compaction/compactor.js.map +0 -1
  48. package/dist/config/config-loader.js.map +0 -1
  49. package/dist/config/config-writer.js.map +0 -1
  50. package/dist/config/schema.js.map +0 -1
  51. package/dist/credentials/credentials.js.map +0 -1
  52. package/dist/credentials/defaults.js.map +0 -1
  53. package/dist/fs/atomic-write.js.map +0 -1
  54. package/dist/github/errors.js.map +0 -1
  55. package/dist/github/github-client.js.map +0 -1
  56. package/dist/github/schema.js.map +0 -1
  57. package/dist/index.js.map +0 -1
  58. package/dist/logger/logger.js.map +0 -1
  59. package/dist/loop/run-loop-adapter.js.map +0 -1
  60. package/dist/loop/take-over-flow.js.map +0 -1
  61. package/dist/loop/work-loop.js.map +0 -1
  62. package/dist/mcp/mcp-client.js.map +0 -1
  63. package/dist/mcp/schema.js.map +0 -1
  64. package/dist/openrouter/client.js.map +0 -1
  65. package/dist/openrouter/model-limits.js.map +0 -1
  66. package/dist/openrouter/server-tools.js.map +0 -1
  67. package/dist/orchestrator/orchestrator.js.map +0 -1
  68. package/dist/orchestrator/subagent-tools.js.map +0 -1
  69. package/dist/orchestrator/system-prompts.js.map +0 -1
  70. package/dist/plan/plan-graph.js.map +0 -1
  71. package/dist/plan/schema.js.map +0 -1
  72. package/dist/state/schema.js.map +0 -1
  73. package/dist/state/state-store.js.map +0 -1
  74. package/dist/subagents/factory.js.map +0 -1
  75. package/dist/subagents/planner.js.map +0 -1
  76. package/dist/subagents/reviewer.js.map +0 -1
  77. package/dist/subagents/worker.js.map +0 -1
  78. package/dist/testing/temp-repo.js.map +0 -1
  79. package/dist/tools/datetime.js.map +0 -1
  80. package/dist/tools/fetch-html.js.map +0 -1
  81. package/dist/tools/github-thread-tool.js.map +0 -1
  82. package/dist/tools/web-fetch.js.map +0 -1
  83. package/dist/workspace/worktree-pool.js.map +0 -1
@@ -1,17 +1,3 @@
1
- // Production wiring for `aitm start`. Composes the WorkLoop's structural ports out of the
2
- // real Planner, Orchestrator, WorktreePool, PlanGraph, MCP tools and GitHubClient.
3
- //
4
- // Symmetric counterpart to the merge-pr flow: `runStart` injects this as its `runLoop` seam
5
- // (see src/cli/commands.ts `defaultRunLoop`). Every external dependency is reachable through
6
- // a seam so the four WorkLoopResult branches are unit-testable without spawning subagents,
7
- // git, or `gh` — the integration suite (test/integration/) covers the real stack.
8
- //
9
- // Flow:
10
- // 1. Resume detection — if state already holds prGroups, reuse them; else run the Planner.
11
- // 2. Persist the plan into RunState (status → working).
12
- // 3. Build a *live* PlanGraph that re-reads the mirrored prGroups on every ready()/isComplete()
13
- // so status transitions written by the loop are visible (PlanGraph snapshots at construction).
14
- // 4. Bridge the Orchestrator/subagents into the WorkLoopOrchestrator port and run the loop.
15
1
  import { resolve as resolvePath } from 'node:path';
16
2
  import { bashTool, composeSystemPrompt, editFileTool, globTool, grepTool, multiEditTool, readFileTool, writeFileTool, } from '@developerz.ai/ai-claude-compat';
17
3
  import { tool } from 'ai';
@@ -25,10 +11,6 @@ import { createReviewerAgent, REVIEWER_SYSTEM_PREFIX, runReviewer as runReviewer
25
11
  import { createWorkerAgent, runWorker as runWorkerSubagent, WORKER_SYSTEM_PREFIX, } from "../subagents/worker.js";
26
12
  import { WorktreePool } from "../workspace/worktree-pool.js";
27
13
  import { WorkLoop, } from "./work-loop.js";
28
- // Worktree-scoped Claude-Code-style tools the Worker/Reviewer fall back to when no MCP server
29
- // supplies them. aitm is MCP-first, but a bare `aitm start` (no `mcpServers` configured) must
30
- // still be able to read, search, edit, commit and open a PR — so it uses the compat lib's
31
- // tools, scoped to the active worktree.
32
14
  export function localEditTools(cwd) {
33
15
  return {
34
16
  readFile: readFileTool({ cwd }),
@@ -40,7 +22,6 @@ export function localEditTools(cwd) {
40
22
  bash: bashTool({ cwd }),
41
23
  };
42
24
  }
43
- // Read-only subset for the Planner — survey the repo without write/edit/bash.
44
25
  export function localReadTools(cwd) {
45
26
  return {
46
27
  readFile: readFileTool({ cwd }),
@@ -50,8 +31,6 @@ export function localReadTools(cwd) {
50
31
  }
51
32
  export async function runLoopAdapter(input, seams = {}) {
52
33
  const state = seams.state ?? input.state;
53
- // MCP is only needed when a real Planner / Orchestrator default runs. When both are stubbed
54
- // (unit tests), we never connect, so no transport is spawned.
55
34
  const usesMcp = !seams.planGroups || !seams.makeOrchestrator;
56
35
  const mcp = seams.makeMcp
57
36
  ? seams.makeMcp(input)
@@ -64,7 +43,6 @@ export async function runLoopAdapter(input, seams = {}) {
64
43
  }
65
44
  const current = await state.read();
66
45
  const rollingContext = (await state.readContext?.()) ?? '';
67
- // ---- Plan (fresh) or resume (prior prGroups present) -------------------
68
46
  let groups;
69
47
  if (current.prGroups.length > 0) {
70
48
  groups = current.prGroups;
@@ -84,9 +62,6 @@ export async function runLoopAdapter(input, seams = {}) {
84
62
  groups = outcome.groups;
85
63
  await state.update((s) => ({ ...s, status: 'working', prGroups: groups }));
86
64
  }
87
- // ---- Live graph + state proxy ------------------------------------------
88
- // PlanGraph captures its groups at construction, so rebuild it per call against the
89
- // mirror that workLoopState keeps in sync after every persisted update.
90
65
  let liveGroups = groups;
91
66
  const graph = {
92
67
  ready: () => new PlanGraph([...liveGroups]).ready(),
@@ -99,7 +74,6 @@ export async function runLoopAdapter(input, seams = {}) {
99
74
  return next;
100
75
  },
101
76
  };
102
- // ---- Remaining deps ----------------------------------------------------
103
77
  const pool = seams.makePool?.(input) ??
104
78
  new WorktreePool(input.cwd, resolvePath(input.cwd, '.ai-task-master'), input.resolved.concurrency);
105
79
  const github = seams.makeGithub?.(input) ?? input.github;
@@ -128,7 +102,6 @@ export async function runLoopAdapter(input, seams = {}) {
128
102
  }
129
103
  }
130
104
  }
131
- // ---- Plan ------------------------------------------------------------------
132
105
  export function planToPrGroups(plan) {
133
106
  return plan.groups.map((g) => ({
134
107
  id: g.id,
@@ -159,7 +132,6 @@ async function defaultPlanGroups(input, mcp) {
159
132
  return { kind: 'blocked', reason: result.reason };
160
133
  return { kind: 'error', error: result.error };
161
134
  }
162
- // ---- Orchestrator bridge ---------------------------------------------------
163
135
  function defaultMakeOrchestrator(ctx) {
164
136
  const { input, mcp, rollingContext } = ctx;
165
137
  const style = input.agentConfig.contents;
@@ -172,8 +144,6 @@ function defaultMakeOrchestrator(ctx) {
172
144
  });
173
145
  return {
174
146
  runWorker: async ({ group, worktree, baseBranch }) => {
175
- // Prefer MCP-supplied tools; partial-fill any the server omits from the local set so a
176
- // bare `aitm start` (no mcpServers configured) can still edit, commit and open a PR.
177
147
  const tools = resolveWorkerTools(mcp.toolsForRole('worker'), worktree.path);
178
148
  const agent = createWorkerAgent({
179
149
  model: input.credentials.modelFor('worker'),
@@ -190,16 +160,12 @@ function defaultMakeOrchestrator(ctx) {
190
160
  },
191
161
  finalizeCommit: (group, delivery, worktreePath) => orch.finalizeCommit(group, delivery, worktreePath),
192
162
  openPr: async (group, delivery, baseBranch) => {
193
- // The Worker's commits live on the group branch in a linked worktree (shared object
194
- // store). Push it to origin first — `gh pr create` won't open a PR for a branch that
195
- // isn't on the remote ("No commits between … / Head ref must be a branch").
196
163
  const head = group.branch ?? `aitm/${group.id}`;
197
164
  await execa('git', ['push', '-u', 'origin', head], { cwd: input.cwd });
198
165
  return orch.openPr(group, delivery, baseBranch);
199
166
  },
200
167
  runReviewer: async ({ pr, threads, worktree }) => {
201
168
  const github = githubThreadTool(input.github);
202
- // Same partial-fill as the Worker, plus the local `github` thread tool.
203
169
  const tools = resolveReviewerTools(mcp.toolsForRole('reviewer'), worktree.path, github);
204
170
  const agent = createReviewerAgent({
205
171
  model: input.credentials.modelFor('reviewer'),
@@ -215,10 +181,6 @@ function defaultMakeOrchestrator(ctx) {
215
181
  },
216
182
  };
217
183
  }
218
- // MCP exposes a dynamically-typed ToolSet. The subagents need a statically-shaped tool record.
219
- // Rather than fail closed when a server only exports the legacy readFile/writeFile/bash, we
220
- // partial-fill: prefer the MCP tool for each name, falling back to the local worktree-scoped
221
- // tool for any the server omits. The shape is asserted once at this boundary.
222
184
  function resolveWorkerTools(set, cwd) {
223
185
  const local = localEditTools(cwd);
224
186
  return {
@@ -234,10 +196,6 @@ function resolveWorkerTools(set, cwd) {
234
196
  function resolveReviewerTools(set, cwd, github) {
235
197
  return { ...resolveWorkerTools(set, cwd), github };
236
198
  }
237
- // The Planner gets only the read-only subset, partial-filled the same way. This is also the fix
238
- // for the latent no-MCP bug: previously the Planner was handed the raw MCP ToolSet with no local
239
- // fallback, so a bare `aitm start` left it with zero tools despite its prompt promising
240
- // readFile/grep/glob.
241
199
  function resolvePlannerTools(set, cwd) {
242
200
  const local = localReadTools(cwd);
243
201
  return {
@@ -246,8 +204,6 @@ function resolvePlannerTools(set, cwd) {
246
204
  glob: set.glob ?? local.glob,
247
205
  };
248
206
  }
249
- // Flat object, not a discriminatedUnion → no `oneOf` in the tool params (rejected by some
250
- // OpenRouter-routed providers). `body` applies to replyToThread only.
251
207
  const githubToolInputSchema = z.object({
252
208
  action: z.enum(['replyToThread', 'resolveThread']),
253
209
  threadId: z.string().min(1),
@@ -267,4 +223,3 @@ export function githubThreadTool(github) {
267
223
  },
268
224
  });
269
225
  }
270
- //# sourceMappingURL=run-loop-adapter.js.map
@@ -1,23 +1,3 @@
1
- // Take-over merge flow. Drives an externally-built PR (Claude Code, gh pr create, etc.)
2
- // to merge: waits for CI, runs Reviewer to address unresolved review threads (CodeRabbit
3
- // + human reviewers), pushes fixes, and merges. Mirrors the claude-task-master
4
- // `merge_pr()` shape from src/claude_task_master/cli_commands/fix_pr.py:
5
- //
6
- // for iteration in 0..maxIterations:
7
- // status = waitForChecks(pr)
8
- // threads = listUnresolvedThreads(pr)
9
- // if status == success and threads.empty: break
10
- // if status == failure: runWorker (CI-fix path, optional)
11
- // if threads.any: runReviewer per thread, push commits
12
- // sleep(cooldown) # let CI restart
13
- // mergePr(pr)
14
- //
15
- // Unlike WorkLoop.autoMergeFlow, this does NOT acquire a `git worktree`. The user is
16
- // expected to be on the PR branch in their cwd; everything happens in-place. That's
17
- // the simpler model and matches how a human reviewer would handle it.
18
- //
19
- // docs/vendor/ai-sdk/chunk-09.md §"Subagents" — Reviewer/Worker are built ad-hoc per loop
20
- // iteration because their tool bindings (worktree, threads) change each iteration.
21
1
  import { composeSystemPrompt } from '@developerz.ai/ai-claude-compat';
22
2
  import { CiFailed } from "../github/errors.js";
23
3
  import { createReviewerAgent, REVIEWER_SYSTEM_PREFIX, runReviewer, } from "../subagents/reviewer.js";
@@ -29,21 +9,14 @@ export async function runTakeOverFlow(input) {
29
9
  const cooldownMs = input.cooldownMs ?? DEFAULT_COOLDOWN_MS;
30
10
  const sleep = input.sleep ?? defaultSleep;
31
11
  const log = input.logger;
32
- // Hoisted so the post-loop merge can report how many iterations actually ran: on an
33
- // early `break` it holds the break index, on natural exhaustion it equals maxIterations.
34
12
  let iteration = 0;
35
13
  for (; iteration < maxIterations; iteration++) {
36
14
  log?.info('take-over: iteration start', { pr: input.pr, iteration });
37
- // 1. Wait for CI to settle. waitForChecks throws CiFailed on hard failure; we treat
38
- // that as "Worker should try to fix" rather than a fatal error.
39
15
  const ciStatus = await observeCheckStatus(input.github, input.pr);
40
16
  log?.info('take-over: ci status', { pr: input.pr, ciStatus });
41
- // 2. Pull review threads. Always runs — even on CI failure, threads may exist and
42
- // fixing them might happen to fix CI too.
43
17
  const threads = await input.github.listUnresolvedThreads(input.pr);
44
18
  log?.info('take-over: threads', { pr: input.pr, count: threads.length });
45
19
  if (ciStatus === 'success' && threads.length === 0) {
46
- // Happy path: nothing left to do. Merge.
47
20
  break;
48
21
  }
49
22
  let pushedSomething = false;
@@ -69,7 +42,6 @@ export async function runTakeOverFlow(input) {
69
42
  iterations: iteration,
70
43
  };
71
44
  }
72
- // Reviewer commits per-thread fixes via the bash tool; we still need to push them.
73
45
  if (reviewed.resolutions.some((r) => r.kind === 'fixed')) {
74
46
  pushedSomething = true;
75
47
  }
@@ -78,12 +50,9 @@ export async function runTakeOverFlow(input) {
78
50
  await input.push(input.worktreePath);
79
51
  log?.info('take-over: pushed fixes', { pr: input.pr });
80
52
  }
81
- // Sleep so the next iteration's waitForChecks sees fresh CI state, not the stale
82
- // success/failure from before our push triggered a new run.
83
53
  if (cooldownMs > 0)
84
54
  await sleep(cooldownMs);
85
55
  }
86
- // Final state check — make sure we didn't fall through the loop with a hung iteration.
87
56
  const finalStatus = await observeCheckStatus(input.github, input.pr);
88
57
  const finalThreads = await input.github.listUnresolvedThreads(input.pr);
89
58
  if (finalStatus !== 'success') {
@@ -104,8 +73,6 @@ export async function runTakeOverFlow(input) {
104
73
  log?.info('take-over: merged', { pr: input.pr });
105
74
  return { kind: 'merged', pr: input.pr, iterations: iteration };
106
75
  }
107
- // Convert waitForChecks' throw-on-failure semantics into a status return so the loop can
108
- // treat CI failure as a recoverable state.
109
76
  async function observeCheckStatus(github, pr) {
110
77
  try {
111
78
  return await github.waitForChecks(pr);
@@ -137,9 +104,6 @@ async function runReviewerThreads(input, threads) {
137
104
  styleContents: input.subagents.styleContents,
138
105
  });
139
106
  }
140
- // Worker CI-fix path. Build a synthetic PR group whose only task is "fix CI on this PR",
141
- // then run the regular Worker. Worker emits a FileManifest and runs per-file editors —
142
- // suitable for "test failed, fix it" if Worker has enough context from the worktree.
143
107
  async function runWorkerCiFix(input) {
144
108
  const group = {
145
109
  id: `takeover-ci-${input.pr}`,
@@ -180,4 +144,3 @@ function defaultSleep(ms) {
180
144
  setTimeout(resolve, ms);
181
145
  });
182
146
  }
183
- //# sourceMappingURL=take-over-flow.js.map
@@ -1,21 +1,5 @@
1
- // docs/architecture.md (WorkLoop row), docs/commands/start.md §Flow
2
- // Drives the Orchestrator group-by-group. Extended for concurrent execution:
3
- //
4
- // while !plan.isComplete():
5
- // ready = planGraph.ready()
6
- // batch = ready.slice(0, free worker slots)
7
- // await Promise.all(batch.map(g => runGroup(g)))
8
- //
9
- // Each runGroup acquires a WorktreePool slot, runs Worker, and on PR open hands off to
10
- // the merge-pr flow (CI wait + Reviewer + GitHubClient.mergePr).
11
- //
12
- // Deps are structural ports — concrete classes (Orchestrator, GitHubClient, StateStore,
13
- // WorktreePool, PlanGraph) satisfy them at runtime; tests pass literal stubs.
14
1
  import { CiFailed } from "../github/errors.js";
15
2
  const DEFAULT_MERGE_METHOD = 'squash';
16
- // Thrown when a state-write fails *after* an external side effect (openPr/mergePr) already
17
- // succeeded. Carries the real outcome so runGroup doesn't roll the group back to 'blocked'
18
- // and cause a retry to reopen/re-merge work that already landed.
19
3
  class StateWriteAfterSuccess extends Error {
20
4
  outcome;
21
5
  cause;
@@ -52,7 +36,6 @@ export class WorkLoop {
52
36
  }
53
37
  return this.finalResult();
54
38
  }
55
- // Run a single group end-to-end: worktree → Worker → (optionally) merge-pr inline.
56
39
  async runGroup(group) {
57
40
  const branch = group.branch ?? `aitm/${group.id}`;
58
41
  let acquired = false;
@@ -71,18 +54,13 @@ export class WorkLoop {
71
54
  }
72
55
  catch (err) {
73
56
  if (acquired) {
74
- // best-effort release if processGroup itself threw before the inner finally ran;
75
- // the inner finally would have run already in normal flow, so this is defensive.
76
57
  try {
77
58
  await this.deps.pool.release(group.id);
78
59
  }
79
60
  catch {
80
- /* swallow */
81
61
  }
82
62
  }
83
63
  if (err instanceof StateWriteAfterSuccess) {
84
- // External side effect (openPr/mergePr) already succeeded; persist failed.
85
- // Keep the real outcome so a retry doesn't reopen or re-merge.
86
64
  this.outcomes.push(err.outcome);
87
65
  return;
88
66
  }
@@ -91,7 +69,6 @@ export class WorkLoop {
91
69
  await this.markStatus(group.id, 'blocked');
92
70
  }
93
71
  catch {
94
- /* swallow secondary failures */
95
72
  }
96
73
  this.outcomes.push({ groupId: group.id, status: 'blocked', reason });
97
74
  }
@@ -108,20 +85,15 @@ export class WorkLoop {
108
85
  const delivery = workerResult.delivery;
109
86
  await orchestrator.finalizeCommit(group, delivery, worktree.path);
110
87
  const pr = await orchestrator.openPr(group, delivery, baseBranch);
111
- // openPr already landed externally; if persistence fails here, surface the real
112
- // outcome via StateWriteAfterSuccess so the outer catch doesn't flip us to 'blocked'.
113
88
  await this.persistAfterSideEffect({ groupId: group.id, status: 'awaiting-pr', pr: pr.number }, () => this.markStatus(group.id, 'awaiting-pr', { pr: pr.number }));
114
89
  if (!this.deps.autoMerge) {
115
90
  this.outcomes.push({ groupId: group.id, status: 'awaiting-pr', pr: pr.number });
116
91
  return;
117
92
  }
118
93
  await this.autoMergeFlow(group, pr, worktree, baseBranch);
119
- // mergePr already landed externally; same guard as above.
120
94
  await this.persistAfterSideEffect({ groupId: group.id, status: 'merged', pr: pr.number }, () => this.markStatus(group.id, 'merged'));
121
95
  this.outcomes.push({ groupId: group.id, status: 'merged', pr: pr.number });
122
96
  }
123
- // Run a state write that follows a successful external side effect. If the write throws,
124
- // wrap the error in StateWriteAfterSuccess so callers don't roll the outcome back.
125
97
  async persistAfterSideEffect(outcome, write) {
126
98
  try {
127
99
  await write();
@@ -132,7 +104,6 @@ export class WorkLoop {
132
104
  }
133
105
  async autoMergeFlow(group, pr, worktree, baseBranch) {
134
106
  const { orchestrator, github } = this.deps;
135
- // CI: wait for checks. On failure, ask Worker to fix and re-check.
136
107
  try {
137
108
  await github.waitForChecks(pr.number);
138
109
  }
@@ -147,7 +118,6 @@ export class WorkLoop {
147
118
  await orchestrator.finalizeCommit(group, fix.delivery, worktree.path);
148
119
  await github.waitForChecks(pr.number);
149
120
  }
150
- // Review: resolve any unresolved threads via Reviewer.
151
121
  const threads = await github.listUnresolvedThreads(pr.number);
152
122
  if (threads.length > 0) {
153
123
  const review = await orchestrator.runReviewer({ pr: pr.number, threads, worktree });
@@ -166,16 +136,11 @@ export class WorkLoop {
166
136
  return Math.min(concurrency, readyCount, remaining);
167
137
  }
168
138
  async markStatus(id, status, patch = {}) {
169
- // Status transitions do not bump sessionCount — that's owned by incrementSessionCount,
170
- // which fires once per batch dispatch so the in-memory and persisted counters agree.
171
139
  await this.deps.state.update((s) => ({
172
140
  ...s,
173
141
  prGroups: s.prGroups.map((g) => (g.id === id ? { ...g, ...patch, status } : g)),
174
142
  }));
175
143
  }
176
- // Single source of truth for session counting: bump both the in-memory counter (used by
177
- // run() to enforce maxSessions) and the persisted counter (used by reporting/resume) in
178
- // one call. Drops in-memory if persistence fails so the two stay aligned.
179
144
  async incrementSessionCount(by) {
180
145
  if (by <= 0)
181
146
  return;
@@ -208,4 +173,3 @@ export class WorkLoop {
208
173
  return { kind: 'success', outcomes: this.outcomes.slice() };
209
174
  }
210
175
  }
211
- //# sourceMappingURL=work-loop.js.map
@@ -1,13 +1,3 @@
1
- // Connects to every MCP server declared in `mcpServers` (config) and exposes the
2
- // union of their tools to subagents. The Vercel AI SDK's @ai-sdk/mcp client gives
3
- // us tool-conversion; we wire transport per entry and merge the tool maps.
4
- //
5
- // docs/vendor/ai-sdk/chunk-15.md §"Initializing an MCP Client"
6
- // docs/mcp.md
7
- //
8
- // Lifecycle: connectAll() at run start, toolsForRole() during agent build, close() on exit
9
- // (success / blocked / SIGINT). Failures on individual servers are logged + skipped — a
10
- // broken MCP server should not block the whole run.
11
1
  import { experimental_createMCPClient } from '@ai-sdk/mcp';
12
2
  import { Experimental_StdioMCPTransport } from '@ai-sdk/mcp/mcp-stdio';
13
3
  export class McpClientManager {
@@ -20,8 +10,6 @@ export class McpClientManager {
20
10
  }
21
11
  async connectAll() {
22
12
  for (const [name, server] of Object.entries(this.init.servers)) {
23
- // Track the client outside the try so a failure during tools() can still
24
- // close the spawned process / socket instead of leaking it.
25
13
  let client;
26
14
  try {
27
15
  const transport = transportKind(server);
@@ -120,4 +108,3 @@ function clientNameFor(name) {
120
108
  function errorMessage(err) {
121
109
  return err instanceof Error ? err.message : String(err);
122
110
  }
123
- //# sourceMappingURL=mcp-client.js.map
@@ -1,23 +1,9 @@
1
- // MCP *client* config — aitm consumes external MCP servers as additional tool surfaces
2
- // for subagents. aitm is NOT exposed as an MCP server (banned by CLAUDE.md §"Out of scope").
3
- //
4
- // Shape mirrors Claude Code's `mcpServers` config — three transports, same keys.
5
- // Refs:
6
- // https://code.claude.com/docs/en/mcp (Claude Code reference)
7
- // https://modelcontextprotocol.io/ (spec)
8
- // docs/vendor/ai-sdk/chunk-15.md §"Model Context Protocol" (Vercel AI SDK client)
9
- //
10
- // Transports:
11
- // stdio — local binary launched as a child process. Default when `type` omitted.
12
- // sse — HTTP server-sent events. URL + optional headers.
13
- // http — Streamable HTTP transport. URL + optional headers. Preferred for production.
14
1
  import { z } from 'zod';
15
2
  export const McpStdioServerSchema = z.object({
16
3
  type: z.literal('stdio').optional(),
17
4
  command: z.string(),
18
5
  args: z.array(z.string()).optional(),
19
6
  env: z.record(z.string(), z.string()).optional(),
20
- // Working directory for the spawned process. Defaults to repo root.
21
7
  cwd: z.string().optional(),
22
8
  });
23
9
  export const McpSseServerSchema = z.object({
@@ -36,4 +22,3 @@ export const McpServerSchema = z.union([
36
22
  McpHttpServerSchema,
37
23
  ]);
38
24
  export const McpServersSchema = z.record(z.string(), McpServerSchema);
39
- //# sourceMappingURL=schema.js.map
@@ -1,6 +1,3 @@
1
- // Thin OpenRouter API client. Only the endpoints we actually need:
2
- // GET /api/v1/models — model catalog including context length / pricing.
3
- // docs/auth.md §"LLM provider", docs/runtime.md (web fetch, not Bun.fetch).
4
1
  import { z } from 'zod';
5
2
  export const OpenRouterModelSchema = z
6
3
  .object({
@@ -37,4 +34,3 @@ export class OpenRouterClient {
37
34
  return OpenRouterModelsResponseSchema.parse(json).data;
38
35
  }
39
36
  }
40
- //# sourceMappingURL=client.js.map
@@ -1,6 +1,3 @@
1
- // Exposes per-model limits (context window, pricing hints) used by the Compactor.
2
- // Cached per-run after first fetch — model catalog changes slowly.
3
- // docs/auth.md, src/compaction/compactor.ts
4
1
  export class ModelNotFound extends Error {
5
2
  modelId;
6
3
  name = 'ModelNotFound';
@@ -36,4 +33,3 @@ export class ModelLimitsRegistry {
36
33
  this.cache = next;
37
34
  }
38
35
  }
39
- //# sourceMappingURL=model-limits.js.map
@@ -1,25 +1,9 @@
1
- // OpenRouter server tools — model-decides tools executed on OpenRouter's side.
2
- // These are NOT Vercel AI SDK function tools; they ride in `providerOptions.openrouter`
3
- // (or equivalent extraBody) so OpenRouter sees them in the request `tools[]` array verbatim.
4
- //
5
- // Refs:
6
- // https://openrouter.ai/docs/guides/features/server-tools/web-search
7
- // https://openrouter.ai/docs/guides/features/server-tools/web-fetch
8
- //
9
- // Web search → citations land as annotations[].url_citation on the assistant message,
10
- // plus usage.server_tool_use.web_search_requests for cost accounting. No round-trip.
11
- // Web fetch → standard tool_call round-trip with { url, title, content, status, retrieved_at }.
12
1
  export function webSearchTool(options = {}) {
13
2
  return { type: 'openrouter:web_search', parameters: options };
14
3
  }
15
- // Renamed from webFetchTool — the canonical webFetchTool is now the local variant in
16
- // src/tools/web-fetch.ts. This one is the explicit "delegate to OpenRouter" opt-in.
17
4
  export function webFetchServerTool(options = {}) {
18
5
  return { type: 'openrouter:web_fetch', parameters: options };
19
6
  }
20
- // Build the providerOptions.openrouter fragment to be merged into an AI SDK call.
21
- // Usage at the model handle layer (src/credentials/credentials.ts) — see Credentials.modelFor.
22
7
  export function providerOptionsWithServerTools(tools) {
23
8
  return { openrouter: { tools: [...tools] } };
24
9
  }
25
- //# sourceMappingURL=server-tools.js.map
@@ -1,17 +1,3 @@
1
- // docs/architecture.md §Flow, docs/subagents.md §Composition
2
- // Top-level agent. Owns:
3
- // - prompt composition (style payload + role prefix + rolling context)
4
- // - routing between Planner / Worker / Reviewer (each exposed as a tool)
5
- // - **PR creation** — title, body, commit message: docs say Worker opens the PR,
6
- // but the Worker can be inconsistent on global-context narration. Orchestrator
7
- // re-composes the commit message and opens the PR via GitHubClient, taking the
8
- // Worker's draft as input. This is the reliability win: one place that knows
9
- // the whole plan writes the PR-level prose.
10
- //
11
- // SDK references:
12
- // docs/vendor/ai-sdk/chunk-04.md §"ToolLoopAgent"
13
- // docs/vendor/ai-sdk/chunk-09.md §"Subagents" §"Controlling What the Model Sees"
14
- // docs/vendor/ai-sdk/chunk-09.md §"Loop Control" — stopWhen: [stepCountIs(N), hasToolCall('done')]
15
1
  import { generateText, hasToolCall, Output, stepCountIs, ToolLoopAgent } from 'ai';
16
2
  import { ExecaError, execa } from 'execa';
17
3
  import { z } from 'zod';
@@ -36,8 +22,6 @@ export const defaultRunCmd = async (file, args, options) => {
36
22
  throw err;
37
23
  }
38
24
  };
39
- // Inlined per CLAUDE.md "no premature abstraction". Full system prompt is
40
- // `agentConfig.contents + ORCHESTRATOR_ROLE_PREFIX + rollingContext`.
41
25
  export const ORCHESTRATOR_ROLE_PREFIX = [
42
26
  '',
43
27
  '## Role: Orchestrator',
@@ -56,16 +40,11 @@ export const ORCHESTRATOR_ROLE_PREFIX = [
56
40
  ' - Be specific and terse. No marketing prose.',
57
41
  ' - Conventional commit subjects, ≤72 chars.',
58
42
  ].join('\n');
59
- // Structured-output schema for PR composition. Title cap reinforces conventional-commit
60
- // brevity; body is free-form markdown.
61
43
  const PrCompositionSchema = z.object({
62
44
  title: z.string().min(1).max(72),
63
45
  body: z.string().min(1),
64
46
  });
65
- // Fallback session cap when caller passes null / 0 / negative `maxSessions`.
66
47
  export const DEFAULT_MAX_STEPS = 50;
67
- // Resolve the agent step cap from caller-provided `maxSessions`. Falls back to the
68
- // default when the value is null, zero, or negative. Exported for unit testing.
69
48
  export function resolveMaxSteps(maxSessions) {
70
49
  return typeof maxSessions === 'number' && maxSessions > 0 ? maxSessions : DEFAULT_MAX_STEPS;
71
50
  }
@@ -111,8 +90,6 @@ export class Orchestrator {
111
90
  this.init.rollingContext,
112
91
  ].join('\n');
113
92
  }
114
- // Re-write the Worker's draft commit message via the orchestrator model, then
115
- // `git commit --amend` on the active worktree. Returns the new HEAD SHA.
116
93
  async finalizeCommit(group, delivery, worktreePath) {
117
94
  const refined = await this.refineCommitMessage(group, delivery);
118
95
  const runCmd = this.init.runCmd ?? defaultRunCmd;
@@ -126,8 +103,6 @@ export class Orchestrator {
126
103
  }
127
104
  return sha.stdout.trim();
128
105
  }
129
- // Compose PR title + body via the orchestrator model, then open the PR through the github
130
- // client. Falls back to `aitm/<group.id>` when `group.branch` is unset.
131
106
  async openPr(group, delivery, baseBranch) {
132
107
  const { title, body } = await this.composePr(group, delivery);
133
108
  const head = group.branch ?? `aitm/${group.id}`;
@@ -177,4 +152,3 @@ export class Orchestrator {
177
152
  ].join('\n');
178
153
  }
179
154
  }
180
- //# sourceMappingURL=orchestrator.js.map
@@ -1,16 +1,3 @@
1
- // Wrap each subagent as a tool consumable by the Orchestrator agent.
2
- // Pattern verbatim from docs/vendor/ai-sdk/chunk-09.md §"Subagents" and §"Controlling What the
3
- // Model Sees" — `toModelOutput` keeps the Orchestrator context lean by collapsing the full
4
- // subagent result to a one-line `<role> [<status>]: <summary>`.
5
- //
6
- // Note: AI SDK 6 names the field `toModelOutput` (the SDK-5 name was
7
- // `experimental_toToolResultContent`; the task brief uses the older spelling).
8
- //
9
- // Each wrapper:
10
- // - inputSchema — Zod schema; the Orchestrator's model fills it in per call
11
- // - execute — builds the subagent via its factory using `credentials.modelFor(role)` and
12
- // runs the matching runner; returns the full structured result
13
- // - toModelOutput — collapses that result to a single status line for the Orchestrator
14
1
  import { tool } from 'ai';
15
2
  import { z } from 'zod';
16
3
  import { createPlannerAgent, PLANNER_SYSTEM_PREFIX, runPlanner, } from "../subagents/planner.js";
@@ -21,8 +8,6 @@ const plannerInputSchema = z.object({
21
8
  criteria: z.string().optional().describe('Optional acceptance criteria'),
22
9
  maxPrs: z.number().int().positive().describe('Cap on PR-group count'),
23
10
  });
24
- // Worker + Reviewer have all state bound in deps — the Orchestrator's model invokes them with
25
- // no arguments, signalling "process the currently-active group / review threads".
26
11
  const emptyInputSchema = z.object({});
27
12
  export function makePlannerTool(deps) {
28
13
  return tool({
@@ -85,13 +70,7 @@ export function makeReviewerTool(deps) {
85
70
  toModelOutput: ({ output }) => ({ type: 'text', value: summarizeReviewerResult(output) }),
86
71
  });
87
72
  }
88
- // Cap per-line summary length so a verbose subagent reply (long IDs, multiline draft
89
- // commit messages, error payloads) can't bloat the orchestrator's context. 220 chars
90
- // is enough for a status line + a few tens of chars of free text.
91
73
  const MAX_SUMMARY_CHARS = 220;
92
- // Show only the first N planner group IDs, then `+M more` — the count alone is what
93
- // matters to the orchestrator; the orchestrator can re-read the plan if it needs the
94
- // full ID list.
95
74
  const MAX_PREVIEW_IDS = 8;
96
75
  function compactOneLine(text, max = MAX_SUMMARY_CHARS) {
97
76
  return text.replace(/\s+/g, ' ').trim().slice(0, max);
@@ -130,4 +109,3 @@ function summarizeReviewerResult(r) {
130
109
  return compactOneLine(`reviewer [blocked]: ${r.reason}`);
131
110
  return compactOneLine(`reviewer [error]: ${r.error}`);
132
111
  }
133
- //# sourceMappingURL=subagent-tools.js.map
@@ -1,5 +1,3 @@
1
- // Role system-prompt prefixes for each agent. Inlined per CLAUDE.md "no premature abstraction".
2
- // Appended after `agentConfig.contents` (house style) to form the full instructions string.
3
1
  export const ORCHESTRATOR_PREFIX = [
4
2
  '',
5
3
  '## Role: Orchestrator',
@@ -75,4 +73,3 @@ export const REVIEWER_PREFIX = [
75
73
  ' - Resolve the thread for "fixed" and "wontfix"; leave it open for "replied".',
76
74
  ' - Return JSON that matches the ThreadResolutionOutput schema exactly.',
77
75
  ].join('\n');
78
- //# sourceMappingURL=system-prompts.js.map
@@ -1,6 +1,3 @@
1
- // Dependency DAG over PR groups. Drives concurrent execution in src/loop/work-loop.ts.
2
- // Why a graph and not a list — docs/task-groups.md (extended): large goals split into
3
- // independent PRs that can run in parallel. A linear list serializes work needlessly.
4
1
  export class PlanGraph {
5
2
  groups;
6
3
  index;
@@ -9,12 +6,10 @@ export class PlanGraph {
9
6
  PlanGraph.validate(groups);
10
7
  this.index = new Map(groups.map((g) => [g.id, g]));
11
8
  }
12
- // Groups currently ready to run: status === 'pending' AND all deps merged.
13
9
  ready() {
14
10
  return this.groups.filter((g) => g.status === 'pending' &&
15
11
  g.dependsOn.every((dep) => this.index.get(dep)?.status === 'merged'));
16
12
  }
17
- // Groups blocked on at least one unmerged dep.
18
13
  blocked() {
19
14
  return this.groups.filter((g) => g.status === 'pending' &&
20
15
  g.dependsOn.some((dep) => this.index.get(dep)?.status !== 'merged'));
@@ -22,12 +17,9 @@ export class PlanGraph {
22
17
  byId(id) {
23
18
  return this.index.get(id);
24
19
  }
25
- // True when every group is in a terminal state (merged or blocked).
26
20
  isComplete() {
27
21
  return this.groups.every((g) => g.status === 'merged' || g.status === 'blocked');
28
22
  }
29
- // Static: detect cycles + dangling deps at plan-acceptance time.
30
- // DFS coloring — white=unvisited, gray=on stack, black=fully explored.
31
23
  static validate(groups) {
32
24
  const ids = new Set();
33
25
  for (const g of groups) {
@@ -66,4 +58,3 @@ export class PlanGraph {
66
58
  visit(g.id, []);
67
59
  }
68
60
  }
69
- //# sourceMappingURL=plan-graph.js.map
@@ -1,19 +1,12 @@
1
- // Plan-time output of Planner subagent — see docs/subagents.md §Roster (Planner row).
2
- // PrGroup is persisted via src/state/schema.ts; the plan schema below is what
3
- // Planner returns to the Orchestrator before that persistence step.
4
1
  import { z } from 'zod';
5
2
  export const PlannedTaskSchema = z.object({
6
3
  description: z.string(),
7
- // Optional file hint helps Worker plan its parallel file-edit fanout.
8
- // See docs/vendor/ai-sdk/chunk-09.md §"Orchestrator-Worker" — Worker uses this
9
- // to emit a file manifest, then Promise.all over per-file editor subagents.
10
4
  filesHint: z.array(z.string()).optional(),
11
5
  });
12
6
  export const PlannedGroupSchema = z.object({
13
7
  id: z.string(),
14
8
  title: z.string(),
15
9
  tasks: z.array(PlannedTaskSchema),
16
- // Group ids that must merge before this group runs. Empty = root of DAG.
17
10
  dependsOn: z.array(z.string()).default([]),
18
11
  });
19
12
  export const PlanSchema = z.object({
@@ -21,4 +14,3 @@ export const PlanSchema = z.object({
21
14
  criteria: z.string().optional(),
22
15
  groups: z.array(PlannedGroupSchema),
23
16
  });
24
- //# sourceMappingURL=schema.js.map