mulch-cli 0.2.4 → 0.3.0

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 (58) hide show
  1. package/README.md +36 -4
  2. package/dist/cli.js +3 -1
  3. package/dist/cli.js.map +1 -1
  4. package/dist/commands/compact.d.ts +2 -0
  5. package/dist/commands/compact.d.ts.map +1 -1
  6. package/dist/commands/compact.js +292 -4
  7. package/dist/commands/compact.js.map +1 -1
  8. package/dist/commands/diff.d.ts +11 -0
  9. package/dist/commands/diff.d.ts.map +1 -0
  10. package/dist/commands/diff.js +170 -0
  11. package/dist/commands/diff.js.map +1 -0
  12. package/dist/commands/prime.d.ts.map +1 -1
  13. package/dist/commands/prime.js +93 -16
  14. package/dist/commands/prime.js.map +1 -1
  15. package/dist/commands/record.d.ts +10 -0
  16. package/dist/commands/record.d.ts.map +1 -1
  17. package/dist/commands/record.js +155 -3
  18. package/dist/commands/record.js.map +1 -1
  19. package/dist/commands/status.d.ts.map +1 -1
  20. package/dist/commands/status.js +10 -2
  21. package/dist/commands/status.js.map +1 -1
  22. package/dist/commands/sync.js +5 -5
  23. package/dist/commands/sync.js.map +1 -1
  24. package/dist/index.d.ts +3 -0
  25. package/dist/index.d.ts.map +1 -1
  26. package/dist/index.js +6 -0
  27. package/dist/index.js.map +1 -1
  28. package/dist/schemas/index.d.ts +1 -0
  29. package/dist/schemas/index.d.ts.map +1 -1
  30. package/dist/schemas/index.js +1 -0
  31. package/dist/schemas/index.js.map +1 -1
  32. package/dist/schemas/record-schema.d.ts +3 -0
  33. package/dist/schemas/record-schema.d.ts.map +1 -1
  34. package/dist/schemas/record-schema.js +1 -0
  35. package/dist/schemas/record-schema.js.map +1 -1
  36. package/dist/schemas/record.d.ts +1 -0
  37. package/dist/schemas/record.d.ts.map +1 -1
  38. package/dist/utils/budget.d.ts +33 -0
  39. package/dist/utils/budget.d.ts.map +1 -0
  40. package/dist/utils/budget.js +110 -0
  41. package/dist/utils/budget.js.map +1 -0
  42. package/dist/utils/config.d.ts +1 -0
  43. package/dist/utils/config.d.ts.map +1 -1
  44. package/dist/utils/config.js +6 -0
  45. package/dist/utils/config.js.map +1 -1
  46. package/dist/utils/expertise.d.ts +23 -1
  47. package/dist/utils/expertise.d.ts.map +1 -1
  48. package/dist/utils/expertise.js +74 -2
  49. package/dist/utils/expertise.js.map +1 -1
  50. package/dist/utils/format.d.ts +1 -0
  51. package/dist/utils/format.d.ts.map +1 -1
  52. package/dist/utils/format.js +47 -0
  53. package/dist/utils/format.js.map +1 -1
  54. package/dist/utils/git.js +5 -5
  55. package/dist/utils/git.js.map +1 -1
  56. package/dist/utils/lock.js +2 -2
  57. package/dist/utils/lock.js.map +1 -1
  58. package/package.json +1 -1
@@ -0,0 +1,170 @@
1
+ import { execFileSync } from "node:child_process";
2
+ import chalk from "chalk";
3
+ import { isGitRepo } from "../utils/git.js";
4
+ import { getRecordSummary } from "../utils/format.js";
5
+ import { outputJson, outputJsonError } from "../utils/json-output.js";
6
+ export function parseExpertiseDiff(diffOutput) {
7
+ const lines = diffOutput.split("\n");
8
+ const entriesMap = new Map();
9
+ let currentDomain = null;
10
+ for (const line of lines) {
11
+ // Extract domain from 'diff --git' headers
12
+ if (line.startsWith("diff --git")) {
13
+ const match = line.match(/\.mulch\/expertise\/([^/]+)\.jsonl/);
14
+ if (match) {
15
+ currentDomain = match[1];
16
+ if (!entriesMap.has(currentDomain)) {
17
+ entriesMap.set(currentDomain, {
18
+ domain: currentDomain,
19
+ added: [],
20
+ removed: [],
21
+ });
22
+ }
23
+ }
24
+ continue;
25
+ }
26
+ // Skip file metadata lines
27
+ if (line.startsWith("+++") || line.startsWith("---") || line.startsWith("@@")) {
28
+ continue;
29
+ }
30
+ // Parse added records (lines starting with '+')
31
+ if (line.startsWith("+") && currentDomain) {
32
+ const jsonStr = line.slice(1);
33
+ try {
34
+ const record = JSON.parse(jsonStr);
35
+ const entry = entriesMap.get(currentDomain);
36
+ if (entry) {
37
+ entry.added.push(record);
38
+ }
39
+ }
40
+ catch {
41
+ // Skip lines that fail JSON.parse (context lines, hunk headers, etc.)
42
+ }
43
+ continue;
44
+ }
45
+ // Parse removed records (lines starting with '-')
46
+ if (line.startsWith("-") && currentDomain) {
47
+ const jsonStr = line.slice(1);
48
+ try {
49
+ const record = JSON.parse(jsonStr);
50
+ const entry = entriesMap.get(currentDomain);
51
+ if (entry) {
52
+ entry.removed.push(record);
53
+ }
54
+ }
55
+ catch {
56
+ // Skip lines that fail JSON.parse (context lines, hunk headers, etc.)
57
+ }
58
+ continue;
59
+ }
60
+ }
61
+ // Return entries sorted by domain, filtered to non-empty
62
+ return [...entriesMap.values()]
63
+ .filter((entry) => entry.added.length > 0 || entry.removed.length > 0)
64
+ .sort((a, b) => a.domain.localeCompare(b.domain));
65
+ }
66
+ export function formatDiffOutput(entries, since) {
67
+ const lines = [];
68
+ lines.push(`Expertise changes since ${since}`);
69
+ lines.push("");
70
+ for (const entry of entries) {
71
+ const changeCount = entry.added.length + entry.removed.length;
72
+ const plural = changeCount === 1 ? "change" : "changes";
73
+ lines.push(`${entry.domain} (${changeCount} ${plural}):`);
74
+ for (const record of entry.added) {
75
+ const summary = getRecordSummary(record);
76
+ lines.push(` + [${record.type}] ${record.id} ${summary}`);
77
+ }
78
+ for (const record of entry.removed) {
79
+ const summary = getRecordSummary(record);
80
+ lines.push(` - [${record.type}] ${record.id} ${summary}`);
81
+ }
82
+ lines.push("");
83
+ }
84
+ return lines.join("\n").trimEnd();
85
+ }
86
+ export function registerDiffCommand(program) {
87
+ program
88
+ .command("diff")
89
+ .description("Show expertise record changes since a git ref")
90
+ .option("--since <ref>", "git ref to diff against", "HEAD~1")
91
+ .action(async (options) => {
92
+ const jsonMode = program.opts().json === true;
93
+ const cwd = process.cwd();
94
+ if (!isGitRepo(cwd)) {
95
+ if (jsonMode) {
96
+ outputJsonError("diff", "Not in a git repository. Run this command from within a git repository.");
97
+ }
98
+ else {
99
+ console.error(chalk.red("Error: not in a git repository."));
100
+ }
101
+ process.exitCode = 1;
102
+ return;
103
+ }
104
+ try {
105
+ let diffOutput;
106
+ try {
107
+ diffOutput = execFileSync("git", ["diff", options.since, "--", ".mulch/expertise/"], {
108
+ cwd,
109
+ encoding: "utf-8",
110
+ stdio: ["pipe", "pipe", "pipe"],
111
+ });
112
+ }
113
+ catch {
114
+ // ref doesn't exist or no changes, treat as empty diff
115
+ diffOutput = "";
116
+ }
117
+ const entries = parseExpertiseDiff(diffOutput);
118
+ if (entries.length === 0) {
119
+ if (jsonMode) {
120
+ outputJson({
121
+ success: true,
122
+ command: "diff",
123
+ since: options.since,
124
+ domains: [],
125
+ message: "No expertise changes found.",
126
+ });
127
+ }
128
+ else {
129
+ console.log("No expertise changes found.");
130
+ }
131
+ return;
132
+ }
133
+ if (jsonMode) {
134
+ outputJson({
135
+ success: true,
136
+ command: "diff",
137
+ since: options.since,
138
+ domains: entries,
139
+ });
140
+ return;
141
+ }
142
+ // Plain text output with colors
143
+ console.log(chalk.bold(`\nExpertise changes since ${options.since}\n`));
144
+ for (const entry of entries) {
145
+ const changeCount = entry.added.length + entry.removed.length;
146
+ const plural = changeCount === 1 ? "change" : "changes";
147
+ console.log(chalk.cyan(`${entry.domain} (${changeCount} ${plural}):`));
148
+ for (const record of entry.added) {
149
+ const summary = getRecordSummary(record);
150
+ console.log(chalk.green(` + [${record.type}] ${record.id} ${summary}`));
151
+ }
152
+ for (const record of entry.removed) {
153
+ const summary = getRecordSummary(record);
154
+ console.log(chalk.red(` - [${record.type}] ${record.id} ${summary}`));
155
+ }
156
+ console.log("");
157
+ }
158
+ }
159
+ catch (err) {
160
+ if (jsonMode) {
161
+ outputJsonError("diff", err.message);
162
+ }
163
+ else {
164
+ console.error(chalk.red(`Error: ${err.message}`));
165
+ }
166
+ process.exitCode = 1;
167
+ }
168
+ });
169
+ }
170
+ //# sourceMappingURL=diff.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"diff.js","sourceRoot":"","sources":["../../src/commands/diff.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAQtE,MAAM,UAAU,kBAAkB,CAAC,UAAkB;IACnD,MAAM,KAAK,GAAG,UAAU,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IACrC,MAAM,UAAU,GAAG,IAAI,GAAG,EAAqB,CAAC;IAChD,IAAI,aAAa,GAAkB,IAAI,CAAC;IAExC,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,2CAA2C;QAC3C,IAAI,IAAI,CAAC,UAAU,CAAC,YAAY,CAAC,EAAE,CAAC;YAClC,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,oCAAoC,CAAC,CAAC;YAC/D,IAAI,KAAK,EAAE,CAAC;gBACV,aAAa,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;gBACzB,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,aAAa,CAAC,EAAE,CAAC;oBACnC,UAAU,CAAC,GAAG,CAAC,aAAa,EAAE;wBAC5B,MAAM,EAAE,aAAa;wBACrB,KAAK,EAAE,EAAE;wBACT,OAAO,EAAE,EAAE;qBACZ,CAAC,CAAC;gBACL,CAAC;YACH,CAAC;YACD,SAAS;QACX,CAAC;QAED,2BAA2B;QAC3B,IAAI,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,IAAI,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,IAAI,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;YAC9E,SAAS;QACX,CAAC;QAED,gDAAgD;QAChD,IAAI,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,aAAa,EAAE,CAAC;YAC1C,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;YAC9B,IAAI,CAAC;gBACH,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAoB,CAAC;gBACtD,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC;gBAC5C,IAAI,KAAK,EAAE,CAAC;oBACV,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;gBAC3B,CAAC;YACH,CAAC;YAAC,MAAM,CAAC;gBACP,sEAAsE;YACxE,CAAC;YACD,SAAS;QACX,CAAC;QAED,kDAAkD;QAClD,IAAI,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,aAAa,EAAE,CAAC;YAC1C,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;YAC9B,IAAI,CAAC;gBACH,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAoB,CAAC;gBACtD,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC;gBAC5C,IAAI,KAAK,EAAE,CAAC;oBACV,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;gBAC7B,CAAC;YACH,CAAC;YAAC,MAAM,CAAC;gBACP,sEAAsE;YACxE,CAAC;YACD,SAAS;QACX,CAAC;IACH,CAAC;IAED,yDAAyD;IACzD,OAAO,CAAC,GAAG,UAAU,CAAC,MAAM,EAAE,CAAC;SAC5B,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC;SACrE,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC;AACtD,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,OAAoB,EAAE,KAAa;IAClE,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,KAAK,CAAC,IAAI,CAAC,2BAA2B,KAAK,EAAE,CAAC,CAAC;IAC/C,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAEf,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;QAC5B,MAAM,WAAW,GAAG,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC;QAC9D,MAAM,MAAM,GAAG,WAAW,KAAK,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC;QACxD,KAAK,CAAC,IAAI,CAAC,GAAG,KAAK,CAAC,MAAM,KAAK,WAAW,IAAI,MAAM,IAAI,CAAC,CAAC;QAE1D,KAAK,MAAM,MAAM,IAAI,KAAK,CAAC,KAAK,EAAE,CAAC;YACjC,MAAM,OAAO,GAAG,gBAAgB,CAAC,MAAM,CAAC,CAAC;YACzC,KAAK,CAAC,IAAI,CAAC,QAAQ,MAAM,CAAC,IAAI,KAAK,MAAM,CAAC,EAAE,IAAI,OAAO,EAAE,CAAC,CAAC;QAC7D,CAAC;QAED,KAAK,MAAM,MAAM,IAAI,KAAK,CAAC,OAAO,EAAE,CAAC;YACnC,MAAM,OAAO,GAAG,gBAAgB,CAAC,MAAM,CAAC,CAAC;YACzC,KAAK,CAAC,IAAI,CAAC,QAAQ,MAAM,CAAC,IAAI,KAAK,MAAM,CAAC,EAAE,IAAI,OAAO,EAAE,CAAC,CAAC;QAC7D,CAAC;QAED,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACjB,CAAC;IAED,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,CAAC;AACpC,CAAC;AAED,MAAM,UAAU,mBAAmB,CAAC,OAAgB;IAClD,OAAO;SACJ,OAAO,CAAC,MAAM,CAAC;SACf,WAAW,CAAC,+CAA+C,CAAC;SAC5D,MAAM,CAAC,eAAe,EAAE,yBAAyB,EAAE,QAAQ,CAAC;SAC5D,MAAM,CAAC,KAAK,EAAE,OAA0B,EAAE,EAAE;QAC3C,MAAM,QAAQ,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC,IAAI,KAAK,IAAI,CAAC;QAC9C,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC;QAE1B,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,EAAE,CAAC;YACpB,IAAI,QAAQ,EAAE,CAAC;gBACb,eAAe,CACb,MAAM,EACN,yEAAyE,CAC1E,CAAC;YACJ,CAAC;iBAAM,CAAC;gBACN,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,iCAAiC,CAAC,CAAC,CAAC;YAC9D,CAAC;YACD,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;YACrB,OAAO;QACT,CAAC;QAED,IAAI,CAAC;YACH,IAAI,UAAkB,CAAC;YACvB,IAAI,CAAC;gBACH,UAAU,GAAG,YAAY,CACvB,KAAK,EACL,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,EAAE,IAAI,EAAE,mBAAmB,CAAC,EAClD;oBACE,GAAG;oBACH,QAAQ,EAAE,OAAO;oBACjB,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC;iBAChC,CACF,CAAC;YACJ,CAAC;YAAC,MAAM,CAAC;gBACP,uDAAuD;gBACvD,UAAU,GAAG,EAAE,CAAC;YAClB,CAAC;YAED,MAAM,OAAO,GAAG,kBAAkB,CAAC,UAAU,CAAC,CAAC;YAE/C,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBACzB,IAAI,QAAQ,EAAE,CAAC;oBACb,UAAU,CAAC;wBACT,OAAO,EAAE,IAAI;wBACb,OAAO,EAAE,MAAM;wBACf,KAAK,EAAE,OAAO,CAAC,KAAK;wBACpB,OAAO,EAAE,EAAE;wBACX,OAAO,EAAE,6BAA6B;qBACvC,CAAC,CAAC;gBACL,CAAC;qBAAM,CAAC;oBACN,OAAO,CAAC,GAAG,CAAC,6BAA6B,CAAC,CAAC;gBAC7C,CAAC;gBACD,OAAO;YACT,CAAC;YAED,IAAI,QAAQ,EAAE,CAAC;gBACb,UAAU,CAAC;oBACT,OAAO,EAAE,IAAI;oBACb,OAAO,EAAE,MAAM;oBACf,KAAK,EAAE,OAAO,CAAC,KAAK;oBACpB,OAAO,EAAE,OAAO;iBACjB,CAAC,CAAC;gBACH,OAAO;YACT,CAAC;YAED,gCAAgC;YAChC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,6BAA6B,OAAO,CAAC,KAAK,IAAI,CAAC,CAAC,CAAC;YAExE,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;gBAC5B,MAAM,WAAW,GAAG,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC;gBAC9D,MAAM,MAAM,GAAG,WAAW,KAAK,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC;gBACxD,OAAO,CAAC,GAAG,CACT,KAAK,CAAC,IAAI,CAAC,GAAG,KAAK,CAAC,MAAM,KAAK,WAAW,IAAI,MAAM,IAAI,CAAC,CAC1D,CAAC;gBAEF,KAAK,MAAM,MAAM,IAAI,KAAK,CAAC,KAAK,EAAE,CAAC;oBACjC,MAAM,OAAO,GAAG,gBAAgB,CAAC,MAAM,CAAC,CAAC;oBACzC,OAAO,CAAC,GAAG,CACT,KAAK,CAAC,KAAK,CAAC,QAAQ,MAAM,CAAC,IAAI,KAAK,MAAM,CAAC,EAAE,IAAI,OAAO,EAAE,CAAC,CAC5D,CAAC;gBACJ,CAAC;gBAED,KAAK,MAAM,MAAM,IAAI,KAAK,CAAC,OAAO,EAAE,CAAC;oBACnC,MAAM,OAAO,GAAG,gBAAgB,CAAC,MAAM,CAAC,CAAC;oBACzC,OAAO,CAAC,GAAG,CACT,KAAK,CAAC,GAAG,CAAC,QAAQ,MAAM,CAAC,IAAI,KAAK,MAAM,CAAC,EAAE,IAAI,OAAO,EAAE,CAAC,CAC1D,CAAC;gBACJ,CAAC;gBAED,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YAClB,CAAC;QACH,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,IAAI,QAAQ,EAAE,CAAC;gBACb,eAAe,CAAC,MAAM,EAAG,GAAa,CAAC,OAAO,CAAC,CAAC;YAClD,CAAC;iBAAM,CAAC;gBACN,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,UAAW,GAAa,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;YAC/D,CAAC;YACD,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;QACvB,CAAC;IACH,CAAC,CAAC,CAAC;AACP,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"prime.d.ts","sourceRoot":"","sources":["../../src/commands/prime.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAU,MAAM,WAAW,CAAC;AA6B5C,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAoK3D"}
1
+ {"version":3,"file":"prime.d.ts","sourceRoot":"","sources":["../../src/commands/prime.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAU,MAAM,WAAW,CAAC;AAiE5C,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAoO3D"}
@@ -3,9 +3,34 @@ import { Option } from "commander";
3
3
  import chalk from "chalk";
4
4
  import { readConfig, getExpertisePath } from "../utils/config.js";
5
5
  import { readExpertiseFile, getFileModTime } from "../utils/expertise.js";
6
- import { formatDomainExpertise, formatPrimeOutput, formatDomainExpertiseXml, formatPrimeOutputXml, formatDomainExpertisePlain, formatPrimeOutputPlain, formatDomainExpertiseCompact, formatPrimeOutputCompact, formatMcpOutput, } from "../utils/format.js";
6
+ import { formatDomainExpertise, formatPrimeOutput, formatDomainExpertiseXml, formatPrimeOutputXml, formatDomainExpertisePlain, formatPrimeOutputPlain, formatDomainExpertiseCompact, formatPrimeOutputCompact, formatMcpOutput, getSessionEndReminder, } from "../utils/format.js";
7
7
  import { outputJsonError } from "../utils/json-output.js";
8
8
  import { isGitRepo, getChangedFiles, filterByContext } from "../utils/git.js";
9
+ import { DEFAULT_BUDGET, applyBudget, formatBudgetSummary, } from "../utils/budget.js";
10
+ /**
11
+ * Produce a rough text representation of a record for token estimation.
12
+ * Uses a simple format similar to compact lines.
13
+ */
14
+ function estimateRecordText(record) {
15
+ switch (record.type) {
16
+ case "convention":
17
+ return `[convention] ${record.content}`;
18
+ case "pattern": {
19
+ const files = record.files && record.files.length > 0 ? ` (${record.files.join(", ")})` : "";
20
+ return `[pattern] ${record.name}: ${record.description}${files}`;
21
+ }
22
+ case "failure":
23
+ return `[failure] ${record.description} -> ${record.resolution}`;
24
+ case "decision":
25
+ return `[decision] ${record.title}: ${record.rationale}`;
26
+ case "reference": {
27
+ const refFiles = record.files && record.files.length > 0 ? `: ${record.files.join(", ")}` : `: ${record.description}`;
28
+ return `[reference] ${record.name}${refFiles}`;
29
+ }
30
+ case "guide":
31
+ return `[guide] ${record.name}: ${record.description}`;
32
+ }
33
+ }
9
34
  export function registerPrimeCommand(program) {
10
35
  program
11
36
  .command("prime")
@@ -15,11 +40,15 @@ export function registerPrimeCommand(program) {
15
40
  .option("-v, --verbose", "full output with section headers and recording instructions")
16
41
  .option("--mcp", "output in MCP-compatible JSON format")
17
42
  .option("--domain <domains...>", "domain(s) to include")
43
+ .option("--exclude-domain <domains...>", "domain(s) to exclude")
18
44
  .addOption(new Option("--format <format>", "output format")
19
45
  .choices(["markdown", "xml", "plain"])
20
46
  .default("markdown"))
21
47
  .option("--context", "filter records to only those relevant to changed files")
48
+ .option("--files <paths...>", "filter records to only those relevant to specified files")
22
49
  .option("--export <path>", "export output to a file")
50
+ .option("--budget <tokens>", `token budget for output (default: ${DEFAULT_BUDGET})`)
51
+ .option("--no-limit", "disable token budget limit")
23
52
  .action(async (domainsArg, options) => {
24
53
  const jsonMode = program.opts().json === true;
25
54
  try {
@@ -39,11 +68,25 @@ export function registerPrimeCommand(program) {
39
68
  return;
40
69
  }
41
70
  }
42
- const targetDomains = unique.length > 0
71
+ const excluded = options.excludeDomain ?? [];
72
+ for (const d of excluded) {
73
+ if (!config.domains.includes(d)) {
74
+ if (jsonMode) {
75
+ outputJsonError("prime", `Excluded domain "${d}" not found in config. Available domains: ${config.domains.join(", ")}`);
76
+ }
77
+ else {
78
+ console.error(`Error: Excluded domain "${d}" not found in config. Available domains: ${config.domains.join(", ")}`);
79
+ }
80
+ process.exitCode = 1;
81
+ return;
82
+ }
83
+ }
84
+ let targetDomains = unique.length > 0
43
85
  ? unique
44
86
  : config.domains;
45
- // Resolve changed files for --context filtering
46
- let changedFiles;
87
+ targetDomains = targetDomains.filter(d => !excluded.includes(d));
88
+ // Resolve changed files for --context or --files filtering
89
+ let filesToFilter;
47
90
  if (options.context) {
48
91
  const cwd = process.cwd();
49
92
  if (!isGitRepo(cwd)) {
@@ -57,8 +100,8 @@ export function registerPrimeCommand(program) {
57
100
  process.exitCode = 1;
58
101
  return;
59
102
  }
60
- changedFiles = getChangedFiles(cwd, "HEAD~1");
61
- if (changedFiles.length === 0) {
103
+ filesToFilter = getChangedFiles(cwd, "HEAD~1");
104
+ if (filesToFilter.length === 0) {
62
105
  if (jsonMode) {
63
106
  outputJsonError("prime", "No changed files found. Nothing to filter by.");
64
107
  }
@@ -68,34 +111,63 @@ export function registerPrimeCommand(program) {
68
111
  return;
69
112
  }
70
113
  }
114
+ else if (options.files && options.files.length > 0) {
115
+ filesToFilter = options.files;
116
+ }
117
+ // Determine budget settings
118
+ const isMachineOutput = options.mcp === true || jsonMode;
119
+ const budgetEnabled = !isMachineOutput && options.noLimit !== true;
120
+ const budget = options.budget ? parseInt(options.budget, 10) : DEFAULT_BUDGET;
71
121
  let output;
72
- if (options.mcp || jsonMode) {
73
- // --json and --mcp produce the same structured output
122
+ if (isMachineOutput) {
123
+ // --json and --mcp produce the same structured output — no budget
74
124
  const domains = [];
75
125
  for (const domain of targetDomains) {
76
126
  const filePath = getExpertisePath(domain);
77
127
  let records = await readExpertiseFile(filePath);
78
- if (changedFiles) {
79
- records = filterByContext(records, changedFiles);
128
+ if (filesToFilter) {
129
+ records = filterByContext(records, filesToFilter);
80
130
  }
81
- if (!changedFiles || records.length > 0) {
131
+ if (!filesToFilter || records.length > 0) {
82
132
  domains.push({ domain, entry_count: records.length, records });
83
133
  }
84
134
  }
85
135
  output = formatMcpOutput(domains);
86
136
  }
87
137
  else {
88
- const domainSections = [];
138
+ // Load all records per domain
139
+ const allDomainRecords = [];
140
+ const modTimes = new Map();
89
141
  for (const domain of targetDomains) {
90
142
  const filePath = getExpertisePath(domain);
91
143
  let records = await readExpertiseFile(filePath);
92
- if (changedFiles) {
93
- records = filterByContext(records, changedFiles);
144
+ if (filesToFilter) {
145
+ records = filterByContext(records, filesToFilter);
94
146
  if (records.length === 0)
95
147
  continue;
96
148
  }
149
+ allDomainRecords.push({ domain, records });
97
150
  const lastUpdated = await getFileModTime(filePath);
98
- if (options.verbose || format !== "markdown") {
151
+ modTimes.set(domain, lastUpdated);
152
+ }
153
+ // Apply budget filtering
154
+ let domainRecordsToFormat;
155
+ let droppedCount = 0;
156
+ let droppedDomainCount = 0;
157
+ if (budgetEnabled) {
158
+ const result = applyBudget(allDomainRecords, budget, (record) => estimateRecordText(record));
159
+ domainRecordsToFormat = result.kept;
160
+ droppedCount = result.droppedCount;
161
+ droppedDomainCount = result.droppedDomainCount;
162
+ }
163
+ else {
164
+ domainRecordsToFormat = allDomainRecords;
165
+ }
166
+ // Format domain sections
167
+ const domainSections = [];
168
+ for (const { domain, records } of domainRecordsToFormat) {
169
+ const lastUpdated = modTimes.get(domain) ?? null;
170
+ if (options.verbose || options.full || format !== "markdown") {
99
171
  switch (format) {
100
172
  case "xml":
101
173
  domainSections.push(formatDomainExpertiseXml(domain, records, lastUpdated));
@@ -114,7 +186,7 @@ export function registerPrimeCommand(program) {
114
186
  domainSections.push(formatDomainExpertiseCompact(domain, records, lastUpdated));
115
187
  }
116
188
  }
117
- if (options.verbose || format !== "markdown") {
189
+ if (options.verbose || options.full || format !== "markdown") {
118
190
  switch (format) {
119
191
  case "xml":
120
192
  output = formatPrimeOutputXml(domainSections);
@@ -130,6 +202,11 @@ export function registerPrimeCommand(program) {
130
202
  else {
131
203
  output = formatPrimeOutputCompact(domainSections);
132
204
  }
205
+ // Append truncation summary before session reminder
206
+ if (droppedCount > 0) {
207
+ output += "\n\n" + formatBudgetSummary(droppedCount, droppedDomainCount);
208
+ }
209
+ output += "\n\n" + getSessionEndReminder(format);
133
210
  }
134
211
  if (options.export) {
135
212
  await writeFile(options.export, output + "\n", "utf-8");
@@ -1 +1 @@
1
- {"version":3,"file":"prime.js","sourceRoot":"","sources":["../../src/commands/prime.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC7C,OAAO,EAAW,MAAM,EAAE,MAAM,WAAW,CAAC;AAC5C,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,UAAU,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAClE,OAAO,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAC1E,OAAO,EACL,qBAAqB,EACrB,iBAAiB,EACjB,wBAAwB,EACxB,oBAAoB,EACpB,0BAA0B,EAC1B,sBAAsB,EACtB,4BAA4B,EAC5B,wBAAwB,EACxB,eAAe,GAChB,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAC1D,OAAO,EAAE,SAAS,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAY9E,MAAM,UAAU,oBAAoB,CAAC,OAAgB;IACnD,OAAO;SACJ,OAAO,CAAC,OAAO,CAAC;SAChB,WAAW,CAAC,kDAAkD,CAAC;SAC/D,QAAQ,CAAC,cAAc,EAAE,uCAAuC,CAAC;SACjE,MAAM,CAAC,QAAQ,EAAE,wDAAwD,CAAC;SAC1E,MAAM,CAAC,eAAe,EAAE,6DAA6D,CAAC;SACtF,MAAM,CAAC,OAAO,EAAE,sCAAsC,CAAC;SACvD,MAAM,CAAC,uBAAuB,EAAE,sBAAsB,CAAC;SACvD,SAAS,CACR,IAAI,MAAM,CAAC,mBAAmB,EAAE,eAAe,CAAC;SAC7C,OAAO,CAAC,CAAC,UAAU,EAAE,KAAK,EAAE,OAAO,CAAC,CAAC;SACrC,OAAO,CAAC,UAAU,CAAC,CACvB;SACA,MAAM,CAAC,WAAW,EAAE,wDAAwD,CAAC;SAC7E,MAAM,CAAC,iBAAiB,EAAE,yBAAyB,CAAC;SACpD,MAAM,CAAC,KAAK,EAAE,UAAoB,EAAE,OAAqB,EAAE,EAAE;QAC5D,MAAM,QAAQ,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC,IAAI,KAAK,IAAI,CAAC;QAC9C,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,UAAU,EAAE,CAAC;YAClC,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,IAAI,UAAU,CAAC;YAE5C,MAAM,SAAS,GAAG,CAAC,GAAG,UAAU,EAAE,GAAG,CAAC,OAAO,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC,CAAC;YAC7D,MAAM,MAAM,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC;YAEvC,KAAK,MAAM,CAAC,IAAI,MAAM,EAAE,CAAC;gBACvB,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC;oBAChC,IAAI,QAAQ,EAAE,CAAC;wBACb,eAAe,CAAC,OAAO,EAAE,WAAW,CAAC,6CAA6C,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;oBACjH,CAAC;yBAAM,CAAC;wBACN,OAAO,CAAC,KAAK,CACX,kBAAkB,CAAC,6CAA6C,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAC5F,CAAC;oBACJ,CAAC;oBACD,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;oBACrB,OAAO;gBACT,CAAC;YACH,CAAC;YAED,MAAM,aAAa,GAAG,MAAM,CAAC,MAAM,GAAG,CAAC;gBACrC,CAAC,CAAC,MAAM;gBACR,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC;YAEnB,gDAAgD;YAChD,IAAI,YAAkC,CAAC;YACvC,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;gBACpB,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC;gBAC1B,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,EAAE,CAAC;oBACpB,MAAM,GAAG,GAAG,kDAAkD,CAAC;oBAC/D,IAAI,QAAQ,EAAE,CAAC;wBACb,eAAe,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC;oBAChC,CAAC;yBAAM,CAAC;wBACN,OAAO,CAAC,KAAK,CAAC,UAAU,GAAG,EAAE,CAAC,CAAC;oBACjC,CAAC;oBACD,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;oBACrB,OAAO;gBACT,CAAC;gBACD,YAAY,GAAG,eAAe,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC;gBAC9C,IAAI,YAAY,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;oBAC9B,IAAI,QAAQ,EAAE,CAAC;wBACb,eAAe,CAAC,OAAO,EAAE,+CAA+C,CAAC,CAAC;oBAC5E,CAAC;yBAAM,CAAC;wBACN,OAAO,CAAC,GAAG,CAAC,+CAA+C,CAAC,CAAC;oBAC/D,CAAC;oBACD,OAAO;gBACT,CAAC;YACH,CAAC;YAED,IAAI,MAAc,CAAC;YAEnB,IAAI,OAAO,CAAC,GAAG,IAAI,QAAQ,EAAE,CAAC;gBAC5B,sDAAsD;gBACtD,MAAM,OAAO,GAAgB,EAAE,CAAC;gBAChC,KAAK,MAAM,MAAM,IAAI,aAAa,EAAE,CAAC;oBACnC,MAAM,QAAQ,GAAG,gBAAgB,CAAC,MAAM,CAAC,CAAC;oBAC1C,IAAI,OAAO,GAAG,MAAM,iBAAiB,CAAC,QAAQ,CAAC,CAAC;oBAChD,IAAI,YAAY,EAAE,CAAC;wBACjB,OAAO,GAAG,eAAe,CAAC,OAAO,EAAE,YAAY,CAAC,CAAC;oBACnD,CAAC;oBACD,IAAI,CAAC,YAAY,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;wBACxC,OAAO,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,WAAW,EAAE,OAAO,CAAC,MAAM,EAAE,OAAO,EAAE,CAAC,CAAC;oBACjE,CAAC;gBACH,CAAC;gBACD,MAAM,GAAG,eAAe,CAAC,OAAO,CAAC,CAAC;YACpC,CAAC;iBAAM,CAAC;gBACN,MAAM,cAAc,GAAa,EAAE,CAAC;gBACpC,KAAK,MAAM,MAAM,IAAI,aAAa,EAAE,CAAC;oBACnC,MAAM,QAAQ,GAAG,gBAAgB,CAAC,MAAM,CAAC,CAAC;oBAC1C,IAAI,OAAO,GAAG,MAAM,iBAAiB,CAAC,QAAQ,CAAC,CAAC;oBAChD,IAAI,YAAY,EAAE,CAAC;wBACjB,OAAO,GAAG,eAAe,CAAC,OAAO,EAAE,YAAY,CAAC,CAAC;wBACjD,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;4BAAE,SAAS;oBACrC,CAAC;oBACD,MAAM,WAAW,GAAG,MAAM,cAAc,CAAC,QAAQ,CAAC,CAAC;oBAEnD,IAAI,OAAO,CAAC,OAAO,IAAI,MAAM,KAAK,UAAU,EAAE,CAAC;wBAC7C,QAAQ,MAAM,EAAE,CAAC;4BACf,KAAK,KAAK;gCACR,cAAc,CAAC,IAAI,CACjB,wBAAwB,CAAC,MAAM,EAAE,OAAO,EAAE,WAAW,CAAC,CACvD,CAAC;gCACF,MAAM;4BACR,KAAK,OAAO;gCACV,cAAc,CAAC,IAAI,CACjB,0BAA0B,CAAC,MAAM,EAAE,OAAO,EAAE,WAAW,CAAC,CACzD,CAAC;gCACF,MAAM;4BACR;gCACE,cAAc,CAAC,IAAI,CACjB,qBAAqB,CAAC,MAAM,EAAE,OAAO,EAAE,WAAW,EAAE;oCAClD,IAAI,EAAE,OAAO,CAAC,IAAI;iCACnB,CAAC,CACH,CAAC;gCACF,MAAM;wBACV,CAAC;oBACH,CAAC;yBAAM,CAAC;wBACN,cAAc,CAAC,IAAI,CACjB,4BAA4B,CAAC,MAAM,EAAE,OAAO,EAAE,WAAW,CAAC,CAC3D,CAAC;oBACJ,CAAC;gBACH,CAAC;gBAED,IAAI,OAAO,CAAC,OAAO,IAAI,MAAM,KAAK,UAAU,EAAE,CAAC;oBAC7C,QAAQ,MAAM,EAAE,CAAC;wBACf,KAAK,KAAK;4BACR,MAAM,GAAG,oBAAoB,CAAC,cAAc,CAAC,CAAC;4BAC9C,MAAM;wBACR,KAAK,OAAO;4BACV,MAAM,GAAG,sBAAsB,CAAC,cAAc,CAAC,CAAC;4BAChD,MAAM;wBACR;4BACE,MAAM,GAAG,iBAAiB,CAAC,cAAc,CAAC,CAAC;4BAC3C,MAAM;oBACV,CAAC;gBACH,CAAC;qBAAM,CAAC;oBACN,MAAM,GAAG,wBAAwB,CAAC,cAAc,CAAC,CAAC;gBACpD,CAAC;YACH,CAAC;YAED,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;gBACnB,MAAM,SAAS,CAAC,OAAO,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,EAAE,OAAO,CAAC,CAAC;gBACxD,IAAI,CAAC,QAAQ,EAAE,CAAC;oBACd,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,eAAe,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;gBAC5D,CAAC;YACH,CAAC;iBAAM,CAAC;gBACN,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;YACtB,CAAC;QACH,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,IAAK,GAA6B,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;gBACrD,IAAI,QAAQ,EAAE,CAAC;oBACb,eAAe,CAAC,OAAO,EAAE,qDAAqD,CAAC,CAAC;gBAClF,CAAC;qBAAM,CAAC;oBACN,OAAO,CAAC,KAAK,CAAC,4DAA4D,CAAC,CAAC;gBAC9E,CAAC;YACH,CAAC;iBAAM,CAAC;gBACN,IAAI,QAAQ,EAAE,CAAC;oBACb,eAAe,CAAC,OAAO,EAAG,GAAa,CAAC,OAAO,CAAC,CAAC;gBACnD,CAAC;qBAAM,CAAC;oBACN,OAAO,CAAC,KAAK,CAAC,UAAW,GAAa,CAAC,OAAO,EAAE,CAAC,CAAC;gBACpD,CAAC;YACH,CAAC;YACD,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;QACvB,CAAC;IACH,CAAC,CAAC,CAAC;AACP,CAAC"}
1
+ {"version":3,"file":"prime.js","sourceRoot":"","sources":["../../src/commands/prime.ts"],"names":[],"mappings":"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"}
@@ -1,3 +1,13 @@
1
1
  import { Command } from "commander";
2
+ /**
3
+ * Process records from stdin (JSON single object or array)
4
+ * Validates, dedups, and appends with file locking
5
+ */
6
+ export declare function processStdinRecords(domain: string, jsonMode: boolean, force: boolean, stdinData?: string, cwd?: string): Promise<{
7
+ created: number;
8
+ updated: number;
9
+ skipped: number;
10
+ errors: string[];
11
+ }>;
2
12
  export declare function registerRecordCommand(program: Command): void;
3
13
  //# sourceMappingURL=record.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"record.d.ts","sourceRoot":"","sources":["../../src/commands/record.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAU,MAAM,WAAW,CAAC;AAqB5C,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAqX5D"}
1
+ {"version":3,"file":"record.d.ts","sourceRoot":"","sources":["../../src/commands/record.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAU,MAAM,WAAW,CAAC;AAsB5C;;;GAGG;AACH,wBAAsB,mBAAmB,CACvC,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,OAAO,EACjB,KAAK,EAAE,OAAO,EACd,SAAS,CAAC,EAAE,MAAM,EAClB,GAAG,CAAC,EAAE,MAAM,GACX,OAAO,CAAC;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC,CA+FlF;AAED,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CA6b5D"}
@@ -7,6 +7,92 @@ import { appendRecord, readExpertiseFile, writeExpertiseFile, findDuplicate, } f
7
7
  import { withFileLock } from "../utils/lock.js";
8
8
  import { recordSchema } from "../schemas/record-schema.js";
9
9
  import { outputJson, outputJsonError } from "../utils/json-output.js";
10
+ import { readFileSync } from "node:fs";
11
+ /**
12
+ * Process records from stdin (JSON single object or array)
13
+ * Validates, dedups, and appends with file locking
14
+ */
15
+ export async function processStdinRecords(domain, jsonMode, force, stdinData, cwd) {
16
+ const config = await readConfig(cwd);
17
+ if (!config.domains.includes(domain)) {
18
+ throw new Error(`Domain "${domain}" not found in config. Available domains: ${config.domains.join(", ") || "(none)"}`);
19
+ }
20
+ // Read stdin (or use provided data for testing)
21
+ const inputData = stdinData ?? readFileSync(0, "utf-8");
22
+ let inputRecords;
23
+ try {
24
+ const parsed = JSON.parse(inputData);
25
+ inputRecords = Array.isArray(parsed) ? parsed : [parsed];
26
+ }
27
+ catch (err) {
28
+ throw new Error(`Failed to parse JSON from stdin: ${err instanceof Error ? err.message : String(err)}`);
29
+ }
30
+ // Validate each record against schema
31
+ const ajv = new Ajv();
32
+ const validate = ajv.compile(recordSchema);
33
+ const errors = [];
34
+ const validRecords = [];
35
+ for (let i = 0; i < inputRecords.length; i++) {
36
+ const record = inputRecords[i];
37
+ // Ensure recorded_at and classification are set
38
+ if (typeof record === "object" && record !== null) {
39
+ if (!("recorded_at" in record)) {
40
+ record.recorded_at = new Date().toISOString();
41
+ }
42
+ if (!("classification" in record)) {
43
+ record.classification = "tactical";
44
+ }
45
+ }
46
+ if (!validate(record)) {
47
+ const validationErrors = (validate.errors ?? [])
48
+ .map((err) => `${err.instancePath} ${err.message}`)
49
+ .join("; ");
50
+ errors.push(`Record ${i}: ${validationErrors}`);
51
+ continue;
52
+ }
53
+ validRecords.push(record);
54
+ }
55
+ if (validRecords.length === 0) {
56
+ return { created: 0, updated: 0, skipped: 0, errors };
57
+ }
58
+ // Process valid records with file locking
59
+ const filePath = getExpertisePath(domain, cwd);
60
+ let created = 0;
61
+ let updated = 0;
62
+ let skipped = 0;
63
+ await withFileLock(filePath, async () => {
64
+ const existing = await readExpertiseFile(filePath);
65
+ let currentRecords = [...existing];
66
+ for (const record of validRecords) {
67
+ const dup = findDuplicate(currentRecords, record);
68
+ if (dup && !force) {
69
+ const isNamed = record.type === "pattern" ||
70
+ record.type === "decision" ||
71
+ record.type === "reference" ||
72
+ record.type === "guide";
73
+ if (isNamed) {
74
+ // Upsert: replace in place
75
+ currentRecords[dup.index] = record;
76
+ updated++;
77
+ }
78
+ else {
79
+ // Exact match: skip
80
+ skipped++;
81
+ }
82
+ }
83
+ else {
84
+ // New record: append
85
+ currentRecords.push(record);
86
+ created++;
87
+ }
88
+ }
89
+ // Write all changes at once
90
+ if (created > 0 || updated > 0) {
91
+ await writeExpertiseFile(filePath, currentRecords);
92
+ }
93
+ });
94
+ return { created, updated, skipped, errors };
95
+ }
10
96
  export function registerRecordCommand(program) {
11
97
  program
12
98
  .command("record")
@@ -14,8 +100,7 @@ export function registerRecordCommand(program) {
14
100
  .argument("[content]", "record content")
15
101
  .description("Record an expertise record")
16
102
  .addOption(new Option("--type <type>", "record type")
17
- .choices(["convention", "pattern", "failure", "decision", "reference", "guide"])
18
- .makeOptionMandatory())
103
+ .choices(["convention", "pattern", "failure", "decision", "reference", "guide"]))
19
104
  .addOption(new Option("--classification <classification>", "classification level")
20
105
  .choices(["foundational", "tactical", "observational"])
21
106
  .default("tactical"))
@@ -29,11 +114,65 @@ export function registerRecordCommand(program) {
29
114
  .option("--evidence-commit <commit>", "evidence: commit hash")
30
115
  .option("--evidence-issue <issue>", "evidence: issue reference")
31
116
  .option("--evidence-file <file>", "evidence: file path")
117
+ .option("--evidence-bead <bead>", "evidence: bead ID")
32
118
  .option("--relates-to <ids>", "comma-separated record IDs this relates to")
33
119
  .option("--supersedes <ids>", "comma-separated record IDs this supersedes")
34
120
  .option("--force", "force recording even if duplicate exists")
121
+ .option("--stdin", "read JSON record(s) from stdin (single object or array)")
35
122
  .action(async (domain, content, options) => {
36
123
  const jsonMode = program.opts().json === true;
124
+ // Handle --stdin mode
125
+ if (options.stdin === true) {
126
+ try {
127
+ const result = await processStdinRecords(domain, jsonMode, options.force === true);
128
+ if (result.errors.length > 0) {
129
+ if (jsonMode) {
130
+ outputJsonError("record", `Validation errors: ${result.errors.join("; ")}`);
131
+ }
132
+ else {
133
+ console.error(chalk.red("Validation errors:"));
134
+ for (const error of result.errors) {
135
+ console.error(chalk.red(` ${error}`));
136
+ }
137
+ }
138
+ }
139
+ if (jsonMode) {
140
+ outputJson({
141
+ success: result.errors.length === 0 || result.created + result.updated > 0,
142
+ command: "record",
143
+ domain,
144
+ created: result.created,
145
+ updated: result.updated,
146
+ skipped: result.skipped,
147
+ errors: result.errors,
148
+ });
149
+ }
150
+ else {
151
+ if (result.created > 0) {
152
+ console.log(chalk.green(`✔ Created ${result.created} record(s) in ${domain}`));
153
+ }
154
+ if (result.updated > 0) {
155
+ console.log(chalk.green(`✔ Updated ${result.updated} record(s) in ${domain}`));
156
+ }
157
+ if (result.skipped > 0) {
158
+ console.log(chalk.yellow(`Skipped ${result.skipped} duplicate(s) in ${domain}`));
159
+ }
160
+ }
161
+ if (result.errors.length > 0 && result.created + result.updated === 0) {
162
+ process.exitCode = 1;
163
+ }
164
+ }
165
+ catch (err) {
166
+ if (jsonMode) {
167
+ outputJsonError("record", err instanceof Error ? err.message : String(err));
168
+ }
169
+ else {
170
+ console.error(chalk.red(`Error: ${err instanceof Error ? err.message : String(err)}`));
171
+ }
172
+ process.exitCode = 1;
173
+ }
174
+ return;
175
+ }
37
176
  const config = await readConfig();
38
177
  if (!config.domains.includes(domain)) {
39
178
  if (jsonMode) {
@@ -46,12 +185,23 @@ export function registerRecordCommand(program) {
46
185
  process.exitCode = 1;
47
186
  return;
48
187
  }
188
+ // Validate --type is provided for non-stdin mode
189
+ if (!options.type) {
190
+ if (jsonMode) {
191
+ outputJsonError("record", "--type is required (convention, pattern, failure, decision, reference, guide)");
192
+ }
193
+ else {
194
+ console.error(chalk.red("Error: --type is required (convention, pattern, failure, decision, reference, guide)"));
195
+ }
196
+ process.exitCode = 1;
197
+ return;
198
+ }
49
199
  const recordType = options.type;
50
200
  const classification = options.classification ?? "tactical";
51
201
  const recordedAt = new Date().toISOString();
52
202
  // Build evidence if any evidence option is provided
53
203
  let evidence;
54
- if (options.evidenceCommit || options.evidenceIssue || options.evidenceFile) {
204
+ if (options.evidenceCommit || options.evidenceIssue || options.evidenceFile || options.evidenceBead) {
55
205
  evidence = {};
56
206
  if (options.evidenceCommit)
57
207
  evidence.commit = options.evidenceCommit;
@@ -59,6 +209,8 @@ export function registerRecordCommand(program) {
59
209
  evidence.issue = options.evidenceIssue;
60
210
  if (options.evidenceFile)
61
211
  evidence.file = options.evidenceFile;
212
+ if (options.evidenceBead)
213
+ evidence.bead = options.evidenceBead;
62
214
  }
63
215
  const tags = typeof options.tags === "string"
64
216
  ? options.tags