@papi-ai/server 0.7.70 → 0.7.72
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/backfill-cycle-metrics.js +13 -3
- package/dist/index.js +367 -94
- package/package.json +1 -1
|
@@ -1621,6 +1621,13 @@ Check PAPI_PROJECT_ID in your .mcp.json config. Find your project ID in the PAPI
|
|
|
1621
1621
|
insertPlanRun(entry) {
|
|
1622
1622
|
return this.invoke("insertPlanRun", [entry]);
|
|
1623
1623
|
}
|
|
1624
|
+
/** task-2940: forwarded per-tool telemetry write. Requires the deployed
|
|
1625
|
+
* data-proxy to carry the matching `insertToolRun` case — until that deploy
|
|
1626
|
+
* lands, hosted callers fail this invoke and the caller swallows it (telemetry
|
|
1627
|
+
* must never fail a tool call). */
|
|
1628
|
+
insertToolRun(entry) {
|
|
1629
|
+
return this.invoke("insertToolRun", [entry]);
|
|
1630
|
+
}
|
|
1624
1631
|
getCostSummary(cycleNumber) {
|
|
1625
1632
|
return this.invoke("getCostSummary", [cycleNumber]);
|
|
1626
1633
|
}
|
|
@@ -1865,9 +1872,12 @@ Check PAPI_PROJECT_ID in your .mcp.json config. Find your project ID in the PAPI
|
|
|
1865
1872
|
return this.invoke("getOwnerIdentity", []);
|
|
1866
1873
|
}
|
|
1867
1874
|
// --- Contributor cohort (task-2029, C288) ---
|
|
1868
|
-
// Owner-only enforcement is BOTH tool-layer (resolveOwnerGate) and
|
|
1869
|
-
//
|
|
1870
|
-
//
|
|
1875
|
+
// Owner-only enforcement is BOTH tool-layer (resolveOwnerGate) and server-side
|
|
1876
|
+
// in the edge function (auth-derived caller vs project owner). task-2442 (C351)
|
|
1877
|
+
// added addContributorByEmail/removeContributorByEmail to the proxy WRITE_METHODS
|
|
1878
|
+
// set too, so a viewer is now rejected at the role-gate wall before the handler —
|
|
1879
|
+
// three layers of defence, since this client runs on the user's machine.
|
|
1880
|
+
// listContributors stays a member-open read (task-2377).
|
|
1871
1881
|
async listContributors() {
|
|
1872
1882
|
return this.invoke("listContributors", []);
|
|
1873
1883
|
}
|
package/dist/index.js
CHANGED
|
@@ -1738,6 +1738,13 @@ Check PAPI_PROJECT_ID in your .mcp.json config. Find your project ID in the PAPI
|
|
|
1738
1738
|
insertPlanRun(entry) {
|
|
1739
1739
|
return this.invoke("insertPlanRun", [entry]);
|
|
1740
1740
|
}
|
|
1741
|
+
/** task-2940: forwarded per-tool telemetry write. Requires the deployed
|
|
1742
|
+
* data-proxy to carry the matching `insertToolRun` case — until that deploy
|
|
1743
|
+
* lands, hosted callers fail this invoke and the caller swallows it (telemetry
|
|
1744
|
+
* must never fail a tool call). */
|
|
1745
|
+
insertToolRun(entry) {
|
|
1746
|
+
return this.invoke("insertToolRun", [entry]);
|
|
1747
|
+
}
|
|
1741
1748
|
getCostSummary(cycleNumber) {
|
|
1742
1749
|
return this.invoke("getCostSummary", [cycleNumber]);
|
|
1743
1750
|
}
|
|
@@ -1982,9 +1989,12 @@ Check PAPI_PROJECT_ID in your .mcp.json config. Find your project ID in the PAPI
|
|
|
1982
1989
|
return this.invoke("getOwnerIdentity", []);
|
|
1983
1990
|
}
|
|
1984
1991
|
// --- Contributor cohort (task-2029, C288) ---
|
|
1985
|
-
// Owner-only enforcement is BOTH tool-layer (resolveOwnerGate) and
|
|
1986
|
-
//
|
|
1987
|
-
//
|
|
1992
|
+
// Owner-only enforcement is BOTH tool-layer (resolveOwnerGate) and server-side
|
|
1993
|
+
// in the edge function (auth-derived caller vs project owner). task-2442 (C351)
|
|
1994
|
+
// added addContributorByEmail/removeContributorByEmail to the proxy WRITE_METHODS
|
|
1995
|
+
// set too, so a viewer is now rejected at the role-gate wall before the handler —
|
|
1996
|
+
// three layers of defence, since this client runs on the user's machine.
|
|
1997
|
+
// listContributors stays a member-open read (task-2377).
|
|
1988
1998
|
async listContributors() {
|
|
1989
1999
|
return this.invoke("listContributors", []);
|
|
1990
2000
|
}
|
|
@@ -16915,7 +16925,9 @@ async function viewBoard(adapter2, phaseFilter, options) {
|
|
|
16915
16925
|
total,
|
|
16916
16926
|
returned: paged.length,
|
|
16917
16927
|
offset,
|
|
16918
|
-
hasMore: offset + paged.length < total
|
|
16928
|
+
hasMore: offset + paged.length < total,
|
|
16929
|
+
totalUnfiltered: allTasks.length,
|
|
16930
|
+
limit
|
|
16919
16931
|
};
|
|
16920
16932
|
}
|
|
16921
16933
|
async function viewBoardSummary(adapter2) {
|
|
@@ -16963,7 +16975,87 @@ async function archiveTasks(adapter2, phases, statuses) {
|
|
|
16963
16975
|
return { archivedCount: result.archivedCount, phases };
|
|
16964
16976
|
}
|
|
16965
16977
|
|
|
16978
|
+
// src/lib/projection.ts
|
|
16979
|
+
function resolveFields(raw, allowed, toolName) {
|
|
16980
|
+
if (raw === void 0 || raw === null) return { ok: true, fields: null };
|
|
16981
|
+
if (typeof raw !== "string") {
|
|
16982
|
+
return { ok: false, error: `${toolName}: fields must be a comma-separated string (got ${typeof raw}).` };
|
|
16983
|
+
}
|
|
16984
|
+
const trimmed = raw.trim();
|
|
16985
|
+
if (trimmed === "") return { ok: true, fields: null };
|
|
16986
|
+
const canonical = new Map(allowed.map((a) => [a.toLowerCase(), a]));
|
|
16987
|
+
const requested = [];
|
|
16988
|
+
for (const part of trimmed.split(",")) {
|
|
16989
|
+
const token = part.trim();
|
|
16990
|
+
if (token === "") continue;
|
|
16991
|
+
const hit = canonical.get(token.toLowerCase());
|
|
16992
|
+
if (!hit) {
|
|
16993
|
+
return {
|
|
16994
|
+
ok: false,
|
|
16995
|
+
error: `${toolName}: unknown field "${token}". Allowed fields: ${allowed.join(", ")}.`
|
|
16996
|
+
};
|
|
16997
|
+
}
|
|
16998
|
+
if (!requested.includes(hit)) requested.push(hit);
|
|
16999
|
+
}
|
|
17000
|
+
if (requested.length === 0) return { ok: true, fields: null };
|
|
17001
|
+
if (!requested.includes("id")) requested.unshift("id");
|
|
17002
|
+
return { ok: true, fields: requested };
|
|
17003
|
+
}
|
|
17004
|
+
function selectColumns(columns, fields) {
|
|
17005
|
+
if (!fields) return columns;
|
|
17006
|
+
const byName = new Map(columns.map((c) => [c.name, c]));
|
|
17007
|
+
return fields.map((f) => byName.get(f)).filter((c) => c !== void 0);
|
|
17008
|
+
}
|
|
17009
|
+
function wantsMeta(raw) {
|
|
17010
|
+
return raw === true || raw === "true";
|
|
17011
|
+
}
|
|
17012
|
+
function formatMeta(meta) {
|
|
17013
|
+
return `**meta:** ${JSON.stringify(meta)}`;
|
|
17014
|
+
}
|
|
17015
|
+
function withMeta(body, meta) {
|
|
17016
|
+
return meta ? `${body}
|
|
17017
|
+
|
|
17018
|
+
${formatMeta(meta)}` : body;
|
|
17019
|
+
}
|
|
17020
|
+
function pad(value, width) {
|
|
17021
|
+
return value.length >= width ? value : value + " ".repeat(width - value.length);
|
|
17022
|
+
}
|
|
17023
|
+
function renderTable(headers, rows) {
|
|
17024
|
+
const widths = headers.map((h, i) => Math.max(h.length, ...rows.map((r) => r[i].length)));
|
|
17025
|
+
const headerLine = headers.map((h, i) => pad(h, widths[i])).join(" | ");
|
|
17026
|
+
const separator = widths.map((w) => "-".repeat(w)).join(" | ");
|
|
17027
|
+
const dataLines = rows.map((row) => row.map((cell, i) => pad(cell, widths[i])).join(" | "));
|
|
17028
|
+
return [headerLine, separator, ...dataLines].join("\n");
|
|
17029
|
+
}
|
|
17030
|
+
|
|
16966
17031
|
// src/tools/board.ts
|
|
17032
|
+
var BOARD_COLUMNS = [
|
|
17033
|
+
{ name: "priority", header: "Priority", value: (t) => t.priority },
|
|
17034
|
+
{ name: "id", header: "Task", value: (t) => t.id },
|
|
17035
|
+
{ name: "title", header: "Summary", value: (t) => truncateTitle(t.title) },
|
|
17036
|
+
{ name: "status", header: "Status", value: (t) => t.status },
|
|
17037
|
+
{ name: "cycle", header: "Cycle", value: (t) => t.cycle != null ? String(t.cycle) : "-" },
|
|
17038
|
+
{ name: "phase", header: "Phase", value: (t) => t.phase ?? "-" },
|
|
17039
|
+
{ name: "module", header: "Module", value: (t) => t.module ?? "-" },
|
|
17040
|
+
{ name: "epic", header: "Epic", value: (t) => t.epic ?? "-" },
|
|
17041
|
+
{ name: "complexity", header: "Effort", value: (t) => t.complexity ?? "-" },
|
|
17042
|
+
{ name: "createdAt", header: "Created", value: (t) => t.createdAt ?? "-" },
|
|
17043
|
+
{ name: "source", header: "Source", value: (t) => t.source ?? "-" }
|
|
17044
|
+
];
|
|
17045
|
+
var BOARD_FIELDS = BOARD_COLUMNS.map((c) => c.name);
|
|
17046
|
+
var BOARD_SINGLE_LABELS = {
|
|
17047
|
+
id: "Task",
|
|
17048
|
+
title: "Title",
|
|
17049
|
+
status: "Status",
|
|
17050
|
+
priority: "Priority",
|
|
17051
|
+
cycle: "Cycle",
|
|
17052
|
+
phase: "Phase",
|
|
17053
|
+
module: "Module",
|
|
17054
|
+
epic: "Epic",
|
|
17055
|
+
complexity: "Effort",
|
|
17056
|
+
createdAt: "Created",
|
|
17057
|
+
source: "Source"
|
|
17058
|
+
};
|
|
16967
17059
|
var boardViewTool = {
|
|
16968
17060
|
name: "board_view",
|
|
16969
17061
|
description: 'View the Board. To find a SPECIFIC task or subset, FILTER FIRST \u2014 do not dump the whole board: pass task_id for one task (full detail), query="<text>" for a title/notes substring match, or cycle=<n> for one cycle. Combine with status/phase. By default shows active tasks only (excludes Done/Cancelled), sorted by priority, limited to 50; titles are truncated in the table (single-task lookup shows the full title). Use status="all" to see everything, mode="summary" for counts only. Does not call the Anthropic API.',
|
|
@@ -17008,6 +17100,14 @@ var boardViewTool = {
|
|
|
17008
17100
|
type: "string",
|
|
17009
17101
|
enum: ["full", "summary"],
|
|
17010
17102
|
description: 'Output mode: "full" (default) shows task table, "summary" shows counts only.'
|
|
17103
|
+
},
|
|
17104
|
+
fields: {
|
|
17105
|
+
type: "string",
|
|
17106
|
+
description: `Optional sparse-field projection \u2014 comma-separated column names, e.g. "title,status". Returns ONLY those columns (id is always included so rows stay addressable) which is far cheaper on session context than the full table. Allowed: ${BOARD_FIELDS.join(", ")}. An unknown name is an error.`
|
|
17107
|
+
},
|
|
17108
|
+
meta: {
|
|
17109
|
+
type: "boolean",
|
|
17110
|
+
description: "Append a {total, filtered, returned, limit, offset} envelope so you can tell a truncated result from a complete one. Default false."
|
|
17011
17111
|
}
|
|
17012
17112
|
},
|
|
17013
17113
|
required: []
|
|
@@ -17172,14 +17272,27 @@ var boardEditTool = {
|
|
|
17172
17272
|
required: ["task_id"]
|
|
17173
17273
|
}
|
|
17174
17274
|
};
|
|
17175
|
-
function pad(value, width) {
|
|
17176
|
-
return value.length >= width ? value : value + " ".repeat(width - value.length);
|
|
17177
|
-
}
|
|
17178
17275
|
var TITLE_MAX = 80;
|
|
17179
17276
|
function truncateTitle(title) {
|
|
17180
17277
|
return title.length > TITLE_MAX ? `${title.slice(0, TITLE_MAX - 1)}\u2026` : title;
|
|
17181
17278
|
}
|
|
17182
|
-
function formatSingleTask(t) {
|
|
17279
|
+
function formatSingleTask(t, fields) {
|
|
17280
|
+
if (fields) {
|
|
17281
|
+
const values2 = {
|
|
17282
|
+
id: t.id,
|
|
17283
|
+
title: t.title,
|
|
17284
|
+
status: t.status,
|
|
17285
|
+
priority: t.priority,
|
|
17286
|
+
cycle: t.cycle != null ? String(t.cycle) : "-",
|
|
17287
|
+
phase: t.phase ?? "-",
|
|
17288
|
+
module: t.module ?? "-",
|
|
17289
|
+
epic: t.epic ?? "-",
|
|
17290
|
+
complexity: t.complexity ?? "-",
|
|
17291
|
+
createdAt: t.createdAt ?? "-",
|
|
17292
|
+
source: t.source ?? "-"
|
|
17293
|
+
};
|
|
17294
|
+
return fields.map((f) => `- **${BOARD_SINGLE_LABELS[f] ?? f}:** ${values2[f]}`).join("\n");
|
|
17295
|
+
}
|
|
17183
17296
|
const lines = [
|
|
17184
17297
|
`**${t.id} \u2014 ${t.title}**`,
|
|
17185
17298
|
"",
|
|
@@ -17196,41 +17309,20 @@ function formatSingleTask(t) {
|
|
|
17196
17309
|
if (t.why?.trim()) lines.push(`- **Why:** ${t.why.trim()}`);
|
|
17197
17310
|
return lines.join("\n");
|
|
17198
17311
|
}
|
|
17199
|
-
function formatBoard(result) {
|
|
17312
|
+
function formatBoard(result, fields) {
|
|
17200
17313
|
if (result.tasks.length === 0) {
|
|
17201
17314
|
return "No tasks found.";
|
|
17202
17315
|
}
|
|
17203
|
-
const
|
|
17204
|
-
const
|
|
17205
|
-
|
|
17206
|
-
t.id,
|
|
17207
|
-
truncateTitle(t.title),
|
|
17208
|
-
t.status,
|
|
17209
|
-
t.cycle != null ? String(t.cycle) : "-",
|
|
17210
|
-
t.phase ?? "-",
|
|
17211
|
-
t.module ?? "-",
|
|
17212
|
-
t.epic ?? "-",
|
|
17213
|
-
t.complexity ?? "-",
|
|
17214
|
-
t.createdAt ?? "-",
|
|
17215
|
-
t.source ?? "-"
|
|
17216
|
-
]);
|
|
17217
|
-
const widths = headers.map(
|
|
17218
|
-
(h, i) => Math.max(h.length, ...rows.map((r) => r[i].length))
|
|
17219
|
-
);
|
|
17220
|
-
const headerLine = headers.map((h, i) => pad(h, widths[i])).join(" | ");
|
|
17221
|
-
const separator = widths.map((w) => "-".repeat(w)).join(" | ");
|
|
17222
|
-
const dataLines = rows.map(
|
|
17223
|
-
(row) => row.map((cell, i) => pad(cell, widths[i])).join(" | ")
|
|
17224
|
-
);
|
|
17316
|
+
const columns = selectColumns(BOARD_COLUMNS, fields);
|
|
17317
|
+
const headers = columns.map((c) => c.header);
|
|
17318
|
+
const rows = result.tasks.map((t) => columns.map((c) => c.value(t)));
|
|
17225
17319
|
const lines = [];
|
|
17226
17320
|
lines.push(`**${result.returned} of ${result.total} tasks**`);
|
|
17227
17321
|
if (result.hasMore) {
|
|
17228
17322
|
lines.push(`_Showing ${result.offset + 1}\u2013${result.offset + result.returned}. Use offset=${result.offset + result.returned} to see more._`);
|
|
17229
17323
|
}
|
|
17230
17324
|
lines.push("");
|
|
17231
|
-
lines.push(
|
|
17232
|
-
lines.push(separator);
|
|
17233
|
-
lines.push(...dataLines);
|
|
17325
|
+
lines.push(renderTable(headers, rows));
|
|
17234
17326
|
return lines.join("\n");
|
|
17235
17327
|
}
|
|
17236
17328
|
function formatSummary(summary) {
|
|
@@ -17252,15 +17344,21 @@ function formatSummary(summary) {
|
|
|
17252
17344
|
}
|
|
17253
17345
|
async function handleBoardView(adapter2, args) {
|
|
17254
17346
|
const mode = args.mode;
|
|
17347
|
+
const projection = resolveFields(args.fields, BOARD_FIELDS, "board_view");
|
|
17348
|
+
if (!projection.ok) return errorResponse(projection.error);
|
|
17349
|
+
const fields = projection.fields;
|
|
17350
|
+
const emitMeta = wantsMeta(args.meta);
|
|
17255
17351
|
if (mode === "summary") {
|
|
17256
17352
|
const summary = await viewBoardSummary(adapter2);
|
|
17257
|
-
|
|
17353
|
+
const meta2 = emitMeta ? { total: summary.total, filtered: summary.total, returned: summary.total, limit: null, offset: 0 } : null;
|
|
17354
|
+
return textResponse(withMeta(formatSummary(summary), meta2));
|
|
17258
17355
|
}
|
|
17259
17356
|
const taskIdArg = args.task_id ?? args.display_id;
|
|
17260
17357
|
if (taskIdArg) {
|
|
17261
17358
|
const task = await adapter2.getTask(taskIdArg);
|
|
17262
17359
|
if (!task) return errorResponse(`Task ${taskIdArg} not found.`);
|
|
17263
|
-
|
|
17360
|
+
const meta2 = emitMeta ? { total: 1, filtered: 1, returned: 1, limit: null, offset: 0 } : null;
|
|
17361
|
+
return textResponse(withMeta(formatSingleTask(task, fields), meta2));
|
|
17264
17362
|
}
|
|
17265
17363
|
const result = await viewBoard(adapter2, void 0, {
|
|
17266
17364
|
phase: args.phase,
|
|
@@ -17270,17 +17368,26 @@ async function handleBoardView(adapter2, args) {
|
|
|
17270
17368
|
query: args.query,
|
|
17271
17369
|
cycle: args.cycle
|
|
17272
17370
|
});
|
|
17273
|
-
let output = formatBoard(result);
|
|
17274
|
-
|
|
17275
|
-
|
|
17276
|
-
|
|
17277
|
-
|
|
17278
|
-
|
|
17279
|
-
|
|
17371
|
+
let output = formatBoard(result, fields);
|
|
17372
|
+
if (!fields) {
|
|
17373
|
+
try {
|
|
17374
|
+
const comments = await adapter2.getRecentTaskComments?.(30);
|
|
17375
|
+
if (comments && comments.length > 0) {
|
|
17376
|
+
const taskIds = new Set(result.tasks.map((t) => t.id));
|
|
17377
|
+
const section = formatTaskComments(comments, taskIds, "**Task Comments:**");
|
|
17378
|
+
if (section) output += "\n" + section;
|
|
17379
|
+
}
|
|
17380
|
+
} catch {
|
|
17280
17381
|
}
|
|
17281
|
-
} catch {
|
|
17282
17382
|
}
|
|
17283
|
-
|
|
17383
|
+
const meta = emitMeta ? {
|
|
17384
|
+
total: result.totalUnfiltered,
|
|
17385
|
+
filtered: result.total,
|
|
17386
|
+
returned: result.returned,
|
|
17387
|
+
limit: result.limit,
|
|
17388
|
+
offset: result.offset
|
|
17389
|
+
} : null;
|
|
17390
|
+
return textResponse(withMeta(output, meta));
|
|
17284
17391
|
}
|
|
17285
17392
|
async function handleBoardDeprioritise(adapter2, args) {
|
|
17286
17393
|
const taskId = args.task_id;
|
|
@@ -21089,6 +21196,18 @@ function clearBuildCheckpoint(key) {
|
|
|
21089
21196
|
} catch {
|
|
21090
21197
|
}
|
|
21091
21198
|
}
|
|
21199
|
+
function writeBuildCheckpointIfLocal(input) {
|
|
21200
|
+
if (!hasLocalWorkspace()) return;
|
|
21201
|
+
writeBuildCheckpoint(input);
|
|
21202
|
+
}
|
|
21203
|
+
function readBuildCheckpointIfLocal(key) {
|
|
21204
|
+
if (!hasLocalWorkspace()) return null;
|
|
21205
|
+
return readBuildCheckpoint(key);
|
|
21206
|
+
}
|
|
21207
|
+
function clearBuildCheckpointIfLocal(key) {
|
|
21208
|
+
if (!hasLocalWorkspace()) return;
|
|
21209
|
+
clearBuildCheckpoint(key);
|
|
21210
|
+
}
|
|
21092
21211
|
function formatResumeNote(cp) {
|
|
21093
21212
|
const files = cp.modifiedFiles.filter((f) => f && f.trim());
|
|
21094
21213
|
const fileList = files.length > 0 ? files.slice(0, 12).map((f) => ` - ${f}`).join("\n") + (files.length > 12 ? `
|
|
@@ -21515,7 +21634,7 @@ async function listBuilds(adapter2, config2) {
|
|
|
21515
21634
|
]);
|
|
21516
21635
|
const currentCycle = health?.totalCycles ?? 0;
|
|
21517
21636
|
if (tasks.length === 0) {
|
|
21518
|
-
return { sorted: [], inProgress: [], backlog: [], blocked: [], warnings, isEmpty: true, noHandoffs: false, currentCycle };
|
|
21637
|
+
return { sorted: [], inProgress: [], backlog: [], blocked: [], warnings, isEmpty: true, noHandoffs: false, currentCycle, totalTasks: tasks.length };
|
|
21519
21638
|
}
|
|
21520
21639
|
const withHandoff = tasks.filter((t) => {
|
|
21521
21640
|
if (!t.buildHandoff) return false;
|
|
@@ -21526,7 +21645,7 @@ async function listBuilds(adapter2, config2) {
|
|
|
21526
21645
|
return true;
|
|
21527
21646
|
});
|
|
21528
21647
|
if (withHandoff.length === 0) {
|
|
21529
|
-
return { sorted: [], inProgress: [], backlog: [], blocked: [], warnings, isEmpty: false, noHandoffs: true, currentCycle };
|
|
21648
|
+
return { sorted: [], inProgress: [], backlog: [], blocked: [], warnings, isEmpty: false, noHandoffs: true, currentCycle, totalTasks: tasks.length };
|
|
21530
21649
|
}
|
|
21531
21650
|
const buildable = [];
|
|
21532
21651
|
const blocked = [];
|
|
@@ -21541,7 +21660,7 @@ async function listBuilds(adapter2, config2) {
|
|
|
21541
21660
|
const inProgress = buildable.filter((t) => t.status === "In Progress");
|
|
21542
21661
|
const backlog = buildable.filter((t) => t.status !== "In Progress");
|
|
21543
21662
|
const sorted = [...inProgress, ...backlog];
|
|
21544
|
-
return { sorted, inProgress, backlog, blocked, warnings, isEmpty: false, noHandoffs: sorted.length === 0 && blocked.length === 0, currentCycle };
|
|
21663
|
+
return { sorted, inProgress, backlog, blocked, warnings, isEmpty: false, noHandoffs: sorted.length === 0 && blocked.length === 0, currentCycle, totalTasks: tasks.length };
|
|
21545
21664
|
}
|
|
21546
21665
|
async function describeTask(adapter2, taskId) {
|
|
21547
21666
|
const task = await adapter2.getTask(taskId);
|
|
@@ -21858,7 +21977,7 @@ async function startBuild(adapter2, config2, taskId, options = {}, clientName) {
|
|
|
21858
21977
|
);
|
|
21859
21978
|
} catch {
|
|
21860
21979
|
}
|
|
21861
|
-
|
|
21980
|
+
writeBuildCheckpointIfLocal({
|
|
21862
21981
|
cwd: config2.projectRoot,
|
|
21863
21982
|
taskId,
|
|
21864
21983
|
branch: getCurrentBranch(config2.projectRoot),
|
|
@@ -22362,12 +22481,14 @@ async function completeBuild(adapter2, config2, taskId, input, options = {}, cli
|
|
|
22362
22481
|
}
|
|
22363
22482
|
} catch {
|
|
22364
22483
|
}
|
|
22365
|
-
|
|
22366
|
-
|
|
22367
|
-
|
|
22484
|
+
if (hasLocalWorkspace()) {
|
|
22485
|
+
try {
|
|
22486
|
+
clearActiveTaskScope(config2.projectRoot);
|
|
22487
|
+
} catch {
|
|
22488
|
+
}
|
|
22368
22489
|
}
|
|
22369
22490
|
try {
|
|
22370
|
-
|
|
22491
|
+
clearBuildCheckpointIfLocal({ cwd: config2.projectRoot, taskId });
|
|
22371
22492
|
} catch {
|
|
22372
22493
|
}
|
|
22373
22494
|
return {
|
|
@@ -22393,7 +22514,7 @@ async function completeBuild(adapter2, config2, taskId, input, options = {}, cli
|
|
|
22393
22514
|
localPreview
|
|
22394
22515
|
};
|
|
22395
22516
|
}
|
|
22396
|
-
async function cancelBuild(adapter2, taskId, reason) {
|
|
22517
|
+
async function cancelBuild(adapter2, taskId, reason, projectRoot) {
|
|
22397
22518
|
const task = await adapter2.getTask(taskId);
|
|
22398
22519
|
if (!task) {
|
|
22399
22520
|
throw new Error(`Task "${taskId}" not found.`);
|
|
@@ -22402,6 +22523,18 @@ async function cancelBuild(adapter2, taskId, reason) {
|
|
|
22402
22523
|
status: "Cancelled",
|
|
22403
22524
|
closureReason: reason
|
|
22404
22525
|
});
|
|
22526
|
+
if (projectRoot) {
|
|
22527
|
+
if (hasLocalWorkspace()) {
|
|
22528
|
+
try {
|
|
22529
|
+
clearActiveTaskScope(projectRoot);
|
|
22530
|
+
} catch {
|
|
22531
|
+
}
|
|
22532
|
+
}
|
|
22533
|
+
try {
|
|
22534
|
+
clearBuildCheckpointIfLocal({ cwd: projectRoot, taskId });
|
|
22535
|
+
} catch {
|
|
22536
|
+
}
|
|
22537
|
+
}
|
|
22405
22538
|
return { task, reason };
|
|
22406
22539
|
}
|
|
22407
22540
|
|
|
@@ -22947,6 +23080,17 @@ async function handleDocReorder(adapter2, args) {
|
|
|
22947
23080
|
|
|
22948
23081
|
// src/tools/build.ts
|
|
22949
23082
|
init_git();
|
|
23083
|
+
var BUILD_LIST_COLUMNS = [
|
|
23084
|
+
{ name: "id", header: "Task", value: (t) => t.id },
|
|
23085
|
+
{ name: "title", header: "Summary", value: (t) => t.title },
|
|
23086
|
+
{ name: "status", header: "Status", value: (t) => t.status },
|
|
23087
|
+
{ name: "priority", header: "Priority", value: (t) => t.priority },
|
|
23088
|
+
{ name: "complexity", header: "Effort", value: (t) => t.complexity ?? "-" },
|
|
23089
|
+
{ name: "module", header: "Module", value: (t) => t.module ?? "-" },
|
|
23090
|
+
{ name: "epic", header: "Epic", value: (t) => t.epic ?? "-" },
|
|
23091
|
+
{ name: "cycle", header: "Cycle", value: (t) => t.cycle != null ? String(t.cycle) : "-" }
|
|
23092
|
+
];
|
|
23093
|
+
var BUILD_LIST_FIELDS = BUILD_LIST_COLUMNS.map((c) => c.name);
|
|
22950
23094
|
var buildListTool = {
|
|
22951
23095
|
name: "build_list",
|
|
22952
23096
|
description: "List cycle tasks that have BUILD HANDOFFs ready for execution. Shows task ID, title, status, priority, and complexity. In Progress tasks appear first, then Backlog. Does not call the Anthropic API.",
|
|
@@ -22956,7 +23100,16 @@ var buildListTool = {
|
|
|
22956
23100
|
properties: {
|
|
22957
23101
|
limit: {
|
|
22958
23102
|
type: "integer",
|
|
22959
|
-
|
|
23103
|
+
minimum: 1,
|
|
23104
|
+
description: "Optional maximum number of tasks to return per section (In Progress / Backlog / Blocked). Omit to return all."
|
|
23105
|
+
},
|
|
23106
|
+
fields: {
|
|
23107
|
+
type: "string",
|
|
23108
|
+
description: `Optional sparse-field projection \u2014 comma-separated column names, e.g. "title,status". Returns a compact table of ONLY those columns (id is always included) instead of the full per-task block, which is far cheaper on session context. Allowed: ${BUILD_LIST_FIELDS.join(", ")}. An unknown name is an error.`
|
|
23109
|
+
},
|
|
23110
|
+
meta: {
|
|
23111
|
+
type: "boolean",
|
|
23112
|
+
description: "Append a {total, filtered, returned, limit, offset} envelope so you can tell a truncated result from a complete one. Default false."
|
|
22960
23113
|
}
|
|
22961
23114
|
},
|
|
22962
23115
|
required: []
|
|
@@ -23221,50 +23374,74 @@ var AD_CONFLICT_GATE_INSTRUCTION = "\n**AD conflict gate:** If this task conflic
|
|
|
23221
23374
|
function hasReportFields(args) {
|
|
23222
23375
|
return !!(args.completed || args.effort || args.estimated_effort || args.surprises || args.discovered_issues || args.architecture_notes);
|
|
23223
23376
|
}
|
|
23224
|
-
async function handleBuildList(adapter2, config2) {
|
|
23377
|
+
async function handleBuildList(adapter2, config2, args = {}) {
|
|
23378
|
+
const projection = resolveFields(args.fields, BUILD_LIST_FIELDS, "build_list");
|
|
23379
|
+
if (!projection.ok) return errorResponse(projection.error);
|
|
23380
|
+
const fields = projection.fields;
|
|
23381
|
+
const emitMeta = wantsMeta(args.meta);
|
|
23382
|
+
const rawLimit = args.limit;
|
|
23383
|
+
const limit = typeof rawLimit === "number" && Number.isInteger(rawLimit) && rawLimit > 0 ? rawLimit : null;
|
|
23384
|
+
const cap = (rows) => limit === null ? rows : rows.slice(0, limit);
|
|
23225
23385
|
const result = await listBuilds(adapter2, config2);
|
|
23226
23386
|
const warningPrefix = result.warnings.length > 0 ? result.warnings.map((w) => `> ${w}`).join("\n") + "\n\n" : "";
|
|
23387
|
+
const emptyMeta = () => emitMeta ? { total: result.totalTasks, filtered: 0, returned: 0, limit, offset: 0 } : null;
|
|
23227
23388
|
if (result.isEmpty) {
|
|
23228
23389
|
if (result.currentCycle === 0) {
|
|
23229
|
-
return textResponse(warningPrefix + `No tasks found \u2014 this looks like a new project. Run \`setup\` to initialise your project, then \`plan\` to create your first cycle
|
|
23390
|
+
return textResponse(withMeta(warningPrefix + `No tasks found \u2014 this looks like a new project. Run \`setup\` to initialise your project, then \`plan\` to create your first cycle.`, emptyMeta()));
|
|
23230
23391
|
}
|
|
23231
|
-
return textResponse(warningPrefix + `**Cycle ${result.currentCycle}** \u2014 Board is empty. Run \`plan\` to create your next cycle
|
|
23392
|
+
return textResponse(withMeta(warningPrefix + `**Cycle ${result.currentCycle}** \u2014 Board is empty. Run \`plan\` to create your next cycle.`, emptyMeta()));
|
|
23232
23393
|
}
|
|
23233
23394
|
if (result.noHandoffs) {
|
|
23234
|
-
return textResponse(warningPrefix + `**Cycle ${result.currentCycle}** \u2014 No tasks with BUILD HANDOFFs. Run \`plan\` to generate your next cycle tasks
|
|
23395
|
+
return textResponse(withMeta(warningPrefix + `**Cycle ${result.currentCycle}** \u2014 No tasks with BUILD HANDOFFs. Run \`plan\` to generate your next cycle tasks.`, emptyMeta()));
|
|
23235
23396
|
}
|
|
23397
|
+
const inProgress = cap(result.inProgress);
|
|
23398
|
+
const backlog = cap(result.backlog);
|
|
23399
|
+
const blocked = cap(result.blocked);
|
|
23236
23400
|
const lines = [];
|
|
23237
23401
|
if (warningPrefix) {
|
|
23238
23402
|
lines.push(warningPrefix.trimEnd(), "");
|
|
23239
23403
|
}
|
|
23240
23404
|
const totalCount = result.sorted.length + result.blocked.length;
|
|
23241
23405
|
lines.push(`**Cycle ${result.currentCycle}** \u2014 ${totalCount} tasks with BUILD HANDOFFs:`, "");
|
|
23242
|
-
if (
|
|
23243
|
-
|
|
23244
|
-
|
|
23245
|
-
lines.push(
|
|
23246
|
-
}
|
|
23247
|
-
|
|
23248
|
-
|
|
23249
|
-
|
|
23250
|
-
|
|
23251
|
-
|
|
23252
|
-
|
|
23253
|
-
|
|
23254
|
-
lines.push(
|
|
23406
|
+
if (fields) {
|
|
23407
|
+
const columns = selectColumns(BUILD_LIST_COLUMNS, fields);
|
|
23408
|
+
const rows = [...inProgress, ...backlog, ...blocked.map((b2) => b2.task)].map((t) => columns.map((c) => c.value(t)));
|
|
23409
|
+
lines.push(renderTable(columns.map((c) => c.header), rows));
|
|
23410
|
+
} else {
|
|
23411
|
+
if (inProgress.length > 0) {
|
|
23412
|
+
lines.push(`## In Progress (${result.inProgress.length})`);
|
|
23413
|
+
lines.push(...inProgress.map(formatListItem));
|
|
23414
|
+
lines.push("");
|
|
23415
|
+
}
|
|
23416
|
+
if (backlog.length > 0) {
|
|
23417
|
+
lines.push(`## Backlog (${result.backlog.length})`);
|
|
23418
|
+
lines.push(...backlog.map(formatListItem));
|
|
23419
|
+
}
|
|
23420
|
+
if (blocked.length > 0) {
|
|
23421
|
+
lines.push("", `## Blocked (${result.blocked.length}) \u2014 waiting on dependencies`);
|
|
23422
|
+
for (const { task, unresolvedDeps } of blocked) {
|
|
23423
|
+
lines.push(`- **${task.id}:** ${task.title}
|
|
23255
23424
|
Waiting on: ${unresolvedDeps.join(", ")}`);
|
|
23425
|
+
}
|
|
23256
23426
|
}
|
|
23257
|
-
|
|
23258
|
-
|
|
23259
|
-
|
|
23260
|
-
|
|
23261
|
-
|
|
23262
|
-
|
|
23263
|
-
|
|
23427
|
+
try {
|
|
23428
|
+
const comments = await adapter2.getRecentTaskComments?.(30);
|
|
23429
|
+
if (comments && comments.length > 0) {
|
|
23430
|
+
const taskIds = new Set([...result.sorted, ...result.blocked.map((b2) => b2.task)].map((t) => t.id));
|
|
23431
|
+
const section = formatTaskComments(comments, taskIds);
|
|
23432
|
+
if (section) lines.push(section);
|
|
23433
|
+
}
|
|
23434
|
+
} catch {
|
|
23264
23435
|
}
|
|
23265
|
-
} catch {
|
|
23266
23436
|
}
|
|
23267
|
-
|
|
23437
|
+
const meta = emitMeta ? {
|
|
23438
|
+
total: result.totalTasks,
|
|
23439
|
+
filtered: totalCount,
|
|
23440
|
+
returned: inProgress.length + backlog.length + blocked.length,
|
|
23441
|
+
limit,
|
|
23442
|
+
offset: 0
|
|
23443
|
+
} : null;
|
|
23444
|
+
return textResponse(withMeta(lines.join("\n"), meta));
|
|
23268
23445
|
}
|
|
23269
23446
|
async function handleBuildDescribe(adapter2, args) {
|
|
23270
23447
|
const taskId = args.task_id;
|
|
@@ -23318,7 +23495,7 @@ async function handleBuildExecute(adapter2, config2, args, clientName) {
|
|
|
23318
23495
|
}
|
|
23319
23496
|
let resumeNote = "";
|
|
23320
23497
|
if (scopeTask?.status === "In Progress") {
|
|
23321
|
-
const existing =
|
|
23498
|
+
const existing = readBuildCheckpointIfLocal({ cwd: config2.projectRoot, taskId });
|
|
23322
23499
|
if (existing) resumeNote = formatResumeNote(existing);
|
|
23323
23500
|
}
|
|
23324
23501
|
await tracker.recordStep("started");
|
|
@@ -23739,7 +23916,7 @@ function formatCompleteResult(result) {
|
|
|
23739
23916
|
}
|
|
23740
23917
|
return lines.join("\n");
|
|
23741
23918
|
}
|
|
23742
|
-
async function handleBuildCancel(adapter2, args) {
|
|
23919
|
+
async function handleBuildCancel(adapter2, config2, args) {
|
|
23743
23920
|
const taskId = args.task_id;
|
|
23744
23921
|
if (!taskId) {
|
|
23745
23922
|
return errorResponse("task_id is required.");
|
|
@@ -23749,7 +23926,7 @@ async function handleBuildCancel(adapter2, args) {
|
|
|
23749
23926
|
return errorResponse("reason is required.");
|
|
23750
23927
|
}
|
|
23751
23928
|
try {
|
|
23752
|
-
const result = await cancelBuild(adapter2, taskId, reason);
|
|
23929
|
+
const result = await cancelBuild(adapter2, taskId, reason, config2.projectRoot);
|
|
23753
23930
|
return textResponse(`Cancelled **${result.task.id}** (${result.task.title}).
|
|
23754
23931
|
|
|
23755
23932
|
Reason: ${result.reason}`);
|
|
@@ -28677,7 +28854,7 @@ ${section}`;
|
|
|
28677
28854
|
}
|
|
28678
28855
|
tracker.mark("format-summary");
|
|
28679
28856
|
const deepHint = deepHousekeeping ? "" : "\n\n*Tip: pass `full: true` for Research Signals + version-drift, or `deep_housekeeping: true` to also check orphaned branches, merged-but-In-Progress tasks, unrecorded commits, unregistered docs, and stale skill forks (implies `full`).*";
|
|
28680
|
-
return textResponse(projectBannerNote + formatOrientSummary(healthResult, buildInfo, hierarchy, latestTag, config2.projectRoot, environment, subAgents, projectName, teamSummary, clientName, carryForwardRefs) + unblockNote + deferredGateNote + feedbackResolvedNote + alertsNote + ttfvNote + reconciliationNote + mergedInProgressNote + unrecordedNote + unregisteredDocsNote + staleSkillsNote + researchSignalsNote + recsNote + pendingReviewNote + patternsNote + unactionedIssuesNote + skillProposalsNote + sessionGuidanceNote + onboardingCoachingNote + versionNote + enrichmentNote + deliveryShapeNote + preBuildCheckNote + deepHint + enrichmentFilesSection);
|
|
28857
|
+
return { ...textResponse(projectBannerNote + formatOrientSummary(healthResult, buildInfo, hierarchy, latestTag, config2.projectRoot, environment, subAgents, projectName, teamSummary, clientName, carryForwardRefs) + unblockNote + deferredGateNote + feedbackResolvedNote + alertsNote + ttfvNote + reconciliationNote + mergedInProgressNote + unrecordedNote + unregisteredDocsNote + staleSkillsNote + researchSignalsNote + recsNote + pendingReviewNote + patternsNote + unactionedIssuesNote + skillProposalsNote + sessionGuidanceNote + onboardingCoachingNote + versionNote + enrichmentNote + deliveryShapeNote + preBuildCheckNote + deepHint + enrichmentFilesSection), _cycleNumber: healthResult.cycleNumber };
|
|
28681
28858
|
} catch (err) {
|
|
28682
28859
|
const message = err instanceof Error ? err.message : String(err);
|
|
28683
28860
|
const isKnownFriendly = /^(Orient failed|Project not found|No project|Setup required)/i.test(message);
|
|
@@ -29781,6 +29958,15 @@ function readLlmResponse(args) {
|
|
|
29781
29958
|
}
|
|
29782
29959
|
|
|
29783
29960
|
// src/tools/ad-view.ts
|
|
29961
|
+
var AD_COLUMNS = [
|
|
29962
|
+
{ name: "id", header: "AD", value: (d) => d.id },
|
|
29963
|
+
{ name: "title", header: "Title", value: (d) => d.title },
|
|
29964
|
+
{ name: "confidence", header: "Confidence", value: (d) => d.confidence },
|
|
29965
|
+
{ name: "superseded", header: "Superseded", value: (d) => d.superseded ? "yes" : "no" },
|
|
29966
|
+
{ name: "supersededBy", header: "Superseded By", value: (d) => d.supersededBy ?? "-" },
|
|
29967
|
+
{ name: "body", header: "Body", value: (d) => d.body.replace(/\s+/g, " ").trim() }
|
|
29968
|
+
];
|
|
29969
|
+
var AD_FIELDS = AD_COLUMNS.map((c) => c.name);
|
|
29784
29970
|
var adViewTool = {
|
|
29785
29971
|
name: "ad_view",
|
|
29786
29972
|
description: "View one or all Active Decisions with full bodies. Use when you need to read the complete reasoning and evidence behind a specific AD before running strategy_change.",
|
|
@@ -29795,6 +29981,24 @@ var adViewTool = {
|
|
|
29795
29981
|
include_superseded: {
|
|
29796
29982
|
type: "boolean",
|
|
29797
29983
|
description: "Include superseded (retired) ADs. Default false."
|
|
29984
|
+
},
|
|
29985
|
+
fields: {
|
|
29986
|
+
type: "string",
|
|
29987
|
+
description: `Optional sparse-field projection \u2014 comma-separated field names, e.g. "title,confidence". Returns a compact table of ONLY those fields (id is always included) instead of full AD bodies, which is far cheaper on session context when you just need the index. Allowed: ${AD_FIELDS.join(", ")}. An unknown name is an error.`
|
|
29988
|
+
},
|
|
29989
|
+
limit: {
|
|
29990
|
+
type: "integer",
|
|
29991
|
+
minimum: 1,
|
|
29992
|
+
description: "Max decisions to return. Omit to return all."
|
|
29993
|
+
},
|
|
29994
|
+
offset: {
|
|
29995
|
+
type: "integer",
|
|
29996
|
+
minimum: 0,
|
|
29997
|
+
description: "Skip the first N decisions (pagination). Default 0."
|
|
29998
|
+
},
|
|
29999
|
+
meta: {
|
|
30000
|
+
type: "boolean",
|
|
30001
|
+
description: "Append a {total, filtered, returned, limit, offset} envelope so you can tell a truncated result from a complete one. Default false."
|
|
29798
30002
|
}
|
|
29799
30003
|
},
|
|
29800
30004
|
required: []
|
|
@@ -29803,6 +30007,14 @@ var adViewTool = {
|
|
|
29803
30007
|
async function handleAdView(adapter2, args) {
|
|
29804
30008
|
const adId = args.ad_id;
|
|
29805
30009
|
const includeSuperseded = args.include_superseded === true;
|
|
30010
|
+
const projection = resolveFields(args.fields, AD_FIELDS, "ad_view");
|
|
30011
|
+
if (!projection.ok) return errorResponse(projection.error);
|
|
30012
|
+
const fields = projection.fields;
|
|
30013
|
+
const emitMeta = wantsMeta(args.meta);
|
|
30014
|
+
const rawLimit = args.limit;
|
|
30015
|
+
const limit = typeof rawLimit === "number" && Number.isInteger(rawLimit) && rawLimit > 0 ? rawLimit : null;
|
|
30016
|
+
const rawOffset = args.offset;
|
|
30017
|
+
const offset = typeof rawOffset === "number" && Number.isInteger(rawOffset) && rawOffset > 0 ? rawOffset : 0;
|
|
29806
30018
|
let decisions;
|
|
29807
30019
|
try {
|
|
29808
30020
|
decisions = await adapter2.getActiveDecisions({ includeRetired: includeSuperseded });
|
|
@@ -29815,26 +30027,79 @@ async function handleAdView(adapter2, args) {
|
|
|
29815
30027
|
const ids = decisions.map((d) => d.id).join(", ");
|
|
29816
30028
|
return errorResponse(`AD not found: ${adId}. Available: ${ids || "none"}`);
|
|
29817
30029
|
}
|
|
30030
|
+
const meta2 = emitMeta ? { total: decisions.length, filtered: 1, returned: 1, limit: null, offset: 0 } : null;
|
|
30031
|
+
if (fields) {
|
|
30032
|
+
const columns = selectColumns(AD_COLUMNS, fields);
|
|
30033
|
+
const body = columns.map((c) => `- **${c.header}:** ${c.value(target)}`).join("\n");
|
|
30034
|
+
return textResponse(withMeta(body, meta2));
|
|
30035
|
+
}
|
|
29818
30036
|
const supersededNote = target.superseded ? ` [SUPERSEDED by ${target.supersededBy}]` : "";
|
|
29819
30037
|
return textResponse(
|
|
29820
|
-
`## ${target.id}: ${target.title} [${target.confidence}]${supersededNote}
|
|
30038
|
+
withMeta(`## ${target.id}: ${target.title} [${target.confidence}]${supersededNote}
|
|
29821
30039
|
|
|
29822
|
-
${target.body}
|
|
30040
|
+
${target.body}`, meta2)
|
|
29823
30041
|
);
|
|
29824
30042
|
}
|
|
29825
30043
|
const filtered = includeSuperseded ? decisions : decisions.filter((d) => !d.superseded);
|
|
29826
|
-
|
|
29827
|
-
|
|
30044
|
+
const paged = limit === null && offset === 0 ? filtered : filtered.slice(offset, limit === null ? void 0 : offset + limit);
|
|
30045
|
+
const meta = emitMeta ? { total: decisions.length, filtered: filtered.length, returned: paged.length, limit, offset } : null;
|
|
30046
|
+
if (paged.length === 0) {
|
|
30047
|
+
return textResponse(withMeta("No active decisions found.", meta));
|
|
30048
|
+
}
|
|
30049
|
+
if (fields) {
|
|
30050
|
+
const columns = selectColumns(AD_COLUMNS, fields);
|
|
30051
|
+
const rows = paged.map((d) => columns.map((c) => c.value(d)));
|
|
30052
|
+
return textResponse(withMeta(renderTable(columns.map((c) => c.header), rows), meta));
|
|
29828
30053
|
}
|
|
29829
|
-
const formatted =
|
|
30054
|
+
const formatted = paged.map((d) => {
|
|
29830
30055
|
const supersededNote = d.superseded ? ` [SUPERSEDED by ${d.supersededBy}]` : "";
|
|
29831
30056
|
return `## ${d.id}: ${d.title} [${d.confidence}]${supersededNote}
|
|
29832
30057
|
|
|
29833
30058
|
${d.body}`;
|
|
29834
30059
|
}).join("\n\n---\n\n");
|
|
29835
|
-
return textResponse(`# Active Decisions (${
|
|
30060
|
+
return textResponse(withMeta(`# Active Decisions (${paged.length})
|
|
29836
30061
|
|
|
29837
|
-
${formatted}
|
|
30062
|
+
${formatted}`, meta));
|
|
30063
|
+
}
|
|
30064
|
+
|
|
30065
|
+
// src/lib/tool-telemetry.ts
|
|
30066
|
+
var INSTRUMENTED_TOOLS = /* @__PURE__ */ new Set([
|
|
30067
|
+
"plan",
|
|
30068
|
+
"orient",
|
|
30069
|
+
"build_execute",
|
|
30070
|
+
"review_submit",
|
|
30071
|
+
"release",
|
|
30072
|
+
"strategy_review"
|
|
30073
|
+
]);
|
|
30074
|
+
var WRAPPER_INSTRUMENTED_TOOLS = new Set(
|
|
30075
|
+
[...INSTRUMENTED_TOOLS].filter((t) => t !== "plan")
|
|
30076
|
+
);
|
|
30077
|
+
function measureResultBytes(content) {
|
|
30078
|
+
let total = 0;
|
|
30079
|
+
for (const part of content) {
|
|
30080
|
+
if (typeof part.text === "string") total += Buffer.byteLength(part.text, "utf-8");
|
|
30081
|
+
}
|
|
30082
|
+
return total;
|
|
30083
|
+
}
|
|
30084
|
+
function recordToolRun(adapter2, sample) {
|
|
30085
|
+
if (!WRAPPER_INSTRUMENTED_TOOLS.has(sample.toolName)) return;
|
|
30086
|
+
if (typeof adapter2.insertToolRun !== "function") return;
|
|
30087
|
+
try {
|
|
30088
|
+
const promise = adapter2.insertToolRun({
|
|
30089
|
+
toolName: sample.toolName,
|
|
30090
|
+
contextBytes: sample.contextBytes,
|
|
30091
|
+
durationMs: sample.durationMs,
|
|
30092
|
+
cycleNumber: sample.cycleNumber ?? null,
|
|
30093
|
+
source: "mcp-server"
|
|
30094
|
+
});
|
|
30095
|
+
if (promise && typeof promise.catch === "function") {
|
|
30096
|
+
promise.catch((err) => {
|
|
30097
|
+
console.error(`[telemetry] insertToolRun failed (non-blocking): ${err instanceof Error ? err.message : String(err)}`);
|
|
30098
|
+
});
|
|
30099
|
+
}
|
|
30100
|
+
} catch (err) {
|
|
30101
|
+
console.error(`[telemetry] insertToolRun threw (non-blocking): ${err instanceof Error ? err.message : String(err)}`);
|
|
30102
|
+
}
|
|
29838
30103
|
}
|
|
29839
30104
|
|
|
29840
30105
|
// src/tools/learning-action.ts
|
|
@@ -31131,13 +31396,13 @@ function createServer(adapter2, config2) {
|
|
|
31131
31396
|
case "setup":
|
|
31132
31397
|
return handleSetup(adapter2, config2, safeArgs, server2.getClientVersion()?.name);
|
|
31133
31398
|
case "build_list":
|
|
31134
|
-
return handleBuildList(adapter2, config2);
|
|
31399
|
+
return handleBuildList(adapter2, config2, safeArgs);
|
|
31135
31400
|
case "build_describe":
|
|
31136
31401
|
return handleBuildDescribe(adapter2, safeArgs);
|
|
31137
31402
|
case "build_execute":
|
|
31138
31403
|
return handleBuildExecute(adapter2, config2, safeArgs, server2.getClientVersion()?.name);
|
|
31139
31404
|
case "build_cancel":
|
|
31140
|
-
return handleBuildCancel(adapter2, safeArgs);
|
|
31405
|
+
return handleBuildCancel(adapter2, config2, safeArgs);
|
|
31141
31406
|
case "idea":
|
|
31142
31407
|
return handleIdea(adapter2, config2, safeArgs);
|
|
31143
31408
|
case "backlog_import":
|
|
@@ -31260,9 +31525,17 @@ ${usageLine(decision.usage)}`;
|
|
|
31260
31525
|
const usage = result._usage;
|
|
31261
31526
|
const contextBytes = result._contextBytes;
|
|
31262
31527
|
const contextUtilisation = result._contextUtilisation;
|
|
31528
|
+
const toolCycleNumber = result._cycleNumber;
|
|
31263
31529
|
delete result._usage;
|
|
31264
31530
|
delete result._contextBytes;
|
|
31265
31531
|
delete result._contextUtilisation;
|
|
31532
|
+
delete result._cycleNumber;
|
|
31533
|
+
recordToolRun(adapter2, {
|
|
31534
|
+
toolName: name,
|
|
31535
|
+
contextBytes: measureResultBytes(result.content),
|
|
31536
|
+
durationMs: elapsed,
|
|
31537
|
+
cycleNumber: toolCycleNumber ?? null
|
|
31538
|
+
});
|
|
31266
31539
|
const isError = result.content.some((c) => c.text.startsWith("Error:") || c.text.startsWith("\u274C"));
|
|
31267
31540
|
recordToolOutcome(!isError, callerKey);
|
|
31268
31541
|
try {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@papi-ai/server",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.72",
|
|
4
4
|
"description": "PAPI MCP server — AI-powered sprint planning, build execution, and strategy review for software projects",
|
|
5
5
|
"license": "Elastic-2.0",
|
|
6
6
|
"mcpName": "io.github.getpapi/papi",
|