@jabworks/condux 0.2.0 → 0.3.1
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/LICENSE +21 -0
- package/README.md +27 -29
- package/index.js +21 -0
- package/package.json +6 -4
- package/skills/code-review/SKILL.md +136 -0
- package/skills/code-review/evals/trigger_eval.json +22 -0
- package/skills/discovery/SKILL.md +242 -0
- package/skills/discovery/evals/trigger_eval.json +24 -0
- package/skills/discovery/references/choice-server.js +216 -0
- package/skills/discovery/references/mockup-picker.md +90 -0
- package/skills/discovery/references/package.json +3 -0
- package/skills/draft-plan/SKILL.md +229 -0
- package/skills/draft-plan/evals/trigger_eval.json +22 -0
- package/skills/draft-plan/references/plan-template.md +96 -0
- package/skills/finalize/SKILL.md +123 -0
- package/skills/finalize/evals/trigger_eval.json +22 -0
- package/skills/finalize/references/rationalization-table.md +15 -0
- package/skills/plan-review/SKILL.md +215 -0
- package/skills/plan-review/evals/trigger_eval.json +22 -0
- package/skills/plan-review/references/annotate-server.js +465 -0
- package/skills/plan-review/references/install-codex-hook.sh +112 -0
- package/skills/plan-review/references/package.json +3 -0
- package/skills/plan-review/references/plan-review-template.html +730 -0
- package/skills/preflight/SKILL.md +128 -0
- package/skills/preflight/evals/trigger_eval.json +26 -0
- package/skills/root-cause-analysis/SKILL.md +82 -0
- package/skills/root-cause-analysis/evals/trigger_eval.json +22 -0
- package/skills/subagent-deployment/SKILL.md +105 -0
- package/skills/subagent-deployment/evals/trigger_eval.json +22 -0
- package/skills/subagent-deployment/references/safety-checklist.md +54 -0
- package/skills/subagent-execution/SKILL.md +182 -0
- package/skills/subagent-execution/agents/coder.md +55 -0
- package/skills/subagent-execution/agents/explorer.md +77 -0
- package/skills/subagent-execution/agents/planner.md +101 -0
- package/skills/subagent-execution/agents/researcher.md +78 -0
- package/skills/subagent-execution/evals/trigger_eval.json +22 -0
- package/skills/subagent-execution/references/install-codex-agents.mjs +98 -0
- package/skills/subagent-execution/references/review-package.sh +29 -0
- package/skills/subagent-execution/references/spawn-rules.md +88 -0
- package/skills/subagent-execution/references/task-brief.sh +39 -0
- package/skills/technical-spec/SKILL.md +190 -0
- package/skills/technical-spec/evals/trigger_eval.json +23 -0
- package/skills/technical-spec/references/scaffold.sh +98 -0
- package/skills/technical-spec/references/templates.md +159 -0
- package/skills/test-first-development/SKILL.md +121 -0
- package/skills/test-first-development/evals/trigger_eval.json +22 -0
- package/skills/test-first-development/references/rationalization-table.md +16 -0
- package/skills/workflow/SKILL.md +241 -0
- package/skills/workflow/evals/trigger_eval.json +30 -0
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Local option-picker surface for /discovery visual mockups. Serves an
|
|
3
|
+
// agent-authored, self-contained HTML mockup, injects click-to-select wiring,
|
|
4
|
+
// and delivers the user's pick back to the agent over a long-poll. No npm deps.
|
|
5
|
+
// Binds 127.0.0.1 only. One-shot: the server exits once a choice is delivered.
|
|
6
|
+
//
|
|
7
|
+
// Usage:
|
|
8
|
+
// node choice-server.js <mockup.html> [--multi] [--port 7788]
|
|
9
|
+
//
|
|
10
|
+
// The agent tags each option element in the mockup with data-choice="<label>",
|
|
11
|
+
// launches this server in the background, then BLOCKS on
|
|
12
|
+
// GET /api/choice (a long-poll) which resolves when the user picks:
|
|
13
|
+
// {"choices":["Option B"],"note":"<free text>","submittedAt":"<iso>"}
|
|
14
|
+
//
|
|
15
|
+
// Single mode (default): clicking an option submits it immediately.
|
|
16
|
+
// Multi mode (--multi): clicking toggles; a bottom bar sends the set + note.
|
|
17
|
+
//
|
|
18
|
+
// Default port 7788 (distinct from plan-review's 7777) so a discovery picker
|
|
19
|
+
// and a plan-review steer server can run at once. Diagnostics go to stderr.
|
|
20
|
+
'use strict';
|
|
21
|
+
|
|
22
|
+
const http = require('http');
|
|
23
|
+
const fs = require('fs');
|
|
24
|
+
const path = require('path');
|
|
25
|
+
const { exec } = require('child_process');
|
|
26
|
+
|
|
27
|
+
const log = console.error; // stdout stays clean; the agent talks to us over HTTP
|
|
28
|
+
|
|
29
|
+
const MULTI = process.argv.includes('--multi');
|
|
30
|
+
const portArg = (function () { const i = process.argv.indexOf('--port'); return i >= 0 ? parseInt(process.argv[i + 1], 10) : NaN; })();
|
|
31
|
+
const PORT = !isNaN(portArg) ? portArg : 7788;
|
|
32
|
+
|
|
33
|
+
let choiceWaiter = null; // pending GET /api/choice response
|
|
34
|
+
let pendingChoice = null; // choice that arrived before the agent polled
|
|
35
|
+
let delivered = false; // one-shot guard
|
|
36
|
+
|
|
37
|
+
let mockupFile;
|
|
38
|
+
|
|
39
|
+
function fail(msg) { console.error(' choice-picker: ' + msg); process.exit(1); }
|
|
40
|
+
|
|
41
|
+
function readMockup() {
|
|
42
|
+
try { return fs.readFileSync(mockupFile, 'utf8'); } catch (e) { return ''; }
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Injected client: wires [data-choice] elements to POST /api/choice. Kept
|
|
46
|
+
// dependency-free and defensive so it works inside whatever HTML the agent wrote.
|
|
47
|
+
function injectedScript() {
|
|
48
|
+
return `
|
|
49
|
+
<style>
|
|
50
|
+
#__vc_banner{position:fixed;top:0;left:0;right:0;z-index:99998;font:600 13px/1.4 system-ui,sans-serif;
|
|
51
|
+
background:#1f2937;color:#fff;padding:10px 16px;text-align:center;box-shadow:0 1px 6px rgba(0,0,0,.25)}
|
|
52
|
+
body{padding-top:44px}
|
|
53
|
+
[data-choice]{cursor:pointer;transition:outline-color .12s,box-shadow .12s;outline:2px solid transparent;outline-offset:2px}
|
|
54
|
+
[data-choice]:hover{outline-color:#93c5fd}
|
|
55
|
+
[data-choice].__vc_sel{outline-color:#2563eb;box-shadow:0 0 0 4px rgba(37,99,235,.18)}
|
|
56
|
+
#__vc_bar{position:fixed;bottom:0;left:0;right:0;z-index:99998;display:none;gap:10px;align-items:center;
|
|
57
|
+
background:#111827;color:#fff;padding:12px 16px;box-shadow:0 -1px 8px rgba(0,0,0,.3);font:14px system-ui,sans-serif}
|
|
58
|
+
#__vc_bar.__vc_show{display:flex}
|
|
59
|
+
#__vc_note{flex:1;padding:8px 10px;border-radius:6px;border:1px solid #374151;background:#1f2937;color:#fff;font:14px system-ui,sans-serif}
|
|
60
|
+
#__vc_send{padding:8px 16px;border:0;border-radius:6px;background:#2563eb;color:#fff;font:600 14px system-ui,sans-serif;cursor:pointer}
|
|
61
|
+
#__vc_send:disabled{background:#374151;cursor:default}
|
|
62
|
+
#__vc_done{position:fixed;inset:0;z-index:99999;display:none;align-items:center;justify-content:center;
|
|
63
|
+
background:rgba(17,24,39,.92);color:#fff;font:600 18px system-ui,sans-serif;text-align:center;padding:24px}
|
|
64
|
+
#__vc_done.__vc_show{display:flex}
|
|
65
|
+
</style>
|
|
66
|
+
<div id="__vc_banner"></div>
|
|
67
|
+
<div id="__vc_bar"><span id="__vc_count">0 selected</span>
|
|
68
|
+
<input id="__vc_note" placeholder="Optional note (e.g. B, but with A's header)…" />
|
|
69
|
+
<button id="__vc_send" disabled>Send selection →</button></div>
|
|
70
|
+
<div id="__vc_done">✓ Sent to the agent — you can close this tab.</div>
|
|
71
|
+
<script>
|
|
72
|
+
(function(){
|
|
73
|
+
var MULTI = ${MULTI ? 'true' : 'false'};
|
|
74
|
+
var opts = [].slice.call(document.querySelectorAll('[data-choice]'));
|
|
75
|
+
var banner = document.getElementById('__vc_banner');
|
|
76
|
+
var done = document.getElementById('__vc_done');
|
|
77
|
+
if(!opts.length){ banner.textContent = 'No options found — tag each option element with data-choice="…".'; return; }
|
|
78
|
+
banner.textContent = MULTI
|
|
79
|
+
? 'Click options to select, add an optional note, then Send. Your pick goes back to the agent.'
|
|
80
|
+
: 'Click an option to send your choice to the agent.';
|
|
81
|
+
var locked = false;
|
|
82
|
+
function post(payload){
|
|
83
|
+
if(locked) return; locked = true;
|
|
84
|
+
fetch('/api/choice',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify(payload)})
|
|
85
|
+
.then(function(){ done.classList.add('__vc_show'); })
|
|
86
|
+
.catch(function(){ locked = false; alert('Could not reach the picker server.'); });
|
|
87
|
+
}
|
|
88
|
+
if(!MULTI){
|
|
89
|
+
opts.forEach(function(el){
|
|
90
|
+
el.addEventListener('click', function(){ post({choices:[el.getAttribute('data-choice')]}); });
|
|
91
|
+
});
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
var bar = document.getElementById('__vc_bar');
|
|
95
|
+
var count = document.getElementById('__vc_count');
|
|
96
|
+
var note = document.getElementById('__vc_note');
|
|
97
|
+
var send = document.getElementById('__vc_send');
|
|
98
|
+
var sel = [];
|
|
99
|
+
function refresh(){
|
|
100
|
+
count.textContent = sel.length + ' selected';
|
|
101
|
+
send.disabled = sel.length === 0;
|
|
102
|
+
bar.classList.toggle('__vc_show', true);
|
|
103
|
+
}
|
|
104
|
+
opts.forEach(function(el){
|
|
105
|
+
el.addEventListener('click', function(){
|
|
106
|
+
var label = el.getAttribute('data-choice');
|
|
107
|
+
var i = sel.indexOf(label);
|
|
108
|
+
if(i >= 0){ sel.splice(i,1); el.classList.remove('__vc_sel'); }
|
|
109
|
+
else { sel.push(label); el.classList.add('__vc_sel'); }
|
|
110
|
+
refresh();
|
|
111
|
+
});
|
|
112
|
+
});
|
|
113
|
+
send.addEventListener('click', function(){ if(sel.length) post({choices:sel, note:note.value.trim()}); });
|
|
114
|
+
})();
|
|
115
|
+
</script>`;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function serveMockup(res) {
|
|
119
|
+
let html = readMockup();
|
|
120
|
+
const inject = injectedScript();
|
|
121
|
+
if (/<\/body>/i.test(html)) html = html.replace(/<\/body>/i, inject + '\n</body>');
|
|
122
|
+
else html += inject;
|
|
123
|
+
res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
|
|
124
|
+
res.end(html);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function sendJson(res, code, obj) {
|
|
128
|
+
res.writeHead(code, { 'Content-Type': 'application/json' });
|
|
129
|
+
res.end(JSON.stringify(obj));
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// Deliver a choice to the agent's long-poll, then shut down (one-shot).
|
|
133
|
+
function deliver(payload) {
|
|
134
|
+
if (delivered) return;
|
|
135
|
+
delivered = true;
|
|
136
|
+
if (choiceWaiter) {
|
|
137
|
+
try { choiceWaiter.writeHead(200, { 'Content-Type': 'application/json' }); choiceWaiter.end(JSON.stringify(payload)); } catch (e) { /* client gone */ }
|
|
138
|
+
choiceWaiter = null;
|
|
139
|
+
} else {
|
|
140
|
+
pendingChoice = payload; // agent hasn't polled yet — the next GET picks it up
|
|
141
|
+
delivered = false; // not truly delivered until a GET drains it
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
log('\n Choice: ' + JSON.stringify(payload.choices) + (payload.note ? ' note: ' + payload.note : '') + ' — delivered to agent. Stopping.\n');
|
|
145
|
+
setTimeout(function () { process.exit(0); }, 300);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function handler(req, res) {
|
|
149
|
+
const url = new URL(req.url, 'http://127.0.0.1');
|
|
150
|
+
const p = url.pathname;
|
|
151
|
+
|
|
152
|
+
if (req.method === 'GET' && p === '/') return serveMockup(res);
|
|
153
|
+
|
|
154
|
+
if (req.method === 'GET' && p === '/api/choice') {
|
|
155
|
+
if (pendingChoice) { const c = pendingChoice; pendingChoice = null; delivered = true; sendJson(res, 200, c); log('\n Choice delivered to agent. Stopping.\n'); return setTimeout(function () { process.exit(0); }, 300); }
|
|
156
|
+
res.setTimeout(0); // a human may take minutes — don't close the idle socket
|
|
157
|
+
choiceWaiter = res;
|
|
158
|
+
req.on('close', function () { if (choiceWaiter === res) choiceWaiter = null; });
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
if (req.method === 'POST' && p === '/api/choice') {
|
|
163
|
+
let body = '';
|
|
164
|
+
req.on('data', function (c) { body += c; if (body.length > 1e6) req.destroy(); });
|
|
165
|
+
req.on('end', function () {
|
|
166
|
+
try {
|
|
167
|
+
const data = JSON.parse(body);
|
|
168
|
+
const choices = Array.isArray(data.choices) ? data.choices : (data.choice ? [data.choice] : []);
|
|
169
|
+
if (!choices.length) return sendJson(res, 400, { error: 'no choices' });
|
|
170
|
+
sendJson(res, 200, { status: 'received' });
|
|
171
|
+
deliver({ choices: choices, note: (data.note || '').trim(), submittedAt: new Date().toISOString() });
|
|
172
|
+
} catch (e) {
|
|
173
|
+
sendJson(res, 400, { error: 'invalid JSON' });
|
|
174
|
+
}
|
|
175
|
+
});
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
res.writeHead(404);
|
|
180
|
+
res.end();
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
let server;
|
|
184
|
+
function listen() {
|
|
185
|
+
server = http.createServer(handler);
|
|
186
|
+
server.on('error', function (e) {
|
|
187
|
+
if (e && e.code === 'EADDRINUSE') fail('port ' + PORT + ' is in use — pass a free --port <n>.');
|
|
188
|
+
fail(String((e && e.message) || e));
|
|
189
|
+
});
|
|
190
|
+
server.listen(PORT, '127.0.0.1', function () {
|
|
191
|
+
const url = 'http://127.0.0.1:' + server.address().port;
|
|
192
|
+
log('\n Option picker → ' + url + (MULTI ? ' (multi-select)' : ''));
|
|
193
|
+
log(' Mockup → ' + mockupFile);
|
|
194
|
+
log(' Choice → GET ' + url + '/api/choice (long-poll)');
|
|
195
|
+
log('\n Pick in the browser; the choice returns to the agent. Ctrl+C to stop.\n');
|
|
196
|
+
const opener = process.platform === 'darwin' ? 'open' :
|
|
197
|
+
process.platform === 'win32' ? 'start ""' : 'xdg-open';
|
|
198
|
+
exec(opener + ' ' + url, function () {});
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
process.on('SIGINT', function () {
|
|
203
|
+
log('\n Option picker stopped.\n');
|
|
204
|
+
if (server) server.close(function () { process.exit(0); }); else process.exit(0);
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
// First non-flag arg is the mockup file (skip --port's value).
|
|
208
|
+
const args = process.argv.slice(2);
|
|
209
|
+
let file = null;
|
|
210
|
+
for (let i = 0; i < args.length; i++) {
|
|
211
|
+
if (args[i] === '--port') { i++; continue; }
|
|
212
|
+
if (args[i][0] !== '-') { file = args[i]; break; }
|
|
213
|
+
}
|
|
214
|
+
if (!file || !fs.existsSync(file)) fail('mockup file not found — pass a self-contained HTML file path.');
|
|
215
|
+
mockupFile = path.resolve(file);
|
|
216
|
+
listen();
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
# Mockup Picker
|
|
2
|
+
|
|
3
|
+
Reference for `/discovery`. Use when spatial or visual content communicates better
|
|
4
|
+
than text alone — HTML option mockups, plus the click-to-pick server that returns
|
|
5
|
+
the user's choice straight to the agent.
|
|
6
|
+
|
|
7
|
+
## When to Go Visual
|
|
8
|
+
|
|
9
|
+
**Use a browser visual** when:
|
|
10
|
+
- Comparing 2–4 UI layout options side-by-side
|
|
11
|
+
- Architecture diagrams where spatial relationships matter
|
|
12
|
+
- Component hierarchy that's easier to parse visually
|
|
13
|
+
- Design polish decisions where aesthetic is the question
|
|
14
|
+
|
|
15
|
+
**Stay in terminal** when:
|
|
16
|
+
- Clarifying requirements (A/B/C prose is fine)
|
|
17
|
+
- Tradeoff analysis (a markdown table works)
|
|
18
|
+
- The question has a clear right answer
|
|
19
|
+
- The user would answer in one word
|
|
20
|
+
|
|
21
|
+
## How to Generate
|
|
22
|
+
|
|
23
|
+
Write a **self-contained HTML file** — inline all CSS, no external dependencies:
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
# Write the file, then open:
|
|
27
|
+
xdg-open /tmp/discovery-<topic>-$(date +%Y%m).html # Linux
|
|
28
|
+
open /tmp/discovery-<topic>-$(date +%Y%m).html # macOS
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Use semantic filenames: `discovery-checkout-flow-202601.html`
|
|
32
|
+
|
|
33
|
+
Tag each option's container element with `data-choice="<label>"` (e.g.
|
|
34
|
+
`<div data-choice="Option A">`). This is inert for a plain open-in-browser
|
|
35
|
+
glance, but it's what the interactive picker below wires up — so add it by
|
|
36
|
+
default.
|
|
37
|
+
|
|
38
|
+
## HTML Guidelines
|
|
39
|
+
|
|
40
|
+
- Max 2–4 options per page — more causes decision paralysis
|
|
41
|
+
- Show real content, not lorem ipsum
|
|
42
|
+
- Label each option clearly: **Option A**, **Option B**
|
|
43
|
+
- Tag each option container with `data-choice="Option A"` for click-to-select
|
|
44
|
+
- One-line rationale under each option
|
|
45
|
+
- Keep it light — mockup fidelity, not production polish
|
|
46
|
+
|
|
47
|
+
## Interactive Pick (Optional)
|
|
48
|
+
|
|
49
|
+
For a plain glance, opening the file is enough — the user tells you their pick
|
|
50
|
+
in chat. When you want the choice to come **straight back to you** without the
|
|
51
|
+
user re-typing it, serve the mockup through the picker server. The agent
|
|
52
|
+
launches it; the user never starts anything.
|
|
53
|
+
|
|
54
|
+
Locate the script from the installed `discovery` skill and launch it in the
|
|
55
|
+
background, pointing at the mockup file:
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
CHOICE=$(find ~/.claude ~/.agents ~/.codex -name "choice-server.js" -path "*/discovery/*" 2>/dev/null | head -1)
|
|
59
|
+
node "$CHOICE" /tmp/discovery-checkout-flow-202601.html # single-select
|
|
60
|
+
node "$CHOICE" /tmp/discovery-checkout-flow-202601.html --multi # multi-select
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
It opens the browser automatically. In single mode a click submits instantly;
|
|
64
|
+
in `--multi` mode clicks toggle a selection and a bottom bar sends the set plus
|
|
65
|
+
an optional note. Then **block on the long-poll** and continue once it resolves:
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
curl -s http://127.0.0.1:7788/api/choice
|
|
69
|
+
# → {"choices":["Option B"],"note":"B, but with A's header","submittedAt":"…"}
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
The server is one-shot: it delivers the pick and exits — no HTML is written to
|
|
73
|
+
disk, nothing for the user to clean up. Port defaults to `7788` (distinct from
|
|
74
|
+
`plan-review`'s `7777`, so both can run at once); pass `--port <n>` to override.
|
|
75
|
+
|
|
76
|
+
## What NOT to Do
|
|
77
|
+
|
|
78
|
+
```
|
|
79
|
+
✗ Building a multi-file app just for a mockup
|
|
80
|
+
✗ Making the user start or manage a server (the agent launches the picker itself)
|
|
81
|
+
✗ Showing more than 4 options at once
|
|
82
|
+
✗ Lorem ipsum placeholders when real content is possible
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
## Return to Terminal
|
|
86
|
+
|
|
87
|
+
After the user reviews the visual, return to the discovery sign-off flow. If
|
|
88
|
+
you used the picker, the chosen option(s) and any note arrive over the
|
|
89
|
+
long-poll; otherwise capture the decision from chat. Note which option was
|
|
90
|
+
chosen, then continue with `/draft-plan` or the next step.
|
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: draft-plan
|
|
3
|
+
description: "Turns a signed-off design into an executable plan of lean task cards (what, why, gotchas, dependencies). Writes Markdown; review in the browser via plan-review. Run for LARGE tasks after /discovery sign-off. Never writes a plan without a signed-off design — if discovery hasn't run, apply the soft gate first."
|
|
4
|
+
argument-hint: "<feature name or design summary>"
|
|
5
|
+
effort: high
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# /draft-plan
|
|
9
|
+
|
|
10
|
+
Turn a signed-off design into a clear, executable plan. Lean task cards, not micro-step novels.
|
|
11
|
+
|
|
12
|
+
## Usage
|
|
13
|
+
|
|
14
|
+
```
|
|
15
|
+
/draft-plan $ARGUMENTS
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Before Writing
|
|
19
|
+
|
|
20
|
+
1. Confirm discovery has run and design is signed off. First glob
|
|
21
|
+
`.condux/designs/*<slug>*.md` and `specs/<slug>/` (slug = kebab-case
|
|
22
|
+
of the feature name) for an existing signed-off design — if found,
|
|
23
|
+
treat this check as satisfied without asking. Otherwise ask: "We
|
|
24
|
+
haven't aligned on the design yet — run /discovery first, or confirm
|
|
25
|
+
you want to skip it."
|
|
26
|
+
|
|
27
|
+
The plan is written as Markdown — optimized for AI agent consumption during
|
|
28
|
+
execution. To read it in the browser, use `plan-review` (see After Saving);
|
|
29
|
+
no separate HTML file is produced.
|
|
30
|
+
|
|
31
|
+
## How It Works
|
|
32
|
+
|
|
33
|
+
```
|
|
34
|
+
┌──────────────────────────────────────────────────────────────────┐
|
|
35
|
+
│ DRAFT-PLAN │
|
|
36
|
+
├──────────────────────────────────────────────────────────────────┤
|
|
37
|
+
│ Step 1: MAP FILES │
|
|
38
|
+
│ List every file to be created or modified. │
|
|
39
|
+
│ Assign one clear responsibility per file. │
|
|
40
|
+
│ Decomposition decisions get locked in here. │
|
|
41
|
+
│ │
|
|
42
|
+
│ Step 2: WRITE TASK CARDS │
|
|
43
|
+
│ Each card = one meaningful unit of work. │
|
|
44
|
+
│ See Task Card Format below. │
|
|
45
|
+
│ │
|
|
46
|
+
│ Step 3: INLINE SELF-REVIEW │
|
|
47
|
+
│ Check against design: │
|
|
48
|
+
│ ✓ Every requirement maps to a task │
|
|
49
|
+
│ ✓ No TBDs, TODOs, or "implement later" │
|
|
50
|
+
│ ✓ No undefined references (types, functions, methods) │
|
|
51
|
+
│ ✓ No "similar to Task N" shortcuts │
|
|
52
|
+
│ ✓ Dependencies between tasks are explicit │
|
|
53
|
+
│ │
|
|
54
|
+
│ Step 4: PRESENT TO USER │
|
|
55
|
+
│ Show plan in sections, get sign-off before saving. │
|
|
56
|
+
│ │
|
|
57
|
+
│ Step 5: SAVE │
|
|
58
|
+
│ Write to .condux/plans/ per AGENTS.md or default path. │
|
|
59
|
+
│ Do NOT modify the plan file after saving. │
|
|
60
|
+
└──────────────────────────────────────────────────────────────────┘
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## Task Card Format
|
|
64
|
+
|
|
65
|
+
Each task card is a **top-level `##` heading** — this makes every task a
|
|
66
|
+
first-class entry in the plan-review navigation menu (the TOC is generated
|
|
67
|
+
from headings, and `###` renders as a greyed sub-entry).
|
|
68
|
+
|
|
69
|
+
```markdown
|
|
70
|
+
## Task N: <Short Name>
|
|
71
|
+
|
|
72
|
+
**What:** One paragraph — what this task builds or changes.
|
|
73
|
+
|
|
74
|
+
**Why:** One sentence — why this is needed / what it enables.
|
|
75
|
+
|
|
76
|
+
**Files:**
|
|
77
|
+
|
|
78
|
+
- Create: `exact/path/to/file.ts`
|
|
79
|
+
- Modify: `exact/path/to/existing.ts:123-145`
|
|
80
|
+
|
|
81
|
+
**Interfaces:**
|
|
82
|
+
|
|
83
|
+
- Consumes: what this task uses from earlier tasks — exact names / signatures
|
|
84
|
+
- Produces: what later tasks rely on — exact function names, parameter and
|
|
85
|
+
return types (or "None" for a leaf task)
|
|
86
|
+
|
|
87
|
+
**Sketch:** _(optional)_
|
|
88
|
+
|
|
89
|
+
```ts
|
|
90
|
+
// only the code worth annotating before it's written
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
**Gotchas:**
|
|
94
|
+
|
|
95
|
+
- Use `dayjs` for date handling, not native JS Date
|
|
96
|
+
- This touches the auth middleware — don't change its signature
|
|
97
|
+
- [Any project conventions or known quirks relevant to this task]
|
|
98
|
+
|
|
99
|
+
**Dependencies:** Task 2, Task 3 (or "None")
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
The `Interfaces` block carries the typed contract between tasks: a task's
|
|
103
|
+
implementer (especially a subagent) sees only their own card, so `Consumes`
|
|
104
|
+
/ `Produces` is how neighbouring tasks agree on exact names and types. This
|
|
105
|
+
is what prevents drift like `clearLayers()` in Task 3 vs `clearFullLayers()`
|
|
106
|
+
in Task 7.
|
|
107
|
+
|
|
108
|
+
## Code Sketches
|
|
109
|
+
|
|
110
|
+
Plans are reviewed by annotation before any code is written, so the cheapest
|
|
111
|
+
place to catch a bad approach is the plan. Include a **Sketch** whenever
|
|
112
|
+
seeing the code changes whether you'd approve the task:
|
|
113
|
+
|
|
114
|
+
- ✓ The non-obvious algorithm or the tricky transform
|
|
115
|
+
- ✓ A key data shape, schema, or type/interface definition
|
|
116
|
+
- ✓ A function signature whose contract other tasks depend on
|
|
117
|
+
|
|
118
|
+
Keep sketches to the code that carries a decision. They are illustrative
|
|
119
|
+
intent for the reviewer to tweak — not the final implementation, and not a
|
|
120
|
+
substitute for the implementer's judgement. Do **not** sketch:
|
|
121
|
+
|
|
122
|
+
- ✗ Boilerplate the implementer will obviously write (CRUD, wiring, imports)
|
|
123
|
+
- ✗ Full file bodies — show the part worth reviewing, elide the rest with `…`
|
|
124
|
+
- ✗ Test code (that belongs to `/test-first-development`)
|
|
125
|
+
- ✗ The TDD micro-step cycle (write test → run → implement → run → commit)
|
|
126
|
+
|
|
127
|
+
## Plan Document Structure
|
|
128
|
+
|
|
129
|
+
Fill in `references/plan-template.md` — it is the canonical skeleton, kept
|
|
130
|
+
consistent so the plan-review nav renders the same predictable TOC every
|
|
131
|
+
time (`Overview · Global Constraints · Files Affected · Task Checklist ·
|
|
132
|
+
Task 1…N`). Do not reinvent the section set or reorder it.
|
|
133
|
+
|
|
134
|
+
```markdown
|
|
135
|
+
# Plan: <Feature Name>
|
|
136
|
+
|
|
137
|
+
> Date: YYYY-MM-DD
|
|
138
|
+
|
|
139
|
+
## Overview
|
|
140
|
+
|
|
141
|
+
**Goal:** one sentence. **Approach:** 2-3 sentences. **Tech / conventions:** key libs and rules.
|
|
142
|
+
|
|
143
|
+
## Global Constraints
|
|
144
|
+
|
|
145
|
+
- Project-wide rules copied verbatim from the design/spec (delete if none).
|
|
146
|
+
|
|
147
|
+
## Files Affected
|
|
148
|
+
|
|
149
|
+
- `path/to/file.ts` — what it does / what changes
|
|
150
|
+
|
|
151
|
+
## Task Checklist
|
|
152
|
+
|
|
153
|
+
- [ ] Task 1: ...
|
|
154
|
+
- [ ] Task 2: ...
|
|
155
|
+
|
|
156
|
+
---
|
|
157
|
+
|
|
158
|
+
[Task cards follow — each a top-level ## heading]
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
## Save Path
|
|
162
|
+
|
|
163
|
+
Write the plan to `.condux/plans/YYYY-MM-DD-<feature>.md`.
|
|
164
|
+
|
|
165
|
+
`.condux/` is gitignored working state, created on demand at the git root
|
|
166
|
+
(see `/workflow` → Artifacts). Before the first write, make sure it's
|
|
167
|
+
ignored — see the bootstrap step there.
|
|
168
|
+
|
|
169
|
+
Check `AGENTS.md` for project-specific overrides.
|
|
170
|
+
|
|
171
|
+
## After Saving
|
|
172
|
+
|
|
173
|
+
Once the plan file is saved, always ask — regardless of whether this
|
|
174
|
+
skill was invoked via `/workflow` or standalone:
|
|
175
|
+
|
|
176
|
+
> "Plan saved to `<path>`. Want to review it in the browser before
|
|
177
|
+
> implementing, or go straight to implementation?"
|
|
178
|
+
|
|
179
|
+
Accept either answer, same as any other soft gate in this skill.
|
|
180
|
+
|
|
181
|
+
**If review chosen:** locate `annotate-server.js` from the installed
|
|
182
|
+
`plan-review` skill (`find ~/.claude ~/.codex ~/.agents -name
|
|
183
|
+
annotate-server.js -path '*plan-review*' 2>/dev/null | head -1`) and
|
|
184
|
+
launch it in `--steer` mode against the saved plan file:
|
|
185
|
+
```bash
|
|
186
|
+
node /path/to/plan-review/references/annotate-server.js .condux/plans/<file>.md --steer
|
|
187
|
+
```
|
|
188
|
+
Poll `GET http://127.0.0.1:7777/api/decision` (long-poll — blocks until a
|
|
189
|
+
decision is submitted) and branch on the result:
|
|
190
|
+
- **Approve** → proceed to implementation, using any feedback as notes.
|
|
191
|
+
- **Request Revisions** → revise the plan file in place per the feedback
|
|
192
|
+
(the open browser tab live-reloads over SSE), then poll again.
|
|
193
|
+
- **Deny** → stop; report the feedback and rework the approach before
|
|
194
|
+
re-planning.
|
|
195
|
+
|
|
196
|
+
**If straight to implementation chosen:** proceed directly, no server
|
|
197
|
+
launch.
|
|
198
|
+
|
|
199
|
+
This applies to the plan doc this skill produces (LARGE tier).
|
|
200
|
+
`/workflow`'s MEDIUM-tier quick-plan uses the same section shape and card
|
|
201
|
+
fields (Overview · Files · Interfaces · Sketch · Gotchas) inline and lean —
|
|
202
|
+
it is not written via this skill, but stays consistent with this structure.
|
|
203
|
+
|
|
204
|
+
## Plan Failures — Never Write These
|
|
205
|
+
|
|
206
|
+
```
|
|
207
|
+
✗ "TBD", "TODO", "implement later", "fill in details"
|
|
208
|
+
✗ "Add appropriate error handling" (be specific)
|
|
209
|
+
✗ "Similar to Task N" (repeat the content)
|
|
210
|
+
✗ Steps that describe what to do without saying what changes
|
|
211
|
+
✗ References to types or functions not defined anywhere in the plan
|
|
212
|
+
✗ Micro-steps (write test → run test → write code → run test → commit)
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
## Task Granularity
|
|
216
|
+
|
|
217
|
+
Tasks should be **meaningful units of work**, not micro-steps. A task is something a developer would recognize as a coherent piece of work — not "write one function" and not "implement the whole feature."
|
|
218
|
+
|
|
219
|
+
Ask yourself: could this task be reviewed as a standalone PR? If yes, it's the right size.
|
|
220
|
+
|
|
221
|
+
## What Does NOT Happen
|
|
222
|
+
|
|
223
|
+
```
|
|
224
|
+
✗ Full implementations or boilerplate (sketch decision-carrying code only — see Code Sketches)
|
|
225
|
+
✗ Exact commands with expected output
|
|
226
|
+
✗ Test code in the plan (tests are written during /test-first-development)
|
|
227
|
+
✗ Modifying the plan file after it's saved
|
|
228
|
+
✗ Writing a plan without design sign-off
|
|
229
|
+
```
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
[
|
|
2
|
+
{ "query": "write the implementation plan", "should_trigger": true, "expected_skill": "draft-plan", "accept": ["workflow"] },
|
|
3
|
+
{ "query": "turn the design into task cards", "should_trigger": true, "expected_skill": "draft-plan" },
|
|
4
|
+
{ "query": "plan out the epic we signed off yesterday", "should_trigger": true, "expected_skill": "draft-plan" },
|
|
5
|
+
{ "query": "create the .condux/plans file for this feature", "should_trigger": true, "expected_skill": "draft-plan" },
|
|
6
|
+
{ "query": "break this down into tasks with dependencies", "should_trigger": true, "expected_skill": "draft-plan" },
|
|
7
|
+
{ "query": "write-plan for checkout v2", "should_trigger": true, "expected_skill": "draft-plan" },
|
|
8
|
+
{ "query": "we havent designed this yet but write a plan anyway", "should_trigger": true, "expected_skill": "draft-plan", "accept": ["discovery"] },
|
|
9
|
+
{ "query": "sketch the code for task 3 in the plan", "should_trigger": true, "expected_skill": "draft-plan", "kind": "in-context" },
|
|
10
|
+
{ "query": "the plan needs revision per the review feedback", "should_trigger": true, "expected_skill": "draft-plan" },
|
|
11
|
+
{ "query": "whats the task card format again", "should_trigger": true, "expected_skill": "draft-plan", "kind": "in-context" },
|
|
12
|
+
{ "query": "plan for migrating to app router, design is signed off", "should_trigger": true, "expected_skill": "draft-plan" },
|
|
13
|
+
{ "query": "how big should a task be, one function or the whole feature", "should_trigger": true, "expected_skill": "draft-plan", "kind": "in-context" },
|
|
14
|
+
{ "query": "can I edit the plan file after saving it", "should_trigger": true, "expected_skill": "draft-plan", "kind": "in-context" },
|
|
15
|
+
{ "query": "quick inline plan for a medium sized task", "should_trigger": true, "expected_skill": "workflow" },
|
|
16
|
+
{ "query": "review the plan in the browser before implementing", "should_trigger": true, "expected_skill": "plan-review" },
|
|
17
|
+
{ "query": "execute the plan with agents", "should_trigger": true, "expected_skill": "subagent-execution" },
|
|
18
|
+
{ "query": "design isnt agreed yet, help me align first", "should_trigger": true, "expected_skill": "discovery" },
|
|
19
|
+
{ "query": "add a task to my todo list", "should_trigger": false, "expected_skill": null },
|
|
20
|
+
{ "query": "plan my sprint for next week", "should_trigger": false, "expected_skill": null },
|
|
21
|
+
{ "query": "make a project timeline gantt chart", "should_trigger": false, "expected_skill": null }
|
|
22
|
+
]
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
# Plan: <Feature Name>
|
|
2
|
+
|
|
3
|
+
> Date: YYYY-MM-DD
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
**Goal:** One sentence — what this builds.
|
|
8
|
+
|
|
9
|
+
**Approach:** 2–3 sentences on the chosen direction.
|
|
10
|
+
|
|
11
|
+
**Tech / conventions:** Key libraries, patterns, and rules in play for this work.
|
|
12
|
+
|
|
13
|
+
## Global Constraints
|
|
14
|
+
|
|
15
|
+
Project-wide rules that every task must honour — copy exact values verbatim
|
|
16
|
+
from the design/spec, one line each. Delete this section only if there are
|
|
17
|
+
genuinely none.
|
|
18
|
+
|
|
19
|
+
- <version floor / dependency limit>
|
|
20
|
+
- <naming or copy rule>
|
|
21
|
+
- <platform / runtime requirement>
|
|
22
|
+
|
|
23
|
+
## Files Affected
|
|
24
|
+
|
|
25
|
+
- `path/to/file.ts` — what it does / what changes
|
|
26
|
+
- `path/to/other.ts:120-160` — the specific region touched
|
|
27
|
+
|
|
28
|
+
## Task Checklist
|
|
29
|
+
|
|
30
|
+
- [ ] Task 1: <Short Name>
|
|
31
|
+
- [ ] Task 2: <Short Name>
|
|
32
|
+
- [ ] Task 3: <Short Name>
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
## Task 1: <Short Name>
|
|
37
|
+
|
|
38
|
+
**What:** One paragraph — what this task builds or changes.
|
|
39
|
+
|
|
40
|
+
**Why:** One sentence — why this is needed / what it enables.
|
|
41
|
+
|
|
42
|
+
**Files:**
|
|
43
|
+
|
|
44
|
+
- Create: `exact/path/to/file.ts`
|
|
45
|
+
- Modify: `exact/path/to/existing.ts:123-145`
|
|
46
|
+
|
|
47
|
+
**Interfaces:**
|
|
48
|
+
|
|
49
|
+
- Consumes: what this task uses from earlier tasks — exact names / signatures
|
|
50
|
+
- Produces: what later tasks rely on — exact function names, parameter and
|
|
51
|
+
return types. A task's implementer sees only their own card; this block is
|
|
52
|
+
how neighbouring tasks learn the names and types they share. Use "None" for
|
|
53
|
+
a leaf task.
|
|
54
|
+
|
|
55
|
+
**Sketch:** _(optional — include only where seeing the code changes whether
|
|
56
|
+
you'd approve: the non-obvious algorithm, a tricky transform, a key data
|
|
57
|
+
shape or type definition. Skip boilerplate the implementer will obviously
|
|
58
|
+
write.)_
|
|
59
|
+
|
|
60
|
+
```ts
|
|
61
|
+
type Layer = { id: string; visible: boolean };
|
|
62
|
+
|
|
63
|
+
// core logic worth reviewing before it's written
|
|
64
|
+
function mergeLayers(base: Layer[], incoming: Layer[]): Layer[] {
|
|
65
|
+
// …the actual approach, concrete enough to annotate and tweak
|
|
66
|
+
}
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
**Gotchas:**
|
|
70
|
+
|
|
71
|
+
- Use `dayjs` for date handling, not native JS `Date`
|
|
72
|
+
- This touches the auth middleware — don't change its signature
|
|
73
|
+
- <any project convention or known quirk relevant to this task>
|
|
74
|
+
|
|
75
|
+
**Dependencies:** Task 2, Task 3 (or "None")
|
|
76
|
+
|
|
77
|
+
## Task 2: <Short Name>
|
|
78
|
+
|
|
79
|
+
**What:** …
|
|
80
|
+
|
|
81
|
+
**Why:** …
|
|
82
|
+
|
|
83
|
+
**Files:**
|
|
84
|
+
|
|
85
|
+
- Create: `…`
|
|
86
|
+
|
|
87
|
+
**Interfaces:**
|
|
88
|
+
|
|
89
|
+
- Consumes: …
|
|
90
|
+
- Produces: …
|
|
91
|
+
|
|
92
|
+
**Gotchas:**
|
|
93
|
+
|
|
94
|
+
- …
|
|
95
|
+
|
|
96
|
+
**Dependencies:** …
|