@triedotdev/mcp 1.0.168 → 1.0.170

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 (149) hide show
  1. package/README.md +54 -500
  2. package/dist/chunk-2YXOBNKW.js +619 -0
  3. package/dist/chunk-2YXOBNKW.js.map +1 -0
  4. package/dist/chunk-QR64Y5TI.js +363 -0
  5. package/dist/chunk-QR64Y5TI.js.map +1 -0
  6. package/dist/cli/main.d.ts +0 -15
  7. package/dist/cli/main.js +356 -3100
  8. package/dist/cli/main.js.map +1 -1
  9. package/dist/index.js +2 -36
  10. package/dist/index.js.map +1 -1
  11. package/dist/server/mcp-server.js +2 -36
  12. package/package.json +8 -31
  13. package/dist/autonomy-config-FSERX3O3.js +0 -30
  14. package/dist/autonomy-config-FSERX3O3.js.map +0 -1
  15. package/dist/chat-store-JNGNTDSN.js +0 -15
  16. package/dist/chat-store-JNGNTDSN.js.map +0 -1
  17. package/dist/chunk-2HF65EHQ.js +0 -311
  18. package/dist/chunk-2HF65EHQ.js.map +0 -1
  19. package/dist/chunk-43X6JBEM.js +0 -36
  20. package/dist/chunk-43X6JBEM.js.map +0 -1
  21. package/dist/chunk-4MXH2ZPT.js +0 -1827
  22. package/dist/chunk-4MXH2ZPT.js.map +0 -1
  23. package/dist/chunk-575YT2SD.js +0 -737
  24. package/dist/chunk-575YT2SD.js.map +0 -1
  25. package/dist/chunk-5BRRRTN6.js +0 -354
  26. package/dist/chunk-5BRRRTN6.js.map +0 -1
  27. package/dist/chunk-6NLHFIYA.js +0 -344
  28. package/dist/chunk-6NLHFIYA.js.map +0 -1
  29. package/dist/chunk-7WITSO22.js +0 -824
  30. package/dist/chunk-7WITSO22.js.map +0 -1
  31. package/dist/chunk-DGUM43GV.js +0 -11
  32. package/dist/chunk-DGUM43GV.js.map +0 -1
  33. package/dist/chunk-EFWVF6TI.js +0 -267
  34. package/dist/chunk-EFWVF6TI.js.map +0 -1
  35. package/dist/chunk-F6WFNUAY.js +0 -216
  36. package/dist/chunk-F6WFNUAY.js.map +0 -1
  37. package/dist/chunk-FQ45QP5A.js +0 -361
  38. package/dist/chunk-FQ45QP5A.js.map +0 -1
  39. package/dist/chunk-G2TGF6TR.js +0 -573
  40. package/dist/chunk-G2TGF6TR.js.map +0 -1
  41. package/dist/chunk-GTKYBOXL.js +0 -700
  42. package/dist/chunk-GTKYBOXL.js.map +0 -1
  43. package/dist/chunk-HVCDY3AK.js +0 -850
  44. package/dist/chunk-HVCDY3AK.js.map +0 -1
  45. package/dist/chunk-JVMBCWKS.js +0 -348
  46. package/dist/chunk-JVMBCWKS.js.map +0 -1
  47. package/dist/chunk-KDHN2ZQE.js +0 -313
  48. package/dist/chunk-KDHN2ZQE.js.map +0 -1
  49. package/dist/chunk-LQIMKE3P.js +0 -12524
  50. package/dist/chunk-LQIMKE3P.js.map +0 -1
  51. package/dist/chunk-ME2OERF5.js +0 -345
  52. package/dist/chunk-ME2OERF5.js.map +0 -1
  53. package/dist/chunk-MRHKX5M5.js +0 -662
  54. package/dist/chunk-MRHKX5M5.js.map +0 -1
  55. package/dist/chunk-OBQ74FOU.js +0 -27
  56. package/dist/chunk-OBQ74FOU.js.map +0 -1
  57. package/dist/chunk-OMR4YCBS.js +0 -987
  58. package/dist/chunk-OMR4YCBS.js.map +0 -1
  59. package/dist/chunk-Q5EKA5YA.js +0 -254
  60. package/dist/chunk-Q5EKA5YA.js.map +0 -1
  61. package/dist/chunk-Q63FFI6D.js +0 -132
  62. package/dist/chunk-Q63FFI6D.js.map +0 -1
  63. package/dist/chunk-SY6KQG44.js +0 -983
  64. package/dist/chunk-SY6KQG44.js.map +0 -1
  65. package/dist/chunk-T63OHG4Q.js +0 -440
  66. package/dist/chunk-T63OHG4Q.js.map +0 -1
  67. package/dist/chunk-TN5WEKWI.js +0 -173
  68. package/dist/chunk-TN5WEKWI.js.map +0 -1
  69. package/dist/chunk-VUL52BQL.js +0 -402
  70. package/dist/chunk-VUL52BQL.js.map +0 -1
  71. package/dist/chunk-VVITXIHN.js +0 -189
  72. package/dist/chunk-VVITXIHN.js.map +0 -1
  73. package/dist/chunk-WCN7S3EI.js +0 -14
  74. package/dist/chunk-WCN7S3EI.js.map +0 -1
  75. package/dist/chunk-XE6KQRKZ.js +0 -816
  76. package/dist/chunk-XE6KQRKZ.js.map +0 -1
  77. package/dist/chunk-XPZZFPBZ.js +0 -491
  78. package/dist/chunk-XPZZFPBZ.js.map +0 -1
  79. package/dist/chunk-XTFWT2XM.js +0 -727
  80. package/dist/chunk-XTFWT2XM.js.map +0 -1
  81. package/dist/chunk-YDHUCDHM.js +0 -4011
  82. package/dist/chunk-YDHUCDHM.js.map +0 -1
  83. package/dist/chunk-YZ6Y2H3P.js +0 -1289
  84. package/dist/chunk-YZ6Y2H3P.js.map +0 -1
  85. package/dist/chunk-ZJF5FTBX.js +0 -1396
  86. package/dist/chunk-ZJF5FTBX.js.map +0 -1
  87. package/dist/chunk-ZV2K6M7T.js +0 -74
  88. package/dist/chunk-ZV2K6M7T.js.map +0 -1
  89. package/dist/cli/create-agent.d.ts +0 -1
  90. package/dist/cli/create-agent.js +0 -1050
  91. package/dist/cli/create-agent.js.map +0 -1
  92. package/dist/cli/yolo-daemon.d.ts +0 -1
  93. package/dist/cli/yolo-daemon.js +0 -423
  94. package/dist/cli/yolo-daemon.js.map +0 -1
  95. package/dist/client-NJPZE5JT.js +0 -28
  96. package/dist/client-NJPZE5JT.js.map +0 -1
  97. package/dist/codebase-index-VAPF32XX.js +0 -12
  98. package/dist/codebase-index-VAPF32XX.js.map +0 -1
  99. package/dist/fast-analyzer-XXYMOXRK.js +0 -216
  100. package/dist/fast-analyzer-XXYMOXRK.js.map +0 -1
  101. package/dist/git-EO5SRFMN.js +0 -28
  102. package/dist/git-EO5SRFMN.js.map +0 -1
  103. package/dist/github-ingester-ZOKK6GRS.js +0 -11
  104. package/dist/github-ingester-ZOKK6GRS.js.map +0 -1
  105. package/dist/goal-manager-YOB7VWK7.js +0 -25
  106. package/dist/goal-manager-YOB7VWK7.js.map +0 -1
  107. package/dist/goal-validator-ULKIBDPX.js +0 -24
  108. package/dist/goal-validator-ULKIBDPX.js.map +0 -1
  109. package/dist/graph-B3NA4S7I.js +0 -10
  110. package/dist/graph-B3NA4S7I.js.map +0 -1
  111. package/dist/hypothesis-7BFFT5JY.js +0 -23
  112. package/dist/hypothesis-7BFFT5JY.js.map +0 -1
  113. package/dist/incident-index-EFNUSGWL.js +0 -11
  114. package/dist/incident-index-EFNUSGWL.js.map +0 -1
  115. package/dist/insight-store-EC4PLSAW.js +0 -22
  116. package/dist/insight-store-EC4PLSAW.js.map +0 -1
  117. package/dist/issue-store-ZIRP23EP.js +0 -36
  118. package/dist/issue-store-ZIRP23EP.js.map +0 -1
  119. package/dist/ledger-TWZTGDFA.js +0 -58
  120. package/dist/ledger-TWZTGDFA.js.map +0 -1
  121. package/dist/linear-ingester-XXPAZZRW.js +0 -11
  122. package/dist/linear-ingester-XXPAZZRW.js.map +0 -1
  123. package/dist/output-manager-RVJ37XKA.js +0 -13
  124. package/dist/output-manager-RVJ37XKA.js.map +0 -1
  125. package/dist/parse-goal-violation-SACGFG3C.js +0 -8
  126. package/dist/parse-goal-violation-SACGFG3C.js.map +0 -1
  127. package/dist/pattern-discovery-F7LU5K6E.js +0 -8
  128. package/dist/pattern-discovery-F7LU5K6E.js.map +0 -1
  129. package/dist/progress-SRQ2V3BP.js +0 -18
  130. package/dist/progress-SRQ2V3BP.js.map +0 -1
  131. package/dist/project-state-AHPA77SM.js +0 -28
  132. package/dist/project-state-AHPA77SM.js.map +0 -1
  133. package/dist/sync-M2FSWPBC.js +0 -12
  134. package/dist/sync-M2FSWPBC.js.map +0 -1
  135. package/dist/terminal-spawn-5YXDMUCF.js +0 -157
  136. package/dist/terminal-spawn-5YXDMUCF.js.map +0 -1
  137. package/dist/tiered-storage-Z3YCR465.js +0 -12
  138. package/dist/tiered-storage-Z3YCR465.js.map +0 -1
  139. package/dist/trie-agent-3YDPEGHJ.js +0 -28
  140. package/dist/trie-agent-3YDPEGHJ.js.map +0 -1
  141. package/dist/ui/chat.html +0 -1014
  142. package/dist/ui/goals.html +0 -967
  143. package/dist/ui/hypotheses.html +0 -1011
  144. package/dist/ui/ledger.html +0 -954
  145. package/dist/ui/nudges.html +0 -995
  146. package/dist/vibe-code-signatures-F6URTBW3.js +0 -16
  147. package/dist/vibe-code-signatures-F6URTBW3.js.map +0 -1
  148. package/dist/vulnerability-signatures-T7SKHORW.js +0 -18
  149. package/dist/vulnerability-signatures-T7SKHORW.js.map +0 -1
package/dist/cli/main.js CHANGED
@@ -1,3134 +1,390 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
- completeBootstrap,
4
- initProjectInfo,
5
- initializeBootstrapFiles,
6
- loadContextState,
7
- loadProjectInfo,
8
- needsBootstrap,
9
- projectInfoExists
10
- } from "../chunk-HVCDY3AK.js";
11
- import {
12
- LinearIngester
13
- } from "../chunk-Q63FFI6D.js";
14
- import {
15
- LearningEngine,
16
- formatFriendlyError,
17
- isTrieInitialized,
18
- perceiveCurrentChanges,
19
- reasonAboutChangesHumanReadable
20
- } from "../chunk-YZ6Y2H3P.js";
21
- import {
22
- measureInitialGoalValue
23
- } from "../chunk-5BRRRTN6.js";
24
- import {
25
- exportToJson
26
- } from "../chunk-OBQ74FOU.js";
27
- import {
28
- loadConfig,
29
- saveConfig
30
- } from "../chunk-XPZZFPBZ.js";
31
- import {
32
- IncidentIndex
33
- } from "../chunk-TN5WEKWI.js";
34
- import "../chunk-ZV2K6M7T.js";
35
- import {
36
- GotchaPredictor,
37
- SlackIntegration,
38
- findCrossProjectPatterns,
39
- getGlobalMemoryStats,
40
- listTrackedProjects,
41
- searchGlobalPatterns,
42
- updateGlobalMemoryMd
43
- } from "../chunk-7WITSO22.js";
44
- import "../chunk-SY6KQG44.js";
45
- import "../chunk-OMR4YCBS.js";
46
- import {
47
- ContextGraph
48
- } from "../chunk-VUL52BQL.js";
49
- import "../chunk-6NLHFIYA.js";
50
- import {
51
- getStorage
52
- } from "../chunk-575YT2SD.js";
53
- import "../chunk-FQ45QP5A.js";
54
- import {
55
- getDailyLogs,
56
- getMemoryStats,
57
- getRecentIssues,
58
- markIssueResolved,
59
- purgeIssues,
60
- searchIssues
61
- } from "../chunk-XE6KQRKZ.js";
62
- import {
63
- compressOldBlocks,
64
- correctLedgerEntries,
65
- detectLegacyLedger,
66
- generateKeyPair,
67
- getCorrectionStats,
68
- getEntryCorrectionHistory,
69
- getLedgerBlocks,
70
- getLedgerSyncStatus,
71
- getPublicKey,
72
- getStorageStats,
73
- hasSigningKey,
74
- initializeSharedLedger,
75
- loadKeyPair,
76
- migrateLegacyLedger,
77
- pushLedgerToShared,
78
- saveKeyPair,
79
- shouldCompress,
80
- syncLedgerFromShared,
81
- verifyLedger
82
- } from "../chunk-ZJF5FTBX.js";
83
- import {
84
- getProjectState
85
- } from "../chunk-GTKYBOXL.js";
86
- import "../chunk-EFWVF6TI.js";
87
- import "../chunk-43X6JBEM.js";
88
- import {
89
- getAutonomyConfig,
90
- recordBypass,
91
- shouldAutoFix,
92
- shouldBlockPush,
93
- trackIssueOccurrence
94
- } from "../chunk-ME2OERF5.js";
95
- import {
96
- getTrieDirectory,
97
- getWorkingDirectory
98
- } from "../chunk-VVITXIHN.js";
99
- import "../chunk-KDHN2ZQE.js";
100
- import {
101
- getStagedChanges,
102
- getUncommittedChanges,
103
- isGitRepo
104
- } from "../chunk-2HF65EHQ.js";
105
- import {
106
- __require
107
- } from "../chunk-DGUM43GV.js";
108
-
109
- // src/cli/main.ts
110
- import { resolve, join as join4, dirname } from "path";
111
- import { readFileSync as readFileSync2, realpathSync } from "fs";
112
- import { fileURLToPath } from "url";
113
- import pc9 from "picocolors";
114
-
115
- // src/hooks/install.ts
116
- import fs from "fs";
117
- import path from "path";
118
-
119
- // src/hooks/templates.ts
120
- var PATH_FIX = 'export PATH="/usr/local/bin:/opt/homebrew/bin:$PATH"\n';
121
- function hookHeader() {
122
- return "#!/bin/sh\n" + PATH_FIX + "\n";
123
- }
124
- function trieCommand(name) {
125
- return `
126
- TRIE_BIN="$(command -v trie || command -v trie-agent || true)"
127
- if [ -n "$TRIE_BIN" ]; then
128
- "$TRIE_BIN" ${name} || true
129
- else
130
- echo "Trie not available; skipping ${name} hook."
131
- fi
132
- `;
133
- }
134
- function prePushCommand() {
135
- return `
136
- TRIE_BIN="$(command -v trie || command -v trie-agent || true)"
137
- if [ -n "$TRIE_BIN" ]; then
138
- # Check for bypass
139
- if [ "$TRIE_BYPASS" = "1" ] || [ "$TRIE_BYPASS" = "true" ]; then
140
- echo "Trie push blocking bypassed (TRIE_BYPASS=1)"
141
- "$TRIE_BIN" pre-push --bypassed || true
142
- exit 0
143
- fi
144
-
145
- # Run pre-push check with blocking enabled
146
- "$TRIE_BIN" pre-push --block
147
- EXIT_CODE=$?
148
-
149
- if [ $EXIT_CODE -eq 0 ]; then
150
- exit 0
151
- elif [ $EXIT_CODE -eq 2 ]; then
152
- # Exit code 2 = blocked but bypassable
153
- echo ""
154
- echo "\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501"
155
- echo "Push blocked by Trie due to critical issues."
156
- echo ""
157
- echo "To bypass (not recommended):"
158
- echo " TRIE_BYPASS=1 git push"
159
- echo " OR: git push --no-verify"
160
- echo ""
161
- echo "To fix issues first:"
162
- echo " trie fix"
163
- echo "\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501"
164
- exit 1
165
- else
166
- # Other errors - don't block
167
- exit 0
168
- fi
169
- else
170
- echo "Trie not available; skipping pre-push hook."
171
- fi
172
- `;
173
- }
174
- function preCommitTemplate() {
175
- return (hookHeader() + "# Trie pre-commit hook\n" + trieCommand("pre-commit")).trim() + "\n";
176
- }
177
- function postCommitTemplate() {
178
- return (hookHeader() + "# Trie post-commit hook\n" + trieCommand("post-commit")).trim() + "\n";
179
- }
180
- function prePushTemplate() {
181
- return (hookHeader() + "# Trie pre-push hook with blocking support\n# Bypass with: TRIE_BYPASS=1 git push\n" + prePushCommand()).trim() + "\n";
182
- }
183
-
184
- // src/hooks/install.ts
185
- var HOOK_BUILDERS = {
186
- "pre-commit": preCommitTemplate,
187
- "post-commit": postCommitTemplate,
188
- "pre-push": prePushTemplate
189
- };
190
- function hookPath(repoPath, name) {
191
- return path.join(repoPath, ".git", "hooks", name);
192
- }
193
- function ensureHookDir(repoPath) {
194
- const dir = path.join(repoPath, ".git", "hooks");
195
- fs.mkdirSync(dir, { recursive: true });
196
- }
197
- function appendHookFile(targetPath, content) {
198
- if (fs.existsSync(targetPath)) {
199
- const existing = fs.readFileSync(targetPath, "utf8");
200
- if (existing.includes("# Trie pre-commit hook") || existing.includes("# Trie post-commit hook")) {
201
- return;
202
- }
203
- fs.appendFileSync(targetPath, `
204
- # --- Added by Trie ---
205
- ${content}`);
206
- } else {
207
- fs.writeFileSync(targetPath, content, { mode: 493 });
208
- }
209
- fs.chmodSync(targetPath, 493);
210
- }
211
- function installGitHooks(projectPath) {
212
- const gitDir = path.join(projectPath, ".git");
213
- if (!fs.existsSync(gitDir)) {
214
- return [];
215
- }
216
- ensureHookDir(projectPath);
217
- const installed = [];
218
- Object.keys(HOOK_BUILDERS).forEach((name) => {
219
- const content = HOOK_BUILDERS[name]();
220
- const target = hookPath(projectPath, name);
221
- appendHookFile(target, content);
222
- installed.push(name);
223
- });
224
- return installed;
225
- }
226
-
227
- // src/cli/init.ts
228
- import fs2 from "fs";
229
- import path2 from "path";
230
- function ensureGitignore(workDir) {
231
- const target = path2.join(workDir, ".gitignore");
232
- const entries = [".trie/context.db", ".trie/context.json", ".trie/*.db", "trie-reports/"];
233
- let content = "";
234
- if (fs2.existsSync(target)) {
235
- content = fs2.readFileSync(target, "utf8");
236
- }
237
- const missing = entries.filter((e) => !content.includes(e));
238
- if (missing.length > 0) {
239
- const updated = content.trimEnd() + "\n" + missing.join("\n") + "\n";
240
- fs2.writeFileSync(target, updated);
241
- }
242
- }
243
- async function handleInitCommand(args) {
244
- try {
245
- const subcommand = args[0]?.toLowerCase();
246
- const workDir = getWorkingDirectory(void 0, true);
247
- if (subcommand === "complete") {
248
- const result2 = await completeBootstrap(workDir);
249
- if (result2) {
250
- console.log("Bootstrap completed. BOOTSTRAP.md has been deleted.");
251
- } else {
252
- console.log("No BOOTSTRAP.md file found.");
253
- }
254
- return;
255
- }
256
- if (subcommand === "status") {
257
- const needs = needsBootstrap(workDir);
258
- if (needs) {
259
- console.log("Bootstrap pending: .trie/BOOTSTRAP.md exists");
260
- console.log('Run "trie init complete" after finishing setup.');
261
- } else {
262
- console.log("Bootstrap complete: No pending setup.");
263
- }
264
- return;
265
- }
266
- const force = args.includes("--force") || args.includes("-f");
267
- const skipBootstrap = args.includes("--skip-bootstrap");
268
- console.log(`
269
- \u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557
270
- \u2551 \u2551
271
- \u2551 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2551
272
- \u2551 \u255A\u2550\u2550\u2588\u2588\u2554\u2550\u2550\u255D\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D \u2551
273
- \u2551 \u2588\u2588\u2551 \u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2588\u2557 \u2551
274
- \u2551 \u2588\u2588\u2551 \u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u255D \u2551
275
- \u2551 \u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2551
276
- \u2551 \u255A\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u255D\u255A\u2550\u255D\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D \u2551
277
- \u2551 \u2551
278
- \u2551 Your code quality agent and tools registry \u2551
279
- \u2551 \u2551
280
- \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D
281
-
282
- by Louis Kishfy
283
-
284
- Download the Trie workspace: https://www.trie.dev
285
- Follow me on X: https://x.com/louiskishfy
286
-
287
- `);
288
- const result = await initializeBootstrapFiles({
289
- workDir,
290
- force,
291
- skipBootstrap
292
- });
293
- const isReInit = result.created.length === 0 && result.skipped.length > 0;
294
- if (isReInit && !force) {
295
- console.log("Trie is already initialized in this project.\n");
296
- console.log("Your files:");
297
- for (const file of result.skipped) {
298
- console.log(` \u2713 .trie/${file}`);
299
- }
300
- console.log("\nTo reset and recreate files: trie init --force");
301
- } else {
302
- if (result.created.length > 0) {
303
- console.log("Created files:");
304
- for (const file of result.created) {
305
- console.log(` + .trie/${file}`);
306
- }
307
- }
308
- if (result.skipped.length > 0 && !force) {
309
- console.log("\nSkipped (already exist):");
310
- for (const file of result.skipped) {
311
- console.log(` - .trie/${file}`);
312
- }
313
- }
314
- }
315
- console.log("\nDetected Stack:");
316
- if (result.stack.framework) console.log(` Framework: ${result.stack.framework}`);
317
- if (result.stack.language) console.log(` Language: ${result.stack.language}`);
318
- if (result.stack.database) console.log(` Database: ${result.stack.database}`);
319
- if (result.stack.auth) console.log(` Auth: ${result.stack.auth}`);
320
- if (result.stack.packageManager) console.log(` Package Manager: ${result.stack.packageManager}`);
321
- console.log(`
322
- Quick Start:
323
- 1. trie watch # Start watching (nudges as you code)
324
- 2. trie tell "what broke" # Teach Trie about past incidents
325
- 3. trie check # Risk check before pushing
326
-
327
- Trie is now active via git hooks (pre-commit, pre-push).
328
- For real-time nudges while coding, run "trie watch" in a terminal.
329
-
330
- Customize Later:
331
- - .trie/PROJECT.md \u2014 Describe your project for better context
332
- - .trie/RULES.md \u2014 Define coding standards Trie should enforce
333
-
334
- Run "trie help" to see all available commands.
335
- `);
336
- const hooks = installGitHooks(workDir);
337
- if (hooks.length > 0) {
338
- console.log("\nGit hooks installed:");
339
- for (const hook of hooks) {
340
- console.log(` .git/hooks/${hook}`);
341
- }
342
- } else {
343
- console.log("\nGit hooks not installed (no .git directory found).");
344
- }
345
- ensureGitignore(workDir);
346
- const skipKeys = args.includes("--no-keys");
347
- if (!skipKeys && !hasSigningKey(workDir)) {
348
- console.log("\nSetting up Ed25519 signing for governance ledger...");
349
- try {
350
- const keyPair = await generateKeyPair();
351
- saveKeyPair(keyPair, workDir);
352
- console.log(` \u2713 Signing key generated`);
353
- console.log(` Public key: ${keyPair.publicKey.slice(0, 16)}...${keyPair.publicKey.slice(-8)}`);
354
- console.log(" All ledger entries will be signed automatically.");
355
- console.log(" Your private key stays local (never committed to git).");
356
- } catch (keyError) {
357
- console.log(` \u26A0 Could not generate signing key: ${keyError}`);
358
- console.log(' Run "trie keys generate" to set up signing later.');
359
- }
360
- } else if (hasSigningKey(workDir)) {
361
- console.log("\n\u2713 Signing key already configured (ledger entries will be signed)");
362
- }
363
- if (!process.env.ANTHROPIC_API_KEY) {
364
- console.log(`
365
- Note: Running in pattern-only mode. For AI-powered analysis, set your API key:
366
-
367
- This session only:
368
- export ANTHROPIC_API_KEY=sk-ant-...
369
-
370
- Persist in shell config (~/.zshrc or ~/.bashrc):
371
- echo 'export ANTHROPIC_API_KEY=sk-ant-...' >> ~/.zshrc
372
- source ~/.zshrc
373
-
374
- Or add to your MCP config for Cursor/Claude Code (see README).`);
375
- }
376
- } catch (error) {
377
- const friendly = formatFriendlyError(error);
378
- console.error(friendly.userMessage);
379
- }
380
- }
381
-
382
- // src/cli/memory.ts
383
- async function handleMemoryCommand(args) {
384
- const subcommand = args[0]?.toLowerCase();
385
- const restArgs = args.slice(1);
386
- switch (subcommand) {
387
- case "search":
388
- await handleSearch(restArgs);
389
- break;
390
- case "stats":
391
- await handleStats();
392
- break;
393
- case "recent":
394
- await handleRecent(restArgs);
395
- break;
396
- case "logs":
397
- await handleLogs();
398
- break;
399
- case "resolve":
400
- await handleResolve(restArgs);
401
- break;
402
- case "purge":
403
- await handlePurge(restArgs);
404
- break;
405
- case "global":
406
- await handleGlobal(restArgs);
407
- break;
408
- default:
409
- showHelp();
410
- }
411
- }
412
- async function handleSearch(args) {
413
- const query = args.join(" ");
414
- if (!query) {
415
- console.log("Usage: trie memory search <query>");
416
- console.log('Example: trie memory search "SQL injection"');
417
- return;
418
- }
419
- const workDir = getWorkingDirectory(void 0, true);
420
- const results = await searchIssues(query, { workDir, limit: 10 });
421
- if (results.length === 0) {
422
- console.log(`No issues found matching "${query}"`);
423
- return;
424
- }
425
- console.log(`Found ${results.length} issue(s) matching "${query}":
426
- `);
427
- for (const result of results) {
428
- const i = result.issue;
429
- const status = i.resolved ? "[RESOLVED]" : "";
430
- console.log(`[${i.severity.toUpperCase()}] ${status} ${i.issue.slice(0, 80)}`);
431
- console.log(` File: ${i.file}${i.line ? `:${i.line}` : ""}`);
432
- console.log(` Agent: ${i.agent} | Score: ${(result.score * 100).toFixed(0)}%`);
433
- console.log(` Date: ${new Date(i.timestamp).toLocaleDateString()}`);
434
- console.log("");
435
- }
436
- }
437
- async function handleStats() {
438
- const workDir = getWorkingDirectory(void 0, true);
439
- const stats = await getMemoryStats(workDir);
440
- const globalStats = await getGlobalMemoryStats();
441
- console.log("Local Issue Memory Stats:");
442
- console.log(` Active Issues: ${stats.activeIssues}`);
443
- console.log(` Resolved: ${stats.resolvedCount}`);
444
- console.log(` Total (all-time): ${stats.totalIssues}`);
445
- const cap = stats.capacityInfo;
446
- if (cap.isAtCap) {
447
- console.log("\n\u26A0\uFE0F CAPACITY WARNING");
448
- console.log(` Memory is at maximum capacity (${cap.current}/${cap.max}, ${cap.percentFull}%)`);
449
- console.log(" New repeats are deduplicated. Older issues are compacted into summaries, and if still over cap the oldest/lowest-value issues are pruned.");
450
- console.log("\n To free up space:");
451
- console.log(" trie memory purge smart - Remove resolved & old low-priority (recommended)");
452
- console.log(" trie memory purge resolved - Remove all resolved issues");
453
- console.log(" trie memory purge old - Remove issues older than 90 days");
454
- console.log(" trie memory purge all - Clear all issues (keeps summaries)");
455
- } else if (cap.percentFull >= 80) {
456
- console.log(`
457
- \u2139\uFE0F Memory Usage: ${cap.percentFull}% (${cap.current}/${cap.max})`);
458
- console.log(" Consider running: trie memory purge smart");
459
- } else {
460
- console.log(` Memory Usage: ${cap.percentFull}% (${cap.current}/${cap.max})`);
461
- }
462
- if (stats.deduplicationStats.duplicatesAvoided > 0) {
463
- console.log("\n Deduplication:");
464
- console.log(` Unique Patterns: ${stats.deduplicationStats.uniquePatterns}`);
465
- console.log(` Duplicates Avoided: ${stats.deduplicationStats.duplicatesAvoided}`);
466
- }
467
- if (stats.oldestIssue) {
468
- console.log(`
469
- Date Range: ${stats.oldestIssue.split("T")[0]} to ${stats.newestIssue?.split("T")[0]}`);
470
- }
471
- console.log("\n Active Issues by Severity:");
472
- const severityOrder = ["critical", "serious", "moderate", "low", "info"];
473
- let hasActiveIssues = false;
474
- for (const severity of severityOrder) {
475
- const activeCount = stats.activeIssuesBySeverity[severity] || 0;
476
- const totalCount = stats.issuesBySeverity[severity] || 0;
477
- if (totalCount > 0) {
478
- hasActiveIssues = hasActiveIssues || activeCount > 0;
479
- console.log(` ${severity}: ${activeCount} active (${totalCount} all-time)`);
480
- }
481
- }
482
- if (!hasActiveIssues && stats.totalIssues > 0) {
483
- console.log(" \u2705 All issues have been resolved!");
484
- }
485
- console.log("\n By Agent:");
486
- const sortedAgents = Object.entries(stats.issuesByAgent).sort((a, b) => b[1] - a[1]);
487
- for (const [agent, count] of sortedAgents.slice(0, 10)) {
488
- console.log(` ${agent}: ${count}`);
489
- }
490
- console.log("\nGlobal Cross-Project Stats:");
491
- console.log(` Tracked Projects: ${globalStats.trackedProjects}`);
492
- console.log(` Total Patterns: ${globalStats.totalPatterns}`);
493
- console.log(` Cross-Project Patterns: ${globalStats.crossProjectPatterns}`);
494
- console.log(` Fixed Patterns: ${globalStats.fixedPatterns}`);
495
- }
496
- async function handleRecent(args) {
497
- const limit = parseInt(args[0] || "10", 10);
498
- const workDir = getWorkingDirectory(void 0, true);
499
- const issues = await getRecentIssues({ workDir, limit });
500
- if (issues.length === 0) {
501
- console.log("No recent issues found.");
502
- return;
503
- }
504
- console.log(`Recent Issues (last 7 days):
505
- `);
506
- for (const i of issues) {
507
- const status = i.resolved ? "[RESOLVED]" : "";
508
- console.log(`[${i.severity.toUpperCase()}] ${status} ${i.issue.slice(0, 70)}`);
509
- console.log(` ${i.file}${i.line ? `:${i.line}` : ""} | ${i.agent}`);
510
- console.log("");
511
- }
512
- }
513
- async function handleLogs() {
514
- const workDir = getWorkingDirectory(void 0, true);
515
- const logs = await getDailyLogs(workDir);
516
- if (logs.length === 0) {
517
- console.log("No daily logs found.");
518
- return;
519
- }
520
- console.log("Daily Logs (.trie/memory/):\n");
521
- for (const log of logs.slice(0, 14)) {
522
- console.log(` ${log}`);
523
- }
524
- if (logs.length > 14) {
525
- console.log(` ... and ${logs.length - 14} more`);
526
- }
527
- }
528
- async function handleResolve(args) {
529
- const issueId = args[0];
530
- if (!issueId) {
531
- console.log("Usage: trie memory resolve <issue-id>");
532
- return;
533
- }
534
- const workDir = getWorkingDirectory(void 0, true);
535
- const result = await markIssueResolved(issueId, workDir);
536
- if (result) {
537
- console.log(`Issue ${issueId} marked as resolved.`);
538
- } else {
539
- console.log(`Issue ${issueId} not found.`);
540
- }
541
- }
542
- async function handleGlobal(args) {
543
- const action = args[0]?.toLowerCase();
544
- if (action === "patterns") {
545
- const patterns = await findCrossProjectPatterns(2);
546
- if (patterns.length === 0) {
547
- console.log("No cross-project patterns found yet.");
548
- console.log("Patterns appear when the same issue is detected in multiple projects.");
549
- return;
550
- }
551
- console.log(`Cross-Project Patterns (${patterns.length}):
552
- `);
553
- for (const p of patterns.slice(0, 10)) {
554
- console.log(`[${p.severity.toUpperCase()}] ${p.pattern.slice(0, 60)}`);
555
- console.log(` Occurrences: ${p.occurrences} across ${p.projects.length} projects`);
556
- console.log(` Agent: ${p.agent}`);
557
- if (p.fixApplied) {
558
- console.log(` Fixed in: ${p.fixApplied.project}`);
559
- }
560
- console.log("");
561
- }
562
- return;
563
- }
564
- if (action === "projects") {
565
- const projects = await listTrackedProjects();
566
- if (projects.length === 0) {
567
- console.log("No projects tracked yet.");
568
- return;
569
- }
570
- console.log(`Tracked Projects (${projects.length}):
571
- `);
572
- console.log("| Project | Last Scan | Issues |");
573
- console.log("|---------|-----------|--------|");
574
- for (const p of projects) {
575
- console.log(`| ${p.name.slice(0, 20)} | ${p.lastScan.split("T")[0]} | ${p.totalIssues} |`);
576
- }
577
- return;
578
- }
579
- if (action === "search") {
580
- const query = args.slice(1).join(" ");
581
- if (!query) {
582
- console.log("Usage: trie memory global search <query>");
583
- return;
584
- }
585
- const patterns = await searchGlobalPatterns(query, { limit: 10 });
586
- if (patterns.length === 0) {
587
- console.log(`No global patterns found matching "${query}"`);
588
- return;
589
- }
590
- console.log(`Global patterns matching "${query}":
591
- `);
592
- for (const p of patterns) {
593
- console.log(`[${p.severity.toUpperCase()}] ${p.pattern.slice(0, 60)}`);
594
- console.log(` Projects: ${p.projects.slice(0, 3).join(", ")}`);
595
- console.log("");
596
- }
597
- return;
598
- }
599
- if (action === "update") {
600
- await updateGlobalMemoryMd();
601
- console.log("Updated ~/.trie/memory/GLOBAL_MEMORY.md");
602
- return;
603
- }
604
- console.log(`
605
- Global Memory Commands:
606
- trie memory global patterns Show cross-project patterns
607
- trie memory global projects Show tracked projects
608
- trie memory global search <q> Search global patterns
609
- trie memory global update Update GLOBAL_MEMORY.md
610
- `);
611
- }
612
- async function handlePurge(args) {
613
- const strategy = args[0]?.toLowerCase() || "smart";
614
- const validStrategies = ["smart", "resolved", "old", "all"];
615
- if (!validStrategies.includes(strategy)) {
616
- console.log("Invalid purge strategy. Use: smart, resolved, old, or all");
617
- console.log("\nUsage:");
618
- console.log(" trie memory purge smart - Remove resolved & old low-priority (recommended)");
619
- console.log(" trie memory purge resolved - Remove all resolved issues");
620
- console.log(" trie memory purge old [days] - Remove issues older than N days (default 90)");
621
- console.log(" trie memory purge all - Clear all issues (keeps summaries)");
622
- return;
623
- }
624
- const daysOld = strategy === "old" ? parseInt(args[1] || "90", 10) : void 0;
625
- if (strategy === "all") {
626
- console.log("\u26A0\uFE0F WARNING: This will clear ALL issues from active memory.");
627
- console.log("Historical summaries will be preserved, but individual issues will be removed.");
628
- console.log("\nTo confirm, run: trie memory purge all --confirm");
629
- if (!args.includes("--confirm")) {
630
- return;
631
- }
632
- }
633
- const workDir = getWorkingDirectory(void 0, true);
634
- console.log(`
635
- Purging issues with strategy: ${strategy}...`);
636
- const options = { workDir };
637
- if (daysOld !== void 0) {
638
- options.daysOld = daysOld;
639
- }
640
- const result = await purgeIssues(strategy, options);
641
- console.log(`
642
- \u2705 Purge Complete`);
643
- console.log(` Strategy: ${result.strategy}`);
644
- console.log(` Removed: ${result.removed} issues`);
645
- console.log(` Remaining: ${result.remaining} issues`);
646
- switch (strategy) {
647
- case "smart":
648
- console.log("\nWhat was removed:");
649
- console.log(" \u2022 Resolved issues older than 30 days");
650
- console.log(" \u2022 Low-priority issues (info/low) older than 30 days");
651
- console.log("\nWhat was kept:");
652
- console.log(" \u2022 All critical and high severity issues");
653
- console.log(" \u2022 All unresolved issues");
654
- console.log(" \u2022 All issues from the last 30 days");
655
- break;
656
- case "resolved":
657
- console.log("\nRemoved all resolved issues.");
658
- console.log("Kept all unresolved issues.");
659
- break;
660
- case "old":
661
- console.log(`
662
- Removed all issues older than ${daysOld} days.`);
663
- console.log("Kept all recent issues.");
664
- break;
665
- case "all":
666
- console.log("\nCleared all issues from active memory.");
667
- console.log("Historical summaries preserved in .trie/memory/compacted-summaries.json");
668
- break;
669
- }
670
- console.log('\nRun "trie memory stats" to see updated memory usage.');
671
- }
672
- function showHelp() {
673
- console.log(`
674
- Memory - Search and manage issue memory
675
-
676
- Commands:
677
- trie memory search <query> Search issues by keyword
678
- trie memory stats Show memory statistics
679
- trie memory recent [limit] Show recent issues
680
- trie memory logs List daily log files
681
- trie memory resolve <id> Mark issue as resolved
682
- trie memory purge <strategy> Free up memory space
683
- trie memory global <action> Cross-project memory
684
-
685
- Purge Strategies:
686
- smart Remove resolved & old low-priority issues (recommended)
687
- resolved Remove all resolved issues
688
- old [N] Remove issues older than N days (default 90)
689
- all Clear all issues (keeps historical summaries)
690
-
691
- Examples:
692
- trie memory search "SQL injection"
693
- trie memory recent 20
694
- trie memory purge smart
695
- trie memory global patterns
696
- `);
697
- }
698
-
699
- // src/cli/check.ts
700
- function parseArgs(args) {
701
- let mode = "full";
702
- const filesFlag = args.find((a) => a.startsWith("--files="));
703
- const parsedFiles = filesFlag ? filesFlag.replace("--files=", "").split(",").map((f) => f.trim()).filter(Boolean) : [];
704
- if (args.includes("--quick") || args.includes("-q")) mode = "quick";
705
- if (args.includes("--offline")) mode = "offline";
706
- if (args.includes("--full")) mode = "full";
707
- const result = { mode };
708
- if (parsedFiles.length > 0) result.files = parsedFiles;
709
- return result;
710
- }
711
- function printResult(result) {
712
- console.log(`
713
- Risk: ${result.original.riskLevel.toUpperCase()} (${result.original.shouldBlock ? "block" : "allow"})`);
714
- console.log(`Explanation: ${result.original.explanation}`);
715
- console.log(`Recommendation: ${result.original.recommendation}`);
716
- if (result.original.files?.length) {
717
- console.log("\nHigh-risk files:");
718
- for (const file of result.original.files.filter((f) => ["high", "critical"].includes(f.level))) {
719
- console.log(` - ${file.file}: ${file.reasons.join("; ")}`);
720
- }
721
- }
722
- console.log("\nPlain-English summary:");
723
- console.log(` Summary: ${result.summary}`);
724
- console.log(` What I found: ${result.whatIFound}`);
725
- console.log(` How bad: ${result.howBad}`);
726
- console.log(` What to do: ${result.whatToDo}`);
727
- }
728
- async function handleCheckCommand(args) {
729
- try {
730
- const options = parseArgs(args);
731
- const projectPath = getWorkingDirectory(void 0, true);
732
- let files = options.files;
733
- if (!files || files.length === 0) {
734
- const perception = await perceiveCurrentChanges(projectPath);
735
- files = perception.diffSummary.files.map((f) => f.filePath);
736
- if (files.length === 0) {
737
- console.log("No changes detected. Provide files with --files=path1,path2 or make a change first.");
738
- return;
739
- }
740
- }
741
- const runAgents = options.mode === "full";
742
- const reasoning = await reasonAboutChangesHumanReadable(projectPath, files, {
743
- runAgents,
744
- scanContext: {
745
- config: { timeoutMs: options.mode === "quick" ? 15e3 : 6e4 }
746
- }
747
- });
748
- printResult(reasoning);
749
- } catch (error) {
750
- const friendly = formatFriendlyError(error);
751
- console.error(friendly.userMessage);
752
- }
753
- }
754
-
755
- // src/cli/pre-push.ts
756
- import pc from "picocolors";
757
- function parseArgs2(args) {
758
- return {
759
- block: args.includes("--block"),
760
- bypassed: args.includes("--bypassed"),
761
- quick: args.includes("--quick") || args.includes("-q")
762
- };
763
- }
764
- async function handlePrePushCommand(args) {
765
- const options = parseArgs2(args);
766
- const projectPath = getWorkingDirectory(void 0, true);
767
- try {
768
- const autonomyConfig = await getAutonomyConfig(projectPath);
769
- if (options.bypassed) {
770
- console.error(pc.yellow("Pre-push check bypassed by user"));
771
- if (autonomyConfig.pushBlocking.logBypasses) {
772
- await recordBypass(
773
- projectPath,
774
- "git-push",
775
- void 0,
776
- "pre-push-bypass",
777
- "env",
778
- "User set TRIE_BYPASS=1"
779
- );
780
- }
781
- process.exit(0);
782
- }
783
- const perception = await perceiveCurrentChanges(projectPath);
784
- const files = perception.diffSummary.files.map((f) => f.filePath);
785
- if (files.length === 0) {
786
- console.error(pc.green("\u2713 No changes to check"));
787
- process.exit(0);
788
- }
789
- console.error(pc.cyan(`
790
- \u{1F50D} Checking ${files.length} file(s) before push...
791
- `));
792
- const reasoning = await reasonAboutChangesHumanReadable(projectPath, files, {
793
- runAgents: !options.quick,
794
- scanContext: {
795
- config: { timeoutMs: options.quick ? 15e3 : 3e4 }
796
- }
797
- });
798
- if (options.block && autonomyConfig.pushBlocking.enabled) {
799
- const issues = collectIssuesForBlocking(reasoning);
800
- for (const issue of issues) {
801
- await trackIssueOccurrence(
802
- projectPath,
803
- issue.file,
804
- issue.line,
805
- issue.severity,
806
- autonomyConfig
807
- );
808
- }
809
- const blockResult = shouldBlockPush(issues, autonomyConfig);
810
- if (blockResult.blocked) {
811
- console.error("");
812
- console.error(pc.red("\u2501".repeat(60)));
813
- console.error(pc.bold(pc.red("PUSH BLOCKED")));
814
- console.error(pc.red("\u2501".repeat(60)));
815
- console.error("");
816
- console.error(pc.yellow(`Found ${blockResult.blockingIssues.length} blocking issue(s):`));
817
- console.error("");
818
- for (const issue of blockResult.blockingIssues.slice(0, 5)) {
819
- const icon = issue.severity === "critical" ? "[!]" : "[!]";
820
- console.error(` ${icon} ${pc.dim(issue.file)}${issue.line ? `:${issue.line}` : ""}`);
821
- console.error(` ${issue.issue.slice(0, 80)}${issue.issue.length > 80 ? "..." : ""}`);
822
- }
823
- if (blockResult.blockingIssues.length > 5) {
824
- console.error(` ... and ${blockResult.blockingIssues.length - 5} more`);
825
- }
826
- console.error("");
827
- console.error(pc.dim("\u2500".repeat(60)));
828
- if (blockResult.bypassInstructions) {
829
- console.error("");
830
- console.error(pc.yellow("To bypass (not recommended):"));
831
- for (const line of blockResult.bypassInstructions.split("\n")) {
832
- console.error(pc.dim(line));
833
- }
834
- }
835
- console.error("");
836
- console.error(pc.cyan("To fix issues:"));
837
- console.error(pc.dim(" trie fix # Auto-fix trivial issues"));
838
- console.error(pc.dim(" trie check # See all issues"));
839
- console.error("");
840
- process.exit(2);
841
- } else if (blockResult.bypassed) {
842
- console.error(pc.yellow(`Bypassed: ${blockResult.blockingIssues.length} issue(s) ignored`));
843
- }
844
- }
845
- const riskLevel = reasoning.original?.riskLevel || "low";
846
- const riskColor = riskLevel === "critical" ? pc.red : riskLevel === "high" ? pc.yellow : riskLevel === "medium" ? pc.blue : pc.green;
847
- console.error("");
848
- console.error(riskColor(`Risk: ${riskLevel.toUpperCase()}`));
849
- console.error(pc.dim(reasoning.summary || "No significant risks detected"));
850
- console.error("");
851
- process.exit(0);
852
- } catch (error) {
853
- const friendly = formatFriendlyError(error);
854
- console.error(pc.yellow(`Pre-push check error: ${friendly.userMessage}`));
855
- console.error(pc.dim("Allowing push to continue."));
856
- process.exit(0);
857
- }
858
- }
859
- function collectIssuesForBlocking(reasoning) {
860
- const issues = [];
861
- if (reasoning.original?.files) {
862
- for (const file of reasoning.original.files) {
863
- if (file.level === "critical" || file.level === "high") {
864
- issues.push({
865
- file: file.file,
866
- severity: file.level === "critical" ? "critical" : "serious",
867
- issue: file.reasons?.join("; ") || "High-risk file"
868
- });
869
- }
870
- }
871
- }
872
- if (reasoning.scanResults?.issues) {
873
- for (const issue of reasoning.scanResults.issues) {
874
- if (issue.severity === "critical" || issue.severity === "serious") {
875
- issues.push({
876
- file: issue.file,
877
- line: issue.line,
878
- severity: issue.severity,
879
- issue: issue.issue
880
- });
881
- }
882
- }
883
- }
884
- return issues;
885
- }
886
-
887
- // src/cli/hooks.ts
888
- import { writeFile, mkdir, chmod } from "fs/promises";
889
- import { join } from "path";
890
- import { existsSync } from "fs";
891
- import pc2 from "picocolors";
892
- async function installGitHooks2() {
893
- const workDir = getWorkingDirectory(void 0, true);
894
- const isRepo = await isGitRepo(workDir);
895
- if (!isRepo) {
896
- console.log(pc2.yellow("\u26A0 Not a git repository. Git hooks not installed."));
897
- return;
898
- }
899
- const hooksDir = join(workDir, ".git", "hooks");
900
- if (!existsSync(hooksDir)) {
901
- await mkdir(hooksDir, { recursive: true });
902
- }
903
- const prePushHook = `#!/bin/sh
904
- # Auto-sync ledger to shared storage before push
905
-
906
- # Only run if trie is initialized
907
- if [ ! -d ".trie" ]; then
908
- exit 0
909
- fi
910
-
911
- # Check if trie command is available
912
- if ! command -v trie >/dev/null 2>&1; then
913
- echo "Warning: trie command not found, skipping ledger sync"
914
- exit 0
915
- fi
916
-
917
- # Sync ledger to shared storage
918
- echo "Syncing ledger to shared storage..."
919
- trie sync push 2>/dev/null || true
920
-
921
- exit 0
922
- `;
923
- const postMergeHook = `#!/bin/sh
924
- # Auto-sync ledger from shared storage after merge
925
-
926
- # Only run if trie is initialized
927
- if [ ! -d ".trie" ]; then
928
- exit 0
929
- fi
930
-
931
- # Check if trie command is available
932
- if ! command -v trie >/dev/null 2>&1; then
933
- exit 0
934
- fi
935
-
936
- # Sync ledger from shared storage
937
- echo "Syncing ledger from shared storage..."
938
- trie sync pull 2>/dev/null || true
939
-
940
- exit 0
941
- `;
942
- const prePushPath = join(hooksDir, "pre-push");
943
- const postMergePath = join(hooksDir, "post-merge");
944
- await writeFile(prePushPath, prePushHook);
945
- await writeFile(postMergePath, postMergeHook);
946
- await chmod(prePushPath, 493);
947
- await chmod(postMergePath, 493);
948
- console.log(pc2.green("\u2713 Git hooks installed successfully"));
949
- console.log(pc2.dim(" pre-push: Syncs ledger to shared storage"));
950
- console.log(pc2.dim(" post-merge: Syncs ledger from shared storage"));
951
- }
952
- async function checkGitHooks() {
953
- const workDir = getWorkingDirectory(void 0, true);
954
- const isRepo = await isGitRepo(workDir);
955
- if (!isRepo) {
956
- return {
957
- isGitRepo: false,
958
- prePushInstalled: false,
959
- postMergeInstalled: false
960
- };
961
- }
962
- const hooksDir = join(workDir, ".git", "hooks");
963
- const prePushPath = join(hooksDir, "pre-push");
964
- const postMergePath = join(hooksDir, "post-merge");
965
- return {
966
- isGitRepo: true,
967
- prePushInstalled: existsSync(prePushPath),
968
- postMergeInstalled: existsSync(postMergePath)
969
- };
970
- }
971
- async function handlePreCommitCommand(_args) {
972
- console.log(pc2.cyan("\u{1F50D} Running pre-commit hook..."));
973
- console.log(pc2.dim("Checking ledger for issues in staged files...\n"));
974
- try {
975
- const { execSync } = await import("child_process");
976
- const workDir = getWorkingDirectory(void 0, true);
977
- let stagedFiles;
978
- try {
979
- const output = execSync("git diff --cached --name-only --diff-filter=ACMR", {
980
- cwd: workDir,
981
- encoding: "utf-8"
982
- });
983
- stagedFiles = output.trim().split("\n").filter(Boolean);
984
- if (stagedFiles.length === 0) {
985
- console.log(pc2.green("\u2713 No staged files to check"));
986
- process.exit(0);
987
- }
988
- console.log(pc2.dim(`Checking ${stagedFiles.length} staged file(s)...
989
- `));
990
- } catch (error) {
991
- console.error(pc2.yellow("\u26A0 Could not get staged files, skipping pre-commit check"));
992
- process.exit(0);
993
- }
994
- const { searchIssues: searchIssues2 } = await import("../issue-store-ZIRP23EP.js");
995
- let criticalIssues = 0;
996
- let totalIssues = 0;
997
- const issuesByFile = /* @__PURE__ */ new Map();
998
- for (const file of stagedFiles) {
999
- const results = await searchIssues2(file, {
1000
- workDir,
1001
- limit: 100,
1002
- includeResolved: false
1003
- // Only unresolved issues
1004
- });
1005
- if (results.length > 0) {
1006
- totalIssues += results.length;
1007
- const issues = results.map((r) => ({
1008
- severity: r.issue.severity,
1009
- issue: r.issue.issue
1010
- }));
1011
- const critical = issues.filter((i) => i.severity === "critical").length;
1012
- criticalIssues += critical;
1013
- issuesByFile.set(file, issues);
1014
- }
1015
- }
1016
- if (criticalIssues > 0) {
1017
- console.log(pc2.red(`\u2717 Found ${criticalIssues} critical issue(s) in staged files
1018
- `));
1019
- for (const [file, issues] of issuesByFile.entries()) {
1020
- const critical = issues.filter((i) => i.severity === "critical");
1021
- if (critical.length > 0) {
1022
- console.log(pc2.red(` ${file}:`));
1023
- for (const issue of critical) {
1024
- console.log(pc2.dim(` \u2022 ${issue.issue.slice(0, 80)}...`));
1025
- }
1026
- console.log();
1027
- }
1028
- }
1029
- console.log(pc2.dim("Fix critical issues before committing, or use:"));
1030
- console.log(pc2.dim(" git commit --no-verify (to skip this check)\n"));
1031
- process.exit(1);
1032
- } else if (totalIssues > 0) {
1033
- console.log(pc2.yellow(`\u26A0 Found ${totalIssues} non-critical issue(s) in staged files`));
1034
- console.log(pc2.dim("(Not blocking commit - only critical issues block commits)\n"));
1035
- process.exit(0);
1036
- } else {
1037
- console.log(pc2.green("\u2713 No issues found in staged files"));
1038
- console.log(pc2.dim("Commit allowed\n"));
1039
- process.exit(0);
1040
- }
1041
- } catch (error) {
1042
- console.error(pc2.red("Error in pre-commit hook:"), error);
1043
- console.log(pc2.yellow("\n\u26A0 Pre-commit check failed, but not blocking commit"));
1044
- console.log(pc2.dim("(Hook errors never block commits)\n"));
1045
- process.exit(0);
1046
- }
1047
- }
1048
- async function handlePostCommitCommand(_args) {
1049
- console.log(pc2.cyan("\u{1F4DD} Running post-commit hook..."));
1050
- try {
1051
- const workDir = getWorkingDirectory(void 0, true);
1052
- const { spawn } = await import("child_process");
1053
- const updateProcess = spawn("node", [
1054
- "-e",
1055
- `
1056
- const { updateContextAfterCommit } = require('./context.js');
1057
- updateContextAfterCommit('${workDir}').catch(console.error);
1058
- `
1059
- ], {
1060
- stdio: "pipe",
1061
- env: process.env
1062
- });
1063
- updateProcess.on("close", (code) => {
1064
- if (code === 0) {
1065
- console.log(pc2.green("\u2713 Post-commit hook completed - context updated"));
1066
- } else {
1067
- console.log(pc2.yellow("\u26A0 Post-commit hook completed with warnings"));
1068
- }
1069
- });
1070
- updateProcess.unref();
1071
- } catch (error) {
1072
- console.error(pc2.yellow("Warning in post-commit hook:"), error);
1073
- }
1074
- }
1075
-
1076
- // src/cli/setup.ts
1077
- import pc3 from "picocolors";
1078
- import { readFileSync, existsSync as existsSync2 } from "fs";
1079
- import { join as join2 } from "path";
1080
- async function handleSetupCommand(args) {
1081
- const subcommand = args[0]?.toLowerCase();
1082
- if (subcommand === "slack") {
1083
- await setupSlackIntegration(args.slice(1));
1084
- return;
1085
- }
1086
- if (subcommand === "test-slack") {
1087
- await testSlackIntegration();
1088
- return;
1089
- }
1090
- showSetup();
1091
- }
1092
- async function setupSlackIntegration(args) {
1093
- const webhookUrl = args[0];
1094
- if (!webhookUrl) {
1095
- console.log(`
1096
- \u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557
1097
- \u2551 Slack Integration Setup \u2551
1098
- \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D
1099
-
1100
- STEP 1: Create a Slack Webhook
1101
- ${pc3.dim("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500")}
1102
- 1. Go to https://api.slack.com/apps
1103
- 2. Click "Create New App" > "From scratch"
1104
- 3. Name your app (e.g. "Trie Security Bot")
1105
- 4. Choose your workspace
1106
- 5. Go to "Incoming Webhooks" in the sidebar
1107
- 6. Toggle "Activate Incoming Webhooks" to On
1108
- 7. Click "Add New Webhook to Workspace"
1109
- 8. Choose a channel (e.g. #security, #alerts)
1110
- 9. Copy the webhook URL
1111
-
1112
- STEP 2: Configure Trie
1113
- ${pc3.dim("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500")}
1114
- Run: trie setup slack <webhook-url> [channel]
1115
-
1116
- EXAMPLES:
1117
- trie setup slack https://hooks.slack.com/services/T.../B.../... #security
1118
- trie setup slack https://hooks.slack.com/services/T.../B.../...
1119
-
1120
- STEP 3: Test the Integration
1121
- ${pc3.dim("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500")}
1122
- Run: trie setup test-slack
1123
-
1124
- WHAT TRIE SENDS TO SLACK:
1125
- \u2022 Decision extractions from incidents
1126
- \u2022 Active blockers and risk predictions
1127
- \u2022 Daily/weekly team summaries
1128
- \u2022 Critical security findings
1129
- \u2022 Auto-escalation notifications
1130
- `);
1131
- return;
1132
- }
1133
- const channel = args[1];
1134
- try {
1135
- if (!webhookUrl.startsWith("https://hooks.slack.com/services/")) {
1136
- console.error(pc3.red("Error:"), "Invalid Slack webhook URL format");
1137
- console.error("Expected: https://hooks.slack.com/services/...");
1138
- process.exit(1);
1139
- }
1140
- const config = await loadConfig();
1141
- const updatedConfig = {
1142
- ...config,
1143
- integrations: {
1144
- ...config.integrations,
1145
- slack: {
1146
- enabled: true,
1147
- webhook: webhookUrl,
1148
- channel: channel || void 0
1149
- }
1150
- }
1151
- };
1152
- await saveConfig(updatedConfig);
1153
- console.log(pc3.green("\u2713"), "Slack integration configured successfully!");
1154
- if (channel) {
1155
- console.log(pc3.dim(` Channel: ${channel}`));
1156
- }
1157
- console.log(pc3.dim(` Webhook: ${webhookUrl.substring(0, 50)}...`));
1158
- console.log(`
1159
- ${pc3.bold("Next steps:")}
1160
- 1. Test the integration: ${pc3.cyan("trie setup test-slack")}
1161
- 2. Configure auto-escalation (optional):
1162
- ${pc3.dim("Add to .trie/config.json:")}
1163
- ${pc3.dim(`{
1164
- "autoEscalation": {
1165
- "enabled": true,
1166
- "webhookUrl": "${webhookUrl}",
1167
- "quietHours": { "start": "21:00", "end": "08:00" }
1168
- }
1169
- }`)}
1170
- 3. Notifications will be sent for:
1171
- \u2022 Critical security issues
1172
- \u2022 Team assignments and escalations
1173
- \u2022 Daily/weekly summaries
1174
- `);
1175
- } catch (error) {
1176
- console.error(pc3.red("Error configuring Slack integration:"), error);
1177
- process.exit(1);
1178
- }
1179
- }
1180
- async function testSlackIntegration() {
1181
- try {
1182
- const config = await loadConfig();
1183
- if (!config.integrations?.slack?.enabled || !config.integrations.slack.webhook) {
1184
- console.error(pc3.red("Error:"), "Slack integration not configured");
1185
- console.error("Run:", pc3.cyan("trie setup slack <webhook-url>"));
1186
- process.exit(1);
1187
- }
1188
- console.log(pc3.cyan("\u{1F50D}"), "Testing Slack integration...");
1189
- const slack = new SlackIntegration({
1190
- webhookUrl: config.integrations.slack.webhook,
1191
- channel: config.integrations.slack.channel
1192
- });
1193
- const success = await slack.testConnection();
1194
- if (success) {
1195
- console.log(pc3.green("\u2713"), "Slack integration test successful!");
1196
- console.log(pc3.dim(" Check your Slack channel for the test message"));
1197
- } else {
1198
- console.log(pc3.red("\u2717"), "Slack integration test failed");
1199
- console.log(pc3.dim(" Check your webhook URL and network connection"));
1200
- process.exit(1);
1201
- }
1202
- } catch (error) {
1203
- console.error(pc3.red("Error testing Slack integration:"), error);
1204
- process.exit(1);
1205
- }
1206
- }
1207
- function showSetup() {
1208
- const config = getCurrentConfig();
1209
- const hasSlack = config?.integrations?.slack?.enabled;
1210
- const hasGitHub = config?.apiKeys?.github;
1211
- const hasLinear = config?.apiKeys?.linear;
1212
- console.log(`
1213
- \u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557
1214
- \u2551 Trie Agent Setup \u2551
1215
- \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D
1216
-
1217
- STEP 1: API Keys
1218
- ${pc3.dim("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500")}
1219
- Trie can work offline, but performs best with these keys:
1220
-
1221
- 1. ANTHROPIC_API_KEY (Required for AI analysis)
1222
- Get it: https://console.anthropic.com/
1223
-
1224
- 2. LINEAR_API_KEY (Required for JIT defect prediction)
1225
- Get it: https://linear.app/settings/api
1226
-
1227
- 3. GITHUB_TOKEN (Optional for GitHub integration)
1228
- Get it: https://github.com/settings/tokens
1229
-
1230
- Set them in your environment (~/.zshrc) or .trie/config.json:
1231
- export ANTHROPIC_API_KEY=sk-ant-...
1232
- export LINEAR_API_KEY=lin_api_...
1233
- export GITHUB_TOKEN=ghp_...
1234
-
1235
- STEP 2: Team Integrations
1236
- ${pc3.dim("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500")}
1237
- ${hasSlack ? pc3.green("\u2713") : pc3.red("\u2717")} Slack: ${hasSlack ? "Configured" : "trie setup slack <webhook>"}
1238
- ${hasGitHub ? pc3.green("\u2713") : pc3.red("\u2717")} GitHub: ${hasGitHub ? "Configured" : "Set GITHUB_TOKEN"}
1239
- ${hasLinear ? pc3.green("\u2713") : pc3.red("\u2717")} Linear: ${hasLinear ? "Configured" : "Set LINEAR_API_KEY"}
1240
-
1241
- SLACK SETUP:
1242
- trie setup slack # Show Slack setup guide
1243
- trie setup slack <webhook> [channel] # Configure Slack webhook
1244
- trie setup test-slack # Test Slack integration
1245
-
1246
- STEP 3: Configure for your AI tool
1247
- ${pc3.dim("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500")}
1248
-
1249
- For CLAUDE CODE:
1250
- claude mcp add Trie -- npx @triedotdev/mcp
1251
-
1252
- For CURSOR (~/.cursor/mcp.json):
1253
- {
1254
- "mcpServers": {
1255
- "Trie": {
1256
- "command": "npx",
1257
- "args": ["-y", "@triedotdev/mcp"],
1258
- "env": {
1259
- "ANTHROPIC_API_KEY": "your-key-here",
1260
- "LINEAR_API_KEY": "your-key-here"
1261
- }
1262
- }
1263
- }
1264
- }
1265
-
1266
- STEP 4: Get started!
1267
- ${pc3.dim("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500")}
1268
- Ask your AI assistant: "Use trie" for the menu, or "Use trie_fix" for fixes
1269
- `);
1270
- }
1271
- function getCurrentConfig() {
1272
- try {
1273
- const configPath = join2(getTrieDirectory(getWorkingDirectory(void 0, true)), "config.json");
1274
- if (existsSync2(configPath)) {
1275
- return JSON.parse(readFileSync(configPath, "utf-8"));
1276
- }
1277
- } catch {
1278
- }
1279
- return null;
1280
- }
1281
-
1282
- // src/cli/auto-fix.ts
1283
- import { readFile as readFile2 } from "fs/promises";
1284
- import { existsSync as existsSync4 } from "fs";
1285
- import { createInterface } from "readline";
1286
- import pc4 from "picocolors";
1287
-
1288
- // src/utils/auto-fix-apply.ts
1289
- import { readFile, writeFile as writeFile2 } from "fs/promises";
1290
- import { existsSync as existsSync3 } from "fs";
1291
- async function applyAutoFix(fix) {
1292
- try {
1293
- if (!existsSync3(fix.file)) {
1294
- return false;
1295
- }
1296
- const content = await readFile(fix.file, "utf-8");
1297
- const lines = content.split("\n");
1298
- if (fix.line === void 0 || fix.line < 1 || fix.line > lines.length) {
1299
- return false;
1300
- }
1301
- const lineIndex = fix.line - 1;
1302
- let newContent;
1303
- if (fix.type === "remove-console-log" || fix.type === "remove-debugger" || fix.fixed === "") {
1304
- lines.splice(lineIndex, 1);
1305
- newContent = lines.join("\n");
1306
- } else if (fix.type === "remove-emoji") {
1307
- const emojiPattern = /[\u{1F300}-\u{1F9FF}\u{2600}-\u{26FF}\u{2700}-\u{27BF}]/gu;
1308
- lines[lineIndex] = lines[lineIndex].replace(emojiPattern, "");
1309
- newContent = lines.join("\n");
1310
- } else if (fix.fixed) {
1311
- lines[lineIndex] = fix.fixed;
1312
- newContent = lines.join("\n");
1313
- } else {
1314
- return false;
1315
- }
1316
- await writeFile2(fix.file, newContent);
1317
- return true;
1318
- } catch {
1319
- return false;
1320
- }
1321
- }
1322
-
1323
- // src/cli/auto-fix.ts
1324
- function detectAutoFixes(issues) {
1325
- const fixes = [];
1326
- for (const issue of issues) {
1327
- const fix = generateFixForIssue(issue);
1328
- if (fix) {
1329
- fixes.push(fix);
1330
- }
1331
- }
1332
- return fixes;
1333
- }
1334
- function generateFixForIssue(issue) {
1335
- if (!issue.line) return null;
1336
- if (issue.issue.toLowerCase().includes("console.log") || issue.issue.toLowerCase().includes("console log")) {
1337
- return {
1338
- id: `fix-${issue.id}`,
1339
- file: issue.file,
1340
- line: issue.line,
1341
- original: "",
1342
- // Will be filled when reading file
1343
- fixed: "",
1344
- // Will be deletion
1345
- type: "remove-console-log",
1346
- category: "trivial",
1347
- description: "Remove console.log statement",
1348
- confidence: 0.95
1349
- };
1350
- }
1351
- if (issue.issue.toLowerCase().includes("debugger")) {
1352
- return {
1353
- id: `fix-${issue.id}`,
1354
- file: issue.file,
1355
- line: issue.line,
1356
- original: "",
1357
- fixed: "",
1358
- type: "remove-debugger",
1359
- category: "trivial",
1360
- description: "Remove debugger statement",
1361
- confidence: 0.98
1362
- };
1363
- }
1364
- return null;
1365
- }
1366
- async function promptUser(question) {
1367
- const rl = createInterface({
1368
- input: process.stdin,
1369
- output: process.stderr
1370
- });
1371
- return new Promise((resolve2) => {
1372
- rl.question(question, (answer) => {
1373
- rl.close();
1374
- resolve2(answer.toLowerCase().trim());
1375
- });
1376
- });
1377
- }
1378
- function displayFixPreview(fix, index, total) {
1379
- console.error("");
1380
- console.error(pc4.cyan(`\u2501\u2501\u2501 Fix ${index + 1}/${total} \u2501\u2501\u2501`));
1381
- console.error(pc4.dim(`File: ${fix.file}${fix.line ? `:${fix.line}` : ""}`));
1382
- console.error(pc4.dim(`Type: ${fix.type}`));
1383
- console.error(pc4.yellow(`Description: ${fix.description}`));
1384
- console.error("");
1385
- if (fix.original) {
1386
- console.error(pc4.red("- " + fix.original));
1387
- }
1388
- if (fix.fixed) {
1389
- console.error(pc4.green("+ " + fix.fixed));
1390
- } else if (fix.original) {
1391
- console.error(pc4.dim(" (line will be removed)"));
1392
- }
1393
- console.error("");
1394
- }
1395
- async function loadFixContent(fixes) {
1396
- const enrichedFixes = [];
1397
- for (const fix of fixes) {
1398
- if (!fix.line || !existsSync4(fix.file)) {
1399
- continue;
1400
- }
1401
- try {
1402
- const content = await readFile2(fix.file, "utf-8");
1403
- const lines = content.split("\n");
1404
- const originalLine = lines[fix.line - 1];
1405
- if (originalLine) {
1406
- enrichedFixes.push({
1407
- ...fix,
1408
- original: originalLine
1409
- });
1410
- }
1411
- } catch {
1412
- }
1413
- }
1414
- return enrichedFixes;
1415
- }
1416
- async function handleAutoFixCommand(args, issues) {
1417
- const projectPath = getWorkingDirectory(void 0, true);
1418
- const config = await getAutonomyConfig(projectPath);
1419
- if (!config.autoFix.enabled) {
1420
- console.error(pc4.yellow("Auto-fix is disabled in config."));
1421
- console.error(pc4.dim("Enable it with: trie config set autoFix.enabled true"));
1422
- return;
1423
- }
1424
- const dryRun = args.includes("--dry-run");
1425
- const skipConfirm = args.includes("--yes") || args.includes("-y");
1426
- if (!issues || issues.length === 0) {
1427
- console.error(pc4.yellow("No issues provided. Run trie watch first, or use trie_fix via MCP."));
1428
- console.error(pc4.dim(" trie watch"));
1429
- return;
1430
- }
1431
- let fixes = detectAutoFixes(issues);
1432
- if (fixes.length === 0) {
1433
- console.error(pc4.green("\u2713 No auto-fixable issues found."));
1434
- return;
1435
- }
1436
- fixes = fixes.filter((fix) => shouldAutoFix(fix, config));
1437
- if (fixes.length === 0) {
1438
- console.error(pc4.yellow("Found fixable issues but they are not in allowed categories."));
1439
- console.error(pc4.dim("Allowed categories: " + config.autoFix.categories.join(", ")));
1440
- return;
1441
- }
1442
- fixes = await loadFixContent(fixes);
1443
- if (fixes.length === 0) {
1444
- console.error(pc4.yellow("Could not load fix content from files."));
1445
- return;
1446
- }
1447
- console.error("");
1448
- console.error(pc4.cyan("\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501"));
1449
- console.error(pc4.bold("Auto-Fix: Human-in-the-Loop"));
1450
- console.error(pc4.cyan("\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501"));
1451
- console.error("");
1452
- console.error(`Found ${pc4.bold(fixes.length.toString())} auto-fixable issues:`);
1453
- const byCategory = /* @__PURE__ */ new Map();
1454
- for (const fix of fixes) {
1455
- byCategory.set(fix.category, (byCategory.get(fix.category) || 0) + 1);
1456
- }
1457
- for (const [cat, count] of byCategory) {
1458
- console.error(` \u2022 ${cat}: ${count}`);
1459
- }
1460
- console.error("");
1461
- if (dryRun) {
1462
- console.error(pc4.yellow("[DRY RUN] No changes will be made."));
1463
- console.error("");
1464
- for (let i = 0; i < fixes.length; i++) {
1465
- displayFixPreview(fixes[i], i, fixes.length);
1466
- }
1467
- return;
1468
- }
1469
- if (config.autoFix.askFirst && !skipConfirm) {
1470
- console.error(pc4.yellow("Trie will ask before applying each fix."));
1471
- console.error("");
1472
- const response = await promptUser(
1473
- `Apply all ${fixes.length} fixes? (y)es / (n)o / (r)eview each: `
1474
- );
1475
- if (response === "n" || response === "no") {
1476
- console.error(pc4.dim("Cancelled."));
1477
- return;
1478
- }
1479
- if (response === "r" || response === "review") {
1480
- let applied2 = 0;
1481
- let skipped = 0;
1482
- for (let i = 0; i < fixes.length; i++) {
1483
- const fix = fixes[i];
1484
- displayFixPreview(fix, i, fixes.length);
1485
- const answer = await promptUser("Apply this fix? (y/n/q): ");
1486
- if (answer === "q" || answer === "quit") {
1487
- console.error(pc4.dim("Stopped."));
1488
- break;
1489
- }
1490
- if (answer === "y" || answer === "yes") {
1491
- const success = await applyAutoFix(fix);
1492
- if (success) {
1493
- console.error(pc4.green(`\u2713 Applied fix to ${fix.file}:${fix.line}`));
1494
- applied2++;
1495
- } else {
1496
- console.error(pc4.red(`\u2717 Failed to apply fix`));
1497
- }
1498
- } else {
1499
- console.error(pc4.dim("Skipped."));
1500
- skipped++;
1501
- }
1502
- }
1503
- console.error("");
1504
- console.error(pc4.cyan("\u2501\u2501\u2501 Summary \u2501\u2501\u2501"));
1505
- console.error(`Applied: ${pc4.green(applied2.toString())}`);
1506
- console.error(`Skipped: ${pc4.dim(skipped.toString())}`);
1507
- return;
1508
- }
1509
- }
1510
- console.error("");
1511
- console.error(pc4.cyan("Applying fixes..."));
1512
- console.error("");
1513
- let applied = 0;
1514
- let failed = 0;
1515
- for (const fix of fixes) {
1516
- const success = await applyAutoFix(fix);
1517
- if (success) {
1518
- console.error(pc4.green(`\u2713 Applied fix to ${fix.file}:${fix.line}`));
1519
- applied++;
1520
- } else {
1521
- console.error(pc4.red(`\u2717 Failed to apply fix to ${fix.file}:${fix.line}`));
1522
- failed++;
1523
- }
1524
- }
1525
- console.error("");
1526
- console.error(pc4.cyan("\u2501\u2501\u2501 Complete \u2501\u2501\u2501"));
1527
- console.error(`Applied: ${pc4.green(applied.toString())}`);
1528
- if (failed > 0) {
1529
- console.error(`Failed: ${pc4.red(failed.toString())}`);
1530
- }
1531
- }
1532
-
1533
- // src/cli/goal.ts
1534
- import pc5 from "picocolors";
1535
- async function handleGoalCommand(args) {
1536
- const projectPath = getWorkingDirectory(void 0, true);
1537
- const projectState = getProjectState(projectPath);
1538
- await projectState.load();
1539
- const subcommand = args[0];
1540
- const restArgs = args.slice(1);
1541
- switch (subcommand) {
1542
- case "add":
1543
- case "create":
1544
- await addGoal(projectState, restArgs);
1545
- break;
1546
- case "list":
1547
- case "ls":
1548
- await listGoals(projectState);
1549
- break;
1550
- case "complete":
1551
- case "done":
1552
- await completeGoal(projectState, restArgs[0]);
1553
- break;
1554
- case "delete":
1555
- case "rm":
1556
- await deleteGoal(projectState, restArgs[0]);
1557
- break;
1558
- default:
1559
- if (subcommand && !subcommand.startsWith("-")) {
1560
- await addGoal(projectState, [subcommand, ...restArgs]);
1561
- } else {
1562
- printGoalHelp();
1563
- }
1564
- }
1565
- }
1566
- async function addGoal(projectState, args) {
1567
- const description = args.join(" ").replace(/^["']|["']$/g, "");
1568
- if (!description) {
1569
- console.error(pc5.red("Please provide a goal description."));
1570
- console.error(pc5.dim('Example: trie goal add "Reduce auth issues by 50%"'));
1571
- return;
1572
- }
1573
- const categoryMatch = description.match(/--category[=\s](\w+)/);
1574
- const targetMatch = description.match(/--target[=\s](\d+)/);
1575
- const deadlineMatch = description.match(/--deadline[=\s](\S+)/);
1576
- let cleanDesc = description.replace(/--category[=\s]\w+/g, "").replace(/--target[=\s]\d+/g, "").replace(/--deadline[=\s]\S+/g, "").trim();
1577
- const reductionMatch = cleanDesc.match(/reduce.*?(\d+)%/i);
1578
- const eliminateMatch = cleanDesc.match(/eliminate|remove all|zero/i);
1579
- const streakMatch = cleanDesc.match(/(\d+)\s*days?\s*(streak|in a row|consecutive)/i);
1580
- let goalType = "custom";
1581
- let target = 100;
1582
- let metric = "progress";
1583
- if (reductionMatch && reductionMatch[1]) {
1584
- goalType = "reduction";
1585
- target = parseInt(reductionMatch[1], 10);
1586
- metric = "reduction_percent";
1587
- } else if (eliminateMatch) {
1588
- goalType = "reduction";
1589
- target = 100;
1590
- metric = "elimination";
1591
- } else if (streakMatch && streakMatch[1]) {
1592
- goalType = "streak";
1593
- target = parseInt(streakMatch[1], 10);
1594
- metric = "days_streak";
1595
- }
1596
- if (targetMatch && targetMatch[1]) {
1597
- target = parseInt(targetMatch[1], 10);
1598
- }
1599
- const category = categoryMatch?.[1] || detectCategory(cleanDesc);
1600
- const deadline = /* @__PURE__ */ new Date();
1601
- if (deadlineMatch && deadlineMatch[1]) {
1602
- const days = parseInt(deadlineMatch[1], 10);
1603
- deadline.setDate(deadline.getDate() + (isNaN(days) ? 14 : days));
1604
- } else {
1605
- deadline.setDate(deadline.getDate() + 14);
1606
- }
1607
- const workDir = getWorkingDirectory(void 0, true);
1608
- const initialValue = await measureInitialGoalValue(cleanDesc, workDir);
1609
- const goal = {
1610
- id: `goal-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
1611
- description: cleanDesc,
1612
- type: goalType,
1613
- metric,
1614
- target,
1615
- currentValue: initialValue,
1616
- startValue: initialValue,
1617
- status: "active",
1618
- autoGenerated: false,
1619
- // User-created
1620
- createdAt: (/* @__PURE__ */ new Date()).toISOString(),
1621
- updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
1622
- deadline: deadline.toISOString(),
1623
- category
1624
- };
1625
- await projectState.addGoal(goal);
1626
- console.error("");
1627
- console.error(pc5.green("\u2713 Goal created"));
1628
- console.error("");
1629
- console.error(pc5.bold(cleanDesc));
1630
- console.error(pc5.dim(`ID: ${goal.id}`));
1631
- console.error(pc5.dim(`Type: ${goalType} | Target: ${target} | Category: ${category}`));
1632
- if (initialValue > 0) {
1633
- console.error(pc5.dim(`Starting value: ${initialValue} issues found`));
1634
- }
1635
- console.error(pc5.dim(`Deadline: ${deadline.toLocaleDateString()}`));
1636
- console.error("");
1637
- console.error(pc5.cyan("Progress will be tracked and you'll be notified when you achieve it!"));
1638
- }
1639
- async function listGoals(projectState) {
1640
- const goals = projectState.getAllGoals();
1641
- if (goals.length === 0) {
1642
- console.error(pc5.dim("No goals yet."));
1643
- console.error(pc5.dim('Create one: trie goal add "Reduce auth issues by 50%"'));
1644
- return;
1645
- }
1646
- console.error("");
1647
- console.error(pc5.bold("Goals"));
1648
- console.error("");
1649
- const active = goals.filter((g) => g.status === "active");
1650
- const achieved = goals.filter((g) => g.status === "achieved");
1651
- const other = goals.filter((g) => g.status !== "active" && g.status !== "achieved");
1652
- if (active.length > 0) {
1653
- console.error(pc5.cyan("Active:"));
1654
- for (const goal of active) {
1655
- const progress = calculateGoalProgress(goal);
1656
- const bar = renderProgressBar(progress);
1657
- const source = goal.autoGenerated ? pc5.dim("[auto]") : pc5.dim("[manual]");
1658
- console.error(` ${bar} ${goal.description} ${source}`);
1659
- console.error(pc5.dim(` ID: ${goal.id} | ${progress}% complete`));
1660
- }
1661
- console.error("");
1662
- }
1663
- if (achieved.length > 0) {
1664
- console.error(pc5.green("Achieved:"));
1665
- for (const goal of achieved) {
1666
- console.error(` [+] ${goal.description}`);
1667
- }
1668
- console.error("");
1669
- }
1670
- if (other.length > 0) {
1671
- console.error(pc5.dim("Other:"));
1672
- for (const goal of other) {
1673
- const icon = goal.status === "failed" ? "[X]" : goal.status === "paused" ? "[P]" : "\u2022";
1674
- console.error(` ${icon} ${goal.description} (${goal.status})`);
1675
- }
1676
- }
1677
- }
1678
- async function completeGoal(projectState, goalId) {
1679
- if (!goalId) {
1680
- console.error(pc5.red("Please provide a goal ID."));
1681
- console.error(pc5.dim('Run "trie goal list" to see IDs.'));
1682
- return;
1683
- }
1684
- const goals = projectState.getAllGoals();
1685
- const goal = goals.find((g) => g.id === goalId || g.id.startsWith(goalId));
1686
- if (!goal) {
1687
- console.error(pc5.red(`Goal not found: ${goalId}`));
1688
- return;
1689
- }
1690
- await projectState.updateGoal(goal.id, {
1691
- status: "achieved",
1692
- currentValue: goal.target,
1693
- achievedAt: (/* @__PURE__ */ new Date()).toISOString()
1694
- });
1695
- console.error("");
1696
- console.error(pc5.green("Goal achieved!"));
1697
- console.error(pc5.bold(goal.description));
1698
- console.error("");
1699
- }
1700
- async function deleteGoal(projectState, goalId) {
1701
- if (!goalId) {
1702
- console.error(pc5.red("Please provide a goal ID."));
1703
- return;
1704
- }
1705
- const goals = projectState.getAllGoals();
1706
- const goal = goals.find((g) => g.id === goalId || g.id.startsWith(goalId));
1707
- if (!goal) {
1708
- console.error(pc5.red(`Goal not found: ${goalId}`));
1709
- return;
1710
- }
1711
- await projectState.updateGoal(goal.id, { status: "rejected" });
1712
- console.error(pc5.dim(`Removed goal: ${goal.description}`));
1713
- }
1714
- function calculateGoalProgress(goal) {
1715
- if (goal.target <= 0) return 0;
1716
- const startValue = goal.startValue ?? goal.currentValue;
1717
- if (startValue > goal.target) {
1718
- const totalReduction = startValue - goal.target;
1719
- if (totalReduction === 0) return 100;
1720
- const actualReduction = startValue - goal.currentValue;
1721
- return Math.round(actualReduction / totalReduction * 100);
1722
- }
1723
- return Math.round(goal.currentValue / goal.target * 100);
1724
- }
1725
- function renderProgressBar(percent) {
1726
- const width = 10;
1727
- const clamped = Math.max(0, Math.min(100, percent));
1728
- const filled = Math.round(clamped / 100 * width);
1729
- const empty = width - filled;
1730
- return `[${pc5.green("\u2588".repeat(filled))}${pc5.gray("\u2591".repeat(empty))}]`;
1731
- }
1732
- function detectCategory(description) {
1733
- const lower = description.toLowerCase();
1734
- if (/security|auth|password|token|xss|sql|inject/i.test(lower)) return "security";
1735
- if (/quality|bug|issue|error|fix/i.test(lower)) return "quality";
1736
- if (/performance|speed|fast|slow|latency/i.test(lower)) return "performance";
1737
- if (/coverage|test|spec/i.test(lower)) return "coverage";
1738
- return "general";
1739
- }
1740
- function printGoalHelp() {
1741
- console.error("");
1742
- console.error(pc5.bold("trie goal - Manage guardian goals"));
1743
- console.error("");
1744
- console.error(pc5.cyan("Usage:"));
1745
- console.error(' trie goal add "Reduce auth issues by 50%"');
1746
- console.error(" trie goal list");
1747
- console.error(" trie goal complete <id>");
1748
- console.error(" trie goal delete <id>");
1749
- console.error("");
1750
- console.error(pc5.cyan("Options for add:"));
1751
- console.error(" --category=security|quality|performance|coverage|general");
1752
- console.error(" --target=<number>");
1753
- console.error(" --deadline=<days>");
1754
- console.error("");
1755
- console.error(pc5.cyan("Examples:"));
1756
- console.error(' trie goal add "Eliminate all critical security issues"');
1757
- console.error(' trie goal add "7 days streak of clean builds"');
1758
- console.error(' trie goal add "Reduce test flakiness by 80%" --category=quality');
1759
- console.error("");
1760
- }
1761
- async function handleHypothesisCommand(args) {
1762
- const projectPath = getWorkingDirectory(void 0, true);
1763
- const projectState = getProjectState(projectPath);
1764
- await projectState.load();
1765
- const subcommand = args[0];
1766
- const restArgs = args.slice(1);
1767
- switch (subcommand) {
1768
- case "add":
1769
- case "create":
1770
- await addHypothesis(projectState, restArgs);
1771
- break;
1772
- case "list":
1773
- case "ls":
1774
- await listHypotheses(projectState);
1775
- break;
1776
- case "validate":
1777
- await validateHypothesis(projectState, restArgs[0]);
1778
- break;
1779
- case "invalidate":
1780
- await invalidateHypothesis(projectState, restArgs[0]);
1781
- break;
1782
- case "delete":
1783
- case "rm":
1784
- await deleteHypothesis(projectState, restArgs[0]);
1785
- break;
1786
- default:
1787
- if (subcommand && !subcommand.startsWith("-")) {
1788
- await addHypothesis(projectState, [subcommand, ...restArgs]);
1789
- } else {
1790
- printHypothesisHelp();
1791
- }
1792
- }
1793
- }
1794
- async function addHypothesis(projectState, args) {
1795
- const statement = args.join(" ").replace(/^["']|["']$/g, "");
1796
- if (!statement) {
1797
- console.error(pc5.red("Please provide a hypothesis statement."));
1798
- console.error(pc5.dim('Example: trie hypothesis add "Mondays have more bugs than Fridays"'));
1799
- return;
1800
- }
1801
- const categoryMatch = statement.match(/--category[=\s](\w+)/);
1802
- const testMatch = statement.match(/--test[=\s]["']([^"']+)["']/);
1803
- let cleanStatement = statement.replace(/--category[=\s]\w+/g, "").replace(/--test[=\s]["'][^"']+["']/g, "").trim();
1804
- const category = categoryMatch?.[1] || detectHypothesisCategory(cleanStatement);
1805
- const testCriteria = testMatch?.[1] || generateTestCriteria(cleanStatement);
1806
- const hypothesis = {
1807
- id: `hypo-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
1808
- statement: cleanStatement,
1809
- confidence: 0,
1810
- // No evidence yet
1811
- status: "testing",
1812
- evidence: [],
1813
- createdAt: (/* @__PURE__ */ new Date()).toISOString(),
1814
- updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
1815
- testCriteria,
1816
- category,
1817
- autoGenerated: false
1818
- };
1819
- await projectState.addHypothesis(hypothesis);
1820
- console.error("");
1821
- console.error(pc5.green("\u2713 Hypothesis created"));
1822
- console.error("");
1823
- console.error(pc5.bold(`"${cleanStatement}"`));
1824
- console.error(pc5.dim(`ID: ${hypothesis.id}`));
1825
- console.error(pc5.dim(`Status: testing | Confidence: untested | Category: ${category}`));
1826
- console.error("");
1827
- console.error(pc5.cyan("The agent will collect evidence and update confidence over time."));
1828
- console.error(pc5.dim(`Test criteria: ${testCriteria}`));
1829
- }
1830
- async function listHypotheses(projectState) {
1831
- const hypotheses = projectState.getAllHypotheses();
1832
- if (hypotheses.length === 0) {
1833
- console.error(pc5.dim("No hypotheses yet."));
1834
- console.error(pc5.dim('Create one: trie hypothesis add "Mondays have more bugs"'));
1835
- return;
1836
- }
1837
- console.error("");
1838
- console.error(pc5.bold("Hypotheses"));
1839
- console.error("");
1840
- const testing = hypotheses.filter((h) => h.status === "testing");
1841
- const validated = hypotheses.filter((h) => h.status === "validated");
1842
- const invalidated = hypotheses.filter((h) => h.status === "invalidated");
1843
- if (testing.length > 0) {
1844
- console.error(pc5.cyan("Testing:"));
1845
- for (const hypo of testing) {
1846
- const conf = Math.round(hypo.confidence * 100);
1847
- const confColor = conf >= 70 ? pc5.green : conf >= 40 ? pc5.yellow : pc5.red;
1848
- console.error(` [?] "${hypo.statement}" ${confColor(`(${conf}% confidence)`)}`);
1849
- console.error(pc5.dim(` ID: ${hypo.id} | Evidence: ${hypo.evidence.length} points`));
1850
- }
1851
- console.error("");
1852
- }
1853
- if (validated.length > 0) {
1854
- console.error(pc5.green("Validated:"));
1855
- for (const hypo of validated) {
1856
- console.error(` [+] "${hypo.statement}" (${Math.round(hypo.confidence * 100)}%)`);
1857
- }
1858
- console.error("");
1859
- }
1860
- if (invalidated.length > 0) {
1861
- console.error(pc5.red("Invalidated:"));
1862
- for (const hypo of invalidated) {
1863
- console.error(` [X] "${hypo.statement}"`);
1864
- }
1865
- }
1866
- }
1867
- async function validateHypothesis(projectState, hypoId) {
1868
- if (!hypoId) {
1869
- console.error(pc5.red("Please provide a hypothesis ID."));
1870
- return;
1871
- }
1872
- const hypotheses = projectState.getAllHypotheses();
1873
- const hypo = hypotheses.find((h) => h.id === hypoId || h.id.startsWith(hypoId));
1874
- if (!hypo) {
1875
- console.error(pc5.red(`Hypothesis not found: ${hypoId}`));
1876
- return;
1877
- }
1878
- await projectState.updateHypothesis(hypo.id, {
1879
- status: "validated",
1880
- confidence: 0.9,
1881
- validatedAt: (/* @__PURE__ */ new Date()).toISOString()
1882
- });
1883
- console.error(pc5.green(`Hypothesis validated: "${hypo.statement}"`));
1884
- }
1885
- async function invalidateHypothesis(projectState, hypoId) {
1886
- if (!hypoId) {
1887
- console.error(pc5.red("Please provide a hypothesis ID."));
1888
- return;
1889
- }
1890
- const hypotheses = projectState.getAllHypotheses();
1891
- const hypo = hypotheses.find((h) => h.id === hypoId || h.id.startsWith(hypoId));
1892
- if (!hypo) {
1893
- console.error(pc5.red(`Hypothesis not found: ${hypoId}`));
1894
- return;
1895
- }
1896
- await projectState.updateHypothesis(hypo.id, {
1897
- status: "invalidated",
1898
- confidence: 0.1
1899
- });
1900
- console.error(pc5.red(`Hypothesis invalidated: "${hypo.statement}"`));
1901
- }
1902
- async function deleteHypothesis(projectState, hypoId) {
1903
- if (!hypoId) {
1904
- console.error(pc5.red("Please provide a hypothesis ID."));
1905
- return;
1906
- }
1907
- const hypotheses = projectState.getAllHypotheses();
1908
- const hypo = hypotheses.find((h) => h.id === hypoId || h.id.startsWith(hypoId));
1909
- if (!hypo) {
1910
- console.error(pc5.red(`Hypothesis not found: ${hypoId}`));
1911
- return;
1912
- }
1913
- await projectState.updateHypothesis(hypo.id, { status: "retired" });
1914
- console.error(pc5.dim(`Removed hypothesis: "${hypo.statement}"`));
1915
- }
1916
- function detectHypothesisCategory(statement) {
1917
- const lower = statement.toLowerCase();
1918
- if (/monday|friday|weekend|morning|night|time|day|hour/i.test(lower)) return "timing";
1919
- if (/pattern|recurring|always|never|consistently/i.test(lower)) return "pattern";
1920
- if (/team|developer|engineer|review/i.test(lower)) return "team";
1921
- if (/code|file|function|class|module|refactor/i.test(lower)) return "code";
1922
- return "general";
1923
- }
1924
- function generateTestCriteria(statement) {
1925
- const lower = statement.toLowerCase();
1926
- if (lower.includes("monday")) {
1927
- return "Compare Monday issue counts to weekly average";
1928
- }
1929
- if (lower.includes("friday")) {
1930
- return "Compare Friday issue introduction rate to other days";
1931
- }
1932
- if (lower.includes("more bug") || lower.includes("more issue")) {
1933
- return "Compare issue counts between conditions over time";
1934
- }
1935
- if (lower.includes("causes") || lower.includes("leads to")) {
1936
- return "Track correlation between trigger and outcome events";
1937
- }
1938
- return "Collect supporting and contradicting evidence from scans";
1939
- }
1940
- function printHypothesisHelp() {
1941
- console.error("");
1942
- console.error(pc5.bold("trie hypothesis - Manage project hypotheses"));
1943
- console.error("");
1944
- console.error(pc5.cyan("Usage:"));
1945
- console.error(' trie hypothesis add "Mondays have more bugs than Fridays"');
1946
- console.error(" trie hypothesis list");
1947
- console.error(" trie hypothesis validate <id>");
1948
- console.error(" trie hypothesis invalidate <id>");
1949
- console.error(" trie hypothesis delete <id>");
1950
- console.error("");
1951
- console.error(pc5.cyan("Options for add:"));
1952
- console.error(" --category=timing|pattern|team|code|general");
1953
- console.error(' --test="<test criteria>"');
1954
- console.error("");
1955
- console.error(pc5.cyan("Examples:"));
1956
- console.error(' trie hypothesis add "Code reviews reduce bug rate"');
1957
- console.error(' trie hypothesis add "Auth module has highest churn"');
1958
- console.error(' trie hypothesis add "Deploy Fridays cause weekend incidents"');
1959
- console.error("");
1960
- }
3
+ cloudClient,
4
+ fetchDocuments,
5
+ fetchGraph,
6
+ fetchLedger,
7
+ fetchSignals,
8
+ getMachineFingerprint,
9
+ loadLocalConfig,
10
+ normalizeAndValidateApiBaseUrl,
11
+ persistAuthTokens,
12
+ revokeSession,
13
+ saveLocalConfig,
14
+ sessionCache
15
+ } from "../chunk-QR64Y5TI.js";
1961
16
 
1962
- // src/cli/tell.ts
1963
- import path3 from "path";
1964
- function escalateRisk(level) {
1965
- if (level === "low") return "medium";
1966
- if (level === "medium") return "high";
1967
- if (level === "high") return "critical";
1968
- return "critical";
1969
- }
1970
- function extractFilePathsFromDescription(description) {
1971
- const matches = description.match(/[\\w./_-]+\\.(ts|tsx|js|jsx|mjs|cjs)/gi);
1972
- if (!matches) return [];
1973
- const unique = /* @__PURE__ */ new Set();
1974
- matches.forEach((m) => unique.add(m.replace(/^\.\/+/, "")));
1975
- return Array.from(unique);
1976
- }
1977
- async function handleTellCommand(args) {
1978
- try {
1979
- const projectPath = getWorkingDirectory(void 0, true);
1980
- const description = args.join(" ").trim();
1981
- if (!description) {
1982
- console.log('Usage: trie tell "<incident description>"');
1983
- return;
1984
- }
1985
- const graph = new ContextGraph(projectPath);
1986
- const now = (/* @__PURE__ */ new Date()).toISOString();
1987
- const change = (await graph.getRecentChanges(1))[0];
1988
- const incident = await graph.addNode("incident", {
1989
- description,
1990
- severity: "major",
1991
- affectedUsers: null,
1992
- duration: null,
1993
- timestamp: now,
1994
- resolved: false,
1995
- resolution: null,
1996
- fixChangeId: change?.id ?? null,
1997
- reportedVia: "manual"
1998
- });
1999
- const linkedFiles = /* @__PURE__ */ new Set();
2000
- if (change) {
2001
- await graph.addEdge(change.id, incident.id, "leadTo");
2002
- await graph.addEdge(incident.id, change.id, "causedBy");
2003
- for (const filePath of change.data.files) {
2004
- linkedFiles.add(filePath);
2005
- const fileNode = await graph.getNode("file", path3.resolve(projectPath, filePath));
2006
- if (fileNode) {
2007
- const data = fileNode.data;
2008
- await graph.updateNode("file", fileNode.id, {
2009
- incidentCount: (data.incidentCount ?? 0) + 1,
2010
- riskLevel: escalateRisk(data.riskLevel)
2011
- });
2012
- }
2013
- }
2014
- }
2015
- const mentionedFiles = extractFilePathsFromDescription(description);
2016
- mentionedFiles.forEach((f) => linkedFiles.add(f));
2017
- const incidentIndex = new IncidentIndex(graph, projectPath);
2018
- incidentIndex.addIncidentToTrie(incident, Array.from(linkedFiles));
2019
- await exportToJson(graph);
2020
- console.log("Incident recorded.");
2021
- if (change) {
2022
- console.log(`Linked to change: ${change.id}`);
2023
- } else {
2024
- console.log("No recent change found; incident recorded without linkage.");
2025
- }
2026
- } catch (error) {
2027
- const friendly = formatFriendlyError(error);
2028
- console.error(friendly.userMessage);
2029
- }
2030
- }
17
+ // src/cli/main.ts
18
+ import { mkdir, writeFile } from "fs/promises";
19
+ import { existsSync } from "fs";
20
+ import { join } from "path";
2031
21
 
2032
- // src/cli/linear.ts
2033
- async function handleLinearCommand(args) {
2034
- try {
2035
- const subcommand = args[0]?.toLowerCase();
2036
- const workDir = getWorkingDirectory(void 0, true);
2037
- if (subcommand === "auth") {
2038
- const apiKey = args[1];
2039
- if (!apiKey) {
2040
- console.log("Usage: trie linear auth <your-linear-api-key>");
2041
- return;
2042
- }
2043
- const config = await loadConfig();
2044
- if (!config.apiKeys) {
2045
- config.apiKeys = {};
2046
- }
2047
- config.apiKeys.linear = apiKey;
2048
- await saveConfig(config);
2049
- console.log("Linear API key saved successfully.");
2050
- return;
2051
- }
2052
- if (subcommand === "sync") {
2053
- const graph = new ContextGraph(workDir);
2054
- const ingester = new LinearIngester(workDir, graph);
2055
- console.log("Syncing Linear tickets...");
2056
- await ingester.syncTickets();
2057
- console.log("Linear tickets synced successfully.");
2058
- return;
2059
- }
2060
- console.log("Usage:");
2061
- console.log(" trie linear auth <api-key> Save Linear API key");
2062
- console.log(" trie linear sync Sync active tickets");
2063
- } catch (error) {
2064
- const friendly = formatFriendlyError(error);
2065
- console.error(friendly.userMessage);
2066
- }
22
+ // src/cloud/teams.ts
23
+ async function listTeams() {
24
+ const data = await cloudClient.request("/api/teams");
25
+ return data.teams || data.items || [];
2067
26
  }
2068
-
2069
- // src/cli/gotcha.ts
2070
- import picocolors from "picocolors";
2071
- async function handleGotchaCommand() {
2072
- try {
2073
- const workDir = getWorkingDirectory(void 0, true);
2074
- const graph = new ContextGraph(workDir);
2075
- const predictor = new GotchaPredictor(workDir, graph);
2076
- console.log('Analyzing current changes for "gotchas"...');
2077
- const staged = await getStagedChanges(workDir);
2078
- const uncommitted = await getUncommittedChanges(workDir);
2079
- const allChanges = [...staged, ...uncommitted].map((c) => c.path);
2080
- const uniqueFiles = Array.from(new Set(allChanges));
2081
- if (uniqueFiles.length === 0) {
2082
- console.log("No changes detected to analyze.");
2083
- return;
2084
- }
2085
- const gotchas = await predictor.predictGotchas(uniqueFiles);
2086
- if (gotchas.length === 0) {
2087
- console.log(picocolors.green('\u2713 No "gotchas" predicted for your current changes.'));
2088
- return;
2089
- }
2090
- console.log(picocolors.yellow(`
2091
- Found ${gotchas.length} potential "gotchas":
2092
- `));
2093
- const storage = getStorage(workDir);
2094
- await storage.initialize();
2095
- const primaryFile = uniqueFiles[0];
2096
- for (const gotcha of gotchas) {
2097
- const ledgerGotcha = {
2098
- id: gotcha.id,
2099
- message: gotcha.message,
2100
- confidence: gotcha.confidence,
2101
- riskLevel: gotcha.riskLevel,
2102
- recommendation: gotcha.recommendation,
2103
- ...primaryFile && { file: primaryFile },
2104
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
2105
- ...gotcha.precedentId && { precedentId: gotcha.precedentId },
2106
- tags: gotcha.evidence.matchingPatterns,
2107
- evidence: gotcha.evidence,
2108
- resolved: false
2109
- };
2110
- await storage.storeGotcha(ledgerGotcha);
2111
- }
2112
- for (const gotcha of gotchas) {
2113
- const color = gotcha.riskLevel === "critical" ? picocolors.red : gotcha.riskLevel === "high" ? picocolors.yellow : picocolors.blue;
2114
- console.log(color(`[${gotcha.riskLevel.toUpperCase()}] ${gotcha.message}`));
2115
- const explanation = await predictor.synthesizeGotchaExplanation(gotcha);
2116
- if (explanation !== gotcha.message) {
2117
- console.log(picocolors.dim(` ${explanation}`));
2118
- }
2119
- console.log(picocolors.cyan(` \u{1F4A1} Recommendation: ${gotcha.recommendation}`));
2120
- console.log("");
2121
- }
2122
- console.log(picocolors.dim(`Stored ${gotchas.length} gotcha(s) in Ledger. View in: trie watch \u2192 Ledger tab`));
2123
- } catch (error) {
2124
- const friendly = formatFriendlyError(error);
2125
- console.error(friendly.userMessage);
27
+ async function switchTeam(teamId) {
28
+ const data = await cloudClient.request("/api/teams/switch", {
29
+ method: "POST",
30
+ body: JSON.stringify({ teamId })
31
+ });
32
+ const confirmedTeamId = data.team?.id;
33
+ if (confirmedTeamId && confirmedTeamId !== teamId) {
34
+ throw new Error("Server returned mismatched team selection.");
35
+ }
36
+ const cfg = await loadLocalConfig();
37
+ const nextConfig = {
38
+ ...cfg,
39
+ activeTeamId: confirmedTeamId || teamId
40
+ };
41
+ if (data.team?.name) {
42
+ nextConfig.activeTeamName = data.team.name;
43
+ }
44
+ await saveLocalConfig(nextConfig);
45
+ }
46
+
47
+ // src/auth/browser-login.ts
48
+ import { createServer } from "http";
49
+ import { spawn } from "child_process";
50
+ import crypto2 from "crypto";
51
+
52
+ // src/auth/pkce.ts
53
+ import crypto from "crypto";
54
+ function base64Url(input) {
55
+ return input.toString("base64").replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/g, "");
56
+ }
57
+ function generateCodeVerifier() {
58
+ return base64Url(crypto.randomBytes(64));
59
+ }
60
+ function generateCodeChallenge(verifier) {
61
+ return base64Url(crypto.createHash("sha256").update(verifier).digest());
62
+ }
63
+ function generateVerificationCode() {
64
+ const chars = "ABCDEFGHJKLMNPQRSTUVWXYZ23456789";
65
+ const bytes = crypto.randomBytes(8);
66
+ let out = "";
67
+ for (const b of bytes) {
68
+ out += chars[b % chars.length];
69
+ }
70
+ return `${out.slice(0, 4)}-${out.slice(4)}`;
71
+ }
72
+ function generateStateParts() {
73
+ const nonce = base64Url(crypto.randomBytes(32));
74
+ const timestamp = Date.now().toString();
75
+ const payload = `${nonce}.${timestamp}`;
76
+ const secret = base64Url(crypto.randomBytes(32));
77
+ const sig = base64Url(crypto.createHmac("sha256", secret).update(payload).digest());
78
+ return { nonce: payload, secret, state: `${payload}.${sig}` };
79
+ }
80
+ function verifyState(state, secret, maxAgeMs = 5 * 6e4) {
81
+ const parts = state.split(".");
82
+ if (parts.length < 3) return false;
83
+ const ts = Number(parts[1]);
84
+ if (!Number.isFinite(ts) || Date.now() - ts > maxAgeMs) return false;
85
+ const payload = `${parts[0]}.${parts[1]}`;
86
+ const providedSig = parts.slice(2).join(".");
87
+ const expectedSig = base64Url(
88
+ crypto.createHmac("sha256", secret).update(payload).digest()
89
+ );
90
+ const a = Buffer.from(providedSig);
91
+ const b = Buffer.from(expectedSig);
92
+ if (a.length !== b.length) return false;
93
+ return crypto.timingSafeEqual(a, b);
94
+ }
95
+
96
+ // src/auth/browser-login.ts
97
+ function openUrl(url) {
98
+ const command = process.platform === "win32" ? "start" : process.platform === "darwin" ? "open" : "xdg-open";
99
+ spawn(command, [url], { stdio: "ignore", detached: true }).unref();
100
+ }
101
+ var CALLBACK_MAX_BODY_BYTES = 64 * 1024;
102
+ var ALLOWED_ORIGINS = /* @__PURE__ */ new Set(["https://trie.dev", "https://www.trie.dev"]);
103
+ var RequestError = class extends Error {
104
+ constructor(message, statusCode) {
105
+ super(message);
106
+ this.statusCode = statusCode;
2126
107
  }
2127
- }
2128
-
2129
- // src/cli/learn.ts
2130
- import picocolors2 from "picocolors";
2131
- async function handleLearnCommand(args) {
108
+ };
109
+ function writeCors(res, origin) {
110
+ res.setHeader("Access-Control-Allow-Origin", origin);
111
+ res.setHeader("Access-Control-Allow-Methods", "POST, OPTIONS");
112
+ res.setHeader("Access-Control-Allow-Headers", "Content-Type");
113
+ }
114
+ function parseContentLength(req) {
115
+ const header = req.headers["content-length"];
116
+ if (!header) return null;
117
+ const first = Array.isArray(header) ? header[0] : header;
118
+ const parsed = Number(first);
119
+ if (!Number.isFinite(parsed) || parsed < 0) {
120
+ throw new RequestError("Invalid content length", 400);
121
+ }
122
+ return parsed;
123
+ }
124
+ async function readJsonBody(req) {
125
+ const contentLength = parseContentLength(req);
126
+ if (contentLength !== null && contentLength > CALLBACK_MAX_BODY_BYTES) {
127
+ throw new RequestError("Payload too large", 413);
128
+ }
129
+ const chunks = [];
130
+ let seenBytes = 0;
131
+ for await (const chunk of req) {
132
+ const buffer = Buffer.from(chunk);
133
+ chunks.push(buffer);
134
+ seenBytes += buffer.length;
135
+ if (seenBytes > CALLBACK_MAX_BODY_BYTES) {
136
+ throw new RequestError("Payload too large", 413);
137
+ }
138
+ }
139
+ const text = Buffer.concat(chunks).toString("utf-8");
2132
140
  try {
2133
- const workDir = getWorkingDirectory(void 0, true);
2134
- const engine = new LearningEngine(workDir);
2135
- const firstArg = args[0]?.toLowerCase();
2136
- if (firstArg === "ok" || firstArg === "good") {
2137
- const note = args.slice(1).join(" ");
2138
- const staged = await getStagedChanges(workDir);
2139
- const files = staged.map((s) => s.path);
2140
- await engine.learn({
2141
- manualFeedback: { helpful: true, files, note }
2142
- });
2143
- console.log(picocolors2.green("\u{1F44D} Learned that this pattern is good."));
2144
- if (files.length > 0) {
2145
- console.log(picocolors2.dim(` Linked to: ${files.join(", ")}`));
2146
- }
2147
- return;
2148
- }
2149
- if (firstArg === "bad" || firstArg === "wrong") {
2150
- const note = args.slice(1).join(" ");
2151
- const staged = await getStagedChanges(workDir);
2152
- const files = staged.map((s) => s.path);
2153
- await engine.learn({
2154
- manualFeedback: { helpful: false, files, note }
2155
- });
2156
- console.log(picocolors2.yellow("\u{1F44E} Learned that this pattern is problematic."));
2157
- if (files.length > 0) {
2158
- console.log(picocolors2.dim(` Linked to: ${files.join(", ")}`));
2159
- }
2160
- return;
2161
- }
2162
- console.log(picocolors2.cyan("Scanning history for implicit failure signals (reverts/fixes)..."));
2163
- const results = await engine.learn({ limit: 50 });
2164
- const implicit = results.find((r) => r.source === "git-history");
2165
- console.log(picocolors2.green(`\u2713 Learned ${implicit?.learned ?? 0} new cases from your git history.`));
2166
- console.log(picocolors2.dim(" Trie will now use these precedents to predict future gotchas."));
2167
- } catch (error) {
2168
- const friendly = formatFriendlyError(error);
2169
- console.error(friendly.userMessage);
2170
- }
2171
- }
2172
-
2173
- // src/cli/sync.ts
2174
- import pc6 from "picocolors";
2175
- import { Command } from "commander";
2176
- function createSyncCommand() {
2177
- const sync = new Command("sync").description("Sync ledger with shared storage");
2178
- sync.command("init").description("Initialize shared ledger storage").action(async () => {
2179
- try {
2180
- const workDir = getWorkingDirectory(void 0, true);
2181
- console.log(`Initializing shared ledger in ${workDir}...`);
2182
- await initializeSharedLedger();
2183
- console.log(pc6.green("\u2713 Shared ledger initialized successfully"));
2184
- } catch (error) {
2185
- console.error(pc6.red("Failed to initialize shared ledger:"), error);
2186
- process.exit(1);
2187
- }
2188
- });
2189
- sync.command("pull").description("Pull updates from shared ledger").action(async () => {
2190
- try {
2191
- console.log("Syncing from shared ledger...");
2192
- const hasLegacy = await detectLegacyLedger();
2193
- if (hasLegacy) {
2194
- console.log("Detected legacy ledger, migrating...");
2195
- await migrateLegacyLedger();
2196
- }
2197
- const result = await syncLedgerFromShared();
2198
- console.log(pc6.green("\u2713 Sync completed"));
2199
- console.log(`Merged ${result.stats.mergedBlocks} blocks`);
2200
- if (result.conflicts.length > 0) {
2201
- console.log(pc6.yellow(`\u26A0 ${result.conflicts.length} conflicts detected`));
2202
- console.log('Run "trie sync status" to see details');
2203
- }
2204
- if (result.stats.duplicatesRemoved > 0) {
2205
- console.log(`Removed ${result.stats.duplicatesRemoved} duplicate entries`);
2206
- }
2207
- } catch (error) {
2208
- console.error(pc6.red("Failed to sync from shared ledger:"), error);
2209
- process.exit(1);
2210
- }
2211
- });
2212
- sync.command("push").description("Push local changes to shared ledger").action(async () => {
2213
- try {
2214
- console.log("Pushing to shared ledger...");
2215
- const hasLegacy = await detectLegacyLedger();
2216
- if (hasLegacy) {
2217
- console.log("Detected legacy ledger, migrating...");
2218
- await migrateLegacyLedger();
2219
- }
2220
- await pushLedgerToShared();
2221
- console.log(pc6.green("\u2713 Push completed"));
2222
- } catch (error) {
2223
- console.error(pc6.red("Failed to push to shared ledger:"), error);
2224
- process.exit(1);
2225
- }
2226
- });
2227
- sync.command("status").description("Show ledger sync status").action(async () => {
2228
- try {
2229
- const status = await getLedgerSyncStatus();
2230
- console.log(pc6.bold("Ledger Sync Status\n"));
2231
- if (!status.isInitialized) {
2232
- console.log(pc6.yellow('\u26A0 Shared ledger not initialized. Run "trie sync init" first.'));
2233
- return;
2234
- }
2235
- if (status.hasLegacyLedger) {
2236
- console.log(pc6.yellow('\u26A0 Legacy ledger detected. Run "trie sync pull" to migrate.'));
2237
- }
2238
- console.log(`Local blocks: ${pc6.cyan(status.localBlocks.toString())}`);
2239
- console.log(`Shared blocks: ${pc6.cyan(status.sharedBlocks.toString())}`);
2240
- if (status.syncState) {
2241
- const lastSync = new Date(status.syncState.lastSyncTimestamp);
2242
- console.log(`Last sync: ${pc6.dim(lastSync.toLocaleString())}`);
2243
- if (status.conflicts > 0) {
2244
- console.log(pc6.yellow(`Conflicts: ${status.conflicts}`));
2245
- console.log("\nConflicts:");
2246
- for (const conflict of status.syncState.conflicts) {
2247
- console.log(` \u2022 ${conflict.type}: ${conflict.description}`);
2248
- }
2249
- } else {
2250
- console.log(pc6.green("\u2713 No conflicts"));
141
+ return text ? JSON.parse(text) : {};
142
+ } catch {
143
+ throw new RequestError("Invalid JSON body", 400);
144
+ }
145
+ }
146
+ async function loginWithBrowser() {
147
+ const cfg = await loadLocalConfig();
148
+ const apiBaseUrl = normalizeAndValidateApiBaseUrl(cfg.apiBaseUrl);
149
+ const verifier = generateCodeVerifier();
150
+ const challenge = generateCodeChallenge(verifier);
151
+ const { state, secret } = generateStateParts();
152
+ const code = generateVerificationCode();
153
+ const callbackToken = crypto2.randomBytes(16).toString("hex");
154
+ const callbackPath = `/callback/${callbackToken}`;
155
+ let used = false;
156
+ let timeout;
157
+ let closeServer;
158
+ const tokenPromise = new Promise((resolve, reject) => {
159
+ const server = createServer(async (req, res) => {
160
+ const reqOrigin = req.headers.origin || "";
161
+ const originAllowed = ALLOWED_ORIGINS.has(reqOrigin);
162
+ if (originAllowed) {
163
+ writeCors(res, reqOrigin);
164
+ }
165
+ if (req.method === "OPTIONS") {
166
+ if (!originAllowed) {
167
+ res.statusCode = 403;
168
+ res.end("Invalid origin");
169
+ return;
2251
170
  }
2252
- }
2253
- if (status.manifest) {
2254
- console.log(`
2255
- Total entries: ${pc6.cyan(status.manifest.totalEntries.toString())}`);
2256
- console.log(`Compression: ${status.manifest.compressionConfig.enabled ? pc6.green("enabled") : pc6.red("disabled")}`);
2257
- }
2258
- } catch (error) {
2259
- console.error(pc6.red("Failed to get sync status:"), error);
2260
- process.exit(1);
2261
- }
2262
- });
2263
- sync.command("migrate").description("Migrate legacy ledger to new format").action(async () => {
2264
- try {
2265
- const hasLegacy = await detectLegacyLedger();
2266
- if (!hasLegacy) {
2267
- console.log(pc6.yellow("No legacy ledger found to migrate"));
2268
- return;
2269
- }
2270
- console.log("Migrating legacy ledger...");
2271
- const success = await migrateLegacyLedger();
2272
- if (success) {
2273
- console.log(pc6.green("\u2713 Migration completed successfully"));
2274
- } else {
2275
- console.log(pc6.red("Migration failed"));
2276
- process.exit(1);
2277
- }
2278
- } catch (error) {
2279
- console.error(pc6.red("Failed to migrate legacy ledger:"), error);
2280
- process.exit(1);
2281
- }
2282
- });
2283
- sync.command("hooks").description("Manage git hooks for automatic sync").option("--install", "Install git hooks").option("--status", "Check git hooks status").action(async (options) => {
2284
- try {
2285
- if (options.install) {
2286
- await installGitHooks2();
171
+ res.statusCode = 204;
172
+ res.end();
2287
173
  return;
2288
174
  }
2289
- const status = await checkGitHooks();
2290
- if (!status.isGitRepo) {
2291
- console.log(pc6.yellow("Not a git repository"));
175
+ if (used) {
176
+ res.statusCode = 403;
177
+ res.end("Callback already used");
2292
178
  return;
2293
179
  }
2294
- console.log(pc6.bold("Git Hooks Status\n"));
2295
- console.log(`Pre-push hook: ${status.prePushInstalled ? pc6.green("\u2713 installed") : pc6.red("\u2717 not installed")}`);
2296
- console.log(`Post-merge hook: ${status.postMergeInstalled ? pc6.green("\u2713 installed") : pc6.red("\u2717 not installed")}`);
2297
- if (!status.prePushInstalled || !status.postMergeInstalled) {
2298
- console.log(`
2299
- ${pc6.yellow("\u{1F4A1} Tip:")} Run ${pc6.bold("trie sync hooks --install")} to install missing hooks`);
2300
- }
2301
- } catch (error) {
2302
- console.error(pc6.red("Failed to manage git hooks:"), error);
2303
- process.exit(1);
2304
- }
2305
- });
2306
- return sync;
2307
- }
2308
-
2309
- // src/cli/ledger.ts
2310
- import pc7 from "picocolors";
2311
- import { Command as Command2 } from "commander";
2312
- function createLedgerCommand() {
2313
- const ledger = new Command2("ledger").description("Manage and inspect the ledger");
2314
- ledger.command("verify").description("Verify ledger chain integrity and signatures").action(async () => {
2315
- try {
2316
- console.log("Verifying ledger chain...");
2317
- console.log(" \u2022 Checking block hashes");
2318
- console.log(" \u2022 Verifying Merkle roots");
2319
- console.log(" \u2022 Validating Ed25519 signatures");
2320
- const result = await verifyLedger();
2321
- if (result.valid) {
2322
- console.log(pc7.green("\u2713 Ledger chain is valid"));
2323
- console.log(pc7.dim(" All blocks, hashes, and signatures verified"));
2324
- } else {
2325
- console.log(pc7.red(`\u2717 Ledger chain is invalid: ${result.error}`));
2326
- process.exit(1);
2327
- }
2328
- } catch (error) {
2329
- console.error(pc7.red("Failed to verify ledger:"), error);
2330
- process.exit(1);
2331
- }
2332
- });
2333
- ledger.command("history").description("Show ledger history with author attribution").option("-l, --limit <number>", "Number of blocks to show", "10").action(async (options) => {
2334
- try {
2335
- const limit = parseInt(options.limit, 10);
2336
- const blocks = await getLedgerBlocks();
2337
- if (blocks.length === 0) {
2338
- console.log("No ledger blocks found");
180
+ if (req.method !== "POST" || req.url !== callbackPath) {
181
+ res.statusCode = 405;
182
+ res.end("Method not allowed");
2339
183
  return;
2340
184
  }
2341
- console.log(pc7.bold(`Ledger History (last ${Math.min(limit, blocks.length)} blocks)
2342
- `));
2343
- const recentBlocks = blocks.slice(-limit).reverse();
2344
- for (const block of recentBlocks) {
2345
- const syncableBlock = block;
2346
- console.log(pc7.bold(pc7.cyan(`Block ${block.date}`)));
2347
- if (syncableBlock.author) {
2348
- console.log(`Author: ${pc7.dim(syncableBlock.author)}`);
2349
- }
2350
- if (syncableBlock.gitCommit) {
2351
- console.log(`Git: ${pc7.dim(syncableBlock.gitCommit.slice(0, 8))}`);
2352
- }
2353
- console.log(`Entries: ${block.entries.length}`);
2354
- console.log(`Hash: ${pc7.dim(block.blockHash.slice(0, 16))}...`);
2355
- const signedEntries = block.entries.filter((e) => e.signature);
2356
- if (signedEntries.length > 0) {
2357
- console.log(`Signed: ${pc7.green(`${signedEntries.length}/${block.entries.length}`)}`);
2358
- }
2359
- if (block.entries.length > 0) {
2360
- console.log("Issues:");
2361
- for (const entry of block.entries) {
2362
- const signatureIndicator = entry.signature ? pc7.green("\u2713") : pc7.dim("\u25CB");
2363
- console.log(` ${signatureIndicator} ${entry.severity} - ${entry.file} (${entry.agent})`);
2364
- }
2365
- }
2366
- console.log("");
2367
- }
2368
- } catch (error) {
2369
- console.error(pc7.red("Failed to show ledger history:"), error);
2370
- process.exit(1);
2371
- }
2372
- });
2373
- ledger.command("stats").description("Show ledger statistics").action(async () => {
2374
- try {
2375
- const status = await getLedgerSyncStatus();
2376
- const blocks = await getLedgerBlocks();
2377
- console.log(pc7.bold("Ledger Statistics\n"));
2378
- console.log(`Total blocks: ${pc7.cyan(blocks.length.toString())}`);
2379
- if (status.manifest) {
2380
- console.log(`Total entries: ${pc7.cyan(status.manifest.totalEntries.toString())}`);
2381
- console.log(`Active blocks: ${pc7.cyan(status.manifest.activeBlocks.length.toString())}`);
2382
- console.log(`Archived blocks: ${pc7.cyan(status.manifest.archivedBlocks.length.toString())}`);
2383
- if (status.manifest.compressionConfig.enabled) {
2384
- const hotSize = Math.round(status.manifest.compressionConfig.maxHotStorageSize / 1024 / 1024);
2385
- console.log(`Hot storage limit: ${pc7.cyan(`${hotSize}MB`)}`);
2386
- console.log(`Archive after: ${pc7.cyan(`${status.manifest.compressionConfig.archiveAfterDays} days`)}`);
2387
- }
2388
- }
2389
- if (blocks.length > 0) {
2390
- const firstBlock = blocks[0];
2391
- const lastBlock = blocks[blocks.length - 1];
2392
- if (firstBlock && lastBlock) {
2393
- console.log(`Date range: ${pc7.dim(`${firstBlock.date} to ${lastBlock.date}`)}`);
2394
- }
2395
- }
2396
- const authorCounts = /* @__PURE__ */ new Map();
2397
- for (const block of blocks) {
2398
- const syncableBlock = block;
2399
- if (syncableBlock.author) {
2400
- authorCounts.set(syncableBlock.author, (authorCounts.get(syncableBlock.author) || 0) + 1);
2401
- }
2402
- }
2403
- if (authorCounts.size > 0) {
2404
- console.log("\nBlocks by author:");
2405
- const sortedAuthors = Array.from(authorCounts.entries()).sort((a, b) => b[1] - a[1]);
2406
- for (const [author, count] of sortedAuthors.slice(0, 10)) {
2407
- console.log(` ${author}: ${pc7.cyan(count.toString())}`);
2408
- }
2409
- }
2410
- const severityCounts = /* @__PURE__ */ new Map();
2411
- for (const block of blocks) {
2412
- for (const entry of block.entries) {
2413
- severityCounts.set(entry.severity, (severityCounts.get(entry.severity) || 0) + 1);
2414
- }
2415
- }
2416
- if (severityCounts.size > 0) {
2417
- console.log("\nIssues by severity:");
2418
- const severityOrder = ["critical", "high", "medium", "low", "info"];
2419
- for (const severity of severityOrder) {
2420
- const count = severityCounts.get(severity);
2421
- if (count) {
2422
- const color = severity === "critical" ? pc7.red : severity === "high" ? pc7.yellow : severity === "medium" ? pc7.blue : pc7.gray;
2423
- console.log(` ${color(severity)}: ${pc7.cyan(count.toString())}`);
2424
- }
2425
- }
2426
- }
2427
- } catch (error) {
2428
- console.error(pc7.red("Failed to show ledger stats:"), error);
2429
- process.exit(1);
2430
- }
2431
- });
2432
- ledger.command("diff").description("Compare local and shared ledger state").action(async () => {
2433
- try {
2434
- const status = await getLedgerSyncStatus();
2435
- console.log(pc7.bold("Ledger Diff\n"));
2436
- if (!status.isInitialized) {
2437
- console.log(pc7.yellow("Shared ledger not initialized"));
185
+ if (!originAllowed) {
186
+ res.statusCode = 403;
187
+ res.end("Invalid origin");
2438
188
  return;
2439
189
  }
2440
- console.log(`Local blocks: ${pc7.cyan(status.localBlocks.toString())}`);
2441
- console.log(`Shared blocks: ${pc7.cyan(status.sharedBlocks.toString())}`);
2442
- const diff = status.sharedBlocks - status.localBlocks;
2443
- if (diff > 0) {
2444
- console.log(pc7.green(`\u2193 ${diff} blocks available to pull`));
2445
- } else if (diff < 0) {
2446
- console.log(pc7.blue(`\u2191 ${-diff} local blocks not yet pushed`));
2447
- } else {
2448
- console.log(pc7.green("\u2713 Local and shared ledgers are in sync"));
2449
- }
2450
- if (status.conflicts > 0) {
2451
- console.log(pc7.red(`\u26A0 ${status.conflicts} conflicts detected`));
2452
- }
2453
- } catch (error) {
2454
- console.error(pc7.red("Failed to diff ledgers:"), error);
2455
- process.exit(1);
2456
- }
2457
- });
2458
- ledger.command("compress").description("Manually compress and archive old blocks").action(async () => {
2459
- try {
2460
- const shouldRun = await shouldCompress();
2461
- if (!shouldRun) {
2462
- console.log("No compression needed at this time");
190
+ if (!(req.headers["content-type"] || "").includes("application/json")) {
191
+ res.statusCode = 400;
192
+ res.end("Invalid content type");
2463
193
  return;
2464
194
  }
2465
- console.log("Compressing old ledger blocks...");
2466
- const result = await compressOldBlocks();
2467
- if (result.archived > 0) {
2468
- console.log(pc7.green(`\u2713 Compressed ${result.archived} blocks`));
2469
- console.log(`Space saved: ${result.sizeReduction}%`);
2470
- } else {
2471
- console.log("No blocks were archived");
2472
- }
2473
- } catch (error) {
2474
- console.error(pc7.red("Failed to compress blocks:"), error);
2475
- process.exit(1);
2476
- }
2477
- });
2478
- ledger.command("storage").description("Show detailed storage statistics").action(async () => {
2479
- try {
2480
- const stats = await getStorageStats();
2481
- console.log(pc7.bold("Ledger Storage Statistics\n"));
2482
- console.log(pc7.bold("Block Storage:"));
2483
- console.log(` Active blocks: ${pc7.cyan(stats.activeBlocks.toString())}`);
2484
- console.log(` Archived blocks: ${pc7.cyan(stats.archivedBlocks.toString())}`);
2485
- console.log(pc7.bold("\nStorage Usage:"));
2486
- const activeMB = (stats.activeSize / 1024 / 1024).toFixed(2);
2487
- const archivedMB = (stats.archivedSize / 1024 / 1024).toFixed(2);
2488
- const totalMB = (Number(activeMB) + Number(archivedMB)).toFixed(2);
2489
- console.log(` Active storage: ${pc7.cyan(`${activeMB} MB`)}`);
2490
- console.log(` Archived storage: ${pc7.cyan(`${archivedMB} MB`)}`);
2491
- console.log(` Total storage: ${pc7.bold(pc7.cyan(`${totalMB} MB`))}`);
2492
- if (stats.compressionRatio > 0) {
2493
- console.log(` Compression ratio: ${pc7.green(`${stats.compressionRatio}%`)}`);
2494
- }
2495
- console.log(pc7.bold("\nData:"));
2496
- console.log(` Total entries: ${pc7.cyan(stats.totalEntries.toString())}`);
2497
- const needsCompression = await shouldCompress();
2498
- if (needsCompression) {
2499
- console.log(`
2500
- ${pc7.yellow("\u{1F4A1} Tip:")} Run ${pc7.bold("trie ledger compress")} to archive old blocks`);
2501
- }
2502
- } catch (error) {
2503
- console.error(pc7.red("Failed to get storage stats:"), error);
2504
- process.exit(1);
2505
- }
2506
- });
2507
- ledger.command("correct").description("Mark ledger entries as corrected (maintains immutability)").argument("<entry-ids...>", "Entry IDs to mark as corrected").option("-r, --reason <reason>", "Reason for the correction (required)").option("-t, --type <type>", "Correction type: corrected or false-positive", "corrected").action(async (entryIds, options) => {
2508
- try {
2509
- if (!options.reason) {
2510
- console.error(pc7.red("\u2717 Correction reason is required"));
2511
- console.log("");
2512
- console.log("Usage:");
2513
- console.log(` ${pc7.dim('trie ledger correct <entry-id> -r "reason for correction"')}`);
2514
- console.log("");
2515
- console.log("Examples:");
2516
- console.log(` ${pc7.dim('trie ledger correct abc123 -r "False positive - this is expected behavior"')}`);
2517
- console.log(` ${pc7.dim('trie ledger correct xyz789 -r "Duplicate detection" -t false-positive')}`);
2518
- process.exit(1);
2519
- }
2520
- const validTypes = ["corrected", "false-positive"];
2521
- if (!validTypes.includes(options.type)) {
2522
- console.error(pc7.red(`\u2717 Invalid correction type: ${options.type}`));
2523
- console.log(`Valid types: ${validTypes.join(", ")}`);
2524
- process.exit(1);
2525
- }
2526
- console.log(pc7.yellow(`\u{1F4DD} Marking ${entryIds.length} entry(ies) as ${options.type}...`));
2527
- console.log(`Reason: ${pc7.dim(options.reason)}`);
2528
- console.log("");
2529
- const result = await correctLedgerEntries(
2530
- entryIds,
2531
- options.reason,
2532
- options.type
2533
- );
2534
- if (result.success) {
2535
- console.log(pc7.green(`\u2713 Successfully corrected ${result.correctedEntries} entry(ies)`));
2536
- console.log("");
2537
- console.log(pc7.dim("Note: Original entries remain in the ledger for audit trail."));
2538
- console.log(pc7.dim(" Queries will automatically filter out corrected entries."));
2539
- } else {
2540
- console.error(pc7.red("\u2717 Correction failed:"), result.error);
2541
- process.exit(1);
2542
- }
2543
- } catch (error) {
2544
- console.error(pc7.red("Failed to correct entries:"), error);
2545
- process.exit(1);
2546
- }
2547
- });
2548
- ledger.command("corrections").description("Show correction statistics and history").option("-e, --entries <ids...>", "Show correction history for specific entry IDs").action(async (options) => {
2549
- try {
2550
- if (options.entries && options.entries.length > 0) {
2551
- const history = await getEntryCorrectionHistory(options.entries);
2552
- if (history.size === 0) {
2553
- console.log("No correction history found for the specified entries");
195
+ try {
196
+ const body = await readJsonBody(req);
197
+ const returnedState = String(body.state || "");
198
+ const firebaseToken = String(body.firebase_token || "");
199
+ const verificationCode = String(body.code || "");
200
+ if (!verifyState(returnedState, secret)) {
201
+ res.statusCode = 403;
202
+ res.end("Invalid state");
2554
203
  return;
2555
204
  }
2556
- console.log(pc7.bold("Correction History\n"));
2557
- for (const [entryId, data] of history) {
2558
- console.log(pc7.bold(pc7.cyan(`Entry: ${entryId}`)));
2559
- console.log(`Status: ${pc7.yellow(data.original.status || "active")}`);
2560
- console.log(`File: ${pc7.dim(data.original.file)}`);
2561
- console.log(`Agent: ${pc7.dim(data.original.agent)}`);
2562
- if (data.original.correction) {
2563
- console.log(`Correction: ${data.original.correction}`);
2564
- }
2565
- if (data.original.correctionTimestamp) {
2566
- console.log(`Corrected at: ${pc7.dim(data.original.correctionTimestamp)}`);
2567
- }
2568
- if (data.corrections.length > 0) {
2569
- console.log("\nCorrection Entries:");
2570
- for (const correction of data.corrections) {
2571
- console.log(` \u2022 ${correction.id} - ${correction.correction}`);
2572
- }
2573
- }
2574
- console.log("");
205
+ if (verificationCode && verificationCode !== code) {
206
+ res.statusCode = 403;
207
+ res.end("Invalid verification code");
208
+ return;
2575
209
  }
2576
- } else {
2577
- const stats = await getCorrectionStats();
2578
- console.log(pc7.bold("Correction Statistics\n"));
2579
- console.log(`Total entries: ${pc7.cyan(stats.totalEntries.toString())}`);
2580
- console.log(`Active entries: ${pc7.green(stats.activeEntries.toString())}`);
2581
- console.log(`Corrected entries: ${pc7.yellow(stats.correctedEntries.toString())}`);
2582
- console.log(`False positives: ${pc7.yellow(stats.falsePositives.toString())}`);
2583
- console.log(`Correction rate: ${pc7.cyan(`${stats.correctionRate}%`)}`);
2584
- console.log("");
2585
- console.log(pc7.dim("\u{1F4A1} Tip: Use --entries <id> to see correction history for specific entries"));
2586
- }
2587
- } catch (error) {
2588
- console.error(pc7.red("Failed to show corrections:"), error);
2589
- process.exit(1);
2590
- }
210
+ used = true;
211
+ const exchangeRes = await fetch(`${apiBaseUrl}/api/auth/cli/exchange`, {
212
+ method: "POST",
213
+ headers: {
214
+ "Content-Type": "application/json",
215
+ Authorization: `Bearer ${firebaseToken}`
216
+ },
217
+ body: JSON.stringify({
218
+ firebase_token: firebaseToken,
219
+ code_verifier: verifier,
220
+ code_challenge: challenge,
221
+ machine_fingerprint: getMachineFingerprint()
222
+ })
223
+ });
224
+ if (!exchangeRes.ok) {
225
+ throw new Error(`Auth exchange failed (${exchangeRes.status})`);
226
+ }
227
+ const tokens = await exchangeRes.json();
228
+ await persistAuthTokens(tokens);
229
+ res.statusCode = 200;
230
+ res.end("Authorized. You can return to your terminal.");
231
+ resolve(tokens);
232
+ } catch (err) {
233
+ if (err instanceof RequestError) {
234
+ res.statusCode = err.statusCode;
235
+ res.end(err.message);
236
+ } else {
237
+ res.statusCode = 500;
238
+ res.end("Authorization failed");
239
+ }
240
+ reject(err);
241
+ } finally {
242
+ await closeServer?.();
243
+ }
244
+ }).listen(0, "127.0.0.1");
245
+ server.requestTimeout = 15e3;
246
+ server.headersTimeout = 1e4;
247
+ server.keepAliveTimeout = 5e3;
248
+ closeServer = async () => {
249
+ if (timeout) clearTimeout(timeout);
250
+ await new Promise((done) => server.close(() => done()));
251
+ };
252
+ server.on("listening", async () => {
253
+ const port = server.address().port;
254
+ const loginUrl = new URL("/cli-auth", "https://trie.dev");
255
+ loginUrl.searchParams.set("challenge", challenge);
256
+ loginUrl.searchParams.set("state", state);
257
+ loginUrl.searchParams.set("port", String(port));
258
+ loginUrl.searchParams.set("code", code);
259
+ loginUrl.searchParams.set("callback_path", callbackPath);
260
+ console.log(`Verification code: ${code}`);
261
+ console.log(`Opening browser: ${loginUrl.origin}${loginUrl.pathname}`);
262
+ openUrl(loginUrl.toString());
263
+ });
264
+ timeout = setTimeout(async () => {
265
+ await closeServer?.();
266
+ reject(new Error("Login timed out after 5 minutes."));
267
+ }, 5 * 6e4);
2591
268
  });
2592
- return ledger;
2593
- }
2594
-
2595
- // src/cli/keys.ts
2596
- import pc8 from "picocolors";
2597
- import { join as join3 } from "path";
2598
- async function handleKeysCommand(args) {
2599
- const subcommand = args[0]?.toLowerCase() || "status";
2600
- const workDir = getWorkingDirectory(void 0, true);
2601
- switch (subcommand) {
2602
- case "generate":
2603
- case "create":
2604
- case "init":
2605
- await handleKeysGenerate(workDir, args.includes("--force") || args.includes("-f"));
2606
- break;
2607
- case "status":
2608
- case "info":
2609
- await handleKeysStatus(workDir);
2610
- break;
2611
- case "public":
2612
- case "pubkey":
2613
- await handleKeysPublic(workDir);
2614
- break;
2615
- case "help":
2616
- case "--help":
2617
- case "-h":
2618
- showKeysHelp();
2619
- break;
2620
- default:
2621
- console.error(pc8.red(`Unknown subcommand: ${subcommand}`));
2622
- showKeysHelp();
2623
- process.exit(1);
2624
- }
2625
- }
2626
- async function handleKeysGenerate(workDir, force) {
2627
- const hasKey = hasSigningKey(workDir);
2628
- if (hasKey && !force) {
2629
- console.log(pc8.yellow("Signing key already exists."));
2630
- console.log(pc8.dim("Use --force to regenerate (WARNING: will invalidate existing signatures)"));
2631
- const pubKey = getPublicKey(workDir);
2632
- if (pubKey) {
2633
- console.log(`
2634
- Public key: ${pc8.cyan(pubKey.slice(0, 16))}...${pc8.cyan(pubKey.slice(-8))}`);
2635
- }
2636
- return;
2637
- }
2638
- if (hasKey && force) {
2639
- console.log(pc8.yellow("Regenerating signing key..."));
2640
- console.log(pc8.red("WARNING: This will invalidate all existing signatures in the ledger."));
2641
- }
2642
- console.log("Generating Ed25519 signing key pair...\n");
2643
- const keyPair = await generateKeyPair();
2644
- saveKeyPair(keyPair, workDir);
2645
- const keysDir = join3(getTrieDirectory(workDir), "keys");
2646
- console.log(pc8.green("\u2713 Signing key generated successfully\n"));
2647
- console.log(`Location: ${pc8.dim(keysDir)}`);
2648
- console.log(`Public key: ${pc8.cyan(keyPair.publicKey.slice(0, 16))}...${pc8.cyan(keyPair.publicKey.slice(-8))}`);
2649
- console.log(`Algorithm: ${pc8.dim("Ed25519")}
2650
- `);
2651
- console.log(pc8.bold("What this means:"));
2652
- console.log(" \u2022 All new ledger entries will be signed automatically");
2653
- console.log(" \u2022 Signatures prove who created each entry");
2654
- console.log(" \u2022 Tampering with entries will be detectable");
2655
- console.log(" \u2022 Your private key stays local (never committed to git)\n");
2656
- console.log(pc8.yellow("Security notes:"));
2657
- console.log(" \u2022 The private key is stored in .trie/keys/signing-key.json");
2658
- console.log(" \u2022 This directory is automatically added to .gitignore");
2659
- console.log(" \u2022 Back up this key if you need to prove authorship later");
2660
- console.log(" \u2022 Team members each generate their own keys");
2661
- }
2662
- async function handleKeysStatus(workDir) {
2663
- const hasKey = hasSigningKey(workDir);
2664
- const keysDir = join3(getTrieDirectory(workDir), "keys");
2665
- console.log(pc8.bold("Signing Key Status\n"));
2666
- if (hasKey) {
2667
- const keyPair = loadKeyPair(workDir);
2668
- const keyPath = join3(keysDir, "signing-key.json");
2669
- console.log(`Status: ${pc8.green("\u2713 Active")}`);
2670
- console.log(`Location: ${pc8.dim(keyPath)}`);
2671
- if (keyPair) {
2672
- console.log(`Public key: ${pc8.cyan(keyPair.publicKey.slice(0, 16))}...${pc8.cyan(keyPair.publicKey.slice(-8))}`);
2673
- }
2674
- console.log(`Algorithm: ${pc8.dim("Ed25519")}`);
2675
- console.log(`
2676
- New ledger entries will be ${pc8.green("signed automatically")}.`);
2677
- } else {
2678
- console.log(`Status: ${pc8.yellow("\u25CB Not configured")}`);
2679
- console.log(`
2680
- Ledger entries are ${pc8.yellow("not being signed")}.`);
2681
- console.log(`
2682
- To enable signing, run:`);
2683
- console.log(` ${pc8.cyan("trie keys generate")}`);
2684
- }
2685
- }
2686
- async function handleKeysPublic(workDir) {
2687
- const pubKey = getPublicKey(workDir);
2688
- if (!pubKey) {
2689
- console.error(pc8.yellow("No signing key found."));
2690
- console.error(`Run ${pc8.cyan("trie keys generate")} to create one.`);
2691
- process.exit(1);
2692
- }
2693
- console.log(pubKey);
269
+ return tokenPromise;
2694
270
  }
2695
- function showKeysHelp() {
2696
- console.log(`
2697
- ${pc8.bold("trie keys")} - Manage Ed25519 signing keys for the governance ledger
2698
-
2699
- ${pc8.bold("USAGE:")}
2700
- trie keys <command> [options]
2701
-
2702
- ${pc8.bold("COMMANDS:")}
2703
- generate Generate a new Ed25519 signing key pair
2704
- status Show current key status (default)
2705
- public Print the public key (for sharing/verification)
2706
- help Show this help message
2707
-
2708
- ${pc8.bold("OPTIONS:")}
2709
- --force, -f Force regenerate key (WARNING: invalidates signatures)
2710
-
2711
- ${pc8.bold("EXAMPLES:")}
2712
- trie keys # Check if signing is configured
2713
- trie keys generate # Generate a new signing key
2714
- trie keys public # Get your public key for verification
2715
271
 
2716
- ${pc8.bold("ABOUT SIGNING:")}
2717
- Ed25519 signatures provide:
2718
- \u2022 ${pc8.green("Authenticity")} - Prove who created each ledger entry
2719
- \u2022 ${pc8.green("Tamper-evidence")} - Detect if entries are modified
2720
- \u2022 ${pc8.green("Accountability")} - Track decisions to humans or agents
2721
-
2722
- Keys are stored locally in .trie/keys/ and never committed to git.
2723
- Each team member should generate their own key.
2724
- `);
272
+ // src/auth/sanitize.ts
273
+ function scrubProcessArgv() {
274
+ process.argv = process.argv.map((arg) => {
275
+ if (arg.startsWith("--token=")) return "--token=[REDACTED]";
276
+ if (arg.startsWith("--access_token=")) return "--access_token=[REDACTED]";
277
+ return arg;
278
+ });
2725
279
  }
2726
280
 
2727
281
  // src/cli/main.ts
2728
- var __filename2 = fileURLToPath(import.meta.url);
2729
- var __dirname2 = dirname(__filename2);
2730
- var DEFAULT_VERSION = "0.0.0";
2731
- function getCliVersion() {
2732
- try {
2733
- const pkgPath = resolve(__dirname2, "..", "..", "package.json");
2734
- const pkg = JSON.parse(readFileSync2(pkgPath, "utf-8"));
2735
- return typeof pkg.version === "string" && pkg.version.trim() ? pkg.version.trim() : DEFAULT_VERSION;
2736
- } catch {
2737
- return DEFAULT_VERSION;
2738
- }
2739
- }
2740
- function showBanner() {
2741
- console.log(`
2742
- \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557
2743
- \u255A\u2550\u2550\u2588\u2588\u2554\u2550\u2550\u255D\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D
2744
- \u2588\u2588\u2551 \u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2588\u2557
2745
- \u2588\u2588\u2551 \u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u255D
2746
- \u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557
2747
- \u255A\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u255D\u255A\u2550\u255D\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D
2748
-
2749
- Your decision ledger that travels
2750
-
2751
- by Louis Kishfy
2752
-
2753
- Download the Trie workspace: https://www.trie.dev
2754
- Follow me on X: https://x.com/louiskishfy
2755
- `);
2756
- }
2757
- function showHelp2() {
2758
- showBanner();
2759
- console.log(`Version: ${getCliVersion()}
2760
-
2761
- USAGE:
2762
- trie <command> [options]
2763
-
2764
- COMMANDS:
2765
- init Initialize Trie in your project
2766
- watch Start watching (nudges as you code)
2767
- Use screen/tmux to run in background
2768
- check Run risk check before pushing
2769
-
2770
- tell "<incident>" Report an incident ("users can't log in")
2771
- learn Train Trie from git history or feedback (alias: train)
2772
- ok Mark current pattern as good (alias: learn ok)
2773
- bad Mark current pattern as bad (alias: learn bad)
2774
-
2775
- linear sync Sync active tickets from Linear
2776
- gotcha Predict problems with current changes (alias: predict)
2777
-
2778
- memory Search and manage issue memory
2779
- status Quick health check (project health + memory stats)
2780
- project View/manage project info (.trie/PROJECT.md)
2781
- setup Configure API key and environment
2782
- keys Manage Ed25519 signing keys (for ledger signatures)
2783
-
2784
- help Show this help message
2785
- version Show version information
2786
-
2787
- EXAMPLES:
2788
- trie init # Initialize Trie in your project
2789
- trie watch # Start watching (nudges as you code)
2790
- trie watch # Run in background: screen -S trie-watch
2791
-
2792
- trie tell "users couldn't log in" # Report an incident
2793
- trie ok # Mark current pattern as good
2794
- trie bad # Mark current pattern as bad
2795
-
2796
- trie linear sync # Sync active tickets from Linear
2797
- trie learn # Scan history for precedents
2798
- trie gotcha # Predict for current changes
2799
-
2800
- trie check # Risk check before pushing
2801
-
2802
- trie memory search "SQL injection"
2803
- trie memory stats # View memory statistics
2804
- trie status # Quick health check (project + memory)
2805
-
2806
- MCP TOOLS (use via Cursor/Claude Desktop):
2807
- trie_linear_sync Sync active tickets from Linear
2808
- trie_fix Generate high-confidence fix prompts
2809
- trie_explain Explain code, issues, or changes
2810
- trie_watch Watch mode for autonomous reporting
2811
-
2812
- `);
2813
- }
2814
- function showVersion() {
2815
- showBanner();
2816
- console.error(`v${getCliVersion()}`);
2817
- }
2818
- async function handleStatusCommand() {
2819
- const workDir = getWorkingDirectory(void 0, true);
2820
- try {
2821
- const state = await loadContextState();
2822
- const memoryStats = await getMemoryStats(workDir);
2823
- console.log("\n\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557");
2824
- console.log("\u2551 PROJECT STATUS \u2551");
2825
- console.log("\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D\n");
2826
- if (state.lastScan) {
2827
- const lastScanDate = new Date(state.lastScan.timestamp);
2828
- const daysAgo = Math.floor((Date.now() - lastScanDate.getTime()) / (1e3 * 60 * 60 * 24));
2829
- console.log(`
2830
- ${pc9.bold("Last Scan:")} ${lastScanDate.toLocaleDateString()} (${daysAgo === 0 ? "today" : daysAgo === 1 ? "yesterday" : `${daysAgo} days ago`})`);
2831
- console.log(pc9.dim(` Files scanned: ${state.lastScan.filesScanned}`));
2832
- console.log(pc9.dim(` Issues found: ${state.lastScan.issues.total} (${state.lastScan.issues.critical} critical, ${state.lastScan.issues.serious} serious)`));
2833
- } else {
2834
- console.log(`
2835
- ${pc9.bold("Last Scan:")} Never ${pc9.dim("(run `trie watch` to get started)")}`);
2836
- }
2837
- console.log(`
2838
- ${pc9.bold("Memory Stats:")}`);
2839
- console.log(pc9.dim(` Active Issues: ${memoryStats.activeIssues}`));
2840
- console.log(pc9.dim(` Resolved: ${memoryStats.resolvedCount}`));
2841
- console.log(pc9.dim(` Total (all-time): ${memoryStats.totalIssues}`));
2842
- const cap = memoryStats.capacityInfo;
2843
- const capIndicator = cap.isAtCap ? pc9.red("\u25CB") : cap.percentFull >= 80 ? pc9.yellow("\u25C9") : pc9.green("\u25CF");
2844
- console.log(` ${capIndicator} Memory Usage: ${pc9.bold(cap.percentFull + "%")} ${pc9.dim(`(${cap.current}/${cap.max})`)}`);
2845
- if (memoryStats.activeIssues > 0) {
2846
- console.log(`
2847
- ${pc9.bold("Active Issues by Severity:")}`);
2848
- const severityOrder = ["critical", "serious", "moderate", "low", "info"];
2849
- for (const severity of severityOrder) {
2850
- const count = memoryStats.activeIssuesBySeverity[severity] || 0;
2851
- if (count > 0) {
2852
- console.log(pc9.dim(` ${severity}: ${count}`));
2853
- }
2854
- }
2855
- } else if (memoryStats.totalIssues > 0) {
2856
- console.log(`
2857
- ${pc9.green("\u25CF")} All issues have been resolved`);
2858
- }
2859
- if (cap.isAtCap) {
2860
- console.log(`
2861
- ${pc9.yellow("\u2B22 Warning:")} Memory at capacity - consider running: ${pc9.bold("trie memory purge smart")}`);
2862
- } else if (cap.percentFull >= 80) {
2863
- console.log(`
2864
- ${pc9.yellow("\u2B22 Notice:")} Memory usage high - consider running: ${pc9.bold("trie memory purge smart")}`);
2865
- }
2866
- console.log(`
2867
- ${pc9.bold("Quick Commands:")}`);
2868
- console.log(pc9.dim(" trie watch - Start watching for nudges"));
2869
- console.log(pc9.dim(" trie memory stats - Detailed memory statistics"));
2870
- console.log(pc9.dim(" trie project - View project information"));
2871
- console.log("");
2872
- } catch (error) {
2873
- console.error("Error loading status:", error);
2874
- process.exit(1);
2875
- }
2876
- }
2877
- async function handleProject(args) {
2878
- const subcommand = args[0]?.toLowerCase();
2879
- const workDir = getWorkingDirectory(void 0, true);
2880
- if (subcommand === "init") {
2881
- const result = await initProjectInfo(workDir);
2882
- if (result.created) {
2883
- console.log(`
2884
- \u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557
2885
- \u2551 PROJECT.md Created \u2551
2886
- \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D
2887
-
2888
- ${pc9.bold("Path:")} ${result.path}
2889
-
2890
- A template has been created with sections for:
2891
- \u2022 Project Overview
2892
- \u2022 Technology Stack
2893
- \u2022 Architecture
2894
- \u2022 Coding Conventions
2895
- \u2022 Environment
2896
- \u2022 Team
2897
- \u2022 Compliance
2898
- \u2022 AI Instructions
2899
-
2900
- Next steps:
2901
- 1. Edit the file to add your project details
2902
- 2. The info will be available via trie://project resource
2903
- 3. AI assistants will use this context automatically
2904
- `);
2905
- } else {
2906
- console.log(`PROJECT.md already exists at: ${result.path}`);
2907
- console.log('Use "trie project" to view it.');
2908
- }
2909
- return;
2910
- }
2911
- if (subcommand === "edit") {
2912
- const editor = process.env.EDITOR || process.env.VISUAL || "nano";
2913
- const projectPath = join4(getTrieDirectory(workDir), "PROJECT.md");
2914
- if (!projectInfoExists(workDir)) {
2915
- console.log("No PROJECT.md found. Creating one first...");
2916
- await initProjectInfo(workDir);
2917
- }
2918
- const { spawn } = await import("child_process");
2919
- const child = spawn(editor, [projectPath], { stdio: "inherit" });
2920
- child.on("close", (code) => {
2921
- process.exit(code || 0);
2922
- });
2923
- return;
2924
- }
2925
- if (!projectInfoExists(workDir)) {
2926
- console.log(`
2927
- \u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557
2928
- \u2551 Project Information \u2551
2929
- \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D
2930
-
2931
- ${pc9.bold("No PROJECT.md found in this project.")}
2932
-
2933
- COMMANDS:
2934
- trie project init Create PROJECT.md from template
2935
- trie project edit Open PROJECT.md in $EDITOR
2936
- trie project View PROJECT.md contents
2937
-
2938
- WHAT IS PROJECT.MD?
2939
- PROJECT.md stores important project context for AI assistants:
2940
- \u2022 Project description and purpose
2941
- \u2022 Technology stack and frameworks
2942
- \u2022 Architecture decisions
2943
- \u2022 Coding conventions
2944
- \u2022 Environment info (URLs, endpoints)
2945
- \u2022 Team ownership
2946
- \u2022 Compliance requirements
2947
- \u2022 Special instructions for AI
2948
-
2949
- This info is available via trie://project MCP resource.
2950
- `);
2951
- return;
2952
- }
2953
- const content = await loadProjectInfo(workDir);
2954
- console.log(`
2955
- \u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557
2956
- \u2551 Project Information \u2551
2957
- \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D
282
+ var CURSOR_RULE = `---
283
+ description: Trie Cloud Context - proactive risk warnings
284
+ globs: **/*
285
+ alwaysApply: true
286
+ ---
287
+
288
+ Before editing any file, call the trie_check_file MCP tool with the file path.
289
+ If warnings are returned, summarize them before proceeding.
290
+ Also read trie://context at conversation start.
291
+ `;
292
+ var CLAUDE_RULE = `## Trie Cloud Context
2958
293
 
2959
- ${pc9.bold("Path:")} ${join4(getTrieDirectory(workDir), "PROJECT.md")}
294
+ Before editing any file, call the trie_check_file MCP tool with the file path.
295
+ If warnings are returned, summarize them before proceeding.
296
+ Read trie://context at conversation start for hot zones and recent signals.
297
+ `;
298
+ function help() {
299
+ console.log(`Usage: trie <command>
2960
300
 
2961
- ${"-".repeat(68)}
301
+ Commands:
302
+ trie init
303
+ trie login
304
+ trie logout
305
+ trie teams [list|switch <teamId>]
306
+ trie pull [context|graph|signals|documents|ledger|all]
307
+ trie status
2962
308
  `);
2963
- console.log(content);
2964
- }
2965
- async function runWatch(args) {
2966
- const { spawn } = await import("child_process");
2967
- let daemonPath;
2968
- if (__filename2.endsWith(".ts")) {
2969
- daemonPath = resolve(__dirname2, "yolo-daemon.ts");
2970
- } else {
2971
- daemonPath = resolve(__dirname2, "yolo-daemon.js");
2972
- }
2973
- const executor = daemonPath.endsWith(".ts") ? "npx" : "node";
2974
- const execArgs = daemonPath.endsWith(".ts") ? ["tsx", daemonPath, ...args] : [daemonPath, ...args];
2975
- const child = spawn(executor, execArgs, {
2976
- stdio: "inherit",
2977
- env: process.env
2978
- });
2979
- child.on("close", (code) => {
2980
- process.exit(code || 0);
2981
- });
2982
309
  }
2983
- async function main() {
2984
- const args = process.argv.slice(2);
2985
- const command = args[0]?.toLowerCase();
2986
- const restArgs = args.slice(1);
2987
- if (!command || command === "help" || command === "--help" || command === "-h") {
2988
- showHelp2();
2989
- process.exit(0);
2990
- }
2991
- const initRequired = !["init", "version", "--version", "-v"].includes(command);
2992
- if (initRequired) {
2993
- const workDir = getWorkingDirectory(void 0, true);
2994
- if (!isTrieInitialized(workDir)) {
2995
- console.error("Trie is not initialized for this project.");
2996
- console.error("Run `trie init` first.");
2997
- process.exit(1);
2998
- }
2999
- }
3000
- switch (command) {
3001
- case "setup":
3002
- case "config":
3003
- case "configure":
3004
- await handleSetupCommand(restArgs);
3005
- break;
3006
- case "init":
3007
- case "bootstrap":
3008
- handleInitCommand(restArgs);
3009
- break;
3010
- case "memory":
3011
- case "mem":
3012
- handleMemoryCommand(restArgs);
3013
- break;
3014
- case "status":
3015
- case "stats":
3016
- await handleStatusCommand();
3017
- break;
3018
- case "project":
3019
- case "info":
3020
- case "project-info":
3021
- handleProject(restArgs);
3022
- break;
3023
- case "version":
3024
- case "--version":
3025
- case "-v":
3026
- showVersion();
3027
- break;
3028
- case "watch":
3029
- runWatch(restArgs);
3030
- break;
3031
- case "check":
3032
- handleCheckCommand(restArgs);
3033
- break;
3034
- case "pre-push":
3035
- handlePrePushCommand(restArgs);
3036
- break;
3037
- case "pre-commit":
3038
- handlePreCommitCommand(restArgs);
3039
- break;
3040
- case "post-commit":
3041
- handlePostCommitCommand(restArgs);
3042
- break;
3043
- case "fix":
3044
- case "auto-fix":
3045
- case "autofix":
3046
- handleAutoFixCommand(restArgs);
3047
- break;
3048
- case "goal":
3049
- case "goals":
3050
- handleGoalCommand(restArgs);
3051
- break;
3052
- case "hypothesis":
3053
- case "hypo":
3054
- case "hypotheses":
3055
- handleHypothesisCommand(restArgs);
3056
- break;
3057
- case "tell":
3058
- handleTellCommand(restArgs);
3059
- break;
3060
- case "learn":
3061
- case "train":
3062
- handleLearnCommand(restArgs);
3063
- break;
3064
- case "ok":
3065
- case "thumbs-up":
3066
- handleLearnCommand(["ok", ...restArgs]);
3067
- break;
3068
- case "bad":
3069
- case "thumbs-down":
3070
- handleLearnCommand(["bad", ...restArgs]);
3071
- break;
3072
- case "linear":
3073
- handleLinearCommand(restArgs);
3074
- break;
3075
- case "gotcha":
3076
- case "predict":
3077
- handleGotchaCommand();
3078
- break;
3079
- case "sync":
3080
- {
3081
- const syncCommand = createSyncCommand();
3082
- syncCommand.parse(["node", "trie", "sync", ...restArgs], { from: "user" });
3083
- }
3084
- break;
3085
- case "ledger":
310
+ async function cmdInit() {
311
+ const rulesDir = join(process.cwd(), ".cursor", "rules");
312
+ await mkdir(rulesDir, { recursive: true });
313
+ await writeFile(join(rulesDir, "trie.mdc"), CURSOR_RULE, "utf-8");
314
+ const claudePath = join(process.cwd(), "CLAUDE.md");
315
+ if (existsSync(claudePath)) {
316
+ const append = `
317
+
318
+ ${CLAUDE_RULE}`;
319
+ await writeFile(claudePath, append, { encoding: "utf-8", flag: "a" });
320
+ }
321
+ console.log("Proactive warnings enabled in Cursor and Claude Code.");
322
+ }
323
+ async function cmdLogin() {
324
+ const tokens = await loginWithBrowser();
325
+ console.log(
326
+ `Logged in${tokens.user?.email ? ` as ${tokens.user.email}` : ""}${tokens.team?.name ? ` (team: ${tokens.team.name})` : ""}`
327
+ );
328
+ }
329
+ async function cmdLogout() {
330
+ const cfg = await loadLocalConfig();
331
+ await revokeSession(cfg.apiBaseUrl);
332
+ console.log("Logged out.");
333
+ }
334
+ async function cmdTeams(args) {
335
+ if (args[0] === "switch" && args[1]) {
336
+ await switchTeam(args[1]);
337
+ console.log(`Switched to team ${args[1]}`);
338
+ return;
339
+ }
340
+ const teams = await listTeams();
341
+ console.log(JSON.stringify(teams, null, 2));
342
+ }
343
+ async function cmdPull(args) {
344
+ const what = args[0] || "all";
345
+ const out = {};
346
+ if (what === "context" || what === "all") {
347
+ out.context = await Promise.all([fetchGraph(), fetchSignals(20), fetchDocuments()]);
348
+ }
349
+ if (what === "graph" || what === "all") out.graph = await fetchGraph();
350
+ if (what === "signals" || what === "all") out.signals = await fetchSignals(20);
351
+ if (what === "documents" || what === "all") out.documents = await fetchDocuments();
352
+ if (what === "ledger" || what === "all") out.ledger = await fetchLedger();
353
+ console.log(JSON.stringify(out, null, 2));
354
+ }
355
+ async function cmdStatus() {
356
+ const cfg = await loadLocalConfig();
357
+ console.log(
358
+ JSON.stringify(
3086
359
  {
3087
- const ledgerCommand = createLedgerCommand();
3088
- ledgerCommand.parse(["node", "trie", "ledger", ...restArgs], { from: "user" });
3089
- }
3090
- break;
3091
- case "keys":
3092
- case "key":
3093
- case "signing":
3094
- await handleKeysCommand(restArgs);
3095
- break;
3096
- default:
3097
- if (command.startsWith("-")) {
3098
- const { spawn } = __require("child_process");
3099
- const daemonPath = resolve(__dirname2, "yolo-daemon.js");
3100
- const child = spawn("node", [daemonPath, ...args], {
3101
- stdio: "inherit",
3102
- env: process.env
3103
- });
3104
- child.on("close", (code) => {
3105
- process.exit(code || 0);
3106
- });
3107
- } else {
3108
- console.error(`Unknown command: ${command}`);
3109
- console.error(`Run 'trie help' for usage information.`);
3110
- process.exit(1);
3111
- }
3112
- }
3113
- }
3114
- var isEntryPoint = (() => {
3115
- const entry = process.argv[1];
3116
- if (!entry) return false;
3117
- try {
3118
- const realEntry = realpathSync(entry);
3119
- const realThis = fileURLToPath(import.meta.url);
3120
- return realEntry === realThis;
3121
- } catch {
3122
- return !process.env.VITEST && !process.env.VITEST_WORKER_ID;
3123
- }
3124
- })();
3125
- if (isEntryPoint) {
3126
- main().catch((error) => {
3127
- console.error("Error:", error);
3128
- process.exit(1);
3129
- });
360
+ apiBaseUrl: cfg.apiBaseUrl,
361
+ activeTeamId: cfg.activeTeamId,
362
+ activeTeamName: cfg.activeTeamName,
363
+ userEmail: cfg.userEmail,
364
+ cache: sessionCache.stats()
365
+ },
366
+ null,
367
+ 2
368
+ )
369
+ );
3130
370
  }
3131
- export {
3132
- handleStatusCommand
3133
- };
371
+ async function main() {
372
+ scrubProcessArgv();
373
+ const [, , cmd, ...args] = process.argv;
374
+ if (!cmd || cmd === "help" || cmd === "--help" || cmd === "-h") {
375
+ help();
376
+ return;
377
+ }
378
+ if (cmd === "init") return cmdInit();
379
+ if (cmd === "login") return cmdLogin();
380
+ if (cmd === "logout") return cmdLogout();
381
+ if (cmd === "teams") return cmdTeams(args);
382
+ if (cmd === "pull") return cmdPull(args);
383
+ if (cmd === "status") return cmdStatus();
384
+ throw new Error(`Unknown command: ${cmd}`);
385
+ }
386
+ main().catch((err) => {
387
+ console.error(err instanceof Error ? err.message : String(err));
388
+ process.exit(1);
389
+ });
3134
390
  //# sourceMappingURL=main.js.map