@triedotdev/mcp 1.0.169 → 1.0.171
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.
- package/README.md +62 -540
- package/dist/chunk-2YXOBNKW.js +619 -0
- package/dist/chunk-2YXOBNKW.js.map +1 -0
- package/dist/chunk-QR64Y5TI.js +363 -0
- package/dist/chunk-QR64Y5TI.js.map +1 -0
- package/dist/cli/main.d.ts +0 -15
- package/dist/cli/main.js +356 -3098
- package/dist/cli/main.js.map +1 -1
- package/dist/index.js +2 -34
- package/dist/index.js.map +1 -1
- package/dist/server/mcp-server.js +2 -34
- package/package.json +8 -31
- package/dist/autonomy-config-FSERX3O3.js +0 -30
- package/dist/autonomy-config-FSERX3O3.js.map +0 -1
- package/dist/chat-store-JNGNTDSN.js +0 -15
- package/dist/chat-store-JNGNTDSN.js.map +0 -1
- package/dist/chunk-2HF65EHQ.js +0 -311
- package/dist/chunk-2HF65EHQ.js.map +0 -1
- package/dist/chunk-3XR6WVAW.js +0 -4011
- package/dist/chunk-3XR6WVAW.js.map +0 -1
- package/dist/chunk-43X6JBEM.js +0 -36
- package/dist/chunk-43X6JBEM.js.map +0 -1
- package/dist/chunk-6NLHFIYA.js +0 -344
- package/dist/chunk-6NLHFIYA.js.map +0 -1
- package/dist/chunk-7IO4YUI3.js +0 -1827
- package/dist/chunk-7IO4YUI3.js.map +0 -1
- package/dist/chunk-AHD2CBQ7.js +0 -846
- package/dist/chunk-AHD2CBQ7.js.map +0 -1
- package/dist/chunk-BUTOP5EB.js +0 -931
- package/dist/chunk-BUTOP5EB.js.map +0 -1
- package/dist/chunk-DGUM43GV.js +0 -11
- package/dist/chunk-DGUM43GV.js.map +0 -1
- package/dist/chunk-EFWVF6TI.js +0 -267
- package/dist/chunk-EFWVF6TI.js.map +0 -1
- package/dist/chunk-F6WFNUAY.js +0 -216
- package/dist/chunk-F6WFNUAY.js.map +0 -1
- package/dist/chunk-FBNURWRY.js +0 -662
- package/dist/chunk-FBNURWRY.js.map +0 -1
- package/dist/chunk-FQ45QP5A.js +0 -361
- package/dist/chunk-FQ45QP5A.js.map +0 -1
- package/dist/chunk-FVRO5RN3.js +0 -1306
- package/dist/chunk-FVRO5RN3.js.map +0 -1
- package/dist/chunk-G2TGF6TR.js +0 -573
- package/dist/chunk-G2TGF6TR.js.map +0 -1
- package/dist/chunk-G3I7SZLW.js +0 -354
- package/dist/chunk-G3I7SZLW.js.map +0 -1
- package/dist/chunk-GTKYBOXL.js +0 -700
- package/dist/chunk-GTKYBOXL.js.map +0 -1
- package/dist/chunk-HVCDY3AK.js +0 -850
- package/dist/chunk-HVCDY3AK.js.map +0 -1
- package/dist/chunk-I2O5OYQT.js +0 -727
- package/dist/chunk-I2O5OYQT.js.map +0 -1
- package/dist/chunk-JVMBCWKS.js +0 -348
- package/dist/chunk-JVMBCWKS.js.map +0 -1
- package/dist/chunk-KCUOWRPX.js +0 -816
- package/dist/chunk-KCUOWRPX.js.map +0 -1
- package/dist/chunk-KDHN2ZQE.js +0 -313
- package/dist/chunk-KDHN2ZQE.js.map +0 -1
- package/dist/chunk-ME2OERF5.js +0 -345
- package/dist/chunk-ME2OERF5.js.map +0 -1
- package/dist/chunk-OBQ74FOU.js +0 -27
- package/dist/chunk-OBQ74FOU.js.map +0 -1
- package/dist/chunk-Q5EKA5YA.js +0 -254
- package/dist/chunk-Q5EKA5YA.js.map +0 -1
- package/dist/chunk-Q63FFI6D.js +0 -132
- package/dist/chunk-Q63FFI6D.js.map +0 -1
- package/dist/chunk-SASNMSB5.js +0 -12597
- package/dist/chunk-SASNMSB5.js.map +0 -1
- package/dist/chunk-T63OHG4Q.js +0 -440
- package/dist/chunk-T63OHG4Q.js.map +0 -1
- package/dist/chunk-TN5WEKWI.js +0 -173
- package/dist/chunk-TN5WEKWI.js.map +0 -1
- package/dist/chunk-VUL52BQL.js +0 -402
- package/dist/chunk-VUL52BQL.js.map +0 -1
- package/dist/chunk-VVITXIHN.js +0 -189
- package/dist/chunk-VVITXIHN.js.map +0 -1
- package/dist/chunk-WCN7S3EI.js +0 -14
- package/dist/chunk-WCN7S3EI.js.map +0 -1
- package/dist/chunk-XPZZFPBZ.js +0 -491
- package/dist/chunk-XPZZFPBZ.js.map +0 -1
- package/dist/chunk-ZJF5FTBX.js +0 -1396
- package/dist/chunk-ZJF5FTBX.js.map +0 -1
- package/dist/chunk-ZV2K6M7T.js +0 -74
- package/dist/chunk-ZV2K6M7T.js.map +0 -1
- package/dist/cli/create-agent.d.ts +0 -1
- package/dist/cli/create-agent.js +0 -1050
- package/dist/cli/create-agent.js.map +0 -1
- package/dist/cli/yolo-daemon.d.ts +0 -1
- package/dist/cli/yolo-daemon.js +0 -421
- package/dist/cli/yolo-daemon.js.map +0 -1
- package/dist/client-NJPZE5JT.js +0 -28
- package/dist/client-NJPZE5JT.js.map +0 -1
- package/dist/codebase-index-VAPF32XX.js +0 -12
- package/dist/codebase-index-VAPF32XX.js.map +0 -1
- package/dist/fast-analyzer-3GCCZMLK.js +0 -216
- package/dist/fast-analyzer-3GCCZMLK.js.map +0 -1
- package/dist/git-EO5SRFMN.js +0 -28
- package/dist/git-EO5SRFMN.js.map +0 -1
- package/dist/github-ingester-ZOKK6GRS.js +0 -11
- package/dist/github-ingester-ZOKK6GRS.js.map +0 -1
- package/dist/goal-manager-QUKX2W6C.js +0 -25
- package/dist/goal-manager-QUKX2W6C.js.map +0 -1
- package/dist/goal-validator-2SFSKKVU.js +0 -24
- package/dist/goal-validator-2SFSKKVU.js.map +0 -1
- package/dist/graph-B3NA4S7I.js +0 -10
- package/dist/graph-B3NA4S7I.js.map +0 -1
- package/dist/hypothesis-KCPBR652.js +0 -23
- package/dist/hypothesis-KCPBR652.js.map +0 -1
- package/dist/incident-index-EFNUSGWL.js +0 -11
- package/dist/incident-index-EFNUSGWL.js.map +0 -1
- package/dist/insight-store-EC4PLSAW.js +0 -22
- package/dist/insight-store-EC4PLSAW.js.map +0 -1
- package/dist/issue-store-YAXTNRRY.js +0 -36
- package/dist/issue-store-YAXTNRRY.js.map +0 -1
- package/dist/ledger-TWZTGDFA.js +0 -58
- package/dist/ledger-TWZTGDFA.js.map +0 -1
- package/dist/linear-ingester-XXPAZZRW.js +0 -11
- package/dist/linear-ingester-XXPAZZRW.js.map +0 -1
- package/dist/output-manager-RVJ37XKA.js +0 -13
- package/dist/output-manager-RVJ37XKA.js.map +0 -1
- package/dist/parse-goal-violation-SACGFG3C.js +0 -8
- package/dist/parse-goal-violation-SACGFG3C.js.map +0 -1
- package/dist/pattern-discovery-F7LU5K6E.js +0 -8
- package/dist/pattern-discovery-F7LU5K6E.js.map +0 -1
- package/dist/progress-SRQ2V3BP.js +0 -18
- package/dist/progress-SRQ2V3BP.js.map +0 -1
- package/dist/project-state-AHPA77SM.js +0 -28
- package/dist/project-state-AHPA77SM.js.map +0 -1
- package/dist/sync-M2FSWPBC.js +0 -12
- package/dist/sync-M2FSWPBC.js.map +0 -1
- package/dist/terminal-spawn-5YXDMUCF.js +0 -157
- package/dist/terminal-spawn-5YXDMUCF.js.map +0 -1
- package/dist/tiered-storage-DYNC5CQ6.js +0 -13
- package/dist/tiered-storage-DYNC5CQ6.js.map +0 -1
- package/dist/trie-agent-I3HAHY2G.js +0 -26
- package/dist/trie-agent-I3HAHY2G.js.map +0 -1
- package/dist/ui/chat.html +0 -1014
- package/dist/ui/goals.html +0 -967
- package/dist/ui/hypotheses.html +0 -1011
- package/dist/ui/ledger.html +0 -954
- package/dist/ui/nudges.html +0 -995
- package/dist/vibe-code-signatures-5ZULYP3D.js +0 -987
- package/dist/vibe-code-signatures-5ZULYP3D.js.map +0 -1
- package/dist/vulnerability-signatures-2URZSXAQ.js +0 -983
- 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
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
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/
|
|
1961
|
-
import
|
|
1962
|
-
|
|
1963
|
-
|
|
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/
|
|
2031
|
-
async function
|
|
2032
|
-
|
|
2033
|
-
|
|
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
|
-
|
|
2068
|
-
|
|
2069
|
-
|
|
2070
|
-
|
|
2071
|
-
|
|
2072
|
-
|
|
2073
|
-
|
|
2074
|
-
|
|
2075
|
-
|
|
2076
|
-
|
|
2077
|
-
|
|
2078
|
-
|
|
2079
|
-
|
|
2080
|
-
|
|
2081
|
-
|
|
2082
|
-
|
|
2083
|
-
|
|
2084
|
-
|
|
2085
|
-
|
|
2086
|
-
|
|
2087
|
-
|
|
2088
|
-
|
|
2089
|
-
|
|
2090
|
-
|
|
2091
|
-
|
|
2092
|
-
|
|
2093
|
-
|
|
2094
|
-
|
|
2095
|
-
|
|
2096
|
-
|
|
2097
|
-
|
|
2098
|
-
|
|
2099
|
-
|
|
2100
|
-
|
|
2101
|
-
|
|
2102
|
-
|
|
2103
|
-
|
|
2104
|
-
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
|
|
2108
|
-
|
|
2109
|
-
|
|
2110
|
-
|
|
2111
|
-
|
|
2112
|
-
|
|
2113
|
-
|
|
2114
|
-
|
|
2115
|
-
|
|
2116
|
-
|
|
2117
|
-
|
|
2118
|
-
|
|
2119
|
-
|
|
2120
|
-
|
|
2121
|
-
|
|
2122
|
-
|
|
2123
|
-
|
|
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
|
-
|
|
2128
|
-
|
|
2129
|
-
|
|
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
|
-
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
|
|
2140
|
-
|
|
2141
|
-
|
|
2142
|
-
|
|
2143
|
-
|
|
2144
|
-
|
|
2145
|
-
|
|
2146
|
-
|
|
2147
|
-
|
|
2148
|
-
|
|
2149
|
-
|
|
2150
|
-
const
|
|
2151
|
-
|
|
2152
|
-
|
|
2153
|
-
|
|
2154
|
-
|
|
2155
|
-
if (
|
|
2156
|
-
|
|
2157
|
-
|
|
2158
|
-
|
|
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
|
-
|
|
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
|
-
|
|
2288
|
-
|
|
2289
|
-
|
|
175
|
+
if (used) {
|
|
176
|
+
res.statusCode = 403;
|
|
177
|
+
res.end("Callback already used");
|
|
2290
178
|
return;
|
|
2291
179
|
}
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
|
|
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
|
-
|
|
2340
|
-
|
|
2341
|
-
|
|
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
|
-
|
|
2439
|
-
|
|
2440
|
-
|
|
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
|
-
|
|
2464
|
-
|
|
2465
|
-
|
|
2466
|
-
|
|
2467
|
-
|
|
2468
|
-
|
|
2469
|
-
|
|
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
|
-
|
|
2555
|
-
|
|
2556
|
-
|
|
2557
|
-
|
|
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
|
-
|
|
2575
|
-
const
|
|
2576
|
-
|
|
2577
|
-
|
|
2578
|
-
|
|
2579
|
-
|
|
2580
|
-
|
|
2581
|
-
|
|
2582
|
-
|
|
2583
|
-
|
|
2584
|
-
|
|
2585
|
-
|
|
2586
|
-
|
|
2587
|
-
|
|
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
|
|
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
|
-
|
|
2715
|
-
|
|
2716
|
-
|
|
2717
|
-
|
|
2718
|
-
|
|
2719
|
-
|
|
2720
|
-
|
|
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
|
|
2727
|
-
|
|
2728
|
-
|
|
2729
|
-
|
|
2730
|
-
|
|
2731
|
-
|
|
2732
|
-
|
|
2733
|
-
|
|
2734
|
-
|
|
2735
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
2982
|
-
const
|
|
2983
|
-
|
|
2984
|
-
|
|
2985
|
-
|
|
2986
|
-
|
|
2987
|
-
|
|
2988
|
-
|
|
2989
|
-
|
|
2990
|
-
|
|
2991
|
-
|
|
2992
|
-
|
|
2993
|
-
|
|
2994
|
-
|
|
2995
|
-
|
|
2996
|
-
|
|
2997
|
-
|
|
2998
|
-
|
|
2999
|
-
|
|
3000
|
-
|
|
3001
|
-
|
|
3002
|
-
|
|
3003
|
-
|
|
3004
|
-
|
|
3005
|
-
|
|
3006
|
-
|
|
3007
|
-
|
|
3008
|
-
|
|
3009
|
-
|
|
3010
|
-
|
|
3011
|
-
|
|
3012
|
-
|
|
3013
|
-
|
|
3014
|
-
|
|
3015
|
-
|
|
3016
|
-
|
|
3017
|
-
|
|
3018
|
-
|
|
3019
|
-
|
|
3020
|
-
|
|
3021
|
-
|
|
3022
|
-
|
|
3023
|
-
|
|
3024
|
-
|
|
3025
|
-
|
|
3026
|
-
|
|
3027
|
-
|
|
3028
|
-
|
|
3029
|
-
|
|
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
|
-
|
|
3086
|
-
|
|
3087
|
-
|
|
3088
|
-
|
|
3089
|
-
|
|
3090
|
-
|
|
3091
|
-
|
|
3092
|
-
|
|
3093
|
-
|
|
3094
|
-
|
|
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
|
-
|
|
3130
|
-
|
|
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
|