@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.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 ServiceSchema = z.object({
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
- path: z.string().default("."),
6
- type: z.enum(["nextjs", "react", "fastify", "express", "hono", "dotnet", "unity"]),
7
- tsconfig: z.string().optional(),
8
- csproj: z.string().optional()
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 ArcBridgeConfigSchema = z.object({
33
+ var QualityScenariosFileSchema = z.object({
11
34
  schema_version: z.literal(1).default(1),
12
- project_name: z.string().min(1),
13
- project_type: z.enum([
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: z.array(ServiceSchema).default([]),
21
- platforms: z.array(z.enum(["claude", "copilot", "gemini", "codex"])).default(["claude"]),
22
- quality_priorities: z.array(
23
- z.enum([
24
- "security",
25
- "performance",
26
- "accessibility",
27
- "reliability",
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: z.object({
40
- test_command: z.string().min(1).default("npx vitest run").describe("Command to run tests (space-separated, no shell syntax). File paths are appended as arguments."),
41
- timeout_ms: z.number().int().min(1e3).default(6e4).describe("Timeout per test run in milliseconds")
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: z.object({
44
- ignore_paths: z.array(z.string()).default([]).describe(
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: z.object({
49
- auto_record: z.boolean().default(false).describe(
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: z.object({
54
- auto_detect_drift: z.boolean().default(true),
55
- drift_severity_threshold: z.enum(["info", "warning", "error"]).default("warning"),
56
- propose_updates_on: z.enum(["session-end", "phase-complete", "manual"]).default("phase-complete")
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 taskPath = join15(
7285
- projectRoot,
7286
- ".arcbridge",
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
- let taskFile;
7312
- if (existsSync8(taskPath)) {
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 phasesPath = join15(
7331
- projectRoot,
7332
- ".arcbridge",
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 phasesPath = join15(
7352
- projectRoot,
7353
- ".arcbridge",
7354
- "plan",
7355
- "phases.yaml"
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 = result.data;
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 taskPath = join15(
7433
- projectRoot,
7434
- ".arcbridge",
7435
- "plan",
7436
- "tasks",
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 = result.data;
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 phasesPath = join15(projectRoot, ".arcbridge", "plan", "phases.yaml");
7468
- if (!existsSync8(phasesPath)) {
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 = result.data;
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,