@arcbridge/mcp-server 0.1.3 → 0.1.5
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/README.md +4 -2
- package/dist/index.js +428 -161
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -22,7 +22,7 @@ function createContext() {
|
|
|
22
22
|
// src/tools/init-project.ts
|
|
23
23
|
import { z } from "zod";
|
|
24
24
|
import { join } from "path";
|
|
25
|
-
import { existsSync } from "fs";
|
|
25
|
+
import { existsSync, writeFileSync } from "fs";
|
|
26
26
|
import {
|
|
27
27
|
generateConfig,
|
|
28
28
|
generateArc42,
|
|
@@ -30,7 +30,8 @@ import {
|
|
|
30
30
|
generateAgentRoles,
|
|
31
31
|
generateDatabase,
|
|
32
32
|
generateSyncFiles,
|
|
33
|
-
indexProject
|
|
33
|
+
indexProject,
|
|
34
|
+
loadConfig
|
|
34
35
|
} from "@arcbridge/core";
|
|
35
36
|
import { getAdapter } from "@arcbridge/adapters";
|
|
36
37
|
function registerInitProject(server, ctx) {
|
|
@@ -39,24 +40,67 @@ function registerInitProject(server, ctx) {
|
|
|
39
40
|
"Initialize ArcBridge in a project directory. Creates .arcbridge/ with arc42 documentation, phase plan, agent roles, SQLite database, and platform-specific configs.",
|
|
40
41
|
{
|
|
41
42
|
name: z.string().min(1).describe("Project name"),
|
|
42
|
-
template: z.enum(["nextjs-app-router", "react-vite", "api-service", "dotnet-webapi"]).default("nextjs-app-router").describe(
|
|
43
|
+
template: z.enum(["nextjs-app-router", "react-vite", "api-service", "dotnet-webapi"]).default("nextjs-app-router").describe(
|
|
44
|
+
"Project template: nextjs-app-router (Next.js with App Router, SSR/SSG), react-vite (React SPA with Vite, client-only), api-service (Node.js API with Express/Fastify/Hono), dotnet-webapi (ASP.NET Core Web API, C#)"
|
|
45
|
+
),
|
|
43
46
|
features: z.array(z.enum(["auth", "database", "api"])).default([]).describe("Features to scaffold"),
|
|
44
|
-
quality_priorities: z.array(z.string()).default(["security", "performance", "accessibility"]).describe("Quality priorities in order"),
|
|
47
|
+
quality_priorities: z.array(z.string()).default(["security", "performance", "accessibility", "maintainability"]).describe("Quality priorities in order"),
|
|
45
48
|
platforms: z.array(z.string()).default(["claude"]).describe("Target platforms for agent config generation"),
|
|
46
|
-
target_dir: z.string().describe("Absolute path to the target project directory")
|
|
49
|
+
target_dir: z.string().describe("Absolute path to the target project directory"),
|
|
50
|
+
spec: z.string().optional().describe(
|
|
51
|
+
"Project specification or requirements text. Saved to .arcbridge/spec.md and referenced by agents for context. Can be a description, user stories, or any text that defines what the project should do."
|
|
52
|
+
)
|
|
47
53
|
},
|
|
48
54
|
async (params) => {
|
|
49
55
|
const targetDir = params.target_dir;
|
|
50
|
-
|
|
56
|
+
const dbExists = existsSync(join(targetDir, ".arcbridge", "index.db"));
|
|
57
|
+
const configExists = existsSync(join(targetDir, ".arcbridge", "config.yaml"));
|
|
58
|
+
if (dbExists && configExists) {
|
|
59
|
+
const { error: validationError } = loadConfig(targetDir);
|
|
60
|
+
const msg = validationError ? `ArcBridge is initialized in ${targetDir} but config has issues: ${validationError}. Use \`arcbridge_get_project_status\` to see the current state, or delete \`.arcbridge/\` to reinitialize.` : `ArcBridge is already initialized in ${targetDir}. Use \`arcbridge_get_project_status\` to see the current state, or delete \`.arcbridge/\` to reinitialize.`;
|
|
61
|
+
return {
|
|
62
|
+
content: [{ type: "text", text: msg }]
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
if (dbExists && !configExists) {
|
|
51
66
|
return {
|
|
52
67
|
content: [
|
|
53
68
|
{
|
|
54
69
|
type: "text",
|
|
55
|
-
text: `ArcBridge
|
|
70
|
+
text: `ArcBridge database exists in ${targetDir} but config.yaml is missing. Delete \`.arcbridge/\` to reinitialize, or restore config.yaml.`
|
|
56
71
|
}
|
|
57
72
|
]
|
|
58
73
|
};
|
|
59
74
|
}
|
|
75
|
+
if (configExists && !dbExists) {
|
|
76
|
+
const { config: existingConfig } = loadConfig(targetDir);
|
|
77
|
+
if (existingConfig) {
|
|
78
|
+
const recoverInput = {
|
|
79
|
+
name: existingConfig.project_name,
|
|
80
|
+
template: existingConfig.project_type,
|
|
81
|
+
features: [],
|
|
82
|
+
quality_priorities: existingConfig.quality_priorities,
|
|
83
|
+
platforms: existingConfig.platforms,
|
|
84
|
+
projectRoot: targetDir
|
|
85
|
+
};
|
|
86
|
+
const { db: recoveredDb } = generateDatabase(targetDir, recoverInput);
|
|
87
|
+
ctx.db = recoveredDb;
|
|
88
|
+
ctx.projectRoot = targetDir;
|
|
89
|
+
if (params.spec) {
|
|
90
|
+
writeFileSync(join(targetDir, ".arcbridge", "spec.md"), params.spec, "utf-8");
|
|
91
|
+
}
|
|
92
|
+
return {
|
|
93
|
+
content: [
|
|
94
|
+
{
|
|
95
|
+
type: "text",
|
|
96
|
+
text: `ArcBridge database recovered from existing config in ${targetDir}. Your arc42 docs and plans were preserved.
|
|
97
|
+
|
|
98
|
+
Use \`arcbridge_get_project_status\` to see the current state.`
|
|
99
|
+
}
|
|
100
|
+
]
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
}
|
|
60
104
|
const input = {
|
|
61
105
|
name: params.name,
|
|
62
106
|
template: params.template,
|
|
@@ -83,6 +127,13 @@ function registerInitProject(server, ctx) {
|
|
|
83
127
|
platformWarnings.push(`Platform '${platform}': ${msg}`);
|
|
84
128
|
}
|
|
85
129
|
}
|
|
130
|
+
if (params.spec) {
|
|
131
|
+
writeFileSync(
|
|
132
|
+
join(targetDir, ".arcbridge", "spec.md"),
|
|
133
|
+
params.spec,
|
|
134
|
+
"utf-8"
|
|
135
|
+
);
|
|
136
|
+
}
|
|
86
137
|
let indexResult = null;
|
|
87
138
|
try {
|
|
88
139
|
const result = await indexProject(db, { projectRoot: targetDir });
|
|
@@ -105,6 +156,7 @@ function registerInitProject(server, ctx) {
|
|
|
105
156
|
`**Template:** ${input.template}`,
|
|
106
157
|
`**Features:** ${input.features.length > 0 ? input.features.join(", ") : "none"}`,
|
|
107
158
|
`**Platforms:** ${params.platforms.join(", ")}`,
|
|
159
|
+
...params.spec ? [`**Spec:** saved to .arcbridge/spec.md`] : [],
|
|
108
160
|
"",
|
|
109
161
|
"## Created",
|
|
110
162
|
"",
|
|
@@ -137,6 +189,16 @@ function registerInitProject(server, ctx) {
|
|
|
137
189
|
...allWarnings.map((w) => `- ${w}`)
|
|
138
190
|
] : [],
|
|
139
191
|
"",
|
|
192
|
+
"## Next Steps \u2014 PLAN FIRST, BUILD SECOND",
|
|
193
|
+
"",
|
|
194
|
+
"**Do not start implementing yet.** First plan the full project roadmap:",
|
|
195
|
+
"",
|
|
196
|
+
"1. **Activate the architect role** \u2014 run `arcbridge_activate_role` with role `architect`",
|
|
197
|
+
"2. **Review the spec** \u2014 read `.arcbridge/spec.md` (if provided) and understand the full scope",
|
|
198
|
+
"3. **Review and adapt the phase plan** \u2014 run `arcbridge_get_phase_plan`. The 4 generated phases are a starting template. For larger projects, add more phases by editing `.arcbridge/plan/phases.yaml` and running `arcbridge_reindex`.",
|
|
199
|
+
"4. **Plan real tasks for ALL phases** \u2014 Phase 0-1 tasks are ready to use. Phase 2+ tasks are examples only \u2014 replace them with real tasks from the project's requirements using `arcbridge_create_task`.",
|
|
200
|
+
"5. **Only then start building** \u2014 use `arcbridge_get_current_tasks` to see what to do next",
|
|
201
|
+
"",
|
|
140
202
|
"Use `arcbridge_get_project_status` to see the full project status."
|
|
141
203
|
];
|
|
142
204
|
return {
|
|
@@ -637,6 +699,7 @@ function registerGetPhasePlan(server, ctx) {
|
|
|
637
699
|
lines.push(
|
|
638
700
|
`## ${icon} Phase ${phase.phase_number}: ${phase.name}`,
|
|
639
701
|
"",
|
|
702
|
+
`**ID:** \`${phase.id}\``,
|
|
640
703
|
`**Status:** ${phase.status}`,
|
|
641
704
|
`**Description:** ${phase.description}`
|
|
642
705
|
);
|
|
@@ -757,6 +820,23 @@ function registerGetCurrentTasks(server, ctx) {
|
|
|
757
820
|
lines.push("");
|
|
758
821
|
}
|
|
759
822
|
}
|
|
823
|
+
const emptyFuturePhases = db.prepare(`
|
|
824
|
+
SELECT p.id, p.name, p.phase_number FROM phases p
|
|
825
|
+
WHERE p.status IN ('planned', 'in-progress')
|
|
826
|
+
AND p.id != ?
|
|
827
|
+
AND NOT EXISTS (SELECT 1 FROM tasks t WHERE t.phase_id = p.id)
|
|
828
|
+
ORDER BY p.phase_number
|
|
829
|
+
`).all(currentPhase.id);
|
|
830
|
+
if (emptyFuturePhases.length > 0) {
|
|
831
|
+
lines.push(
|
|
832
|
+
"---",
|
|
833
|
+
"",
|
|
834
|
+
"**Warning:** The following phases have no tasks yet:",
|
|
835
|
+
...emptyFuturePhases.map((p) => `- Phase ${p.phase_number}: ${p.name} (\`${p.id}\`)`),
|
|
836
|
+
"",
|
|
837
|
+
"Use `arcbridge_create_task` to plan tasks before reaching these phases."
|
|
838
|
+
);
|
|
839
|
+
}
|
|
760
840
|
return {
|
|
761
841
|
content: [{ type: "text", text: lines.join("\n") }]
|
|
762
842
|
};
|
|
@@ -769,7 +849,7 @@ import { z as z8 } from "zod";
|
|
|
769
849
|
import { syncTaskToYaml } from "@arcbridge/core";
|
|
770
850
|
|
|
771
851
|
// src/auto-record.ts
|
|
772
|
-
import { loadConfig, insertActivity } from "@arcbridge/core";
|
|
852
|
+
import { loadConfig as loadConfig2, insertActivity } from "@arcbridge/core";
|
|
773
853
|
var configCache = /* @__PURE__ */ new Map();
|
|
774
854
|
var CACHE_TTL_MS = 3e4;
|
|
775
855
|
function isAutoRecordEnabled(projectRoot) {
|
|
@@ -777,7 +857,7 @@ function isAutoRecordEnabled(projectRoot) {
|
|
|
777
857
|
if (cached && Date.now() - cached.loadedAt < CACHE_TTL_MS) {
|
|
778
858
|
return cached.autoRecord;
|
|
779
859
|
}
|
|
780
|
-
const { config } =
|
|
860
|
+
const { config } = loadConfig2(projectRoot);
|
|
781
861
|
const autoRecord2 = config?.metrics?.auto_record ?? false;
|
|
782
862
|
configCache.set(projectRoot, { autoRecord: autoRecord2, loadedAt: Date.now() });
|
|
783
863
|
return autoRecord2;
|
|
@@ -810,7 +890,7 @@ function registerUpdateTask(server, ctx) {
|
|
|
810
890
|
{
|
|
811
891
|
target_dir: z8.string().describe("Absolute path to the project directory"),
|
|
812
892
|
task_id: z8.string().describe("Task ID (e.g., 'task-0.1-init-nextjs')"),
|
|
813
|
-
status: z8.enum(["in-progress", "done", "blocked"]).describe("New status"),
|
|
893
|
+
status: z8.enum(["in-progress", "done", "blocked", "cancelled"]).describe("New status. Use 'cancelled' for tasks that are no longer relevant."),
|
|
814
894
|
notes: z8.string().optional().describe("Optional notes about the status change")
|
|
815
895
|
},
|
|
816
896
|
async (params) => {
|
|
@@ -835,10 +915,9 @@ function registerUpdateTask(server, ctx) {
|
|
|
835
915
|
"UPDATE tasks SET status = ?, completed_at = ? WHERE id = ?"
|
|
836
916
|
).run(params.status, now, params.task_id);
|
|
837
917
|
} else {
|
|
838
|
-
db.prepare(
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
);
|
|
918
|
+
db.prepare(
|
|
919
|
+
"UPDATE tasks SET status = ?, completed_at = NULL WHERE id = ?"
|
|
920
|
+
).run(params.status, params.task_id);
|
|
842
921
|
}
|
|
843
922
|
syncTaskToYaml(
|
|
844
923
|
params.target_dir,
|
|
@@ -855,15 +934,15 @@ function registerUpdateTask(server, ctx) {
|
|
|
855
934
|
if (params.notes) {
|
|
856
935
|
lines.push("", `**Notes:** ${params.notes}`);
|
|
857
936
|
}
|
|
858
|
-
if (params.status === "done") {
|
|
937
|
+
if (params.status === "done" || params.status === "cancelled") {
|
|
859
938
|
const phaseStats = db.prepare(
|
|
860
|
-
"SELECT
|
|
939
|
+
"SELECT SUM(CASE WHEN status != 'cancelled' THEN 1 ELSE 0 END) as total, SUM(CASE WHEN status = 'done' THEN 1 ELSE 0 END) as done FROM tasks WHERE phase_id = ?"
|
|
861
940
|
).get(task.phase_id);
|
|
862
941
|
lines.push(
|
|
863
942
|
"",
|
|
864
943
|
`**Phase progress:** ${phaseStats.done}/${phaseStats.total} tasks complete`
|
|
865
944
|
);
|
|
866
|
-
if (phaseStats.done === phaseStats.total) {
|
|
945
|
+
if (phaseStats.total > 0 && phaseStats.done === phaseStats.total) {
|
|
867
946
|
lines.push(
|
|
868
947
|
"",
|
|
869
948
|
"All tasks in this phase are complete! The phase is ready to advance."
|
|
@@ -895,7 +974,9 @@ function registerCreateTask(server, ctx) {
|
|
|
895
974
|
target_dir: z9.string().describe("Absolute path to the project directory"),
|
|
896
975
|
phase_id: z9.string().describe("Phase ID to add the task to"),
|
|
897
976
|
title: z9.string().min(1).describe("Task title"),
|
|
898
|
-
building_block: z9.string().optional().describe(
|
|
977
|
+
building_block: z9.string().min(1).optional().describe(
|
|
978
|
+
"Building block this task belongs to. Use `arcbridge_get_building_blocks` to see available blocks. If no suitable block exists, create one in `.arcbridge/arc42/05-building-blocks.md` and run `arcbridge_reindex` first."
|
|
979
|
+
),
|
|
899
980
|
quality_scenarios: z9.array(z9.string()).default([]).describe("Quality scenario IDs this task addresses"),
|
|
900
981
|
acceptance_criteria: z9.array(z9.string()).default([]).describe("Acceptance criteria for this task")
|
|
901
982
|
},
|
|
@@ -920,6 +1001,27 @@ function registerCreateTask(server, ctx) {
|
|
|
920
1001
|
const slug = params.title.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "").slice(0, 30);
|
|
921
1002
|
const taskId = `task-${phase.phase_number}.${taskNum}-${slug}`;
|
|
922
1003
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
1004
|
+
const blockId = params.building_block ?? null;
|
|
1005
|
+
if (blockId) {
|
|
1006
|
+
const block = db.prepare("SELECT id FROM building_blocks WHERE id = ?").get(blockId);
|
|
1007
|
+
if (!block) {
|
|
1008
|
+
const available = db.prepare("SELECT id, name FROM building_blocks ORDER BY id").all();
|
|
1009
|
+
const blockList = available.length > 0 ? available.map((b) => ` - \`${b.id}\` (${b.name})`).join("\n") : " (none \u2014 run `arcbridge_reindex` to populate from arc42 docs)";
|
|
1010
|
+
return {
|
|
1011
|
+
content: [
|
|
1012
|
+
{
|
|
1013
|
+
type: "text",
|
|
1014
|
+
text: `Building block \`${blockId}\` not found.
|
|
1015
|
+
|
|
1016
|
+
**Available blocks:**
|
|
1017
|
+
${blockList}
|
|
1018
|
+
|
|
1019
|
+
If you need a new block, add it to \`.arcbridge/arc42/05-building-blocks.md\` and run \`arcbridge_reindex\`, then retry.`
|
|
1020
|
+
}
|
|
1021
|
+
]
|
|
1022
|
+
};
|
|
1023
|
+
}
|
|
1024
|
+
}
|
|
923
1025
|
db.prepare(
|
|
924
1026
|
"INSERT INTO tasks (id, phase_id, title, description, status, building_block, quality_scenarios, acceptance_criteria, created_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)"
|
|
925
1027
|
).run(
|
|
@@ -928,7 +1030,7 @@ function registerCreateTask(server, ctx) {
|
|
|
928
1030
|
params.title,
|
|
929
1031
|
null,
|
|
930
1032
|
"todo",
|
|
931
|
-
|
|
1033
|
+
blockId,
|
|
932
1034
|
JSON.stringify(params.quality_scenarios),
|
|
933
1035
|
JSON.stringify(params.acceptance_criteria),
|
|
934
1036
|
now
|
|
@@ -945,7 +1047,7 @@ function registerCreateTask(server, ctx) {
|
|
|
945
1047
|
`Task created: **${taskId}**`,
|
|
946
1048
|
"",
|
|
947
1049
|
`**Title:** ${params.title}`,
|
|
948
|
-
`**Phase:** ${phase.name}`,
|
|
1050
|
+
`**Phase:** ${phase.name} (\`${phase.id}\`)`,
|
|
949
1051
|
`**Status:** todo`
|
|
950
1052
|
];
|
|
951
1053
|
if (params.building_block) {
|
|
@@ -969,16 +1071,49 @@ function registerCreateTask(server, ctx) {
|
|
|
969
1071
|
);
|
|
970
1072
|
}
|
|
971
1073
|
|
|
972
|
-
// src/tools/
|
|
1074
|
+
// src/tools/delete-task.ts
|
|
973
1075
|
import { z as z10 } from "zod";
|
|
1076
|
+
import { deleteTaskFromYaml } from "@arcbridge/core";
|
|
1077
|
+
function registerDeleteTask(server, ctx) {
|
|
1078
|
+
server.tool(
|
|
1079
|
+
"arcbridge_delete_task",
|
|
1080
|
+
"Delete a task permanently. Use this to remove example/template tasks or duplicates. For tasks that were planned but are no longer relevant, prefer `arcbridge_update_task` with status 'cancelled' instead \u2014 this preserves the decision trail.",
|
|
1081
|
+
{
|
|
1082
|
+
target_dir: z10.string().describe("Absolute path to the project directory"),
|
|
1083
|
+
task_id: z10.string().describe("Task ID to delete")
|
|
1084
|
+
},
|
|
1085
|
+
async (params) => {
|
|
1086
|
+
const db = ensureDb(ctx, params.target_dir);
|
|
1087
|
+
if (!db) return notInitialized();
|
|
1088
|
+
const task = db.prepare("SELECT id, title, phase_id FROM tasks WHERE id = ?").get(params.task_id);
|
|
1089
|
+
if (!task) {
|
|
1090
|
+
return textResult(
|
|
1091
|
+
`Task '${params.task_id}' not found. Use \`arcbridge_get_current_tasks\` to see available tasks.`
|
|
1092
|
+
);
|
|
1093
|
+
}
|
|
1094
|
+
const yamlResult = deleteTaskFromYaml(params.target_dir, task.phase_id, params.task_id);
|
|
1095
|
+
db.prepare("DELETE FROM tasks WHERE id = ?").run(params.task_id);
|
|
1096
|
+
const msg = `Task **${task.id}** deleted: "${task.title}"`;
|
|
1097
|
+
if (yamlResult.warning) {
|
|
1098
|
+
return textResult(`${msg}
|
|
1099
|
+
|
|
1100
|
+
**Warning:** ${yamlResult.warning}`);
|
|
1101
|
+
}
|
|
1102
|
+
return textResult(msg);
|
|
1103
|
+
}
|
|
1104
|
+
);
|
|
1105
|
+
}
|
|
1106
|
+
|
|
1107
|
+
// src/tools/get-relevant-adrs.ts
|
|
1108
|
+
import { z as z11 } from "zod";
|
|
974
1109
|
function registerGetRelevantAdrs(server, ctx) {
|
|
975
1110
|
server.tool(
|
|
976
1111
|
"arcbridge_get_relevant_adrs",
|
|
977
1112
|
"Get architectural decision records (ADRs) relevant to a specific file path or building block.",
|
|
978
1113
|
{
|
|
979
|
-
target_dir:
|
|
980
|
-
file_path:
|
|
981
|
-
building_block:
|
|
1114
|
+
target_dir: z11.string().describe("Absolute path to the project directory"),
|
|
1115
|
+
file_path: z11.string().optional().describe("File path to find relevant ADRs for"),
|
|
1116
|
+
building_block: z11.string().optional().describe("Building block ID to find relevant ADRs for")
|
|
982
1117
|
},
|
|
983
1118
|
async (params) => {
|
|
984
1119
|
const db = ensureDb(ctx, params.target_dir);
|
|
@@ -1062,17 +1197,17 @@ function formatAdrs(adrs, title) {
|
|
|
1062
1197
|
}
|
|
1063
1198
|
|
|
1064
1199
|
// src/tools/reindex.ts
|
|
1065
|
-
import { z as
|
|
1200
|
+
import { z as z12 } from "zod";
|
|
1066
1201
|
import { indexProject as indexProject2, refreshFromDocs as refreshFromDocs4 } from "@arcbridge/core";
|
|
1067
1202
|
function registerReindex(server, ctx) {
|
|
1068
1203
|
server.tool(
|
|
1069
1204
|
"arcbridge_reindex",
|
|
1070
1205
|
"Re-index the project: refreshes architecture docs from arc42/YAML files, then reindexes code symbols (TypeScript & C#/.NET). This is the first step of the sync pipeline \u2014 use it to pick up manual doc edits and code changes.",
|
|
1071
1206
|
{
|
|
1072
|
-
target_dir:
|
|
1073
|
-
tsconfig_path:
|
|
1074
|
-
service:
|
|
1075
|
-
language:
|
|
1207
|
+
target_dir: z12.string().describe("Absolute path to the project directory"),
|
|
1208
|
+
tsconfig_path: z12.string().optional().describe("Override tsconfig.json path (default: auto-detect). Only used for TypeScript projects."),
|
|
1209
|
+
service: z12.string().optional().describe("Service name for monorepo projects (default: 'main')"),
|
|
1210
|
+
language: z12.enum(["typescript", "csharp", "auto"]).optional().describe("Project language. 'auto' detects from project files (default: 'auto')")
|
|
1076
1211
|
},
|
|
1077
1212
|
async (params) => {
|
|
1078
1213
|
const start = Date.now();
|
|
@@ -1087,9 +1222,13 @@ function registerReindex(server, ctx) {
|
|
|
1087
1222
|
language: params.language
|
|
1088
1223
|
});
|
|
1089
1224
|
const lines = [
|
|
1090
|
-
"#
|
|
1225
|
+
"# Reindex Complete",
|
|
1226
|
+
"",
|
|
1227
|
+
"## Architecture Docs",
|
|
1228
|
+
`- **Refreshed from docs:** building blocks, phases, tasks, quality scenarios, ADRs`,
|
|
1229
|
+
...docWarnings.length > 0 ? [`- **Warnings:** ${docWarnings.join("; ")}`] : [],
|
|
1091
1230
|
"",
|
|
1092
|
-
|
|
1231
|
+
"## Code Symbols",
|
|
1093
1232
|
`- **Files processed:** ${result.filesProcessed}`,
|
|
1094
1233
|
`- **Files skipped (unchanged):** ${result.filesSkipped}`,
|
|
1095
1234
|
`- **Files removed:** ${result.filesRemoved}`,
|
|
@@ -1114,16 +1253,16 @@ function registerReindex(server, ctx) {
|
|
|
1114
1253
|
}
|
|
1115
1254
|
|
|
1116
1255
|
// src/tools/search-symbols.ts
|
|
1117
|
-
import { z as
|
|
1256
|
+
import { z as z13 } from "zod";
|
|
1118
1257
|
function registerSearchSymbols(server, ctx) {
|
|
1119
1258
|
server.tool(
|
|
1120
1259
|
"arcbridge_search_symbols",
|
|
1121
1260
|
"Search code symbols by name, kind, file path, or building block. Supports TypeScript and C#. Returns matching symbols with type signatures.",
|
|
1122
1261
|
{
|
|
1123
|
-
target_dir:
|
|
1124
|
-
query:
|
|
1125
|
-
service:
|
|
1126
|
-
kind:
|
|
1262
|
+
target_dir: z13.string().describe("Absolute path to the project directory"),
|
|
1263
|
+
query: z13.string().optional().describe("Search term to match against symbol names"),
|
|
1264
|
+
service: z13.string().optional().describe("Filter by service name (for multi-project solutions). Omit to search all services."),
|
|
1265
|
+
kind: z13.enum([
|
|
1127
1266
|
"function",
|
|
1128
1267
|
"class",
|
|
1129
1268
|
"type",
|
|
@@ -1135,10 +1274,10 @@ function registerSearchSymbols(server, ctx) {
|
|
|
1135
1274
|
"hook",
|
|
1136
1275
|
"context"
|
|
1137
1276
|
]).optional().describe("Filter by symbol kind"),
|
|
1138
|
-
file_path:
|
|
1139
|
-
is_exported:
|
|
1140
|
-
building_block:
|
|
1141
|
-
limit:
|
|
1277
|
+
file_path: z13.string().optional().describe("Filter by file path (prefix match)"),
|
|
1278
|
+
is_exported: z13.boolean().optional().describe("Filter by export status"),
|
|
1279
|
+
building_block: z13.string().optional().describe("Filter by building block ID (matches against code_paths)"),
|
|
1280
|
+
limit: z13.number().int().min(1).max(200).default(50).describe("Maximum results to return (default: 50)")
|
|
1142
1281
|
},
|
|
1143
1282
|
async (params) => {
|
|
1144
1283
|
const db = ensureDb(ctx, params.target_dir);
|
|
@@ -1225,7 +1364,7 @@ function registerSearchSymbols(server, ctx) {
|
|
|
1225
1364
|
}
|
|
1226
1365
|
|
|
1227
1366
|
// src/tools/get-symbol.ts
|
|
1228
|
-
import { z as
|
|
1367
|
+
import { z as z14 } from "zod";
|
|
1229
1368
|
import { readFileSync, existsSync as existsSync3 } from "fs";
|
|
1230
1369
|
import { join as join3 } from "path";
|
|
1231
1370
|
function registerGetSymbol(server, ctx) {
|
|
@@ -1233,11 +1372,11 @@ function registerGetSymbol(server, ctx) {
|
|
|
1233
1372
|
"arcbridge_get_symbol",
|
|
1234
1373
|
"Get detailed information about a specific TypeScript symbol including its source code, type signature, and relationships.",
|
|
1235
1374
|
{
|
|
1236
|
-
target_dir:
|
|
1237
|
-
symbol_id:
|
|
1375
|
+
target_dir: z14.string().describe("Absolute path to the project directory"),
|
|
1376
|
+
symbol_id: z14.string().describe(
|
|
1238
1377
|
"Symbol ID (e.g. 'src/utils.ts::formatName#function')"
|
|
1239
1378
|
),
|
|
1240
|
-
include_source:
|
|
1379
|
+
include_source: z14.boolean().default(true).describe("Include source code snippet (default: true)")
|
|
1241
1380
|
},
|
|
1242
1381
|
async (params) => {
|
|
1243
1382
|
const db = ensureDb(ctx, params.target_dir);
|
|
@@ -1335,21 +1474,21 @@ Use \`arcbridge_search_symbols\` to find symbols by name.`
|
|
|
1335
1474
|
}
|
|
1336
1475
|
|
|
1337
1476
|
// src/tools/get-dependency-graph.ts
|
|
1338
|
-
import { z as
|
|
1477
|
+
import { z as z15 } from "zod";
|
|
1339
1478
|
function registerGetDependencyGraph(server, ctx) {
|
|
1340
1479
|
server.tool(
|
|
1341
1480
|
"arcbridge_get_dependency_graph",
|
|
1342
1481
|
"Get the dependency graph for a module or file. Shows imports, calls, type usage, and inheritance relationships between symbols.",
|
|
1343
1482
|
{
|
|
1344
|
-
target_dir:
|
|
1345
|
-
module:
|
|
1483
|
+
target_dir: z15.string().describe("Absolute path to the project directory"),
|
|
1484
|
+
module: z15.string().describe(
|
|
1346
1485
|
"Module path relative to project root (e.g. 'src/lib/auth')"
|
|
1347
1486
|
),
|
|
1348
|
-
direction:
|
|
1487
|
+
direction: z15.enum(["dependencies", "dependents", "both"]).default("both").describe(
|
|
1349
1488
|
"Graph direction: 'dependencies' (what this module uses), 'dependents' (what uses this module), or 'both'"
|
|
1350
1489
|
),
|
|
1351
|
-
depth:
|
|
1352
|
-
service:
|
|
1490
|
+
depth: z15.number().int().min(1).max(5).default(1).describe("How many levels to traverse (default: 1, max: 5)"),
|
|
1491
|
+
service: z15.string().optional().describe("Filter by service name (for multi-project solutions). Omit to search all services.")
|
|
1353
1492
|
},
|
|
1354
1493
|
async (params) => {
|
|
1355
1494
|
const maybeDb = ensureDb(ctx, params.target_dir);
|
|
@@ -1496,16 +1635,16 @@ function formatEdges(edges, modulePath, direction) {
|
|
|
1496
1635
|
}
|
|
1497
1636
|
|
|
1498
1637
|
// src/tools/get-component-graph.ts
|
|
1499
|
-
import { z as
|
|
1638
|
+
import { z as z16 } from "zod";
|
|
1500
1639
|
function registerGetComponentGraph(server, ctx) {
|
|
1501
1640
|
server.tool(
|
|
1502
1641
|
"arcbridge_get_component_graph",
|
|
1503
1642
|
"Get the React component graph: component hierarchy, props, state, context usage, and server/client boundaries.",
|
|
1504
1643
|
{
|
|
1505
|
-
target_dir:
|
|
1506
|
-
file_path:
|
|
1507
|
-
client_only:
|
|
1508
|
-
with_state:
|
|
1644
|
+
target_dir: z16.string().describe("Absolute path to the project directory"),
|
|
1645
|
+
file_path: z16.string().optional().describe("Filter to components in a specific file or directory prefix"),
|
|
1646
|
+
client_only: z16.boolean().optional().describe("Only show client components ('use client')"),
|
|
1647
|
+
with_state: z16.boolean().optional().describe("Only show components that use state (useState/useReducer)")
|
|
1509
1648
|
},
|
|
1510
1649
|
async (params) => {
|
|
1511
1650
|
const db = ensureDb(ctx, params.target_dir);
|
|
@@ -1609,16 +1748,16 @@ function registerGetComponentGraph(server, ctx) {
|
|
|
1609
1748
|
}
|
|
1610
1749
|
|
|
1611
1750
|
// src/tools/get-route-map.ts
|
|
1612
|
-
import { z as
|
|
1751
|
+
import { z as z17 } from "zod";
|
|
1613
1752
|
function registerGetRouteMap(server, ctx) {
|
|
1614
1753
|
server.tool(
|
|
1615
1754
|
"arcbridge_get_route_map",
|
|
1616
1755
|
"Get the route map: pages, layouts, API routes, and their hierarchy. Works with Next.js, ASP.NET controllers, and minimal APIs.",
|
|
1617
1756
|
{
|
|
1618
|
-
target_dir:
|
|
1619
|
-
kind:
|
|
1620
|
-
route_prefix:
|
|
1621
|
-
service:
|
|
1757
|
+
target_dir: z17.string().describe("Absolute path to the project directory"),
|
|
1758
|
+
kind: z17.enum(["page", "layout", "loading", "error", "not-found", "api-route", "middleware"]).optional().describe("Filter by route kind"),
|
|
1759
|
+
route_prefix: z17.string().optional().describe("Filter by route path prefix (e.g. '/dashboard' or '/api/orders')"),
|
|
1760
|
+
service: z17.string().optional().describe("Filter by service name (for multi-project solutions). Omit to show all services.")
|
|
1622
1761
|
},
|
|
1623
1762
|
async (params) => {
|
|
1624
1763
|
const db = ensureDb(ctx, params.target_dir);
|
|
@@ -1694,13 +1833,13 @@ function registerGetRouteMap(server, ctx) {
|
|
|
1694
1833
|
}
|
|
1695
1834
|
|
|
1696
1835
|
// src/tools/get-boundary-analysis.ts
|
|
1697
|
-
import { z as
|
|
1836
|
+
import { z as z18 } from "zod";
|
|
1698
1837
|
function registerGetBoundaryAnalysis(server, ctx) {
|
|
1699
1838
|
server.tool(
|
|
1700
1839
|
"arcbridge_get_boundary_analysis",
|
|
1701
1840
|
"Analyze server/client boundaries in a Next.js project. Identifies client components, server components, server actions, and potential boundary violations.",
|
|
1702
1841
|
{
|
|
1703
|
-
target_dir:
|
|
1842
|
+
target_dir: z18.string().describe("Absolute path to the project directory")
|
|
1704
1843
|
},
|
|
1705
1844
|
async (params) => {
|
|
1706
1845
|
const db = ensureDb(ctx, params.target_dir);
|
|
@@ -1804,15 +1943,15 @@ function registerGetBoundaryAnalysis(server, ctx) {
|
|
|
1804
1943
|
}
|
|
1805
1944
|
|
|
1806
1945
|
// src/tools/check-drift.ts
|
|
1807
|
-
import { z as
|
|
1946
|
+
import { z as z19 } from "zod";
|
|
1808
1947
|
import { detectDrift, writeDriftLog } from "@arcbridge/core";
|
|
1809
1948
|
function registerCheckDrift(server, ctx) {
|
|
1810
1949
|
server.tool(
|
|
1811
1950
|
"arcbridge_check_drift",
|
|
1812
1951
|
"Detect architecture drift: undocumented modules, missing code paths, cross-block dependency violations, and stale ADR references.",
|
|
1813
1952
|
{
|
|
1814
|
-
target_dir:
|
|
1815
|
-
persist:
|
|
1953
|
+
target_dir: z19.string().describe("Absolute path to the project directory"),
|
|
1954
|
+
persist: z19.boolean().default(true).describe("Write findings to drift_log table (default: true)")
|
|
1816
1955
|
},
|
|
1817
1956
|
async (params) => {
|
|
1818
1957
|
const start = Date.now();
|
|
@@ -1884,15 +2023,16 @@ function registerCheckDrift(server, ctx) {
|
|
|
1884
2023
|
}
|
|
1885
2024
|
|
|
1886
2025
|
// src/tools/get-guidance.ts
|
|
1887
|
-
import { z as
|
|
2026
|
+
import { z as z20 } from "zod";
|
|
2027
|
+
import { loadConfig as loadConfig3 } from "@arcbridge/core";
|
|
1888
2028
|
function registerGetGuidance(server, ctx) {
|
|
1889
2029
|
server.tool(
|
|
1890
2030
|
"arcbridge_get_guidance",
|
|
1891
2031
|
"Get context-aware architectural guidance for a code change. Surfaces relevant quality scenarios, patterns, constraints, and questions to consider.",
|
|
1892
2032
|
{
|
|
1893
|
-
target_dir:
|
|
1894
|
-
file_path:
|
|
1895
|
-
action:
|
|
2033
|
+
target_dir: z20.string().describe("Absolute path to the project directory"),
|
|
2034
|
+
file_path: z20.string().optional().describe("File path you're working on (to determine building block and context)"),
|
|
2035
|
+
action: z20.enum([
|
|
1896
2036
|
"adding-component",
|
|
1897
2037
|
"adding-api-route",
|
|
1898
2038
|
"adding-hook",
|
|
@@ -2022,7 +2162,10 @@ function registerGetGuidance(server, ctx) {
|
|
|
2022
2162
|
}
|
|
2023
2163
|
lines.push("");
|
|
2024
2164
|
}
|
|
2025
|
-
const
|
|
2165
|
+
const projectRoot = ctx.projectRoot ?? params.target_dir;
|
|
2166
|
+
const { config: projConfig, error: configError } = loadConfig3(projectRoot);
|
|
2167
|
+
const projectType = configError ? "unknown" : projConfig?.project_type ?? "nextjs-app-router";
|
|
2168
|
+
const actionGuidance = getActionGuidance(params.action, projectType);
|
|
2026
2169
|
if (actionGuidance) {
|
|
2027
2170
|
lines.push("## Guidance", "", actionGuidance, "");
|
|
2028
2171
|
}
|
|
@@ -2046,28 +2189,55 @@ function filterRelevantScenarios(scenarios, action, _blockId) {
|
|
|
2046
2189
|
}
|
|
2047
2190
|
return scenarios.filter((s) => relevantCategories.includes(s.category));
|
|
2048
2191
|
}
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
|
|
2059
|
-
|
|
2192
|
+
var FRONTEND_GUIDANCE = {
|
|
2193
|
+
"adding-component": "- Follow existing component patterns in this directory\n- Add props interface alongside the component\n- Consider server vs. client: does this need interactivity (`'use client'`)?\n- Check accessibility: keyboard navigation, ARIA labels, screen reader support\n- **Arc42:** If this introduces a new UI pattern, document it in `08-crosscutting.md`",
|
|
2194
|
+
"adding-api-route": "- Ensure authentication middleware covers this route\n- Validate all input with zod or equivalent\n- Follow existing error response patterns\n- Consider rate limiting for public endpoints\n- **Arc42:** Update `03-context.md` if this exposes a new external integration",
|
|
2195
|
+
"adding-hook": "- Follow the `use` prefix convention\n- Keep hooks focused \u2014 one responsibility per hook\n- Consider memoization for expensive computations\n- Document the hook's return type",
|
|
2196
|
+
"modifying-auth": "- Check all API routes still have auth coverage after changes\n- Verify no secrets leak to client components\n- Test edge cases: expired tokens, revoked sessions, role changes\n- **Arc42:** Update `08-crosscutting.md` with the auth pattern",
|
|
2197
|
+
"new-dependency": "- Document the dependency rationale in an ADR\n- Check bundle size impact (client-side deps)\n- Verify no known CVEs\n- **Arc42:** If this introduces a new external system, update `03-context.md`"
|
|
2198
|
+
};
|
|
2199
|
+
var DOTNET_GUIDANCE = {
|
|
2200
|
+
"adding-component": "- Follow the existing service/repository pattern\n- Register the new class in DI (Program.cs or extension method)\n- Add an interface if the component needs to be mockable\n- **Arc42:** Update `05-building-blocks.md` if this is a new architectural layer",
|
|
2201
|
+
"adding-api-route": "- Use `[HttpGet]`, `[HttpPost]`, etc. attributes for controller routes, or `MapGet`/`MapPost` for minimal APIs\n- Apply `[Authorize]` for protected endpoints\n- Validate input with data annotations or FluentValidation\n- Follow existing error response patterns (ProblemDetails)\n- **Arc42:** Update `03-context.md` if this exposes new external integrations; update `06-runtime-views.md` for key workflows",
|
|
2202
|
+
"adding-hook": "- .NET equivalent: create a middleware, filter, or hosted service\n- Register in the DI container\n- Follow the single responsibility principle",
|
|
2203
|
+
"modifying-auth": "- Check `[Authorize]` coverage on all controllers/endpoints\n- Verify JWT validation, claims, and policy configuration\n- Test edge cases: expired tokens, revoked sessions, role changes\n- **Arc42:** Update `08-crosscutting.md` with the auth pattern",
|
|
2204
|
+
"new-dependency": "- Document the NuGet package rationale in an ADR\n- Check for known vulnerabilities with `dotnet list package --vulnerable`\n- Verify license compatibility\n- **Arc42:** If this introduces a new external system, update `03-context.md`"
|
|
2205
|
+
};
|
|
2206
|
+
var API_GUIDANCE = {
|
|
2207
|
+
"adding-component": "- Follow existing module/service patterns\n- Add TypeScript interfaces for public APIs\n- Register in the dependency injection or module system\n- **Arc42:** Update `05-building-blocks.md` if this is a new architectural layer",
|
|
2208
|
+
"adding-api-route": "- Ensure authentication middleware covers this route\n- Validate all input with zod or equivalent\n- Follow existing error response patterns\n- Consider rate limiting for public endpoints\n- **Arc42:** Update `03-context.md` if this exposes a new external integration",
|
|
2209
|
+
"adding-hook": "- Use middleware for cross-cutting concerns\n- Keep middleware focused \u2014 one responsibility per middleware\n- Document the middleware's purpose and order",
|
|
2210
|
+
"modifying-auth": "- Check all routes still have auth coverage\n- Verify token validation and session handling\n- Test edge cases: expired tokens, revoked sessions\n- **Arc42:** Update `08-crosscutting.md` with the auth pattern",
|
|
2211
|
+
"new-dependency": "- Document the dependency rationale in an ADR\n- Verify no known CVEs\n- Ensure license compatibility\n- **Arc42:** If this introduces a new external system, update `03-context.md`"
|
|
2212
|
+
};
|
|
2213
|
+
var SHARED_GUIDANCE = {
|
|
2214
|
+
"refactoring": "- Ensure no cross-block boundary violations are introduced\n- Maintain existing public API contracts\n- Run tests before and after to verify behavior preservation\n- If the refactoring changes architectural patterns, update or create an ADR\n- **Arc42:** Update `05-building-blocks.md` if module structure changed; update `08-crosscutting.md` if patterns changed",
|
|
2215
|
+
"general": "- Check `arcbridge_get_relevant_adrs` for existing decisions that may constrain this change\n- If you're choosing between approaches, document the decision in an ADR\n- **Arc42:** Consider which documentation sections may need updating (check `.arcbridge/arc42/`)"
|
|
2216
|
+
};
|
|
2217
|
+
function getActionGuidance(action, projectType) {
|
|
2218
|
+
if (SHARED_GUIDANCE[action]) return SHARED_GUIDANCE[action];
|
|
2219
|
+
switch (projectType) {
|
|
2220
|
+
case "dotnet-webapi":
|
|
2221
|
+
return DOTNET_GUIDANCE[action] ?? null;
|
|
2222
|
+
case "api-service":
|
|
2223
|
+
return API_GUIDANCE[action] ?? null;
|
|
2224
|
+
case "react-vite":
|
|
2225
|
+
case "nextjs-app-router":
|
|
2226
|
+
return FRONTEND_GUIDANCE[action] ?? null;
|
|
2227
|
+
default:
|
|
2228
|
+
return null;
|
|
2229
|
+
}
|
|
2060
2230
|
}
|
|
2061
2231
|
|
|
2062
2232
|
// src/tools/get-open-questions.ts
|
|
2063
|
-
import { z as
|
|
2233
|
+
import { z as z21 } from "zod";
|
|
2064
2234
|
function registerGetOpenQuestions(server, ctx) {
|
|
2065
2235
|
server.tool(
|
|
2066
2236
|
"arcbridge_get_open_questions",
|
|
2067
2237
|
"Surface architectural gaps: untested quality scenarios, building blocks without boundaries, unresolved drift, and tasks missing acceptance criteria.",
|
|
2068
2238
|
{
|
|
2069
|
-
target_dir:
|
|
2070
|
-
scope:
|
|
2239
|
+
target_dir: z21.string().describe("Absolute path to the project directory"),
|
|
2240
|
+
scope: z21.string().optional().describe("Focus scope: 'current-phase', 'building-block:<id>', or omit for project-wide")
|
|
2071
2241
|
},
|
|
2072
2242
|
async (params) => {
|
|
2073
2243
|
const db = ensureDb(ctx, params.target_dir);
|
|
@@ -2177,7 +2347,7 @@ function registerGetOpenQuestions(server, ctx) {
|
|
|
2177
2347
|
}
|
|
2178
2348
|
|
|
2179
2349
|
// src/tools/propose-arc42-update.ts
|
|
2180
|
-
import { z as
|
|
2350
|
+
import { z as z22 } from "zod";
|
|
2181
2351
|
import {
|
|
2182
2352
|
resolveRef,
|
|
2183
2353
|
getChangedFiles,
|
|
@@ -2189,9 +2359,9 @@ function registerProposeArc42Update(server, ctx) {
|
|
|
2189
2359
|
"arcbridge_propose_arc42_update",
|
|
2190
2360
|
"Analyze code changes since a reference point and generate specific, actionable proposals for updating arc42 documentation.",
|
|
2191
2361
|
{
|
|
2192
|
-
target_dir:
|
|
2193
|
-
changes_since:
|
|
2194
|
-
update_sync_point:
|
|
2362
|
+
target_dir: z22.string().describe("Absolute path to the project directory"),
|
|
2363
|
+
changes_since: z22.string().default("last-sync").describe("Reference point: 'last-commit', 'last-sync', 'last-phase', or a git ref"),
|
|
2364
|
+
update_sync_point: z22.boolean().default(false).describe("Update the stored sync commit to HEAD after generating proposals")
|
|
2195
2365
|
},
|
|
2196
2366
|
async (params) => {
|
|
2197
2367
|
const db = ensureDb(ctx, params.target_dir);
|
|
@@ -2379,10 +2549,11 @@ function findCrossBlockConsumers(db, symbolId, sourceBlockId, blocks) {
|
|
|
2379
2549
|
}
|
|
2380
2550
|
|
|
2381
2551
|
// src/tools/get-practice-review.ts
|
|
2382
|
-
import { z as
|
|
2552
|
+
import { z as z23 } from "zod";
|
|
2383
2553
|
import {
|
|
2384
2554
|
resolveRef as resolveRef2,
|
|
2385
2555
|
getChangedFiles as getChangedFiles2,
|
|
2556
|
+
scopeToProject,
|
|
2386
2557
|
detectDrift as detectDrift2
|
|
2387
2558
|
} from "@arcbridge/core";
|
|
2388
2559
|
function registerGetPracticeReview(server, ctx) {
|
|
@@ -2390,15 +2561,16 @@ function registerGetPracticeReview(server, ctx) {
|
|
|
2390
2561
|
"arcbridge_get_practice_review",
|
|
2391
2562
|
"Structured, practice-aware review of recent code changes across 5 dimensions: Architecture, Security, Testing, Documentation, and Complexity.",
|
|
2392
2563
|
{
|
|
2393
|
-
target_dir:
|
|
2394
|
-
since:
|
|
2564
|
+
target_dir: z23.string().describe("Absolute path to the project directory"),
|
|
2565
|
+
since: z23.string().default("last-commit").describe("Reference point: 'last-commit', 'last-session', or 'last-phase'")
|
|
2395
2566
|
},
|
|
2396
2567
|
async (params) => {
|
|
2397
2568
|
const db = ensureDb(ctx, params.target_dir);
|
|
2398
2569
|
if (!db) return notInitialized();
|
|
2399
2570
|
const projectRoot = ctx.projectRoot ?? params.target_dir;
|
|
2400
2571
|
const ref = resolveRef2(projectRoot, params.since, db);
|
|
2401
|
-
const
|
|
2572
|
+
const allChangedFiles = getChangedFiles2(projectRoot, ref.sha);
|
|
2573
|
+
const changedFiles = scopeToProject(allChangedFiles, projectRoot);
|
|
2402
2574
|
if (changedFiles.length === 0) {
|
|
2403
2575
|
return textResult(
|
|
2404
2576
|
`# Practice Review
|
|
@@ -2706,7 +2878,7 @@ function reviewComplexity(db, changedFiles, findings) {
|
|
|
2706
2878
|
}
|
|
2707
2879
|
|
|
2708
2880
|
// src/tools/complete-phase.ts
|
|
2709
|
-
import { z as
|
|
2881
|
+
import { z as z24 } from "zod";
|
|
2710
2882
|
import {
|
|
2711
2883
|
detectDrift as detectDrift3,
|
|
2712
2884
|
writeDriftLog as writeDriftLog2,
|
|
@@ -2715,7 +2887,7 @@ import {
|
|
|
2715
2887
|
inferTaskStatuses,
|
|
2716
2888
|
applyInferences,
|
|
2717
2889
|
verifyScenarios,
|
|
2718
|
-
loadConfig as
|
|
2890
|
+
loadConfig as loadConfig4,
|
|
2719
2891
|
refreshFromDocs as refreshFromDocs5,
|
|
2720
2892
|
syncPhaseToYaml,
|
|
2721
2893
|
transaction
|
|
@@ -2725,11 +2897,11 @@ function registerCompletePhase(server, ctx) {
|
|
|
2725
2897
|
"arcbridge_complete_phase",
|
|
2726
2898
|
"Attempt to complete a phase by validating all gates: tasks done, no critical drift, quality scenarios passing. Transitions the phase to 'complete' if all gates pass.",
|
|
2727
2899
|
{
|
|
2728
|
-
target_dir:
|
|
2729
|
-
phase_id:
|
|
2730
|
-
notes:
|
|
2731
|
-
auto_infer:
|
|
2732
|
-
run_tests:
|
|
2900
|
+
target_dir: z24.string().describe("Absolute path to the project directory"),
|
|
2901
|
+
phase_id: z24.string().optional().describe("Phase ID to complete (defaults to current in-progress phase)"),
|
|
2902
|
+
notes: z24.string().optional().describe("Optional notes about this phase completion"),
|
|
2903
|
+
auto_infer: z24.boolean().default(true).describe("Automatically infer task statuses from code state before checking gates"),
|
|
2904
|
+
run_tests: z24.boolean().default(false).describe("Run linked tests for quality scenarios before checking the quality gate")
|
|
2733
2905
|
},
|
|
2734
2906
|
async (params) => {
|
|
2735
2907
|
const start = Date.now();
|
|
@@ -2770,7 +2942,7 @@ function registerCompletePhase(server, ctx) {
|
|
|
2770
2942
|
}
|
|
2771
2943
|
}
|
|
2772
2944
|
const tasks = db.prepare("SELECT id, title, status FROM tasks WHERE phase_id = ?").all(phase.id);
|
|
2773
|
-
const incompleteTasks = tasks.filter((t) => t.status !== "done");
|
|
2945
|
+
const incompleteTasks = tasks.filter((t) => t.status !== "done" && t.status !== "cancelled");
|
|
2774
2946
|
const tasksPass = incompleteTasks.length === 0;
|
|
2775
2947
|
const driftEntries = detectDrift3(db);
|
|
2776
2948
|
writeDriftLog2(db, driftEntries);
|
|
@@ -2780,7 +2952,7 @@ function registerCompletePhase(server, ctx) {
|
|
|
2780
2952
|
const projectRoot = ctx.projectRoot ?? params.target_dir;
|
|
2781
2953
|
let testCommand = "npx vitest run";
|
|
2782
2954
|
let timeoutMs = 6e4;
|
|
2783
|
-
const configResult =
|
|
2955
|
+
const configResult = loadConfig4(params.target_dir);
|
|
2784
2956
|
if (configResult.config) {
|
|
2785
2957
|
testCommand = configResult.config.testing.test_command;
|
|
2786
2958
|
timeoutMs = configResult.config.testing.timeout_ms;
|
|
@@ -2944,18 +3116,18 @@ function registerCompletePhase(server, ctx) {
|
|
|
2944
3116
|
}
|
|
2945
3117
|
|
|
2946
3118
|
// src/tools/activate-role.ts
|
|
2947
|
-
import { z as
|
|
3119
|
+
import { z as z25 } from "zod";
|
|
2948
3120
|
import { loadRole, loadRoles } from "@arcbridge/core";
|
|
2949
3121
|
function registerActivateRole(server, ctx) {
|
|
2950
3122
|
server.tool(
|
|
2951
3123
|
"arcbridge_activate_role",
|
|
2952
3124
|
"Activate an agent role: loads the role's system prompt, required tools, quality focus, and pre-loaded architectural context.",
|
|
2953
3125
|
{
|
|
2954
|
-
target_dir:
|
|
2955
|
-
role:
|
|
3126
|
+
target_dir: z25.string().describe("Absolute path to the project directory"),
|
|
3127
|
+
role: z25.string().describe(
|
|
2956
3128
|
"Role ID to activate (e.g., 'architect', 'implementer', 'security-reviewer')"
|
|
2957
3129
|
),
|
|
2958
|
-
building_block:
|
|
3130
|
+
building_block: z25.string().optional().describe("Focus on a specific building block (for implementer/code-reviewer roles)")
|
|
2959
3131
|
},
|
|
2960
3132
|
async (params) => {
|
|
2961
3133
|
const db = ensureDb(ctx, params.target_dir);
|
|
@@ -3257,18 +3429,18 @@ function getRoleDefinition(roleId) {
|
|
|
3257
3429
|
}
|
|
3258
3430
|
|
|
3259
3431
|
// src/tools/verify-scenarios.ts
|
|
3260
|
-
import { z as
|
|
3261
|
-
import { verifyScenarios as verifyScenarios2, loadConfig as
|
|
3432
|
+
import { z as z26 } from "zod";
|
|
3433
|
+
import { verifyScenarios as verifyScenarios2, loadConfig as loadConfig5 } from "@arcbridge/core";
|
|
3262
3434
|
function registerVerifyScenarios(server, ctx) {
|
|
3263
3435
|
server.tool(
|
|
3264
3436
|
"arcbridge_verify_scenarios",
|
|
3265
3437
|
"Run linked tests for quality scenarios and update their pass/fail status. Only runs scenarios with verification='automatic' or 'semi-automatic' and non-empty linked_tests.",
|
|
3266
3438
|
{
|
|
3267
|
-
target_dir:
|
|
3268
|
-
scenario_ids:
|
|
3439
|
+
target_dir: z26.string().describe("Absolute path to the project directory"),
|
|
3440
|
+
scenario_ids: z26.array(z26.string()).optional().describe(
|
|
3269
3441
|
"Specific scenario IDs to verify (e.g., ['SEC-01', 'PERF-01']). If omitted, verifies all automatic scenarios."
|
|
3270
3442
|
),
|
|
3271
|
-
test_command:
|
|
3443
|
+
test_command: z26.string().optional().describe(
|
|
3272
3444
|
"Override the test command from config (e.g., 'npx jest'). File paths are appended as arguments."
|
|
3273
3445
|
)
|
|
3274
3446
|
},
|
|
@@ -3277,7 +3449,7 @@ function registerVerifyScenarios(server, ctx) {
|
|
|
3277
3449
|
if (!db) return notInitialized();
|
|
3278
3450
|
let testCommand = "npx vitest run";
|
|
3279
3451
|
let timeoutMs = 6e4;
|
|
3280
|
-
const configResult =
|
|
3452
|
+
const configResult = loadConfig5(params.target_dir);
|
|
3281
3453
|
if (configResult.config) {
|
|
3282
3454
|
testCommand = configResult.config.testing.test_command;
|
|
3283
3455
|
timeoutMs = configResult.config.testing.timeout_ms;
|
|
@@ -3335,13 +3507,14 @@ function registerVerifyScenarios(server, ctx) {
|
|
|
3335
3507
|
}
|
|
3336
3508
|
|
|
3337
3509
|
// src/tools/run-role-check.ts
|
|
3338
|
-
import { z as
|
|
3510
|
+
import { z as z27 } from "zod";
|
|
3339
3511
|
import {
|
|
3340
3512
|
loadRole as loadRole2,
|
|
3341
3513
|
loadRoles as loadRoles2,
|
|
3342
3514
|
detectDrift as detectDrift4,
|
|
3343
3515
|
resolveRef as resolveRef3,
|
|
3344
|
-
getChangedFiles as getChangedFiles3
|
|
3516
|
+
getChangedFiles as getChangedFiles3,
|
|
3517
|
+
scopeToProject as scopeToProject2
|
|
3345
3518
|
} from "@arcbridge/core";
|
|
3346
3519
|
var SCOPE_VALUES = ["last-commit", "current-phase", "full-project"];
|
|
3347
3520
|
function registerRunRoleCheck(server, ctx) {
|
|
@@ -3349,11 +3522,11 @@ function registerRunRoleCheck(server, ctx) {
|
|
|
3349
3522
|
"arcbridge_run_role_check",
|
|
3350
3523
|
"Run a role-specific architectural analysis: resolves the role and executes relevant checks (drift, quality scenarios, boundaries, changed files) based on the role's focus areas.",
|
|
3351
3524
|
{
|
|
3352
|
-
target_dir:
|
|
3353
|
-
role:
|
|
3525
|
+
target_dir: z27.string().describe("Absolute path to the project directory"),
|
|
3526
|
+
role: z27.string().describe(
|
|
3354
3527
|
"Role ID to run checks for (e.g., 'security-reviewer', 'quality-guardian', 'architect', 'phase-manager', 'code-reviewer')"
|
|
3355
3528
|
),
|
|
3356
|
-
scope:
|
|
3529
|
+
scope: z27.enum(SCOPE_VALUES).default("current-phase").describe(
|
|
3357
3530
|
"Scope of analysis: 'last-commit' (recent changes), 'current-phase' (since phase start), 'full-project' (everything)"
|
|
3358
3531
|
)
|
|
3359
3532
|
},
|
|
@@ -3420,7 +3593,8 @@ function getChangedFilesForScope(db, projectRoot, scope) {
|
|
|
3420
3593
|
const since = scope === "last-commit" ? "last-commit" : "last-phase";
|
|
3421
3594
|
const ref = resolveRef3(projectRoot, since, db);
|
|
3422
3595
|
try {
|
|
3423
|
-
|
|
3596
|
+
const files = getChangedFiles3(projectRoot, ref.sha);
|
|
3597
|
+
return scopeToProject2(files, projectRoot);
|
|
3424
3598
|
} catch {
|
|
3425
3599
|
return null;
|
|
3426
3600
|
}
|
|
@@ -3709,7 +3883,7 @@ function runPhaseManagerCheck(db, projectRoot, lines) {
|
|
|
3709
3883
|
}
|
|
3710
3884
|
const ref = resolveRef3(projectRoot, "last-sync", db);
|
|
3711
3885
|
try {
|
|
3712
|
-
const changedFiles = getChangedFiles3(projectRoot, ref.sha);
|
|
3886
|
+
const changedFiles = scopeToProject2(getChangedFiles3(projectRoot, ref.sha), projectRoot);
|
|
3713
3887
|
if (changedFiles.length > 0) {
|
|
3714
3888
|
lines.push(`## Changes Since Last Sync (${ref.label})`, "");
|
|
3715
3889
|
appendChangedFilesList(lines, changedFiles);
|
|
@@ -3772,34 +3946,125 @@ function runCustomRoleCheck(db, lines, roleDef) {
|
|
|
3772
3946
|
appendDriftSection(db, lines);
|
|
3773
3947
|
}
|
|
3774
3948
|
|
|
3949
|
+
// src/tools/update-scenario-status.ts
|
|
3950
|
+
import { z as z28 } from "zod";
|
|
3951
|
+
import { syncScenarioToYaml, transaction as transaction2 } from "@arcbridge/core";
|
|
3952
|
+
function registerUpdateScenarioStatus(server, ctx) {
|
|
3953
|
+
server.tool(
|
|
3954
|
+
"arcbridge_update_scenario_status",
|
|
3955
|
+
"Update a quality scenario's status and optionally link test files. Use this to mark scenarios as passing/failing after manual verification, or to link test files so `arcbridge_verify_scenarios` can run them automatically.",
|
|
3956
|
+
{
|
|
3957
|
+
target_dir: z28.string().describe("Absolute path to the project directory"),
|
|
3958
|
+
scenario_id: z28.string().describe("Quality scenario ID (e.g., 'SEC-01', 'PERF-01')"),
|
|
3959
|
+
status: z28.enum(["passing", "failing", "untested", "partial"]).describe("New status for the scenario"),
|
|
3960
|
+
linked_tests: z28.array(z28.string()).optional().describe(
|
|
3961
|
+
"Test file paths to link to this scenario (e.g., ['src/__tests__/auth.test.ts']). Once linked, `arcbridge_verify_scenarios` can run them automatically. Also sets verification to 'semi-automatic' if currently 'manual'."
|
|
3962
|
+
)
|
|
3963
|
+
},
|
|
3964
|
+
async (params) => {
|
|
3965
|
+
const db = ensureDb(ctx, params.target_dir);
|
|
3966
|
+
if (!db) return notInitialized();
|
|
3967
|
+
const projectRoot = ctx.projectRoot ?? params.target_dir;
|
|
3968
|
+
const scenario = db.prepare("SELECT id, name, status, linked_tests, verification FROM quality_scenarios WHERE id = ?").get(params.scenario_id);
|
|
3969
|
+
if (!scenario) {
|
|
3970
|
+
const available = db.prepare("SELECT id, name, status FROM quality_scenarios ORDER BY id").all();
|
|
3971
|
+
const list = available.length > 0 ? available.map((s) => ` - \`${s.id}\` ${s.name} (${s.status})`).join("\n") : " (none)";
|
|
3972
|
+
return textResult(
|
|
3973
|
+
`Scenario '${params.scenario_id}' not found.
|
|
3974
|
+
|
|
3975
|
+
**Available scenarios:**
|
|
3976
|
+
${list}`
|
|
3977
|
+
);
|
|
3978
|
+
}
|
|
3979
|
+
const oldStatus = scenario.status;
|
|
3980
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
3981
|
+
if (params.linked_tests) {
|
|
3982
|
+
const { isAbsolute, normalize } = await import("path");
|
|
3983
|
+
const invalid = params.linked_tests.filter((t) => {
|
|
3984
|
+
const norm = normalize(t);
|
|
3985
|
+
return isAbsolute(norm) || norm.startsWith("..");
|
|
3986
|
+
});
|
|
3987
|
+
if (invalid.length > 0) {
|
|
3988
|
+
return textResult(
|
|
3989
|
+
`Invalid test paths (must be relative, no '..' segments):
|
|
3990
|
+
${invalid.map((p) => ` - ${p}`).join("\n")}`
|
|
3991
|
+
);
|
|
3992
|
+
}
|
|
3993
|
+
}
|
|
3994
|
+
transaction2(db, () => {
|
|
3995
|
+
db.prepare("UPDATE quality_scenarios SET status = ?, last_checked = ? WHERE id = ?").run(
|
|
3996
|
+
params.status,
|
|
3997
|
+
now,
|
|
3998
|
+
params.scenario_id
|
|
3999
|
+
);
|
|
4000
|
+
if (params.linked_tests) {
|
|
4001
|
+
db.prepare("UPDATE quality_scenarios SET linked_tests = ? WHERE id = ?").run(
|
|
4002
|
+
JSON.stringify(params.linked_tests),
|
|
4003
|
+
params.scenario_id
|
|
4004
|
+
);
|
|
4005
|
+
if (scenario.verification === "manual") {
|
|
4006
|
+
db.prepare("UPDATE quality_scenarios SET verification = 'semi-automatic' WHERE id = ?").run(
|
|
4007
|
+
params.scenario_id
|
|
4008
|
+
);
|
|
4009
|
+
}
|
|
4010
|
+
}
|
|
4011
|
+
});
|
|
4012
|
+
let yamlWarning;
|
|
4013
|
+
try {
|
|
4014
|
+
const newVerification = params.linked_tests && scenario.verification === "manual" ? "semi-automatic" : void 0;
|
|
4015
|
+
syncScenarioToYaml(projectRoot, params.scenario_id, params.status, params.linked_tests, newVerification);
|
|
4016
|
+
} catch (err) {
|
|
4017
|
+
yamlWarning = `YAML sync failed: ${err instanceof Error ? err.message : String(err)}. DB updated but YAML may be out of sync.`;
|
|
4018
|
+
}
|
|
4019
|
+
const lines = [
|
|
4020
|
+
`Scenario **${scenario.id}** (${scenario.name}) updated: ${oldStatus} \u2192 **${params.status}**`
|
|
4021
|
+
];
|
|
4022
|
+
if (params.linked_tests) {
|
|
4023
|
+
lines.push(
|
|
4024
|
+
"",
|
|
4025
|
+
`**Linked tests:** ${params.linked_tests.length} file(s)`,
|
|
4026
|
+
...params.linked_tests.map((t) => ` - ${t}`)
|
|
4027
|
+
);
|
|
4028
|
+
if (scenario.verification === "manual") {
|
|
4029
|
+
lines.push("", "*Verification upgraded from manual to semi-automatic*");
|
|
4030
|
+
}
|
|
4031
|
+
}
|
|
4032
|
+
if (yamlWarning) {
|
|
4033
|
+
lines.push("", `**Warning:** ${yamlWarning}`);
|
|
4034
|
+
}
|
|
4035
|
+
return textResult(lines.join("\n"));
|
|
4036
|
+
}
|
|
4037
|
+
);
|
|
4038
|
+
}
|
|
4039
|
+
|
|
3775
4040
|
// src/tools/record-activity.ts
|
|
3776
|
-
import { z as
|
|
4041
|
+
import { z as z29 } from "zod";
|
|
3777
4042
|
import { insertActivity as insertActivity2, getSessionTotals } from "@arcbridge/core";
|
|
3778
4043
|
function registerRecordActivity(server, ctx) {
|
|
3779
4044
|
server.tool(
|
|
3780
4045
|
"arcbridge_record_activity",
|
|
3781
4046
|
"Record agent activity \u2014 model, tokens, cost, duration, and optional quality snapshot. Use this to track what work was done and measure agent performance.",
|
|
3782
4047
|
{
|
|
3783
|
-
target_dir:
|
|
3784
|
-
tool_name:
|
|
3785
|
-
action:
|
|
3786
|
-
model:
|
|
3787
|
-
agent_role:
|
|
3788
|
-
task_id:
|
|
3789
|
-
phase_id:
|
|
3790
|
-
input_tokens:
|
|
3791
|
-
output_tokens:
|
|
3792
|
-
total_tokens:
|
|
3793
|
-
cost_usd:
|
|
3794
|
-
duration_ms:
|
|
3795
|
-
drift_count:
|
|
3796
|
-
drift_errors:
|
|
3797
|
-
test_pass_count:
|
|
3798
|
-
test_fail_count:
|
|
3799
|
-
lint_clean:
|
|
3800
|
-
typecheck_clean:
|
|
3801
|
-
notes:
|
|
3802
|
-
metadata:
|
|
4048
|
+
target_dir: z29.string().describe("Absolute path to the project directory"),
|
|
4049
|
+
tool_name: z29.string().describe("Name of the tool or action performed (e.g., 'arcbridge_update_task', 'code_edit')"),
|
|
4050
|
+
action: z29.string().optional().describe("Human-readable label (e.g., 'implement login form')"),
|
|
4051
|
+
model: z29.string().optional().describe("Model identifier (e.g., 'claude-sonnet-4-20250514')"),
|
|
4052
|
+
agent_role: z29.string().optional().describe("Active ArcBridge role (e.g., 'implementer')"),
|
|
4053
|
+
task_id: z29.string().optional().describe("Associated task ID"),
|
|
4054
|
+
phase_id: z29.string().optional().describe("Associated phase ID"),
|
|
4055
|
+
input_tokens: z29.number().int().nonnegative().optional().describe("Input/prompt tokens"),
|
|
4056
|
+
output_tokens: z29.number().int().nonnegative().optional().describe("Output/completion tokens"),
|
|
4057
|
+
total_tokens: z29.number().int().nonnegative().optional().describe("Total tokens (auto-computed if input+output given)"),
|
|
4058
|
+
cost_usd: z29.number().nonnegative().optional().describe("Estimated cost in USD"),
|
|
4059
|
+
duration_ms: z29.number().int().nonnegative().optional().describe("Wall-clock duration in ms"),
|
|
4060
|
+
drift_count: z29.number().int().nonnegative().optional().describe("Current drift count"),
|
|
4061
|
+
drift_errors: z29.number().int().nonnegative().optional().describe("Current drift errors"),
|
|
4062
|
+
test_pass_count: z29.number().int().nonnegative().optional().describe("Passing tests"),
|
|
4063
|
+
test_fail_count: z29.number().int().nonnegative().optional().describe("Failing tests"),
|
|
4064
|
+
lint_clean: z29.boolean().optional().describe("Whether lint passes cleanly"),
|
|
4065
|
+
typecheck_clean: z29.boolean().optional().describe("Whether typecheck passes cleanly"),
|
|
4066
|
+
notes: z29.string().optional().describe("Free-form notes"),
|
|
4067
|
+
metadata: z29.record(z29.unknown()).optional().describe("Additional key-value metadata")
|
|
3803
4068
|
},
|
|
3804
4069
|
async (params) => {
|
|
3805
4070
|
const db = ensureDb(ctx, params.target_dir);
|
|
@@ -3854,23 +4119,23 @@ function registerRecordActivity(server, ctx) {
|
|
|
3854
4119
|
}
|
|
3855
4120
|
|
|
3856
4121
|
// src/tools/get-metrics.ts
|
|
3857
|
-
import { z as
|
|
4122
|
+
import { z as z30 } from "zod";
|
|
3858
4123
|
import { queryMetrics } from "@arcbridge/core";
|
|
3859
4124
|
function registerGetMetrics(server, ctx) {
|
|
3860
4125
|
server.tool(
|
|
3861
4126
|
"arcbridge_get_metrics",
|
|
3862
4127
|
"Query agent activity metrics \u2014 filter by model, task, phase, or time range. Group by model/task/phase/tool/day for aggregated views.",
|
|
3863
4128
|
{
|
|
3864
|
-
target_dir:
|
|
3865
|
-
task_id:
|
|
3866
|
-
phase_id:
|
|
3867
|
-
model:
|
|
3868
|
-
agent_role:
|
|
3869
|
-
tool_name:
|
|
3870
|
-
since:
|
|
3871
|
-
until:
|
|
3872
|
-
group_by:
|
|
3873
|
-
limit:
|
|
4129
|
+
target_dir: z30.string().describe("Absolute path to the project directory"),
|
|
4130
|
+
task_id: z30.string().optional().describe("Filter by task ID"),
|
|
4131
|
+
phase_id: z30.string().optional().describe("Filter by phase ID"),
|
|
4132
|
+
model: z30.string().optional().describe("Filter by model name"),
|
|
4133
|
+
agent_role: z30.string().optional().describe("Filter by agent role"),
|
|
4134
|
+
tool_name: z30.string().optional().describe("Filter by tool name"),
|
|
4135
|
+
since: z30.string().optional().describe("ISO 8601 timestamp \u2014 activity after this time"),
|
|
4136
|
+
until: z30.string().optional().describe("ISO 8601 timestamp \u2014 activity before this time"),
|
|
4137
|
+
group_by: z30.enum(["model", "task", "phase", "tool", "day", "none"]).default("none").describe("Group results for aggregation"),
|
|
4138
|
+
limit: z30.number().int().min(1).max(500).default(50).describe("Max rows in detail view (group_by=none)")
|
|
3874
4139
|
},
|
|
3875
4140
|
async (params) => {
|
|
3876
4141
|
const db = ensureDb(ctx, params.target_dir);
|
|
@@ -3953,23 +4218,23 @@ function mdCell(val) {
|
|
|
3953
4218
|
}
|
|
3954
4219
|
|
|
3955
4220
|
// src/tools/export-metrics.ts
|
|
3956
|
-
import { z as
|
|
4221
|
+
import { z as z31 } from "zod";
|
|
3957
4222
|
import { exportMetrics } from "@arcbridge/core";
|
|
3958
4223
|
function registerExportMetrics(server, ctx) {
|
|
3959
4224
|
server.tool(
|
|
3960
4225
|
"arcbridge_export_metrics",
|
|
3961
4226
|
"Export agent activity metrics to a file (JSON, CSV, or Markdown) in .arcbridge/metrics/ for git commits or reporting.",
|
|
3962
4227
|
{
|
|
3963
|
-
target_dir:
|
|
3964
|
-
format:
|
|
3965
|
-
task_id:
|
|
3966
|
-
phase_id:
|
|
3967
|
-
model:
|
|
3968
|
-
agent_role:
|
|
3969
|
-
tool_name:
|
|
3970
|
-
since:
|
|
3971
|
-
until:
|
|
3972
|
-
max_rows:
|
|
4228
|
+
target_dir: z31.string().describe("Absolute path to the project directory"),
|
|
4229
|
+
format: z31.enum(["json", "csv", "markdown"]).default("json").describe("Export format"),
|
|
4230
|
+
task_id: z31.string().optional().describe("Filter by task ID"),
|
|
4231
|
+
phase_id: z31.string().optional().describe("Filter by phase ID"),
|
|
4232
|
+
model: z31.string().optional().describe("Filter by model name"),
|
|
4233
|
+
agent_role: z31.string().optional().describe("Filter by agent role"),
|
|
4234
|
+
tool_name: z31.string().optional().describe("Filter by tool name"),
|
|
4235
|
+
since: z31.string().optional().describe("ISO 8601 \u2014 activity after this time"),
|
|
4236
|
+
until: z31.string().optional().describe("ISO 8601 \u2014 activity before this time"),
|
|
4237
|
+
max_rows: z31.number().int().min(1).default(1e5).describe("Maximum rows to export (default: 100,000)")
|
|
3973
4238
|
},
|
|
3974
4239
|
async (params) => {
|
|
3975
4240
|
const db = ensureDb(ctx, params.target_dir);
|
|
@@ -4017,6 +4282,7 @@ function createArcBridgeServer() {
|
|
|
4017
4282
|
registerGetCurrentTasks(server, ctx);
|
|
4018
4283
|
registerUpdateTask(server, ctx);
|
|
4019
4284
|
registerCreateTask(server, ctx);
|
|
4285
|
+
registerDeleteTask(server, ctx);
|
|
4020
4286
|
registerReindex(server, ctx);
|
|
4021
4287
|
registerSearchSymbols(server, ctx);
|
|
4022
4288
|
registerGetSymbol(server, ctx);
|
|
@@ -4032,6 +4298,7 @@ function createArcBridgeServer() {
|
|
|
4032
4298
|
registerCompletePhase(server, ctx);
|
|
4033
4299
|
registerActivateRole(server, ctx);
|
|
4034
4300
|
registerVerifyScenarios(server, ctx);
|
|
4301
|
+
registerUpdateScenarioStatus(server, ctx);
|
|
4035
4302
|
registerRunRoleCheck(server, ctx);
|
|
4036
4303
|
registerRecordActivity(server, ctx);
|
|
4037
4304
|
registerGetMetrics(server, ctx);
|