codealmanac 0.2.0 → 0.2.2
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/README.md +96 -91
- package/dist/{chunk-D3B2EEHL.js → chunk-B2AGSRXL.js} +2 -2
- package/dist/{chunk-3C5SY5SE.js → chunk-KQUVMF27.js} +5 -2
- package/dist/chunk-KQUVMF27.js.map +1 -0
- package/dist/{cli-CMGYLJSN.js → cli-3XAVBTYG.js} +189 -14
- package/dist/cli-3XAVBTYG.js.map +1 -0
- package/dist/codealmanac.js +1 -1
- package/dist/{doctor-BTH7GCFV.js → doctor-3BYSF3JD.js} +2 -2
- package/dist/{register-commands-7SKQLQW4.js → register-commands-7QCIENRZ.js} +433 -48
- package/dist/register-commands-7QCIENRZ.js.map +1 -0
- package/dist/{wiki-IPSRRGOT.js → wiki-IGNRNLUZ.js} +2 -2
- package/package.json +3 -2
- package/dist/chunk-3C5SY5SE.js.map +0 -1
- package/dist/cli-CMGYLJSN.js.map +0 -1
- package/dist/register-commands-7SKQLQW4.js.map +0 -1
- /package/dist/{chunk-D3B2EEHL.js.map → chunk-B2AGSRXL.js.map} +0 -0
- /package/dist/{doctor-BTH7GCFV.js.map → doctor-3BYSF3JD.js.map} +0 -0
- /package/dist/{wiki-IPSRRGOT.js.map → wiki-IGNRNLUZ.js.map} +0 -0
|
@@ -33,10 +33,10 @@ import {
|
|
|
33
33
|
titleCase,
|
|
34
34
|
toKebabCase,
|
|
35
35
|
writeTopicsFile
|
|
36
|
-
} from "./chunk-
|
|
36
|
+
} from "./chunk-KQUVMF27.js";
|
|
37
37
|
import {
|
|
38
38
|
runDoctor
|
|
39
|
-
} from "./chunk-
|
|
39
|
+
} from "./chunk-B2AGSRXL.js";
|
|
40
40
|
import "./chunk-V3QOQSXI.js";
|
|
41
41
|
import "./chunk-4CODZRHH.js";
|
|
42
42
|
import {
|
|
@@ -1905,6 +1905,7 @@ async function loadPrompt(name) {
|
|
|
1905
1905
|
// src/agent/sdk.ts
|
|
1906
1906
|
import { spawn } from "child_process";
|
|
1907
1907
|
import { query } from "@anthropic-ai/claude-agent-sdk";
|
|
1908
|
+
var DEFAULT_AGENT_MODEL = "claude-sonnet-4-6";
|
|
1908
1909
|
async function runAgent(opts) {
|
|
1909
1910
|
const provider = opts.provider ?? "claude";
|
|
1910
1911
|
if (provider === "codex") {
|
|
@@ -1924,7 +1925,7 @@ async function runClaudeAgent(opts) {
|
|
|
1924
1925
|
allowedTools: opts.allowedTools,
|
|
1925
1926
|
agents: opts.agents ?? {},
|
|
1926
1927
|
cwd: opts.cwd,
|
|
1927
|
-
model: opts.model ??
|
|
1928
|
+
model: opts.model ?? DEFAULT_AGENT_MODEL,
|
|
1928
1929
|
maxTurns: opts.maxTurns ?? 100,
|
|
1929
1930
|
...claudeExecutable !== void 0 ? { pathToClaudeCodeExecutable: claudeExecutable } : {},
|
|
1930
1931
|
env: {
|
|
@@ -2043,6 +2044,7 @@ function runJsonlCli(opts) {
|
|
|
2043
2044
|
let turns = 0;
|
|
2044
2045
|
let result = "";
|
|
2045
2046
|
let sessionId;
|
|
2047
|
+
let usage;
|
|
2046
2048
|
let success = false;
|
|
2047
2049
|
let finalSeen = false;
|
|
2048
2050
|
let error;
|
|
@@ -2051,6 +2053,9 @@ function runJsonlCli(opts) {
|
|
|
2051
2053
|
if (sessionId === void 0 && typeof msg.session_id === "string" && msg.session_id.length > 0) {
|
|
2052
2054
|
sessionId = msg.session_id;
|
|
2053
2055
|
}
|
|
2056
|
+
if (sessionId === void 0 && typeof msg.thread_id === "string" && msg.thread_id.length > 0) {
|
|
2057
|
+
sessionId = msg.thread_id;
|
|
2058
|
+
}
|
|
2054
2059
|
const final = opts.parseFinal(msg);
|
|
2055
2060
|
if (final === null) return;
|
|
2056
2061
|
finalSeen = true;
|
|
@@ -2058,6 +2063,7 @@ function runJsonlCli(opts) {
|
|
|
2058
2063
|
if (final.turns !== void 0) turns = final.turns;
|
|
2059
2064
|
if (final.result !== void 0) result = final.result;
|
|
2060
2065
|
if (final.sessionId !== void 0) sessionId = final.sessionId;
|
|
2066
|
+
if (final.usage !== void 0) usage = final.usage;
|
|
2061
2067
|
if (final.success !== void 0) success = final.success;
|
|
2062
2068
|
if (final.error !== void 0) error = final.error;
|
|
2063
2069
|
};
|
|
@@ -2091,6 +2097,7 @@ function runJsonlCli(opts) {
|
|
|
2091
2097
|
turns,
|
|
2092
2098
|
result,
|
|
2093
2099
|
sessionId,
|
|
2100
|
+
usage,
|
|
2094
2101
|
error: err.code === "ENOENT" ? `${opts.command} not found on PATH` : err.message
|
|
2095
2102
|
});
|
|
2096
2103
|
});
|
|
@@ -2103,7 +2110,7 @@ function runJsonlCli(opts) {
|
|
|
2103
2110
|
}
|
|
2104
2111
|
}
|
|
2105
2112
|
if (code === 0 && finalSeen && success) {
|
|
2106
|
-
resolve({ success, cost, turns, result, sessionId });
|
|
2113
|
+
resolve({ success, cost, turns, result, sessionId, usage });
|
|
2107
2114
|
return;
|
|
2108
2115
|
}
|
|
2109
2116
|
const firstStderr = stderr.trim().split("\n")[0];
|
|
@@ -2113,6 +2120,7 @@ function runJsonlCli(opts) {
|
|
|
2113
2120
|
turns,
|
|
2114
2121
|
result,
|
|
2115
2122
|
sessionId,
|
|
2123
|
+
usage,
|
|
2116
2124
|
error: error ?? (firstStderr !== void 0 && firstStderr.length > 0 ? firstStderr : `${opts.command} exited ${code ?? 1}`)
|
|
2117
2125
|
});
|
|
2118
2126
|
});
|
|
@@ -2130,7 +2138,7 @@ function parseCodexFinal(msg) {
|
|
|
2130
2138
|
return null;
|
|
2131
2139
|
}
|
|
2132
2140
|
if (msg.type === "turn.completed") {
|
|
2133
|
-
return { success: true };
|
|
2141
|
+
return { success: true, turns: 1, usage: parseUsage(msg.usage) };
|
|
2134
2142
|
}
|
|
2135
2143
|
if (msg.type === "turn.failed" || msg.type === "error") {
|
|
2136
2144
|
return {
|
|
@@ -2140,13 +2148,29 @@ function parseCodexFinal(msg) {
|
|
|
2140
2148
|
}
|
|
2141
2149
|
return null;
|
|
2142
2150
|
}
|
|
2151
|
+
function parseUsage(value) {
|
|
2152
|
+
if (value === null || typeof value !== "object") return void 0;
|
|
2153
|
+
const obj = value;
|
|
2154
|
+
return {
|
|
2155
|
+
inputTokens: numberField(obj, "input_tokens") ?? numberField(obj, "inputTokens"),
|
|
2156
|
+
cachedInputTokens: numberField(obj, "cached_input_tokens") ?? numberField(obj, "cachedInputTokens") ?? numberField(obj, "cacheReadTokens"),
|
|
2157
|
+
outputTokens: numberField(obj, "output_tokens") ?? numberField(obj, "outputTokens"),
|
|
2158
|
+
reasoningOutputTokens: numberField(obj, "reasoning_output_tokens") ?? numberField(obj, "reasoningOutputTokens")
|
|
2159
|
+
};
|
|
2160
|
+
}
|
|
2161
|
+
function numberField(input, key) {
|
|
2162
|
+
const value = input[key];
|
|
2163
|
+
return typeof value === "number" ? value : void 0;
|
|
2164
|
+
}
|
|
2143
2165
|
function parseCursorFinal(msg) {
|
|
2144
2166
|
if (msg.type !== "result") return null;
|
|
2145
2167
|
const isError = msg.is_error === true || msg.subtype !== "success";
|
|
2146
2168
|
return {
|
|
2147
2169
|
success: !isError,
|
|
2170
|
+
turns: 1,
|
|
2148
2171
|
result: typeof msg.result === "string" ? msg.result : "",
|
|
2149
2172
|
sessionId: typeof msg.session_id === "string" ? msg.session_id : void 0,
|
|
2173
|
+
usage: parseUsage(msg.usage),
|
|
2150
2174
|
error: isError ? typeof msg.result === "string" ? msg.result : `cursor result: ${String(msg.subtype ?? "error")}` : void 0
|
|
2151
2175
|
};
|
|
2152
2176
|
}
|
|
@@ -2415,8 +2439,39 @@ async function resolveAgentSelection(args) {
|
|
|
2415
2439
|
function formatFinalLine(result, logPath, repoRoot) {
|
|
2416
2440
|
const status = result.success ? "done" : "failed";
|
|
2417
2441
|
const rel = relative(repoRoot, logPath);
|
|
2418
|
-
const
|
|
2419
|
-
return `[${status}]
|
|
2442
|
+
const usage = formatRunUsage(result);
|
|
2443
|
+
return `[${status}] ${usage} (transcript: ${rel})`;
|
|
2444
|
+
}
|
|
2445
|
+
function formatRunUsage(result) {
|
|
2446
|
+
const parts = [];
|
|
2447
|
+
if (result.cost > 0 || result.usage === void 0) {
|
|
2448
|
+
parts.push(`cost: $${result.cost.toFixed(3)}`);
|
|
2449
|
+
}
|
|
2450
|
+
parts.push(`turns: ${result.turns}`);
|
|
2451
|
+
const usage = result.usage;
|
|
2452
|
+
if (usage !== void 0) {
|
|
2453
|
+
const tokenParts = [];
|
|
2454
|
+
if (usage.inputTokens !== void 0) {
|
|
2455
|
+
tokenParts.push(`${usage.inputTokens.toLocaleString("en-US")} in`);
|
|
2456
|
+
}
|
|
2457
|
+
if (usage.outputTokens !== void 0) {
|
|
2458
|
+
tokenParts.push(`${usage.outputTokens.toLocaleString("en-US")} out`);
|
|
2459
|
+
}
|
|
2460
|
+
if (usage.cachedInputTokens !== void 0) {
|
|
2461
|
+
tokenParts.push(
|
|
2462
|
+
`${usage.cachedInputTokens.toLocaleString("en-US")} cached`
|
|
2463
|
+
);
|
|
2464
|
+
}
|
|
2465
|
+
if (usage.reasoningOutputTokens !== void 0) {
|
|
2466
|
+
tokenParts.push(
|
|
2467
|
+
`${usage.reasoningOutputTokens.toLocaleString("en-US")} reasoning`
|
|
2468
|
+
);
|
|
2469
|
+
}
|
|
2470
|
+
if (tokenParts.length > 0) {
|
|
2471
|
+
parts.push(`tokens: ${tokenParts.join(", ")}`);
|
|
2472
|
+
}
|
|
2473
|
+
}
|
|
2474
|
+
return parts.join(", ");
|
|
2420
2475
|
}
|
|
2421
2476
|
async function countMarkdownPages(pagesDir) {
|
|
2422
2477
|
try {
|
|
@@ -2473,14 +2528,16 @@ var StreamingFormatter = class {
|
|
|
2473
2528
|
}
|
|
2474
2529
|
return;
|
|
2475
2530
|
}
|
|
2476
|
-
if (msg.type === "
|
|
2477
|
-
|
|
2478
|
-
|
|
2479
|
-
|
|
2480
|
-
|
|
2481
|
-
|
|
2482
|
-
|
|
2483
|
-
|
|
2531
|
+
if (msg.type === "item.started" && isRecord(msg.item)) {
|
|
2532
|
+
this.handleCodexItemStarted(msg.item);
|
|
2533
|
+
return;
|
|
2534
|
+
}
|
|
2535
|
+
if (msg.type === "item.completed" && isRecord(msg.item)) {
|
|
2536
|
+
this.handleCodexItemCompleted(msg.item);
|
|
2537
|
+
return;
|
|
2538
|
+
}
|
|
2539
|
+
if (msg.type === "tool_call") {
|
|
2540
|
+
this.handleCursorToolCall(msg);
|
|
2484
2541
|
return;
|
|
2485
2542
|
}
|
|
2486
2543
|
}
|
|
@@ -2495,6 +2552,33 @@ var StreamingFormatter = class {
|
|
|
2495
2552
|
}
|
|
2496
2553
|
const summary = formatToolSummary(name, input);
|
|
2497
2554
|
this.sink.write(`[${this.currentAgent}] ${summary}
|
|
2555
|
+
`);
|
|
2556
|
+
}
|
|
2557
|
+
handleCodexItemStarted(item) {
|
|
2558
|
+
if (item.type !== "command_execution") return;
|
|
2559
|
+
const command = stringField(item, "command") ?? "?";
|
|
2560
|
+
this.sink.write(`[${this.currentAgent}] bash ${truncate(command, 80)}
|
|
2561
|
+
`);
|
|
2562
|
+
}
|
|
2563
|
+
handleCodexItemCompleted(item) {
|
|
2564
|
+
if (item.type !== "file_change") return;
|
|
2565
|
+
const changes = Array.isArray(item.changes) ? item.changes : [];
|
|
2566
|
+
for (const change of changes) {
|
|
2567
|
+
if (!isRecord(change)) continue;
|
|
2568
|
+
const rawPath = stringField(change, "path") ?? "?";
|
|
2569
|
+
const kind = stringField(change, "kind") ?? "edit";
|
|
2570
|
+
const verb = kind === "add" ? "writing" : kind === "delete" ? "deleting" : "editing";
|
|
2571
|
+
this.sink.write(
|
|
2572
|
+
`[${this.currentAgent}] ${verb} ${formatCodexPath(rawPath)}
|
|
2573
|
+
`
|
|
2574
|
+
);
|
|
2575
|
+
}
|
|
2576
|
+
}
|
|
2577
|
+
handleCursorToolCall(msg) {
|
|
2578
|
+
if (msg.subtype !== "started" || !isRecord(msg.tool_call)) return;
|
|
2579
|
+
const summary = formatCursorToolSummary(msg.tool_call);
|
|
2580
|
+
if (summary === void 0) return;
|
|
2581
|
+
this.sink.write(`[${this.currentAgent}] ${summary}
|
|
2498
2582
|
`);
|
|
2499
2583
|
}
|
|
2500
2584
|
};
|
|
@@ -2538,18 +2622,57 @@ function formatToolSummary(name, input) {
|
|
|
2538
2622
|
}
|
|
2539
2623
|
case "Bash": {
|
|
2540
2624
|
const command = stringField(input, "command") ?? "?";
|
|
2541
|
-
|
|
2542
|
-
return `bash ${trimmed}`;
|
|
2625
|
+
return `bash ${truncate(command, 80)}`;
|
|
2543
2626
|
}
|
|
2544
2627
|
default: {
|
|
2545
2628
|
return name;
|
|
2546
2629
|
}
|
|
2547
2630
|
}
|
|
2548
2631
|
}
|
|
2632
|
+
function formatCursorToolSummary(toolCall) {
|
|
2633
|
+
if (isRecord(toolCall.readToolCall)) {
|
|
2634
|
+
const args = recordField(toolCall.readToolCall, "args");
|
|
2635
|
+
return `reading ${stringField(args, "path") ?? "?"}`;
|
|
2636
|
+
}
|
|
2637
|
+
if (isRecord(toolCall.editToolCall)) {
|
|
2638
|
+
const args = recordField(toolCall.editToolCall, "args");
|
|
2639
|
+
return `editing ${formatCodexPath(stringField(args, "path") ?? "?")}`;
|
|
2640
|
+
}
|
|
2641
|
+
if (isRecord(toolCall.globToolCall)) {
|
|
2642
|
+
const args = recordField(toolCall.globToolCall, "args");
|
|
2643
|
+
return `glob ${stringField(args, "globPattern") ?? "?"}`;
|
|
2644
|
+
}
|
|
2645
|
+
if (isRecord(toolCall.grepToolCall)) {
|
|
2646
|
+
const args = recordField(toolCall.grepToolCall, "args");
|
|
2647
|
+
return `grep ${stringField(args, "pattern") ?? "?"}`;
|
|
2648
|
+
}
|
|
2649
|
+
if (isRecord(toolCall.shellToolCall)) {
|
|
2650
|
+
const args = recordField(toolCall.shellToolCall, "args");
|
|
2651
|
+
const description = stringField(toolCall.shellToolCall, "description");
|
|
2652
|
+
const command = stringField(args, "command") ?? description ?? "?";
|
|
2653
|
+
return `bash ${truncate(command, 80)}`;
|
|
2654
|
+
}
|
|
2655
|
+
return void 0;
|
|
2656
|
+
}
|
|
2657
|
+
function truncate(value, max) {
|
|
2658
|
+
return value.length > max ? `${value.slice(0, max - 3)}...` : value;
|
|
2659
|
+
}
|
|
2660
|
+
function formatCodexPath(value) {
|
|
2661
|
+
const marker = "/.almanac/";
|
|
2662
|
+
const idx = value.indexOf(marker);
|
|
2663
|
+
if (idx !== -1) {
|
|
2664
|
+
return `.almanac/${value.slice(idx + marker.length)}`;
|
|
2665
|
+
}
|
|
2666
|
+
return value;
|
|
2667
|
+
}
|
|
2549
2668
|
function stringField(input, key) {
|
|
2550
2669
|
const value = input[key];
|
|
2551
2670
|
return typeof value === "string" ? value : void 0;
|
|
2552
2671
|
}
|
|
2672
|
+
function recordField(input, key) {
|
|
2673
|
+
const value = input[key];
|
|
2674
|
+
return isRecord(value) ? value : {};
|
|
2675
|
+
}
|
|
2553
2676
|
function isRecord(value) {
|
|
2554
2677
|
return value !== null && typeof value === "object";
|
|
2555
2678
|
}
|
|
@@ -2558,12 +2681,219 @@ function isRecord(value) {
|
|
|
2558
2681
|
import { createHash } from "crypto";
|
|
2559
2682
|
import {
|
|
2560
2683
|
createWriteStream as createWriteStream2,
|
|
2561
|
-
existsSync as
|
|
2684
|
+
existsSync as existsSync7,
|
|
2562
2685
|
statSync
|
|
2563
2686
|
} from "fs";
|
|
2564
|
-
import { mkdir as
|
|
2687
|
+
import { mkdir as mkdir4, readFile as readFile7, readdir as readdir3, stat } from "fs/promises";
|
|
2565
2688
|
import { homedir } from "os";
|
|
2566
|
-
import { basename as basename3, join as
|
|
2689
|
+
import { basename as basename3, join as join8, relative as relative3 } from "path";
|
|
2690
|
+
|
|
2691
|
+
// src/commands/captureStatus.ts
|
|
2692
|
+
import { existsSync as existsSync6 } from "fs";
|
|
2693
|
+
import { mkdir as mkdir3, readFile as readFile6, readdir as readdir2, rename as rename2, writeFile as writeFile3 } from "fs/promises";
|
|
2694
|
+
import { dirname, join as join7, relative as relative2 } from "path";
|
|
2695
|
+
function captureStatePath(dir, stem) {
|
|
2696
|
+
return join7(dir, `.capture-${stem}.state.json`);
|
|
2697
|
+
}
|
|
2698
|
+
async function writeCaptureRunRecord(path2, record) {
|
|
2699
|
+
await mkdir3(dirname(path2), { recursive: true });
|
|
2700
|
+
const tmp = `${path2}.tmp-${process.pid}`;
|
|
2701
|
+
await writeFile3(tmp, `${JSON.stringify(record, null, 2)}
|
|
2702
|
+
`, "utf8");
|
|
2703
|
+
await rename2(tmp, path2);
|
|
2704
|
+
}
|
|
2705
|
+
function buildStartedCaptureRecord(args) {
|
|
2706
|
+
return {
|
|
2707
|
+
version: 1,
|
|
2708
|
+
kind: "capture",
|
|
2709
|
+
status: "running",
|
|
2710
|
+
sessionId: args.sessionId ?? args.stem,
|
|
2711
|
+
repoRoot: args.repoRoot,
|
|
2712
|
+
pid: process.pid,
|
|
2713
|
+
model: args.model ?? DEFAULT_AGENT_MODEL,
|
|
2714
|
+
transcriptPath: args.transcriptPath,
|
|
2715
|
+
startedAt: args.startedAt.toISOString(),
|
|
2716
|
+
logPath: join7(args.almanacDir, `.capture-${args.stem}.log`),
|
|
2717
|
+
jsonlPath: join7(args.almanacDir, `.capture-${args.stem}.jsonl`)
|
|
2718
|
+
};
|
|
2719
|
+
}
|
|
2720
|
+
function finishCaptureRecord(args) {
|
|
2721
|
+
const started = Date.parse(args.record.startedAt);
|
|
2722
|
+
const finished = args.finishedAt.getTime();
|
|
2723
|
+
return {
|
|
2724
|
+
...args.record,
|
|
2725
|
+
status: args.status,
|
|
2726
|
+
finishedAt: args.finishedAt.toISOString(),
|
|
2727
|
+
durationMs: Number.isFinite(started) ? Math.max(0, finished - started) : void 0,
|
|
2728
|
+
summary: args.summary,
|
|
2729
|
+
error: args.error
|
|
2730
|
+
};
|
|
2731
|
+
}
|
|
2732
|
+
async function runCaptureStatus(options) {
|
|
2733
|
+
const repoRoot = findNearestAlmanacDir(options.cwd);
|
|
2734
|
+
if (repoRoot === null) {
|
|
2735
|
+
return {
|
|
2736
|
+
stdout: "",
|
|
2737
|
+
stderr: "almanac: no .almanac/ found in this directory or any parent. Run 'almanac bootstrap' first.\n",
|
|
2738
|
+
exitCode: 1
|
|
2739
|
+
};
|
|
2740
|
+
}
|
|
2741
|
+
const almanacDir = getRepoAlmanacDir(repoRoot);
|
|
2742
|
+
const records = await readCaptureRecords(almanacDir);
|
|
2743
|
+
const now = options.now?.() ?? /* @__PURE__ */ new Date();
|
|
2744
|
+
const isPidAlive = options.isPidAlive ?? defaultIsPidAlive;
|
|
2745
|
+
const views = records.map((record) => toView(record, repoRoot, now, isPidAlive)).sort((a, b) => b.sortTime - a.sortTime);
|
|
2746
|
+
if (options.json === true) {
|
|
2747
|
+
return {
|
|
2748
|
+
stdout: `${JSON.stringify(
|
|
2749
|
+
{
|
|
2750
|
+
repo: repoRoot,
|
|
2751
|
+
captures: views.map(({ sortTime: _sortTime, ...v }) => v)
|
|
2752
|
+
},
|
|
2753
|
+
null,
|
|
2754
|
+
2
|
|
2755
|
+
)}
|
|
2756
|
+
`,
|
|
2757
|
+
stderr: "",
|
|
2758
|
+
exitCode: 0
|
|
2759
|
+
};
|
|
2760
|
+
}
|
|
2761
|
+
return {
|
|
2762
|
+
stdout: formatCaptureStatus(views),
|
|
2763
|
+
stderr: "",
|
|
2764
|
+
exitCode: 0
|
|
2765
|
+
};
|
|
2766
|
+
}
|
|
2767
|
+
async function readCaptureRecords(almanacDir) {
|
|
2768
|
+
if (!existsSync6(almanacDir)) return [];
|
|
2769
|
+
const out = [];
|
|
2770
|
+
const dirs = [join7(almanacDir, "logs"), almanacDir];
|
|
2771
|
+
for (const dir of dirs) {
|
|
2772
|
+
let entries;
|
|
2773
|
+
try {
|
|
2774
|
+
entries = await readdir2(dir);
|
|
2775
|
+
} catch {
|
|
2776
|
+
continue;
|
|
2777
|
+
}
|
|
2778
|
+
for (const entry of entries) {
|
|
2779
|
+
if (!entry.startsWith(".capture-") || !entry.endsWith(".state.json")) {
|
|
2780
|
+
continue;
|
|
2781
|
+
}
|
|
2782
|
+
try {
|
|
2783
|
+
const parsed = JSON.parse(await readFile6(join7(dir, entry), "utf8"));
|
|
2784
|
+
if (isCaptureRunRecord(parsed)) out.push(parsed);
|
|
2785
|
+
} catch {
|
|
2786
|
+
continue;
|
|
2787
|
+
}
|
|
2788
|
+
}
|
|
2789
|
+
}
|
|
2790
|
+
return out;
|
|
2791
|
+
}
|
|
2792
|
+
function isCaptureRunRecord(value) {
|
|
2793
|
+
if (value === null || typeof value !== "object") return false;
|
|
2794
|
+
const v = value;
|
|
2795
|
+
return v.version === 1 && v.kind === "capture" && (v.status === "running" || v.status === "done" || v.status === "failed") && typeof v.sessionId === "string" && typeof v.repoRoot === "string" && typeof v.pid === "number" && typeof v.model === "string" && typeof v.transcriptPath === "string" && typeof v.startedAt === "string" && typeof v.logPath === "string" && typeof v.jsonlPath === "string";
|
|
2796
|
+
}
|
|
2797
|
+
function toView(record, repoRoot, now, isPidAlive) {
|
|
2798
|
+
const started = Date.parse(record.startedAt);
|
|
2799
|
+
const finished = record.finishedAt !== void 0 ? Date.parse(record.finishedAt) : void 0;
|
|
2800
|
+
const elapsedMs = record.durationMs ?? (Number.isFinite(started) ? Math.max(0, (finished ?? now.getTime()) - started) : 0);
|
|
2801
|
+
const status = record.status === "running" && !isPidAlive(record.pid) ? "stale" : record.status;
|
|
2802
|
+
return {
|
|
2803
|
+
status,
|
|
2804
|
+
sessionId: record.sessionId,
|
|
2805
|
+
model: record.model,
|
|
2806
|
+
elapsedMs,
|
|
2807
|
+
startedAt: record.startedAt,
|
|
2808
|
+
finishedAt: record.finishedAt,
|
|
2809
|
+
pid: record.pid,
|
|
2810
|
+
logPath: relative2(repoRoot, record.logPath),
|
|
2811
|
+
jsonlPath: relative2(repoRoot, record.jsonlPath),
|
|
2812
|
+
summary: record.summary,
|
|
2813
|
+
error: status === "stale" ? "process ended without a final status" : record.error,
|
|
2814
|
+
sortTime: finished ?? (Number.isFinite(started) ? started : 0)
|
|
2815
|
+
};
|
|
2816
|
+
}
|
|
2817
|
+
function formatCaptureStatus(views) {
|
|
2818
|
+
const lines = ["Capture jobs", ""];
|
|
2819
|
+
if (views.length === 0) {
|
|
2820
|
+
lines.push("No capture jobs found.");
|
|
2821
|
+
return `${lines.join("\n")}
|
|
2822
|
+
`;
|
|
2823
|
+
}
|
|
2824
|
+
const active = views.filter((v) => v.status === "running" || v.status === "stale");
|
|
2825
|
+
const finished = views.filter((v) => v.status === "done" || v.status === "failed");
|
|
2826
|
+
if (active.length === 0) {
|
|
2827
|
+
lines.push("No active captures.", "");
|
|
2828
|
+
} else {
|
|
2829
|
+
for (const view of active) {
|
|
2830
|
+
lines.push(formatRow(view));
|
|
2831
|
+
lines.push(` log: ${view.logPath}`);
|
|
2832
|
+
if (view.error !== void 0) lines.push(` error: ${view.error}`);
|
|
2833
|
+
lines.push("");
|
|
2834
|
+
}
|
|
2835
|
+
}
|
|
2836
|
+
if (finished.length > 0) {
|
|
2837
|
+
lines.push(active.length === 0 ? "Last capture:" : "Last finished:");
|
|
2838
|
+
for (const view of finished.slice(0, 3)) {
|
|
2839
|
+
lines.push(formatRow(view));
|
|
2840
|
+
if (view.status === "failed") {
|
|
2841
|
+
lines.push(` log: ${view.logPath}`);
|
|
2842
|
+
if (view.error !== void 0) lines.push(` error: ${view.error}`);
|
|
2843
|
+
}
|
|
2844
|
+
}
|
|
2845
|
+
}
|
|
2846
|
+
return `${trimTrailingBlank(lines).join("\n")}
|
|
2847
|
+
`;
|
|
2848
|
+
}
|
|
2849
|
+
function formatRow(view) {
|
|
2850
|
+
const status = view.status.padEnd(7, " ");
|
|
2851
|
+
const session = view.sessionId.padEnd(12, " ");
|
|
2852
|
+
const model = view.model.padEnd(17, " ");
|
|
2853
|
+
const elapsed = formatDuration(view.elapsedMs);
|
|
2854
|
+
const summary = formatSummary(view);
|
|
2855
|
+
return `${status} ${session} ${model} ${elapsed}${summary.length > 0 ? ` ${summary}` : ""}`;
|
|
2856
|
+
}
|
|
2857
|
+
function formatSummary(view) {
|
|
2858
|
+
if (view.status === "failed") return "failed; see log";
|
|
2859
|
+
if (view.summary === void 0) return "";
|
|
2860
|
+
const parts = [];
|
|
2861
|
+
if (view.summary.updated > 0) {
|
|
2862
|
+
parts.push(`${view.summary.updated} updated`);
|
|
2863
|
+
}
|
|
2864
|
+
if (view.summary.created > 0) {
|
|
2865
|
+
parts.push(`${view.summary.created} created`);
|
|
2866
|
+
}
|
|
2867
|
+
if (view.summary.archived > 0) {
|
|
2868
|
+
parts.push(`${view.summary.archived} archived`);
|
|
2869
|
+
}
|
|
2870
|
+
return parts.length > 0 ? parts.join(", ") : "0 pages written";
|
|
2871
|
+
}
|
|
2872
|
+
function formatDuration(ms) {
|
|
2873
|
+
const totalSeconds = Math.max(0, Math.floor(ms / 1e3));
|
|
2874
|
+
const minutes = Math.floor(totalSeconds / 60);
|
|
2875
|
+
const seconds = totalSeconds % 60;
|
|
2876
|
+
if (minutes < 60) return `${minutes}m${seconds.toString().padStart(2, "0")}s`;
|
|
2877
|
+
const hours = Math.floor(minutes / 60);
|
|
2878
|
+
const restMinutes = minutes % 60;
|
|
2879
|
+
return `${hours}h${restMinutes.toString().padStart(2, "0")}m`;
|
|
2880
|
+
}
|
|
2881
|
+
function trimTrailingBlank(lines) {
|
|
2882
|
+
while (lines.length > 0 && lines[lines.length - 1] === "") {
|
|
2883
|
+
lines.pop();
|
|
2884
|
+
}
|
|
2885
|
+
return lines;
|
|
2886
|
+
}
|
|
2887
|
+
function defaultIsPidAlive(pid) {
|
|
2888
|
+
try {
|
|
2889
|
+
process.kill(pid, 0);
|
|
2890
|
+
return true;
|
|
2891
|
+
} catch {
|
|
2892
|
+
return false;
|
|
2893
|
+
}
|
|
2894
|
+
}
|
|
2895
|
+
|
|
2896
|
+
// src/commands/capture.ts
|
|
2567
2897
|
var WRITER_TOOLS = ["Read", "Write", "Edit", "Glob", "Grep", "Bash", "Agent"];
|
|
2568
2898
|
var REVIEWER_TOOLS = ["Read", "Grep", "Glob", "Bash"];
|
|
2569
2899
|
var REVIEWER_DESCRIPTION = "Reviews proposed wiki changes against the full knowledge base for cohesion, duplication, missing links, notability, and writing conventions.";
|
|
@@ -2601,7 +2931,7 @@ async function runCapture(options) {
|
|
|
2601
2931
|
};
|
|
2602
2932
|
}
|
|
2603
2933
|
const almanacDir = getRepoAlmanacDir(repoRoot);
|
|
2604
|
-
const pagesDir =
|
|
2934
|
+
const pagesDir = join8(almanacDir, "pages");
|
|
2605
2935
|
const transcriptResolution = await resolveTranscript({
|
|
2606
2936
|
repoRoot,
|
|
2607
2937
|
explicit: options.transcriptPath,
|
|
@@ -2627,12 +2957,24 @@ async function runCapture(options) {
|
|
|
2627
2957
|
tools: REVIEWER_TOOLS
|
|
2628
2958
|
}
|
|
2629
2959
|
};
|
|
2630
|
-
const
|
|
2631
|
-
const logStem = options.sessionId !== void 0 && options.sessionId.length > 0 ? options.sessionId : formatTimestamp2(
|
|
2632
|
-
const logsDir =
|
|
2633
|
-
await
|
|
2960
|
+
const startedAt = options.now?.() ?? /* @__PURE__ */ new Date();
|
|
2961
|
+
const logStem = options.sessionId !== void 0 && options.sessionId.length > 0 ? options.sessionId : formatTimestamp2(startedAt);
|
|
2962
|
+
const logsDir = join8(almanacDir, "logs");
|
|
2963
|
+
await mkdir4(logsDir, { recursive: true });
|
|
2634
2964
|
const logName = `.capture-${logStem}.jsonl`;
|
|
2635
|
-
const logPath =
|
|
2965
|
+
const logPath = join8(logsDir, logName);
|
|
2966
|
+
const statePath = captureStatePath(logsDir, logStem);
|
|
2967
|
+
const stateRecord = buildStartedCaptureRecord({
|
|
2968
|
+
repoRoot,
|
|
2969
|
+
almanacDir: logsDir,
|
|
2970
|
+
stem: logStem,
|
|
2971
|
+
sessionId: options.sessionId,
|
|
2972
|
+
transcriptPath,
|
|
2973
|
+
model: options.model,
|
|
2974
|
+
startedAt
|
|
2975
|
+
});
|
|
2976
|
+
await writeCaptureRunRecord(statePath, stateRecord).catch(() => {
|
|
2977
|
+
});
|
|
2636
2978
|
const logStream = createWriteStream2(logPath, { flags: "w" });
|
|
2637
2979
|
const out = process.stdout;
|
|
2638
2980
|
const formatter = new StreamingFormatter({
|
|
@@ -2674,16 +3016,43 @@ Working directory: ${repoRoot}.`;
|
|
|
2674
3016
|
}
|
|
2675
3017
|
const snapshotAfter = await snapshotPages(pagesDir);
|
|
2676
3018
|
const delta = diffSnapshots(snapshotBefore, snapshotAfter);
|
|
3019
|
+
const finishedAt = options.now?.() ?? /* @__PURE__ */ new Date();
|
|
3020
|
+
const captureSummary = {
|
|
3021
|
+
...delta,
|
|
3022
|
+
cost: result.cost,
|
|
3023
|
+
turns: result.turns
|
|
3024
|
+
};
|
|
2677
3025
|
if (!result.success) {
|
|
3026
|
+
await writeCaptureRunRecord(
|
|
3027
|
+
statePath,
|
|
3028
|
+
finishCaptureRecord({
|
|
3029
|
+
record: stateRecord,
|
|
3030
|
+
status: "failed",
|
|
3031
|
+
finishedAt,
|
|
3032
|
+
summary: captureSummary,
|
|
3033
|
+
error: result.error ?? "unknown error"
|
|
3034
|
+
})
|
|
3035
|
+
).catch(() => {
|
|
3036
|
+
});
|
|
2678
3037
|
return {
|
|
2679
3038
|
stdout: "",
|
|
2680
3039
|
stderr: `almanac: capture failed: ${result.error ?? "unknown error"}
|
|
2681
|
-
(transcript: ${
|
|
3040
|
+
(transcript: ${relative3(repoRoot, logPath)})
|
|
2682
3041
|
`,
|
|
2683
3042
|
exitCode: 1
|
|
2684
3043
|
};
|
|
2685
3044
|
}
|
|
2686
|
-
|
|
3045
|
+
await writeCaptureRunRecord(
|
|
3046
|
+
statePath,
|
|
3047
|
+
finishCaptureRecord({
|
|
3048
|
+
record: stateRecord,
|
|
3049
|
+
status: "done",
|
|
3050
|
+
finishedAt,
|
|
3051
|
+
summary: captureSummary
|
|
3052
|
+
})
|
|
3053
|
+
).catch(() => {
|
|
3054
|
+
});
|
|
3055
|
+
const summary = formatSummary2(result, delta, logPath, repoRoot);
|
|
2687
3056
|
return {
|
|
2688
3057
|
stdout: `${summary}
|
|
2689
3058
|
`,
|
|
@@ -2706,7 +3075,7 @@ async function resolveAgentSelection2(args) {
|
|
|
2706
3075
|
}
|
|
2707
3076
|
async function resolveTranscript(args) {
|
|
2708
3077
|
if (args.explicit !== void 0 && args.explicit.length > 0) {
|
|
2709
|
-
if (!
|
|
3078
|
+
if (!existsSync7(args.explicit)) {
|
|
2710
3079
|
return {
|
|
2711
3080
|
ok: false,
|
|
2712
3081
|
error: `transcript not found: ${args.explicit}`
|
|
@@ -2714,8 +3083,8 @@ async function resolveTranscript(args) {
|
|
|
2714
3083
|
}
|
|
2715
3084
|
return { ok: true, path: args.explicit };
|
|
2716
3085
|
}
|
|
2717
|
-
const projectsDir = args.claudeProjectsDir ??
|
|
2718
|
-
if (!
|
|
3086
|
+
const projectsDir = args.claudeProjectsDir ?? join8(homedir(), ".claude", "projects");
|
|
3087
|
+
if (!existsSync7(projectsDir)) {
|
|
2719
3088
|
return {
|
|
2720
3089
|
ok: false,
|
|
2721
3090
|
error: `could not auto-resolve transcript; ${projectsDir} does not exist. Pass --session <id> or <transcript-path>.`
|
|
@@ -2747,21 +3116,21 @@ async function collectTranscripts(projectsDir) {
|
|
|
2747
3116
|
const out = [];
|
|
2748
3117
|
let topLevel;
|
|
2749
3118
|
try {
|
|
2750
|
-
topLevel = await
|
|
3119
|
+
topLevel = await readdir3(projectsDir);
|
|
2751
3120
|
} catch {
|
|
2752
3121
|
return out;
|
|
2753
3122
|
}
|
|
2754
3123
|
for (const name of topLevel) {
|
|
2755
|
-
const projectDir =
|
|
3124
|
+
const projectDir = join8(projectsDir, name);
|
|
2756
3125
|
let entries;
|
|
2757
3126
|
try {
|
|
2758
|
-
entries = await
|
|
3127
|
+
entries = await readdir3(projectDir);
|
|
2759
3128
|
} catch {
|
|
2760
3129
|
continue;
|
|
2761
3130
|
}
|
|
2762
3131
|
for (const entry of entries) {
|
|
2763
3132
|
if (!entry.endsWith(".jsonl")) continue;
|
|
2764
|
-
const full =
|
|
3133
|
+
const full = join8(projectDir, entry);
|
|
2765
3134
|
try {
|
|
2766
3135
|
const st = await stat(full);
|
|
2767
3136
|
if (st.isFile()) {
|
|
@@ -2776,7 +3145,7 @@ async function collectTranscripts(projectsDir) {
|
|
|
2776
3145
|
async function filterTranscriptsByCwd(transcripts, repoRoot) {
|
|
2777
3146
|
const dirHash = `-${repoRoot.replace(/^\/+/, "").replace(/\//g, "-")}`;
|
|
2778
3147
|
const byDirName = transcripts.filter((t) => {
|
|
2779
|
-
const parent = basename3(
|
|
3148
|
+
const parent = basename3(join8(t.path, ".."));
|
|
2780
3149
|
return parent === dirHash || parent.endsWith(dirHash);
|
|
2781
3150
|
});
|
|
2782
3151
|
if (byDirName.length > 0) return byDirName;
|
|
@@ -2793,26 +3162,26 @@ async function filterTranscriptsByCwd(transcripts, repoRoot) {
|
|
|
2793
3162
|
return hits;
|
|
2794
3163
|
}
|
|
2795
3164
|
async function readHead(path2, bytes) {
|
|
2796
|
-
const content = await
|
|
3165
|
+
const content = await readFile7(path2, "utf8");
|
|
2797
3166
|
return content.length > bytes ? content.slice(0, bytes) : content;
|
|
2798
3167
|
}
|
|
2799
3168
|
async function snapshotPages(pagesDir) {
|
|
2800
3169
|
const out = /* @__PURE__ */ new Map();
|
|
2801
|
-
if (!
|
|
3170
|
+
if (!existsSync7(pagesDir)) return out;
|
|
2802
3171
|
let entries;
|
|
2803
3172
|
try {
|
|
2804
|
-
entries = await
|
|
3173
|
+
entries = await readdir3(pagesDir);
|
|
2805
3174
|
} catch {
|
|
2806
3175
|
return out;
|
|
2807
3176
|
}
|
|
2808
3177
|
for (const entry of entries) {
|
|
2809
3178
|
if (!entry.endsWith(".md")) continue;
|
|
2810
3179
|
const slug = entry.slice(0, -3);
|
|
2811
|
-
const full =
|
|
3180
|
+
const full = join8(pagesDir, entry);
|
|
2812
3181
|
try {
|
|
2813
3182
|
const st = statSync(full);
|
|
2814
3183
|
if (!st.isFile()) continue;
|
|
2815
|
-
const content = await
|
|
3184
|
+
const content = await readFile7(full, "utf8");
|
|
2816
3185
|
const hash = createHash("sha256").update(content).digest("hex");
|
|
2817
3186
|
const fm = parseFrontmatter(content);
|
|
2818
3187
|
out.set(slug, {
|
|
@@ -2846,14 +3215,14 @@ function diffSnapshots(before, after) {
|
|
|
2846
3215
|
}
|
|
2847
3216
|
return { created, updated, archived };
|
|
2848
3217
|
}
|
|
2849
|
-
function
|
|
2850
|
-
const rel =
|
|
2851
|
-
const
|
|
3218
|
+
function formatSummary2(result, delta, logPath, repoRoot) {
|
|
3219
|
+
const rel = relative3(repoRoot, logPath);
|
|
3220
|
+
const usage = formatRunUsage(result);
|
|
2852
3221
|
const { created, updated, archived } = delta;
|
|
2853
3222
|
if (created === 0 && updated === 0 && archived === 0) {
|
|
2854
|
-
return `[capture] no new knowledge met the notability bar (0 pages written),
|
|
3223
|
+
return `[capture] no new knowledge met the notability bar (0 pages written), ${usage} (transcript: ${rel})`;
|
|
2855
3224
|
}
|
|
2856
|
-
return `[done] ${updated} page${updated === 1 ? "" : "s"} updated, ${created} created, ${archived} archived,
|
|
3225
|
+
return `[done] ${updated} page${updated === 1 ? "" : "s"} updated, ${created} created, ${archived} archived, ${usage} (transcript: ${rel})`;
|
|
2857
3226
|
}
|
|
2858
3227
|
function formatTimestamp2(d) {
|
|
2859
3228
|
const pad = (n) => n.toString().padStart(2, "0");
|
|
@@ -2894,7 +3263,7 @@ function registerWikiLifecycleCommands(program) {
|
|
|
2894
3263
|
emit(result);
|
|
2895
3264
|
}
|
|
2896
3265
|
);
|
|
2897
|
-
program.command("capture [transcript]").description("run the writer/reviewer pipeline on a session (usually automatic)").option("--session <id>", "target a specific session by ID").option("--quiet", "suppress per-tool streaming; print only the final summary").option("--agent <agent>", "agent provider: claude, codex, or cursor").option("--model <model>", "override the agent model").action(
|
|
3266
|
+
const capture = program.command("capture [transcript]").alias("c").description("run the writer/reviewer pipeline on a session (usually automatic)").option("--session <id>", "target a specific session by ID").option("--quiet", "suppress per-tool streaming; print only the final summary").option("--agent <agent>", "agent provider: claude, codex, or cursor").option("--model <model>", "override the agent model").action(
|
|
2898
3267
|
async (transcript, opts) => {
|
|
2899
3268
|
await autoRegisterIfNeeded(process.cwd());
|
|
2900
3269
|
const result = await runCapture({
|
|
@@ -2908,6 +3277,22 @@ function registerWikiLifecycleCommands(program) {
|
|
|
2908
3277
|
emit(result);
|
|
2909
3278
|
}
|
|
2910
3279
|
);
|
|
3280
|
+
capture.command("status").description("show running and recent capture jobs").option("--json", "emit structured JSON").action(async (opts) => {
|
|
3281
|
+
await autoRegisterIfNeeded(process.cwd());
|
|
3282
|
+
const result = await runCaptureStatus({
|
|
3283
|
+
cwd: process.cwd(),
|
|
3284
|
+
json: opts.json
|
|
3285
|
+
});
|
|
3286
|
+
emit(result);
|
|
3287
|
+
});
|
|
3288
|
+
program.command("ps").description("show running and recent capture jobs").option("--json", "emit structured JSON").action(async (opts) => {
|
|
3289
|
+
await autoRegisterIfNeeded(process.cwd());
|
|
3290
|
+
const result = await runCaptureStatus({
|
|
3291
|
+
cwd: process.cwd(),
|
|
3292
|
+
json: opts.json
|
|
3293
|
+
});
|
|
3294
|
+
emit(result);
|
|
3295
|
+
});
|
|
2911
3296
|
const hook = program.command("hook").description("manage the SessionEnd auto-capture hook");
|
|
2912
3297
|
hook.command("install").description("add a SessionEnd entry that runs 'almanac capture' on session end").option("--source <source>", "claude, codex, cursor, or all").action(async (opts) => {
|
|
2913
3298
|
const result = await runHookInstall({
|
|
@@ -2950,4 +3335,4 @@ function registerCommands(program) {
|
|
|
2950
3335
|
export {
|
|
2951
3336
|
registerCommands
|
|
2952
3337
|
};
|
|
2953
|
-
//# sourceMappingURL=register-commands-
|
|
3338
|
+
//# sourceMappingURL=register-commands-7QCIENRZ.js.map
|