open-coleslaw 0.6.6 → 0.6.8
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 +2 -1
- package/dist/index.js +226 -0
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -157,13 +157,14 @@ See [`CLAUDE.md`](CLAUDE.md) and [`docs/smoke-tests.md`](docs/smoke-tests.md) be
|
|
|
157
157
|
---
|
|
158
158
|
|
|
159
159
|
<details>
|
|
160
|
-
<summary><strong>🛠
|
|
160
|
+
<summary><strong>🛠 16 MCP tools</strong> (the pipeline calls these — you don't)</summary>
|
|
161
161
|
|
|
162
162
|
| Tool | What it does |
|
|
163
163
|
|------|-------------|
|
|
164
164
|
| `start-meeting` | Creates a meeting record (kickoff / design / verify-retry) |
|
|
165
165
|
| `add-transcript` | Saves a speaker's turn |
|
|
166
166
|
| `generate-minutes` | Writes PRD minutes from transcripts |
|
|
167
|
+
| `update-mvps` | Upserts the MVP list or patches one MVP's status; powers the dashboard sidebar |
|
|
167
168
|
| `get-meeting-status` | Reads meeting progress |
|
|
168
169
|
| `get-minutes` | Retrieves full / summary / tasks-only minutes |
|
|
169
170
|
| `execute-tasks` | Returns the structured task list from minutes for worker dispatch |
|
package/dist/index.js
CHANGED
|
@@ -503,6 +503,25 @@ function getMinutesByMeeting(meetingId) {
|
|
|
503
503
|
const row = db.prepare("SELECT * FROM minutes WHERE meeting_id = ?").get(meetingId);
|
|
504
504
|
return row ? rowToMinutes(row) : null;
|
|
505
505
|
}
|
|
506
|
+
function updateMinutes(meetingId, updates) {
|
|
507
|
+
const db = getDb();
|
|
508
|
+
const existing = getMinutesByMeeting(meetingId);
|
|
509
|
+
if (!existing) return null;
|
|
510
|
+
const fields = [];
|
|
511
|
+
const values = [];
|
|
512
|
+
if (updates.content !== void 0) {
|
|
513
|
+
fields.push("content = ?");
|
|
514
|
+
values.push(updates.content);
|
|
515
|
+
}
|
|
516
|
+
if (updates.actionItems !== void 0) {
|
|
517
|
+
fields.push("action_items = ?");
|
|
518
|
+
values.push(JSON.stringify(updates.actionItems));
|
|
519
|
+
}
|
|
520
|
+
if (fields.length === 0) return existing;
|
|
521
|
+
values.push(meetingId);
|
|
522
|
+
db.prepare(`UPDATE minutes SET ${fields.join(", ")} WHERE meeting_id = ?`).run(...values);
|
|
523
|
+
return getMinutesByMeeting(meetingId);
|
|
524
|
+
}
|
|
506
525
|
|
|
507
526
|
// src/storage/task-store.ts
|
|
508
527
|
function getTasksFromMinutes(meetingId) {
|
|
@@ -514,6 +533,73 @@ function getTasksFromMinutes(meetingId) {
|
|
|
514
533
|
|
|
515
534
|
// src/storage/mvp-store.ts
|
|
516
535
|
import { v4 as uuidv46 } from "uuid";
|
|
536
|
+
function rowToMvp(row) {
|
|
537
|
+
return {
|
|
538
|
+
id: row.id,
|
|
539
|
+
kickoffMeetingId: row.kickoff_meeting_id,
|
|
540
|
+
title: row.title,
|
|
541
|
+
goal: row.goal,
|
|
542
|
+
status: row.status,
|
|
543
|
+
orderIndex: row.order_index,
|
|
544
|
+
designMeetingId: row.design_meeting_id,
|
|
545
|
+
createdAt: row.created_at,
|
|
546
|
+
completedAt: row.completed_at
|
|
547
|
+
};
|
|
548
|
+
}
|
|
549
|
+
function createMvp(input) {
|
|
550
|
+
const db = getDb();
|
|
551
|
+
const id = input.id ?? uuidv46();
|
|
552
|
+
const createdAt = Date.now();
|
|
553
|
+
const status = input.status ?? "pending";
|
|
554
|
+
db.prepare(
|
|
555
|
+
`INSERT INTO mvps (id, kickoff_meeting_id, title, goal, status, order_index, created_at)
|
|
556
|
+
VALUES (?, ?, ?, ?, ?, ?, ?)`
|
|
557
|
+
).run(id, input.kickoffMeetingId, input.title, input.goal, status, input.orderIndex, createdAt);
|
|
558
|
+
return {
|
|
559
|
+
id,
|
|
560
|
+
kickoffMeetingId: input.kickoffMeetingId,
|
|
561
|
+
title: input.title,
|
|
562
|
+
goal: input.goal,
|
|
563
|
+
status,
|
|
564
|
+
orderIndex: input.orderIndex,
|
|
565
|
+
designMeetingId: null,
|
|
566
|
+
createdAt,
|
|
567
|
+
completedAt: null
|
|
568
|
+
};
|
|
569
|
+
}
|
|
570
|
+
function getMvp(id) {
|
|
571
|
+
const db = getDb();
|
|
572
|
+
const row = db.prepare("SELECT * FROM mvps WHERE id = ?").get(id);
|
|
573
|
+
return row ? rowToMvp(row) : null;
|
|
574
|
+
}
|
|
575
|
+
function listMvpsByKickoff(kickoffMeetingId) {
|
|
576
|
+
const db = getDb();
|
|
577
|
+
const rows = db.prepare("SELECT * FROM mvps WHERE kickoff_meeting_id = ? ORDER BY order_index ASC").all(kickoffMeetingId);
|
|
578
|
+
return rows.map(rowToMvp);
|
|
579
|
+
}
|
|
580
|
+
function updateMvp(id, updates) {
|
|
581
|
+
const db = getDb();
|
|
582
|
+
const current = getMvp(id);
|
|
583
|
+
if (!current) return null;
|
|
584
|
+
const fields = [];
|
|
585
|
+
const values = [];
|
|
586
|
+
if (updates.status !== void 0) {
|
|
587
|
+
fields.push("status = ?");
|
|
588
|
+
values.push(updates.status);
|
|
589
|
+
}
|
|
590
|
+
if (updates.designMeetingId !== void 0) {
|
|
591
|
+
fields.push("design_meeting_id = ?");
|
|
592
|
+
values.push(updates.designMeetingId);
|
|
593
|
+
}
|
|
594
|
+
if (updates.completedAt !== void 0) {
|
|
595
|
+
fields.push("completed_at = ?");
|
|
596
|
+
values.push(updates.completedAt);
|
|
597
|
+
}
|
|
598
|
+
if (fields.length === 0) return current;
|
|
599
|
+
values.push(id);
|
|
600
|
+
db.prepare(`UPDATE mvps SET ${fields.join(", ")} WHERE id = ?`).run(...values);
|
|
601
|
+
return getMvp(id);
|
|
602
|
+
}
|
|
517
603
|
|
|
518
604
|
// src/types/agent.ts
|
|
519
605
|
var DEPARTMENT_TOOLS = {
|
|
@@ -2682,6 +2768,44 @@ var MeetingRunner = class {
|
|
|
2682
2768
|
if (!meeting) {
|
|
2683
2769
|
throw new Error(`Meeting not found: ${this.meetingId}`);
|
|
2684
2770
|
}
|
|
2771
|
+
const existing = getMinutesByMeeting(this.meetingId);
|
|
2772
|
+
if (existing) {
|
|
2773
|
+
const newEntries = getTranscript(this.meetingId).filter(
|
|
2774
|
+
(e) => e.createdAt > existing.createdAt
|
|
2775
|
+
);
|
|
2776
|
+
if (newEntries.length === 0) {
|
|
2777
|
+
logger.info("generateMinutes: no new transcripts since last minutes; returning existing", {
|
|
2778
|
+
meetingId: this.meetingId
|
|
2779
|
+
});
|
|
2780
|
+
return existing.id;
|
|
2781
|
+
}
|
|
2782
|
+
const appended = [];
|
|
2783
|
+
appended.push(existing.content.replace(/\s+$/, ""));
|
|
2784
|
+
appended.push("");
|
|
2785
|
+
appended.push(
|
|
2786
|
+
`## Follow-up Discussion \u2014 ${(/* @__PURE__ */ new Date()).toISOString()}`
|
|
2787
|
+
);
|
|
2788
|
+
appended.push("");
|
|
2789
|
+
for (const e of newEntries) {
|
|
2790
|
+
const stanceNote = e.agendaItemIndex === -3 ? "_(user comment)_" : e.agendaItemIndex === -2 ? "_(synthesis)_" : "";
|
|
2791
|
+
appended.push(
|
|
2792
|
+
`**${e.speakerRole}** (round ${e.roundNumber}) ${stanceNote}:`
|
|
2793
|
+
);
|
|
2794
|
+
appended.push(e.content);
|
|
2795
|
+
appended.push("");
|
|
2796
|
+
}
|
|
2797
|
+
const newContent = appended.join("\n");
|
|
2798
|
+
updateMinutes(this.meetingId, { content: newContent });
|
|
2799
|
+
updateMeeting(this.meetingId, {
|
|
2800
|
+
status: "completed",
|
|
2801
|
+
completedAt: Date.now()
|
|
2802
|
+
});
|
|
2803
|
+
logger.info("Minutes appended with follow-up discussion", {
|
|
2804
|
+
meetingId: this.meetingId,
|
|
2805
|
+
newEntries: newEntries.length
|
|
2806
|
+
});
|
|
2807
|
+
return existing.id;
|
|
2808
|
+
}
|
|
2685
2809
|
updateMeeting(this.meetingId, {
|
|
2686
2810
|
phase: "minutes-generation",
|
|
2687
2811
|
status: "minutes-generation"
|
|
@@ -2883,6 +3007,102 @@ function extractSection(md, heading) {
|
|
|
2883
3007
|
return out;
|
|
2884
3008
|
}
|
|
2885
3009
|
|
|
3010
|
+
// src/tools/update-mvps.ts
|
|
3011
|
+
import { z as z15 } from "zod";
|
|
3012
|
+
var mvpInput = z15.object({
|
|
3013
|
+
id: z15.string().min(1).describe('Stable MVP id (e.g., "mvp-1")'),
|
|
3014
|
+
title: z15.string().min(1),
|
|
3015
|
+
goal: z15.string(),
|
|
3016
|
+
status: z15.enum(["pending", "in-progress", "done", "blocked"]),
|
|
3017
|
+
orderIndex: z15.number().int().min(0)
|
|
3018
|
+
});
|
|
3019
|
+
var patchInput = z15.object({
|
|
3020
|
+
id: z15.string().min(1),
|
|
3021
|
+
status: z15.enum(["pending", "in-progress", "done", "blocked"]).optional()
|
|
3022
|
+
});
|
|
3023
|
+
var updateMvpsSchema = {
|
|
3024
|
+
kickoffMeetingId: z15.string().optional().describe("The kickoff meeting id these MVPs belong to. Required when `mvps` is provided."),
|
|
3025
|
+
mvps: z15.array(mvpInput).optional().describe(
|
|
3026
|
+
"Full MVP list to upsert. Use after the kickoff decompose step. Replaces the entire per-kickoff list."
|
|
3027
|
+
),
|
|
3028
|
+
patch: patchInput.optional().describe(
|
|
3029
|
+
"Single-MVP patch. Use when an MVP transitions to in-progress (design meeting starts), done (verifier PASS), or blocked (verifier FAIL)."
|
|
3030
|
+
)
|
|
3031
|
+
};
|
|
3032
|
+
function toSummary(r) {
|
|
3033
|
+
return {
|
|
3034
|
+
id: r.id,
|
|
3035
|
+
title: r.title,
|
|
3036
|
+
goal: r.goal,
|
|
3037
|
+
status: r.status,
|
|
3038
|
+
orderIndex: r.orderIndex
|
|
3039
|
+
};
|
|
3040
|
+
}
|
|
3041
|
+
async function updateMvpsHandler({
|
|
3042
|
+
kickoffMeetingId,
|
|
3043
|
+
mvps,
|
|
3044
|
+
patch
|
|
3045
|
+
}) {
|
|
3046
|
+
try {
|
|
3047
|
+
if (!mvps && !patch) {
|
|
3048
|
+
throw new Error("Provide either `mvps` (full list) or `patch` (single update).");
|
|
3049
|
+
}
|
|
3050
|
+
let current = [];
|
|
3051
|
+
if (mvps) {
|
|
3052
|
+
if (!kickoffMeetingId) {
|
|
3053
|
+
throw new Error("`kickoffMeetingId` is required when `mvps` is provided.");
|
|
3054
|
+
}
|
|
3055
|
+
for (const m of mvps) {
|
|
3056
|
+
const existing = getMvp(m.id);
|
|
3057
|
+
if (existing) {
|
|
3058
|
+
updateMvp(m.id, { status: m.status });
|
|
3059
|
+
} else {
|
|
3060
|
+
createMvp({
|
|
3061
|
+
id: m.id,
|
|
3062
|
+
kickoffMeetingId,
|
|
3063
|
+
title: m.title,
|
|
3064
|
+
goal: m.goal,
|
|
3065
|
+
status: m.status,
|
|
3066
|
+
orderIndex: m.orderIndex
|
|
3067
|
+
});
|
|
3068
|
+
}
|
|
3069
|
+
}
|
|
3070
|
+
current = listMvpsByKickoff(kickoffMeetingId);
|
|
3071
|
+
}
|
|
3072
|
+
if (patch) {
|
|
3073
|
+
const existing = getMvp(patch.id);
|
|
3074
|
+
if (!existing) {
|
|
3075
|
+
throw new Error(`MVP not found: ${patch.id}`);
|
|
3076
|
+
}
|
|
3077
|
+
const completedAt = patch.status === "done" ? Date.now() : void 0;
|
|
3078
|
+
updateMvp(patch.id, {
|
|
3079
|
+
status: patch.status,
|
|
3080
|
+
...completedAt !== void 0 ? { completedAt } : {}
|
|
3081
|
+
});
|
|
3082
|
+
current = listMvpsByKickoff(existing.kickoffMeetingId);
|
|
3083
|
+
}
|
|
3084
|
+
const summaries = current.map(toSummary);
|
|
3085
|
+
eventBus.emitAgentEvent({ kind: "mvp_progress", mvps: summaries });
|
|
3086
|
+
logger.info(
|
|
3087
|
+
`update-mvps: emitted mvp_progress with ${summaries.length} MVP(s)`
|
|
3088
|
+
);
|
|
3089
|
+
return {
|
|
3090
|
+
content: [
|
|
3091
|
+
{
|
|
3092
|
+
type: "text",
|
|
3093
|
+
text: JSON.stringify({ mvps: summaries }, null, 2)
|
|
3094
|
+
}
|
|
3095
|
+
]
|
|
3096
|
+
};
|
|
3097
|
+
} catch (error) {
|
|
3098
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
3099
|
+
return {
|
|
3100
|
+
content: [{ type: "text", text: JSON.stringify({ error: message }, null, 2) }],
|
|
3101
|
+
isError: true
|
|
3102
|
+
};
|
|
3103
|
+
}
|
|
3104
|
+
}
|
|
3105
|
+
|
|
2886
3106
|
// src/server.ts
|
|
2887
3107
|
function createServer() {
|
|
2888
3108
|
const server = new McpServer({
|
|
@@ -2978,6 +3198,12 @@ function createServer() {
|
|
|
2978
3198
|
generateMinutesSchema,
|
|
2979
3199
|
generateMinutesHandler
|
|
2980
3200
|
);
|
|
3201
|
+
server.tool(
|
|
3202
|
+
"update-mvps",
|
|
3203
|
+
"Upsert the full per-kickoff MVP list (pass `mvps`) OR patch a single MVP status (pass `patch`). Emits `mvp_progress` so the dashboard sidebar stays in sync. Call after kickoff decompose, when each MVP design meeting starts, and when verifier PASS/FAIL finalises an MVP.",
|
|
3204
|
+
updateMvpsSchema,
|
|
3205
|
+
updateMvpsHandler
|
|
3206
|
+
);
|
|
2981
3207
|
return server;
|
|
2982
3208
|
}
|
|
2983
3209
|
|