codeimpact 0.3.1 → 0.4.0
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 +1754 -728
- package/dist/index.js.map +4 -4
- 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---/);
|
|
@@ -8644,22 +8663,1290 @@ var init_research_trust = __esm({
|
|
|
8644
8663
|
}
|
|
8645
8664
|
});
|
|
8646
8665
|
|
|
8647
|
-
// src/core/agents/
|
|
8648
|
-
|
|
8666
|
+
// src/core/agents/hardcoded-knowledge.ts
|
|
8667
|
+
var TECH_KNOWLEDGE, FEATURE_NAME_RULES, FEATURE_NAME_PITFALLS, WELL_KNOWN_FEATURES, DIR_PURPOSE_MAP, SCOPE_LABELS, DOC_SOURCE_REGISTRY;
|
|
8668
|
+
var init_hardcoded_knowledge = __esm({
|
|
8669
|
+
"src/core/agents/hardcoded-knowledge.ts"() {
|
|
8670
|
+
"use strict";
|
|
8671
|
+
TECH_KNOWLEDGE = {
|
|
8672
|
+
"express": {
|
|
8673
|
+
rules: [
|
|
8674
|
+
"Use Router() for modular route definitions",
|
|
8675
|
+
"Always register error handler middleware last (4 args: err, req, res, next)"
|
|
8676
|
+
],
|
|
8677
|
+
pitfalls: [
|
|
8678
|
+
"Forgetting to call next() in middleware causes request to hang",
|
|
8679
|
+
"Async errors in handlers need explicit try/catch or express-async-errors"
|
|
8680
|
+
]
|
|
8681
|
+
},
|
|
8682
|
+
"hono": {
|
|
8683
|
+
rules: [
|
|
8684
|
+
"Use app.route() for modular route grouping",
|
|
8685
|
+
"Return c.json() or c.text() \u2014 do not use res.send()"
|
|
8686
|
+
],
|
|
8687
|
+
pitfalls: [
|
|
8688
|
+
"Hono middleware must call next() or return a Response \u2014 skipping both hangs the request"
|
|
8689
|
+
]
|
|
8690
|
+
},
|
|
8691
|
+
"better-sqlite3": {
|
|
8692
|
+
rules: [
|
|
8693
|
+
"Use db.prepare().all() for SELECT, db.prepare().run() for INSERT/UPDATE/DELETE",
|
|
8694
|
+
"better-sqlite3 is synchronous \u2014 do NOT use async/await with db calls",
|
|
8695
|
+
"All database access should go through a single module"
|
|
8696
|
+
],
|
|
8697
|
+
pitfalls: [
|
|
8698
|
+
"db.exec() returns nothing \u2014 using it for SELECT gives undefined",
|
|
8699
|
+
'WAL mode must be set once after opening: db.pragma("journal_mode = WAL")'
|
|
8700
|
+
]
|
|
8701
|
+
},
|
|
8702
|
+
"stripe": {
|
|
8703
|
+
rules: [
|
|
8704
|
+
"Always verify webhook signatures before processing events",
|
|
8705
|
+
"Use idempotency keys for payment creation to prevent double-charges"
|
|
8706
|
+
],
|
|
8707
|
+
pitfalls: [
|
|
8708
|
+
"Stripe webhook events may arrive out of order \u2014 handle idempotently",
|
|
8709
|
+
"stripe.webhooks.constructEvent() throws on invalid signature \u2014 wrap in try/catch"
|
|
8710
|
+
]
|
|
8711
|
+
},
|
|
8712
|
+
"jsonwebtoken": {
|
|
8713
|
+
rules: [
|
|
8714
|
+
"Always use jwt.verify() \u2014 never trust jwt.decode() alone",
|
|
8715
|
+
"Set explicit expiration (expiresIn) on all tokens"
|
|
8716
|
+
],
|
|
8717
|
+
pitfalls: [
|
|
8718
|
+
"Using jwt.decode() without verify() is a security vulnerability",
|
|
8719
|
+
'The "none" algorithm attack \u2014 always specify algorithms: ["HS256"]'
|
|
8720
|
+
]
|
|
8721
|
+
},
|
|
8722
|
+
"@modelcontextprotocol/sdk": {
|
|
8723
|
+
rules: [
|
|
8724
|
+
"Use server.tool() to register MCP tools with zod schema validation",
|
|
8725
|
+
"All tool handlers must return { content: [...] } response objects"
|
|
8726
|
+
],
|
|
8727
|
+
pitfalls: [
|
|
8728
|
+
"Tool names must be unique across the server \u2014 duplicates silently overwrite",
|
|
8729
|
+
"Forgetting to call server.connect(transport) means no requests are handled"
|
|
8730
|
+
]
|
|
8731
|
+
},
|
|
8732
|
+
"prisma": {
|
|
8733
|
+
rules: [
|
|
8734
|
+
"Run npx prisma generate after schema changes",
|
|
8735
|
+
"Use transactions for multi-table operations: prisma.$transaction()"
|
|
8736
|
+
],
|
|
8737
|
+
pitfalls: [
|
|
8738
|
+
"Forgetting prisma generate after schema change causes type mismatches at runtime",
|
|
8739
|
+
"N+1 queries \u2014 use include/select to eagerly load relations"
|
|
8740
|
+
]
|
|
8741
|
+
},
|
|
8742
|
+
"zod": {
|
|
8743
|
+
rules: [
|
|
8744
|
+
"Define schemas once, derive TypeScript types with z.infer<typeof schema>",
|
|
8745
|
+
"Use .safeParse() in handlers for graceful error handling"
|
|
8746
|
+
],
|
|
8747
|
+
pitfalls: [
|
|
8748
|
+
".parse() throws on invalid data \u2014 use .safeParse() in request handlers"
|
|
8749
|
+
]
|
|
8750
|
+
},
|
|
8751
|
+
"vitest": {
|
|
8752
|
+
rules: [
|
|
8753
|
+
"Use describe/it/expect patterns for test organization",
|
|
8754
|
+
"Use vi.mock() for module mocking, vi.fn() for function stubs"
|
|
8755
|
+
],
|
|
8756
|
+
pitfalls: [
|
|
8757
|
+
"vi.mock() is hoisted to top of file \u2014 cannot access variables from outer scope"
|
|
8758
|
+
]
|
|
8759
|
+
},
|
|
8760
|
+
"web-tree-sitter": {
|
|
8761
|
+
rules: [
|
|
8762
|
+
"Initialize Parser with await Parser.init() before use",
|
|
8763
|
+
"Load language WASM files with Parser.Language.load()"
|
|
8764
|
+
],
|
|
8765
|
+
pitfalls: [
|
|
8766
|
+
"Parser.init() must complete before any parsing \u2014 race condition if not awaited",
|
|
8767
|
+
"WASM files must be bundled/copied to dist \u2014 not resolved from node_modules at runtime"
|
|
8768
|
+
]
|
|
8769
|
+
},
|
|
8770
|
+
"@xenova/transformers": {
|
|
8771
|
+
rules: [
|
|
8772
|
+
"Use pipeline() for high-level tasks, AutoModel for custom inference",
|
|
8773
|
+
"Cache downloaded models by setting env.cacheDir"
|
|
8774
|
+
],
|
|
8775
|
+
pitfalls: [
|
|
8776
|
+
"First model load downloads weights (~100MB+) \u2014 cache for subsequent runs",
|
|
8777
|
+
"ONNX runtime may fail on some architectures \u2014 test on target platform"
|
|
8778
|
+
]
|
|
8779
|
+
},
|
|
8780
|
+
"chokidar": {
|
|
8781
|
+
rules: [
|
|
8782
|
+
"Use chokidar.watch() with ignored patterns to skip node_modules",
|
|
8783
|
+
'Handle both "add" and "change" events for file watching'
|
|
8784
|
+
],
|
|
8785
|
+
pitfalls: [
|
|
8786
|
+
"Not closing watcher on process exit leaks file handles",
|
|
8787
|
+
"Rapid file changes may fire multiple events \u2014 debounce handlers"
|
|
8788
|
+
]
|
|
8789
|
+
},
|
|
8790
|
+
"react": {
|
|
8791
|
+
rules: [
|
|
8792
|
+
"Components must be pure \u2014 same props must produce same output",
|
|
8793
|
+
"Use useState for local UI state, useReducer for complex state logic",
|
|
8794
|
+
"Memoize expensive computations with useMemo, callbacks with useCallback"
|
|
8795
|
+
],
|
|
8796
|
+
pitfalls: [
|
|
8797
|
+
"Missing key prop in lists causes React reconciliation bugs \u2014 always use stable keys",
|
|
8798
|
+
"useEffect with missing deps causes stale closures \u2014 lint rules catch most cases",
|
|
8799
|
+
"Setting state during render causes infinite re-render loop"
|
|
8800
|
+
]
|
|
8801
|
+
},
|
|
8802
|
+
"react-router-dom": {
|
|
8803
|
+
rules: [
|
|
8804
|
+
"Use <Link> for navigation \u2014 never use <a href> for internal routes",
|
|
8805
|
+
"Use useNavigate() for programmatic navigation in event handlers"
|
|
8806
|
+
],
|
|
8807
|
+
pitfalls: [
|
|
8808
|
+
"Routes must be nested under a <Router> provider \u2014 missing it causes runtime crash",
|
|
8809
|
+
"useSearchParams() returns URLSearchParams \u2014 .get() returns null not undefined"
|
|
8810
|
+
]
|
|
8811
|
+
},
|
|
8812
|
+
"react-hook-form": {
|
|
8813
|
+
rules: [
|
|
8814
|
+
"Use register() for uncontrolled inputs, Controller for controlled (MUI, Radix)",
|
|
8815
|
+
"Define validation with zod resolver \u2014 avoid inline validate functions"
|
|
8816
|
+
],
|
|
8817
|
+
pitfalls: [
|
|
8818
|
+
"Forgetting to call handleSubmit() wrapper \u2014 form submits without validation",
|
|
8819
|
+
"watch() causes re-render on every change \u2014 use getValues() for one-time reads"
|
|
8820
|
+
]
|
|
8821
|
+
},
|
|
8822
|
+
"tailwindcss": {
|
|
8823
|
+
rules: [
|
|
8824
|
+
"Use Tailwind utility classes \u2014 avoid custom CSS unless absolutely necessary",
|
|
8825
|
+
"Use cn() or clsx() for conditional class merging"
|
|
8826
|
+
],
|
|
8827
|
+
pitfalls: [
|
|
8828
|
+
"Dynamic class names like `bg-${color}-500` are purged \u2014 use full class names",
|
|
8829
|
+
"Order matters for conflicting utilities \u2014 last class wins"
|
|
8830
|
+
]
|
|
8831
|
+
},
|
|
8832
|
+
"vite": {
|
|
8833
|
+
rules: [
|
|
8834
|
+
"Use import.meta.env for env variables \u2014 must be prefixed with VITE_",
|
|
8835
|
+
"Configure aliases in vite.config.ts resolve.alias"
|
|
8836
|
+
],
|
|
8837
|
+
pitfalls: [
|
|
8838
|
+
"process.env is not available in Vite \u2014 use import.meta.env instead",
|
|
8839
|
+
"Environment variables without VITE_ prefix are not exposed to client code"
|
|
8840
|
+
]
|
|
8841
|
+
},
|
|
8842
|
+
"next": {
|
|
8843
|
+
rules: [
|
|
8844
|
+
'Use server components by default \u2014 add "use client" only when needed',
|
|
8845
|
+
"Data fetching in server components \u2014 no useEffect for initial data"
|
|
8846
|
+
],
|
|
8847
|
+
pitfalls: [
|
|
8848
|
+
"Importing client-only code (useState, useEffect) in server components crashes build",
|
|
8849
|
+
"next/image requires explicit width/height or fill prop \u2014 missing causes error"
|
|
8850
|
+
]
|
|
8851
|
+
},
|
|
8852
|
+
"framer-motion": {
|
|
8853
|
+
rules: [
|
|
8854
|
+
"Use motion.div (not regular div) for animated elements",
|
|
8855
|
+
"Define variants objects for reusable animation states"
|
|
8856
|
+
],
|
|
8857
|
+
pitfalls: [
|
|
8858
|
+
"AnimatePresence requires direct children to have unique key props",
|
|
8859
|
+
"Exit animations only work when component is direct child of AnimatePresence"
|
|
8860
|
+
]
|
|
8861
|
+
},
|
|
8862
|
+
"d3": {
|
|
8863
|
+
rules: [
|
|
8864
|
+
"Use D3 for data transforms/scales, React for DOM rendering \u2014 avoid d3.select in React",
|
|
8865
|
+
"Use useRef to get DOM reference for D3 direct DOM manipulation when needed"
|
|
8866
|
+
],
|
|
8867
|
+
pitfalls: [
|
|
8868
|
+
"Mixing D3 DOM manipulation with React causes conflicts \u2014 pick one rendering strategy",
|
|
8869
|
+
"D3 scales are mutable \u2014 creating them in render without useMemo causes bugs"
|
|
8870
|
+
]
|
|
8871
|
+
},
|
|
8872
|
+
"zustand": {
|
|
8873
|
+
rules: [
|
|
8874
|
+
"Create stores with create() \u2014 use selectors to prevent unnecessary re-renders",
|
|
8875
|
+
"Keep store state serializable \u2014 no functions or class instances in state"
|
|
8876
|
+
],
|
|
8877
|
+
pitfalls: [
|
|
8878
|
+
"Not using selectors subscribes to entire store \u2014 causes excess re-renders",
|
|
8879
|
+
"Mutating state directly (state.x = y) does not trigger re-render \u2014 use set()"
|
|
8880
|
+
]
|
|
8881
|
+
},
|
|
8882
|
+
"@tanstack/react-query": {
|
|
8883
|
+
rules: [
|
|
8884
|
+
"Use useQuery for GET, useMutation for POST/PUT/DELETE",
|
|
8885
|
+
"Set staleTime to avoid unnecessary refetches \u2014 default 0 refetches every mount"
|
|
8886
|
+
],
|
|
8887
|
+
pitfalls: [
|
|
8888
|
+
"Forgetting queryClient.invalidateQueries after mutation shows stale data",
|
|
8889
|
+
"Query keys must be serializable arrays \u2014 objects in keys cause cache misses"
|
|
8890
|
+
]
|
|
8891
|
+
}
|
|
8892
|
+
};
|
|
8893
|
+
FEATURE_NAME_RULES = {
|
|
8894
|
+
server: [
|
|
8895
|
+
"All tool/resource handlers must validate inputs before processing",
|
|
8896
|
+
"Return structured error responses \u2014 never throw raw errors to clients"
|
|
8897
|
+
],
|
|
8898
|
+
storage: [
|
|
8899
|
+
"All database access should go through this module \u2014 no direct imports elsewhere",
|
|
8900
|
+
"Use transactions for multi-step operations to ensure consistency"
|
|
8901
|
+
],
|
|
8902
|
+
indexing: [
|
|
8903
|
+
"Index operations should be idempotent \u2014 reindexing same file produces same result",
|
|
8904
|
+
"Handle parse errors gracefully \u2014 a broken file should not crash the indexer"
|
|
8905
|
+
],
|
|
8906
|
+
core: [
|
|
8907
|
+
"Core modules should have no side effects on import",
|
|
8908
|
+
"Export types alongside functions for downstream consumers"
|
|
8909
|
+
],
|
|
8910
|
+
test: [
|
|
8911
|
+
"Tests should be deterministic \u2014 no reliance on external services or timing",
|
|
8912
|
+
"Clean up temporary files and directories in afterEach/afterAll hooks"
|
|
8913
|
+
],
|
|
8914
|
+
knowledge: [
|
|
8915
|
+
"Skill files must follow the agentskills.io SKILL.md format",
|
|
8916
|
+
"Generated content must use marker comments to preserve manual edits"
|
|
8917
|
+
],
|
|
8918
|
+
doc: [
|
|
8919
|
+
"Generated docs must be kept in sync with source code changes",
|
|
8920
|
+
"Use relative paths for internal links"
|
|
8921
|
+
],
|
|
8922
|
+
auth: [
|
|
8923
|
+
"Never store plain-text passwords \u2014 use bcrypt or argon2",
|
|
8924
|
+
"Validate and sanitize all user inputs before processing"
|
|
8925
|
+
],
|
|
8926
|
+
api: [
|
|
8927
|
+
"All endpoints must validate request bodies/params before processing",
|
|
8928
|
+
"Return consistent error response shapes across all endpoints"
|
|
8929
|
+
],
|
|
8930
|
+
components: [
|
|
8931
|
+
"Components should be pure \u2014 derive UI from props, avoid internal side effects",
|
|
8932
|
+
"Use composition over inheritance \u2014 prefer small, focused components",
|
|
8933
|
+
"Co-locate styles, types, and tests with the component file"
|
|
8934
|
+
],
|
|
8935
|
+
pages: [
|
|
8936
|
+
"Pages handle routing and data fetching \u2014 delegate UI to components",
|
|
8937
|
+
"Keep page components thin \u2014 extract business logic to hooks or services"
|
|
8938
|
+
],
|
|
8939
|
+
hooks: [
|
|
8940
|
+
'Custom hooks must start with "use" prefix',
|
|
8941
|
+
"Keep hooks focused on one concern \u2014 avoid monolithic hooks",
|
|
8942
|
+
"Hooks should be reusable \u2014 avoid coupling to specific component internals"
|
|
8943
|
+
],
|
|
8944
|
+
layouts: [
|
|
8945
|
+
"Layouts define page structure \u2014 children should not assume layout behavior",
|
|
8946
|
+
"Keep layouts thin \u2014 avoid business logic in layout wrappers"
|
|
8947
|
+
],
|
|
8948
|
+
views: [
|
|
8949
|
+
"Views handle routing and data fetching \u2014 delegate UI to components",
|
|
8950
|
+
"Keep view components thin \u2014 extract business logic to hooks or services"
|
|
8951
|
+
],
|
|
8952
|
+
services: [
|
|
8953
|
+
"Services handle API communication \u2014 keep HTTP details out of components",
|
|
8954
|
+
"Return typed responses \u2014 avoid returning raw API responses"
|
|
8955
|
+
],
|
|
8956
|
+
stores: [
|
|
8957
|
+
"Keep store state minimal \u2014 derive computed values instead of storing them",
|
|
8958
|
+
"Actions should be thin \u2014 delegate complex logic to services"
|
|
8959
|
+
],
|
|
8960
|
+
store: [
|
|
8961
|
+
"Keep store state minimal \u2014 derive computed values instead of storing them",
|
|
8962
|
+
"Actions should be thin \u2014 delegate complex logic to services"
|
|
8963
|
+
],
|
|
8964
|
+
contexts: [
|
|
8965
|
+
"Context should hold only values that many components need \u2014 avoid overuse",
|
|
8966
|
+
"Split large contexts into focused providers to prevent unnecessary re-renders"
|
|
8967
|
+
],
|
|
8968
|
+
providers: [
|
|
8969
|
+
"Provider order matters \u2014 auth before data fetching, theme before UI",
|
|
8970
|
+
"Keep providers thin \u2014 initialize services, do not embed business logic"
|
|
8971
|
+
],
|
|
8972
|
+
routes: [
|
|
8973
|
+
"Route definitions should be declarative \u2014 avoid complex inline logic",
|
|
8974
|
+
"Use lazy loading for route components to optimize bundle size"
|
|
8975
|
+
]
|
|
8976
|
+
};
|
|
8977
|
+
FEATURE_NAME_PITFALLS = {
|
|
8978
|
+
server: [
|
|
8979
|
+
"Unhandled promise rejections in handlers crash the process \u2014 always catch async errors",
|
|
8980
|
+
"Adding middleware order matters \u2014 auth before route handlers"
|
|
8981
|
+
],
|
|
8982
|
+
storage: [
|
|
8983
|
+
"Forgetting to close database connections leaks file handles",
|
|
8984
|
+
"Concurrent writes without transactions may cause data corruption"
|
|
8985
|
+
],
|
|
8986
|
+
indexing: [
|
|
8987
|
+
"Large files can cause out-of-memory \u2014 set size limits on parsed content",
|
|
8988
|
+
"File paths must be normalized (forward slashes) for cross-platform compatibility"
|
|
8989
|
+
],
|
|
8990
|
+
core: [
|
|
8991
|
+
"Circular imports between core modules cause runtime errors \u2014 check dependency direction",
|
|
8992
|
+
"Changing public API signatures breaks downstream consumers"
|
|
8993
|
+
],
|
|
8994
|
+
test: [
|
|
8995
|
+
"Tests sharing mutable state between runs cause flaky failures",
|
|
8996
|
+
"Temp directories not cleaned up fill disk over repeated test runs"
|
|
8997
|
+
],
|
|
8998
|
+
components: [
|
|
8999
|
+
"Missing key prop in lists causes React reconciliation bugs \u2014 always use stable keys",
|
|
9000
|
+
"Direct DOM manipulation bypasses React \u2014 use refs only when necessary",
|
|
9001
|
+
"Prop drilling through 3+ levels signals need for context or composition"
|
|
9002
|
+
],
|
|
9003
|
+
pages: [
|
|
9004
|
+
"Data fetching in render causes waterfall requests \u2014 use loaders or top-level hooks",
|
|
9005
|
+
"Missing error boundaries let one page crash the whole app"
|
|
9006
|
+
],
|
|
9007
|
+
hooks: [
|
|
9008
|
+
"Missing deps in useEffect cause stale closures \u2014 React will warn but won't auto-fix",
|
|
9009
|
+
"Hooks called conditionally break React \u2014 hooks must be called in same order every render"
|
|
9010
|
+
],
|
|
9011
|
+
services: [
|
|
9012
|
+
"Not handling API errors shows raw errors to users \u2014 always catch and transform",
|
|
9013
|
+
"Caching stale data causes UI inconsistency \u2014 invalidate cache on mutations"
|
|
9014
|
+
],
|
|
9015
|
+
stores: [
|
|
9016
|
+
"Mutating state directly causes silent bugs \u2014 always return new references",
|
|
9017
|
+
"Large store updates trigger unnecessary re-renders \u2014 split into focused slices"
|
|
9018
|
+
],
|
|
9019
|
+
store: [
|
|
9020
|
+
"Mutating state directly causes silent bugs \u2014 always return new references",
|
|
9021
|
+
"Large store updates trigger unnecessary re-renders \u2014 split into focused slices"
|
|
9022
|
+
]
|
|
9023
|
+
};
|
|
9024
|
+
WELL_KNOWN_FEATURES = /* @__PURE__ */ new Set([
|
|
9025
|
+
"server",
|
|
9026
|
+
"storage",
|
|
9027
|
+
"indexing",
|
|
9028
|
+
"core",
|
|
9029
|
+
"test",
|
|
9030
|
+
"knowledge",
|
|
9031
|
+
"doc",
|
|
9032
|
+
"auth",
|
|
9033
|
+
"api",
|
|
9034
|
+
"billing",
|
|
9035
|
+
"cli",
|
|
9036
|
+
"components",
|
|
9037
|
+
"pages",
|
|
9038
|
+
"hooks",
|
|
9039
|
+
"layouts",
|
|
9040
|
+
"views",
|
|
9041
|
+
"services",
|
|
9042
|
+
"stores",
|
|
9043
|
+
"store",
|
|
9044
|
+
"contexts",
|
|
9045
|
+
"providers",
|
|
9046
|
+
"routes"
|
|
9047
|
+
]);
|
|
9048
|
+
DIR_PURPOSE_MAP = {
|
|
9049
|
+
// Backend
|
|
9050
|
+
core: "Core business logic and domain modules",
|
|
9051
|
+
server: "Server, transports, and request handling",
|
|
9052
|
+
storage: "Database access and persistence",
|
|
9053
|
+
indexing: "Code indexing and symbol extraction",
|
|
9054
|
+
api: "API routes and handlers",
|
|
9055
|
+
auth: "Authentication and authorization",
|
|
9056
|
+
billing: "Payment processing",
|
|
9057
|
+
test: "Tests and evaluation harness",
|
|
9058
|
+
tests: "Tests and evaluation harness",
|
|
9059
|
+
doc: "Documentation",
|
|
9060
|
+
knowledge: "AI knowledge system",
|
|
9061
|
+
cli: "Command-line interface",
|
|
9062
|
+
base: "Base configuration and fixtures",
|
|
9063
|
+
src: "Application source code",
|
|
9064
|
+
lib: "Shared utilities",
|
|
9065
|
+
// Frontend / React
|
|
9066
|
+
components: "Reusable UI components",
|
|
9067
|
+
pages: "Page-level route components",
|
|
9068
|
+
hooks: "Custom React hooks",
|
|
9069
|
+
layouts: "Page layout wrappers",
|
|
9070
|
+
views: "View components (page-level)",
|
|
9071
|
+
features: "Feature modules (co-located logic + UI)",
|
|
9072
|
+
services: "API client and service layer",
|
|
9073
|
+
stores: "State management (stores/slices)",
|
|
9074
|
+
store: "State management (stores/slices)",
|
|
9075
|
+
utils: "Utility functions and helpers",
|
|
9076
|
+
contexts: "React context providers",
|
|
9077
|
+
providers: "App-level providers and wrappers",
|
|
9078
|
+
routes: "Route definitions and navigation",
|
|
9079
|
+
assets: "Static assets (images, fonts, icons)",
|
|
9080
|
+
styles: "Global styles and theme configuration",
|
|
9081
|
+
types: "TypeScript type definitions",
|
|
9082
|
+
config: "App configuration",
|
|
9083
|
+
middleware: "Request/response middleware",
|
|
9084
|
+
models: "Data models and schemas",
|
|
9085
|
+
controllers: "Request handlers (MVC)",
|
|
9086
|
+
resolvers: "GraphQL resolvers",
|
|
9087
|
+
schemas: "Data schemas and validation",
|
|
9088
|
+
migrations: "Database migrations",
|
|
9089
|
+
// Go
|
|
9090
|
+
cmd: "CLI entry points and commands",
|
|
9091
|
+
pkg: "Reusable library packages",
|
|
9092
|
+
internal: "Private packages (unexported)",
|
|
9093
|
+
// Rust
|
|
9094
|
+
bin: "Binary entry points",
|
|
9095
|
+
benches: "Benchmarks",
|
|
9096
|
+
// Python
|
|
9097
|
+
templates: "Template files (HTML, Jinja2)",
|
|
9098
|
+
static: "Static files for web serving",
|
|
9099
|
+
management: "Management commands (Django)",
|
|
9100
|
+
// General
|
|
9101
|
+
scripts: "Build/deploy/automation scripts",
|
|
9102
|
+
tools: "Developer tools and utilities",
|
|
9103
|
+
docs: "Documentation",
|
|
9104
|
+
examples: "Example code and demos",
|
|
9105
|
+
fixtures: "Test fixtures and sample data",
|
|
9106
|
+
mocks: "Mock objects for testing",
|
|
9107
|
+
helpers: "Helper functions and utilities",
|
|
9108
|
+
adapters: "External service adapters",
|
|
9109
|
+
ports: "Port interfaces (hexagonal architecture)",
|
|
9110
|
+
domain: "Domain logic (DDD)",
|
|
9111
|
+
entities: "Domain entities",
|
|
9112
|
+
repositories: "Data access repositories",
|
|
9113
|
+
handlers: "Request/event handlers",
|
|
9114
|
+
events: "Event definitions and dispatchers",
|
|
9115
|
+
jobs: "Background jobs and workers",
|
|
9116
|
+
workers: "Background workers",
|
|
9117
|
+
queues: "Job queue definitions",
|
|
9118
|
+
cron: "Scheduled task definitions"
|
|
9119
|
+
};
|
|
9120
|
+
SCOPE_LABELS = {
|
|
9121
|
+
"@radix-ui": "Radix UI (shadcn/ui primitives)",
|
|
9122
|
+
"@tanstack": "TanStack (query/router/table)",
|
|
9123
|
+
"@trpc": "tRPC",
|
|
9124
|
+
"@nestjs": "NestJS",
|
|
9125
|
+
"@prisma": "Prisma",
|
|
9126
|
+
"@aws-sdk": "AWS SDK",
|
|
9127
|
+
"@google-cloud": "Google Cloud SDK",
|
|
9128
|
+
"@azure": "Azure SDK",
|
|
9129
|
+
"@mui": "Material UI",
|
|
9130
|
+
"@chakra-ui": "Chakra UI",
|
|
9131
|
+
"@headlessui": "Headless UI",
|
|
9132
|
+
"@mantine": "Mantine",
|
|
9133
|
+
"@emotion": "Emotion CSS",
|
|
9134
|
+
"@testing-library": "Testing Library",
|
|
9135
|
+
"@storybook": "Storybook",
|
|
9136
|
+
"@sentry": "Sentry",
|
|
9137
|
+
"@opentelemetry": "OpenTelemetry",
|
|
9138
|
+
"@supabase": "Supabase",
|
|
9139
|
+
"@firebase": "Firebase",
|
|
9140
|
+
"@stripe": "Stripe SDK"
|
|
9141
|
+
};
|
|
9142
|
+
DOC_SOURCE_REGISTRY = [
|
|
9143
|
+
{
|
|
9144
|
+
packageNames: ["express"],
|
|
9145
|
+
docsUrl: "https://expressjs.com/en/api.html",
|
|
9146
|
+
apiRefUrl: "https://expressjs.com/en/4x/api.html",
|
|
9147
|
+
changelogUrl: "https://github.com/expressjs/express/blob/master/History.md"
|
|
9148
|
+
},
|
|
9149
|
+
{
|
|
9150
|
+
packageNames: ["react", "react-dom"],
|
|
9151
|
+
docsUrl: "https://react.dev/reference/react",
|
|
9152
|
+
apiRefUrl: "https://react.dev/reference/react",
|
|
9153
|
+
changelogUrl: "https://github.com/facebook/react/blob/main/CHANGELOG.md"
|
|
9154
|
+
},
|
|
9155
|
+
{
|
|
9156
|
+
packageNames: ["next"],
|
|
9157
|
+
docsUrl: "https://nextjs.org/docs",
|
|
9158
|
+
apiRefUrl: "https://nextjs.org/docs/api-reference",
|
|
9159
|
+
changelogUrl: "https://github.com/vercel/next.js/releases"
|
|
9160
|
+
},
|
|
9161
|
+
{
|
|
9162
|
+
packageNames: ["typescript"],
|
|
9163
|
+
docsUrl: "https://www.typescriptlang.org/docs/",
|
|
9164
|
+
changelogUrl: "https://www.typescriptlang.org/docs/handbook/release-notes/overview.html"
|
|
9165
|
+
},
|
|
9166
|
+
{
|
|
9167
|
+
packageNames: ["vitest"],
|
|
9168
|
+
docsUrl: "https://vitest.dev/api/",
|
|
9169
|
+
apiRefUrl: "https://vitest.dev/api/",
|
|
9170
|
+
changelogUrl: "https://github.com/vitest-dev/vitest/releases"
|
|
9171
|
+
},
|
|
9172
|
+
{
|
|
9173
|
+
packageNames: ["jest"],
|
|
9174
|
+
docsUrl: "https://jestjs.io/docs/api",
|
|
9175
|
+
apiRefUrl: "https://jestjs.io/docs/expect"
|
|
9176
|
+
},
|
|
9177
|
+
{
|
|
9178
|
+
packageNames: ["prisma", "@prisma/client"],
|
|
9179
|
+
docsUrl: "https://www.prisma.io/docs",
|
|
9180
|
+
apiRefUrl: "https://www.prisma.io/docs/reference/api-reference",
|
|
9181
|
+
changelogUrl: "https://github.com/prisma/prisma/releases"
|
|
9182
|
+
},
|
|
9183
|
+
{
|
|
9184
|
+
packageNames: ["stripe"],
|
|
9185
|
+
docsUrl: "https://docs.stripe.com/api",
|
|
9186
|
+
changelogUrl: "https://docs.stripe.com/changelog"
|
|
9187
|
+
},
|
|
9188
|
+
{
|
|
9189
|
+
packageNames: ["@modelcontextprotocol/sdk"],
|
|
9190
|
+
docsUrl: "https://modelcontextprotocol.io/docs",
|
|
9191
|
+
apiRefUrl: "https://github.com/modelcontextprotocol/typescript-sdk"
|
|
9192
|
+
},
|
|
9193
|
+
{
|
|
9194
|
+
packageNames: ["better-sqlite3"],
|
|
9195
|
+
docsUrl: "https://github.com/WiseLibs/better-sqlite3/blob/master/docs/api.md",
|
|
9196
|
+
apiRefUrl: "https://github.com/WiseLibs/better-sqlite3/blob/master/docs/api.md"
|
|
9197
|
+
},
|
|
9198
|
+
{
|
|
9199
|
+
packageNames: ["zod"],
|
|
9200
|
+
docsUrl: "https://zod.dev/",
|
|
9201
|
+
apiRefUrl: "https://zod.dev/"
|
|
9202
|
+
},
|
|
9203
|
+
{
|
|
9204
|
+
packageNames: ["tailwindcss"],
|
|
9205
|
+
docsUrl: "https://tailwindcss.com/docs"
|
|
9206
|
+
},
|
|
9207
|
+
{
|
|
9208
|
+
packageNames: ["vite"],
|
|
9209
|
+
docsUrl: "https://vitejs.dev/guide/",
|
|
9210
|
+
apiRefUrl: "https://vitejs.dev/config/"
|
|
9211
|
+
},
|
|
9212
|
+
{
|
|
9213
|
+
packageNames: ["passport"],
|
|
9214
|
+
docsUrl: "https://www.passportjs.org/docs/"
|
|
9215
|
+
},
|
|
9216
|
+
{
|
|
9217
|
+
packageNames: ["jsonwebtoken"],
|
|
9218
|
+
docsUrl: "https://github.com/auth0/node-jsonwebtoken#readme"
|
|
9219
|
+
},
|
|
9220
|
+
{
|
|
9221
|
+
packageNames: ["axios"],
|
|
9222
|
+
docsUrl: "https://axios-http.com/docs/intro",
|
|
9223
|
+
apiRefUrl: "https://axios-http.com/docs/api_intro"
|
|
9224
|
+
},
|
|
9225
|
+
{
|
|
9226
|
+
packageNames: ["fastify"],
|
|
9227
|
+
docsUrl: "https://fastify.dev/docs/latest/",
|
|
9228
|
+
apiRefUrl: "https://fastify.dev/docs/latest/Reference/"
|
|
9229
|
+
},
|
|
9230
|
+
{
|
|
9231
|
+
packageNames: ["drizzle-orm"],
|
|
9232
|
+
docsUrl: "https://orm.drizzle.team/docs/overview"
|
|
9233
|
+
},
|
|
9234
|
+
{
|
|
9235
|
+
packageNames: ["trpc", "@trpc/server", "@trpc/client"],
|
|
9236
|
+
docsUrl: "https://trpc.io/docs"
|
|
9237
|
+
},
|
|
9238
|
+
{
|
|
9239
|
+
packageNames: ["hono"],
|
|
9240
|
+
docsUrl: "https://hono.dev/docs/",
|
|
9241
|
+
apiRefUrl: "https://hono.dev/docs/api/hono"
|
|
9242
|
+
}
|
|
9243
|
+
];
|
|
9244
|
+
}
|
|
9245
|
+
});
|
|
9246
|
+
|
|
9247
|
+
// src/core/agents/universal-inference.ts
|
|
9248
|
+
import { existsSync as existsSync25, readFileSync as readFileSync20, readdirSync as readdirSync7 } from "fs";
|
|
8649
9249
|
import { join as join25 } from "path";
|
|
9250
|
+
function inferTechRulesAndPitfalls(tech, researchDir) {
|
|
9251
|
+
const result = { rules: [], pitfalls: [] };
|
|
9252
|
+
if (researchDir) {
|
|
9253
|
+
const research = extractRulesFromResearch(tech, researchDir);
|
|
9254
|
+
result.rules.push(...research.rules);
|
|
9255
|
+
result.pitfalls.push(...research.pitfalls);
|
|
9256
|
+
}
|
|
9257
|
+
const hardcoded = TECH_KNOWLEDGE[tech.name];
|
|
9258
|
+
if (hardcoded) {
|
|
9259
|
+
for (const rule of hardcoded.rules) {
|
|
9260
|
+
if (!result.rules.includes(rule)) result.rules.push(rule);
|
|
9261
|
+
}
|
|
9262
|
+
for (const pitfall of hardcoded.pitfalls) {
|
|
9263
|
+
if (!result.pitfalls.includes(pitfall)) result.pitfalls.push(pitfall);
|
|
9264
|
+
}
|
|
9265
|
+
}
|
|
9266
|
+
if (result.rules.length === 0 || result.pitfalls.length === 0) {
|
|
9267
|
+
const category = classifyTechnology(tech);
|
|
9268
|
+
const categoryKnowledge = CATEGORY_RULES[category];
|
|
9269
|
+
if (categoryKnowledge) {
|
|
9270
|
+
if (result.rules.length === 0) result.rules.push(...categoryKnowledge.rules);
|
|
9271
|
+
if (result.pitfalls.length === 0) result.pitfalls.push(...categoryKnowledge.pitfalls);
|
|
9272
|
+
}
|
|
9273
|
+
}
|
|
9274
|
+
if (result.rules.length === 0) {
|
|
9275
|
+
result.rules.push(
|
|
9276
|
+
`Follow ${tech.name} documentation for API usage patterns`,
|
|
9277
|
+
"Pin dependency version to avoid unexpected breaking changes"
|
|
9278
|
+
);
|
|
9279
|
+
}
|
|
9280
|
+
if (result.pitfalls.length === 0) {
|
|
9281
|
+
result.pitfalls.push(
|
|
9282
|
+
`Check ${tech.name} changelog when upgrading for breaking changes`,
|
|
9283
|
+
"Ensure error handling covers all library-specific error types"
|
|
9284
|
+
);
|
|
9285
|
+
}
|
|
9286
|
+
return result;
|
|
9287
|
+
}
|
|
9288
|
+
function classifyFeature(feature, importGraph) {
|
|
9289
|
+
const name = feature.name.toLowerCase();
|
|
9290
|
+
if (/^(server|api|routes?|controllers?|handlers?|endpoints?)$/.test(name)) return "api-layer";
|
|
9291
|
+
if (/^(storage|db|database|models?|repositories|entities|migrations?)$/.test(name)) return "data-layer";
|
|
9292
|
+
if (/^(core|domain|business|logic|engine|services?)$/.test(name)) return "business-logic";
|
|
9293
|
+
if (/^(components?|ui|views?|pages?|layouts?|widgets?)$/.test(name)) return "ui-components";
|
|
9294
|
+
if (/^(stores?|state|contexts?|providers?|hooks?)$/.test(name)) return "state-management";
|
|
9295
|
+
if (/^(tests?|spec|__tests__|e2e|integration|fixtures?)$/.test(name)) return "testing";
|
|
9296
|
+
if (/^(cli|cmd|commands?)$/.test(name)) return "cli";
|
|
9297
|
+
if (/^(auth|authentication|authorization|security|identity)$/.test(name)) return "auth";
|
|
9298
|
+
if (/^(docs?|documentation|knowledge)$/.test(name)) return "documentation";
|
|
9299
|
+
if (/^(infra|infrastructure|deploy|ci|scripts?|tools?|config)$/.test(name)) return "infrastructure";
|
|
9300
|
+
if (/^(utils?|helpers?|lib|shared|common|pkg|internal)$/.test(name)) return "utility";
|
|
9301
|
+
if (feature.testFiles.length > 0 && feature.fileCount > 0) {
|
|
9302
|
+
if (feature.testFiles.length / feature.fileCount > 0.5) return "testing";
|
|
9303
|
+
}
|
|
9304
|
+
if (importGraph) {
|
|
9305
|
+
const featurePaths = new Set(feature.paths.map((p) => p.replace("/**", "")));
|
|
9306
|
+
let importedByOthers = 0;
|
|
9307
|
+
for (const [file2, imports] of importGraph) {
|
|
9308
|
+
if (featurePaths.has(file2)) continue;
|
|
9309
|
+
for (const imp of imports) {
|
|
9310
|
+
for (const fp of featurePaths) {
|
|
9311
|
+
if (imp.startsWith(fp)) importedByOthers++;
|
|
9312
|
+
}
|
|
9313
|
+
}
|
|
9314
|
+
}
|
|
9315
|
+
if (importedByOthers > 10) return "utility";
|
|
9316
|
+
}
|
|
9317
|
+
const techNames = feature.technologies.map((t) => t.toLowerCase());
|
|
9318
|
+
if (techNames.some((t) => /prisma|typeorm|sqlalchemy|diesel|gorm|sequelize|drizzle/.test(t))) return "data-layer";
|
|
9319
|
+
if (techNames.some((t) => /react|vue|svelte|angular|solid/.test(t))) return "ui-components";
|
|
9320
|
+
if (techNames.some((t) => /express|fastify|hono|flask|gin|actix|django|rails/.test(t))) return "api-layer";
|
|
9321
|
+
return "generic";
|
|
9322
|
+
}
|
|
9323
|
+
function inferDirectoryPurpose(dirName, context) {
|
|
9324
|
+
const lower = dirName.toLowerCase();
|
|
9325
|
+
const hardcoded = DIR_PURPOSE_MAP[lower];
|
|
9326
|
+
if (hardcoded) return hardcoded;
|
|
9327
|
+
if (/^(spec|__tests__|test_|_test)/.test(lower)) return "Tests and test helpers";
|
|
9328
|
+
if (/^(build|dist|out|target|bin)$/.test(lower)) return "Build output";
|
|
9329
|
+
if (/^(vendor|third[_-]?party|extern)/.test(lower)) return "Third-party vendored code";
|
|
9330
|
+
if (/^(proto|protobuf|grpc)/.test(lower)) return "Protocol buffer definitions";
|
|
9331
|
+
if (/^(gen|generated)/.test(lower)) return "Auto-generated code";
|
|
9332
|
+
if (/^(i18n|l10n|locale|translations?)/.test(lower)) return "Internationalization and localization";
|
|
9333
|
+
if (/^(plugin|extension|addon)s?$/.test(lower)) return "Plugin/extension modules";
|
|
9334
|
+
if (/^(seed|seeds|fixtures|testdata)$/.test(lower)) return "Seed data and test fixtures";
|
|
9335
|
+
if (/^(public|static|assets|resources|res)$/.test(lower)) return "Static assets and resources";
|
|
9336
|
+
if (/^(cache|tmp|temp)$/.test(lower)) return "Temporary/cache files";
|
|
9337
|
+
if (context) {
|
|
9338
|
+
if (context.dominantExtensions) {
|
|
9339
|
+
const exts = context.dominantExtensions;
|
|
9340
|
+
if (exts.some((e) => /\.(test|spec)\.(ts|js|py|go|rs)$/.test(e))) return "Tests and test helpers";
|
|
9341
|
+
if (exts.some((e) => /\.(css|scss|less|styl)$/.test(e))) return "Stylesheets";
|
|
9342
|
+
if (exts.some((e) => /\.(html|hbs|ejs|pug|jinja2?)$/.test(e))) return "Template files";
|
|
9343
|
+
if (exts.some((e) => /\.(proto)$/.test(e))) return "Protocol buffer definitions";
|
|
9344
|
+
}
|
|
9345
|
+
if (context.technologies) {
|
|
9346
|
+
if (context.technologies.some((t) => /react|vue|svelte|angular/.test(t))) return "UI components";
|
|
9347
|
+
if (context.technologies.some((t) => /express|fastify|flask|gin/.test(t))) return "Server handlers";
|
|
9348
|
+
}
|
|
9349
|
+
}
|
|
9350
|
+
return `${capitalize(dirName)} module`;
|
|
9351
|
+
}
|
|
9352
|
+
function inferDataFlowFromGraph(features, importGraph) {
|
|
9353
|
+
const names = features.map((f) => f.name);
|
|
9354
|
+
if (importGraph && importGraph.size > 0) {
|
|
9355
|
+
const flow = inferFlowFromTopology(features, importGraph);
|
|
9356
|
+
if (flow.length >= 2) return flow;
|
|
9357
|
+
}
|
|
9358
|
+
return inferFlowFromNames(names);
|
|
9359
|
+
}
|
|
9360
|
+
function inferFlowFromTopology(features, importGraph) {
|
|
9361
|
+
const featurePathMap = /* @__PURE__ */ new Map();
|
|
9362
|
+
for (const f of features) {
|
|
9363
|
+
for (const p of f.paths) {
|
|
9364
|
+
const clean = p.replace("/**", "");
|
|
9365
|
+
featurePathMap.set(clean, f.name);
|
|
9366
|
+
}
|
|
9367
|
+
}
|
|
9368
|
+
const edges = /* @__PURE__ */ new Map();
|
|
9369
|
+
for (const [file2, imports] of importGraph) {
|
|
9370
|
+
const sourceFeature = findFeatureForFile(file2, featurePathMap);
|
|
9371
|
+
if (!sourceFeature) continue;
|
|
9372
|
+
for (const imp of imports) {
|
|
9373
|
+
const targetFeature = findFeatureForFile(imp, featurePathMap);
|
|
9374
|
+
if (!targetFeature || targetFeature === sourceFeature) continue;
|
|
9375
|
+
if (!edges.has(sourceFeature)) edges.set(sourceFeature, /* @__PURE__ */ new Map());
|
|
9376
|
+
const edgeMap = edges.get(sourceFeature);
|
|
9377
|
+
edgeMap.set(targetFeature, (edgeMap.get(targetFeature) || 0) + 1);
|
|
9378
|
+
}
|
|
9379
|
+
}
|
|
9380
|
+
const inDegree = /* @__PURE__ */ new Map();
|
|
9381
|
+
const outDegree = /* @__PURE__ */ new Map();
|
|
9382
|
+
for (const f of features) {
|
|
9383
|
+
inDegree.set(f.name, 0);
|
|
9384
|
+
outDegree.set(f.name, 0);
|
|
9385
|
+
}
|
|
9386
|
+
for (const [source, targets] of edges) {
|
|
9387
|
+
for (const [target, weight] of targets) {
|
|
9388
|
+
inDegree.set(target, (inDegree.get(target) || 0) + weight);
|
|
9389
|
+
outDegree.set(source, (outDegree.get(source) || 0) + weight);
|
|
9390
|
+
}
|
|
9391
|
+
}
|
|
9392
|
+
const sorted = features.map((f) => ({
|
|
9393
|
+
name: f.name,
|
|
9394
|
+
score: (outDegree.get(f.name) || 0) - (inDegree.get(f.name) || 0)
|
|
9395
|
+
})).sort((a, b) => b.score - a.score).map((f) => capitalize(f.name));
|
|
9396
|
+
return sorted.slice(0, 6);
|
|
9397
|
+
}
|
|
9398
|
+
function findFeatureForFile(file2, featurePathMap) {
|
|
9399
|
+
for (const [path, feature] of featurePathMap) {
|
|
9400
|
+
if (file2.startsWith(path)) return feature;
|
|
9401
|
+
}
|
|
9402
|
+
return null;
|
|
9403
|
+
}
|
|
9404
|
+
function inferFlowFromNames(names) {
|
|
9405
|
+
const flow = [];
|
|
9406
|
+
const isFrontend = names.some((n) => /^(components?|pages?|views?|hooks?|widgets?)$/.test(n));
|
|
9407
|
+
if (isFrontend) {
|
|
9408
|
+
flow.push("User");
|
|
9409
|
+
if (names.some((n) => /^(pages?|views?)$/.test(n))) flow.push("Pages");
|
|
9410
|
+
if (names.some((n) => /^(components?|widgets?)$/.test(n))) flow.push("Components");
|
|
9411
|
+
if (names.some((n) => /^hooks?$/.test(n))) flow.push("Hooks");
|
|
9412
|
+
if (names.some((n) => /^services?$/.test(n))) flow.push("Services");
|
|
9413
|
+
if (names.some((n) => /^(stores?|state)$/.test(n))) flow.push("Store");
|
|
9414
|
+
return flow.length >= 2 ? flow : [];
|
|
9415
|
+
}
|
|
9416
|
+
if (names.some((n) => /^(server|api)$/.test(n))) flow.push("Request");
|
|
9417
|
+
if (names.includes("server")) flow.push("Server");
|
|
9418
|
+
if (names.includes("api")) flow.push("API");
|
|
9419
|
+
if (names.some((n) => /^(controllers?|handlers?)$/.test(n))) flow.push("Controllers");
|
|
9420
|
+
if (names.includes("core")) flow.push("Core");
|
|
9421
|
+
if (names.some((n) => /^services?$/.test(n))) flow.push("Services");
|
|
9422
|
+
if (names.some((n) => /^(storage|db|database)$/.test(n))) flow.push("Storage");
|
|
9423
|
+
if (names.some((n) => /^(indexing|search)$/.test(n))) flow.push("Indexer");
|
|
9424
|
+
if (names.some((n) => /^(cmd|cli)$/.test(n)) && flow.length === 0) {
|
|
9425
|
+
flow.push("CLI");
|
|
9426
|
+
if (names.includes("core")) flow.push("Core");
|
|
9427
|
+
if (names.some((n) => /^(pkg|lib)$/.test(n))) flow.push("Lib");
|
|
9428
|
+
}
|
|
9429
|
+
return flow.length >= 2 ? flow : [];
|
|
9430
|
+
}
|
|
9431
|
+
function inferScopeLabel(scope, memberCount, researchDir) {
|
|
9432
|
+
const hardcoded = SCOPE_LABELS[scope];
|
|
9433
|
+
if (hardcoded) return hardcoded;
|
|
9434
|
+
if (researchDir) {
|
|
9435
|
+
try {
|
|
9436
|
+
if (existsSync25(researchDir)) {
|
|
9437
|
+
const files = readdirSync7(researchDir);
|
|
9438
|
+
const scopeClean = scope.replace("@", "");
|
|
9439
|
+
const match = files.find((f) => f.startsWith(scopeClean));
|
|
9440
|
+
if (match) {
|
|
9441
|
+
const content = readFileSync20(join25(researchDir, match), "utf-8");
|
|
9442
|
+
const titleMatch = content.match(/^#\s+(.+?)(?:\s+—|\s+v)/m);
|
|
9443
|
+
if (titleMatch?.[1]) return titleMatch[1];
|
|
9444
|
+
}
|
|
9445
|
+
}
|
|
9446
|
+
} catch {
|
|
9447
|
+
}
|
|
9448
|
+
}
|
|
9449
|
+
return `${scope} (${memberCount} packages)`;
|
|
9450
|
+
}
|
|
9451
|
+
function inferDocSource(packageName, source) {
|
|
9452
|
+
for (const entry of DOC_SOURCE_REGISTRY) {
|
|
9453
|
+
if (entry.packageNames.includes(packageName)) return entry;
|
|
9454
|
+
}
|
|
9455
|
+
const ecosystem = detectEcosystem(packageName, source);
|
|
9456
|
+
switch (ecosystem) {
|
|
9457
|
+
case "npm":
|
|
9458
|
+
return {
|
|
9459
|
+
packageNames: [packageName],
|
|
9460
|
+
docsUrl: `https://www.npmjs.com/package/${packageName}`
|
|
9461
|
+
};
|
|
9462
|
+
case "go":
|
|
9463
|
+
return {
|
|
9464
|
+
packageNames: [packageName],
|
|
9465
|
+
docsUrl: `https://pkg.go.dev/${packageName}`
|
|
9466
|
+
};
|
|
9467
|
+
case "rust":
|
|
9468
|
+
return {
|
|
9469
|
+
packageNames: [packageName],
|
|
9470
|
+
docsUrl: `https://docs.rs/${packageName}`
|
|
9471
|
+
};
|
|
9472
|
+
case "python":
|
|
9473
|
+
return {
|
|
9474
|
+
packageNames: [packageName],
|
|
9475
|
+
docsUrl: `https://pypi.org/project/${packageName}`
|
|
9476
|
+
};
|
|
9477
|
+
case "ruby":
|
|
9478
|
+
return {
|
|
9479
|
+
packageNames: [packageName],
|
|
9480
|
+
docsUrl: `https://rubygems.org/gems/${packageName}`
|
|
9481
|
+
};
|
|
9482
|
+
case "java":
|
|
9483
|
+
return {
|
|
9484
|
+
packageNames: [packageName],
|
|
9485
|
+
docsUrl: `https://mvnrepository.com/artifact/${packageName}`
|
|
9486
|
+
};
|
|
9487
|
+
case "dotnet":
|
|
9488
|
+
return {
|
|
9489
|
+
packageNames: [packageName],
|
|
9490
|
+
docsUrl: `https://www.nuget.org/packages/${packageName}`
|
|
9491
|
+
};
|
|
9492
|
+
default:
|
|
9493
|
+
return null;
|
|
9494
|
+
}
|
|
9495
|
+
}
|
|
9496
|
+
function detectEcosystem(packageName, source) {
|
|
9497
|
+
if (source) {
|
|
9498
|
+
if (/package\.json|package-lock|yarn\.lock|pnpm-lock|bun\.lock/.test(source)) return "npm";
|
|
9499
|
+
if (/go\.mod|go\.sum/.test(source)) return "go";
|
|
9500
|
+
if (/Cargo\.toml|Cargo\.lock/.test(source)) return "rust";
|
|
9501
|
+
if (/requirements\.txt|setup\.py|pyproject\.toml|Pipfile|poetry\.lock/.test(source)) return "python";
|
|
9502
|
+
if (/Gemfile|\.gemspec/.test(source)) return "ruby";
|
|
9503
|
+
if (/pom\.xml|build\.gradle/.test(source)) return "java";
|
|
9504
|
+
if (/\.csproj|\.sln|nuget/.test(source)) return "dotnet";
|
|
9505
|
+
}
|
|
9506
|
+
if (packageName.startsWith("@") || /^[a-z][\w.-]*$/.test(packageName)) return "npm";
|
|
9507
|
+
if (packageName.includes("/") && !packageName.startsWith("@")) return "go";
|
|
9508
|
+
return "unknown";
|
|
9509
|
+
}
|
|
9510
|
+
function extractRulesFromResearch(tech, researchDir) {
|
|
9511
|
+
const result = { rules: [], pitfalls: [] };
|
|
9512
|
+
const safeName = tech.name.replace(/^@/, "").replace(/\//g, "__");
|
|
9513
|
+
const candidates = [
|
|
9514
|
+
join25(researchDir, `${safeName}@${tech.version}.md`),
|
|
9515
|
+
join25(researchDir, `${tech.name}@${tech.version}.md`)
|
|
9516
|
+
];
|
|
9517
|
+
let content = null;
|
|
9518
|
+
for (const candidate of candidates) {
|
|
9519
|
+
if (existsSync25(candidate)) {
|
|
9520
|
+
try {
|
|
9521
|
+
content = readFileSync20(candidate, "utf-8");
|
|
9522
|
+
break;
|
|
9523
|
+
} catch {
|
|
9524
|
+
}
|
|
9525
|
+
}
|
|
9526
|
+
}
|
|
9527
|
+
if (!content) {
|
|
9528
|
+
try {
|
|
9529
|
+
if (existsSync25(researchDir)) {
|
|
9530
|
+
const files = readdirSync7(researchDir);
|
|
9531
|
+
const match = files.find((f) => f.startsWith(`${safeName}@`));
|
|
9532
|
+
if (match) {
|
|
9533
|
+
content = readFileSync20(join25(researchDir, match), "utf-8");
|
|
9534
|
+
}
|
|
9535
|
+
}
|
|
9536
|
+
} catch {
|
|
9537
|
+
}
|
|
9538
|
+
}
|
|
9539
|
+
if (!content) return result;
|
|
9540
|
+
const trustMatch = content.match(/trust_score:\s*([\d.]+)/);
|
|
9541
|
+
if (trustMatch) {
|
|
9542
|
+
const trustScore = parseFloat(trustMatch[1]);
|
|
9543
|
+
if (trustScore < 0.5) return result;
|
|
9544
|
+
}
|
|
9545
|
+
const apiRules = extractBulletPoints(content, "Key API Patterns");
|
|
9546
|
+
result.rules.push(...apiRules.slice(0, 5));
|
|
9547
|
+
const pitfalls = extractBulletPoints(content, "Common Pitfalls");
|
|
9548
|
+
result.pitfalls.push(...pitfalls.slice(0, 5));
|
|
9549
|
+
const breakingChanges = extractBulletPoints(content, "Breaking Changes");
|
|
9550
|
+
result.pitfalls.push(...breakingChanges.slice(0, 3));
|
|
9551
|
+
return result;
|
|
9552
|
+
}
|
|
9553
|
+
function extractBulletPoints(content, sectionName) {
|
|
9554
|
+
const lines = content.split("\n");
|
|
9555
|
+
const points = [];
|
|
9556
|
+
let inSection = false;
|
|
9557
|
+
for (const line of lines) {
|
|
9558
|
+
if (line.match(new RegExp(`^##\\s+.*${escapeRegex2(sectionName)}`, "i"))) {
|
|
9559
|
+
inSection = true;
|
|
9560
|
+
continue;
|
|
9561
|
+
}
|
|
9562
|
+
if (inSection && line.match(/^##\s/)) {
|
|
9563
|
+
break;
|
|
9564
|
+
}
|
|
9565
|
+
if (inSection) {
|
|
9566
|
+
const bulletMatch = line.match(/^[-*]\s+(.+)/);
|
|
9567
|
+
if (bulletMatch?.[1]) {
|
|
9568
|
+
const text = bulletMatch[1].trim();
|
|
9569
|
+
if (text.length > 10 && text.length < 200) {
|
|
9570
|
+
points.push(text);
|
|
9571
|
+
}
|
|
9572
|
+
}
|
|
9573
|
+
}
|
|
9574
|
+
}
|
|
9575
|
+
return points;
|
|
9576
|
+
}
|
|
9577
|
+
function escapeRegex2(str) {
|
|
9578
|
+
return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
9579
|
+
}
|
|
9580
|
+
function classifyTechnology(tech) {
|
|
9581
|
+
const name = tech.name.toLowerCase();
|
|
9582
|
+
if (/^(express|fastify|hono|koa|flask|gin|actix|django|rails|spring|laravel|fiber|echo|chi|rocket|axum|phoenix|sinatra|bottle)$/.test(name)) return "web-framework";
|
|
9583
|
+
if (/^(react|vue|svelte|angular|solid|preact|lit|qwik|next|nuxt|gatsby|remix|astro|sveltekit)$/.test(name)) return "ui-framework";
|
|
9584
|
+
if (/prisma|typeorm|sqlalchemy|diesel|gorm|sequelize|mongoose|drizzle|knex|bookshelf|objection|mikro-orm|ecto/.test(name)) return "orm";
|
|
9585
|
+
if (/vitest|jest|pytest|mocha|jasmine|chai|supertest|testing-library|playwright|cypress|selenium/.test(name)) return "testing";
|
|
9586
|
+
if (/passport|jsonwebtoken|jwt|oauth|bcrypt|argon2|lucia|next-auth|auth0|clerk|supertokens/.test(name)) return "auth";
|
|
9587
|
+
if (/stripe|paypal|braintree|square|adyen|mollie/.test(name)) return "payment";
|
|
9588
|
+
if (/vite|webpack|esbuild|rollup|parcel|turbopack|swc|babel|tsc/.test(name)) return "build-tool";
|
|
9589
|
+
if (/zustand|redux|mobx|jotai|recoil|pinia|vuex|xstate|ngrx/.test(name)) return "state-management";
|
|
9590
|
+
if (/tailwind|styled-components|emotion|css-modules|sass|less|postcss|bootstrap|bulma/.test(name)) return "css-framework";
|
|
9591
|
+
if (/^(zod|yup|joi|ajv|superstruct|io-ts|valibot|typebox)$/.test(name)) return "validation";
|
|
9592
|
+
if (/^(axios|got|ky|node-fetch|undici|superagent|request)$/.test(name)) return "api-client";
|
|
9593
|
+
if (/better-sqlite3|pg|mysql2|redis|ioredis|mongodb|mariadb|sqlite3|tedious/.test(name)) return "database-driver";
|
|
9594
|
+
if (/winston|pino|bunyan|log4js|morgan|loglevel|consola/.test(name)) return "logging";
|
|
9595
|
+
return "general";
|
|
9596
|
+
}
|
|
9597
|
+
function deriveFeatureRules(feature, technologies, researchDir) {
|
|
9598
|
+
const rules = [];
|
|
9599
|
+
if (WELL_KNOWN_FEATURES.has(feature.name)) {
|
|
9600
|
+
const nameRules = FEATURE_NAME_RULES[feature.name];
|
|
9601
|
+
if (nameRules) rules.push(...nameRules);
|
|
9602
|
+
const techRules = [];
|
|
9603
|
+
for (const tech of technologies) {
|
|
9604
|
+
const inferred = inferTechRulesAndPitfalls(tech, researchDir);
|
|
9605
|
+
techRules.push(...inferred.rules);
|
|
9606
|
+
}
|
|
9607
|
+
for (const tr of techRules.slice(0, 2)) {
|
|
9608
|
+
if (!rules.some((r) => r.includes(tr.split(" ")[0] || ""))) {
|
|
9609
|
+
rules.push(tr);
|
|
9610
|
+
}
|
|
9611
|
+
}
|
|
9612
|
+
} else {
|
|
9613
|
+
for (const tech of technologies) {
|
|
9614
|
+
const inferred = inferTechRulesAndPitfalls(tech, researchDir);
|
|
9615
|
+
rules.push(...inferred.rules);
|
|
9616
|
+
}
|
|
9617
|
+
if (rules.length === 0) {
|
|
9618
|
+
const category = classifyFeature(feature);
|
|
9619
|
+
const categoryRules = FEATURE_CATEGORY_RULES[category];
|
|
9620
|
+
if (categoryRules) {
|
|
9621
|
+
rules.push(...categoryRules);
|
|
9622
|
+
} else {
|
|
9623
|
+
const nameRules = FEATURE_NAME_RULES[feature.name];
|
|
9624
|
+
rules.push(...nameRules || [
|
|
9625
|
+
"Follow existing patterns in this module for consistency",
|
|
9626
|
+
"Export public API from index file \u2014 keep internal helpers unexported"
|
|
9627
|
+
]);
|
|
9628
|
+
}
|
|
9629
|
+
}
|
|
9630
|
+
}
|
|
9631
|
+
if (feature.paths.length > 0) {
|
|
9632
|
+
const scope = feature.paths[0]?.replace("/**", "") || "";
|
|
9633
|
+
rules.push(`Changes here should not import from other feature directories \u2014 keep ${scope} self-contained`);
|
|
9634
|
+
}
|
|
9635
|
+
return rules;
|
|
9636
|
+
}
|
|
9637
|
+
function deriveFeaturePitfalls(feature, technologies, researchDir) {
|
|
9638
|
+
const pitfalls = [];
|
|
9639
|
+
if (WELL_KNOWN_FEATURES.has(feature.name)) {
|
|
9640
|
+
const namePitfalls = FEATURE_NAME_PITFALLS[feature.name];
|
|
9641
|
+
if (namePitfalls) pitfalls.push(...namePitfalls);
|
|
9642
|
+
for (const tech of technologies) {
|
|
9643
|
+
const inferred = inferTechRulesAndPitfalls(tech, researchDir);
|
|
9644
|
+
for (const p of inferred.pitfalls.slice(0, 1)) {
|
|
9645
|
+
if (!pitfalls.includes(p)) pitfalls.push(p);
|
|
9646
|
+
}
|
|
9647
|
+
}
|
|
9648
|
+
} else {
|
|
9649
|
+
for (const tech of technologies) {
|
|
9650
|
+
const inferred = inferTechRulesAndPitfalls(tech, researchDir);
|
|
9651
|
+
pitfalls.push(...inferred.pitfalls);
|
|
9652
|
+
}
|
|
9653
|
+
if (pitfalls.length === 0) {
|
|
9654
|
+
const namePitfalls = FEATURE_NAME_PITFALLS[feature.name];
|
|
9655
|
+
if (namePitfalls) {
|
|
9656
|
+
pitfalls.push(...namePitfalls);
|
|
9657
|
+
} else {
|
|
9658
|
+
const category = classifyFeature(feature);
|
|
9659
|
+
const categoryPitfalls = FEATURE_CATEGORY_PITFALLS[category];
|
|
9660
|
+
if (categoryPitfalls) {
|
|
9661
|
+
pitfalls.push(...categoryPitfalls);
|
|
9662
|
+
} else {
|
|
9663
|
+
pitfalls.push(
|
|
9664
|
+
"Check for null/undefined before property access on external data",
|
|
9665
|
+
"Changing exports may break other modules that depend on this feature"
|
|
9666
|
+
);
|
|
9667
|
+
}
|
|
9668
|
+
}
|
|
9669
|
+
}
|
|
9670
|
+
}
|
|
9671
|
+
return pitfalls;
|
|
9672
|
+
}
|
|
9673
|
+
function capitalize(str) {
|
|
9674
|
+
return str.charAt(0).toUpperCase() + str.slice(1).replace(/-/g, " ");
|
|
9675
|
+
}
|
|
9676
|
+
var CATEGORY_RULES, FEATURE_CATEGORY_RULES, FEATURE_CATEGORY_PITFALLS;
|
|
9677
|
+
var init_universal_inference = __esm({
|
|
9678
|
+
"src/core/agents/universal-inference.ts"() {
|
|
9679
|
+
"use strict";
|
|
9680
|
+
init_hardcoded_knowledge();
|
|
9681
|
+
CATEGORY_RULES = {
|
|
9682
|
+
"web-framework": {
|
|
9683
|
+
rules: [
|
|
9684
|
+
"Validate all user inputs at the boundary \u2014 never trust request data",
|
|
9685
|
+
"Use middleware for cross-cutting concerns (auth, logging, CORS)",
|
|
9686
|
+
"Return consistent error response shapes across all endpoints"
|
|
9687
|
+
],
|
|
9688
|
+
pitfalls: [
|
|
9689
|
+
"Unhandled async errors crash the server \u2014 always use error middleware",
|
|
9690
|
+
"Missing input validation leads to injection vulnerabilities"
|
|
9691
|
+
]
|
|
9692
|
+
},
|
|
9693
|
+
"ui-framework": {
|
|
9694
|
+
rules: [
|
|
9695
|
+
"Components should be pure \u2014 same props must produce same output",
|
|
9696
|
+
"Use composition over inheritance \u2014 prefer small, focused components",
|
|
9697
|
+
"Keep components focused on one responsibility"
|
|
9698
|
+
],
|
|
9699
|
+
pitfalls: [
|
|
9700
|
+
"Mutating state directly causes rendering bugs \u2014 always create new references",
|
|
9701
|
+
"Missing key props in lists causes reconciliation bugs"
|
|
9702
|
+
]
|
|
9703
|
+
},
|
|
9704
|
+
"orm": {
|
|
9705
|
+
rules: [
|
|
9706
|
+
"Use migrations for all schema changes \u2014 never modify production DB directly",
|
|
9707
|
+
"Use transactions for multi-table operations",
|
|
9708
|
+
"Select only needed fields to avoid over-fetching"
|
|
9709
|
+
],
|
|
9710
|
+
pitfalls: [
|
|
9711
|
+
"N+1 query problem \u2014 use eager loading or batch queries for relations",
|
|
9712
|
+
"Forgetting to run migrations after schema changes causes runtime errors"
|
|
9713
|
+
]
|
|
9714
|
+
},
|
|
9715
|
+
"testing": {
|
|
9716
|
+
rules: [
|
|
9717
|
+
"Tests should be deterministic \u2014 no reliance on external services or timing",
|
|
9718
|
+
"Clean up test state between runs to prevent flaky failures",
|
|
9719
|
+
"Use descriptive test names that explain the expected behavior"
|
|
9720
|
+
],
|
|
9721
|
+
pitfalls: [
|
|
9722
|
+
"Shared mutable state between tests causes flaky failures",
|
|
9723
|
+
"Mocking too much makes tests pass but misses real integration bugs"
|
|
9724
|
+
]
|
|
9725
|
+
},
|
|
9726
|
+
"auth": {
|
|
9727
|
+
rules: [
|
|
9728
|
+
"Never store plain-text passwords \u2014 use bcrypt or argon2",
|
|
9729
|
+
"Set explicit token expiration \u2014 never issue non-expiring tokens",
|
|
9730
|
+
"Validate tokens on every request \u2014 never trust client-side auth state"
|
|
9731
|
+
],
|
|
9732
|
+
pitfalls: [
|
|
9733
|
+
"Using weak algorithms (MD5, SHA1) for password hashing is a security vulnerability",
|
|
9734
|
+
"Not revoking tokens on logout allows session reuse"
|
|
9735
|
+
]
|
|
9736
|
+
},
|
|
9737
|
+
"payment": {
|
|
9738
|
+
rules: [
|
|
9739
|
+
"Always verify webhook signatures before processing payment events",
|
|
9740
|
+
"Use idempotency keys for payment operations to prevent double-charges",
|
|
9741
|
+
"Log all payment events for audit trails"
|
|
9742
|
+
],
|
|
9743
|
+
pitfalls: [
|
|
9744
|
+
"Webhook events may arrive out of order \u2014 handle idempotently",
|
|
9745
|
+
"Not handling declined payments gracefully shows raw errors to users"
|
|
9746
|
+
]
|
|
9747
|
+
},
|
|
9748
|
+
"build-tool": {
|
|
9749
|
+
rules: [
|
|
9750
|
+
"Configure source maps for debugging in development",
|
|
9751
|
+
"Set up separate dev/prod build configurations"
|
|
9752
|
+
],
|
|
9753
|
+
pitfalls: [
|
|
9754
|
+
"Large bundle sizes slow page load \u2014 use code splitting and tree shaking",
|
|
9755
|
+
"Misconfigured output paths overwrite source files"
|
|
9756
|
+
]
|
|
9757
|
+
},
|
|
9758
|
+
"state-management": {
|
|
9759
|
+
rules: [
|
|
9760
|
+
"Keep store state minimal \u2014 derive computed values instead of storing them",
|
|
9761
|
+
"Use selectors to prevent unnecessary re-renders",
|
|
9762
|
+
"Keep store state serializable for debugging and persistence"
|
|
9763
|
+
],
|
|
9764
|
+
pitfalls: [
|
|
9765
|
+
"Mutating state directly causes silent bugs \u2014 always use the update API",
|
|
9766
|
+
"Subscribing to entire store causes excessive re-renders"
|
|
9767
|
+
]
|
|
9768
|
+
},
|
|
9769
|
+
"css-framework": {
|
|
9770
|
+
rules: [
|
|
9771
|
+
"Follow the framework's utility-first or component-based approach consistently",
|
|
9772
|
+
"Use conditional class merging utilities for dynamic styles"
|
|
9773
|
+
],
|
|
9774
|
+
pitfalls: [
|
|
9775
|
+
"Dynamic class names may be purged in production \u2014 use full static class names",
|
|
9776
|
+
"Overriding framework styles with !important leads to unmaintainable CSS"
|
|
9777
|
+
]
|
|
9778
|
+
},
|
|
9779
|
+
"validation": {
|
|
9780
|
+
rules: [
|
|
9781
|
+
"Define schemas once \u2014 derive types from schemas to keep them in sync",
|
|
9782
|
+
"Validate at system boundaries \u2014 user input, API responses, config files"
|
|
9783
|
+
],
|
|
9784
|
+
pitfalls: [
|
|
9785
|
+
"Throwing on validation failure crashes the app \u2014 use safe parse methods in handlers",
|
|
9786
|
+
"Missing optional field validation causes runtime errors on undefined access"
|
|
9787
|
+
]
|
|
9788
|
+
},
|
|
9789
|
+
"api-client": {
|
|
9790
|
+
rules: [
|
|
9791
|
+
"Set request timeouts to prevent hanging on unresponsive servers",
|
|
9792
|
+
"Handle network errors and HTTP errors separately"
|
|
9793
|
+
],
|
|
9794
|
+
pitfalls: [
|
|
9795
|
+
"Not handling network errors shows cryptic errors to users",
|
|
9796
|
+
"Forgetting to set Content-Type header causes request parsing failures"
|
|
9797
|
+
]
|
|
9798
|
+
},
|
|
9799
|
+
"database-driver": {
|
|
9800
|
+
rules: [
|
|
9801
|
+
"Use parameterized queries \u2014 never concatenate user input into SQL",
|
|
9802
|
+
"Use connection pooling for concurrent requests",
|
|
9803
|
+
"Close connections properly to prevent resource leaks"
|
|
9804
|
+
],
|
|
9805
|
+
pitfalls: [
|
|
9806
|
+
"SQL injection from string concatenation \u2014 always use parameterized queries",
|
|
9807
|
+
"Not closing connections leaks file handles or socket connections"
|
|
9808
|
+
]
|
|
9809
|
+
},
|
|
9810
|
+
"logging": {
|
|
9811
|
+
rules: [
|
|
9812
|
+
"Use structured logging (JSON) for machine-parseable log output",
|
|
9813
|
+
"Set appropriate log levels \u2014 debug in dev, info/warn in production"
|
|
9814
|
+
],
|
|
9815
|
+
pitfalls: [
|
|
9816
|
+
"Logging sensitive data (passwords, tokens) is a security risk",
|
|
9817
|
+
"Synchronous logging in hot paths impacts performance"
|
|
9818
|
+
]
|
|
9819
|
+
},
|
|
9820
|
+
"general": {
|
|
9821
|
+
rules: [
|
|
9822
|
+
"Follow existing patterns in this module for consistency",
|
|
9823
|
+
"Pin dependency versions to avoid unexpected breaking changes"
|
|
9824
|
+
],
|
|
9825
|
+
pitfalls: [
|
|
9826
|
+
"Check changelog when upgrading for breaking changes",
|
|
9827
|
+
"Ensure error handling covers library-specific error types"
|
|
9828
|
+
]
|
|
9829
|
+
}
|
|
9830
|
+
};
|
|
9831
|
+
FEATURE_CATEGORY_RULES = {
|
|
9832
|
+
"api-layer": [
|
|
9833
|
+
"All endpoints must validate request bodies/params before processing",
|
|
9834
|
+
"Return consistent error response shapes across all endpoints"
|
|
9835
|
+
],
|
|
9836
|
+
"data-layer": [
|
|
9837
|
+
"All database access should go through this module \u2014 no direct imports elsewhere",
|
|
9838
|
+
"Use transactions for multi-step operations to ensure consistency"
|
|
9839
|
+
],
|
|
9840
|
+
"business-logic": [
|
|
9841
|
+
"Core modules should have no side effects on import",
|
|
9842
|
+
"Export types alongside functions for downstream consumers"
|
|
9843
|
+
],
|
|
9844
|
+
"ui-components": [
|
|
9845
|
+
"Components should be pure \u2014 derive UI from props, avoid internal side effects",
|
|
9846
|
+
"Use composition over inheritance \u2014 prefer small, focused components"
|
|
9847
|
+
],
|
|
9848
|
+
"state-management": [
|
|
9849
|
+
"Keep store state minimal \u2014 derive computed values instead of storing them",
|
|
9850
|
+
"Actions should be thin \u2014 delegate complex logic to services"
|
|
9851
|
+
],
|
|
9852
|
+
"testing": [
|
|
9853
|
+
"Tests should be deterministic \u2014 no reliance on external services or timing",
|
|
9854
|
+
"Clean up temporary files and directories in teardown hooks"
|
|
9855
|
+
],
|
|
9856
|
+
"cli": [
|
|
9857
|
+
"Validate all command-line arguments before processing",
|
|
9858
|
+
"Provide helpful error messages with usage hints on invalid input"
|
|
9859
|
+
],
|
|
9860
|
+
"auth": [
|
|
9861
|
+
"Never store plain-text passwords \u2014 use bcrypt or argon2",
|
|
9862
|
+
"Validate and sanitize all user inputs before processing"
|
|
9863
|
+
],
|
|
9864
|
+
"documentation": [
|
|
9865
|
+
"Generated docs must be kept in sync with source code changes",
|
|
9866
|
+
"Use relative paths for internal links"
|
|
9867
|
+
],
|
|
9868
|
+
"infrastructure": [
|
|
9869
|
+
"Configuration should be environment-aware (dev/staging/prod)",
|
|
9870
|
+
"Scripts should be idempotent \u2014 safe to run multiple times"
|
|
9871
|
+
],
|
|
9872
|
+
"utility": [
|
|
9873
|
+
"Utility functions should be pure \u2014 no side effects",
|
|
9874
|
+
"Export public API from index file \u2014 keep internal helpers unexported"
|
|
9875
|
+
],
|
|
9876
|
+
"generic": [
|
|
9877
|
+
"Follow existing patterns in this module for consistency",
|
|
9878
|
+
"Export public API from index file \u2014 keep internal helpers unexported"
|
|
9879
|
+
]
|
|
9880
|
+
};
|
|
9881
|
+
FEATURE_CATEGORY_PITFALLS = {
|
|
9882
|
+
"api-layer": [
|
|
9883
|
+
"Unhandled async errors crash the server \u2014 always catch errors in handlers",
|
|
9884
|
+
"Missing input validation leads to injection vulnerabilities"
|
|
9885
|
+
],
|
|
9886
|
+
"data-layer": [
|
|
9887
|
+
"Forgetting to close connections leaks file handles",
|
|
9888
|
+
"Concurrent writes without transactions may cause data corruption"
|
|
9889
|
+
],
|
|
9890
|
+
"business-logic": [
|
|
9891
|
+
"Circular imports between core modules cause runtime errors \u2014 check dependency direction",
|
|
9892
|
+
"Changing public API signatures breaks downstream consumers"
|
|
9893
|
+
],
|
|
9894
|
+
"ui-components": [
|
|
9895
|
+
"Missing key prop in lists causes reconciliation bugs \u2014 always use stable keys",
|
|
9896
|
+
"Direct DOM manipulation bypasses the framework \u2014 use refs only when necessary"
|
|
9897
|
+
],
|
|
9898
|
+
"state-management": [
|
|
9899
|
+
"Mutating state directly causes silent bugs \u2014 always return new references",
|
|
9900
|
+
"Large store updates trigger unnecessary re-renders \u2014 split into focused slices"
|
|
9901
|
+
],
|
|
9902
|
+
"testing": [
|
|
9903
|
+
"Tests sharing mutable state between runs cause flaky failures",
|
|
9904
|
+
"Temp directories not cleaned up fill disk over repeated test runs"
|
|
9905
|
+
],
|
|
9906
|
+
"cli": [
|
|
9907
|
+
"Missing error handling on subprocess execution causes silent failures",
|
|
9908
|
+
"Not quoting file paths breaks on paths with spaces"
|
|
9909
|
+
],
|
|
9910
|
+
"auth": [
|
|
9911
|
+
"Using weak hashing algorithms is a security vulnerability",
|
|
9912
|
+
"Not revoking tokens on logout allows session reuse"
|
|
9913
|
+
],
|
|
9914
|
+
"documentation": [
|
|
9915
|
+
"Stale documentation is worse than no documentation",
|
|
9916
|
+
"Broken internal links confuse users \u2014 validate links on changes"
|
|
9917
|
+
],
|
|
9918
|
+
"infrastructure": [
|
|
9919
|
+
"Hardcoded secrets in config files are a security risk \u2014 use environment variables",
|
|
9920
|
+
"Non-idempotent scripts cause issues on re-run"
|
|
9921
|
+
],
|
|
9922
|
+
"utility": [
|
|
9923
|
+
"Changing utility signatures breaks all callers \u2014 check usage before modifying",
|
|
9924
|
+
"Missing error handling in utilities propagates errors to all consumers"
|
|
9925
|
+
],
|
|
9926
|
+
"generic": [
|
|
9927
|
+
"Check for null/undefined before property access on external data",
|
|
9928
|
+
"Changing exports may break other modules that depend on this feature"
|
|
9929
|
+
]
|
|
9930
|
+
};
|
|
9931
|
+
}
|
|
9932
|
+
});
|
|
9933
|
+
|
|
9934
|
+
// src/core/agents/research-engine.ts
|
|
9935
|
+
import { existsSync as existsSync26, readFileSync as readFileSync21, writeFileSync as writeFileSync15, mkdirSync as mkdirSync15, readdirSync as readdirSync8 } from "fs";
|
|
9936
|
+
import { join as join26 } from "path";
|
|
8650
9937
|
async function researchTechnology(projectPath, tech, options) {
|
|
8651
9938
|
const paths = getAgentWorkspacePaths(projectPath);
|
|
8652
9939
|
const safeName = tech.name.replace(/^@/, "").replace(/\//g, "__");
|
|
8653
9940
|
const fileName = `${safeName}@${tech.version}.md`;
|
|
8654
|
-
const filePath =
|
|
9941
|
+
const filePath = join26(paths.researchDir, fileName);
|
|
8655
9942
|
const relPath = `research/${fileName}`;
|
|
8656
|
-
if (!options.force &&
|
|
9943
|
+
if (!options.force && existsSync26(filePath)) {
|
|
8657
9944
|
const existing = readResearchFile(filePath);
|
|
8658
9945
|
if (existing && !isStale(existing, options.cadenceHours)) {
|
|
8659
9946
|
return { technology: tech.name, version: tech.version, file: relPath, status: "cached" };
|
|
8660
9947
|
}
|
|
8661
9948
|
}
|
|
8662
|
-
const docSource =
|
|
9949
|
+
const docSource = inferDocSource(tech.name, tech.source);
|
|
8663
9950
|
if (!docSource) {
|
|
8664
9951
|
const stub = generateStubResearch(tech);
|
|
8665
9952
|
mkdirSync15(paths.researchDir, { recursive: true });
|
|
@@ -8669,10 +9956,10 @@ async function researchTechnology(projectPath, tech, options) {
|
|
|
8669
9956
|
try {
|
|
8670
9957
|
const fetchFn = options.fetchFn || defaultFetch;
|
|
8671
9958
|
const content = await fetchAndDistill(tech, docSource, fetchFn, options.maxTokensPerTech);
|
|
9959
|
+
const isNew = !existsSync26(filePath);
|
|
8672
9960
|
mkdirSync15(paths.researchDir, { recursive: true });
|
|
8673
9961
|
writeFileSync15(filePath, content);
|
|
8674
9962
|
const tokenCount = estimateTokens4(content);
|
|
8675
|
-
const isNew = !existsSync25(filePath);
|
|
8676
9963
|
return {
|
|
8677
9964
|
technology: tech.name,
|
|
8678
9965
|
version: tech.version,
|
|
@@ -8681,7 +9968,7 @@ async function researchTechnology(projectPath, tech, options) {
|
|
|
8681
9968
|
tokenCount
|
|
8682
9969
|
};
|
|
8683
9970
|
} catch (err) {
|
|
8684
|
-
if (!
|
|
9971
|
+
if (!existsSync26(filePath)) {
|
|
8685
9972
|
const stub = generateStubResearch(tech);
|
|
8686
9973
|
mkdirSync15(paths.researchDir, { recursive: true });
|
|
8687
9974
|
writeFileSync15(filePath, stub);
|
|
@@ -8703,14 +9990,6 @@ async function researchAllTechnologies(projectPath, technologies, options) {
|
|
|
8703
9990
|
}
|
|
8704
9991
|
return results;
|
|
8705
9992
|
}
|
|
8706
|
-
function findDocSource(packageName) {
|
|
8707
|
-
for (const source of DOC_SOURCE_REGISTRY) {
|
|
8708
|
-
if (source.packageNames.includes(packageName)) {
|
|
8709
|
-
return source;
|
|
8710
|
-
}
|
|
8711
|
-
}
|
|
8712
|
-
return null;
|
|
8713
|
-
}
|
|
8714
9993
|
async function fetchAndDistill(tech, docSource, fetchFn, maxTokens) {
|
|
8715
9994
|
const urls = [docSource.docsUrl];
|
|
8716
9995
|
if (docSource.apiRefUrl && docSource.apiRefUrl !== docSource.docsUrl) {
|
|
@@ -8876,7 +10155,7 @@ function generateStubResearch(tech) {
|
|
|
8876
10155
|
}
|
|
8877
10156
|
function readResearchFile(filePath) {
|
|
8878
10157
|
try {
|
|
8879
|
-
const content =
|
|
10158
|
+
const content = readFileSync21(filePath, "utf-8");
|
|
8880
10159
|
const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---/);
|
|
8881
10160
|
if (!frontmatterMatch) return null;
|
|
8882
10161
|
const fm = frontmatterMatch[1] || "";
|
|
@@ -8901,16 +10180,16 @@ function readResearchFile(filePath) {
|
|
|
8901
10180
|
function getResearchContent(projectPath, technology, version2) {
|
|
8902
10181
|
const paths = getAgentWorkspacePaths(projectPath);
|
|
8903
10182
|
if (version2) {
|
|
8904
|
-
const exactPath =
|
|
8905
|
-
if (
|
|
8906
|
-
return
|
|
10183
|
+
const exactPath = join26(paths.researchDir, `${technology}@${version2}.md`);
|
|
10184
|
+
if (existsSync26(exactPath)) {
|
|
10185
|
+
return readFileSync21(exactPath, "utf-8");
|
|
8907
10186
|
}
|
|
8908
10187
|
}
|
|
8909
|
-
if (
|
|
8910
|
-
const files =
|
|
10188
|
+
if (existsSync26(paths.researchDir)) {
|
|
10189
|
+
const files = readdirSync8(paths.researchDir);
|
|
8911
10190
|
const match = files.find((f) => f.startsWith(`${technology}@`));
|
|
8912
10191
|
if (match) {
|
|
8913
|
-
return
|
|
10192
|
+
return readFileSync21(join26(paths.researchDir, match), "utf-8");
|
|
8914
10193
|
}
|
|
8915
10194
|
}
|
|
8916
10195
|
return null;
|
|
@@ -8973,114 +10252,12 @@ function htmlToMarkdown(html) {
|
|
|
8973
10252
|
text = text.trim();
|
|
8974
10253
|
return text;
|
|
8975
10254
|
}
|
|
8976
|
-
var DOC_SOURCE_REGISTRY;
|
|
8977
10255
|
var init_research_engine = __esm({
|
|
8978
10256
|
"src/core/agents/research-engine.ts"() {
|
|
8979
10257
|
"use strict";
|
|
8980
10258
|
init_workspace2();
|
|
8981
10259
|
init_research_trust();
|
|
8982
|
-
|
|
8983
|
-
{
|
|
8984
|
-
packageNames: ["express"],
|
|
8985
|
-
docsUrl: "https://expressjs.com/en/api.html",
|
|
8986
|
-
apiRefUrl: "https://expressjs.com/en/4x/api.html",
|
|
8987
|
-
changelogUrl: "https://github.com/expressjs/express/blob/master/History.md"
|
|
8988
|
-
},
|
|
8989
|
-
{
|
|
8990
|
-
packageNames: ["react", "react-dom"],
|
|
8991
|
-
docsUrl: "https://react.dev/reference/react",
|
|
8992
|
-
apiRefUrl: "https://react.dev/reference/react",
|
|
8993
|
-
changelogUrl: "https://github.com/facebook/react/blob/main/CHANGELOG.md"
|
|
8994
|
-
},
|
|
8995
|
-
{
|
|
8996
|
-
packageNames: ["next"],
|
|
8997
|
-
docsUrl: "https://nextjs.org/docs",
|
|
8998
|
-
apiRefUrl: "https://nextjs.org/docs/api-reference",
|
|
8999
|
-
changelogUrl: "https://github.com/vercel/next.js/releases"
|
|
9000
|
-
},
|
|
9001
|
-
{
|
|
9002
|
-
packageNames: ["typescript"],
|
|
9003
|
-
docsUrl: "https://www.typescriptlang.org/docs/",
|
|
9004
|
-
changelogUrl: "https://www.typescriptlang.org/docs/handbook/release-notes/overview.html"
|
|
9005
|
-
},
|
|
9006
|
-
{
|
|
9007
|
-
packageNames: ["vitest"],
|
|
9008
|
-
docsUrl: "https://vitest.dev/api/",
|
|
9009
|
-
apiRefUrl: "https://vitest.dev/api/",
|
|
9010
|
-
changelogUrl: "https://github.com/vitest-dev/vitest/releases"
|
|
9011
|
-
},
|
|
9012
|
-
{
|
|
9013
|
-
packageNames: ["jest"],
|
|
9014
|
-
docsUrl: "https://jestjs.io/docs/api",
|
|
9015
|
-
apiRefUrl: "https://jestjs.io/docs/expect"
|
|
9016
|
-
},
|
|
9017
|
-
{
|
|
9018
|
-
packageNames: ["prisma", "@prisma/client"],
|
|
9019
|
-
docsUrl: "https://www.prisma.io/docs",
|
|
9020
|
-
apiRefUrl: "https://www.prisma.io/docs/reference/api-reference",
|
|
9021
|
-
changelogUrl: "https://github.com/prisma/prisma/releases"
|
|
9022
|
-
},
|
|
9023
|
-
{
|
|
9024
|
-
packageNames: ["stripe"],
|
|
9025
|
-
docsUrl: "https://docs.stripe.com/api",
|
|
9026
|
-
changelogUrl: "https://docs.stripe.com/changelog"
|
|
9027
|
-
},
|
|
9028
|
-
{
|
|
9029
|
-
packageNames: ["@modelcontextprotocol/sdk"],
|
|
9030
|
-
docsUrl: "https://modelcontextprotocol.io/docs",
|
|
9031
|
-
apiRefUrl: "https://github.com/modelcontextprotocol/typescript-sdk"
|
|
9032
|
-
},
|
|
9033
|
-
{
|
|
9034
|
-
packageNames: ["better-sqlite3"],
|
|
9035
|
-
docsUrl: "https://github.com/WiseLibs/better-sqlite3/blob/master/docs/api.md",
|
|
9036
|
-
apiRefUrl: "https://github.com/WiseLibs/better-sqlite3/blob/master/docs/api.md"
|
|
9037
|
-
},
|
|
9038
|
-
{
|
|
9039
|
-
packageNames: ["zod"],
|
|
9040
|
-
docsUrl: "https://zod.dev/",
|
|
9041
|
-
apiRefUrl: "https://zod.dev/"
|
|
9042
|
-
},
|
|
9043
|
-
{
|
|
9044
|
-
packageNames: ["tailwindcss"],
|
|
9045
|
-
docsUrl: "https://tailwindcss.com/docs"
|
|
9046
|
-
},
|
|
9047
|
-
{
|
|
9048
|
-
packageNames: ["vite"],
|
|
9049
|
-
docsUrl: "https://vitejs.dev/guide/",
|
|
9050
|
-
apiRefUrl: "https://vitejs.dev/config/"
|
|
9051
|
-
},
|
|
9052
|
-
{
|
|
9053
|
-
packageNames: ["passport"],
|
|
9054
|
-
docsUrl: "https://www.passportjs.org/docs/"
|
|
9055
|
-
},
|
|
9056
|
-
{
|
|
9057
|
-
packageNames: ["jsonwebtoken"],
|
|
9058
|
-
docsUrl: "https://github.com/auth0/node-jsonwebtoken#readme"
|
|
9059
|
-
},
|
|
9060
|
-
{
|
|
9061
|
-
packageNames: ["axios"],
|
|
9062
|
-
docsUrl: "https://axios-http.com/docs/intro",
|
|
9063
|
-
apiRefUrl: "https://axios-http.com/docs/api_intro"
|
|
9064
|
-
},
|
|
9065
|
-
{
|
|
9066
|
-
packageNames: ["fastify"],
|
|
9067
|
-
docsUrl: "https://fastify.dev/docs/latest/",
|
|
9068
|
-
apiRefUrl: "https://fastify.dev/docs/latest/Reference/"
|
|
9069
|
-
},
|
|
9070
|
-
{
|
|
9071
|
-
packageNames: ["drizzle-orm"],
|
|
9072
|
-
docsUrl: "https://orm.drizzle.team/docs/overview"
|
|
9073
|
-
},
|
|
9074
|
-
{
|
|
9075
|
-
packageNames: ["trpc", "@trpc/server", "@trpc/client"],
|
|
9076
|
-
docsUrl: "https://trpc.io/docs"
|
|
9077
|
-
},
|
|
9078
|
-
{
|
|
9079
|
-
packageNames: ["hono"],
|
|
9080
|
-
docsUrl: "https://hono.dev/docs/",
|
|
9081
|
-
apiRefUrl: "https://hono.dev/docs/api/hono"
|
|
9082
|
-
}
|
|
9083
|
-
];
|
|
10260
|
+
init_universal_inference();
|
|
9084
10261
|
}
|
|
9085
10262
|
});
|
|
9086
10263
|
|
|
@@ -9412,8 +10589,8 @@ var init_diagnosis_table = __esm({
|
|
|
9412
10589
|
});
|
|
9413
10590
|
|
|
9414
10591
|
// src/core/agents/improvement-engine.ts
|
|
9415
|
-
import { existsSync as
|
|
9416
|
-
import { join as
|
|
10592
|
+
import { existsSync as existsSync27, readFileSync as readFileSync22, writeFileSync as writeFileSync16, readdirSync as readdirSync9 } from "fs";
|
|
10593
|
+
import { join as join27 } from "path";
|
|
9417
10594
|
function learnFromOutcome(db, projectPath, outcome) {
|
|
9418
10595
|
if (outcome.result === "success") {
|
|
9419
10596
|
confirmRelatedLessons(projectPath, outcome);
|
|
@@ -9583,7 +10760,7 @@ function promoteLessons(db, projectPath) {
|
|
|
9583
10760
|
const agentFiles = findAgentFiles(paths);
|
|
9584
10761
|
const skillFiles = findSkillFiles(paths);
|
|
9585
10762
|
for (const filePath of agentFiles) {
|
|
9586
|
-
const content =
|
|
10763
|
+
const content = readFileSync22(filePath, "utf-8");
|
|
9587
10764
|
if (!content.includes("## Lessons Learned")) continue;
|
|
9588
10765
|
const lessonsMatch = content.match(/## Lessons Learned\n([\s\S]*?)(?=\n##|\n<!-- |$)/);
|
|
9589
10766
|
if (!lessonsMatch || !lessonsMatch[1]) continue;
|
|
@@ -9595,8 +10772,8 @@ function promoteLessons(db, projectPath) {
|
|
|
9595
10772
|
const confirmations = countConfirmations(db, fingerprint);
|
|
9596
10773
|
if (confirmations >= 3) {
|
|
9597
10774
|
const featureName = extractFeatureFromPath(filePath);
|
|
9598
|
-
const skillPath = featureName ?
|
|
9599
|
-
if (
|
|
10775
|
+
const skillPath = featureName ? join27(paths.featuresDir, featureName, "SKILL.md") : join27(paths.projectDir, "SKILL.md");
|
|
10776
|
+
if (existsSync27(skillPath)) {
|
|
9600
10777
|
const ruleAdded = addRuleToSkill(skillPath, lessonContent, config2);
|
|
9601
10778
|
if (ruleAdded) {
|
|
9602
10779
|
const updatedContent = content.replace(lesson, `${lesson} [promoted]`);
|
|
@@ -9615,9 +10792,9 @@ function diagnoseFailure(failure, db, projectPath) {
|
|
|
9615
10792
|
const isInScope = (filePath, agentName) => {
|
|
9616
10793
|
const paths = getAgentWorkspacePaths(projectPath);
|
|
9617
10794
|
const name = agentName.replace(/-agent$/, "");
|
|
9618
|
-
const agentPath =
|
|
9619
|
-
if (!
|
|
9620
|
-
const agentContent =
|
|
10795
|
+
const agentPath = join27(paths.featuresDir, name, "AGENT.md");
|
|
10796
|
+
if (!existsSync27(agentPath)) return true;
|
|
10797
|
+
const agentContent = readFileSync22(agentPath, "utf-8");
|
|
9621
10798
|
return fileMatchesAgentScope(filePath, agentContent);
|
|
9622
10799
|
};
|
|
9623
10800
|
const ctx = {
|
|
@@ -9643,10 +10820,10 @@ function fileMatchesAgentScope(filePath, agentMdContent) {
|
|
|
9643
10820
|
function patchSkillWithFix(projectPath, outcome, diagnosis, config2) {
|
|
9644
10821
|
const paths = getAgentWorkspacePaths(projectPath);
|
|
9645
10822
|
const agentName = outcome.agent.replace(/-agent$/, "");
|
|
9646
|
-
const featureSkillPath =
|
|
9647
|
-
const targetPath =
|
|
9648
|
-
if (!
|
|
9649
|
-
const content =
|
|
10823
|
+
const featureSkillPath = join27(paths.featuresDir, agentName, "SKILL.md");
|
|
10824
|
+
const targetPath = existsSync27(featureSkillPath) ? featureSkillPath : join27(paths.projectDir, "SKILL.md");
|
|
10825
|
+
if (!existsSync27(targetPath)) return null;
|
|
10826
|
+
const content = readFileSync22(targetPath, "utf-8");
|
|
9650
10827
|
const pitfallEntry = `- ${diagnosis.description.slice(0, 150)} \u2192 Fix: ${outcome.fixApplied.slice(0, 100)}`;
|
|
9651
10828
|
if (!content.includes(config2.markers.start)) return null;
|
|
9652
10829
|
const startIdx = content.indexOf(config2.markers.start);
|
|
@@ -9689,9 +10866,9 @@ function identifyAffectedTechnologies(outcome, projectPath) {
|
|
|
9689
10866
|
techs.push(pkg);
|
|
9690
10867
|
}
|
|
9691
10868
|
}
|
|
9692
|
-
if (
|
|
10869
|
+
if (existsSync27(paths.researchDir)) {
|
|
9693
10870
|
try {
|
|
9694
|
-
const researchFiles =
|
|
10871
|
+
const researchFiles = readdirSync9(paths.researchDir);
|
|
9695
10872
|
for (const file2 of researchFiles) {
|
|
9696
10873
|
const techName = file2.replace(/@.*\.md$/, "");
|
|
9697
10874
|
if (outcome.errorMessage && outcome.errorMessage.toLowerCase().includes(techName.toLowerCase())) {
|
|
@@ -9714,9 +10891,9 @@ function confirmRelatedLessons(projectPath, outcome) {
|
|
|
9714
10891
|
if (outcome.relatedSkills.length === 0) return;
|
|
9715
10892
|
const paths = getAgentWorkspacePaths(projectPath);
|
|
9716
10893
|
const agentName = outcome.agent.replace(/-agent$/, "");
|
|
9717
|
-
const agentPath =
|
|
9718
|
-
if (!
|
|
9719
|
-
const content =
|
|
10894
|
+
const agentPath = join27(paths.featuresDir, agentName, "AGENT.md");
|
|
10895
|
+
if (!existsSync27(agentPath)) return;
|
|
10896
|
+
const content = readFileSync22(agentPath, "utf-8");
|
|
9720
10897
|
const taskWords = outcome.task.toLowerCase().split(/\W+/).filter((w) => w.length > 3);
|
|
9721
10898
|
let updated = content;
|
|
9722
10899
|
let changed = false;
|
|
@@ -9744,28 +10921,28 @@ function writeLesson(projectPath, failure, diagnosis, config2) {
|
|
|
9744
10921
|
return null;
|
|
9745
10922
|
case "scope-error": {
|
|
9746
10923
|
const agentName = failure.agent.replace(/-agent$/, "");
|
|
9747
|
-
targetPath =
|
|
10924
|
+
targetPath = join27(paths.featuresDir, agentName, "AGENT.md");
|
|
9748
10925
|
break;
|
|
9749
10926
|
}
|
|
9750
10927
|
case "wrong-assumption":
|
|
9751
10928
|
case "missing-test": {
|
|
9752
10929
|
const agentName = failure.agent.replace(/-agent$/, "");
|
|
9753
|
-
const featurePath =
|
|
9754
|
-
targetPath =
|
|
10930
|
+
const featurePath = join27(paths.featuresDir, agentName, "AGENT.md");
|
|
10931
|
+
targetPath = existsSync27(featurePath) ? featurePath : join27(paths.projectDir, "AGENT.md");
|
|
9755
10932
|
break;
|
|
9756
10933
|
}
|
|
9757
10934
|
case "missing-convention":
|
|
9758
|
-
targetPath =
|
|
10935
|
+
targetPath = join27(paths.projectDir, "AGENT.md");
|
|
9759
10936
|
break;
|
|
9760
10937
|
case "external-change":
|
|
9761
10938
|
default:
|
|
9762
|
-
targetPath =
|
|
10939
|
+
targetPath = join27(paths.projectDir, "AGENT.md");
|
|
9763
10940
|
break;
|
|
9764
10941
|
}
|
|
9765
|
-
if (!
|
|
10942
|
+
if (!existsSync27(targetPath)) return null;
|
|
9766
10943
|
const evidenceRef = failure.fixApplied ? ` Fix: ${failure.fixApplied.slice(0, 80)}` : "";
|
|
9767
10944
|
const lessonText = `- ${today}: ${diagnosis.description.slice(0, 200)}${evidenceRef} (outcome: ${failure.id.slice(0, 8)})`;
|
|
9768
|
-
const existing =
|
|
10945
|
+
const existing = readFileSync22(targetPath, "utf-8");
|
|
9769
10946
|
const fingerprint = computeErrorFingerprint(diagnosis.description);
|
|
9770
10947
|
if (existing.includes(fingerprint.slice(0, 30))) {
|
|
9771
10948
|
return null;
|
|
@@ -9788,7 +10965,7 @@ function writeLesson(projectPath, failure, diagnosis, config2) {
|
|
|
9788
10965
|
return targetPath;
|
|
9789
10966
|
}
|
|
9790
10967
|
function addRuleToSkill(skillPath, ruleContent, config2) {
|
|
9791
|
-
const content =
|
|
10968
|
+
const content = readFileSync22(skillPath, "utf-8");
|
|
9792
10969
|
if (!content.includes(config2.markers.start)) return false;
|
|
9793
10970
|
const startIdx = content.indexOf(config2.markers.start);
|
|
9794
10971
|
const endIdx = content.indexOf(config2.markers.end);
|
|
@@ -9866,7 +11043,7 @@ function decayOldLessons(projectPath, maxAgeDays = 90) {
|
|
|
9866
11043
|
const cutoff = new Date(Date.now() - maxAgeDays * 24 * 60 * 60 * 1e3).toISOString().split("T")[0];
|
|
9867
11044
|
const agentFiles = findAgentFiles(paths);
|
|
9868
11045
|
for (const filePath of agentFiles) {
|
|
9869
|
-
const content =
|
|
11046
|
+
const content = readFileSync22(filePath, "utf-8");
|
|
9870
11047
|
if (!content.includes(config2.markers.start)) continue;
|
|
9871
11048
|
const updated = content.replace(
|
|
9872
11049
|
/^(- \d{4}-\d{2}-\d{2}: .+?)(?<!\[unconfirmed\])(?<!\[confirmed\])(?<!\[promoted\])$/gm,
|
|
@@ -9887,14 +11064,14 @@ function decayOldLessons(projectPath, maxAgeDays = 90) {
|
|
|
9887
11064
|
}
|
|
9888
11065
|
function findAgentFiles(paths) {
|
|
9889
11066
|
const files = [];
|
|
9890
|
-
const superAgent =
|
|
9891
|
-
if (
|
|
9892
|
-
if (
|
|
11067
|
+
const superAgent = join27(paths.projectDir, "AGENT.md");
|
|
11068
|
+
if (existsSync27(superAgent)) files.push(superAgent);
|
|
11069
|
+
if (existsSync27(paths.featuresDir)) {
|
|
9893
11070
|
try {
|
|
9894
|
-
const features =
|
|
11071
|
+
const features = readdirSync9(paths.featuresDir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
|
|
9895
11072
|
for (const feature of features) {
|
|
9896
|
-
const agentPath =
|
|
9897
|
-
if (
|
|
11073
|
+
const agentPath = join27(paths.featuresDir, feature, "AGENT.md");
|
|
11074
|
+
if (existsSync27(agentPath)) files.push(agentPath);
|
|
9898
11075
|
}
|
|
9899
11076
|
} catch {
|
|
9900
11077
|
}
|
|
@@ -9903,14 +11080,14 @@ function findAgentFiles(paths) {
|
|
|
9903
11080
|
}
|
|
9904
11081
|
function findSkillFiles(paths) {
|
|
9905
11082
|
const files = [];
|
|
9906
|
-
const projectSkill =
|
|
9907
|
-
if (
|
|
9908
|
-
if (
|
|
11083
|
+
const projectSkill = join27(paths.projectDir, "SKILL.md");
|
|
11084
|
+
if (existsSync27(projectSkill)) files.push(projectSkill);
|
|
11085
|
+
if (existsSync27(paths.featuresDir)) {
|
|
9909
11086
|
try {
|
|
9910
|
-
const features =
|
|
11087
|
+
const features = readdirSync9(paths.featuresDir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
|
|
9911
11088
|
for (const feature of features) {
|
|
9912
|
-
const skillPath =
|
|
9913
|
-
if (
|
|
11089
|
+
const skillPath = join27(paths.featuresDir, feature, "SKILL.md");
|
|
11090
|
+
if (existsSync27(skillPath)) files.push(skillPath);
|
|
9914
11091
|
}
|
|
9915
11092
|
} catch {
|
|
9916
11093
|
}
|
|
@@ -10063,8 +11240,8 @@ var init_telemetry = __esm({
|
|
|
10063
11240
|
});
|
|
10064
11241
|
|
|
10065
11242
|
// src/core/agents/tech-detector.ts
|
|
10066
|
-
import { existsSync as
|
|
10067
|
-
import { join as
|
|
11243
|
+
import { existsSync as existsSync29, readFileSync as readFileSync24 } from "fs";
|
|
11244
|
+
import { join as join30 } from "path";
|
|
10068
11245
|
function detectTechnologies(projectPath) {
|
|
10069
11246
|
const technologies = [];
|
|
10070
11247
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -10092,23 +11269,23 @@ function detectTechnologies(projectPath) {
|
|
|
10092
11269
|
return technologies;
|
|
10093
11270
|
}
|
|
10094
11271
|
function detectFromLockfiles(projectPath) {
|
|
10095
|
-
const npmLock =
|
|
10096
|
-
if (
|
|
11272
|
+
const npmLock = join30(projectPath, "package-lock.json");
|
|
11273
|
+
if (existsSync29(npmLock)) {
|
|
10097
11274
|
return parsePackageLock(npmLock);
|
|
10098
11275
|
}
|
|
10099
|
-
const pnpmLock =
|
|
10100
|
-
if (
|
|
11276
|
+
const pnpmLock = join30(projectPath, "pnpm-lock.yaml");
|
|
11277
|
+
if (existsSync29(pnpmLock)) {
|
|
10101
11278
|
return parsePnpmLock(pnpmLock);
|
|
10102
11279
|
}
|
|
10103
|
-
const yarnLock =
|
|
10104
|
-
if (
|
|
11280
|
+
const yarnLock = join30(projectPath, "yarn.lock");
|
|
11281
|
+
if (existsSync29(yarnLock)) {
|
|
10105
11282
|
return parseYarnLock(yarnLock);
|
|
10106
11283
|
}
|
|
10107
11284
|
return [];
|
|
10108
11285
|
}
|
|
10109
11286
|
function parsePackageLock(lockPath) {
|
|
10110
11287
|
try {
|
|
10111
|
-
const content = JSON.parse(
|
|
11288
|
+
const content = JSON.parse(readFileSync24(lockPath, "utf-8"));
|
|
10112
11289
|
const techs = [];
|
|
10113
11290
|
if (content.packages) {
|
|
10114
11291
|
for (const [key, pkg] of Object.entries(content.packages)) {
|
|
@@ -10143,7 +11320,7 @@ function parsePackageLock(lockPath) {
|
|
|
10143
11320
|
}
|
|
10144
11321
|
function parsePnpmLock(lockPath) {
|
|
10145
11322
|
try {
|
|
10146
|
-
const content =
|
|
11323
|
+
const content = readFileSync24(lockPath, "utf-8");
|
|
10147
11324
|
const techs = [];
|
|
10148
11325
|
const packageRegex = /^\s*[/'"]?(@?[^@\s'":]+)@(\d+\.\d+\.\d+[^'":]*)/gm;
|
|
10149
11326
|
let match;
|
|
@@ -10168,7 +11345,7 @@ function parsePnpmLock(lockPath) {
|
|
|
10168
11345
|
}
|
|
10169
11346
|
function parseYarnLock(lockPath) {
|
|
10170
11347
|
try {
|
|
10171
|
-
const content =
|
|
11348
|
+
const content = readFileSync24(lockPath, "utf-8");
|
|
10172
11349
|
const techs = [];
|
|
10173
11350
|
const seen = /* @__PURE__ */ new Set();
|
|
10174
11351
|
const lines = content.split("\n");
|
|
@@ -10202,10 +11379,10 @@ function parseYarnLock(lockPath) {
|
|
|
10202
11379
|
}
|
|
10203
11380
|
function detectFromManifests(projectPath) {
|
|
10204
11381
|
const techs = [];
|
|
10205
|
-
const pkgPath =
|
|
10206
|
-
if (
|
|
11382
|
+
const pkgPath = join30(projectPath, "package.json");
|
|
11383
|
+
if (existsSync29(pkgPath)) {
|
|
10207
11384
|
try {
|
|
10208
|
-
const pkg = JSON.parse(
|
|
11385
|
+
const pkg = JSON.parse(readFileSync24(pkgPath, "utf-8"));
|
|
10209
11386
|
const allDeps = {
|
|
10210
11387
|
...pkg.dependencies,
|
|
10211
11388
|
...pkg.devDependencies
|
|
@@ -10220,10 +11397,10 @@ function detectFromManifests(projectPath) {
|
|
|
10220
11397
|
} catch {
|
|
10221
11398
|
}
|
|
10222
11399
|
}
|
|
10223
|
-
const reqPath =
|
|
10224
|
-
if (
|
|
11400
|
+
const reqPath = join30(projectPath, "requirements.txt");
|
|
11401
|
+
if (existsSync29(reqPath)) {
|
|
10225
11402
|
try {
|
|
10226
|
-
const content =
|
|
11403
|
+
const content = readFileSync24(reqPath, "utf-8");
|
|
10227
11404
|
for (const line of content.split("\n")) {
|
|
10228
11405
|
const match = line.match(/^([a-zA-Z0-9_-]+)==([^\s]+)/);
|
|
10229
11406
|
if (match && match[1] && match[2]) {
|
|
@@ -10233,10 +11410,10 @@ function detectFromManifests(projectPath) {
|
|
|
10233
11410
|
} catch {
|
|
10234
11411
|
}
|
|
10235
11412
|
}
|
|
10236
|
-
const goModPath =
|
|
10237
|
-
if (
|
|
11413
|
+
const goModPath = join30(projectPath, "go.mod");
|
|
11414
|
+
if (existsSync29(goModPath)) {
|
|
10238
11415
|
try {
|
|
10239
|
-
const content =
|
|
11416
|
+
const content = readFileSync24(goModPath, "utf-8");
|
|
10240
11417
|
const reqRegex = /^\s+([^\s]+)\s+v([^\s]+)/gm;
|
|
10241
11418
|
let match;
|
|
10242
11419
|
while ((match = reqRegex.exec(content)) !== null) {
|
|
@@ -10247,10 +11424,10 @@ function detectFromManifests(projectPath) {
|
|
|
10247
11424
|
} catch {
|
|
10248
11425
|
}
|
|
10249
11426
|
}
|
|
10250
|
-
const cargoPath =
|
|
10251
|
-
if (
|
|
11427
|
+
const cargoPath = join30(projectPath, "Cargo.toml");
|
|
11428
|
+
if (existsSync29(cargoPath)) {
|
|
10252
11429
|
try {
|
|
10253
|
-
const content =
|
|
11430
|
+
const content = readFileSync24(cargoPath, "utf-8");
|
|
10254
11431
|
const depRegex = /^([a-zA-Z0-9_-]+)\s*=\s*"([^"]+)"/gm;
|
|
10255
11432
|
let match;
|
|
10256
11433
|
let inDeps = false;
|
|
@@ -10278,12 +11455,12 @@ function detectFromManifests(projectPath) {
|
|
|
10278
11455
|
function detectFromConfigFiles(projectPath) {
|
|
10279
11456
|
const techs = [];
|
|
10280
11457
|
for (const detection of CONFIG_DETECTIONS) {
|
|
10281
|
-
const filePath =
|
|
10282
|
-
if (
|
|
11458
|
+
const filePath = join30(projectPath, detection.file);
|
|
11459
|
+
if (existsSync29(filePath)) {
|
|
10283
11460
|
let version2 = "detected";
|
|
10284
11461
|
if (detection.versionExtractor) {
|
|
10285
11462
|
try {
|
|
10286
|
-
const content =
|
|
11463
|
+
const content = readFileSync24(filePath, "utf-8");
|
|
10287
11464
|
version2 = detection.versionExtractor(content) || "detected";
|
|
10288
11465
|
} catch {
|
|
10289
11466
|
}
|
|
@@ -10298,43 +11475,43 @@ function detectFromConfigFiles(projectPath) {
|
|
|
10298
11475
|
return techs;
|
|
10299
11476
|
}
|
|
10300
11477
|
function detectMonorepo(projectPath) {
|
|
10301
|
-
const pnpmWorkspace =
|
|
10302
|
-
if (
|
|
10303
|
-
const content =
|
|
11478
|
+
const pnpmWorkspace = join30(projectPath, "pnpm-workspace.yaml");
|
|
11479
|
+
if (existsSync29(pnpmWorkspace)) {
|
|
11480
|
+
const content = readFileSync24(pnpmWorkspace, "utf-8");
|
|
10304
11481
|
const packages = extractWorkspacePatterns(content);
|
|
10305
11482
|
return { type: "pnpm", packages, rootPath: projectPath };
|
|
10306
11483
|
}
|
|
10307
|
-
if (
|
|
10308
|
-
const pkgJson =
|
|
11484
|
+
if (existsSync29(join30(projectPath, "turbo.json"))) {
|
|
11485
|
+
const pkgJson = join30(projectPath, "package.json");
|
|
10309
11486
|
let packages = [];
|
|
10310
|
-
if (
|
|
11487
|
+
if (existsSync29(pkgJson)) {
|
|
10311
11488
|
try {
|
|
10312
|
-
const pkg = JSON.parse(
|
|
11489
|
+
const pkg = JSON.parse(readFileSync24(pkgJson, "utf-8"));
|
|
10313
11490
|
packages = Array.isArray(pkg.workspaces) ? pkg.workspaces : pkg.workspaces?.packages || [];
|
|
10314
11491
|
} catch {
|
|
10315
11492
|
}
|
|
10316
11493
|
}
|
|
10317
11494
|
return { type: "turborepo", packages, rootPath: projectPath };
|
|
10318
11495
|
}
|
|
10319
|
-
const pkgPath =
|
|
10320
|
-
if (
|
|
11496
|
+
const pkgPath = join30(projectPath, "package.json");
|
|
11497
|
+
if (existsSync29(pkgPath)) {
|
|
10321
11498
|
try {
|
|
10322
|
-
const pkg = JSON.parse(
|
|
11499
|
+
const pkg = JSON.parse(readFileSync24(pkgPath, "utf-8"));
|
|
10323
11500
|
if (pkg.workspaces) {
|
|
10324
11501
|
const patterns = Array.isArray(pkg.workspaces) ? pkg.workspaces : pkg.workspaces.packages || [];
|
|
10325
|
-
const type =
|
|
11502
|
+
const type = existsSync29(join30(projectPath, "yarn.lock")) ? "yarn-workspaces" : "npm-workspaces";
|
|
10326
11503
|
return { type, packages: patterns, rootPath: projectPath };
|
|
10327
11504
|
}
|
|
10328
11505
|
} catch {
|
|
10329
11506
|
}
|
|
10330
11507
|
}
|
|
10331
|
-
if (
|
|
11508
|
+
if (existsSync29(join30(projectPath, "nx.json"))) {
|
|
10332
11509
|
return { type: "nx", packages: ["packages/*", "apps/*"], rootPath: projectPath };
|
|
10333
11510
|
}
|
|
10334
|
-
const lernaPath =
|
|
10335
|
-
if (
|
|
11511
|
+
const lernaPath = join30(projectPath, "lerna.json");
|
|
11512
|
+
if (existsSync29(lernaPath)) {
|
|
10336
11513
|
try {
|
|
10337
|
-
const lerna = JSON.parse(
|
|
11514
|
+
const lerna = JSON.parse(readFileSync24(lernaPath, "utf-8"));
|
|
10338
11515
|
return { type: "lerna", packages: lerna.packages || ["packages/*"], rootPath: projectPath };
|
|
10339
11516
|
} catch {
|
|
10340
11517
|
}
|
|
@@ -10399,8 +11576,8 @@ var init_tech_detector = __esm({
|
|
|
10399
11576
|
});
|
|
10400
11577
|
|
|
10401
11578
|
// src/core/agents/feature-detector.ts
|
|
10402
|
-
import { existsSync as
|
|
10403
|
-
import { join as
|
|
11579
|
+
import { existsSync as existsSync30, readFileSync as readFileSync25, readdirSync as readdirSync11 } from "fs";
|
|
11580
|
+
import { join as join31, relative as relative7, basename as basename10 } from "path";
|
|
10404
11581
|
function detectFeatures(input) {
|
|
10405
11582
|
const { projectPath, config: config2, importGraph, indexedFiles, fileTechMap } = input;
|
|
10406
11583
|
let candidates = [];
|
|
@@ -10461,13 +11638,15 @@ function findSubPackageJsons(projectPath) {
|
|
|
10461
11638
|
function walk(dir, depth) {
|
|
10462
11639
|
if (depth > maxDepth) return;
|
|
10463
11640
|
try {
|
|
10464
|
-
const entries =
|
|
11641
|
+
const entries = readdirSync11(dir, { withFileTypes: true });
|
|
10465
11642
|
for (const entry of entries) {
|
|
10466
11643
|
if (!entry.isDirectory()) continue;
|
|
10467
|
-
|
|
10468
|
-
|
|
10469
|
-
|
|
10470
|
-
|
|
11644
|
+
const lower = entry.name.toLowerCase();
|
|
11645
|
+
if (lower === "node_modules" || lower === ".git" || lower === "dist") continue;
|
|
11646
|
+
if (EXCLUDED_FEATURE_DIRS.has(lower)) continue;
|
|
11647
|
+
const subDir = join31(dir, entry.name);
|
|
11648
|
+
const pkgJson = join31(subDir, "package.json");
|
|
11649
|
+
if (existsSync30(pkgJson) && subDir !== projectPath) {
|
|
10471
11650
|
results.push(subDir);
|
|
10472
11651
|
}
|
|
10473
11652
|
walk(subDir, depth + 1);
|
|
@@ -10481,20 +11660,20 @@ function findSubPackageJsons(projectPath) {
|
|
|
10481
11660
|
function detectCodeownerFeatures(projectPath, indexedFiles) {
|
|
10482
11661
|
const features = [];
|
|
10483
11662
|
const codeownersLocations = [
|
|
10484
|
-
|
|
10485
|
-
|
|
10486
|
-
|
|
11663
|
+
join31(projectPath, "CODEOWNERS"),
|
|
11664
|
+
join31(projectPath, ".github", "CODEOWNERS"),
|
|
11665
|
+
join31(projectPath, "docs", "CODEOWNERS")
|
|
10487
11666
|
];
|
|
10488
11667
|
let codeownersPath = null;
|
|
10489
11668
|
for (const loc of codeownersLocations) {
|
|
10490
|
-
if (
|
|
11669
|
+
if (existsSync30(loc)) {
|
|
10491
11670
|
codeownersPath = loc;
|
|
10492
11671
|
break;
|
|
10493
11672
|
}
|
|
10494
11673
|
}
|
|
10495
11674
|
if (!codeownersPath) return features;
|
|
10496
11675
|
try {
|
|
10497
|
-
const content =
|
|
11676
|
+
const content = readFileSync25(codeownersPath, "utf-8");
|
|
10498
11677
|
const entries = parseCodeowners(content);
|
|
10499
11678
|
for (const entry of entries) {
|
|
10500
11679
|
const matchingFiles = indexedFiles.filter((f) => matchCodeownerPattern(f, entry.pattern));
|
|
@@ -10571,6 +11750,9 @@ function groupByDirectory(files, projectPath) {
|
|
|
10571
11750
|
if (srcIdx >= 0 && parts.length > srcIdx + 2) {
|
|
10572
11751
|
dirKey = parts.slice(0, srcIdx + 2).join("/");
|
|
10573
11752
|
} else if (parts.length >= 2 && parts[0]) {
|
|
11753
|
+
const topDir = parts[0].toLowerCase();
|
|
11754
|
+
if (EXCLUDED_FEATURE_DIRS.has(topDir)) continue;
|
|
11755
|
+
if (!SOURCE_TOP_DIRS.has(topDir)) continue;
|
|
10574
11756
|
dirKey = parts[0];
|
|
10575
11757
|
} else {
|
|
10576
11758
|
continue;
|
|
@@ -10672,15 +11854,33 @@ function findTestFiles(featurePaths, allFiles) {
|
|
|
10672
11854
|
return testFiles;
|
|
10673
11855
|
}
|
|
10674
11856
|
function isUtilityDir(dirName) {
|
|
10675
|
-
|
|
11857
|
+
const lower = dirName.toLowerCase();
|
|
11858
|
+
return UTILITY_DIRS.has(lower) || EXCLUDED_FEATURE_DIRS.has(lower);
|
|
10676
11859
|
}
|
|
10677
11860
|
function slugify6(name) {
|
|
10678
11861
|
return name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 40);
|
|
10679
11862
|
}
|
|
10680
|
-
var UTILITY_DIRS;
|
|
11863
|
+
var SOURCE_TOP_DIRS, UTILITY_DIRS, EXCLUDED_FEATURE_DIRS;
|
|
10681
11864
|
var init_feature_detector = __esm({
|
|
10682
11865
|
"src/core/agents/feature-detector.ts"() {
|
|
10683
11866
|
"use strict";
|
|
11867
|
+
SOURCE_TOP_DIRS = /* @__PURE__ */ new Set([
|
|
11868
|
+
"src",
|
|
11869
|
+
"app",
|
|
11870
|
+
"apps",
|
|
11871
|
+
"packages",
|
|
11872
|
+
"services",
|
|
11873
|
+
"modules",
|
|
11874
|
+
"test",
|
|
11875
|
+
"tests",
|
|
11876
|
+
"spec",
|
|
11877
|
+
"e2e",
|
|
11878
|
+
"api",
|
|
11879
|
+
"lib",
|
|
11880
|
+
"cmd",
|
|
11881
|
+
"internal",
|
|
11882
|
+
"pkg"
|
|
11883
|
+
]);
|
|
10684
11884
|
UTILITY_DIRS = /* @__PURE__ */ new Set([
|
|
10685
11885
|
"lib",
|
|
10686
11886
|
"libs",
|
|
@@ -10699,13 +11899,54 @@ var init_feature_detector = __esm({
|
|
|
10699
11899
|
"vendor",
|
|
10700
11900
|
"third-party"
|
|
10701
11901
|
]);
|
|
11902
|
+
EXCLUDED_FEATURE_DIRS = /* @__PURE__ */ new Set([
|
|
11903
|
+
// Runtime-generated / workspace
|
|
11904
|
+
"knowledge",
|
|
11905
|
+
".code-impact",
|
|
11906
|
+
".codeimpact",
|
|
11907
|
+
".claude",
|
|
11908
|
+
".cursor",
|
|
11909
|
+
// Documentation
|
|
11910
|
+
"doc",
|
|
11911
|
+
"docs",
|
|
11912
|
+
"documentation",
|
|
11913
|
+
// Build / output
|
|
11914
|
+
"dist",
|
|
11915
|
+
"build",
|
|
11916
|
+
"out",
|
|
11917
|
+
".next",
|
|
11918
|
+
".nuxt",
|
|
11919
|
+
".output",
|
|
11920
|
+
// Dependencies
|
|
11921
|
+
"node_modules",
|
|
11922
|
+
".yarn",
|
|
11923
|
+
".pnpm-store",
|
|
11924
|
+
// CI / config
|
|
11925
|
+
".github",
|
|
11926
|
+
".gitlab",
|
|
11927
|
+
".circleci",
|
|
11928
|
+
"scripts",
|
|
11929
|
+
".husky",
|
|
11930
|
+
// Test fixtures (not features themselves)
|
|
11931
|
+
"fixtures",
|
|
11932
|
+
"base",
|
|
11933
|
+
"__fixtures__",
|
|
11934
|
+
"__snapshots__",
|
|
11935
|
+
// Data / migrations
|
|
11936
|
+
"migrations",
|
|
11937
|
+
"seeds",
|
|
11938
|
+
"data",
|
|
11939
|
+
// IDE / editor
|
|
11940
|
+
".vscode",
|
|
11941
|
+
".idea"
|
|
11942
|
+
]);
|
|
10702
11943
|
}
|
|
10703
11944
|
});
|
|
10704
11945
|
|
|
10705
11946
|
// src/core/agents/git-operations.ts
|
|
10706
11947
|
import { execSync as execSync9 } from "child_process";
|
|
10707
|
-
import { existsSync as
|
|
10708
|
-
import { join as
|
|
11948
|
+
import { existsSync as existsSync31 } from "fs";
|
|
11949
|
+
import { join as join32 } from "path";
|
|
10709
11950
|
function isGitRepo(projectPath) {
|
|
10710
11951
|
try {
|
|
10711
11952
|
git(projectPath, "rev-parse --is-inside-work-tree");
|
|
@@ -10747,7 +11988,7 @@ function commitChanges(projectPath, options) {
|
|
|
10747
11988
|
}
|
|
10748
11989
|
} else {
|
|
10749
11990
|
git(projectPath, "add .code-impact/");
|
|
10750
|
-
if (
|
|
11991
|
+
if (existsSync31(join32(projectPath, "AGENTS.md"))) {
|
|
10751
11992
|
git(projectPath, "add AGENTS.md");
|
|
10752
11993
|
}
|
|
10753
11994
|
}
|
|
@@ -10867,7 +12108,7 @@ var init_git_operations = __esm({
|
|
|
10867
12108
|
});
|
|
10868
12109
|
|
|
10869
12110
|
// src/core/agents/marker-writer.ts
|
|
10870
|
-
import { existsSync as
|
|
12111
|
+
import { existsSync as existsSync32, readFileSync as readFileSync26, writeFileSync as writeFileSync18, mkdirSync as mkdirSync16 } from "fs";
|
|
10871
12112
|
import { dirname as dirname13 } from "path";
|
|
10872
12113
|
function writeMarkedFile(filePath, frontmatter, sections, options) {
|
|
10873
12114
|
const result = {
|
|
@@ -10887,8 +12128,8 @@ function writeMarkedFile(filePath, frontmatter, sections, options) {
|
|
|
10887
12128
|
result.tokenCount = estimateTokens5(autoContent);
|
|
10888
12129
|
return result;
|
|
10889
12130
|
}
|
|
10890
|
-
if (
|
|
10891
|
-
const existing =
|
|
12131
|
+
if (existsSync32(filePath)) {
|
|
12132
|
+
const existing = readFileSync26(filePath, "utf-8");
|
|
10892
12133
|
if (existing.includes(options.markers.start)) {
|
|
10893
12134
|
const existingAutoContent = extractAutoContent(existing, options.markers);
|
|
10894
12135
|
const existingHash = computeContentHash(existingAutoContent);
|
|
@@ -11109,19 +12350,19 @@ var init_token_budget = __esm({
|
|
|
11109
12350
|
});
|
|
11110
12351
|
|
|
11111
12352
|
// src/core/agents/generator.ts
|
|
11112
|
-
import { existsSync as
|
|
11113
|
-
import { join as
|
|
12353
|
+
import { existsSync as existsSync33, readFileSync as readFileSync27, mkdirSync as mkdirSync17 } from "fs";
|
|
12354
|
+
import { join as join34 } from "path";
|
|
11114
12355
|
function generateProjectFiles(input) {
|
|
11115
12356
|
const { projectPath, intelligence, technologies, features, index } = input;
|
|
11116
12357
|
const config2 = readAgentConfig(projectPath);
|
|
11117
12358
|
const paths = getAgentWorkspacePaths(projectPath);
|
|
11118
12359
|
const result = { filesWritten: [], filesSkipped: [] };
|
|
11119
12360
|
mkdirSync17(paths.projectDir, { recursive: true });
|
|
11120
|
-
const skillPath =
|
|
12361
|
+
const skillPath = join34(paths.projectDir, "SKILL.md");
|
|
11121
12362
|
writeMarkedFile2(skillPath, renderProjectSkill(intelligence, technologies, config2, projectPath), config2, result, "project_skill");
|
|
11122
|
-
const convPath =
|
|
12363
|
+
const convPath = join34(paths.projectDir, "CONVENTIONS.md");
|
|
11123
12364
|
writeMarkedFile2(convPath, renderConventions(intelligence, config2), config2, result, "project_conventions");
|
|
11124
|
-
const archPath =
|
|
12365
|
+
const archPath = join34(paths.projectDir, "ARCHITECTURE.md");
|
|
11125
12366
|
writeMarkedFile2(archPath, renderArchitecture(intelligence, config2), config2, result, "project_architecture");
|
|
11126
12367
|
return result;
|
|
11127
12368
|
}
|
|
@@ -11131,9 +12372,9 @@ function generateFeatureFiles(input) {
|
|
|
11131
12372
|
const paths = getAgentWorkspacePaths(projectPath);
|
|
11132
12373
|
const result = { filesWritten: [], filesSkipped: [] };
|
|
11133
12374
|
for (const feature of features) {
|
|
11134
|
-
const featureDir =
|
|
12375
|
+
const featureDir = join34(paths.featuresDir, feature.name);
|
|
11135
12376
|
mkdirSync17(featureDir, { recursive: true });
|
|
11136
|
-
const skillPath =
|
|
12377
|
+
const skillPath = join34(featureDir, "SKILL.md");
|
|
11137
12378
|
const featureTechs = technologies.filter((t) => feature.technologies.includes(t.name));
|
|
11138
12379
|
writeMarkedFile2(skillPath, renderFeatureSkill(feature, featureTechs, config2), config2, result, "feature_skill");
|
|
11139
12380
|
}
|
|
@@ -11191,7 +12432,7 @@ function renderProjectSkill(intel, technologies, config2, projectPath) {
|
|
|
11191
12432
|
} else {
|
|
11192
12433
|
for (const dir of intel.codebase.keyDirectories) {
|
|
11193
12434
|
const dirName = dir.split("/").pop() || dir;
|
|
11194
|
-
const purpose =
|
|
12435
|
+
const purpose = inferDirectoryPurpose(dirName);
|
|
11195
12436
|
lines.push(`| ${dir}/ | ${purpose} |`);
|
|
11196
12437
|
}
|
|
11197
12438
|
}
|
|
@@ -11293,8 +12534,10 @@ function renderArchitecture(intel, config2) {
|
|
|
11293
12534
|
return { frontmatter, autoContent: lines.join("\n") };
|
|
11294
12535
|
}
|
|
11295
12536
|
function renderFeatureSkill(feature, technologies, config2) {
|
|
11296
|
-
const
|
|
11297
|
-
const
|
|
12537
|
+
const grouped = groupScopedPackages(technologies);
|
|
12538
|
+
const techNames = grouped.map((t) => t.name);
|
|
12539
|
+
const researchTechs = technologies.slice(0, 15);
|
|
12540
|
+
const researchRefs = researchTechs.map((t) => `research/${t.name}@${t.version}.md`);
|
|
11298
12541
|
const frontmatter = [
|
|
11299
12542
|
"---",
|
|
11300
12543
|
`name: ${feature.name}-feature`,
|
|
@@ -11307,7 +12550,7 @@ function renderFeatureSkill(feature, technologies, config2) {
|
|
|
11307
12550
|
` research_refs: [${researchRefs.join(", ")}]`,
|
|
11308
12551
|
"---",
|
|
11309
12552
|
"",
|
|
11310
|
-
`# ${
|
|
12553
|
+
`# ${capitalize2(feature.name)} Feature`
|
|
11311
12554
|
].join("\n");
|
|
11312
12555
|
const lines = [];
|
|
11313
12556
|
lines.push("## Scope");
|
|
@@ -11315,8 +12558,9 @@ function renderFeatureSkill(feature, technologies, config2) {
|
|
|
11315
12558
|
lines.push(`- ${p}`);
|
|
11316
12559
|
}
|
|
11317
12560
|
if (feature.testFiles.length > 0) {
|
|
11318
|
-
|
|
11319
|
-
|
|
12561
|
+
const testGlobs = summarizeTestFiles(feature.testFiles);
|
|
12562
|
+
for (const tg of testGlobs) {
|
|
12563
|
+
lines.push(`- ${tg}`);
|
|
11320
12564
|
}
|
|
11321
12565
|
}
|
|
11322
12566
|
lines.push("");
|
|
@@ -11326,14 +12570,16 @@ function renderFeatureSkill(feature, technologies, config2) {
|
|
|
11326
12570
|
if (feature.owner) {
|
|
11327
12571
|
lines.push(`- Owner: ${feature.owner}`);
|
|
11328
12572
|
}
|
|
11329
|
-
if (
|
|
11330
|
-
|
|
12573
|
+
if (grouped.length > 0) {
|
|
12574
|
+
const displayTechs = techNames.slice(0, 10);
|
|
12575
|
+
const suffix = techNames.length > 10 ? `, +${techNames.length - 10} more` : "";
|
|
12576
|
+
lines.push(`- Technologies: ${displayTechs.join(", ")}${suffix}`);
|
|
11331
12577
|
}
|
|
11332
12578
|
lines.push("");
|
|
11333
12579
|
if (researchRefs.length > 0) {
|
|
11334
12580
|
lines.push("## Research References");
|
|
11335
12581
|
for (const ref of researchRefs) {
|
|
11336
|
-
const tech =
|
|
12582
|
+
const tech = researchTechs.find((t) => ref.includes(t.name));
|
|
11337
12583
|
if (tech) {
|
|
11338
12584
|
lines.push(`- [${tech.name} v${tech.version}](../${ref})`);
|
|
11339
12585
|
}
|
|
@@ -11341,13 +12587,13 @@ function renderFeatureSkill(feature, technologies, config2) {
|
|
|
11341
12587
|
lines.push("");
|
|
11342
12588
|
}
|
|
11343
12589
|
lines.push("## Rules");
|
|
11344
|
-
const rules =
|
|
12590
|
+
const rules = deriveFeatureRules(feature, technologies);
|
|
11345
12591
|
for (const rule of rules) {
|
|
11346
12592
|
lines.push(`- ${rule}`);
|
|
11347
12593
|
}
|
|
11348
12594
|
lines.push("");
|
|
11349
12595
|
lines.push("## Pitfalls");
|
|
11350
|
-
const pitfalls =
|
|
12596
|
+
const pitfalls = deriveFeaturePitfalls(feature, technologies);
|
|
11351
12597
|
for (const pitfall of pitfalls) {
|
|
11352
12598
|
lines.push(`- ${pitfall}`);
|
|
11353
12599
|
}
|
|
@@ -11399,130 +12645,26 @@ function parseAutoContentSections(autoContent) {
|
|
|
11399
12645
|
}
|
|
11400
12646
|
return sections;
|
|
11401
12647
|
}
|
|
11402
|
-
function
|
|
11403
|
-
|
|
11404
|
-
|
|
11405
|
-
|
|
11406
|
-
|
|
11407
|
-
|
|
11408
|
-
|
|
11409
|
-
|
|
11410
|
-
|
|
11411
|
-
|
|
11412
|
-
|
|
11413
|
-
|
|
11414
|
-
|
|
11415
|
-
|
|
11416
|
-
|
|
11417
|
-
lib: "Shared utilities"
|
|
11418
|
-
};
|
|
11419
|
-
return purposes[dirName.toLowerCase()] || `${dirName} module`;
|
|
11420
|
-
}
|
|
11421
|
-
function deriveRules(feature, technologies) {
|
|
11422
|
-
const rules = [];
|
|
11423
|
-
for (const tech of technologies) {
|
|
11424
|
-
const knowledge = TECH_KNOWLEDGE[tech.name];
|
|
11425
|
-
if (knowledge) {
|
|
11426
|
-
rules.push(...knowledge.rules);
|
|
11427
|
-
}
|
|
11428
|
-
}
|
|
11429
|
-
if (rules.length === 0) {
|
|
11430
|
-
const featureRules = getFeatureNameRules(feature.name);
|
|
11431
|
-
rules.push(...featureRules);
|
|
11432
|
-
}
|
|
11433
|
-
if (feature.paths.length > 0) {
|
|
11434
|
-
const scope = feature.paths[0]?.replace("/**", "") || "";
|
|
11435
|
-
rules.push(`Changes here should not import from other feature directories \u2014 keep ${scope} self-contained`);
|
|
11436
|
-
}
|
|
11437
|
-
return rules;
|
|
11438
|
-
}
|
|
11439
|
-
function derivePitfalls(feature, technologies) {
|
|
11440
|
-
const pitfalls = [];
|
|
11441
|
-
for (const tech of technologies) {
|
|
11442
|
-
const knowledge = TECH_KNOWLEDGE[tech.name];
|
|
11443
|
-
if (knowledge) {
|
|
11444
|
-
pitfalls.push(...knowledge.pitfalls);
|
|
12648
|
+
function summarizeTestFiles(testFiles) {
|
|
12649
|
+
if (testFiles.length === 0) return [];
|
|
12650
|
+
const dirCounts = /* @__PURE__ */ new Map();
|
|
12651
|
+
for (const tf of testFiles) {
|
|
12652
|
+
const parts = tf.split("/");
|
|
12653
|
+
const dir = parts.slice(0, -1).join("/");
|
|
12654
|
+
dirCounts.set(dir, (dirCounts.get(dir) || 0) + 1);
|
|
12655
|
+
}
|
|
12656
|
+
const globs = [];
|
|
12657
|
+
for (const [dir, count] of dirCounts) {
|
|
12658
|
+
if (count >= 2) {
|
|
12659
|
+
globs.push(`${dir}/**`);
|
|
12660
|
+
} else {
|
|
12661
|
+
const file2 = testFiles.find((f) => f.startsWith(dir + "/"));
|
|
12662
|
+
if (file2) globs.push(file2);
|
|
11445
12663
|
}
|
|
11446
12664
|
}
|
|
11447
|
-
|
|
11448
|
-
const featurePitfalls = getFeatureNamePitfalls(feature.name);
|
|
11449
|
-
pitfalls.push(...featurePitfalls);
|
|
11450
|
-
}
|
|
11451
|
-
return pitfalls;
|
|
11452
|
-
}
|
|
11453
|
-
function getFeatureNameRules(name) {
|
|
11454
|
-
const rules = {
|
|
11455
|
-
server: [
|
|
11456
|
-
"All tool/resource handlers must validate inputs before processing",
|
|
11457
|
-
"Return structured error responses \u2014 never throw raw errors to clients"
|
|
11458
|
-
],
|
|
11459
|
-
storage: [
|
|
11460
|
-
"All database access should go through this module \u2014 no direct imports elsewhere",
|
|
11461
|
-
"Use transactions for multi-step operations to ensure consistency"
|
|
11462
|
-
],
|
|
11463
|
-
indexing: [
|
|
11464
|
-
"Index operations should be idempotent \u2014 reindexing same file produces same result",
|
|
11465
|
-
"Handle parse errors gracefully \u2014 a broken file should not crash the indexer"
|
|
11466
|
-
],
|
|
11467
|
-
core: [
|
|
11468
|
-
"Core modules should have no side effects on import",
|
|
11469
|
-
"Export types alongside functions for downstream consumers"
|
|
11470
|
-
],
|
|
11471
|
-
test: [
|
|
11472
|
-
"Tests should be deterministic \u2014 no reliance on external services or timing",
|
|
11473
|
-
"Clean up temporary files and directories in afterEach/afterAll hooks"
|
|
11474
|
-
],
|
|
11475
|
-
knowledge: [
|
|
11476
|
-
"Skill files must follow the agentskills.io SKILL.md format",
|
|
11477
|
-
"Generated content must use marker comments to preserve manual edits"
|
|
11478
|
-
],
|
|
11479
|
-
doc: [
|
|
11480
|
-
"Generated docs must be kept in sync with source code changes",
|
|
11481
|
-
"Use relative paths for internal links"
|
|
11482
|
-
],
|
|
11483
|
-
auth: [
|
|
11484
|
-
"Never store plain-text passwords \u2014 use bcrypt or argon2",
|
|
11485
|
-
"Validate and sanitize all user inputs before processing"
|
|
11486
|
-
],
|
|
11487
|
-
api: [
|
|
11488
|
-
"All endpoints must validate request bodies/params before processing",
|
|
11489
|
-
"Return consistent error response shapes across all endpoints"
|
|
11490
|
-
]
|
|
11491
|
-
};
|
|
11492
|
-
return rules[name] || [
|
|
11493
|
-
"Follow existing patterns in this module for consistency",
|
|
11494
|
-
"Export public API from index.ts \u2014 keep internal helpers unexported"
|
|
11495
|
-
];
|
|
11496
|
-
}
|
|
11497
|
-
function getFeatureNamePitfalls(name) {
|
|
11498
|
-
const pitfalls = {
|
|
11499
|
-
server: [
|
|
11500
|
-
"Unhandled promise rejections in handlers crash the process \u2014 always catch async errors",
|
|
11501
|
-
"Adding middleware order matters \u2014 auth before route handlers"
|
|
11502
|
-
],
|
|
11503
|
-
storage: [
|
|
11504
|
-
"Forgetting to close database connections leaks file handles",
|
|
11505
|
-
"Concurrent writes without transactions may cause data corruption"
|
|
11506
|
-
],
|
|
11507
|
-
indexing: [
|
|
11508
|
-
"Large files can cause out-of-memory \u2014 set size limits on parsed content",
|
|
11509
|
-
"File paths must be normalized (forward slashes) for cross-platform compatibility"
|
|
11510
|
-
],
|
|
11511
|
-
core: [
|
|
11512
|
-
"Circular imports between core modules cause runtime errors \u2014 check dependency direction",
|
|
11513
|
-
"Changing public API signatures breaks downstream consumers"
|
|
11514
|
-
],
|
|
11515
|
-
test: [
|
|
11516
|
-
"Tests sharing mutable state between runs cause flaky failures",
|
|
11517
|
-
"Temp directories not cleaned up fill disk over repeated test runs"
|
|
11518
|
-
]
|
|
11519
|
-
};
|
|
11520
|
-
return pitfalls[name] || [
|
|
11521
|
-
"Check for null/undefined before property access on external data",
|
|
11522
|
-
"Changing exports may break other modules that depend on this feature"
|
|
11523
|
-
];
|
|
12665
|
+
return globs;
|
|
11524
12666
|
}
|
|
11525
|
-
function
|
|
12667
|
+
function capitalize2(str) {
|
|
11526
12668
|
return str.charAt(0).toUpperCase() + str.slice(1).replace(/-/g, " ");
|
|
11527
12669
|
}
|
|
11528
12670
|
function filterDirectDependencies(technologies, projectPath) {
|
|
@@ -11534,13 +12676,13 @@ function filterDirectDependencies(technologies, projectPath) {
|
|
|
11534
12676
|
}
|
|
11535
12677
|
if (directNames.size === 0) {
|
|
11536
12678
|
const searchPaths = [
|
|
11537
|
-
projectPath ?
|
|
11538
|
-
|
|
12679
|
+
projectPath ? join34(projectPath, "package.json") : "",
|
|
12680
|
+
join34(process.cwd(), "package.json")
|
|
11539
12681
|
].filter(Boolean);
|
|
11540
12682
|
for (const p of searchPaths) {
|
|
11541
12683
|
try {
|
|
11542
|
-
if (
|
|
11543
|
-
const pkg = JSON.parse(
|
|
12684
|
+
if (existsSync33(p)) {
|
|
12685
|
+
const pkg = JSON.parse(readFileSync27(p, "utf-8"));
|
|
11544
12686
|
for (const d of Object.keys(pkg.dependencies || {})) directNames.add(d);
|
|
11545
12687
|
for (const d of Object.keys(pkg.devDependencies || {})) directNames.add(d);
|
|
11546
12688
|
break;
|
|
@@ -11557,138 +12699,50 @@ function filterDirectDependencies(technologies, projectPath) {
|
|
|
11557
12699
|
/^@parcel\//,
|
|
11558
12700
|
/^@biomejs\//
|
|
11559
12701
|
];
|
|
12702
|
+
let filtered;
|
|
11560
12703
|
if (directNames.size > 0) {
|
|
11561
|
-
|
|
12704
|
+
filtered = technologies.filter((t) => directNames.has(t.name)).filter((t) => !skipPatterns.some((p) => p.test(t.name)));
|
|
12705
|
+
} else {
|
|
12706
|
+
filtered = technologies.filter((t) => !skipPatterns.some((p) => p.test(t.name))).slice(0, 30);
|
|
12707
|
+
}
|
|
12708
|
+
return groupScopedPackages(filtered);
|
|
12709
|
+
}
|
|
12710
|
+
function groupScopedPackages(techs) {
|
|
12711
|
+
const scopeMap = /* @__PURE__ */ new Map();
|
|
12712
|
+
const ungrouped = [];
|
|
12713
|
+
for (const t of techs) {
|
|
12714
|
+
const m = t.name.match(/^(@[^/]+)\//);
|
|
12715
|
+
if (m) {
|
|
12716
|
+
const scope = m[1];
|
|
12717
|
+
if (!scopeMap.has(scope)) scopeMap.set(scope, []);
|
|
12718
|
+
scopeMap.get(scope).push(t);
|
|
12719
|
+
} else {
|
|
12720
|
+
ungrouped.push(t);
|
|
12721
|
+
}
|
|
12722
|
+
}
|
|
12723
|
+
const result = [...ungrouped];
|
|
12724
|
+
for (const [scope, members] of scopeMap.entries()) {
|
|
12725
|
+
if (members.length >= 3) {
|
|
12726
|
+
const label = inferScopeLabel(scope, members.length);
|
|
12727
|
+
result.push({
|
|
12728
|
+
name: label,
|
|
12729
|
+
version: members[0].version,
|
|
12730
|
+
source: members[0].source,
|
|
12731
|
+
importPaths: members.flatMap((m) => m.importPaths || [])
|
|
12732
|
+
});
|
|
12733
|
+
} else {
|
|
12734
|
+
result.push(...members);
|
|
12735
|
+
}
|
|
11562
12736
|
}
|
|
11563
|
-
return
|
|
12737
|
+
return result;
|
|
11564
12738
|
}
|
|
11565
|
-
var TECH_KNOWLEDGE;
|
|
11566
12739
|
var init_generator = __esm({
|
|
11567
12740
|
"src/core/agents/generator.ts"() {
|
|
11568
12741
|
"use strict";
|
|
11569
12742
|
init_workspace2();
|
|
11570
12743
|
init_marker_writer();
|
|
11571
12744
|
init_token_budget();
|
|
11572
|
-
|
|
11573
|
-
"express": {
|
|
11574
|
-
rules: [
|
|
11575
|
-
"Use Router() for modular route definitions",
|
|
11576
|
-
"Always register error handler middleware last (4 args: err, req, res, next)"
|
|
11577
|
-
],
|
|
11578
|
-
pitfalls: [
|
|
11579
|
-
"Forgetting to call next() in middleware causes request to hang",
|
|
11580
|
-
"Async errors in handlers need explicit try/catch or express-async-errors"
|
|
11581
|
-
]
|
|
11582
|
-
},
|
|
11583
|
-
"hono": {
|
|
11584
|
-
rules: [
|
|
11585
|
-
"Use app.route() for modular route grouping",
|
|
11586
|
-
"Return c.json() or c.text() \u2014 do not use res.send()"
|
|
11587
|
-
],
|
|
11588
|
-
pitfalls: [
|
|
11589
|
-
"Hono middleware must call next() or return a Response \u2014 skipping both hangs the request"
|
|
11590
|
-
]
|
|
11591
|
-
},
|
|
11592
|
-
"better-sqlite3": {
|
|
11593
|
-
rules: [
|
|
11594
|
-
"Use db.prepare().all() for SELECT, db.prepare().run() for INSERT/UPDATE/DELETE",
|
|
11595
|
-
"better-sqlite3 is synchronous \u2014 do NOT use async/await with db calls",
|
|
11596
|
-
"All database access should go through a single module"
|
|
11597
|
-
],
|
|
11598
|
-
pitfalls: [
|
|
11599
|
-
"db.exec() returns nothing \u2014 using it for SELECT gives undefined",
|
|
11600
|
-
'WAL mode must be set once after opening: db.pragma("journal_mode = WAL")'
|
|
11601
|
-
]
|
|
11602
|
-
},
|
|
11603
|
-
"stripe": {
|
|
11604
|
-
rules: [
|
|
11605
|
-
"Always verify webhook signatures before processing events",
|
|
11606
|
-
"Use idempotency keys for payment creation to prevent double-charges"
|
|
11607
|
-
],
|
|
11608
|
-
pitfalls: [
|
|
11609
|
-
"Stripe webhook events may arrive out of order \u2014 handle idempotently",
|
|
11610
|
-
"stripe.webhooks.constructEvent() throws on invalid signature \u2014 wrap in try/catch"
|
|
11611
|
-
]
|
|
11612
|
-
},
|
|
11613
|
-
"jsonwebtoken": {
|
|
11614
|
-
rules: [
|
|
11615
|
-
"Always use jwt.verify() \u2014 never trust jwt.decode() alone",
|
|
11616
|
-
"Set explicit expiration (expiresIn) on all tokens"
|
|
11617
|
-
],
|
|
11618
|
-
pitfalls: [
|
|
11619
|
-
"Using jwt.decode() without verify() is a security vulnerability",
|
|
11620
|
-
'The "none" algorithm attack \u2014 always specify algorithms: ["HS256"]'
|
|
11621
|
-
]
|
|
11622
|
-
},
|
|
11623
|
-
"@modelcontextprotocol/sdk": {
|
|
11624
|
-
rules: [
|
|
11625
|
-
"Use server.tool() to register MCP tools with zod schema validation",
|
|
11626
|
-
"All tool handlers must return { content: [...] } response objects"
|
|
11627
|
-
],
|
|
11628
|
-
pitfalls: [
|
|
11629
|
-
"Tool names must be unique across the server \u2014 duplicates silently overwrite",
|
|
11630
|
-
"Forgetting to call server.connect(transport) means no requests are handled"
|
|
11631
|
-
]
|
|
11632
|
-
},
|
|
11633
|
-
"prisma": {
|
|
11634
|
-
rules: [
|
|
11635
|
-
"Run npx prisma generate after schema changes",
|
|
11636
|
-
"Use transactions for multi-table operations: prisma.$transaction()"
|
|
11637
|
-
],
|
|
11638
|
-
pitfalls: [
|
|
11639
|
-
"Forgetting prisma generate after schema change causes type mismatches at runtime",
|
|
11640
|
-
"N+1 queries \u2014 use include/select to eagerly load relations"
|
|
11641
|
-
]
|
|
11642
|
-
},
|
|
11643
|
-
"zod": {
|
|
11644
|
-
rules: [
|
|
11645
|
-
"Define schemas once, derive TypeScript types with z.infer<typeof schema>",
|
|
11646
|
-
"Use .safeParse() in handlers for graceful error handling"
|
|
11647
|
-
],
|
|
11648
|
-
pitfalls: [
|
|
11649
|
-
".parse() throws on invalid data \u2014 use .safeParse() in request handlers"
|
|
11650
|
-
]
|
|
11651
|
-
},
|
|
11652
|
-
"vitest": {
|
|
11653
|
-
rules: [
|
|
11654
|
-
"Use describe/it/expect patterns for test organization",
|
|
11655
|
-
"Use vi.mock() for module mocking, vi.fn() for function stubs"
|
|
11656
|
-
],
|
|
11657
|
-
pitfalls: [
|
|
11658
|
-
"vi.mock() is hoisted to top of file \u2014 cannot access variables from outer scope"
|
|
11659
|
-
]
|
|
11660
|
-
},
|
|
11661
|
-
"web-tree-sitter": {
|
|
11662
|
-
rules: [
|
|
11663
|
-
"Initialize Parser with await Parser.init() before use",
|
|
11664
|
-
"Load language WASM files with Parser.Language.load()"
|
|
11665
|
-
],
|
|
11666
|
-
pitfalls: [
|
|
11667
|
-
"Parser.init() must complete before any parsing \u2014 race condition if not awaited",
|
|
11668
|
-
"WASM files must be bundled/copied to dist \u2014 not resolved from node_modules at runtime"
|
|
11669
|
-
]
|
|
11670
|
-
},
|
|
11671
|
-
"@xenova/transformers": {
|
|
11672
|
-
rules: [
|
|
11673
|
-
"Use pipeline() for high-level tasks, AutoModel for custom inference",
|
|
11674
|
-
"Cache downloaded models by setting env.cacheDir"
|
|
11675
|
-
],
|
|
11676
|
-
pitfalls: [
|
|
11677
|
-
"First model load downloads weights (~100MB+) \u2014 cache for subsequent runs",
|
|
11678
|
-
"ONNX runtime may fail on some architectures \u2014 test on target platform"
|
|
11679
|
-
]
|
|
11680
|
-
},
|
|
11681
|
-
"chokidar": {
|
|
11682
|
-
rules: [
|
|
11683
|
-
"Use chokidar.watch() with ignored patterns to skip node_modules",
|
|
11684
|
-
'Handle both "add" and "change" events for file watching'
|
|
11685
|
-
],
|
|
11686
|
-
pitfalls: [
|
|
11687
|
-
"Not closing watcher on process exit leaks file handles",
|
|
11688
|
-
"Rapid file changes may fire multiple events \u2014 debounce handlers"
|
|
11689
|
-
]
|
|
11690
|
-
}
|
|
11691
|
-
};
|
|
12745
|
+
init_universal_inference();
|
|
11692
12746
|
}
|
|
11693
12747
|
});
|
|
11694
12748
|
|
|
@@ -11996,8 +13050,8 @@ var init_co_change_analyzer = __esm({
|
|
|
11996
13050
|
});
|
|
11997
13051
|
|
|
11998
13052
|
// src/core/agents/lifecycle.ts
|
|
11999
|
-
import { existsSync as
|
|
12000
|
-
import { join as
|
|
13053
|
+
import { existsSync as existsSync34, readdirSync as readdirSync12, statSync as statSync6 } from "fs";
|
|
13054
|
+
import { join as join35 } from "path";
|
|
12001
13055
|
function analyzeLifecycle(projectPath, features, config2 = DEFAULT_LIFECYCLE_CONFIG, executor = new RealGitExecutor()) {
|
|
12002
13056
|
const startTime = Date.now();
|
|
12003
13057
|
const proposals = [];
|
|
@@ -12043,14 +13097,14 @@ function analyzeLifecycle(projectPath, features, config2 = DEFAULT_LIFECYCLE_CON
|
|
|
12043
13097
|
}
|
|
12044
13098
|
}
|
|
12045
13099
|
const paths = getAgentWorkspacePaths(projectPath);
|
|
12046
|
-
if (
|
|
13100
|
+
if (existsSync34(paths.featuresDir)) {
|
|
12047
13101
|
try {
|
|
12048
|
-
const featureDirs =
|
|
13102
|
+
const featureDirs = readdirSync12(paths.featuresDir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
|
|
12049
13103
|
const activeFeatureNames = new Set(features.map((f) => f.name));
|
|
12050
13104
|
const cutoffDate = Date.now() - config2.pruneInactiveDays * 24 * 60 * 60 * 1e3;
|
|
12051
13105
|
for (const dirName of featureDirs) {
|
|
12052
13106
|
if (!activeFeatureNames.has(dirName)) {
|
|
12053
|
-
const dirPath =
|
|
13107
|
+
const dirPath = join35(paths.featuresDir, dirName);
|
|
12054
13108
|
const stat = statSync6(dirPath);
|
|
12055
13109
|
if (stat.mtimeMs < cutoffDate) {
|
|
12056
13110
|
proposals.push({
|
|
@@ -12098,8 +13152,8 @@ var init_lifecycle = __esm({
|
|
|
12098
13152
|
});
|
|
12099
13153
|
|
|
12100
13154
|
// src/core/agents/orchestrator.ts
|
|
12101
|
-
import { existsSync as
|
|
12102
|
-
import { join as
|
|
13155
|
+
import { existsSync as existsSync35, readFileSync as readFileSync28, readdirSync as readdirSync13 } from "fs";
|
|
13156
|
+
import { join as join36, relative as relative9 } from "path";
|
|
12103
13157
|
function agentsInit(projectPath) {
|
|
12104
13158
|
const paths = initAgentWorkspace(projectPath);
|
|
12105
13159
|
return {
|
|
@@ -12175,7 +13229,7 @@ async function agentsGenerate(options) {
|
|
|
12175
13229
|
currentIndex.reResearchQueue = [];
|
|
12176
13230
|
writeAgentIndex(projectPath, currentIndex);
|
|
12177
13231
|
}
|
|
12178
|
-
const intel = intelligence || createMinimalIntelligence(projectPath, technologies, features);
|
|
13232
|
+
const intel = intelligence || createMinimalIntelligence(projectPath, technologies, features, importGraph);
|
|
12179
13233
|
const genInput = {
|
|
12180
13234
|
projectPath,
|
|
12181
13235
|
intelligence: intel,
|
|
@@ -12285,8 +13339,8 @@ function agentsStatus(projectPath) {
|
|
|
12285
13339
|
const paths = getAgentWorkspacePaths(projectPath);
|
|
12286
13340
|
let researchFiles = 0;
|
|
12287
13341
|
let staleResearch = 0;
|
|
12288
|
-
if (
|
|
12289
|
-
const files =
|
|
13342
|
+
if (existsSync35(paths.researchDir)) {
|
|
13343
|
+
const files = readdirSync13(paths.researchDir);
|
|
12290
13344
|
researchFiles = files.filter((f) => f.endsWith(".md")).length;
|
|
12291
13345
|
const now = Date.now();
|
|
12292
13346
|
for (const [tech, timestamp] of Object.entries(index.lastResearch)) {
|
|
@@ -12319,10 +13373,10 @@ function getIndexedFilesFromFS(projectPath) {
|
|
|
12319
13373
|
function walk(dir, depth) {
|
|
12320
13374
|
if (depth > 5) return;
|
|
12321
13375
|
try {
|
|
12322
|
-
const entries =
|
|
13376
|
+
const entries = readdirSync13(dir, { withFileTypes: true });
|
|
12323
13377
|
for (const entry of entries) {
|
|
12324
13378
|
if (entry.name.startsWith(".") || entry.name === "node_modules" || entry.name === "dist") continue;
|
|
12325
|
-
const fullPath =
|
|
13379
|
+
const fullPath = join36(dir, entry.name);
|
|
12326
13380
|
if (entry.isDirectory()) {
|
|
12327
13381
|
walk(fullPath, depth + 1);
|
|
12328
13382
|
} else {
|
|
@@ -12356,7 +13410,7 @@ function filterTopTechnologies(technologies) {
|
|
|
12356
13410
|
];
|
|
12357
13411
|
return technologies.filter((t) => !skipPatterns.some((p) => p.test(t.name))).filter((t) => t.source.includes("lock") || t.source === "package.json").slice(0, 20);
|
|
12358
13412
|
}
|
|
12359
|
-
function createMinimalIntelligence(projectPath, technologies, features) {
|
|
13413
|
+
function createMinimalIntelligence(projectPath, technologies, features, importGraph) {
|
|
12360
13414
|
const sourceFiles = getIndexedFilesFromFS(projectPath);
|
|
12361
13415
|
const codeFiles = sourceFiles.filter(
|
|
12362
13416
|
(f) => /\.(ts|tsx|js|jsx|py|go|rs|java|rb|php|cs|cpp|c|h)$/.test(f) && !f.includes("node_modules") && !f.includes("dist")
|
|
@@ -12364,7 +13418,7 @@ function createMinimalIntelligence(projectPath, technologies, features) {
|
|
|
12364
13418
|
let totalLines = 0;
|
|
12365
13419
|
for (const f of codeFiles.slice(0, 500)) {
|
|
12366
13420
|
try {
|
|
12367
|
-
const content =
|
|
13421
|
+
const content = readFileSync28(join36(projectPath, f), "utf-8");
|
|
12368
13422
|
totalLines += content.split("\n").length;
|
|
12369
13423
|
} catch {
|
|
12370
13424
|
}
|
|
@@ -12379,7 +13433,7 @@ function createMinimalIntelligence(projectPath, technologies, features) {
|
|
|
12379
13433
|
const layers = features.map((f) => ({
|
|
12380
13434
|
name: f.name.charAt(0).toUpperCase() + f.name.slice(1),
|
|
12381
13435
|
directory: f.paths[0]?.replace("/**", "") || f.name,
|
|
12382
|
-
purpose:
|
|
13436
|
+
purpose: inferDirectoryPurpose(f.name),
|
|
12383
13437
|
fileCount: f.fileCount
|
|
12384
13438
|
}));
|
|
12385
13439
|
const testFramework = detectTestFramework(projectPath);
|
|
@@ -12396,8 +13450,10 @@ function createMinimalIntelligence(projectPath, technologies, features) {
|
|
|
12396
13450
|
},
|
|
12397
13451
|
architecture: {
|
|
12398
13452
|
layers,
|
|
12399
|
-
dataFlow:
|
|
13453
|
+
dataFlow: inferDataFlowFromGraph(features, importGraph),
|
|
12400
13454
|
keyComponents: [],
|
|
13455
|
+
patternCategories: {},
|
|
13456
|
+
topPatterns: [],
|
|
12401
13457
|
functionStats: { total: 0, exported: 0 }
|
|
12402
13458
|
},
|
|
12403
13459
|
dependencyHotspots: [],
|
|
@@ -12425,7 +13481,7 @@ function buildFileTechMap(projectPath, indexedFiles, technologies) {
|
|
|
12425
13481
|
);
|
|
12426
13482
|
for (const file2 of sourceFiles.slice(0, 300)) {
|
|
12427
13483
|
try {
|
|
12428
|
-
const content =
|
|
13484
|
+
const content = readFileSync28(join36(projectPath, file2), "utf-8");
|
|
12429
13485
|
const techs = /* @__PURE__ */ new Set();
|
|
12430
13486
|
const importRegex = /(?:from\s+['"]|require\s*\(\s*['"])([^./'"@][^'"]*|@[^/'"]+\/[^'"]+)/g;
|
|
12431
13487
|
let match;
|
|
@@ -12463,39 +13519,11 @@ function extToLanguage(ext) {
|
|
|
12463
13519
|
};
|
|
12464
13520
|
return map2[ext] || null;
|
|
12465
13521
|
}
|
|
12466
|
-
function inferLayerPurpose(featureName) {
|
|
12467
|
-
const purposes = {
|
|
12468
|
-
core: "Core business logic and domain modules",
|
|
12469
|
-
server: "MCP server, transports, and request handling",
|
|
12470
|
-
storage: "Database access and persistence layer",
|
|
12471
|
-
indexing: "Code indexing, parsing, and symbol extraction",
|
|
12472
|
-
api: "API routes, handlers, and middleware",
|
|
12473
|
-
auth: "Authentication and authorization",
|
|
12474
|
-
billing: "Payment processing and subscription management",
|
|
12475
|
-
test: "Test fixtures, harness, and evaluation scenarios",
|
|
12476
|
-
doc: "Documentation generation and management",
|
|
12477
|
-
knowledge: "AI knowledge system and skill management",
|
|
12478
|
-
cli: "Command-line interface",
|
|
12479
|
-
base: "Base configuration and shared utilities"
|
|
12480
|
-
};
|
|
12481
|
-
return purposes[featureName] || `${featureName} module`;
|
|
12482
|
-
}
|
|
12483
|
-
function inferDataFlow(features) {
|
|
12484
|
-
const names = features.map((f) => f.name);
|
|
12485
|
-
const flow = [];
|
|
12486
|
-
if (names.includes("server") || names.includes("api")) flow.push("Request");
|
|
12487
|
-
if (names.includes("server")) flow.push("Server");
|
|
12488
|
-
if (names.includes("api")) flow.push("API");
|
|
12489
|
-
if (names.includes("core")) flow.push("Core");
|
|
12490
|
-
if (names.includes("storage")) flow.push("Storage");
|
|
12491
|
-
if (names.includes("indexing")) flow.push("Indexer");
|
|
12492
|
-
return flow.length >= 2 ? flow : [];
|
|
12493
|
-
}
|
|
12494
13522
|
function detectTestFramework(projectPath) {
|
|
12495
|
-
const pkgPath =
|
|
12496
|
-
if (
|
|
13523
|
+
const pkgPath = join36(projectPath, "package.json");
|
|
13524
|
+
if (existsSync35(pkgPath)) {
|
|
12497
13525
|
try {
|
|
12498
|
-
const pkg = JSON.parse(
|
|
13526
|
+
const pkg = JSON.parse(readFileSync28(pkgPath, "utf-8"));
|
|
12499
13527
|
const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
12500
13528
|
if (allDeps["vitest"]) return "vitest";
|
|
12501
13529
|
if (allDeps["jest"]) return "jest";
|
|
@@ -12516,6 +13544,7 @@ var init_orchestrator = __esm({
|
|
|
12516
13544
|
init_research_engine();
|
|
12517
13545
|
init_generator();
|
|
12518
13546
|
init_agent_generator();
|
|
13547
|
+
init_universal_inference();
|
|
12519
13548
|
init_git_operations();
|
|
12520
13549
|
init_improvement_engine();
|
|
12521
13550
|
init_outcome_storage();
|
|
@@ -12525,8 +13554,8 @@ var init_orchestrator = __esm({
|
|
|
12525
13554
|
});
|
|
12526
13555
|
|
|
12527
13556
|
// src/core/agents/migration.ts
|
|
12528
|
-
import { existsSync as
|
|
12529
|
-
import { join as
|
|
13557
|
+
import { existsSync as existsSync36, readFileSync as readFileSync29, writeFileSync as writeFileSync20, mkdirSync as mkdirSync18, cpSync, readdirSync as readdirSync14 } from "fs";
|
|
13558
|
+
import { join as join37, relative as relative10, dirname as dirname15 } from "path";
|
|
12530
13559
|
function migrateKnowledge(options) {
|
|
12531
13560
|
const { projectPath, dryRun = false, backup = false } = options;
|
|
12532
13561
|
const result = {
|
|
@@ -12535,8 +13564,8 @@ function migrateKnowledge(options) {
|
|
|
12535
13564
|
skipped: [],
|
|
12536
13565
|
conflicts: []
|
|
12537
13566
|
};
|
|
12538
|
-
const knowledgeDir =
|
|
12539
|
-
if (!
|
|
13567
|
+
const knowledgeDir = join37(projectPath, "knowledge");
|
|
13568
|
+
if (!existsSync36(knowledgeDir)) {
|
|
12540
13569
|
result.skipped.push({ source: "knowledge/", reason: "Directory does not exist" });
|
|
12541
13570
|
return result;
|
|
12542
13571
|
}
|
|
@@ -12545,7 +13574,7 @@ function migrateKnowledge(options) {
|
|
|
12545
13574
|
}
|
|
12546
13575
|
const paths = getAgentWorkspacePaths(projectPath);
|
|
12547
13576
|
if (backup && !dryRun) {
|
|
12548
|
-
const backupDir =
|
|
13577
|
+
const backupDir = join37(projectPath, `knowledge-backup-${Date.now()}`);
|
|
12549
13578
|
cpSync(knowledgeDir, backupDir, { recursive: true });
|
|
12550
13579
|
result.backupDir = backupDir;
|
|
12551
13580
|
}
|
|
@@ -12558,7 +13587,7 @@ function migrateKnowledge(options) {
|
|
|
12558
13587
|
continue;
|
|
12559
13588
|
}
|
|
12560
13589
|
const targetPath = resolveTargetPath(paths, classification);
|
|
12561
|
-
if (
|
|
13590
|
+
if (existsSync36(targetPath) && !dryRun) {
|
|
12562
13591
|
const merged = mergeSkills(skillFile, targetPath);
|
|
12563
13592
|
if (merged) {
|
|
12564
13593
|
result.conflicts.push({ source: relPath, target: relative10(projectPath, targetPath), resolution: "merged" });
|
|
@@ -12582,10 +13611,10 @@ function migrateKnowledge(options) {
|
|
|
12582
13611
|
function discoverSkillFiles(dir) {
|
|
12583
13612
|
const files = [];
|
|
12584
13613
|
function walk(current) {
|
|
12585
|
-
if (!
|
|
12586
|
-
const entries =
|
|
13614
|
+
if (!existsSync36(current)) return;
|
|
13615
|
+
const entries = readdirSync14(current, { withFileTypes: true });
|
|
12587
13616
|
for (const entry of entries) {
|
|
12588
|
-
const fullPath =
|
|
13617
|
+
const fullPath = join37(current, entry.name);
|
|
12589
13618
|
if (entry.isDirectory()) {
|
|
12590
13619
|
walk(fullPath);
|
|
12591
13620
|
} else if (entry.name === "SKILL.md" || entry.name.endsWith(".skill.md")) {
|
|
@@ -12622,19 +13651,19 @@ function classifySkillPath(relPath) {
|
|
|
12622
13651
|
function resolveTargetPath(paths, classification) {
|
|
12623
13652
|
switch (classification.type) {
|
|
12624
13653
|
case "technology":
|
|
12625
|
-
return
|
|
13654
|
+
return join37(paths.projectDir, `${classification.name}-SKILL.md`);
|
|
12626
13655
|
case "feature":
|
|
12627
|
-
return
|
|
13656
|
+
return join37(paths.featuresDir, classification.name, "SKILL.md");
|
|
12628
13657
|
case "project":
|
|
12629
|
-
return
|
|
13658
|
+
return join37(paths.projectDir, "SKILL.md");
|
|
12630
13659
|
default:
|
|
12631
|
-
return
|
|
13660
|
+
return join37(paths.projectDir, `${classification.name}-SKILL.md`);
|
|
12632
13661
|
}
|
|
12633
13662
|
}
|
|
12634
13663
|
function mergeSkills(sourcePath, targetPath) {
|
|
12635
13664
|
try {
|
|
12636
|
-
const sourceContent =
|
|
12637
|
-
const targetContent =
|
|
13665
|
+
const sourceContent = readFileSync29(sourcePath, "utf-8");
|
|
13666
|
+
const targetContent = readFileSync29(targetPath, "utf-8");
|
|
12638
13667
|
const sourceRules = extractSection2(sourceContent, "Rules");
|
|
12639
13668
|
const sourcePitfalls = extractSection2(sourceContent, "Pitfalls");
|
|
12640
13669
|
if (!sourceRules && !sourcePitfalls) return false;
|
|
@@ -12697,9 +13726,16 @@ __export(agents_exports, {
|
|
|
12697
13726
|
DEFAULT_OUTPUT_DIR: () => DEFAULT_OUTPUT_DIR,
|
|
12698
13727
|
DEFAULT_TRUST_CONFIG: () => DEFAULT_TRUST_CONFIG,
|
|
12699
13728
|
DIAGNOSIS_RULES: () => DIAGNOSIS_RULES,
|
|
13729
|
+
DIR_PURPOSE_MAP: () => DIR_PURPOSE_MAP,
|
|
13730
|
+
DOC_SOURCE_REGISTRY: () => DOC_SOURCE_REGISTRY,
|
|
13731
|
+
FEATURE_NAME_PITFALLS: () => FEATURE_NAME_PITFALLS,
|
|
13732
|
+
FEATURE_NAME_RULES: () => FEATURE_NAME_RULES,
|
|
12700
13733
|
MockGitExecutor: () => MockGitExecutor,
|
|
12701
13734
|
RealGitExecutor: () => RealGitExecutor,
|
|
13735
|
+
SCOPE_LABELS: () => SCOPE_LABELS,
|
|
12702
13736
|
SECTION_PRIORITIES: () => SECTION_PRIORITIES,
|
|
13737
|
+
TECH_KNOWLEDGE: () => TECH_KNOWLEDGE,
|
|
13738
|
+
WELL_KNOWN_FEATURES: () => WELL_KNOWN_FEATURES,
|
|
12703
13739
|
agentWorkspaceExists: () => agentWorkspaceExists,
|
|
12704
13740
|
agentsGenerate: () => agentsGenerate,
|
|
12705
13741
|
agentsInit: () => agentsInit,
|
|
@@ -12707,6 +13743,8 @@ __export(agents_exports, {
|
|
|
12707
13743
|
analyzeCoChanges: () => analyzeCoChanges,
|
|
12708
13744
|
analyzeLifecycle: () => analyzeLifecycle,
|
|
12709
13745
|
assessTrust: () => assessTrust,
|
|
13746
|
+
classifyFeature: () => classifyFeature,
|
|
13747
|
+
classifyTechnology: () => classifyTechnology,
|
|
12710
13748
|
commitAndPush: () => commitAndPush,
|
|
12711
13749
|
commitChanges: () => commitChanges,
|
|
12712
13750
|
computeContentHash: () => computeContentHash,
|
|
@@ -12718,6 +13756,8 @@ __export(agents_exports, {
|
|
|
12718
13756
|
createOutcomeTable: () => createOutcomeTable,
|
|
12719
13757
|
createTelemetryTable: () => createTelemetryTable,
|
|
12720
13758
|
decayOldLessons: () => decayOldLessons,
|
|
13759
|
+
deriveFeaturePitfalls: () => deriveFeaturePitfalls,
|
|
13760
|
+
deriveFeatureRules: () => deriveFeatureRules,
|
|
12721
13761
|
detectFeatures: () => detectFeatures,
|
|
12722
13762
|
detectMonorepo: () => detectMonorepo,
|
|
12723
13763
|
detectProvider: () => detectProvider,
|
|
@@ -12730,6 +13770,7 @@ __export(agents_exports, {
|
|
|
12730
13770
|
ensureFeatureDir: () => ensureFeatureDir,
|
|
12731
13771
|
estimateTokens: () => estimateTokens6,
|
|
12732
13772
|
extractManualContent: () => extractManualContent,
|
|
13773
|
+
extractRulesFromResearch: () => extractRulesFromResearch,
|
|
12733
13774
|
findMergeCandidates: () => findMergeCandidates,
|
|
12734
13775
|
generateAgentFiles: () => generateAgentFiles,
|
|
12735
13776
|
generateAgentsShim: () => generateAgentsShim,
|
|
@@ -12746,6 +13787,11 @@ __export(agents_exports, {
|
|
|
12746
13787
|
getResearchContent: () => getResearchContent,
|
|
12747
13788
|
getSectionPriority: () => getSectionPriority,
|
|
12748
13789
|
hasChanges: () => hasChanges,
|
|
13790
|
+
inferDataFlowFromGraph: () => inferDataFlowFromGraph,
|
|
13791
|
+
inferDirectoryPurpose: () => inferDirectoryPurpose,
|
|
13792
|
+
inferDocSource: () => inferDocSource,
|
|
13793
|
+
inferScopeLabel: () => inferScopeLabel,
|
|
13794
|
+
inferTechRulesAndPitfalls: () => inferTechRulesAndPitfalls,
|
|
12749
13795
|
initAgentWorkspace: () => initAgentWorkspace,
|
|
12750
13796
|
isGitRepo: () => isGitRepo,
|
|
12751
13797
|
learnFromOutcome: () => learnFromOutcome,
|
|
@@ -12791,6 +13837,8 @@ var init_agents = __esm({
|
|
|
12791
13837
|
init_co_change_analyzer();
|
|
12792
13838
|
init_lifecycle();
|
|
12793
13839
|
init_telemetry();
|
|
13840
|
+
init_hardcoded_knowledge();
|
|
13841
|
+
init_universal_inference();
|
|
12794
13842
|
}
|
|
12795
13843
|
});
|
|
12796
13844
|
|
|
@@ -31153,8 +32201,8 @@ ${decision.files.map((f) => `- \`${f}\``).join("\n")}
|
|
|
31153
32201
|
getExistingADRFiles(dir) {
|
|
31154
32202
|
if (!existsSync6(dir)) return [];
|
|
31155
32203
|
try {
|
|
31156
|
-
const { readdirSync:
|
|
31157
|
-
return
|
|
32204
|
+
const { readdirSync: readdirSync15 } = __require("fs");
|
|
32205
|
+
return readdirSync15(dir).filter((f) => /^\d{4}-.*\.md$/.test(f)).sort();
|
|
31158
32206
|
} catch {
|
|
31159
32207
|
return [];
|
|
31160
32208
|
}
|
|
@@ -43124,22 +44172,32 @@ ${END_MARKER}`;
|
|
|
43124
44172
|
return { content: `${block}
|
|
43125
44173
|
`, changed: true };
|
|
43126
44174
|
}
|
|
43127
|
-
|
|
43128
|
-
|
|
43129
|
-
|
|
43130
|
-
const
|
|
43131
|
-
const
|
|
43132
|
-
|
|
43133
|
-
|
|
43134
|
-
|
|
43135
|
-
|
|
43136
|
-
|
|
43137
|
-
|
|
44175
|
+
let cleaned = existing;
|
|
44176
|
+
let hadMarkers = false;
|
|
44177
|
+
while (true) {
|
|
44178
|
+
const start = cleaned.indexOf(START_MARKER);
|
|
44179
|
+
const end = cleaned.indexOf(END_MARKER, start >= 0 ? start : 0);
|
|
44180
|
+
if (start >= 0 && end > start) {
|
|
44181
|
+
hadMarkers = true;
|
|
44182
|
+
cleaned = cleaned.slice(0, start) + cleaned.slice(end + END_MARKER.length);
|
|
44183
|
+
} else if (end >= 0 && start < 0) {
|
|
44184
|
+
hadMarkers = true;
|
|
44185
|
+
cleaned = cleaned.slice(0, end) + cleaned.slice(end + END_MARKER.length);
|
|
44186
|
+
} else {
|
|
44187
|
+
break;
|
|
44188
|
+
}
|
|
43138
44189
|
}
|
|
43139
|
-
|
|
44190
|
+
cleaned = cleaned.replace(/\n{3,}/g, "\n\n").trim();
|
|
44191
|
+
let content;
|
|
44192
|
+
if (!cleaned) {
|
|
44193
|
+
content = `${block}
|
|
44194
|
+
`;
|
|
44195
|
+
} else {
|
|
44196
|
+
content = `${cleaned}
|
|
43140
44197
|
|
|
43141
44198
|
${block}
|
|
43142
44199
|
`;
|
|
44200
|
+
}
|
|
43143
44201
|
return { content, changed: content !== existing };
|
|
43144
44202
|
}
|
|
43145
44203
|
function writeManagedFile(targetPath, section, options) {
|
|
@@ -43172,93 +44230,61 @@ function renderPlatformSection(platform, paths, skillIndex, evolutionGuidance) {
|
|
|
43172
44230
|
let attentionSection = "";
|
|
43173
44231
|
if (evolutionGuidance && evolutionGuidance.length > 0) {
|
|
43174
44232
|
attentionSection = `
|
|
43175
|
-
|
|
43176
44233
|
### Skills Needing Attention
|
|
43177
44234
|
${evolutionGuidance.map((g) => `- ${g}`).join("\n")}`;
|
|
43178
44235
|
}
|
|
43179
|
-
return `# CodeImpact
|
|
44236
|
+
return `# CodeImpact \u2014 AI-Powered Codebase Intelligence
|
|
44237
|
+
|
|
44238
|
+
You have access to **CodeImpact**, a persistent knowledge system that understands this codebase. It provides project skills, architecture knowledge, and self-learning agents.
|
|
43180
44239
|
|
|
43181
|
-
|
|
44240
|
+
## IMPORTANT: Session Start
|
|
44241
|
+
|
|
44242
|
+
1. **Read project knowledge** before making changes:
|
|
44243
|
+
- \`.code-impact/project/SKILL.md\` \u2014 Tech stack, architecture, key directories
|
|
44244
|
+
- \`.code-impact/project/CONVENTIONS.md\` \u2014 Coding standards and patterns
|
|
44245
|
+
- \`.code-impact/project/ARCHITECTURE.md\` \u2014 System layers and data flow
|
|
44246
|
+
2. **Read feature knowledge** for the files you're modifying:
|
|
44247
|
+
- \`.code-impact/features/{feature}/SKILL.md\` \u2014 Feature-specific rules, pitfalls, and research refs
|
|
44248
|
+
- Features: check \`.code-impact/features/\` for available feature directories
|
|
44249
|
+
3. Run \`${tool("memory_status")}\` for project overview and recent changes
|
|
44250
|
+
|
|
44251
|
+
## MCP Tools (Use FIRST before built-in tools)
|
|
43182
44252
|
|
|
43183
|
-
## Tools
|
|
43184
44253
|
| Task | Tool |
|
|
43185
44254
|
|------|------|
|
|
43186
|
-
|
|
|
43187
|
-
|
|
|
43188
|
-
| Verify
|
|
44255
|
+
| Search code semantically | \`${tool("memory_query")}\` |
|
|
44256
|
+
| Review code before changes | \`${tool("memory_review")}\` |
|
|
44257
|
+
| Verify before committing | \`${tool("memory_verify")}\` |
|
|
43189
44258
|
| Project status | \`${tool("memory_status")}\` |
|
|
43190
44259
|
| Impact analysis | \`${tool("memory_blast_radius")}\` |
|
|
44260
|
+
| Agent system | \`${tool("memory_agents")}\` |
|
|
43191
44261
|
| Build knowledge | \`${tool("memory_evolve")}\` |
|
|
43192
44262
|
|
|
43193
|
-
##
|
|
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.
|
|
43196
|
-
|
|
43197
|
-
## Skill Creation Protocol
|
|
43198
|
-
|
|
43199
|
-
**After completing any task involving 3+ files**, create or improve a skill.
|
|
43200
|
-
|
|
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)"
|
|
43208
|
-
|
|
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"
|
|
43214
|
-
|
|
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
|
|
44263
|
+
## Agent System
|
|
43231
44264
|
|
|
43232
|
-
|
|
43233
|
-
|
|
44265
|
+
This project has feature-level agents in \`.code-impact/features/\`. Each agent has:
|
|
44266
|
+
- **SKILL.md** \u2014 Rules, pitfalls, and technology-specific guidance
|
|
44267
|
+
- **AGENT.md** \u2014 Scope, allowed tools, and lessons learned from past mistakes
|
|
43234
44268
|
|
|
43235
|
-
|
|
43236
|
-
|
|
43237
|
-
- API: better-sqlite3 (synchronous, NOT async)
|
|
44269
|
+
Before modifying files, check the relevant feature's SKILL.md for rules and pitfalls.
|
|
44270
|
+
After completing a task, record the outcome via \`${tool("memory_agents")}\` with action="record_outcome".
|
|
43238
44271
|
|
|
43239
|
-
##
|
|
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.
|
|
44272
|
+
## Skill Management
|
|
43242
44273
|
|
|
43243
|
-
|
|
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.
|
|
44274
|
+
After completing any task involving 3+ files, create or improve a skill:
|
|
43246
44275
|
|
|
43247
|
-
|
|
43248
|
-
- npx tsc --noEmit passes with no type errors on database code.
|
|
43249
|
-
\`\`\`
|
|
44276
|
+
\`${tool("memory_evolve")}\` with action="create_skill" (new) or action="improve_skill" (update)
|
|
43250
44277
|
|
|
43251
44278
|
### Quality Rules
|
|
43252
|
-
-
|
|
43253
|
-
-
|
|
43254
|
-
-
|
|
43255
|
-
- **Every line earns its tokens** \u2014 no filler, no generic advice the AI already knows
|
|
44279
|
+
- Under 5000 tokens per skill
|
|
44280
|
+
- Be specific: "Use db.prepare().all() for SELECT" not "follow project patterns"
|
|
44281
|
+
- Include pitfalls with symptoms: "you'll get undefined" not "don't misuse"
|
|
43256
44282
|
|
|
43257
44283
|
## Existing Skills
|
|
43258
44284
|
${skillList}
|
|
43259
44285
|
${attentionSection}
|
|
43260
44286
|
|
|
43261
|
-
## Workspace
|
|
44287
|
+
## Knowledge Workspace
|
|
43262
44288
|
\`${paths.root.replace(/\\/g, "/")}\``;
|
|
43263
44289
|
}
|
|
43264
44290
|
var PlatformRuleSync = class {
|
|
@@ -43642,12 +44668,12 @@ var SkillEvolutionEngine = class {
|
|
|
43642
44668
|
}
|
|
43643
44669
|
}
|
|
43644
44670
|
findSkillFile(dir, skillId) {
|
|
43645
|
-
const { readdirSync:
|
|
43646
|
-
const { join:
|
|
44671
|
+
const { readdirSync: readdirSync15, statSync: statSync8 } = __require("fs");
|
|
44672
|
+
const { join: join39 } = __require("path");
|
|
43647
44673
|
if (!existsSync20(dir)) return null;
|
|
43648
|
-
const entries =
|
|
44674
|
+
const entries = readdirSync15(dir);
|
|
43649
44675
|
for (const entry of entries) {
|
|
43650
|
-
const fullPath =
|
|
44676
|
+
const fullPath = join39(dir, entry);
|
|
43651
44677
|
const stat = statSync8(fullPath);
|
|
43652
44678
|
if (stat.isDirectory()) {
|
|
43653
44679
|
const found = this.findSkillFile(fullPath, skillId);
|
|
@@ -49084,8 +50110,8 @@ init_research_engine();
|
|
|
49084
50110
|
init_outcome_storage();
|
|
49085
50111
|
init_improvement_engine();
|
|
49086
50112
|
init_telemetry();
|
|
49087
|
-
import { existsSync as
|
|
49088
|
-
import { join as
|
|
50113
|
+
import { existsSync as existsSync28, readFileSync as readFileSync23, readdirSync as readdirSync10, writeFileSync as writeFileSync17 } from "fs";
|
|
50114
|
+
import { join as join28 } from "path";
|
|
49089
50115
|
async function handleMemoryAgents(engine, input) {
|
|
49090
50116
|
const projectPath = engine.getProjectPath();
|
|
49091
50117
|
if (!agentWorkspaceExists(projectPath)) {
|
|
@@ -49126,34 +50152,34 @@ function handleListSkills(projectPath, input) {
|
|
|
49126
50152
|
if (!input.scope || input.scope === "project") {
|
|
49127
50153
|
const projectFiles = ["SKILL.md", "CONVENTIONS.md", "ARCHITECTURE.md"];
|
|
49128
50154
|
for (const file2 of projectFiles) {
|
|
49129
|
-
const filePath =
|
|
49130
|
-
if (
|
|
50155
|
+
const filePath = join28(paths.projectDir, file2);
|
|
50156
|
+
if (existsSync28(filePath)) {
|
|
49131
50157
|
skills.push({ name: file2.replace(".md", "").toLowerCase(), type: "project", path: `project/${file2}`, source: ".code-impact" });
|
|
49132
50158
|
}
|
|
49133
50159
|
}
|
|
49134
50160
|
}
|
|
49135
50161
|
if (!input.scope || input.scope === "feature") {
|
|
49136
|
-
if (
|
|
49137
|
-
const features =
|
|
50162
|
+
if (existsSync28(paths.featuresDir)) {
|
|
50163
|
+
const features = readdirSync10(paths.featuresDir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
|
|
49138
50164
|
for (const feature of features) {
|
|
49139
50165
|
if (input.feature && feature !== input.feature) continue;
|
|
49140
|
-
const skillPath =
|
|
49141
|
-
if (
|
|
50166
|
+
const skillPath = join28(paths.featuresDir, feature, "SKILL.md");
|
|
50167
|
+
if (existsSync28(skillPath)) {
|
|
49142
50168
|
skills.push({ name: `${feature}-skill`, type: "feature", path: `features/${feature}/SKILL.md`, source: ".code-impact" });
|
|
49143
50169
|
}
|
|
49144
50170
|
}
|
|
49145
50171
|
}
|
|
49146
50172
|
}
|
|
49147
|
-
const legacySkillsDir =
|
|
49148
|
-
if (
|
|
50173
|
+
const legacySkillsDir = join28(projectPath, "knowledge", "skills");
|
|
50174
|
+
if (existsSync28(legacySkillsDir)) {
|
|
49149
50175
|
try {
|
|
49150
50176
|
for (const scope of ["technology", "feature", "core", "risk"]) {
|
|
49151
|
-
const scopeDir =
|
|
49152
|
-
if (!
|
|
49153
|
-
const skillDirs =
|
|
50177
|
+
const scopeDir = join28(legacySkillsDir, scope);
|
|
50178
|
+
if (!existsSync28(scopeDir)) continue;
|
|
50179
|
+
const skillDirs = readdirSync10(scopeDir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
|
|
49154
50180
|
for (const skillName of skillDirs) {
|
|
49155
|
-
const skillPath =
|
|
49156
|
-
if (
|
|
50181
|
+
const skillPath = join28(scopeDir, skillName, "SKILL.md");
|
|
50182
|
+
if (existsSync28(skillPath)) {
|
|
49157
50183
|
const alreadyListed = skills.some((s) => s.name === skillName || s.name === `${skillName}-skill`);
|
|
49158
50184
|
if (!alreadyListed) {
|
|
49159
50185
|
skills.push({
|
|
@@ -49179,9 +50205,9 @@ function handleListSkills(projectPath, input) {
|
|
|
49179
50205
|
function handleListAgents(projectPath) {
|
|
49180
50206
|
const paths = getAgentWorkspacePaths(projectPath);
|
|
49181
50207
|
const agents = [];
|
|
49182
|
-
const superAgentPath =
|
|
49183
|
-
if (
|
|
49184
|
-
const content =
|
|
50208
|
+
const superAgentPath = join28(paths.projectDir, "AGENT.md");
|
|
50209
|
+
if (existsSync28(superAgentPath)) {
|
|
50210
|
+
const content = readFileSync23(superAgentPath, "utf-8");
|
|
49185
50211
|
const agent = parseAgentMd(content);
|
|
49186
50212
|
if (agent) {
|
|
49187
50213
|
agents.push({
|
|
@@ -49192,12 +50218,12 @@ function handleListAgents(projectPath) {
|
|
|
49192
50218
|
});
|
|
49193
50219
|
}
|
|
49194
50220
|
}
|
|
49195
|
-
if (
|
|
49196
|
-
const features =
|
|
50221
|
+
if (existsSync28(paths.featuresDir)) {
|
|
50222
|
+
const features = readdirSync10(paths.featuresDir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
|
|
49197
50223
|
for (const feature of features) {
|
|
49198
|
-
const agentPath =
|
|
49199
|
-
if (
|
|
49200
|
-
const content =
|
|
50224
|
+
const agentPath = join28(paths.featuresDir, feature, "AGENT.md");
|
|
50225
|
+
if (existsSync28(agentPath)) {
|
|
50226
|
+
const content = readFileSync23(agentPath, "utf-8");
|
|
49201
50227
|
const agent = parseAgentMd(content);
|
|
49202
50228
|
if (agent) {
|
|
49203
50229
|
agents.push({
|
|
@@ -49222,32 +50248,32 @@ function handleGetSkill(projectPath, input) {
|
|
|
49222
50248
|
return { success: false, action: "get_skill", message: "Missing required parameter: name" };
|
|
49223
50249
|
}
|
|
49224
50250
|
const paths = getAgentWorkspacePaths(projectPath);
|
|
49225
|
-
const projectFile =
|
|
49226
|
-
if (
|
|
50251
|
+
const projectFile = join28(paths.projectDir, `${input.name.toUpperCase()}.md`);
|
|
50252
|
+
if (existsSync28(projectFile)) {
|
|
49227
50253
|
return {
|
|
49228
50254
|
success: true,
|
|
49229
50255
|
action: "get_skill",
|
|
49230
50256
|
message: `Found project skill: ${input.name}`,
|
|
49231
|
-
data: { content:
|
|
50257
|
+
data: { content: readFileSync23(projectFile, "utf-8"), path: `project/${input.name.toUpperCase()}.md` }
|
|
49232
50258
|
};
|
|
49233
50259
|
}
|
|
49234
|
-
const skillFile =
|
|
49235
|
-
if (input.name === "project-overview" &&
|
|
50260
|
+
const skillFile = join28(paths.projectDir, "SKILL.md");
|
|
50261
|
+
if (input.name === "project-overview" && existsSync28(skillFile)) {
|
|
49236
50262
|
return {
|
|
49237
50263
|
success: true,
|
|
49238
50264
|
action: "get_skill",
|
|
49239
50265
|
message: "Found project skill",
|
|
49240
|
-
data: { content:
|
|
50266
|
+
data: { content: readFileSync23(skillFile, "utf-8"), path: "project/SKILL.md" }
|
|
49241
50267
|
};
|
|
49242
50268
|
}
|
|
49243
50269
|
const featureName = input.name.replace(/-skill$/, "").replace(/-feature$/, "");
|
|
49244
|
-
const featureSkill =
|
|
49245
|
-
if (
|
|
50270
|
+
const featureSkill = join28(paths.featuresDir, featureName, "SKILL.md");
|
|
50271
|
+
if (existsSync28(featureSkill)) {
|
|
49246
50272
|
return {
|
|
49247
50273
|
success: true,
|
|
49248
50274
|
action: "get_skill",
|
|
49249
50275
|
message: `Found feature skill: ${featureName}`,
|
|
49250
|
-
data: { content:
|
|
50276
|
+
data: { content: readFileSync23(featureSkill, "utf-8"), path: `features/${featureName}/SKILL.md` }
|
|
49251
50277
|
};
|
|
49252
50278
|
}
|
|
49253
50279
|
return { success: false, action: "get_skill", message: `Skill not found: ${input.name}` };
|
|
@@ -49258,9 +50284,9 @@ function handleGetAgent(projectPath, input) {
|
|
|
49258
50284
|
}
|
|
49259
50285
|
const paths = getAgentWorkspacePaths(projectPath);
|
|
49260
50286
|
if (input.name === "project-coordinator") {
|
|
49261
|
-
const superPath =
|
|
49262
|
-
if (
|
|
49263
|
-
const content =
|
|
50287
|
+
const superPath = join28(paths.projectDir, "AGENT.md");
|
|
50288
|
+
if (existsSync28(superPath)) {
|
|
50289
|
+
const content = readFileSync23(superPath, "utf-8");
|
|
49264
50290
|
const agent = parseAgentMd(content);
|
|
49265
50291
|
return {
|
|
49266
50292
|
success: true,
|
|
@@ -49271,9 +50297,9 @@ function handleGetAgent(projectPath, input) {
|
|
|
49271
50297
|
}
|
|
49272
50298
|
}
|
|
49273
50299
|
const featureName = input.name.replace(/-agent$/, "");
|
|
49274
|
-
const agentPath =
|
|
49275
|
-
if (
|
|
49276
|
-
const content =
|
|
50300
|
+
const agentPath = join28(paths.featuresDir, featureName, "AGENT.md");
|
|
50301
|
+
if (existsSync28(agentPath)) {
|
|
50302
|
+
const content = readFileSync23(agentPath, "utf-8");
|
|
49277
50303
|
const agent = parseAgentMd(content);
|
|
49278
50304
|
return {
|
|
49279
50305
|
success: true,
|
|
@@ -49306,15 +50332,15 @@ function handleValidateAction(projectPath, input) {
|
|
|
49306
50332
|
const paths = getAgentWorkspacePaths(projectPath);
|
|
49307
50333
|
let agentDef = null;
|
|
49308
50334
|
if (input.agent === "project-coordinator") {
|
|
49309
|
-
const superPath =
|
|
49310
|
-
if (
|
|
49311
|
-
agentDef = parseAgentMd(
|
|
50335
|
+
const superPath = join28(paths.projectDir, "AGENT.md");
|
|
50336
|
+
if (existsSync28(superPath)) {
|
|
50337
|
+
agentDef = parseAgentMd(readFileSync23(superPath, "utf-8"));
|
|
49312
50338
|
}
|
|
49313
50339
|
} else {
|
|
49314
50340
|
const featureName = input.agent.replace(/-agent$/, "");
|
|
49315
|
-
const agentPath =
|
|
49316
|
-
if (
|
|
49317
|
-
agentDef = parseAgentMd(
|
|
50341
|
+
const agentPath = join28(paths.featuresDir, featureName, "AGENT.md");
|
|
50342
|
+
if (existsSync28(agentPath)) {
|
|
50343
|
+
agentDef = parseAgentMd(readFileSync23(agentPath, "utf-8"));
|
|
49318
50344
|
}
|
|
49319
50345
|
}
|
|
49320
50346
|
if (!agentDef) {
|
|
@@ -49415,11 +50441,11 @@ function handleProposeImprovement(projectPath, input) {
|
|
|
49415
50441
|
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
49416
50442
|
};
|
|
49417
50443
|
const paths = getAgentWorkspacePaths(projectPath);
|
|
49418
|
-
const proposalsPath =
|
|
50444
|
+
const proposalsPath = join28(paths.root, "proposals.json");
|
|
49419
50445
|
let proposals = [];
|
|
49420
|
-
if (
|
|
50446
|
+
if (existsSync28(proposalsPath)) {
|
|
49421
50447
|
try {
|
|
49422
|
-
proposals = JSON.parse(
|
|
50448
|
+
proposals = JSON.parse(readFileSync23(proposalsPath, "utf-8"));
|
|
49423
50449
|
} catch {
|
|
49424
50450
|
}
|
|
49425
50451
|
}
|
|
@@ -53118,13 +54144,13 @@ var StreamableHTTPServerTransport = class {
|
|
|
53118
54144
|
};
|
|
53119
54145
|
|
|
53120
54146
|
// src/utils/config.ts
|
|
53121
|
-
import { join as
|
|
54147
|
+
import { join as join29, resolve as resolve2 } from "path";
|
|
53122
54148
|
function getDefaultConfig(projectPath) {
|
|
53123
54149
|
const normalizedPath = resolve2(projectPath);
|
|
53124
54150
|
return {
|
|
53125
54151
|
projectPath: normalizedPath,
|
|
53126
54152
|
// Store in project directory (standard practice like .git/, .vscode/)
|
|
53127
|
-
dataDir:
|
|
54153
|
+
dataDir: join29(normalizedPath, ".codeimpact"),
|
|
53128
54154
|
maxTokens: 6e3,
|
|
53129
54155
|
embeddingModel: "Xenova/all-MiniLM-L6-v2",
|
|
53130
54156
|
// Fallback model, faster and smaller
|
|
@@ -53478,9 +54504,9 @@ To connect clients, run in each project:`);
|
|
|
53478
54504
|
};
|
|
53479
54505
|
|
|
53480
54506
|
// src/cli/commands.ts
|
|
53481
|
-
import { join as
|
|
54507
|
+
import { join as join38, resolve as resolve3 } from "path";
|
|
53482
54508
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
53483
|
-
import { existsSync as
|
|
54509
|
+
import { existsSync as existsSync37, readFileSync as readFileSync30, writeFileSync as writeFileSync21, mkdirSync as mkdirSync19 } from "fs";
|
|
53484
54510
|
import { homedir as homedir2 } from "os";
|
|
53485
54511
|
var projectManager = new ProjectManager();
|
|
53486
54512
|
function listProjects() {
|
|
@@ -53595,10 +54621,10 @@ function exportDecisions(projectPath, options = {}) {
|
|
|
53595
54621
|
message: `Project not registered: ${targetPath}. Use "codeimpact projects add ${targetPath}" first.`
|
|
53596
54622
|
};
|
|
53597
54623
|
}
|
|
53598
|
-
let dbPath =
|
|
53599
|
-
if (!
|
|
53600
|
-
const oldDbPath =
|
|
53601
|
-
if (
|
|
54624
|
+
let dbPath = join38(projectInfo.dataDir, "codeimpact.db");
|
|
54625
|
+
if (!existsSync37(dbPath)) {
|
|
54626
|
+
const oldDbPath = join38(projectInfo.dataDir, "codeimpact.db");
|
|
54627
|
+
if (existsSync37(oldDbPath)) {
|
|
53602
54628
|
dbPath = oldDbPath;
|
|
53603
54629
|
} else {
|
|
53604
54630
|
return {
|
|
@@ -53625,17 +54651,17 @@ function exportDecisions(projectPath, options = {}) {
|
|
|
53625
54651
|
});
|
|
53626
54652
|
return {
|
|
53627
54653
|
success: true,
|
|
53628
|
-
message: `Exported ${exportedFiles.length} ADR files to ${options.outputDir ||
|
|
54654
|
+
message: `Exported ${exportedFiles.length} ADR files to ${options.outputDir || join38(targetPath, "docs", "decisions")}`,
|
|
53629
54655
|
data: exportedFiles
|
|
53630
54656
|
};
|
|
53631
54657
|
}
|
|
53632
54658
|
function findDatabasePath(projectInfo) {
|
|
53633
|
-
const centralizedPath =
|
|
53634
|
-
if (
|
|
54659
|
+
const centralizedPath = join38(projectInfo.dataDir, "codeimpact.db");
|
|
54660
|
+
if (existsSync37(centralizedPath)) {
|
|
53635
54661
|
return centralizedPath;
|
|
53636
54662
|
}
|
|
53637
|
-
const projectLocalPath =
|
|
53638
|
-
if (
|
|
54663
|
+
const projectLocalPath = join38(projectInfo.path, ".codeimpact", "codeimpact.db");
|
|
54664
|
+
if (existsSync37(projectLocalPath)) {
|
|
53639
54665
|
return projectLocalPath;
|
|
53640
54666
|
}
|
|
53641
54667
|
return null;
|
|
@@ -54197,8 +55223,8 @@ function showProject(projectId) {
|
|
|
54197
55223
|
function configureMCPClient(clientName, configPath, serverName, projectPath) {
|
|
54198
55224
|
let config2 = { mcpServers: {} };
|
|
54199
55225
|
try {
|
|
54200
|
-
if (
|
|
54201
|
-
const content =
|
|
55226
|
+
if (existsSync37(configPath)) {
|
|
55227
|
+
const content = readFileSync30(configPath, "utf-8");
|
|
54202
55228
|
config2 = JSON.parse(content);
|
|
54203
55229
|
} else {
|
|
54204
55230
|
const sep = process.platform === "win32" ? "\\" : "/";
|
|
@@ -54234,8 +55260,8 @@ function configureMCPClient(clientName, configPath, serverName, projectPath) {
|
|
|
54234
55260
|
function configureProjectMCP(configPath, projectPath) {
|
|
54235
55261
|
let config2 = { mcpServers: {} };
|
|
54236
55262
|
try {
|
|
54237
|
-
if (
|
|
54238
|
-
const content =
|
|
55263
|
+
if (existsSync37(configPath)) {
|
|
55264
|
+
const content = readFileSync30(configPath, "utf-8");
|
|
54239
55265
|
config2 = JSON.parse(content);
|
|
54240
55266
|
}
|
|
54241
55267
|
} catch {
|
|
@@ -54267,7 +55293,7 @@ function configureProjectMCP(configPath, projectPath) {
|
|
|
54267
55293
|
}
|
|
54268
55294
|
}
|
|
54269
55295
|
function configureClaudeMD(projectPath) {
|
|
54270
|
-
const claudeMdPath =
|
|
55296
|
+
const claudeMdPath = join38(projectPath, "CLAUDE.md");
|
|
54271
55297
|
const codeimpactSection = `
|
|
54272
55298
|
## CodeImpact Integration
|
|
54273
55299
|
|
|
@@ -54337,8 +55363,8 @@ codeimpact reindex
|
|
|
54337
55363
|
`;
|
|
54338
55364
|
try {
|
|
54339
55365
|
let existingContent = "";
|
|
54340
|
-
if (
|
|
54341
|
-
existingContent =
|
|
55366
|
+
if (existsSync37(claudeMdPath)) {
|
|
55367
|
+
existingContent = readFileSync30(claudeMdPath, "utf-8");
|
|
54342
55368
|
if (existingContent.includes("## CodeImpact Integration")) {
|
|
54343
55369
|
const startMarker = "## CodeImpact Integration";
|
|
54344
55370
|
const startIndex = existingContent.indexOf(startMarker);
|
|
@@ -54369,18 +55395,18 @@ ${codeimpactSection}`;
|
|
|
54369
55395
|
}
|
|
54370
55396
|
}
|
|
54371
55397
|
function configureCursorProjectMCP(projectPath) {
|
|
54372
|
-
const cursorDir =
|
|
54373
|
-
const configPath =
|
|
55398
|
+
const cursorDir = join38(projectPath, ".cursor");
|
|
55399
|
+
const configPath = join38(cursorDir, "mcp.json");
|
|
54374
55400
|
try {
|
|
54375
|
-
if (!
|
|
55401
|
+
if (!existsSync37(cursorDir)) {
|
|
54376
55402
|
mkdirSync19(cursorDir, { recursive: true });
|
|
54377
55403
|
}
|
|
54378
55404
|
} catch {
|
|
54379
55405
|
}
|
|
54380
55406
|
let config2 = { mcpServers: {} };
|
|
54381
55407
|
try {
|
|
54382
|
-
if (
|
|
54383
|
-
const content =
|
|
55408
|
+
if (existsSync37(configPath)) {
|
|
55409
|
+
const content = readFileSync30(configPath, "utf-8");
|
|
54384
55410
|
config2 = JSON.parse(content);
|
|
54385
55411
|
}
|
|
54386
55412
|
} catch {
|
|
@@ -54411,7 +55437,7 @@ function configureCursorProjectMCP(projectPath) {
|
|
|
54411
55437
|
}
|
|
54412
55438
|
}
|
|
54413
55439
|
function configureCursorRules(projectPath) {
|
|
54414
|
-
const cursorRulesPath =
|
|
55440
|
+
const cursorRulesPath = join38(projectPath, ".cursorrules");
|
|
54415
55441
|
const codeimpactSection = `
|
|
54416
55442
|
# CodeImpact Integration
|
|
54417
55443
|
|
|
@@ -54475,8 +55501,8 @@ codeimpact stats
|
|
|
54475
55501
|
`;
|
|
54476
55502
|
try {
|
|
54477
55503
|
let existingContent = "";
|
|
54478
|
-
if (
|
|
54479
|
-
existingContent =
|
|
55504
|
+
if (existsSync37(cursorRulesPath)) {
|
|
55505
|
+
existingContent = readFileSync30(cursorRulesPath, "utf-8");
|
|
54480
55506
|
if (existingContent.includes("# CodeImpact Integration")) {
|
|
54481
55507
|
const startMarker = "# CodeImpact Integration";
|
|
54482
55508
|
const startIndex = existingContent.indexOf(startMarker);
|
|
@@ -54505,11 +55531,11 @@ codeimpact stats
|
|
|
54505
55531
|
}
|
|
54506
55532
|
}
|
|
54507
55533
|
function configureOpenCode(projectPath) {
|
|
54508
|
-
const configPath =
|
|
55534
|
+
const configPath = join38(projectPath, "opencode.json");
|
|
54509
55535
|
let config2 = {};
|
|
54510
55536
|
try {
|
|
54511
|
-
if (
|
|
54512
|
-
const content =
|
|
55537
|
+
if (existsSync37(configPath)) {
|
|
55538
|
+
const content = readFileSync30(configPath, "utf-8");
|
|
54513
55539
|
config2 = JSON.parse(content);
|
|
54514
55540
|
}
|
|
54515
55541
|
} catch {
|
|
@@ -54545,8 +55571,8 @@ function configureOpenCode(projectPath) {
|
|
|
54545
55571
|
function configureRemoteMCPClient(clientName, configPath, serverName, serverUrl) {
|
|
54546
55572
|
let config2 = { mcpServers: {} };
|
|
54547
55573
|
try {
|
|
54548
|
-
if (
|
|
54549
|
-
config2 = JSON.parse(
|
|
55574
|
+
if (existsSync37(configPath)) {
|
|
55575
|
+
config2 = JSON.parse(readFileSync30(configPath, "utf-8"));
|
|
54550
55576
|
}
|
|
54551
55577
|
} catch {
|
|
54552
55578
|
}
|
|
@@ -54554,7 +55580,7 @@ function configureRemoteMCPClient(clientName, configPath, serverName, serverUrl)
|
|
|
54554
55580
|
config2.mcpServers[serverName] = { url: serverUrl };
|
|
54555
55581
|
try {
|
|
54556
55582
|
const dir = configPath.substring(0, configPath.lastIndexOf("/") || configPath.lastIndexOf("\\"));
|
|
54557
|
-
if (dir && !
|
|
55583
|
+
if (dir && !existsSync37(dir)) mkdirSync19(dir, { recursive: true });
|
|
54558
55584
|
writeFileSync21(configPath, JSON.stringify(config2, null, 2));
|
|
54559
55585
|
return { success: true, message: `${clientName}: ${configPath}` };
|
|
54560
55586
|
} catch (err) {
|
|
@@ -54574,27 +55600,27 @@ function initProject(projectPath, serverUrl) {
|
|
|
54574
55600
|
const failedClients = [];
|
|
54575
55601
|
let claudeConfigPath;
|
|
54576
55602
|
if (platform === "win32") {
|
|
54577
|
-
claudeConfigPath =
|
|
55603
|
+
claudeConfigPath = join38(homedir2(), "AppData", "Roaming", "Claude", "claude_desktop_config.json");
|
|
54578
55604
|
} else if (platform === "darwin") {
|
|
54579
|
-
claudeConfigPath =
|
|
55605
|
+
claudeConfigPath = join38(homedir2(), "Library", "Application Support", "Claude", "claude_desktop_config.json");
|
|
54580
55606
|
} else {
|
|
54581
|
-
claudeConfigPath =
|
|
55607
|
+
claudeConfigPath = join38(homedir2(), ".config", "claude", "claude_desktop_config.json");
|
|
54582
55608
|
}
|
|
54583
55609
|
if (serverUrl) {
|
|
54584
55610
|
const remoteProjectUrl = `${serverUrl}/mcp?project=${encodeURIComponent(resolve3(targetPath))}`;
|
|
54585
55611
|
const claudeResult = configureRemoteMCPClient("Claude Desktop", claudeConfigPath, serverName, remoteProjectUrl);
|
|
54586
55612
|
if (claudeResult.success) configuredClients.push(claudeResult.message);
|
|
54587
55613
|
else failedClients.push(claudeResult.message);
|
|
54588
|
-
const claudeCodeConfigPath =
|
|
55614
|
+
const claudeCodeConfigPath = join38(targetPath, ".mcp.json");
|
|
54589
55615
|
const claudeCodeResult = configureRemoteMCPClient("Claude Code", claudeCodeConfigPath, "codeimpact", remoteProjectUrl);
|
|
54590
55616
|
if (claudeCodeResult.success) configuredClients.push(claudeCodeResult.message);
|
|
54591
55617
|
let cursorConfigPath;
|
|
54592
55618
|
if (platform === "win32") {
|
|
54593
|
-
cursorConfigPath =
|
|
55619
|
+
cursorConfigPath = join38(homedir2(), "AppData", "Roaming", "Cursor", "User", "globalStorage", "cursor.mcp", "mcp.json");
|
|
54594
55620
|
} else if (platform === "darwin") {
|
|
54595
|
-
cursorConfigPath =
|
|
55621
|
+
cursorConfigPath = join38(homedir2(), "Library", "Application Support", "Cursor", "User", "globalStorage", "cursor.mcp", "mcp.json");
|
|
54596
55622
|
} else {
|
|
54597
|
-
cursorConfigPath =
|
|
55623
|
+
cursorConfigPath = join38(homedir2(), ".config", "Cursor", "User", "globalStorage", "cursor.mcp", "mcp.json");
|
|
54598
55624
|
}
|
|
54599
55625
|
const cursorResult = configureRemoteMCPClient("Cursor (global)", cursorConfigPath, serverName, remoteProjectUrl);
|
|
54600
55626
|
if (cursorResult.success) configuredClients.push(cursorResult.message);
|
|
@@ -54606,16 +55632,16 @@ function initProject(projectPath, serverUrl) {
|
|
|
54606
55632
|
const openCodeResult = configureOpenCode(targetPath);
|
|
54607
55633
|
if (openCodeResult.success) configuredClients.push(openCodeResult.message);
|
|
54608
55634
|
else failedClients.push(openCodeResult.message);
|
|
54609
|
-
const claudeCodeConfigPath =
|
|
55635
|
+
const claudeCodeConfigPath = join38(targetPath, ".mcp.json");
|
|
54610
55636
|
const claudeCodeResult = configureProjectMCP(claudeCodeConfigPath, targetPath);
|
|
54611
55637
|
if (claudeCodeResult.success) configuredClients.push(claudeCodeResult.message);
|
|
54612
55638
|
let cursorConfigPath;
|
|
54613
55639
|
if (platform === "win32") {
|
|
54614
|
-
cursorConfigPath =
|
|
55640
|
+
cursorConfigPath = join38(homedir2(), "AppData", "Roaming", "Cursor", "User", "globalStorage", "cursor.mcp", "mcp.json");
|
|
54615
55641
|
} else if (platform === "darwin") {
|
|
54616
|
-
cursorConfigPath =
|
|
55642
|
+
cursorConfigPath = join38(homedir2(), "Library", "Application Support", "Cursor", "User", "globalStorage", "cursor.mcp", "mcp.json");
|
|
54617
55643
|
} else {
|
|
54618
|
-
cursorConfigPath =
|
|
55644
|
+
cursorConfigPath = join38(homedir2(), ".config", "Cursor", "User", "globalStorage", "cursor.mcp", "mcp.json");
|
|
54619
55645
|
}
|
|
54620
55646
|
const cursorGlobalResult = configureMCPClient("Cursor (global)", cursorConfigPath, serverName, targetPath);
|
|
54621
55647
|
if (cursorGlobalResult.success) configuredClients.push(cursorGlobalResult.message);
|