@arcbridge/mcp-server 0.1.4 → 0.1.6
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 +5 -2
- package/dist/index.js +449 -163
- 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,
|
|
@@ -44,9 +44,12 @@ function registerInitProject(server, ctx) {
|
|
|
44
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
45
|
),
|
|
46
46
|
features: z.array(z.enum(["auth", "database", "api"])).default([]).describe("Features to scaffold"),
|
|
47
|
-
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"),
|
|
48
48
|
platforms: z.array(z.string()).default(["claude"]).describe("Target platforms for agent config generation"),
|
|
49
|
-
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
|
+
)
|
|
50
53
|
},
|
|
51
54
|
async (params) => {
|
|
52
55
|
const targetDir = params.target_dir;
|
|
@@ -83,6 +86,9 @@ function registerInitProject(server, ctx) {
|
|
|
83
86
|
const { db: recoveredDb } = generateDatabase(targetDir, recoverInput);
|
|
84
87
|
ctx.db = recoveredDb;
|
|
85
88
|
ctx.projectRoot = targetDir;
|
|
89
|
+
if (params.spec) {
|
|
90
|
+
writeFileSync(join(targetDir, ".arcbridge", "spec.md"), params.spec, "utf-8");
|
|
91
|
+
}
|
|
86
92
|
return {
|
|
87
93
|
content: [
|
|
88
94
|
{
|
|
@@ -121,6 +127,13 @@ Use \`arcbridge_get_project_status\` to see the current state.`
|
|
|
121
127
|
platformWarnings.push(`Platform '${platform}': ${msg}`);
|
|
122
128
|
}
|
|
123
129
|
}
|
|
130
|
+
if (params.spec) {
|
|
131
|
+
writeFileSync(
|
|
132
|
+
join(targetDir, ".arcbridge", "spec.md"),
|
|
133
|
+
params.spec,
|
|
134
|
+
"utf-8"
|
|
135
|
+
);
|
|
136
|
+
}
|
|
124
137
|
let indexResult = null;
|
|
125
138
|
try {
|
|
126
139
|
const result = await indexProject(db, { projectRoot: targetDir });
|
|
@@ -143,6 +156,7 @@ Use \`arcbridge_get_project_status\` to see the current state.`
|
|
|
143
156
|
`**Template:** ${input.template}`,
|
|
144
157
|
`**Features:** ${input.features.length > 0 ? input.features.join(", ") : "none"}`,
|
|
145
158
|
`**Platforms:** ${params.platforms.join(", ")}`,
|
|
159
|
+
...params.spec ? [`**Spec:** saved to .arcbridge/spec.md`] : [],
|
|
146
160
|
"",
|
|
147
161
|
"## Created",
|
|
148
162
|
"",
|
|
@@ -175,12 +189,15 @@ Use \`arcbridge_get_project_status\` to see the current state.`
|
|
|
175
189
|
...allWarnings.map((w) => `- ${w}`)
|
|
176
190
|
] : [],
|
|
177
191
|
"",
|
|
178
|
-
"## Next Steps",
|
|
192
|
+
"## Next Steps \u2014 PLAN FIRST, BUILD SECOND",
|
|
193
|
+
"",
|
|
194
|
+
"**Do not start implementing yet.** First plan the full project roadmap:",
|
|
179
195
|
"",
|
|
180
|
-
"1. **
|
|
181
|
-
"2. **
|
|
182
|
-
"3. **
|
|
183
|
-
"4. **
|
|
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",
|
|
184
201
|
"",
|
|
185
202
|
"Use `arcbridge_get_project_status` to see the full project status."
|
|
186
203
|
];
|
|
@@ -803,6 +820,23 @@ function registerGetCurrentTasks(server, ctx) {
|
|
|
803
820
|
lines.push("");
|
|
804
821
|
}
|
|
805
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
|
+
}
|
|
806
840
|
return {
|
|
807
841
|
content: [{ type: "text", text: lines.join("\n") }]
|
|
808
842
|
};
|
|
@@ -856,7 +890,7 @@ function registerUpdateTask(server, ctx) {
|
|
|
856
890
|
{
|
|
857
891
|
target_dir: z8.string().describe("Absolute path to the project directory"),
|
|
858
892
|
task_id: z8.string().describe("Task ID (e.g., 'task-0.1-init-nextjs')"),
|
|
859
|
-
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."),
|
|
860
894
|
notes: z8.string().optional().describe("Optional notes about the status change")
|
|
861
895
|
},
|
|
862
896
|
async (params) => {
|
|
@@ -881,10 +915,9 @@ function registerUpdateTask(server, ctx) {
|
|
|
881
915
|
"UPDATE tasks SET status = ?, completed_at = ? WHERE id = ?"
|
|
882
916
|
).run(params.status, now, params.task_id);
|
|
883
917
|
} else {
|
|
884
|
-
db.prepare(
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
);
|
|
918
|
+
db.prepare(
|
|
919
|
+
"UPDATE tasks SET status = ?, completed_at = NULL WHERE id = ?"
|
|
920
|
+
).run(params.status, params.task_id);
|
|
888
921
|
}
|
|
889
922
|
syncTaskToYaml(
|
|
890
923
|
params.target_dir,
|
|
@@ -901,15 +934,15 @@ function registerUpdateTask(server, ctx) {
|
|
|
901
934
|
if (params.notes) {
|
|
902
935
|
lines.push("", `**Notes:** ${params.notes}`);
|
|
903
936
|
}
|
|
904
|
-
if (params.status === "done") {
|
|
937
|
+
if (params.status === "done" || params.status === "cancelled") {
|
|
905
938
|
const phaseStats = db.prepare(
|
|
906
|
-
"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 = ?"
|
|
907
940
|
).get(task.phase_id);
|
|
908
941
|
lines.push(
|
|
909
942
|
"",
|
|
910
943
|
`**Phase progress:** ${phaseStats.done}/${phaseStats.total} tasks complete`
|
|
911
944
|
);
|
|
912
|
-
if (phaseStats.done === phaseStats.total) {
|
|
945
|
+
if (phaseStats.total > 0 && phaseStats.done === phaseStats.total) {
|
|
913
946
|
lines.push(
|
|
914
947
|
"",
|
|
915
948
|
"All tasks in this phase are complete! The phase is ready to advance."
|
|
@@ -941,7 +974,9 @@ function registerCreateTask(server, ctx) {
|
|
|
941
974
|
target_dir: z9.string().describe("Absolute path to the project directory"),
|
|
942
975
|
phase_id: z9.string().describe("Phase ID to add the task to"),
|
|
943
976
|
title: z9.string().min(1).describe("Task title"),
|
|
944
|
-
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
|
+
),
|
|
945
980
|
quality_scenarios: z9.array(z9.string()).default([]).describe("Quality scenario IDs this task addresses"),
|
|
946
981
|
acceptance_criteria: z9.array(z9.string()).default([]).describe("Acceptance criteria for this task")
|
|
947
982
|
},
|
|
@@ -966,6 +1001,27 @@ function registerCreateTask(server, ctx) {
|
|
|
966
1001
|
const slug = params.title.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "").slice(0, 30);
|
|
967
1002
|
const taskId = `task-${phase.phase_number}.${taskNum}-${slug}`;
|
|
968
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
|
+
}
|
|
969
1025
|
db.prepare(
|
|
970
1026
|
"INSERT INTO tasks (id, phase_id, title, description, status, building_block, quality_scenarios, acceptance_criteria, created_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)"
|
|
971
1027
|
).run(
|
|
@@ -974,7 +1030,7 @@ function registerCreateTask(server, ctx) {
|
|
|
974
1030
|
params.title,
|
|
975
1031
|
null,
|
|
976
1032
|
"todo",
|
|
977
|
-
|
|
1033
|
+
blockId,
|
|
978
1034
|
JSON.stringify(params.quality_scenarios),
|
|
979
1035
|
JSON.stringify(params.acceptance_criteria),
|
|
980
1036
|
now
|
|
@@ -1015,16 +1071,113 @@ function registerCreateTask(server, ctx) {
|
|
|
1015
1071
|
);
|
|
1016
1072
|
}
|
|
1017
1073
|
|
|
1018
|
-
// src/tools/
|
|
1074
|
+
// src/tools/delete-task.ts
|
|
1019
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/create-phase.ts
|
|
1108
|
+
import { z as z11 } from "zod";
|
|
1109
|
+
import { addPhaseToYaml, refreshFromDocs as refreshFromDocs4 } from "@arcbridge/core";
|
|
1110
|
+
function registerCreatePhase(server, ctx) {
|
|
1111
|
+
server.tool(
|
|
1112
|
+
"arcbridge_create_phase",
|
|
1113
|
+
"Create a new phase in the project plan. Use this to add phases beyond the initial 4-phase template when the project scope requires it.",
|
|
1114
|
+
{
|
|
1115
|
+
target_dir: z11.string().describe("Absolute path to the project directory"),
|
|
1116
|
+
name: z11.string().min(1).describe("Phase name (e.g., 'Integrations', 'Performance Optimization')"),
|
|
1117
|
+
description: z11.string().min(1).describe("What this phase covers"),
|
|
1118
|
+
phase_number: z11.number().int().min(0).optional().describe("Phase number (default: next after highest existing phase)"),
|
|
1119
|
+
gate_requirements: z11.array(z11.string()).default([]).describe("Requirements that must be met to complete this phase")
|
|
1120
|
+
},
|
|
1121
|
+
async (params) => {
|
|
1122
|
+
const db = ensureDb(ctx, params.target_dir);
|
|
1123
|
+
if (!db) return notInitialized();
|
|
1124
|
+
refreshFromDocs4(db, params.target_dir);
|
|
1125
|
+
const maxPhase = db.prepare("SELECT MAX(phase_number) as max FROM phases").get();
|
|
1126
|
+
const phaseNumber = params.phase_number ?? (maxPhase.max ?? -1) + 1;
|
|
1127
|
+
const existing = db.prepare("SELECT id FROM phases WHERE phase_number = ?").get(phaseNumber);
|
|
1128
|
+
if (existing) {
|
|
1129
|
+
return textResult(
|
|
1130
|
+
`Phase number ${phaseNumber} already exists (\`${existing.id}\`). Choose a different number or omit to auto-assign.`
|
|
1131
|
+
);
|
|
1132
|
+
}
|
|
1133
|
+
const slug = params.name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "").slice(0, 30) || "unnamed";
|
|
1134
|
+
const phaseId = `phase-${phaseNumber}-${slug}`;
|
|
1135
|
+
const yamlResult = addPhaseToYaml(params.target_dir, {
|
|
1136
|
+
id: phaseId,
|
|
1137
|
+
name: params.name,
|
|
1138
|
+
phase_number: phaseNumber,
|
|
1139
|
+
description: params.description,
|
|
1140
|
+
gate_requirements: params.gate_requirements
|
|
1141
|
+
});
|
|
1142
|
+
if (!yamlResult.success) {
|
|
1143
|
+
return textResult(
|
|
1144
|
+
`Failed to create phase: ${yamlResult.warning ?? "YAML update failed"}`
|
|
1145
|
+
);
|
|
1146
|
+
}
|
|
1147
|
+
refreshFromDocs4(db, params.target_dir);
|
|
1148
|
+
const lines = [
|
|
1149
|
+
`Phase created: **${phaseId}**`,
|
|
1150
|
+
"",
|
|
1151
|
+
`**Name:** ${params.name}`,
|
|
1152
|
+
`**Number:** ${phaseNumber}`,
|
|
1153
|
+
`**Status:** planned`,
|
|
1154
|
+
`**Description:** ${params.description}`
|
|
1155
|
+
];
|
|
1156
|
+
if (params.gate_requirements.length > 0) {
|
|
1157
|
+
lines.push("", "**Gate requirements:**");
|
|
1158
|
+
for (const r of params.gate_requirements) {
|
|
1159
|
+
lines.push(`- [ ] ${r}`);
|
|
1160
|
+
}
|
|
1161
|
+
}
|
|
1162
|
+
lines.push(
|
|
1163
|
+
"",
|
|
1164
|
+
`Use \`arcbridge_create_task\` with phase ID \`${phaseId}\` to add tasks.`
|
|
1165
|
+
);
|
|
1166
|
+
return textResult(lines.join("\n"));
|
|
1167
|
+
}
|
|
1168
|
+
);
|
|
1169
|
+
}
|
|
1170
|
+
|
|
1171
|
+
// src/tools/get-relevant-adrs.ts
|
|
1172
|
+
import { z as z12 } from "zod";
|
|
1020
1173
|
function registerGetRelevantAdrs(server, ctx) {
|
|
1021
1174
|
server.tool(
|
|
1022
1175
|
"arcbridge_get_relevant_adrs",
|
|
1023
1176
|
"Get architectural decision records (ADRs) relevant to a specific file path or building block.",
|
|
1024
1177
|
{
|
|
1025
|
-
target_dir:
|
|
1026
|
-
file_path:
|
|
1027
|
-
building_block:
|
|
1178
|
+
target_dir: z12.string().describe("Absolute path to the project directory"),
|
|
1179
|
+
file_path: z12.string().optional().describe("File path to find relevant ADRs for"),
|
|
1180
|
+
building_block: z12.string().optional().describe("Building block ID to find relevant ADRs for")
|
|
1028
1181
|
},
|
|
1029
1182
|
async (params) => {
|
|
1030
1183
|
const db = ensureDb(ctx, params.target_dir);
|
|
@@ -1108,24 +1261,24 @@ function formatAdrs(adrs, title) {
|
|
|
1108
1261
|
}
|
|
1109
1262
|
|
|
1110
1263
|
// src/tools/reindex.ts
|
|
1111
|
-
import { z as
|
|
1112
|
-
import { indexProject as indexProject2, refreshFromDocs as
|
|
1264
|
+
import { z as z13 } from "zod";
|
|
1265
|
+
import { indexProject as indexProject2, refreshFromDocs as refreshFromDocs5 } from "@arcbridge/core";
|
|
1113
1266
|
function registerReindex(server, ctx) {
|
|
1114
1267
|
server.tool(
|
|
1115
1268
|
"arcbridge_reindex",
|
|
1116
1269
|
"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.",
|
|
1117
1270
|
{
|
|
1118
|
-
target_dir:
|
|
1119
|
-
tsconfig_path:
|
|
1120
|
-
service:
|
|
1121
|
-
language:
|
|
1271
|
+
target_dir: z13.string().describe("Absolute path to the project directory"),
|
|
1272
|
+
tsconfig_path: z13.string().optional().describe("Override tsconfig.json path (default: auto-detect). Only used for TypeScript projects."),
|
|
1273
|
+
service: z13.string().optional().describe("Service name for monorepo projects (default: 'main')"),
|
|
1274
|
+
language: z13.enum(["typescript", "csharp", "auto"]).optional().describe("Project language. 'auto' detects from project files (default: 'auto')")
|
|
1122
1275
|
},
|
|
1123
1276
|
async (params) => {
|
|
1124
1277
|
const start = Date.now();
|
|
1125
1278
|
const db = ensureDb(ctx, params.target_dir);
|
|
1126
1279
|
if (!db) return notInitialized();
|
|
1127
1280
|
try {
|
|
1128
|
-
const docWarnings =
|
|
1281
|
+
const docWarnings = refreshFromDocs5(db, params.target_dir);
|
|
1129
1282
|
const result = await indexProject2(db, {
|
|
1130
1283
|
projectRoot: params.target_dir,
|
|
1131
1284
|
tsconfigPath: params.tsconfig_path,
|
|
@@ -1133,9 +1286,13 @@ function registerReindex(server, ctx) {
|
|
|
1133
1286
|
language: params.language
|
|
1134
1287
|
});
|
|
1135
1288
|
const lines = [
|
|
1136
|
-
"#
|
|
1289
|
+
"# Reindex Complete",
|
|
1290
|
+
"",
|
|
1291
|
+
"## Architecture Docs",
|
|
1292
|
+
`- **Refreshed from docs:** building blocks, phases, tasks, quality scenarios, ADRs`,
|
|
1293
|
+
...docWarnings.length > 0 ? [`- **Warnings:** ${docWarnings.join("; ")}`] : [],
|
|
1137
1294
|
"",
|
|
1138
|
-
|
|
1295
|
+
"## Code Symbols",
|
|
1139
1296
|
`- **Files processed:** ${result.filesProcessed}`,
|
|
1140
1297
|
`- **Files skipped (unchanged):** ${result.filesSkipped}`,
|
|
1141
1298
|
`- **Files removed:** ${result.filesRemoved}`,
|
|
@@ -1160,16 +1317,16 @@ function registerReindex(server, ctx) {
|
|
|
1160
1317
|
}
|
|
1161
1318
|
|
|
1162
1319
|
// src/tools/search-symbols.ts
|
|
1163
|
-
import { z as
|
|
1320
|
+
import { z as z14 } from "zod";
|
|
1164
1321
|
function registerSearchSymbols(server, ctx) {
|
|
1165
1322
|
server.tool(
|
|
1166
1323
|
"arcbridge_search_symbols",
|
|
1167
1324
|
"Search code symbols by name, kind, file path, or building block. Supports TypeScript and C#. Returns matching symbols with type signatures.",
|
|
1168
1325
|
{
|
|
1169
|
-
target_dir:
|
|
1170
|
-
query:
|
|
1171
|
-
service:
|
|
1172
|
-
kind:
|
|
1326
|
+
target_dir: z14.string().describe("Absolute path to the project directory"),
|
|
1327
|
+
query: z14.string().optional().describe("Search term to match against symbol names"),
|
|
1328
|
+
service: z14.string().optional().describe("Filter by service name (for multi-project solutions). Omit to search all services."),
|
|
1329
|
+
kind: z14.enum([
|
|
1173
1330
|
"function",
|
|
1174
1331
|
"class",
|
|
1175
1332
|
"type",
|
|
@@ -1181,10 +1338,10 @@ function registerSearchSymbols(server, ctx) {
|
|
|
1181
1338
|
"hook",
|
|
1182
1339
|
"context"
|
|
1183
1340
|
]).optional().describe("Filter by symbol kind"),
|
|
1184
|
-
file_path:
|
|
1185
|
-
is_exported:
|
|
1186
|
-
building_block:
|
|
1187
|
-
limit:
|
|
1341
|
+
file_path: z14.string().optional().describe("Filter by file path (prefix match)"),
|
|
1342
|
+
is_exported: z14.boolean().optional().describe("Filter by export status"),
|
|
1343
|
+
building_block: z14.string().optional().describe("Filter by building block ID (matches against code_paths)"),
|
|
1344
|
+
limit: z14.number().int().min(1).max(200).default(50).describe("Maximum results to return (default: 50)")
|
|
1188
1345
|
},
|
|
1189
1346
|
async (params) => {
|
|
1190
1347
|
const db = ensureDb(ctx, params.target_dir);
|
|
@@ -1271,7 +1428,7 @@ function registerSearchSymbols(server, ctx) {
|
|
|
1271
1428
|
}
|
|
1272
1429
|
|
|
1273
1430
|
// src/tools/get-symbol.ts
|
|
1274
|
-
import { z as
|
|
1431
|
+
import { z as z15 } from "zod";
|
|
1275
1432
|
import { readFileSync, existsSync as existsSync3 } from "fs";
|
|
1276
1433
|
import { join as join3 } from "path";
|
|
1277
1434
|
function registerGetSymbol(server, ctx) {
|
|
@@ -1279,11 +1436,11 @@ function registerGetSymbol(server, ctx) {
|
|
|
1279
1436
|
"arcbridge_get_symbol",
|
|
1280
1437
|
"Get detailed information about a specific TypeScript symbol including its source code, type signature, and relationships.",
|
|
1281
1438
|
{
|
|
1282
|
-
target_dir:
|
|
1283
|
-
symbol_id:
|
|
1439
|
+
target_dir: z15.string().describe("Absolute path to the project directory"),
|
|
1440
|
+
symbol_id: z15.string().describe(
|
|
1284
1441
|
"Symbol ID (e.g. 'src/utils.ts::formatName#function')"
|
|
1285
1442
|
),
|
|
1286
|
-
include_source:
|
|
1443
|
+
include_source: z15.boolean().default(true).describe("Include source code snippet (default: true)")
|
|
1287
1444
|
},
|
|
1288
1445
|
async (params) => {
|
|
1289
1446
|
const db = ensureDb(ctx, params.target_dir);
|
|
@@ -1381,21 +1538,21 @@ Use \`arcbridge_search_symbols\` to find symbols by name.`
|
|
|
1381
1538
|
}
|
|
1382
1539
|
|
|
1383
1540
|
// src/tools/get-dependency-graph.ts
|
|
1384
|
-
import { z as
|
|
1541
|
+
import { z as z16 } from "zod";
|
|
1385
1542
|
function registerGetDependencyGraph(server, ctx) {
|
|
1386
1543
|
server.tool(
|
|
1387
1544
|
"arcbridge_get_dependency_graph",
|
|
1388
1545
|
"Get the dependency graph for a module or file. Shows imports, calls, type usage, and inheritance relationships between symbols.",
|
|
1389
1546
|
{
|
|
1390
|
-
target_dir:
|
|
1391
|
-
module:
|
|
1547
|
+
target_dir: z16.string().describe("Absolute path to the project directory"),
|
|
1548
|
+
module: z16.string().describe(
|
|
1392
1549
|
"Module path relative to project root (e.g. 'src/lib/auth')"
|
|
1393
1550
|
),
|
|
1394
|
-
direction:
|
|
1551
|
+
direction: z16.enum(["dependencies", "dependents", "both"]).default("both").describe(
|
|
1395
1552
|
"Graph direction: 'dependencies' (what this module uses), 'dependents' (what uses this module), or 'both'"
|
|
1396
1553
|
),
|
|
1397
|
-
depth:
|
|
1398
|
-
service:
|
|
1554
|
+
depth: z16.number().int().min(1).max(5).default(1).describe("How many levels to traverse (default: 1, max: 5)"),
|
|
1555
|
+
service: z16.string().optional().describe("Filter by service name (for multi-project solutions). Omit to search all services.")
|
|
1399
1556
|
},
|
|
1400
1557
|
async (params) => {
|
|
1401
1558
|
const maybeDb = ensureDb(ctx, params.target_dir);
|
|
@@ -1542,16 +1699,16 @@ function formatEdges(edges, modulePath, direction) {
|
|
|
1542
1699
|
}
|
|
1543
1700
|
|
|
1544
1701
|
// src/tools/get-component-graph.ts
|
|
1545
|
-
import { z as
|
|
1702
|
+
import { z as z17 } from "zod";
|
|
1546
1703
|
function registerGetComponentGraph(server, ctx) {
|
|
1547
1704
|
server.tool(
|
|
1548
1705
|
"arcbridge_get_component_graph",
|
|
1549
1706
|
"Get the React component graph: component hierarchy, props, state, context usage, and server/client boundaries.",
|
|
1550
1707
|
{
|
|
1551
|
-
target_dir:
|
|
1552
|
-
file_path:
|
|
1553
|
-
client_only:
|
|
1554
|
-
with_state:
|
|
1708
|
+
target_dir: z17.string().describe("Absolute path to the project directory"),
|
|
1709
|
+
file_path: z17.string().optional().describe("Filter to components in a specific file or directory prefix"),
|
|
1710
|
+
client_only: z17.boolean().optional().describe("Only show client components ('use client')"),
|
|
1711
|
+
with_state: z17.boolean().optional().describe("Only show components that use state (useState/useReducer)")
|
|
1555
1712
|
},
|
|
1556
1713
|
async (params) => {
|
|
1557
1714
|
const db = ensureDb(ctx, params.target_dir);
|
|
@@ -1655,16 +1812,16 @@ function registerGetComponentGraph(server, ctx) {
|
|
|
1655
1812
|
}
|
|
1656
1813
|
|
|
1657
1814
|
// src/tools/get-route-map.ts
|
|
1658
|
-
import { z as
|
|
1815
|
+
import { z as z18 } from "zod";
|
|
1659
1816
|
function registerGetRouteMap(server, ctx) {
|
|
1660
1817
|
server.tool(
|
|
1661
1818
|
"arcbridge_get_route_map",
|
|
1662
1819
|
"Get the route map: pages, layouts, API routes, and their hierarchy. Works with Next.js, ASP.NET controllers, and minimal APIs.",
|
|
1663
1820
|
{
|
|
1664
|
-
target_dir:
|
|
1665
|
-
kind:
|
|
1666
|
-
route_prefix:
|
|
1667
|
-
service:
|
|
1821
|
+
target_dir: z18.string().describe("Absolute path to the project directory"),
|
|
1822
|
+
kind: z18.enum(["page", "layout", "loading", "error", "not-found", "api-route", "middleware"]).optional().describe("Filter by route kind"),
|
|
1823
|
+
route_prefix: z18.string().optional().describe("Filter by route path prefix (e.g. '/dashboard' or '/api/orders')"),
|
|
1824
|
+
service: z18.string().optional().describe("Filter by service name (for multi-project solutions). Omit to show all services.")
|
|
1668
1825
|
},
|
|
1669
1826
|
async (params) => {
|
|
1670
1827
|
const db = ensureDb(ctx, params.target_dir);
|
|
@@ -1740,13 +1897,13 @@ function registerGetRouteMap(server, ctx) {
|
|
|
1740
1897
|
}
|
|
1741
1898
|
|
|
1742
1899
|
// src/tools/get-boundary-analysis.ts
|
|
1743
|
-
import { z as
|
|
1900
|
+
import { z as z19 } from "zod";
|
|
1744
1901
|
function registerGetBoundaryAnalysis(server, ctx) {
|
|
1745
1902
|
server.tool(
|
|
1746
1903
|
"arcbridge_get_boundary_analysis",
|
|
1747
1904
|
"Analyze server/client boundaries in a Next.js project. Identifies client components, server components, server actions, and potential boundary violations.",
|
|
1748
1905
|
{
|
|
1749
|
-
target_dir:
|
|
1906
|
+
target_dir: z19.string().describe("Absolute path to the project directory")
|
|
1750
1907
|
},
|
|
1751
1908
|
async (params) => {
|
|
1752
1909
|
const db = ensureDb(ctx, params.target_dir);
|
|
@@ -1850,15 +2007,15 @@ function registerGetBoundaryAnalysis(server, ctx) {
|
|
|
1850
2007
|
}
|
|
1851
2008
|
|
|
1852
2009
|
// src/tools/check-drift.ts
|
|
1853
|
-
import { z as
|
|
2010
|
+
import { z as z20 } from "zod";
|
|
1854
2011
|
import { detectDrift, writeDriftLog } from "@arcbridge/core";
|
|
1855
2012
|
function registerCheckDrift(server, ctx) {
|
|
1856
2013
|
server.tool(
|
|
1857
2014
|
"arcbridge_check_drift",
|
|
1858
2015
|
"Detect architecture drift: undocumented modules, missing code paths, cross-block dependency violations, and stale ADR references.",
|
|
1859
2016
|
{
|
|
1860
|
-
target_dir:
|
|
1861
|
-
persist:
|
|
2017
|
+
target_dir: z20.string().describe("Absolute path to the project directory"),
|
|
2018
|
+
persist: z20.boolean().default(true).describe("Write findings to drift_log table (default: true)")
|
|
1862
2019
|
},
|
|
1863
2020
|
async (params) => {
|
|
1864
2021
|
const start = Date.now();
|
|
@@ -1930,15 +2087,16 @@ function registerCheckDrift(server, ctx) {
|
|
|
1930
2087
|
}
|
|
1931
2088
|
|
|
1932
2089
|
// src/tools/get-guidance.ts
|
|
1933
|
-
import { z as
|
|
2090
|
+
import { z as z21 } from "zod";
|
|
2091
|
+
import { loadConfig as loadConfig3 } from "@arcbridge/core";
|
|
1934
2092
|
function registerGetGuidance(server, ctx) {
|
|
1935
2093
|
server.tool(
|
|
1936
2094
|
"arcbridge_get_guidance",
|
|
1937
2095
|
"Get context-aware architectural guidance for a code change. Surfaces relevant quality scenarios, patterns, constraints, and questions to consider.",
|
|
1938
2096
|
{
|
|
1939
|
-
target_dir:
|
|
1940
|
-
file_path:
|
|
1941
|
-
action:
|
|
2097
|
+
target_dir: z21.string().describe("Absolute path to the project directory"),
|
|
2098
|
+
file_path: z21.string().optional().describe("File path you're working on (to determine building block and context)"),
|
|
2099
|
+
action: z21.enum([
|
|
1942
2100
|
"adding-component",
|
|
1943
2101
|
"adding-api-route",
|
|
1944
2102
|
"adding-hook",
|
|
@@ -2068,7 +2226,10 @@ function registerGetGuidance(server, ctx) {
|
|
|
2068
2226
|
}
|
|
2069
2227
|
lines.push("");
|
|
2070
2228
|
}
|
|
2071
|
-
const
|
|
2229
|
+
const projectRoot = ctx.projectRoot ?? params.target_dir;
|
|
2230
|
+
const { config: projConfig, error: configError } = loadConfig3(projectRoot);
|
|
2231
|
+
const projectType = configError ? "unknown" : projConfig?.project_type ?? "nextjs-app-router";
|
|
2232
|
+
const actionGuidance = getActionGuidance(params.action, projectType);
|
|
2072
2233
|
if (actionGuidance) {
|
|
2073
2234
|
lines.push("## Guidance", "", actionGuidance, "");
|
|
2074
2235
|
}
|
|
@@ -2092,28 +2253,55 @@ function filterRelevantScenarios(scenarios, action, _blockId) {
|
|
|
2092
2253
|
}
|
|
2093
2254
|
return scenarios.filter((s) => relevantCategories.includes(s.category));
|
|
2094
2255
|
}
|
|
2095
|
-
|
|
2096
|
-
|
|
2097
|
-
|
|
2098
|
-
|
|
2099
|
-
|
|
2100
|
-
|
|
2101
|
-
|
|
2102
|
-
|
|
2103
|
-
|
|
2104
|
-
|
|
2105
|
-
|
|
2256
|
+
var FRONTEND_GUIDANCE = {
|
|
2257
|
+
"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`",
|
|
2258
|
+
"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",
|
|
2259
|
+
"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",
|
|
2260
|
+
"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",
|
|
2261
|
+
"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`"
|
|
2262
|
+
};
|
|
2263
|
+
var DOTNET_GUIDANCE = {
|
|
2264
|
+
"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",
|
|
2265
|
+
"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",
|
|
2266
|
+
"adding-hook": "- .NET equivalent: create a middleware, filter, or hosted service\n- Register in the DI container\n- Follow the single responsibility principle",
|
|
2267
|
+
"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",
|
|
2268
|
+
"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`"
|
|
2269
|
+
};
|
|
2270
|
+
var API_GUIDANCE = {
|
|
2271
|
+
"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",
|
|
2272
|
+
"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",
|
|
2273
|
+
"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",
|
|
2274
|
+
"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",
|
|
2275
|
+
"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`"
|
|
2276
|
+
};
|
|
2277
|
+
var SHARED_GUIDANCE = {
|
|
2278
|
+
"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",
|
|
2279
|
+
"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/`)"
|
|
2280
|
+
};
|
|
2281
|
+
function getActionGuidance(action, projectType) {
|
|
2282
|
+
if (SHARED_GUIDANCE[action]) return SHARED_GUIDANCE[action];
|
|
2283
|
+
switch (projectType) {
|
|
2284
|
+
case "dotnet-webapi":
|
|
2285
|
+
return DOTNET_GUIDANCE[action] ?? null;
|
|
2286
|
+
case "api-service":
|
|
2287
|
+
return API_GUIDANCE[action] ?? null;
|
|
2288
|
+
case "react-vite":
|
|
2289
|
+
case "nextjs-app-router":
|
|
2290
|
+
return FRONTEND_GUIDANCE[action] ?? null;
|
|
2291
|
+
default:
|
|
2292
|
+
return null;
|
|
2293
|
+
}
|
|
2106
2294
|
}
|
|
2107
2295
|
|
|
2108
2296
|
// src/tools/get-open-questions.ts
|
|
2109
|
-
import { z as
|
|
2297
|
+
import { z as z22 } from "zod";
|
|
2110
2298
|
function registerGetOpenQuestions(server, ctx) {
|
|
2111
2299
|
server.tool(
|
|
2112
2300
|
"arcbridge_get_open_questions",
|
|
2113
2301
|
"Surface architectural gaps: untested quality scenarios, building blocks without boundaries, unresolved drift, and tasks missing acceptance criteria.",
|
|
2114
2302
|
{
|
|
2115
|
-
target_dir:
|
|
2116
|
-
scope:
|
|
2303
|
+
target_dir: z22.string().describe("Absolute path to the project directory"),
|
|
2304
|
+
scope: z22.string().optional().describe("Focus scope: 'current-phase', 'building-block:<id>', or omit for project-wide")
|
|
2117
2305
|
},
|
|
2118
2306
|
async (params) => {
|
|
2119
2307
|
const db = ensureDb(ctx, params.target_dir);
|
|
@@ -2223,7 +2411,7 @@ function registerGetOpenQuestions(server, ctx) {
|
|
|
2223
2411
|
}
|
|
2224
2412
|
|
|
2225
2413
|
// src/tools/propose-arc42-update.ts
|
|
2226
|
-
import { z as
|
|
2414
|
+
import { z as z23 } from "zod";
|
|
2227
2415
|
import {
|
|
2228
2416
|
resolveRef,
|
|
2229
2417
|
getChangedFiles,
|
|
@@ -2235,9 +2423,9 @@ function registerProposeArc42Update(server, ctx) {
|
|
|
2235
2423
|
"arcbridge_propose_arc42_update",
|
|
2236
2424
|
"Analyze code changes since a reference point and generate specific, actionable proposals for updating arc42 documentation.",
|
|
2237
2425
|
{
|
|
2238
|
-
target_dir:
|
|
2239
|
-
changes_since:
|
|
2240
|
-
update_sync_point:
|
|
2426
|
+
target_dir: z23.string().describe("Absolute path to the project directory"),
|
|
2427
|
+
changes_since: z23.string().default("last-sync").describe("Reference point: 'last-commit', 'last-sync', 'last-phase', or a git ref"),
|
|
2428
|
+
update_sync_point: z23.boolean().default(false).describe("Update the stored sync commit to HEAD after generating proposals")
|
|
2241
2429
|
},
|
|
2242
2430
|
async (params) => {
|
|
2243
2431
|
const db = ensureDb(ctx, params.target_dir);
|
|
@@ -2425,10 +2613,11 @@ function findCrossBlockConsumers(db, symbolId, sourceBlockId, blocks) {
|
|
|
2425
2613
|
}
|
|
2426
2614
|
|
|
2427
2615
|
// src/tools/get-practice-review.ts
|
|
2428
|
-
import { z as
|
|
2616
|
+
import { z as z24 } from "zod";
|
|
2429
2617
|
import {
|
|
2430
2618
|
resolveRef as resolveRef2,
|
|
2431
2619
|
getChangedFiles as getChangedFiles2,
|
|
2620
|
+
scopeToProject,
|
|
2432
2621
|
detectDrift as detectDrift2
|
|
2433
2622
|
} from "@arcbridge/core";
|
|
2434
2623
|
function registerGetPracticeReview(server, ctx) {
|
|
@@ -2436,15 +2625,16 @@ function registerGetPracticeReview(server, ctx) {
|
|
|
2436
2625
|
"arcbridge_get_practice_review",
|
|
2437
2626
|
"Structured, practice-aware review of recent code changes across 5 dimensions: Architecture, Security, Testing, Documentation, and Complexity.",
|
|
2438
2627
|
{
|
|
2439
|
-
target_dir:
|
|
2440
|
-
since:
|
|
2628
|
+
target_dir: z24.string().describe("Absolute path to the project directory"),
|
|
2629
|
+
since: z24.string().default("last-commit").describe("Reference point: 'last-commit', 'last-session', or 'last-phase'")
|
|
2441
2630
|
},
|
|
2442
2631
|
async (params) => {
|
|
2443
2632
|
const db = ensureDb(ctx, params.target_dir);
|
|
2444
2633
|
if (!db) return notInitialized();
|
|
2445
2634
|
const projectRoot = ctx.projectRoot ?? params.target_dir;
|
|
2446
2635
|
const ref = resolveRef2(projectRoot, params.since, db);
|
|
2447
|
-
const
|
|
2636
|
+
const allChangedFiles = getChangedFiles2(projectRoot, ref.sha);
|
|
2637
|
+
const changedFiles = scopeToProject(allChangedFiles, projectRoot);
|
|
2448
2638
|
if (changedFiles.length === 0) {
|
|
2449
2639
|
return textResult(
|
|
2450
2640
|
`# Practice Review
|
|
@@ -2752,7 +2942,7 @@ function reviewComplexity(db, changedFiles, findings) {
|
|
|
2752
2942
|
}
|
|
2753
2943
|
|
|
2754
2944
|
// src/tools/complete-phase.ts
|
|
2755
|
-
import { z as
|
|
2945
|
+
import { z as z25 } from "zod";
|
|
2756
2946
|
import {
|
|
2757
2947
|
detectDrift as detectDrift3,
|
|
2758
2948
|
writeDriftLog as writeDriftLog2,
|
|
@@ -2761,8 +2951,8 @@ import {
|
|
|
2761
2951
|
inferTaskStatuses,
|
|
2762
2952
|
applyInferences,
|
|
2763
2953
|
verifyScenarios,
|
|
2764
|
-
loadConfig as
|
|
2765
|
-
refreshFromDocs as
|
|
2954
|
+
loadConfig as loadConfig4,
|
|
2955
|
+
refreshFromDocs as refreshFromDocs6,
|
|
2766
2956
|
syncPhaseToYaml,
|
|
2767
2957
|
transaction
|
|
2768
2958
|
} from "@arcbridge/core";
|
|
@@ -2771,17 +2961,17 @@ function registerCompletePhase(server, ctx) {
|
|
|
2771
2961
|
"arcbridge_complete_phase",
|
|
2772
2962
|
"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.",
|
|
2773
2963
|
{
|
|
2774
|
-
target_dir:
|
|
2775
|
-
phase_id:
|
|
2776
|
-
notes:
|
|
2777
|
-
auto_infer:
|
|
2778
|
-
run_tests:
|
|
2964
|
+
target_dir: z25.string().describe("Absolute path to the project directory"),
|
|
2965
|
+
phase_id: z25.string().optional().describe("Phase ID to complete (defaults to current in-progress phase)"),
|
|
2966
|
+
notes: z25.string().optional().describe("Optional notes about this phase completion"),
|
|
2967
|
+
auto_infer: z25.boolean().default(true).describe("Automatically infer task statuses from code state before checking gates"),
|
|
2968
|
+
run_tests: z25.boolean().default(false).describe("Run linked tests for quality scenarios before checking the quality gate")
|
|
2779
2969
|
},
|
|
2780
2970
|
async (params) => {
|
|
2781
2971
|
const start = Date.now();
|
|
2782
2972
|
const db = ensureDb(ctx, params.target_dir);
|
|
2783
2973
|
if (!db) return notInitialized();
|
|
2784
|
-
|
|
2974
|
+
refreshFromDocs6(db, params.target_dir);
|
|
2785
2975
|
let phase;
|
|
2786
2976
|
if (params.phase_id) {
|
|
2787
2977
|
phase = db.prepare("SELECT id, name, phase_number, status, gate_status FROM phases WHERE id = ?").get(params.phase_id);
|
|
@@ -2816,7 +3006,7 @@ function registerCompletePhase(server, ctx) {
|
|
|
2816
3006
|
}
|
|
2817
3007
|
}
|
|
2818
3008
|
const tasks = db.prepare("SELECT id, title, status FROM tasks WHERE phase_id = ?").all(phase.id);
|
|
2819
|
-
const incompleteTasks = tasks.filter((t) => t.status !== "done");
|
|
3009
|
+
const incompleteTasks = tasks.filter((t) => t.status !== "done" && t.status !== "cancelled");
|
|
2820
3010
|
const tasksPass = incompleteTasks.length === 0;
|
|
2821
3011
|
const driftEntries = detectDrift3(db);
|
|
2822
3012
|
writeDriftLog2(db, driftEntries);
|
|
@@ -2826,7 +3016,7 @@ function registerCompletePhase(server, ctx) {
|
|
|
2826
3016
|
const projectRoot = ctx.projectRoot ?? params.target_dir;
|
|
2827
3017
|
let testCommand = "npx vitest run";
|
|
2828
3018
|
let timeoutMs = 6e4;
|
|
2829
|
-
const configResult =
|
|
3019
|
+
const configResult = loadConfig4(params.target_dir);
|
|
2830
3020
|
if (configResult.config) {
|
|
2831
3021
|
testCommand = configResult.config.testing.test_command;
|
|
2832
3022
|
timeoutMs = configResult.config.testing.timeout_ms;
|
|
@@ -2990,18 +3180,18 @@ function registerCompletePhase(server, ctx) {
|
|
|
2990
3180
|
}
|
|
2991
3181
|
|
|
2992
3182
|
// src/tools/activate-role.ts
|
|
2993
|
-
import { z as
|
|
3183
|
+
import { z as z26 } from "zod";
|
|
2994
3184
|
import { loadRole, loadRoles } from "@arcbridge/core";
|
|
2995
3185
|
function registerActivateRole(server, ctx) {
|
|
2996
3186
|
server.tool(
|
|
2997
3187
|
"arcbridge_activate_role",
|
|
2998
3188
|
"Activate an agent role: loads the role's system prompt, required tools, quality focus, and pre-loaded architectural context.",
|
|
2999
3189
|
{
|
|
3000
|
-
target_dir:
|
|
3001
|
-
role:
|
|
3190
|
+
target_dir: z26.string().describe("Absolute path to the project directory"),
|
|
3191
|
+
role: z26.string().describe(
|
|
3002
3192
|
"Role ID to activate (e.g., 'architect', 'implementer', 'security-reviewer')"
|
|
3003
3193
|
),
|
|
3004
|
-
building_block:
|
|
3194
|
+
building_block: z26.string().optional().describe("Focus on a specific building block (for implementer/code-reviewer roles)")
|
|
3005
3195
|
},
|
|
3006
3196
|
async (params) => {
|
|
3007
3197
|
const db = ensureDb(ctx, params.target_dir);
|
|
@@ -3303,18 +3493,18 @@ function getRoleDefinition(roleId) {
|
|
|
3303
3493
|
}
|
|
3304
3494
|
|
|
3305
3495
|
// src/tools/verify-scenarios.ts
|
|
3306
|
-
import { z as
|
|
3307
|
-
import { verifyScenarios as verifyScenarios2, loadConfig as
|
|
3496
|
+
import { z as z27 } from "zod";
|
|
3497
|
+
import { verifyScenarios as verifyScenarios2, loadConfig as loadConfig5 } from "@arcbridge/core";
|
|
3308
3498
|
function registerVerifyScenarios(server, ctx) {
|
|
3309
3499
|
server.tool(
|
|
3310
3500
|
"arcbridge_verify_scenarios",
|
|
3311
3501
|
"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.",
|
|
3312
3502
|
{
|
|
3313
|
-
target_dir:
|
|
3314
|
-
scenario_ids:
|
|
3503
|
+
target_dir: z27.string().describe("Absolute path to the project directory"),
|
|
3504
|
+
scenario_ids: z27.array(z27.string()).optional().describe(
|
|
3315
3505
|
"Specific scenario IDs to verify (e.g., ['SEC-01', 'PERF-01']). If omitted, verifies all automatic scenarios."
|
|
3316
3506
|
),
|
|
3317
|
-
test_command:
|
|
3507
|
+
test_command: z27.string().optional().describe(
|
|
3318
3508
|
"Override the test command from config (e.g., 'npx jest'). File paths are appended as arguments."
|
|
3319
3509
|
)
|
|
3320
3510
|
},
|
|
@@ -3323,7 +3513,7 @@ function registerVerifyScenarios(server, ctx) {
|
|
|
3323
3513
|
if (!db) return notInitialized();
|
|
3324
3514
|
let testCommand = "npx vitest run";
|
|
3325
3515
|
let timeoutMs = 6e4;
|
|
3326
|
-
const configResult =
|
|
3516
|
+
const configResult = loadConfig5(params.target_dir);
|
|
3327
3517
|
if (configResult.config) {
|
|
3328
3518
|
testCommand = configResult.config.testing.test_command;
|
|
3329
3519
|
timeoutMs = configResult.config.testing.timeout_ms;
|
|
@@ -3381,13 +3571,14 @@ function registerVerifyScenarios(server, ctx) {
|
|
|
3381
3571
|
}
|
|
3382
3572
|
|
|
3383
3573
|
// src/tools/run-role-check.ts
|
|
3384
|
-
import { z as
|
|
3574
|
+
import { z as z28 } from "zod";
|
|
3385
3575
|
import {
|
|
3386
3576
|
loadRole as loadRole2,
|
|
3387
3577
|
loadRoles as loadRoles2,
|
|
3388
3578
|
detectDrift as detectDrift4,
|
|
3389
3579
|
resolveRef as resolveRef3,
|
|
3390
|
-
getChangedFiles as getChangedFiles3
|
|
3580
|
+
getChangedFiles as getChangedFiles3,
|
|
3581
|
+
scopeToProject as scopeToProject2
|
|
3391
3582
|
} from "@arcbridge/core";
|
|
3392
3583
|
var SCOPE_VALUES = ["last-commit", "current-phase", "full-project"];
|
|
3393
3584
|
function registerRunRoleCheck(server, ctx) {
|
|
@@ -3395,11 +3586,11 @@ function registerRunRoleCheck(server, ctx) {
|
|
|
3395
3586
|
"arcbridge_run_role_check",
|
|
3396
3587
|
"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.",
|
|
3397
3588
|
{
|
|
3398
|
-
target_dir:
|
|
3399
|
-
role:
|
|
3589
|
+
target_dir: z28.string().describe("Absolute path to the project directory"),
|
|
3590
|
+
role: z28.string().describe(
|
|
3400
3591
|
"Role ID to run checks for (e.g., 'security-reviewer', 'quality-guardian', 'architect', 'phase-manager', 'code-reviewer')"
|
|
3401
3592
|
),
|
|
3402
|
-
scope:
|
|
3593
|
+
scope: z28.enum(SCOPE_VALUES).default("current-phase").describe(
|
|
3403
3594
|
"Scope of analysis: 'last-commit' (recent changes), 'current-phase' (since phase start), 'full-project' (everything)"
|
|
3404
3595
|
)
|
|
3405
3596
|
},
|
|
@@ -3466,7 +3657,8 @@ function getChangedFilesForScope(db, projectRoot, scope) {
|
|
|
3466
3657
|
const since = scope === "last-commit" ? "last-commit" : "last-phase";
|
|
3467
3658
|
const ref = resolveRef3(projectRoot, since, db);
|
|
3468
3659
|
try {
|
|
3469
|
-
|
|
3660
|
+
const files = getChangedFiles3(projectRoot, ref.sha);
|
|
3661
|
+
return scopeToProject2(files, projectRoot);
|
|
3470
3662
|
} catch {
|
|
3471
3663
|
return null;
|
|
3472
3664
|
}
|
|
@@ -3755,7 +3947,7 @@ function runPhaseManagerCheck(db, projectRoot, lines) {
|
|
|
3755
3947
|
}
|
|
3756
3948
|
const ref = resolveRef3(projectRoot, "last-sync", db);
|
|
3757
3949
|
try {
|
|
3758
|
-
const changedFiles = getChangedFiles3(projectRoot, ref.sha);
|
|
3950
|
+
const changedFiles = scopeToProject2(getChangedFiles3(projectRoot, ref.sha), projectRoot);
|
|
3759
3951
|
if (changedFiles.length > 0) {
|
|
3760
3952
|
lines.push(`## Changes Since Last Sync (${ref.label})`, "");
|
|
3761
3953
|
appendChangedFilesList(lines, changedFiles);
|
|
@@ -3818,34 +4010,125 @@ function runCustomRoleCheck(db, lines, roleDef) {
|
|
|
3818
4010
|
appendDriftSection(db, lines);
|
|
3819
4011
|
}
|
|
3820
4012
|
|
|
4013
|
+
// src/tools/update-scenario-status.ts
|
|
4014
|
+
import { z as z29 } from "zod";
|
|
4015
|
+
import { syncScenarioToYaml, transaction as transaction2 } from "@arcbridge/core";
|
|
4016
|
+
function registerUpdateScenarioStatus(server, ctx) {
|
|
4017
|
+
server.tool(
|
|
4018
|
+
"arcbridge_update_scenario_status",
|
|
4019
|
+
"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.",
|
|
4020
|
+
{
|
|
4021
|
+
target_dir: z29.string().describe("Absolute path to the project directory"),
|
|
4022
|
+
scenario_id: z29.string().describe("Quality scenario ID (e.g., 'SEC-01', 'PERF-01')"),
|
|
4023
|
+
status: z29.enum(["passing", "failing", "untested", "partial"]).describe("New status for the scenario"),
|
|
4024
|
+
linked_tests: z29.array(z29.string()).optional().describe(
|
|
4025
|
+
"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'."
|
|
4026
|
+
)
|
|
4027
|
+
},
|
|
4028
|
+
async (params) => {
|
|
4029
|
+
const db = ensureDb(ctx, params.target_dir);
|
|
4030
|
+
if (!db) return notInitialized();
|
|
4031
|
+
const projectRoot = ctx.projectRoot ?? params.target_dir;
|
|
4032
|
+
const scenario = db.prepare("SELECT id, name, status, linked_tests, verification FROM quality_scenarios WHERE id = ?").get(params.scenario_id);
|
|
4033
|
+
if (!scenario) {
|
|
4034
|
+
const available = db.prepare("SELECT id, name, status FROM quality_scenarios ORDER BY id").all();
|
|
4035
|
+
const list = available.length > 0 ? available.map((s) => ` - \`${s.id}\` ${s.name} (${s.status})`).join("\n") : " (none)";
|
|
4036
|
+
return textResult(
|
|
4037
|
+
`Scenario '${params.scenario_id}' not found.
|
|
4038
|
+
|
|
4039
|
+
**Available scenarios:**
|
|
4040
|
+
${list}`
|
|
4041
|
+
);
|
|
4042
|
+
}
|
|
4043
|
+
const oldStatus = scenario.status;
|
|
4044
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
4045
|
+
if (params.linked_tests) {
|
|
4046
|
+
const { isAbsolute, normalize } = await import("path");
|
|
4047
|
+
const invalid = params.linked_tests.filter((t) => {
|
|
4048
|
+
const norm = normalize(t);
|
|
4049
|
+
return isAbsolute(norm) || norm.startsWith("..");
|
|
4050
|
+
});
|
|
4051
|
+
if (invalid.length > 0) {
|
|
4052
|
+
return textResult(
|
|
4053
|
+
`Invalid test paths (must be relative, no '..' segments):
|
|
4054
|
+
${invalid.map((p) => ` - ${p}`).join("\n")}`
|
|
4055
|
+
);
|
|
4056
|
+
}
|
|
4057
|
+
}
|
|
4058
|
+
transaction2(db, () => {
|
|
4059
|
+
db.prepare("UPDATE quality_scenarios SET status = ?, last_checked = ? WHERE id = ?").run(
|
|
4060
|
+
params.status,
|
|
4061
|
+
now,
|
|
4062
|
+
params.scenario_id
|
|
4063
|
+
);
|
|
4064
|
+
if (params.linked_tests) {
|
|
4065
|
+
db.prepare("UPDATE quality_scenarios SET linked_tests = ? WHERE id = ?").run(
|
|
4066
|
+
JSON.stringify(params.linked_tests),
|
|
4067
|
+
params.scenario_id
|
|
4068
|
+
);
|
|
4069
|
+
if (scenario.verification === "manual") {
|
|
4070
|
+
db.prepare("UPDATE quality_scenarios SET verification = 'semi-automatic' WHERE id = ?").run(
|
|
4071
|
+
params.scenario_id
|
|
4072
|
+
);
|
|
4073
|
+
}
|
|
4074
|
+
}
|
|
4075
|
+
});
|
|
4076
|
+
let yamlWarning;
|
|
4077
|
+
try {
|
|
4078
|
+
const newVerification = params.linked_tests && scenario.verification === "manual" ? "semi-automatic" : void 0;
|
|
4079
|
+
syncScenarioToYaml(projectRoot, params.scenario_id, params.status, params.linked_tests, newVerification);
|
|
4080
|
+
} catch (err) {
|
|
4081
|
+
yamlWarning = `YAML sync failed: ${err instanceof Error ? err.message : String(err)}. DB updated but YAML may be out of sync.`;
|
|
4082
|
+
}
|
|
4083
|
+
const lines = [
|
|
4084
|
+
`Scenario **${scenario.id}** (${scenario.name}) updated: ${oldStatus} \u2192 **${params.status}**`
|
|
4085
|
+
];
|
|
4086
|
+
if (params.linked_tests) {
|
|
4087
|
+
lines.push(
|
|
4088
|
+
"",
|
|
4089
|
+
`**Linked tests:** ${params.linked_tests.length} file(s)`,
|
|
4090
|
+
...params.linked_tests.map((t) => ` - ${t}`)
|
|
4091
|
+
);
|
|
4092
|
+
if (scenario.verification === "manual") {
|
|
4093
|
+
lines.push("", "*Verification upgraded from manual to semi-automatic*");
|
|
4094
|
+
}
|
|
4095
|
+
}
|
|
4096
|
+
if (yamlWarning) {
|
|
4097
|
+
lines.push("", `**Warning:** ${yamlWarning}`);
|
|
4098
|
+
}
|
|
4099
|
+
return textResult(lines.join("\n"));
|
|
4100
|
+
}
|
|
4101
|
+
);
|
|
4102
|
+
}
|
|
4103
|
+
|
|
3821
4104
|
// src/tools/record-activity.ts
|
|
3822
|
-
import { z as
|
|
4105
|
+
import { z as z30 } from "zod";
|
|
3823
4106
|
import { insertActivity as insertActivity2, getSessionTotals } from "@arcbridge/core";
|
|
3824
4107
|
function registerRecordActivity(server, ctx) {
|
|
3825
4108
|
server.tool(
|
|
3826
4109
|
"arcbridge_record_activity",
|
|
3827
4110
|
"Record agent activity \u2014 model, tokens, cost, duration, and optional quality snapshot. Use this to track what work was done and measure agent performance.",
|
|
3828
4111
|
{
|
|
3829
|
-
target_dir:
|
|
3830
|
-
tool_name:
|
|
3831
|
-
action:
|
|
3832
|
-
model:
|
|
3833
|
-
agent_role:
|
|
3834
|
-
task_id:
|
|
3835
|
-
phase_id:
|
|
3836
|
-
input_tokens:
|
|
3837
|
-
output_tokens:
|
|
3838
|
-
total_tokens:
|
|
3839
|
-
cost_usd:
|
|
3840
|
-
duration_ms:
|
|
3841
|
-
drift_count:
|
|
3842
|
-
drift_errors:
|
|
3843
|
-
test_pass_count:
|
|
3844
|
-
test_fail_count:
|
|
3845
|
-
lint_clean:
|
|
3846
|
-
typecheck_clean:
|
|
3847
|
-
notes:
|
|
3848
|
-
metadata:
|
|
4112
|
+
target_dir: z30.string().describe("Absolute path to the project directory"),
|
|
4113
|
+
tool_name: z30.string().describe("Name of the tool or action performed (e.g., 'arcbridge_update_task', 'code_edit')"),
|
|
4114
|
+
action: z30.string().optional().describe("Human-readable label (e.g., 'implement login form')"),
|
|
4115
|
+
model: z30.string().optional().describe("Model identifier (e.g., 'claude-sonnet-4-20250514')"),
|
|
4116
|
+
agent_role: z30.string().optional().describe("Active ArcBridge role (e.g., 'implementer')"),
|
|
4117
|
+
task_id: z30.string().optional().describe("Associated task ID"),
|
|
4118
|
+
phase_id: z30.string().optional().describe("Associated phase ID"),
|
|
4119
|
+
input_tokens: z30.number().int().nonnegative().optional().describe("Input/prompt tokens"),
|
|
4120
|
+
output_tokens: z30.number().int().nonnegative().optional().describe("Output/completion tokens"),
|
|
4121
|
+
total_tokens: z30.number().int().nonnegative().optional().describe("Total tokens (auto-computed if input+output given)"),
|
|
4122
|
+
cost_usd: z30.number().nonnegative().optional().describe("Estimated cost in USD"),
|
|
4123
|
+
duration_ms: z30.number().int().nonnegative().optional().describe("Wall-clock duration in ms"),
|
|
4124
|
+
drift_count: z30.number().int().nonnegative().optional().describe("Current drift count"),
|
|
4125
|
+
drift_errors: z30.number().int().nonnegative().optional().describe("Current drift errors"),
|
|
4126
|
+
test_pass_count: z30.number().int().nonnegative().optional().describe("Passing tests"),
|
|
4127
|
+
test_fail_count: z30.number().int().nonnegative().optional().describe("Failing tests"),
|
|
4128
|
+
lint_clean: z30.boolean().optional().describe("Whether lint passes cleanly"),
|
|
4129
|
+
typecheck_clean: z30.boolean().optional().describe("Whether typecheck passes cleanly"),
|
|
4130
|
+
notes: z30.string().optional().describe("Free-form notes"),
|
|
4131
|
+
metadata: z30.record(z30.unknown()).optional().describe("Additional key-value metadata")
|
|
3849
4132
|
},
|
|
3850
4133
|
async (params) => {
|
|
3851
4134
|
const db = ensureDb(ctx, params.target_dir);
|
|
@@ -3900,23 +4183,23 @@ function registerRecordActivity(server, ctx) {
|
|
|
3900
4183
|
}
|
|
3901
4184
|
|
|
3902
4185
|
// src/tools/get-metrics.ts
|
|
3903
|
-
import { z as
|
|
4186
|
+
import { z as z31 } from "zod";
|
|
3904
4187
|
import { queryMetrics } from "@arcbridge/core";
|
|
3905
4188
|
function registerGetMetrics(server, ctx) {
|
|
3906
4189
|
server.tool(
|
|
3907
4190
|
"arcbridge_get_metrics",
|
|
3908
4191
|
"Query agent activity metrics \u2014 filter by model, task, phase, or time range. Group by model/task/phase/tool/day for aggregated views.",
|
|
3909
4192
|
{
|
|
3910
|
-
target_dir:
|
|
3911
|
-
task_id:
|
|
3912
|
-
phase_id:
|
|
3913
|
-
model:
|
|
3914
|
-
agent_role:
|
|
3915
|
-
tool_name:
|
|
3916
|
-
since:
|
|
3917
|
-
until:
|
|
3918
|
-
group_by:
|
|
3919
|
-
limit:
|
|
4193
|
+
target_dir: z31.string().describe("Absolute path to the project directory"),
|
|
4194
|
+
task_id: z31.string().optional().describe("Filter by task ID"),
|
|
4195
|
+
phase_id: z31.string().optional().describe("Filter by phase ID"),
|
|
4196
|
+
model: z31.string().optional().describe("Filter by model name"),
|
|
4197
|
+
agent_role: z31.string().optional().describe("Filter by agent role"),
|
|
4198
|
+
tool_name: z31.string().optional().describe("Filter by tool name"),
|
|
4199
|
+
since: z31.string().optional().describe("ISO 8601 timestamp \u2014 activity after this time"),
|
|
4200
|
+
until: z31.string().optional().describe("ISO 8601 timestamp \u2014 activity before this time"),
|
|
4201
|
+
group_by: z31.enum(["model", "task", "phase", "tool", "day", "none"]).default("none").describe("Group results for aggregation"),
|
|
4202
|
+
limit: z31.number().int().min(1).max(500).default(50).describe("Max rows in detail view (group_by=none)")
|
|
3920
4203
|
},
|
|
3921
4204
|
async (params) => {
|
|
3922
4205
|
const db = ensureDb(ctx, params.target_dir);
|
|
@@ -3999,23 +4282,23 @@ function mdCell(val) {
|
|
|
3999
4282
|
}
|
|
4000
4283
|
|
|
4001
4284
|
// src/tools/export-metrics.ts
|
|
4002
|
-
import { z as
|
|
4285
|
+
import { z as z32 } from "zod";
|
|
4003
4286
|
import { exportMetrics } from "@arcbridge/core";
|
|
4004
4287
|
function registerExportMetrics(server, ctx) {
|
|
4005
4288
|
server.tool(
|
|
4006
4289
|
"arcbridge_export_metrics",
|
|
4007
4290
|
"Export agent activity metrics to a file (JSON, CSV, or Markdown) in .arcbridge/metrics/ for git commits or reporting.",
|
|
4008
4291
|
{
|
|
4009
|
-
target_dir:
|
|
4010
|
-
format:
|
|
4011
|
-
task_id:
|
|
4012
|
-
phase_id:
|
|
4013
|
-
model:
|
|
4014
|
-
agent_role:
|
|
4015
|
-
tool_name:
|
|
4016
|
-
since:
|
|
4017
|
-
until:
|
|
4018
|
-
max_rows:
|
|
4292
|
+
target_dir: z32.string().describe("Absolute path to the project directory"),
|
|
4293
|
+
format: z32.enum(["json", "csv", "markdown"]).default("json").describe("Export format"),
|
|
4294
|
+
task_id: z32.string().optional().describe("Filter by task ID"),
|
|
4295
|
+
phase_id: z32.string().optional().describe("Filter by phase ID"),
|
|
4296
|
+
model: z32.string().optional().describe("Filter by model name"),
|
|
4297
|
+
agent_role: z32.string().optional().describe("Filter by agent role"),
|
|
4298
|
+
tool_name: z32.string().optional().describe("Filter by tool name"),
|
|
4299
|
+
since: z32.string().optional().describe("ISO 8601 \u2014 activity after this time"),
|
|
4300
|
+
until: z32.string().optional().describe("ISO 8601 \u2014 activity before this time"),
|
|
4301
|
+
max_rows: z32.number().int().min(1).default(1e5).describe("Maximum rows to export (default: 100,000)")
|
|
4019
4302
|
},
|
|
4020
4303
|
async (params) => {
|
|
4021
4304
|
const db = ensureDb(ctx, params.target_dir);
|
|
@@ -4063,6 +4346,8 @@ function createArcBridgeServer() {
|
|
|
4063
4346
|
registerGetCurrentTasks(server, ctx);
|
|
4064
4347
|
registerUpdateTask(server, ctx);
|
|
4065
4348
|
registerCreateTask(server, ctx);
|
|
4349
|
+
registerDeleteTask(server, ctx);
|
|
4350
|
+
registerCreatePhase(server, ctx);
|
|
4066
4351
|
registerReindex(server, ctx);
|
|
4067
4352
|
registerSearchSymbols(server, ctx);
|
|
4068
4353
|
registerGetSymbol(server, ctx);
|
|
@@ -4078,6 +4363,7 @@ function createArcBridgeServer() {
|
|
|
4078
4363
|
registerCompletePhase(server, ctx);
|
|
4079
4364
|
registerActivateRole(server, ctx);
|
|
4080
4365
|
registerVerifyScenarios(server, ctx);
|
|
4366
|
+
registerUpdateScenarioStatus(server, ctx);
|
|
4081
4367
|
registerRunRoleCheck(server, ctx);
|
|
4082
4368
|
registerRecordActivity(server, ctx);
|
|
4083
4369
|
registerGetMetrics(server, ctx);
|