open-coleslaw 0.6.5 → 0.6.7
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 +8 -6
- package/dist/index.js +169 -0
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
> **Type a prompt. Get a real multi-agent engineering team. No commands to learn.**
|
|
9
9
|
|
|
10
|
-
Open Coleslaw is a multi-agent orchestrator plugin for [Claude Code](https://claude.com/claude-code). Every prompt runs
|
|
10
|
+
Open Coleslaw is a multi-agent orchestrator plugin for [Claude Code](https://claude.com/claude-code). Every prompt enters Claude Code's native **plan mode**, runs a **clarify → kickoff → per-MVP design meeting** cycle inside it, and surfaces the synthesised plan via `ExitPlanMode` — each speaker turn being a real `Agent` dispatch, not role-play.
|
|
11
11
|
|
|
12
12
|

|
|
13
13
|
|
|
@@ -36,9 +36,9 @@ That's it. Watch the meeting unfold at **http://localhost:35143**.
|
|
|
36
36
|
|
|
37
37
|
| You type | The pipeline runs |
|
|
38
38
|
|---|---|
|
|
39
|
-
| `Build me a balance-game web app` |
|
|
40
|
-
| `Fix the flaky login test` |
|
|
41
|
-
| `Should we migrate from Redux to Zustand?` |
|
|
39
|
+
| `Build me a balance-game web app` | EnterPlanMode → planner asks 3-4 clarifying questions → MVP list → per-MVP design meeting → ExitPlanMode with plan → workers → verified |
|
|
40
|
+
| `Fix the flaky login test` | EnterPlanMode → planner returns `READY` (no clarify) → 1-MVP design w/ engineer + verifier → ExitPlanMode → fix → green |
|
|
41
|
+
| `Should we migrate from Redux to Zustand?` | EnterPlanMode → design meeting w/ architect + engineer + researcher → ExitPlanMode with a recommendation plan |
|
|
42
42
|
|
|
43
43
|
You don't call a tool. You don't pick a department. You don't write prompt templates.
|
|
44
44
|
The main Claude session **dispatches each specialist as a real subagent** and
|
|
@@ -131,7 +131,8 @@ Coleslaw is a side dish that's already made. You don't prepare it — you just e
|
|
|
131
131
|
### Key Decisions
|
|
132
132
|
|
|
133
133
|
- **The orchestrator is your proxy, not a CEO.** You are the decision-maker. The orchestrator acts on your behalf but escalates important choices via `@mention`.
|
|
134
|
-
- **
|
|
134
|
+
- **The meeting IS the plan.** Every planning cycle runs inside Claude Code's native plan mode. The `ExitPlanMode` approval is your checkpoint, not a separate step.
|
|
135
|
+
- **Clarify first, then decompose.** Kickoff planner may ask up to 4 structured questions (via `AskUserQuestion`) before breaking the request into MVPs.
|
|
135
136
|
- **Consensus, not round count.** A meeting ends when everyone actually agrees (or you're asked to break a tie).
|
|
136
137
|
- **Minutes are the real artifact.** They survive `/compact` and `/clear` — your Claude Code context is disposable.
|
|
137
138
|
- **TDD by default.** The engineer and verifier draft tests before workers start writing code.
|
|
@@ -156,13 +157,14 @@ See [`CLAUDE.md`](CLAUDE.md) and [`docs/smoke-tests.md`](docs/smoke-tests.md) be
|
|
|
156
157
|
---
|
|
157
158
|
|
|
158
159
|
<details>
|
|
159
|
-
<summary><strong>🛠
|
|
160
|
+
<summary><strong>🛠 16 MCP tools</strong> (the pipeline calls these — you don't)</summary>
|
|
160
161
|
|
|
161
162
|
| Tool | What it does |
|
|
162
163
|
|------|-------------|
|
|
163
164
|
| `start-meeting` | Creates a meeting record (kickoff / design / verify-retry) |
|
|
164
165
|
| `add-transcript` | Saves a speaker's turn |
|
|
165
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 |
|
|
166
168
|
| `get-meeting-status` | Reads meeting progress |
|
|
167
169
|
| `get-minutes` | Retrieves full / summary / tasks-only minutes |
|
|
168
170
|
| `execute-tasks` | Returns the structured task list from minutes for worker dispatch |
|
package/dist/index.js
CHANGED
|
@@ -514,6 +514,73 @@ function getTasksFromMinutes(meetingId) {
|
|
|
514
514
|
|
|
515
515
|
// src/storage/mvp-store.ts
|
|
516
516
|
import { v4 as uuidv46 } from "uuid";
|
|
517
|
+
function rowToMvp(row) {
|
|
518
|
+
return {
|
|
519
|
+
id: row.id,
|
|
520
|
+
kickoffMeetingId: row.kickoff_meeting_id,
|
|
521
|
+
title: row.title,
|
|
522
|
+
goal: row.goal,
|
|
523
|
+
status: row.status,
|
|
524
|
+
orderIndex: row.order_index,
|
|
525
|
+
designMeetingId: row.design_meeting_id,
|
|
526
|
+
createdAt: row.created_at,
|
|
527
|
+
completedAt: row.completed_at
|
|
528
|
+
};
|
|
529
|
+
}
|
|
530
|
+
function createMvp(input) {
|
|
531
|
+
const db = getDb();
|
|
532
|
+
const id = input.id ?? uuidv46();
|
|
533
|
+
const createdAt = Date.now();
|
|
534
|
+
const status = input.status ?? "pending";
|
|
535
|
+
db.prepare(
|
|
536
|
+
`INSERT INTO mvps (id, kickoff_meeting_id, title, goal, status, order_index, created_at)
|
|
537
|
+
VALUES (?, ?, ?, ?, ?, ?, ?)`
|
|
538
|
+
).run(id, input.kickoffMeetingId, input.title, input.goal, status, input.orderIndex, createdAt);
|
|
539
|
+
return {
|
|
540
|
+
id,
|
|
541
|
+
kickoffMeetingId: input.kickoffMeetingId,
|
|
542
|
+
title: input.title,
|
|
543
|
+
goal: input.goal,
|
|
544
|
+
status,
|
|
545
|
+
orderIndex: input.orderIndex,
|
|
546
|
+
designMeetingId: null,
|
|
547
|
+
createdAt,
|
|
548
|
+
completedAt: null
|
|
549
|
+
};
|
|
550
|
+
}
|
|
551
|
+
function getMvp(id) {
|
|
552
|
+
const db = getDb();
|
|
553
|
+
const row = db.prepare("SELECT * FROM mvps WHERE id = ?").get(id);
|
|
554
|
+
return row ? rowToMvp(row) : null;
|
|
555
|
+
}
|
|
556
|
+
function listMvpsByKickoff(kickoffMeetingId) {
|
|
557
|
+
const db = getDb();
|
|
558
|
+
const rows = db.prepare("SELECT * FROM mvps WHERE kickoff_meeting_id = ? ORDER BY order_index ASC").all(kickoffMeetingId);
|
|
559
|
+
return rows.map(rowToMvp);
|
|
560
|
+
}
|
|
561
|
+
function updateMvp(id, updates) {
|
|
562
|
+
const db = getDb();
|
|
563
|
+
const current = getMvp(id);
|
|
564
|
+
if (!current) return null;
|
|
565
|
+
const fields = [];
|
|
566
|
+
const values = [];
|
|
567
|
+
if (updates.status !== void 0) {
|
|
568
|
+
fields.push("status = ?");
|
|
569
|
+
values.push(updates.status);
|
|
570
|
+
}
|
|
571
|
+
if (updates.designMeetingId !== void 0) {
|
|
572
|
+
fields.push("design_meeting_id = ?");
|
|
573
|
+
values.push(updates.designMeetingId);
|
|
574
|
+
}
|
|
575
|
+
if (updates.completedAt !== void 0) {
|
|
576
|
+
fields.push("completed_at = ?");
|
|
577
|
+
values.push(updates.completedAt);
|
|
578
|
+
}
|
|
579
|
+
if (fields.length === 0) return current;
|
|
580
|
+
values.push(id);
|
|
581
|
+
db.prepare(`UPDATE mvps SET ${fields.join(", ")} WHERE id = ?`).run(...values);
|
|
582
|
+
return getMvp(id);
|
|
583
|
+
}
|
|
517
584
|
|
|
518
585
|
// src/types/agent.ts
|
|
519
586
|
var DEPARTMENT_TOOLS = {
|
|
@@ -2883,6 +2950,102 @@ function extractSection(md, heading) {
|
|
|
2883
2950
|
return out;
|
|
2884
2951
|
}
|
|
2885
2952
|
|
|
2953
|
+
// src/tools/update-mvps.ts
|
|
2954
|
+
import { z as z15 } from "zod";
|
|
2955
|
+
var mvpInput = z15.object({
|
|
2956
|
+
id: z15.string().min(1).describe('Stable MVP id (e.g., "mvp-1")'),
|
|
2957
|
+
title: z15.string().min(1),
|
|
2958
|
+
goal: z15.string(),
|
|
2959
|
+
status: z15.enum(["pending", "in-progress", "done", "blocked"]),
|
|
2960
|
+
orderIndex: z15.number().int().min(0)
|
|
2961
|
+
});
|
|
2962
|
+
var patchInput = z15.object({
|
|
2963
|
+
id: z15.string().min(1),
|
|
2964
|
+
status: z15.enum(["pending", "in-progress", "done", "blocked"]).optional()
|
|
2965
|
+
});
|
|
2966
|
+
var updateMvpsSchema = {
|
|
2967
|
+
kickoffMeetingId: z15.string().optional().describe("The kickoff meeting id these MVPs belong to. Required when `mvps` is provided."),
|
|
2968
|
+
mvps: z15.array(mvpInput).optional().describe(
|
|
2969
|
+
"Full MVP list to upsert. Use after the kickoff decompose step. Replaces the entire per-kickoff list."
|
|
2970
|
+
),
|
|
2971
|
+
patch: patchInput.optional().describe(
|
|
2972
|
+
"Single-MVP patch. Use when an MVP transitions to in-progress (design meeting starts), done (verifier PASS), or blocked (verifier FAIL)."
|
|
2973
|
+
)
|
|
2974
|
+
};
|
|
2975
|
+
function toSummary(r) {
|
|
2976
|
+
return {
|
|
2977
|
+
id: r.id,
|
|
2978
|
+
title: r.title,
|
|
2979
|
+
goal: r.goal,
|
|
2980
|
+
status: r.status,
|
|
2981
|
+
orderIndex: r.orderIndex
|
|
2982
|
+
};
|
|
2983
|
+
}
|
|
2984
|
+
async function updateMvpsHandler({
|
|
2985
|
+
kickoffMeetingId,
|
|
2986
|
+
mvps,
|
|
2987
|
+
patch
|
|
2988
|
+
}) {
|
|
2989
|
+
try {
|
|
2990
|
+
if (!mvps && !patch) {
|
|
2991
|
+
throw new Error("Provide either `mvps` (full list) or `patch` (single update).");
|
|
2992
|
+
}
|
|
2993
|
+
let current = [];
|
|
2994
|
+
if (mvps) {
|
|
2995
|
+
if (!kickoffMeetingId) {
|
|
2996
|
+
throw new Error("`kickoffMeetingId` is required when `mvps` is provided.");
|
|
2997
|
+
}
|
|
2998
|
+
for (const m of mvps) {
|
|
2999
|
+
const existing = getMvp(m.id);
|
|
3000
|
+
if (existing) {
|
|
3001
|
+
updateMvp(m.id, { status: m.status });
|
|
3002
|
+
} else {
|
|
3003
|
+
createMvp({
|
|
3004
|
+
id: m.id,
|
|
3005
|
+
kickoffMeetingId,
|
|
3006
|
+
title: m.title,
|
|
3007
|
+
goal: m.goal,
|
|
3008
|
+
status: m.status,
|
|
3009
|
+
orderIndex: m.orderIndex
|
|
3010
|
+
});
|
|
3011
|
+
}
|
|
3012
|
+
}
|
|
3013
|
+
current = listMvpsByKickoff(kickoffMeetingId);
|
|
3014
|
+
}
|
|
3015
|
+
if (patch) {
|
|
3016
|
+
const existing = getMvp(patch.id);
|
|
3017
|
+
if (!existing) {
|
|
3018
|
+
throw new Error(`MVP not found: ${patch.id}`);
|
|
3019
|
+
}
|
|
3020
|
+
const completedAt = patch.status === "done" ? Date.now() : void 0;
|
|
3021
|
+
updateMvp(patch.id, {
|
|
3022
|
+
status: patch.status,
|
|
3023
|
+
...completedAt !== void 0 ? { completedAt } : {}
|
|
3024
|
+
});
|
|
3025
|
+
current = listMvpsByKickoff(existing.kickoffMeetingId);
|
|
3026
|
+
}
|
|
3027
|
+
const summaries = current.map(toSummary);
|
|
3028
|
+
eventBus.emitAgentEvent({ kind: "mvp_progress", mvps: summaries });
|
|
3029
|
+
logger.info(
|
|
3030
|
+
`update-mvps: emitted mvp_progress with ${summaries.length} MVP(s)`
|
|
3031
|
+
);
|
|
3032
|
+
return {
|
|
3033
|
+
content: [
|
|
3034
|
+
{
|
|
3035
|
+
type: "text",
|
|
3036
|
+
text: JSON.stringify({ mvps: summaries }, null, 2)
|
|
3037
|
+
}
|
|
3038
|
+
]
|
|
3039
|
+
};
|
|
3040
|
+
} catch (error) {
|
|
3041
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
3042
|
+
return {
|
|
3043
|
+
content: [{ type: "text", text: JSON.stringify({ error: message }, null, 2) }],
|
|
3044
|
+
isError: true
|
|
3045
|
+
};
|
|
3046
|
+
}
|
|
3047
|
+
}
|
|
3048
|
+
|
|
2886
3049
|
// src/server.ts
|
|
2887
3050
|
function createServer() {
|
|
2888
3051
|
const server = new McpServer({
|
|
@@ -2978,6 +3141,12 @@ function createServer() {
|
|
|
2978
3141
|
generateMinutesSchema,
|
|
2979
3142
|
generateMinutesHandler
|
|
2980
3143
|
);
|
|
3144
|
+
server.tool(
|
|
3145
|
+
"update-mvps",
|
|
3146
|
+
"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.",
|
|
3147
|
+
updateMvpsSchema,
|
|
3148
|
+
updateMvpsHandler
|
|
3149
|
+
);
|
|
2981
3150
|
return server;
|
|
2982
3151
|
}
|
|
2983
3152
|
|