@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,5 +1,3 @@
1
- // docs/agent-config-detection.md, docs/coding-style.md
2
- // Style signal only — never selects a provider. Prefer CLAUDE.md over AGENTS.md when both exist.
3
1
  import { readFile } from 'node:fs/promises';
4
2
  import { isAbsolute, join, relative, resolve } from 'node:path';
5
3
  export class AgentConfigDetector {
@@ -53,4 +51,3 @@ function isNotFound(err) {
53
51
  'code' in err &&
54
52
  err.code === 'ENOENT');
55
53
  }
56
- //# sourceMappingURL=agent-config-detector.js.map
package/dist/cli/args.js CHANGED
@@ -1,5 +1,3 @@
1
- // docs/commands/start.md §Signature, docs/commands/merge-pr.md §Signature, docs/commands/config.md
2
- // Tiny dependency-free argv parser. Pure function — easy to unit-test.
3
1
  const HELP = { kind: 'help' };
4
2
  export function parseArgs(argv) {
5
3
  const [command, ...rest] = argv;
@@ -65,8 +63,6 @@ function parseStart(args) {
65
63
  i += consumed(inlineValue !== null);
66
64
  }
67
65
  else if (flag === '--no-automerge') {
68
- // Boolean flag rejects any inline value: `--no-automerge=true` is a usage error,
69
- // not silently treated as the boolean.
70
66
  if (inlineValue !== null)
71
67
  return HELP;
72
68
  autoMerge = false;
@@ -158,7 +154,6 @@ function parseConfig(args) {
158
154
  scope = 'project';
159
155
  }
160
156
  else if (arg.startsWith('--')) {
161
- // `--project=anything` is a usage error: --project is a boolean flag.
162
157
  return HELP;
163
158
  }
164
159
  else {
@@ -211,8 +206,6 @@ function parsePositiveInt(s) {
211
206
  const n = parseNonNegativeInt(s);
212
207
  return n !== null && n > 0 ? n : null;
213
208
  }
214
- // Split `--key=value` into flag + inline value. For `--key` alone, inlineValue is null
215
- // and the caller must read args[i+1] for the value (two-token form).
216
209
  function splitFlag(raw) {
217
210
  if (!raw.startsWith('--')) {
218
211
  return { flag: raw, inlineValue: null, consumed: () => 1 };
@@ -227,12 +220,9 @@ function splitFlag(raw) {
227
220
  consumed: (inline) => (inline ? 1 : 2),
228
221
  };
229
222
  }
230
- // Resolve the value for a flag: prefer the inline form (--key=value); fall back to the
231
- // next argv token (--key value). Returns null when neither is present.
232
223
  function takeValue(args, i, inlineValue) {
233
224
  if (inlineValue !== null)
234
225
  return inlineValue;
235
226
  const next = args[i + 1];
236
227
  return next ?? null;
237
228
  }
238
- //# sourceMappingURL=args.js.map
package/dist/cli/cli.js CHANGED
@@ -1,6 +1,4 @@
1
1
  #!/usr/bin/env node
2
- // docs/commands/start.md, docs/commands/merge-pr.md, docs/commands/config.md
3
- // Single entry. Parses argv, dispatches, exits with the right code.
4
2
  import { realpathSync } from 'node:fs';
5
3
  import { pathToFileURL } from 'node:url';
6
4
  import { parseArgs } from "./args.js";
@@ -86,8 +84,6 @@ Exit codes:
86
84
  2 cancelled
87
85
 
88
86
  Docs: docs/commands/start.md, docs/commands/merge-pr.md, docs/commands/config.md`;
89
- // Entry-point: when invoked as a script (via the `aitm` bin), parse process.argv
90
- // and propagate the exit code. When imported (e.g. from tests), this is skipped.
91
87
  if (isEntrypoint(import.meta.url, process.argv[1])) {
92
88
  main(process.argv.slice(2)).then((code) => {
93
89
  process.exit(code);
@@ -96,9 +92,6 @@ if (isEntrypoint(import.meta.url, process.argv[1])) {
96
92
  process.exit(1);
97
93
  });
98
94
  }
99
- // Exported for unit-test coverage of the symlink case (global installs put a symlink at
100
- // e.g. ~/.bun/bin/aitm pointing at dist/cli/cli.js — argv[1] and import.meta.url differ
101
- // until argv[1] is resolved via realpath).
102
95
  export function isEntrypoint(metaUrl, argv1) {
103
96
  if (argv1 === undefined)
104
97
  return false;
@@ -110,4 +103,3 @@ export function isEntrypoint(metaUrl, argv1) {
110
103
  return false;
111
104
  }
112
105
  }
113
- //# sourceMappingURL=cli.js.map
@@ -1,10 +1,3 @@
1
- // docs/commands/start.md, docs/commands/merge-pr.md, docs/commands/config.md
2
- // Dispatcher only. Each command does precondition checks → wires deps → kicks WorkLoop / writes config.
3
- //
4
- // The heavy WorkLoop+Orchestrator wiring is exposed via the `runLoop` / `runMergeFlow`
5
- // injection seams so this module stays pure dispatch and is unit-testable without
6
- // spinning up real subagents. Default seam implementations live below; integration
7
- // tests (PR 12) cover the end-to-end stack.
8
1
  import { homedir } from 'node:os';
9
2
  import { join, resolve as resolvePath } from 'node:path';
10
3
  import { AgentConfigDetector } from "../agent-config/agent-config-detector.js";
@@ -67,10 +60,6 @@ export async function runStart(args, ctx = {}) {
67
60
  const github = new GitHubClient(cwd);
68
61
  const stateDir = resolvePath(cwd, '.ai-task-master');
69
62
  const state = new StateStore(stateDir);
70
- // Resume detection: if a previous run left a valid state.json, skip re-init so
71
- // runId and prGroups are preserved. Only fall back on expected "missing/invalid
72
- // prior state" cases (ENOENT, JSON parse failure, schema mismatch); surface every
73
- // other error (permissions, IO) as a hard failure rather than silently re-initing.
74
63
  let resuming = false;
75
64
  let existingState = null;
76
65
  try {
@@ -81,7 +70,6 @@ export async function runStart(args, ctx = {}) {
81
70
  if (!isMissingOrInvalidState(err, stateDir)) {
82
71
  return { code: 1, message: `Could not read run state: ${errMsg(err)}` };
83
72
  }
84
- // No valid state.json — proceed with fresh init.
85
73
  }
86
74
  if (!resuming) {
87
75
  const initial = buildInitialRunState({ resolved, agentConfig });
@@ -94,13 +82,6 @@ export async function runStart(args, ctx = {}) {
94
82
  return { code: 1, message: errMsg(err) };
95
83
  }
96
84
  }
97
- // Planning phase (issue #17): a one-shot step that runs the Planner once, before the loop,
98
- // so `prGroups` is populated and the loop has something to iterate. Gated on whether a plan
99
- // is already persisted — not merely on `resuming` — because a prior run whose planning
100
- // blocked leaves a resumable state.json with empty `prGroups`; that case must re-plan rather
101
- // than hand the loop an empty plan. Only runs when a `runPlanner` seam is injected; otherwise
102
- // planning is handled inside the WorkLoop adapter (merged default), keeping production
103
- // behaviour unchanged and this module pure dispatch.
104
85
  const hasPersistedPlan = (existingState?.prGroups.length ?? 0) > 0;
105
86
  if (ctx.runPlanner && !hasPersistedPlan) {
106
87
  let plan;
@@ -144,9 +125,6 @@ export async function runStart(args, ctx = {}) {
144
125
  catch (err) {
145
126
  return { code: 1, message: errMsg(err) };
146
127
  }
147
- // Persist the first awaiting-pr number into state so `aitm merge-pr` (with no --pr)
148
- // can pick it up. WorkLoop tracks per-group PR numbers but does not nominate one as
149
- // "current"; that's a CLI-level concern resolved here.
150
128
  if (result.kind === 'awaiting-pr' && result.prs.length > 0) {
151
129
  const firstPr = result.prs[0];
152
130
  if (firstPr !== undefined) {
@@ -182,11 +160,6 @@ export async function runMergePr(args, ctx = {}) {
182
160
  const stateDir = resolvePath(cwd, '.ai-task-master');
183
161
  const state = new StateStore(stateDir);
184
162
  const github = ctx.github ?? new GitHubClient(cwd);
185
- // Take-over flow: `aitm merge-pr` (no args, no prior state) should work against any PR
186
- // the user built by hand — e.g. via Claude Code or `gh pr create`. We mirror the
187
- // claude-task-master `merge_pr` pattern: try to read existing state, and if absent,
188
- // synthesize a minimal one from --pr (or the current branch's PR) and persist it so
189
- // subsequent calls resume.
190
163
  let runState;
191
164
  try {
192
165
  runState = await state.read();
@@ -284,7 +257,6 @@ export async function runConfig(args, ctx = {}) {
284
257
  }
285
258
  case 'config-list': {
286
259
  const file = await writer.list(args.scope);
287
- // Never dump the API key in cleartext — `config list` output lands in terminals/logs.
288
260
  const safe = file.openrouterApiKey
289
261
  ? { ...file, openrouterApiKey: maskSecret(file.openrouterApiKey) }
290
262
  : file;
@@ -302,7 +274,6 @@ export async function runConfig(args, ctx = {}) {
302
274
  return { code: 1, message: errMsg(err) };
303
275
  }
304
276
  }
305
- // ---- helpers ---------------------------------------------------------------
306
277
  function toCliOverrides(args) {
307
278
  const out = {};
308
279
  if (args.maxPrs !== undefined)
@@ -367,10 +338,6 @@ function buildInitialRunState(input) {
367
338
  function errMsg(err) {
368
339
  return err instanceof Error ? err.message : String(err);
369
340
  }
370
- // Recognises "no valid prior state" — the only conditions where we fall through to a
371
- // fresh init. ENOENT means the file is absent. StateStore.read() prefixes any JSON
372
- // parse or Zod schema error with the state-file path, so we match that prefix to
373
- // distinguish corrupt state from genuine IO/permission errors.
374
341
  function isMissingOrInvalidState(err, stateDir) {
375
342
  if (typeof err === 'object' &&
376
343
  err !== null &&
@@ -385,8 +352,6 @@ function isMissingOrInvalidState(err, stateDir) {
385
352
  }
386
353
  return false;
387
354
  }
388
- // Mask a secret for display: keep the non-secret `sk-or-` prefix + last 4 chars so the user can
389
- // confirm WHICH key is set without exposing it. Short values are fully hidden.
390
355
  function maskSecret(value) {
391
356
  return value.length <= 12 ? '***' : `${value.slice(0, 6)}…${value.slice(-4)}`;
392
357
  }
@@ -398,15 +363,9 @@ function formatConfigValue(value) {
398
363
  return JSON.stringify(value, null, 2);
399
364
  }
400
365
  const defaultAuthStatus = (cwd) => new GitHubClient(cwd).authStatus();
401
- // Default loop seam — production wiring of Planner → PlanGraph → WorktreePool → WorkLoop with
402
- // the Orchestrator/Worker/Reviewer subagents and MCP tools. Lives in run-loop-adapter.ts so this
403
- // module stays pure dispatch; the adapter exposes its own seams for unit + integration tests.
404
366
  async function defaultRunLoop(input) {
405
367
  return runLoopAdapter(input);
406
368
  }
407
- // Real merge-pr adapter. Drives runTakeOverFlow against the cwd worktree: wait CI →
408
- // Reviewer per unresolved thread → push → loop → merge. See src/loop/take-over-flow.ts
409
- // for the iteration shape (mirrors claude-task-master `merge_pr`).
410
369
  async function defaultRunMergeFlow(input) {
411
370
  const { runTakeOverFlow } = await import("../loop/take-over-flow.js");
412
371
  const { execa } = await import('execa');
@@ -414,8 +373,6 @@ async function defaultRunMergeFlow(input) {
414
373
  const worktreePath = input.cwd;
415
374
  const baseBranch = await input.github.defaultBranch();
416
375
  const styleContents = input.agentConfig.contents;
417
- // Build the Claude-Code-style tool surface scoped to the cwd worktree. The Worker gets the
418
- // full read/write/edit/search/bash set; the Reviewer adds the `github` thread tool.
419
376
  const workerTools = localEditTools(worktreePath);
420
377
  const github = githubThreadTool({ github: input.github });
421
378
  const result = await runTakeOverFlow({
@@ -450,8 +407,6 @@ async function defaultRunMergeFlow(input) {
450
407
  outcomes: [{ groupId: `takeover-${input.pr}`, status: 'blocked', reason: result.reason }],
451
408
  };
452
409
  }
453
- // Build a minimal RunState that lets `merge-pr` take over a PR opened outside aitm. PR
454
- // number comes from --pr or, failing that, from `gh pr view` against the current branch.
455
410
  async function synthesizeTakeoverState(input) {
456
411
  const { args, github, resolved } = input;
457
412
  let pr = args.pr ?? null;
@@ -518,4 +473,3 @@ function isFileNotFound(err) {
518
473
  'code' in err &&
519
474
  err.code === 'ENOENT');
520
475
  }
521
- //# sourceMappingURL=commands.js.map
@@ -1,19 +1,3 @@
1
- // Drives context compaction for long-running agent loops. Keeps the orchestrator and
2
- // each subagent coherent on huge PRs by summarizing chat history when usage crosses
3
- // a fraction of the model's context window.
4
- //
5
- // Strategy:
6
- // 1. Pull contextLength for the active model from ModelLimitsRegistry.
7
- // 2. Estimate live token usage from the agent's step.usage.inputTokens running total.
8
- // 3. When usage / contextLength >= threshold (default 0.7), invoke a `fast`-tier
9
- // summarization step that rewrites the early conversation into a compact note;
10
- // the next step resumes with the summary + the most recent N steps verbatim.
11
- //
12
- // SDK references:
13
- // docs/vendor/ai-sdk/chunk-09.md §"Subagents" §"Controlling What the Model Sees"
14
- // (toModelOutput is the per-tool version of the same idea)
15
- // docs/vendor/ai-sdk/chunk-09.md §"Loop Control" §"Prepare Step"
16
- // (use prepareStep to swap in compacted messages between steps)
17
1
  import { generateText } from 'ai';
18
2
  const DEFAULT_THRESHOLD = 0.7;
19
3
  const DEFAULT_KEEP_LAST_STEPS = 6;
@@ -33,8 +17,6 @@ export class Compactor {
33
17
  }
34
18
  async shouldCompact(modelId, liveInputTokens) {
35
19
  const { contextLength } = await this.init.limits.forModel(modelId);
36
- // A non-finite or non-positive window would make ratio NaN/Infinity and force a
37
- // wrong decision. Treat it as "we don't know enough to compact" — skip.
38
20
  if (!Number.isFinite(contextLength) || contextLength <= 0) {
39
21
  return { kind: 'skip' };
40
22
  }
@@ -48,7 +30,6 @@ export class Compactor {
48
30
  }
49
31
  return { kind: 'skip' };
50
32
  }
51
- // Produce a compact summary suitable for replacing the older conversation prefix.
52
33
  async compact(olderMessages) {
53
34
  const { text } = await generateText({
54
35
  model: this.init.summarizer,
@@ -57,10 +38,6 @@ export class Compactor {
57
38
  return text;
58
39
  }
59
40
  }
60
- // Cycle-safe JSON.stringify. SDK message objects can transitively reference each other
61
- // (tool result -> tool call -> step -> message), and a single circular ref would throw a
62
- // raw TypeError out of compact() and crash the agent loop mid-step. Replace any cycle
63
- // with the literal "[CYCLE]" so the summarizer still gets a usable transcript.
64
41
  function safeStringify(value) {
65
42
  const seen = new WeakSet();
66
43
  return JSON.stringify(value, (_key, v) => {
@@ -72,4 +49,3 @@ function safeStringify(value) {
72
49
  return v;
73
50
  });
74
51
  }
75
- //# sourceMappingURL=compactor.js.map
@@ -1,6 +1,3 @@
1
- // docs/config.md §"Resolution order", docs/auth.md §"LLM provider"
2
- // Only module allowed to read ~/.aitm.json and .ai-task-master/config.json.
3
- // Merge order: defaults < global < project < env < CLI flags. Frozen snapshot written by writeSnapshot().
4
1
  import { readFile } from 'node:fs/promises';
5
2
  import { join } from 'node:path';
6
3
  import { ZodError, z } from 'zod';
@@ -12,10 +9,6 @@ const GLOBAL_FILE = '.aitm.json';
12
9
  const PROJECT_DIR = '.ai-task-master';
13
10
  const PROJECT_FILE = 'config.json';
14
11
  const SNAPSHOT_FILE = 'config.snapshot.json';
15
- // Claude Code's standard MCP config files. Discovering these lets users plug aitm into
16
- // the same MCP servers their Claude Code session already uses, without re-declaring them.
17
- // Refs: https://code.claude.com/docs/en/mcp ("Project scope" = .mcp.json in project root;
18
- // "User scope" = ~/.claude.json with an mcpServers key).
19
12
  const CLAUDE_PROJECT_MCP_FILE = '.mcp.json';
20
13
  const CLAUDE_USER_FILE = '.claude.json';
21
14
  const KNOWN_KEYS = new Set([
@@ -75,7 +68,6 @@ export class ConfigLoader {
75
68
  autoMerge: pick(cliOverrides.autoMerge, project?.autoMerge, global?.autoMerge, DEFAULTS.autoMerge),
76
69
  mergeMethod: pick(cliOverrides.mergeMethod, project?.mergeMethod, global?.mergeMethod, DEFAULTS.mergeMethod),
77
70
  stylePath: pickNullable(cliOverrides.stylePath, project?.stylePath, global?.stylePath, DEFAULTS.stylePath),
78
- // logLevel is not exposed via CliOverrides — project/global only.
79
71
  logLevel: pick(undefined, project?.logLevel, global?.logLevel, DEFAULTS.logLevel),
80
72
  concurrency: pick(cliOverrides.concurrency, project?.concurrency, global?.concurrency, DEFAULTS.concurrency),
81
73
  mcpServers,
@@ -88,19 +80,12 @@ export class ConfigLoader {
88
80
  async readProject() {
89
81
  return this.readConfigFile(join(this.cwd, PROJECT_DIR, PROJECT_FILE));
90
82
  }
91
- // Read Claude Code's project-scoped MCP file (./.mcp.json). Schema is permissive:
92
- // we only extract `mcpServers`, ignore any other keys Claude Code may add.
93
83
  async readClaudeProjectMcp() {
94
84
  return this.readMcpEnvelope(join(this.cwd, CLAUDE_PROJECT_MCP_FILE));
95
85
  }
96
- // Read Claude Code's user-scoped config (~/.claude.json) and extract the `mcpServers`
97
- // block, if any. ~/.claude.json holds many unrelated keys (auth tokens, history); we
98
- // intentionally read it but only consume `mcpServers`.
99
86
  async readClaudeUserMcp() {
100
87
  return this.readMcpEnvelope(join(this.homeDir, CLAUDE_USER_FILE));
101
88
  }
102
- // Frozen run snapshot. API key value is replaced by its source label so the
103
- // file is safe to inspect; only the resolution source is recorded.
104
89
  async writeSnapshot(resolved, stateDir) {
105
90
  const redacted = {
106
91
  ...resolved,
@@ -109,9 +94,6 @@ export class ConfigLoader {
109
94
  const path = join(stateDir, SNAPSHOT_FILE);
110
95
  await atomicWrite(path, `${JSON.stringify(redacted, null, 2)}\n`);
111
96
  }
112
- // Reads any JSON file whose only field we care about is `mcpServers` (Claude Code's
113
- // .mcp.json or the much larger ~/.claude.json). Missing file → null. Malformed JSON
114
- // is a hard error — we don't want to silently ignore a corrupted user file.
115
97
  async readMcpEnvelope(path) {
116
98
  let raw;
117
99
  try {
@@ -137,12 +119,6 @@ export class ConfigLoader {
137
119
  return envelope.data.mcpServers ?? null;
138
120
  }
139
121
  resolveMcpServers(sources) {
140
- // Precedence, lowest → highest:
141
- // 1. ~/.claude.json (user-scoped Claude Code config)
142
- // 2. ~/.aitm.json (user-scoped aitm config)
143
- // 3. ./.mcp.json (project-scoped Claude Code config, checked into git)
144
- // 4. ./.ai-task-master/config.json (project-scoped aitm config — final word)
145
- // Same name in two places: higher precedence wins; the lower is shadowed with a warn.
146
122
  const layers = [
147
123
  ['claude-user', sources.claudeUser],
148
124
  ['aitm-global', sources.aitmGlobal],
@@ -231,8 +207,6 @@ export class ConfigLoader {
231
207
  if (src.fast)
232
208
  merged.fast = src.fast;
233
209
  }
234
- // --model pins the `generic` tier — the fallback every other capability
235
- // inherits when not explicitly set. See docs/config.md §"Per-role models".
236
210
  if (cliOverrides.model)
237
211
  merged.generic = cliOverrides.model;
238
212
  return merged;
@@ -265,11 +239,8 @@ function isNotFound(err) {
265
239
  function formatZodError(err) {
266
240
  return err.issues.map((i) => `${i.path.join('.') || '<root>'}: ${i.message}`).join('; ');
267
241
  }
268
- // Permissive envelope for Claude Code config files: we only extract `mcpServers` and
269
- // ignore every other key (~/.claude.json especially has many auth/history fields).
270
242
  const McpEnvelopeSchema = z
271
243
  .object({
272
244
  mcpServers: McpServersSchema.optional(),
273
245
  })
274
246
  .passthrough();
275
- //# sourceMappingURL=config-loader.js.map
@@ -1,6 +1,3 @@
1
- // docs/commands/config.md, docs/config.md
2
- // Mutates ~/.aitm.json (or, with --project, ./.ai-task-master/config.json) for `aitm config set/unset`.
3
- // Atomic write: temp file + rename. Refuses to write unknown top-level keys.
4
1
  import { mkdir, readFile } from 'node:fs/promises';
5
2
  import { dirname, join } from 'node:path';
6
3
  import { ZodError } from 'zod';
@@ -110,7 +107,6 @@ function parseValue(v) {
110
107
  return JSON.parse(v);
111
108
  }
112
109
  catch {
113
- // Bare strings ("squash", "sk-or-...") aren't valid JSON; treat them as literal strings.
114
110
  return v;
115
111
  }
116
112
  }
@@ -175,4 +171,3 @@ function isNotFound(err) {
175
171
  function formatZodError(err) {
176
172
  return err.issues.map((i) => `${i.path.join('.') || '<root>'}: ${i.message}`).join('; ');
177
173
  }
178
- //# sourceMappingURL=config-writer.js.map
@@ -1,10 +1,3 @@
1
- // docs/config.md §Schema, docs/auth.md §LLM provider
2
- // Models are configured by *capability tier*, not by subagent role. The mapping
3
- // role → tier lives in src/credentials/credentials.ts.
4
- // generic — fallback for anything not otherwise specified
5
- // smart — best reasoning (Planner, Reviewer)
6
- // coding — code generation / edits (Worker)
7
- // fast — cheap routing / summarization (Orchestrator, toModelOutput compaction)
8
1
  import { z } from 'zod';
9
2
  import { McpServersSchema } from "../mcp/schema.js";
10
3
  export const CapabilityModelsSchema = z
@@ -27,12 +20,7 @@ export const ConfigFileSchema = z
27
20
  mergeMethod: MergeMethodSchema.optional(),
28
21
  stylePath: z.string().nullable().optional(),
29
22
  logLevel: LogLevelSchema.optional(),
30
- // How many PR groups may have a Worker running at the same time. Default 1 = sequential.
31
- // See src/loop/work-loop.ts and src/workspace/worktree-pool.ts.
32
23
  concurrency: z.number().int().positive().optional(),
33
- // External MCP servers to mount into subagent tool surfaces (client only — aitm is never
34
- // exposed as an MCP server). See docs/mcp.md and src/mcp/schema.ts.
35
24
  mcpServers: McpServersSchema.optional(),
36
25
  })
37
26
  .passthrough();
38
- //# sourceMappingURL=schema.js.map
@@ -1,7 +1,3 @@
1
- // docs/auth.md, docs/runtime.md, docs/config.md
2
- // Maps subagent role → capability tier → OpenRouter model handle.
3
- // Never reads config files or env directly — that's ConfigLoader's job.
4
- // SDK reference: docs/vendor/ai-sdk/chunk-09.md §"Subagents", chunk-04.md §"ToolLoopAgent".
5
1
  import { createOpenRouter } from '@openrouter/ai-sdk-provider';
6
2
  import { DEFAULT_MODELS } from "./defaults.js";
7
3
  export const ROLE_CAPABILITY = {
@@ -12,14 +8,10 @@ export const ROLE_CAPABILITY = {
12
8
  };
13
9
  export class Credentials {
14
10
  resolved;
15
- // Lazy: provider creation also asserts the API key is present, so callers that
16
- // only inspect role/capability mapping (tests, dry-run) don't need a real key.
17
11
  providerInstance;
18
12
  constructor(resolved) {
19
13
  this.resolved = resolved;
20
14
  }
21
- // Build a handle per role using ROLE_CAPABILITY. Capability fallback chain:
22
- // models[capability] → models.generic → built-in default.
23
15
  handles() {
24
16
  return {
25
17
  planner: this.modelFor('planner'),
@@ -35,13 +27,8 @@ export class Credentials {
35
27
  const modelId = this.resolved.models[capability] ||
36
28
  this.resolved.models.generic ||
37
29
  DEFAULT_MODELS[capability];
38
- // Subagents lean on structured output (Output.object). OpenRouter may route a model to a
39
- // provider — notably Amazon Bedrock — that rejects the AI SDK's structured-output request
40
- // (`output_config.format`), failing the Planner/Worker/Reviewer at random. Skip Bedrock so
41
- // those calls land on a provider that accepts the parameter.
42
30
  return this.provider().chat(modelId, { provider: { ignore: ['amazon-bedrock'] } });
43
31
  }
44
- // Lets CLI fail fast before any LLM call (docs/commands/start.md §Preconditions step 2).
45
32
  static assertApiKeyPresent(resolved) {
46
33
  if (!resolved.openrouterApiKey || resolved.openrouterApiKey.trim() === '') {
47
34
  throw new Error('OPENROUTER_API_KEY is missing. Set OPENROUTER_API_KEY in the environment, or run `aitm config set openrouterApiKey <key>` (get one at https://openrouter.ai/keys).');
@@ -55,4 +42,3 @@ export class Credentials {
55
42
  return this.providerInstance;
56
43
  }
57
44
  }
58
- //# sourceMappingURL=credentials.js.map
@@ -1,21 +1,6 @@
1
- // Canonical capability defaults — all OpenRouter routes (docs/auth.md §"LLM provider").
2
- // User-set models.{generic,smart,coding,fast} always wins; these only fill gaps.
3
- //
4
- // We don't fork defaults by AgentConfigFlavor (CLAUDE.md vs AGENTS.md). Flavor is a
5
- // *style* signal, not a *vendor* signal — every model goes through OpenRouter, so a
6
- // project's convention file does not constrain which model serves a request. The
7
- // flavor only affects which markdown is fed to subagent system prompts.
8
- //
9
- // docs/agent-config-detection.md, docs/config.md, docs/runtime.md
10
- // Tier mapping rationale (Claude family via OpenRouter — the most flexible coding stack today):
11
- // haiku → fast : routing, orchestration, summarization (toModelOutput compaction)
12
- // sonnet → generic : default fallback for any unspecified tier
13
- // opus → smart : Planner, Reviewer (architectural reasoning, critique)
14
- // opus → coding : Worker (best-in-class code generation)
15
1
  export const DEFAULT_MODELS = {
16
2
  fast: 'anthropic/claude-haiku-4.5',
17
3
  generic: 'anthropic/claude-sonnet-4.6',
18
4
  smart: 'anthropic/claude-opus-4.7',
19
5
  coding: 'anthropic/claude-opus-4.7',
20
6
  };
21
- //# sourceMappingURL=defaults.js.map
@@ -1,9 +1,3 @@
1
- // Shared atomic-file primitive. Temp file + fsync + rename so readers never see
2
- // a half-written file. Used by ConfigLoader.writeSnapshot, ConfigWriter, StateStore.
3
- //
4
- // Temp name carries a random suffix so concurrent writes to the same path don't
5
- // clobber each other's in-flight temp file. Mode 0o600 keeps secret-bearing
6
- // config files owner-readable only on POSIX.
7
1
  import { randomUUID } from 'node:crypto';
8
2
  import { open, rename, rm } from 'node:fs/promises';
9
3
  export async function atomicWrite(path, contents) {
@@ -24,4 +18,3 @@ export async function atomicWrite(path, contents) {
24
18
  throw err;
25
19
  }
26
20
  }
27
- //# sourceMappingURL=atomic-write.js.map
@@ -1,4 +1,3 @@
1
- // docs/github-integration.md §"Result typing" — never raw stderr; always domain errors.
2
1
  export class PrNotFound extends Error {
3
2
  name = 'PrNotFound';
4
3
  }
@@ -17,4 +16,3 @@ export class GhAuthRequired extends Error {
17
16
  export class MergeConflict extends Error {
18
17
  name = 'MergeConflict';
19
18
  }
20
- //# sourceMappingURL=errors.js.map
@@ -1,5 +1,3 @@
1
- // docs/github-integration.md, docs/auth.md §"GitHub"
2
- // Only module allowed to shell out to gh. Uses execa (docs/runtime.md — Bun.$ forbidden in src/).
3
1
  import { ExecaError, execa } from 'execa';
4
2
  import { z } from 'zod';
5
3
  import { CiFailed, MergeConflict } from "./errors.js";
@@ -34,8 +32,6 @@ export class GitHubClient {
34
32
  cwd;
35
33
  runCmd;
36
34
  sleep;
37
- // Capability matrix — docs/github-integration.md §"Capabilities".
38
- // Backoff — docs/github-integration.md §"Rate limits" (1s, doubling, 60s cap).
39
35
  constructor(cwd, runCmd = defaultRunCmd, sleep = defaultSleep) {
40
36
  this.cwd = cwd;
41
37
  this.runCmd = runCmd;
@@ -94,9 +90,6 @@ export class GitHubClient {
94
90
  args.push('--draft');
95
91
  for (const label of labels)
96
92
  args.push('--label', label);
97
- // `gh pr create --label X` fails if X doesn't exist yet — which it won't on a fresh repo the
98
- // first time aitm opens a PR. Ensure each label exists first (idempotent via --force; the
99
- // result is intentionally not checked so a labels-permission gap doesn't block PR creation).
100
93
  for (const label of labels) {
101
94
  await this.runCmd('gh', ['label', 'create', label, '--force'], { cwd: this.cwd });
102
95
  }
@@ -104,7 +97,6 @@ export class GitHubClient {
104
97
  if (r.exitCode !== 0) {
105
98
  throw new Error(`gh pr create failed: ${r.stderr.trim() || r.stdout.trim()}`);
106
99
  }
107
- // gh prints the PR URL to stdout; we re-fetch to get the full typed shape.
108
100
  const pr = await this.getPrForBranch(input.head);
109
101
  if (!pr) {
110
102
  throw new Error(`gh pr create succeeded for ${input.head} but PR lookup returned null (stdout: ${r.stdout.trim()})`);
@@ -115,8 +107,6 @@ export class GitHubClient {
115
107
  let delay = CHECKS_INITIAL_DELAY_MS;
116
108
  while (true) {
117
109
  const r = await this.runCmd('gh', ['pr', 'checks', String(pr), '--json', 'bucket,name,state'], { cwd: this.cwd });
118
- // `gh pr checks` exits 8 when any check fails but still emits JSON on stdout. Treat any
119
- // exit code as "command ran" if stdout parses; otherwise propagate the failure.
120
110
  const rows = tryParseChecks(r.stdout);
121
111
  if (!rows) {
122
112
  throw new Error(`gh pr checks failed: ${r.stderr.trim() || r.stdout.trim()}`);
@@ -133,8 +123,6 @@ export class GitHubClient {
133
123
  }
134
124
  async listUnresolvedThreads(pr) {
135
125
  const { owner, name } = await this.repoMeta();
136
- // GitHub caps connections at 100 nodes per page — page through threads and
137
- // their comments to avoid silently dropping data on large PRs.
138
126
  const threads = await this.paginateReviewThreads(owner, name, pr);
139
127
  const unresolved = threads.filter((t) => !t.isResolved);
140
128
  for (const thread of unresolved) {
@@ -256,19 +244,14 @@ export class GitHubClient {
256
244
  const r = await this.runCmd('gh', ['auth', 'status', '--hostname', 'github.com'], {
257
245
  cwd: this.cwd,
258
246
  });
259
- // `gh auth status` writes its human-readable summary to stderr; stdout is usually empty.
260
247
  const text = `${r.stderr}\n${r.stdout}`;
261
248
  const scopes = parseScopes(text);
262
249
  return { ok: r.exitCode === 0, scopes };
263
250
  }
264
251
  }
265
- // `gh pr view` exits non-zero with messages like:
266
- // "no pull requests found for branch <name>"
267
- // "GraphQL: Could not resolve to a PullRequest..."
268
252
  function isPrNotFoundStderr(stderr) {
269
253
  return /no pull requests? found|could not resolve to a pullrequest|no open pull requests/i.test(stderr);
270
254
  }
271
- // `gh auth status` line shape: " - Token scopes: 'repo', 'workflow', 'read:org'"
272
255
  function parseScopes(text) {
273
256
  const match = text.match(/Token scopes:\s*([^\n]+)/i);
274
257
  if (!match?.[1])
@@ -281,8 +264,6 @@ function parseScopes(text) {
281
264
  }
282
265
  return scopes;
283
266
  }
284
- // Wire shapes for `gh pr checks --json bucket,name,state`. The bucket field is the gh CLI's
285
- // normalized status across providers (Actions, Circle, etc.); CheckStatus is our domain.
286
267
  const CheckBucketSchema = z.enum(['pass', 'fail', 'pending', 'cancel', 'skipping']);
287
268
  const CheckRowSchema = z.object({
288
269
  bucket: CheckBucketSchema,
@@ -331,7 +312,6 @@ function summarizeFailures(rows) {
331
312
  return 'unknown';
332
313
  return bad.map((r) => `${r.name}=${r.bucket}`).join(', ');
333
314
  }
334
- // `gh repo view --json owner,name` returns `{ owner: { login }, name }`.
335
315
  const RepoOwnerNameSchema = z.object({
336
316
  owner: z.object({ login: z.string() }),
337
317
  name: z.string(),
@@ -414,4 +394,3 @@ const GqlThreadCommentsResponseSchema = z.object({
414
394
  }),
415
395
  }),
416
396
  });
417
- //# sourceMappingURL=github-client.js.map
@@ -1,4 +1,3 @@
1
- // docs/github-integration.md — JSON shapes returned by `gh` (parsed through Zod).
2
1
  import { z } from 'zod';
3
2
  export const PrStateSchema = z.enum(['OPEN', 'CLOSED', 'MERGED']);
4
3
  export const PullRequestSchema = z.object({
@@ -20,4 +19,3 @@ export const ReviewThreadSchema = z.object({
20
19
  path: z.string().nullable(),
21
20
  comments: z.array(ReviewCommentSchema),
22
21
  });
23
- //# sourceMappingURL=schema.js.map
package/dist/index.js CHANGED
@@ -1,4 +1,3 @@
1
- // Public surface. Keep this list narrow — most internals are not stable yet.
2
1
  export { AgentConfigDetector } from "./agent-config/agent-config-detector.js";
3
2
  export { main } from "./cli/cli.js";
4
3
  export { Compactor } from "./compaction/compactor.js";
@@ -19,4 +18,3 @@ export { datetimeTool } from "./tools/datetime.js";
19
18
  export { DEFAULT_IMPERSONATE_TARGETS, fetchHtmlTool, isFetchHtmlAvailable, } from "./tools/fetch-html.js";
20
19
  export { DEFAULT_STEALTH_HEADERS, webFetchTool } from "./tools/web-fetch.js";
21
20
  export { WorktreePool } from "./workspace/worktree-pool.js";
22
- //# sourceMappingURL=index.js.map
@@ -1,6 +1,3 @@
1
- // docs/state.md (logs dir), docs/auth.md §Security (redaction policy)
2
- // Structured logs to .ai-task-master/logs/run-{ts}.log; user-facing status to stdout.
3
- // Redact /key|token|secret|authorization/i before serializing.
4
1
  import { appendFile, mkdir } from 'node:fs/promises';
5
2
  import { dirname } from 'node:path';
6
3
  const LEVEL_RANK = {
@@ -42,7 +39,6 @@ export class Logger {
42
39
  const out = redactValue(fields);
43
40
  return out;
44
41
  }
45
- // Flush pending file writes — tests and shutdown hooks await this.
46
42
  async flush() {
47
43
  await this.writeTail;
48
44
  if (this.lastError) {
@@ -86,7 +82,6 @@ export class Logger {
86
82
  await appendFile(file, line);
87
83
  }
88
84
  catch (err) {
89
- // Surface failures via flush() but never crash callers.
90
85
  this.lastError = err instanceof Error ? err : new Error(String(err));
91
86
  }
92
87
  });
@@ -120,4 +115,3 @@ function redactValue(value, seen = new WeakSet()) {
120
115
  function bigintReplacer(_key, value) {
121
116
  return typeof value === 'bigint' ? value.toString() : value;
122
117
  }
123
- //# sourceMappingURL=logger.js.map