flow-cc 0.1.0 → 0.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.
package/CHANGELOG.md ADDED
@@ -0,0 +1,44 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [0.2.0] - 2026-02-11
9
+
10
+ ### Added
11
+ - `CHANGELOG.md` in Keep a Changelog format
12
+ - `CONTRIBUTING.md` with skill file format docs and testing expectations
13
+ - Example lesson in `templates/lessons.md.template` showing PATTERN/CAUSE/FIX/RULE format
14
+ - Node.js version check (requires >= 18) with clear error message
15
+ - Write permission check before install starts
16
+ - Rollback on install failure — tracks created files and cleans up
17
+ - Post-install verification — checks key files exist after copy
18
+ - `--verify` flag for install health check without modifying anything
19
+ - `engines` field in package.json (`node >= 18`)
20
+ - Error logging for hooks — writes to `~/.claude/hooks/flow-error.log` (capped at 50KB)
21
+ - Context size limits in `/flow:spec` — caps codebase scan at 50 files, focused mode for 500+ file repos
22
+
23
+ ### Fixed
24
+ - `.gitignore` now has proper Node.js entries (was just `nul`)
25
+ - `/flow:done` archive step now creates `.planning/archive/` if missing and skips gracefully when no `.planning/` exists
26
+ - `/flow:update` now shows changelog for the new version before confirming update
27
+
28
+ ## [0.1.1] - 2025-05-01
29
+
30
+ ### Changed
31
+ - Updated package.json metadata (description, keywords, repository links)
32
+ - Polished README with badges and install instructions
33
+ - Added MIT license file
34
+
35
+ ## [0.1.0] - 2025-05-01
36
+
37
+ ### Added
38
+ - Initial release of Flow plugin for Claude Code
39
+ - 8 skills: init, spec, go, done, status, task, intro, update
40
+ - 2 hooks: statusline display, background update checker
41
+ - 4 templates: CLAUDE.md, STATE.md, ROADMAP.md, lessons.md
42
+ - One-command installer via `npx flow-cc`
43
+ - Automatic statusLine configuration
44
+ - Uninstall support via `npx flow-cc --uninstall`
@@ -0,0 +1,59 @@
1
+ # Contributing to Flow
2
+
3
+ Thanks for your interest in contributing! Flow is a structured workflow system for Claude Code, and contributions are welcome.
4
+
5
+ ## Reporting Bugs
6
+
7
+ Open an issue at [github.com/troyhoffman-oss/flow-plugin/issues](https://github.com/troyhoffman-oss/flow-plugin/issues) with:
8
+
9
+ - Steps to reproduce
10
+ - Expected vs. actual behavior
11
+ - Your OS and Node.js version
12
+ - Claude Code version (if relevant)
13
+
14
+ ## Suggesting Features
15
+
16
+ Open an issue with the `enhancement` label. Describe:
17
+
18
+ - The problem you're trying to solve
19
+ - Your proposed solution
20
+ - Any alternatives you've considered
21
+
22
+ ## Submitting Pull Requests
23
+
24
+ 1. Fork the repo and create a branch from `master`
25
+ 2. Make your changes
26
+ 3. Test the installer: `node bin/install.js` from the repo root
27
+ 4. Verify skills load in Claude Code (run `/flow:intro`)
28
+ 5. Open a PR with a clear description of what changed and why
29
+
30
+ ### Skill File Format
31
+
32
+ Skills live in `skills/` as Markdown files with YAML frontmatter:
33
+
34
+ ```markdown
35
+ ---
36
+ name: flow:example
37
+ description: One-line description
38
+ user_invocable: true
39
+ ---
40
+
41
+ # /flow:example — Title
42
+
43
+ Instructions for Claude Code to follow when this skill is invoked.
44
+ ```
45
+
46
+ ### Testing Expectations
47
+
48
+ - Run `node bin/install.js` and verify all files land in `~/.claude/`
49
+ - Run `node bin/install.js --uninstall` and verify clean removal
50
+ - Run `node bin/install.js --verify` to check install health
51
+ - Test on both Windows and macOS/Linux if possible
52
+
53
+ ## Architecture
54
+
55
+ See [DESIGN.md](DESIGN.md) for architecture decisions and system design.
56
+
57
+ ## Code of Conduct
58
+
59
+ Be kind, constructive, and respectful. We're all here to build better tools.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Troy Hoffman
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,66 +1,132 @@
1
- # Flow Plugin
1
+ <p align="center">
2
+ <h1 align="center">Flow</h1>
3
+ <p align="center">A structured workflow system for Claude Code.<br/>Spec interviews. Agent-team execution. Session handoffs. Compounding knowledge.</p>
4
+ </p>
2
5
 
3
- **Formalized workflow skills for Claude Code. Turns a proven spec-interview, agent-team-execution, session-handoff pattern into reusable `/flow:*` commands.**
6
+ <p align="center">
7
+ <a href="https://www.npmjs.com/package/flow-cc"><img src="https://img.shields.io/npm/v/flow-cc.svg" alt="npm version"></a>
8
+ <a href="https://github.com/troyhoffman-oss/flow-plugin/blob/master/LICENSE"><img src="https://img.shields.io/npm/l/flow-cc.svg" alt="license"></a>
9
+ <a href="https://www.npmjs.com/package/flow-cc"><img src="https://img.shields.io/npm/dm/flow-cc.svg" alt="downloads"></a>
10
+ </p>
4
11
 
5
- ## What It Does
12
+ ---
6
13
 
7
- - `/flow:intro` -- Walkthrough of the system — **start here**
8
- - `/flow:init` -- Initialize a new project or milestone with planning scaffolding
9
- - `/flow:spec` -- Run a spec interview that produces an executable PRD with wave-based phases
10
- - `/flow:task` -- Lightweight task execution — bug fixes, cleanup, small features without a PRD
11
- - `/flow:go` -- Execute the next phase from the PRD using agent teams
12
- - `/flow:done` -- Session-end docs, lessons refinement, and handoff prompt generation
13
- - `/flow:status` -- Quick "where am I?" orientation
14
- - `/flow:update` -- Pull latest and re-install skills from any session
15
-
16
- ## Installation
14
+ ## Install
17
15
 
18
16
  ```bash
19
- # Mac/Linux
20
- git clone https://github.com/troyhoffman/flow-plugin.git && cd flow-plugin && bash setup.sh
21
-
22
- # Windows (PowerShell)
23
- git clone https://github.com/troyhoffman/flow-plugin.git; cd flow-plugin; .\setup.ps1
17
+ npx flow-cc
24
18
  ```
25
19
 
26
- **Update:** Run `/flow:update` in any Claude Code session
20
+ One command. Installs skills, hooks, templates, and configures your statusLine. Works on Mac, Linux, and Windows.
21
+
22
+ ## Why Flow?
23
+
24
+ Claude Code is powerful but unstructured. Without a system, you lose context between sessions, repeat mistakes, and waste tokens re-explaining what you've already decided.
25
+
26
+ Flow fixes this by giving Claude Code a **memory system and execution framework**:
27
27
 
28
- ## Getting Started
28
+ - **Spec interviews** extract decisions upfront so agents execute instead of guessing
29
+ - **PRDs become execution contracts** that agent teams follow phase-by-phase
30
+ - **Session handoffs** preserve context across fresh sessions — no more "where was I?"
31
+ - **Lessons compound** — mistakes get captured, refined, and promoted into permanent rules
29
32
 
30
- After installing, run `/flow:intro` in any Claude Code session. It explains the full lifecycle, what each command does, and typical usage patterns.
33
+ ## Commands
34
+
35
+ | Command | What it does |
36
+ |---|---|
37
+ | `/flow:intro` | Walkthrough of the system — **start here** |
38
+ | `/flow:init` | Initialize a project with `.planning/` scaffolding, CLAUDE.md, templates |
39
+ | `/flow:spec` | Spec interview that produces an executable PRD with phased execution plan |
40
+ | `/flow:go` | Execute the next phase from the PRD using wave-based agent teams |
41
+ | `/flow:task` | Bug fixes, cleanup, small features — no PRD needed |
42
+ | `/flow:done` | Session-end documentation, lessons refinement, handoff prompt |
43
+ | `/flow:status` | Quick orientation — current milestone, phase progress, next actions |
44
+ | `/flow:update` | Update Flow to the latest version |
31
45
 
32
46
  ## How It Works
33
47
 
34
- Skills install to `~/.claude/commands/flow/` and are immediately available in any Claude Code session. The workflow:
48
+ ```
49
+ /flow:init → /flow:spec → /flow:go (repeat per phase) → /flow:done
50
+
51
+ handoff prompt for next session
52
+
53
+ /flow:task ← standalone path for bug fixes and small features
54
+ ```
55
+
56
+ **The lifecycle in practice:**
35
57
 
36
- 1. `/flow:init` -- Creates `.planning/` directory structure, CLAUDE.md, and initial docs
37
- 2. `/flow:spec` -- Interviews you about the milestone, produces PRD.md with wave-based phases
38
- 3. `/flow:go` -- Reads PRD, spawns agent teams per wave structure, verifies, commits
39
- 4. `/flow:task` -- Quick fixes and small features — executes, verifies, commits (no PRD needed)
40
- 5. `/flow:done` -- Updates STATE.md, ROADMAP.md, lessons.md, generates handoff prompt
41
- 6. `/flow:status` -- Read-only orientation check
58
+ 1. **`/flow:init`** Creates `.planning/` directory, CLAUDE.md, STATE.md, ROADMAP.md, lessons.md
59
+ 2. **`/flow:spec`** Interviews you about the milestone. Produces a PRD with wave-based phases, acceptance criteria, and agent-team structure
60
+ 3. **`/flow:go`** Reads the PRD, spawns parallel agent teams per wave, builds, verifies, commits
61
+ 4. **`/flow:done`** Updates all planning docs, captures lessons, generates a one-line handoff prompt so the next session starts instantly
62
+ 5. **Repeat** Next session picks up from the handoff. Context lives in the repo, not the conversation.
42
63
 
43
- ## Lifecycle
64
+ ## What Gets Installed
44
65
 
45
66
  ```
46
- /flow:init -> /flow:spec -> /flow:go (repeat per phase) -> /flow:done
47
- |
48
- handoff prompt for next session
67
+ ~/.claude/
68
+ ├── commands/flow/
69
+ │ ├── flow-init.md # 8 skill files
70
+ │ ├── flow-spec.md
71
+ │ ├── flow-go.md
72
+ │ ├── flow-task.md
73
+ │ ├── flow-done.md
74
+ │ ├── flow-status.md
75
+ │ ├── flow-intro.md
76
+ │ ├── flow-update.md
77
+ │ ├── VERSION
78
+ │ └── templates/ # Project scaffolding templates
79
+ │ ├── CLAUDE.md.template
80
+ │ ├── STATE.md.template
81
+ │ ├── ROADMAP.md.template
82
+ │ └── lessons.md.template
83
+ ├── hooks/
84
+ │ ├── flow-check-update.js # Notifies when updates are available
85
+ │ └── flow-statusline.js # Shows project context in statusLine
86
+ └── settings.json # statusLine configured automatically
87
+ ```
88
+
89
+ ## Project Structure
90
+
91
+ Every Flow project gets this structure via `/flow:init`:
49
92
 
50
- /flow:task ← standalone path for bug fixes, cleanup, small features (no PRD needed)
51
93
  ```
94
+ your-project/
95
+ ├── CLAUDE.md # Project-specific execution rules
96
+ ├── PRD.md # Current milestone spec (created by /flow:spec)
97
+ ├── .planning/
98
+ │ ├── STATE.md # Session GPS — current status, next actions
99
+ │ ├── ROADMAP.md # Milestone phases and progress
100
+ │ └── archive/ # Completed milestones and old PRDs
101
+ └── tasks/
102
+ └── lessons.md # Mistake catalog → refined into permanent rules
103
+ ```
104
+
105
+ ## The Lessons System
52
106
 
53
- ## Compatible with GSD
107
+ Flow's knowledge compounding is what makes it get better over time:
54
108
 
55
- Uses the same `.planning/` directory structure. You can still use `/gsd:debug`, `/gsd:map-codebase`, etc. alongside Flow commands.
109
+ 1. **Capture** Mistake happens, lesson written to `tasks/lessons.md`
110
+ 2. **Refine** — Each `/flow:done` merges duplicates, sharpens rules, removes obvious ones
111
+ 3. **Promote** — Universal lessons move to `~/.claude/lessons.md` (all projects)
112
+ 4. **Permanence** — Recurring patterns become rules in `CLAUDE.md`
56
113
 
57
- ## Philosophy
114
+ The goal: fewer, sharper lessons over time — not an ever-growing list.
58
115
 
59
- - Front-load decisions into the spec interview, making execution simple
60
- - Knowledge compounds through lessons.md to CLAUDE.md promotion lifecycle
61
- - Fresh context per session; memory lives in the repo, not the conversation
62
- - PRD is the execution contract -- agents execute what was specified
116
+ ## Compatible With GSD
117
+
118
+ Flow uses the same `.planning/` directory structure as [GSD](https://github.com/gsd-framework/gsd). You can use `/gsd:debug`, `/gsd:map-codebase`, and other GSD commands alongside Flow.
63
119
 
64
120
  ## Requirements
65
121
 
66
- Claude Code CLI with skills support.
122
+ [Claude Code](https://docs.anthropic.com/en/docs/claude-code) CLI with skills support.
123
+
124
+ ## Links
125
+
126
+ - [Changelog](CHANGELOG.md) — what changed in each version
127
+ - [Contributing](CONTRIBUTING.md) — how to report bugs, suggest features, submit PRs
128
+ - [Design](DESIGN.md) — architecture decisions and system design
129
+
130
+ ## License
131
+
132
+ MIT
package/VERSION CHANGED
@@ -1 +1 @@
1
- 0.1.0
1
+ 0.2.0
package/bin/install.js CHANGED
@@ -1,11 +1,18 @@
1
1
  #!/usr/bin/env node
2
2
  // Flow plugin installer for Claude Code
3
- // Usage: npx flow-cc [--uninstall]
3
+ // Usage: npx flow-cc [--uninstall] [--verify]
4
4
 
5
5
  const fs = require('fs');
6
6
  const path = require('path');
7
7
  const os = require('os');
8
8
 
9
+ // ---------- Node.js version check ----------
10
+ const nodeMajor = parseInt(process.versions.node.split('.')[0], 10);
11
+ if (nodeMajor < 18) {
12
+ console.error('Flow requires Node.js 18 or later. You have ' + process.version);
13
+ process.exit(1);
14
+ }
15
+
9
16
  const homeDir = os.homedir();
10
17
  const claudeDir = path.join(homeDir, '.claude');
11
18
  const commandsDir = path.join(claudeDir, 'commands', 'flow');
@@ -21,6 +28,93 @@ const templatesDir = path.join(pkgRoot, 'templates');
21
28
  const versionFile = path.join(pkgRoot, 'VERSION');
22
29
 
23
30
  const uninstall = process.argv.includes('--uninstall') || process.argv.includes('-u');
31
+ const verify = process.argv.includes('--verify') || process.argv.includes('-v');
32
+
33
+ // ---------- Verify ----------
34
+ if (verify) {
35
+ console.log('Flow install health check:\n');
36
+
37
+ let passed = 0;
38
+ const total = 5;
39
+
40
+ // 1. Skills installed
41
+ try {
42
+ const files = fs.existsSync(commandsDir)
43
+ ? fs.readdirSync(commandsDir).filter(f => f.endsWith('.md'))
44
+ : [];
45
+ if (files.length > 0) {
46
+ console.log(` \u2713 Skills installed (${files.length} files)`);
47
+ passed++;
48
+ } else {
49
+ console.log(' \u2717 Skills not installed (0 .md files in commands/flow/)');
50
+ }
51
+ } catch (e) {
52
+ console.log(' \u2717 Skills not installed (cannot read commands/flow/)');
53
+ }
54
+
55
+ // 2. Hooks installed
56
+ try {
57
+ const hookFiles = ['flow-check-update.js', 'flow-statusline.js'];
58
+ const found = hookFiles.filter(h => fs.existsSync(path.join(hooksDir, h)));
59
+ if (found.length === 2) {
60
+ console.log(' \u2713 Hooks installed (2 files)');
61
+ passed++;
62
+ } else {
63
+ console.log(` \u2717 Hooks incomplete (${found.length}/2 files)`);
64
+ }
65
+ } catch (e) {
66
+ console.log(' \u2717 Hooks not installed');
67
+ }
68
+
69
+ // 3. VERSION file
70
+ try {
71
+ const vPath = path.join(commandsDir, 'VERSION');
72
+ if (fs.existsSync(vPath)) {
73
+ const ver = fs.readFileSync(vPath, 'utf8').trim();
74
+ console.log(` \u2713 VERSION file present (${ver})`);
75
+ passed++;
76
+ } else {
77
+ console.log(' \u2717 VERSION file missing');
78
+ }
79
+ } catch (e) {
80
+ console.log(' \u2717 VERSION file missing');
81
+ }
82
+
83
+ // 4. Templates installed
84
+ try {
85
+ const tDir = path.join(commandsDir, 'templates');
86
+ const files = fs.existsSync(tDir) ? fs.readdirSync(tDir) : [];
87
+ if (files.length > 0) {
88
+ console.log(` \u2713 Templates installed (${files.length} files)`);
89
+ passed++;
90
+ } else {
91
+ console.log(' \u2717 Templates not installed (0 files)');
92
+ }
93
+ } catch (e) {
94
+ console.log(' \u2717 Templates not installed');
95
+ }
96
+
97
+ // 5. StatusLine configured
98
+ try {
99
+ if (fs.existsSync(settingsPath)) {
100
+ const settings = JSON.parse(fs.readFileSync(settingsPath, 'utf8'));
101
+ if (settings.statusLine && typeof settings.statusLine.command === 'string' &&
102
+ settings.statusLine.command.includes('flow-statusline')) {
103
+ console.log(' \u2713 StatusLine configured');
104
+ passed++;
105
+ } else {
106
+ console.log(' \u2717 StatusLine not configured');
107
+ }
108
+ } else {
109
+ console.log(' \u2717 StatusLine not configured (no settings.json)');
110
+ }
111
+ } catch (e) {
112
+ console.log(' \u2717 StatusLine not configured');
113
+ }
114
+
115
+ console.log(`\nResult: ${passed}/${total} checks passed`);
116
+ process.exit(passed === total ? 0 : 1);
117
+ }
24
118
 
25
119
  // ---------- Uninstall ----------
26
120
  if (uninstall) {
@@ -60,7 +154,7 @@ if (uninstall) {
60
154
  console.log(' Removed statusLine from ~/.claude/settings.json');
61
155
  }
62
156
  } catch (e) {
63
- // settings.json parse error leave it alone
157
+ // settings.json parse error -- leave it alone
64
158
  }
65
159
  }
66
160
 
@@ -71,79 +165,175 @@ if (uninstall) {
71
165
  // ---------- Install ----------
72
166
  console.log('Installing Flow plugin...\n');
73
167
 
74
- // 1. Create directories
75
- for (const dir of [commandsDir, hooksDir, cacheDir]) {
76
- fs.mkdirSync(dir, { recursive: true });
168
+ // ---------- Write permission check ----------
169
+ const permTestDir = fs.existsSync(claudeDir) ? claudeDir : path.dirname(claudeDir);
170
+ const permTestFile = path.join(permTestDir, '.flow-perm-test-' + process.pid);
171
+ try {
172
+ fs.writeFileSync(permTestFile, 'test');
173
+ fs.unlinkSync(permTestFile);
174
+ } catch (e) {
175
+ console.error('Cannot write to ' + claudeDir + ' \u2014 check permissions.');
176
+ process.exit(1);
77
177
  }
78
178
 
79
- // 2. Copy skills: skills/flow-*.md → commands/flow/*.md (strip "flow-" prefix)
80
- const skillFiles = fs.readdirSync(skillsDir).filter(f => f.startsWith('flow-') && f.endsWith('.md'));
81
- for (const file of skillFiles) {
82
- const dest = file.replace(/^flow-/, '');
83
- fs.copyFileSync(path.join(skillsDir, file), path.join(commandsDir, dest));
179
+ // ---------- Rollback tracking ----------
180
+ const created = []; // paths created during install (files and dirs we created fresh)
181
+
182
+ function trackFile(filePath) {
183
+ created.push({ type: 'file', path: filePath });
84
184
  }
85
- console.log(` Installed ${skillFiles.length} skills → ~/.claude/commands/flow/`);
86
185
 
87
- // 3. Copy hooks
88
- const hookFiles = ['flow-check-update.js', 'flow-statusline.js'];
89
- for (const hook of hookFiles) {
90
- const src = path.join(srcHooksDir, hook);
91
- if (fs.existsSync(src)) {
92
- fs.copyFileSync(src, path.join(hooksDir, hook));
186
+ function trackDir(dirPath) {
187
+ // Only track if we actually created it (didn't exist before)
188
+ if (!fs.existsSync(dirPath)) {
189
+ created.push({ type: 'dir', path: dirPath });
93
190
  }
94
191
  }
95
- console.log(' Installed hooks → ~/.claude/hooks/');
96
192
 
97
- // 4. Copy VERSION
98
- fs.copyFileSync(versionFile, path.join(commandsDir, 'VERSION'));
99
- console.log(' Installed VERSION ~/.claude/commands/flow/VERSION');
100
-
101
- // 5. Copy templates
102
- const destTemplatesDir = path.join(commandsDir, 'templates');
103
- fs.mkdirSync(destTemplatesDir, { recursive: true });
104
- if (fs.existsSync(templatesDir)) {
105
- const templateFiles = fs.readdirSync(templatesDir);
106
- for (const file of templateFiles) {
107
- fs.copyFileSync(path.join(templatesDir, file), path.join(destTemplatesDir, file));
193
+ function rollback() {
194
+ console.error('\nRolling back partial install...');
195
+ for (let i = created.length - 1; i >= 0; i--) {
196
+ const item = created[i];
197
+ try {
198
+ if (item.type === 'file' && fs.existsSync(item.path)) {
199
+ fs.unlinkSync(item.path);
200
+ } else if (item.type === 'dir' && fs.existsSync(item.path)) {
201
+ fs.rmSync(item.path, { recursive: true });
202
+ }
203
+ } catch (e) {
204
+ // Best-effort cleanup
205
+ }
108
206
  }
109
- console.log(` Installed ${templateFiles.length} templates → ~/.claude/commands/flow/templates/`);
207
+ console.error('Rollback complete.');
110
208
  }
111
209
 
112
- // 6. Merge statusLine into settings.json
113
- let settings = {};
114
- if (fs.existsSync(settingsPath)) {
115
- try {
116
- settings = JSON.parse(fs.readFileSync(settingsPath, 'utf8'));
117
- } catch (e) {
118
- // Corrupted settings — start fresh but warn
119
- console.log(' Warning: could not parse existing settings.json, preserving as backup');
120
- fs.copyFileSync(settingsPath, settingsPath + '.bak');
210
+ try {
211
+ // 1. Create directories
212
+ for (const dir of [commandsDir, hooksDir, cacheDir]) {
213
+ trackDir(dir);
214
+ fs.mkdirSync(dir, { recursive: true });
121
215
  }
122
- }
123
- settings.statusLine = {
124
- type: 'command',
125
- command: `node "${path.join(hooksDir, 'flow-statusline.js')}"`
126
- };
127
- fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + '\n');
128
- console.log(' Configured statusLine in ~/.claude/settings.json');
129
-
130
- // 7. Write .source breadcrumb (for dev/setup.sh compat)
131
- fs.writeFileSync(path.join(commandsDir, '.source'), pkgRoot + '\n');
132
-
133
- // Done
134
- const version = fs.readFileSync(versionFile, 'utf8').trim();
135
- console.log(`
216
+
217
+ // 2. Copy skills: skills/flow-*.md -> commands/flow/*.md (strip "flow-" prefix)
218
+ const skillFiles = fs.readdirSync(skillsDir).filter(f => f.startsWith('flow-') && f.endsWith('.md'));
219
+ for (const file of skillFiles) {
220
+ const dest = file.replace(/^flow-/, '');
221
+ const destPath = path.join(commandsDir, dest);
222
+ fs.copyFileSync(path.join(skillsDir, file), destPath);
223
+ trackFile(destPath);
224
+ }
225
+ console.log(` Installed ${skillFiles.length} skills \u2192 ~/.claude/commands/flow/`);
226
+
227
+ // 3. Copy hooks
228
+ const hookFiles = ['flow-check-update.js', 'flow-statusline.js'];
229
+ for (const hook of hookFiles) {
230
+ const src = path.join(srcHooksDir, hook);
231
+ if (fs.existsSync(src)) {
232
+ const destPath = path.join(hooksDir, hook);
233
+ fs.copyFileSync(src, destPath);
234
+ trackFile(destPath);
235
+ }
236
+ }
237
+ console.log(' Installed hooks \u2192 ~/.claude/hooks/');
238
+
239
+ // 4. Copy VERSION
240
+ const versionDest = path.join(commandsDir, 'VERSION');
241
+ fs.copyFileSync(versionFile, versionDest);
242
+ trackFile(versionDest);
243
+ console.log(' Installed VERSION \u2192 ~/.claude/commands/flow/VERSION');
244
+
245
+ // 5. Copy templates
246
+ const destTemplatesDir = path.join(commandsDir, 'templates');
247
+ trackDir(destTemplatesDir);
248
+ fs.mkdirSync(destTemplatesDir, { recursive: true });
249
+ if (fs.existsSync(templatesDir)) {
250
+ const templateFiles = fs.readdirSync(templatesDir);
251
+ for (const file of templateFiles) {
252
+ const destPath = path.join(destTemplatesDir, file);
253
+ fs.copyFileSync(path.join(templatesDir, file), destPath);
254
+ trackFile(destPath);
255
+ }
256
+ console.log(` Installed ${templateFiles.length} templates \u2192 ~/.claude/commands/flow/templates/`);
257
+ }
258
+
259
+ // 6. Merge statusLine into settings.json
260
+ let settings = {};
261
+ if (fs.existsSync(settingsPath)) {
262
+ try {
263
+ settings = JSON.parse(fs.readFileSync(settingsPath, 'utf8'));
264
+ } catch (e) {
265
+ // Corrupted settings -- start fresh but warn
266
+ console.log(' Warning: could not parse existing settings.json, preserving as backup');
267
+ fs.copyFileSync(settingsPath, settingsPath + '.bak');
268
+ }
269
+ }
270
+ settings.statusLine = {
271
+ type: 'command',
272
+ command: `node "${path.join(hooksDir, 'flow-statusline.js')}"`
273
+ };
274
+ fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + '\n');
275
+ trackFile(settingsPath);
276
+ console.log(' Configured statusLine in ~/.claude/settings.json');
277
+
278
+ // 7. Write .source breadcrumb (for dev/setup.sh compat)
279
+ const sourcePath = path.join(commandsDir, '.source');
280
+ fs.writeFileSync(sourcePath, pkgRoot + '\n');
281
+ trackFile(sourcePath);
282
+
283
+ // ---------- Post-install verification ----------
284
+ let warnings = 0;
285
+
286
+ // Check: at least 1 file in commandsDir
287
+ const installedSkills = fs.readdirSync(commandsDir).filter(f => f.endsWith('.md'));
288
+ if (installedSkills.length === 0) {
289
+ console.log(' Warning: no skill files found in commands/flow/');
290
+ warnings++;
291
+ }
292
+
293
+ // Check: both hook files
294
+ const installedHooks = hookFiles.filter(h => fs.existsSync(path.join(hooksDir, h)));
295
+ if (installedHooks.length < 2) {
296
+ console.log(` Warning: only ${installedHooks.length}/2 hook files installed`);
297
+ warnings++;
298
+ }
299
+
300
+ // Check: VERSION file
301
+ if (!fs.existsSync(path.join(commandsDir, 'VERSION'))) {
302
+ console.log(' Warning: VERSION file not found after install');
303
+ warnings++;
304
+ }
305
+
306
+ // Check: templates directory with at least 1 file
307
+ const tplDir = path.join(commandsDir, 'templates');
308
+ const tplFiles = fs.existsSync(tplDir) ? fs.readdirSync(tplDir) : [];
309
+ if (tplFiles.length === 0) {
310
+ console.log(' Warning: no template files found after install');
311
+ warnings++;
312
+ }
313
+
314
+ if (warnings > 0) {
315
+ console.log(` (${warnings} verification warning${warnings > 1 ? 's' : ''} — install may be incomplete)`);
316
+ }
317
+
318
+ // Done
319
+ const version = fs.readFileSync(versionFile, 'utf8').trim();
320
+ console.log(`
136
321
  Flow v${version} installed successfully!
137
322
 
138
323
  Commands available:
139
- /flow:intro Learn the Flow workflow
140
- /flow:init Start a new project or milestone
141
- /flow:spec Spec interview executable PRD
142
- /flow:go Execute next phase with agent teams
143
- /flow:done Session-end documentation
144
- /flow:status Quick orientation
145
- /flow:task Lightweight task execution
146
- /flow:update Update Flow to latest version
324
+ /flow:intro \u2014 Learn the Flow workflow
325
+ /flow:init \u2014 Start a new project or milestone
326
+ /flow:spec \u2014 Spec interview \u2192 executable PRD
327
+ /flow:go \u2014 Execute next phase with agent teams
328
+ /flow:done \u2014 Session-end documentation
329
+ /flow:status \u2014 Quick orientation
330
+ /flow:task \u2014 Lightweight task execution
331
+ /flow:update \u2014 Update Flow to latest version
147
332
 
148
333
  Get started: run /flow:intro in any Claude Code session.
149
334
  `);
335
+ } catch (err) {
336
+ rollback();
337
+ console.error('\nInstall failed: ' + err.message);
338
+ process.exit(1);
339
+ }
@@ -11,43 +11,90 @@ const homeDir = os.homedir();
11
11
  const cacheDir = path.join(homeDir, '.claude', 'cache');
12
12
  const cacheFile = path.join(cacheDir, 'flow-update-check.json');
13
13
  const versionFile = path.join(homeDir, '.claude', 'commands', 'flow', 'VERSION');
14
+ const errorLog = path.join(homeDir, '.claude', 'hooks', 'flow-error.log');
14
15
 
15
- // Ensure cache directory exists
16
- if (!fs.existsSync(cacheDir)) {
17
- fs.mkdirSync(cacheDir, { recursive: true });
16
+ function logError(context, err) {
17
+ try {
18
+ const line = `[${new Date().toISOString()}] flow-check-update: ${context}: ${err.message || err}\n`;
19
+ // Cap log at 50KB — truncate oldest entries
20
+ if (fs.existsSync(errorLog)) {
21
+ const stat = fs.statSync(errorLog);
22
+ if (stat.size > 50 * 1024) {
23
+ const content = fs.readFileSync(errorLog, 'utf8');
24
+ const lines = content.split('\n');
25
+ fs.writeFileSync(errorLog, lines.slice(Math.floor(lines.length / 2)).join('\n'));
26
+ }
27
+ }
28
+ fs.appendFileSync(errorLog, line);
29
+ } catch (_) {
30
+ // Logging must never throw
31
+ }
18
32
  }
19
33
 
20
- // Run check in background (spawn detached process, windowsHide prevents console flash)
21
- const child = spawn(process.execPath, ['-e', `
22
- const fs = require('fs');
23
- const { execSync } = require('child_process');
34
+ try {
35
+ // Ensure cache directory exists
36
+ if (!fs.existsSync(cacheDir)) {
37
+ fs.mkdirSync(cacheDir, { recursive: true });
38
+ }
24
39
 
25
- const cacheFile = ${JSON.stringify(cacheFile)};
26
- const versionFile = ${JSON.stringify(versionFile)};
40
+ // Run check in background (spawn detached process, windowsHide prevents console flash)
41
+ const child = spawn(process.execPath, ['-e', `
42
+ const fs = require('fs');
43
+ const { execSync } = require('child_process');
27
44
 
28
- let installed = '0.0.0';
29
- try {
30
- if (fs.existsSync(versionFile)) {
31
- installed = fs.readFileSync(versionFile, 'utf8').trim();
45
+ const cacheFile = ${JSON.stringify(cacheFile)};
46
+ const versionFile = ${JSON.stringify(versionFile)};
47
+ const errorLog = ${JSON.stringify(errorLog)};
48
+
49
+ function logError(context, err) {
50
+ try {
51
+ const line = '[' + new Date().toISOString() + '] flow-check-update(child): ' + context + ': ' + (err.message || err) + '\\n';
52
+ if (fs.existsSync(errorLog)) {
53
+ const stat = fs.statSync(errorLog);
54
+ if (stat.size > 50 * 1024) {
55
+ const content = fs.readFileSync(errorLog, 'utf8');
56
+ const lines = content.split('\\n');
57
+ fs.writeFileSync(errorLog, lines.slice(Math.floor(lines.length / 2)).join('\\n'));
58
+ }
59
+ }
60
+ fs.appendFileSync(errorLog, line);
61
+ } catch (_) {}
32
62
  }
33
- } catch (e) {}
34
63
 
35
- let latest = null;
36
- try {
37
- latest = execSync('npm view flow-cc version', { encoding: 'utf8', timeout: 10000, windowsHide: true }).trim();
38
- } catch (e) {}
39
-
40
- const result = {
41
- update_available: latest && installed !== latest,
42
- installed,
43
- latest: latest || 'unknown',
44
- checked: Math.floor(Date.now() / 1000)
45
- };
46
-
47
- fs.writeFileSync(cacheFile, JSON.stringify(result));
48
- `], {
49
- stdio: 'ignore',
50
- windowsHide: true
51
- });
52
-
53
- child.unref();
64
+ try {
65
+ let installed = '0.0.0';
66
+ try {
67
+ if (fs.existsSync(versionFile)) {
68
+ installed = fs.readFileSync(versionFile, 'utf8').trim();
69
+ }
70
+ } catch (e) {
71
+ logError('read-version', e);
72
+ }
73
+
74
+ let latest = null;
75
+ try {
76
+ latest = execSync('npm view flow-cc version', { encoding: 'utf8', timeout: 10000, windowsHide: true }).trim();
77
+ } catch (e) {
78
+ logError('npm-view', e);
79
+ }
80
+
81
+ const result = {
82
+ update_available: latest && installed !== latest,
83
+ installed,
84
+ latest: latest || 'unknown',
85
+ checked: Math.floor(Date.now() / 1000)
86
+ };
87
+
88
+ fs.writeFileSync(cacheFile, JSON.stringify(result));
89
+ } catch (e) {
90
+ logError('main', e);
91
+ }
92
+ `], {
93
+ stdio: 'ignore',
94
+ windowsHide: true
95
+ });
96
+
97
+ child.unref();
98
+ } catch (e) {
99
+ logError('spawn', e);
100
+ }
@@ -7,6 +7,25 @@ const path = require('path');
7
7
  const os = require('os');
8
8
 
9
9
  const homeDir = os.homedir();
10
+ const errorLog = path.join(homeDir, '.claude', 'hooks', 'flow-error.log');
11
+
12
+ function logError(context, err) {
13
+ try {
14
+ const line = `[${new Date().toISOString()}] flow-statusline: ${context}: ${err.message || err}\n`;
15
+ // Cap log at 50KB — truncate oldest entries
16
+ if (fs.existsSync(errorLog)) {
17
+ const stat = fs.statSync(errorLog);
18
+ if (stat.size > 50 * 1024) {
19
+ const content = fs.readFileSync(errorLog, 'utf8');
20
+ const lines = content.split('\n');
21
+ fs.writeFileSync(errorLog, lines.slice(Math.floor(lines.length / 2)).join('\n'));
22
+ }
23
+ }
24
+ fs.appendFileSync(errorLog, line);
25
+ } catch (_) {
26
+ // Logging must never throw
27
+ }
28
+ }
10
29
 
11
30
  // Read JSON from stdin (Claude Code statusline protocol)
12
31
  let input = '';
@@ -56,9 +75,13 @@ process.stdin.on('end', () => {
56
75
  const todos = JSON.parse(fs.readFileSync(path.join(todosDir, files[0].name), 'utf8'));
57
76
  const inProgress = todos.find(t => t.status === 'in_progress');
58
77
  if (inProgress) task = inProgress.activeForm || '';
59
- } catch (e) {}
78
+ } catch (e) {
79
+ logError('parse-todos', e);
80
+ }
60
81
  }
61
- } catch (e) {}
82
+ } catch (e) {
83
+ logError('read-todos', e);
84
+ }
62
85
  }
63
86
 
64
87
  // --- Flow update notification ---
@@ -78,6 +101,7 @@ process.stdin.on('end', () => {
78
101
  shouldCheck = true;
79
102
  }
80
103
  } catch (e) {
104
+ logError('parse-cache', e);
81
105
  shouldCheck = true;
82
106
  }
83
107
  } else {
@@ -96,7 +120,9 @@ process.stdin.on('end', () => {
96
120
  });
97
121
  child.unref();
98
122
  }
99
- } catch (e) {}
123
+ } catch (e) {
124
+ logError('spawn-update-check', e);
125
+ }
100
126
  }
101
127
 
102
128
  // --- Output ---
@@ -107,6 +133,7 @@ process.stdin.on('end', () => {
107
133
  process.stdout.write(`${flowUpdate}\x1b[2m${model}\x1b[0m \u2502 \x1b[2m${dirname}\x1b[0m${ctx}`);
108
134
  }
109
135
  } catch (e) {
136
+ logError('main', e);
110
137
  // Silent fail — statusline must never crash
111
138
  }
112
139
  });
package/package.json CHANGED
@@ -1,15 +1,40 @@
1
1
  {
2
2
  "name": "flow-cc",
3
- "version": "0.1.0",
4
- "description": "Flow workflow plugin for Claude Code",
3
+ "version": "0.2.0",
4
+ "description": "Structured workflow system for Claude Code — spec interviews, agent-team execution, session handoffs, compounding knowledge",
5
+ "author": "Troy Hoffman",
6
+ "license": "MIT",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "https://github.com/troyhoffman-oss/flow-plugin.git"
10
+ },
11
+ "homepage": "https://github.com/troyhoffman-oss/flow-plugin",
12
+ "bugs": {
13
+ "url": "https://github.com/troyhoffman-oss/flow-plugin/issues"
14
+ },
15
+ "keywords": [
16
+ "claude-code",
17
+ "claude",
18
+ "workflow",
19
+ "ai-agents",
20
+ "developer-tools",
21
+ "cli",
22
+ "skills",
23
+ "anthropic"
24
+ ],
5
25
  "bin": {
6
26
  "flow-cc": "bin/install.js"
7
27
  },
28
+ "engines": {
29
+ "node": ">=18"
30
+ },
8
31
  "files": [
9
32
  "bin",
10
33
  "skills",
11
34
  "hooks",
12
35
  "templates",
13
- "VERSION"
36
+ "VERSION",
37
+ "CHANGELOG.md",
38
+ "CONTRIBUTING.md"
14
39
  ]
15
40
  }
@@ -65,6 +65,8 @@ Structure:
65
65
  - Mark completed phases with completion date
66
66
  - Ensure pending phases have enough detail that the next session can start with a one-line prompt
67
67
  - **Archive check:** If the current milestone is fully complete:
68
+ - If `.planning/` does not exist, skip archiving entirely — there's nothing to archive
69
+ - Create `.planning/archive/` if it doesn't already exist (use `mkdir -p` or equivalent)
68
70
  - Move milestone phase details to `.planning/archive/milestones-vX.md`
69
71
  - Keep only the summary row in the ROADMAP milestone table
70
72
  - Move `PRD.md` to `.planning/archive/PRD-vX.md`
@@ -16,8 +16,9 @@ You are executing the `/flow:spec` skill. This is the KEYSTONE skill of the flow
16
16
  2. Read `CLAUDE.md` — understand project rules and tech stack
17
17
  3. Read `PRD.md` if it exists — check for existing spec to build on
18
18
  4. **Codebase scan** (brownfield projects):
19
- - Use Glob to find: components, pages/routes, API endpoints, types, utilities, config files, database models
20
- - Use Grep for: export patterns, route definitions, key function signatures
19
+ - **Size check first:** Use Glob with `**/*` to estimate total file count. If > 500 files, switch to focused mode (see below).
20
+ - **Standard mode (≤ 500 files):** Use Glob to find components, pages/routes, API endpoints, types, utilities, config files, database models. Use Grep for export patterns, route definitions, key function signatures. **Cap at 50 files sampled** — prioritize entry points, config, and type definitions.
21
+ - **Focused mode (> 500 files):** Scan ONLY: package.json/config files, entry points (index.ts, main.ts, app.ts), route definitions, database schema/models, and type definition files. Skip component trees, test files, and generated code entirely.
21
22
  - Build internal summary: "Here's what exists that we can reuse"
22
23
  5. Print a brief context summary to the user: "Here's what I found in the codebase: [key components, patterns, data layer]. Starting the spec interview."
23
24
 
@@ -46,14 +46,23 @@ If installed version equals latest version, print this and stop:
46
46
  Flow is up to date (v<version>)
47
47
  ```
48
48
 
49
- ## Step 4: Confirm update
49
+ ## Step 4: Show what's new
50
50
 
51
- Print the available update and ask the user to confirm:
51
+ Fetch the CHANGELOG.md from the Flow repo to show the user what changed:
52
+
53
+ 1. Run: `npx -y -p flow-cc@<latest> node -e "const fs=require('fs'),p=require('path');try{console.log(fs.readFileSync(p.join(require.resolve('flow-cc/package.json'),'..','CHANGELOG.md'),'utf8'))}catch(e){console.log('CHANGELOG not available')}"`
54
+ 2. Parse the output and extract only the section for `v<latest>` (from the `## [<latest>]` heading to the next `## [` heading or end of file)
55
+ 3. Print:
52
56
 
53
57
  ```
54
58
  Update available: v<installed> → v<latest>
59
+
60
+ Here's what's new in v<latest>:
61
+ <extracted changelog section>
55
62
  ```
56
63
 
64
+ If the CHANGELOG fetch fails, just print the version line without the changelog section. Don't let a missing changelog block the update.
65
+
57
66
  Wait for user confirmation before proceeding.
58
67
 
59
68
  ## Step 5: Run update
@@ -5,6 +5,16 @@ Format: PATTERN → CAUSE → FIX → RULE
5
5
  ## Execution Patterns
6
6
  <!-- Lessons about workflow, delegation, verification -->
7
7
 
8
+ <!-- EXAMPLE (replace with real lessons as you work):
9
+
10
+ ### Agent context overflow on large files
11
+ - **PATTERN:** Spawned agent tried to read a 2000-line file and ran out of context before finishing its task
12
+ - **CAUSE:** Agent prompt didn't specify which lines/functions to read — it read the whole file
13
+ - **FIX:** Added explicit line ranges and function names to the agent prompt
14
+ - **RULE:** Always tell agents exactly which functions/sections to read. Never say "read file.ts" — say "read file.ts lines 50-120 (the handleSubmit function)"
15
+
16
+ END EXAMPLE -->
17
+
8
18
  ## Domain Knowledge
9
19
  <!-- Lessons about business logic, data models, user behavior -->
10
20