codeimpact 0.3.0 → 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 +630 -141
- package/dist/index.js.map +3 -3
- package/package.json +1 -1
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
|
@@ -11109,7 +11192,7 @@ var init_token_budget = __esm({
|
|
|
11109
11192
|
});
|
|
11110
11193
|
|
|
11111
11194
|
// src/core/agents/generator.ts
|
|
11112
|
-
import { mkdirSync as mkdirSync17 } from "fs";
|
|
11195
|
+
import { existsSync as existsSync32, readFileSync as readFileSync26, mkdirSync as mkdirSync17 } from "fs";
|
|
11113
11196
|
import { join as join33 } from "path";
|
|
11114
11197
|
function generateProjectFiles(input) {
|
|
11115
11198
|
const { projectPath, intelligence, technologies, features, index } = input;
|
|
@@ -11118,7 +11201,7 @@ function generateProjectFiles(input) {
|
|
|
11118
11201
|
const result = { filesWritten: [], filesSkipped: [] };
|
|
11119
11202
|
mkdirSync17(paths.projectDir, { recursive: true });
|
|
11120
11203
|
const skillPath = join33(paths.projectDir, "SKILL.md");
|
|
11121
|
-
writeMarkedFile2(skillPath, renderProjectSkill(intelligence, technologies, config2), config2, result, "project_skill");
|
|
11204
|
+
writeMarkedFile2(skillPath, renderProjectSkill(intelligence, technologies, config2, projectPath), config2, result, "project_skill");
|
|
11122
11205
|
const convPath = join33(paths.projectDir, "CONVENTIONS.md");
|
|
11123
11206
|
writeMarkedFile2(convPath, renderConventions(intelligence, config2), config2, result, "project_conventions");
|
|
11124
11207
|
const archPath = join33(paths.projectDir, "ARCHITECTURE.md");
|
|
@@ -11139,7 +11222,7 @@ function generateFeatureFiles(input) {
|
|
|
11139
11222
|
}
|
|
11140
11223
|
return result;
|
|
11141
11224
|
}
|
|
11142
|
-
function renderProjectSkill(intel, technologies, config2) {
|
|
11225
|
+
function renderProjectSkill(intel, technologies, config2, projectPath) {
|
|
11143
11226
|
const frontmatter = [
|
|
11144
11227
|
"---",
|
|
11145
11228
|
"name: project-overview",
|
|
@@ -11156,9 +11239,13 @@ function renderProjectSkill(intel, technologies, config2) {
|
|
|
11156
11239
|
const lines = [];
|
|
11157
11240
|
lines.push("## Tech Stack");
|
|
11158
11241
|
if (technologies.length > 0) {
|
|
11159
|
-
const
|
|
11160
|
-
|
|
11161
|
-
|
|
11242
|
+
const directDeps = filterDirectDependencies(technologies, projectPath);
|
|
11243
|
+
if (directDeps.length > 0) {
|
|
11244
|
+
for (const tech of directDeps.slice(0, 20)) {
|
|
11245
|
+
lines.push(`- ${tech.name} ${tech.version}`);
|
|
11246
|
+
}
|
|
11247
|
+
} else {
|
|
11248
|
+
lines.push(`- Languages: ${intel.codebase.languages.join(", ")}`);
|
|
11162
11249
|
}
|
|
11163
11250
|
} else {
|
|
11164
11251
|
lines.push(`- Languages: ${intel.codebase.languages.join(", ")}`);
|
|
@@ -11186,7 +11273,9 @@ function renderProjectSkill(intel, technologies, config2) {
|
|
|
11186
11273
|
}
|
|
11187
11274
|
} else {
|
|
11188
11275
|
for (const dir of intel.codebase.keyDirectories) {
|
|
11189
|
-
|
|
11276
|
+
const dirName = dir.split("/").pop() || dir;
|
|
11277
|
+
const purpose = inferDirPurpose(dirName);
|
|
11278
|
+
lines.push(`| ${dir}/ | ${purpose} |`);
|
|
11190
11279
|
}
|
|
11191
11280
|
}
|
|
11192
11281
|
lines.push("");
|
|
@@ -11309,8 +11398,9 @@ function renderFeatureSkill(feature, technologies, config2) {
|
|
|
11309
11398
|
lines.push(`- ${p}`);
|
|
11310
11399
|
}
|
|
11311
11400
|
if (feature.testFiles.length > 0) {
|
|
11312
|
-
|
|
11313
|
-
|
|
11401
|
+
const testGlobs = summarizeTestFiles(feature.testFiles);
|
|
11402
|
+
for (const tg of testGlobs) {
|
|
11403
|
+
lines.push(`- ${tg}`);
|
|
11314
11404
|
}
|
|
11315
11405
|
}
|
|
11316
11406
|
lines.push("");
|
|
@@ -11335,46 +11425,15 @@ function renderFeatureSkill(feature, technologies, config2) {
|
|
|
11335
11425
|
lines.push("");
|
|
11336
11426
|
}
|
|
11337
11427
|
lines.push("## Rules");
|
|
11338
|
-
|
|
11339
|
-
|
|
11340
|
-
|
|
11341
|
-
lines.push("- Use Router() for modular route definitions");
|
|
11342
|
-
lines.push("- Always register error handler middleware last");
|
|
11343
|
-
} else if (tech.name === "better-sqlite3") {
|
|
11344
|
-
lines.push("- Use db.prepare().all() for SELECT, db.prepare().run() for INSERT/UPDATE/DELETE");
|
|
11345
|
-
lines.push("- better-sqlite3 is synchronous \u2014 do NOT use async/await");
|
|
11346
|
-
} else if (tech.name === "stripe") {
|
|
11347
|
-
lines.push("- Always verify webhook signatures before processing events");
|
|
11348
|
-
lines.push("- Use idempotency keys for payment creation");
|
|
11349
|
-
} else if (tech.name === "jsonwebtoken") {
|
|
11350
|
-
lines.push("- Always verify tokens before trusting decoded data");
|
|
11351
|
-
lines.push("- Set explicit expiration times on all tokens");
|
|
11352
|
-
} else {
|
|
11353
|
-
lines.push(`- Follow ${tech.name} v${tech.version} API conventions`);
|
|
11354
|
-
}
|
|
11355
|
-
}
|
|
11356
|
-
} else {
|
|
11357
|
-
lines.push("- Follow project coding conventions for this feature");
|
|
11428
|
+
const rules = deriveRules(feature, technologies);
|
|
11429
|
+
for (const rule of rules) {
|
|
11430
|
+
lines.push(`- ${rule}`);
|
|
11358
11431
|
}
|
|
11359
11432
|
lines.push("");
|
|
11360
11433
|
lines.push("## Pitfalls");
|
|
11361
|
-
|
|
11362
|
-
|
|
11363
|
-
|
|
11364
|
-
lines.push("- Forgetting to call next() in middleware causes request to hang");
|
|
11365
|
-
} else if (tech.name === "better-sqlite3") {
|
|
11366
|
-
lines.push("- db.exec() returns nothing \u2014 using it for SELECT gives undefined");
|
|
11367
|
-
} else if (tech.name === "stripe") {
|
|
11368
|
-
lines.push("- Stripe webhook events may arrive out of order \u2014 handle idempotently");
|
|
11369
|
-
} else if (tech.name === "jsonwebtoken") {
|
|
11370
|
-
lines.push("- Using jwt.decode() without verify() is a security vulnerability");
|
|
11371
|
-
}
|
|
11372
|
-
}
|
|
11373
|
-
if (technologies.every((t) => !["express", "better-sqlite3", "stripe", "jsonwebtoken"].includes(t.name))) {
|
|
11374
|
-
lines.push("- Check research docs for version-specific breaking changes");
|
|
11375
|
-
}
|
|
11376
|
-
} else {
|
|
11377
|
-
lines.push("- Check for null/undefined before property access on external data");
|
|
11434
|
+
const pitfalls = derivePitfalls(feature, technologies);
|
|
11435
|
+
for (const pitfall of pitfalls) {
|
|
11436
|
+
lines.push(`- ${pitfall}`);
|
|
11378
11437
|
}
|
|
11379
11438
|
lines.push("");
|
|
11380
11439
|
return { frontmatter, autoContent: lines.join("\n") };
|
|
@@ -11424,15 +11483,348 @@ function parseAutoContentSections(autoContent) {
|
|
|
11424
11483
|
}
|
|
11425
11484
|
return sections;
|
|
11426
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
|
+
}
|
|
11505
|
+
function inferDirPurpose(dirName) {
|
|
11506
|
+
const purposes = {
|
|
11507
|
+
core: "Core business logic and domain modules",
|
|
11508
|
+
server: "Server, transports, and request handling",
|
|
11509
|
+
storage: "Database access and persistence",
|
|
11510
|
+
indexing: "Code indexing and symbol extraction",
|
|
11511
|
+
api: "API routes and handlers",
|
|
11512
|
+
auth: "Authentication and authorization",
|
|
11513
|
+
billing: "Payment processing",
|
|
11514
|
+
test: "Tests and evaluation harness",
|
|
11515
|
+
doc: "Documentation",
|
|
11516
|
+
knowledge: "AI knowledge system",
|
|
11517
|
+
cli: "Command-line interface",
|
|
11518
|
+
base: "Base configuration and fixtures",
|
|
11519
|
+
src: "Application source code",
|
|
11520
|
+
lib: "Shared utilities"
|
|
11521
|
+
};
|
|
11522
|
+
return purposes[dirName.toLowerCase()] || `${dirName} module`;
|
|
11523
|
+
}
|
|
11524
|
+
function deriveRules(feature, technologies) {
|
|
11525
|
+
const 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));
|
|
11545
|
+
}
|
|
11546
|
+
}
|
|
11547
|
+
if (feature.paths.length > 0) {
|
|
11548
|
+
const scope = feature.paths[0]?.replace("/**", "") || "";
|
|
11549
|
+
rules.push(`Changes here should not import from other feature directories \u2014 keep ${scope} self-contained`);
|
|
11550
|
+
}
|
|
11551
|
+
return rules;
|
|
11552
|
+
}
|
|
11553
|
+
function derivePitfalls(feature, technologies) {
|
|
11554
|
+
const 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));
|
|
11572
|
+
}
|
|
11573
|
+
}
|
|
11574
|
+
return pitfalls;
|
|
11575
|
+
}
|
|
11576
|
+
function getFeatureNameRules(name) {
|
|
11577
|
+
const rules = {
|
|
11578
|
+
server: [
|
|
11579
|
+
"All tool/resource handlers must validate inputs before processing",
|
|
11580
|
+
"Return structured error responses \u2014 never throw raw errors to clients"
|
|
11581
|
+
],
|
|
11582
|
+
storage: [
|
|
11583
|
+
"All database access should go through this module \u2014 no direct imports elsewhere",
|
|
11584
|
+
"Use transactions for multi-step operations to ensure consistency"
|
|
11585
|
+
],
|
|
11586
|
+
indexing: [
|
|
11587
|
+
"Index operations should be idempotent \u2014 reindexing same file produces same result",
|
|
11588
|
+
"Handle parse errors gracefully \u2014 a broken file should not crash the indexer"
|
|
11589
|
+
],
|
|
11590
|
+
core: [
|
|
11591
|
+
"Core modules should have no side effects on import",
|
|
11592
|
+
"Export types alongside functions for downstream consumers"
|
|
11593
|
+
],
|
|
11594
|
+
test: [
|
|
11595
|
+
"Tests should be deterministic \u2014 no reliance on external services or timing",
|
|
11596
|
+
"Clean up temporary files and directories in afterEach/afterAll hooks"
|
|
11597
|
+
],
|
|
11598
|
+
knowledge: [
|
|
11599
|
+
"Skill files must follow the agentskills.io SKILL.md format",
|
|
11600
|
+
"Generated content must use marker comments to preserve manual edits"
|
|
11601
|
+
],
|
|
11602
|
+
doc: [
|
|
11603
|
+
"Generated docs must be kept in sync with source code changes",
|
|
11604
|
+
"Use relative paths for internal links"
|
|
11605
|
+
],
|
|
11606
|
+
auth: [
|
|
11607
|
+
"Never store plain-text passwords \u2014 use bcrypt or argon2",
|
|
11608
|
+
"Validate and sanitize all user inputs before processing"
|
|
11609
|
+
],
|
|
11610
|
+
api: [
|
|
11611
|
+
"All endpoints must validate request bodies/params before processing",
|
|
11612
|
+
"Return consistent error response shapes across all endpoints"
|
|
11613
|
+
]
|
|
11614
|
+
};
|
|
11615
|
+
return rules[name] || [
|
|
11616
|
+
"Follow existing patterns in this module for consistency",
|
|
11617
|
+
"Export public API from index.ts \u2014 keep internal helpers unexported"
|
|
11618
|
+
];
|
|
11619
|
+
}
|
|
11620
|
+
function getFeatureNamePitfalls(name) {
|
|
11621
|
+
const pitfalls = {
|
|
11622
|
+
server: [
|
|
11623
|
+
"Unhandled promise rejections in handlers crash the process \u2014 always catch async errors",
|
|
11624
|
+
"Adding middleware order matters \u2014 auth before route handlers"
|
|
11625
|
+
],
|
|
11626
|
+
storage: [
|
|
11627
|
+
"Forgetting to close database connections leaks file handles",
|
|
11628
|
+
"Concurrent writes without transactions may cause data corruption"
|
|
11629
|
+
],
|
|
11630
|
+
indexing: [
|
|
11631
|
+
"Large files can cause out-of-memory \u2014 set size limits on parsed content",
|
|
11632
|
+
"File paths must be normalized (forward slashes) for cross-platform compatibility"
|
|
11633
|
+
],
|
|
11634
|
+
core: [
|
|
11635
|
+
"Circular imports between core modules cause runtime errors \u2014 check dependency direction",
|
|
11636
|
+
"Changing public API signatures breaks downstream consumers"
|
|
11637
|
+
],
|
|
11638
|
+
test: [
|
|
11639
|
+
"Tests sharing mutable state between runs cause flaky failures",
|
|
11640
|
+
"Temp directories not cleaned up fill disk over repeated test runs"
|
|
11641
|
+
]
|
|
11642
|
+
};
|
|
11643
|
+
return pitfalls[name] || [
|
|
11644
|
+
"Check for null/undefined before property access on external data",
|
|
11645
|
+
"Changing exports may break other modules that depend on this feature"
|
|
11646
|
+
];
|
|
11647
|
+
}
|
|
11427
11648
|
function capitalize(str) {
|
|
11428
11649
|
return str.charAt(0).toUpperCase() + str.slice(1).replace(/-/g, " ");
|
|
11429
11650
|
}
|
|
11651
|
+
function filterDirectDependencies(technologies, projectPath) {
|
|
11652
|
+
const directNames = /* @__PURE__ */ new Set();
|
|
11653
|
+
for (const tech of technologies) {
|
|
11654
|
+
if (tech.source === "package.json") {
|
|
11655
|
+
directNames.add(tech.name);
|
|
11656
|
+
}
|
|
11657
|
+
}
|
|
11658
|
+
if (directNames.size === 0) {
|
|
11659
|
+
const searchPaths = [
|
|
11660
|
+
projectPath ? join33(projectPath, "package.json") : "",
|
|
11661
|
+
join33(process.cwd(), "package.json")
|
|
11662
|
+
].filter(Boolean);
|
|
11663
|
+
for (const p of searchPaths) {
|
|
11664
|
+
try {
|
|
11665
|
+
if (existsSync32(p)) {
|
|
11666
|
+
const pkg = JSON.parse(readFileSync26(p, "utf-8"));
|
|
11667
|
+
for (const d of Object.keys(pkg.dependencies || {})) directNames.add(d);
|
|
11668
|
+
for (const d of Object.keys(pkg.devDependencies || {})) directNames.add(d);
|
|
11669
|
+
break;
|
|
11670
|
+
}
|
|
11671
|
+
} catch {
|
|
11672
|
+
}
|
|
11673
|
+
}
|
|
11674
|
+
}
|
|
11675
|
+
const skipPatterns = [
|
|
11676
|
+
/^@types\//,
|
|
11677
|
+
/^@esbuild\//,
|
|
11678
|
+
/^@rollup\//,
|
|
11679
|
+
/^@swc\//,
|
|
11680
|
+
/^@parcel\//,
|
|
11681
|
+
/^@biomejs\//
|
|
11682
|
+
];
|
|
11683
|
+
if (directNames.size > 0) {
|
|
11684
|
+
return technologies.filter((t) => directNames.has(t.name)).filter((t) => !skipPatterns.some((p) => p.test(t.name)));
|
|
11685
|
+
}
|
|
11686
|
+
return technologies.filter((t) => !skipPatterns.some((p) => p.test(t.name))).slice(0, 20);
|
|
11687
|
+
}
|
|
11688
|
+
var TECH_KNOWLEDGE, WELL_KNOWN_FEATURES;
|
|
11430
11689
|
var init_generator = __esm({
|
|
11431
11690
|
"src/core/agents/generator.ts"() {
|
|
11432
11691
|
"use strict";
|
|
11433
11692
|
init_workspace2();
|
|
11434
11693
|
init_marker_writer();
|
|
11435
11694
|
init_token_budget();
|
|
11695
|
+
TECH_KNOWLEDGE = {
|
|
11696
|
+
"express": {
|
|
11697
|
+
rules: [
|
|
11698
|
+
"Use Router() for modular route definitions",
|
|
11699
|
+
"Always register error handler middleware last (4 args: err, req, res, next)"
|
|
11700
|
+
],
|
|
11701
|
+
pitfalls: [
|
|
11702
|
+
"Forgetting to call next() in middleware causes request to hang",
|
|
11703
|
+
"Async errors in handlers need explicit try/catch or express-async-errors"
|
|
11704
|
+
]
|
|
11705
|
+
},
|
|
11706
|
+
"hono": {
|
|
11707
|
+
rules: [
|
|
11708
|
+
"Use app.route() for modular route grouping",
|
|
11709
|
+
"Return c.json() or c.text() \u2014 do not use res.send()"
|
|
11710
|
+
],
|
|
11711
|
+
pitfalls: [
|
|
11712
|
+
"Hono middleware must call next() or return a Response \u2014 skipping both hangs the request"
|
|
11713
|
+
]
|
|
11714
|
+
},
|
|
11715
|
+
"better-sqlite3": {
|
|
11716
|
+
rules: [
|
|
11717
|
+
"Use db.prepare().all() for SELECT, db.prepare().run() for INSERT/UPDATE/DELETE",
|
|
11718
|
+
"better-sqlite3 is synchronous \u2014 do NOT use async/await with db calls",
|
|
11719
|
+
"All database access should go through a single module"
|
|
11720
|
+
],
|
|
11721
|
+
pitfalls: [
|
|
11722
|
+
"db.exec() returns nothing \u2014 using it for SELECT gives undefined",
|
|
11723
|
+
'WAL mode must be set once after opening: db.pragma("journal_mode = WAL")'
|
|
11724
|
+
]
|
|
11725
|
+
},
|
|
11726
|
+
"stripe": {
|
|
11727
|
+
rules: [
|
|
11728
|
+
"Always verify webhook signatures before processing events",
|
|
11729
|
+
"Use idempotency keys for payment creation to prevent double-charges"
|
|
11730
|
+
],
|
|
11731
|
+
pitfalls: [
|
|
11732
|
+
"Stripe webhook events may arrive out of order \u2014 handle idempotently",
|
|
11733
|
+
"stripe.webhooks.constructEvent() throws on invalid signature \u2014 wrap in try/catch"
|
|
11734
|
+
]
|
|
11735
|
+
},
|
|
11736
|
+
"jsonwebtoken": {
|
|
11737
|
+
rules: [
|
|
11738
|
+
"Always use jwt.verify() \u2014 never trust jwt.decode() alone",
|
|
11739
|
+
"Set explicit expiration (expiresIn) on all tokens"
|
|
11740
|
+
],
|
|
11741
|
+
pitfalls: [
|
|
11742
|
+
"Using jwt.decode() without verify() is a security vulnerability",
|
|
11743
|
+
'The "none" algorithm attack \u2014 always specify algorithms: ["HS256"]'
|
|
11744
|
+
]
|
|
11745
|
+
},
|
|
11746
|
+
"@modelcontextprotocol/sdk": {
|
|
11747
|
+
rules: [
|
|
11748
|
+
"Use server.tool() to register MCP tools with zod schema validation",
|
|
11749
|
+
"All tool handlers must return { content: [...] } response objects"
|
|
11750
|
+
],
|
|
11751
|
+
pitfalls: [
|
|
11752
|
+
"Tool names must be unique across the server \u2014 duplicates silently overwrite",
|
|
11753
|
+
"Forgetting to call server.connect(transport) means no requests are handled"
|
|
11754
|
+
]
|
|
11755
|
+
},
|
|
11756
|
+
"prisma": {
|
|
11757
|
+
rules: [
|
|
11758
|
+
"Run npx prisma generate after schema changes",
|
|
11759
|
+
"Use transactions for multi-table operations: prisma.$transaction()"
|
|
11760
|
+
],
|
|
11761
|
+
pitfalls: [
|
|
11762
|
+
"Forgetting prisma generate after schema change causes type mismatches at runtime",
|
|
11763
|
+
"N+1 queries \u2014 use include/select to eagerly load relations"
|
|
11764
|
+
]
|
|
11765
|
+
},
|
|
11766
|
+
"zod": {
|
|
11767
|
+
rules: [
|
|
11768
|
+
"Define schemas once, derive TypeScript types with z.infer<typeof schema>",
|
|
11769
|
+
"Use .safeParse() in handlers for graceful error handling"
|
|
11770
|
+
],
|
|
11771
|
+
pitfalls: [
|
|
11772
|
+
".parse() throws on invalid data \u2014 use .safeParse() in request handlers"
|
|
11773
|
+
]
|
|
11774
|
+
},
|
|
11775
|
+
"vitest": {
|
|
11776
|
+
rules: [
|
|
11777
|
+
"Use describe/it/expect patterns for test organization",
|
|
11778
|
+
"Use vi.mock() for module mocking, vi.fn() for function stubs"
|
|
11779
|
+
],
|
|
11780
|
+
pitfalls: [
|
|
11781
|
+
"vi.mock() is hoisted to top of file \u2014 cannot access variables from outer scope"
|
|
11782
|
+
]
|
|
11783
|
+
},
|
|
11784
|
+
"web-tree-sitter": {
|
|
11785
|
+
rules: [
|
|
11786
|
+
"Initialize Parser with await Parser.init() before use",
|
|
11787
|
+
"Load language WASM files with Parser.Language.load()"
|
|
11788
|
+
],
|
|
11789
|
+
pitfalls: [
|
|
11790
|
+
"Parser.init() must complete before any parsing \u2014 race condition if not awaited",
|
|
11791
|
+
"WASM files must be bundled/copied to dist \u2014 not resolved from node_modules at runtime"
|
|
11792
|
+
]
|
|
11793
|
+
},
|
|
11794
|
+
"@xenova/transformers": {
|
|
11795
|
+
rules: [
|
|
11796
|
+
"Use pipeline() for high-level tasks, AutoModel for custom inference",
|
|
11797
|
+
"Cache downloaded models by setting env.cacheDir"
|
|
11798
|
+
],
|
|
11799
|
+
pitfalls: [
|
|
11800
|
+
"First model load downloads weights (~100MB+) \u2014 cache for subsequent runs",
|
|
11801
|
+
"ONNX runtime may fail on some architectures \u2014 test on target platform"
|
|
11802
|
+
]
|
|
11803
|
+
},
|
|
11804
|
+
"chokidar": {
|
|
11805
|
+
rules: [
|
|
11806
|
+
"Use chokidar.watch() with ignored patterns to skip node_modules",
|
|
11807
|
+
'Handle both "add" and "change" events for file watching'
|
|
11808
|
+
],
|
|
11809
|
+
pitfalls: [
|
|
11810
|
+
"Not closing watcher on process exit leaks file handles",
|
|
11811
|
+
"Rapid file changes may fire multiple events \u2014 debounce handlers"
|
|
11812
|
+
]
|
|
11813
|
+
}
|
|
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
|
+
]);
|
|
11436
11828
|
}
|
|
11437
11829
|
});
|
|
11438
11830
|
|
|
@@ -11842,7 +12234,7 @@ var init_lifecycle = __esm({
|
|
|
11842
12234
|
});
|
|
11843
12235
|
|
|
11844
12236
|
// src/core/agents/orchestrator.ts
|
|
11845
|
-
import { existsSync as existsSync34, readdirSync as readdirSync12 } from "fs";
|
|
12237
|
+
import { existsSync as existsSync34, readFileSync as readFileSync27, readdirSync as readdirSync12 } from "fs";
|
|
11846
12238
|
import { join as join35, relative as relative9 } from "path";
|
|
11847
12239
|
function agentsInit(projectPath) {
|
|
11848
12240
|
const paths = initAgentWorkspace(projectPath);
|
|
@@ -11877,11 +12269,13 @@ async function agentsGenerate(options) {
|
|
|
11877
12269
|
result.technologies = technologies.length;
|
|
11878
12270
|
const importGraph = options.importGraph || /* @__PURE__ */ new Map();
|
|
11879
12271
|
const indexedFiles = options.indexedFiles || getIndexedFilesFromFS(projectPath);
|
|
12272
|
+
const fileTechMap = buildFileTechMap(projectPath, indexedFiles, technologies);
|
|
11880
12273
|
const featureInput = {
|
|
11881
12274
|
projectPath,
|
|
11882
12275
|
config: config2.feature_detection,
|
|
11883
12276
|
importGraph,
|
|
11884
|
-
indexedFiles
|
|
12277
|
+
indexedFiles,
|
|
12278
|
+
fileTechMap
|
|
11885
12279
|
};
|
|
11886
12280
|
const features = detectFeatures(featureInput);
|
|
11887
12281
|
result.features = features.length;
|
|
@@ -12099,30 +12493,57 @@ function filterTopTechnologies(technologies) {
|
|
|
12099
12493
|
return technologies.filter((t) => !skipPatterns.some((p) => p.test(t.name))).filter((t) => t.source.includes("lock") || t.source === "package.json").slice(0, 20);
|
|
12100
12494
|
}
|
|
12101
12495
|
function createMinimalIntelligence(projectPath, technologies, features) {
|
|
12496
|
+
const sourceFiles = getIndexedFilesFromFS(projectPath);
|
|
12497
|
+
const codeFiles = sourceFiles.filter(
|
|
12498
|
+
(f) => /\.(ts|tsx|js|jsx|py|go|rs|java|rb|php|cs|cpp|c|h)$/.test(f) && !f.includes("node_modules") && !f.includes("dist")
|
|
12499
|
+
);
|
|
12500
|
+
let totalLines = 0;
|
|
12501
|
+
for (const f of codeFiles.slice(0, 500)) {
|
|
12502
|
+
try {
|
|
12503
|
+
const content = readFileSync27(join35(projectPath, f), "utf-8");
|
|
12504
|
+
totalLines += content.split("\n").length;
|
|
12505
|
+
} catch {
|
|
12506
|
+
}
|
|
12507
|
+
}
|
|
12508
|
+
const extCounts = /* @__PURE__ */ new Map();
|
|
12509
|
+
for (const f of codeFiles) {
|
|
12510
|
+
const ext = f.split(".").pop() || "";
|
|
12511
|
+
const lang = extToLanguage(ext);
|
|
12512
|
+
if (lang) extCounts.set(lang, (extCounts.get(lang) || 0) + 1);
|
|
12513
|
+
}
|
|
12514
|
+
const languages = [...extCounts.entries()].sort((a, b) => b[1] - a[1]).map(([lang]) => lang);
|
|
12515
|
+
const layers = features.map((f) => ({
|
|
12516
|
+
name: f.name.charAt(0).toUpperCase() + f.name.slice(1),
|
|
12517
|
+
directory: f.paths[0]?.replace("/**", "") || f.name,
|
|
12518
|
+
purpose: inferLayerPurpose(f.name),
|
|
12519
|
+
fileCount: f.fileCount
|
|
12520
|
+
}));
|
|
12521
|
+
const testFramework = detectTestFramework(projectPath);
|
|
12102
12522
|
return {
|
|
12103
12523
|
collectedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
12104
12524
|
codebase: {
|
|
12105
|
-
fileCount:
|
|
12106
|
-
totalLines
|
|
12107
|
-
languages:
|
|
12108
|
-
if (t.source.includes("package")) return "typescript";
|
|
12109
|
-
if (t.source.includes("Cargo")) return "rust";
|
|
12110
|
-
if (t.source.includes("go.")) return "go";
|
|
12111
|
-
if (t.source.includes("requirements") || t.source.includes("pyproject")) return "python";
|
|
12112
|
-
return "unknown";
|
|
12113
|
-
}))],
|
|
12525
|
+
fileCount: codeFiles.length,
|
|
12526
|
+
totalLines,
|
|
12527
|
+
languages: languages.length > 0 ? languages : ["typescript"],
|
|
12114
12528
|
keyDirectories: features.map((f) => f.paths[0]?.replace("/**", "") || ""),
|
|
12115
12529
|
symbolCount: 0,
|
|
12116
12530
|
description: "",
|
|
12117
12531
|
architectureNotes: ""
|
|
12118
12532
|
},
|
|
12119
|
-
architecture:
|
|
12533
|
+
architecture: {
|
|
12534
|
+
layers,
|
|
12535
|
+
dataFlow: inferDataFlow(features),
|
|
12536
|
+
keyComponents: [],
|
|
12537
|
+
patternCategories: {},
|
|
12538
|
+
topPatterns: [],
|
|
12539
|
+
functionStats: { total: 0, exported: 0 }
|
|
12540
|
+
},
|
|
12120
12541
|
dependencyHotspots: [],
|
|
12121
12542
|
patterns: [],
|
|
12122
12543
|
decisions: [],
|
|
12123
12544
|
riskFiles: [],
|
|
12124
12545
|
deadCode: null,
|
|
12125
|
-
tests: { framework:
|
|
12546
|
+
tests: { framework: testFramework, testCount: 0, coverageGaps: [], uncoveredFunctions: [] },
|
|
12126
12547
|
recentBugs: [],
|
|
12127
12548
|
changeHotspots: [],
|
|
12128
12549
|
activeFeature: null,
|
|
@@ -12134,6 +12555,96 @@ function createMinimalIntelligence(projectPath, technologies, features) {
|
|
|
12134
12555
|
}))
|
|
12135
12556
|
};
|
|
12136
12557
|
}
|
|
12558
|
+
function buildFileTechMap(projectPath, indexedFiles, technologies) {
|
|
12559
|
+
const techNames = new Set(technologies.map((t) => t.name));
|
|
12560
|
+
const fileTechMap = /* @__PURE__ */ new Map();
|
|
12561
|
+
const sourceFiles = indexedFiles.filter(
|
|
12562
|
+
(f) => /\.(ts|tsx|js|jsx)$/.test(f) && !f.includes("node_modules") && !f.includes("dist")
|
|
12563
|
+
);
|
|
12564
|
+
for (const file2 of sourceFiles.slice(0, 300)) {
|
|
12565
|
+
try {
|
|
12566
|
+
const content = readFileSync27(join35(projectPath, file2), "utf-8");
|
|
12567
|
+
const techs = /* @__PURE__ */ new Set();
|
|
12568
|
+
const importRegex = /(?:from\s+['"]|require\s*\(\s*['"])([^./'"@][^'"]*|@[^/'"]+\/[^'"]+)/g;
|
|
12569
|
+
let match;
|
|
12570
|
+
while ((match = importRegex.exec(content)) !== null) {
|
|
12571
|
+
const pkg = match[1] || "";
|
|
12572
|
+
const normalized = pkg.startsWith("@") ? pkg.split("/").slice(0, 2).join("/") : pkg.split("/")[0] || "";
|
|
12573
|
+
if (normalized && techNames.has(normalized)) {
|
|
12574
|
+
techs.add(normalized);
|
|
12575
|
+
}
|
|
12576
|
+
}
|
|
12577
|
+
if (techs.size > 0) {
|
|
12578
|
+
fileTechMap.set(file2, [...techs]);
|
|
12579
|
+
}
|
|
12580
|
+
} catch {
|
|
12581
|
+
}
|
|
12582
|
+
}
|
|
12583
|
+
return fileTechMap;
|
|
12584
|
+
}
|
|
12585
|
+
function extToLanguage(ext) {
|
|
12586
|
+
const map2 = {
|
|
12587
|
+
ts: "typescript",
|
|
12588
|
+
tsx: "typescript",
|
|
12589
|
+
js: "javascript",
|
|
12590
|
+
jsx: "javascript",
|
|
12591
|
+
py: "python",
|
|
12592
|
+
go: "go",
|
|
12593
|
+
rs: "rust",
|
|
12594
|
+
java: "java",
|
|
12595
|
+
rb: "ruby",
|
|
12596
|
+
php: "php",
|
|
12597
|
+
cs: "c#",
|
|
12598
|
+
cpp: "c++",
|
|
12599
|
+
c: "c",
|
|
12600
|
+
h: "c"
|
|
12601
|
+
};
|
|
12602
|
+
return map2[ext] || null;
|
|
12603
|
+
}
|
|
12604
|
+
function inferLayerPurpose(featureName) {
|
|
12605
|
+
const purposes = {
|
|
12606
|
+
core: "Core business logic and domain modules",
|
|
12607
|
+
server: "MCP server, transports, and request handling",
|
|
12608
|
+
storage: "Database access and persistence layer",
|
|
12609
|
+
indexing: "Code indexing, parsing, and symbol extraction",
|
|
12610
|
+
api: "API routes, handlers, and middleware",
|
|
12611
|
+
auth: "Authentication and authorization",
|
|
12612
|
+
billing: "Payment processing and subscription management",
|
|
12613
|
+
test: "Test fixtures, harness, and evaluation scenarios",
|
|
12614
|
+
doc: "Documentation generation and management",
|
|
12615
|
+
knowledge: "AI knowledge system and skill management",
|
|
12616
|
+
cli: "Command-line interface",
|
|
12617
|
+
base: "Base configuration and shared utilities"
|
|
12618
|
+
};
|
|
12619
|
+
return purposes[featureName] || `${featureName} module`;
|
|
12620
|
+
}
|
|
12621
|
+
function inferDataFlow(features) {
|
|
12622
|
+
const names = features.map((f) => f.name);
|
|
12623
|
+
const flow = [];
|
|
12624
|
+
if (names.includes("server") || names.includes("api")) flow.push("Request");
|
|
12625
|
+
if (names.includes("server")) flow.push("Server");
|
|
12626
|
+
if (names.includes("api")) flow.push("API");
|
|
12627
|
+
if (names.includes("core")) flow.push("Core");
|
|
12628
|
+
if (names.includes("storage")) flow.push("Storage");
|
|
12629
|
+
if (names.includes("indexing")) flow.push("Indexer");
|
|
12630
|
+
return flow.length >= 2 ? flow : [];
|
|
12631
|
+
}
|
|
12632
|
+
function detectTestFramework(projectPath) {
|
|
12633
|
+
const pkgPath = join35(projectPath, "package.json");
|
|
12634
|
+
if (existsSync34(pkgPath)) {
|
|
12635
|
+
try {
|
|
12636
|
+
const pkg = JSON.parse(readFileSync27(pkgPath, "utf-8"));
|
|
12637
|
+
const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
12638
|
+
if (allDeps["vitest"]) return "vitest";
|
|
12639
|
+
if (allDeps["jest"]) return "jest";
|
|
12640
|
+
if (allDeps["mocha"]) return "mocha";
|
|
12641
|
+
if (allDeps["tap"]) return "tap";
|
|
12642
|
+
if (pkg.scripts?.test?.includes("--test")) return "node:test";
|
|
12643
|
+
} catch {
|
|
12644
|
+
}
|
|
12645
|
+
}
|
|
12646
|
+
return "unknown";
|
|
12647
|
+
}
|
|
12137
12648
|
var init_orchestrator = __esm({
|
|
12138
12649
|
"src/core/agents/orchestrator.ts"() {
|
|
12139
12650
|
"use strict";
|
|
@@ -12153,7 +12664,7 @@ var init_orchestrator = __esm({
|
|
|
12153
12664
|
|
|
12154
12665
|
// src/core/agents/migration.ts
|
|
12155
12666
|
import { existsSync as existsSync35, readFileSync as readFileSync28, writeFileSync as writeFileSync20, mkdirSync as mkdirSync18, cpSync, readdirSync as readdirSync13 } from "fs";
|
|
12156
|
-
import { join as join36, relative as relative10, dirname as
|
|
12667
|
+
import { join as join36, relative as relative10, dirname as dirname15 } from "path";
|
|
12157
12668
|
function migrateKnowledge(options) {
|
|
12158
12669
|
const { projectPath, dryRun = false, backup = false } = options;
|
|
12159
12670
|
const result = {
|
|
@@ -12195,7 +12706,7 @@ function migrateKnowledge(options) {
|
|
|
12195
12706
|
continue;
|
|
12196
12707
|
}
|
|
12197
12708
|
if (!dryRun) {
|
|
12198
|
-
mkdirSync18(
|
|
12709
|
+
mkdirSync18(dirname15(targetPath), { recursive: true });
|
|
12199
12710
|
cpSync(skillFile, targetPath);
|
|
12200
12711
|
}
|
|
12201
12712
|
result.migrated.push({
|
|
@@ -42751,22 +43262,32 @@ ${END_MARKER}`;
|
|
|
42751
43262
|
return { content: `${block}
|
|
42752
43263
|
`, changed: true };
|
|
42753
43264
|
}
|
|
42754
|
-
|
|
42755
|
-
|
|
42756
|
-
|
|
42757
|
-
const
|
|
42758
|
-
const
|
|
42759
|
-
|
|
42760
|
-
|
|
42761
|
-
|
|
42762
|
-
|
|
42763
|
-
|
|
42764
|
-
|
|
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
|
+
}
|
|
42765
43279
|
}
|
|
42766
|
-
|
|
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}
|
|
42767
43287
|
|
|
42768
43288
|
${block}
|
|
42769
43289
|
`;
|
|
43290
|
+
}
|
|
42770
43291
|
return { content, changed: content !== existing };
|
|
42771
43292
|
}
|
|
42772
43293
|
function writeManagedFile(targetPath, section, options) {
|
|
@@ -42799,93 +43320,61 @@ function renderPlatformSection(platform, paths, skillIndex, evolutionGuidance) {
|
|
|
42799
43320
|
let attentionSection = "";
|
|
42800
43321
|
if (evolutionGuidance && evolutionGuidance.length > 0) {
|
|
42801
43322
|
attentionSection = `
|
|
42802
|
-
|
|
42803
43323
|
### Skills Needing Attention
|
|
42804
43324
|
${evolutionGuidance.map((g) => `- ${g}`).join("\n")}`;
|
|
42805
43325
|
}
|
|
42806
|
-
return `# CodeImpact
|
|
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.
|
|
42807
43329
|
|
|
42808
|
-
|
|
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
|
|
43340
|
+
|
|
43341
|
+
## MCP Tools (Use FIRST before built-in tools)
|
|
42809
43342
|
|
|
42810
|
-
## Tools
|
|
42811
43343
|
| Task | Tool |
|
|
42812
43344
|
|------|------|
|
|
42813
|
-
|
|
|
42814
|
-
|
|
|
42815
|
-
| Verify
|
|
43345
|
+
| Search code semantically | \`${tool("memory_query")}\` |
|
|
43346
|
+
| Review code before changes | \`${tool("memory_review")}\` |
|
|
43347
|
+
| Verify before committing | \`${tool("memory_verify")}\` |
|
|
42816
43348
|
| Project status | \`${tool("memory_status")}\` |
|
|
42817
43349
|
| Impact analysis | \`${tool("memory_blast_radius")}\` |
|
|
43350
|
+
| Agent system | \`${tool("memory_agents")}\` |
|
|
42818
43351
|
| Build knowledge | \`${tool("memory_evolve")}\` |
|
|
42819
43352
|
|
|
42820
|
-
##
|
|
42821
|
-
1. Run \`${tool("memory_status")}\` \u2014 check \`knowledge_gaps\` for uncovered technologies and high-risk files.
|
|
42822
|
-
2. Read relevant skills from \`knowledge/skills/\` for the current task.
|
|
42823
|
-
|
|
42824
|
-
## Skill Creation Protocol
|
|
43353
|
+
## Agent System
|
|
42825
43354
|
|
|
42826
|
-
|
|
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
|
|
42827
43358
|
|
|
42828
|
-
|
|
42829
|
-
\`${tool("
|
|
42830
|
-
- action="create_skill"
|
|
42831
|
-
- name="technology-or-area-name" (slug format)
|
|
42832
|
-
- description="One line: when to use this skill"
|
|
42833
|
-
- scope="technology|feature|risk|core"
|
|
42834
|
-
- content="Full markdown body (see format below)"
|
|
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".
|
|
42835
43361
|
|
|
42836
|
-
|
|
42837
|
-
\`${tool("memory_evolve")}\` with:
|
|
42838
|
-
- action="improve_skill", skill_id="skill-name"
|
|
42839
|
-
- Patch mode: old_text="exact text to replace", new_text="replacement"
|
|
42840
|
-
- Append mode: section="pitfalls", content="New pitfall to add"
|
|
43362
|
+
## Skill Management
|
|
42841
43363
|
|
|
42842
|
-
|
|
42843
|
-
\`${tool("memory_evolve")}\` with action="list_signals"
|
|
43364
|
+
After completing any task involving 3+ files, create or improve a skill:
|
|
42844
43365
|
|
|
42845
|
-
|
|
42846
|
-
|
|
42847
|
-
\`\`\`markdown
|
|
42848
|
-
---
|
|
42849
|
-
name: better-sqlite3-patterns
|
|
42850
|
-
description: Synchronous database patterns. Use when working with database queries or schema changes.
|
|
42851
|
-
version: 1.0
|
|
42852
|
-
metadata:
|
|
42853
|
-
scope: technology
|
|
42854
|
-
created_by: ai
|
|
42855
|
-
---
|
|
42856
|
-
|
|
42857
|
-
# better-sqlite3 Patterns
|
|
42858
|
-
|
|
42859
|
-
## When to Use
|
|
42860
|
-
When modifying database queries, adding tables, or working with files that import from src/storage/database.ts.
|
|
42861
|
-
|
|
42862
|
-
## Key Facts
|
|
42863
|
-
- Database: .codeimpact/codeimpact.db (SQLite, WAL mode)
|
|
42864
|
-
- API: better-sqlite3 (synchronous, NOT async)
|
|
42865
|
-
|
|
42866
|
-
## Rules
|
|
42867
|
-
- ALL database access goes through database.ts \u2014 never import better-sqlite3 directly.
|
|
42868
|
-
- Use db.prepare().all() for SELECT, db.prepare().run() for INSERT/UPDATE/DELETE.
|
|
42869
|
-
|
|
42870
|
-
## Pitfalls
|
|
42871
|
-
- db.exec() returns nothing. If you use it for SELECT, you get undefined.
|
|
42872
|
-
- better-sqlite3 is synchronous. Do NOT wrap in async/await.
|
|
42873
|
-
|
|
42874
|
-
## Verification
|
|
42875
|
-
- npx tsc --noEmit passes with no type errors on database code.
|
|
42876
|
-
\`\`\`
|
|
43366
|
+
\`${tool("memory_evolve")}\` with action="create_skill" (new) or action="improve_skill" (update)
|
|
42877
43367
|
|
|
42878
43368
|
### Quality Rules
|
|
42879
|
-
-
|
|
42880
|
-
-
|
|
42881
|
-
-
|
|
42882
|
-
- **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"
|
|
42883
43372
|
|
|
42884
43373
|
## Existing Skills
|
|
42885
43374
|
${skillList}
|
|
42886
43375
|
${attentionSection}
|
|
42887
43376
|
|
|
42888
|
-
## Workspace
|
|
43377
|
+
## Knowledge Workspace
|
|
42889
43378
|
\`${paths.root.replace(/\\/g, "/")}\``;
|
|
42890
43379
|
}
|
|
42891
43380
|
var PlatformRuleSync = class {
|