@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.
Files changed (56) hide show
  1. package/README.md +591 -52
  2. package/dist/agent-smith-W4HUCFGC.js +14 -0
  3. package/dist/{agent-smith-runner-ZU4R3I2Z.js → agent-smith-runner-QRVOEOBE.js} +13 -7
  4. package/dist/agent-smith-runner-QRVOEOBE.js.map +1 -0
  5. package/dist/chunk-4YSLDGBL.js +674 -0
  6. package/dist/chunk-4YSLDGBL.js.map +1 -0
  7. package/dist/chunk-7KHT2NKR.js +212 -0
  8. package/dist/chunk-7KHT2NKR.js.map +1 -0
  9. package/dist/{chunk-XSPS463E.js → chunk-ALA6733H.js} +492 -14
  10. package/dist/chunk-ALA6733H.js.map +1 -0
  11. package/dist/chunk-AQCAMIQQ.js +139 -0
  12. package/dist/chunk-AQCAMIQQ.js.map +1 -0
  13. package/dist/chunk-D3DMONAJ.js +904 -0
  14. package/dist/chunk-D3DMONAJ.js.map +1 -0
  15. package/dist/{chunk-KB5ZN6K2.js → chunk-GWSNINKX.js} +2 -2
  16. package/dist/{chunk-32WLOG6E.js → chunk-K6BQBKIR.js} +662 -633
  17. package/dist/chunk-K6BQBKIR.js.map +1 -0
  18. package/dist/{chunk-ASGSTVVF.js → chunk-KOFQ47YW.js} +10 -6
  19. package/dist/chunk-KOFQ47YW.js.map +1 -0
  20. package/dist/{chunk-XVGHO2Z5.js → chunk-N2AZH3EQ.js} +7683 -4777
  21. package/dist/chunk-N2AZH3EQ.js.map +1 -0
  22. package/dist/chunk-PBOVCPKE.js +2566 -0
  23. package/dist/chunk-PBOVCPKE.js.map +1 -0
  24. package/dist/{chunk-NUT4G5AY.js → chunk-R7Z7OHTJ.js} +493 -650
  25. package/dist/chunk-R7Z7OHTJ.js.map +1 -0
  26. package/dist/chunk-TSHZQKCM.js +933 -0
  27. package/dist/chunk-TSHZQKCM.js.map +1 -0
  28. package/dist/{chunk-S4VGGLXF.js → chunk-X2PABPBH.js} +461 -892
  29. package/dist/chunk-X2PABPBH.js.map +1 -0
  30. package/dist/cli/create-agent.js +3 -2
  31. package/dist/cli/create-agent.js.map +1 -1
  32. package/dist/cli/main.js +1120 -70
  33. package/dist/cli/main.js.map +1 -1
  34. package/dist/cli/yolo-daemon.js +151 -41
  35. package/dist/cli/yolo-daemon.js.map +1 -1
  36. package/dist/goal-manager-KFBOAP4X.js +20 -0
  37. package/dist/goal-manager-KFBOAP4X.js.map +1 -0
  38. package/dist/guardian-agent-PULK546O.js +17 -0
  39. package/dist/guardian-agent-PULK546O.js.map +1 -0
  40. package/dist/index.js +173 -39
  41. package/dist/index.js.map +1 -1
  42. package/dist/issue-store-QRDF3X55.js +22 -0
  43. package/dist/issue-store-QRDF3X55.js.map +1 -0
  44. package/dist/workers/agent-worker.js +6 -3
  45. package/dist/workers/agent-worker.js.map +1 -1
  46. package/package.json +1 -1
  47. package/dist/agent-smith-57MKX5QC.js +0 -13
  48. package/dist/agent-smith-runner-ZU4R3I2Z.js.map +0 -1
  49. package/dist/chunk-32WLOG6E.js.map +0 -1
  50. package/dist/chunk-ASGSTVVF.js.map +0 -1
  51. package/dist/chunk-NUT4G5AY.js.map +0 -1
  52. package/dist/chunk-S4VGGLXF.js.map +0 -1
  53. package/dist/chunk-XSPS463E.js.map +0 -1
  54. package/dist/chunk-XVGHO2Z5.js.map +0 -1
  55. /package/dist/{agent-smith-57MKX5QC.js.map → agent-smith-W4HUCFGC.js.map} +0 -0
  56. /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-32WLOG6E.js";
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-ASGSTVVF.js";
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: /AKIA[A-Z0-9]{16}/, severity: "critical", issue: "AWS Access Key exposed", fix: "Remove AWS key and rotate immediately. Use environment variables." },
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
- `Review and fix accessibility issues starting with critical problems. Use axe-core or Lighthouse for additional validation.`,
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: 5e3,
7703
- // Critical bugs found in dev - high impact, deep fixes
7704
- serious: 2e3,
7705
- // Serious issues need careful remediation
7706
- moderate: 500,
7707
- // Moderate issues are quicker fixes
7708
- low: 150
7709
- // Low severity - mostly cleanup
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: 12,
7713
- // Critical bugs can cause outages, breaches
7714
- serious: 8,
7715
- // Serious bugs cause significant user impact
7716
- moderate: 4,
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: 2
7741
+ low: 1.5
7719
7742
  // Low bugs accumulate tech debt
7720
7743
  };
7721
7744
  var CATEGORY_MULTIPLIERS = {
7722
- // Security categories - important but realistic
7723
- "security": { multiplier: 6, reason: "Security vulnerabilities require careful remediation" },
7724
- "authentication": { multiplier: 8, reason: "Auth bypass is serious but fixable" },
7725
- "authorization": { multiplier: 6, reason: "Authorization flaws expose sensitive data" },
7726
- "injection": { multiplier: 12, reason: "SQL/Command injection is severe - prioritize fix" },
7727
- "xss": { multiplier: 4, reason: "XSS needs fixing but React mitigates most cases" },
7728
- "secrets": { multiplier: 8, reason: "Exposed secrets require rotation" },
7729
- "cryptography": { multiplier: 4, reason: "Weak crypto should be updated" },
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: 10, reason: "Data loss is serious - ensure backups exist" },
7732
- "data-corruption": { multiplier: 6, reason: "Data corruption requires recovery work" },
7733
- "privacy": { multiplier: 8, reason: "Privacy violations carry compliance risk" },
7734
- // Financial categories
7735
- "payment": { multiplier: 25, reason: "Payment bugs need immediate attention" },
7736
- "billing": { multiplier: 8, reason: "Billing errors erode trust" },
7737
- "financial-calculation": { multiplier: 6, reason: "Incorrect calculations compound over time" },
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: 4, reason: "Crashes hurt user experience" },
7740
- "memory-leak": { multiplier: 3, reason: "Memory leaks cause gradual degradation" },
7741
- "deadlock": { multiplier: 4, reason: "Deadlocks require restarts" },
7742
- "race-condition": { multiplier: 4, reason: "Race conditions are hard to debug" },
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.5, reason: "Accessibility issues should be addressed" },
7745
- "performance": { multiplier: 2, reason: "Performance issues hurt conversion rates" },
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.5, reason: "Logic errors produce incorrect outcomes" },
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.5,
7766
- easy: 2,
7767
- medium: 8,
7768
- hard: 24
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.5, label: "MVP (50 users)" },
7775
- { threshold: 250, multiplier: 1, label: "Early stage (250 users)" },
7776
- // Baseline (default)
7777
- { threshold: 1e3, multiplier: 3, label: "Growing (1K users)" },
7778
- { threshold: 5e3, multiplier: 6, label: "Traction (5K users)" },
7779
- { threshold: 25e3, multiplier: 10, label: "Scale-up (25K users)" },
7780
- { threshold: 1e5, multiplier: 15, label: "Growth (100K users)" },
7781
- { threshold: 5e5, multiplier: 18, label: "Large (500K users)" },
7782
- { threshold: 1e6, multiplier: 20, label: "Enterprise (1M+ users)" }
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 effort = issue.effort || "medium";
7948
- const fixHours = EFFORT_HOURS[effort] || 8;
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
- const BASE_NOW_CAP = 25e3;
7952
- const BASE_PROD_CAP = 25e4;
7953
- const maxNowCost = BASE_NOW_CAP * userScaleMultiplier;
7954
- const maxProdCost = BASE_PROD_CAP * userScaleMultiplier;
7955
- const rawNowCost = baseCost * categoryMultiplier * contextMultiplier + fixCost;
7956
- const totalNowCost = Math.round(Math.min(rawNowCost, maxNowCost));
7957
- const rawProductionCost = baseCost * productionMultiplier * categoryMultiplier * contextMultiplier + perUserCost + // Add per-user costs for production impact
7958
- fixCost * 2;
7959
- const totalProductionCost = Math.round(Math.min(rawProductionCost, maxProdCost));
7960
- const savings = totalProductionCost - totalNowCost;
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, userCount) {
8136
+ generateCostSummary(_issue, nowCost, productionCost, savings, contextFactors, _userCount) {
8020
8137
  const formatCurrency = (n) => n >= 1e3 ? `$${(n / 1e3).toFixed(1)}k` : `$${n}`;
8021
- const formatUsers = (n) => n >= 1e6 ? `${(n / 1e6).toFixed(1)}M` : n >= 1e3 ? `${(n / 1e3).toFixed(0)}K` : `${n}`;
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
- summary += ` (${formatUsers(userCount)} users)`;
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 scaledHighThreshold = 5e4 * (userCount / 1e3);
8028
- const scaledMediumThreshold = 1e4 * (userCount / 1e3);
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: $5k now, $150k in production
8192
- - Serious bugs: $2k now, $40k in production
8193
- - Moderate bugs: $500 now, $5k in production
8194
- - Low bugs: $100 now, $500 in production
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 > 1e5 * scaleMultiplier) riskLevel = "critical";
8254
- else if (totalProductionCost > 25e3 * scaleMultiplier) riskLevel = "high";
8255
- else if (totalProductionCost > 5e3 * scaleMultiplier) riskLevel = "medium";
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
- output().readiness(readinessScore, metRequirements, totalRequirements, status);
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
- async function installSkill(source, skillName) {
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 < 2) {
8771
- return { success: false, name: "unknown", error: "Invalid source format. Use owner/repo or owner/repo/skill-name" };
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 = parts[0];
8774
- const repo = parts[1];
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 parsed = await parseSkillMd(sourcePath);
8787
- const name = parsed.frontmatter.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
- // src/utils/context-state.ts
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
- if (existsSync3(summaryPath)) {
9203
- return JSON.parse(await readFile4(summaryPath, "utf-8"));
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
- // src/memory/issue-store.ts
9254
- async function storeIssues(issues, project, workDir) {
9255
- const projectDir = workDir || getWorkingDirectory(void 0, true);
9256
- const memoryDir = join5(projectDir, ".trie", "memory");
9257
- await mkdir4(memoryDir, { recursive: true });
9258
- const stored = [];
9259
- const now = (/* @__PURE__ */ new Date()).toISOString();
9260
- for (const issue of issues) {
9261
- const hash = hashIssue(issue);
9262
- const storedIssue = {
9263
- id: issue.id,
9264
- hash,
9265
- severity: issue.severity,
9266
- issue: issue.issue,
9267
- fix: issue.fix,
9268
- file: issue.file,
9269
- agent: issue.agent,
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
- const bm25 = new BM25Index();
9304
- const issueMap = /* @__PURE__ */ new Map();
9305
- for (const issue of filteredIssues) {
9306
- const searchText = `${issue.issue} ${issue.fix} ${issue.file} ${issue.agent} ${issue.category || ""} ${issue.severity}`;
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 appendToDailyLog(issues, projectDir) {
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
- if (existsSync4(logPath)) {
9406
- content = await readFile5(logPath, "utf-8");
9407
- } else {
9408
- content = `# Issue Log: ${today}
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
- const time = (/* @__PURE__ */ new Date()).toTimeString().split(" ")[0];
9418
- const newEntries = issues.map(
9419
- (i) => `## [${time}] ${i.severity.toUpperCase()}: ${i.issue.slice(0, 80)}${i.issue.length > 80 ? "..." : ""}
9420
- - **File:** \`${i.file}\`${i.line ? `:${i.line}` : ""}
9421
- - **Agent:** ${i.agent}
9422
- - **Fix:** ${i.fix.slice(0, 200)}${i.fix.length > 200 ? "..." : ""}
9423
- `
9424
- ).join("\n");
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 listTrackedProjects() {
9528
- const projectsDir = join6(GLOBAL_MEMORY_DIR, "projects");
9529
- try {
9530
- if (!existsSync5(projectsDir)) return [];
9531
- const files = await readdir3(projectsDir);
9532
- const summaries = [];
9533
- for (const file of files) {
9534
- if (!file.endsWith(".json")) continue;
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 = join7(workDir, STATE_JSON_PATH);
9246
+ const statePath = join4(workDir, STATE_JSON_PATH);
9669
9247
  const defaults = getDefaultState();
9670
9248
  try {
9671
- if (existsSync6(statePath)) {
9672
- const content = await readFile7(statePath, "utf-8");
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 = join7(workDir, ".trie");
9687
- const statePath = join7(workDir, STATE_JSON_PATH);
9688
- await mkdir6(trieDir, { recursive: true });
9689
- await writeFile6(statePath, JSON.stringify(state, null, 2));
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 = basename2(workDir);
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 = join7(workDir, AGENTS_MD_PATH);
9325
+ const mdPath = join4(workDir, AGENTS_MD_PATH);
9748
9326
  let content;
9749
9327
  try {
9750
- content = await readFile7(mdPath, "utf-8");
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 writeFile6(mdPath, content);
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 existsSync7 } from "fs";
10083
- import { readFile as readFile8 } from "fs/promises";
10084
- import { join as join8 } from "path";
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) => !existsSync7(join8(projectDir, 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 = join8(projectDir, "package.json");
10154
- if (!existsSync7(pkgPath)) {
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 readFile8(pkgPath, "utf-8"));
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 readdir4, readFile as readFile9 } from "fs/promises";
10624
- import { join as join9 } from "path";
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 = join9(getWorkingDirectory(void 0, true), ".trie", "agents");
10681
- const files = await readdir4(skillsDir);
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 = join9(skillsDir, file);
10687
- const content = await readFile9(configPath, "utf-8");
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-S4VGGLXF.js.map
10454
+ //# sourceMappingURL=chunk-X2PABPBH.js.map