create-issflow 1.0.3 → 1.2.0

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.
Files changed (45) hide show
  1. package/README.md +61 -56
  2. package/bin/cli.js +270 -259
  3. package/package.json +32 -28
  4. package/template/.claude/agents/debugger.md +47 -47
  5. package/template/.claude/agents/e2e-runner.md +66 -66
  6. package/template/.claude/agents/implementer.md +79 -75
  7. package/template/.claude/agents/planner.md +93 -71
  8. package/template/.claude/agents/researcher.md +103 -103
  9. package/template/.claude/agents/synthesizer.md +78 -72
  10. package/template/.claude/agents/test-author.md +70 -70
  11. package/template/.claude/commands/change-request.md +53 -0
  12. package/template/.claude/commands/log-decision.md +33 -33
  13. package/template/.claude/commands/log-issue.md +28 -28
  14. package/template/.claude/commands/overview.md +114 -99
  15. package/template/.claude/commands/phase.md +230 -202
  16. package/template/.claude/commands/propose.md +71 -0
  17. package/template/.claude/commands/qa-audit.md +53 -0
  18. package/template/.claude/commands/quick.md +30 -30
  19. package/template/.claude/commands/replan.md +68 -63
  20. package/template/.claude/commands/security-audit.md +56 -0
  21. package/template/.claude/commands/store-wisdom.md +195 -195
  22. package/template/.claude/commands/synthesize.md +26 -26
  23. package/template/.claude/commands/ui-audit.md +54 -0
  24. package/template/.claude/commands/unstuck.md +40 -40
  25. package/template/.claude/hooks/pre-compact.js +42 -0
  26. package/template/.claude/hooks/session-start.js +137 -0
  27. package/template/.claude/hooks/subagent-stop.js +18 -0
  28. package/template/.claude/istartsoft-flow/METHODOLOGY.md +432 -229
  29. package/template/.claude/skills/caveman/SKILL.md +39 -39
  30. package/template/.claude/skills/code-standards/SKILL.md +61 -0
  31. package/template/.claude/skills/code-standards/references/architecture.md +61 -0
  32. package/template/.claude/skills/code-standards/references/naming.md +60 -0
  33. package/template/.claude/skills/grill-me/SKILL.md +31 -10
  34. package/template/.claude/skills/karpathy-guidelines/SKILL.md +34 -34
  35. package/template/.claude/skills/security/SKILL.md +70 -0
  36. package/template/.claude/skills/security/references/pentest-checklist.md +46 -0
  37. package/template/.claude/skills/security/references/secure-coding.md +50 -0
  38. package/template/.claude/skills/security/references/standards.md +60 -0
  39. package/template/.claude/skills/security/references/threat-modeling.md +36 -0
  40. package/template/.claude/skills/ux-design/SKILL.md +117 -99
  41. package/template/.claude/skills/ux-design/{wireframe-template.md → references/wireframe-template.md} +95 -95
  42. package/template/.claude/templates/proposal.html +126 -0
  43. package/template/.claude/hooks/pre-compact.sh +0 -25
  44. package/template/.claude/hooks/session-start.sh +0 -120
  45. package/template/.claude/hooks/subagent-stop.sh +0 -11
@@ -0,0 +1,54 @@
1
+ ---
2
+ description: Holistic UI audit — sweep the WHOLE product's UI against the ux-design cookbook (+ a11y / responsive / consistency), score it, and produce a prioritized findings report. On-demand or before a release. This is NOT the per-phase gate — the `ux-design` gate checks one screen at phase close (pass/block); this audit sweeps every screen and reports accumulated drift.
3
+ argument-hint: [optional scope — a route, or "all"]
4
+ ---
5
+
6
+ Caveman ULTRA mode. You are the ORCHESTRATOR.
7
+
8
+ Purpose: a periodic, WHOLE-PRODUCT UI audit — distinct from the inline `ux-design`
9
+ gate. The gate validates ONE screen at phase close; this AUDIT sweeps EVERY screen,
10
+ scores the product, and surfaces drift that accumulated across changes. Run before a
11
+ release, after big UI work, or on request.
12
+
13
+ ## PRE-FLIGHT
14
+ Read the rubric: `.claude/skills/ux-design/SKILL.md` (the cookbook) and
15
+ `references/wireframe-template.md` (the frame). The cookbook IS the checklist —
16
+ do not invent new criteria; audit against it.
17
+
18
+ ## STEP 1 — INVENTORY
19
+ List every screen / route / major component to audit (from the router, the
20
+ wireframe baseline, or `$ARGUMENTS`). Audit shared components once.
21
+
22
+ ## STEP 2 — SWEEP (dispatch a worker per area to keep context lean)
23
+ Score each screen against the cookbook dimensions:
24
+ - design tokens · 8-pt spacing · type scale (no raw hex/px)
25
+ - iconography — a real SVG set, **NEVER emoji**
26
+ - accessibility (WCAG 2.1 AA): contrast ≥ 4.5:1, visible focus, keyboard reach,
27
+ semantic HTML, labels / alt / aria, 44×44 targets, `prefers-reduced-motion`
28
+ - state matrix: default · hover · focus · active · disabled · loading · empty · error
29
+ - responsive breakpoints (no overflow / break)
30
+ - content & i18n (no hardcoded strings; growth-safe)
31
+ - consistency / wireframe conformance (no drift BETWEEN screens)
32
+ Run automated tools if the project has them (axe-core / Lighthouse / pa11y) and fold
33
+ their output in; otherwise do the manual cookbook sweep.
34
+
35
+ ## STEP 3 — SCORE + FINDINGS
36
+ Rate each dimension PASS / WARN / FAIL. For every finding record:
37
+ - **severity**: BLOCKER (a11y / contrast / unusable) · MAJOR (drift / missing state)
38
+ · MINOR (polish)
39
+ - **location**: screen + element
40
+ - **issue** + the cookbook rule it breaks
41
+ - **fix**: the concrete change
42
+
43
+ ## STEP 4 — REPORT
44
+ Write `docs/ui-audit-<YYYY-MM-DD>.md`:
45
+ - coverage (screens audited) · a per-dimension scoreboard · the findings table sorted
46
+ by severity · a prioritized fix list.
47
+ - Log BLOCKER / MAJOR findings to `docs/ISSUES.md`.
48
+ - **VERDICT: SHIP | FIX-FIRST** — a release must not ship with open BLOCKERs.
49
+
50
+ ## STEP 5 — REMEDIATE
51
+ AUTO: fix MINOR / MAJOR that don't change the visual direction, re-audit them, log.
52
+ A new visual direction or a design-token change → confirm with the user first
53
+ (hard rule 9 — UI conforms to the frame; new direction is a human call).
54
+ Hand back the report + what was fixed vs parked.
@@ -1,40 +1,40 @@
1
- ---
2
- description: Forced re-research after a circuit breaker. Stops flailing, re-routes to deep research with full memory of dead ends.
3
- ---
4
-
5
- Caveman ULTRA mode.
6
-
7
- Trigger: I chose "re-research" at a circuit breaker (see /phase step 5).
8
-
9
- Steps:
10
-
11
- 1. WRITE IT DOWN. Append to docs/ISSUES.md as OPEN:
12
- ```
13
-
14
- ### <error title>
15
-
16
- - [ ] open - stuck after 3 attempts
17
- - symptom: <…>
18
- - attempts that FAILED: <hypothesis 1>, <2>, <3>
19
-
20
- ```
21
- Reference the existing debug-<slug>.md.
22
-
23
- 2. RESET FRAME. The 3 failed hypotheses are probably all wrong. Discard them.
24
-
25
- 3. DEEP RESEARCH. Dispatch `researcher` in IMPL mode WIDE:
26
- - Read existing debug-<slug>.md and ISSUES.md failed-attempts FIRST.
27
- - Re-read the actual error from scratch.
28
- - Check real external service contract / docs.
29
- - Look one layer below: config? env? version? data shape?
30
- - Return fresh HYPOTHESIS backed by NEW evidence.
31
-
32
- 4. RE-PLAN if needed. Research shows phase design was wrong -> dispatch planner.
33
-
34
- 5. RESUME. Hand fresh hypothesis to `debugger`. It reads the prior debug file
35
- (already knows what's ruled out). Budget = 3, NEW hypotheses only.
36
-
37
- 6. This counts as the path chosen at the first breaker. If STUCK again ->
38
- /phase step 5 SECOND STUCK. Do not loop further.
39
-
40
- Report each step.
1
+ ---
2
+ description: Forced re-research after a circuit breaker. Stops flailing, re-routes to deep research with full memory of dead ends.
3
+ ---
4
+
5
+ Caveman ULTRA mode.
6
+
7
+ Trigger: I chose "re-research" at a circuit breaker (see /phase step 5).
8
+
9
+ Steps:
10
+
11
+ 1. WRITE IT DOWN. Append to docs/ISSUES.md as OPEN:
12
+ ```
13
+
14
+ ### <error title>
15
+
16
+ - [ ] open - stuck after 3 attempts
17
+ - symptom: <…>
18
+ - attempts that FAILED: <hypothesis 1>, <2>, <3>
19
+
20
+ ```
21
+ Reference the existing debug-<slug>.md.
22
+
23
+ 2. RESET FRAME. The 3 failed hypotheses are probably all wrong. Discard them.
24
+
25
+ 3. DEEP RESEARCH. Dispatch `researcher` in IMPL mode WIDE:
26
+ - Read existing debug-<slug>.md and ISSUES.md failed-attempts FIRST.
27
+ - Re-read the actual error from scratch.
28
+ - Check real external service contract / docs.
29
+ - Look one layer below: config? env? version? data shape?
30
+ - Return fresh HYPOTHESIS backed by NEW evidence.
31
+
32
+ 4. RE-PLAN if needed. Research shows phase design was wrong -> dispatch planner.
33
+
34
+ 5. RESUME. Hand fresh hypothesis to `debugger`. It reads the prior debug file
35
+ (already knows what's ruled out). Budget = 3, NEW hypotheses only.
36
+
37
+ 6. This counts as the path chosen at the first breaker. If STUCK again ->
38
+ /phase step 5 SECOND STUCK. Do not loop further.
39
+
40
+ Report each step.
@@ -0,0 +1,42 @@
1
+ #!/usr/bin/env node
2
+ // PreCompact hook. Fires before auto/manual compaction. Snapshots the live
3
+ // position so a post-compact session can recover. Pure Node, cross-platform.
4
+ 'use strict';
5
+ const fs = require('fs');
6
+ const path = require('path');
7
+ const { execSync } = require('child_process');
8
+
9
+ try { process.chdir(process.env.CLAUDE_PROJECT_DIR || '.'); } catch (_) {}
10
+
11
+ const sh = (cmd) => { try { return execSync(cmd, { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] }); } catch (_) { return ''; } };
12
+ const read = (f) => { try { return fs.readFileSync(f, 'utf8'); } catch (_) { return null; } };
13
+
14
+ fs.mkdirSync('docs/.snapshots', { recursive: true });
15
+ const d = new Date();
16
+ const p2 = (n) => String(n).padStart(2, '0');
17
+ const stamp = `${d.getFullYear()}${p2(d.getMonth() + 1)}${p2(d.getDate())}-${p2(d.getHours())}${p2(d.getMinutes())}${p2(d.getSeconds())}`;
18
+ const snap = path.join('docs/.snapshots', `precompact-${stamp}.md`);
19
+
20
+ const state = read('docs/STATE.md');
21
+ const body = [
22
+ `# Pre-compact snapshot ${stamp}`, '',
23
+ '## Git',
24
+ sh('git status --short').replace(/\n+$/, ''),
25
+ sh('git diff --stat').replace(/\n+$/, ''),
26
+ '',
27
+ '## STATE.md at compact time',
28
+ state !== null ? state.replace(/\n$/, '') : '(no STATE.md)',
29
+ '',
30
+ ].join('\n');
31
+ fs.writeFileSync(snap, body);
32
+
33
+ // keep the 5 newest precompact snapshots
34
+ const old = fs.readdirSync('docs/.snapshots')
35
+ .filter((f) => /^precompact-.*\.md$/.test(f))
36
+ .map((f) => ({ f, t: fs.statSync(path.join('docs/.snapshots', f)).mtimeMs }))
37
+ .sort((a, b) => b.t - a.t)
38
+ .slice(5);
39
+ for (const { f } of old) { try { fs.unlinkSync(path.join('docs/.snapshots', f)); } catch (_) {} }
40
+
41
+ process.stdout.write(`Context was compacted. Recovery snapshot saved at ${snap}.\n`);
42
+ process.stdout.write('STATE.md and ISSUES.md were re-injected by the SessionStart hook - trust those.\n');
@@ -0,0 +1,137 @@
1
+ #!/usr/bin/env node
2
+ // SessionStart hook. stdout is injected into the agent's context every session.
3
+ // Pure Node, cross-platform (macOS / Windows / Linux) — no bash/jq/python needed.
4
+ 'use strict';
5
+ const fs = require('fs');
6
+ const path = require('path');
7
+ const os = require('os');
8
+ const { execSync } = require('child_process');
9
+
10
+ try { process.chdir(process.env.CLAUDE_PROJECT_DIR || '.'); } catch (_) {}
11
+
12
+ const out = [];
13
+ const emit = (s = '') => out.push(s);
14
+ const read = (f) => { try { return fs.readFileSync(f, 'utf8'); } catch (_) { return null; } };
15
+ const exists = (f) => fs.existsSync(f);
16
+ const sh = (cmd) => { try { return execSync(cmd, { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] }); } catch (_) { return ''; } };
17
+
18
+ emit('=== iStartSoftFlow AUTO-CONTEXT (injected by hook, NOT optional) ===');
19
+ emit('');
20
+
21
+ // 1. git state
22
+ emit('## Git');
23
+ emit('branch: ' + (sh('git branch --show-current').trim() || 'n/a'));
24
+ const uncommitted = sh('git status --short').split('\n').filter((l) => l.trim() !== '').length;
25
+ emit('uncommitted: ' + uncommitted + ' file(s)');
26
+ for (const l of sh('git log --oneline -3').replace(/\n+$/, '').split('\n').filter(Boolean)) emit(' ' + l);
27
+ emit('');
28
+
29
+ // 2. active state (cap size — STATE.md is meant to stay small)
30
+ const state = read('docs/STATE.md');
31
+ if (state !== null) {
32
+ emit('## STATE.md (current position - READ THIS FIRST)');
33
+ const sl = state.replace(/\n$/, '').split('\n');
34
+ for (const l of sl.slice(0, 40)) emit(l);
35
+ if (sl.length > 40) emit(`… (+${sl.length - 40} more — STATE.md should be small; trim it)`);
36
+ emit('');
37
+ } else {
38
+ emit('## STATE.md missing -> run /overview to bootstrap the project.');
39
+ emit('');
40
+ }
41
+
42
+ // 3. issue log — inject only OPEN issues (resolved ones stay in the file for
43
+ // grep, but are NOT re-paid in tokens every session). Capped.
44
+ const issues = read('docs/ISSUES.md');
45
+ if (issues !== null) {
46
+ const blocks = [];
47
+ let cur = null;
48
+ for (const l of issues.split('\n')) {
49
+ if (/^### /.test(l)) { if (cur) blocks.push(cur); cur = [l]; }
50
+ else if (cur) cur.push(l);
51
+ }
52
+ if (cur) blocks.push(cur);
53
+ const open = blocks.filter((b) => b.some((l) => /- \[ \]/.test(l)));
54
+ emit(`## ISSUES.md (${open.length} open) - grep this file before debugging anything`);
55
+ if (open.length) {
56
+ const flat = open.flat();
57
+ for (const l of flat.slice(0, 50)) emit(l);
58
+ if (flat.length > 50) emit('… (more — grep docs/ISSUES.md for full detail)');
59
+ } else {
60
+ emit('(no open issues)');
61
+ }
62
+ emit('');
63
+ }
64
+
65
+ // 3b. research index
66
+ const idx = read('docs/research/INDEX.md');
67
+ if (idx !== null) {
68
+ const rows = idx.split('\n').filter((l) => /^[0-9]/.test(l));
69
+ emit(`## research/INDEX.md (${rows.length} prior investigations)`);
70
+ emit('grep this before any new research or debugging.');
71
+ for (const l of rows.slice(-15)) emit(' ' + l);
72
+ emit('');
73
+ }
74
+
75
+ // 3d. shared KB — pull latest + load snapshot
76
+ const KB_CONFIG = '.claude/kb-config.json';
77
+ let kbActive = false;
78
+ if (exists(KB_CONFIG)) {
79
+ let cfg = {}; try { cfg = JSON.parse(read(KB_CONFIG) || '{}'); } catch (_) {}
80
+ let kbPath = cfg.kb_path || '';
81
+ if (kbPath.startsWith('~')) kbPath = path.join(os.homedir(), kbPath.slice(1));
82
+ if (kbPath && exists(kbPath) && fs.statSync(kbPath).isDirectory()) {
83
+ emit('## Shared KB');
84
+ let pulled = true;
85
+ try { execSync(`git -C "${kbPath}" pull --ff-only --quiet`, { stdio: 'ignore' }); } catch (_) { pulled = false; }
86
+ emit(pulled ? 'KB pulled: OK' : 'KB pull skipped (offline or conflict — using local copy)');
87
+
88
+ const cut = new Date(); cut.setMonth(cut.getMonth() - 6);
89
+ const cutoff = cut.toISOString().slice(0, 10);
90
+ const today = new Date().toISOString().slice(0, 10);
91
+ const kbIndex = path.join(kbPath, 'INDEX.md');
92
+ if (exists(kbIndex)) {
93
+ const lines = (read(kbIndex) || '').split('\n').filter((l) => l && !l.startsWith('#'));
94
+ const snap = [`# KB snapshot — loaded ${today}`, `# Stale = created date older than ${cutoff}`, ''];
95
+ let stale = 0, total = 0;
96
+ for (const line of lines) {
97
+ if (line.includes('|')) total++;
98
+ const entryDate = (line.split('|')[0] || '').trim();
99
+ if (/^\d{4}-\d{2}-\d{2}$/.test(entryDate) && entryDate < cutoff) { snap.push('[STALE] ' + line); stale++; }
100
+ else snap.push(line);
101
+ }
102
+ try { fs.mkdirSync('docs', { recursive: true }); fs.writeFileSync('docs/.kb-snapshot.md', snap.join('\n') + '\n'); } catch (_) {}
103
+ emit(`KB snapshot loaded: ${total} entries (${stale} stale — researcher will re-research these)`);
104
+ emit('Snapshot at docs/.kb-snapshot.md — researcher reads this before web search.');
105
+ } else {
106
+ emit(`KB INDEX.md not found at ${kbPath} — run /store-wisdom to populate it.`);
107
+ }
108
+ emit('');
109
+ kbActive = true;
110
+ } else {
111
+ emit('## Shared KB: configured but path not found (' + (cfg.kb_path || '') + ')');
112
+ emit('Fix kb_path in .claude/kb-config.json (or re-run: npx create-issflow).');
113
+ emit('');
114
+ }
115
+ }
116
+
117
+ // 4. hard rule reminder
118
+ emit('## RULES (enforced this session)');
119
+ emit('- AUTO mode (default) governs the DEV loop: follow the plan — decide + log +');
120
+ emit(' continue, do NOT stop to ask. (Planning / grill still asks — that part is fine.)');
121
+ emit(' Hard-stops only: security / irreversible-or-outbound actions / contradictory spec.');
122
+ emit('- caveman ULTRA mode is active.');
123
+ emit('- before debugging ANY error: grep ISSUES.md AND research/INDEX.md first.');
124
+ emit('- debug attempts: WARN at 2; cap 3. AUTO: log + park the slice + continue (batched');
125
+ emit(' report at the phase boundary). GUIDED: stop and ask you.');
126
+ emit('- end of every phase: run /synthesize, then /clear.');
127
+ emit('- small obvious change? use /quick, not /phase.');
128
+ emit('- token economy: keep context lean. Delegate noisy work to subagents (they');
129
+ emit(' return summaries). If one phase grows past ~50% of the model window, split');
130
+ emit(' it or /synthesize -> /clear — do not coast to auto-compact.');
131
+ if (kbActive) {
132
+ emit('- KB active: researcher checks docs/.kb-snapshot.md before web search.');
133
+ emit('- learned something worth keeping? run /store-wisdom.');
134
+ }
135
+ emit('=== END AUTO-CONTEXT ===');
136
+
137
+ process.stdout.write(out.join('\n') + '\n');
@@ -0,0 +1,18 @@
1
+ #!/usr/bin/env node
2
+ // SubagentStop hook. Appends a trace line, keeps the log bounded. Cross-platform.
3
+ 'use strict';
4
+ const fs = require('fs');
5
+ const path = require('path');
6
+
7
+ try { process.chdir(process.env.CLAUDE_PROJECT_DIR || '.'); } catch (_) {}
8
+
9
+ fs.mkdirSync('docs/.snapshots', { recursive: true });
10
+ const log = path.join('docs/.snapshots', 'agent-trace.log');
11
+ const d = new Date();
12
+ const p2 = (n) => String(n).padStart(2, '0');
13
+ const ts = `${p2(d.getHours())}:${p2(d.getMinutes())}:${p2(d.getSeconds())}`;
14
+
15
+ let lines = [];
16
+ try { lines = fs.readFileSync(log, 'utf8').split('\n').filter(Boolean); } catch (_) {}
17
+ lines.push(`${ts} subagent finished`);
18
+ fs.writeFileSync(log, lines.slice(-50).join('\n') + '\n');