@plumpslabs/kuma 2.2.2 → 2.2.5

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 (3) hide show
  1. package/README.md +221 -165
  2. package/dist/index.js +534 -74
  3. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -100,13 +100,14 @@ async function handleSmartGrep(params) {
100
100
  const projectRoot = getProjectRoot();
101
101
  try {
102
102
  const searchPattern = targetFolder ? path.join(targetFolder, "**/*").replace(/\\/g, "/") : "**/*";
103
+ const targetDepth = targetFolder ? targetFolder.split("/").filter(Boolean).length : 0;
104
+ const maxDepth = targetFolder ? Math.min(targetDepth + 5, 20) : 10;
103
105
  let entries = await fg(searchPattern, {
104
106
  cwd: projectRoot,
105
107
  ignore: IGNORE_PATTERNS,
106
108
  onlyFiles: true,
107
109
  absolute: false,
108
- deep: targetFolder ? 3 : 10,
109
- // Batasi kedalaman
110
+ deep: maxDepth,
110
111
  dot: false
111
112
  // Skip dotfiles
112
113
  });
@@ -175,13 +176,14 @@ async function handleSmartGrep(params) {
175
176
  }
176
177
  }
177
178
  function createRegex(query) {
179
+ const normalized = query.replace(/\\\|/g, "|");
178
180
  try {
179
- if (/[.\\+*?[\](){}^$|]/.test(query)) {
180
- return new RegExp(query, "i");
181
+ if (/[.\\+*?[\](){}^$|]/.test(normalized)) {
182
+ return new RegExp(normalized, "i");
181
183
  }
182
184
  } catch {
183
185
  }
184
- return new RegExp(query.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"), "i");
186
+ return new RegExp(normalized.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"), "i");
185
187
  }
186
188
  function formatResults(results, query, filesScanned) {
187
189
  if (results.length === 0) {
@@ -887,10 +889,10 @@ File "${filePath}" restored from backup: "${relBackupPath}".`;
887
889
  }
888
890
  async function handleCommitRollback(filePath, version) {
889
891
  try {
890
- const { execSync: execSync9 } = await import("child_process");
892
+ const { execSync: execSync10 } = await import("child_process");
891
893
  const root = getProjectRoot();
892
894
  if (version === "list") {
893
- const log = execSync9("git log --oneline -20", { cwd: root, encoding: "utf-8" });
895
+ const log = execSync10("git log --oneline -20", { cwd: root, encoding: "utf-8" });
894
896
  const lines = log.trim().split("\n").map((l) => ` ${l}`).join("\n");
895
897
  return [
896
898
  `\u{1F4CB} **Recent Commits:**`,
@@ -900,7 +902,7 @@ async function handleCommitRollback(filePath, version) {
900
902
  `\u{1F4A1} Use { action: "rollback", scope: "commit", version: <N> } to restore a specific commit.`
901
903
  ].join("\n");
902
904
  }
903
- const status = execSync9("git status --porcelain", { cwd: root, encoding: "utf-8" }).trim();
905
+ const status = execSync10("git status --porcelain", { cwd: root, encoding: "utf-8" }).trim();
904
906
  if (status) {
905
907
  return [
906
908
  `\u26A0\uFE0F **Uncommitted changes detected.**`,
@@ -921,10 +923,10 @@ async function handleCommitRollback(filePath, version) {
921
923
  target = "HEAD~1";
922
924
  }
923
925
  if (filePath) {
924
- execSync9(`git checkout ${target} -- "${filePath}"`, { cwd: root, encoding: "utf-8" });
926
+ execSync10(`git checkout ${target} -- "${filePath}"`, { cwd: root, encoding: "utf-8" });
925
927
  return `\u2705 **Commit Rollback** \u2014 File "${filePath}" restored from ${target}.`;
926
928
  } else {
927
- execSync9(`git checkout ${target}`, { cwd: root, encoding: "utf-8" });
929
+ execSync10(`git checkout ${target}`, { cwd: root, encoding: "utf-8" });
928
930
  return `\u2705 **Commit Rollback** \u2014 Project restored to ${target}.`;
929
931
  }
930
932
  } catch (err) {
@@ -1214,8 +1216,10 @@ async function detectConventions(forceRescan = false) {
1214
1216
  }
1215
1217
  const projectRoot = getProjectRoot();
1216
1218
  const workspaces = detectWorkspaces(projectRoot);
1219
+ const lang = detectLanguage(projectRoot);
1220
+ const framework = lang !== "typescript" && lang !== "javascript" ? detectMultiLanguageFramework(projectRoot, lang) : detectFramework(projectRoot);
1217
1221
  const conventions = {
1218
- framework: detectFramework(projectRoot),
1222
+ framework,
1219
1223
  projectType: detectProjectType(projectRoot),
1220
1224
  testRunner: detectTestRunner(projectRoot),
1221
1225
  styling: detectStyling(projectRoot),
@@ -1228,6 +1232,9 @@ async function detectConventions(forceRescan = false) {
1228
1232
  isMonorepo: workspaces.length > 0,
1229
1233
  workspaces
1230
1234
  };
1235
+ if (lang !== "typescript" && lang !== "javascript") {
1236
+ conventions.moduleSystem = "other";
1237
+ }
1231
1238
  cachedConventions = conventions;
1232
1239
  return conventions;
1233
1240
  }
@@ -1504,13 +1511,169 @@ function detectModuleSystem(root) {
1504
1511
  return "cjs";
1505
1512
  }
1506
1513
  function detectLanguage(root) {
1514
+ if (fs5.existsSync(path5.join(root, "go.mod"))) return "go";
1515
+ if (fs5.existsSync(path5.join(root, "Cargo.toml"))) return "rust";
1516
+ if (fs5.existsSync(path5.join(root, "composer.json"))) return "php";
1517
+ if (fs5.existsSync(path5.join(root, "pyproject.toml")) || fs5.existsSync(path5.join(root, "requirements.txt")) || fs5.existsSync(path5.join(root, "setup.py"))) return "python";
1518
+ if (fs5.existsSync(path5.join(root, "Gemfile"))) return "ruby";
1519
+ if (fs5.existsSync(path5.join(root, "pom.xml"))) return "java";
1520
+ if (fs5.existsSync(path5.join(root, "build.gradle")) || fs5.existsSync(path5.join(root, "build.gradle.kts"))) return "kotlin";
1521
+ if (fs5.existsSync(path5.join(root, "Package.swift"))) return "swift";
1522
+ try {
1523
+ const entries = fs5.readdirSync(root);
1524
+ if (entries.some((e) => e.endsWith(".csproj"))) return "csharp";
1525
+ } catch {
1526
+ }
1507
1527
  if (fs5.existsSync(path5.join(root, "tsconfig.json"))) return "typescript";
1508
1528
  const srcDir = path5.join(root, "src");
1509
1529
  if (fs5.existsSync(srcDir)) {
1510
1530
  const tsFiles = findFiles(srcDir, [".ts", ".tsx"]);
1511
1531
  if (tsFiles.length > 0) return "typescript";
1532
+ const jsFiles = findFiles(srcDir, [".js", ".jsx"]);
1533
+ if (jsFiles.length > 0) return "javascript";
1534
+ }
1535
+ try {
1536
+ const tsFiles = findFiles(root, [".ts", ".tsx"]);
1537
+ if (tsFiles.length > 0) return "typescript";
1538
+ const jsFiles = findFiles(root, [".js", ".jsx"]);
1539
+ if (jsFiles.length > 0) return "javascript";
1540
+ } catch {
1541
+ }
1542
+ return "typescript";
1543
+ }
1544
+ function detectMultiLanguageFramework(root, language) {
1545
+ switch (language) {
1546
+ case "go": {
1547
+ try {
1548
+ const goMod = fs5.readFileSync(path5.join(root, "go.mod"), "utf-8");
1549
+ if (goMod.includes("github.com/gin-gonic/gin")) return "Gin";
1550
+ if (goMod.includes("github.com/labstack/echo")) return "Echo";
1551
+ if (goMod.includes("github.com/gofiber/fiber")) return "Fiber";
1552
+ if (goMod.includes("github.com/go-chi/chi")) return "Chi";
1553
+ if (goMod.includes("github.com/gorilla/mux")) return "Gorilla Mux";
1554
+ if (goMod.includes("github.com/urfave/cli")) return "CLI (urfave/cli)";
1555
+ if (goMod.includes("github.com/spf13/cobra")) return "Cobra CLI";
1556
+ } catch {
1557
+ }
1558
+ return "Go";
1559
+ }
1560
+ case "php": {
1561
+ try {
1562
+ const composer = JSON.parse(fs5.readFileSync(path5.join(root, "composer.json"), "utf-8"));
1563
+ const req = composer.require ?? {};
1564
+ const reqDev = composer["require-dev"] ?? {};
1565
+ const deps = { ...req, ...reqDev };
1566
+ if (deps["laravel/framework"]) return "Laravel";
1567
+ if (deps["symfony/symfony"] || deps["symfony/framework-bundle"]) return "Symfony";
1568
+ if (deps["codeigniter4/framework"]) return "CodeIgniter";
1569
+ if (deps["yiisoft/yii2"]) return "Yii2";
1570
+ if (deps["cakephp/cakephp"]) return "CakePHP";
1571
+ if (deps["phalcon/incubator"]) return "Phalcon";
1572
+ if (deps["wp-coding-standards/wpcs"]) return "WordPress";
1573
+ } catch {
1574
+ }
1575
+ return "PHP";
1576
+ }
1577
+ case "python": {
1578
+ try {
1579
+ if (fs5.existsSync(path5.join(root, "pyproject.toml"))) {
1580
+ const content = fs5.readFileSync(path5.join(root, "pyproject.toml"), "utf-8");
1581
+ if (content.includes("django")) return "Django";
1582
+ if (content.includes("flask")) return "Flask";
1583
+ if (content.includes("fastapi")) return "FastAPI";
1584
+ }
1585
+ if (fs5.existsSync(path5.join(root, "requirements.txt"))) {
1586
+ const content = fs5.readFileSync(path5.join(root, "requirements.txt"), "utf-8");
1587
+ if (content.includes("django")) return "Django";
1588
+ if (content.includes("flask")) return "Flask";
1589
+ if (content.includes("fastapi")) return "FastAPI";
1590
+ if (content.includes("tornado")) return "Tornado";
1591
+ if (content.includes("aiohttp")) return "aiohttp";
1592
+ }
1593
+ } catch {
1594
+ }
1595
+ return "Python";
1596
+ }
1597
+ case "rust": {
1598
+ try {
1599
+ const cargo = fs5.readFileSync(path5.join(root, "Cargo.toml"), "utf-8");
1600
+ if (cargo.includes("axum")) return "Axum";
1601
+ if (cargo.includes("actix-web")) return "Actix Web";
1602
+ if (cargo.includes("rocket")) return "Rocket";
1603
+ if (cargo.includes("warp")) return "Warp";
1604
+ if (cargo.includes("tide")) return "Tide";
1605
+ if (cargo.includes("poem")) return "Poem";
1606
+ if (cargo.includes("clap")) return "Clap CLI";
1607
+ } catch {
1608
+ }
1609
+ return "Rust";
1610
+ }
1611
+ case "java": {
1612
+ try {
1613
+ const content = fs5.readFileSync(path5.join(root, "pom.xml"), "utf-8");
1614
+ if (content.includes("spring-boot")) return "Spring Boot";
1615
+ if (content.includes("quarkus")) return "Quarkus";
1616
+ if (content.includes("micronaut")) return "Micronaut";
1617
+ if (content.includes("jakarta")) return "Jakarta EE";
1618
+ if (content.includes("helidon")) return "Helidon";
1619
+ } catch {
1620
+ try {
1621
+ const gradle = fs5.readFileSync(path5.join(root, "build.gradle"), "utf-8");
1622
+ if (gradle.includes("spring")) return "Spring Boot";
1623
+ if (gradle.includes("quarkus")) return "Quarkus";
1624
+ } catch {
1625
+ }
1626
+ }
1627
+ return "Java";
1628
+ }
1629
+ case "kotlin": {
1630
+ try {
1631
+ const gradle = fs5.readFileSync(path5.join(root, "build.gradle.kts"), "utf-8");
1632
+ if (gradle.includes("spring")) return "Spring Boot (Kotlin)";
1633
+ if (gradle.includes("ktor")) return "Ktor";
1634
+ } catch {
1635
+ }
1636
+ return "Kotlin";
1637
+ }
1638
+ case "ruby": {
1639
+ try {
1640
+ const gemfile = fs5.readFileSync(path5.join(root, "Gemfile"), "utf-8");
1641
+ if (gemfile.includes("rails")) return "Ruby on Rails";
1642
+ if (gemfile.includes("sinatra")) return "Sinatra";
1643
+ if (gemfile.includes("hanami")) return "Hanami";
1644
+ if (gemfile.includes("grape")) return "Grape API";
1645
+ } catch {
1646
+ }
1647
+ return "Ruby";
1648
+ }
1649
+ case "csharp": {
1650
+ try {
1651
+ const entries = fs5.readdirSync(root);
1652
+ const csproj = entries.find((e) => e.endsWith(".csproj"));
1653
+ if (csproj) {
1654
+ const content = fs5.readFileSync(path5.join(root, csproj), "utf-8");
1655
+ if (content.includes("Microsoft.AspNetCore")) return "ASP.NET Core";
1656
+ if (content.includes("Microsoft.Maui")) return ".NET MAUI";
1657
+ if (content.includes("Serilog")) return "Serilog";
1658
+ }
1659
+ } catch {
1660
+ }
1661
+ return "C#";
1662
+ }
1663
+ case "swift": {
1664
+ try {
1665
+ const content = fs5.readFileSync(path5.join(root, "Package.swift"), "utf-8");
1666
+ if (content.includes("vapor")) return "Vapor";
1667
+ if (content.includes("swift-nio")) return "SwiftNIO";
1668
+ if (content.includes("perfect")) return "Perfect";
1669
+ if (content.includes("kitura")) return "Kitura";
1670
+ } catch {
1671
+ }
1672
+ return "Swift";
1673
+ }
1674
+ default:
1675
+ return detectFramework(root);
1512
1676
  }
1513
- return "javascript";
1514
1677
  }
1515
1678
  function detectFeatures(root) {
1516
1679
  const features = [];
@@ -7965,57 +8128,236 @@ function formatDecisionTemplate() {
7965
8128
  }
7966
8129
 
7967
8130
  // src/engine/kumaContextEngine.ts
7968
- async function buildContextForGoal(goal) {
7969
- const items = [];
7970
- const terms = goal.toLowerCase().split(/\s+/).filter((w) => w.length > 2);
8131
+ import { execSync as execSync8 } from "child_process";
8132
+ var STOP_WORDS = /* @__PURE__ */ new Set([
8133
+ "the",
8134
+ "this",
8135
+ "that",
8136
+ "with",
8137
+ "from",
8138
+ "what",
8139
+ "which",
8140
+ "fix",
8141
+ "bug",
8142
+ "bikin",
8143
+ "buat",
8144
+ "add",
8145
+ "new",
8146
+ "need",
8147
+ "make",
8148
+ "want",
8149
+ "can",
8150
+ "get",
8151
+ "set",
8152
+ "use",
8153
+ "using",
8154
+ "would",
8155
+ "could",
8156
+ "should",
8157
+ "have",
8158
+ "has",
8159
+ "had",
8160
+ "for",
8161
+ "and",
8162
+ "not",
8163
+ "but",
8164
+ "are",
8165
+ "was",
8166
+ "were",
8167
+ "all",
8168
+ "any",
8169
+ "each",
8170
+ "every",
8171
+ "some",
8172
+ "find",
8173
+ "show",
8174
+ "list",
8175
+ "create",
8176
+ "update",
8177
+ "delete",
8178
+ "remove",
8179
+ "change"
8180
+ ]);
8181
+ function extractTerms(goal) {
8182
+ const words = goal.toLowerCase().split(/[\s_\/-]+/).filter((w) => w.length > 2);
8183
+ const filtered = words.filter((w) => !STOP_WORDS.has(w));
8184
+ const unique = [...new Set(filtered)];
8185
+ return unique.slice(0, 10);
8186
+ }
8187
+ async function searchNodesFts(db, terms) {
8188
+ const results = [];
8189
+ if (terms.length === 0) return results;
7971
8190
  try {
7972
- const db = await getDb();
7973
- for (const term of terms) {
8191
+ const escaped = terms.map((t) => t.replace(/"/g, '""'));
8192
+ const ftsQuery = escaped.map((t) => '"' + t + '"').join(" OR ");
8193
+ const stmt = db.prepare(`
8194
+ SELECT n.id, n.type, n.name, n.file_path, rank
8195
+ FROM nodes_fts f
8196
+ JOIN nodes n ON n.rowid = f.rowid
8197
+ WHERE nodes_fts MATCH ?
8198
+ ORDER BY rank
8199
+ LIMIT 10
8200
+ `);
8201
+ stmt.bind([ftsQuery]);
8202
+ while (stmt.step()) {
8203
+ const row = stmt.getAsObject();
8204
+ results.push({
8205
+ id: row.id,
8206
+ type: row.type,
8207
+ name: row.name,
8208
+ file_path: row.file_path,
8209
+ rank: row.rank || 0
8210
+ });
8211
+ }
8212
+ stmt.free();
8213
+ return results;
8214
+ } catch {
8215
+ }
8216
+ const seen = /* @__PURE__ */ new Set();
8217
+ for (const term of terms) {
8218
+ try {
7974
8219
  const stmt = db.prepare(`
7975
8220
  SELECT id, type, name, file_path FROM nodes
7976
- WHERE (name LIKE ? OR file_path LIKE ?)
8221
+ WHERE name LIKE ? OR file_path LIKE ?
7977
8222
  ORDER BY updated_at DESC LIMIT 5
7978
8223
  `);
7979
8224
  stmt.bind([`%${term}%`, `%${term}%`]);
7980
8225
  while (stmt.step()) {
7981
8226
  const row = stmt.getAsObject();
7982
- items.push({
7983
- type: "graph",
7984
- label: row.name || "",
7985
- detail: `${row.type}: ${row.file_path || row.id}`,
7986
- priority: 80
7987
- });
8227
+ const id = row.id;
8228
+ if (!seen.has(id)) {
8229
+ seen.add(id);
8230
+ results.push({
8231
+ id,
8232
+ type: row.type,
8233
+ name: row.name,
8234
+ file_path: row.file_path,
8235
+ rank: 0
8236
+ });
8237
+ }
7988
8238
  }
7989
8239
  stmt.free();
7990
- const edgeStmt = db.prepare(`
7991
- SELECT e.type, e.weight, n.name, n.file_path
7992
- FROM edges e
7993
- JOIN nodes n ON n.id = e.target_id
7994
- WHERE e.source_id LIKE ?
7995
- ORDER BY e.weight DESC LIMIT 5
7996
- `);
7997
- edgeStmt.bind([`%${term}%`]);
7998
- while (edgeStmt.step()) {
7999
- const row = edgeStmt.getAsObject();
8000
- items.push({
8001
- type: "graph",
8002
- label: row.name || "",
8003
- detail: `${row.type} (weight: ${row.weight})`,
8004
- priority: 60
8005
- });
8240
+ } catch {
8241
+ }
8242
+ }
8243
+ return results.slice(0, 10);
8244
+ }
8245
+ function getFileRecencySeconds(filePath) {
8246
+ try {
8247
+ const escapedPath = filePath.replace(/"/g, '\\"');
8248
+ const result = execSync8(
8249
+ 'git log -1 --format=%ct -- "' + escapedPath + '" 2>/dev/null || echo 0',
8250
+ { encoding: "utf-8", timeout: 2e3 }
8251
+ );
8252
+ const timestamp = parseInt(result.trim(), 10);
8253
+ if (!timestamp || timestamp === 0) return null;
8254
+ return Math.floor(Date.now() / 1e3) - timestamp;
8255
+ } catch {
8256
+ return null;
8257
+ }
8258
+ }
8259
+ function calculatePriority(type, label, terms, recencySeconds) {
8260
+ let score = 0;
8261
+ switch (type) {
8262
+ case "failure":
8263
+ score = 90;
8264
+ break;
8265
+ case "file":
8266
+ score = 60;
8267
+ break;
8268
+ case "graph":
8269
+ score = 50;
8270
+ break;
8271
+ case "memory":
8272
+ score = 40;
8273
+ break;
8274
+ }
8275
+ const lower = label.toLowerCase();
8276
+ for (const term of terms) {
8277
+ if (lower === term) {
8278
+ score += 30;
8279
+ } else if (lower.includes(term)) {
8280
+ score += 15;
8281
+ }
8282
+ }
8283
+ if (recencySeconds !== null) {
8284
+ if (recencySeconds < 3600) score += 20;
8285
+ else if (recencySeconds < 86400) score += 10;
8286
+ else if (recencySeconds < 604800) score += 5;
8287
+ }
8288
+ return Math.min(score, 100);
8289
+ }
8290
+ async function buildContextForGoal(goal) {
8291
+ const items = [];
8292
+ const terms = extractTerms(goal);
8293
+ if (terms.length === 0) {
8294
+ const modifiedFiles2 = sessionMemory.getModifiedFiles();
8295
+ for (const mf of modifiedFiles2) {
8296
+ items.push({
8297
+ type: "file",
8298
+ label: mf.filePath,
8299
+ detail: "Modified (" + mf.status + ")",
8300
+ priority: calculatePriority("file", mf.filePath, [], null)
8301
+ });
8302
+ }
8303
+ return { context: items.slice(0, 15), summary: "\u{1F4CB} No specific terms in goal. Showing " + Math.min(items.length, 15) + " modified file(s)." };
8304
+ }
8305
+ try {
8306
+ const db = await getDb();
8307
+ const nodeResults = await searchNodesFts(db, terms);
8308
+ const seenNodes = /* @__PURE__ */ new Set();
8309
+ for (const node of nodeResults) {
8310
+ if (seenNodes.has(node.id)) continue;
8311
+ seenNodes.add(node.id);
8312
+ const recency = node.file_path ? getFileRecencySeconds(node.file_path) : null;
8313
+ const priority = calculatePriority("graph", node.name, terms, recency);
8314
+ items.push({
8315
+ type: "graph",
8316
+ label: node.name,
8317
+ detail: node.type + ": " + (node.file_path || node.id),
8318
+ priority
8319
+ });
8320
+ if (nodeResults.indexOf(node) < 3) {
8321
+ try {
8322
+ const edgeStmt = db.prepare(`
8323
+ SELECT e.type, e.weight, n.name, n.file_path
8324
+ FROM edges e
8325
+ JOIN nodes n ON n.id = e.target_id
8326
+ WHERE e.source_id = ?
8327
+ ORDER BY e.weight DESC LIMIT 3
8328
+ `);
8329
+ edgeStmt.bind([node.id]);
8330
+ while (edgeStmt.step()) {
8331
+ const row = edgeStmt.getAsObject();
8332
+ const edgeName = row.name || "";
8333
+ const edgeKey = "edge:" + edgeName;
8334
+ if (!seenNodes.has(edgeKey)) {
8335
+ seenNodes.add(edgeKey);
8336
+ items.push({
8337
+ type: "graph",
8338
+ label: edgeName,
8339
+ detail: row.type + " (weight: " + row.weight + ")",
8340
+ priority: calculatePriority("graph", edgeName, terms, null) - 10
8341
+ });
8342
+ }
8343
+ }
8344
+ edgeStmt.free();
8345
+ } catch {
8346
+ }
8006
8347
  }
8007
- edgeStmt.free();
8008
8348
  }
8009
8349
  } catch {
8010
8350
  }
8011
8351
  const modifiedFiles = sessionMemory.getModifiedFiles();
8012
8352
  for (const mf of modifiedFiles) {
8013
8353
  if (terms.some((t) => mf.filePath.toLowerCase().includes(t))) {
8354
+ const recency = getFileRecencySeconds(mf.filePath);
8355
+ const priority = calculatePriority("file", mf.filePath, terms, recency);
8014
8356
  items.push({
8015
8357
  type: "file",
8016
8358
  label: mf.filePath,
8017
- detail: `Modified (${mf.status})`,
8018
- priority: 70
8359
+ detail: "Modified (" + mf.status + ")",
8360
+ priority
8019
8361
  });
8020
8362
  }
8021
8363
  }
@@ -8023,11 +8365,12 @@ async function buildContextForGoal(goal) {
8023
8365
  for (const ff of failedFiles) {
8024
8366
  for (const f of ff.failures) {
8025
8367
  if (!f.resolved && terms.some((t) => f.error.toLowerCase().includes(t))) {
8368
+ const priority = calculatePriority("failure", ff.task, terms, null);
8026
8369
  items.push({
8027
8370
  type: "failure",
8028
8371
  label: ff.task,
8029
8372
  detail: f.error.substring(0, 120),
8030
- priority: 90
8373
+ priority
8031
8374
  });
8032
8375
  break;
8033
8376
  }
@@ -8035,16 +8378,17 @@ async function buildContextForGoal(goal) {
8035
8378
  }
8036
8379
  const memories = scoreMemoryRelevance(goal, 3);
8037
8380
  for (const m of memories) {
8381
+ const priority = calculatePriority("memory", m.topic, terms, null);
8038
8382
  items.push({
8039
8383
  type: "memory",
8040
8384
  label: m.topic,
8041
8385
  detail: m.content.substring(0, 100),
8042
- priority: m.score
8386
+ priority
8043
8387
  });
8044
8388
  }
8045
8389
  const seen = /* @__PURE__ */ new Set();
8046
8390
  const unique = items.filter((i) => {
8047
- const key = `${i.type}:${i.label}`;
8391
+ const key = i.type + ":" + i.label;
8048
8392
  if (seen.has(key)) return false;
8049
8393
  seen.add(key);
8050
8394
  return true;
@@ -8054,18 +8398,18 @@ async function buildContextForGoal(goal) {
8054
8398
  const fileCount = unique.filter((i) => i.type === "file").length;
8055
8399
  const failureCount = unique.filter((i) => i.type === "failure").length;
8056
8400
  const memoryCount = unique.filter((i) => i.type === "memory").length;
8057
- const summary = `\u{1F4CB} Context for "${goal.substring(0, 40)}": ${unique.length} items (${graphCount} graph, ${fileCount} files, ${failureCount} failures, ${memoryCount} memories)`;
8401
+ const summary = '\u{1F4CB} Context for "' + goal.substring(0, 40) + '": ' + unique.length + " items (" + graphCount + " graph, " + fileCount + " files, " + failureCount + " failures, " + memoryCount + " memories)";
8058
8402
  return { context: unique.slice(0, 15), summary };
8059
8403
  }
8060
8404
  function formatContextItems(items, summary) {
8061
8405
  if (items.length === 0) return "";
8062
8406
  const lines = [
8063
8407
  summary,
8064
- "\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501"
8408
+ "\u2501".repeat(25)
8065
8409
  ];
8066
8410
  for (const item of items) {
8067
8411
  const icon = item.type === "graph" ? "\u{1F517}" : item.type === "file" ? "\u{1F4C4}" : item.type === "failure" ? "\u274C" : item.type === "memory" ? "\u{1F9E0}" : "\u{1F4CC}";
8068
- lines.push(` ${icon} **${item.label}** \u2014 ${item.detail}`);
8412
+ lines.push(" " + icon + " **" + item.label + "** \u2014 " + item.detail);
8069
8413
  }
8070
8414
  return lines.join("\n");
8071
8415
  }
@@ -8832,7 +9176,7 @@ function countFiles(dir) {
8832
9176
  async function investigate(problem) {
8833
9177
  try {
8834
9178
  const db = await getDb();
8835
- const terms = extractTerms(problem);
9179
+ const terms = extractTerms2(problem);
8836
9180
  const paths = [];
8837
9181
  const seen = /* @__PURE__ */ new Set();
8838
9182
  let step = 0;
@@ -8898,7 +9242,7 @@ async function investigate(problem) {
8898
9242
  return `Error investigating: ${err}`;
8899
9243
  }
8900
9244
  }
8901
- function extractTerms(problem) {
9245
+ function extractTerms2(problem) {
8902
9246
  return problem.toLowerCase().replace(/[^a-z0-9\s]/g, "").split(/\s+/).filter((w) => w.length > 2 && !["the", "and", "for", "are", "but", "not", "you", "all", "can", "had", "her", "was", "one", "our", "out", "has", "have", "been", "some", "them", "then", "their", "this", "that", "with", "from", "they", "will", "would", "could", "should", "about", "into", "over", "after", "also", "does", "each", "made", "just", "more", "most", "much", "must", "only", "other", "such", "than", "very", "when", "where", "which", "while", "your", "fix", "bug", "slow", "error", "issue", "problem"].includes(w)).slice(0, 8);
8903
9247
  }
8904
9248
  function findBottlenecks(paths) {
@@ -10717,7 +11061,7 @@ function formatCollectivePatterns(patterns) {
10717
11061
  }
10718
11062
 
10719
11063
  // src/engine/kumaMarketplace.ts
10720
- import { execSync as execSync8 } from "child_process";
11064
+ import { execSync as execSync9 } from "child_process";
10721
11065
  import fs27 from "fs";
10722
11066
  import path28 from "path";
10723
11067
  var BUILT_IN_TEMPLATES = [
@@ -11103,7 +11447,7 @@ async function tryInstallFromNpm(templateId) {
11103
11447
  try {
11104
11448
  const npmPackage = `@kuma-templates/${name}`;
11105
11449
  const root = getProjectRoot();
11106
- execSync8(`npm install --no-save ${npmPackage}`, {
11450
+ execSync9(`npm install --no-save ${npmPackage}`, {
11107
11451
  cwd: root,
11108
11452
  encoding: "utf-8",
11109
11453
  timeout: 3e4,
@@ -11761,7 +12105,113 @@ async function handleAdvanced(action, params) {
11761
12105
  }
11762
12106
  }
11763
12107
 
12108
+ // src/utils/kumaOutput.ts
12109
+ import crypto3 from "crypto";
12110
+ var dedupCache = /* @__PURE__ */ new Map();
12111
+ var DEDUP_TTL_MS = 6e4;
12112
+ function getCachedOutput(key) {
12113
+ const entry = dedupCache.get(key);
12114
+ if (entry && Date.now() - entry.timestamp < DEDUP_TTL_MS) {
12115
+ return entry.output;
12116
+ }
12117
+ return null;
12118
+ }
12119
+ function setCachedOutput(key, output) {
12120
+ dedupCache.set(key, { output, timestamp: Date.now() });
12121
+ if (dedupCache.size > 50) {
12122
+ const now = Date.now();
12123
+ for (const [k, v] of dedupCache) {
12124
+ if (now - v.timestamp > DEDUP_TTL_MS) dedupCache.delete(k);
12125
+ }
12126
+ }
12127
+ }
12128
+ function buildCacheKey(toolName, params) {
12129
+ const stable = {};
12130
+ for (const key of Object.keys(params).sort()) {
12131
+ const val = params[key];
12132
+ if (typeof val === "string") stable[key] = val.trim().substring(0, 200);
12133
+ else if (typeof val === "number") stable[key] = val;
12134
+ else if (typeof val === "boolean") stable[key] = val;
12135
+ else if (Array.isArray(val)) stable[key] = val.length;
12136
+ else if (val === null || val === void 0) stable[key] = null;
12137
+ else stable[key] = JSON.stringify(val).substring(0, 200);
12138
+ }
12139
+ const str = JSON.stringify(stable);
12140
+ return toolName + ":" + crypto3.createHash("md5").update(str).digest("hex").substring(0, 12);
12141
+ }
12142
+ var EMOJI_PATTERN = /[\u{1F000}-\u{1FFFF}\u{2600}-\u{26FF}\u{2700}-\u{27BF}\u{2300}-\u{23FF}]/gu;
12143
+ var BOLD_PATTERN = /\*\*/g;
12144
+ var DECORATIVE_LINE = /[━┉═]{5,}/g;
12145
+ var MULTI_NEWLINE = /\n{3,}/g;
12146
+ function stripFormatting(text) {
12147
+ return text.replace(EMOJI_PATTERN, "").replace(BOLD_PATTERN, "").replace(DECORATIVE_LINE, "").replace(MULTI_NEWLINE, "\n\n").split("\n").map((l) => l.trim()).join("\n").trim();
12148
+ }
12149
+ function adaptiveCompress(text, tokenBudget) {
12150
+ const lines = text.split("\n");
12151
+ const estimatedTokens = estimateTokens(text);
12152
+ if (!tokenBudget || estimatedTokens <= tokenBudget) return text;
12153
+ const stripped = stripFormatting(text);
12154
+ const strippedTokens = estimateTokens(stripped);
12155
+ if (strippedTokens <= tokenBudget) return stripped;
12156
+ if (lines.length < 50) {
12157
+ const maxChars = tokenBudget * 3;
12158
+ return stripped.slice(0, maxChars) + `
12159
+
12160
+ [...truncated: ~${estimatedTokens}tokens > ${tokenBudget}tokens]`;
12161
+ }
12162
+ if (lines.length < 200) {
12163
+ const head = lines.slice(0, 30).join("\n");
12164
+ const tail = lines.slice(-10).join("\n");
12165
+ return `${stripFormatting(head)}
12166
+
12167
+ [...${lines.length - 40} lines hidden - ${estimatedTokens}tokens > ${tokenBudget}tokens]
12168
+
12169
+ ${stripFormatting(tail)}`;
12170
+ }
12171
+ const summaryLines = lines.filter((l) => {
12172
+ const t = l.trim();
12173
+ return t.length > 10 && !t.startsWith(" ") && !t.startsWith(".") && !/^[━┉═\s]+$/.test(t);
12174
+ });
12175
+ const compressed = stripFormatting(summaryLines.slice(0, 20).join("\n"));
12176
+ return `${compressed}
12177
+
12178
+ [...${lines.length} lines compressed to ${summaryLines.length} key lines - ~${estimatedTokens}tokens > ${tokenBudget}tokens]`;
12179
+ }
12180
+ function formatOutput2(text, options = {}) {
12181
+ const { compact, responseBudget } = options;
12182
+ let result = text;
12183
+ if (options.cacheKey) {
12184
+ const cached = getCachedOutput(options.cacheKey);
12185
+ if (cached !== null) {
12186
+ return cached;
12187
+ }
12188
+ }
12189
+ if (compact) {
12190
+ result = stripFormatting(result);
12191
+ }
12192
+ if (responseBudget && responseBudget > 0) {
12193
+ const tokens = estimateTokens(result);
12194
+ if (tokens > responseBudget) {
12195
+ result = adaptiveCompress(result, responseBudget);
12196
+ }
12197
+ }
12198
+ if (options.cacheKey) {
12199
+ setCachedOutput(options.cacheKey, result);
12200
+ }
12201
+ return result;
12202
+ }
12203
+
11764
12204
  // src/manifest.ts
12205
+ function wrapOutput(text, toolName, params) {
12206
+ const compact = params.compact === true;
12207
+ const responseBudget = params.responseBudget;
12208
+ const cacheKey = buildCacheKey(toolName, params);
12209
+ return formatOutput2(text, { compact, responseBudget, cacheKey });
12210
+ }
12211
+ var compactSchema = {
12212
+ compact: z.boolean().optional().default(false).describe("Compact output mode (strips emojis/formatting, saves ~50% tokens)"),
12213
+ responseBudget: z.number().min(100).max(1e4).optional().describe("Max tokens for response (auto-compresses if exceeded)")
12214
+ };
11765
12215
  function registerAllTools(server) {
11766
12216
  server.tool(
11767
12217
  "kuma_init",
@@ -11770,12 +12220,13 @@ function registerAllTools(server) {
11770
12220
  action: z.enum(["init", "conventions", "structure"]).default("init").describe("Action: init=load all context, conventions=detect stack, structure=show tree"),
11771
12221
  projectRoot: z.string().optional().describe("Project root path (auto-detected)"),
11772
12222
  forceRescan: z.boolean().optional().default(false).describe("Force rescan for conventions"),
11773
- depth: z.number().min(1).max(6).optional().default(3).describe("Tree depth for structure")
12223
+ depth: z.number().min(1).max(6).optional().default(3).describe("Tree depth for structure"),
12224
+ ...compactSchema
11774
12225
  },
11775
12226
  async (params) => {
11776
12227
  try {
11777
12228
  const text = await handleInit(params.action || "init", params);
11778
- return { content: [{ type: "text", text }] };
12229
+ return { content: [{ type: "text", text: wrapOutput(text, "kuma_init", params) }] };
11779
12230
  } catch (err) {
11780
12231
  return { content: [{ type: "text", text: `Error in kuma_init: ${err}` }], isError: true };
11781
12232
  }
@@ -11820,17 +12271,18 @@ function registerAllTools(server) {
11820
12271
  line: z.number().min(0).optional().describe("Line number (0-indexed) for LSP"),
11821
12272
  character: z.number().min(0).optional().describe("Character position (0-indexed) for LSP"),
11822
12273
  lspAction: z.enum(["def", "refs", "type", "rename"]).optional().describe("LSP sub-action"),
11823
- newName: z.string().optional().describe("New name for rename action")
12274
+ newName: z.string().optional().describe("New name for rename action"),
12275
+ ...compactSchema
11824
12276
  },
11825
12277
  async (params) => {
11826
12278
  try {
11827
12279
  const action = params.action || "grep";
11828
12280
  if (action === "edit") {
11829
12281
  const text2 = await safeEditHandler(params);
11830
- return { content: [{ type: "text", text: text2 }] };
12282
+ return { content: [{ type: "text", text: wrapOutput(text2, "kuma_core", params) }] };
11831
12283
  }
11832
12284
  const text = await handleCore(action, params);
11833
- return { content: [{ type: "text", text }] };
12285
+ return { content: [{ type: "text", text: wrapOutput(text, "kuma_core", params) }] };
11834
12286
  } catch (err) {
11835
12287
  return { content: [{ type: "text", text: `Error in kuma_core: ${err}` }], isError: true };
11836
12288
  }
@@ -11854,12 +12306,13 @@ function registerAllTools(server) {
11854
12306
  format: z.enum(["text", "json"]).optional().default("text").describe("Output format"),
11855
12307
  // lint params
11856
12308
  tool: z.enum(["eslint", "tsc", "prettier", "ruff", "all"]).optional().default("all").describe("Lint tool"),
11857
- autoFix: z.boolean().optional().default(false).describe("Auto-fix issues")
12309
+ autoFix: z.boolean().optional().default(false).describe("Auto-fix issues"),
12310
+ ...compactSchema
11858
12311
  },
11859
12312
  async (params) => {
11860
12313
  try {
11861
12314
  const text = await handleVerify(params.action || "test", params);
11862
- return { content: [{ type: "text", text }] };
12315
+ return { content: [{ type: "text", text: wrapOutput(text, "kuma_verify", params) }] };
11863
12316
  } catch (err) {
11864
12317
  return { content: [{ type: "text", text: `Error in kuma_verify: ${err}` }], isError: true };
11865
12318
  }
@@ -11880,7 +12333,8 @@ function registerAllTools(server) {
11880
12333
  check: z.enum(["all", "anti-pattern", "loop", "drift", "context"]).optional().default("all").describe("Guard check type"),
11881
12334
  packageName: z.string().optional().describe("Package name for dependency guard"),
11882
12335
  packageVersion: z.string().optional().describe("Package version for dependency guard"),
11883
- actionCheck: z.string().optional().describe("Check sub-action (e.g. 'edit')")
12336
+ actionCheck: z.string().optional().describe("Check sub-action (e.g. 'edit')"),
12337
+ ...compactSchema
11884
12338
  },
11885
12339
  async (params) => {
11886
12340
  try {
@@ -11890,7 +12344,7 @@ function registerAllTools(server) {
11890
12344
  actionCheck: params.actionCheck || params.action,
11891
12345
  contextAction: params.contextAction || (action === "context" ? "save" : void 0)
11892
12346
  });
11893
- return { content: [{ type: "text", text }] };
12347
+ return { content: [{ type: "text", text: wrapOutput(text, "kuma_safety", params) }] };
11894
12348
  } catch (err) {
11895
12349
  return { content: [{ type: "text", text: `Error in kuma_safety: ${err}` }], isError: true };
11896
12350
  }
@@ -11912,12 +12366,13 @@ function registerAllTools(server) {
11912
12366
  experienceAction: z.enum(["suggest", "errors", "prune"]).optional().describe("Experience sub-action"),
11913
12367
  toolName: z.string().optional().describe("Tool name for experience errors"),
11914
12368
  intentAction: z.enum(["suggest"]).optional().describe("Intent sub-action"),
11915
- intent: z.string().optional().describe("Intent description")
12369
+ intent: z.string().optional().describe("Intent description"),
12370
+ ...compactSchema
11916
12371
  },
11917
12372
  async (params) => {
11918
12373
  try {
11919
12374
  const text = await handleGraph(params.action || "query", params);
11920
- return { content: [{ type: "text", text }] };
12375
+ return { content: [{ type: "text", text: wrapOutput(text, "kuma_graph", params) }] };
11921
12376
  } catch (err) {
11922
12377
  return { content: [{ type: "text", text: `Error in kuma_graph: ${err}` }], isError: true };
11923
12378
  }
@@ -11938,12 +12393,13 @@ function registerAllTools(server) {
11938
12393
  title: z.string().optional().describe("Decision title for record"),
11939
12394
  rationale: z.string().optional().describe("Decision rationale for record"),
11940
12395
  outcome: z.string().optional().describe("Decision outcome for record"),
11941
- healAction: z.enum(["check", "heal"]).optional().default("heal").describe("Heal sub-action")
12396
+ healAction: z.enum(["check", "heal"]).optional().default("heal").describe("Heal sub-action"),
12397
+ ...compactSchema
11942
12398
  },
11943
12399
  async (params) => {
11944
12400
  try {
11945
12401
  const text = await handleMemory(params.action || "get", params);
11946
- return { content: [{ type: "text", text }] };
12402
+ return { content: [{ type: "text", text: wrapOutput(text, "kuma_memory", params) }] };
11947
12403
  } catch (err) {
11948
12404
  return { content: [{ type: "text", text: `Error in kuma_memory: ${err}` }], isError: true };
11949
12405
  }
@@ -11957,12 +12413,13 @@ function registerAllTools(server) {
11957
12413
  goal: z.string().optional().describe("Goal for reflect"),
11958
12414
  context: z.string().optional().describe("Context for predict"),
11959
12415
  target: z.string().optional().describe("Target for confidence"),
11960
- sessionStats: z.boolean().optional().default(false).describe("Include session stats in heatmap")
12416
+ sessionStats: z.boolean().optional().default(false).describe("Include session stats in heatmap"),
12417
+ ...compactSchema
11961
12418
  },
11962
12419
  async (params) => {
11963
12420
  try {
11964
12421
  const text = await handleAnalytics(params.action || "reflect", params);
11965
- return { content: [{ type: "text", text }] };
12422
+ return { content: [{ type: "text", text: wrapOutput(text, "kuma_analytics", params) }] };
11966
12423
  } catch (err) {
11967
12424
  return { content: [{ type: "text", text: `Error in kuma_analytics: ${err}` }], isError: true };
11968
12425
  }
@@ -11981,12 +12438,13 @@ function registerAllTools(server) {
11981
12438
  staged: z.boolean().optional().default(false).describe("Show staged changes for diff"),
11982
12439
  contextLines: z.number().min(1).max(20).optional().default(3).describe("Context lines for diff"),
11983
12440
  baseRef: z.string().optional().describe("Base git ref for diff"),
11984
- targetRef: z.string().optional().describe("Target git ref for diff")
12441
+ targetRef: z.string().optional().describe("Target git ref for diff"),
12442
+ ...compactSchema
11985
12443
  },
11986
12444
  async (params) => {
11987
12445
  try {
11988
12446
  const text = await handleHistory(params.action || "log", params);
11989
- return { content: [{ type: "text", text }] };
12447
+ return { content: [{ type: "text", text: wrapOutput(text, "kuma_history", params) }] };
11990
12448
  } catch (err) {
11991
12449
  return { content: [{ type: "text", text: `Error in kuma_history: ${err}` }], isError: true };
11992
12450
  }
@@ -11998,12 +12456,13 @@ function registerAllTools(server) {
11998
12456
  {
11999
12457
  action: z.enum(["acquire", "release", "list", "clean"]).describe("Lock action"),
12000
12458
  filePath: z.string().optional().describe("File path for acquire/release"),
12001
- agentId: z.string().optional().describe("Agent identifier for lock")
12459
+ agentId: z.string().optional().describe("Agent identifier for lock"),
12460
+ ...compactSchema
12002
12461
  },
12003
12462
  async (params) => {
12004
12463
  try {
12005
12464
  const text = await handleLock(params.action || "list", params);
12006
- return { content: [{ type: "text", text }] };
12465
+ return { content: [{ type: "text", text: wrapOutput(text, "kuma_lock", params) }] };
12007
12466
  } catch (err) {
12008
12467
  return { content: [{ type: "text", text: `Error in kuma_lock: ${err}` }], isError: true };
12009
12468
  }
@@ -12030,12 +12489,13 @@ function registerAllTools(server) {
12030
12489
  marketplaceAction: z.enum(["list", "install"]).optional().describe("Marketplace sub-action"),
12031
12490
  template: z.string().optional().describe("Template ID for marketplace install"),
12032
12491
  // query params
12033
- query: z.string().optional().describe("Query for failure query")
12492
+ query: z.string().optional().describe("Query for failure query"),
12493
+ ...compactSchema
12034
12494
  },
12035
12495
  async (params) => {
12036
12496
  try {
12037
12497
  const text = await handleAdvanced(params.action || "marketplace", params);
12038
- return { content: [{ type: "text", text }] };
12498
+ return { content: [{ type: "text", text: wrapOutput(text, "kuma_advanced", params) }] };
12039
12499
  } catch (err) {
12040
12500
  return { content: [{ type: "text", text: `Error in kuma_advanced: ${err}` }], isError: true };
12041
12501
  }