@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.
- package/dist/agent-config/agent-config-detector.js +0 -3
- package/dist/cli/args.js +0 -10
- package/dist/cli/cli.js +0 -8
- package/dist/cli/commands.js +0 -46
- package/dist/compaction/compactor.js +0 -24
- package/dist/config/config-loader.js +0 -29
- package/dist/config/config-writer.js +0 -5
- package/dist/config/schema.js +0 -12
- package/dist/credentials/credentials.js +0 -14
- package/dist/credentials/defaults.js +0 -15
- package/dist/fs/atomic-write.js +0 -7
- package/dist/github/errors.js +0 -2
- package/dist/github/github-client.js +0 -21
- package/dist/github/schema.js +0 -2
- package/dist/index.js +0 -2
- package/dist/logger/logger.js +0 -6
- package/dist/loop/run-loop-adapter.js +0 -45
- package/dist/loop/take-over-flow.js +0 -37
- package/dist/loop/work-loop.js +0 -36
- package/dist/mcp/mcp-client.js +0 -13
- package/dist/mcp/schema.js +0 -15
- package/dist/openrouter/client.js +0 -4
- package/dist/openrouter/model-limits.js +0 -4
- package/dist/openrouter/server-tools.js +0 -16
- package/dist/orchestrator/orchestrator.js +0 -26
- package/dist/orchestrator/subagent-tools.js +0 -22
- package/dist/orchestrator/system-prompts.js +0 -3
- package/dist/plan/plan-graph.js +0 -9
- package/dist/plan/schema.js +0 -8
- package/dist/state/schema.js +0 -5
- package/dist/state/state-store.js +0 -8
- package/dist/subagents/factory.js +0 -9
- package/dist/subagents/planner.js +0 -9
- package/dist/subagents/reviewer.js +0 -25
- package/dist/subagents/worker.js +0 -27
- package/dist/testing/temp-repo.js +0 -4
- package/dist/tools/datetime.js +0 -9
- package/dist/tools/fetch-html.js +0 -24
- package/dist/tools/github-thread-tool.js +0 -15
- package/dist/tools/web-fetch.js +0 -32
- package/dist/workspace/worktree-pool.js +0 -21
- package/package.json +2 -2
- package/dist/agent-config/agent-config-detector.js.map +0 -1
- package/dist/cli/args.js.map +0 -1
- package/dist/cli/cli.js.map +0 -1
- package/dist/cli/commands.js.map +0 -1
- package/dist/compaction/compactor.js.map +0 -1
- package/dist/config/config-loader.js.map +0 -1
- package/dist/config/config-writer.js.map +0 -1
- package/dist/config/schema.js.map +0 -1
- package/dist/credentials/credentials.js.map +0 -1
- package/dist/credentials/defaults.js.map +0 -1
- package/dist/fs/atomic-write.js.map +0 -1
- package/dist/github/errors.js.map +0 -1
- package/dist/github/github-client.js.map +0 -1
- package/dist/github/schema.js.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/logger/logger.js.map +0 -1
- package/dist/loop/run-loop-adapter.js.map +0 -1
- package/dist/loop/take-over-flow.js.map +0 -1
- package/dist/loop/work-loop.js.map +0 -1
- package/dist/mcp/mcp-client.js.map +0 -1
- package/dist/mcp/schema.js.map +0 -1
- package/dist/openrouter/client.js.map +0 -1
- package/dist/openrouter/model-limits.js.map +0 -1
- package/dist/openrouter/server-tools.js.map +0 -1
- package/dist/orchestrator/orchestrator.js.map +0 -1
- package/dist/orchestrator/subagent-tools.js.map +0 -1
- package/dist/orchestrator/system-prompts.js.map +0 -1
- package/dist/plan/plan-graph.js.map +0 -1
- package/dist/plan/schema.js.map +0 -1
- package/dist/state/schema.js.map +0 -1
- package/dist/state/state-store.js.map +0 -1
- package/dist/subagents/factory.js.map +0 -1
- package/dist/subagents/planner.js.map +0 -1
- package/dist/subagents/reviewer.js.map +0 -1
- package/dist/subagents/worker.js.map +0 -1
- package/dist/testing/temp-repo.js.map +0 -1
- package/dist/tools/datetime.js.map +0 -1
- package/dist/tools/fetch-html.js.map +0 -1
- package/dist/tools/github-thread-tool.js.map +0 -1
- package/dist/tools/web-fetch.js.map +0 -1
- 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
|
package/dist/cli/commands.js
CHANGED
|
@@ -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
|
package/dist/config/schema.js
CHANGED
|
@@ -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
|
package/dist/fs/atomic-write.js
CHANGED
|
@@ -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
|
package/dist/github/errors.js
CHANGED
|
@@ -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
|
package/dist/github/schema.js
CHANGED
|
@@ -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
|
package/dist/logger/logger.js
CHANGED
|
@@ -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
|