@mmnto/cli 1.5.1 → 1.5.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 (181) hide show
  1. package/dist/commands/add-lesson.d.ts.map +1 -1
  2. package/dist/commands/add-lesson.js +33 -18
  3. package/dist/commands/add-lesson.js.map +1 -1
  4. package/dist/commands/add-lesson.test.d.ts +2 -0
  5. package/dist/commands/add-lesson.test.d.ts.map +1 -0
  6. package/dist/commands/add-lesson.test.js +63 -0
  7. package/dist/commands/add-lesson.test.js.map +1 -0
  8. package/dist/commands/add-secret.d.ts +5 -0
  9. package/dist/commands/add-secret.d.ts.map +1 -0
  10. package/dist/commands/add-secret.js +85 -0
  11. package/dist/commands/add-secret.js.map +1 -0
  12. package/dist/commands/add-secret.test.d.ts +2 -0
  13. package/dist/commands/add-secret.test.d.ts.map +1 -0
  14. package/dist/commands/add-secret.test.js +97 -0
  15. package/dist/commands/add-secret.test.js.map +1 -0
  16. package/dist/commands/audit.d.ts.map +1 -1
  17. package/dist/commands/audit.js +4 -4
  18. package/dist/commands/audit.js.map +1 -1
  19. package/dist/commands/bridge.d.ts +1 -1
  20. package/dist/commands/bridge.d.ts.map +1 -1
  21. package/dist/commands/bridge.js +7 -7
  22. package/dist/commands/bridge.js.map +1 -1
  23. package/dist/commands/briefing.d.ts.map +1 -1
  24. package/dist/commands/briefing.js +8 -6
  25. package/dist/commands/briefing.js.map +1 -1
  26. package/dist/commands/compile-templates.d.ts +1 -1
  27. package/dist/commands/compile-templates.d.ts.map +1 -1
  28. package/dist/commands/compile-templates.js +4 -3
  29. package/dist/commands/compile-templates.js.map +1 -1
  30. package/dist/commands/compile.d.ts.map +1 -1
  31. package/dist/commands/compile.js +2 -2
  32. package/dist/commands/compile.js.map +1 -1
  33. package/dist/commands/docs.d.ts.map +1 -1
  34. package/dist/commands/docs.js +22 -14
  35. package/dist/commands/docs.js.map +1 -1
  36. package/dist/commands/doctor.d.ts +1 -0
  37. package/dist/commands/doctor.d.ts.map +1 -1
  38. package/dist/commands/doctor.js +36 -1
  39. package/dist/commands/doctor.js.map +1 -1
  40. package/dist/commands/doctor.test.js +86 -2
  41. package/dist/commands/doctor.test.js.map +1 -1
  42. package/dist/commands/drift.d.ts.map +1 -1
  43. package/dist/commands/drift.js +3 -3
  44. package/dist/commands/drift.js.map +1 -1
  45. package/dist/commands/eject.d.ts.map +1 -1
  46. package/dist/commands/eject.js +3 -3
  47. package/dist/commands/eject.js.map +1 -1
  48. package/dist/commands/explain.d.ts.map +1 -1
  49. package/dist/commands/explain.js +4 -4
  50. package/dist/commands/explain.js.map +1 -1
  51. package/dist/commands/extract.d.ts.map +1 -1
  52. package/dist/commands/extract.js +8 -5
  53. package/dist/commands/extract.js.map +1 -1
  54. package/dist/commands/extract.test.js +53 -0
  55. package/dist/commands/extract.test.js.map +1 -1
  56. package/dist/commands/handoff.d.ts.map +1 -1
  57. package/dist/commands/handoff.js +4 -3
  58. package/dist/commands/handoff.js.map +1 -1
  59. package/dist/commands/init.d.ts +1 -2
  60. package/dist/commands/init.d.ts.map +1 -1
  61. package/dist/commands/init.js +24 -6
  62. package/dist/commands/init.js.map +1 -1
  63. package/dist/commands/install-hooks.d.ts.map +1 -1
  64. package/dist/commands/install-hooks.js +15 -0
  65. package/dist/commands/install-hooks.js.map +1 -1
  66. package/dist/commands/install-hooks.test.js +20 -0
  67. package/dist/commands/install-hooks.test.js.map +1 -1
  68. package/dist/commands/link.d.ts.map +1 -1
  69. package/dist/commands/link.js +1 -1
  70. package/dist/commands/link.js.map +1 -1
  71. package/dist/commands/lint.d.ts.map +1 -1
  72. package/dist/commands/lint.js +22 -1
  73. package/dist/commands/lint.js.map +1 -1
  74. package/dist/commands/lint.test.d.ts +2 -0
  75. package/dist/commands/lint.test.d.ts.map +1 -0
  76. package/dist/commands/lint.test.js +107 -0
  77. package/dist/commands/lint.test.js.map +1 -0
  78. package/dist/commands/list-secrets.d.ts +15 -0
  79. package/dist/commands/list-secrets.d.ts.map +1 -0
  80. package/dist/commands/list-secrets.js +104 -0
  81. package/dist/commands/list-secrets.js.map +1 -0
  82. package/dist/commands/list-secrets.test.d.ts +2 -0
  83. package/dist/commands/list-secrets.test.d.ts.map +1 -0
  84. package/dist/commands/list-secrets.test.js +85 -0
  85. package/dist/commands/list-secrets.test.js.map +1 -0
  86. package/dist/commands/list.d.ts.map +1 -1
  87. package/dist/commands/list.js +1 -1
  88. package/dist/commands/list.js.map +1 -1
  89. package/dist/commands/migrate-lessons.d.ts.map +1 -1
  90. package/dist/commands/migrate-lessons.js +6 -6
  91. package/dist/commands/migrate-lessons.js.map +1 -1
  92. package/dist/commands/remove-secret.d.ts +2 -0
  93. package/dist/commands/remove-secret.d.ts.map +1 -0
  94. package/dist/commands/remove-secret.js +53 -0
  95. package/dist/commands/remove-secret.js.map +1 -0
  96. package/dist/commands/remove-secret.test.d.ts +2 -0
  97. package/dist/commands/remove-secret.test.d.ts.map +1 -0
  98. package/dist/commands/remove-secret.test.js +85 -0
  99. package/dist/commands/remove-secret.test.js.map +1 -0
  100. package/dist/commands/review-learn-templates.d.ts +7 -0
  101. package/dist/commands/review-learn-templates.d.ts.map +1 -0
  102. package/dist/commands/review-learn-templates.js +36 -0
  103. package/dist/commands/review-learn-templates.js.map +1 -0
  104. package/dist/commands/review-learn-templates.test.d.ts +2 -0
  105. package/dist/commands/review-learn-templates.test.d.ts.map +1 -0
  106. package/dist/commands/review-learn-templates.test.js +29 -0
  107. package/dist/commands/review-learn-templates.test.js.map +1 -0
  108. package/dist/commands/review-learn.d.ts +13 -0
  109. package/dist/commands/review-learn.d.ts.map +1 -0
  110. package/dist/commands/review-learn.js +260 -0
  111. package/dist/commands/review-learn.js.map +1 -0
  112. package/dist/commands/review-learn.test.d.ts +2 -0
  113. package/dist/commands/review-learn.test.d.ts.map +1 -0
  114. package/dist/commands/review-learn.test.js +218 -0
  115. package/dist/commands/review-learn.test.js.map +1 -0
  116. package/dist/commands/run-compiled-rules.d.ts +2 -0
  117. package/dist/commands/run-compiled-rules.d.ts.map +1 -1
  118. package/dist/commands/run-compiled-rules.js +53 -8
  119. package/dist/commands/run-compiled-rules.js.map +1 -1
  120. package/dist/commands/run-compiled-rules.test.js +107 -1
  121. package/dist/commands/run-compiled-rules.test.js.map +1 -1
  122. package/dist/commands/search.d.ts.map +1 -1
  123. package/dist/commands/search.js +3 -3
  124. package/dist/commands/search.js.map +1 -1
  125. package/dist/commands/shield-classify.d.ts +10 -0
  126. package/dist/commands/shield-classify.d.ts.map +1 -0
  127. package/dist/commands/shield-classify.js +137 -0
  128. package/dist/commands/shield-classify.js.map +1 -0
  129. package/dist/commands/shield-classify.test.d.ts +2 -0
  130. package/dist/commands/shield-classify.test.d.ts.map +1 -0
  131. package/dist/commands/shield-classify.test.js +180 -0
  132. package/dist/commands/shield-classify.test.js.map +1 -0
  133. package/dist/commands/shield-hints.d.ts +16 -2
  134. package/dist/commands/shield-hints.d.ts.map +1 -1
  135. package/dist/commands/shield-hints.js +35 -20
  136. package/dist/commands/shield-hints.js.map +1 -1
  137. package/dist/commands/shield-hints.test.js +70 -1
  138. package/dist/commands/shield-hints.test.js.map +1 -1
  139. package/dist/commands/shield-templates.d.ts +66 -0
  140. package/dist/commands/shield-templates.d.ts.map +1 -1
  141. package/dist/commands/shield-templates.js +122 -0
  142. package/dist/commands/shield-templates.js.map +1 -1
  143. package/dist/commands/shield.d.ts +23 -4
  144. package/dist/commands/shield.d.ts.map +1 -1
  145. package/dist/commands/shield.js +232 -65
  146. package/dist/commands/shield.js.map +1 -1
  147. package/dist/commands/shield.test.js +225 -1
  148. package/dist/commands/shield.test.js.map +1 -1
  149. package/dist/commands/spec.d.ts +2 -3
  150. package/dist/commands/spec.d.ts.map +1 -1
  151. package/dist/commands/spec.js +11 -8
  152. package/dist/commands/spec.js.map +1 -1
  153. package/dist/commands/spec.test.js +18 -18
  154. package/dist/commands/spec.test.js.map +1 -1
  155. package/dist/commands/stats.d.ts.map +1 -1
  156. package/dist/commands/stats.js +3 -3
  157. package/dist/commands/stats.js.map +1 -1
  158. package/dist/commands/sync.d.ts.map +1 -1
  159. package/dist/commands/sync.js +14 -8
  160. package/dist/commands/sync.js.map +1 -1
  161. package/dist/commands/triage.d.ts.map +1 -1
  162. package/dist/commands/triage.js +5 -4
  163. package/dist/commands/triage.js.map +1 -1
  164. package/dist/commands/wrap.d.ts.map +1 -1
  165. package/dist/commands/wrap.js +1 -1
  166. package/dist/commands/wrap.js.map +1 -1
  167. package/dist/index.js +62 -3
  168. package/dist/index.js.map +1 -1
  169. package/dist/parsers/bot-review-parser.d.ts +48 -0
  170. package/dist/parsers/bot-review-parser.d.ts.map +1 -0
  171. package/dist/parsers/bot-review-parser.js +139 -0
  172. package/dist/parsers/bot-review-parser.js.map +1 -0
  173. package/dist/parsers/bot-review-parser.test.d.ts +2 -0
  174. package/dist/parsers/bot-review-parser.test.d.ts.map +1 -0
  175. package/dist/parsers/bot-review-parser.test.js +240 -0
  176. package/dist/parsers/bot-review-parser.test.js.map +1 -0
  177. package/dist/utils.d.ts +5 -1
  178. package/dist/utils.d.ts.map +1 -1
  179. package/dist/utils.js +4 -3
  180. package/dist/utils.js.map +1 -1
  181. package/package.json +2 -2
@@ -1,5 +1,5 @@
1
- import fs from 'node:fs';
2
1
  export async function listCommand() {
2
+ const fs = await import('node:fs');
3
3
  const { readRegistry } = await import('@mmnto/totem');
4
4
  const registry = readRegistry();
5
5
  const entries = Object.values(registry);
@@ -1 +1 @@
1
- {"version":3,"file":"list.js","sourceRoot":"","sources":["../../src/commands/list.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AAEzB,MAAM,CAAC,KAAK,UAAU,WAAW;IAC/B,MAAM,EAAE,YAAY,EAAE,GAAG,MAAM,MAAM,CAAC,cAAc,CAAC,CAAC;IAEtD,MAAM,QAAQ,GAAG,YAAY,EAAE,CAAC;IAChC,MAAM,OAAO,GAAG,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;IAExC,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACzB,OAAO,CAAC,KAAK,CACX,iFAAiF,CAClF,CAAC;QACF,OAAO;IACT,CAAC;IAED,8BAA8B;IAC9B,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,OAAO,EAAE,GAAG,IAAI,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC;IAExF,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IACvB,MAAM,QAAQ,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;IAE1C,OAAO,CAAC,KAAK,CAAC,kCAAkC,CAAC,CAAC;IAElD,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;QAC5B,MAAM,GAAG,GAAG,GAAG,GAAG,IAAI,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,OAAO,EAAE,CAAC;QACrD,MAAM,OAAO,GAAG,kBAAkB,CAAC,GAAG,CAAC,CAAC;QACxC,MAAM,MAAM,GAAG,EAAE,CAAC,UAAU,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QACzC,MAAM,KAAK,GAAG,GAAG,GAAG,QAAQ,CAAC;QAE7B,MAAM,KAAK,GAAa,EAAE,CAAC;QAC3B,IAAI,CAAC,MAAM;YAAE,KAAK,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;QACrC,IAAI,KAAK;YAAE,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QAEjC,MAAM,OAAO,GAAG,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,GAAG,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QAE9D,OAAO,CAAC,KAAK,CAAC,KAAK,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC;QACjC,OAAO,CAAC,KAAK,CACX,eAAe,KAAK,CAAC,UAAU,gBAAgB,OAAO,kBAAkB,KAAK,CAAC,QAAQ,GAAG,OAAO,EAAE,CACnG,CAAC;QACF,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IACpB,CAAC;AACH,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,EAAU;IAC3C,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,IAAI,CAAC,CAAC;IACnD,IAAI,OAAO,GAAG,EAAE;QAAE,OAAO,GAAG,OAAO,OAAO,CAAC;IAC3C,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,GAAG,EAAE,CAAC,CAAC;IACzC,IAAI,OAAO,GAAG,EAAE;QAAE,OAAO,GAAG,OAAO,OAAO,CAAC;IAC3C,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,GAAG,EAAE,CAAC,CAAC;IACvC,IAAI,KAAK,GAAG,EAAE;QAAE,OAAO,GAAG,KAAK,OAAO,CAAC;IACvC,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,EAAE,CAAC,CAAC;IACpC,OAAO,GAAG,IAAI,OAAO,CAAC;AACxB,CAAC"}
1
+ {"version":3,"file":"list.js","sourceRoot":"","sources":["../../src/commands/list.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"migrate-lessons.d.ts","sourceRoot":"","sources":["../../src/commands/migrate-lessons.ts"],"names":[],"mappings":"AAWA;;;;;;;;;GASG;AACH,wBAAsB,qBAAqB,IAAI,OAAO,CAAC,IAAI,CAAC,CA4E3D"}
1
+ {"version":3,"file":"migrate-lessons.d.ts","sourceRoot":"","sources":["../../src/commands/migrate-lessons.ts"],"names":[],"mappings":"AAEA;;;;;;;;;GASG;AACH,wBAAsB,qBAAqB,IAAI,OAAO,CAAC,IAAI,CAAC,CAmF3D"}
@@ -1,9 +1,3 @@
1
- import * as fs from 'node:fs'; // totem-ignore
2
- import * as path from 'node:path'; // totem-ignore
3
- import { parseLessonsFile, writeLessonFile } from '@mmnto/totem'; // totem-ignore
4
- import { BASELINE_MARKER } from '../assets/universal-lessons.js'; // totem-ignore
5
- import { log } from '../ui.js'; // totem-ignore
6
- import { loadConfig, resolveConfigPath } from '../utils.js'; // totem-ignore
7
1
  const TAG = 'Migrate';
8
2
  /**
9
3
  * Migrate from `.totem/lessons.md` (single file) to `.totem/lessons/` (directory of discrete files).
@@ -16,6 +10,12 @@ const TAG = 'Migrate';
16
10
  * 6. Log summary
17
11
  */
18
12
  export async function migrateLessonsCommand() {
13
+ const fs = await import('node:fs'); // totem-ignore
14
+ const path = await import('node:path'); // totem-ignore
15
+ const { parseLessonsFile, writeLessonFile } = await import('@mmnto/totem'); // totem-ignore
16
+ const { BASELINE_MARKER } = await import('../assets/universal-lessons.js'); // totem-ignore
17
+ const { log } = await import('../ui.js'); // totem-ignore
18
+ const { loadConfig, resolveConfigPath } = await import('../utils.js'); // totem-ignore
19
19
  const cwd = process.cwd();
20
20
  const configPath = resolveConfigPath(cwd);
21
21
  const config = await loadConfig(configPath);
@@ -1 +1 @@
1
- {"version":3,"file":"migrate-lessons.js","sourceRoot":"","sources":["../../src/commands/migrate-lessons.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"migrate-lessons.js","sourceRoot":"","sources":["../../src/commands/migrate-lessons.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export declare function removeSecretCommand(indexStr: string, cwd?: string, totemDir?: string): Promise<void>;
2
+ //# sourceMappingURL=remove-secret.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"remove-secret.d.ts","sourceRoot":"","sources":["../../src/commands/remove-secret.ts"],"names":[],"mappings":"AAYA,wBAAsB,mBAAmB,CACvC,QAAQ,EAAE,MAAM,EAChB,GAAG,SAAgB,EACnB,QAAQ,SAAW,GAClB,OAAO,CAAC,IAAI,CAAC,CA+Df"}
@@ -0,0 +1,53 @@
1
+ import * as fs from 'node:fs';
2
+ import * as path from 'node:path';
3
+ // ─── Constants ──────────────────────────────────────────
4
+ const TAG = 'RemoveSecret';
5
+ const SECRETS_REL_PATH = '.totem/secrets.json';
6
+ // ─── Main ───────────────────────────────────────────────
7
+ export async function removeSecretCommand(indexStr, cwd = process.cwd(), totemDir = '.totem') {
8
+ const { log } = await import('../ui.js');
9
+ const { buildSecretEntries } = await import('./list-secrets.js');
10
+ // 1. Build the full list to resolve index → source (silent, no output)
11
+ const entries = await buildSecretEntries(cwd, totemDir);
12
+ const index = parseInt(indexStr, 10);
13
+ if (isNaN(index) || index < 1 || index > entries.length) {
14
+ log.error('Totem Error', `Index ${indexStr} is out of range. Valid range: 1–${entries.length || 0}.`);
15
+ process.exit(1);
16
+ }
17
+ const target = entries[index - 1];
18
+ // 2. Reject shared/yaml secrets
19
+ if (target.source === 'shared/yaml') {
20
+ log.error('Totem Error', 'Cannot remove shared secrets from CLI. Edit your totem.config.yaml directly.');
21
+ process.exit(1);
22
+ }
23
+ // 3. Read secrets.json
24
+ const secretsPath = path.join(cwd, SECRETS_REL_PATH);
25
+ let data = { secrets: [] };
26
+ try {
27
+ const content = fs.readFileSync(secretsPath, 'utf-8');
28
+ data = JSON.parse(content);
29
+ if (!Array.isArray(data.secrets)) {
30
+ data.secrets = [];
31
+ }
32
+ }
33
+ catch {
34
+ log.error('Totem Error', `Failed to read ${SECRETS_REL_PATH}.`);
35
+ process.exit(1);
36
+ }
37
+ // 4. Compute the local index within secrets.json
38
+ // The entry's position among local/json entries maps to the JSON array.
39
+ // Count how many shared/yaml entries come before this entry.
40
+ const yamlCount = entries.filter((e) => e.source === 'shared/yaml').length;
41
+ const localIndex = index - 1 - yamlCount;
42
+ if (localIndex < 0 || localIndex >= data.secrets.length) {
43
+ log.error('Totem Error', `Index ${indexStr} is out of range for local secrets.`);
44
+ process.exit(1);
45
+ }
46
+ // 5. Remove
47
+ const [removed] = data.secrets.splice(localIndex, 1);
48
+ // 6. Write back
49
+ fs.writeFileSync(secretsPath, JSON.stringify(data, null, 2) + '\n', 'utf-8');
50
+ // 7. Confirm
51
+ log.success(TAG, `Removed ${removed.type} secret: "${removed.type === 'literal' ? removed.value.slice(0, 4) + '***' : removed.value}"`);
52
+ }
53
+ //# sourceMappingURL=remove-secret.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"remove-secret.js","sourceRoot":"","sources":["../../src/commands/remove-secret.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=remove-secret.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"remove-secret.test.d.ts","sourceRoot":"","sources":["../../src/commands/remove-secret.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,85 @@
1
+ import * as fs from 'node:fs';
2
+ import * as os from 'node:os';
3
+ import * as path from 'node:path';
4
+ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
5
+ import { removeSecretCommand } from './remove-secret.js';
6
+ // ─── Helpers ────────────────────────────────────────────
7
+ let tmpDirs = [];
8
+ function makeTmpDir() {
9
+ const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'totem-remove-secret-'));
10
+ tmpDirs.push(dir);
11
+ return dir;
12
+ }
13
+ function writeSecretsJson(cwd, totemDir, data) {
14
+ const dirPath = path.join(cwd, totemDir);
15
+ fs.mkdirSync(dirPath, { recursive: true });
16
+ fs.writeFileSync(path.join(dirPath, 'secrets.json'), JSON.stringify(data, null, 2) + '\n', 'utf-8');
17
+ }
18
+ function readSecretsJson(cwd, totemDir) {
19
+ const content = fs.readFileSync(path.join(cwd, totemDir, 'secrets.json'), 'utf-8');
20
+ return JSON.parse(content);
21
+ }
22
+ // ─── Tests ──────────────────────────────────────────────
23
+ describe('removeSecretCommand', () => {
24
+ let stderrSpy;
25
+ let exitSpy;
26
+ beforeEach(() => {
27
+ stderrSpy = vi.spyOn(console, 'error').mockImplementation(() => { });
28
+ exitSpy = vi.spyOn(process, 'exit').mockImplementation(() => {
29
+ throw new Error('process.exit called');
30
+ });
31
+ });
32
+ afterEach(() => {
33
+ stderrSpy.mockRestore();
34
+ exitSpy.mockRestore();
35
+ for (const dir of tmpDirs) {
36
+ fs.rmSync(dir, { recursive: true, force: true });
37
+ }
38
+ tmpDirs = [];
39
+ });
40
+ it('removes entry at specified index', async () => {
41
+ const cwd = makeTmpDir();
42
+ const totemDir = '.totem';
43
+ writeSecretsJson(cwd, totemDir, {
44
+ secrets: [
45
+ { type: 'literal', value: 'first-secret' },
46
+ { type: 'pattern', value: 'PATTERN-\\d+' },
47
+ { type: 'literal', value: 'third-secret' },
48
+ ],
49
+ });
50
+ // Remove the second entry (index 2, which is the second local/json secret)
51
+ await removeSecretCommand('2', cwd, totemDir);
52
+ const updated = readSecretsJson(cwd, totemDir);
53
+ expect(updated.secrets).toHaveLength(2);
54
+ expect(updated.secrets[0].value).toBe('first-secret');
55
+ expect(updated.secrets[1].value).toBe('third-secret');
56
+ const output = stderrSpy.mock.calls.map((c) => c[0]).join('\n');
57
+ expect(output).toContain('Removed');
58
+ });
59
+ it('rejects out-of-range index', async () => {
60
+ const cwd = makeTmpDir();
61
+ const totemDir = '.totem';
62
+ writeSecretsJson(cwd, totemDir, {
63
+ secrets: [{ type: 'literal', value: 'only-secret' }],
64
+ });
65
+ await expect(removeSecretCommand('5', cwd, totemDir)).rejects.toThrow('process.exit called');
66
+ const output = stderrSpy.mock.calls.map((c) => c[0]).join('\n');
67
+ expect(output).toContain('out of range');
68
+ });
69
+ it('rejects removal of shared yaml secrets', async () => {
70
+ const cwd = makeTmpDir();
71
+ const totemDir = '.totem';
72
+ // Write a YAML config with a shared secret
73
+ fs.writeFileSync(path.join(cwd, 'totem.yaml'), 'secrets:\n - type: literal\n value: "yaml-secret-val"\n', 'utf-8');
74
+ // Write local secrets.json
75
+ writeSecretsJson(cwd, totemDir, {
76
+ secrets: [{ type: 'literal', value: 'local-secret' }],
77
+ });
78
+ // Index 1 should be the yaml secret
79
+ await expect(removeSecretCommand('1', cwd, totemDir)).rejects.toThrow('process.exit called');
80
+ const output = stderrSpy.mock.calls.map((c) => c[0]).join('\n');
81
+ expect(output).toContain('Cannot remove shared secrets from CLI');
82
+ expect(output).toContain('totem.config.yaml');
83
+ });
84
+ });
85
+ //# sourceMappingURL=remove-secret.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"remove-secret.test.js","sourceRoot":"","sources":["../../src/commands/remove-secret.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ /** Maximum existing lessons to include for dedup context */
2
+ export declare const MAX_EXISTING_LESSONS = 10;
3
+ /** Maximum assembled prompt size in characters */
4
+ export declare const MAX_PROMPT_CHARS = 100000;
5
+ /** System prompt for review-learn — instructs LLM to extract lessons from resolved bot findings */
6
+ export declare const REVIEW_LEARN_SYSTEM_PROMPT = "You are Totem's lesson extractor for bot code review findings.\n\nYou receive a set of code review findings from automated bots (CodeRabbit, Gemini Code Assist) that were RESOLVED (the developer accepted and fixed them). Your job is to extract reusable architectural lessons from these findings.\n\nRULES:\n1. Only extract lessons that represent reusable patterns \u2014 NOT one-off fixes.\n2. Each lesson must be actionable: what the symptom is, what the fix is, and why it matters.\n3. Every lesson MUST include lifecycle: nursery in its metadata \u2014 these are unproven until validated.\n4. Deduplicate against the provided existing lessons. Do NOT repeat known patterns.\n5. Focus on architectural and security findings. Skip pure style/formatting nits unless they represent a real pattern.\n6. If no findings warrant a lesson, return an empty array.\n\nOUTPUT FORMAT:\nReturn a JSON array of lesson objects. Each lesson has:\n- \"tags\": string[] \u2014 relevant tags (e.g., [\"security\", \"typescript\", \"architecture\"])\n- \"text\": string \u2014 the lesson body. Start with the symptom/pattern, then the fix.\n- \"lifecycle\": \"nursery\" \u2014 REQUIRED, always \"nursery\"\n\nExample:\n[\n {\n \"tags\": [\"security\", \"shell\"],\n \"text\": \"Using execSync with string interpolation for shell commands creates injection risk. Use spawnSync with an args array to pass arguments safely without shell interpretation.\",\n \"lifecycle\": \"nursery\"\n }\n]\n\nIf no lessons should be extracted, return: []";
7
+ //# sourceMappingURL=review-learn-templates.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"review-learn-templates.d.ts","sourceRoot":"","sources":["../../src/commands/review-learn-templates.ts"],"names":[],"mappings":"AAEA,4DAA4D;AAC5D,eAAO,MAAM,oBAAoB,KAAK,CAAC;AAEvC,kDAAkD;AAClD,eAAO,MAAM,gBAAgB,SAAU,CAAC;AAIxC,mGAAmG;AACnG,eAAO,MAAM,0BAA0B,+/CA2BO,CAAC"}
@@ -0,0 +1,36 @@
1
+ // ─── Constants ──────────────────────────────────────────
2
+ /** Maximum existing lessons to include for dedup context */
3
+ export const MAX_EXISTING_LESSONS = 10;
4
+ /** Maximum assembled prompt size in characters */
5
+ export const MAX_PROMPT_CHARS = 100_000;
6
+ // ─── System prompt ──────────────────────────────────────
7
+ /** System prompt for review-learn — instructs LLM to extract lessons from resolved bot findings */
8
+ export const REVIEW_LEARN_SYSTEM_PROMPT = `You are Totem's lesson extractor for bot code review findings.
9
+
10
+ You receive a set of code review findings from automated bots (CodeRabbit, Gemini Code Assist) that were RESOLVED (the developer accepted and fixed them). Your job is to extract reusable architectural lessons from these findings.
11
+
12
+ RULES:
13
+ 1. Only extract lessons that represent reusable patterns — NOT one-off fixes.
14
+ 2. Each lesson must be actionable: what the symptom is, what the fix is, and why it matters.
15
+ 3. Every lesson MUST include lifecycle: nursery in its metadata — these are unproven until validated.
16
+ 4. Deduplicate against the provided existing lessons. Do NOT repeat known patterns.
17
+ 5. Focus on architectural and security findings. Skip pure style/formatting nits unless they represent a real pattern.
18
+ 6. If no findings warrant a lesson, return an empty array.
19
+
20
+ OUTPUT FORMAT:
21
+ Return a JSON array of lesson objects. Each lesson has:
22
+ - "tags": string[] — relevant tags (e.g., ["security", "typescript", "architecture"])
23
+ - "text": string — the lesson body. Start with the symptom/pattern, then the fix.
24
+ - "lifecycle": "nursery" — REQUIRED, always "nursery"
25
+
26
+ Example:
27
+ [
28
+ {
29
+ "tags": ["security", "shell"],
30
+ "text": "Using execSync with string interpolation for shell commands creates injection risk. Use spawnSync with an args array to pass arguments safely without shell interpretation.",
31
+ "lifecycle": "nursery"
32
+ }
33
+ ]
34
+
35
+ If no lessons should be extracted, return: []`;
36
+ //# sourceMappingURL=review-learn-templates.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"review-learn-templates.js","sourceRoot":"","sources":["../../src/commands/review-learn-templates.ts"],"names":[],"mappings":"AAAA,2DAA2D;AAE3D,4DAA4D;AAC5D,MAAM,CAAC,MAAM,oBAAoB,GAAG,EAAE,CAAC;AAEvC,kDAAkD;AAClD,MAAM,CAAC,MAAM,gBAAgB,GAAG,OAAO,CAAC;AAExC,2DAA2D;AAE3D,mGAAmG;AACnG,MAAM,CAAC,MAAM,0BAA0B,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;8CA2BI,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=review-learn-templates.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"review-learn-templates.test.d.ts","sourceRoot":"","sources":["../../src/commands/review-learn-templates.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,29 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { MAX_EXISTING_LESSONS, MAX_PROMPT_CHARS, REVIEW_LEARN_SYSTEM_PROMPT, } from './review-learn-templates.js';
3
+ describe('REVIEW_LEARN_SYSTEM_PROMPT', () => {
4
+ it('includes lifecycle nursery instruction', () => {
5
+ expect(REVIEW_LEARN_SYSTEM_PROMPT).toContain('lifecycle: nursery');
6
+ expect(REVIEW_LEARN_SYSTEM_PROMPT).toContain('"lifecycle": "nursery"');
7
+ });
8
+ it('includes dedup instruction', () => {
9
+ expect(REVIEW_LEARN_SYSTEM_PROMPT).toContain('Deduplicate');
10
+ expect(REVIEW_LEARN_SYSTEM_PROMPT).toContain('Do NOT repeat known patterns');
11
+ });
12
+ it('instructs to return empty array when no lessons', () => {
13
+ expect(REVIEW_LEARN_SYSTEM_PROMPT).toContain('return an empty array');
14
+ expect(REVIEW_LEARN_SYSTEM_PROMPT).toContain('return: []');
15
+ });
16
+ it('mentions CodeRabbit and Gemini Code Assist', () => {
17
+ expect(REVIEW_LEARN_SYSTEM_PROMPT).toContain('CodeRabbit');
18
+ expect(REVIEW_LEARN_SYSTEM_PROMPT).toContain('Gemini Code Assist');
19
+ });
20
+ });
21
+ describe('constants', () => {
22
+ it('MAX_EXISTING_LESSONS is a positive number', () => {
23
+ expect(MAX_EXISTING_LESSONS).toBeGreaterThan(0);
24
+ });
25
+ it('MAX_PROMPT_CHARS is a positive number', () => {
26
+ expect(MAX_PROMPT_CHARS).toBeGreaterThan(0);
27
+ });
28
+ });
29
+ //# sourceMappingURL=review-learn-templates.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"review-learn-templates.test.js","sourceRoot":"","sources":["../../src/commands/review-learn-templates.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAE9C,OAAO,EACL,oBAAoB,EACpB,gBAAgB,EAChB,0BAA0B,GAC3B,MAAM,6BAA6B,CAAC;AAErC,QAAQ,CAAC,4BAA4B,EAAE,GAAG,EAAE;IAC1C,EAAE,CAAC,wCAAwC,EAAE,GAAG,EAAE;QAChD,MAAM,CAAC,0BAA0B,CAAC,CAAC,SAAS,CAAC,oBAAoB,CAAC,CAAC;QACnE,MAAM,CAAC,0BAA0B,CAAC,CAAC,SAAS,CAAC,wBAAwB,CAAC,CAAC;IACzE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,4BAA4B,EAAE,GAAG,EAAE;QACpC,MAAM,CAAC,0BAA0B,CAAC,CAAC,SAAS,CAAC,aAAa,CAAC,CAAC;QAC5D,MAAM,CAAC,0BAA0B,CAAC,CAAC,SAAS,CAAC,8BAA8B,CAAC,CAAC;IAC/E,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,iDAAiD,EAAE,GAAG,EAAE;QACzD,MAAM,CAAC,0BAA0B,CAAC,CAAC,SAAS,CAAC,uBAAuB,CAAC,CAAC;QACtE,MAAM,CAAC,0BAA0B,CAAC,CAAC,SAAS,CAAC,YAAY,CAAC,CAAC;IAC7D,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,4CAA4C,EAAE,GAAG,EAAE;QACpD,MAAM,CAAC,0BAA0B,CAAC,CAAC,SAAS,CAAC,YAAY,CAAC,CAAC;QAC3D,MAAM,CAAC,0BAA0B,CAAC,CAAC,SAAS,CAAC,oBAAoB,CAAC,CAAC;IACrE,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,WAAW,EAAE,GAAG,EAAE;IACzB,EAAE,CAAC,2CAA2C,EAAE,GAAG,EAAE;QACnD,MAAM,CAAC,oBAAoB,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC;IAClD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,uCAAuC,EAAE,GAAG,EAAE;QAC/C,MAAM,CAAC,gBAAgB,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC;IAC9C,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -0,0 +1,13 @@
1
+ import type { SearchResult } from '@mmnto/totem';
2
+ import type { NormalizedBotFinding } from '../parsers/bot-review-parser.js';
3
+ export declare function assembleReviewLearnPrompt(findings: NormalizedBotFinding[], existingLessons: SearchResult[], systemPrompt: string): string;
4
+ export interface ReviewLearnOptions {
5
+ raw?: boolean;
6
+ out?: string;
7
+ model?: string;
8
+ fresh?: boolean;
9
+ dryRun?: boolean;
10
+ yes?: boolean;
11
+ }
12
+ export declare function reviewLearnCommand(prNumber: string, options: ReviewLearnOptions): Promise<void>;
13
+ //# sourceMappingURL=review-learn.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"review-learn.d.ts","sourceRoot":"","sources":["../../src/commands/review-learn.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAmB,YAAY,EAAE,MAAM,cAAc,CAAC;AAelE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,iCAAiC,CAAC;AAyE5E,wBAAgB,yBAAyB,CACvC,QAAQ,EAAE,oBAAoB,EAAE,EAChC,eAAe,EAAE,YAAY,EAAE,EAC/B,YAAY,EAAE,MAAM,GACnB,MAAM,CA4BR;AA6BD,MAAM,WAAW,kBAAkB;IACjC,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,GAAG,CAAC,EAAE,OAAO,CAAC;CACf;AAED,wBAAsB,kBAAkB,CACtC,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,kBAAkB,GAC1B,OAAO,CAAC,IAAI,CAAC,CAwOf"}
@@ -0,0 +1,260 @@
1
+ import { createEmbedder, deduplicateLessons, flagSuspiciousLessons, generateLessonHeading, LanceStore, loadCustomSecrets, runSync, TotemConfigError, truncateHeading, writeLessonFile, } from '@mmnto/totem';
2
+ import { log } from '../ui.js';
3
+ import { formatResults, getSystemPrompt, loadConfig, loadEnv, requireEmbedding, resolveConfigPath, runOrchestrator, sanitize, wrapUntrustedXml, } from '../utils.js';
4
+ // Reuse parsing and selection helpers from extract (they are exported)
5
+ import { parseLessons, selectLessons } from './extract.js';
6
+ import { MAX_EXISTING_LESSONS, MAX_PROMPT_CHARS, REVIEW_LEARN_SYSTEM_PROMPT, } from './review-learn-templates.js';
7
+ const TAG = 'ReviewLearn';
8
+ function groupIntoThreads(comments) {
9
+ const byId = new Map();
10
+ for (const c of comments)
11
+ byId.set(c.id, c);
12
+ const threadMap = new Map();
13
+ for (const c of comments) {
14
+ const rootId = c.inReplyToId ?? c.id;
15
+ const thread = threadMap.get(rootId) ?? [];
16
+ thread.push(c);
17
+ threadMap.set(rootId, thread);
18
+ }
19
+ const threads = [];
20
+ for (const [rootId, threadComments] of threadMap) {
21
+ threadComments.sort((a, b) => {
22
+ if (!a.createdAt || !b.createdAt)
23
+ return 0;
24
+ return a.createdAt.localeCompare(b.createdAt);
25
+ });
26
+ const root = byId.get(rootId) ?? threadComments[0];
27
+ threads.push({
28
+ path: root.path,
29
+ diffHunk: root.diffHunk,
30
+ comments: threadComments.map((c) => ({ author: c.author, body: c.body })),
31
+ });
32
+ }
33
+ return threads;
34
+ }
35
+ // ─── LanceDB retrieval ───────────────────────────────
36
+ async function retrieveExistingLessons(store) {
37
+ return store.search({
38
+ query: 'lesson trap pattern decision',
39
+ typeFilter: 'spec',
40
+ maxResults: MAX_EXISTING_LESSONS,
41
+ });
42
+ }
43
+ // ─── Prompt assembly ─────────────────────────────────
44
+ export function assembleReviewLearnPrompt(findings, existingLessons, systemPrompt) {
45
+ const sections = [systemPrompt];
46
+ sections.push('\n=== RESOLVED BOT FINDINGS ===');
47
+ for (let i = 0; i < findings.length; i++) {
48
+ const f = findings[i];
49
+ sections.push(`\n--- Finding ${i + 1} [${f.tool}/${f.severity}] ${sanitize(f.file)} ---`);
50
+ sections.push(wrapUntrustedXml('finding_body', f.body));
51
+ if (f.suggestion) {
52
+ sections.push('Suggestion:');
53
+ sections.push(wrapUntrustedXml('suggestion', f.suggestion));
54
+ }
55
+ }
56
+ // Existing lessons for dedup context
57
+ const lessonSection = formatResults(existingLessons, 'EXISTING LESSONS (do NOT duplicate)');
58
+ if (lessonSection) {
59
+ sections.push('\n=== DEDUP CONTEXT ===');
60
+ sections.push(lessonSection);
61
+ }
62
+ // Truncate if needed
63
+ let prompt = sections.join('\n');
64
+ if (prompt.length > MAX_PROMPT_CHARS) {
65
+ prompt = prompt.slice(0, MAX_PROMPT_CHARS) + '\n\n... [content truncated] ...';
66
+ }
67
+ return prompt;
68
+ }
69
+ // ─── Nursery lesson writer ───────────────────────────
70
+ /**
71
+ * Append lessons with `lifecycle: nursery` YAML frontmatter.
72
+ * Wraps each lesson entry with frontmatter before writing.
73
+ */
74
+ function appendNurseryLessons(lessons, lessonsDir) {
75
+ for (const l of lessons) {
76
+ const heading = l.heading || generateLessonHeading(l.text);
77
+ const tags = l.tags;
78
+ // Build YAML frontmatter with lifecycle: nursery
79
+ const frontmatter = [
80
+ '---',
81
+ `tags: [${tags.map((t) => `"${t}"`).join(', ')}]`,
82
+ 'lifecycle: nursery',
83
+ '---',
84
+ ].join('\n');
85
+ const body = `## Lesson — ${truncateHeading(heading) || 'Lesson'}\n\n**Tags:** ${tags.join(', ')}\n\n${l.text}\n`;
86
+ const entry = `${frontmatter}\n\n${body}`;
87
+ writeLessonFile(lessonsDir, entry);
88
+ }
89
+ }
90
+ export async function reviewLearnCommand(prNumber, options) {
91
+ const path = await import('node:path');
92
+ const { GitHubCliPrAdapter } = await import('../adapters/github-cli-pr.js');
93
+ const { isBotComment, extractResolvedBotFindings } = await import('../parsers/bot-review-parser.js');
94
+ // 1. Parse and validate PR number
95
+ const num = parseInt(prNumber, 10);
96
+ if (isNaN(num) || num <= 0 || String(num) !== prNumber) {
97
+ throw new TotemConfigError(`Invalid PR number: '${prNumber}'. Must be a positive integer.`, 'Pass a numeric PR number, e.g. `totem review-learn 123`.', 'CONFIG_INVALID');
98
+ }
99
+ // 2. Load config, env, connect to LanceDB for dedup
100
+ const cwd = process.cwd();
101
+ const configPath = resolveConfigPath(cwd);
102
+ loadEnv(cwd);
103
+ const config = await loadConfig(configPath);
104
+ // Load user-defined custom secrets for DLP (#921)
105
+ const customSecrets = loadCustomSecrets(cwd, config.totemDir, (msg) => log.warn(TAG, msg));
106
+ // Connect to LanceDB for dedup context
107
+ const embedding = requireEmbedding(config);
108
+ const embedder = createEmbedder(embedding);
109
+ const store = new LanceStore(path.join(cwd, config.lanceDir), embedder);
110
+ await store.connect();
111
+ log.info(TAG, 'Querying existing lessons for dedup...');
112
+ const existingLessons = await retrieveExistingLessons(store);
113
+ log.info(TAG, `Found ${existingLessons.length} existing lessons for context`);
114
+ // 3. Fetch PR — error if not MERGED/CLOSED
115
+ log.info(TAG, `Fetching PR #${num}...`);
116
+ const adapter = new GitHubCliPrAdapter(cwd);
117
+ const pr = adapter.fetchPr(num);
118
+ log.info(TAG, `Title: ${pr.title}`);
119
+ const prState = pr.state.toUpperCase();
120
+ if (prState !== 'MERGED' && prState !== 'CLOSED') {
121
+ throw new TotemConfigError(`PR #${num} is ${pr.state}. review-learn only works on merged or closed PRs.`, 'Wait for the PR to be merged, or use `totem extract` for open PRs.', 'CONFIG_INVALID');
122
+ }
123
+ // 4. Fetch review comments
124
+ log.info(TAG, 'Fetching review comments...');
125
+ const reviewComments = adapter.fetchReviewComments(num);
126
+ log.info(TAG, `Found ${reviewComments.length} inline review comments`);
127
+ // 5. Group ALL comments into threads first (need human replies for resolution detection)
128
+ const allThreads = groupIntoThreads(reviewComments);
129
+ // 6. Filter to threads that START with a bot comment
130
+ const threads = allThreads.filter((t) => t.comments.length > 0 && isBotComment(t.comments[0].author));
131
+ if (threads.length === 0) {
132
+ log.dim(TAG, 'No bot review comments found. Nothing to learn from.');
133
+ return;
134
+ }
135
+ log.info(TAG, `Found ${threads.length} bot review thread(s)`);
136
+ // 7. Apply resolution filter
137
+ const findings = extractResolvedBotFindings(threads);
138
+ if (findings.length === 0) {
139
+ log.dim(TAG, 'No resolved bot findings found. Only fixed findings produce lessons.');
140
+ return;
141
+ }
142
+ log.info(TAG, `Found ${findings.length} resolved bot finding(s)`);
143
+ // 8. Resolve system prompt (allow .totem/prompts/review-learn.md override)
144
+ const systemPrompt = getSystemPrompt('review-learn', REVIEW_LEARN_SYSTEM_PROMPT, cwd, config.totemDir);
145
+ // 9. Assemble prompt
146
+ const prompt = assembleReviewLearnPrompt(findings, existingLessons, systemPrompt);
147
+ log.dim(TAG, `Prompt: ${(prompt.length / 1024).toFixed(0)}KB`);
148
+ // 10. Run orchestrator
149
+ const content = await runOrchestrator({
150
+ prompt,
151
+ tag: TAG,
152
+ options,
153
+ config,
154
+ cwd,
155
+ temperature: 0.4,
156
+ customSecrets,
157
+ });
158
+ if (content == null)
159
+ return; // --raw mode — prompt already output
160
+ // 11. Parse lessons from LLM output
161
+ const lessons = parseLessons(content);
162
+ if (lessons.length === 0) {
163
+ log.dim(TAG, 'No lessons extracted from resolved bot findings.');
164
+ return;
165
+ }
166
+ log.success(TAG, `Extracted ${lessons.length} lesson(s)`);
167
+ // 12. Semantic dedup against existing lessons and intra-batch
168
+ log.info(TAG, 'Deduplicating against existing lessons...');
169
+ const { kept: novelLessons, dropped: dupLessons } = await deduplicateLessons(lessons, store, embedder);
170
+ if (dupLessons.length > 0) {
171
+ log.dim(TAG, `Dropped ${dupLessons.length} semantically duplicate lesson(s)`);
172
+ }
173
+ if (novelLessons.length === 0) {
174
+ log.dim(TAG, 'All extracted lessons are duplicates of existing ones.');
175
+ return;
176
+ }
177
+ // 13. Flag suspicious lessons
178
+ const flaggedLessons = flagSuspiciousLessons(novelLessons);
179
+ const suspiciousCount = flaggedLessons.filter((l) => l.suspiciousFlags?.length).length;
180
+ if (suspiciousCount > 0) {
181
+ log.warn(TAG, `${suspiciousCount} lesson(s) flagged as suspicious`);
182
+ }
183
+ log.success(TAG, `Total: ${flaggedLessons.length} nursery lesson(s) from PR #${num}`);
184
+ // --dry-run mode: preview lessons to stdout without writing
185
+ if (options.dryRun) {
186
+ log.dim(TAG, 'Dry run — lessons not written.');
187
+ for (const lesson of flaggedLessons) {
188
+ const prefix = lesson.suspiciousFlags?.length ? '[!] ' : '';
189
+ console.log(`\n ${prefix}Tags: ${sanitize(lesson.tags.join(', ')).replace(/\n/g, ' ')}`); // totem-ignore — stdout for piping
190
+ console.log(` Lifecycle: nursery`); // totem-ignore — stdout for piping
191
+ console.log(` ${sanitize(lesson.text).replace(/\n/g, '\n ')}`); // totem-ignore — stdout for piping
192
+ if (lesson.suspiciousFlags?.length) {
193
+ for (const flag of lesson.suspiciousFlags) {
194
+ console.log(` [!] ${flag}`); // totem-ignore — stdout for piping
195
+ }
196
+ }
197
+ }
198
+ if (options.yes && suspiciousCount > 0) {
199
+ process.exitCode = 1;
200
+ }
201
+ return;
202
+ }
203
+ // 14. Interactive selection
204
+ if (!options.yes) {
205
+ console.error('');
206
+ log.warn(TAG, 'WARNING: These lessons were extracted from bot review comments. Review each carefully before accepting.');
207
+ log.warn(TAG, 'All accepted lessons will have lifecycle: nursery (unproven until validated).\n');
208
+ for (let i = 0; i < flaggedLessons.length; i++) {
209
+ const lesson = flaggedLessons[i];
210
+ const prefix = lesson.suspiciousFlags?.length ? `[!] ` : '';
211
+ console.error(` [${i + 1}] ${prefix}Tags: ${sanitize(lesson.tags.join(', ')).replace(/\n/g, ' ')}`);
212
+ console.error(` Lifecycle: nursery`);
213
+ console.error(` ${sanitize(lesson.text).replace(/\n/g, '\n ')}`);
214
+ if (lesson.suspiciousFlags?.length) {
215
+ for (const flag of lesson.suspiciousFlags) {
216
+ console.error(` [!] ${flag}`);
217
+ }
218
+ }
219
+ console.error('');
220
+ }
221
+ }
222
+ const selected = await selectLessons(flaggedLessons, {
223
+ yes: options.yes,
224
+ isTTY: !!process.stdin.isTTY,
225
+ });
226
+ if (selected.length === 0) {
227
+ log.dim(TAG, 'No lessons selected — nothing written.');
228
+ return;
229
+ }
230
+ // Sanitize before persisting
231
+ const sanitizedLessons = selected.map((l) => ({
232
+ tags: l.tags.map((t) => sanitize(t)),
233
+ text: sanitize(l.text),
234
+ heading: l.heading,
235
+ }));
236
+ // 15. Write lessons with lifecycle: nursery frontmatter
237
+ const lessonsDir = path.join(cwd, config.totemDir, 'lessons');
238
+ appendNurseryLessons(sanitizedLessons, lessonsDir);
239
+ log.success(TAG, `Appended ${sanitizedLessons.length} nursery lesson(s) to ${config.totemDir}/lessons/`); // totem-ignore
240
+ // 16. Incremental sync
241
+ log.info(TAG, 'Running incremental sync...');
242
+ const syncResult = await runSync(config, {
243
+ projectRoot: cwd,
244
+ incremental: true,
245
+ onProgress: (msg) => log.dim(TAG, msg),
246
+ });
247
+ log.success(TAG, `Sync complete: ${syncResult.chunksProcessed} chunks from ${syncResult.filesProcessed} files`);
248
+ // Print summary
249
+ console.log(`\nExtracted ${sanitizedLessons.length} nursery lesson(s) from PR #${num}:`);
250
+ for (const lesson of sanitizedLessons) {
251
+ console.log(`\n Tags: ${lesson.tags.join(', ').replace(/\n/g, ' ')}`);
252
+ console.log(` Lifecycle: nursery`);
253
+ console.log(` ${lesson.text.replace(/\n/g, '\n ')}`);
254
+ }
255
+ // Exit non-zero if --yes mode dropped suspicious lessons
256
+ if (options.yes && suspiciousCount > 0) {
257
+ process.exitCode = 1;
258
+ }
259
+ }
260
+ //# sourceMappingURL=review-learn.js.map