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 CHANGED
@@ -8300,7 +8300,7 @@ function buildFeatureAgentDefinition(feature) {
8300
8300
  created_by: "code-impact"
8301
8301
  },
8302
8302
  scope: {
8303
- includedPaths: [...feature.paths, ...feature.testFiles],
8303
+ includedPaths: [...feature.paths, ...deriveTestGlobs(feature)],
8304
8304
  excludedPaths: feature.paths.map((p) => p.replace("/**", "/__mocks__/**"))
8305
8305
  },
8306
8306
  allowedTools: [
@@ -8473,6 +8473,25 @@ function renderAgentsShim(agents, config2) {
8473
8473
  }
8474
8474
  return lines.join("\n");
8475
8475
  }
8476
+ function deriveTestGlobs(feature) {
8477
+ if (feature.testFiles.length === 0) return [];
8478
+ const dirCounts = /* @__PURE__ */ new Map();
8479
+ for (const tf of feature.testFiles) {
8480
+ const parts = tf.split("/");
8481
+ const dir = parts.slice(0, -1).join("/");
8482
+ dirCounts.set(dir, (dirCounts.get(dir) || 0) + 1);
8483
+ }
8484
+ const globs = [];
8485
+ for (const [dir, count] of dirCounts) {
8486
+ if (count >= 2) {
8487
+ globs.push(`${dir}/**`);
8488
+ } else {
8489
+ const file2 = feature.testFiles.find((f) => f.startsWith(dir + "/"));
8490
+ if (file2) globs.push(file2);
8491
+ }
8492
+ }
8493
+ return globs;
8494
+ }
8476
8495
  function parseAgentMd(content) {
8477
8496
  try {
8478
8497
  const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---/);
@@ -8644,22 +8663,1290 @@ var init_research_trust = __esm({
8644
8663
  }
8645
8664
  });
8646
8665
 
8647
- // src/core/agents/research-engine.ts
8648
- import { existsSync as existsSync25, readFileSync as readFileSync20, writeFileSync as writeFileSync15, mkdirSync as mkdirSync15, readdirSync as readdirSync7 } from "fs";
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 = join25(paths.researchDir, fileName);
9941
+ const filePath = join26(paths.researchDir, fileName);
8655
9942
  const relPath = `research/${fileName}`;
8656
- if (!options.force && existsSync25(filePath)) {
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 = findDocSource(tech.name);
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 (!existsSync25(filePath)) {
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 = readFileSync20(filePath, "utf-8");
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 = join25(paths.researchDir, `${technology}@${version2}.md`);
8905
- if (existsSync25(exactPath)) {
8906
- return readFileSync20(exactPath, "utf-8");
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 (existsSync25(paths.researchDir)) {
8910
- const files = readdirSync7(paths.researchDir);
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 readFileSync20(join25(paths.researchDir, match), "utf-8");
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
- DOC_SOURCE_REGISTRY = [
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 existsSync26, readFileSync as readFileSync21, writeFileSync as writeFileSync16, readdirSync as readdirSync8 } from "fs";
9416
- import { join as join26 } from "path";
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 = readFileSync21(filePath, "utf-8");
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 ? join26(paths.featuresDir, featureName, "SKILL.md") : join26(paths.projectDir, "SKILL.md");
9599
- if (existsSync26(skillPath)) {
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 = join26(paths.featuresDir, name, "AGENT.md");
9619
- if (!existsSync26(agentPath)) return true;
9620
- const agentContent = readFileSync21(agentPath, "utf-8");
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 = join26(paths.featuresDir, agentName, "SKILL.md");
9647
- const targetPath = existsSync26(featureSkillPath) ? featureSkillPath : join26(paths.projectDir, "SKILL.md");
9648
- if (!existsSync26(targetPath)) return null;
9649
- const content = readFileSync21(targetPath, "utf-8");
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 (existsSync26(paths.researchDir)) {
10869
+ if (existsSync27(paths.researchDir)) {
9693
10870
  try {
9694
- const researchFiles = readdirSync8(paths.researchDir);
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 = join26(paths.featuresDir, agentName, "AGENT.md");
9718
- if (!existsSync26(agentPath)) return;
9719
- const content = readFileSync21(agentPath, "utf-8");
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 = join26(paths.featuresDir, agentName, "AGENT.md");
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 = join26(paths.featuresDir, agentName, "AGENT.md");
9754
- targetPath = existsSync26(featurePath) ? featurePath : join26(paths.projectDir, "AGENT.md");
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 = join26(paths.projectDir, "AGENT.md");
10935
+ targetPath = join27(paths.projectDir, "AGENT.md");
9759
10936
  break;
9760
10937
  case "external-change":
9761
10938
  default:
9762
- targetPath = join26(paths.projectDir, "AGENT.md");
10939
+ targetPath = join27(paths.projectDir, "AGENT.md");
9763
10940
  break;
9764
10941
  }
9765
- if (!existsSync26(targetPath)) return null;
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 = readFileSync21(targetPath, "utf-8");
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 = readFileSync21(skillPath, "utf-8");
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 = readFileSync21(filePath, "utf-8");
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 = join26(paths.projectDir, "AGENT.md");
9891
- if (existsSync26(superAgent)) files.push(superAgent);
9892
- if (existsSync26(paths.featuresDir)) {
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 = readdirSync8(paths.featuresDir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
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 = join26(paths.featuresDir, feature, "AGENT.md");
9897
- if (existsSync26(agentPath)) files.push(agentPath);
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 = join26(paths.projectDir, "SKILL.md");
9907
- if (existsSync26(projectSkill)) files.push(projectSkill);
9908
- if (existsSync26(paths.featuresDir)) {
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 = readdirSync8(paths.featuresDir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
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 = join26(paths.featuresDir, feature, "SKILL.md");
9913
- if (existsSync26(skillPath)) files.push(skillPath);
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 existsSync28, readFileSync as readFileSync23 } from "fs";
10067
- import { join as join29 } from "path";
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 = join29(projectPath, "package-lock.json");
10096
- if (existsSync28(npmLock)) {
11272
+ const npmLock = join30(projectPath, "package-lock.json");
11273
+ if (existsSync29(npmLock)) {
10097
11274
  return parsePackageLock(npmLock);
10098
11275
  }
10099
- const pnpmLock = join29(projectPath, "pnpm-lock.yaml");
10100
- if (existsSync28(pnpmLock)) {
11276
+ const pnpmLock = join30(projectPath, "pnpm-lock.yaml");
11277
+ if (existsSync29(pnpmLock)) {
10101
11278
  return parsePnpmLock(pnpmLock);
10102
11279
  }
10103
- const yarnLock = join29(projectPath, "yarn.lock");
10104
- if (existsSync28(yarnLock)) {
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(readFileSync23(lockPath, "utf-8"));
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 = readFileSync23(lockPath, "utf-8");
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 = readFileSync23(lockPath, "utf-8");
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 = join29(projectPath, "package.json");
10206
- if (existsSync28(pkgPath)) {
11382
+ const pkgPath = join30(projectPath, "package.json");
11383
+ if (existsSync29(pkgPath)) {
10207
11384
  try {
10208
- const pkg = JSON.parse(readFileSync23(pkgPath, "utf-8"));
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 = join29(projectPath, "requirements.txt");
10224
- if (existsSync28(reqPath)) {
11400
+ const reqPath = join30(projectPath, "requirements.txt");
11401
+ if (existsSync29(reqPath)) {
10225
11402
  try {
10226
- const content = readFileSync23(reqPath, "utf-8");
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 = join29(projectPath, "go.mod");
10237
- if (existsSync28(goModPath)) {
11413
+ const goModPath = join30(projectPath, "go.mod");
11414
+ if (existsSync29(goModPath)) {
10238
11415
  try {
10239
- const content = readFileSync23(goModPath, "utf-8");
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 = join29(projectPath, "Cargo.toml");
10251
- if (existsSync28(cargoPath)) {
11427
+ const cargoPath = join30(projectPath, "Cargo.toml");
11428
+ if (existsSync29(cargoPath)) {
10252
11429
  try {
10253
- const content = readFileSync23(cargoPath, "utf-8");
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 = join29(projectPath, detection.file);
10282
- if (existsSync28(filePath)) {
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 = readFileSync23(filePath, "utf-8");
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 = join29(projectPath, "pnpm-workspace.yaml");
10302
- if (existsSync28(pnpmWorkspace)) {
10303
- const content = readFileSync23(pnpmWorkspace, "utf-8");
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 (existsSync28(join29(projectPath, "turbo.json"))) {
10308
- const pkgJson = join29(projectPath, "package.json");
11484
+ if (existsSync29(join30(projectPath, "turbo.json"))) {
11485
+ const pkgJson = join30(projectPath, "package.json");
10309
11486
  let packages = [];
10310
- if (existsSync28(pkgJson)) {
11487
+ if (existsSync29(pkgJson)) {
10311
11488
  try {
10312
- const pkg = JSON.parse(readFileSync23(pkgJson, "utf-8"));
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 = join29(projectPath, "package.json");
10320
- if (existsSync28(pkgPath)) {
11496
+ const pkgPath = join30(projectPath, "package.json");
11497
+ if (existsSync29(pkgPath)) {
10321
11498
  try {
10322
- const pkg = JSON.parse(readFileSync23(pkgPath, "utf-8"));
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 = existsSync28(join29(projectPath, "yarn.lock")) ? "yarn-workspaces" : "npm-workspaces";
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 (existsSync28(join29(projectPath, "nx.json"))) {
11508
+ if (existsSync29(join30(projectPath, "nx.json"))) {
10332
11509
  return { type: "nx", packages: ["packages/*", "apps/*"], rootPath: projectPath };
10333
11510
  }
10334
- const lernaPath = join29(projectPath, "lerna.json");
10335
- if (existsSync28(lernaPath)) {
11511
+ const lernaPath = join30(projectPath, "lerna.json");
11512
+ if (existsSync29(lernaPath)) {
10336
11513
  try {
10337
- const lerna = JSON.parse(readFileSync23(lernaPath, "utf-8"));
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 existsSync29, readFileSync as readFileSync24, readdirSync as readdirSync10 } from "fs";
10403
- import { join as join30, relative as relative7, basename as basename10 } from "path";
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 = readdirSync10(dir, { withFileTypes: true });
11641
+ const entries = readdirSync11(dir, { withFileTypes: true });
10465
11642
  for (const entry of entries) {
10466
11643
  if (!entry.isDirectory()) continue;
10467
- if (entry.name === "node_modules" || entry.name === ".git" || entry.name === "dist") continue;
10468
- const subDir = join30(dir, entry.name);
10469
- const pkgJson = join30(subDir, "package.json");
10470
- if (existsSync29(pkgJson) && subDir !== projectPath) {
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
- join30(projectPath, "CODEOWNERS"),
10485
- join30(projectPath, ".github", "CODEOWNERS"),
10486
- join30(projectPath, "docs", "CODEOWNERS")
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 (existsSync29(loc)) {
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 = readFileSync24(codeownersPath, "utf-8");
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
- return UTILITY_DIRS.has(dirName.toLowerCase());
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 existsSync30 } from "fs";
10708
- import { join as join31 } from "path";
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 (existsSync30(join31(projectPath, "AGENTS.md"))) {
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 existsSync31, readFileSync as readFileSync25, writeFileSync as writeFileSync18, mkdirSync as mkdirSync16 } from "fs";
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 (existsSync31(filePath)) {
10891
- const existing = readFileSync25(filePath, "utf-8");
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 existsSync32, readFileSync as readFileSync26, mkdirSync as mkdirSync17 } from "fs";
11113
- import { join as join33 } from "path";
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 = join33(paths.projectDir, "SKILL.md");
12361
+ const skillPath = join34(paths.projectDir, "SKILL.md");
11121
12362
  writeMarkedFile2(skillPath, renderProjectSkill(intelligence, technologies, config2, projectPath), config2, result, "project_skill");
11122
- const convPath = join33(paths.projectDir, "CONVENTIONS.md");
12363
+ const convPath = join34(paths.projectDir, "CONVENTIONS.md");
11123
12364
  writeMarkedFile2(convPath, renderConventions(intelligence, config2), config2, result, "project_conventions");
11124
- const archPath = join33(paths.projectDir, "ARCHITECTURE.md");
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 = join33(paths.featuresDir, feature.name);
12375
+ const featureDir = join34(paths.featuresDir, feature.name);
11135
12376
  mkdirSync17(featureDir, { recursive: true });
11136
- const skillPath = join33(featureDir, "SKILL.md");
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 = inferDirPurpose(dirName);
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 techNames = technologies.map((t) => t.name);
11297
- const researchRefs = technologies.map((t) => `research/${t.name}@${t.version}.md`);
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
- `# ${capitalize(feature.name)} Feature`
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
- for (const tf of feature.testFiles) {
11319
- lines.push(`- ${tf}`);
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 (technologies.length > 0) {
11330
- lines.push(`- Technologies: ${techNames.join(", ")}`);
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 = technologies.find((t) => ref.includes(t.name));
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 = deriveRules(feature, technologies);
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 = derivePitfalls(feature, technologies);
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 inferDirPurpose(dirName) {
11403
- const purposes = {
11404
- core: "Core business logic and domain modules",
11405
- server: "Server, transports, and request handling",
11406
- storage: "Database access and persistence",
11407
- indexing: "Code indexing and symbol extraction",
11408
- api: "API routes and handlers",
11409
- auth: "Authentication and authorization",
11410
- billing: "Payment processing",
11411
- test: "Tests and evaluation harness",
11412
- doc: "Documentation",
11413
- knowledge: "AI knowledge system",
11414
- cli: "Command-line interface",
11415
- base: "Base configuration and fixtures",
11416
- src: "Application source code",
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
- if (pitfalls.length === 0) {
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 capitalize(str) {
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 ? join33(projectPath, "package.json") : "",
11538
- join33(process.cwd(), "package.json")
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 (existsSync32(p)) {
11543
- const pkg = JSON.parse(readFileSync26(p, "utf-8"));
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
- return technologies.filter((t) => directNames.has(t.name)).filter((t) => !skipPatterns.some((p) => p.test(t.name)));
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 technologies.filter((t) => !skipPatterns.some((p) => p.test(t.name))).slice(0, 20);
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
- TECH_KNOWLEDGE = {
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 existsSync33, readdirSync as readdirSync11, statSync as statSync6 } from "fs";
12000
- import { join as join34 } from "path";
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 (existsSync33(paths.featuresDir)) {
13100
+ if (existsSync34(paths.featuresDir)) {
12047
13101
  try {
12048
- const featureDirs = readdirSync11(paths.featuresDir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
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 = join34(paths.featuresDir, dirName);
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 existsSync34, readFileSync as readFileSync27, readdirSync as readdirSync12 } from "fs";
12102
- import { join as join35, relative as relative9 } from "path";
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 (existsSync34(paths.researchDir)) {
12289
- const files = readdirSync12(paths.researchDir);
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 = readdirSync12(dir, { withFileTypes: true });
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 = join35(dir, entry.name);
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 = readFileSync27(join35(projectPath, f), "utf-8");
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: inferLayerPurpose(f.name),
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: inferDataFlow(features),
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 = readFileSync27(join35(projectPath, file2), "utf-8");
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 = join35(projectPath, "package.json");
12496
- if (existsSync34(pkgPath)) {
13523
+ const pkgPath = join36(projectPath, "package.json");
13524
+ if (existsSync35(pkgPath)) {
12497
13525
  try {
12498
- const pkg = JSON.parse(readFileSync27(pkgPath, "utf-8"));
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 existsSync35, readFileSync as readFileSync28, writeFileSync as writeFileSync20, mkdirSync as mkdirSync18, cpSync, readdirSync as readdirSync13 } from "fs";
12529
- import { join as join36, relative as relative10, dirname as dirname15 } from "path";
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 = join36(projectPath, "knowledge");
12539
- if (!existsSync35(knowledgeDir)) {
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 = join36(projectPath, `knowledge-backup-${Date.now()}`);
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 (existsSync35(targetPath) && !dryRun) {
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 (!existsSync35(current)) return;
12586
- const entries = readdirSync13(current, { withFileTypes: true });
13614
+ if (!existsSync36(current)) return;
13615
+ const entries = readdirSync14(current, { withFileTypes: true });
12587
13616
  for (const entry of entries) {
12588
- const fullPath = join36(current, entry.name);
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 join36(paths.projectDir, `${classification.name}-SKILL.md`);
13654
+ return join37(paths.projectDir, `${classification.name}-SKILL.md`);
12626
13655
  case "feature":
12627
- return join36(paths.featuresDir, classification.name, "SKILL.md");
13656
+ return join37(paths.featuresDir, classification.name, "SKILL.md");
12628
13657
  case "project":
12629
- return join36(paths.projectDir, "SKILL.md");
13658
+ return join37(paths.projectDir, "SKILL.md");
12630
13659
  default:
12631
- return join36(paths.projectDir, `${classification.name}-SKILL.md`);
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 = readFileSync28(sourcePath, "utf-8");
12637
- const targetContent = readFileSync28(targetPath, "utf-8");
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: readdirSync14 } = __require("fs");
31157
- return readdirSync14(dir).filter((f) => /^\d{4}-.*\.md$/.test(f)).sort();
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
- const start = existing.indexOf(START_MARKER);
43128
- const end = existing.indexOf(END_MARKER);
43129
- if (start >= 0 && end > start) {
43130
- const before = existing.slice(0, start).trimEnd();
43131
- const after = existing.slice(end + END_MARKER.length).trimStart();
43132
- const content2 = `${before}
43133
-
43134
- ${block}
43135
-
43136
- ${after}`.trim() + "\n";
43137
- return { content: content2, changed: content2 !== existing };
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
- const content = `${existing.trimEnd()}
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 Knowledge System
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
- You are part of a **self-improving knowledge system**. Skills you create persist across sessions. Future AI sessions benefit from the knowledge you build now.
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
- | Find code | \`${tool("memory_query")}\` |
43187
- | Check code | \`${tool("memory_review")}\` |
43188
- | Verify code | \`${tool("memory_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
- ## Session Start
43194
- 1. Run \`${tool("memory_status")}\` \u2014 check \`knowledge_gaps\` for uncovered technologies and high-risk files.
43195
- 2. Read relevant skills from \`knowledge/skills/\` for the current task.
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
- ## When to Use
43233
- When modifying database queries, adding tables, or working with files that import from src/storage/database.ts.
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
- ## Key Facts
43236
- - Database: .codeimpact/codeimpact.db (SQLite, WAL mode)
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
- ## Rules
43240
- - ALL database access goes through database.ts \u2014 never import better-sqlite3 directly.
43241
- - Use db.prepare().all() for SELECT, db.prepare().run() for INSERT/UPDATE/DELETE.
44272
+ ## Skill Management
43242
44273
 
43243
- ## Pitfalls
43244
- - db.exec() returns nothing. If you use it for SELECT, you get undefined.
43245
- - better-sqlite3 is synchronous. Do NOT wrap in async/await.
44274
+ After completing any task involving 3+ files, create or improve a skill:
43246
44275
 
43247
- ## Verification
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
- - **Under 5000 tokens** per skill
43253
- - **Be specific**: "Use db.prepare().all() for SELECT" not "follow project patterns"
43254
- - **Pitfalls with symptoms**: "you'll get undefined" not "don't misuse"
43255
- - **Every line earns its tokens** \u2014 no filler, no generic advice the AI already knows
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: readdirSync14, statSync: statSync8 } = __require("fs");
43646
- const { join: join38 } = __require("path");
44671
+ const { readdirSync: readdirSync15, statSync: statSync8 } = __require("fs");
44672
+ const { join: join39 } = __require("path");
43647
44673
  if (!existsSync20(dir)) return null;
43648
- const entries = readdirSync14(dir);
44674
+ const entries = readdirSync15(dir);
43649
44675
  for (const entry of entries) {
43650
- const fullPath = join38(dir, entry);
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 existsSync27, readFileSync as readFileSync22, readdirSync as readdirSync9, writeFileSync as writeFileSync17 } from "fs";
49088
- import { join as join27 } from "path";
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 = join27(paths.projectDir, file2);
49130
- if (existsSync27(filePath)) {
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 (existsSync27(paths.featuresDir)) {
49137
- const features = readdirSync9(paths.featuresDir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
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 = join27(paths.featuresDir, feature, "SKILL.md");
49141
- if (existsSync27(skillPath)) {
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 = join27(projectPath, "knowledge", "skills");
49148
- if (existsSync27(legacySkillsDir)) {
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 = join27(legacySkillsDir, scope);
49152
- if (!existsSync27(scopeDir)) continue;
49153
- const skillDirs = readdirSync9(scopeDir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
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 = join27(scopeDir, skillName, "SKILL.md");
49156
- if (existsSync27(skillPath)) {
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 = join27(paths.projectDir, "AGENT.md");
49183
- if (existsSync27(superAgentPath)) {
49184
- const content = readFileSync22(superAgentPath, "utf-8");
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 (existsSync27(paths.featuresDir)) {
49196
- const features = readdirSync9(paths.featuresDir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
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 = join27(paths.featuresDir, feature, "AGENT.md");
49199
- if (existsSync27(agentPath)) {
49200
- const content = readFileSync22(agentPath, "utf-8");
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 = join27(paths.projectDir, `${input.name.toUpperCase()}.md`);
49226
- if (existsSync27(projectFile)) {
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: readFileSync22(projectFile, "utf-8"), path: `project/${input.name.toUpperCase()}.md` }
50257
+ data: { content: readFileSync23(projectFile, "utf-8"), path: `project/${input.name.toUpperCase()}.md` }
49232
50258
  };
49233
50259
  }
49234
- const skillFile = join27(paths.projectDir, "SKILL.md");
49235
- if (input.name === "project-overview" && existsSync27(skillFile)) {
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: readFileSync22(skillFile, "utf-8"), path: "project/SKILL.md" }
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 = join27(paths.featuresDir, featureName, "SKILL.md");
49245
- if (existsSync27(featureSkill)) {
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: readFileSync22(featureSkill, "utf-8"), path: `features/${featureName}/SKILL.md` }
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 = join27(paths.projectDir, "AGENT.md");
49262
- if (existsSync27(superPath)) {
49263
- const content = readFileSync22(superPath, "utf-8");
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 = join27(paths.featuresDir, featureName, "AGENT.md");
49275
- if (existsSync27(agentPath)) {
49276
- const content = readFileSync22(agentPath, "utf-8");
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 = join27(paths.projectDir, "AGENT.md");
49310
- if (existsSync27(superPath)) {
49311
- agentDef = parseAgentMd(readFileSync22(superPath, "utf-8"));
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 = join27(paths.featuresDir, featureName, "AGENT.md");
49316
- if (existsSync27(agentPath)) {
49317
- agentDef = parseAgentMd(readFileSync22(agentPath, "utf-8"));
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 = join27(paths.root, "proposals.json");
50444
+ const proposalsPath = join28(paths.root, "proposals.json");
49419
50445
  let proposals = [];
49420
- if (existsSync27(proposalsPath)) {
50446
+ if (existsSync28(proposalsPath)) {
49421
50447
  try {
49422
- proposals = JSON.parse(readFileSync22(proposalsPath, "utf-8"));
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 join28, resolve as resolve2 } from "path";
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: join28(normalizedPath, ".codeimpact"),
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 join37, resolve as resolve3 } from "path";
54507
+ import { join as join38, resolve as resolve3 } from "path";
53482
54508
  import { fileURLToPath as fileURLToPath2 } from "url";
53483
- import { existsSync as existsSync36, readFileSync as readFileSync29, writeFileSync as writeFileSync21, mkdirSync as mkdirSync19 } from "fs";
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 = join37(projectInfo.dataDir, "codeimpact.db");
53599
- if (!existsSync36(dbPath)) {
53600
- const oldDbPath = join37(projectInfo.dataDir, "codeimpact.db");
53601
- if (existsSync36(oldDbPath)) {
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 || join37(targetPath, "docs", "decisions")}`,
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 = join37(projectInfo.dataDir, "codeimpact.db");
53634
- if (existsSync36(centralizedPath)) {
54659
+ const centralizedPath = join38(projectInfo.dataDir, "codeimpact.db");
54660
+ if (existsSync37(centralizedPath)) {
53635
54661
  return centralizedPath;
53636
54662
  }
53637
- const projectLocalPath = join37(projectInfo.path, ".codeimpact", "codeimpact.db");
53638
- if (existsSync36(projectLocalPath)) {
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 (existsSync36(configPath)) {
54201
- const content = readFileSync29(configPath, "utf-8");
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 (existsSync36(configPath)) {
54238
- const content = readFileSync29(configPath, "utf-8");
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 = join37(projectPath, "CLAUDE.md");
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 (existsSync36(claudeMdPath)) {
54341
- existingContent = readFileSync29(claudeMdPath, "utf-8");
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 = join37(projectPath, ".cursor");
54373
- const configPath = join37(cursorDir, "mcp.json");
55398
+ const cursorDir = join38(projectPath, ".cursor");
55399
+ const configPath = join38(cursorDir, "mcp.json");
54374
55400
  try {
54375
- if (!existsSync36(cursorDir)) {
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 (existsSync36(configPath)) {
54383
- const content = readFileSync29(configPath, "utf-8");
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 = join37(projectPath, ".cursorrules");
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 (existsSync36(cursorRulesPath)) {
54479
- existingContent = readFileSync29(cursorRulesPath, "utf-8");
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 = join37(projectPath, "opencode.json");
55534
+ const configPath = join38(projectPath, "opencode.json");
54509
55535
  let config2 = {};
54510
55536
  try {
54511
- if (existsSync36(configPath)) {
54512
- const content = readFileSync29(configPath, "utf-8");
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 (existsSync36(configPath)) {
54549
- config2 = JSON.parse(readFileSync29(configPath, "utf-8"));
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 && !existsSync36(dir)) mkdirSync19(dir, { recursive: true });
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 = join37(homedir2(), "AppData", "Roaming", "Claude", "claude_desktop_config.json");
55603
+ claudeConfigPath = join38(homedir2(), "AppData", "Roaming", "Claude", "claude_desktop_config.json");
54578
55604
  } else if (platform === "darwin") {
54579
- claudeConfigPath = join37(homedir2(), "Library", "Application Support", "Claude", "claude_desktop_config.json");
55605
+ claudeConfigPath = join38(homedir2(), "Library", "Application Support", "Claude", "claude_desktop_config.json");
54580
55606
  } else {
54581
- claudeConfigPath = join37(homedir2(), ".config", "claude", "claude_desktop_config.json");
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 = join37(targetPath, ".mcp.json");
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 = join37(homedir2(), "AppData", "Roaming", "Cursor", "User", "globalStorage", "cursor.mcp", "mcp.json");
55619
+ cursorConfigPath = join38(homedir2(), "AppData", "Roaming", "Cursor", "User", "globalStorage", "cursor.mcp", "mcp.json");
54594
55620
  } else if (platform === "darwin") {
54595
- cursorConfigPath = join37(homedir2(), "Library", "Application Support", "Cursor", "User", "globalStorage", "cursor.mcp", "mcp.json");
55621
+ cursorConfigPath = join38(homedir2(), "Library", "Application Support", "Cursor", "User", "globalStorage", "cursor.mcp", "mcp.json");
54596
55622
  } else {
54597
- cursorConfigPath = join37(homedir2(), ".config", "Cursor", "User", "globalStorage", "cursor.mcp", "mcp.json");
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 = join37(targetPath, ".mcp.json");
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 = join37(homedir2(), "AppData", "Roaming", "Cursor", "User", "globalStorage", "cursor.mcp", "mcp.json");
55640
+ cursorConfigPath = join38(homedir2(), "AppData", "Roaming", "Cursor", "User", "globalStorage", "cursor.mcp", "mcp.json");
54615
55641
  } else if (platform === "darwin") {
54616
- cursorConfigPath = join37(homedir2(), "Library", "Application Support", "Cursor", "User", "globalStorage", "cursor.mcp", "mcp.json");
55642
+ cursorConfigPath = join38(homedir2(), "Library", "Application Support", "Cursor", "User", "globalStorage", "cursor.mcp", "mcp.json");
54617
55643
  } else {
54618
- cursorConfigPath = join37(homedir2(), ".config", "Cursor", "User", "globalStorage", "cursor.mcp", "mcp.json");
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);