codealmanac 0.2.2 → 0.2.4

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 (53) hide show
  1. package/README.md +19 -6
  2. package/dist/agents-4Y7X24WW.js +25 -0
  3. package/dist/chunk-BF2J4XTC.js +766 -0
  4. package/dist/chunk-BF2J4XTC.js.map +1 -0
  5. package/dist/{chunk-B2AGSRXL.js → chunk-CW4HRLMS.js} +88 -7
  6. package/dist/chunk-CW4HRLMS.js.map +1 -0
  7. package/dist/{chunk-P3LDTCLB.js → chunk-H37GKBWI.js} +13 -1
  8. package/dist/chunk-H37GKBWI.js.map +1 -0
  9. package/dist/{chunk-MX2EW5MR.js → chunk-H6QKCB7M.js} +2 -2
  10. package/dist/{chunk-QQHIVTXT.js → chunk-MRRX4UQB.js} +4 -4
  11. package/dist/{chunk-QQHIVTXT.js.map → chunk-MRRX4UQB.js.map} +1 -1
  12. package/dist/chunk-P5WGG4FJ.js +359 -0
  13. package/dist/chunk-P5WGG4FJ.js.map +1 -0
  14. package/dist/{chunk-ZDJSJIB6.js → chunk-QRK3JLFX.js} +131 -15
  15. package/dist/chunk-QRK3JLFX.js.map +1 -0
  16. package/dist/{chunk-V3QOQSXI.js → chunk-TILAKDN6.js} +14 -8
  17. package/dist/chunk-TILAKDN6.js.map +1 -0
  18. package/dist/chunk-TT6ZP4GS.js +282 -0
  19. package/dist/chunk-TT6ZP4GS.js.map +1 -0
  20. package/dist/chunk-UU6FBRQO.js +187 -0
  21. package/dist/chunk-UU6FBRQO.js.map +1 -0
  22. package/dist/{cli-3XAVBTYG.js → cli-MYMZ66EN.js} +123 -32
  23. package/dist/cli-MYMZ66EN.js.map +1 -0
  24. package/dist/codealmanac.js +1 -1
  25. package/dist/config-ML2RCR7J.js +16 -0
  26. package/dist/{doctor-3BYSF3JD.js → doctor-W5KQQLAX.js} +6 -6
  27. package/dist/{register-commands-7QCIENRZ.js → register-commands-XTK2G2FB.js} +293 -393
  28. package/dist/register-commands-XTK2G2FB.js.map +1 -0
  29. package/dist/{uninstall-FDIOBAAR.js → uninstall-N7JY7ZV2.js} +5 -5
  30. package/dist/{update-RAF7QRYF.js → update-P2IPG7RO.js} +3 -3
  31. package/guides/mini.md +1 -1
  32. package/guides/reference.md +68 -9
  33. package/package.json +1 -1
  34. package/dist/agents-A4II4YJC.js +0 -15
  35. package/dist/auth-S5DVUIUJ.js +0 -18
  36. package/dist/chunk-B2AGSRXL.js.map +0 -1
  37. package/dist/chunk-P3LDTCLB.js.map +0 -1
  38. package/dist/chunk-R3URPHGH.js +0 -194
  39. package/dist/chunk-R3URPHGH.js.map +0 -1
  40. package/dist/chunk-SSYMRT4I.js +0 -126
  41. package/dist/chunk-SSYMRT4I.js.map +0 -1
  42. package/dist/chunk-V3QOQSXI.js.map +0 -1
  43. package/dist/chunk-WRUSDYYE.js +0 -97
  44. package/dist/chunk-WRUSDYYE.js.map +0 -1
  45. package/dist/chunk-ZDJSJIB6.js.map +0 -1
  46. package/dist/cli-3XAVBTYG.js.map +0 -1
  47. package/dist/register-commands-7QCIENRZ.js.map +0 -1
  48. /package/dist/{agents-A4II4YJC.js.map → agents-4Y7X24WW.js.map} +0 -0
  49. /package/dist/{chunk-MX2EW5MR.js.map → chunk-H6QKCB7M.js.map} +0 -0
  50. /package/dist/{auth-S5DVUIUJ.js.map → config-ML2RCR7J.js.map} +0 -0
  51. /package/dist/{doctor-3BYSF3JD.js.map → doctor-W5KQQLAX.js.map} +0 -0
  52. /package/dist/{uninstall-FDIOBAAR.js.map → uninstall-N7JY7ZV2.js.map} +0 -0
  53. /package/dist/{update-RAF7QRYF.js.map → update-P2IPG7RO.js.map} +0 -0
@@ -1,16 +1,26 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  collectOption,
4
+ deprecationWarning,
4
5
  emit,
5
6
  parsePositiveInt,
6
- readStdin
7
- } from "./chunk-P3LDTCLB.js";
7
+ readStdin,
8
+ withWarning
9
+ } from "./chunk-H37GKBWI.js";
8
10
  import {
9
- assertAgentAuth,
11
+ runAgentsDoctor,
10
12
  runAgentsList,
11
- runSetAgentModel,
12
- runSetDefaultAgent
13
- } from "./chunk-R3URPHGH.js";
13
+ runAgentsModel,
14
+ runAgentsUse,
15
+ runDeprecatedSetAgentModel,
16
+ runDeprecatedSetDefaultAgent
17
+ } from "./chunk-UU6FBRQO.js";
18
+ import {
19
+ runConfigGet,
20
+ runConfigList,
21
+ runConfigSet,
22
+ runConfigUnset
23
+ } from "./chunk-TT6ZP4GS.js";
14
24
  import {
15
25
  addEntry,
16
26
  ancestorsInFile,
@@ -36,8 +46,8 @@ import {
36
46
  } from "./chunk-KQUVMF27.js";
37
47
  import {
38
48
  runDoctor
39
- } from "./chunk-B2AGSRXL.js";
40
- import "./chunk-V3QOQSXI.js";
49
+ } from "./chunk-CW4HRLMS.js";
50
+ import "./chunk-TILAKDN6.js";
41
51
  import "./chunk-4CODZRHH.js";
42
52
  import {
43
53
  BLUE,
@@ -47,26 +57,30 @@ import {
47
57
  } from "./chunk-FM3VRDK7.js";
48
58
  import {
49
59
  runUninstall
50
- } from "./chunk-MX2EW5MR.js";
60
+ } from "./chunk-H6QKCB7M.js";
51
61
  import {
52
62
  runSetup
53
- } from "./chunk-ZDJSJIB6.js";
63
+ } from "./chunk-QRK3JLFX.js";
54
64
  import {
55
65
  runHookInstall,
56
66
  runHookStatus,
57
67
  runHookUninstall
58
68
  } from "./chunk-447U3GQJ.js";
59
69
  import {
60
- resolveClaudeExecutable
61
- } from "./chunk-SSYMRT4I.js";
70
+ DEFAULT_AGENT_MODEL,
71
+ assertAgentAuth,
72
+ getAgentProvider,
73
+ getProviderDefaultModel,
74
+ parseAgentSelection
75
+ } from "./chunk-BF2J4XTC.js";
62
76
  import {
63
77
  runUpdate
64
- } from "./chunk-QQHIVTXT.js";
78
+ } from "./chunk-MRRX4UQB.js";
65
79
  import "./chunk-F53U6JQG.js";
66
80
  import {
67
81
  isAgentProviderId,
68
82
  readConfig
69
- } from "./chunk-WRUSDYYE.js";
83
+ } from "./chunk-P5WGG4FJ.js";
70
84
  import {
71
85
  findNearestAlmanacDir,
72
86
  getRepoAlmanacDir
@@ -1695,11 +1709,11 @@ function registerQueryCommands(program) {
1695
1709
  "--mentions <path>",
1696
1710
  "pages referencing this path; matches exact file, trailing-slash folders, and any file under a folder prefix"
1697
1711
  ).option("--since <duration>", "updated within duration, by file mtime (e.g. 2w, 30d)").option("--stale <duration>", "NOT updated within duration, by file mtime").option("--orphan", "pages with no topics").option("--include-archive", "include archived pages").option("--archived", "archived pages only").option("--wiki <name>", "target a specific registered wiki").option("--json", "emit structured JSON").option("--limit <n>", "cap results", parsePositiveInt).action(
1698
- async (query2, opts) => {
1712
+ async (query, opts) => {
1699
1713
  await autoRegisterIfNeeded(process.cwd());
1700
1714
  const result = await runSearch({
1701
1715
  cwd: process.cwd(),
1702
- query: query2,
1716
+ query,
1703
1717
  topics: opts.topic ?? [],
1704
1718
  mentions: opts.mentions,
1705
1719
  since: opts.since,
@@ -1714,7 +1728,7 @@ function registerQueryCommands(program) {
1714
1728
  emit(result);
1715
1729
  }
1716
1730
  );
1717
- program.command("show [slug]").description("print a page (metadata + body; flags to narrow)").option("--stdin", "read slugs from stdin (one per line)").option("--wiki <name>", "target a specific registered wiki").option("--json", "structured JSON (overrides other view/field flags)").option("--raw", "body only (alias: --body)").option("--body", "body only (alias: --raw)").option("--meta", "metadata only, no body").option("--lead", "first paragraph of the body only").option("--title", "print title").option("--topics", "print topics").option("--files", "print file refs").option("--links", "print outgoing wikilinks").option("--backlinks", "print incoming wikilinks").option("--xwiki", "print cross-wiki links").option("--lineage", "print archived_at / supersedes / superseded_by").option("--updated", "print updated timestamp").option("--path", "print absolute file path").action(
1731
+ program.command("show [slug]").description("print a page (metadata + body; flags to narrow)").option("--stdin", "read slugs from stdin (one per line)").option("--wiki <name>", "target a specific registered wiki").option("--json", "structured JSON (overrides other view/field flags)").option("--body", "body only").option("--raw", "deprecated alias for --body").option("--meta", "metadata only, no body").option("--lead", "first paragraph of the body only").option("--title", "print title").option("--topics", "print topics").option("--files", "print file refs").option("--links", "print outgoing wikilinks").option("--backlinks", "print incoming wikilinks").option("--xwiki", "print cross-wiki links").option("--lineage", "print archived_at / supersedes / superseded_by").option("--updated", "print updated timestamp").option("--path", "print absolute file path").action(
1718
1732
  async (slug, opts) => {
1719
1733
  await autoRegisterIfNeeded(process.cwd());
1720
1734
  const result = await runShow({
@@ -1737,7 +1751,10 @@ function registerQueryCommands(program) {
1737
1751
  updated: opts.updated,
1738
1752
  path: opts.path
1739
1753
  });
1740
- emit(result);
1754
+ emit(opts.raw === true ? withWarning(
1755
+ result,
1756
+ deprecationWarning("almanac show <slug> --raw", "almanac show <slug> --body")
1757
+ ) : result);
1741
1758
  }
1742
1759
  );
1743
1760
  program.command("health").description("report graph integrity problems").option("--topic <name>", "scope to a topic + its descendants").option("--stale <duration>", "stale threshold (default 90d)").option("--stdin", "read page slugs from stdin (limit to these pages)").option("--json", "emit structured JSON").option("--wiki <name>", "target a specific registered wiki").action(
@@ -1776,13 +1793,39 @@ function registerSetupCommands(program) {
1776
1793
  agents.command("list").description("show Claude, Codex, and Cursor provider status").action(async () => {
1777
1794
  emit(await runAgentsList());
1778
1795
  });
1796
+ agents.command("doctor").description("diagnose supported AI agent providers").action(async () => {
1797
+ emit(await runAgentsDoctor());
1798
+ });
1799
+ agents.command("use").description("set the default AI agent provider").argument("<provider>", "claude, codex, cursor, or claude/<model>").action(async (provider) => {
1800
+ emit(await runAgentsUse({ provider }));
1801
+ });
1802
+ agents.command("model").description("set or reset a provider model").argument("<provider>", "claude, codex, or cursor").argument("[model]", "provider-specific model id").option("--default", "reset to provider default").action(async (provider, model, opts) => {
1803
+ emit(await runAgentsModel({
1804
+ provider,
1805
+ model,
1806
+ defaultModel: opts.default
1807
+ }));
1808
+ });
1809
+ const config = program.command("config").description("read and write codealmanac settings");
1810
+ config.command("list").description("show supported config keys").option("--json", "emit structured JSON").option("--show-origin", "show whether each value came from file or default").action(async (opts) => {
1811
+ emit(await runConfigList(opts));
1812
+ });
1813
+ config.command("get").description("print one config value").argument("<key>", "config key").option("--json", "emit structured JSON").option("--show-origin", "show whether the value came from file or default").action(async (key, opts) => {
1814
+ emit(await runConfigGet({ key, ...opts }));
1815
+ });
1816
+ config.command("set").description("set one config value").argument("<key>", "config key").argument("<value>", "config value").option("--project", "write .almanac/config.toml for this repo").action(async (key, value, opts) => {
1817
+ emit(await runConfigSet({ key, value, project: opts.project }));
1818
+ });
1819
+ config.command("unset").description("restore one config value to default").argument("<key>", "config key").option("--project", "remove from .almanac/config.toml for this repo").action(async (key, opts) => {
1820
+ emit(await runConfigUnset({ key, project: opts.project }));
1821
+ });
1779
1822
  program.command("set").description("configure codealmanac defaults").argument("<key>", "setting key, e.g. default-agent or model").argument("[value...]", "setting value").action(async (key, value) => {
1780
1823
  if (key === "default-agent") {
1781
- emit(await runSetDefaultAgent({ provider: value[0] ?? "" }));
1824
+ emit(await runDeprecatedSetDefaultAgent({ provider: value[0] ?? "" }));
1782
1825
  return;
1783
1826
  }
1784
1827
  if (key === "model") {
1785
- emit(await runSetAgentModel({
1828
+ emit(await runDeprecatedSetAgentModel({
1786
1829
  provider: value[0] ?? "",
1787
1830
  model: value[1]
1788
1831
  }));
@@ -1794,11 +1837,12 @@ function registerSetupCommands(program) {
1794
1837
  exitCode: 1
1795
1838
  });
1796
1839
  });
1797
- program.command("setup").description("install the hook + CLAUDE.md guides (bare codealmanac alias)").option("-y, --yes", "skip prompts; install everything").option("--agent <agent>", "default agent: claude, codex, or cursor").option("--skip-hook", "opt out of the SessionEnd hook").option("--skip-guides", "opt out of the CLAUDE.md guides").action(
1840
+ program.command("setup").description("install the hook + CLAUDE.md guides (bare codealmanac alias)").option("-y, --yes", "skip prompts; install everything").option("--agent <agent>", "default agent: claude, codex, or cursor").option("--model <model>", "default model for the selected agent").option("--skip-hook", "opt out of the SessionEnd hook").option("--skip-guides", "opt out of the CLAUDE.md guides").action(
1798
1841
  async (opts) => {
1799
1842
  const result = await runSetup({
1800
1843
  yes: opts.yes,
1801
1844
  agent: opts.agent,
1845
+ model: opts.model,
1802
1846
  skipHook: opts.skipHook,
1803
1847
  skipGuides: opts.skipGuides
1804
1848
  });
@@ -1821,10 +1865,10 @@ function registerSetupCommands(program) {
1821
1865
  "silence the update banner for the current `latest_version` without installing"
1822
1866
  ).option("--check", "force a registry check now (bypasses the 24h cache); no install").option(
1823
1867
  "--enable-notifier",
1824
- "re-enable the pre-command update banner (writes ~/.almanac/config.json)"
1868
+ "deprecated: use `almanac config set update_notifier true`"
1825
1869
  ).option(
1826
1870
  "--disable-notifier",
1827
- "silence the pre-command update banner (writes ~/.almanac/config.json)"
1871
+ "deprecated: use `almanac config set update_notifier false`"
1828
1872
  ).action(
1829
1873
  async (opts) => {
1830
1874
  const result = await runUpdate({
@@ -1902,277 +1946,74 @@ async function loadPrompt(name) {
1902
1946
  return readFile4(path.join(dir, `${name}.md`), "utf8");
1903
1947
  }
1904
1948
 
1905
- // src/agent/sdk.ts
1906
- import { spawn } from "child_process";
1907
- import { query } from "@anthropic-ai/claude-agent-sdk";
1908
- var DEFAULT_AGENT_MODEL = "claude-sonnet-4-6";
1909
- async function runAgent(opts) {
1910
- const provider = opts.provider ?? "claude";
1911
- if (provider === "codex") {
1912
- return await runCodexAgent(opts);
1913
- }
1914
- if (provider === "cursor") {
1915
- return await runCursorAgent(opts);
1916
- }
1917
- return await runClaudeAgent(opts);
1918
- }
1919
- async function runClaudeAgent(opts) {
1920
- const claudeExecutable = resolveClaudeExecutable();
1921
- const q = query({
1922
- prompt: opts.prompt,
1923
- options: {
1924
- systemPrompt: opts.systemPrompt,
1925
- allowedTools: opts.allowedTools,
1926
- agents: opts.agents ?? {},
1927
- cwd: opts.cwd,
1928
- model: opts.model ?? DEFAULT_AGENT_MODEL,
1929
- maxTurns: opts.maxTurns ?? 100,
1930
- ...claudeExecutable !== void 0 ? { pathToClaudeCodeExecutable: claudeExecutable } : {},
1931
- env: {
1932
- ...process.env,
1933
- CODEALMANAC_INTERNAL_SESSION: "1"
1934
- },
1935
- // REQUIRED for streaming text deltas. Without it, `stream_event`
1936
- // messages never fire and the CLI has no progress visibility during
1937
- // long turns. See docs/research/agent-sdk.md §12 pitfall #1.
1938
- includePartialMessages: true
1939
- }
1940
- });
1941
- let cost = 0;
1942
- let turns = 0;
1943
- let result = "";
1944
- let sessionId;
1945
- let success = false;
1946
- let errorMsg;
1947
- try {
1948
- for await (const msg of q) {
1949
- opts.onMessage?.(msg);
1950
- if (sessionId === void 0 && typeof msg.session_id === "string") {
1951
- sessionId = msg.session_id;
1952
- }
1953
- if (msg.type === "result") {
1954
- cost = msg.total_cost_usd;
1955
- turns = msg.num_turns;
1956
- if (msg.subtype === "success") {
1957
- success = true;
1958
- result = msg.result;
1959
- } else {
1960
- success = false;
1961
- errorMsg = // `SDKResultError` variants don't carry a `result` string; the
1962
- // useful detail lives in `errors` (array of strings) or the
1963
- // subtype itself (e.g. "error_max_turns").
1964
- (msg.errors?.join("; ") ?? "") || `agent error: ${msg.subtype}`;
1965
- }
1966
- }
1967
- }
1968
- } catch (err) {
1969
- errorMsg = err instanceof Error ? err.message : String(err);
1970
- success = false;
1949
+ // src/agent/selection.ts
1950
+ async function resolveAgentSelection(args) {
1951
+ const config = await readConfig({ cwd: args.cwd });
1952
+ const rawAgent = args.agent ?? process.env.ALMANAC_AGENT ?? config.agent.default;
1953
+ const agentSource = args.agent !== void 0 ? "flag" : process.env.ALMANAC_AGENT !== void 0 ? "env" : "config";
1954
+ const parsed = parseAgentSelection(rawAgent);
1955
+ if (parsed.provider === null || !isAgentProviderId(parsed.provider)) {
1956
+ return {
1957
+ ok: false,
1958
+ error: `unknown agent '${rawAgent}'. Expected one of: claude, codex, cursor.`
1959
+ };
1971
1960
  }
1972
- return { success, cost, turns, result, sessionId, error: errorMsg };
1961
+ const provider = parsed.provider;
1962
+ const configuredModel = config.agent.models[provider] ?? void 0;
1963
+ const model = args.model !== void 0 ? args.model : parsed.model !== void 0 && agentSource === "flag" ? parsed.model : process.env.ALMANAC_MODEL !== void 0 ? process.env.ALMANAC_MODEL : parsed.model !== void 0 ? parsed.model : configuredModel === null ? void 0 : configuredModel;
1964
+ return { ok: true, provider, model };
1973
1965
  }
1974
- function combinedPrompt(opts) {
1975
- const reviewerFallback = buildReviewerFallback(opts);
1976
- return `${opts.systemPrompt}${reviewerFallback}
1977
1966
 
1978
- ---
1979
-
1980
- ${opts.prompt}`;
1981
- }
1982
- function buildReviewerFallback(opts) {
1983
- if ((opts.provider ?? "claude") === "claude") return "";
1984
- const reviewer = opts.agents?.reviewer;
1985
- if (reviewer === void 0) return "";
1986
- return "\n\nNon-Claude provider note: this runtime does not receive Claude's nested Agent tool contract. When the writer prompt asks you to invoke the reviewer subagent, perform that review pass yourself before final wiki edits. Treat this reviewer prompt as read-only review guidance:\n\n" + reviewer.prompt;
1987
- }
1988
- async function runCodexAgent(opts) {
1989
- const args = [
1990
- "exec",
1991
- "--json",
1992
- "--sandbox",
1993
- "workspace-write",
1994
- "--skip-git-repo-check",
1995
- "-C",
1996
- opts.cwd
1997
- ];
1998
- if (opts.model !== void 0 && opts.model.length > 0) {
1999
- args.push("--model", opts.model);
2000
- }
2001
- args.push(combinedPrompt(opts));
2002
- return await runJsonlCli({
2003
- command: "codex",
2004
- args,
2005
- cwd: opts.cwd,
2006
- env: { ...process.env, CODEALMANAC_INTERNAL_SESSION: "1" },
2007
- onMessage: opts.onMessage,
2008
- parseFinal: parseCodexFinal
2009
- });
2010
- }
2011
- async function runCursorAgent(opts) {
2012
- const args = [
2013
- "--print",
2014
- "--output-format",
2015
- "stream-json",
2016
- "--stream-partial-output",
2017
- "--trust",
2018
- "--workspace",
2019
- opts.cwd
2020
- ];
2021
- if (opts.model !== void 0 && opts.model.length > 0) {
2022
- args.push("--model", opts.model);
2023
- }
2024
- args.push(combinedPrompt(opts));
2025
- return await runJsonlCli({
2026
- command: "cursor-agent",
2027
- args,
2028
- cwd: opts.cwd,
2029
- env: { ...process.env, CODEALMANAC_INTERNAL_SESSION: "1" },
2030
- onMessage: opts.onMessage,
2031
- parseFinal: parseCursorFinal
2032
- });
2033
- }
2034
- function runJsonlCli(opts) {
2035
- return new Promise((resolve) => {
2036
- const child = spawn(opts.command, opts.args, {
2037
- cwd: opts.cwd,
2038
- env: opts.env,
2039
- stdio: ["ignore", "pipe", "pipe"]
2040
- });
2041
- let stdoutBuf = "";
2042
- let stderr = "";
2043
- let cost = 0;
2044
- let turns = 0;
2045
- let result = "";
2046
- let sessionId;
2047
- let usage;
2048
- let success = false;
2049
- let finalSeen = false;
2050
- let error;
2051
- const observe = (msg) => {
2052
- opts.onMessage?.(msg);
2053
- if (sessionId === void 0 && typeof msg.session_id === "string" && msg.session_id.length > 0) {
2054
- sessionId = msg.session_id;
2055
- }
2056
- if (sessionId === void 0 && typeof msg.thread_id === "string" && msg.thread_id.length > 0) {
2057
- sessionId = msg.thread_id;
2058
- }
2059
- const final = opts.parseFinal(msg);
2060
- if (final === null) return;
2061
- finalSeen = true;
2062
- if (final.cost !== void 0) cost = final.cost;
2063
- if (final.turns !== void 0) turns = final.turns;
2064
- if (final.result !== void 0) result = final.result;
2065
- if (final.sessionId !== void 0) sessionId = final.sessionId;
2066
- if (final.usage !== void 0) usage = final.usage;
2067
- if (final.success !== void 0) success = final.success;
2068
- if (final.error !== void 0) error = final.error;
2069
- };
2070
- const flushLines = () => {
2071
- let idx = stdoutBuf.indexOf("\n");
2072
- while (idx !== -1) {
2073
- const rawLine = stdoutBuf.slice(0, idx);
2074
- stdoutBuf = stdoutBuf.slice(idx + 1);
2075
- const line = rawLine.trim();
2076
- if (line.length > 0) {
2077
- try {
2078
- const parsed = JSON.parse(line);
2079
- observe(parsed);
2080
- } catch {
2081
- }
2082
- }
2083
- idx = stdoutBuf.indexOf("\n");
2084
- }
1967
+ // src/cli/outcome.ts
1968
+ function renderOutcome(outcome, opts = {}) {
1969
+ const exitCode = opts.exitCode ?? defaultExitCode(outcome);
1970
+ if (opts.json === true) {
1971
+ return {
1972
+ stdout: `${JSON.stringify(outcome, null, 2)}
1973
+ `,
1974
+ stderr: "",
1975
+ exitCode
2085
1976
  };
2086
- child.stdout.on("data", (chunk) => {
2087
- stdoutBuf += chunk.toString("utf8");
2088
- flushLines();
2089
- });
2090
- child.stderr.on("data", (chunk) => {
2091
- stderr += chunk.toString("utf8");
2092
- });
2093
- child.on("error", (err) => {
2094
- resolve({
2095
- success: false,
2096
- cost,
2097
- turns,
2098
- result,
2099
- sessionId,
2100
- usage,
2101
- error: err.code === "ENOENT" ? `${opts.command} not found on PATH` : err.message
2102
- });
2103
- });
2104
- child.on("close", (code) => {
2105
- flushLines();
2106
- if (stdoutBuf.trim().length > 0) {
2107
- try {
2108
- observe(JSON.parse(stdoutBuf.trim()));
2109
- } catch {
2110
- }
2111
- }
2112
- if (code === 0 && finalSeen && success) {
2113
- resolve({ success, cost, turns, result, sessionId, usage });
2114
- return;
2115
- }
2116
- const firstStderr = stderr.trim().split("\n")[0];
2117
- resolve({
2118
- success: false,
2119
- cost,
2120
- turns,
2121
- result,
2122
- sessionId,
2123
- usage,
2124
- error: error ?? (firstStderr !== void 0 && firstStderr.length > 0 ? firstStderr : `${opts.command} exited ${code ?? 1}`)
2125
- });
2126
- });
2127
- });
2128
- }
2129
- function parseCodexFinal(msg) {
2130
- if (msg.type === "item.completed") {
2131
- const item = msg.item;
2132
- if (item !== null && typeof item === "object") {
2133
- const obj = item;
2134
- if (obj.type === "agent_message" && typeof obj.text === "string") {
2135
- return { result: obj.text };
2136
- }
2137
- }
2138
- return null;
2139
1977
  }
2140
- if (msg.type === "turn.completed") {
2141
- return { success: true, turns: 1, usage: parseUsage(msg.usage) };
1978
+ if (outcome.type === "needs-action") {
1979
+ return {
1980
+ stdout: opts.stdout ?? "",
1981
+ stderr: `almanac: ${outcome.message}
1982
+ ${outcome.fix}
1983
+ `,
1984
+ exitCode
1985
+ };
2142
1986
  }
2143
- if (msg.type === "turn.failed" || msg.type === "error") {
1987
+ if (outcome.type === "error") {
2144
1988
  return {
2145
- success: false,
2146
- error: typeof msg.message === "string" ? msg.message : typeof msg.error === "string" ? msg.error : "codex turn failed"
1989
+ stdout: opts.stdout ?? "",
1990
+ stderr: `almanac: ${outcome.message}
1991
+ `,
1992
+ exitCode
2147
1993
  };
2148
1994
  }
2149
- return null;
2150
- }
2151
- function parseUsage(value) {
2152
- if (value === null || typeof value !== "object") return void 0;
2153
- const obj = value;
2154
1995
  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")
1996
+ stdout: opts.stdout ?? `${outcome.message}
1997
+ `,
1998
+ stderr: "",
1999
+ exitCode
2159
2000
  };
2160
2001
  }
2161
- function numberField(input, key) {
2162
- const value = input[key];
2163
- return typeof value === "number" ? value : void 0;
2002
+ function defaultExitCode(outcome) {
2003
+ switch (outcome.type) {
2004
+ case "success":
2005
+ case "noop":
2006
+ return 0;
2007
+ case "needs-action":
2008
+ case "error":
2009
+ return 1;
2010
+ }
2164
2011
  }
2165
- function parseCursorFinal(msg) {
2166
- if (msg.type !== "result") return null;
2167
- const isError = msg.is_error === true || msg.subtype !== "success";
2168
- return {
2169
- success: !isError,
2170
- turns: 1,
2171
- result: typeof msg.result === "string" ? msg.result : "",
2172
- sessionId: typeof msg.session_id === "string" ? msg.session_id : void 0,
2173
- usage: parseUsage(msg.usage),
2174
- error: isError ? typeof msg.result === "string" ? msg.result : `cursor result: ${String(msg.subtype ?? "error")}` : void 0
2175
- };
2012
+
2013
+ // src/agent/sdk.ts
2014
+ async function runAgent(opts) {
2015
+ const provider = opts.provider ?? "claude";
2016
+ return await getAgentProvider(provider).run(opts);
2176
2017
  }
2177
2018
 
2178
2019
  // src/commands/init.ts
@@ -2318,42 +2159,47 @@ and optional \`files:\`. The rest is prose.
2318
2159
  // src/commands/bootstrap.ts
2319
2160
  var BOOTSTRAP_TOOLS = ["Read", "Write", "Edit", "Glob", "Grep", "Bash"];
2320
2161
  async function runBootstrap(options) {
2162
+ const repoRoot = findNearestAlmanacDir(options.cwd) ?? options.cwd;
2321
2163
  const providerResolution = await resolveAgentSelection({
2322
2164
  agent: options.agent,
2323
- model: options.model
2165
+ model: options.model,
2166
+ cwd: repoRoot
2324
2167
  });
2325
2168
  if (!providerResolution.ok) {
2326
- return {
2327
- stdout: "",
2328
- stderr: `almanac: ${providerResolution.error}
2329
- `,
2330
- exitCode: 1
2331
- };
2169
+ return renderOutcome(
2170
+ { type: "error", message: providerResolution.error },
2171
+ { json: options.json }
2172
+ );
2332
2173
  }
2333
2174
  const { provider, model } = providerResolution;
2334
2175
  try {
2335
2176
  await assertAgentAuth({ provider, spawnCli: options.spawnCli });
2336
2177
  } catch (err) {
2337
2178
  const msg = err instanceof Error ? err.message : String(err);
2338
- return {
2339
- stdout: "",
2340
- stderr: `almanac: ${msg}
2341
- `,
2342
- exitCode: 1
2343
- };
2179
+ return renderOutcome(
2180
+ {
2181
+ type: "needs-action",
2182
+ message: msg,
2183
+ fix: authFixFor(provider),
2184
+ data: { provider }
2185
+ },
2186
+ { json: options.json }
2187
+ );
2344
2188
  }
2345
- const repoRoot = findNearestAlmanacDir(options.cwd) ?? options.cwd;
2346
2189
  const almanacDir = getRepoAlmanacDir(repoRoot);
2347
2190
  const pagesDir = join6(almanacDir, "pages");
2348
2191
  if (options.force !== true && existsSync5(pagesDir)) {
2349
2192
  const existing = await countMarkdownPages(pagesDir);
2350
2193
  if (existing > 0) {
2351
- return {
2352
- stdout: "",
2353
- stderr: `almanac: .almanac/ already initialized with ${existing} page${existing === 1 ? "" : "s"}. Use 'almanac capture' instead, or --force to overwrite.
2354
- `,
2355
- exitCode: 1
2356
- };
2194
+ return renderOutcome(
2195
+ {
2196
+ type: "needs-action",
2197
+ message: `.almanac/ already initialized with ${existing} page${existing === 1 ? "" : "s"}.`,
2198
+ fix: "run: almanac capture (or pass --force to overwrite)",
2199
+ data: { pages: existing }
2200
+ },
2201
+ { json: options.json }
2202
+ );
2357
2203
  }
2358
2204
  }
2359
2205
  if (!existsSync5(almanacDir)) {
@@ -2361,12 +2207,10 @@ async function runBootstrap(options) {
2361
2207
  await initWiki({ cwd: repoRoot });
2362
2208
  } catch (err) {
2363
2209
  const msg = err instanceof Error ? err.message : String(err);
2364
- return {
2365
- stdout: "",
2366
- stderr: `almanac: init failed during bootstrap: ${msg}
2367
- `,
2368
- exitCode: 1
2369
- };
2210
+ return renderOutcome(
2211
+ { type: "error", message: `init failed during bootstrap: ${msg}` },
2212
+ { json: options.json }
2213
+ );
2370
2214
  }
2371
2215
  }
2372
2216
  const systemPrompt = await loadPrompt("bootstrap");
@@ -2379,7 +2223,7 @@ async function runBootstrap(options) {
2379
2223
  const out = process.stdout;
2380
2224
  const formatter = new StreamingFormatter({
2381
2225
  write: (line) => {
2382
- if (options.quiet !== true) out.write(line);
2226
+ if (options.quiet !== true && options.json !== true) out.write(line);
2383
2227
  }
2384
2228
  });
2385
2229
  const onMessage = (msg) => {
@@ -2408,33 +2252,47 @@ async function runBootstrap(options) {
2408
2252
  }
2409
2253
  const finalLine = formatFinalLine(result, logPath, repoRoot);
2410
2254
  if (result.success) {
2411
- return {
2412
- stdout: `${finalLine}
2413
- `,
2414
- stderr: "",
2415
- exitCode: 0
2416
- };
2255
+ return renderOutcome(
2256
+ {
2257
+ type: "success",
2258
+ message: finalLine,
2259
+ data: runData(result, logPath, repoRoot)
2260
+ },
2261
+ { json: options.json }
2262
+ );
2417
2263
  }
2418
- return {
2419
- stdout: options.quiet === true ? "" : `${finalLine}
2420
- `,
2421
- stderr: `almanac: bootstrap failed: ${result.error ?? "unknown error"}
2422
- `,
2423
- exitCode: 1
2424
- };
2264
+ return renderOutcome(
2265
+ {
2266
+ type: "error",
2267
+ message: `bootstrap failed: ${result.error ?? "unknown error"}`,
2268
+ data: runData(result, logPath, repoRoot)
2269
+ },
2270
+ {
2271
+ json: options.json,
2272
+ stdout: options.quiet === true || options.json === true ? "" : `${finalLine}
2273
+ `
2274
+ }
2275
+ );
2425
2276
  }
2426
- async function resolveAgentSelection(args) {
2427
- const config = await readConfig();
2428
- const rawProvider = args.agent ?? config.agent.default;
2429
- if (!isAgentProviderId(rawProvider)) {
2430
- return {
2431
- ok: false,
2432
- error: `unknown agent '${rawProvider}'. Expected one of: claude, codex, cursor.`
2433
- };
2277
+ function authFixFor(provider) {
2278
+ switch (provider) {
2279
+ case "claude":
2280
+ return "run: claude auth login --claudeai (or export ANTHROPIC_API_KEY)";
2281
+ case "codex":
2282
+ return "run: codex login";
2283
+ case "cursor":
2284
+ return "run: cursor-agent login";
2285
+ default:
2286
+ return "run: almanac agents doctor";
2434
2287
  }
2435
- const configuredModel = config.agent.models[rawProvider] ?? void 0;
2436
- const model = args.model !== void 0 ? args.model : configuredModel === null ? void 0 : configuredModel;
2437
- return { ok: true, provider: rawProvider, model };
2288
+ }
2289
+ function runData(result, logPath, repoRoot) {
2290
+ return {
2291
+ transcript: relative(repoRoot, logPath),
2292
+ cost: result.cost,
2293
+ turns: result.turns,
2294
+ sessionId: result.sessionId
2295
+ };
2438
2296
  }
2439
2297
  function formatFinalLine(result, logPath, repoRoot) {
2440
2298
  const status = result.success ? "done" : "failed";
@@ -2898,37 +2756,46 @@ var WRITER_TOOLS = ["Read", "Write", "Edit", "Glob", "Grep", "Bash", "Agent"];
2898
2756
  var REVIEWER_TOOLS = ["Read", "Grep", "Glob", "Bash"];
2899
2757
  var REVIEWER_DESCRIPTION = "Reviews proposed wiki changes against the full knowledge base for cohesion, duplication, missing links, notability, and writing conventions.";
2900
2758
  async function runCapture(options) {
2901
- const providerResolution = await resolveAgentSelection2({
2759
+ const repoRoot = findNearestAlmanacDir(options.cwd);
2760
+ if (repoRoot === null) {
2761
+ return renderOutcome(
2762
+ {
2763
+ type: "needs-action",
2764
+ message: "no .almanac/ found in this directory or any parent.",
2765
+ fix: "run: almanac bootstrap"
2766
+ },
2767
+ { json: options.json }
2768
+ );
2769
+ }
2770
+ const providerResolution = await resolveAgentSelection({
2902
2771
  agent: options.agent,
2903
- model: options.model
2772
+ model: options.model,
2773
+ cwd: repoRoot
2904
2774
  });
2905
2775
  if (!providerResolution.ok) {
2906
- return {
2907
- stdout: "",
2908
- stderr: `almanac: ${providerResolution.error}
2909
- `,
2910
- exitCode: 1
2911
- };
2776
+ return renderOutcome(
2777
+ { type: "error", message: providerResolution.error },
2778
+ { json: options.json }
2779
+ );
2912
2780
  }
2913
2781
  const { provider, model } = providerResolution;
2782
+ const statusModel = model ?? getProviderDefaultModel(provider) ?? "provider default";
2914
2783
  try {
2915
- await assertAgentAuth({ provider, spawnCli: options.spawnCli });
2784
+ await (options.assertAgentAuthFn ?? assertAgentAuth)({
2785
+ provider,
2786
+ spawnCli: options.spawnCli
2787
+ });
2916
2788
  } catch (err) {
2917
2789
  const msg = err instanceof Error ? err.message : String(err);
2918
- return {
2919
- stdout: "",
2920
- stderr: `almanac: ${msg}
2921
- `,
2922
- exitCode: 1
2923
- };
2924
- }
2925
- const repoRoot = findNearestAlmanacDir(options.cwd);
2926
- if (repoRoot === null) {
2927
- return {
2928
- stdout: "",
2929
- stderr: "almanac: no .almanac/ found in this directory or any parent. Run 'almanac bootstrap' first.\n",
2930
- exitCode: 1
2931
- };
2790
+ return renderOutcome(
2791
+ {
2792
+ type: "needs-action",
2793
+ message: msg,
2794
+ fix: authFixFor2(provider),
2795
+ data: { provider }
2796
+ },
2797
+ { json: options.json }
2798
+ );
2932
2799
  }
2933
2800
  const almanacDir = getRepoAlmanacDir(repoRoot);
2934
2801
  const pagesDir = join8(almanacDir, "pages");
@@ -2939,12 +2806,14 @@ async function runCapture(options) {
2939
2806
  claudeProjectsDir: options.claudeProjectsDir
2940
2807
  });
2941
2808
  if (!transcriptResolution.ok) {
2942
- return {
2943
- stdout: "",
2944
- stderr: `almanac: ${transcriptResolution.error}
2945
- `,
2946
- exitCode: 1
2947
- };
2809
+ return renderOutcome(
2810
+ {
2811
+ type: "needs-action",
2812
+ message: transcriptResolution.error,
2813
+ fix: transcriptFix(transcriptResolution.error)
2814
+ },
2815
+ { json: options.json }
2816
+ );
2948
2817
  }
2949
2818
  const transcriptPath = transcriptResolution.path;
2950
2819
  const snapshotBefore = await snapshotPages(pagesDir);
@@ -2970,7 +2839,7 @@ async function runCapture(options) {
2970
2839
  stem: logStem,
2971
2840
  sessionId: options.sessionId,
2972
2841
  transcriptPath,
2973
- model: options.model,
2842
+ model: statusModel,
2974
2843
  startedAt
2975
2844
  });
2976
2845
  await writeCaptureRunRecord(statePath, stateRecord).catch(() => {
@@ -2979,7 +2848,7 @@ async function runCapture(options) {
2979
2848
  const out = process.stdout;
2980
2849
  const formatter = new StreamingFormatter({
2981
2850
  write: (line) => {
2982
- if (options.quiet !== true) out.write(line);
2851
+ if (options.quiet !== true && options.json !== true) out.write(line);
2983
2852
  }
2984
2853
  });
2985
2854
  formatter.setAgent("writer");
@@ -3034,13 +2903,18 @@ Working directory: ${repoRoot}.`;
3034
2903
  })
3035
2904
  ).catch(() => {
3036
2905
  });
3037
- return {
3038
- stdout: "",
3039
- stderr: `almanac: capture failed: ${result.error ?? "unknown error"}
3040
- (transcript: ${relative3(repoRoot, logPath)})
3041
- `,
3042
- exitCode: 1
3043
- };
2906
+ return renderOutcome(
2907
+ {
2908
+ type: "error",
2909
+ message: `capture failed: ${result.error ?? "unknown error"}
2910
+ (transcript: ${relative3(repoRoot, logPath)})`,
2911
+ data: captureOutcomeData(result, delta, logPath, repoRoot)
2912
+ },
2913
+ {
2914
+ json: options.json,
2915
+ stdout: ""
2916
+ }
2917
+ );
3044
2918
  }
3045
2919
  await writeCaptureRunRecord(
3046
2920
  statePath,
@@ -3053,25 +2927,46 @@ Working directory: ${repoRoot}.`;
3053
2927
  ).catch(() => {
3054
2928
  });
3055
2929
  const summary = formatSummary2(result, delta, logPath, repoRoot);
2930
+ return renderOutcome(
2931
+ {
2932
+ type: isNoopDelta(delta) ? "noop" : "success",
2933
+ message: summary,
2934
+ data: captureOutcomeData(result, delta, logPath, repoRoot)
2935
+ },
2936
+ { json: options.json }
2937
+ );
2938
+ }
2939
+ function transcriptFix(error) {
2940
+ if (error.includes("transcript not found")) {
2941
+ return "pass a valid <transcript-path>";
2942
+ }
2943
+ return "pass --session <id> or <transcript-path>";
2944
+ }
2945
+ function authFixFor2(provider) {
2946
+ switch (provider) {
2947
+ case "claude":
2948
+ return "run: claude auth login --claudeai (or export ANTHROPIC_API_KEY)";
2949
+ case "codex":
2950
+ return "run: codex login";
2951
+ case "cursor":
2952
+ return "run: cursor-agent login";
2953
+ default:
2954
+ return "run: almanac agents doctor";
2955
+ }
2956
+ }
2957
+ function captureOutcomeData(result, delta, logPath, repoRoot) {
3056
2958
  return {
3057
- stdout: `${summary}
3058
- `,
3059
- stderr: "",
3060
- exitCode: 0
2959
+ transcript: relative3(repoRoot, logPath),
2960
+ updated: delta.updated,
2961
+ created: delta.created,
2962
+ archived: delta.archived,
2963
+ cost: result.cost,
2964
+ turns: result.turns,
2965
+ sessionId: result.sessionId
3061
2966
  };
3062
2967
  }
3063
- async function resolveAgentSelection2(args) {
3064
- const config = await readConfig();
3065
- const rawProvider = args.agent ?? config.agent.default;
3066
- if (!isAgentProviderId(rawProvider)) {
3067
- return {
3068
- ok: false,
3069
- error: `unknown agent '${rawProvider}'. Expected one of: claude, codex, cursor.`
3070
- };
3071
- }
3072
- const configuredModel = config.agent.models[rawProvider] ?? void 0;
3073
- const model = args.model !== void 0 ? args.model : configuredModel === null ? void 0 : configuredModel;
3074
- return { ok: true, provider: rawProvider, model };
2968
+ function isNoopDelta(delta) {
2969
+ return delta.created === 0 && delta.updated === 0 && delta.archived === 0;
3075
2970
  }
3076
2971
  async function resolveTranscript(args) {
3077
2972
  if (args.explicit !== void 0 && args.explicit.length > 0) {
@@ -3251,19 +3146,20 @@ async function runReindex(options) {
3251
3146
  function registerWikiLifecycleCommands(program) {
3252
3147
  program.command("bootstrap").description(
3253
3148
  "scaffold a wiki in this repo via an AI agent (requires ANTHROPIC_API_KEY or Claude subscription)"
3254
- ).option("--quiet", "suppress per-tool streaming; print only the final line").option("--agent <agent>", "agent provider: claude, codex, or cursor").option("--model <model>", "override the agent model").option("--force", "overwrite an existing populated wiki (default: refuse)").action(
3149
+ ).option("--quiet", "suppress per-tool streaming; print only the final line").option("--agent <agent>", "agent provider: claude, codex, or cursor").option("--model <model>", "override the agent model").option("--force", "overwrite an existing populated wiki (default: refuse)").option("--json", "emit structured CommandOutcome JSON").action(
3255
3150
  async (opts) => {
3256
3151
  const result = await runBootstrap({
3257
3152
  cwd: process.cwd(),
3258
3153
  quiet: opts.quiet,
3259
3154
  agent: opts.agent,
3260
3155
  model: opts.model,
3261
- force: opts.force
3156
+ force: opts.force,
3157
+ json: opts.json
3262
3158
  });
3263
3159
  emit(result);
3264
3160
  }
3265
3161
  );
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(
3162
+ 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").option("--json", "emit structured CommandOutcome JSON").action(
3267
3163
  async (transcript, opts) => {
3268
3164
  await autoRegisterIfNeeded(process.cwd());
3269
3165
  const result = await runCapture({
@@ -3272,7 +3168,8 @@ function registerWikiLifecycleCommands(program) {
3272
3168
  sessionId: opts.session,
3273
3169
  quiet: opts.quiet,
3274
3170
  agent: opts.agent,
3275
- model: opts.model
3171
+ model: opts.model,
3172
+ json: opts.json
3276
3173
  });
3277
3174
  emit(result);
3278
3175
  }
@@ -3285,13 +3182,16 @@ function registerWikiLifecycleCommands(program) {
3285
3182
  });
3286
3183
  emit(result);
3287
3184
  });
3288
- program.command("ps").description("show running and recent capture jobs").option("--json", "emit structured JSON").action(async (opts) => {
3185
+ program.command("ps").description("deprecated alias for capture status").option("--json", "emit structured JSON").action(async (opts) => {
3289
3186
  await autoRegisterIfNeeded(process.cwd());
3290
3187
  const result = await runCaptureStatus({
3291
3188
  cwd: process.cwd(),
3292
3189
  json: opts.json
3293
3190
  });
3294
- emit(result);
3191
+ emit(withWarning(
3192
+ result,
3193
+ deprecationWarning("almanac ps", "almanac capture status")
3194
+ ));
3295
3195
  });
3296
3196
  const hook = program.command("hook").description("manage the SessionEnd auto-capture hook");
3297
3197
  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) => {
@@ -3335,4 +3235,4 @@ function registerCommands(program) {
3335
3235
  export {
3336
3236
  registerCommands
3337
3237
  };
3338
- //# sourceMappingURL=register-commands-7QCIENRZ.js.map
3238
+ //# sourceMappingURL=register-commands-XTK2G2FB.js.map