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