@arcbridge/core 0.1.3 → 0.1.4

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.d.ts CHANGED
@@ -1033,7 +1033,9 @@ interface GitRef {
1033
1033
  */
1034
1034
  declare function resolveRef(projectRoot: string, since: string, db?: Database): GitRef;
1035
1035
  /**
1036
- * Get list of changed files between a ref and HEAD.
1036
+ * Get list of changed files between a ref and HEAD, including uncommitted changes.
1037
+ * Merges committed diffs with staged+unstaged working tree changes so that
1038
+ * practice reviews and drift checks see all work, not just committed code.
1037
1039
  */
1038
1040
  declare function getChangedFiles(projectRoot: string, ref: string): ChangedFile[];
1039
1041
  /**
package/dist/index.js CHANGED
@@ -1140,10 +1140,16 @@ ${input.template === "dotnet-webapi" ? `| Platform | Description | Notes |
1140
1140
  // src/templates/arc42/09-decisions.ts
1141
1141
  function firstAdrTemplate(input) {
1142
1142
  const now = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
1143
- if (input.template === "dotnet-webapi") {
1144
- return dotnetAdr(input, now);
1143
+ switch (input.template) {
1144
+ case "dotnet-webapi":
1145
+ return dotnetAdr(input, now);
1146
+ case "react-vite":
1147
+ return reactViteAdr(input, now);
1148
+ case "api-service":
1149
+ return apiServiceAdr(input, now);
1150
+ default:
1151
+ return nextjsAdr(input, now);
1145
1152
  }
1146
- return nextjsAdr(input, now);
1147
1153
  }
1148
1154
  function nextjsAdr(input, date) {
1149
1155
  const { appPrefix } = detectProjectLayout(input.projectRoot, input.template);
@@ -1178,6 +1184,73 @@ Use Next.js with the App Router (introduced in Next.js 13+) as the application f
1178
1184
  `
1179
1185
  };
1180
1186
  }
1187
+ function reactViteAdr(input, date) {
1188
+ const { srcPrefix } = detectProjectLayout(input.projectRoot, input.template);
1189
+ return {
1190
+ filename: "001-react-vite.md",
1191
+ frontmatter: {
1192
+ id: "001-react-vite",
1193
+ title: "Use React with Vite",
1194
+ status: "accepted",
1195
+ date,
1196
+ affected_blocks: ["app-shell"],
1197
+ affected_files: [srcPrefix || "./"],
1198
+ quality_scenarios: []
1199
+ },
1200
+ body: `# ADR-001: Use React with Vite
1201
+
1202
+ ## Context
1203
+
1204
+ ${input.name} needs a fast, modern frontend framework for building a single-page application (SPA) with TypeScript.
1205
+
1206
+ ## Decision
1207
+
1208
+ Use React with Vite as the build tool and development server.
1209
+
1210
+ ## Consequences
1211
+
1212
+ - **Positive:** Extremely fast dev server with hot module replacement (HMR)
1213
+ - **Positive:** Optimized production builds with tree shaking and code splitting
1214
+ - **Positive:** Simple configuration, no SSR complexity
1215
+ - **Positive:** Large React ecosystem of libraries and components
1216
+ - **Negative:** Client-side only \u2014 no built-in server-side rendering (add later if needed)
1217
+ - **Negative:** Requires separate backend service for API endpoints
1218
+ `
1219
+ };
1220
+ }
1221
+ function apiServiceAdr(input, date) {
1222
+ const { srcPrefix } = detectProjectLayout(input.projectRoot, input.template);
1223
+ return {
1224
+ filename: "001-api-service.md",
1225
+ frontmatter: {
1226
+ id: "001-api-service",
1227
+ title: "Use Node.js API Service",
1228
+ status: "accepted",
1229
+ date,
1230
+ affected_blocks: ["app-shell"],
1231
+ affected_files: [srcPrefix || "./"],
1232
+ quality_scenarios: []
1233
+ },
1234
+ body: `# ADR-001: Use Node.js API Service
1235
+
1236
+ ## Context
1237
+
1238
+ ${input.name} needs a backend API service with TypeScript support, good performance, and a simple architecture.
1239
+
1240
+ ## Decision
1241
+
1242
+ Use a Node.js HTTP framework (Express, Fastify, or Hono) as the API service foundation.
1243
+
1244
+ ## Consequences
1245
+
1246
+ - **Positive:** TypeScript-first with full type safety
1247
+ - **Positive:** Rich middleware ecosystem for auth, validation, logging
1248
+ - **Positive:** Easy to deploy to containers, serverless, or traditional hosts
1249
+ - **Negative:** Single-threaded \u2014 CPU-intensive operations need worker threads or external services
1250
+ - **Negative:** No built-in ORM \u2014 need to choose data access strategy
1251
+ `
1252
+ };
1253
+ }
1181
1254
  function dotnetAdr(input, date) {
1182
1255
  return {
1183
1256
  filename: "001-aspnet-core-webapi.md",
@@ -1961,6 +2034,116 @@ function phaseTasksTemplate(_input, phaseId) {
1961
2034
  ]
1962
2035
  }
1963
2036
  ]
2037
+ },
2038
+ "phase-2-features": {
2039
+ schema_version: 1,
2040
+ phase_id: "phase-2-features",
2041
+ tasks: [
2042
+ {
2043
+ id: "task-2.1-core-pages",
2044
+ title: "Implement core application pages",
2045
+ status: "todo",
2046
+ quality_scenarios: ["PERF-01"],
2047
+ acceptance_criteria: [
2048
+ "Core pages created with proper data fetching",
2049
+ "Server/client components properly separated",
2050
+ "Loading and error states handled"
2051
+ ]
2052
+ },
2053
+ {
2054
+ id: "task-2.2-api-routes",
2055
+ title: "Implement API routes",
2056
+ status: "todo",
2057
+ quality_scenarios: ["SEC-01", "PERF-02"],
2058
+ acceptance_criteria: [
2059
+ "API routes created for core operations",
2060
+ "Input validation on all endpoints",
2061
+ "Error responses standardized"
2062
+ ]
2063
+ },
2064
+ {
2065
+ id: "task-2.3-data-layer",
2066
+ title: "Set up data access layer",
2067
+ status: "todo",
2068
+ quality_scenarios: ["MAINT-01"],
2069
+ acceptance_criteria: [
2070
+ "Data fetching patterns established",
2071
+ "Caching strategy implemented",
2072
+ "Type-safe data access"
2073
+ ]
2074
+ },
2075
+ {
2076
+ id: "task-2.4-integration-tests",
2077
+ title: "Write integration tests for core flows",
2078
+ status: "todo",
2079
+ quality_scenarios: ["MAINT-02"],
2080
+ acceptance_criteria: [
2081
+ "Happy path tested for each core feature",
2082
+ "API route tests written",
2083
+ "Test coverage meets threshold"
2084
+ ]
2085
+ },
2086
+ {
2087
+ id: "task-2.5-document-decisions",
2088
+ title: "Document Phase 2 architectural decisions",
2089
+ status: "todo",
2090
+ quality_scenarios: [],
2091
+ acceptance_criteria: [
2092
+ "ADRs for data fetching and API design choices",
2093
+ "Building blocks updated with new code paths"
2094
+ ]
2095
+ }
2096
+ ]
2097
+ },
2098
+ "phase-3-polish": {
2099
+ schema_version: 1,
2100
+ phase_id: "phase-3-polish",
2101
+ tasks: [
2102
+ {
2103
+ id: "task-3.1-error-handling",
2104
+ title: "Implement comprehensive error handling",
2105
+ status: "todo",
2106
+ quality_scenarios: ["SEC-01"],
2107
+ acceptance_criteria: [
2108
+ "Error boundaries at route level",
2109
+ "Custom error.tsx and not-found.tsx pages",
2110
+ "API error responses standardized"
2111
+ ]
2112
+ },
2113
+ {
2114
+ id: "task-3.2-accessibility",
2115
+ title: "Accessibility audit and fixes",
2116
+ status: "todo",
2117
+ quality_scenarios: ["A11Y-01", "A11Y-02"],
2118
+ acceptance_criteria: [
2119
+ "Keyboard navigation works for all interactive elements",
2120
+ "Screen reader compatible (ARIA labels, roles)",
2121
+ "WCAG 2.1 AA compliance verified"
2122
+ ]
2123
+ },
2124
+ {
2125
+ id: "task-3.3-performance",
2126
+ title: "Performance optimization",
2127
+ status: "todo",
2128
+ quality_scenarios: ["PERF-01"],
2129
+ acceptance_criteria: [
2130
+ "Bundle size optimized (dynamic imports, tree shaking)",
2131
+ "Core Web Vitals meet thresholds (LCP < 2.5s)",
2132
+ "Server-side rendering verified for SEO pages"
2133
+ ]
2134
+ },
2135
+ {
2136
+ id: "task-3.4-deployment",
2137
+ title: "Configure production deployment",
2138
+ status: "todo",
2139
+ quality_scenarios: [],
2140
+ acceptance_criteria: [
2141
+ "Production build configuration verified",
2142
+ "Environment variables documented",
2143
+ "Deployment to Vercel/hosting configured"
2144
+ ]
2145
+ }
2146
+ ]
1964
2147
  }
1965
2148
  };
1966
2149
  return tasksByPhase[phaseId] ?? null;
@@ -2128,6 +2311,105 @@ function phaseTasksTemplate2(_input, phaseId) {
2128
2311
  ]
2129
2312
  }
2130
2313
  ]
2314
+ },
2315
+ "phase-2-features": {
2316
+ schema_version: 1,
2317
+ phase_id: "phase-2-features",
2318
+ tasks: [
2319
+ {
2320
+ id: "task-2.1-core-feature",
2321
+ title: "Implement primary feature",
2322
+ status: "todo",
2323
+ quality_scenarios: ["PERF-01"],
2324
+ acceptance_criteria: [
2325
+ "Core user flow works end-to-end",
2326
+ "Unit and component tests written",
2327
+ "Meets performance budgets"
2328
+ ]
2329
+ },
2330
+ {
2331
+ id: "task-2.2-state-management",
2332
+ title: "Set up state management for complex data",
2333
+ status: "todo",
2334
+ quality_scenarios: ["MAINT-01"],
2335
+ acceptance_criteria: [
2336
+ "State management pattern established",
2337
+ "Data fetching/caching strategy implemented",
2338
+ "Loading and error states handled"
2339
+ ]
2340
+ },
2341
+ {
2342
+ id: "task-2.3-integration-tests",
2343
+ title: "Write integration tests for core flows",
2344
+ status: "todo",
2345
+ quality_scenarios: ["MAINT-02"],
2346
+ acceptance_criteria: [
2347
+ "Happy path tested for each core feature",
2348
+ "Error scenarios tested",
2349
+ "Test coverage meets threshold"
2350
+ ]
2351
+ },
2352
+ {
2353
+ id: "task-2.4-document-decisions",
2354
+ title: "Document Phase 2 architectural decisions",
2355
+ status: "todo",
2356
+ quality_scenarios: [],
2357
+ acceptance_criteria: [
2358
+ "ADRs for state management and data fetching choices",
2359
+ "Building blocks updated with new code paths"
2360
+ ]
2361
+ }
2362
+ ]
2363
+ },
2364
+ "phase-3-polish": {
2365
+ schema_version: 1,
2366
+ phase_id: "phase-3-polish",
2367
+ tasks: [
2368
+ {
2369
+ id: "task-3.1-error-handling",
2370
+ title: "Implement comprehensive error handling",
2371
+ status: "todo",
2372
+ quality_scenarios: ["SEC-01"],
2373
+ acceptance_criteria: [
2374
+ "Error boundaries at route level",
2375
+ "User-friendly error messages",
2376
+ "Error logging configured"
2377
+ ]
2378
+ },
2379
+ {
2380
+ id: "task-3.2-accessibility",
2381
+ title: "Accessibility audit and fixes",
2382
+ status: "todo",
2383
+ quality_scenarios: ["A11Y-01", "A11Y-02"],
2384
+ acceptance_criteria: [
2385
+ "Keyboard navigation works for all interactive elements",
2386
+ "Screen reader compatible (ARIA labels, roles)",
2387
+ "WCAG 2.1 AA compliance verified"
2388
+ ]
2389
+ },
2390
+ {
2391
+ id: "task-3.3-performance",
2392
+ title: "Performance optimization",
2393
+ status: "todo",
2394
+ quality_scenarios: ["PERF-01"],
2395
+ acceptance_criteria: [
2396
+ "Bundle size optimized (code splitting, tree shaking)",
2397
+ "Lighthouse performance score >= 90",
2398
+ "No unnecessary re-renders in hot paths"
2399
+ ]
2400
+ },
2401
+ {
2402
+ id: "task-3.4-deployment",
2403
+ title: "Configure production build and deployment",
2404
+ status: "todo",
2405
+ quality_scenarios: [],
2406
+ acceptance_criteria: [
2407
+ "Production build configuration verified",
2408
+ "Deployment pipeline configured",
2409
+ "Environment variables documented"
2410
+ ]
2411
+ }
2412
+ ]
2131
2413
  }
2132
2414
  };
2133
2415
  return tasksByPhase[phaseId] ?? null;
@@ -2646,6 +2928,18 @@ You are responsible for maintaining these sections in \`.arcbridge/arc42/\`. Upd
2646
2928
  - All ADRs and their status
2647
2929
  - Building block \u2192 code mapping
2648
2930
 
2931
+ ## Project Planning
2932
+
2933
+ At the start of a project (after init), plan tasks across ALL phases:
2934
+ - **Phase 0-1 tasks are ready to use** \u2014 they cover setup and foundation for this template
2935
+ - **Phase 2+ tasks are examples** \u2014 replace them with real tasks derived from the project's requirements and specifications
2936
+ - Review the phase plan with \`arcbridge_get_phase_plan\` and replace example tasks with project-specific ones
2937
+ - Create tasks using \`arcbridge_create_task\` with the phase ID shown in the plan
2938
+ - Keep each phase reasonably scoped \u2014 3-6 tasks per phase is ideal
2939
+ - Map tasks to building blocks so drift detection tracks coverage
2940
+ - Link tasks to quality scenarios so gate checks are meaningful
2941
+ - Plan the full roadmap before diving into implementation \u2014 this prevents phases from becoming too large or unfocused
2942
+
2649
2943
  ## Working Style
2650
2944
 
2651
2945
  Think at the system level. Before making changes, consider:
@@ -2883,13 +3177,25 @@ function phaseManagerTemplate() {
2883
3177
  - Tasks must be "done" before a phase can complete
2884
3178
  - Quality scenarios linked to phase tasks must be verified
2885
3179
 
3180
+ ## Task Planning
3181
+
3182
+ Before starting any phase, ensure proper task planning:
3183
+ - **Phase 0-1 tasks are concrete** \u2014 they cover project setup and foundation. Follow them as-is.
3184
+ - **Phase 2+ tasks are examples only** \u2014 they show the *shape* of later phases but must be replaced with real tasks derived from the project's actual requirements and specs.
3185
+ - **At project start, plan ALL phases** \u2014 review \`arcbridge_get_phase_plan\`, delete example tasks in Phase 2+, and create real tasks based on the product spec and building blocks.
3186
+ - **Keep phases small and focused** \u2014 if a phase has more than 6-8 tasks, split it into sub-phases
3187
+ - **Tasks should be concrete and verifiable** \u2014 each task needs clear acceptance criteria
3188
+ - **Link tasks to building blocks** \u2014 this enables drift detection and progress tracking
3189
+ - **Use \`arcbridge_create_task\` with the phase ID** shown in \`arcbridge_get_phase_plan\` output (e.g., \`phase-2-features\`)
3190
+
2886
3191
  ## Phase Transition Process
2887
3192
 
2888
3193
  1. Verify all tasks in current phase are "done"
2889
- 2. Run drift detection (check_drift)
2890
- 3. Propose arc42 updates if drift is detected
2891
- 4. Check quality gate requirements
2892
- 5. Mark phase complete or report blockers`
3194
+ 2. Review task coverage for the NEXT phase \u2014 create tasks if empty
3195
+ 3. Run drift detection (\`arcbridge_check_drift\`)
3196
+ 4. Propose arc42 updates if drift is detected
3197
+ 5. Check quality gate requirements
3198
+ 6. Mark phase complete or report blockers`
2893
3199
  };
2894
3200
  }
2895
3201
 
@@ -3353,6 +3659,7 @@ function refreshFromDocs(db, targetDir) {
3353
3659
  const scenarioStatusMap = new Map(
3354
3660
  existingScenarios.map((s) => [s.id, s.status])
3355
3661
  );
3662
+ db.exec("PRAGMA foreign_keys = OFF");
3356
3663
  const refresh = () => transaction(db, () => {
3357
3664
  db.prepare("DELETE FROM tasks").run();
3358
3665
  db.prepare("DELETE FROM phases").run();
@@ -3365,6 +3672,16 @@ function refreshFromDocs(db, targetDir) {
3365
3672
  warnings.push(...populateQualityScenarios(db, targetDir));
3366
3673
  warnings.push(...populatePhases(db, targetDir));
3367
3674
  warnings.push(...populateAdrs(db, targetDir));
3675
+ const orphaned = db.prepare(`
3676
+ UPDATE tasks SET building_block = NULL
3677
+ WHERE building_block IS NOT NULL
3678
+ AND building_block NOT IN (SELECT id FROM building_blocks)
3679
+ `).run();
3680
+ if (orphaned.changes > 0) {
3681
+ warnings.push(
3682
+ `${orphaned.changes} task(s) referenced removed building blocks (references cleared)`
3683
+ );
3684
+ }
3368
3685
  const restoreTask = db.prepare(
3369
3686
  "UPDATE tasks SET status = ?, completed_at = ? WHERE id = ?"
3370
3687
  );
@@ -3390,7 +3707,11 @@ function refreshFromDocs(db, targetDir) {
3390
3707
  }
3391
3708
  }
3392
3709
  });
3393
- refresh();
3710
+ try {
3711
+ refresh();
3712
+ } finally {
3713
+ db.exec("PRAGMA foreign_keys = ON");
3714
+ }
3394
3715
  return warnings;
3395
3716
  }
3396
3717
  function generateDatabase(targetDir, input) {
@@ -3437,7 +3758,7 @@ import YAML from "yaml";
3437
3758
 
3438
3759
  // src/indexer/program.ts
3439
3760
  import ts from "typescript";
3440
- import { join as join7 } from "path";
3761
+ import { join as join7, dirname } from "path";
3441
3762
  function createTsProgram(options) {
3442
3763
  const projectRoot = options.projectRoot;
3443
3764
  const configPath = options.tsconfigPath ?? ts.findConfigFile(projectRoot, ts.sys.fileExists, "tsconfig.json");
@@ -3446,7 +3767,7 @@ function createTsProgram(options) {
3446
3767
  `No tsconfig.json found in ${projectRoot}. TypeScript indexing requires a tsconfig.json.`
3447
3768
  );
3448
3769
  }
3449
- const configFile = ts.readConfigFile(configPath, ts.sys.readFile);
3770
+ let configFile = ts.readConfigFile(configPath, ts.sys.readFile);
3450
3771
  if (configFile.error) {
3451
3772
  const message = ts.flattenDiagnosticMessageText(
3452
3773
  configFile.error.messageText,
@@ -3454,12 +3775,45 @@ function createTsProgram(options) {
3454
3775
  );
3455
3776
  throw new Error(`Failed to read tsconfig.json: ${message}`);
3456
3777
  }
3778
+ let resolvedConfigPath = configPath;
3779
+ const config = configFile.config;
3780
+ const hasOwnFiles = config.include && config.include.length > 0 || config.files && config.files.length > 0;
3781
+ if (config.references && !hasOwnFiles) {
3782
+ for (const ref of config.references) {
3783
+ const refRelPath = typeof ref === "string" ? ref : ref.path;
3784
+ if (!refRelPath) continue;
3785
+ const refFullPath = join7(dirname(configPath), refRelPath);
3786
+ const refConfigPath = refFullPath.endsWith(".json") ? refFullPath : join7(refFullPath, "tsconfig.json");
3787
+ if (ts.sys.fileExists(refConfigPath)) {
3788
+ const refConfig = ts.readConfigFile(refConfigPath, ts.sys.readFile);
3789
+ const rc = refConfig.config;
3790
+ if (!refConfig.error && (rc.include?.length > 0 || rc.files?.length > 0)) {
3791
+ configFile = refConfig;
3792
+ resolvedConfigPath = refConfigPath;
3793
+ break;
3794
+ }
3795
+ }
3796
+ }
3797
+ if (resolvedConfigPath === configPath) {
3798
+ for (const candidate of ["tsconfig.app.json", "tsconfig.src.json"]) {
3799
+ const refPath = ts.findConfigFile(projectRoot, ts.sys.fileExists, candidate);
3800
+ if (refPath) {
3801
+ const refConfig = ts.readConfigFile(refPath, ts.sys.readFile);
3802
+ if (!refConfig.error) {
3803
+ configFile = refConfig;
3804
+ resolvedConfigPath = refPath;
3805
+ break;
3806
+ }
3807
+ }
3808
+ }
3809
+ }
3810
+ }
3457
3811
  const parsed = ts.parseJsonConfigFileContent(
3458
3812
  configFile.config,
3459
3813
  ts.sys,
3460
3814
  join7(projectRoot),
3461
3815
  { noEmit: true },
3462
- configPath
3816
+ resolvedConfigPath
3463
3817
  );
3464
3818
  const program = ts.createProgram({
3465
3819
  rootNames: parsed.fileNames,
@@ -4463,7 +4817,7 @@ function writeDependencies(db, dependencies) {
4463
4817
 
4464
4818
  // src/indexer/dotnet-indexer.ts
4465
4819
  import { execFileSync } from "child_process";
4466
- import { resolve, join as join9, dirname, relative as relative4, basename } from "path";
4820
+ import { resolve, join as join9, dirname as dirname2, relative as relative4, basename } from "path";
4467
4821
  import { readdirSync as readdirSync3, readFileSync as readFileSync3, existsSync as existsSync4, accessSync, constants } from "fs";
4468
4822
  import { fileURLToPath } from "url";
4469
4823
  function findDotnetProject(projectRoot) {
@@ -4480,7 +4834,7 @@ function findDotnetProject(projectRoot) {
4480
4834
  }
4481
4835
  function parseSolutionProjects(slnPath) {
4482
4836
  const content = readFileSync3(slnPath, "utf-8");
4483
- const slnDir = dirname(slnPath);
4837
+ const slnDir = dirname2(slnPath);
4484
4838
  const projects = [];
4485
4839
  const projectPattern = /Project\("\{[^}]+\}"\)\s*=\s*"([^"]+)",\s*"([^"]+\.csproj)"/g;
4486
4840
  let match;
@@ -4489,7 +4843,7 @@ function parseSolutionProjects(slnPath) {
4489
4843
  const relativeCsprojPath = match[2].replace(/\\/g, "/");
4490
4844
  const fullCsprojPath = resolve(join9(slnDir, relativeCsprojPath));
4491
4845
  if (!existsSync4(fullCsprojPath)) continue;
4492
- const projectDir = relative4(slnDir, dirname(fullCsprojPath)).replace(/\\/g, "/") || ".";
4846
+ const projectDir = relative4(slnDir, dirname2(fullCsprojPath)).replace(/\\/g, "/") || ".";
4493
4847
  const isTestProject = /[.\x2d]tests?$/i.test(name) || /[.\x2d](unit|integration|functional|e2e)tests?$/i.test(name);
4494
4848
  projects.push({
4495
4849
  name,
@@ -4534,7 +4888,7 @@ function hasGlobalTool() {
4534
4888
  return false;
4535
4889
  }
4536
4890
  function resolveIndexerProject() {
4537
- const currentDir2 = dirname(fileURLToPath(import.meta.url));
4891
+ const currentDir2 = dirname2(fileURLToPath(import.meta.url));
4538
4892
  const candidates = [
4539
4893
  resolve(currentDir2, "../../../../dotnet-indexer/ArcBridge.DotnetIndexer.csproj"),
4540
4894
  resolve(currentDir2, "../../../dotnet-indexer/ArcBridge.DotnetIndexer.csproj"),
@@ -4688,10 +5042,10 @@ import { globbySync } from "globby";
4688
5042
 
4689
5043
  // src/indexer/csharp/parser.ts
4690
5044
  import { accessSync as accessSync2, constants as constants2 } from "fs";
4691
- import { dirname as dirname2, resolve as resolve2 } from "path";
5045
+ import { dirname as dirname3, resolve as resolve2 } from "path";
4692
5046
  import { fileURLToPath as fileURLToPath2 } from "url";
4693
5047
  import "web-tree-sitter";
4694
- var currentDir = dirname2(fileURLToPath2(import.meta.url));
5048
+ var currentDir = dirname3(fileURLToPath2(import.meta.url));
4695
5049
  var cachedParser = null;
4696
5050
  var initPromise = null;
4697
5051
  function resolveGrammarPath() {
@@ -6047,7 +6401,17 @@ function detectNewDependencies(db, entries) {
6047
6401
  const adrs = db.prepare("SELECT id, title, context, decision FROM adrs WHERE status != 'superseded'").all();
6048
6402
  const adrText = adrs.map((a) => `${a.title} ${a.context ?? ""} ${a.decision ?? ""}`.toLowerCase()).join(" ");
6049
6403
  const trivialPackages = /* @__PURE__ */ new Set([
6050
- // npm
6404
+ // npm — frameworks (core deps that don't need ADRs)
6405
+ "react",
6406
+ "react-dom",
6407
+ "next",
6408
+ "vite",
6409
+ "@vitejs/plugin-react",
6410
+ "express",
6411
+ "fastify",
6412
+ "hono",
6413
+ "koa",
6414
+ // npm — dev tooling
6051
6415
  "typescript",
6052
6416
  "eslint",
6053
6417
  "prettier",
@@ -6055,15 +6419,28 @@ function detectNewDependencies(db, entries) {
6055
6419
  "jest",
6056
6420
  "@types/node",
6057
6421
  "@types/react",
6422
+ "@types/react-dom",
6058
6423
  "tsup",
6059
6424
  "tsx",
6060
- // nuget
6425
+ "ts-node",
6426
+ "nodemon",
6427
+ "@eslint/js",
6428
+ "typescript-eslint",
6429
+ // npm — build/bundler
6430
+ "esbuild",
6431
+ "rollup",
6432
+ "webpack",
6433
+ "postcss",
6434
+ "tailwindcss",
6435
+ "autoprefixer",
6436
+ // nuget — test
6061
6437
  "microsoft.net.test.sdk",
6062
6438
  "xunit",
6063
6439
  "xunit.runner.visualstudio",
6064
6440
  "nunit",
6065
6441
  "nunit3testadapter",
6066
6442
  "coverlet.collector",
6443
+ // nuget — framework
6067
6444
  "microsoft.aspnetcore.openapi",
6068
6445
  "swashbuckle.aspnetcore"
6069
6446
  ]);
@@ -6318,7 +6695,7 @@ function safeParseJson2(value, fallback) {
6318
6695
 
6319
6696
  // src/generators/sync-generator.ts
6320
6697
  import { mkdirSync as mkdirSync6, writeFileSync as writeFileSync6 } from "fs";
6321
- import { join as join15, dirname as dirname3 } from "path";
6698
+ import { join as join15, dirname as dirname4 } from "path";
6322
6699
 
6323
6700
  // src/templates/sync/claude-skill.ts
6324
6701
  function claudeSkillTemplate(config) {
@@ -6497,20 +6874,20 @@ function generateSyncFiles(targetDir, config) {
6497
6874
  const generated = [];
6498
6875
  const action = githubActionTemplate(config);
6499
6876
  const actionPath = join15(targetDir, action.relativePath);
6500
- mkdirSync6(dirname3(actionPath), { recursive: true });
6877
+ mkdirSync6(dirname4(actionPath), { recursive: true });
6501
6878
  writeFileSync6(actionPath, action.content, "utf-8");
6502
6879
  generated.push(action.relativePath);
6503
6880
  if (config.platforms.includes("claude")) {
6504
6881
  const skill = claudeSkillTemplate(config);
6505
6882
  const skillPath = join15(targetDir, skill.relativePath);
6506
- mkdirSync6(dirname3(skillPath), { recursive: true });
6883
+ mkdirSync6(dirname4(skillPath), { recursive: true });
6507
6884
  writeFileSync6(skillPath, skill.content, "utf-8");
6508
6885
  generated.push(skill.relativePath);
6509
6886
  }
6510
6887
  if (config.platforms.includes("copilot")) {
6511
6888
  const hook = copilotHookTemplate(config);
6512
6889
  const hookPath = join15(targetDir, hook.relativePath);
6513
- mkdirSync6(dirname3(hookPath), { recursive: true });
6890
+ mkdirSync6(dirname4(hookPath), { recursive: true });
6514
6891
  writeFileSync6(hookPath, hook.content, "utf-8");
6515
6892
  generated.push(hook.relativePath);
6516
6893
  }
@@ -6866,6 +7243,7 @@ function resolveRef(projectRoot, since, db) {
6866
7243
  }
6867
7244
  }
6868
7245
  function getChangedFiles(projectRoot, ref) {
7246
+ const byPath = /* @__PURE__ */ new Map();
6869
7247
  try {
6870
7248
  execFileSync3("git", ["rev-parse", "--verify", ref], {
6871
7249
  cwd: projectRoot,
@@ -6878,16 +7256,24 @@ function getChangedFiles(projectRoot, ref) {
6878
7256
  ["diff", "--name-status", "--no-renames", ref, "HEAD"],
6879
7257
  { cwd: projectRoot, encoding: "utf-8", timeout: 1e4 }
6880
7258
  ).trim();
6881
- if (!output) return [];
6882
- return output.split("\n").map((line) => {
6883
- const [statusCode, ...pathParts] = line.split(" ");
6884
- const path = pathParts.join(" ");
6885
- const status = parseStatusCode(statusCode ?? "M");
6886
- return { status, path };
6887
- });
7259
+ if (output) {
7260
+ for (const line of output.split("\n")) {
7261
+ const [statusCode, ...pathParts] = line.split(" ");
7262
+ const path = pathParts.join(" ");
7263
+ byPath.set(path, { status: parseStatusCode(statusCode ?? "M"), path });
7264
+ }
7265
+ }
6888
7266
  } catch {
6889
- return getUncommittedChanges(projectRoot);
6890
7267
  }
7268
+ for (const change of getUncommittedChanges(projectRoot)) {
7269
+ const existing = byPath.get(change.path);
7270
+ if (!existing) {
7271
+ byPath.set(change.path, change);
7272
+ } else if (change.status === "deleted") {
7273
+ byPath.set(change.path, change);
7274
+ }
7275
+ }
7276
+ return Array.from(byPath.values());
6891
7277
  }
6892
7278
  function getUncommittedChanges(projectRoot) {
6893
7279
  try {
@@ -6895,11 +7281,12 @@ function getUncommittedChanges(projectRoot) {
6895
7281
  "git",
6896
7282
  ["status", "--porcelain", "-uno"],
6897
7283
  { cwd: projectRoot, encoding: "utf-8", timeout: 5e3 }
6898
- ).trim();
6899
- if (!output) return [];
6900
- return output.split("\n").map((line) => {
7284
+ );
7285
+ const lines = output.split("\n").filter((l) => l.length >= 3);
7286
+ if (lines.length === 0) return [];
7287
+ return lines.map((line) => {
6901
7288
  const statusCode = line.slice(0, 2).trim();
6902
- const path = line.slice(3);
7289
+ const path = line.slice(3).replace(/\r$/, "");
6903
7290
  const status = statusCode === "D" ? "deleted" : statusCode === "A" ? "added" : "modified";
6904
7291
  return { status, path };
6905
7292
  });