@triedotdev/mcp 1.0.61 → 1.0.63
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 +591 -52
- package/dist/agent-smith-W4HUCFGC.js +14 -0
- package/dist/{agent-smith-runner-ZU4R3I2Z.js → agent-smith-runner-QRVOEOBE.js} +13 -7
- package/dist/agent-smith-runner-QRVOEOBE.js.map +1 -0
- package/dist/chunk-4YSLDGBL.js +674 -0
- package/dist/chunk-4YSLDGBL.js.map +1 -0
- package/dist/chunk-7KHT2NKR.js +212 -0
- package/dist/chunk-7KHT2NKR.js.map +1 -0
- package/dist/{chunk-XSPS463E.js → chunk-ALA6733H.js} +492 -14
- package/dist/chunk-ALA6733H.js.map +1 -0
- package/dist/chunk-AQCAMIQQ.js +139 -0
- package/dist/chunk-AQCAMIQQ.js.map +1 -0
- package/dist/chunk-D3DMONAJ.js +904 -0
- package/dist/chunk-D3DMONAJ.js.map +1 -0
- package/dist/{chunk-KB5ZN6K2.js → chunk-GWSNINKX.js} +2 -2
- package/dist/{chunk-32WLOG6E.js → chunk-K6BQBKIR.js} +662 -633
- package/dist/chunk-K6BQBKIR.js.map +1 -0
- package/dist/{chunk-ASGSTVVF.js → chunk-KOFQ47YW.js} +10 -6
- package/dist/chunk-KOFQ47YW.js.map +1 -0
- package/dist/{chunk-XVGHO2Z5.js → chunk-N2AZH3EQ.js} +7683 -4777
- package/dist/chunk-N2AZH3EQ.js.map +1 -0
- package/dist/chunk-PBOVCPKE.js +2566 -0
- package/dist/chunk-PBOVCPKE.js.map +1 -0
- package/dist/{chunk-NUT4G5AY.js → chunk-R7Z7OHTJ.js} +493 -650
- package/dist/chunk-R7Z7OHTJ.js.map +1 -0
- package/dist/chunk-TSHZQKCM.js +933 -0
- package/dist/chunk-TSHZQKCM.js.map +1 -0
- package/dist/{chunk-S4VGGLXF.js → chunk-X2PABPBH.js} +461 -892
- package/dist/chunk-X2PABPBH.js.map +1 -0
- package/dist/cli/create-agent.js +3 -2
- package/dist/cli/create-agent.js.map +1 -1
- package/dist/cli/main.js +1120 -70
- package/dist/cli/main.js.map +1 -1
- package/dist/cli/yolo-daemon.js +151 -41
- package/dist/cli/yolo-daemon.js.map +1 -1
- package/dist/goal-manager-KFBOAP4X.js +20 -0
- package/dist/goal-manager-KFBOAP4X.js.map +1 -0
- package/dist/guardian-agent-PULK546O.js +17 -0
- package/dist/guardian-agent-PULK546O.js.map +1 -0
- package/dist/index.js +173 -39
- package/dist/index.js.map +1 -1
- package/dist/issue-store-QRDF3X55.js +22 -0
- package/dist/issue-store-QRDF3X55.js.map +1 -0
- package/dist/workers/agent-worker.js +6 -3
- package/dist/workers/agent-worker.js.map +1 -1
- package/package.json +1 -1
- package/dist/agent-smith-57MKX5QC.js +0 -13
- package/dist/agent-smith-runner-ZU4R3I2Z.js.map +0 -1
- package/dist/chunk-32WLOG6E.js.map +0 -1
- package/dist/chunk-ASGSTVVF.js.map +0 -1
- package/dist/chunk-NUT4G5AY.js.map +0 -1
- package/dist/chunk-S4VGGLXF.js.map +0 -1
- package/dist/chunk-XSPS463E.js.map +0 -1
- package/dist/chunk-XVGHO2Z5.js.map +0 -1
- /package/dist/{agent-smith-57MKX5QC.js.map → agent-smith-W4HUCFGC.js.map} +0 -0
- /package/dist/{chunk-KB5ZN6K2.js.map → chunk-GWSNINKX.js.map} +0 -0
package/dist/cli/main.js
CHANGED
|
@@ -2,56 +2,71 @@
|
|
|
2
2
|
import {
|
|
3
3
|
ContextGraph,
|
|
4
4
|
IncidentIndex,
|
|
5
|
-
SKILL_CATEGORIES,
|
|
6
5
|
TrieFeedbackTool,
|
|
7
6
|
completeBootstrap,
|
|
8
|
-
detectStack,
|
|
9
7
|
exportToJson,
|
|
10
8
|
formatFriendlyError,
|
|
11
|
-
|
|
12
|
-
getSkillsByCategory,
|
|
9
|
+
getAutonomyConfig,
|
|
13
10
|
handleCheckpointCommand,
|
|
14
11
|
importFromJson,
|
|
15
12
|
initializeBootstrapFiles,
|
|
16
13
|
needsBootstrap,
|
|
17
14
|
perceiveCurrentChanges,
|
|
18
|
-
reasonAboutChangesHumanReadable
|
|
19
|
-
|
|
20
|
-
|
|
15
|
+
reasonAboutChangesHumanReadable,
|
|
16
|
+
recordBypass,
|
|
17
|
+
shouldAutoFix,
|
|
18
|
+
shouldBlockPush,
|
|
19
|
+
trackIssueOccurrence
|
|
20
|
+
} from "../chunk-R7Z7OHTJ.js";
|
|
21
|
+
import {
|
|
22
|
+
SKILL_CATEGORIES,
|
|
23
|
+
detectStack,
|
|
24
|
+
getSkillCategories,
|
|
25
|
+
getSkillsByCategory,
|
|
26
|
+
isTrieInitialized
|
|
27
|
+
} from "../chunk-ALA6733H.js";
|
|
28
|
+
import {
|
|
29
|
+
getGuardianState
|
|
30
|
+
} from "../chunk-4YSLDGBL.js";
|
|
21
31
|
import {
|
|
22
|
-
findCrossProjectPatterns,
|
|
23
|
-
getDailyLogs,
|
|
24
|
-
getGlobalMemoryStats,
|
|
25
|
-
getMemoryStats,
|
|
26
|
-
getRecentIssues,
|
|
27
32
|
initProjectInfo,
|
|
28
33
|
installSkill,
|
|
29
34
|
listInstalledSkills,
|
|
30
|
-
listTrackedProjects,
|
|
31
35
|
loadContextState,
|
|
32
36
|
loadProjectInfo,
|
|
33
|
-
markIssueResolved,
|
|
34
37
|
projectInfoExists,
|
|
35
38
|
recordSkillInstalled,
|
|
36
|
-
removeSkill
|
|
39
|
+
removeSkill
|
|
40
|
+
} from "../chunk-X2PABPBH.js";
|
|
41
|
+
import {
|
|
42
|
+
findCrossProjectPatterns,
|
|
43
|
+
getGlobalMemoryStats,
|
|
44
|
+
listTrackedProjects,
|
|
37
45
|
searchGlobalPatterns,
|
|
38
|
-
searchIssues,
|
|
39
46
|
updateGlobalMemoryMd
|
|
40
|
-
} from "../chunk-
|
|
47
|
+
} from "../chunk-7KHT2NKR.js";
|
|
41
48
|
import "../chunk-MVUCBUBR.js";
|
|
42
|
-
import "../chunk-
|
|
49
|
+
import "../chunk-6NLHFIYA.js";
|
|
50
|
+
import "../chunk-K6BQBKIR.js";
|
|
51
|
+
import "../chunk-AQCAMIQQ.js";
|
|
52
|
+
import {
|
|
53
|
+
getDailyLogs,
|
|
54
|
+
getMemoryStats,
|
|
55
|
+
getRecentIssues,
|
|
56
|
+
markIssueResolved,
|
|
57
|
+
searchIssues
|
|
58
|
+
} from "../chunk-TSHZQKCM.js";
|
|
43
59
|
import {
|
|
44
60
|
getWorkingDirectory
|
|
45
|
-
} from "../chunk-
|
|
46
|
-
import "../chunk-6NLHFIYA.js";
|
|
61
|
+
} from "../chunk-KOFQ47YW.js";
|
|
47
62
|
import "../chunk-RAZUNSBI.js";
|
|
48
63
|
import {
|
|
49
64
|
__require
|
|
50
65
|
} from "../chunk-DGUM43GV.js";
|
|
51
66
|
|
|
52
67
|
// src/cli/main.ts
|
|
53
|
-
import { resolve, join, dirname } from "path";
|
|
54
|
-
import { existsSync, readFileSync } from "fs";
|
|
68
|
+
import { resolve, join as join2, dirname } from "path";
|
|
69
|
+
import { existsSync as existsSync3, readFileSync } from "fs";
|
|
55
70
|
import { fileURLToPath } from "url";
|
|
56
71
|
|
|
57
72
|
// src/cli/skills.ts
|
|
@@ -61,12 +76,12 @@ async function handleSkillsCommand(args) {
|
|
|
61
76
|
case "add":
|
|
62
77
|
case "install": {
|
|
63
78
|
if (!rest[0]) {
|
|
64
|
-
console.log("Usage: trie
|
|
79
|
+
console.log("Usage: trie skills add <owner/repo> [skill-name]");
|
|
65
80
|
console.log("");
|
|
66
81
|
console.log("Examples:");
|
|
67
|
-
console.log(" trie
|
|
68
|
-
console.log(" trie
|
|
69
|
-
console.log(" trie
|
|
82
|
+
console.log(" trie skills add vercel-labs/agent-skills react-best-practices");
|
|
83
|
+
console.log(" trie skills add anthropics/skills claude-code-review");
|
|
84
|
+
console.log(" trie skills add myorg/internal-standards");
|
|
70
85
|
return;
|
|
71
86
|
}
|
|
72
87
|
console.log(`Installing skill from ${rest[0]}...`);
|
|
@@ -77,7 +92,7 @@ async function handleSkillsCommand(args) {
|
|
|
77
92
|
console.log(`Path: ${result.path}`);
|
|
78
93
|
console.log("");
|
|
79
94
|
console.log("This skill is now a capability the skill-review agent can apply.");
|
|
80
|
-
console.log('Run "trie
|
|
95
|
+
console.log('Run "trie scan" to apply skills to your codebase.');
|
|
81
96
|
} else {
|
|
82
97
|
console.error(`Failed to install: ${result.error}`);
|
|
83
98
|
process.exit(1);
|
|
@@ -164,7 +179,7 @@ async function handleSkillsCommand(args) {
|
|
|
164
179
|
case "rm":
|
|
165
180
|
case "uninstall": {
|
|
166
181
|
if (!rest[0]) {
|
|
167
|
-
console.log("Usage: trie
|
|
182
|
+
console.log("Usage: trie skills remove <skill-name>");
|
|
168
183
|
return;
|
|
169
184
|
}
|
|
170
185
|
const removed = await removeSkill(rest[0]);
|
|
@@ -206,7 +221,7 @@ async function handleSkillsCommand(args) {
|
|
|
206
221
|
}
|
|
207
222
|
case "info": {
|
|
208
223
|
if (!rest[0]) {
|
|
209
|
-
console.log("Usage: trie
|
|
224
|
+
console.log("Usage: trie skills info <skill-name>");
|
|
210
225
|
return;
|
|
211
226
|
}
|
|
212
227
|
const skills = await listInstalledSkills();
|
|
@@ -290,6 +305,46 @@ else
|
|
|
290
305
|
fi
|
|
291
306
|
`;
|
|
292
307
|
}
|
|
308
|
+
function prePushCommand() {
|
|
309
|
+
return `
|
|
310
|
+
TRIE_BIN="$(command -v trie || command -v trie-agent || true)"
|
|
311
|
+
if [ -n "$TRIE_BIN" ]; then
|
|
312
|
+
# Check for bypass
|
|
313
|
+
if [ "$TRIE_BYPASS" = "1" ] || [ "$TRIE_BYPASS" = "true" ]; then
|
|
314
|
+
echo "\u26A0\uFE0F Trie push blocking bypassed (TRIE_BYPASS=1)"
|
|
315
|
+
"$TRIE_BIN" pre-push --bypassed || true
|
|
316
|
+
exit 0
|
|
317
|
+
fi
|
|
318
|
+
|
|
319
|
+
# Run pre-push check with blocking enabled
|
|
320
|
+
"$TRIE_BIN" pre-push --block
|
|
321
|
+
EXIT_CODE=$?
|
|
322
|
+
|
|
323
|
+
if [ $EXIT_CODE -eq 0 ]; then
|
|
324
|
+
exit 0
|
|
325
|
+
elif [ $EXIT_CODE -eq 2 ]; then
|
|
326
|
+
# Exit code 2 = blocked but bypassable
|
|
327
|
+
echo ""
|
|
328
|
+
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"
|
|
329
|
+
echo "\u{1F6D1} Push blocked by Trie due to critical issues."
|
|
330
|
+
echo ""
|
|
331
|
+
echo "To bypass (not recommended):"
|
|
332
|
+
echo " TRIE_BYPASS=1 git push"
|
|
333
|
+
echo " OR: git push --no-verify"
|
|
334
|
+
echo ""
|
|
335
|
+
echo "To fix issues first:"
|
|
336
|
+
echo " trie fix"
|
|
337
|
+
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"
|
|
338
|
+
exit 1
|
|
339
|
+
else
|
|
340
|
+
# Other errors - don't block
|
|
341
|
+
exit 0
|
|
342
|
+
fi
|
|
343
|
+
else
|
|
344
|
+
echo "Trie not available; skipping pre-push hook."
|
|
345
|
+
fi
|
|
346
|
+
`;
|
|
347
|
+
}
|
|
293
348
|
function preCommitTemplate() {
|
|
294
349
|
return (hookHeader() + "# Trie pre-commit hook\n" + trieCommand("pre-commit")).trim() + "\n";
|
|
295
350
|
}
|
|
@@ -297,7 +352,7 @@ function postCommitTemplate() {
|
|
|
297
352
|
return (hookHeader() + "# Trie post-commit hook\n" + trieCommand("post-commit")).trim() + "\n";
|
|
298
353
|
}
|
|
299
354
|
function prePushTemplate() {
|
|
300
|
-
return (hookHeader() + "# Trie pre-push hook\n" +
|
|
355
|
+
return (hookHeader() + "# Trie pre-push hook with blocking support\n# Bypass with: TRIE_BYPASS=1 git push\n" + prePushCommand()).trim() + "\n";
|
|
301
356
|
}
|
|
302
357
|
|
|
303
358
|
// src/hooks/install.ts
|
|
@@ -767,6 +822,792 @@ async function handleCheckCommand(args) {
|
|
|
767
822
|
}
|
|
768
823
|
}
|
|
769
824
|
|
|
825
|
+
// src/cli/pre-push.ts
|
|
826
|
+
import pc from "picocolors";
|
|
827
|
+
function parseArgs2(args) {
|
|
828
|
+
return {
|
|
829
|
+
block: args.includes("--block"),
|
|
830
|
+
bypassed: args.includes("--bypassed"),
|
|
831
|
+
quick: args.includes("--quick") || args.includes("-q")
|
|
832
|
+
};
|
|
833
|
+
}
|
|
834
|
+
async function handlePrePushCommand(args) {
|
|
835
|
+
const options = parseArgs2(args);
|
|
836
|
+
const projectPath = getWorkingDirectory(void 0, true);
|
|
837
|
+
try {
|
|
838
|
+
const autonomyConfig = await getAutonomyConfig(projectPath);
|
|
839
|
+
if (options.bypassed) {
|
|
840
|
+
console.error(pc.yellow("\u26A0\uFE0F Pre-push check bypassed by user"));
|
|
841
|
+
if (autonomyConfig.pushBlocking.logBypasses) {
|
|
842
|
+
await recordBypass(
|
|
843
|
+
projectPath,
|
|
844
|
+
"git-push",
|
|
845
|
+
void 0,
|
|
846
|
+
"pre-push-bypass",
|
|
847
|
+
"env",
|
|
848
|
+
"User set TRIE_BYPASS=1"
|
|
849
|
+
);
|
|
850
|
+
}
|
|
851
|
+
process.exit(0);
|
|
852
|
+
}
|
|
853
|
+
const perception = await perceiveCurrentChanges(projectPath);
|
|
854
|
+
const files = perception.diffSummary.files.map((f) => f.filePath);
|
|
855
|
+
if (files.length === 0) {
|
|
856
|
+
console.error(pc.green("\u2713 No changes to check"));
|
|
857
|
+
process.exit(0);
|
|
858
|
+
}
|
|
859
|
+
console.error(pc.cyan(`
|
|
860
|
+
\u{1F50D} Checking ${files.length} file(s) before push...
|
|
861
|
+
`));
|
|
862
|
+
const reasoning = await reasonAboutChangesHumanReadable(projectPath, files, {
|
|
863
|
+
runAgents: !options.quick,
|
|
864
|
+
scanContext: {
|
|
865
|
+
config: { timeoutMs: options.quick ? 15e3 : 3e4 }
|
|
866
|
+
}
|
|
867
|
+
});
|
|
868
|
+
if (options.block && autonomyConfig.pushBlocking.enabled) {
|
|
869
|
+
const issues = collectIssuesForBlocking(reasoning);
|
|
870
|
+
for (const issue of issues) {
|
|
871
|
+
await trackIssueOccurrence(
|
|
872
|
+
projectPath,
|
|
873
|
+
issue.file,
|
|
874
|
+
issue.line,
|
|
875
|
+
issue.severity,
|
|
876
|
+
autonomyConfig
|
|
877
|
+
);
|
|
878
|
+
}
|
|
879
|
+
const blockResult = shouldBlockPush(issues, autonomyConfig);
|
|
880
|
+
if (blockResult.blocked) {
|
|
881
|
+
console.error("");
|
|
882
|
+
console.error(pc.red("\u2501".repeat(60)));
|
|
883
|
+
console.error(pc.bold(pc.red("\u{1F6D1} PUSH BLOCKED")));
|
|
884
|
+
console.error(pc.red("\u2501".repeat(60)));
|
|
885
|
+
console.error("");
|
|
886
|
+
console.error(pc.yellow(`Found ${blockResult.blockingIssues.length} blocking issue(s):`));
|
|
887
|
+
console.error("");
|
|
888
|
+
for (const issue of blockResult.blockingIssues.slice(0, 5)) {
|
|
889
|
+
const icon = issue.severity === "critical" ? "\u{1F534}" : "\u{1F7E1}";
|
|
890
|
+
console.error(` ${icon} ${pc.dim(issue.file)}${issue.line ? `:${issue.line}` : ""}`);
|
|
891
|
+
console.error(` ${issue.issue.slice(0, 80)}${issue.issue.length > 80 ? "..." : ""}`);
|
|
892
|
+
}
|
|
893
|
+
if (blockResult.blockingIssues.length > 5) {
|
|
894
|
+
console.error(` ... and ${blockResult.blockingIssues.length - 5} more`);
|
|
895
|
+
}
|
|
896
|
+
console.error("");
|
|
897
|
+
console.error(pc.dim("\u2500".repeat(60)));
|
|
898
|
+
if (blockResult.bypassInstructions) {
|
|
899
|
+
console.error("");
|
|
900
|
+
console.error(pc.yellow("To bypass (not recommended):"));
|
|
901
|
+
for (const line of blockResult.bypassInstructions.split("\n")) {
|
|
902
|
+
console.error(pc.dim(line));
|
|
903
|
+
}
|
|
904
|
+
}
|
|
905
|
+
console.error("");
|
|
906
|
+
console.error(pc.cyan("To fix issues:"));
|
|
907
|
+
console.error(pc.dim(" trie fix # Auto-fix trivial issues"));
|
|
908
|
+
console.error(pc.dim(" trie scan # See all issues"));
|
|
909
|
+
console.error("");
|
|
910
|
+
process.exit(2);
|
|
911
|
+
} else if (blockResult.bypassed) {
|
|
912
|
+
console.error(pc.yellow(`\u26A0\uFE0F Bypassed: ${blockResult.blockingIssues.length} issue(s) ignored`));
|
|
913
|
+
}
|
|
914
|
+
}
|
|
915
|
+
const riskLevel = reasoning.original?.riskLevel || "low";
|
|
916
|
+
const riskColor = riskLevel === "critical" ? pc.red : riskLevel === "high" ? pc.yellow : riskLevel === "medium" ? pc.blue : pc.green;
|
|
917
|
+
console.error("");
|
|
918
|
+
console.error(riskColor(`Risk: ${riskLevel.toUpperCase()}`));
|
|
919
|
+
console.error(pc.dim(reasoning.summary || "No significant risks detected"));
|
|
920
|
+
console.error("");
|
|
921
|
+
process.exit(0);
|
|
922
|
+
} catch (error) {
|
|
923
|
+
const friendly = formatFriendlyError(error);
|
|
924
|
+
console.error(pc.yellow(`\u26A0\uFE0F Pre-push check error: ${friendly.userMessage}`));
|
|
925
|
+
console.error(pc.dim("Allowing push to continue."));
|
|
926
|
+
process.exit(0);
|
|
927
|
+
}
|
|
928
|
+
}
|
|
929
|
+
function collectIssuesForBlocking(reasoning) {
|
|
930
|
+
const issues = [];
|
|
931
|
+
if (reasoning.original?.files) {
|
|
932
|
+
for (const file of reasoning.original.files) {
|
|
933
|
+
if (file.level === "critical" || file.level === "high") {
|
|
934
|
+
issues.push({
|
|
935
|
+
file: file.file,
|
|
936
|
+
severity: file.level === "critical" ? "critical" : "serious",
|
|
937
|
+
issue: file.reasons?.join("; ") || "High-risk file"
|
|
938
|
+
});
|
|
939
|
+
}
|
|
940
|
+
}
|
|
941
|
+
}
|
|
942
|
+
if (reasoning.scanResults?.issues) {
|
|
943
|
+
for (const issue of reasoning.scanResults.issues) {
|
|
944
|
+
if (issue.severity === "critical" || issue.severity === "serious") {
|
|
945
|
+
issues.push({
|
|
946
|
+
file: issue.file,
|
|
947
|
+
line: issue.line,
|
|
948
|
+
severity: issue.severity,
|
|
949
|
+
issue: issue.issue
|
|
950
|
+
});
|
|
951
|
+
}
|
|
952
|
+
}
|
|
953
|
+
}
|
|
954
|
+
return issues;
|
|
955
|
+
}
|
|
956
|
+
|
|
957
|
+
// src/cli/auto-fix.ts
|
|
958
|
+
import { readFile, writeFile } from "fs/promises";
|
|
959
|
+
import { existsSync } from "fs";
|
|
960
|
+
import { createInterface } from "readline";
|
|
961
|
+
import pc2 from "picocolors";
|
|
962
|
+
function detectAutoFixes(issues) {
|
|
963
|
+
const fixes = [];
|
|
964
|
+
for (const issue of issues) {
|
|
965
|
+
const fix = generateFixForIssue(issue);
|
|
966
|
+
if (fix) {
|
|
967
|
+
fixes.push(fix);
|
|
968
|
+
}
|
|
969
|
+
}
|
|
970
|
+
return fixes;
|
|
971
|
+
}
|
|
972
|
+
function generateFixForIssue(issue) {
|
|
973
|
+
if (!issue.line) return null;
|
|
974
|
+
if (issue.issue.toLowerCase().includes("console.log") || issue.issue.toLowerCase().includes("console log")) {
|
|
975
|
+
return {
|
|
976
|
+
id: `fix-${issue.id}`,
|
|
977
|
+
file: issue.file,
|
|
978
|
+
line: issue.line,
|
|
979
|
+
original: "",
|
|
980
|
+
// Will be filled when reading file
|
|
981
|
+
fixed: "",
|
|
982
|
+
// Will be deletion
|
|
983
|
+
type: "remove-console-log",
|
|
984
|
+
category: "trivial",
|
|
985
|
+
description: "Remove console.log statement",
|
|
986
|
+
confidence: 0.95
|
|
987
|
+
};
|
|
988
|
+
}
|
|
989
|
+
if (issue.issue.toLowerCase().includes("debugger")) {
|
|
990
|
+
return {
|
|
991
|
+
id: `fix-${issue.id}`,
|
|
992
|
+
file: issue.file,
|
|
993
|
+
line: issue.line,
|
|
994
|
+
original: "",
|
|
995
|
+
fixed: "",
|
|
996
|
+
type: "remove-debugger",
|
|
997
|
+
category: "trivial",
|
|
998
|
+
description: "Remove debugger statement",
|
|
999
|
+
confidence: 0.98
|
|
1000
|
+
};
|
|
1001
|
+
}
|
|
1002
|
+
return null;
|
|
1003
|
+
}
|
|
1004
|
+
async function promptUser(question) {
|
|
1005
|
+
const rl = createInterface({
|
|
1006
|
+
input: process.stdin,
|
|
1007
|
+
output: process.stderr
|
|
1008
|
+
});
|
|
1009
|
+
return new Promise((resolve2) => {
|
|
1010
|
+
rl.question(question, (answer) => {
|
|
1011
|
+
rl.close();
|
|
1012
|
+
resolve2(answer.toLowerCase().trim());
|
|
1013
|
+
});
|
|
1014
|
+
});
|
|
1015
|
+
}
|
|
1016
|
+
function displayFixPreview(fix, index, total) {
|
|
1017
|
+
console.error("");
|
|
1018
|
+
console.error(pc2.cyan(`\u2501\u2501\u2501 Fix ${index + 1}/${total} \u2501\u2501\u2501`));
|
|
1019
|
+
console.error(pc2.dim(`File: ${fix.file}${fix.line ? `:${fix.line}` : ""}`));
|
|
1020
|
+
console.error(pc2.dim(`Type: ${fix.type}`));
|
|
1021
|
+
console.error(pc2.yellow(`Description: ${fix.description}`));
|
|
1022
|
+
console.error("");
|
|
1023
|
+
if (fix.original) {
|
|
1024
|
+
console.error(pc2.red("- " + fix.original));
|
|
1025
|
+
}
|
|
1026
|
+
if (fix.fixed) {
|
|
1027
|
+
console.error(pc2.green("+ " + fix.fixed));
|
|
1028
|
+
} else if (fix.original) {
|
|
1029
|
+
console.error(pc2.dim(" (line will be removed)"));
|
|
1030
|
+
}
|
|
1031
|
+
console.error("");
|
|
1032
|
+
}
|
|
1033
|
+
async function applyFix(fix) {
|
|
1034
|
+
try {
|
|
1035
|
+
if (!existsSync(fix.file)) {
|
|
1036
|
+
console.error(pc2.red(`File not found: ${fix.file}`));
|
|
1037
|
+
return false;
|
|
1038
|
+
}
|
|
1039
|
+
const content = await readFile(fix.file, "utf-8");
|
|
1040
|
+
const lines = content.split("\n");
|
|
1041
|
+
if (fix.line === void 0 || fix.line < 1 || fix.line > lines.length) {
|
|
1042
|
+
console.error(pc2.red(`Invalid line number: ${fix.line}`));
|
|
1043
|
+
return false;
|
|
1044
|
+
}
|
|
1045
|
+
const lineIndex = fix.line - 1;
|
|
1046
|
+
const originalLine = lines[lineIndex];
|
|
1047
|
+
let newContent;
|
|
1048
|
+
if (fix.type === "remove-console-log" || fix.type === "remove-debugger") {
|
|
1049
|
+
lines.splice(lineIndex, 1);
|
|
1050
|
+
newContent = lines.join("\n");
|
|
1051
|
+
} else if (fix.fixed) {
|
|
1052
|
+
lines[lineIndex] = fix.fixed;
|
|
1053
|
+
newContent = lines.join("\n");
|
|
1054
|
+
} else {
|
|
1055
|
+
console.error(pc2.red(`Unknown fix type: ${fix.type}`));
|
|
1056
|
+
return false;
|
|
1057
|
+
}
|
|
1058
|
+
await writeFile(fix.file, newContent);
|
|
1059
|
+
console.error(pc2.green(`\u2713 Applied fix to ${fix.file}:${fix.line}`));
|
|
1060
|
+
if (originalLine) {
|
|
1061
|
+
console.error(pc2.dim(` Removed: ${originalLine.trim().slice(0, 60)}...`));
|
|
1062
|
+
}
|
|
1063
|
+
return true;
|
|
1064
|
+
} catch (error) {
|
|
1065
|
+
console.error(pc2.red(`Failed to apply fix: ${error}`));
|
|
1066
|
+
return false;
|
|
1067
|
+
}
|
|
1068
|
+
}
|
|
1069
|
+
async function loadFixContent(fixes) {
|
|
1070
|
+
const enrichedFixes = [];
|
|
1071
|
+
for (const fix of fixes) {
|
|
1072
|
+
if (!fix.line || !existsSync(fix.file)) {
|
|
1073
|
+
continue;
|
|
1074
|
+
}
|
|
1075
|
+
try {
|
|
1076
|
+
const content = await readFile(fix.file, "utf-8");
|
|
1077
|
+
const lines = content.split("\n");
|
|
1078
|
+
const originalLine = lines[fix.line - 1];
|
|
1079
|
+
if (originalLine) {
|
|
1080
|
+
enrichedFixes.push({
|
|
1081
|
+
...fix,
|
|
1082
|
+
original: originalLine
|
|
1083
|
+
});
|
|
1084
|
+
}
|
|
1085
|
+
} catch {
|
|
1086
|
+
}
|
|
1087
|
+
}
|
|
1088
|
+
return enrichedFixes;
|
|
1089
|
+
}
|
|
1090
|
+
async function handleAutoFixCommand(args, issues) {
|
|
1091
|
+
const projectPath = getWorkingDirectory(void 0, true);
|
|
1092
|
+
const config = await getAutonomyConfig(projectPath);
|
|
1093
|
+
if (!config.autoFix.enabled) {
|
|
1094
|
+
console.error(pc2.yellow("Auto-fix is disabled in config."));
|
|
1095
|
+
console.error(pc2.dim("Enable it with: trie config set autoFix.enabled true"));
|
|
1096
|
+
return;
|
|
1097
|
+
}
|
|
1098
|
+
const dryRun = args.includes("--dry-run");
|
|
1099
|
+
const skipConfirm = args.includes("--yes") || args.includes("-y");
|
|
1100
|
+
if (!issues || issues.length === 0) {
|
|
1101
|
+
console.error(pc2.yellow("No issues provided. Run a scan first:"));
|
|
1102
|
+
console.error(pc2.dim(" trie scan --fix"));
|
|
1103
|
+
return;
|
|
1104
|
+
}
|
|
1105
|
+
let fixes = detectAutoFixes(issues);
|
|
1106
|
+
if (fixes.length === 0) {
|
|
1107
|
+
console.error(pc2.green("\u2713 No auto-fixable issues found."));
|
|
1108
|
+
return;
|
|
1109
|
+
}
|
|
1110
|
+
fixes = fixes.filter((fix) => shouldAutoFix(fix, config));
|
|
1111
|
+
if (fixes.length === 0) {
|
|
1112
|
+
console.error(pc2.yellow("Found fixable issues but they are not in allowed categories."));
|
|
1113
|
+
console.error(pc2.dim("Allowed categories: " + config.autoFix.categories.join(", ")));
|
|
1114
|
+
return;
|
|
1115
|
+
}
|
|
1116
|
+
fixes = await loadFixContent(fixes);
|
|
1117
|
+
if (fixes.length === 0) {
|
|
1118
|
+
console.error(pc2.yellow("Could not load fix content from files."));
|
|
1119
|
+
return;
|
|
1120
|
+
}
|
|
1121
|
+
console.error("");
|
|
1122
|
+
console.error(pc2.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"));
|
|
1123
|
+
console.error(pc2.bold("Auto-Fix: Human-in-the-Loop"));
|
|
1124
|
+
console.error(pc2.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"));
|
|
1125
|
+
console.error("");
|
|
1126
|
+
console.error(`Found ${pc2.bold(fixes.length.toString())} auto-fixable issues:`);
|
|
1127
|
+
const byCategory = /* @__PURE__ */ new Map();
|
|
1128
|
+
for (const fix of fixes) {
|
|
1129
|
+
byCategory.set(fix.category, (byCategory.get(fix.category) || 0) + 1);
|
|
1130
|
+
}
|
|
1131
|
+
for (const [cat, count] of byCategory) {
|
|
1132
|
+
console.error(` \u2022 ${cat}: ${count}`);
|
|
1133
|
+
}
|
|
1134
|
+
console.error("");
|
|
1135
|
+
if (dryRun) {
|
|
1136
|
+
console.error(pc2.yellow("[DRY RUN] No changes will be made."));
|
|
1137
|
+
console.error("");
|
|
1138
|
+
for (let i = 0; i < fixes.length; i++) {
|
|
1139
|
+
displayFixPreview(fixes[i], i, fixes.length);
|
|
1140
|
+
}
|
|
1141
|
+
return;
|
|
1142
|
+
}
|
|
1143
|
+
if (config.autoFix.askFirst && !skipConfirm) {
|
|
1144
|
+
console.error(pc2.yellow("Trie will ask before applying each fix."));
|
|
1145
|
+
console.error("");
|
|
1146
|
+
const response = await promptUser(
|
|
1147
|
+
`Apply all ${fixes.length} fixes? (y)es / (n)o / (r)eview each: `
|
|
1148
|
+
);
|
|
1149
|
+
if (response === "n" || response === "no") {
|
|
1150
|
+
console.error(pc2.dim("Cancelled."));
|
|
1151
|
+
return;
|
|
1152
|
+
}
|
|
1153
|
+
if (response === "r" || response === "review") {
|
|
1154
|
+
let applied2 = 0;
|
|
1155
|
+
let skipped = 0;
|
|
1156
|
+
for (let i = 0; i < fixes.length; i++) {
|
|
1157
|
+
const fix = fixes[i];
|
|
1158
|
+
displayFixPreview(fix, i, fixes.length);
|
|
1159
|
+
const answer = await promptUser("Apply this fix? (y/n/q): ");
|
|
1160
|
+
if (answer === "q" || answer === "quit") {
|
|
1161
|
+
console.error(pc2.dim("Stopped."));
|
|
1162
|
+
break;
|
|
1163
|
+
}
|
|
1164
|
+
if (answer === "y" || answer === "yes") {
|
|
1165
|
+
const success = await applyFix(fix);
|
|
1166
|
+
if (success) applied2++;
|
|
1167
|
+
} else {
|
|
1168
|
+
console.error(pc2.dim("Skipped."));
|
|
1169
|
+
skipped++;
|
|
1170
|
+
}
|
|
1171
|
+
}
|
|
1172
|
+
console.error("");
|
|
1173
|
+
console.error(pc2.cyan("\u2501\u2501\u2501 Summary \u2501\u2501\u2501"));
|
|
1174
|
+
console.error(`Applied: ${pc2.green(applied2.toString())}`);
|
|
1175
|
+
console.error(`Skipped: ${pc2.dim(skipped.toString())}`);
|
|
1176
|
+
return;
|
|
1177
|
+
}
|
|
1178
|
+
}
|
|
1179
|
+
console.error("");
|
|
1180
|
+
console.error(pc2.cyan("Applying fixes..."));
|
|
1181
|
+
console.error("");
|
|
1182
|
+
let applied = 0;
|
|
1183
|
+
let failed = 0;
|
|
1184
|
+
for (const fix of fixes) {
|
|
1185
|
+
const success = await applyFix(fix);
|
|
1186
|
+
if (success) {
|
|
1187
|
+
applied++;
|
|
1188
|
+
} else {
|
|
1189
|
+
failed++;
|
|
1190
|
+
}
|
|
1191
|
+
}
|
|
1192
|
+
console.error("");
|
|
1193
|
+
console.error(pc2.cyan("\u2501\u2501\u2501 Complete \u2501\u2501\u2501"));
|
|
1194
|
+
console.error(`Applied: ${pc2.green(applied.toString())}`);
|
|
1195
|
+
if (failed > 0) {
|
|
1196
|
+
console.error(`Failed: ${pc2.red(failed.toString())}`);
|
|
1197
|
+
}
|
|
1198
|
+
}
|
|
1199
|
+
|
|
1200
|
+
// src/cli/goal.ts
|
|
1201
|
+
import pc3 from "picocolors";
|
|
1202
|
+
async function handleGoalCommand(args) {
|
|
1203
|
+
const projectPath = getWorkingDirectory(void 0, true);
|
|
1204
|
+
const guardianState = getGuardianState(projectPath);
|
|
1205
|
+
await guardianState.load();
|
|
1206
|
+
const subcommand = args[0];
|
|
1207
|
+
const restArgs = args.slice(1);
|
|
1208
|
+
switch (subcommand) {
|
|
1209
|
+
case "add":
|
|
1210
|
+
case "create":
|
|
1211
|
+
await addGoal(guardianState, restArgs);
|
|
1212
|
+
break;
|
|
1213
|
+
case "list":
|
|
1214
|
+
case "ls":
|
|
1215
|
+
await listGoals(guardianState);
|
|
1216
|
+
break;
|
|
1217
|
+
case "complete":
|
|
1218
|
+
case "done":
|
|
1219
|
+
await completeGoal(guardianState, restArgs[0]);
|
|
1220
|
+
break;
|
|
1221
|
+
case "delete":
|
|
1222
|
+
case "rm":
|
|
1223
|
+
await deleteGoal(guardianState, restArgs[0]);
|
|
1224
|
+
break;
|
|
1225
|
+
default:
|
|
1226
|
+
if (subcommand && !subcommand.startsWith("-")) {
|
|
1227
|
+
await addGoal(guardianState, [subcommand, ...restArgs]);
|
|
1228
|
+
} else {
|
|
1229
|
+
printGoalHelp();
|
|
1230
|
+
}
|
|
1231
|
+
}
|
|
1232
|
+
}
|
|
1233
|
+
async function addGoal(guardianState, args) {
|
|
1234
|
+
const description = args.join(" ").replace(/^["']|["']$/g, "");
|
|
1235
|
+
if (!description) {
|
|
1236
|
+
console.error(pc3.red("Please provide a goal description."));
|
|
1237
|
+
console.error(pc3.dim('Example: trie goal add "Reduce auth issues by 50%"'));
|
|
1238
|
+
return;
|
|
1239
|
+
}
|
|
1240
|
+
const categoryMatch = description.match(/--category[=\s](\w+)/);
|
|
1241
|
+
const targetMatch = description.match(/--target[=\s](\d+)/);
|
|
1242
|
+
const deadlineMatch = description.match(/--deadline[=\s](\S+)/);
|
|
1243
|
+
let cleanDesc = description.replace(/--category[=\s]\w+/g, "").replace(/--target[=\s]\d+/g, "").replace(/--deadline[=\s]\S+/g, "").trim();
|
|
1244
|
+
const reductionMatch = cleanDesc.match(/reduce.*?(\d+)%/i);
|
|
1245
|
+
const eliminateMatch = cleanDesc.match(/eliminate|remove all|zero/i);
|
|
1246
|
+
const streakMatch = cleanDesc.match(/(\d+)\s*days?\s*(streak|in a row|consecutive)/i);
|
|
1247
|
+
let goalType = "custom";
|
|
1248
|
+
let target = 100;
|
|
1249
|
+
let metric = "progress";
|
|
1250
|
+
if (reductionMatch && reductionMatch[1]) {
|
|
1251
|
+
goalType = "reduction";
|
|
1252
|
+
target = parseInt(reductionMatch[1], 10);
|
|
1253
|
+
metric = "reduction_percent";
|
|
1254
|
+
} else if (eliminateMatch) {
|
|
1255
|
+
goalType = "reduction";
|
|
1256
|
+
target = 100;
|
|
1257
|
+
metric = "elimination";
|
|
1258
|
+
} else if (streakMatch && streakMatch[1]) {
|
|
1259
|
+
goalType = "streak";
|
|
1260
|
+
target = parseInt(streakMatch[1], 10);
|
|
1261
|
+
metric = "days_streak";
|
|
1262
|
+
}
|
|
1263
|
+
if (targetMatch && targetMatch[1]) {
|
|
1264
|
+
target = parseInt(targetMatch[1], 10);
|
|
1265
|
+
}
|
|
1266
|
+
const category = categoryMatch?.[1] || detectCategory(cleanDesc);
|
|
1267
|
+
const deadline = /* @__PURE__ */ new Date();
|
|
1268
|
+
if (deadlineMatch && deadlineMatch[1]) {
|
|
1269
|
+
const days = parseInt(deadlineMatch[1], 10);
|
|
1270
|
+
deadline.setDate(deadline.getDate() + (isNaN(days) ? 14 : days));
|
|
1271
|
+
} else {
|
|
1272
|
+
deadline.setDate(deadline.getDate() + 14);
|
|
1273
|
+
}
|
|
1274
|
+
const goal = {
|
|
1275
|
+
id: `goal-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
|
|
1276
|
+
description: cleanDesc,
|
|
1277
|
+
type: goalType,
|
|
1278
|
+
metric,
|
|
1279
|
+
target,
|
|
1280
|
+
currentValue: 0,
|
|
1281
|
+
startValue: 0,
|
|
1282
|
+
status: "active",
|
|
1283
|
+
autoGenerated: false,
|
|
1284
|
+
// User-created
|
|
1285
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1286
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1287
|
+
deadline: deadline.toISOString(),
|
|
1288
|
+
category
|
|
1289
|
+
};
|
|
1290
|
+
await guardianState.addGoal(goal);
|
|
1291
|
+
console.error("");
|
|
1292
|
+
console.error(pc3.green("\u2713 Goal created"));
|
|
1293
|
+
console.error("");
|
|
1294
|
+
console.error(pc3.bold(cleanDesc));
|
|
1295
|
+
console.error(pc3.dim(`ID: ${goal.id}`));
|
|
1296
|
+
console.error(pc3.dim(`Type: ${goalType} | Target: ${target} | Category: ${category}`));
|
|
1297
|
+
console.error(pc3.dim(`Deadline: ${deadline.toLocaleDateString()}`));
|
|
1298
|
+
console.error("");
|
|
1299
|
+
console.error(pc3.cyan("The guardian will track progress and celebrate when you achieve it!"));
|
|
1300
|
+
}
|
|
1301
|
+
async function listGoals(guardianState) {
|
|
1302
|
+
const goals = guardianState.getAllGoals();
|
|
1303
|
+
if (goals.length === 0) {
|
|
1304
|
+
console.error(pc3.dim("No goals yet."));
|
|
1305
|
+
console.error(pc3.dim('Create one: trie goal add "Reduce auth issues by 50%"'));
|
|
1306
|
+
return;
|
|
1307
|
+
}
|
|
1308
|
+
console.error("");
|
|
1309
|
+
console.error(pc3.bold("\u{1F3AF} Goals"));
|
|
1310
|
+
console.error("");
|
|
1311
|
+
const active = goals.filter((g) => g.status === "active");
|
|
1312
|
+
const achieved = goals.filter((g) => g.status === "achieved");
|
|
1313
|
+
const other = goals.filter((g) => g.status !== "active" && g.status !== "achieved");
|
|
1314
|
+
if (active.length > 0) {
|
|
1315
|
+
console.error(pc3.cyan("Active:"));
|
|
1316
|
+
for (const goal of active) {
|
|
1317
|
+
const progress = goal.target > 0 ? Math.round(goal.currentValue / goal.target * 100) : 0;
|
|
1318
|
+
const bar = renderProgressBar(progress);
|
|
1319
|
+
const source = goal.autoGenerated ? pc3.dim("[auto]") : pc3.dim("[manual]");
|
|
1320
|
+
console.error(` ${bar} ${goal.description} ${source}`);
|
|
1321
|
+
console.error(pc3.dim(` ID: ${goal.id} | ${progress}% complete`));
|
|
1322
|
+
}
|
|
1323
|
+
console.error("");
|
|
1324
|
+
}
|
|
1325
|
+
if (achieved.length > 0) {
|
|
1326
|
+
console.error(pc3.green("Achieved:"));
|
|
1327
|
+
for (const goal of achieved) {
|
|
1328
|
+
console.error(` \u2705 ${goal.description}`);
|
|
1329
|
+
}
|
|
1330
|
+
console.error("");
|
|
1331
|
+
}
|
|
1332
|
+
if (other.length > 0) {
|
|
1333
|
+
console.error(pc3.dim("Other:"));
|
|
1334
|
+
for (const goal of other) {
|
|
1335
|
+
const icon = goal.status === "failed" ? "\u274C" : goal.status === "paused" ? "\u23F8\uFE0F" : "\u2022";
|
|
1336
|
+
console.error(` ${icon} ${goal.description} (${goal.status})`);
|
|
1337
|
+
}
|
|
1338
|
+
}
|
|
1339
|
+
}
|
|
1340
|
+
async function completeGoal(guardianState, goalId) {
|
|
1341
|
+
if (!goalId) {
|
|
1342
|
+
console.error(pc3.red("Please provide a goal ID."));
|
|
1343
|
+
console.error(pc3.dim('Run "trie goal list" to see IDs.'));
|
|
1344
|
+
return;
|
|
1345
|
+
}
|
|
1346
|
+
const goals = guardianState.getAllGoals();
|
|
1347
|
+
const goal = goals.find((g) => g.id === goalId || g.id.includes(goalId));
|
|
1348
|
+
if (!goal) {
|
|
1349
|
+
console.error(pc3.red(`Goal not found: ${goalId}`));
|
|
1350
|
+
return;
|
|
1351
|
+
}
|
|
1352
|
+
await guardianState.updateGoal(goal.id, {
|
|
1353
|
+
status: "achieved",
|
|
1354
|
+
currentValue: goal.target,
|
|
1355
|
+
achievedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1356
|
+
});
|
|
1357
|
+
console.error("");
|
|
1358
|
+
console.error(pc3.green("\u{1F389} Goal achieved!"));
|
|
1359
|
+
console.error(pc3.bold(goal.description));
|
|
1360
|
+
console.error("");
|
|
1361
|
+
}
|
|
1362
|
+
async function deleteGoal(guardianState, goalId) {
|
|
1363
|
+
if (!goalId) {
|
|
1364
|
+
console.error(pc3.red("Please provide a goal ID."));
|
|
1365
|
+
return;
|
|
1366
|
+
}
|
|
1367
|
+
const goals = guardianState.getAllGoals();
|
|
1368
|
+
const goal = goals.find((g) => g.id === goalId || g.id.includes(goalId));
|
|
1369
|
+
if (!goal) {
|
|
1370
|
+
console.error(pc3.red(`Goal not found: ${goalId}`));
|
|
1371
|
+
return;
|
|
1372
|
+
}
|
|
1373
|
+
await guardianState.updateGoal(goal.id, { status: "rejected" });
|
|
1374
|
+
console.error(pc3.dim(`Removed goal: ${goal.description}`));
|
|
1375
|
+
}
|
|
1376
|
+
function renderProgressBar(percent) {
|
|
1377
|
+
const width = 10;
|
|
1378
|
+
const filled = Math.round(percent / 100 * width);
|
|
1379
|
+
const empty = width - filled;
|
|
1380
|
+
return `[${pc3.green("\u2588".repeat(filled))}${pc3.dim("\u2591".repeat(empty))}]`;
|
|
1381
|
+
}
|
|
1382
|
+
function detectCategory(description) {
|
|
1383
|
+
const lower = description.toLowerCase();
|
|
1384
|
+
if (/security|auth|password|token|xss|sql|inject/i.test(lower)) return "security";
|
|
1385
|
+
if (/quality|bug|issue|error|fix/i.test(lower)) return "quality";
|
|
1386
|
+
if (/performance|speed|fast|slow|latency/i.test(lower)) return "performance";
|
|
1387
|
+
if (/coverage|test|spec/i.test(lower)) return "coverage";
|
|
1388
|
+
return "general";
|
|
1389
|
+
}
|
|
1390
|
+
function printGoalHelp() {
|
|
1391
|
+
console.error("");
|
|
1392
|
+
console.error(pc3.bold("trie goal - Manage guardian goals"));
|
|
1393
|
+
console.error("");
|
|
1394
|
+
console.error(pc3.cyan("Usage:"));
|
|
1395
|
+
console.error(' trie goal add "Reduce auth issues by 50%"');
|
|
1396
|
+
console.error(" trie goal list");
|
|
1397
|
+
console.error(" trie goal complete <id>");
|
|
1398
|
+
console.error(" trie goal delete <id>");
|
|
1399
|
+
console.error("");
|
|
1400
|
+
console.error(pc3.cyan("Options for add:"));
|
|
1401
|
+
console.error(" --category=security|quality|performance|coverage|general");
|
|
1402
|
+
console.error(" --target=<number>");
|
|
1403
|
+
console.error(" --deadline=<days>");
|
|
1404
|
+
console.error("");
|
|
1405
|
+
console.error(pc3.cyan("Examples:"));
|
|
1406
|
+
console.error(' trie goal add "Eliminate all critical security issues"');
|
|
1407
|
+
console.error(' trie goal add "7 days streak of clean builds"');
|
|
1408
|
+
console.error(' trie goal add "Reduce test flakiness by 80%" --category=quality');
|
|
1409
|
+
console.error("");
|
|
1410
|
+
}
|
|
1411
|
+
async function handleHypothesisCommand(args) {
|
|
1412
|
+
const projectPath = getWorkingDirectory(void 0, true);
|
|
1413
|
+
const guardianState = getGuardianState(projectPath);
|
|
1414
|
+
await guardianState.load();
|
|
1415
|
+
const subcommand = args[0];
|
|
1416
|
+
const restArgs = args.slice(1);
|
|
1417
|
+
switch (subcommand) {
|
|
1418
|
+
case "add":
|
|
1419
|
+
case "create":
|
|
1420
|
+
await addHypothesis(guardianState, restArgs);
|
|
1421
|
+
break;
|
|
1422
|
+
case "list":
|
|
1423
|
+
case "ls":
|
|
1424
|
+
await listHypotheses(guardianState);
|
|
1425
|
+
break;
|
|
1426
|
+
case "validate":
|
|
1427
|
+
await validateHypothesis(guardianState, restArgs[0]);
|
|
1428
|
+
break;
|
|
1429
|
+
case "invalidate":
|
|
1430
|
+
await invalidateHypothesis(guardianState, restArgs[0]);
|
|
1431
|
+
break;
|
|
1432
|
+
case "delete":
|
|
1433
|
+
case "rm":
|
|
1434
|
+
await deleteHypothesis(guardianState, restArgs[0]);
|
|
1435
|
+
break;
|
|
1436
|
+
default:
|
|
1437
|
+
if (subcommand && !subcommand.startsWith("-")) {
|
|
1438
|
+
await addHypothesis(guardianState, [subcommand, ...restArgs]);
|
|
1439
|
+
} else {
|
|
1440
|
+
printHypothesisHelp();
|
|
1441
|
+
}
|
|
1442
|
+
}
|
|
1443
|
+
}
|
|
1444
|
+
async function addHypothesis(guardianState, args) {
|
|
1445
|
+
const statement = args.join(" ").replace(/^["']|["']$/g, "");
|
|
1446
|
+
if (!statement) {
|
|
1447
|
+
console.error(pc3.red("Please provide a hypothesis statement."));
|
|
1448
|
+
console.error(pc3.dim('Example: trie hypothesis add "Mondays have more bugs than Fridays"'));
|
|
1449
|
+
return;
|
|
1450
|
+
}
|
|
1451
|
+
const categoryMatch = statement.match(/--category[=\s](\w+)/);
|
|
1452
|
+
const testMatch = statement.match(/--test[=\s]["']([^"']+)["']/);
|
|
1453
|
+
let cleanStatement = statement.replace(/--category[=\s]\w+/g, "").replace(/--test[=\s]["'][^"']+["']/g, "").trim();
|
|
1454
|
+
const category = categoryMatch?.[1] || detectHypothesisCategory(cleanStatement);
|
|
1455
|
+
const testCriteria = testMatch?.[1] || generateTestCriteria(cleanStatement);
|
|
1456
|
+
const hypothesis = {
|
|
1457
|
+
id: `hypo-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
|
|
1458
|
+
statement: cleanStatement,
|
|
1459
|
+
confidence: 0.5,
|
|
1460
|
+
// Start neutral
|
|
1461
|
+
status: "testing",
|
|
1462
|
+
evidence: [],
|
|
1463
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1464
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1465
|
+
testCriteria,
|
|
1466
|
+
category
|
|
1467
|
+
};
|
|
1468
|
+
await guardianState.addHypothesis(hypothesis);
|
|
1469
|
+
console.error("");
|
|
1470
|
+
console.error(pc3.green("\u2713 Hypothesis created"));
|
|
1471
|
+
console.error("");
|
|
1472
|
+
console.error(pc3.bold(`"${cleanStatement}"`));
|
|
1473
|
+
console.error(pc3.dim(`ID: ${hypothesis.id}`));
|
|
1474
|
+
console.error(pc3.dim(`Status: testing | Confidence: 50% | Category: ${category}`));
|
|
1475
|
+
console.error("");
|
|
1476
|
+
console.error(pc3.cyan("The guardian will collect evidence and update confidence over time."));
|
|
1477
|
+
console.error(pc3.dim(`Test criteria: ${testCriteria}`));
|
|
1478
|
+
}
|
|
1479
|
+
async function listHypotheses(guardianState) {
|
|
1480
|
+
const hypotheses = guardianState.getAllHypotheses();
|
|
1481
|
+
if (hypotheses.length === 0) {
|
|
1482
|
+
console.error(pc3.dim("No hypotheses yet."));
|
|
1483
|
+
console.error(pc3.dim('Create one: trie hypothesis add "Mondays have more bugs"'));
|
|
1484
|
+
return;
|
|
1485
|
+
}
|
|
1486
|
+
console.error("");
|
|
1487
|
+
console.error(pc3.bold("\u{1F52E} Hypotheses"));
|
|
1488
|
+
console.error("");
|
|
1489
|
+
const testing = hypotheses.filter((h) => h.status === "testing");
|
|
1490
|
+
const validated = hypotheses.filter((h) => h.status === "validated");
|
|
1491
|
+
const invalidated = hypotheses.filter((h) => h.status === "invalidated");
|
|
1492
|
+
if (testing.length > 0) {
|
|
1493
|
+
console.error(pc3.cyan("Testing:"));
|
|
1494
|
+
for (const hypo of testing) {
|
|
1495
|
+
const conf = Math.round(hypo.confidence * 100);
|
|
1496
|
+
const confColor = conf >= 70 ? pc3.green : conf >= 40 ? pc3.yellow : pc3.red;
|
|
1497
|
+
console.error(` \u{1F9EA} "${hypo.statement}" ${confColor(`(${conf}% confidence)`)}`);
|
|
1498
|
+
console.error(pc3.dim(` ID: ${hypo.id} | Evidence: ${hypo.evidence.length} points`));
|
|
1499
|
+
}
|
|
1500
|
+
console.error("");
|
|
1501
|
+
}
|
|
1502
|
+
if (validated.length > 0) {
|
|
1503
|
+
console.error(pc3.green("Validated:"));
|
|
1504
|
+
for (const hypo of validated) {
|
|
1505
|
+
console.error(` \u2705 "${hypo.statement}" (${Math.round(hypo.confidence * 100)}%)`);
|
|
1506
|
+
}
|
|
1507
|
+
console.error("");
|
|
1508
|
+
}
|
|
1509
|
+
if (invalidated.length > 0) {
|
|
1510
|
+
console.error(pc3.red("Invalidated:"));
|
|
1511
|
+
for (const hypo of invalidated) {
|
|
1512
|
+
console.error(` \u274C "${hypo.statement}"`);
|
|
1513
|
+
}
|
|
1514
|
+
}
|
|
1515
|
+
}
|
|
1516
|
+
async function validateHypothesis(guardianState, hypoId) {
|
|
1517
|
+
if (!hypoId) {
|
|
1518
|
+
console.error(pc3.red("Please provide a hypothesis ID."));
|
|
1519
|
+
return;
|
|
1520
|
+
}
|
|
1521
|
+
const hypotheses = guardianState.getAllHypotheses();
|
|
1522
|
+
const hypo = hypotheses.find((h) => h.id === hypoId || h.id.includes(hypoId));
|
|
1523
|
+
if (!hypo) {
|
|
1524
|
+
console.error(pc3.red(`Hypothesis not found: ${hypoId}`));
|
|
1525
|
+
return;
|
|
1526
|
+
}
|
|
1527
|
+
await guardianState.updateHypothesis(hypo.id, {
|
|
1528
|
+
status: "validated",
|
|
1529
|
+
confidence: 0.9,
|
|
1530
|
+
validatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1531
|
+
});
|
|
1532
|
+
console.error(pc3.green(`\u2705 Hypothesis validated: "${hypo.statement}"`));
|
|
1533
|
+
}
|
|
1534
|
+
async function invalidateHypothesis(guardianState, hypoId) {
|
|
1535
|
+
if (!hypoId) {
|
|
1536
|
+
console.error(pc3.red("Please provide a hypothesis ID."));
|
|
1537
|
+
return;
|
|
1538
|
+
}
|
|
1539
|
+
const hypotheses = guardianState.getAllHypotheses();
|
|
1540
|
+
const hypo = hypotheses.find((h) => h.id === hypoId || h.id.includes(hypoId));
|
|
1541
|
+
if (!hypo) {
|
|
1542
|
+
console.error(pc3.red(`Hypothesis not found: ${hypoId}`));
|
|
1543
|
+
return;
|
|
1544
|
+
}
|
|
1545
|
+
await guardianState.updateHypothesis(hypo.id, {
|
|
1546
|
+
status: "invalidated",
|
|
1547
|
+
confidence: 0.1
|
|
1548
|
+
});
|
|
1549
|
+
console.error(pc3.red(`\u274C Hypothesis invalidated: "${hypo.statement}"`));
|
|
1550
|
+
}
|
|
1551
|
+
async function deleteHypothesis(guardianState, hypoId) {
|
|
1552
|
+
if (!hypoId) {
|
|
1553
|
+
console.error(pc3.red("Please provide a hypothesis ID."));
|
|
1554
|
+
return;
|
|
1555
|
+
}
|
|
1556
|
+
const hypotheses = guardianState.getAllHypotheses();
|
|
1557
|
+
const hypo = hypotheses.find((h) => h.id === hypoId || h.id.includes(hypoId));
|
|
1558
|
+
if (!hypo) {
|
|
1559
|
+
console.error(pc3.red(`Hypothesis not found: ${hypoId}`));
|
|
1560
|
+
return;
|
|
1561
|
+
}
|
|
1562
|
+
await guardianState.updateHypothesis(hypo.id, { status: "retired" });
|
|
1563
|
+
console.error(pc3.dim(`Removed hypothesis: "${hypo.statement}"`));
|
|
1564
|
+
}
|
|
1565
|
+
function detectHypothesisCategory(statement) {
|
|
1566
|
+
const lower = statement.toLowerCase();
|
|
1567
|
+
if (/monday|friday|weekend|morning|night|time|day|hour/i.test(lower)) return "timing";
|
|
1568
|
+
if (/pattern|recurring|always|never|consistently/i.test(lower)) return "pattern";
|
|
1569
|
+
if (/team|developer|engineer|review/i.test(lower)) return "team";
|
|
1570
|
+
if (/code|file|function|class|module|refactor/i.test(lower)) return "code";
|
|
1571
|
+
return "general";
|
|
1572
|
+
}
|
|
1573
|
+
function generateTestCriteria(statement) {
|
|
1574
|
+
const lower = statement.toLowerCase();
|
|
1575
|
+
if (lower.includes("monday")) {
|
|
1576
|
+
return "Compare Monday issue counts to weekly average";
|
|
1577
|
+
}
|
|
1578
|
+
if (lower.includes("friday")) {
|
|
1579
|
+
return "Compare Friday issue introduction rate to other days";
|
|
1580
|
+
}
|
|
1581
|
+
if (lower.includes("more bug") || lower.includes("more issue")) {
|
|
1582
|
+
return "Compare issue counts between conditions over time";
|
|
1583
|
+
}
|
|
1584
|
+
if (lower.includes("causes") || lower.includes("leads to")) {
|
|
1585
|
+
return "Track correlation between trigger and outcome events";
|
|
1586
|
+
}
|
|
1587
|
+
return "Collect supporting and contradicting evidence from scans";
|
|
1588
|
+
}
|
|
1589
|
+
function printHypothesisHelp() {
|
|
1590
|
+
console.error("");
|
|
1591
|
+
console.error(pc3.bold("trie hypothesis - Manage guardian hypotheses"));
|
|
1592
|
+
console.error("");
|
|
1593
|
+
console.error(pc3.cyan("Usage:"));
|
|
1594
|
+
console.error(' trie hypothesis add "Mondays have more bugs than Fridays"');
|
|
1595
|
+
console.error(" trie hypothesis list");
|
|
1596
|
+
console.error(" trie hypothesis validate <id>");
|
|
1597
|
+
console.error(" trie hypothesis invalidate <id>");
|
|
1598
|
+
console.error(" trie hypothesis delete <id>");
|
|
1599
|
+
console.error("");
|
|
1600
|
+
console.error(pc3.cyan("Options for add:"));
|
|
1601
|
+
console.error(" --category=timing|pattern|team|code|general");
|
|
1602
|
+
console.error(' --test="<test criteria>"');
|
|
1603
|
+
console.error("");
|
|
1604
|
+
console.error(pc3.cyan("Examples:"));
|
|
1605
|
+
console.error(' trie hypothesis add "Code reviews reduce bug rate"');
|
|
1606
|
+
console.error(' trie hypothesis add "Auth module has highest churn"');
|
|
1607
|
+
console.error(' trie hypothesis add "Deploy Fridays cause weekend incidents"');
|
|
1608
|
+
console.error("");
|
|
1609
|
+
}
|
|
1610
|
+
|
|
770
1611
|
// src/cli/tell.ts
|
|
771
1612
|
import path3 from "path";
|
|
772
1613
|
function escalateRisk(level) {
|
|
@@ -908,10 +1749,192 @@ async function handleQuietCommand() {
|
|
|
908
1749
|
}
|
|
909
1750
|
}
|
|
910
1751
|
|
|
1752
|
+
// src/cli/ci.ts
|
|
1753
|
+
import { writeFileSync, existsSync as existsSync2, mkdirSync } from "fs";
|
|
1754
|
+
import { join } from "path";
|
|
1755
|
+
import pc4 from "picocolors";
|
|
1756
|
+
var WORKFLOW_TEMPLATE = `# Trie Security Scan with Memory Persistence
|
|
1757
|
+
# Generated by: trie ci
|
|
1758
|
+
#
|
|
1759
|
+
# This workflow scans your code and caches Trie's memory
|
|
1760
|
+
# across runs for cross-run learning and pattern recognition.
|
|
1761
|
+
|
|
1762
|
+
name: Trie Security Scan
|
|
1763
|
+
|
|
1764
|
+
on:
|
|
1765
|
+
push:
|
|
1766
|
+
branches: [main, master, develop]
|
|
1767
|
+
pull_request:
|
|
1768
|
+
branches: [main, master]
|
|
1769
|
+
|
|
1770
|
+
jobs:
|
|
1771
|
+
security-scan:
|
|
1772
|
+
runs-on: ubuntu-latest
|
|
1773
|
+
permissions:
|
|
1774
|
+
security-events: write
|
|
1775
|
+
pull-requests: write
|
|
1776
|
+
contents: read
|
|
1777
|
+
|
|
1778
|
+
steps:
|
|
1779
|
+
- name: Checkout
|
|
1780
|
+
uses: actions/checkout@v4
|
|
1781
|
+
|
|
1782
|
+
# Restore Trie memory from previous runs
|
|
1783
|
+
# This enables cross-run learning and pattern recognition
|
|
1784
|
+
- name: Restore Trie Memory
|
|
1785
|
+
uses: actions/cache@v4
|
|
1786
|
+
with:
|
|
1787
|
+
path: |
|
|
1788
|
+
.trie/memory
|
|
1789
|
+
.trie/context
|
|
1790
|
+
.trie/summaries
|
|
1791
|
+
key: trie-memory-\${{ github.repository }}-\${{ github.ref_name }}
|
|
1792
|
+
restore-keys: |
|
|
1793
|
+
trie-memory-\${{ github.repository }}-
|
|
1794
|
+
trie-memory-
|
|
1795
|
+
|
|
1796
|
+
- name: Setup Node.js
|
|
1797
|
+
uses: actions/setup-node@v4
|
|
1798
|
+
with:
|
|
1799
|
+
node-version: '20'
|
|
1800
|
+
|
|
1801
|
+
- name: Install Trie
|
|
1802
|
+
run: npm install -g @triedotdev/mcp
|
|
1803
|
+
|
|
1804
|
+
- name: Run Security Scan
|
|
1805
|
+
run: |
|
|
1806
|
+
trie scan --format sarif --output trie-results.sarif
|
|
1807
|
+
env:
|
|
1808
|
+
ANTHROPIC_API_KEY: \${{ secrets.ANTHROPIC_API_KEY }}
|
|
1809
|
+
|
|
1810
|
+
- name: Upload SARIF Results
|
|
1811
|
+
uses: github/codeql-action/upload-sarif@v3
|
|
1812
|
+
if: always()
|
|
1813
|
+
with:
|
|
1814
|
+
sarif_file: trie-results.sarif
|
|
1815
|
+
continue-on-error: true
|
|
1816
|
+
|
|
1817
|
+
# Memory is automatically saved by cache action on job completion
|
|
1818
|
+
`;
|
|
1819
|
+
var WORKFLOW_MINIMAL = `# Trie Security Scan (Minimal)
|
|
1820
|
+
# Generated by: trie ci --minimal
|
|
1821
|
+
|
|
1822
|
+
name: Trie Scan
|
|
1823
|
+
|
|
1824
|
+
on: [push, pull_request]
|
|
1825
|
+
|
|
1826
|
+
jobs:
|
|
1827
|
+
scan:
|
|
1828
|
+
runs-on: ubuntu-latest
|
|
1829
|
+
steps:
|
|
1830
|
+
- uses: actions/checkout@v4
|
|
1831
|
+
|
|
1832
|
+
# Cache Trie memory for cross-run learning
|
|
1833
|
+
- uses: actions/cache@v4
|
|
1834
|
+
with:
|
|
1835
|
+
path: .trie/memory
|
|
1836
|
+
key: trie-memory-\${{ github.repository }}
|
|
1837
|
+
|
|
1838
|
+
- run: npm install -g @triedotdev/mcp
|
|
1839
|
+
- run: trie scan
|
|
1840
|
+
env:
|
|
1841
|
+
ANTHROPIC_API_KEY: \${{ secrets.ANTHROPIC_API_KEY }}
|
|
1842
|
+
`;
|
|
1843
|
+
function handleCISetupCommand(args) {
|
|
1844
|
+
const workDir = getWorkingDirectory(void 0, true);
|
|
1845
|
+
const workflowsDir = join(workDir, ".github", "workflows");
|
|
1846
|
+
const workflowPath = join(workflowsDir, "trie-scan.yml");
|
|
1847
|
+
const isMinimal = args.includes("--minimal") || args.includes("-m");
|
|
1848
|
+
const isDryRun = args.includes("--dry-run") || args.includes("-n");
|
|
1849
|
+
const showHelp3 = args.includes("--help") || args.includes("-h");
|
|
1850
|
+
if (showHelp3) {
|
|
1851
|
+
console.log(`
|
|
1852
|
+
${pc4.bold("trie ci")} - Generate GitHub Actions workflow with memory caching
|
|
1853
|
+
|
|
1854
|
+
${pc4.bold("USAGE:")}
|
|
1855
|
+
trie ci [options]
|
|
1856
|
+
|
|
1857
|
+
${pc4.bold("OPTIONS:")}
|
|
1858
|
+
--minimal, -m Generate minimal workflow (simpler, fewer features)
|
|
1859
|
+
--dry-run, -n Preview workflow without creating files
|
|
1860
|
+
--help, -h Show this help
|
|
1861
|
+
|
|
1862
|
+
${pc4.bold("WHAT IT DOES:")}
|
|
1863
|
+
Creates .github/workflows/trie-scan.yml that:
|
|
1864
|
+
|
|
1865
|
+
1. ${pc4.green("Caches Trie memory")} - Patterns and fixes persist across runs
|
|
1866
|
+
2. ${pc4.green("Enables learning")} - Trie remembers issues from previous PRs
|
|
1867
|
+
3. ${pc4.green("Tracks patterns")} - Detects recurring issues across your codebase
|
|
1868
|
+
4. ${pc4.green("SARIF upload")} - Results appear in GitHub Security tab
|
|
1869
|
+
|
|
1870
|
+
${pc4.bold("MEMORY BENEFITS:")}
|
|
1871
|
+
\u2022 "This issue was introduced 3 PRs ago"
|
|
1872
|
+
\u2022 "Similar issue was fixed in PR #42"
|
|
1873
|
+
\u2022 "This pattern keeps recurring in auth code"
|
|
1874
|
+
\u2022 Trend tracking: improving, stable, or declining
|
|
1875
|
+
|
|
1876
|
+
${pc4.bold("EXAMPLES:")}
|
|
1877
|
+
trie ci # Generate full workflow
|
|
1878
|
+
trie ci --minimal # Generate simple workflow
|
|
1879
|
+
trie ci --dry-run # Preview without writing
|
|
1880
|
+
|
|
1881
|
+
${pc4.bold("REQUIRED SECRETS:")}
|
|
1882
|
+
ANTHROPIC_API_KEY Your Anthropic API key (add in GitHub repo settings)
|
|
1883
|
+
`);
|
|
1884
|
+
return;
|
|
1885
|
+
}
|
|
1886
|
+
const template = isMinimal ? WORKFLOW_MINIMAL : WORKFLOW_TEMPLATE;
|
|
1887
|
+
if (isDryRun) {
|
|
1888
|
+
console.log(pc4.bold("\n\u{1F4CB} Workflow Preview:\n"));
|
|
1889
|
+
console.log(pc4.dim("\u2500".repeat(60)));
|
|
1890
|
+
console.log(template);
|
|
1891
|
+
console.log(pc4.dim("\u2500".repeat(60)));
|
|
1892
|
+
console.log(pc4.dim("\nRun without --dry-run to create the file."));
|
|
1893
|
+
return;
|
|
1894
|
+
}
|
|
1895
|
+
if (existsSync2(workflowPath)) {
|
|
1896
|
+
console.log(pc4.yellow("\u26A0\uFE0F Workflow already exists: .github/workflows/trie-scan.yml"));
|
|
1897
|
+
console.log(pc4.dim(" Run with --dry-run to preview what would be written."));
|
|
1898
|
+
console.log(pc4.dim(" Delete the existing file to regenerate."));
|
|
1899
|
+
return;
|
|
1900
|
+
}
|
|
1901
|
+
if (!existsSync2(workflowsDir)) {
|
|
1902
|
+
mkdirSync(workflowsDir, { recursive: true });
|
|
1903
|
+
}
|
|
1904
|
+
writeFileSync(workflowPath, template);
|
|
1905
|
+
console.log(pc4.green("\u2713") + " Created .github/workflows/trie-scan.yml");
|
|
1906
|
+
console.log("");
|
|
1907
|
+
console.log(pc4.bold("Next steps:"));
|
|
1908
|
+
console.log("");
|
|
1909
|
+
console.log(" 1. Add your Anthropic API key to GitHub Secrets:");
|
|
1910
|
+
console.log(pc4.dim(" Settings \u2192 Secrets \u2192 Actions \u2192 New repository secret"));
|
|
1911
|
+
console.log(pc4.dim(" Name: ANTHROPIC_API_KEY"));
|
|
1912
|
+
console.log("");
|
|
1913
|
+
console.log(" 2. Commit and push:");
|
|
1914
|
+
console.log(pc4.dim(" git add .github/workflows/trie-scan.yml"));
|
|
1915
|
+
console.log(pc4.dim(' git commit -m "Add Trie security scan with memory"'));
|
|
1916
|
+
console.log(pc4.dim(" git push"));
|
|
1917
|
+
console.log("");
|
|
1918
|
+
console.log(pc4.bold("Memory caching benefits:"));
|
|
1919
|
+
console.log(pc4.dim(" \u2022 Trie learns from past scans"));
|
|
1920
|
+
console.log(pc4.dim(" \u2022 Tracks issue trends over time"));
|
|
1921
|
+
console.log(pc4.dim(" \u2022 Remembers when issues were introduced"));
|
|
1922
|
+
console.log(pc4.dim(" \u2022 Recognizes recurring patterns"));
|
|
1923
|
+
}
|
|
1924
|
+
|
|
911
1925
|
// src/cli/main.ts
|
|
912
1926
|
var __filename2 = fileURLToPath(import.meta.url);
|
|
913
1927
|
var __dirname2 = dirname(__filename2);
|
|
914
|
-
var
|
|
1928
|
+
var DEFAULT_VERSION = "0.0.0";
|
|
1929
|
+
function getCliVersion() {
|
|
1930
|
+
try {
|
|
1931
|
+
const pkgPath = resolve(__dirname2, "..", "..", "package.json");
|
|
1932
|
+
const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
|
|
1933
|
+
return typeof pkg.version === "string" && pkg.version.trim() ? pkg.version.trim() : DEFAULT_VERSION;
|
|
1934
|
+
} catch {
|
|
1935
|
+
return DEFAULT_VERSION;
|
|
1936
|
+
}
|
|
1937
|
+
}
|
|
915
1938
|
function showBanner() {
|
|
916
1939
|
console.log(`
|
|
917
1940
|
\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
|
|
@@ -931,20 +1954,24 @@ function showBanner() {
|
|
|
931
1954
|
}
|
|
932
1955
|
function showHelp2() {
|
|
933
1956
|
showBanner();
|
|
934
|
-
console.log(`Version: ${
|
|
1957
|
+
console.log(`Version: ${getCliVersion()}
|
|
935
1958
|
|
|
936
1959
|
USAGE:
|
|
937
|
-
trie
|
|
1960
|
+
trie <command> [options]
|
|
938
1961
|
|
|
939
1962
|
COMMANDS:
|
|
940
1963
|
init Initialize Trie in your project
|
|
941
1964
|
scan Scan codebase once and exit
|
|
942
1965
|
watch Start the guardian (watches + nudges as you code)
|
|
943
1966
|
check Run risk check before pushing
|
|
1967
|
+
|
|
944
1968
|
tell "<incident>" Report an incident ("users can't log in")
|
|
945
1969
|
ok Mark last warning as helpful (thumbs up)
|
|
946
1970
|
bad Mark last warning as not helpful (thumbs down)
|
|
947
1971
|
quiet Snooze nudges for 1 hour
|
|
1972
|
+
|
|
1973
|
+
checkpoint Save work context to .trie/ (aliases: cp, save)
|
|
1974
|
+
reconcile Reconcile context graph from JSON backup
|
|
948
1975
|
|
|
949
1976
|
skills Manage external skills from skills.sh
|
|
950
1977
|
memory Search and manage issue memory
|
|
@@ -955,38 +1982,31 @@ COMMANDS:
|
|
|
955
1982
|
agent-smith Run Agent Smith (35 vibe code hunters)
|
|
956
1983
|
super-reviewer Run interactive PR review
|
|
957
1984
|
|
|
1985
|
+
ci Generate GitHub Actions workflow with memory caching
|
|
1986
|
+
|
|
958
1987
|
help Show this help message
|
|
959
1988
|
version Show version information
|
|
960
1989
|
|
|
961
1990
|
EXAMPLES:
|
|
962
|
-
# Initialize Trie in your project
|
|
963
|
-
trie
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
#
|
|
969
|
-
trie
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
trie
|
|
973
|
-
|
|
974
|
-
#
|
|
975
|
-
|
|
976
|
-
trie bad # Warning was noise
|
|
977
|
-
|
|
978
|
-
# Check before pushing (also runs via git hooks)
|
|
979
|
-
trie check
|
|
980
|
-
|
|
981
|
-
# Install skills from skills.sh
|
|
1991
|
+
trie init # Initialize Trie in your project
|
|
1992
|
+
trie watch # Start the guardian (watches + nudges)
|
|
1993
|
+
trie scan # Scan your codebase now
|
|
1994
|
+
trie scan --agents security,privacy # Scan with specific agents
|
|
1995
|
+
|
|
1996
|
+
trie tell "users couldn't log in" # Report an incident
|
|
1997
|
+
trie ok # Warning was helpful (thumbs up)
|
|
1998
|
+
trie bad # Warning was noise (thumbs down)
|
|
1999
|
+
trie quiet # Snooze nudges for 1 hour
|
|
2000
|
+
|
|
2001
|
+
trie check # Risk check before pushing
|
|
2002
|
+
trie checkpoint "finished auth" # Save work context
|
|
2003
|
+
trie checkpoint list # List recent checkpoints
|
|
2004
|
+
|
|
982
2005
|
trie skills add vercel-labs/agent-skills react-best-practices
|
|
983
|
-
|
|
984
|
-
# Search your incident memory
|
|
985
2006
|
trie memory search "SQL injection"
|
|
986
|
-
|
|
987
|
-
#
|
|
988
|
-
trie
|
|
989
|
-
trie super-reviewer # Interactive PR review
|
|
2007
|
+
trie agent-smith # 35 vibe code hunters
|
|
2008
|
+
trie super-reviewer # Interactive PR review
|
|
2009
|
+
trie ci # Generate CI workflow with memory caching
|
|
990
2010
|
|
|
991
2011
|
MCP TOOLS (use via Cursor/Claude Desktop):
|
|
992
2012
|
trie_scan Intelligent scan with agent selection
|
|
@@ -1085,14 +2105,14 @@ BUILT-IN AGENTS:
|
|
|
1085
2105
|
|
|
1086
2106
|
Custom Skills:
|
|
1087
2107
|
\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500`);
|
|
1088
|
-
const skillsDir =
|
|
1089
|
-
if (
|
|
2108
|
+
const skillsDir = join2(getWorkingDirectory(void 0, true), ".trie", "agents");
|
|
2109
|
+
if (existsSync3(skillsDir)) {
|
|
1090
2110
|
try {
|
|
1091
2111
|
const { readdirSync } = __require("fs");
|
|
1092
2112
|
const files = readdirSync(skillsDir).filter((f) => f.endsWith(".json"));
|
|
1093
2113
|
if (files.length > 0) {
|
|
1094
2114
|
for (const file of files) {
|
|
1095
|
-
const config = JSON.parse(readFileSync(
|
|
2115
|
+
const config = JSON.parse(readFileSync(join2(skillsDir, file), "utf-8"));
|
|
1096
2116
|
console.log(` ${config.name.padEnd(18)} ${config.description || "Custom skill"}`);
|
|
1097
2117
|
}
|
|
1098
2118
|
} else {
|
|
@@ -1118,7 +2138,7 @@ to code reviews. Great for style guides, compliance docs, etc.
|
|
|
1118
2138
|
}
|
|
1119
2139
|
function showVersion() {
|
|
1120
2140
|
showBanner();
|
|
1121
|
-
console.error(`v${
|
|
2141
|
+
console.error(`v${getCliVersion()}`);
|
|
1122
2142
|
}
|
|
1123
2143
|
async function handleProject(args) {
|
|
1124
2144
|
const subcommand = args[0]?.toLowerCase();
|
|
@@ -1150,13 +2170,13 @@ Next steps:
|
|
|
1150
2170
|
`);
|
|
1151
2171
|
} else {
|
|
1152
2172
|
console.log(`PROJECT.md already exists at: ${result.path}`);
|
|
1153
|
-
console.log('Use "trie
|
|
2173
|
+
console.log('Use "trie project" to view it.');
|
|
1154
2174
|
}
|
|
1155
2175
|
return;
|
|
1156
2176
|
}
|
|
1157
2177
|
if (subcommand === "edit") {
|
|
1158
2178
|
const editor = process.env.EDITOR || process.env.VISUAL || "nano";
|
|
1159
|
-
const projectPath =
|
|
2179
|
+
const projectPath = join2(workDir, ".trie", "PROJECT.md");
|
|
1160
2180
|
if (!projectInfoExists(workDir)) {
|
|
1161
2181
|
console.log("No PROJECT.md found. Creating one first...");
|
|
1162
2182
|
await initProjectInfo(workDir);
|
|
@@ -1177,9 +2197,9 @@ Next steps:
|
|
|
1177
2197
|
No PROJECT.md found in this project.
|
|
1178
2198
|
|
|
1179
2199
|
COMMANDS:
|
|
1180
|
-
trie
|
|
1181
|
-
trie
|
|
1182
|
-
trie
|
|
2200
|
+
trie project init Create PROJECT.md from template
|
|
2201
|
+
trie project edit Open PROJECT.md in $EDITOR
|
|
2202
|
+
trie project View PROJECT.md contents
|
|
1183
2203
|
|
|
1184
2204
|
WHAT IS PROJECT.MD?
|
|
1185
2205
|
PROJECT.md stores important project context for AI assistants:
|
|
@@ -1202,7 +2222,7 @@ This info is available via trie://project MCP resource.
|
|
|
1202
2222
|
\u2551 \u{1F4CB} Project Information \u2551
|
|
1203
2223
|
\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
|
|
1204
2224
|
|
|
1205
|
-
Path: ${
|
|
2225
|
+
Path: ${join2(workDir, ".trie", "PROJECT.md")}
|
|
1206
2226
|
|
|
1207
2227
|
${"-".repeat(68)}
|
|
1208
2228
|
`);
|
|
@@ -1253,6 +2273,15 @@ function main() {
|
|
|
1253
2273
|
showHelp2();
|
|
1254
2274
|
process.exit(0);
|
|
1255
2275
|
}
|
|
2276
|
+
const initRequired = !["init", "version", "--version", "-v"].includes(command);
|
|
2277
|
+
if (initRequired) {
|
|
2278
|
+
const workDir = getWorkingDirectory(void 0, true);
|
|
2279
|
+
if (!isTrieInitialized(workDir)) {
|
|
2280
|
+
console.error("Trie is not initialized for this project.");
|
|
2281
|
+
console.error("Run `trie init` first.");
|
|
2282
|
+
process.exit(1);
|
|
2283
|
+
}
|
|
2284
|
+
}
|
|
1256
2285
|
switch (command) {
|
|
1257
2286
|
case "setup":
|
|
1258
2287
|
case "config":
|
|
@@ -1300,6 +2329,23 @@ function main() {
|
|
|
1300
2329
|
case "check":
|
|
1301
2330
|
handleCheckCommand(restArgs);
|
|
1302
2331
|
break;
|
|
2332
|
+
case "pre-push":
|
|
2333
|
+
handlePrePushCommand(restArgs);
|
|
2334
|
+
break;
|
|
2335
|
+
case "fix":
|
|
2336
|
+
case "auto-fix":
|
|
2337
|
+
case "autofix":
|
|
2338
|
+
handleAutoFixCommand(restArgs);
|
|
2339
|
+
break;
|
|
2340
|
+
case "goal":
|
|
2341
|
+
case "goals":
|
|
2342
|
+
handleGoalCommand(restArgs);
|
|
2343
|
+
break;
|
|
2344
|
+
case "hypothesis":
|
|
2345
|
+
case "hypo":
|
|
2346
|
+
case "hypotheses":
|
|
2347
|
+
handleHypothesisCommand(restArgs);
|
|
2348
|
+
break;
|
|
1303
2349
|
case "tell":
|
|
1304
2350
|
handleTellCommand(restArgs);
|
|
1305
2351
|
break;
|
|
@@ -1328,6 +2374,10 @@ function main() {
|
|
|
1328
2374
|
case "trie_super_reviewer":
|
|
1329
2375
|
runScan(["--agents", "super-reviewer", ...restArgs]);
|
|
1330
2376
|
break;
|
|
2377
|
+
case "ci":
|
|
2378
|
+
case "ci-setup":
|
|
2379
|
+
handleCISetupCommand(restArgs);
|
|
2380
|
+
break;
|
|
1331
2381
|
default:
|
|
1332
2382
|
if (command.startsWith("-")) {
|
|
1333
2383
|
const { spawn } = __require("child_process");
|
|
@@ -1341,7 +2391,7 @@ function main() {
|
|
|
1341
2391
|
});
|
|
1342
2392
|
} else {
|
|
1343
2393
|
console.error(`Unknown command: ${command}`);
|
|
1344
|
-
console.error(`Run 'trie
|
|
2394
|
+
console.error(`Run 'trie help' for usage information.`);
|
|
1345
2395
|
process.exit(1);
|
|
1346
2396
|
}
|
|
1347
2397
|
}
|