@jigyasudham/veto 3.3.0 → 3.5.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 (164) hide show
  1. package/README.md +89 -7
  2. package/dist/adapters/index.d.ts.map +1 -1
  3. package/dist/adapters/index.js +1 -0
  4. package/dist/adapters/index.js.map +1 -1
  5. package/dist/agents/llm-runner.d.ts.map +1 -1
  6. package/dist/agents/llm-runner.js +78 -8
  7. package/dist/agents/llm-runner.js.map +1 -1
  8. package/dist/cli/context-guidance.d.ts +16 -0
  9. package/dist/cli/context-guidance.d.ts.map +1 -0
  10. package/dist/cli/context-guidance.js +20 -0
  11. package/dist/cli/context-guidance.js.map +1 -0
  12. package/dist/cli/leftover-guides.d.ts +38 -0
  13. package/dist/cli/leftover-guides.d.ts.map +1 -0
  14. package/dist/cli/leftover-guides.js +135 -0
  15. package/dist/cli/leftover-guides.js.map +1 -0
  16. package/dist/cli/lessons.d.ts +17 -0
  17. package/dist/cli/lessons.d.ts.map +1 -0
  18. package/dist/cli/lessons.js +448 -0
  19. package/dist/cli/lessons.js.map +1 -0
  20. package/dist/cli/statusline-hosts.d.ts +36 -0
  21. package/dist/cli/statusline-hosts.d.ts.map +1 -0
  22. package/dist/cli/statusline-hosts.js +212 -0
  23. package/dist/cli/statusline-hosts.js.map +1 -0
  24. package/dist/cli/statusline.d.ts +7 -0
  25. package/dist/cli/statusline.d.ts.map +1 -1
  26. package/dist/cli/statusline.js +69 -11
  27. package/dist/cli/statusline.js.map +1 -1
  28. package/dist/cli.js +114 -47
  29. package/dist/cli.js.map +1 -1
  30. package/dist/council/llm-council.d.ts.map +1 -1
  31. package/dist/council/llm-council.js +13 -8
  32. package/dist/council/llm-council.js.map +1 -1
  33. package/dist/lessons/adapters/claude.d.ts +10 -0
  34. package/dist/lessons/adapters/claude.d.ts.map +1 -0
  35. package/dist/lessons/adapters/claude.js +18 -0
  36. package/dist/lessons/adapters/claude.js.map +1 -0
  37. package/dist/lessons/adapters/codex.d.ts +7 -0
  38. package/dist/lessons/adapters/codex.d.ts.map +1 -0
  39. package/dist/lessons/adapters/codex.js +9 -0
  40. package/dist/lessons/adapters/codex.js.map +1 -0
  41. package/dist/lessons/adapters/gemini.d.ts +4 -0
  42. package/dist/lessons/adapters/gemini.d.ts.map +1 -0
  43. package/dist/lessons/adapters/gemini.js +44 -0
  44. package/dist/lessons/adapters/gemini.js.map +1 -0
  45. package/dist/lessons/adapters/index.d.ts +6 -0
  46. package/dist/lessons/adapters/index.d.ts.map +1 -0
  47. package/dist/lessons/adapters/index.js +14 -0
  48. package/dist/lessons/adapters/index.js.map +1 -0
  49. package/dist/lessons/adapters/markdown.d.ts +16 -0
  50. package/dist/lessons/adapters/markdown.d.ts.map +1 -0
  51. package/dist/lessons/adapters/markdown.js +130 -0
  52. package/dist/lessons/adapters/markdown.js.map +1 -0
  53. package/dist/lessons/adapters/types.d.ts +27 -0
  54. package/dist/lessons/adapters/types.d.ts.map +1 -0
  55. package/dist/lessons/adapters/types.js +2 -0
  56. package/dist/lessons/adapters/types.js.map +1 -0
  57. package/dist/lessons/classify.d.ts +33 -0
  58. package/dist/lessons/classify.d.ts.map +1 -0
  59. package/dist/lessons/classify.js +108 -0
  60. package/dist/lessons/classify.js.map +1 -0
  61. package/dist/lessons/consent.d.ts +30 -0
  62. package/dist/lessons/consent.d.ts.map +1 -0
  63. package/dist/lessons/consent.js +86 -0
  64. package/dist/lessons/consent.js.map +1 -0
  65. package/dist/lessons/discover.d.ts +26 -0
  66. package/dist/lessons/discover.d.ts.map +1 -0
  67. package/dist/lessons/discover.js +65 -0
  68. package/dist/lessons/discover.js.map +1 -0
  69. package/dist/lessons/harvest.d.ts +84 -0
  70. package/dist/lessons/harvest.d.ts.map +1 -0
  71. package/dist/lessons/harvest.js +271 -0
  72. package/dist/lessons/harvest.js.map +1 -0
  73. package/dist/lessons/identity.d.ts +23 -0
  74. package/dist/lessons/identity.d.ts.map +1 -0
  75. package/dist/lessons/identity.js +81 -0
  76. package/dist/lessons/identity.js.map +1 -0
  77. package/dist/lessons/manage.d.ts +163 -0
  78. package/dist/lessons/manage.d.ts.map +1 -0
  79. package/dist/lessons/manage.js +337 -0
  80. package/dist/lessons/manage.js.map +1 -0
  81. package/dist/lessons/mask.d.ts +6 -0
  82. package/dist/lessons/mask.d.ts.map +1 -0
  83. package/dist/lessons/mask.js +22 -0
  84. package/dist/lessons/mask.js.map +1 -0
  85. package/dist/lessons/on-save.d.ts +18 -0
  86. package/dist/lessons/on-save.d.ts.map +1 -0
  87. package/dist/lessons/on-save.js +53 -0
  88. package/dist/lessons/on-save.js.map +1 -0
  89. package/dist/lessons/select.d.ts +23 -0
  90. package/dist/lessons/select.d.ts.map +1 -0
  91. package/dist/lessons/select.js +136 -0
  92. package/dist/lessons/select.js.map +1 -0
  93. package/dist/lessons/source-project.d.ts +9 -0
  94. package/dist/lessons/source-project.d.ts.map +1 -0
  95. package/dist/lessons/source-project.js +149 -0
  96. package/dist/lessons/source-project.js.map +1 -0
  97. package/dist/lessons/store.d.ts +135 -0
  98. package/dist/lessons/store.d.ts.map +1 -0
  99. package/dist/lessons/store.js +222 -0
  100. package/dist/lessons/store.js.map +1 -0
  101. package/dist/memory/config.d.ts +27 -0
  102. package/dist/memory/config.d.ts.map +1 -1
  103. package/dist/memory/config.js +67 -0
  104. package/dist/memory/config.js.map +1 -1
  105. package/dist/memory/local.d.ts.map +1 -1
  106. package/dist/memory/local.js +19 -1
  107. package/dist/memory/local.js.map +1 -1
  108. package/dist/memory/schema.d.ts +1 -1
  109. package/dist/memory/schema.d.ts.map +1 -1
  110. package/dist/memory/schema.js +104 -0
  111. package/dist/memory/schema.js.map +1 -1
  112. package/dist/server/handlers/council.d.ts.map +1 -1
  113. package/dist/server/handlers/council.js +40 -4
  114. package/dist/server/handlers/council.js.map +1 -1
  115. package/dist/server/handlers/memory.d.ts.map +1 -1
  116. package/dist/server/handlers/memory.js +13 -3
  117. package/dist/server/handlers/memory.js.map +1 -1
  118. package/dist/server/handlers/session.d.ts.map +1 -1
  119. package/dist/server/handlers/session.js +61 -21
  120. package/dist/server/handlers/session.js.map +1 -1
  121. package/dist/server.d.ts.map +1 -1
  122. package/dist/server.js +12 -3
  123. package/dist/server.js.map +1 -1
  124. package/dist/transcripts/cache.d.ts +1 -0
  125. package/dist/transcripts/cache.d.ts.map +1 -1
  126. package/dist/transcripts/cache.js +0 -0
  127. package/dist/transcripts/cache.js.map +1 -1
  128. package/dist/transcripts/claude-paths.d.ts +5 -0
  129. package/dist/transcripts/claude-paths.d.ts.map +1 -0
  130. package/dist/transcripts/claude-paths.js +21 -0
  131. package/dist/transcripts/claude-paths.js.map +1 -0
  132. package/dist/transcripts/context.d.ts +41 -0
  133. package/dist/transcripts/context.d.ts.map +1 -0
  134. package/dist/transcripts/context.js +144 -0
  135. package/dist/transcripts/context.js.map +1 -0
  136. package/dist/transcripts/discover.d.ts +12 -2
  137. package/dist/transcripts/discover.d.ts.map +1 -1
  138. package/dist/transcripts/discover.js +55 -7
  139. package/dist/transcripts/discover.js.map +1 -1
  140. package/dist/transcripts/manage.d.ts +8 -0
  141. package/dist/transcripts/manage.d.ts.map +1 -1
  142. package/dist/transcripts/manage.js +28 -13
  143. package/dist/transcripts/manage.js.map +1 -1
  144. package/dist/transcripts/mapping.d.ts.map +1 -1
  145. package/dist/transcripts/mapping.js +4 -2
  146. package/dist/transcripts/mapping.js.map +1 -1
  147. package/dist/transcripts/on-save.d.ts +9 -0
  148. package/dist/transcripts/on-save.d.ts.map +1 -1
  149. package/dist/transcripts/on-save.js +36 -9
  150. package/dist/transcripts/on-save.js.map +1 -1
  151. package/dist/transcripts/project-key.d.ts +4 -0
  152. package/dist/transcripts/project-key.d.ts.map +1 -0
  153. package/dist/transcripts/project-key.js +30 -0
  154. package/dist/transcripts/project-key.js.map +1 -0
  155. package/dist/transcripts/recall.d.ts +4 -0
  156. package/dist/transcripts/recall.d.ts.map +1 -1
  157. package/dist/transcripts/recall.js +6 -5
  158. package/dist/transcripts/recall.js.map +1 -1
  159. package/dist/transcripts/stopwords.d.ts +2 -0
  160. package/dist/transcripts/stopwords.d.ts.map +1 -0
  161. package/dist/transcripts/stopwords.js +8 -0
  162. package/dist/transcripts/stopwords.js.map +1 -0
  163. package/package.json +1 -1
  164. package/server.json +2 -2
@@ -0,0 +1,135 @@
1
+ // Veto guides that `veto init` left inside other CLIs' own files.
2
+ //
3
+ // From v1.4.5 (2026-05-23) through 3.3.0, `veto init` overwrote
4
+ // ~/.gemini/GEMINI.md (Gemini's own memory file) and wrote
5
+ // ~/.codex/AGENTS.override.md, which Codex reads INSTEAD of the user's
6
+ // ~/.codex/AGENTS.md. Init no longer writes either file, so the set of texts it
7
+ // can have left behind is closed: the eight guides below, taken from git
8
+ // history (every VETO_GUIDE src/cli.ts has ever held; no earlier code named
9
+ // either file).
10
+ //
11
+ // Only a file that IS one of those guides, byte for byte once line endings are
12
+ // normalized, is ever moved, and it is renamed rather than deleted. A file with
13
+ // anything else in it (memories Gemini saved below the guide, the user's own
14
+ // edits) is reported and never touched.
15
+ import { createHash } from 'node:crypto';
16
+ import { existsSync, readFileSync, renameSync, statSync } from 'node:fs';
17
+ import { homedir } from 'node:os';
18
+ import { join } from 'node:path';
19
+ export const SHIPPED_GUIDE_SHA256 = new Set([
20
+ '8c283b8f593027cef97f93cc60e2f5472f84637878f4445f4d1de3456eff3558', // 49 tools (v1.4.5)
21
+ '125506e5606e8a7113e78ed6ed45dfa1213e5d489e3b10c96171097f83242c2d', // 49 tools
22
+ '5f7b7c1de6c89fcc73aaa2a6199ca8e57e334b63dd32dc1520c333b7cf20f550', // 62 tools
23
+ 'cfc7a5cd1af02527e05d055745b3ab4511b64f84db96ddaa465f336b81f6b369', // 89 tools
24
+ '7ba579cfd46bdbcd16a771e9c1d0ccc672c1414726a91c55bb7648e20616dda6', // 90 tools
25
+ 'da098265f2f1d085df681200feef626fcd119ecd1d14e13d771bd4d27bf1e948', // 91 tools
26
+ '0567c129b23ea49fb4e77b43f3e7e757fed2b1eebc6c385dcb4ac967bb8af870', // 92 tools
27
+ '2953a1c3f925955eba2967d87f34cdc30ec951f0785639a4dcb138afd6ad43fe', // 93 tools (through 3.3.0)
28
+ ]);
29
+ // A guide is 1.4 KB. Anything far larger is the user's own file; it is only
30
+ // ever checked for an embedded guide, and never read past this at startup.
31
+ const MAX_READ_BYTES = 256 * 1024;
32
+ const GUIDE_HEADING_RE = /^# Veto MCP Server[ \t]*$/m;
33
+ const normalize = (raw) => raw.replace(/^\uFEFF/, '').replace(/\r\n?/g, '\n');
34
+ const sha256 = (text) => createHash('sha256').update(text).digest('hex');
35
+ /** The only two files init ever wrote into (always under the home directory, never CODEX_HOME). */
36
+ export function leftoverGuideTargets(home = homedir()) {
37
+ return [
38
+ { host: 'codex', path: join(home, '.codex', 'AGENTS.override.md') },
39
+ { host: 'gemini', path: join(home, '.gemini', 'GEMINI.md') },
40
+ ];
41
+ }
42
+ function classify(path) {
43
+ let text;
44
+ try {
45
+ if (statSync(path).size > MAX_READ_BYTES)
46
+ return null;
47
+ text = normalize(readFileSync(path, 'utf8'));
48
+ }
49
+ catch {
50
+ return null;
51
+ }
52
+ if (SHIPPED_GUIDE_SHA256.has(sha256(text)))
53
+ return 'exact';
54
+ return GUIDE_HEADING_RE.test(text) && /\bVeto is active\b/.test(text) ? 'embedded' : null;
55
+ }
56
+ export function findLeftoverGuides(home = homedir()) {
57
+ const found = [];
58
+ for (const target of leftoverGuideTargets(home)) {
59
+ const kind = classify(target.path);
60
+ if (kind)
61
+ found.push({ ...target, kind });
62
+ }
63
+ return found;
64
+ }
65
+ function freeBackupPath(path, now) {
66
+ const plain = `${path}.veto-backup`;
67
+ if (!existsSync(plain))
68
+ return plain;
69
+ const stamp = now.toISOString().replace(/[-:]/g, '').replace(/\..*$/, '');
70
+ return `${plain}-${stamp}`;
71
+ }
72
+ /**
73
+ * Rename each file that is exactly a guide Veto wrote to `<file>.veto-backup`.
74
+ * Embedded guides are returned in `kept`, untouched. The file is re-checked
75
+ * immediately before the rename, so content a host appended in the meantime is
76
+ * never moved away with it.
77
+ */
78
+ export function moveAsideLeftoverGuides(home = homedir(), now = new Date()) {
79
+ const moved = [];
80
+ const kept = [];
81
+ for (const guide of findLeftoverGuides(home)) {
82
+ if (guide.kind !== 'exact' || classify(guide.path) !== 'exact') {
83
+ kept.push(guide);
84
+ continue;
85
+ }
86
+ const backupPath = freeBackupPath(guide.path, now);
87
+ try {
88
+ renameSync(guide.path, backupPath);
89
+ moved.push({ ...guide, backupPath });
90
+ }
91
+ catch {
92
+ kept.push(guide);
93
+ }
94
+ }
95
+ return { moved, kept };
96
+ }
97
+ /**
98
+ * One line about a leftover. `owner` is how the file's owner is named: "your"
99
+ * in the terminal, "the user's" in text handed to the agent.
100
+ */
101
+ export function describeLeftover(guide, home = homedir(), owner = 'your') {
102
+ const shown = guide.path.startsWith(home) ? `~${guide.path.slice(home.length).replace(/\\/g, '/')}` : guide.path;
103
+ const why = guide.host === 'codex' ? `Codex reads it INSTEAD of ${owner} own ~/.codex/AGENTS.md` : "it is Gemini's own memory file";
104
+ return guide.kind === 'exact'
105
+ ? `${shown} is an old Veto guide (${why})`
106
+ : `${shown} starts with an old Veto guide above ${owner} own content`;
107
+ }
108
+ /**
109
+ * Startup tip for the MCP `instructions` field, present only while a leftover
110
+ * exists, so it resolves itself. Like the status-line tip, it hands the offer
111
+ * to the agent; nothing is changed unless the user agrees.
112
+ */
113
+ export function leftoverGuideInstruction(home = homedir()) {
114
+ const found = findLeftoverGuides(home);
115
+ if (found.length === 0)
116
+ return undefined;
117
+ const exact = found.filter(g => g.kind === 'exact');
118
+ const embedded = found.filter(g => g.kind === 'embedded');
119
+ const lines = [
120
+ 'CLEANUP TIP — old Veto guide files (mention once per session; drop it if the user declines).',
121
+ 'Older versions of `veto init` wrote Veto\'s guide into other CLIs\' own files, which no current version does.',
122
+ ];
123
+ if (exact.length) {
124
+ lines.push(`Found: ${exact.map(g => describeLeftover(g, home, "the user's")).join('; ')}.`);
125
+ lines.push('Offer to run `veto doctor --fix` for the user (without a global install:'
126
+ + ' `npx -y --package @jigyasudham/veto@latest veto doctor --fix`). It renames only those Veto-written'
127
+ + ' copies to *.veto-backup and changes nothing else. Do not run it unless the user agrees.');
128
+ }
129
+ if (embedded.length) {
130
+ lines.push(`Also: ${embedded.map(g => describeLeftover(g, home, "the user's")).join('; ')}. Veto will not edit a file that`
131
+ + ' holds the user\'s own content; if they want, they can delete the "# Veto MCP Server" block by hand.');
132
+ }
133
+ return lines.join(' ');
134
+ }
135
+ //# sourceMappingURL=leftover-guides.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"leftover-guides.js","sourceRoot":"","sources":["../../src/cli/leftover-guides.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ type Out = (line?: string) => void;
2
+ export type ConsentIo = {
3
+ env: NodeJS.ProcessEnv;
4
+ /** A person can answer: stdin and stdout are a terminal. */
5
+ interactive: boolean;
6
+ ask: (question: string) => Promise<string>;
7
+ };
8
+ /** Runs one `veto lessons` subcommand and returns its exit code (a promise only for `on`, which may ask a question). */
9
+ export declare function runLessonsCommand(args: string[], options?: {
10
+ out?: Out;
11
+ color?: boolean;
12
+ cwd?: string;
13
+ home?: string;
14
+ io?: ConsentIo;
15
+ }): number | Promise<number>;
16
+ export {};
17
+ //# sourceMappingURL=lessons.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lessons.d.ts","sourceRoot":"","sources":["../../src/cli/lessons.ts"],"names":[],"mappings":"AAeA,KAAK,GAAG,GAAG,CAAC,IAAI,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;AA4RnC,MAAM,MAAM,SAAS,GAAG;IACtB,GAAG,EAAE,MAAM,CAAC,UAAU,CAAC;IACvB,4DAA4D;IAC5D,WAAW,EAAE,OAAO,CAAC;IACrB,GAAG,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;CAC5C,CAAC;AAsEF,wHAAwH;AACxH,wBAAgB,iBAAiB,CAC/B,IAAI,EAAE,MAAM,EAAE,EACd,OAAO,GAAE;IAAE,GAAG,CAAC,EAAE,GAAG,CAAC;IAAC,KAAK,CAAC,EAAE,OAAO,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,EAAE,CAAC,EAAE,SAAS,CAAA;CAAO,GACxF,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAwB1B"}
@@ -0,0 +1,448 @@
1
+ // `veto lessons`: see and control what Veto harvests from each AI's own memory
2
+ // (Harvest-and-Share, council 320c40dc). Nothing here delivers a note to any
3
+ // AI; it only shows the user their notes and where the rules would let them go.
4
+ import { createInterface } from 'node:readline';
5
+ import { LESSON_SOURCES } from '../lessons/adapters/index.js';
6
+ import { acceptLessonsConsent, detectAiSession, lessonsDisclosure } from '../lessons/consent.js';
7
+ import { aliasGroups, explainLesson, excludeProjectDir, findLesson, forgetLesson, HOST_NAMES, includeProjectDir, lessonFlows, lessonReach, lessonsStatus, lessonTitle, listLessons, projectName, recheckSource, refreshLessons, removeProjectAlias, setProjectAlias, turnLessonsOff, unresolvedFolders, } from '../lessons/manage.js';
8
+ const ansi = (code) => (s) => `\x1b[${code}m${s}\x1b[0m`;
9
+ const COLORS = { bold: ansi(1), dim: ansi(2), green: ansi(32), yellow: ansi(33), cyan: ansi(36), red: ansi(31) };
10
+ const PLAIN = { bold: s => s, dim: s => s, green: s => s, yellow: s => s, cyan: s => s, red: s => s };
11
+ const RULE = '─────────────────────────────────────────────────────';
12
+ const SHADOW = 'shadow mode: nothing is delivered to any AI yet';
13
+ const USAGE = [
14
+ 'veto lessons [status]',
15
+ 'veto lessons on (in your own terminal: you type yes to accept)',
16
+ 'veto lessons list [--scope=user|machine|project] [--source=claude|codex|gemini] [--shared] [--held] [--project=<dir>] [--json]',
17
+ 'veto lessons why <id>',
18
+ 'veto lessons forget <id>',
19
+ 'veto lessons flows',
20
+ 'veto lessons off',
21
+ 'veto lessons exclude [<dir>] · veto lessons include [<dir>]',
22
+ 'veto lessons alias [<path> --to=<dir>] · veto lessons alias --remove <path>',
23
+ 'veto lessons recheck <claude|codex|gemini>',
24
+ ];
25
+ const VALUE_FLAGS = new Set(['scope', 'source', 'project', 'to']);
26
+ function parseArgs(args) {
27
+ const positional = [];
28
+ const flags = new Map();
29
+ for (let i = 0; i < args.length; i++) {
30
+ const arg = args[i];
31
+ if (!arg.startsWith('--')) {
32
+ positional.push(arg);
33
+ continue;
34
+ }
35
+ const eq = arg.indexOf('=');
36
+ if (eq > 2) {
37
+ flags.set(arg.slice(2, eq), arg.slice(eq + 1));
38
+ continue;
39
+ }
40
+ const name = arg.slice(2);
41
+ if (VALUE_FLAGS.has(name) && i + 1 < args.length && !args[i + 1].startsWith('--'))
42
+ flags.set(name, args[++i]);
43
+ else
44
+ flags.set(name, true);
45
+ }
46
+ return { positional, flags };
47
+ }
48
+ const shortId = (row) => row.id.slice(0, 8);
49
+ const clip = (text, width) => (text.length > width ? `${text.slice(0, width - 1)}…` : text);
50
+ const plural = (n, one, many = `${one}s`) => `${n} ${n === 1 ? one : many}`;
51
+ function reachLabel(row) {
52
+ const reach = lessonReach(row);
53
+ if (reach === 'any-project')
54
+ return 'any project';
55
+ if (row.quarantined)
56
+ return `held: ${row.quarantine_reason ?? 'held'}`;
57
+ return reach === 'own-project' ? 'own project' : 'nowhere';
58
+ }
59
+ function lookup(found, id, out, c) {
60
+ if ('row' in found)
61
+ return found.row;
62
+ if ('matches' in found) {
63
+ out(c.red(` ${id} matches more than one note. Give more of the ID:`));
64
+ for (const row of found.matches)
65
+ out(` ${c.cyan(row.id.slice(0, 12))} ${clip(lessonTitle(row), 60)}`);
66
+ }
67
+ else if (found.error === 'too-short')
68
+ out(c.red(' Give at least the first 4 characters of the note ID, as veto lessons list shows it.'));
69
+ else
70
+ out(c.red(` No note ${id}. See veto lessons list.`));
71
+ return null;
72
+ }
73
+ function sharingLine(c) {
74
+ const s = lessonsStatus();
75
+ if (s.sharing)
76
+ return `${c.green('on')}${s.consentAt ? c.dim(` · accepted ${s.consentAt.slice(0, 10)}`) : ''} · ${c.dim(SHADOW)}`;
77
+ if (s.needsReconsent)
78
+ return c.yellow('paused: what sharing does has changed since you accepted it') + c.dim(' → veto lessons on');
79
+ return `${c.dim('off')}${s.notes ? '' : c.dim(": Veto has not read any AI's memory")}`;
80
+ }
81
+ function status(out, c, home) {
82
+ refreshLessons(home);
83
+ const s = lessonsStatus();
84
+ out('');
85
+ out(c.bold(' Veto Lessons: notes your AIs wrote, shared between them'));
86
+ out(c.dim(` ${RULE}`));
87
+ out(` Sharing: ${sharingLine(c)}`);
88
+ if (s.notes || s.sharing) {
89
+ out(` Notes: ${s.notes} ${c.dim(`(${LESSON_SOURCES.map(host => `${HOST_NAMES[host]} ${s.byHost[host]}`).join(' · ')})`)}`);
90
+ out(` Reach: ${plural(s.anyProject, 'note')} may go to any project · ${s.held} held in their own project`);
91
+ }
92
+ for (const [host, reason] of s.disabledHosts) {
93
+ out(c.yellow(` Switched off: ${HOST_NAMES[host]}, its memory format changed (${reason})`) + c.dim(` → veto lessons recheck ${host}`));
94
+ }
95
+ if (s.excluded.length)
96
+ out(` Kept out: ${s.excluded.map(e => e.project_label ?? e.project_identity).join(', ')} ${c.dim('(nothing leaves, nothing enters)')}`);
97
+ if (s.unresolved)
98
+ out(` Unlinked: ${plural(s.unresolved, 'memory folder')} with no project Veto can find ${c.dim('→ veto lessons alias')}`);
99
+ if (s.forgotten)
100
+ out(` Forgotten: ${s.forgotten} ${c.dim('(permanent)')}`);
101
+ if (s.shadowLog)
102
+ out(` Shadow log: ${plural(s.shadowLog, 'record')} of what would have been shared`);
103
+ if (s.notes && !s.sharing)
104
+ out(c.yellow(' Sharing is off, but notes harvested earlier are still stored.') + c.dim(' Delete them with: veto lessons off'));
105
+ if (!s.sharing && !s.needsReconsent)
106
+ out(c.dim(' Turn it on, in a terminal of your own: veto lessons on'));
107
+ out('');
108
+ out(c.dim(' on · list · why <id> · forget <id> · flows · off · exclude|include [dir] · alias · recheck <host>'));
109
+ out('');
110
+ return 0;
111
+ }
112
+ function list(flags, out, c, home) {
113
+ const scope = flags.get('scope');
114
+ const source = flags.get('source');
115
+ const project = flags.get('project');
116
+ if (scope !== undefined && !['user', 'machine', 'project'].includes(String(scope))) {
117
+ out(c.red(' --scope must be user, machine or project.'));
118
+ return 1;
119
+ }
120
+ if (source !== undefined && !LESSON_SOURCES.includes(String(source))) {
121
+ out(c.red(' --source must be claude, codex or gemini.'));
122
+ return 1;
123
+ }
124
+ refreshLessons(home);
125
+ const rows = listLessons({
126
+ scope: typeof scope === 'string' ? scope : undefined,
127
+ source: typeof source === 'string' ? source : undefined,
128
+ projectDir: typeof project === 'string' ? project : undefined,
129
+ shared: flags.has('shared'),
130
+ held: flags.has('held'),
131
+ });
132
+ if (flags.has('json')) {
133
+ out(JSON.stringify(rows, null, 2));
134
+ return 0;
135
+ }
136
+ out('');
137
+ out(c.bold(` Notes Veto has read from your AIs' memory: ${rows.length}`));
138
+ out(c.dim(` ${RULE}`));
139
+ out(` Sharing: ${sharingLine(c)}`);
140
+ let group = null;
141
+ for (const row of rows) {
142
+ const name = projectName(row);
143
+ if (name !== group) {
144
+ group = name;
145
+ out('');
146
+ out(` ${c.bold(name)}`);
147
+ }
148
+ out(` ${c.cyan(shortId(row))} ${HOST_NAMES[row.source_cli].padEnd(6)} ${c.dim(row.source_mtime.slice(0, 10))} ${reachLabel(row).padEnd(13)} ${clip(lessonTitle(row), 60)}`);
149
+ }
150
+ if (!rows.length)
151
+ out(c.dim(flags.size ? ' (no notes match)' : ' (none)'));
152
+ out('');
153
+ out(c.dim(' why <id> shows a note and where it may go · forget <id> removes it for good'));
154
+ out('');
155
+ return 0;
156
+ }
157
+ function why(id, out, c, home) {
158
+ if (!id) {
159
+ out(c.red(' Usage: veto lessons why <id>'));
160
+ return 1;
161
+ }
162
+ refreshLessons(home);
163
+ const row = lookup(findLesson(id), id, out, c);
164
+ if (!row)
165
+ return 1;
166
+ const e = explainLesson(row);
167
+ out('');
168
+ out(c.bold(` Note ${shortId(row)}`) + c.dim(` ${e.provenance}`));
169
+ out(c.dim(` ${RULE}`));
170
+ out(` Source ${row.source_path} ${c.dim(`(section: ${row.section_anchor})`)}`);
171
+ out(` Scope ${e.scope}`);
172
+ if (e.held)
173
+ out(` Held ${e.held}`);
174
+ out(` May reach ${e.reach}`);
175
+ if (e.hostDisabled)
176
+ out(c.yellow(` Paused ${HOST_NAMES[row.source_cli]}'s memory format changed, so none of its notes are used (${e.hostDisabled})`));
177
+ out(` Delivered ${c.dim(`never yet: ${SHADOW}`)}`);
178
+ out('');
179
+ out(c.dim(' ┌─ the note as it would be shared (email addresses, home folders and secrets masked)'));
180
+ for (const line of row.text_masked.split('\n'))
181
+ out(`${c.dim(' │')} ${line}`);
182
+ out(c.dim(' └─'));
183
+ out('');
184
+ out(c.dim(` Forget it for good: veto lessons forget ${shortId(row)}`));
185
+ out('');
186
+ return 0;
187
+ }
188
+ function forget(id, out, c) {
189
+ if (!id) {
190
+ out(c.red(' Usage: veto lessons forget <id>'));
191
+ return 1;
192
+ }
193
+ const row = lookup(findLesson(id), id, out, c);
194
+ if (!row)
195
+ return 1;
196
+ const { removed } = forgetLesson(row);
197
+ out('');
198
+ out(c.green(` ✓ Forgot ${shortId(row)}: ${clip(lessonTitle(row), 60)}`));
199
+ if (removed > 1)
200
+ out(c.dim(` Also removed ${plural(removed - 1, 'copy', 'copies')} of it kept elsewhere.`));
201
+ out(c.dim(` It will not be read again, even if ${HOST_NAMES[row.source_cli]} edits it. Its memory file is untouched.`));
202
+ out('');
203
+ return 0;
204
+ }
205
+ function flows(out, c, home) {
206
+ refreshLessons(home);
207
+ const all = lessonFlows();
208
+ const s = lessonsStatus();
209
+ out('');
210
+ out(c.bold(' Where each AI\'s notes may go'));
211
+ out(c.dim(` ${RULE}`));
212
+ out(` Sharing: ${sharingLine(c)}`);
213
+ for (const flow of all) {
214
+ out('');
215
+ out(` ${c.bold(`${HOST_NAMES[flow.source]} memory · ${flow.projectLabel}`)} ${c.dim(plural(flow.notes, 'note'))}`);
216
+ const to = flow.source === 'claude' ? 'Codex, Gemini, and Claude in other folders'
217
+ : `${LESSON_SOURCES.filter(host => host !== flow.source).map(host => HOST_NAMES[host]).join(', ')}`;
218
+ if (flow.anyProject)
219
+ out(` ${String(flow.anyProject).padStart(4)} → any project, in ${to}`);
220
+ if (flow.ownProject)
221
+ out(` ${String(flow.ownProject).padStart(4)} → ${flow.projectLabel} only, in ${to}`);
222
+ if (flow.nowhere) {
223
+ out(` ${String(flow.nowhere).padStart(4)} → nowhere ${c.dim(flow.unresolved ? '(no known project folder: see veto lessons alias)' : '(held back, and no project of their own)')}`);
224
+ }
225
+ const held = Object.entries(flow.heldBack);
226
+ if (held.length)
227
+ out(c.dim(` held in their project instead of shared: ${held.map(([reason, n]) => `${n} ${reason}`).join(', ')}`));
228
+ if (s.disabledHosts.has(flow.source))
229
+ out(c.yellow(` paused: ${HOST_NAMES[flow.source]}'s memory format changed → veto lessons recheck ${flow.source}`));
230
+ }
231
+ if (!all.length) {
232
+ out('');
233
+ out(c.dim(' (no notes)'));
234
+ }
235
+ if (s.excluded.length) {
236
+ out('');
237
+ out(` Kept out: ${s.excluded.map(e => e.project_label ?? e.project_identity).join(', ')} ${c.dim('(nothing leaves, nothing enters)')}`);
238
+ }
239
+ out('');
240
+ out(c.dim(' A note is only ever shared into the project it came from, unless it is about you or this machine'));
241
+ out(c.dim(' and carries no command, web address, credential or instruction.'));
242
+ out('');
243
+ return 0;
244
+ }
245
+ function off(out, c) {
246
+ const r = turnLessonsOff();
247
+ out('');
248
+ out(c.green(` ✓ Sharing is off${r.wasOn ? '' : ' (it already was)'}.`));
249
+ out(` Deleted ${plural(r.notes, 'harvested note')} and ${plural(r.shadowLog, 'shadow-log record')}.`);
250
+ if (r.remaining === 0)
251
+ out(c.dim(' Checked: nothing harvested is left in Veto\'s database. Your AIs\' own memory files are untouched.'));
252
+ else
253
+ out(c.red(` ${r.remaining} rows could not be deleted. Run this again, or report it.`));
254
+ out(c.dim(' Forgotten notes, excluded projects and aliases are kept, so they still apply if you turn sharing back on.'));
255
+ out('');
256
+ return r.remaining === 0 ? 0 : 1;
257
+ }
258
+ function excludeOrInclude(sub, dir, out, c) {
259
+ if (sub === 'exclude') {
260
+ const r = excludeProjectDir(dir);
261
+ out('');
262
+ out(c.green(` ✓ ${r.label} is kept out of sharing${r.added ? '' : ' (it already was)'}.`) + c.dim(' Its notes are not read, and no note is chosen for it.'));
263
+ if (r.removed)
264
+ out(c.dim(` Deleted ${plural(r.removed, 'note')} already harvested from it.`));
265
+ out('');
266
+ return 0;
267
+ }
268
+ const r = includeProjectDir(dir);
269
+ out('');
270
+ out(r.removed ? c.green(` ✓ ${r.label} takes part in sharing again.`) : c.dim(` ${r.label} was not kept out.`));
271
+ out('');
272
+ return 0;
273
+ }
274
+ function alias(positional, flags, out, c, home) {
275
+ const remove = flags.get('remove');
276
+ const target = flags.get('to');
277
+ const path = typeof remove === 'string' ? remove : positional[0];
278
+ if (remove !== undefined) {
279
+ if (!path) {
280
+ out(c.red(' Usage: veto lessons alias --remove <path>'));
281
+ return 1;
282
+ }
283
+ out(removeProjectAlias(path) ? c.green(` ✓ ${path} is no longer linked to another project.`) : c.dim(` ${path} had no link.`));
284
+ return 0;
285
+ }
286
+ if (path) {
287
+ if (typeof target !== 'string') {
288
+ out(c.red(' Usage: veto lessons alias <path> --to=<project folder>'));
289
+ return 1;
290
+ }
291
+ const r = setProjectAlias(path, target);
292
+ if (!r.ok) {
293
+ out(c.red(r.error === 'target-missing' ? ` ${target} is not a folder on this machine.`
294
+ : r.error === 'same-folder' ? ' Those are the same folder.'
295
+ : ` ${path} is a different repository from ${target}, so its notes are not ${target}'s. Nothing was linked.`));
296
+ return 1;
297
+ }
298
+ out(c.green(` ✓ Notes from ${path} now count as ${r.label}'s.`));
299
+ if (!r.memoryFolders)
300
+ out(c.yellow(" No AI memory Veto has read belongs to that path, so nothing moves yet. Check the spelling against veto lessons alias."));
301
+ const report = refreshLessons(home);
302
+ if (report)
303
+ out(c.dim(` Re-read your AIs' memory: ${report.updated} notes moved or changed.`));
304
+ return 0;
305
+ }
306
+ refreshLessons(home);
307
+ const groups = aliasGroups();
308
+ const unresolved = unresolvedFolders();
309
+ out('');
310
+ out(c.bold(' Folders Veto treats as one project'));
311
+ out(c.dim(` ${RULE}`));
312
+ if (!groups.length)
313
+ out(c.dim(' (none: each project is checked out in one place)'));
314
+ for (const group of groups)
315
+ out(` ${group.paths.join(c.dim(' = '))}`);
316
+ if (unresolved.length) {
317
+ out('');
318
+ out(c.bold(' Memory with no project folder'));
319
+ out(c.dim(' Its project notes reach no session until you link it, for example a copy on a drive that is not plugged in:'));
320
+ for (const folder of unresolved) {
321
+ out(` ${folder.label} ${c.dim(plural(folder.notes, 'note'))}`);
322
+ const s = folder.suggestion;
323
+ if (s)
324
+ out(` ${c.dim(s.aliasPath ? 'link it:' : 'probably this project; give its folder:')} veto lessons alias "${s.aliasPath ?? '<its folder>'}" --to="${s.targetDir}"`);
325
+ }
326
+ out(c.dim(' Link one with: veto lessons alias "<its folder>" --to="<the project folder on this machine>"'));
327
+ }
328
+ out('');
329
+ return 0;
330
+ }
331
+ function recheck(host, out, c, home) {
332
+ if (!host || !LESSON_SOURCES.includes(host)) {
333
+ out(c.red(' Usage: veto lessons recheck <claude|codex|gemini>'));
334
+ return 1;
335
+ }
336
+ const r = recheckSource(host, home);
337
+ out('');
338
+ if (r.stillDisabled)
339
+ out(c.yellow(` ${HOST_NAMES[host]}'s memory still has a format Veto does not recognise (${r.stillDisabled}), so it stays off.`));
340
+ else if (!r.report)
341
+ out(c.green(` ✓ ${HOST_NAMES[host]} is back on.`) + c.dim(' It will be read once sharing is on.'));
342
+ else
343
+ out(c.green(` ✓ ${HOST_NAMES[host]} is back on${r.wasDisabled ? '' : ' (it was not off)'}.`) + c.dim(` Read ${plural(r.report.harvested, 'file')}.`));
344
+ out('');
345
+ return r.stillDisabled ? 1 : 0;
346
+ }
347
+ function terminalIo() {
348
+ return {
349
+ env: process.env,
350
+ interactive: Boolean(process.stdin.isTTY && process.stdout.isTTY),
351
+ ask: question => new Promise(resolve => {
352
+ const rl = createInterface({ input: process.stdin, output: process.stdout });
353
+ let answered = false;
354
+ rl.on('close', () => { if (!answered)
355
+ resolve(''); });
356
+ rl.question(question, answer => { answered = true; rl.close(); resolve(answer); });
357
+ }),
358
+ };
359
+ }
360
+ /**
361
+ * Consent v2. The disclosure is shown to anyone, but only a person typing
362
+ * "yes" in their own terminal can accept it. A command an AI runs is refused
363
+ * before any question is asked, so no AI can turn sharing on for the user.
364
+ */
365
+ async function on(out, c, home, io) {
366
+ const s = lessonsStatus();
367
+ if (s.sharing) {
368
+ out('');
369
+ out(c.green(` Sharing is already on${s.consentAt ? ` (accepted ${s.consentAt.slice(0, 10)})` : ''}.`) + c.dim(' Turn it off with: veto lessons off'));
370
+ out('');
371
+ return 0;
372
+ }
373
+ out('');
374
+ for (const line of lessonsDisclosure().split('\n'))
375
+ out(line ? ` ${line}` : '');
376
+ out('');
377
+ const marker = detectAiSession(io.env);
378
+ if (marker) {
379
+ out(c.yellow(` An AI is running this command (${marker} is set), so it cannot accept for you. Nothing changed.`));
380
+ out(' To turn sharing on, open a terminal window yourself and run: veto lessons on');
381
+ out('');
382
+ return 1;
383
+ }
384
+ if (!io.interactive) {
385
+ out(c.yellow(' Accepting needs you to type yes in a terminal window. Nothing changed.'));
386
+ out(' Open one and run: veto lessons on');
387
+ out('');
388
+ return 1;
389
+ }
390
+ let answer = '';
391
+ try {
392
+ answer = await io.ask(' Type yes to turn sharing on: ');
393
+ }
394
+ catch {
395
+ answer = '';
396
+ }
397
+ if (answer.trim().toLowerCase() !== 'yes') {
398
+ out(c.dim(' Nothing changed. Sharing is still off.'));
399
+ out('');
400
+ return 0;
401
+ }
402
+ const r = acceptLessonsConsent(home);
403
+ out('');
404
+ out(c.green(' ✓ Sharing is on.'));
405
+ out('');
406
+ const hosts = LESSON_SOURCES.map(host => `${HOST_NAMES[host]} ${r.byHost[host]}`).join(' · ');
407
+ out(` Veto read ${plural(r.report.sources, 'memory file')} and found ${plural(r.notes, 'note')} ${c.dim(`(${hosts})`)}${r.projects ? ` across ${plural(r.projects, 'project')}` : ''}.`);
408
+ if (r.notes) {
409
+ out(` ${String(r.anyProject).padStart(4)} may be shared into any project: they are about you or this computer`);
410
+ out(` ${String(r.ownProject).padStart(4)} stay in their own project: they are about it, or hold a command, web address or credential`);
411
+ }
412
+ if (r.unlinked)
413
+ out(` ${String(r.unlinked).padStart(4)} ${r.unlinked === 1 ? 'memory folder is' : 'memory folders are'} on a drive Veto cannot see; link ${r.unlinked === 1 ? 'it' : 'them'} with: veto lessons alias`);
414
+ for (const host of r.disabledHosts)
415
+ out(c.yellow(` ${HOST_NAMES[host]}'s memory is in a format Veto does not recognise, so it was skipped.`));
416
+ out('');
417
+ out(c.dim(' This is the trial: nothing has been given to any AI. See every note with: veto lessons list'));
418
+ out('');
419
+ return 0;
420
+ }
421
+ /** Runs one `veto lessons` subcommand and returns its exit code (a promise only for `on`, which may ask a question). */
422
+ export function runLessonsCommand(args, options = {}) {
423
+ const out = options.out ?? ((line = '') => console.log(line));
424
+ const c = options.color === false ? PLAIN : COLORS;
425
+ const [sub = 'status', ...rest] = args;
426
+ const { positional, flags } = parseArgs(rest);
427
+ const cwd = options.cwd ?? process.cwd();
428
+ const home = options.home;
429
+ switch (sub) {
430
+ case 'on': return on(out, c, home, options.io ?? terminalIo());
431
+ case 'status': return status(out, c, home);
432
+ case 'list': return list(flags, out, c, home);
433
+ case 'why': return why(positional[0], out, c, home);
434
+ case 'forget': return forget(positional[0], out, c);
435
+ case 'flows': return flows(out, c, home);
436
+ case 'off': return off(out, c);
437
+ case 'exclude':
438
+ case 'include': return excludeOrInclude(sub, positional[0] ?? cwd, out, c);
439
+ case 'alias': return alias(positional, flags, out, c, home);
440
+ case 'recheck': return recheck(positional[0], out, c, home);
441
+ default:
442
+ out(c.red(` Unknown lessons subcommand: ${sub}`));
443
+ for (const line of USAGE)
444
+ out(c.dim(` ${line}`));
445
+ return 1;
446
+ }
447
+ }
448
+ //# sourceMappingURL=lessons.js.map