@arcbridge/core 0.3.0 → 0.3.1
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 +69 -60
- package/dist/index.js +108 -152
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,103 +1,92 @@
|
|
|
1
1
|
// src/schemas/config.ts
|
|
2
|
+
import { z as z2 } from "zod";
|
|
3
|
+
|
|
4
|
+
// src/schemas/quality-scenarios.ts
|
|
2
5
|
import { z } from "zod";
|
|
3
|
-
var
|
|
6
|
+
var QualityCategorySchema = z.string().regex(/^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/, "Must be lowercase kebab-case (e.g., 'security', 'data-integrity')");
|
|
7
|
+
var QUALITY_PRIORITIES_DESCRIPTION = "Quality priorities in order. Common: security, performance, accessibility, reliability, maintainability, usability, portability, compatibility. Custom categories like data-integrity or compliance are also supported.";
|
|
8
|
+
var QualityPrioritySchema = z.enum(["must", "should", "could"]);
|
|
9
|
+
var QualityScenarioStatusSchema = z.enum([
|
|
10
|
+
"passing",
|
|
11
|
+
"failing",
|
|
12
|
+
"untested",
|
|
13
|
+
"partial"
|
|
14
|
+
]);
|
|
15
|
+
var QualityScenarioSchema = z.object({
|
|
16
|
+
id: z.string().regex(/^[A-Z0-9]+-\d+$/, "Must match pattern like SEC-01"),
|
|
4
17
|
name: z.string().min(1),
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
18
|
+
category: QualityCategorySchema,
|
|
19
|
+
priority: QualityPrioritySchema,
|
|
20
|
+
scenario: z.string().min(1),
|
|
21
|
+
expected: z.string().min(1),
|
|
22
|
+
linked_code: z.array(z.string()).default([]),
|
|
23
|
+
linked_tests: z.array(z.string()).default([]),
|
|
24
|
+
linked_blocks: z.array(z.string()).default([]),
|
|
25
|
+
verification: z.enum(["automatic", "manual", "semi-automatic"]),
|
|
26
|
+
status: QualityScenarioStatusSchema.default("untested")
|
|
27
|
+
});
|
|
28
|
+
var QualityGoalSchema = z.object({
|
|
29
|
+
id: QualityCategorySchema,
|
|
30
|
+
priority: z.number().int().min(1),
|
|
31
|
+
description: z.string().min(1)
|
|
9
32
|
});
|
|
10
|
-
var
|
|
33
|
+
var QualityScenariosFileSchema = z.object({
|
|
11
34
|
schema_version: z.literal(1).default(1),
|
|
12
|
-
|
|
13
|
-
|
|
35
|
+
last_updated: z.string(),
|
|
36
|
+
quality_goals: z.array(QualityGoalSchema),
|
|
37
|
+
scenarios: z.array(QualityScenarioSchema)
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
// src/schemas/config.ts
|
|
41
|
+
var ServiceSchema = z2.object({
|
|
42
|
+
name: z2.string().min(1),
|
|
43
|
+
path: z2.string().default("."),
|
|
44
|
+
type: z2.enum(["nextjs", "react", "fastify", "express", "hono", "dotnet", "unity"]),
|
|
45
|
+
tsconfig: z2.string().optional(),
|
|
46
|
+
csproj: z2.string().optional()
|
|
47
|
+
});
|
|
48
|
+
var ArcBridgeConfigSchema = z2.object({
|
|
49
|
+
schema_version: z2.literal(1).default(1),
|
|
50
|
+
project_name: z2.string().min(1),
|
|
51
|
+
project_type: z2.enum([
|
|
14
52
|
"nextjs-app-router",
|
|
15
53
|
"react-vite",
|
|
16
54
|
"api-service",
|
|
17
55
|
"dotnet-webapi",
|
|
18
56
|
"unity-game"
|
|
19
57
|
]).default("nextjs-app-router"),
|
|
20
|
-
services:
|
|
21
|
-
platforms:
|
|
22
|
-
quality_priorities:
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
"maintainability"
|
|
29
|
-
])
|
|
30
|
-
).default(["security", "performance", "accessibility"]),
|
|
31
|
-
indexing: z.object({
|
|
32
|
-
include: z.array(z.string()).default(["src/**/*", "app/**/*"]),
|
|
33
|
-
exclude: z.array(z.string()).default(["node_modules", "dist", ".next", "coverage"]),
|
|
34
|
-
default_mode: z.enum(["fast", "deep"]).default("fast"),
|
|
35
|
-
csharp_indexer: z.enum(["auto", "roslyn", "tree-sitter"]).default("auto").describe(
|
|
58
|
+
services: z2.array(ServiceSchema).default([]),
|
|
59
|
+
platforms: z2.array(z2.enum(["claude", "copilot", "gemini", "codex"])).default(["claude"]),
|
|
60
|
+
quality_priorities: z2.array(QualityCategorySchema).default(["security", "performance", "accessibility"]).describe(QUALITY_PRIORITIES_DESCRIPTION),
|
|
61
|
+
indexing: z2.object({
|
|
62
|
+
include: z2.array(z2.string()).default(["src/**/*", "app/**/*"]),
|
|
63
|
+
exclude: z2.array(z2.string()).default(["node_modules", "dist", ".next", "coverage"]),
|
|
64
|
+
default_mode: z2.enum(["fast", "deep"]).default("fast"),
|
|
65
|
+
csharp_indexer: z2.enum(["auto", "roslyn", "tree-sitter"]).default("auto").describe(
|
|
36
66
|
"C# indexer backend: 'auto' prefers the arcbridge-dotnet-indexer global tool, falls back to monorepo source if dotnet CLI is available, else tree-sitter. 'tree-sitter' works without .NET SDK. 'roslyn' requires global tool or monorepo source + .NET SDK."
|
|
37
67
|
)
|
|
38
68
|
}).default({}),
|
|
39
|
-
testing:
|
|
40
|
-
test_command:
|
|
41
|
-
timeout_ms:
|
|
69
|
+
testing: z2.object({
|
|
70
|
+
test_command: z2.string().min(1).default("npx vitest run").describe("Command to run tests (space-separated, no shell syntax). File paths are appended as arguments."),
|
|
71
|
+
timeout_ms: z2.number().int().min(1e3).default(6e4).describe("Timeout per test run in milliseconds")
|
|
42
72
|
}).default({}),
|
|
43
|
-
drift:
|
|
44
|
-
ignore_paths:
|
|
73
|
+
drift: z2.object({
|
|
74
|
+
ignore_paths: z2.array(z2.string()).default([]).describe(
|
|
45
75
|
"File paths or prefixes to ignore in undocumented_module drift checks. Framework files (e.g. next.config.ts, root layout/page) are auto-ignored for known project types."
|
|
46
76
|
)
|
|
47
77
|
}).default({}),
|
|
48
|
-
metrics:
|
|
49
|
-
auto_record:
|
|
78
|
+
metrics: z2.object({
|
|
79
|
+
auto_record: z2.boolean().default(false).describe(
|
|
50
80
|
"Automatically record agent activity (tool name, duration, quality snapshot) when key MCP tools are invoked. Token/model info is optional and caller-provided."
|
|
51
81
|
)
|
|
52
82
|
}).default({}),
|
|
53
|
-
sync:
|
|
54
|
-
auto_detect_drift:
|
|
55
|
-
drift_severity_threshold:
|
|
56
|
-
propose_updates_on:
|
|
83
|
+
sync: z2.object({
|
|
84
|
+
auto_detect_drift: z2.boolean().default(true),
|
|
85
|
+
drift_severity_threshold: z2.enum(["info", "warning", "error"]).default("warning"),
|
|
86
|
+
propose_updates_on: z2.enum(["session-end", "phase-complete", "manual"]).default("phase-complete")
|
|
57
87
|
}).default({})
|
|
58
88
|
});
|
|
59
89
|
|
|
60
|
-
// src/schemas/quality-scenarios.ts
|
|
61
|
-
import { z as z2 } from "zod";
|
|
62
|
-
var QualityCategorySchema = z2.enum([
|
|
63
|
-
"security",
|
|
64
|
-
"performance",
|
|
65
|
-
"accessibility",
|
|
66
|
-
"reliability",
|
|
67
|
-
"maintainability"
|
|
68
|
-
]);
|
|
69
|
-
var QualityPrioritySchema = z2.enum(["must", "should", "could"]);
|
|
70
|
-
var QualityScenarioStatusSchema = z2.enum([
|
|
71
|
-
"passing",
|
|
72
|
-
"failing",
|
|
73
|
-
"untested",
|
|
74
|
-
"partial"
|
|
75
|
-
]);
|
|
76
|
-
var QualityScenarioSchema = z2.object({
|
|
77
|
-
id: z2.string().regex(/^[A-Z0-9]+-\d+$/, "Must match pattern like SEC-01"),
|
|
78
|
-
name: z2.string().min(1),
|
|
79
|
-
category: QualityCategorySchema,
|
|
80
|
-
priority: QualityPrioritySchema,
|
|
81
|
-
scenario: z2.string().min(1),
|
|
82
|
-
expected: z2.string().min(1),
|
|
83
|
-
linked_code: z2.array(z2.string()).default([]),
|
|
84
|
-
linked_tests: z2.array(z2.string()).default([]),
|
|
85
|
-
linked_blocks: z2.array(z2.string()).default([]),
|
|
86
|
-
verification: z2.enum(["automatic", "manual", "semi-automatic"]),
|
|
87
|
-
status: QualityScenarioStatusSchema.default("untested")
|
|
88
|
-
});
|
|
89
|
-
var QualityGoalSchema = z2.object({
|
|
90
|
-
id: QualityCategorySchema,
|
|
91
|
-
priority: z2.number().int().min(1),
|
|
92
|
-
description: z2.string().min(1)
|
|
93
|
-
});
|
|
94
|
-
var QualityScenariosFileSchema = z2.object({
|
|
95
|
-
schema_version: z2.literal(1).default(1),
|
|
96
|
-
last_updated: z2.string(),
|
|
97
|
-
quality_goals: z2.array(QualityGoalSchema),
|
|
98
|
-
scenarios: z2.array(QualityScenarioSchema)
|
|
99
|
-
});
|
|
100
|
-
|
|
101
90
|
// src/schemas/building-blocks.ts
|
|
102
91
|
import { z as z3 } from "zod";
|
|
103
92
|
var BuildingBlockSchema = z3.object({
|
|
@@ -7280,20 +7269,26 @@ function safeParseJson(value, fallback) {
|
|
|
7280
7269
|
import { join as join15 } from "path";
|
|
7281
7270
|
import { existsSync as existsSync8, readFileSync as readFileSync8, writeFileSync as writeFileSync5, mkdirSync as mkdirSync5, unlinkSync } from "fs";
|
|
7282
7271
|
import { parse as parse2, stringify as stringify4 } from "yaml";
|
|
7272
|
+
function readTaskFile(projectRoot, phaseId) {
|
|
7273
|
+
const path = join15(projectRoot, ".arcbridge", "plan", "tasks", `${phaseId}.yaml`);
|
|
7274
|
+
if (!existsSync8(path)) return { error: "not-found" };
|
|
7275
|
+
const raw = readFileSync8(path, "utf-8");
|
|
7276
|
+
const result = TaskFileSchema.safeParse(parse2(raw));
|
|
7277
|
+
if (!result.success) return { error: "invalid" };
|
|
7278
|
+
return { data: result.data, path };
|
|
7279
|
+
}
|
|
7280
|
+
function readPhasesFile(projectRoot) {
|
|
7281
|
+
const path = join15(projectRoot, ".arcbridge", "plan", "phases.yaml");
|
|
7282
|
+
if (!existsSync8(path)) return { error: "not-found" };
|
|
7283
|
+
const raw = readFileSync8(path, "utf-8");
|
|
7284
|
+
const result = PhasesFileSchema.safeParse(parse2(raw));
|
|
7285
|
+
if (!result.success) return { error: "invalid" };
|
|
7286
|
+
return { data: result.data, path };
|
|
7287
|
+
}
|
|
7283
7288
|
function syncTaskToYaml(projectRoot, phaseId, taskId, status, completedAt) {
|
|
7284
|
-
const
|
|
7285
|
-
|
|
7286
|
-
|
|
7287
|
-
"plan",
|
|
7288
|
-
"tasks",
|
|
7289
|
-
`${phaseId}.yaml`
|
|
7290
|
-
);
|
|
7291
|
-
if (!existsSync8(taskPath)) return;
|
|
7292
|
-
const raw = readFileSync8(taskPath, "utf-8");
|
|
7293
|
-
const parsed = parse2(raw);
|
|
7294
|
-
const result = TaskFileSchema.safeParse(parsed);
|
|
7295
|
-
if (!result.success) return;
|
|
7296
|
-
const taskFile = result.data;
|
|
7289
|
+
const readResult = readTaskFile(projectRoot, phaseId);
|
|
7290
|
+
if ("error" in readResult) return;
|
|
7291
|
+
const { data: taskFile, path: taskPath } = readResult;
|
|
7297
7292
|
const task = taskFile.tasks.find((t) => t.id === taskId);
|
|
7298
7293
|
if (!task) return;
|
|
7299
7294
|
task.status = status;
|
|
@@ -7306,39 +7301,19 @@ function syncTaskToYaml(projectRoot, phaseId, taskId, status, completedAt) {
|
|
|
7306
7301
|
}
|
|
7307
7302
|
function addTaskToYaml(projectRoot, phaseId, task) {
|
|
7308
7303
|
const tasksDir = join15(projectRoot, ".arcbridge", "plan", "tasks");
|
|
7309
|
-
const taskPath = join15(tasksDir, `${phaseId}.yaml`);
|
|
7310
7304
|
mkdirSync5(tasksDir, { recursive: true });
|
|
7311
|
-
|
|
7312
|
-
|
|
7313
|
-
const raw = readFileSync8(taskPath, "utf-8");
|
|
7314
|
-
const parsed = parse2(raw);
|
|
7315
|
-
const result = TaskFileSchema.safeParse(parsed);
|
|
7316
|
-
if (result.success) {
|
|
7317
|
-
taskFile = result.data;
|
|
7318
|
-
} else {
|
|
7319
|
-
taskFile = { schema_version: 1, phase_id: phaseId, tasks: [] };
|
|
7320
|
-
}
|
|
7321
|
-
} else {
|
|
7322
|
-
taskFile = { schema_version: 1, phase_id: phaseId, tasks: [] };
|
|
7323
|
-
}
|
|
7305
|
+
const readResult = readTaskFile(projectRoot, phaseId);
|
|
7306
|
+
const taskFile = "error" in readResult ? { schema_version: 1, phase_id: phaseId, tasks: [] } : readResult.data;
|
|
7324
7307
|
if (!taskFile.tasks.some((t) => t.id === task.id)) {
|
|
7325
7308
|
taskFile.tasks.push(task);
|
|
7326
7309
|
}
|
|
7310
|
+
const taskPath = join15(tasksDir, `${phaseId}.yaml`);
|
|
7327
7311
|
writeFileSync5(taskPath, stringify4(taskFile), "utf-8");
|
|
7328
7312
|
}
|
|
7329
7313
|
function syncPhaseToYaml(projectRoot, phaseId, status, startedAt, completedAt) {
|
|
7330
|
-
const
|
|
7331
|
-
|
|
7332
|
-
|
|
7333
|
-
"plan",
|
|
7334
|
-
"phases.yaml"
|
|
7335
|
-
);
|
|
7336
|
-
if (!existsSync8(phasesPath)) return;
|
|
7337
|
-
const raw = readFileSync8(phasesPath, "utf-8");
|
|
7338
|
-
const parsed = parse2(raw);
|
|
7339
|
-
const result = PhasesFileSchema.safeParse(parsed);
|
|
7340
|
-
if (!result.success) return;
|
|
7341
|
-
const phasesFile = result.data;
|
|
7314
|
+
const readResult = readPhasesFile(projectRoot);
|
|
7315
|
+
if ("error" in readResult) return;
|
|
7316
|
+
const { data: phasesFile, path: phasesPath } = readResult;
|
|
7342
7317
|
const phase = phasesFile.phases.find((p) => p.id === phaseId);
|
|
7343
7318
|
if (!phase) return;
|
|
7344
7319
|
phase.status = status;
|
|
@@ -7348,21 +7323,14 @@ function syncPhaseToYaml(projectRoot, phaseId, status, startedAt, completedAt) {
|
|
|
7348
7323
|
}
|
|
7349
7324
|
function addPhaseToYaml(projectRoot, phase) {
|
|
7350
7325
|
try {
|
|
7351
|
-
const
|
|
7352
|
-
|
|
7353
|
-
|
|
7354
|
-
|
|
7355
|
-
|
|
7356
|
-
|
|
7357
|
-
if (!existsSync8(phasesPath)) {
|
|
7358
|
-
return { success: false, warning: "phases.yaml not found" };
|
|
7359
|
-
}
|
|
7360
|
-
const raw = readFileSync8(phasesPath, "utf-8");
|
|
7361
|
-
const result = PhasesFileSchema.safeParse(parse2(raw));
|
|
7362
|
-
if (!result.success) {
|
|
7363
|
-
return { success: false, warning: "phases.yaml failed validation" };
|
|
7326
|
+
const readResult = readPhasesFile(projectRoot);
|
|
7327
|
+
if ("error" in readResult) {
|
|
7328
|
+
return {
|
|
7329
|
+
success: false,
|
|
7330
|
+
warning: readResult.error === "not-found" ? "phases.yaml not found" : "phases.yaml failed validation"
|
|
7331
|
+
};
|
|
7364
7332
|
}
|
|
7365
|
-
const phasesFile =
|
|
7333
|
+
const { data: phasesFile, path: phasesPath } = readResult;
|
|
7366
7334
|
const existingById = phasesFile.phases.some((p) => p.id === phase.id);
|
|
7367
7335
|
const existingByNumber = phasesFile.phases.some((p) => p.phase_number === phase.phase_number);
|
|
7368
7336
|
if (existingByNumber && !existingById) {
|
|
@@ -7429,25 +7397,17 @@ function syncScenarioToYaml(projectRoot, scenarioId, status, linkedTests, verifi
|
|
|
7429
7397
|
}
|
|
7430
7398
|
function deleteTaskFromYaml(projectRoot, phaseId, taskId) {
|
|
7431
7399
|
try {
|
|
7432
|
-
const
|
|
7433
|
-
|
|
7434
|
-
|
|
7435
|
-
|
|
7436
|
-
|
|
7437
|
-
`${phaseId}.yaml`
|
|
7438
|
-
);
|
|
7439
|
-
if (!existsSync8(taskPath)) {
|
|
7440
|
-
return { success: true };
|
|
7441
|
-
}
|
|
7442
|
-
const raw = readFileSync8(taskPath, "utf-8");
|
|
7443
|
-
const result = TaskFileSchema.safeParse(parse2(raw));
|
|
7444
|
-
if (!result.success) {
|
|
7400
|
+
const readResult = readTaskFile(projectRoot, phaseId);
|
|
7401
|
+
if ("error" in readResult) {
|
|
7402
|
+
if (readResult.error === "not-found") {
|
|
7403
|
+
return { success: true };
|
|
7404
|
+
}
|
|
7445
7405
|
return {
|
|
7446
7406
|
success: false,
|
|
7447
7407
|
warning: `Could not parse ${phaseId}.yaml \u2014 task may reappear after reindex`
|
|
7448
7408
|
};
|
|
7449
7409
|
}
|
|
7450
|
-
const taskFile =
|
|
7410
|
+
const { data: taskFile, path: taskPath } = readResult;
|
|
7451
7411
|
const before = taskFile.tasks.length;
|
|
7452
7412
|
taskFile.tasks = taskFile.tasks.filter((t) => t.id !== taskId);
|
|
7453
7413
|
if (taskFile.tasks.length === before) {
|
|
@@ -7464,19 +7424,14 @@ function deleteTaskFromYaml(projectRoot, phaseId, taskId) {
|
|
|
7464
7424
|
}
|
|
7465
7425
|
function deletePhaseFromYaml(projectRoot, phaseId) {
|
|
7466
7426
|
try {
|
|
7467
|
-
const
|
|
7468
|
-
if (
|
|
7469
|
-
return { success: false, warning: "phases.yaml not found" };
|
|
7470
|
-
}
|
|
7471
|
-
const raw = readFileSync8(phasesPath, "utf-8");
|
|
7472
|
-
const result = PhasesFileSchema.safeParse(parse2(raw));
|
|
7473
|
-
if (!result.success) {
|
|
7427
|
+
const readResult = readPhasesFile(projectRoot);
|
|
7428
|
+
if ("error" in readResult) {
|
|
7474
7429
|
return {
|
|
7475
7430
|
success: false,
|
|
7476
|
-
warning: "Could not parse phases.yaml \u2014 phase may reappear after reindex"
|
|
7431
|
+
warning: readResult.error === "not-found" ? "phases.yaml not found" : "Could not parse phases.yaml \u2014 phase may reappear after reindex"
|
|
7477
7432
|
};
|
|
7478
7433
|
}
|
|
7479
|
-
const phasesFile =
|
|
7434
|
+
const { data: phasesFile, path: phasesPath } = readResult;
|
|
7480
7435
|
const before = phasesFile.phases.length;
|
|
7481
7436
|
phasesFile.phases = phasesFile.phases.filter((p) => p.id !== phaseId);
|
|
7482
7437
|
if (phasesFile.phases.length === before) {
|
|
@@ -8464,6 +8419,7 @@ export {
|
|
|
8464
8419
|
CURRENT_SCHEMA_VERSION,
|
|
8465
8420
|
PhaseSchema,
|
|
8466
8421
|
PhasesFileSchema,
|
|
8422
|
+
QUALITY_PRIORITIES_DESCRIPTION,
|
|
8467
8423
|
QualityCategorySchema,
|
|
8468
8424
|
QualityPrioritySchema,
|
|
8469
8425
|
QualityScenarioSchema,
|