codealmanac 0.2.3 → 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 (57) 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-HNVOYWC2.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-NBVIEZZQ.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-XNTNXEWY.js → chunk-QRK3JLFX.js} +131 -49
  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-6BOB6KAN.js → cli-MYMZ66EN.js} +123 -33
  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-W5KQQLAX.js +17 -0
  27. package/dist/{register-commands-IXYE5CNZ.js → register-commands-XTK2G2FB.js} +293 -395
  28. package/dist/register-commands-XTK2G2FB.js.map +1 -0
  29. package/dist/uninstall-N7JY7ZV2.js +15 -0
  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-RVYQ44DB.js +0 -16
  35. package/dist/auth-S5DVUIUJ.js +0 -18
  36. package/dist/chunk-HNVOYWC2.js.map +0 -1
  37. package/dist/chunk-P3LDTCLB.js.map +0 -1
  38. package/dist/chunk-PIYJQE4Z.js +0 -102
  39. package/dist/chunk-PIYJQE4Z.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-TWM7I2LU.js +0 -116
  43. package/dist/chunk-TWM7I2LU.js.map +0 -1
  44. package/dist/chunk-V3QOQSXI.js.map +0 -1
  45. package/dist/chunk-WRUSDYYE.js +0 -97
  46. package/dist/chunk-WRUSDYYE.js.map +0 -1
  47. package/dist/chunk-XNTNXEWY.js.map +0 -1
  48. package/dist/cli-6BOB6KAN.js.map +0 -1
  49. package/dist/doctor-DD7EQGCA.js +0 -18
  50. package/dist/register-commands-IXYE5CNZ.js.map +0 -1
  51. package/dist/uninstall-OBV4Z3JE.js +0 -16
  52. /package/dist/{agents-RVYQ44DB.js.map → agents-4Y7X24WW.js.map} +0 -0
  53. /package/dist/{chunk-NBVIEZZQ.js.map → chunk-H6QKCB7M.js.map} +0 -0
  54. /package/dist/{auth-S5DVUIUJ.js.map → config-ML2RCR7J.js.map} +0 -0
  55. /package/dist/{doctor-DD7EQGCA.js.map → doctor-W5KQQLAX.js.map} +0 -0
  56. /package/dist/{uninstall-OBV4Z3JE.js.map → uninstall-N7JY7ZV2.js.map} +0 -0
  57. /package/dist/{update-RAF7QRYF.js.map → update-P2IPG7RO.js.map} +0 -0
@@ -1,15 +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 {
11
+ runAgentsDoctor,
9
12
  runAgentsList,
10
- runSetAgentModel,
11
- runSetDefaultAgent
12
- } from "./chunk-PIYJQE4Z.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";
13
24
  import {
14
25
  addEntry,
15
26
  ancestorsInFile,
@@ -35,8 +46,8 @@ import {
35
46
  } from "./chunk-KQUVMF27.js";
36
47
  import {
37
48
  runDoctor
38
- } from "./chunk-HNVOYWC2.js";
39
- import "./chunk-V3QOQSXI.js";
49
+ } from "./chunk-CW4HRLMS.js";
50
+ import "./chunk-TILAKDN6.js";
40
51
  import "./chunk-4CODZRHH.js";
41
52
  import {
42
53
  BLUE,
@@ -46,29 +57,30 @@ import {
46
57
  } from "./chunk-FM3VRDK7.js";
47
58
  import {
48
59
  runUninstall
49
- } from "./chunk-NBVIEZZQ.js";
60
+ } from "./chunk-H6QKCB7M.js";
50
61
  import {
51
62
  runSetup
52
- } from "./chunk-XNTNXEWY.js";
63
+ } from "./chunk-QRK3JLFX.js";
53
64
  import {
54
65
  runHookInstall,
55
66
  runHookStatus,
56
67
  runHookUninstall
57
68
  } from "./chunk-447U3GQJ.js";
58
69
  import {
59
- assertAgentAuth
60
- } from "./chunk-TWM7I2LU.js";
61
- import {
62
- resolveClaudeExecutable
63
- } from "./chunk-SSYMRT4I.js";
70
+ DEFAULT_AGENT_MODEL,
71
+ assertAgentAuth,
72
+ getAgentProvider,
73
+ getProviderDefaultModel,
74
+ parseAgentSelection
75
+ } from "./chunk-BF2J4XTC.js";
64
76
  import {
65
77
  runUpdate
66
- } from "./chunk-QQHIVTXT.js";
78
+ } from "./chunk-MRRX4UQB.js";
67
79
  import "./chunk-F53U6JQG.js";
68
80
  import {
69
81
  isAgentProviderId,
70
82
  readConfig
71
- } from "./chunk-WRUSDYYE.js";
83
+ } from "./chunk-P5WGG4FJ.js";
72
84
  import {
73
85
  findNearestAlmanacDir,
74
86
  getRepoAlmanacDir
@@ -1697,11 +1709,11 @@ function registerQueryCommands(program) {
1697
1709
  "--mentions <path>",
1698
1710
  "pages referencing this path; matches exact file, trailing-slash folders, and any file under a folder prefix"
1699
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(
1700
- async (query2, opts) => {
1712
+ async (query, opts) => {
1701
1713
  await autoRegisterIfNeeded(process.cwd());
1702
1714
  const result = await runSearch({
1703
1715
  cwd: process.cwd(),
1704
- query: query2,
1716
+ query,
1705
1717
  topics: opts.topic ?? [],
1706
1718
  mentions: opts.mentions,
1707
1719
  since: opts.since,
@@ -1716,7 +1728,7 @@ function registerQueryCommands(program) {
1716
1728
  emit(result);
1717
1729
  }
1718
1730
  );
1719
- 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(
1720
1732
  async (slug, opts) => {
1721
1733
  await autoRegisterIfNeeded(process.cwd());
1722
1734
  const result = await runShow({
@@ -1739,7 +1751,10 @@ function registerQueryCommands(program) {
1739
1751
  updated: opts.updated,
1740
1752
  path: opts.path
1741
1753
  });
1742
- emit(result);
1754
+ emit(opts.raw === true ? withWarning(
1755
+ result,
1756
+ deprecationWarning("almanac show <slug> --raw", "almanac show <slug> --body")
1757
+ ) : result);
1743
1758
  }
1744
1759
  );
1745
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(
@@ -1778,13 +1793,39 @@ function registerSetupCommands(program) {
1778
1793
  agents.command("list").description("show Claude, Codex, and Cursor provider status").action(async () => {
1779
1794
  emit(await runAgentsList());
1780
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
+ });
1781
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) => {
1782
1823
  if (key === "default-agent") {
1783
- emit(await runSetDefaultAgent({ provider: value[0] ?? "" }));
1824
+ emit(await runDeprecatedSetDefaultAgent({ provider: value[0] ?? "" }));
1784
1825
  return;
1785
1826
  }
1786
1827
  if (key === "model") {
1787
- emit(await runSetAgentModel({
1828
+ emit(await runDeprecatedSetAgentModel({
1788
1829
  provider: value[0] ?? "",
1789
1830
  model: value[1]
1790
1831
  }));
@@ -1796,11 +1837,12 @@ function registerSetupCommands(program) {
1796
1837
  exitCode: 1
1797
1838
  });
1798
1839
  });
1799
- 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(
1800
1841
  async (opts) => {
1801
1842
  const result = await runSetup({
1802
1843
  yes: opts.yes,
1803
1844
  agent: opts.agent,
1845
+ model: opts.model,
1804
1846
  skipHook: opts.skipHook,
1805
1847
  skipGuides: opts.skipGuides
1806
1848
  });
@@ -1823,10 +1865,10 @@ function registerSetupCommands(program) {
1823
1865
  "silence the update banner for the current `latest_version` without installing"
1824
1866
  ).option("--check", "force a registry check now (bypasses the 24h cache); no install").option(
1825
1867
  "--enable-notifier",
1826
- "re-enable the pre-command update banner (writes ~/.almanac/config.json)"
1868
+ "deprecated: use `almanac config set update_notifier true`"
1827
1869
  ).option(
1828
1870
  "--disable-notifier",
1829
- "silence the pre-command update banner (writes ~/.almanac/config.json)"
1871
+ "deprecated: use `almanac config set update_notifier false`"
1830
1872
  ).action(
1831
1873
  async (opts) => {
1832
1874
  const result = await runUpdate({
@@ -1904,277 +1946,74 @@ async function loadPrompt(name) {
1904
1946
  return readFile4(path.join(dir, `${name}.md`), "utf8");
1905
1947
  }
1906
1948
 
1907
- // src/agent/sdk.ts
1908
- import { spawn } from "child_process";
1909
- import { query } from "@anthropic-ai/claude-agent-sdk";
1910
- var DEFAULT_AGENT_MODEL = "claude-sonnet-4-6";
1911
- async function runAgent(opts) {
1912
- const provider = opts.provider ?? "claude";
1913
- if (provider === "codex") {
1914
- return await runCodexAgent(opts);
1915
- }
1916
- if (provider === "cursor") {
1917
- return await runCursorAgent(opts);
1918
- }
1919
- return await runClaudeAgent(opts);
1920
- }
1921
- async function runClaudeAgent(opts) {
1922
- const claudeExecutable = resolveClaudeExecutable();
1923
- const q = query({
1924
- prompt: opts.prompt,
1925
- options: {
1926
- systemPrompt: opts.systemPrompt,
1927
- allowedTools: opts.allowedTools,
1928
- agents: opts.agents ?? {},
1929
- cwd: opts.cwd,
1930
- model: opts.model ?? DEFAULT_AGENT_MODEL,
1931
- maxTurns: opts.maxTurns ?? 100,
1932
- ...claudeExecutable !== void 0 ? { pathToClaudeCodeExecutable: claudeExecutable } : {},
1933
- env: {
1934
- ...process.env,
1935
- CODEALMANAC_INTERNAL_SESSION: "1"
1936
- },
1937
- // REQUIRED for streaming text deltas. Without it, `stream_event`
1938
- // messages never fire and the CLI has no progress visibility during
1939
- // long turns. See docs/research/agent-sdk.md §12 pitfall #1.
1940
- includePartialMessages: true
1941
- }
1942
- });
1943
- let cost = 0;
1944
- let turns = 0;
1945
- let result = "";
1946
- let sessionId;
1947
- let success = false;
1948
- let errorMsg;
1949
- try {
1950
- for await (const msg of q) {
1951
- opts.onMessage?.(msg);
1952
- if (sessionId === void 0 && typeof msg.session_id === "string") {
1953
- sessionId = msg.session_id;
1954
- }
1955
- if (msg.type === "result") {
1956
- cost = msg.total_cost_usd;
1957
- turns = msg.num_turns;
1958
- if (msg.subtype === "success") {
1959
- success = true;
1960
- result = msg.result;
1961
- } else {
1962
- success = false;
1963
- errorMsg = // `SDKResultError` variants don't carry a `result` string; the
1964
- // useful detail lives in `errors` (array of strings) or the
1965
- // subtype itself (e.g. "error_max_turns").
1966
- (msg.errors?.join("; ") ?? "") || `agent error: ${msg.subtype}`;
1967
- }
1968
- }
1969
- }
1970
- } catch (err) {
1971
- errorMsg = err instanceof Error ? err.message : String(err);
1972
- 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
+ };
1973
1960
  }
1974
- 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 };
1975
1965
  }
1976
- function combinedPrompt(opts) {
1977
- const reviewerFallback = buildReviewerFallback(opts);
1978
- return `${opts.systemPrompt}${reviewerFallback}
1979
-
1980
- ---
1981
1966
 
1982
- ${opts.prompt}`;
1983
- }
1984
- function buildReviewerFallback(opts) {
1985
- if ((opts.provider ?? "claude") === "claude") return "";
1986
- const reviewer = opts.agents?.reviewer;
1987
- if (reviewer === void 0) return "";
1988
- 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;
1989
- }
1990
- async function runCodexAgent(opts) {
1991
- const args = [
1992
- "exec",
1993
- "--json",
1994
- "--sandbox",
1995
- "workspace-write",
1996
- "--skip-git-repo-check",
1997
- "-C",
1998
- opts.cwd
1999
- ];
2000
- if (opts.model !== void 0 && opts.model.length > 0) {
2001
- args.push("--model", opts.model);
2002
- }
2003
- args.push(combinedPrompt(opts));
2004
- return await runJsonlCli({
2005
- command: "codex",
2006
- args,
2007
- cwd: opts.cwd,
2008
- env: { ...process.env, CODEALMANAC_INTERNAL_SESSION: "1" },
2009
- onMessage: opts.onMessage,
2010
- parseFinal: parseCodexFinal
2011
- });
2012
- }
2013
- async function runCursorAgent(opts) {
2014
- const args = [
2015
- "--print",
2016
- "--output-format",
2017
- "stream-json",
2018
- "--stream-partial-output",
2019
- "--trust",
2020
- "--workspace",
2021
- opts.cwd
2022
- ];
2023
- if (opts.model !== void 0 && opts.model.length > 0) {
2024
- args.push("--model", opts.model);
2025
- }
2026
- args.push(combinedPrompt(opts));
2027
- return await runJsonlCli({
2028
- command: "cursor-agent",
2029
- args,
2030
- cwd: opts.cwd,
2031
- env: { ...process.env, CODEALMANAC_INTERNAL_SESSION: "1" },
2032
- onMessage: opts.onMessage,
2033
- parseFinal: parseCursorFinal
2034
- });
2035
- }
2036
- function runJsonlCli(opts) {
2037
- return new Promise((resolve) => {
2038
- const child = spawn(opts.command, opts.args, {
2039
- cwd: opts.cwd,
2040
- env: opts.env,
2041
- stdio: ["ignore", "pipe", "pipe"]
2042
- });
2043
- let stdoutBuf = "";
2044
- let stderr = "";
2045
- let cost = 0;
2046
- let turns = 0;
2047
- let result = "";
2048
- let sessionId;
2049
- let usage;
2050
- let success = false;
2051
- let finalSeen = false;
2052
- let error;
2053
- const observe = (msg) => {
2054
- opts.onMessage?.(msg);
2055
- if (sessionId === void 0 && typeof msg.session_id === "string" && msg.session_id.length > 0) {
2056
- sessionId = msg.session_id;
2057
- }
2058
- if (sessionId === void 0 && typeof msg.thread_id === "string" && msg.thread_id.length > 0) {
2059
- sessionId = msg.thread_id;
2060
- }
2061
- const final = opts.parseFinal(msg);
2062
- if (final === null) return;
2063
- finalSeen = true;
2064
- if (final.cost !== void 0) cost = final.cost;
2065
- if (final.turns !== void 0) turns = final.turns;
2066
- if (final.result !== void 0) result = final.result;
2067
- if (final.sessionId !== void 0) sessionId = final.sessionId;
2068
- if (final.usage !== void 0) usage = final.usage;
2069
- if (final.success !== void 0) success = final.success;
2070
- if (final.error !== void 0) error = final.error;
2071
- };
2072
- const flushLines = () => {
2073
- let idx = stdoutBuf.indexOf("\n");
2074
- while (idx !== -1) {
2075
- const rawLine = stdoutBuf.slice(0, idx);
2076
- stdoutBuf = stdoutBuf.slice(idx + 1);
2077
- const line = rawLine.trim();
2078
- if (line.length > 0) {
2079
- try {
2080
- const parsed = JSON.parse(line);
2081
- observe(parsed);
2082
- } catch {
2083
- }
2084
- }
2085
- idx = stdoutBuf.indexOf("\n");
2086
- }
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
2087
1976
  };
2088
- child.stdout.on("data", (chunk) => {
2089
- stdoutBuf += chunk.toString("utf8");
2090
- flushLines();
2091
- });
2092
- child.stderr.on("data", (chunk) => {
2093
- stderr += chunk.toString("utf8");
2094
- });
2095
- child.on("error", (err) => {
2096
- resolve({
2097
- success: false,
2098
- cost,
2099
- turns,
2100
- result,
2101
- sessionId,
2102
- usage,
2103
- error: err.code === "ENOENT" ? `${opts.command} not found on PATH` : err.message
2104
- });
2105
- });
2106
- child.on("close", (code) => {
2107
- flushLines();
2108
- if (stdoutBuf.trim().length > 0) {
2109
- try {
2110
- observe(JSON.parse(stdoutBuf.trim()));
2111
- } catch {
2112
- }
2113
- }
2114
- if (code === 0 && finalSeen && success) {
2115
- resolve({ success, cost, turns, result, sessionId, usage });
2116
- return;
2117
- }
2118
- const firstStderr = stderr.trim().split("\n")[0];
2119
- resolve({
2120
- success: false,
2121
- cost,
2122
- turns,
2123
- result,
2124
- sessionId,
2125
- usage,
2126
- error: error ?? (firstStderr !== void 0 && firstStderr.length > 0 ? firstStderr : `${opts.command} exited ${code ?? 1}`)
2127
- });
2128
- });
2129
- });
2130
- }
2131
- function parseCodexFinal(msg) {
2132
- if (msg.type === "item.completed") {
2133
- const item = msg.item;
2134
- if (item !== null && typeof item === "object") {
2135
- const obj = item;
2136
- if (obj.type === "agent_message" && typeof obj.text === "string") {
2137
- return { result: obj.text };
2138
- }
2139
- }
2140
- return null;
2141
1977
  }
2142
- if (msg.type === "turn.completed") {
2143
- 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
+ };
2144
1986
  }
2145
- if (msg.type === "turn.failed" || msg.type === "error") {
1987
+ if (outcome.type === "error") {
2146
1988
  return {
2147
- success: false,
2148
- 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
2149
1993
  };
2150
1994
  }
2151
- return null;
2152
- }
2153
- function parseUsage(value) {
2154
- if (value === null || typeof value !== "object") return void 0;
2155
- const obj = value;
2156
1995
  return {
2157
- inputTokens: numberField(obj, "input_tokens") ?? numberField(obj, "inputTokens"),
2158
- cachedInputTokens: numberField(obj, "cached_input_tokens") ?? numberField(obj, "cachedInputTokens") ?? numberField(obj, "cacheReadTokens"),
2159
- outputTokens: numberField(obj, "output_tokens") ?? numberField(obj, "outputTokens"),
2160
- reasoningOutputTokens: numberField(obj, "reasoning_output_tokens") ?? numberField(obj, "reasoningOutputTokens")
1996
+ stdout: opts.stdout ?? `${outcome.message}
1997
+ `,
1998
+ stderr: "",
1999
+ exitCode
2161
2000
  };
2162
2001
  }
2163
- function numberField(input, key) {
2164
- const value = input[key];
2165
- 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
+ }
2166
2011
  }
2167
- function parseCursorFinal(msg) {
2168
- if (msg.type !== "result") return null;
2169
- const isError = msg.is_error === true || msg.subtype !== "success";
2170
- return {
2171
- success: !isError,
2172
- turns: 1,
2173
- result: typeof msg.result === "string" ? msg.result : "",
2174
- sessionId: typeof msg.session_id === "string" ? msg.session_id : void 0,
2175
- usage: parseUsage(msg.usage),
2176
- error: isError ? typeof msg.result === "string" ? msg.result : `cursor result: ${String(msg.subtype ?? "error")}` : void 0
2177
- };
2012
+
2013
+ // src/agent/sdk.ts
2014
+ async function runAgent(opts) {
2015
+ const provider = opts.provider ?? "claude";
2016
+ return await getAgentProvider(provider).run(opts);
2178
2017
  }
2179
2018
 
2180
2019
  // src/commands/init.ts
@@ -2320,42 +2159,47 @@ and optional \`files:\`. The rest is prose.
2320
2159
  // src/commands/bootstrap.ts
2321
2160
  var BOOTSTRAP_TOOLS = ["Read", "Write", "Edit", "Glob", "Grep", "Bash"];
2322
2161
  async function runBootstrap(options) {
2162
+ const repoRoot = findNearestAlmanacDir(options.cwd) ?? options.cwd;
2323
2163
  const providerResolution = await resolveAgentSelection({
2324
2164
  agent: options.agent,
2325
- model: options.model
2165
+ model: options.model,
2166
+ cwd: repoRoot
2326
2167
  });
2327
2168
  if (!providerResolution.ok) {
2328
- return {
2329
- stdout: "",
2330
- stderr: `almanac: ${providerResolution.error}
2331
- `,
2332
- exitCode: 1
2333
- };
2169
+ return renderOutcome(
2170
+ { type: "error", message: providerResolution.error },
2171
+ { json: options.json }
2172
+ );
2334
2173
  }
2335
2174
  const { provider, model } = providerResolution;
2336
2175
  try {
2337
2176
  await assertAgentAuth({ provider, spawnCli: options.spawnCli });
2338
2177
  } catch (err) {
2339
2178
  const msg = err instanceof Error ? err.message : String(err);
2340
- return {
2341
- stdout: "",
2342
- stderr: `almanac: ${msg}
2343
- `,
2344
- exitCode: 1
2345
- };
2179
+ return renderOutcome(
2180
+ {
2181
+ type: "needs-action",
2182
+ message: msg,
2183
+ fix: authFixFor(provider),
2184
+ data: { provider }
2185
+ },
2186
+ { json: options.json }
2187
+ );
2346
2188
  }
2347
- const repoRoot = findNearestAlmanacDir(options.cwd) ?? options.cwd;
2348
2189
  const almanacDir = getRepoAlmanacDir(repoRoot);
2349
2190
  const pagesDir = join6(almanacDir, "pages");
2350
2191
  if (options.force !== true && existsSync5(pagesDir)) {
2351
2192
  const existing = await countMarkdownPages(pagesDir);
2352
2193
  if (existing > 0) {
2353
- return {
2354
- stdout: "",
2355
- stderr: `almanac: .almanac/ already initialized with ${existing} page${existing === 1 ? "" : "s"}. Use 'almanac capture' instead, or --force to overwrite.
2356
- `,
2357
- exitCode: 1
2358
- };
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
+ );
2359
2203
  }
2360
2204
  }
2361
2205
  if (!existsSync5(almanacDir)) {
@@ -2363,12 +2207,10 @@ async function runBootstrap(options) {
2363
2207
  await initWiki({ cwd: repoRoot });
2364
2208
  } catch (err) {
2365
2209
  const msg = err instanceof Error ? err.message : String(err);
2366
- return {
2367
- stdout: "",
2368
- stderr: `almanac: init failed during bootstrap: ${msg}
2369
- `,
2370
- exitCode: 1
2371
- };
2210
+ return renderOutcome(
2211
+ { type: "error", message: `init failed during bootstrap: ${msg}` },
2212
+ { json: options.json }
2213
+ );
2372
2214
  }
2373
2215
  }
2374
2216
  const systemPrompt = await loadPrompt("bootstrap");
@@ -2381,7 +2223,7 @@ async function runBootstrap(options) {
2381
2223
  const out = process.stdout;
2382
2224
  const formatter = new StreamingFormatter({
2383
2225
  write: (line) => {
2384
- if (options.quiet !== true) out.write(line);
2226
+ if (options.quiet !== true && options.json !== true) out.write(line);
2385
2227
  }
2386
2228
  });
2387
2229
  const onMessage = (msg) => {
@@ -2410,33 +2252,47 @@ async function runBootstrap(options) {
2410
2252
  }
2411
2253
  const finalLine = formatFinalLine(result, logPath, repoRoot);
2412
2254
  if (result.success) {
2413
- return {
2414
- stdout: `${finalLine}
2415
- `,
2416
- stderr: "",
2417
- exitCode: 0
2418
- };
2255
+ return renderOutcome(
2256
+ {
2257
+ type: "success",
2258
+ message: finalLine,
2259
+ data: runData(result, logPath, repoRoot)
2260
+ },
2261
+ { json: options.json }
2262
+ );
2419
2263
  }
2420
- return {
2421
- stdout: options.quiet === true ? "" : `${finalLine}
2422
- `,
2423
- stderr: `almanac: bootstrap failed: ${result.error ?? "unknown error"}
2424
- `,
2425
- exitCode: 1
2426
- };
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
+ );
2427
2276
  }
2428
- async function resolveAgentSelection(args) {
2429
- const config = await readConfig();
2430
- const rawProvider = args.agent ?? config.agent.default;
2431
- if (!isAgentProviderId(rawProvider)) {
2432
- return {
2433
- ok: false,
2434
- error: `unknown agent '${rawProvider}'. Expected one of: claude, codex, cursor.`
2435
- };
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";
2436
2287
  }
2437
- const configuredModel = config.agent.models[rawProvider] ?? void 0;
2438
- const model = args.model !== void 0 ? args.model : configuredModel === null ? void 0 : configuredModel;
2439
- 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
+ };
2440
2296
  }
2441
2297
  function formatFinalLine(result, logPath, repoRoot) {
2442
2298
  const status = result.success ? "done" : "failed";
@@ -2900,37 +2756,46 @@ var WRITER_TOOLS = ["Read", "Write", "Edit", "Glob", "Grep", "Bash", "Agent"];
2900
2756
  var REVIEWER_TOOLS = ["Read", "Grep", "Glob", "Bash"];
2901
2757
  var REVIEWER_DESCRIPTION = "Reviews proposed wiki changes against the full knowledge base for cohesion, duplication, missing links, notability, and writing conventions.";
2902
2758
  async function runCapture(options) {
2903
- 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({
2904
2771
  agent: options.agent,
2905
- model: options.model
2772
+ model: options.model,
2773
+ cwd: repoRoot
2906
2774
  });
2907
2775
  if (!providerResolution.ok) {
2908
- return {
2909
- stdout: "",
2910
- stderr: `almanac: ${providerResolution.error}
2911
- `,
2912
- exitCode: 1
2913
- };
2776
+ return renderOutcome(
2777
+ { type: "error", message: providerResolution.error },
2778
+ { json: options.json }
2779
+ );
2914
2780
  }
2915
2781
  const { provider, model } = providerResolution;
2782
+ const statusModel = model ?? getProviderDefaultModel(provider) ?? "provider default";
2916
2783
  try {
2917
- await assertAgentAuth({ provider, spawnCli: options.spawnCli });
2784
+ await (options.assertAgentAuthFn ?? assertAgentAuth)({
2785
+ provider,
2786
+ spawnCli: options.spawnCli
2787
+ });
2918
2788
  } catch (err) {
2919
2789
  const msg = err instanceof Error ? err.message : String(err);
2920
- return {
2921
- stdout: "",
2922
- stderr: `almanac: ${msg}
2923
- `,
2924
- exitCode: 1
2925
- };
2926
- }
2927
- const repoRoot = findNearestAlmanacDir(options.cwd);
2928
- if (repoRoot === null) {
2929
- return {
2930
- stdout: "",
2931
- stderr: "almanac: no .almanac/ found in this directory or any parent. Run 'almanac bootstrap' first.\n",
2932
- exitCode: 1
2933
- };
2790
+ return renderOutcome(
2791
+ {
2792
+ type: "needs-action",
2793
+ message: msg,
2794
+ fix: authFixFor2(provider),
2795
+ data: { provider }
2796
+ },
2797
+ { json: options.json }
2798
+ );
2934
2799
  }
2935
2800
  const almanacDir = getRepoAlmanacDir(repoRoot);
2936
2801
  const pagesDir = join8(almanacDir, "pages");
@@ -2941,12 +2806,14 @@ async function runCapture(options) {
2941
2806
  claudeProjectsDir: options.claudeProjectsDir
2942
2807
  });
2943
2808
  if (!transcriptResolution.ok) {
2944
- return {
2945
- stdout: "",
2946
- stderr: `almanac: ${transcriptResolution.error}
2947
- `,
2948
- exitCode: 1
2949
- };
2809
+ return renderOutcome(
2810
+ {
2811
+ type: "needs-action",
2812
+ message: transcriptResolution.error,
2813
+ fix: transcriptFix(transcriptResolution.error)
2814
+ },
2815
+ { json: options.json }
2816
+ );
2950
2817
  }
2951
2818
  const transcriptPath = transcriptResolution.path;
2952
2819
  const snapshotBefore = await snapshotPages(pagesDir);
@@ -2972,7 +2839,7 @@ async function runCapture(options) {
2972
2839
  stem: logStem,
2973
2840
  sessionId: options.sessionId,
2974
2841
  transcriptPath,
2975
- model: options.model,
2842
+ model: statusModel,
2976
2843
  startedAt
2977
2844
  });
2978
2845
  await writeCaptureRunRecord(statePath, stateRecord).catch(() => {
@@ -2981,7 +2848,7 @@ async function runCapture(options) {
2981
2848
  const out = process.stdout;
2982
2849
  const formatter = new StreamingFormatter({
2983
2850
  write: (line) => {
2984
- if (options.quiet !== true) out.write(line);
2851
+ if (options.quiet !== true && options.json !== true) out.write(line);
2985
2852
  }
2986
2853
  });
2987
2854
  formatter.setAgent("writer");
@@ -3036,13 +2903,18 @@ Working directory: ${repoRoot}.`;
3036
2903
  })
3037
2904
  ).catch(() => {
3038
2905
  });
3039
- return {
3040
- stdout: "",
3041
- stderr: `almanac: capture failed: ${result.error ?? "unknown error"}
3042
- (transcript: ${relative3(repoRoot, logPath)})
3043
- `,
3044
- exitCode: 1
3045
- };
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
+ );
3046
2918
  }
3047
2919
  await writeCaptureRunRecord(
3048
2920
  statePath,
@@ -3055,25 +2927,46 @@ Working directory: ${repoRoot}.`;
3055
2927
  ).catch(() => {
3056
2928
  });
3057
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) {
3058
2958
  return {
3059
- stdout: `${summary}
3060
- `,
3061
- stderr: "",
3062
- 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
3063
2966
  };
3064
2967
  }
3065
- async function resolveAgentSelection2(args) {
3066
- const config = await readConfig();
3067
- const rawProvider = args.agent ?? config.agent.default;
3068
- if (!isAgentProviderId(rawProvider)) {
3069
- return {
3070
- ok: false,
3071
- error: `unknown agent '${rawProvider}'. Expected one of: claude, codex, cursor.`
3072
- };
3073
- }
3074
- const configuredModel = config.agent.models[rawProvider] ?? void 0;
3075
- const model = args.model !== void 0 ? args.model : configuredModel === null ? void 0 : configuredModel;
3076
- return { ok: true, provider: rawProvider, model };
2968
+ function isNoopDelta(delta) {
2969
+ return delta.created === 0 && delta.updated === 0 && delta.archived === 0;
3077
2970
  }
3078
2971
  async function resolveTranscript(args) {
3079
2972
  if (args.explicit !== void 0 && args.explicit.length > 0) {
@@ -3253,19 +3146,20 @@ async function runReindex(options) {
3253
3146
  function registerWikiLifecycleCommands(program) {
3254
3147
  program.command("bootstrap").description(
3255
3148
  "scaffold a wiki in this repo via an AI agent (requires ANTHROPIC_API_KEY or Claude subscription)"
3256
- ).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(
3257
3150
  async (opts) => {
3258
3151
  const result = await runBootstrap({
3259
3152
  cwd: process.cwd(),
3260
3153
  quiet: opts.quiet,
3261
3154
  agent: opts.agent,
3262
3155
  model: opts.model,
3263
- force: opts.force
3156
+ force: opts.force,
3157
+ json: opts.json
3264
3158
  });
3265
3159
  emit(result);
3266
3160
  }
3267
3161
  );
3268
- 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(
3269
3163
  async (transcript, opts) => {
3270
3164
  await autoRegisterIfNeeded(process.cwd());
3271
3165
  const result = await runCapture({
@@ -3274,7 +3168,8 @@ function registerWikiLifecycleCommands(program) {
3274
3168
  sessionId: opts.session,
3275
3169
  quiet: opts.quiet,
3276
3170
  agent: opts.agent,
3277
- model: opts.model
3171
+ model: opts.model,
3172
+ json: opts.json
3278
3173
  });
3279
3174
  emit(result);
3280
3175
  }
@@ -3287,13 +3182,16 @@ function registerWikiLifecycleCommands(program) {
3287
3182
  });
3288
3183
  emit(result);
3289
3184
  });
3290
- 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) => {
3291
3186
  await autoRegisterIfNeeded(process.cwd());
3292
3187
  const result = await runCaptureStatus({
3293
3188
  cwd: process.cwd(),
3294
3189
  json: opts.json
3295
3190
  });
3296
- emit(result);
3191
+ emit(withWarning(
3192
+ result,
3193
+ deprecationWarning("almanac ps", "almanac capture status")
3194
+ ));
3297
3195
  });
3298
3196
  const hook = program.command("hook").description("manage the SessionEnd auto-capture hook");
3299
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) => {
@@ -3337,4 +3235,4 @@ function registerCommands(program) {
3337
3235
  export {
3338
3236
  registerCommands
3339
3237
  };
3340
- //# sourceMappingURL=register-commands-IXYE5CNZ.js.map
3238
+ //# sourceMappingURL=register-commands-XTK2G2FB.js.map