@arcbridge/core 0.1.2 → 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 +35 -22
- package/dist/index.js +522 -65
- package/dist/index.js.map +1 -1
- package/package.json +5 -4
package/dist/index.js
CHANGED
|
@@ -210,15 +210,93 @@ var AgentRoleSchema = z6.object({
|
|
|
210
210
|
});
|
|
211
211
|
|
|
212
212
|
// src/db/connection.ts
|
|
213
|
-
import
|
|
213
|
+
import { DatabaseSync } from "node:sqlite";
|
|
214
|
+
var suppressed = false;
|
|
215
|
+
function suppressSqliteWarning() {
|
|
216
|
+
if (suppressed) return;
|
|
217
|
+
suppressed = true;
|
|
218
|
+
const origEmit = process.emit;
|
|
219
|
+
process.emit = function(event, ...args) {
|
|
220
|
+
if (event === "warning" && args[0]?.name === "ExperimentalWarning" && typeof args[0]?.message === "string" && args[0].message.includes("SQLite")) {
|
|
221
|
+
return true;
|
|
222
|
+
}
|
|
223
|
+
return origEmit.apply(process, [event, ...args]);
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
function sanitizeParams(params) {
|
|
227
|
+
return params.map((p) => p === void 0 ? null : p);
|
|
228
|
+
}
|
|
214
229
|
function openDatabase(dbPath) {
|
|
215
|
-
const db = new
|
|
216
|
-
db.
|
|
217
|
-
db.
|
|
230
|
+
const db = new DatabaseSync(dbPath);
|
|
231
|
+
db.exec("PRAGMA journal_mode = WAL");
|
|
232
|
+
db.exec("PRAGMA foreign_keys = ON");
|
|
233
|
+
patchPrepare(db);
|
|
218
234
|
return db;
|
|
219
235
|
}
|
|
220
236
|
function openMemoryDatabase() {
|
|
221
|
-
|
|
237
|
+
const db = new DatabaseSync(":memory:");
|
|
238
|
+
db.exec("PRAGMA foreign_keys = ON");
|
|
239
|
+
patchPrepare(db);
|
|
240
|
+
return db;
|
|
241
|
+
}
|
|
242
|
+
function patchPrepare(db) {
|
|
243
|
+
const originalPrepare = db.prepare.bind(db);
|
|
244
|
+
db.prepare = (sql) => {
|
|
245
|
+
const stmt = originalPrepare(sql);
|
|
246
|
+
const origRun = stmt.run.bind(stmt);
|
|
247
|
+
const origGet = stmt.get.bind(stmt);
|
|
248
|
+
const origAll = stmt.all.bind(stmt);
|
|
249
|
+
stmt.run = (...params) => origRun(...sanitizeParams(params));
|
|
250
|
+
stmt.get = (...params) => origGet(...sanitizeParams(params));
|
|
251
|
+
stmt.all = (...params) => origAll(...sanitizeParams(params));
|
|
252
|
+
return stmt;
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
function rejectAsync(fn) {
|
|
256
|
+
if (fn.constructor.name === "AsyncFunction") {
|
|
257
|
+
throw new Error(
|
|
258
|
+
"transaction() received an async function. Use a synchronous function \u2014 node:sqlite is synchronous and the transaction would commit before the async work completes."
|
|
259
|
+
);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
var txDepth = /* @__PURE__ */ new WeakMap();
|
|
263
|
+
function transaction(db, fn) {
|
|
264
|
+
rejectAsync(fn);
|
|
265
|
+
const depth = txDepth.get(db) ?? 0;
|
|
266
|
+
if (depth === 0) {
|
|
267
|
+
db.exec("BEGIN");
|
|
268
|
+
txDepth.set(db, 1);
|
|
269
|
+
try {
|
|
270
|
+
const result = fn();
|
|
271
|
+
db.exec("COMMIT");
|
|
272
|
+
return result;
|
|
273
|
+
} catch (err) {
|
|
274
|
+
try {
|
|
275
|
+
db.exec("ROLLBACK");
|
|
276
|
+
} catch {
|
|
277
|
+
}
|
|
278
|
+
throw err;
|
|
279
|
+
} finally {
|
|
280
|
+
txDepth.set(db, 0);
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
const name = `sp_${depth}`;
|
|
284
|
+
db.exec(`SAVEPOINT ${name}`);
|
|
285
|
+
txDepth.set(db, depth + 1);
|
|
286
|
+
try {
|
|
287
|
+
const result = fn();
|
|
288
|
+
db.exec(`RELEASE ${name}`);
|
|
289
|
+
return result;
|
|
290
|
+
} catch (err) {
|
|
291
|
+
try {
|
|
292
|
+
db.exec(`ROLLBACK TO ${name}`);
|
|
293
|
+
db.exec(`RELEASE ${name}`);
|
|
294
|
+
} catch {
|
|
295
|
+
}
|
|
296
|
+
throw err;
|
|
297
|
+
} finally {
|
|
298
|
+
txDepth.set(db, depth);
|
|
299
|
+
}
|
|
222
300
|
}
|
|
223
301
|
|
|
224
302
|
// src/db/schema.ts
|
|
@@ -477,12 +555,12 @@ function migrate(db) {
|
|
|
477
555
|
}
|
|
478
556
|
const pending = migrations.filter((m) => m.version > currentVersion).sort((a, b) => a.version - b.version);
|
|
479
557
|
for (const migration of pending) {
|
|
480
|
-
|
|
558
|
+
transaction(db, () => {
|
|
481
559
|
migration.up(db);
|
|
482
560
|
db.prepare(
|
|
483
561
|
"UPDATE arcbridge_meta SET value = ? WHERE key = 'schema_version'"
|
|
484
562
|
).run(String(migration.version));
|
|
485
|
-
})
|
|
563
|
+
});
|
|
486
564
|
}
|
|
487
565
|
}
|
|
488
566
|
|
|
@@ -1062,10 +1140,16 @@ ${input.template === "dotnet-webapi" ? `| Platform | Description | Notes |
|
|
|
1062
1140
|
// src/templates/arc42/09-decisions.ts
|
|
1063
1141
|
function firstAdrTemplate(input) {
|
|
1064
1142
|
const now = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
1065
|
-
|
|
1066
|
-
|
|
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);
|
|
1067
1152
|
}
|
|
1068
|
-
return nextjsAdr(input, now);
|
|
1069
1153
|
}
|
|
1070
1154
|
function nextjsAdr(input, date) {
|
|
1071
1155
|
const { appPrefix } = detectProjectLayout(input.projectRoot, input.template);
|
|
@@ -1100,6 +1184,73 @@ Use Next.js with the App Router (introduced in Next.js 13+) as the application f
|
|
|
1100
1184
|
`
|
|
1101
1185
|
};
|
|
1102
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
|
+
}
|
|
1103
1254
|
function dotnetAdr(input, date) {
|
|
1104
1255
|
return {
|
|
1105
1256
|
filename: "001-aspnet-core-webapi.md",
|
|
@@ -1883,6 +2034,116 @@ function phaseTasksTemplate(_input, phaseId) {
|
|
|
1883
2034
|
]
|
|
1884
2035
|
}
|
|
1885
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
|
+
]
|
|
1886
2147
|
}
|
|
1887
2148
|
};
|
|
1888
2149
|
return tasksByPhase[phaseId] ?? null;
|
|
@@ -2050,6 +2311,105 @@ function phaseTasksTemplate2(_input, phaseId) {
|
|
|
2050
2311
|
]
|
|
2051
2312
|
}
|
|
2052
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
|
+
]
|
|
2053
2413
|
}
|
|
2054
2414
|
};
|
|
2055
2415
|
return tasksByPhase[phaseId] ?? null;
|
|
@@ -2568,6 +2928,18 @@ You are responsible for maintaining these sections in \`.arcbridge/arc42/\`. Upd
|
|
|
2568
2928
|
- All ADRs and their status
|
|
2569
2929
|
- Building block \u2192 code mapping
|
|
2570
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
|
+
|
|
2571
2943
|
## Working Style
|
|
2572
2944
|
|
|
2573
2945
|
Think at the system level. Before making changes, consider:
|
|
@@ -2805,13 +3177,25 @@ function phaseManagerTemplate() {
|
|
|
2805
3177
|
- Tasks must be "done" before a phase can complete
|
|
2806
3178
|
- Quality scenarios linked to phase tasks must be verified
|
|
2807
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
|
+
|
|
2808
3191
|
## Phase Transition Process
|
|
2809
3192
|
|
|
2810
3193
|
1. Verify all tasks in current phase are "done"
|
|
2811
|
-
2.
|
|
2812
|
-
3.
|
|
2813
|
-
4.
|
|
2814
|
-
5.
|
|
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`
|
|
2815
3199
|
};
|
|
2816
3200
|
}
|
|
2817
3201
|
|
|
@@ -3275,7 +3659,8 @@ function refreshFromDocs(db, targetDir) {
|
|
|
3275
3659
|
const scenarioStatusMap = new Map(
|
|
3276
3660
|
existingScenarios.map((s) => [s.id, s.status])
|
|
3277
3661
|
);
|
|
3278
|
-
|
|
3662
|
+
db.exec("PRAGMA foreign_keys = OFF");
|
|
3663
|
+
const refresh = () => transaction(db, () => {
|
|
3279
3664
|
db.prepare("DELETE FROM tasks").run();
|
|
3280
3665
|
db.prepare("DELETE FROM phases").run();
|
|
3281
3666
|
db.prepare("UPDATE contracts SET building_block = NULL").run();
|
|
@@ -3287,6 +3672,16 @@ function refreshFromDocs(db, targetDir) {
|
|
|
3287
3672
|
warnings.push(...populateQualityScenarios(db, targetDir));
|
|
3288
3673
|
warnings.push(...populatePhases(db, targetDir));
|
|
3289
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
|
+
}
|
|
3290
3685
|
const restoreTask = db.prepare(
|
|
3291
3686
|
"UPDATE tasks SET status = ?, completed_at = ? WHERE id = ?"
|
|
3292
3687
|
);
|
|
@@ -3312,7 +3707,11 @@ function refreshFromDocs(db, targetDir) {
|
|
|
3312
3707
|
}
|
|
3313
3708
|
}
|
|
3314
3709
|
});
|
|
3315
|
-
|
|
3710
|
+
try {
|
|
3711
|
+
refresh();
|
|
3712
|
+
} finally {
|
|
3713
|
+
db.exec("PRAGMA foreign_keys = ON");
|
|
3714
|
+
}
|
|
3316
3715
|
return warnings;
|
|
3317
3716
|
}
|
|
3318
3717
|
function generateDatabase(targetDir, input) {
|
|
@@ -3326,12 +3725,12 @@ function generateDatabase(targetDir, input) {
|
|
|
3326
3725
|
upsert.run("project_name", input.name);
|
|
3327
3726
|
upsert.run("project_type", input.template);
|
|
3328
3727
|
upsert.run("last_full_index", (/* @__PURE__ */ new Date()).toISOString());
|
|
3329
|
-
|
|
3728
|
+
transaction(db, () => {
|
|
3330
3729
|
allWarnings.push(...populateBuildingBlocks(db, targetDir));
|
|
3331
3730
|
allWarnings.push(...populateQualityScenarios(db, targetDir));
|
|
3332
3731
|
allWarnings.push(...populatePhases(db, targetDir));
|
|
3333
3732
|
allWarnings.push(...populateAdrs(db, targetDir));
|
|
3334
|
-
})
|
|
3733
|
+
});
|
|
3335
3734
|
ensureGitignore(targetDir);
|
|
3336
3735
|
return { db, warnings: allWarnings };
|
|
3337
3736
|
}
|
|
@@ -3359,7 +3758,7 @@ import YAML from "yaml";
|
|
|
3359
3758
|
|
|
3360
3759
|
// src/indexer/program.ts
|
|
3361
3760
|
import ts from "typescript";
|
|
3362
|
-
import { join as join7 } from "path";
|
|
3761
|
+
import { join as join7, dirname } from "path";
|
|
3363
3762
|
function createTsProgram(options) {
|
|
3364
3763
|
const projectRoot = options.projectRoot;
|
|
3365
3764
|
const configPath = options.tsconfigPath ?? ts.findConfigFile(projectRoot, ts.sys.fileExists, "tsconfig.json");
|
|
@@ -3368,7 +3767,7 @@ function createTsProgram(options) {
|
|
|
3368
3767
|
`No tsconfig.json found in ${projectRoot}. TypeScript indexing requires a tsconfig.json.`
|
|
3369
3768
|
);
|
|
3370
3769
|
}
|
|
3371
|
-
|
|
3770
|
+
let configFile = ts.readConfigFile(configPath, ts.sys.readFile);
|
|
3372
3771
|
if (configFile.error) {
|
|
3373
3772
|
const message = ts.flattenDiagnosticMessageText(
|
|
3374
3773
|
configFile.error.messageText,
|
|
@@ -3376,12 +3775,45 @@ function createTsProgram(options) {
|
|
|
3376
3775
|
);
|
|
3377
3776
|
throw new Error(`Failed to read tsconfig.json: ${message}`);
|
|
3378
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
|
+
}
|
|
3379
3811
|
const parsed = ts.parseJsonConfigFileContent(
|
|
3380
3812
|
configFile.config,
|
|
3381
3813
|
ts.sys,
|
|
3382
3814
|
join7(projectRoot),
|
|
3383
3815
|
{ noEmit: true },
|
|
3384
|
-
|
|
3816
|
+
resolvedConfigPath
|
|
3385
3817
|
);
|
|
3386
3818
|
const program = ts.createProgram({
|
|
3387
3819
|
rootNames: parsed.fileNames,
|
|
@@ -4130,7 +4562,7 @@ function writeComponents(db, components) {
|
|
|
4130
4562
|
const existingIds = new Set(
|
|
4131
4563
|
db.prepare("SELECT id FROM symbols").all().map((r) => r.id)
|
|
4132
4564
|
);
|
|
4133
|
-
|
|
4565
|
+
transaction(db, () => {
|
|
4134
4566
|
for (const c of components) {
|
|
4135
4567
|
if (!existingIds.has(c.symbolId)) continue;
|
|
4136
4568
|
insert.run(
|
|
@@ -4144,7 +4576,6 @@ function writeComponents(db, components) {
|
|
|
4144
4576
|
);
|
|
4145
4577
|
}
|
|
4146
4578
|
});
|
|
4147
|
-
run();
|
|
4148
4579
|
}
|
|
4149
4580
|
|
|
4150
4581
|
// src/indexer/route-analyzer.ts
|
|
@@ -4273,7 +4704,7 @@ function writeRoutes(db, routes) {
|
|
|
4273
4704
|
id, route_path, kind, http_methods, has_auth, parent_layout, service
|
|
4274
4705
|
) VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
4275
4706
|
`);
|
|
4276
|
-
|
|
4707
|
+
transaction(db, () => {
|
|
4277
4708
|
for (const r of routes) {
|
|
4278
4709
|
insert.run(
|
|
4279
4710
|
r.id,
|
|
@@ -4286,7 +4717,6 @@ function writeRoutes(db, routes) {
|
|
|
4286
4717
|
);
|
|
4287
4718
|
}
|
|
4288
4719
|
});
|
|
4289
|
-
run();
|
|
4290
4720
|
}
|
|
4291
4721
|
|
|
4292
4722
|
// src/indexer/content-hash.ts
|
|
@@ -4320,7 +4750,7 @@ function removeSymbolsForFiles(db, filePaths) {
|
|
|
4320
4750
|
const deleteComponents = db.prepare(
|
|
4321
4751
|
"DELETE FROM components WHERE symbol_id IN (SELECT id FROM symbols WHERE file_path = ?)"
|
|
4322
4752
|
);
|
|
4323
|
-
|
|
4753
|
+
transaction(db, () => {
|
|
4324
4754
|
for (const fp of filePaths) {
|
|
4325
4755
|
deleteDepsSource.run(fp);
|
|
4326
4756
|
deleteDepsTarget.run(fp);
|
|
@@ -4328,7 +4758,6 @@ function removeSymbolsForFiles(db, filePaths) {
|
|
|
4328
4758
|
deleteSymbols.run(fp);
|
|
4329
4759
|
}
|
|
4330
4760
|
});
|
|
4331
|
-
run();
|
|
4332
4761
|
}
|
|
4333
4762
|
function writeSymbols(db, symbols, service, language = "typescript") {
|
|
4334
4763
|
if (symbols.length === 0) return;
|
|
@@ -4348,7 +4777,7 @@ function writeSymbols(db, symbols, service, language = "typescript") {
|
|
|
4348
4777
|
)
|
|
4349
4778
|
`);
|
|
4350
4779
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
4351
|
-
|
|
4780
|
+
transaction(db, () => {
|
|
4352
4781
|
for (const s of symbols) {
|
|
4353
4782
|
insert.run(
|
|
4354
4783
|
s.id,
|
|
@@ -4372,7 +4801,6 @@ function writeSymbols(db, symbols, service, language = "typescript") {
|
|
|
4372
4801
|
);
|
|
4373
4802
|
}
|
|
4374
4803
|
});
|
|
4375
|
-
run();
|
|
4376
4804
|
}
|
|
4377
4805
|
function writeDependencies(db, dependencies) {
|
|
4378
4806
|
if (dependencies.length === 0) return;
|
|
@@ -4380,17 +4808,16 @@ function writeDependencies(db, dependencies) {
|
|
|
4380
4808
|
INSERT OR IGNORE INTO dependencies (source_symbol, target_symbol, kind)
|
|
4381
4809
|
VALUES (?, ?, ?)
|
|
4382
4810
|
`);
|
|
4383
|
-
|
|
4811
|
+
transaction(db, () => {
|
|
4384
4812
|
for (const dep of dependencies) {
|
|
4385
4813
|
insert.run(dep.sourceSymbolId, dep.targetSymbolId, dep.kind);
|
|
4386
4814
|
}
|
|
4387
4815
|
});
|
|
4388
|
-
run();
|
|
4389
4816
|
}
|
|
4390
4817
|
|
|
4391
4818
|
// src/indexer/dotnet-indexer.ts
|
|
4392
4819
|
import { execFileSync } from "child_process";
|
|
4393
|
-
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";
|
|
4394
4821
|
import { readdirSync as readdirSync3, readFileSync as readFileSync3, existsSync as existsSync4, accessSync, constants } from "fs";
|
|
4395
4822
|
import { fileURLToPath } from "url";
|
|
4396
4823
|
function findDotnetProject(projectRoot) {
|
|
@@ -4407,7 +4834,7 @@ function findDotnetProject(projectRoot) {
|
|
|
4407
4834
|
}
|
|
4408
4835
|
function parseSolutionProjects(slnPath) {
|
|
4409
4836
|
const content = readFileSync3(slnPath, "utf-8");
|
|
4410
|
-
const slnDir =
|
|
4837
|
+
const slnDir = dirname2(slnPath);
|
|
4411
4838
|
const projects = [];
|
|
4412
4839
|
const projectPattern = /Project\("\{[^}]+\}"\)\s*=\s*"([^"]+)",\s*"([^"]+\.csproj)"/g;
|
|
4413
4840
|
let match;
|
|
@@ -4416,7 +4843,7 @@ function parseSolutionProjects(slnPath) {
|
|
|
4416
4843
|
const relativeCsprojPath = match[2].replace(/\\/g, "/");
|
|
4417
4844
|
const fullCsprojPath = resolve(join9(slnDir, relativeCsprojPath));
|
|
4418
4845
|
if (!existsSync4(fullCsprojPath)) continue;
|
|
4419
|
-
const projectDir = relative4(slnDir,
|
|
4846
|
+
const projectDir = relative4(slnDir, dirname2(fullCsprojPath)).replace(/\\/g, "/") || ".";
|
|
4420
4847
|
const isTestProject = /[.\x2d]tests?$/i.test(name) || /[.\x2d](unit|integration|functional|e2e)tests?$/i.test(name);
|
|
4421
4848
|
projects.push({
|
|
4422
4849
|
name,
|
|
@@ -4461,7 +4888,7 @@ function hasGlobalTool() {
|
|
|
4461
4888
|
return false;
|
|
4462
4889
|
}
|
|
4463
4890
|
function resolveIndexerProject() {
|
|
4464
|
-
const currentDir2 =
|
|
4891
|
+
const currentDir2 = dirname2(fileURLToPath(import.meta.url));
|
|
4465
4892
|
const candidates = [
|
|
4466
4893
|
resolve(currentDir2, "../../../../dotnet-indexer/ArcBridge.DotnetIndexer.csproj"),
|
|
4467
4894
|
resolve(currentDir2, "../../../dotnet-indexer/ArcBridge.DotnetIndexer.csproj"),
|
|
@@ -4582,7 +5009,7 @@ function indexDotnetProjectRoslyn(db, options) {
|
|
|
4582
5009
|
INSERT OR REPLACE INTO routes (id, route_path, kind, http_methods, has_auth, service)
|
|
4583
5010
|
VALUES (?, ?, ?, ?, ?, ?)
|
|
4584
5011
|
`);
|
|
4585
|
-
|
|
5012
|
+
transaction(db, () => {
|
|
4586
5013
|
for (const route of output.routes) {
|
|
4587
5014
|
insertRoute.run(
|
|
4588
5015
|
route.id,
|
|
@@ -4594,7 +5021,6 @@ function indexDotnetProjectRoslyn(db, options) {
|
|
|
4594
5021
|
);
|
|
4595
5022
|
}
|
|
4596
5023
|
});
|
|
4597
|
-
runRoutes();
|
|
4598
5024
|
}
|
|
4599
5025
|
return {
|
|
4600
5026
|
symbolsIndexed: output.symbols.length,
|
|
@@ -4616,10 +5042,10 @@ import { globbySync } from "globby";
|
|
|
4616
5042
|
|
|
4617
5043
|
// src/indexer/csharp/parser.ts
|
|
4618
5044
|
import { accessSync as accessSync2, constants as constants2 } from "fs";
|
|
4619
|
-
import { dirname as
|
|
5045
|
+
import { dirname as dirname3, resolve as resolve2 } from "path";
|
|
4620
5046
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
4621
5047
|
import "web-tree-sitter";
|
|
4622
|
-
var currentDir =
|
|
5048
|
+
var currentDir = dirname3(fileURLToPath2(import.meta.url));
|
|
4623
5049
|
var cachedParser = null;
|
|
4624
5050
|
var initPromise = null;
|
|
4625
5051
|
function resolveGrammarPath() {
|
|
@@ -5513,7 +5939,7 @@ async function indexCSharpTreeSitter(db, options) {
|
|
|
5513
5939
|
INSERT OR REPLACE INTO routes (id, route_path, kind, http_methods, has_auth, service)
|
|
5514
5940
|
VALUES (?, ?, ?, ?, ?, ?)
|
|
5515
5941
|
`);
|
|
5516
|
-
|
|
5942
|
+
transaction(db, () => {
|
|
5517
5943
|
for (const route of allRoutes) {
|
|
5518
5944
|
insertRoute.run(
|
|
5519
5945
|
route.id,
|
|
@@ -5525,7 +5951,6 @@ async function indexCSharpTreeSitter(db, options) {
|
|
|
5525
5951
|
);
|
|
5526
5952
|
}
|
|
5527
5953
|
});
|
|
5528
|
-
runRoutes();
|
|
5529
5954
|
}
|
|
5530
5955
|
return {
|
|
5531
5956
|
symbolsIndexed: allNewSymbols.length,
|
|
@@ -5557,12 +5982,11 @@ function indexPackageDependencies(db, projectRoot, service = "main") {
|
|
|
5557
5982
|
const insert = db.prepare(
|
|
5558
5983
|
"INSERT OR IGNORE INTO package_dependencies (name, version, source, service) VALUES (?, ?, ?, ?)"
|
|
5559
5984
|
);
|
|
5560
|
-
|
|
5985
|
+
transaction(db, () => {
|
|
5561
5986
|
for (const dep of deps) {
|
|
5562
5987
|
insert.run(dep.name, dep.version, dep.source, service);
|
|
5563
5988
|
}
|
|
5564
5989
|
});
|
|
5565
|
-
run();
|
|
5566
5990
|
return deps.length;
|
|
5567
5991
|
}
|
|
5568
5992
|
function parsePackageJson(filePath) {
|
|
@@ -5815,12 +6239,11 @@ function writeDriftLog(db, entries) {
|
|
|
5815
6239
|
VALUES (?, ?, ?, ?, ?, ?)
|
|
5816
6240
|
`);
|
|
5817
6241
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
5818
|
-
|
|
6242
|
+
transaction(db, () => {
|
|
5819
6243
|
for (const e of entries) {
|
|
5820
6244
|
insert.run(now, e.kind, e.severity, e.description, e.affectedBlock, e.affectedFile);
|
|
5821
6245
|
}
|
|
5822
6246
|
});
|
|
5823
|
-
run();
|
|
5824
6247
|
}
|
|
5825
6248
|
function detectUndocumentedModules(db, entries, ignorePaths = []) {
|
|
5826
6249
|
const blocks = db.prepare("SELECT id, name, code_paths FROM building_blocks").all();
|
|
@@ -5978,7 +6401,17 @@ function detectNewDependencies(db, entries) {
|
|
|
5978
6401
|
const adrs = db.prepare("SELECT id, title, context, decision FROM adrs WHERE status != 'superseded'").all();
|
|
5979
6402
|
const adrText = adrs.map((a) => `${a.title} ${a.context ?? ""} ${a.decision ?? ""}`.toLowerCase()).join(" ");
|
|
5980
6403
|
const trivialPackages = /* @__PURE__ */ new Set([
|
|
5981
|
-
// 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
|
|
5982
6415
|
"typescript",
|
|
5983
6416
|
"eslint",
|
|
5984
6417
|
"prettier",
|
|
@@ -5986,15 +6419,28 @@ function detectNewDependencies(db, entries) {
|
|
|
5986
6419
|
"jest",
|
|
5987
6420
|
"@types/node",
|
|
5988
6421
|
"@types/react",
|
|
6422
|
+
"@types/react-dom",
|
|
5989
6423
|
"tsup",
|
|
5990
6424
|
"tsx",
|
|
5991
|
-
|
|
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
|
|
5992
6437
|
"microsoft.net.test.sdk",
|
|
5993
6438
|
"xunit",
|
|
5994
6439
|
"xunit.runner.visualstudio",
|
|
5995
6440
|
"nunit",
|
|
5996
6441
|
"nunit3testadapter",
|
|
5997
6442
|
"coverlet.collector",
|
|
6443
|
+
// nuget — framework
|
|
5998
6444
|
"microsoft.aspnetcore.openapi",
|
|
5999
6445
|
"swashbuckle.aspnetcore"
|
|
6000
6446
|
]);
|
|
@@ -6139,12 +6585,11 @@ function applyInferences(db, inferences, projectRoot) {
|
|
|
6139
6585
|
"UPDATE tasks SET status = ?, completed_at = CASE WHEN ? = 'done' THEN ? ELSE completed_at END WHERE id = ?"
|
|
6140
6586
|
);
|
|
6141
6587
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
6142
|
-
|
|
6588
|
+
transaction(db, () => {
|
|
6143
6589
|
for (const inf of inferences) {
|
|
6144
6590
|
update.run(inf.inferredStatus, inf.inferredStatus, now, inf.taskId);
|
|
6145
6591
|
}
|
|
6146
6592
|
});
|
|
6147
|
-
run();
|
|
6148
6593
|
for (const inf of inferences) {
|
|
6149
6594
|
const task = db.prepare("SELECT phase_id FROM tasks WHERE id = ?").get(inf.taskId);
|
|
6150
6595
|
if (task) {
|
|
@@ -6250,7 +6695,7 @@ function safeParseJson2(value, fallback) {
|
|
|
6250
6695
|
|
|
6251
6696
|
// src/generators/sync-generator.ts
|
|
6252
6697
|
import { mkdirSync as mkdirSync6, writeFileSync as writeFileSync6 } from "fs";
|
|
6253
|
-
import { join as join15, dirname as
|
|
6698
|
+
import { join as join15, dirname as dirname4 } from "path";
|
|
6254
6699
|
|
|
6255
6700
|
// src/templates/sync/claude-skill.ts
|
|
6256
6701
|
function claudeSkillTemplate(config) {
|
|
@@ -6429,20 +6874,20 @@ function generateSyncFiles(targetDir, config) {
|
|
|
6429
6874
|
const generated = [];
|
|
6430
6875
|
const action = githubActionTemplate(config);
|
|
6431
6876
|
const actionPath = join15(targetDir, action.relativePath);
|
|
6432
|
-
mkdirSync6(
|
|
6877
|
+
mkdirSync6(dirname4(actionPath), { recursive: true });
|
|
6433
6878
|
writeFileSync6(actionPath, action.content, "utf-8");
|
|
6434
6879
|
generated.push(action.relativePath);
|
|
6435
6880
|
if (config.platforms.includes("claude")) {
|
|
6436
6881
|
const skill = claudeSkillTemplate(config);
|
|
6437
6882
|
const skillPath = join15(targetDir, skill.relativePath);
|
|
6438
|
-
mkdirSync6(
|
|
6883
|
+
mkdirSync6(dirname4(skillPath), { recursive: true });
|
|
6439
6884
|
writeFileSync6(skillPath, skill.content, "utf-8");
|
|
6440
6885
|
generated.push(skill.relativePath);
|
|
6441
6886
|
}
|
|
6442
6887
|
if (config.platforms.includes("copilot")) {
|
|
6443
6888
|
const hook = copilotHookTemplate(config);
|
|
6444
6889
|
const hookPath = join15(targetDir, hook.relativePath);
|
|
6445
|
-
mkdirSync6(
|
|
6890
|
+
mkdirSync6(dirname4(hookPath), { recursive: true });
|
|
6446
6891
|
writeFileSync6(hookPath, hook.content, "utf-8");
|
|
6447
6892
|
generated.push(hook.relativePath);
|
|
6448
6893
|
}
|
|
@@ -6798,6 +7243,7 @@ function resolveRef(projectRoot, since, db) {
|
|
|
6798
7243
|
}
|
|
6799
7244
|
}
|
|
6800
7245
|
function getChangedFiles(projectRoot, ref) {
|
|
7246
|
+
const byPath = /* @__PURE__ */ new Map();
|
|
6801
7247
|
try {
|
|
6802
7248
|
execFileSync3("git", ["rev-parse", "--verify", ref], {
|
|
6803
7249
|
cwd: projectRoot,
|
|
@@ -6810,16 +7256,24 @@ function getChangedFiles(projectRoot, ref) {
|
|
|
6810
7256
|
["diff", "--name-status", "--no-renames", ref, "HEAD"],
|
|
6811
7257
|
{ cwd: projectRoot, encoding: "utf-8", timeout: 1e4 }
|
|
6812
7258
|
).trim();
|
|
6813
|
-
if (
|
|
6814
|
-
|
|
6815
|
-
|
|
6816
|
-
|
|
6817
|
-
|
|
6818
|
-
|
|
6819
|
-
}
|
|
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
|
+
}
|
|
6820
7266
|
} catch {
|
|
6821
|
-
return getUncommittedChanges(projectRoot);
|
|
6822
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());
|
|
6823
7277
|
}
|
|
6824
7278
|
function getUncommittedChanges(projectRoot) {
|
|
6825
7279
|
try {
|
|
@@ -6827,11 +7281,12 @@ function getUncommittedChanges(projectRoot) {
|
|
|
6827
7281
|
"git",
|
|
6828
7282
|
["status", "--porcelain", "-uno"],
|
|
6829
7283
|
{ cwd: projectRoot, encoding: "utf-8", timeout: 5e3 }
|
|
6830
|
-
)
|
|
6831
|
-
|
|
6832
|
-
|
|
7284
|
+
);
|
|
7285
|
+
const lines = output.split("\n").filter((l) => l.length >= 3);
|
|
7286
|
+
if (lines.length === 0) return [];
|
|
7287
|
+
return lines.map((line) => {
|
|
6833
7288
|
const statusCode = line.slice(0, 2).trim();
|
|
6834
|
-
const path = line.slice(3);
|
|
7289
|
+
const path = line.slice(3).replace(/\r$/, "");
|
|
6835
7290
|
const status = statusCode === "D" ? "deleted" : statusCode === "A" ? "added" : "modified";
|
|
6836
7291
|
return { status, path };
|
|
6837
7292
|
});
|
|
@@ -7086,9 +7541,11 @@ export {
|
|
|
7086
7541
|
refreshFromDocs,
|
|
7087
7542
|
resolveRef,
|
|
7088
7543
|
setSyncCommit,
|
|
7544
|
+
suppressSqliteWarning,
|
|
7089
7545
|
syncPhaseToYaml,
|
|
7090
7546
|
syncScenarioToYaml,
|
|
7091
7547
|
syncTaskToYaml,
|
|
7548
|
+
transaction,
|
|
7092
7549
|
verifyScenarios,
|
|
7093
7550
|
writeDriftLog
|
|
7094
7551
|
};
|