codeimpact 0.3.1 → 0.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -8300,7 +8300,7 @@ function buildFeatureAgentDefinition(feature) {
8300
8300
  created_by: "code-impact"
8301
8301
  },
8302
8302
  scope: {
8303
- includedPaths: [...feature.paths, ...feature.testFiles],
8303
+ includedPaths: [...feature.paths, ...deriveTestGlobs(feature)],
8304
8304
  excludedPaths: feature.paths.map((p) => p.replace("/**", "/__mocks__/**"))
8305
8305
  },
8306
8306
  allowedTools: [
@@ -8473,6 +8473,25 @@ function renderAgentsShim(agents, config2) {
8473
8473
  }
8474
8474
  return lines.join("\n");
8475
8475
  }
8476
+ function deriveTestGlobs(feature) {
8477
+ if (feature.testFiles.length === 0) return [];
8478
+ const dirCounts = /* @__PURE__ */ new Map();
8479
+ for (const tf of feature.testFiles) {
8480
+ const parts = tf.split("/");
8481
+ const dir = parts.slice(0, -1).join("/");
8482
+ dirCounts.set(dir, (dirCounts.get(dir) || 0) + 1);
8483
+ }
8484
+ const globs = [];
8485
+ for (const [dir, count] of dirCounts) {
8486
+ if (count >= 2) {
8487
+ globs.push(`${dir}/**`);
8488
+ } else {
8489
+ const file2 = feature.testFiles.find((f) => f.startsWith(dir + "/"));
8490
+ if (file2) globs.push(file2);
8491
+ }
8492
+ }
8493
+ return globs;
8494
+ }
8476
8495
  function parseAgentMd(content) {
8477
8496
  try {
8478
8497
  const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---/);
@@ -10464,7 +10483,9 @@ function findSubPackageJsons(projectPath) {
10464
10483
  const entries = readdirSync10(dir, { withFileTypes: true });
10465
10484
  for (const entry of entries) {
10466
10485
  if (!entry.isDirectory()) continue;
10467
- if (entry.name === "node_modules" || entry.name === ".git" || entry.name === "dist") continue;
10486
+ const lower = entry.name.toLowerCase();
10487
+ if (lower === "node_modules" || lower === ".git" || lower === "dist") continue;
10488
+ if (EXCLUDED_FEATURE_DIRS.has(lower)) continue;
10468
10489
  const subDir = join30(dir, entry.name);
10469
10490
  const pkgJson = join30(subDir, "package.json");
10470
10491
  if (existsSync29(pkgJson) && subDir !== projectPath) {
@@ -10571,6 +10592,9 @@ function groupByDirectory(files, projectPath) {
10571
10592
  if (srcIdx >= 0 && parts.length > srcIdx + 2) {
10572
10593
  dirKey = parts.slice(0, srcIdx + 2).join("/");
10573
10594
  } else if (parts.length >= 2 && parts[0]) {
10595
+ const topDir = parts[0].toLowerCase();
10596
+ if (EXCLUDED_FEATURE_DIRS.has(topDir)) continue;
10597
+ if (!SOURCE_TOP_DIRS.has(topDir)) continue;
10574
10598
  dirKey = parts[0];
10575
10599
  } else {
10576
10600
  continue;
@@ -10672,15 +10696,33 @@ function findTestFiles(featurePaths, allFiles) {
10672
10696
  return testFiles;
10673
10697
  }
10674
10698
  function isUtilityDir(dirName) {
10675
- return UTILITY_DIRS.has(dirName.toLowerCase());
10699
+ const lower = dirName.toLowerCase();
10700
+ return UTILITY_DIRS.has(lower) || EXCLUDED_FEATURE_DIRS.has(lower);
10676
10701
  }
10677
10702
  function slugify6(name) {
10678
10703
  return name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 40);
10679
10704
  }
10680
- var UTILITY_DIRS;
10705
+ var SOURCE_TOP_DIRS, UTILITY_DIRS, EXCLUDED_FEATURE_DIRS;
10681
10706
  var init_feature_detector = __esm({
10682
10707
  "src/core/agents/feature-detector.ts"() {
10683
10708
  "use strict";
10709
+ SOURCE_TOP_DIRS = /* @__PURE__ */ new Set([
10710
+ "src",
10711
+ "app",
10712
+ "apps",
10713
+ "packages",
10714
+ "services",
10715
+ "modules",
10716
+ "test",
10717
+ "tests",
10718
+ "spec",
10719
+ "e2e",
10720
+ "api",
10721
+ "lib",
10722
+ "cmd",
10723
+ "internal",
10724
+ "pkg"
10725
+ ]);
10684
10726
  UTILITY_DIRS = /* @__PURE__ */ new Set([
10685
10727
  "lib",
10686
10728
  "libs",
@@ -10699,6 +10741,47 @@ var init_feature_detector = __esm({
10699
10741
  "vendor",
10700
10742
  "third-party"
10701
10743
  ]);
10744
+ EXCLUDED_FEATURE_DIRS = /* @__PURE__ */ new Set([
10745
+ // Runtime-generated / workspace
10746
+ "knowledge",
10747
+ ".code-impact",
10748
+ ".codeimpact",
10749
+ ".claude",
10750
+ ".cursor",
10751
+ // Documentation
10752
+ "doc",
10753
+ "docs",
10754
+ "documentation",
10755
+ // Build / output
10756
+ "dist",
10757
+ "build",
10758
+ "out",
10759
+ ".next",
10760
+ ".nuxt",
10761
+ ".output",
10762
+ // Dependencies
10763
+ "node_modules",
10764
+ ".yarn",
10765
+ ".pnpm-store",
10766
+ // CI / config
10767
+ ".github",
10768
+ ".gitlab",
10769
+ ".circleci",
10770
+ "scripts",
10771
+ ".husky",
10772
+ // Test fixtures (not features themselves)
10773
+ "fixtures",
10774
+ "base",
10775
+ "__fixtures__",
10776
+ "__snapshots__",
10777
+ // Data / migrations
10778
+ "migrations",
10779
+ "seeds",
10780
+ "data",
10781
+ // IDE / editor
10782
+ ".vscode",
10783
+ ".idea"
10784
+ ]);
10702
10785
  }
10703
10786
  });
10704
10787
 
@@ -11315,8 +11398,9 @@ function renderFeatureSkill(feature, technologies, config2) {
11315
11398
  lines.push(`- ${p}`);
11316
11399
  }
11317
11400
  if (feature.testFiles.length > 0) {
11318
- for (const tf of feature.testFiles) {
11319
- lines.push(`- ${tf}`);
11401
+ const testGlobs = summarizeTestFiles(feature.testFiles);
11402
+ for (const tg of testGlobs) {
11403
+ lines.push(`- ${tg}`);
11320
11404
  }
11321
11405
  }
11322
11406
  lines.push("");
@@ -11399,6 +11483,25 @@ function parseAutoContentSections(autoContent) {
11399
11483
  }
11400
11484
  return sections;
11401
11485
  }
11486
+ function summarizeTestFiles(testFiles) {
11487
+ if (testFiles.length === 0) return [];
11488
+ const dirCounts = /* @__PURE__ */ new Map();
11489
+ for (const tf of testFiles) {
11490
+ const parts = tf.split("/");
11491
+ const dir = parts.slice(0, -1).join("/");
11492
+ dirCounts.set(dir, (dirCounts.get(dir) || 0) + 1);
11493
+ }
11494
+ const globs = [];
11495
+ for (const [dir, count] of dirCounts) {
11496
+ if (count >= 2) {
11497
+ globs.push(`${dir}/**`);
11498
+ } else {
11499
+ const file2 = testFiles.find((f) => f.startsWith(dir + "/"));
11500
+ if (file2) globs.push(file2);
11501
+ }
11502
+ }
11503
+ return globs;
11504
+ }
11402
11505
  function inferDirPurpose(dirName) {
11403
11506
  const purposes = {
11404
11507
  core: "Core business logic and domain modules",
@@ -11420,15 +11523,26 @@ function inferDirPurpose(dirName) {
11420
11523
  }
11421
11524
  function deriveRules(feature, technologies) {
11422
11525
  const rules = [];
11423
- for (const tech of technologies) {
11424
- const knowledge = TECH_KNOWLEDGE[tech.name];
11425
- if (knowledge) {
11426
- rules.push(...knowledge.rules);
11526
+ if (WELL_KNOWN_FEATURES.has(feature.name)) {
11527
+ rules.push(...getFeatureNameRules(feature.name));
11528
+ const techRules = [];
11529
+ for (const tech of technologies) {
11530
+ const knowledge = TECH_KNOWLEDGE[tech.name];
11531
+ if (knowledge) techRules.push(...knowledge.rules);
11532
+ }
11533
+ for (const tr of techRules.slice(0, 2)) {
11534
+ if (!rules.some((r) => r.includes(tr.split(" ")[0] || ""))) {
11535
+ rules.push(tr);
11536
+ }
11537
+ }
11538
+ } else {
11539
+ for (const tech of technologies) {
11540
+ const knowledge = TECH_KNOWLEDGE[tech.name];
11541
+ if (knowledge) rules.push(...knowledge.rules);
11542
+ }
11543
+ if (rules.length === 0) {
11544
+ rules.push(...getFeatureNameRules(feature.name));
11427
11545
  }
11428
- }
11429
- if (rules.length === 0) {
11430
- const featureRules = getFeatureNameRules(feature.name);
11431
- rules.push(...featureRules);
11432
11546
  }
11433
11547
  if (feature.paths.length > 0) {
11434
11548
  const scope = feature.paths[0]?.replace("/**", "") || "";
@@ -11438,15 +11552,24 @@ function deriveRules(feature, technologies) {
11438
11552
  }
11439
11553
  function derivePitfalls(feature, technologies) {
11440
11554
  const pitfalls = [];
11441
- for (const tech of technologies) {
11442
- const knowledge = TECH_KNOWLEDGE[tech.name];
11443
- if (knowledge) {
11444
- pitfalls.push(...knowledge.pitfalls);
11555
+ if (WELL_KNOWN_FEATURES.has(feature.name)) {
11556
+ pitfalls.push(...getFeatureNamePitfalls(feature.name));
11557
+ for (const tech of technologies) {
11558
+ const knowledge = TECH_KNOWLEDGE[tech.name];
11559
+ if (knowledge) {
11560
+ for (const p of knowledge.pitfalls.slice(0, 1)) {
11561
+ if (!pitfalls.includes(p)) pitfalls.push(p);
11562
+ }
11563
+ }
11564
+ }
11565
+ } else {
11566
+ for (const tech of technologies) {
11567
+ const knowledge = TECH_KNOWLEDGE[tech.name];
11568
+ if (knowledge) pitfalls.push(...knowledge.pitfalls);
11569
+ }
11570
+ if (pitfalls.length === 0) {
11571
+ pitfalls.push(...getFeatureNamePitfalls(feature.name));
11445
11572
  }
11446
- }
11447
- if (pitfalls.length === 0) {
11448
- const featurePitfalls = getFeatureNamePitfalls(feature.name);
11449
- pitfalls.push(...featurePitfalls);
11450
11573
  }
11451
11574
  return pitfalls;
11452
11575
  }
@@ -11562,7 +11685,7 @@ function filterDirectDependencies(technologies, projectPath) {
11562
11685
  }
11563
11686
  return technologies.filter((t) => !skipPatterns.some((p) => p.test(t.name))).slice(0, 20);
11564
11687
  }
11565
- var TECH_KNOWLEDGE;
11688
+ var TECH_KNOWLEDGE, WELL_KNOWN_FEATURES;
11566
11689
  var init_generator = __esm({
11567
11690
  "src/core/agents/generator.ts"() {
11568
11691
  "use strict";
@@ -11689,6 +11812,19 @@ var init_generator = __esm({
11689
11812
  ]
11690
11813
  }
11691
11814
  };
11815
+ WELL_KNOWN_FEATURES = /* @__PURE__ */ new Set([
11816
+ "server",
11817
+ "storage",
11818
+ "indexing",
11819
+ "core",
11820
+ "test",
11821
+ "knowledge",
11822
+ "doc",
11823
+ "auth",
11824
+ "api",
11825
+ "billing",
11826
+ "cli"
11827
+ ]);
11692
11828
  }
11693
11829
  });
11694
11830
 
@@ -12398,6 +12534,8 @@ function createMinimalIntelligence(projectPath, technologies, features) {
12398
12534
  layers,
12399
12535
  dataFlow: inferDataFlow(features),
12400
12536
  keyComponents: [],
12537
+ patternCategories: {},
12538
+ topPatterns: [],
12401
12539
  functionStats: { total: 0, exported: 0 }
12402
12540
  },
12403
12541
  dependencyHotspots: [],
@@ -43124,22 +43262,32 @@ ${END_MARKER}`;
43124
43262
  return { content: `${block}
43125
43263
  `, changed: true };
43126
43264
  }
43127
- const start = existing.indexOf(START_MARKER);
43128
- const end = existing.indexOf(END_MARKER);
43129
- if (start >= 0 && end > start) {
43130
- const before = existing.slice(0, start).trimEnd();
43131
- const after = existing.slice(end + END_MARKER.length).trimStart();
43132
- const content2 = `${before}
43133
-
43134
- ${block}
43135
-
43136
- ${after}`.trim() + "\n";
43137
- return { content: content2, changed: content2 !== existing };
43265
+ let cleaned = existing;
43266
+ let hadMarkers = false;
43267
+ while (true) {
43268
+ const start = cleaned.indexOf(START_MARKER);
43269
+ const end = cleaned.indexOf(END_MARKER, start >= 0 ? start : 0);
43270
+ if (start >= 0 && end > start) {
43271
+ hadMarkers = true;
43272
+ cleaned = cleaned.slice(0, start) + cleaned.slice(end + END_MARKER.length);
43273
+ } else if (end >= 0 && start < 0) {
43274
+ hadMarkers = true;
43275
+ cleaned = cleaned.slice(0, end) + cleaned.slice(end + END_MARKER.length);
43276
+ } else {
43277
+ break;
43278
+ }
43138
43279
  }
43139
- const content = `${existing.trimEnd()}
43280
+ cleaned = cleaned.replace(/\n{3,}/g, "\n\n").trim();
43281
+ let content;
43282
+ if (!cleaned) {
43283
+ content = `${block}
43284
+ `;
43285
+ } else {
43286
+ content = `${cleaned}
43140
43287
 
43141
43288
  ${block}
43142
43289
  `;
43290
+ }
43143
43291
  return { content, changed: content !== existing };
43144
43292
  }
43145
43293
  function writeManagedFile(targetPath, section, options) {
@@ -43172,93 +43320,61 @@ function renderPlatformSection(platform, paths, skillIndex, evolutionGuidance) {
43172
43320
  let attentionSection = "";
43173
43321
  if (evolutionGuidance && evolutionGuidance.length > 0) {
43174
43322
  attentionSection = `
43175
-
43176
43323
  ### Skills Needing Attention
43177
43324
  ${evolutionGuidance.map((g) => `- ${g}`).join("\n")}`;
43178
43325
  }
43179
- return `# CodeImpact Knowledge System
43326
+ return `# CodeImpact \u2014 AI-Powered Codebase Intelligence
43327
+
43328
+ You have access to **CodeImpact**, a persistent knowledge system that understands this codebase. It provides project skills, architecture knowledge, and self-learning agents.
43329
+
43330
+ ## IMPORTANT: Session Start
43331
+
43332
+ 1. **Read project knowledge** before making changes:
43333
+ - \`.code-impact/project/SKILL.md\` \u2014 Tech stack, architecture, key directories
43334
+ - \`.code-impact/project/CONVENTIONS.md\` \u2014 Coding standards and patterns
43335
+ - \`.code-impact/project/ARCHITECTURE.md\` \u2014 System layers and data flow
43336
+ 2. **Read feature knowledge** for the files you're modifying:
43337
+ - \`.code-impact/features/{feature}/SKILL.md\` \u2014 Feature-specific rules, pitfalls, and research refs
43338
+ - Features: check \`.code-impact/features/\` for available feature directories
43339
+ 3. Run \`${tool("memory_status")}\` for project overview and recent changes
43180
43340
 
43181
- You are part of a **self-improving knowledge system**. Skills you create persist across sessions. Future AI sessions benefit from the knowledge you build now.
43341
+ ## MCP Tools (Use FIRST before built-in tools)
43182
43342
 
43183
- ## Tools
43184
43343
  | Task | Tool |
43185
43344
  |------|------|
43186
- | Find code | \`${tool("memory_query")}\` |
43187
- | Check code | \`${tool("memory_review")}\` |
43188
- | Verify code | \`${tool("memory_verify")}\` |
43345
+ | Search code semantically | \`${tool("memory_query")}\` |
43346
+ | Review code before changes | \`${tool("memory_review")}\` |
43347
+ | Verify before committing | \`${tool("memory_verify")}\` |
43189
43348
  | Project status | \`${tool("memory_status")}\` |
43190
43349
  | Impact analysis | \`${tool("memory_blast_radius")}\` |
43350
+ | Agent system | \`${tool("memory_agents")}\` |
43191
43351
  | Build knowledge | \`${tool("memory_evolve")}\` |
43192
43352
 
43193
- ## Session Start
43194
- 1. Run \`${tool("memory_status")}\` \u2014 check \`knowledge_gaps\` for uncovered technologies and high-risk files.
43195
- 2. Read relevant skills from \`knowledge/skills/\` for the current task.
43353
+ ## Agent System
43196
43354
 
43197
- ## Skill Creation Protocol
43355
+ This project has feature-level agents in \`.code-impact/features/\`. Each agent has:
43356
+ - **SKILL.md** \u2014 Rules, pitfalls, and technology-specific guidance
43357
+ - **AGENT.md** \u2014 Scope, allowed tools, and lessons learned from past mistakes
43198
43358
 
43199
- **After completing any task involving 3+ files**, create or improve a skill.
43359
+ Before modifying files, check the relevant feature's SKILL.md for rules and pitfalls.
43360
+ After completing a task, record the outcome via \`${tool("memory_agents")}\` with action="record_outcome".
43200
43361
 
43201
- ### To create a new skill:
43202
- \`${tool("memory_evolve")}\` with:
43203
- - action="create_skill"
43204
- - name="technology-or-area-name" (slug format)
43205
- - description="One line: when to use this skill"
43206
- - scope="technology|feature|risk|core"
43207
- - content="Full markdown body (see format below)"
43362
+ ## Skill Management
43208
43363
 
43209
- ### To improve an existing skill:
43210
- \`${tool("memory_evolve")}\` with:
43211
- - action="improve_skill", skill_id="skill-name"
43212
- - Patch mode: old_text="exact text to replace", new_text="replacement"
43213
- - Append mode: section="pitfalls", content="New pitfall to add"
43364
+ After completing any task involving 3+ files, create or improve a skill:
43214
43365
 
43215
- ### To discover gaps:
43216
- \`${tool("memory_evolve")}\` with action="list_signals"
43217
-
43218
- ### Skill Format (agentskills.io SKILL.md)
43219
-
43220
- \`\`\`markdown
43221
- ---
43222
- name: better-sqlite3-patterns
43223
- description: Synchronous database patterns. Use when working with database queries or schema changes.
43224
- version: 1.0
43225
- metadata:
43226
- scope: technology
43227
- created_by: ai
43228
- ---
43229
-
43230
- # better-sqlite3 Patterns
43231
-
43232
- ## When to Use
43233
- When modifying database queries, adding tables, or working with files that import from src/storage/database.ts.
43234
-
43235
- ## Key Facts
43236
- - Database: .codeimpact/codeimpact.db (SQLite, WAL mode)
43237
- - API: better-sqlite3 (synchronous, NOT async)
43238
-
43239
- ## Rules
43240
- - ALL database access goes through database.ts \u2014 never import better-sqlite3 directly.
43241
- - Use db.prepare().all() for SELECT, db.prepare().run() for INSERT/UPDATE/DELETE.
43242
-
43243
- ## Pitfalls
43244
- - db.exec() returns nothing. If you use it for SELECT, you get undefined.
43245
- - better-sqlite3 is synchronous. Do NOT wrap in async/await.
43246
-
43247
- ## Verification
43248
- - npx tsc --noEmit passes with no type errors on database code.
43249
- \`\`\`
43366
+ \`${tool("memory_evolve")}\` with action="create_skill" (new) or action="improve_skill" (update)
43250
43367
 
43251
43368
  ### Quality Rules
43252
- - **Under 5000 tokens** per skill
43253
- - **Be specific**: "Use db.prepare().all() for SELECT" not "follow project patterns"
43254
- - **Pitfalls with symptoms**: "you'll get undefined" not "don't misuse"
43255
- - **Every line earns its tokens** \u2014 no filler, no generic advice the AI already knows
43369
+ - Under 5000 tokens per skill
43370
+ - Be specific: "Use db.prepare().all() for SELECT" not "follow project patterns"
43371
+ - Include pitfalls with symptoms: "you'll get undefined" not "don't misuse"
43256
43372
 
43257
43373
  ## Existing Skills
43258
43374
  ${skillList}
43259
43375
  ${attentionSection}
43260
43376
 
43261
- ## Workspace
43377
+ ## Knowledge Workspace
43262
43378
  \`${paths.root.replace(/\\/g, "/")}\``;
43263
43379
  }
43264
43380
  var PlatformRuleSync = class {