@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
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
import {
|
|
2
|
+
recordToGlobalMemory,
|
|
3
|
+
updateGlobalMemoryMd
|
|
4
|
+
} from "./chunk-7KHT2NKR.js";
|
|
1
5
|
import {
|
|
2
6
|
checkFileLevelIssues,
|
|
3
7
|
getVibeCodeTrie,
|
|
@@ -6,13 +10,20 @@ import {
|
|
|
6
10
|
import {
|
|
7
11
|
AgentSmithSkill,
|
|
8
12
|
BaseSkill,
|
|
13
|
+
output
|
|
14
|
+
} from "./chunk-K6BQBKIR.js";
|
|
15
|
+
import {
|
|
9
16
|
isAIAvailable,
|
|
10
|
-
output,
|
|
11
17
|
runAIAnalysis
|
|
12
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-AQCAMIQQ.js";
|
|
19
|
+
import {
|
|
20
|
+
atomicWriteFile,
|
|
21
|
+
atomicWriteJSON,
|
|
22
|
+
storeIssues
|
|
23
|
+
} from "./chunk-TSHZQKCM.js";
|
|
13
24
|
import {
|
|
14
25
|
getWorkingDirectory
|
|
15
|
-
} from "./chunk-
|
|
26
|
+
} from "./chunk-KOFQ47YW.js";
|
|
16
27
|
import {
|
|
17
28
|
isInteractiveMode
|
|
18
29
|
} from "./chunk-RAZUNSBI.js";
|
|
@@ -284,10 +295,13 @@ var SECURITY_INDICATORS = {
|
|
|
284
295
|
};
|
|
285
296
|
var CRITICAL_PATTERNS = [
|
|
286
297
|
// Real secrets (not placeholders)
|
|
287
|
-
{ pattern:
|
|
298
|
+
{ pattern: /\b(AKI|ASI|AGP|AID|ANP|ARO|AIP)A[A-Z0-9]{16}\b/, severity: "critical", issue: "AWS Access Key exposed", fix: "Remove AWS key and rotate immediately. Use environment variables." },
|
|
299
|
+
{ pattern: /aws_secret_access_key\s*[:=]\s*['"][A-Za-z0-9/+=]{40}['"]/i, severity: "critical", issue: "AWS Secret Access Key exposed", fix: "Remove AWS secret and rotate immediately. Use environment variables or a secret manager." },
|
|
288
300
|
{ pattern: /-----BEGIN (RSA |EC |DSA )?PRIVATE KEY-----/, severity: "critical", issue: "Private key exposed in code", fix: "Remove private key immediately. Store in secure vault." },
|
|
289
301
|
{ pattern: /ghp_[A-Za-z0-9]{36}/, severity: "critical", issue: "GitHub personal access token exposed", fix: "Revoke token and use environment variables." },
|
|
302
|
+
{ pattern: /github_pat_[A-Za-z0-9_]{70,}/, severity: "critical", issue: "GitHub fine-grained token exposed", fix: "Revoke token and use environment variables." },
|
|
290
303
|
{ pattern: /sk_live_[A-Za-z0-9]{24,}/, severity: "critical", issue: "Stripe live API key exposed", fix: "Rotate Stripe key immediately. Use environment variables." },
|
|
304
|
+
{ pattern: /rk_live_[A-Za-z0-9]{24,}/, severity: "critical", issue: "Stripe live restricted key exposed", fix: "Rotate Stripe key immediately. Use environment variables." },
|
|
291
305
|
{ pattern: /xox[baprs]-[A-Za-z0-9-]{10,}/, severity: "critical", issue: "Slack token exposed", fix: "Revoke Slack token and use environment variables." }
|
|
292
306
|
];
|
|
293
307
|
var SecuritySkill = class extends BaseSkill {
|
|
@@ -484,6 +498,7 @@ Output STRICT JSON:
|
|
|
484
498
|
const issues = [];
|
|
485
499
|
for (const file of files) {
|
|
486
500
|
if (this.shouldAlwaysSkip(file)) continue;
|
|
501
|
+
if (this.isTestFile(file)) continue;
|
|
487
502
|
try {
|
|
488
503
|
const content = await this.readFile(file);
|
|
489
504
|
const lines = content.split("\n");
|
|
@@ -1213,17 +1228,25 @@ var AccessibilitySkill = class extends BaseSkill {
|
|
|
1213
1228
|
const seriousCount = issues.filter((i) => i.severity === "serious").length;
|
|
1214
1229
|
if (criticalCount > 0 || seriousCount > 2) {
|
|
1215
1230
|
const score = this.calculateAccessibilityScore(issues);
|
|
1231
|
+
const shouldTriggerVisualQA = criticalCount > 0 || seriousCount >= 2;
|
|
1232
|
+
let recommendation = `Review and fix accessibility issues starting with critical problems. Use axe-core or Lighthouse for additional validation.`;
|
|
1233
|
+
if (shouldTriggerVisualQA) {
|
|
1234
|
+
recommendation = `${recommendation}
|
|
1235
|
+
|
|
1236
|
+
\u2139\uFE0F Consider running 'trie_visual_qa_browser' to capture screenshots for Guardian visual analysis of these accessibility issues.`;
|
|
1237
|
+
}
|
|
1216
1238
|
issues.push(this.createIssue(
|
|
1217
1239
|
this.generateIssueId(),
|
|
1218
1240
|
"low",
|
|
1219
1241
|
// Summary is informational, not a blocker
|
|
1220
|
-
`Accessibility Score: ${score}/100 \u2014 ${criticalCount} critical, ${seriousCount} serious issues`,
|
|
1221
|
-
|
|
1242
|
+
`Accessibility Score: ${score}/100 \u2014 ${criticalCount} critical, ${seriousCount} serious issues${shouldTriggerVisualQA ? " (Visual QA recommended)" : ""}`,
|
|
1243
|
+
recommendation,
|
|
1222
1244
|
files[0] || "project",
|
|
1223
1245
|
void 0,
|
|
1224
1246
|
0.6,
|
|
1225
1247
|
void 0,
|
|
1226
|
-
false
|
|
1248
|
+
false,
|
|
1249
|
+
{ category: "accessibility-summary" }
|
|
1227
1250
|
));
|
|
1228
1251
|
}
|
|
1229
1252
|
return issues;
|
|
@@ -7699,55 +7722,55 @@ var MONEYBAGS_QUOTES = [
|
|
|
7699
7722
|
"Floating-point for money? That's going to be an expensive lesson."
|
|
7700
7723
|
];
|
|
7701
7724
|
var BASE_COST_BY_SEVERITY = {
|
|
7702
|
-
critical:
|
|
7703
|
-
// Critical bugs found in dev
|
|
7704
|
-
serious:
|
|
7705
|
-
// Serious issues
|
|
7706
|
-
moderate:
|
|
7707
|
-
// Moderate issues
|
|
7708
|
-
low:
|
|
7709
|
-
// Low severity -
|
|
7725
|
+
critical: 300,
|
|
7726
|
+
// Critical bugs found in dev (~2 hrs @ $150)
|
|
7727
|
+
serious: 150,
|
|
7728
|
+
// Serious issues (~1 hr @ $150)
|
|
7729
|
+
moderate: 75,
|
|
7730
|
+
// Moderate issues (~30 min @ $150)
|
|
7731
|
+
low: 25
|
|
7732
|
+
// Low severity - quick fixes (~10 min @ $150)
|
|
7710
7733
|
};
|
|
7711
7734
|
var PRODUCTION_MULTIPLIER = {
|
|
7712
|
-
critical:
|
|
7713
|
-
// Critical bugs can cause outages,
|
|
7714
|
-
serious:
|
|
7715
|
-
// Serious bugs cause
|
|
7716
|
-
moderate:
|
|
7735
|
+
critical: 5,
|
|
7736
|
+
// Critical bugs can cause outages, but smaller scale
|
|
7737
|
+
serious: 3,
|
|
7738
|
+
// Serious bugs cause user impact
|
|
7739
|
+
moderate: 2,
|
|
7717
7740
|
// Moderate bugs require hotfixes, user support
|
|
7718
|
-
low:
|
|
7741
|
+
low: 1.5
|
|
7719
7742
|
// Low bugs accumulate tech debt
|
|
7720
7743
|
};
|
|
7721
7744
|
var CATEGORY_MULTIPLIERS = {
|
|
7722
|
-
// Security categories -
|
|
7723
|
-
"security": { multiplier:
|
|
7724
|
-
"authentication": { multiplier:
|
|
7725
|
-
"authorization": { multiplier:
|
|
7726
|
-
"injection": { multiplier:
|
|
7727
|
-
"xss": { multiplier:
|
|
7728
|
-
"secrets": { multiplier:
|
|
7729
|
-
"cryptography": { multiplier:
|
|
7745
|
+
// Security categories - reduced but still prioritized
|
|
7746
|
+
"security": { multiplier: 3, reason: "Security vulnerabilities require careful remediation" },
|
|
7747
|
+
"authentication": { multiplier: 4, reason: "Auth bypass is serious but fixable" },
|
|
7748
|
+
"authorization": { multiplier: 3, reason: "Authorization flaws expose sensitive data" },
|
|
7749
|
+
"injection": { multiplier: 6, reason: "SQL/Command injection is severe - prioritize fix" },
|
|
7750
|
+
"xss": { multiplier: 2.5, reason: "XSS needs fixing but React mitigates most cases" },
|
|
7751
|
+
"secrets": { multiplier: 4, reason: "Exposed secrets require rotation" },
|
|
7752
|
+
"cryptography": { multiplier: 2.5, reason: "Weak crypto should be updated" },
|
|
7730
7753
|
// Data integrity categories
|
|
7731
|
-
"data-loss": { multiplier:
|
|
7732
|
-
"data-corruption": { multiplier:
|
|
7733
|
-
"privacy": { multiplier:
|
|
7734
|
-
// Financial categories
|
|
7735
|
-
"payment": { multiplier:
|
|
7736
|
-
"billing": { multiplier:
|
|
7737
|
-
"financial-calculation": { multiplier:
|
|
7754
|
+
"data-loss": { multiplier: 5, reason: "Data loss is serious - ensure backups exist" },
|
|
7755
|
+
"data-corruption": { multiplier: 3.5, reason: "Data corruption requires recovery work" },
|
|
7756
|
+
"privacy": { multiplier: 3.5, reason: "Privacy violations carry compliance risk" },
|
|
7757
|
+
// Financial categories - still high priority but more reasonable
|
|
7758
|
+
"payment": { multiplier: 8, reason: "Payment bugs need immediate attention" },
|
|
7759
|
+
"billing": { multiplier: 4, reason: "Billing errors erode trust" },
|
|
7760
|
+
"financial-calculation": { multiplier: 3.5, reason: "Incorrect calculations compound over time" },
|
|
7738
7761
|
// Reliability categories
|
|
7739
|
-
"crash": { multiplier:
|
|
7740
|
-
"memory-leak": { multiplier:
|
|
7741
|
-
"deadlock": { multiplier:
|
|
7742
|
-
"race-condition": { multiplier:
|
|
7762
|
+
"crash": { multiplier: 2.5, reason: "Crashes hurt user experience" },
|
|
7763
|
+
"memory-leak": { multiplier: 2, reason: "Memory leaks cause gradual degradation" },
|
|
7764
|
+
"deadlock": { multiplier: 2.5, reason: "Deadlocks require restarts" },
|
|
7765
|
+
"race-condition": { multiplier: 2.5, reason: "Race conditions are hard to debug" },
|
|
7743
7766
|
// User experience
|
|
7744
|
-
"accessibility": { multiplier: 1.
|
|
7745
|
-
"performance": { multiplier:
|
|
7767
|
+
"accessibility": { multiplier: 1.3, reason: "Accessibility issues should be addressed" },
|
|
7768
|
+
"performance": { multiplier: 1.5, reason: "Performance issues hurt conversion rates" },
|
|
7746
7769
|
"ux": { multiplier: 1.2, reason: "UX bugs increase support costs" },
|
|
7747
7770
|
// Code quality
|
|
7748
7771
|
"bug": { multiplier: 1, reason: "General bugs require developer time" },
|
|
7749
7772
|
"type-error": { multiplier: 0.8, reason: "Type errors caught early save debugging time" },
|
|
7750
|
-
"logic-error": { multiplier: 1.
|
|
7773
|
+
"logic-error": { multiplier: 1.3, reason: "Logic errors produce incorrect outcomes" },
|
|
7751
7774
|
// Default
|
|
7752
7775
|
"default": { multiplier: 1, reason: "General code issues" }
|
|
7753
7776
|
};
|
|
@@ -7762,24 +7785,37 @@ var CONTEXT_MULTIPLIERS = {
|
|
|
7762
7785
|
touchesFileSystem: { multiplier: 1.05, description: "File system operations" }
|
|
7763
7786
|
};
|
|
7764
7787
|
var EFFORT_HOURS = {
|
|
7765
|
-
trivial: 0.
|
|
7766
|
-
|
|
7767
|
-
|
|
7768
|
-
|
|
7788
|
+
trivial: 0.25,
|
|
7789
|
+
// 15 minutes - typo fix, obvious one-liner
|
|
7790
|
+
easy: 1,
|
|
7791
|
+
// 1 hour - simple fix, clear solution
|
|
7792
|
+
medium: 4,
|
|
7793
|
+
// 4 hours - half day, requires some investigation
|
|
7794
|
+
hard: 16
|
|
7795
|
+
// 2 days - complex fix, testing needed
|
|
7769
7796
|
};
|
|
7770
7797
|
var DEVELOPER_HOURLY_RATE = 150;
|
|
7798
|
+
var INDUSTRY_MULTIPLIERS = {
|
|
7799
|
+
"solopreneur": { multiplier: 0.2, label: "Solopreneur (1-person team, minimal overhead)" },
|
|
7800
|
+
"startup": { multiplier: 0.6, label: "Startup (small team, fast iteration)" },
|
|
7801
|
+
"saas": { multiplier: 0.8, label: "SaaS (recurring revenue, moderate compliance)" },
|
|
7802
|
+
"ecommerce": { multiplier: 1, label: "E-commerce (transactions, customer data)" },
|
|
7803
|
+
"enterprise": { multiplier: 1.5, label: "Enterprise (large contracts, SLAs)" },
|
|
7804
|
+
"fintech": { multiplier: 2, label: "Fintech (financial regulations, audits)" },
|
|
7805
|
+
"healthcare": { multiplier: 2.5, label: "Healthcare (HIPAA, patient data)" }
|
|
7806
|
+
};
|
|
7771
7807
|
var DEFAULT_USER_COUNT = 250;
|
|
7772
7808
|
var USER_COUNT_MULTIPLIERS = [
|
|
7773
7809
|
{ threshold: 0, multiplier: 0.2, label: "Pre-launch (0 users)" },
|
|
7774
|
-
{ threshold: 50, multiplier: 0.
|
|
7775
|
-
{ threshold: 250, multiplier:
|
|
7776
|
-
|
|
7777
|
-
|
|
7778
|
-
{ threshold: 5e3, multiplier:
|
|
7779
|
-
{ threshold: 25e3, multiplier:
|
|
7780
|
-
{ threshold: 1e5, multiplier:
|
|
7781
|
-
{ threshold: 5e5, multiplier:
|
|
7782
|
-
{ threshold: 1e6, multiplier:
|
|
7810
|
+
{ threshold: 50, multiplier: 0.35, label: "MVP (50 users)" },
|
|
7811
|
+
{ threshold: 250, multiplier: 0.6, label: "Early stage (250 users)" },
|
|
7812
|
+
{ threshold: 1e3, multiplier: 1, label: "Growing (1K users)" },
|
|
7813
|
+
// Baseline
|
|
7814
|
+
{ threshold: 5e3, multiplier: 2, label: "Traction (5K users)" },
|
|
7815
|
+
{ threshold: 25e3, multiplier: 4, label: "Scale-up (25K users)" },
|
|
7816
|
+
{ threshold: 1e5, multiplier: 7, label: "Growth (100K users)" },
|
|
7817
|
+
{ threshold: 5e5, multiplier: 10, label: "Large (500K users)" },
|
|
7818
|
+
{ threshold: 1e6, multiplier: 12, label: "Enterprise (1M+ users)" }
|
|
7783
7819
|
// Capped to avoid runaway estimates
|
|
7784
7820
|
];
|
|
7785
7821
|
var PER_USER_COSTS = {
|
|
@@ -7832,6 +7868,70 @@ var MoneybagSkill = class extends BaseSkill {
|
|
|
7832
7868
|
}
|
|
7833
7869
|
return { multiplier: scale.multiplier, label: scale.label };
|
|
7834
7870
|
}
|
|
7871
|
+
/**
|
|
7872
|
+
* Get the industry multiplier for current industry setting
|
|
7873
|
+
*/
|
|
7874
|
+
getIndustryMultiplier() {
|
|
7875
|
+
const industry = this.config.industry;
|
|
7876
|
+
if (!industry || !INDUSTRY_MULTIPLIERS[industry]) {
|
|
7877
|
+
return { multiplier: 1, label: "General (no industry specified)" };
|
|
7878
|
+
}
|
|
7879
|
+
return INDUSTRY_MULTIPLIERS[industry];
|
|
7880
|
+
}
|
|
7881
|
+
/**
|
|
7882
|
+
* Get paying user count (defaults to userCount if not set)
|
|
7883
|
+
*/
|
|
7884
|
+
get payingUsers() {
|
|
7885
|
+
return this.config.payingUsers && this.config.payingUsers > 0 ? this.config.payingUsers : this.userCount;
|
|
7886
|
+
}
|
|
7887
|
+
/**
|
|
7888
|
+
* Get monthly recurring revenue (MRR)
|
|
7889
|
+
*/
|
|
7890
|
+
get monthlyRevenue() {
|
|
7891
|
+
const rpu = this.config.revenuePerUser ?? 0;
|
|
7892
|
+
return this.payingUsers * rpu;
|
|
7893
|
+
}
|
|
7894
|
+
/**
|
|
7895
|
+
* Get revenue context for cost scaling
|
|
7896
|
+
* If revenue is set, we can make smarter decisions about cost impact
|
|
7897
|
+
*/
|
|
7898
|
+
getRevenueContext() {
|
|
7899
|
+
const rpu = this.config.revenuePerUser ?? 0;
|
|
7900
|
+
const payingUsers = this.payingUsers;
|
|
7901
|
+
const mrr = payingUsers * rpu;
|
|
7902
|
+
if (rpu <= 0 || mrr <= 0) {
|
|
7903
|
+
return {
|
|
7904
|
+
hasRevenue: false,
|
|
7905
|
+
mrr: 0,
|
|
7906
|
+
payingUsers,
|
|
7907
|
+
revenuePerUser: 0,
|
|
7908
|
+
revenueMultiplier: 1,
|
|
7909
|
+
label: "No revenue data"
|
|
7910
|
+
};
|
|
7911
|
+
}
|
|
7912
|
+
let revenueMultiplier;
|
|
7913
|
+
let label;
|
|
7914
|
+
if (mrr < 500) {
|
|
7915
|
+
revenueMultiplier = 0.2;
|
|
7916
|
+
label = `Hobby ($${mrr}/mo MRR)`;
|
|
7917
|
+
} else if (mrr < 2e3) {
|
|
7918
|
+
revenueMultiplier = 0.4;
|
|
7919
|
+
label = `Side project ($${mrr.toLocaleString()}/mo MRR)`;
|
|
7920
|
+
} else if (mrr < 1e4) {
|
|
7921
|
+
revenueMultiplier = 0.7;
|
|
7922
|
+
label = `Small business ($${mrr.toLocaleString()}/mo MRR)`;
|
|
7923
|
+
} else if (mrr < 5e4) {
|
|
7924
|
+
revenueMultiplier = 1;
|
|
7925
|
+
label = `Growing business ($${(mrr / 1e3).toFixed(0)}k/mo MRR)`;
|
|
7926
|
+
} else if (mrr < 2e5) {
|
|
7927
|
+
revenueMultiplier = 1.5;
|
|
7928
|
+
label = `Established ($${(mrr / 1e3).toFixed(0)}k/mo MRR)`;
|
|
7929
|
+
} else {
|
|
7930
|
+
revenueMultiplier = 2;
|
|
7931
|
+
label = `Scale-up ($${(mrr / 1e3).toFixed(0)}k/mo MRR)`;
|
|
7932
|
+
}
|
|
7933
|
+
return { hasRevenue: true, mrr, payingUsers, revenuePerUser: rpu, revenueMultiplier, label };
|
|
7934
|
+
}
|
|
7835
7935
|
/**
|
|
7836
7936
|
* Display the Moneybags entrance banner
|
|
7837
7937
|
* Uses OutputManager to route to TUI or console
|
|
@@ -7931,6 +8031,10 @@ var MoneybagSkill = class extends BaseSkill {
|
|
|
7931
8031
|
const userScaleMultiplier = userScale.multiplier;
|
|
7932
8032
|
const userScaleLabel = userScale.label;
|
|
7933
8033
|
const userCount = this.userCount;
|
|
8034
|
+
const industryScale = this.getIndustryMultiplier();
|
|
8035
|
+
const industryMultiplier = industryScale.multiplier;
|
|
8036
|
+
const revenueContext = this.getRevenueContext();
|
|
8037
|
+
const revenueMultiplier = revenueContext.hasRevenue ? revenueContext.revenueMultiplier : 1;
|
|
7934
8038
|
const perUserRate = PER_USER_COSTS[category] || 0;
|
|
7935
8039
|
const affectedUserPercent = {
|
|
7936
8040
|
critical: 0.75,
|
|
@@ -7944,20 +8048,33 @@ var MoneybagSkill = class extends BaseSkill {
|
|
|
7944
8048
|
};
|
|
7945
8049
|
const affectedUsers = Math.round(userCount * affectedUserPercent[issue.severity]);
|
|
7946
8050
|
const perUserCost = Math.round(perUserRate * affectedUsers);
|
|
7947
|
-
const
|
|
7948
|
-
const
|
|
8051
|
+
const defaultEffort = issue.severity === "critical" ? "hard" : issue.severity === "serious" ? "medium" : "easy";
|
|
8052
|
+
const effort = issue.effort || defaultEffort;
|
|
8053
|
+
const fixHours = EFFORT_HOURS[effort] || 4;
|
|
7949
8054
|
const developerRate = this.config.developerRate ?? DEVELOPER_HOURLY_RATE;
|
|
7950
8055
|
const fixCost = fixHours * developerRate;
|
|
7951
|
-
|
|
7952
|
-
|
|
7953
|
-
|
|
7954
|
-
const
|
|
7955
|
-
|
|
7956
|
-
|
|
7957
|
-
|
|
7958
|
-
|
|
7959
|
-
|
|
7960
|
-
|
|
8056
|
+
let totalNowCost;
|
|
8057
|
+
let totalProductionCost;
|
|
8058
|
+
let savings;
|
|
8059
|
+
const isPreRevenue = !revenueContext.hasRevenue || revenueContext.mrr === 0;
|
|
8060
|
+
if (isPreRevenue) {
|
|
8061
|
+
totalNowCost = Math.round(fixCost);
|
|
8062
|
+
const severityMultiplier = { critical: 2, serious: 1.5, moderate: 1.2, low: 1 }[issue.severity];
|
|
8063
|
+
totalProductionCost = Math.round(fixCost * severityMultiplier);
|
|
8064
|
+
savings = totalProductionCost - totalNowCost;
|
|
8065
|
+
} else {
|
|
8066
|
+
const BASE_NOW_CAP = 3e3;
|
|
8067
|
+
const BASE_PROD_CAP = 15e3;
|
|
8068
|
+
const effectiveMultiplier = Math.min(userScaleMultiplier, revenueMultiplier) * 0.7 + revenueMultiplier * 0.3;
|
|
8069
|
+
const maxNowCost = BASE_NOW_CAP * effectiveMultiplier;
|
|
8070
|
+
const maxProdCost = BASE_PROD_CAP * effectiveMultiplier;
|
|
8071
|
+
const rawNowCost = baseCost * categoryMultiplier * contextMultiplier * industryMultiplier * revenueMultiplier + fixCost;
|
|
8072
|
+
totalNowCost = Math.round(Math.min(rawNowCost, maxNowCost));
|
|
8073
|
+
const rawProductionCost = baseCost * productionMultiplier * categoryMultiplier * contextMultiplier * industryMultiplier * revenueMultiplier + perUserCost + // Add per-user costs for production impact
|
|
8074
|
+
fixCost * 2;
|
|
8075
|
+
totalProductionCost = Math.round(Math.min(rawProductionCost, maxProdCost));
|
|
8076
|
+
savings = totalProductionCost - totalNowCost;
|
|
8077
|
+
}
|
|
7961
8078
|
const summary = this.generateCostSummary(
|
|
7962
8079
|
issue,
|
|
7963
8080
|
totalNowCost,
|
|
@@ -8016,16 +8133,28 @@ var MoneybagSkill = class extends BaseSkill {
|
|
|
8016
8133
|
/**
|
|
8017
8134
|
* Generate a human-readable cost summary
|
|
8018
8135
|
*/
|
|
8019
|
-
generateCostSummary(_issue, nowCost, productionCost, savings, contextFactors,
|
|
8136
|
+
generateCostSummary(_issue, nowCost, productionCost, savings, contextFactors, _userCount) {
|
|
8020
8137
|
const formatCurrency = (n) => n >= 1e3 ? `$${(n / 1e3).toFixed(1)}k` : `$${n}`;
|
|
8021
|
-
const
|
|
8138
|
+
const revenueContext = this.getRevenueContext();
|
|
8139
|
+
const isPreRevenue = !revenueContext.hasRevenue || revenueContext.mrr === 0;
|
|
8140
|
+
if (isPreRevenue) {
|
|
8141
|
+
const hours = nowCost / (this.config.developerRate ?? DEVELOPER_HOURLY_RATE);
|
|
8142
|
+
const hoursStr = hours < 1 ? `${Math.round(hours * 60)}min` : `${hours.toFixed(1)}hr`;
|
|
8143
|
+
let summary2 = `Dev time: ${hoursStr} (${formatCurrency(nowCost)})`;
|
|
8144
|
+
if (contextFactors.length > 0) {
|
|
8145
|
+
summary2 += ` | ${contextFactors.join(", ")}`;
|
|
8146
|
+
}
|
|
8147
|
+
return summary2;
|
|
8148
|
+
}
|
|
8022
8149
|
let summary = `Fix now: ${formatCurrency(nowCost)} | If production: ${formatCurrency(productionCost)} | Save: ${formatCurrency(savings)}`;
|
|
8023
|
-
|
|
8150
|
+
const mrrStr = revenueContext.mrr >= 1e3 ? `$${(revenueContext.mrr / 1e3).toFixed(0)}k MRR` : `$${revenueContext.mrr} MRR`;
|
|
8151
|
+
summary += ` (${mrrStr})`;
|
|
8024
8152
|
if (contextFactors.length > 0) {
|
|
8025
8153
|
summary += ` | Risk factors: ${contextFactors.join(", ")}`;
|
|
8026
8154
|
}
|
|
8027
|
-
const
|
|
8028
|
-
const
|
|
8155
|
+
const mrrScale = Math.max(1, Math.sqrt(revenueContext.mrr / 1e4));
|
|
8156
|
+
const scaledHighThreshold = 75e3 * mrrScale;
|
|
8157
|
+
const scaledMediumThreshold = 2e4 * mrrScale;
|
|
8029
8158
|
if (productionCost > scaledHighThreshold) {
|
|
8030
8159
|
summary = `[HIGH COST RISK] ${summary}`;
|
|
8031
8160
|
} else if (productionCost > scaledMediumThreshold) {
|
|
@@ -8187,11 +8316,11 @@ FOCUS ON HIGH-COST ISSUES:
|
|
|
8187
8316
|
4. **Privacy Violations** (10x multiplier): PII exposure, GDPR violations
|
|
8188
8317
|
5. **System Crashes** (8x multiplier): Production downtime ($5,600/minute average)
|
|
8189
8318
|
|
|
8190
|
-
COST ESTIMATION FACTORS:
|
|
8191
|
-
- Critical bugs: $
|
|
8192
|
-
- Serious bugs: $
|
|
8193
|
-
- Moderate bugs: $
|
|
8194
|
-
- Low bugs: $
|
|
8319
|
+
COST ESTIMATION FACTORS (baseline for 1K users, scale by industry/user count):
|
|
8320
|
+
- Critical bugs: $2k now, $16k in production
|
|
8321
|
+
- Serious bugs: $800 now, $4k in production
|
|
8322
|
+
- Moderate bugs: $200 now, $600 in production
|
|
8323
|
+
- Low bugs: $50 now, $100 in production
|
|
8195
8324
|
|
|
8196
8325
|
For each issue, estimate:
|
|
8197
8326
|
- Cost to fix NOW (during development)
|
|
@@ -8248,11 +8377,11 @@ Output STRICT JSON:
|
|
|
8248
8377
|
const totalSavings = totalProductionCost - totalNowCost;
|
|
8249
8378
|
const userScale = this.getUserScaleMultiplier();
|
|
8250
8379
|
const userCount = this.userCount;
|
|
8251
|
-
const scaleMultiplier = userScale.multiplier;
|
|
8380
|
+
const scaleMultiplier = Math.max(1, userScale.multiplier);
|
|
8252
8381
|
let riskLevel = "low";
|
|
8253
|
-
if (totalProductionCost >
|
|
8254
|
-
else if (totalProductionCost >
|
|
8255
|
-
else if (totalProductionCost >
|
|
8382
|
+
if (totalProductionCost > 2e5 * scaleMultiplier) riskLevel = "critical";
|
|
8383
|
+
else if (totalProductionCost > 75e3 * scaleMultiplier) riskLevel = "high";
|
|
8384
|
+
else if (totalProductionCost > 2e4 * scaleMultiplier) riskLevel = "medium";
|
|
8256
8385
|
const formatCurrency = (n) => n >= 1e6 ? `$${(n / 1e6).toFixed(2)}M` : n >= 1e3 ? `$${(n / 1e3).toFixed(1)}k` : `$${n}`;
|
|
8257
8386
|
const formatUsers = (n) => n >= 1e6 ? `${(n / 1e6).toFixed(1)}M` : n >= 1e3 ? `${Math.round(n / 1e3)}K` : `${n}`;
|
|
8258
8387
|
const summary = `
|
|
@@ -8649,7 +8778,14 @@ var ProductionReadySkill = class extends BaseSkill {
|
|
|
8649
8778
|
metRequirements / totalRequirements * 50 + (50 - criticalIssues * 20 - seriousIssues * 10)
|
|
8650
8779
|
));
|
|
8651
8780
|
const status = criticalIssues > 0 || seriousIssues > 2 ? "not-ready" : seriousIssues > 0 ? "caution" : "ready";
|
|
8652
|
-
|
|
8781
|
+
const requirements = [];
|
|
8782
|
+
for (const [key, config] of Object.entries(PRODUCTION_PATTERNS)) {
|
|
8783
|
+
requirements.push({
|
|
8784
|
+
name: config.requirement,
|
|
8785
|
+
met: this.foundRequirements.has(key)
|
|
8786
|
+
});
|
|
8787
|
+
}
|
|
8788
|
+
output().readiness(readinessScore, metRequirements, totalRequirements, status, requirements);
|
|
8653
8789
|
}
|
|
8654
8790
|
getAIEnhancementSystemPrompt() {
|
|
8655
8791
|
return `You are a production readiness engineer reviewing code for deployment.
|
|
@@ -8713,10 +8849,12 @@ Output STRICT JSON:
|
|
|
8713
8849
|
};
|
|
8714
8850
|
|
|
8715
8851
|
// src/skills/installer.ts
|
|
8716
|
-
import { mkdir as mkdir2, rm, writeFile as writeFile2, readdir, readFile as readFile3, access, cp } from "fs/promises";
|
|
8717
|
-
import { join as join3 } from "path";
|
|
8852
|
+
import { mkdir as mkdir2, rm, writeFile as writeFile2, readdir, readFile as readFile3, access, cp, stat } from "fs/promises";
|
|
8853
|
+
import { join as join3, basename as basename2, extname as extname2 } from "path";
|
|
8718
8854
|
import { exec } from "child_process";
|
|
8719
8855
|
import { promisify } from "util";
|
|
8856
|
+
import { homedir } from "os";
|
|
8857
|
+
import { existsSync as existsSync3 } from "fs";
|
|
8720
8858
|
|
|
8721
8859
|
// src/skills/parser.ts
|
|
8722
8860
|
import { readFile as readFile2 } from "fs/promises";
|
|
@@ -8765,14 +8903,44 @@ function parseYamlFrontmatter(yaml) {
|
|
|
8765
8903
|
|
|
8766
8904
|
// src/skills/installer.ts
|
|
8767
8905
|
var execAsync = promisify(exec);
|
|
8768
|
-
|
|
8906
|
+
var GLOBAL_SKILLS_DIRS = {
|
|
8907
|
+
trie: join3(homedir(), ".trie", "skills"),
|
|
8908
|
+
cursor: join3(homedir(), ".cursor", "skills"),
|
|
8909
|
+
claude: join3(homedir(), ".claude", "skills"),
|
|
8910
|
+
opencode: join3(homedir(), ".config", "opencode", "skills")
|
|
8911
|
+
};
|
|
8912
|
+
var GLOBAL_SKILLS_DIR = GLOBAL_SKILLS_DIRS.trie;
|
|
8913
|
+
function getAllGlobalSkillDirs() {
|
|
8914
|
+
return Object.values(GLOBAL_SKILLS_DIRS);
|
|
8915
|
+
}
|
|
8916
|
+
function parseGitHubSource(source) {
|
|
8917
|
+
const urlMatch = source.match(/github\.com\/([^/]+)\/([^/]+?)(?:\.git)?(?:\/(?:tree|blob)\/[^/]+\/(.+))?$/);
|
|
8918
|
+
if (urlMatch) {
|
|
8919
|
+
const skill = urlMatch[3]?.replace(/\/$/, "");
|
|
8920
|
+
return {
|
|
8921
|
+
owner: urlMatch[1],
|
|
8922
|
+
repo: urlMatch[2],
|
|
8923
|
+
...skill ? { skill } : {}
|
|
8924
|
+
};
|
|
8925
|
+
}
|
|
8769
8926
|
const parts = source.split("/");
|
|
8770
|
-
if (parts.length
|
|
8771
|
-
|
|
8927
|
+
if (parts.length >= 2) {
|
|
8928
|
+
const skill = parts[2];
|
|
8929
|
+
return {
|
|
8930
|
+
owner: parts[0],
|
|
8931
|
+
repo: parts[1],
|
|
8932
|
+
...skill ? { skill } : {}
|
|
8933
|
+
};
|
|
8934
|
+
}
|
|
8935
|
+
return null;
|
|
8936
|
+
}
|
|
8937
|
+
async function installSkill(source, skillName) {
|
|
8938
|
+
const parsed = parseGitHubSource(source);
|
|
8939
|
+
if (!parsed) {
|
|
8940
|
+
return { success: false, name: "unknown", error: "Invalid source format. Use owner/repo, owner/repo/skill-name, or full GitHub URL" };
|
|
8772
8941
|
}
|
|
8773
|
-
const owner =
|
|
8774
|
-
const
|
|
8775
|
-
const specifiedSkill = skillName || parts[2];
|
|
8942
|
+
const { owner, repo, skill: parsedSkill } = parsed;
|
|
8943
|
+
const specifiedSkill = skillName || parsedSkill;
|
|
8776
8944
|
const skillsDir = join3(getWorkingDirectory(void 0, true), ".trie", "skills");
|
|
8777
8945
|
const tempDir = join3(skillsDir, `.temp-${Date.now()}`);
|
|
8778
8946
|
try {
|
|
@@ -8783,8 +8951,8 @@ async function installSkill(source, skillName) {
|
|
|
8783
8951
|
if (!sourcePath) {
|
|
8784
8952
|
throw new Error(`SKILL.md not found in repository. Searched in: root, skills/, ${specifiedSkill || "subdirectories"}`);
|
|
8785
8953
|
}
|
|
8786
|
-
const
|
|
8787
|
-
const name =
|
|
8954
|
+
const parsed2 = await parseSkillMd(sourcePath);
|
|
8955
|
+
const name = parsed2.frontmatter.name;
|
|
8788
8956
|
const targetPath = join3(skillsDir, name);
|
|
8789
8957
|
await rm(targetPath, { recursive: true, force: true });
|
|
8790
8958
|
await cp(sourcePath, targetPath, { recursive: true });
|
|
@@ -8834,8 +9002,77 @@ async function findSkillPath(repoPath, skillName) {
|
|
|
8834
9002
|
} catch {
|
|
8835
9003
|
}
|
|
8836
9004
|
}
|
|
9005
|
+
if (skillName) {
|
|
9006
|
+
const subagentPath = await findClaudeCodeSubagent(repoPath, skillName);
|
|
9007
|
+
if (subagentPath) {
|
|
9008
|
+
return subagentPath;
|
|
9009
|
+
}
|
|
9010
|
+
}
|
|
8837
9011
|
return null;
|
|
8838
9012
|
}
|
|
9013
|
+
async function findClaudeCodeSubagent(repoPath, skillName) {
|
|
9014
|
+
const searchNames = [
|
|
9015
|
+
`${skillName}.md`,
|
|
9016
|
+
`${skillName.replace(/-/g, "_")}.md`,
|
|
9017
|
+
// Also try without common suffixes
|
|
9018
|
+
skillName.replace(/-pro$/, "-pro.md"),
|
|
9019
|
+
skillName.replace(/-expert$/, "-expert.md")
|
|
9020
|
+
];
|
|
9021
|
+
for (const fileName of searchNames) {
|
|
9022
|
+
const filePath = join3(repoPath, fileName);
|
|
9023
|
+
try {
|
|
9024
|
+
await access(filePath);
|
|
9025
|
+
const content = await readFile3(filePath, "utf-8");
|
|
9026
|
+
if (content.startsWith("---")) {
|
|
9027
|
+
return await convertClaudeCodeToSkillMd(repoPath, filePath, skillName);
|
|
9028
|
+
}
|
|
9029
|
+
} catch {
|
|
9030
|
+
}
|
|
9031
|
+
}
|
|
9032
|
+
try {
|
|
9033
|
+
const entries = await readdir(repoPath);
|
|
9034
|
+
const mdFiles = entries.filter((e) => e.endsWith(".md") && !e.toLowerCase().includes("readme") && !e.toLowerCase().includes("license"));
|
|
9035
|
+
if (mdFiles.length > 0) {
|
|
9036
|
+
const availableSkills = mdFiles.map((f) => f.replace(".md", "")).join(", ");
|
|
9037
|
+
throw new Error(`Skill "${skillName}" not found. Available: ${availableSkills}`);
|
|
9038
|
+
}
|
|
9039
|
+
} catch (e) {
|
|
9040
|
+
if (e instanceof Error && e.message.includes("Available:")) {
|
|
9041
|
+
throw e;
|
|
9042
|
+
}
|
|
9043
|
+
}
|
|
9044
|
+
return null;
|
|
9045
|
+
}
|
|
9046
|
+
async function convertClaudeCodeToSkillMd(repoPath, filePath, skillName) {
|
|
9047
|
+
const content = await readFile3(filePath, "utf-8");
|
|
9048
|
+
const fmMatch = content.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);
|
|
9049
|
+
if (!fmMatch) {
|
|
9050
|
+
throw new Error("Invalid frontmatter format");
|
|
9051
|
+
}
|
|
9052
|
+
const frontmatter = fmMatch[1];
|
|
9053
|
+
const body = fmMatch[2];
|
|
9054
|
+
const nameMatch = frontmatter.match(/name:\s*(.+)/);
|
|
9055
|
+
const descMatch = frontmatter.match(/description:\s*(.+)/);
|
|
9056
|
+
const modelMatch = frontmatter.match(/model:\s*(.+)/);
|
|
9057
|
+
const name = nameMatch ? nameMatch[1].trim() : skillName;
|
|
9058
|
+
const description = descMatch ? descMatch[1].trim() : `Skill from Claude Code subagent: ${name}`;
|
|
9059
|
+
const model = modelMatch ? modelMatch[1].trim() : void 0;
|
|
9060
|
+
const tempSkillDir = join3(repoPath, ".converted-skill", name);
|
|
9061
|
+
await mkdir2(tempSkillDir, { recursive: true });
|
|
9062
|
+
let newContent = `---
|
|
9063
|
+
name: ${name}
|
|
9064
|
+
description: ${description}
|
|
9065
|
+
author: claude-code-subagent
|
|
9066
|
+
tags: [claude-code, subagent${model ? `, ${model}` : ""}]
|
|
9067
|
+
---
|
|
9068
|
+
|
|
9069
|
+
# ${name}
|
|
9070
|
+
|
|
9071
|
+
${body}
|
|
9072
|
+
`;
|
|
9073
|
+
await writeFile2(join3(tempSkillDir, "SKILL.md"), newContent, "utf-8");
|
|
9074
|
+
return tempSkillDir;
|
|
9075
|
+
}
|
|
8839
9076
|
async function listInstalledSkills() {
|
|
8840
9077
|
const skillsDir = join3(getWorkingDirectory(void 0, true), ".trie", "skills");
|
|
8841
9078
|
const skills = [];
|
|
@@ -8876,800 +9113,141 @@ async function removeSkill(skillName) {
|
|
|
8876
9113
|
return false;
|
|
8877
9114
|
}
|
|
8878
9115
|
}
|
|
8879
|
-
|
|
8880
|
-
|
|
8881
|
-
import { readFile as readFile7, writeFile as writeFile6, mkdir as mkdir6 } from "fs/promises";
|
|
8882
|
-
import { existsSync as existsSync6 } from "fs";
|
|
8883
|
-
import { join as join7, basename as basename2 } from "path";
|
|
8884
|
-
|
|
8885
|
-
// src/memory/issue-store.ts
|
|
8886
|
-
import { mkdir as mkdir4, writeFile as writeFile4, readFile as readFile5, readdir as readdir2 } from "fs/promises";
|
|
8887
|
-
import { existsSync as existsSync4 } from "fs";
|
|
8888
|
-
import { join as join5 } from "path";
|
|
8889
|
-
|
|
8890
|
-
// src/memory/bm25.ts
|
|
8891
|
-
var BM25Index = class _BM25Index {
|
|
8892
|
-
documents = /* @__PURE__ */ new Map();
|
|
8893
|
-
termFrequencies = /* @__PURE__ */ new Map();
|
|
8894
|
-
documentFrequencies = /* @__PURE__ */ new Map();
|
|
8895
|
-
documentLengths = /* @__PURE__ */ new Map();
|
|
8896
|
-
avgDocLength = 0;
|
|
8897
|
-
k1 = 1.5;
|
|
8898
|
-
b = 0.75;
|
|
8899
|
-
/**
|
|
8900
|
-
* Add a document to the index
|
|
8901
|
-
*/
|
|
8902
|
-
addDocument(doc) {
|
|
8903
|
-
const tokens = this.tokenize(doc.text);
|
|
8904
|
-
this.documents.set(doc.id, doc);
|
|
8905
|
-
this.documentLengths.set(doc.id, tokens.length);
|
|
8906
|
-
const termFreq = /* @__PURE__ */ new Map();
|
|
8907
|
-
const seenTerms = /* @__PURE__ */ new Set();
|
|
8908
|
-
for (const token of tokens) {
|
|
8909
|
-
termFreq.set(token, (termFreq.get(token) || 0) + 1);
|
|
8910
|
-
if (!seenTerms.has(token)) {
|
|
8911
|
-
seenTerms.add(token);
|
|
8912
|
-
this.documentFrequencies.set(token, (this.documentFrequencies.get(token) || 0) + 1);
|
|
8913
|
-
}
|
|
8914
|
-
}
|
|
8915
|
-
this.termFrequencies.set(doc.id, termFreq);
|
|
8916
|
-
this.updateAvgDocLength();
|
|
8917
|
-
}
|
|
8918
|
-
/**
|
|
8919
|
-
* Add multiple documents
|
|
8920
|
-
*/
|
|
8921
|
-
addDocuments(docs) {
|
|
8922
|
-
for (const doc of docs) {
|
|
8923
|
-
this.addDocument(doc);
|
|
8924
|
-
}
|
|
8925
|
-
}
|
|
8926
|
-
/**
|
|
8927
|
-
* Search the index
|
|
8928
|
-
*/
|
|
8929
|
-
search(query, limit = 10) {
|
|
8930
|
-
const queryTokens = this.tokenize(query);
|
|
8931
|
-
const scores = /* @__PURE__ */ new Map();
|
|
8932
|
-
const N = this.documents.size;
|
|
8933
|
-
for (const [docId] of this.documents) {
|
|
8934
|
-
let score = 0;
|
|
8935
|
-
const docLength = this.documentLengths.get(docId) || 0;
|
|
8936
|
-
const termFreqs = this.termFrequencies.get(docId);
|
|
8937
|
-
if (!termFreqs) continue;
|
|
8938
|
-
for (const term of queryTokens) {
|
|
8939
|
-
const tf = termFreqs.get(term) || 0;
|
|
8940
|
-
if (tf === 0) continue;
|
|
8941
|
-
const df = this.documentFrequencies.get(term) || 0;
|
|
8942
|
-
const idf = Math.log((N - df + 0.5) / (df + 0.5) + 1);
|
|
8943
|
-
const numerator = tf * (this.k1 + 1);
|
|
8944
|
-
const denominator = tf + this.k1 * (1 - this.b + this.b * (docLength / this.avgDocLength));
|
|
8945
|
-
score += idf * (numerator / denominator);
|
|
8946
|
-
}
|
|
8947
|
-
if (score > 0) {
|
|
8948
|
-
scores.set(docId, score);
|
|
8949
|
-
}
|
|
8950
|
-
}
|
|
8951
|
-
return Array.from(scores.entries()).sort((a, b) => b[1] - a[1]).slice(0, limit).map(([id, score]) => {
|
|
8952
|
-
const metadata = this.documents.get(id)?.metadata;
|
|
8953
|
-
const result = { id, score };
|
|
8954
|
-
if (metadata !== void 0) {
|
|
8955
|
-
result.metadata = metadata;
|
|
8956
|
-
}
|
|
8957
|
-
return result;
|
|
8958
|
-
});
|
|
8959
|
-
}
|
|
8960
|
-
/**
|
|
8961
|
-
* Get document count
|
|
8962
|
-
*/
|
|
8963
|
-
get size() {
|
|
8964
|
-
return this.documents.size;
|
|
8965
|
-
}
|
|
8966
|
-
/**
|
|
8967
|
-
* Clear the index
|
|
8968
|
-
*/
|
|
8969
|
-
clear() {
|
|
8970
|
-
this.documents.clear();
|
|
8971
|
-
this.termFrequencies.clear();
|
|
8972
|
-
this.documentFrequencies.clear();
|
|
8973
|
-
this.documentLengths.clear();
|
|
8974
|
-
this.avgDocLength = 0;
|
|
8975
|
-
}
|
|
8976
|
-
/**
|
|
8977
|
-
* Serialize the index to JSON
|
|
8978
|
-
*/
|
|
8979
|
-
serialize() {
|
|
8980
|
-
return JSON.stringify({
|
|
8981
|
-
documents: Array.from(this.documents.entries()),
|
|
8982
|
-
termFrequencies: Array.from(this.termFrequencies.entries()).map(([k, v]) => [k, Array.from(v.entries())]),
|
|
8983
|
-
documentFrequencies: Array.from(this.documentFrequencies.entries()),
|
|
8984
|
-
documentLengths: Array.from(this.documentLengths.entries()),
|
|
8985
|
-
avgDocLength: this.avgDocLength
|
|
8986
|
-
});
|
|
8987
|
-
}
|
|
8988
|
-
/**
|
|
8989
|
-
* Load from serialized JSON
|
|
8990
|
-
*/
|
|
8991
|
-
static deserialize(json) {
|
|
8992
|
-
const data = JSON.parse(json);
|
|
8993
|
-
const index = new _BM25Index();
|
|
8994
|
-
index.documents = new Map(data.documents);
|
|
8995
|
-
index.termFrequencies = new Map(data.termFrequencies.map(([k, v]) => [k, new Map(v)]));
|
|
8996
|
-
index.documentFrequencies = new Map(data.documentFrequencies);
|
|
8997
|
-
index.documentLengths = new Map(data.documentLengths);
|
|
8998
|
-
index.avgDocLength = data.avgDocLength;
|
|
8999
|
-
return index;
|
|
9000
|
-
}
|
|
9001
|
-
tokenize(text) {
|
|
9002
|
-
return text.toLowerCase().replace(/[^\w\s]/g, " ").split(/\s+/).filter((token) => token.length > 2 && !this.isStopWord(token));
|
|
9003
|
-
}
|
|
9004
|
-
isStopWord(word) {
|
|
9005
|
-
const stopWords = /* @__PURE__ */ new Set([
|
|
9006
|
-
"the",
|
|
9007
|
-
"be",
|
|
9008
|
-
"to",
|
|
9009
|
-
"of",
|
|
9010
|
-
"and",
|
|
9011
|
-
"a",
|
|
9012
|
-
"in",
|
|
9013
|
-
"that",
|
|
9014
|
-
"have",
|
|
9015
|
-
"i",
|
|
9016
|
-
"it",
|
|
9017
|
-
"for",
|
|
9018
|
-
"not",
|
|
9019
|
-
"on",
|
|
9020
|
-
"with",
|
|
9021
|
-
"he",
|
|
9022
|
-
"as",
|
|
9023
|
-
"you",
|
|
9024
|
-
"do",
|
|
9025
|
-
"at",
|
|
9026
|
-
"this",
|
|
9027
|
-
"but",
|
|
9028
|
-
"his",
|
|
9029
|
-
"by",
|
|
9030
|
-
"from",
|
|
9031
|
-
"they",
|
|
9032
|
-
"we",
|
|
9033
|
-
"say",
|
|
9034
|
-
"her",
|
|
9035
|
-
"she",
|
|
9036
|
-
"or",
|
|
9037
|
-
"an",
|
|
9038
|
-
"will",
|
|
9039
|
-
"my",
|
|
9040
|
-
"one",
|
|
9041
|
-
"all",
|
|
9042
|
-
"would",
|
|
9043
|
-
"there",
|
|
9044
|
-
"their",
|
|
9045
|
-
"what",
|
|
9046
|
-
"so",
|
|
9047
|
-
"up",
|
|
9048
|
-
"out",
|
|
9049
|
-
"if",
|
|
9050
|
-
"about",
|
|
9051
|
-
"who",
|
|
9052
|
-
"get",
|
|
9053
|
-
"which",
|
|
9054
|
-
"go",
|
|
9055
|
-
"me",
|
|
9056
|
-
"when",
|
|
9057
|
-
"make",
|
|
9058
|
-
"can",
|
|
9059
|
-
"like",
|
|
9060
|
-
"time",
|
|
9061
|
-
"no",
|
|
9062
|
-
"just",
|
|
9063
|
-
"him",
|
|
9064
|
-
"know",
|
|
9065
|
-
"take",
|
|
9066
|
-
"into",
|
|
9067
|
-
"year",
|
|
9068
|
-
"your",
|
|
9069
|
-
"some",
|
|
9070
|
-
"could",
|
|
9071
|
-
"them",
|
|
9072
|
-
"see",
|
|
9073
|
-
"other",
|
|
9074
|
-
"than",
|
|
9075
|
-
"then",
|
|
9076
|
-
"now",
|
|
9077
|
-
"look",
|
|
9078
|
-
"only",
|
|
9079
|
-
"come",
|
|
9080
|
-
"its",
|
|
9081
|
-
"over",
|
|
9082
|
-
"also",
|
|
9083
|
-
"back",
|
|
9084
|
-
"after",
|
|
9085
|
-
"use",
|
|
9086
|
-
"two",
|
|
9087
|
-
"how",
|
|
9088
|
-
"our",
|
|
9089
|
-
"first",
|
|
9090
|
-
"way",
|
|
9091
|
-
"even",
|
|
9092
|
-
"new",
|
|
9093
|
-
"want",
|
|
9094
|
-
"because",
|
|
9095
|
-
"any",
|
|
9096
|
-
"these",
|
|
9097
|
-
"give",
|
|
9098
|
-
"day",
|
|
9099
|
-
"most",
|
|
9100
|
-
"us",
|
|
9101
|
-
"should",
|
|
9102
|
-
"been",
|
|
9103
|
-
"has",
|
|
9104
|
-
"was",
|
|
9105
|
-
"are"
|
|
9106
|
-
]);
|
|
9107
|
-
return stopWords.has(word);
|
|
9108
|
-
}
|
|
9109
|
-
updateAvgDocLength() {
|
|
9110
|
-
if (this.documentLengths.size === 0) {
|
|
9111
|
-
this.avgDocLength = 0;
|
|
9112
|
-
return;
|
|
9113
|
-
}
|
|
9114
|
-
const total = Array.from(this.documentLengths.values()).reduce((a, b) => a + b, 0);
|
|
9115
|
-
this.avgDocLength = total / this.documentLengths.size;
|
|
9116
|
-
}
|
|
9117
|
-
};
|
|
9118
|
-
|
|
9119
|
-
// src/memory/compactor.ts
|
|
9120
|
-
import { mkdir as mkdir3, writeFile as writeFile3, readFile as readFile4 } from "fs/promises";
|
|
9121
|
-
import { existsSync as existsSync3 } from "fs";
|
|
9122
|
-
import { join as join4 } from "path";
|
|
9123
|
-
async function compactOldIssues(issues, options = {}) {
|
|
9124
|
-
const keepDays = options.keepDays ?? 30;
|
|
9125
|
-
const minIssues = options.minIssuesToCompact ?? 100;
|
|
9126
|
-
const cutoffDate = /* @__PURE__ */ new Date();
|
|
9127
|
-
cutoffDate.setDate(cutoffDate.getDate() - keepDays);
|
|
9128
|
-
const oldIssues = issues.filter((i) => new Date(i.timestamp) < cutoffDate);
|
|
9129
|
-
const recentIssues = issues.filter((i) => new Date(i.timestamp) >= cutoffDate);
|
|
9130
|
-
if (oldIssues.length < minIssues) {
|
|
9131
|
-
return { summary: null, remaining: issues };
|
|
9132
|
-
}
|
|
9133
|
-
const summary = buildSummary(oldIssues);
|
|
9134
|
-
return { summary, remaining: recentIssues };
|
|
9135
|
-
}
|
|
9136
|
-
function buildSummary(issues) {
|
|
9137
|
-
const sorted = issues.sort(
|
|
9138
|
-
(a, b) => new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime()
|
|
9139
|
-
);
|
|
9140
|
-
const bySeverity = {};
|
|
9141
|
-
const byAgent = {};
|
|
9142
|
-
const patternMap = /* @__PURE__ */ new Map();
|
|
9143
|
-
const fileCount = /* @__PURE__ */ new Map();
|
|
9144
|
-
for (const issue of issues) {
|
|
9145
|
-
bySeverity[issue.severity] = (bySeverity[issue.severity] || 0) + 1;
|
|
9146
|
-
byAgent[issue.agent] = (byAgent[issue.agent] || 0) + 1;
|
|
9147
|
-
const patternKey = normalizePattern(issue.issue);
|
|
9148
|
-
const existing = patternMap.get(patternKey);
|
|
9149
|
-
if (existing) {
|
|
9150
|
-
existing.count++;
|
|
9151
|
-
} else {
|
|
9152
|
-
patternMap.set(patternKey, { count: 1, issue });
|
|
9153
|
-
}
|
|
9154
|
-
const fileName = issue.file.split("/").pop() || issue.file;
|
|
9155
|
-
fileCount.set(fileName, (fileCount.get(fileName) || 0) + 1);
|
|
9156
|
-
}
|
|
9157
|
-
const topPatterns = Array.from(patternMap.entries()).sort((a, b) => b[1].count - a[1].count).slice(0, 10).map(([pattern, data]) => ({
|
|
9158
|
-
pattern: pattern.slice(0, 100),
|
|
9159
|
-
count: data.count,
|
|
9160
|
-
severity: data.issue.severity,
|
|
9161
|
-
agent: data.issue.agent,
|
|
9162
|
-
exampleFix: data.issue.fix.slice(0, 200)
|
|
9163
|
-
}));
|
|
9164
|
-
const hotFiles = Array.from(fileCount.entries()).sort((a, b) => b[1] - a[1]).slice(0, 10).map(([file, count]) => ({ file, count }));
|
|
9165
|
-
return {
|
|
9166
|
-
period: `${sorted[0]?.timestamp.split("T")[0]} to ${sorted[sorted.length - 1]?.timestamp.split("T")[0]}`,
|
|
9167
|
-
startDate: sorted[0]?.timestamp || "",
|
|
9168
|
-
endDate: sorted[sorted.length - 1]?.timestamp || "",
|
|
9169
|
-
totalIssues: issues.length,
|
|
9170
|
-
resolvedCount: issues.filter((i) => i.resolved).length,
|
|
9171
|
-
bySeverity,
|
|
9172
|
-
byAgent,
|
|
9173
|
-
topPatterns,
|
|
9174
|
-
hotFiles,
|
|
9175
|
-
compactedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
9176
|
-
};
|
|
9177
|
-
}
|
|
9178
|
-
function normalizePattern(text) {
|
|
9179
|
-
return text.toLowerCase().replace(/`[^`]+`/g, "CODE").replace(/\b\d+\b/g, "N").replace(/["']/g, "").replace(/\s+/g, " ").trim().slice(0, 150);
|
|
9180
|
-
}
|
|
9181
|
-
async function saveCompactedSummary(summary, projectDir) {
|
|
9182
|
-
const memoryDir = join4(projectDir, ".trie", "memory");
|
|
9183
|
-
await mkdir3(memoryDir, { recursive: true });
|
|
9184
|
-
const summaryPath = join4(memoryDir, "compacted-summaries.json");
|
|
9185
|
-
let summaries = [];
|
|
9186
|
-
try {
|
|
9187
|
-
if (existsSync3(summaryPath)) {
|
|
9188
|
-
summaries = JSON.parse(await readFile4(summaryPath, "utf-8"));
|
|
9189
|
-
}
|
|
9190
|
-
} catch {
|
|
9191
|
-
summaries = [];
|
|
9192
|
-
}
|
|
9193
|
-
summaries.push(summary);
|
|
9194
|
-
if (summaries.length > 12) {
|
|
9195
|
-
summaries = summaries.slice(-12);
|
|
9196
|
-
}
|
|
9197
|
-
await writeFile3(summaryPath, JSON.stringify(summaries, null, 2));
|
|
9198
|
-
}
|
|
9199
|
-
async function loadCompactedSummaries(projectDir) {
|
|
9200
|
-
const summaryPath = join4(projectDir, ".trie", "memory", "compacted-summaries.json");
|
|
9116
|
+
async function listGlobalSkills() {
|
|
9117
|
+
const skills = [];
|
|
9201
9118
|
try {
|
|
9202
|
-
|
|
9203
|
-
|
|
9119
|
+
const entries = await readdir(GLOBAL_SKILLS_DIR, { withFileTypes: true });
|
|
9120
|
+
for (const entry of entries) {
|
|
9121
|
+
if (!entry.isDirectory() || entry.name.startsWith(".")) continue;
|
|
9122
|
+
const skillPath = join3(GLOBAL_SKILLS_DIR, entry.name);
|
|
9123
|
+
try {
|
|
9124
|
+
const parsed = await parseSkillMd(skillPath);
|
|
9125
|
+
const metaPath = join3(skillPath, ".installed.json");
|
|
9126
|
+
let meta = { installedFrom: "custom", installedAt: (/* @__PURE__ */ new Date()).toISOString() };
|
|
9127
|
+
try {
|
|
9128
|
+
meta = JSON.parse(await readFile3(metaPath, "utf-8"));
|
|
9129
|
+
} catch {
|
|
9130
|
+
}
|
|
9131
|
+
skills.push({
|
|
9132
|
+
name: parsed.frontmatter.name,
|
|
9133
|
+
description: parsed.frontmatter.description,
|
|
9134
|
+
path: skillPath,
|
|
9135
|
+
installedFrom: meta.installedFrom,
|
|
9136
|
+
installedAt: meta.installedAt
|
|
9137
|
+
});
|
|
9138
|
+
} catch {
|
|
9139
|
+
}
|
|
9204
9140
|
}
|
|
9205
9141
|
} catch {
|
|
9206
9142
|
}
|
|
9207
|
-
return
|
|
9208
|
-
}
|
|
9209
|
-
async function getHistoricalInsights(projectDir) {
|
|
9210
|
-
const summaries = await loadCompactedSummaries(projectDir);
|
|
9211
|
-
if (summaries.length === 0) {
|
|
9212
|
-
return {
|
|
9213
|
-
totalHistoricalIssues: 0,
|
|
9214
|
-
recurringPatterns: [],
|
|
9215
|
-
improvementTrend: "unknown"
|
|
9216
|
-
};
|
|
9217
|
-
}
|
|
9218
|
-
const totalHistoricalIssues = summaries.reduce((sum, s) => sum + s.totalIssues, 0);
|
|
9219
|
-
const patternCounts = /* @__PURE__ */ new Map();
|
|
9220
|
-
for (const summary of summaries) {
|
|
9221
|
-
for (const pattern of summary.topPatterns) {
|
|
9222
|
-
const key = pattern.pattern;
|
|
9223
|
-
const existing = patternCounts.get(key);
|
|
9224
|
-
if (existing) {
|
|
9225
|
-
existing.count += pattern.count;
|
|
9226
|
-
existing.appearances++;
|
|
9227
|
-
} else {
|
|
9228
|
-
patternCounts.set(key, { ...pattern, appearances: 1 });
|
|
9229
|
-
}
|
|
9230
|
-
}
|
|
9231
|
-
}
|
|
9232
|
-
const recurringPatterns = Array.from(patternCounts.values()).filter((p) => p.appearances >= 2).sort((a, b) => b.count - a.count).slice(0, 5);
|
|
9233
|
-
let improvementTrend = "unknown";
|
|
9234
|
-
if (summaries.length >= 2) {
|
|
9235
|
-
const recent = summaries.slice(-2);
|
|
9236
|
-
const olderCount = recent[0]?.totalIssues || 0;
|
|
9237
|
-
const newerCount = recent[1]?.totalIssues || 0;
|
|
9238
|
-
if (newerCount < olderCount * 0.8) {
|
|
9239
|
-
improvementTrend = "improving";
|
|
9240
|
-
} else if (newerCount > olderCount * 1.2) {
|
|
9241
|
-
improvementTrend = "declining";
|
|
9242
|
-
} else {
|
|
9243
|
-
improvementTrend = "stable";
|
|
9244
|
-
}
|
|
9245
|
-
}
|
|
9246
|
-
return {
|
|
9247
|
-
totalHistoricalIssues,
|
|
9248
|
-
recurringPatterns,
|
|
9249
|
-
improvementTrend
|
|
9250
|
-
};
|
|
9143
|
+
return skills;
|
|
9251
9144
|
}
|
|
9252
|
-
|
|
9253
|
-
|
|
9254
|
-
|
|
9255
|
-
const
|
|
9256
|
-
|
|
9257
|
-
|
|
9258
|
-
|
|
9259
|
-
|
|
9260
|
-
|
|
9261
|
-
|
|
9262
|
-
|
|
9263
|
-
|
|
9264
|
-
|
|
9265
|
-
|
|
9266
|
-
|
|
9267
|
-
|
|
9268
|
-
|
|
9269
|
-
|
|
9270
|
-
timestamp: now,
|
|
9271
|
-
project,
|
|
9272
|
-
resolved: false
|
|
9273
|
-
};
|
|
9274
|
-
if (issue.line !== void 0) {
|
|
9275
|
-
storedIssue.line = issue.line;
|
|
9276
|
-
}
|
|
9277
|
-
if (issue.category !== void 0) {
|
|
9278
|
-
storedIssue.category = issue.category;
|
|
9145
|
+
async function writeSkillToAllDirs(name, skillMdContent, metadata) {
|
|
9146
|
+
const writtenTo = [];
|
|
9147
|
+
let primaryPath = "";
|
|
9148
|
+
for (const [tool, baseDir] of Object.entries(GLOBAL_SKILLS_DIRS)) {
|
|
9149
|
+
try {
|
|
9150
|
+
await mkdir2(baseDir, { recursive: true });
|
|
9151
|
+
const skillPath = join3(baseDir, name);
|
|
9152
|
+
if (existsSync3(skillPath)) {
|
|
9153
|
+
continue;
|
|
9154
|
+
}
|
|
9155
|
+
await mkdir2(skillPath, { recursive: true });
|
|
9156
|
+
await writeFile2(join3(skillPath, "SKILL.md"), skillMdContent, "utf-8");
|
|
9157
|
+
await writeFile2(join3(skillPath, ".installed.json"), JSON.stringify(metadata, null, 2));
|
|
9158
|
+
writtenTo.push(tool);
|
|
9159
|
+
if (tool === "trie") {
|
|
9160
|
+
primaryPath = skillPath;
|
|
9161
|
+
}
|
|
9162
|
+
} catch {
|
|
9279
9163
|
}
|
|
9280
|
-
stored.push(storedIssue);
|
|
9281
|
-
}
|
|
9282
|
-
await appendToDailyLog(stored, projectDir);
|
|
9283
|
-
await updateIssueIndex(stored, projectDir);
|
|
9284
|
-
return stored.length;
|
|
9285
|
-
}
|
|
9286
|
-
async function searchIssues(query, options = {}) {
|
|
9287
|
-
const projectDir = options.workDir || getWorkingDirectory(void 0, true);
|
|
9288
|
-
const limit = options.limit || 10;
|
|
9289
|
-
const allIssues = await loadIssueIndex(projectDir);
|
|
9290
|
-
if (allIssues.length === 0) {
|
|
9291
|
-
return [];
|
|
9292
|
-
}
|
|
9293
|
-
const filteredIssues = allIssues.filter((issue) => {
|
|
9294
|
-
if (options.project && issue.project !== options.project) return false;
|
|
9295
|
-
if (options.severity && !options.severity.includes(issue.severity)) return false;
|
|
9296
|
-
if (options.agent && issue.agent !== options.agent) return false;
|
|
9297
|
-
if (!options.includeResolved && issue.resolved) return false;
|
|
9298
|
-
return true;
|
|
9299
|
-
});
|
|
9300
|
-
if (filteredIssues.length === 0) {
|
|
9301
|
-
return [];
|
|
9302
9164
|
}
|
|
9303
|
-
|
|
9304
|
-
|
|
9305
|
-
|
|
9306
|
-
|
|
9307
|
-
bm25.addDocument({
|
|
9308
|
-
id: issue.id,
|
|
9309
|
-
text: searchText
|
|
9310
|
-
});
|
|
9311
|
-
issueMap.set(issue.id, issue);
|
|
9312
|
-
}
|
|
9313
|
-
const bm25Results = bm25.search(query, limit);
|
|
9314
|
-
return bm25Results.map((result) => ({
|
|
9315
|
-
issue: issueMap.get(result.id),
|
|
9316
|
-
score: result.score,
|
|
9317
|
-
matchType: "bm25"
|
|
9318
|
-
}));
|
|
9319
|
-
}
|
|
9320
|
-
async function findSimilarIssues(issue, options = {}) {
|
|
9321
|
-
const query = `${issue.issue} ${issue.fix} ${issue.agent}`;
|
|
9322
|
-
const searchOptions = {
|
|
9323
|
-
limit: (options.limit || 5) + 5,
|
|
9324
|
-
// Get extra to account for filtering
|
|
9325
|
-
includeResolved: true
|
|
9326
|
-
};
|
|
9327
|
-
if (options.workDir !== void 0) {
|
|
9328
|
-
searchOptions.workDir = options.workDir;
|
|
9329
|
-
}
|
|
9330
|
-
const results = await searchIssues(query, searchOptions);
|
|
9331
|
-
let filtered = results.filter((r) => r.issue.id !== issue.id);
|
|
9332
|
-
if (options.excludeSameFile) {
|
|
9333
|
-
filtered = filtered.filter((r) => r.issue.file !== issue.file);
|
|
9334
|
-
}
|
|
9335
|
-
return filtered.slice(0, options.limit || 5);
|
|
9336
|
-
}
|
|
9337
|
-
async function markIssueResolved(issueId, workDir) {
|
|
9338
|
-
const projectDir = workDir || getWorkingDirectory(void 0, true);
|
|
9339
|
-
const index = await loadIssueIndex(projectDir);
|
|
9340
|
-
const issue = index.find((i) => i.id === issueId);
|
|
9341
|
-
if (!issue) return false;
|
|
9342
|
-
issue.resolved = true;
|
|
9343
|
-
issue.resolvedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
9344
|
-
await saveIssueIndex(index, projectDir);
|
|
9345
|
-
return true;
|
|
9346
|
-
}
|
|
9347
|
-
async function getMemoryStats(workDir) {
|
|
9348
|
-
const projectDir = workDir || getWorkingDirectory(void 0, true);
|
|
9349
|
-
const index = await loadIssueIndex(projectDir);
|
|
9350
|
-
const historical = await getHistoricalInsights(projectDir);
|
|
9351
|
-
const stats = {
|
|
9352
|
-
totalIssues: index.length,
|
|
9353
|
-
issuesByAgent: {},
|
|
9354
|
-
issuesBySeverity: {},
|
|
9355
|
-
resolvedCount: 0,
|
|
9356
|
-
historicalIssues: historical.totalHistoricalIssues,
|
|
9357
|
-
improvementTrend: historical.improvementTrend
|
|
9358
|
-
};
|
|
9359
|
-
for (const issue of index) {
|
|
9360
|
-
stats.issuesByAgent[issue.agent] = (stats.issuesByAgent[issue.agent] || 0) + 1;
|
|
9361
|
-
stats.issuesBySeverity[issue.severity] = (stats.issuesBySeverity[issue.severity] || 0) + 1;
|
|
9362
|
-
if (issue.resolved) stats.resolvedCount++;
|
|
9363
|
-
}
|
|
9364
|
-
if (index.length > 0) {
|
|
9365
|
-
const sorted = [...index].sort(
|
|
9366
|
-
(a, b) => new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime()
|
|
9367
|
-
);
|
|
9368
|
-
const oldest = sorted[0]?.timestamp;
|
|
9369
|
-
const newest = sorted[sorted.length - 1]?.timestamp;
|
|
9370
|
-
if (oldest !== void 0) {
|
|
9371
|
-
stats.oldestIssue = oldest;
|
|
9165
|
+
if (!primaryPath && writtenTo.length > 0) {
|
|
9166
|
+
const firstTool = writtenTo[0];
|
|
9167
|
+
if (firstTool) {
|
|
9168
|
+
primaryPath = join3(GLOBAL_SKILLS_DIRS[firstTool], name);
|
|
9372
9169
|
}
|
|
9373
|
-
if (newest !== void 0) {
|
|
9374
|
-
stats.newestIssue = newest;
|
|
9375
|
-
}
|
|
9376
|
-
}
|
|
9377
|
-
return stats;
|
|
9378
|
-
}
|
|
9379
|
-
async function getRecentIssues(options = {}) {
|
|
9380
|
-
const projectDir = options.workDir || getWorkingDirectory(void 0, true);
|
|
9381
|
-
const index = await loadIssueIndex(projectDir);
|
|
9382
|
-
const limit = options.limit || 20;
|
|
9383
|
-
const daysBack = options.daysBack || 7;
|
|
9384
|
-
const cutoff = /* @__PURE__ */ new Date();
|
|
9385
|
-
cutoff.setDate(cutoff.getDate() - daysBack);
|
|
9386
|
-
return index.filter((i) => new Date(i.timestamp) >= cutoff).sort((a, b) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime()).slice(0, limit);
|
|
9387
|
-
}
|
|
9388
|
-
async function getDailyLogs(workDir) {
|
|
9389
|
-
const projectDir = workDir || getWorkingDirectory(void 0, true);
|
|
9390
|
-
const memoryDir = join5(projectDir, ".trie", "memory");
|
|
9391
|
-
try {
|
|
9392
|
-
if (!existsSync4(memoryDir)) return [];
|
|
9393
|
-
const files = await readdir2(memoryDir);
|
|
9394
|
-
return files.filter((f) => /^\d{4}-\d{2}-\d{2}\.md$/.test(f)).sort().reverse();
|
|
9395
|
-
} catch {
|
|
9396
|
-
return [];
|
|
9397
9170
|
}
|
|
9171
|
+
return { primaryPath, writtenTo };
|
|
9398
9172
|
}
|
|
9399
|
-
async function
|
|
9400
|
-
const memoryDir = join5(projectDir, ".trie", "memory");
|
|
9401
|
-
const today = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
9402
|
-
const logPath = join5(memoryDir, `${today}.md`);
|
|
9403
|
-
let content = "";
|
|
9173
|
+
async function createSkillFromFile(filePath, skillName, description) {
|
|
9404
9174
|
try {
|
|
9405
|
-
|
|
9406
|
-
|
|
9407
|
-
|
|
9408
|
-
|
|
9175
|
+
const stats = await stat(filePath);
|
|
9176
|
+
if (!stats.isFile()) {
|
|
9177
|
+
return { success: false, name: "unknown", error: "Path is not a file" };
|
|
9178
|
+
}
|
|
9179
|
+
const content = await readFile3(filePath, "utf-8");
|
|
9180
|
+
if (!content.trim()) {
|
|
9181
|
+
return { success: false, name: "unknown", error: "File is empty" };
|
|
9182
|
+
}
|
|
9183
|
+
const ext = extname2(filePath);
|
|
9184
|
+
const baseFileName = basename2(filePath, ext);
|
|
9185
|
+
const name = skillName || baseFileName.toLowerCase().replace(/[^a-z0-9-]/g, "-").replace(/-+/g, "-");
|
|
9186
|
+
const primarySkillPath = join3(GLOBAL_SKILLS_DIR, name);
|
|
9187
|
+
if (existsSync3(primarySkillPath)) {
|
|
9188
|
+
return { success: false, name, error: `Skill "${name}" already exists. Use a different name or remove the existing skill.` };
|
|
9189
|
+
}
|
|
9190
|
+
const skillDescription = description || `Custom skill created from ${basename2(filePath)}`;
|
|
9191
|
+
const skillMdContent = `---
|
|
9192
|
+
name: ${name}
|
|
9193
|
+
description: ${skillDescription}
|
|
9194
|
+
author: custom
|
|
9195
|
+
tags: [custom, local]
|
|
9196
|
+
---
|
|
9409
9197
|
|
|
9410
|
-
|
|
9411
|
-
}
|
|
9412
|
-
} catch {
|
|
9413
|
-
content = `# Issue Log: ${today}
|
|
9198
|
+
# ${name}
|
|
9414
9199
|
|
|
9200
|
+
${content}
|
|
9415
9201
|
`;
|
|
9416
|
-
|
|
9417
|
-
|
|
9418
|
-
|
|
9419
|
-
|
|
9420
|
-
|
|
9421
|
-
|
|
9422
|
-
|
|
9423
|
-
|
|
9424
|
-
|
|
9425
|
-
content += newEntries + "\n";
|
|
9426
|
-
await writeFile4(logPath, content);
|
|
9427
|
-
}
|
|
9428
|
-
async function loadIssueIndex(projectDir) {
|
|
9429
|
-
const indexPath = join5(projectDir, ".trie", "memory", "issues.json");
|
|
9430
|
-
try {
|
|
9431
|
-
if (existsSync4(indexPath)) {
|
|
9432
|
-
const content = await readFile5(indexPath, "utf-8");
|
|
9433
|
-
return JSON.parse(content);
|
|
9434
|
-
}
|
|
9435
|
-
} catch {
|
|
9436
|
-
}
|
|
9437
|
-
return [];
|
|
9438
|
-
}
|
|
9439
|
-
async function updateIssueIndex(newIssues, projectDir) {
|
|
9440
|
-
const memoryDir = join5(projectDir, ".trie", "memory");
|
|
9441
|
-
await mkdir4(memoryDir, { recursive: true });
|
|
9442
|
-
let existing = await loadIssueIndex(projectDir);
|
|
9443
|
-
const hashSet = new Set(existing.map((i) => i.hash));
|
|
9444
|
-
const toAdd = newIssues.filter((i) => !hashSet.has(i.hash));
|
|
9445
|
-
existing = [...existing, ...toAdd];
|
|
9446
|
-
if (existing.length > 500) {
|
|
9447
|
-
const { summary, remaining } = await compactOldIssues(existing, {
|
|
9448
|
-
keepDays: 30,
|
|
9449
|
-
minIssuesToCompact: 100
|
|
9450
|
-
});
|
|
9451
|
-
if (summary) {
|
|
9452
|
-
await saveCompactedSummary(summary, projectDir);
|
|
9453
|
-
existing = remaining;
|
|
9454
|
-
}
|
|
9455
|
-
}
|
|
9456
|
-
if (existing.length > 1e3) {
|
|
9457
|
-
existing = existing.sort((a, b) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime()).slice(0, 1e3);
|
|
9458
|
-
}
|
|
9459
|
-
await saveIssueIndex(existing, projectDir);
|
|
9460
|
-
}
|
|
9461
|
-
async function saveIssueIndex(issues, projectDir) {
|
|
9462
|
-
const indexPath = join5(projectDir, ".trie", "memory", "issues.json");
|
|
9463
|
-
await writeFile4(indexPath, JSON.stringify(issues, null, 2));
|
|
9464
|
-
}
|
|
9465
|
-
function hashIssue(issue) {
|
|
9466
|
-
const content = `${issue.issue}|${issue.file}|${issue.severity}|${issue.agent}`;
|
|
9467
|
-
let hash = 0;
|
|
9468
|
-
for (let i = 0; i < content.length; i++) {
|
|
9469
|
-
const char = content.charCodeAt(i);
|
|
9470
|
-
hash = (hash << 5) - hash + char;
|
|
9471
|
-
hash = hash & hash;
|
|
9472
|
-
}
|
|
9473
|
-
return Math.abs(hash).toString(36);
|
|
9474
|
-
}
|
|
9475
|
-
|
|
9476
|
-
// src/memory/global-memory.ts
|
|
9477
|
-
import { mkdir as mkdir5, writeFile as writeFile5, readFile as readFile6, readdir as readdir3 } from "fs/promises";
|
|
9478
|
-
import { existsSync as existsSync5 } from "fs";
|
|
9479
|
-
import { join as join6 } from "path";
|
|
9480
|
-
import { homedir } from "os";
|
|
9481
|
-
var GLOBAL_TRIE_DIR = join6(homedir(), ".trie");
|
|
9482
|
-
var GLOBAL_MEMORY_DIR = join6(GLOBAL_TRIE_DIR, "memory");
|
|
9483
|
-
async function recordToGlobalMemory(issues, projectName, projectPath, healthScore = 0) {
|
|
9484
|
-
await mkdir5(GLOBAL_MEMORY_DIR, { recursive: true });
|
|
9485
|
-
await mkdir5(join6(GLOBAL_MEMORY_DIR, "projects"), { recursive: true });
|
|
9486
|
-
const patterns = await loadGlobalPatterns();
|
|
9487
|
-
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
9488
|
-
for (const issue of issues) {
|
|
9489
|
-
const patternId = extractPatternId(issue);
|
|
9490
|
-
const existing = patterns.find((p) => p.id === patternId);
|
|
9491
|
-
if (existing) {
|
|
9492
|
-
existing.occurrences++;
|
|
9493
|
-
existing.lastSeen = now;
|
|
9494
|
-
if (!existing.projects.includes(projectName)) {
|
|
9495
|
-
existing.projects.push(projectName);
|
|
9496
|
-
}
|
|
9497
|
-
} else {
|
|
9498
|
-
patterns.push({
|
|
9499
|
-
id: patternId,
|
|
9500
|
-
pattern: issue.issue.slice(0, 200),
|
|
9501
|
-
description: issue.fix.slice(0, 200),
|
|
9502
|
-
severity: issue.severity,
|
|
9503
|
-
agent: issue.agent,
|
|
9504
|
-
occurrences: 1,
|
|
9505
|
-
projects: [projectName],
|
|
9506
|
-
firstSeen: now,
|
|
9507
|
-
lastSeen: now
|
|
9508
|
-
});
|
|
9202
|
+
const metadata = {
|
|
9203
|
+
installedFrom: `file://${filePath}`,
|
|
9204
|
+
installedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
9205
|
+
type: "custom",
|
|
9206
|
+
originalFile: basename2(filePath)
|
|
9207
|
+
};
|
|
9208
|
+
const { primaryPath, writtenTo } = await writeSkillToAllDirs(name, skillMdContent, metadata);
|
|
9209
|
+
if (writtenTo.length === 0) {
|
|
9210
|
+
return { success: false, name, error: "Failed to write skill to any directory" };
|
|
9509
9211
|
}
|
|
9212
|
+
return {
|
|
9213
|
+
success: true,
|
|
9214
|
+
name,
|
|
9215
|
+
path: primaryPath,
|
|
9216
|
+
writtenTo
|
|
9217
|
+
};
|
|
9218
|
+
} catch (error) {
|
|
9219
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
9220
|
+
return { success: false, name: skillName || "unknown", error: message };
|
|
9510
9221
|
}
|
|
9511
|
-
await saveGlobalPatterns(patterns);
|
|
9512
|
-
const summaryPath = join6(GLOBAL_MEMORY_DIR, "projects", `${sanitizeName(projectName)}.json`);
|
|
9513
|
-
const summary = {
|
|
9514
|
-
name: projectName,
|
|
9515
|
-
path: projectPath,
|
|
9516
|
-
lastScan: now,
|
|
9517
|
-
healthScore,
|
|
9518
|
-
totalIssues: issues.length,
|
|
9519
|
-
patterns: [...new Set(issues.map((i) => extractPatternId(i)))]
|
|
9520
|
-
};
|
|
9521
|
-
await writeFile5(summaryPath, JSON.stringify(summary, null, 2));
|
|
9522
|
-
}
|
|
9523
|
-
async function findCrossProjectPatterns(minOccurrences = 2) {
|
|
9524
|
-
const patterns = await loadGlobalPatterns();
|
|
9525
|
-
return patterns.filter((p) => p.projects.length >= minOccurrences).sort((a, b) => b.occurrences - a.occurrences);
|
|
9526
9222
|
}
|
|
9527
|
-
async function
|
|
9528
|
-
|
|
9529
|
-
|
|
9530
|
-
|
|
9531
|
-
|
|
9532
|
-
|
|
9533
|
-
|
|
9534
|
-
|
|
9535
|
-
try {
|
|
9536
|
-
const content = await readFile6(join6(projectsDir, file), "utf-8");
|
|
9537
|
-
summaries.push(JSON.parse(content));
|
|
9538
|
-
} catch {
|
|
9223
|
+
async function removeGlobalSkill(skillName) {
|
|
9224
|
+
let removed = false;
|
|
9225
|
+
for (const baseDir of getAllGlobalSkillDirs()) {
|
|
9226
|
+
const skillPath = join3(baseDir, skillName);
|
|
9227
|
+
try {
|
|
9228
|
+
if (existsSync3(skillPath)) {
|
|
9229
|
+
await rm(skillPath, { recursive: true });
|
|
9230
|
+
removed = true;
|
|
9539
9231
|
}
|
|
9232
|
+
} catch {
|
|
9540
9233
|
}
|
|
9541
|
-
return summaries.sort(
|
|
9542
|
-
(a, b) => new Date(b.lastScan).getTime() - new Date(a.lastScan).getTime()
|
|
9543
|
-
);
|
|
9544
|
-
} catch {
|
|
9545
|
-
return [];
|
|
9546
|
-
}
|
|
9547
|
-
}
|
|
9548
|
-
async function getGlobalMemoryStats() {
|
|
9549
|
-
const patterns = await loadGlobalPatterns();
|
|
9550
|
-
const projects = await listTrackedProjects();
|
|
9551
|
-
const patternsByAgent = {};
|
|
9552
|
-
for (const pattern of patterns) {
|
|
9553
|
-
patternsByAgent[pattern.agent] = (patternsByAgent[pattern.agent] || 0) + 1;
|
|
9554
|
-
}
|
|
9555
|
-
return {
|
|
9556
|
-
totalPatterns: patterns.length,
|
|
9557
|
-
crossProjectPatterns: patterns.filter((p) => p.projects.length >= 2).length,
|
|
9558
|
-
trackedProjects: projects.length,
|
|
9559
|
-
totalOccurrences: patterns.reduce((sum, p) => sum + p.occurrences, 0),
|
|
9560
|
-
fixedPatterns: patterns.filter((p) => p.fixApplied).length,
|
|
9561
|
-
patternsByAgent
|
|
9562
|
-
};
|
|
9563
|
-
}
|
|
9564
|
-
async function updateGlobalMemoryMd() {
|
|
9565
|
-
const patterns = await loadGlobalPatterns();
|
|
9566
|
-
const crossProject = patterns.filter((p) => p.projects.length >= 2);
|
|
9567
|
-
const projects = await listTrackedProjects();
|
|
9568
|
-
const lines = [
|
|
9569
|
-
"# Global Trie Memory",
|
|
9570
|
-
"",
|
|
9571
|
-
"> Auto-generated file tracking patterns across all your projects.",
|
|
9572
|
-
`> Last updated: ${(/* @__PURE__ */ new Date()).toISOString()}`,
|
|
9573
|
-
"",
|
|
9574
|
-
"## Summary",
|
|
9575
|
-
"",
|
|
9576
|
-
`- **Projects tracked:** ${projects.length}`,
|
|
9577
|
-
`- **Total patterns:** ${patterns.length}`,
|
|
9578
|
-
`- **Cross-project patterns:** ${crossProject.length}`,
|
|
9579
|
-
"",
|
|
9580
|
-
"## Cross-Project Patterns",
|
|
9581
|
-
"",
|
|
9582
|
-
"These issues appear in multiple projects:",
|
|
9583
|
-
""
|
|
9584
|
-
];
|
|
9585
|
-
for (const p of crossProject.slice(0, 20)) {
|
|
9586
|
-
lines.push(
|
|
9587
|
-
`### ${p.pattern.slice(0, 60)}${p.pattern.length > 60 ? "..." : ""}`,
|
|
9588
|
-
"",
|
|
9589
|
-
`- **Severity:** ${p.severity}`,
|
|
9590
|
-
`- **Agent:** ${p.agent}`,
|
|
9591
|
-
`- **Occurrences:** ${p.occurrences} across ${p.projects.length} projects`,
|
|
9592
|
-
`- **Projects:** ${p.projects.slice(0, 5).join(", ")}${p.projects.length > 5 ? "..." : ""}`
|
|
9593
|
-
);
|
|
9594
|
-
if (p.fixApplied) {
|
|
9595
|
-
lines.push(`- **Fixed in:** ${p.fixApplied.project} on ${p.fixApplied.timestamp.split("T")[0]}`);
|
|
9596
|
-
} else {
|
|
9597
|
-
lines.push("- **Status:** Not fixed");
|
|
9598
|
-
}
|
|
9599
|
-
lines.push("");
|
|
9600
|
-
}
|
|
9601
|
-
lines.push(
|
|
9602
|
-
"## Tracked Projects",
|
|
9603
|
-
"",
|
|
9604
|
-
"| Project | Last Scan | Health | Issues |",
|
|
9605
|
-
"|---------|-----------|--------|--------|"
|
|
9606
|
-
);
|
|
9607
|
-
for (const p of projects.slice(0, 20)) {
|
|
9608
|
-
lines.push(`| ${p.name} | ${p.lastScan.split("T")[0]} | ${p.healthScore}% | ${p.totalIssues} |`);
|
|
9609
|
-
}
|
|
9610
|
-
lines.push("", "---", "", "*This file is auto-generated by Trie. Do not edit manually.*");
|
|
9611
|
-
await mkdir5(GLOBAL_MEMORY_DIR, { recursive: true });
|
|
9612
|
-
await writeFile5(join6(GLOBAL_MEMORY_DIR, "GLOBAL_MEMORY.md"), lines.join("\n"));
|
|
9613
|
-
}
|
|
9614
|
-
async function searchGlobalPatterns(query, options = {}) {
|
|
9615
|
-
const patterns = await loadGlobalPatterns();
|
|
9616
|
-
const limit = options.limit || 10;
|
|
9617
|
-
const queryTerms = query.toLowerCase().split(/\s+/).filter((t) => t.length > 2);
|
|
9618
|
-
const scored = patterns.filter((p) => {
|
|
9619
|
-
if (options.severity && !options.severity.includes(p.severity)) return false;
|
|
9620
|
-
if (options.agent && p.agent !== options.agent) return false;
|
|
9621
|
-
return true;
|
|
9622
|
-
}).map((p) => {
|
|
9623
|
-
const text = `${p.pattern} ${p.description} ${p.agent}`.toLowerCase();
|
|
9624
|
-
let score = 0;
|
|
9625
|
-
for (const term of queryTerms) {
|
|
9626
|
-
if (text.includes(term)) score++;
|
|
9627
|
-
}
|
|
9628
|
-
return { pattern: p, score };
|
|
9629
|
-
}).filter((s) => s.score > 0).sort((a, b) => b.score - a.score).slice(0, limit);
|
|
9630
|
-
return scored.map((s) => s.pattern);
|
|
9631
|
-
}
|
|
9632
|
-
async function loadGlobalPatterns() {
|
|
9633
|
-
const patternsPath = join6(GLOBAL_MEMORY_DIR, "global-patterns.json");
|
|
9634
|
-
try {
|
|
9635
|
-
if (existsSync5(patternsPath)) {
|
|
9636
|
-
const content = await readFile6(patternsPath, "utf-8");
|
|
9637
|
-
return JSON.parse(content);
|
|
9638
|
-
}
|
|
9639
|
-
} catch {
|
|
9640
9234
|
}
|
|
9641
|
-
return
|
|
9642
|
-
}
|
|
9643
|
-
async function saveGlobalPatterns(patterns) {
|
|
9644
|
-
await mkdir5(GLOBAL_MEMORY_DIR, { recursive: true });
|
|
9645
|
-
const patternsPath = join6(GLOBAL_MEMORY_DIR, "global-patterns.json");
|
|
9646
|
-
const pruned = patterns.sort((a, b) => new Date(b.lastSeen).getTime() - new Date(a.lastSeen).getTime()).slice(0, 500);
|
|
9647
|
-
await writeFile5(patternsPath, JSON.stringify(pruned, null, 2));
|
|
9648
|
-
}
|
|
9649
|
-
function extractPatternId(issue) {
|
|
9650
|
-
const normalized = issue.issue.toLowerCase().replace(/`[^`]+`/g, "CODE").replace(/\b\d+\b/g, "N").replace(/['"]/g, "").slice(0, 100);
|
|
9651
|
-
let hash = 0;
|
|
9652
|
-
for (let i = 0; i < normalized.length; i++) {
|
|
9653
|
-
const char = normalized.charCodeAt(i);
|
|
9654
|
-
hash = (hash << 5) - hash + char;
|
|
9655
|
-
hash = hash & hash;
|
|
9656
|
-
}
|
|
9657
|
-
return `${issue.agent}-${issue.severity}-${Math.abs(hash).toString(36)}`;
|
|
9658
|
-
}
|
|
9659
|
-
function sanitizeName(name) {
|
|
9660
|
-
return name.replace(/[^a-zA-Z0-9-_]/g, "-").toLowerCase();
|
|
9235
|
+
return removed;
|
|
9661
9236
|
}
|
|
9662
9237
|
|
|
9663
9238
|
// src/utils/context-state.ts
|
|
9239
|
+
import { readFile as readFile4, mkdir as mkdir3 } from "fs/promises";
|
|
9240
|
+
import { existsSync as existsSync4 } from "fs";
|
|
9241
|
+
import { join as join4, basename as basename3 } from "path";
|
|
9664
9242
|
var AGENTS_MD_PATH = ".trie/AGENTS.md";
|
|
9665
9243
|
var STATE_JSON_PATH = ".trie/state.json";
|
|
9666
9244
|
async function loadContextState() {
|
|
9667
9245
|
const workDir = getWorkingDirectory(void 0, true);
|
|
9668
|
-
const statePath =
|
|
9246
|
+
const statePath = join4(workDir, STATE_JSON_PATH);
|
|
9669
9247
|
const defaults = getDefaultState();
|
|
9670
9248
|
try {
|
|
9671
|
-
if (
|
|
9672
|
-
const content = await
|
|
9249
|
+
if (existsSync4(statePath)) {
|
|
9250
|
+
const content = await readFile4(statePath, "utf-8");
|
|
9673
9251
|
const loaded = JSON.parse(content);
|
|
9674
9252
|
return {
|
|
9675
9253
|
...defaults,
|
|
@@ -9683,10 +9261,10 @@ async function loadContextState() {
|
|
|
9683
9261
|
}
|
|
9684
9262
|
async function saveContextState(state) {
|
|
9685
9263
|
const workDir = getWorkingDirectory(void 0, true);
|
|
9686
|
-
const trieDir =
|
|
9687
|
-
const statePath =
|
|
9688
|
-
await
|
|
9689
|
-
await
|
|
9264
|
+
const trieDir = join4(workDir, ".trie");
|
|
9265
|
+
const statePath = join4(workDir, STATE_JSON_PATH);
|
|
9266
|
+
await mkdir3(trieDir, { recursive: true });
|
|
9267
|
+
await atomicWriteJSON(statePath, state);
|
|
9690
9268
|
}
|
|
9691
9269
|
async function updateContextAfterScan(results, filesScanned, contextSignals, duration) {
|
|
9692
9270
|
const state = await loadContextState();
|
|
@@ -9733,7 +9311,7 @@ async function updateContextAfterScan(results, filesScanned, contextSignals, dur
|
|
|
9733
9311
|
await saveContextState(state);
|
|
9734
9312
|
await updateAgentsMd(state);
|
|
9735
9313
|
if (allIssues.length > 0) {
|
|
9736
|
-
const projectName =
|
|
9314
|
+
const projectName = basename3(workDir);
|
|
9737
9315
|
try {
|
|
9738
9316
|
await storeIssues(allIssues, projectName, workDir);
|
|
9739
9317
|
await recordToGlobalMemory(allIssues, projectName, workDir, state.healthScore);
|
|
@@ -9744,10 +9322,10 @@ async function updateContextAfterScan(results, filesScanned, contextSignals, dur
|
|
|
9744
9322
|
}
|
|
9745
9323
|
async function updateAgentsMd(state) {
|
|
9746
9324
|
const workDir = getWorkingDirectory(void 0, true);
|
|
9747
|
-
const mdPath =
|
|
9325
|
+
const mdPath = join4(workDir, AGENTS_MD_PATH);
|
|
9748
9326
|
let content;
|
|
9749
9327
|
try {
|
|
9750
|
-
content = await
|
|
9328
|
+
content = await readFile4(mdPath, "utf-8");
|
|
9751
9329
|
} catch {
|
|
9752
9330
|
content = getAgentsMdTemplate();
|
|
9753
9331
|
}
|
|
@@ -9762,7 +9340,7 @@ async function updateAgentsMd(state) {
|
|
|
9762
9340
|
/Last updated:.*$/m,
|
|
9763
9341
|
`Last updated: ${(/* @__PURE__ */ new Date()).toISOString()}`
|
|
9764
9342
|
);
|
|
9765
|
-
await
|
|
9343
|
+
await atomicWriteFile(mdPath, content);
|
|
9766
9344
|
}
|
|
9767
9345
|
function updateSection(content, sectionName, newContent) {
|
|
9768
9346
|
const sectionRegex = new RegExp(
|
|
@@ -10079,9 +9657,9 @@ async function getContextForAI() {
|
|
|
10079
9657
|
}
|
|
10080
9658
|
|
|
10081
9659
|
// src/skills/gating.ts
|
|
10082
|
-
import { existsSync as
|
|
10083
|
-
import { readFile as
|
|
10084
|
-
import { join as
|
|
9660
|
+
import { existsSync as existsSync5 } from "fs";
|
|
9661
|
+
import { readFile as readFile5 } from "fs/promises";
|
|
9662
|
+
import { join as join5 } from "path";
|
|
10085
9663
|
import { execSync } from "child_process";
|
|
10086
9664
|
async function checkSkillRequirements(frontmatter, projectDir) {
|
|
10087
9665
|
const result = { allowed: true };
|
|
@@ -10138,7 +9716,7 @@ async function checkSkillRequirements(frontmatter, projectDir) {
|
|
|
10138
9716
|
}
|
|
10139
9717
|
}
|
|
10140
9718
|
if (reqs.configFiles && reqs.configFiles.length > 0) {
|
|
10141
|
-
const missing = reqs.configFiles.filter((file) => !
|
|
9719
|
+
const missing = reqs.configFiles.filter((file) => !existsSync5(join5(projectDir, file)));
|
|
10142
9720
|
if (missing.length > 0) {
|
|
10143
9721
|
result.allowed = false;
|
|
10144
9722
|
result.missingConfigs = missing;
|
|
@@ -10150,11 +9728,11 @@ async function checkSkillRequirements(frontmatter, projectDir) {
|
|
|
10150
9728
|
}
|
|
10151
9729
|
async function getProjectDependencies(projectDir) {
|
|
10152
9730
|
try {
|
|
10153
|
-
const pkgPath =
|
|
10154
|
-
if (!
|
|
9731
|
+
const pkgPath = join5(projectDir, "package.json");
|
|
9732
|
+
if (!existsSync5(pkgPath)) {
|
|
10155
9733
|
return /* @__PURE__ */ new Set();
|
|
10156
9734
|
}
|
|
10157
|
-
const pkg = JSON.parse(await
|
|
9735
|
+
const pkg = JSON.parse(await readFile5(pkgPath, "utf-8"));
|
|
10158
9736
|
return /* @__PURE__ */ new Set([
|
|
10159
9737
|
...Object.keys(pkg.dependencies || {}),
|
|
10160
9738
|
...Object.keys(pkg.devDependencies || {})
|
|
@@ -10620,8 +10198,8 @@ var CustomSkill = class extends BaseSkill {
|
|
|
10620
10198
|
};
|
|
10621
10199
|
|
|
10622
10200
|
// src/skills/built-in/registry.ts
|
|
10623
|
-
import { readdir as
|
|
10624
|
-
import { join as
|
|
10201
|
+
import { readdir as readdir2, readFile as readFile6 } from "fs/promises";
|
|
10202
|
+
import { join as join6 } from "path";
|
|
10625
10203
|
var SkillRegistryImpl = class {
|
|
10626
10204
|
skills = /* @__PURE__ */ new Map();
|
|
10627
10205
|
customSkillsLoaded = false;
|
|
@@ -10677,14 +10255,14 @@ var SkillRegistryImpl = class {
|
|
|
10677
10255
|
async loadCustomSkills() {
|
|
10678
10256
|
if (this.customSkillsLoaded) return;
|
|
10679
10257
|
try {
|
|
10680
|
-
const skillsDir =
|
|
10681
|
-
const files = await
|
|
10258
|
+
const skillsDir = join6(getWorkingDirectory(void 0, true), ".trie", "agents");
|
|
10259
|
+
const files = await readdir2(skillsDir);
|
|
10682
10260
|
const jsonFiles = files.filter((f) => f.endsWith(".json"));
|
|
10683
10261
|
let loadedCount = 0;
|
|
10684
10262
|
for (const file of jsonFiles) {
|
|
10685
10263
|
try {
|
|
10686
|
-
const configPath =
|
|
10687
|
-
const content = await
|
|
10264
|
+
const configPath = join6(skillsDir, file);
|
|
10265
|
+
const content = await readFile6(configPath, "utf-8");
|
|
10688
10266
|
const config = JSON.parse(content);
|
|
10689
10267
|
const skill = new CustomSkill(config);
|
|
10690
10268
|
this.skills.set(skill.name, skill);
|
|
@@ -10855,6 +10433,9 @@ export {
|
|
|
10855
10433
|
installSkill,
|
|
10856
10434
|
listInstalledSkills,
|
|
10857
10435
|
removeSkill,
|
|
10436
|
+
listGlobalSkills,
|
|
10437
|
+
createSkillFromFile,
|
|
10438
|
+
removeGlobalSkill,
|
|
10858
10439
|
projectInfoExists,
|
|
10859
10440
|
loadProjectInfo,
|
|
10860
10441
|
initProjectInfo,
|
|
@@ -10863,18 +10444,6 @@ export {
|
|
|
10863
10444
|
appendToSection,
|
|
10864
10445
|
getProjectSections,
|
|
10865
10446
|
getProjectInfoStructured,
|
|
10866
|
-
getHistoricalInsights,
|
|
10867
|
-
searchIssues,
|
|
10868
|
-
findSimilarIssues,
|
|
10869
|
-
markIssueResolved,
|
|
10870
|
-
getMemoryStats,
|
|
10871
|
-
getRecentIssues,
|
|
10872
|
-
getDailyLogs,
|
|
10873
|
-
findCrossProjectPatterns,
|
|
10874
|
-
listTrackedProjects,
|
|
10875
|
-
getGlobalMemoryStats,
|
|
10876
|
-
updateGlobalMemoryMd,
|
|
10877
|
-
searchGlobalPatterns,
|
|
10878
10447
|
loadContextState,
|
|
10879
10448
|
updateContextAfterScan,
|
|
10880
10449
|
recordSkillInstalled,
|
|
@@ -10882,4 +10451,4 @@ export {
|
|
|
10882
10451
|
CustomSkill,
|
|
10883
10452
|
getSkillRegistry
|
|
10884
10453
|
};
|
|
10885
|
-
//# sourceMappingURL=chunk-
|
|
10454
|
+
//# sourceMappingURL=chunk-X2PABPBH.js.map
|