worclaude 1.4.0 → 1.6.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/README.md CHANGED
@@ -10,7 +10,7 @@
10
10
 
11
11
  [Full Documentation](https://sefaertunc.github.io/Worclaude/) · [Interactive Demo](https://sefaertunc.github.io/Worclaude/demo/) · [npm](https://www.npmjs.com/package/worclaude)
12
12
 
13
- Worclaude scaffolds a complete Claude Code workflow into any project in seconds. It implements all [53 tips by Boris Cherny](https://www.howborisusesclaudecode.com/) — the creator of Claude Code at Anthropic — as a reusable, upgradable scaffold. One `init` command gives you 23 agents, 10 slash commands, 13 skills, hooks, permissions, and a CLAUDE.md template tuned for your tech stack. Whether you're starting fresh or adding structure to an existing project, Worclaude handles the setup so you can focus on building.
13
+ Worclaude scaffolds a complete Claude Code workflow into any project in seconds. It implements all [53 tips by Boris Cherny](https://www.howborisusesclaudecode.com/) — the creator of Claude Code at Anthropic — as a reusable, upgradable scaffold. One `init` command gives you 23 agents, 12 slash commands, 13 skills, hooks, permissions, and a CLAUDE.md template tuned for your tech stack. Whether you're starting fresh or adding structure to an existing project, Worclaude handles the setup so you can focus on building.
14
14
 
15
15
  ---
16
16
 
@@ -23,8 +23,8 @@ Worclaude scaffolds a complete Claude Code workflow into any project in seconds.
23
23
  - 5 universal: plan-reviewer, code-simplifier, test-writer, build-validator, verify-app
24
24
  - 18 optional across 6 categories: Backend, Frontend, DevOps, Quality, Documentation, Data/AI
25
25
 
26
- **Slash Commands (10)**
27
- `/start` `/end` `/commit-push-pr` `/review-plan` `/techdebt` `/verify` `/compact-safe` `/status` `/update-claude-md` `/setup`
26
+ **Slash Commands (12)**
27
+ `/start` `/end` `/commit-push-pr` `/review-plan` `/techdebt` `/verify` `/compact-safe` `/status` `/update-claude-md` `/setup` `/sync` `/conflict-resolver`
28
28
 
29
29
  **Skills (13)**
30
30
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "worclaude",
3
- "version": "1.4.0",
3
+ "version": "1.6.0",
4
4
  "description": "CLI tool that scaffolds a comprehensive Claude Code workflow into any project",
5
5
  "type": "module",
6
6
  "bin": {
@@ -0,0 +1,234 @@
1
+ import path from 'node:path';
2
+ import inquirer from 'inquirer';
3
+ import ora from 'ora';
4
+ import { workflowMetaExists, readWorkflowMeta } from '../core/config.js';
5
+ import { createBackup } from '../core/backup.js';
6
+ import {
7
+ classifyClaudeFiles,
8
+ detectRootFiles,
9
+ removeTrackedFiles,
10
+ removeRootFiles,
11
+ cleanGitignore,
12
+ } from '../core/remover.js';
13
+ import * as display from '../utils/display.js';
14
+
15
+ export async function deleteCommand() {
16
+ const projectRoot = process.cwd();
17
+
18
+ // Pre-flight: ensure worclaude is installed
19
+ if (!(await workflowMetaExists(projectRoot))) {
20
+ display.error('No worclaude workflow found in this project.');
21
+ display.info('Run `worclaude init` to set up a workflow first.');
22
+ return;
23
+ }
24
+
25
+ const meta = await readWorkflowMeta(projectRoot);
26
+ if (!meta) {
27
+ display.error('workflow-meta.json is corrupted or unreadable.');
28
+ display.info('You may need to manually remove the .claude/ directory.');
29
+ return;
30
+ }
31
+
32
+ display.sectionHeader('DELETE WORKFLOW');
33
+ display.newline();
34
+
35
+ // Step 1: Mode selection
36
+ const { mode } = await inquirer.prompt([
37
+ {
38
+ type: 'list',
39
+ name: 'mode',
40
+ message: 'What would you like to do?',
41
+ choices: [
42
+ { name: 'Remove workflow from this project', value: 'project' },
43
+ { name: 'Remove workflow and uninstall worclaude globally', value: 'global' },
44
+ new inquirer.Separator(),
45
+ { name: '← Cancel', value: 'cancel' },
46
+ ],
47
+ },
48
+ ]);
49
+
50
+ if (mode === 'cancel') {
51
+ display.info('Delete cancelled.');
52
+ return;
53
+ }
54
+
55
+ // Step 2: Classify files
56
+ const classification = await classifyClaudeFiles(projectRoot, meta);
57
+ const rootFiles = await detectRootFiles(projectRoot);
58
+
59
+ // Step 3: Show preview
60
+ display.newline();
61
+ display.barLine('Workflow files in .claude/:');
62
+ if (classification.safeToDelete.length > 0) {
63
+ display.barLine(
64
+ ` ${display.green('✓')} ${classification.safeToDelete.length} unmodified files (safe to remove)`
65
+ );
66
+ }
67
+ if (classification.modified.length > 0) {
68
+ display.barLine(
69
+ ` ${display.yellow('~')} ${classification.modified.length} files you've customized`
70
+ );
71
+ for (const key of classification.modified) {
72
+ display.barLine(` ${display.yellow('~')} .claude/${key}`);
73
+ }
74
+ }
75
+ if (classification.userOwned.length > 0) {
76
+ display.barLine(
77
+ ` ${display.blue('●')} ${classification.userOwned.length} user-added files (will NOT be touched)`
78
+ );
79
+ }
80
+
81
+ // Step 4: Handle modified files
82
+ let filesToDelete = [...classification.safeToDelete];
83
+
84
+ if (classification.modified.length > 0) {
85
+ display.newline();
86
+ const { modifiedAction } = await inquirer.prompt([
87
+ {
88
+ type: 'list',
89
+ name: 'modifiedAction',
90
+ message: `${classification.modified.length} file(s) have been customized. What should we do?`,
91
+ choices: [
92
+ { name: "Delete them too (they'll be in the backup)", value: 'delete' },
93
+ { name: 'Keep them in .claude/', value: 'keep' },
94
+ ],
95
+ },
96
+ ]);
97
+
98
+ if (modifiedAction === 'delete') {
99
+ filesToDelete.push(...classification.modified);
100
+ }
101
+ }
102
+
103
+ // Step 5: Handle root-level files (settings.json + project root files)
104
+ let rootFilesToDelete = [];
105
+
106
+ if (rootFiles.length > 0) {
107
+ display.newline();
108
+ display.info('These files were created or modified by worclaude but may contain your work:');
109
+ for (const f of rootFiles) {
110
+ display.dim(` ${f.label}`);
111
+ }
112
+ display.newline();
113
+
114
+ const { rootAction } = await inquirer.prompt([
115
+ {
116
+ type: 'list',
117
+ name: 'rootAction',
118
+ message: 'What would you like to do with these files?',
119
+ choices: [
120
+ { name: 'Keep all (recommended)', value: 'keep' },
121
+ { name: 'Let me choose which to remove', value: 'choose' },
122
+ { name: 'Remove all', value: 'remove' },
123
+ ],
124
+ default: 0,
125
+ },
126
+ ]);
127
+
128
+ if (rootAction === 'remove') {
129
+ rootFilesToDelete = rootFiles.map((f) => f.path);
130
+ } else if (rootAction === 'choose') {
131
+ const { selected } = await inquirer.prompt([
132
+ {
133
+ type: 'checkbox',
134
+ name: 'selected',
135
+ message: 'Select files to remove:',
136
+ choices: rootFiles.map((f) => ({ name: f.label, value: f.path })),
137
+ },
138
+ ]);
139
+ rootFilesToDelete = selected;
140
+ }
141
+ }
142
+
143
+ // Step 6: Final confirmation
144
+ const totalDeletions = filesToDelete.length + rootFilesToDelete.length;
145
+
146
+ if (totalDeletions === 0) {
147
+ display.newline();
148
+ display.info('No files selected for removal.');
149
+ return;
150
+ }
151
+
152
+ display.newline();
153
+ display.warn(`This will permanently delete ${totalDeletions} file(s).`);
154
+ display.dim(' A backup will be created first.');
155
+ display.newline();
156
+
157
+ const { confirm } = await inquirer.prompt([
158
+ {
159
+ type: 'list',
160
+ name: 'confirm',
161
+ message: 'Confirm deletion?',
162
+ choices: [
163
+ { name: 'Yes, delete', value: true },
164
+ { name: 'No, cancel', value: false },
165
+ ],
166
+ default: 1,
167
+ },
168
+ ]);
169
+
170
+ if (!confirm) {
171
+ display.info('Delete cancelled.');
172
+ return;
173
+ }
174
+
175
+ // Step 7: Execute
176
+ const spinner = ora('Creating backup...').start();
177
+
178
+ try {
179
+ const backupDir = await createBackup(projectRoot);
180
+ spinner.text = 'Removing workflow files...';
181
+
182
+ // Remove .claude/ tracked files
183
+ const claudeRemoved = await removeTrackedFiles(projectRoot, filesToDelete);
184
+
185
+ // Remove root files
186
+ let rootRemoved = 0;
187
+ if (rootFilesToDelete.length > 0) {
188
+ rootRemoved = await removeRootFiles(projectRoot, rootFilesToDelete);
189
+ }
190
+
191
+ // Clean .gitignore
192
+ const gitignoreCleaned = await cleanGitignore(projectRoot);
193
+
194
+ spinner.succeed('Workflow removed!');
195
+
196
+ // Step 8: Report
197
+ display.newline();
198
+ if (claudeRemoved > 0) {
199
+ display.success(`Removed ${claudeRemoved} workflow files from .claude/`);
200
+ }
201
+ if (rootRemoved > 0) {
202
+ display.success(`Removed ${rootRemoved} root-level file(s)`);
203
+ }
204
+ if (gitignoreCleaned) {
205
+ display.success('Cleaned up .gitignore');
206
+ }
207
+
208
+ const keptModified = classification.modified.filter((f) => !filesToDelete.includes(f));
209
+ if (keptModified.length > 0) {
210
+ display.info(`Kept ${keptModified.length} customized file(s) in .claude/`);
211
+ }
212
+ if (classification.userOwned.length > 0) {
213
+ display.info(`Kept ${classification.userOwned.length} user-added file(s) in .claude/`);
214
+ }
215
+
216
+ const keptRootFiles = rootFiles.filter((f) => !rootFilesToDelete.includes(f.path));
217
+ if (keptRootFiles.length > 0) {
218
+ display.info(`Kept: ${keptRootFiles.map((f) => f.label).join(', ')}`);
219
+ }
220
+
221
+ display.newline();
222
+ display.dim(` Backup: ${path.basename(backupDir)}/`);
223
+
224
+ // Step 9: Global uninstall hint
225
+ if (mode === 'global') {
226
+ display.newline();
227
+ display.info('To uninstall worclaude CLI globally, run:');
228
+ display.dim(' npm uninstall -g worclaude');
229
+ }
230
+ } catch (err) {
231
+ spinner.fail('Delete failed.');
232
+ display.error(err.message);
233
+ }
234
+ }
@@ -676,6 +676,14 @@ export async function initCommand() {
676
676
  const version = await getPackageVersion();
677
677
  display.banner(version);
678
678
 
679
+ // Windows guidance: hooks require Git Bash
680
+ if (process.platform === 'win32') {
681
+ display.info(
682
+ 'Windows detected \u2014 hooks require Git for Windows (Git Bash). See: https://gitforwindows.org'
683
+ );
684
+ display.newline();
685
+ }
686
+
679
687
  // Step 3: If existing project, show detection report and confirm
680
688
  let existingScan = null;
681
689
  let backupPath = null;
@@ -37,6 +37,16 @@ export async function createBackup(projectRoot) {
37
37
  await copyFile(mcpPath, path.join(backupDir, '.mcp.json'));
38
38
  }
39
39
 
40
+ const progressPath = path.join(projectRoot, 'docs', 'spec', 'PROGRESS.md');
41
+ if (await fileExists(progressPath)) {
42
+ await copyFile(progressPath, path.join(backupDir, 'docs', 'spec', 'PROGRESS.md'));
43
+ }
44
+
45
+ const specPath = path.join(projectRoot, 'docs', 'spec', 'SPEC.md');
46
+ if (await fileExists(specPath)) {
47
+ await copyFile(specPath, path.join(backupDir, 'docs', 'spec', 'SPEC.md'));
48
+ }
49
+
40
50
  return backupDir;
41
51
  }
42
52
 
@@ -0,0 +1,213 @@
1
+ import path from 'node:path';
2
+ import { hashFile } from '../utils/hash.js';
3
+ import {
4
+ fileExists,
5
+ dirExists,
6
+ readFile,
7
+ writeFile,
8
+ listFiles,
9
+ listFilesRecursive,
10
+ removeDirectory,
11
+ } from '../utils/file.js';
12
+
13
+ /**
14
+ * Classify .claude/ files into safe-to-delete, modified, missing, and user-owned.
15
+ * Uses only on-disk hash vs. stored hash (no template hash comparison).
16
+ */
17
+ export async function classifyClaudeFiles(projectRoot, meta) {
18
+ const claudeDir = path.join(projectRoot, '.claude');
19
+ const fileHashes = meta.fileHashes || {};
20
+
21
+ const safeToDelete = [];
22
+ const modified = [];
23
+ const missing = [];
24
+ const userOwned = [];
25
+
26
+ // Classify tracked files by comparing on-disk hash to stored hash
27
+ for (const [key, storedHash] of Object.entries(fileHashes)) {
28
+ const filePath = path.join(claudeDir, ...key.split('/'));
29
+ if (!(await fileExists(filePath))) {
30
+ missing.push(key);
31
+ continue;
32
+ }
33
+ const currentHash = await hashFile(filePath);
34
+ if (currentHash === storedHash) {
35
+ safeToDelete.push(key);
36
+ } else {
37
+ modified.push(key);
38
+ }
39
+ }
40
+
41
+ // workflow-meta.json is always worclaude's — safe to delete
42
+ if (await fileExists(path.join(claudeDir, 'workflow-meta.json'))) {
43
+ safeToDelete.push('workflow-meta.json');
44
+ }
45
+
46
+ // Scan disk for files not in fileHashes
47
+ const allTrackedKeys = new Set([
48
+ ...Object.keys(fileHashes),
49
+ 'workflow-meta.json',
50
+ 'settings.json',
51
+ ]);
52
+ const allDiskFiles = await listFilesRecursive(claudeDir);
53
+
54
+ for (const fp of allDiskFiles) {
55
+ const relKey = path.relative(claudeDir, fp).split(path.sep).join('/');
56
+ if (allTrackedKeys.has(relKey)) continue;
57
+
58
+ // .workflow-ref.md files are upgrade artifacts — safe to delete
59
+ if (relKey.endsWith('.workflow-ref.md')) {
60
+ safeToDelete.push(relKey);
61
+ } else {
62
+ userOwned.push(relKey);
63
+ }
64
+ }
65
+
66
+ return { safeToDelete, modified, missing, userOwned };
67
+ }
68
+
69
+ /**
70
+ * Detect root-level files that worclaude creates or modifies.
71
+ * settings.json is included here since it may contain user customizations.
72
+ */
73
+ export async function detectRootFiles(projectRoot) {
74
+ const candidates = [
75
+ {
76
+ path: path.join('.claude', 'settings.json'),
77
+ label: '.claude/settings.json (permissions & hooks)',
78
+ },
79
+ { path: 'CLAUDE.md', label: 'CLAUDE.md' },
80
+ { path: '.mcp.json', label: '.mcp.json' },
81
+ { path: path.join('docs', 'spec', 'PROGRESS.md'), label: 'docs/spec/PROGRESS.md' },
82
+ { path: path.join('docs', 'spec', 'SPEC.md'), label: 'docs/spec/SPEC.md' },
83
+ ];
84
+
85
+ const found = [];
86
+ for (const c of candidates) {
87
+ if (await fileExists(path.join(projectRoot, c.path))) {
88
+ found.push(c);
89
+ }
90
+ }
91
+
92
+ // CLAUDE.md.workflow-suggestions is an upgrade artifact
93
+ const suggestionsPath = 'CLAUDE.md.workflow-suggestions';
94
+ if (await fileExists(path.join(projectRoot, suggestionsPath))) {
95
+ found.push({ path: suggestionsPath, label: suggestionsPath });
96
+ }
97
+
98
+ return found;
99
+ }
100
+
101
+ /**
102
+ * Delete specified files from .claude/ and clean up empty directories.
103
+ * Uses fs.remove (via removeDirectory) which handles both files and directories.
104
+ */
105
+ export async function removeTrackedFiles(projectRoot, fileKeys) {
106
+ const claudeDir = path.join(projectRoot, '.claude');
107
+ let removedCount = 0;
108
+
109
+ for (const key of fileKeys) {
110
+ const filePath = path.join(claudeDir, ...key.split('/'));
111
+ if (await fileExists(filePath)) {
112
+ await removeDirectory(filePath);
113
+ removedCount++;
114
+ }
115
+ }
116
+
117
+ // Clean up empty subdirectories
118
+ for (const subdir of ['agents', 'commands', 'skills']) {
119
+ const dirPath = path.join(claudeDir, subdir);
120
+ if (await dirExists(dirPath)) {
121
+ const remaining = await listFiles(dirPath);
122
+ if (remaining.length === 0) {
123
+ await removeDirectory(dirPath);
124
+ }
125
+ }
126
+ }
127
+
128
+ // Remove .claude/ itself only if completely empty
129
+ if (await dirExists(claudeDir)) {
130
+ const remaining = await listFilesRecursive(claudeDir);
131
+ if (remaining.length === 0) {
132
+ await removeDirectory(claudeDir);
133
+ }
134
+ }
135
+
136
+ return removedCount;
137
+ }
138
+
139
+ /**
140
+ * Delete specified root-level files and clean up empty docs directories.
141
+ */
142
+ export async function removeRootFiles(projectRoot, filePaths) {
143
+ let removedCount = 0;
144
+
145
+ for (const relPath of filePaths) {
146
+ const fullPath = path.join(projectRoot, relPath);
147
+ if (await fileExists(fullPath)) {
148
+ await removeDirectory(fullPath);
149
+ removedCount++;
150
+ }
151
+ }
152
+
153
+ // Clean up empty docs/spec/ then docs/
154
+ const specDir = path.join(projectRoot, 'docs', 'spec');
155
+ if (await dirExists(specDir)) {
156
+ const remaining = await listFiles(specDir);
157
+ if (remaining.length === 0) {
158
+ await removeDirectory(specDir);
159
+
160
+ const docsDir = path.join(projectRoot, 'docs');
161
+ if (await dirExists(docsDir)) {
162
+ const docsRemaining = await listFilesRecursive(docsDir);
163
+ if (docsRemaining.length === 0) {
164
+ await removeDirectory(docsDir);
165
+ }
166
+ }
167
+ }
168
+ }
169
+
170
+ return removedCount;
171
+ }
172
+
173
+ /**
174
+ * Remove worclaude entries from .gitignore.
175
+ * Matches lines individually. Keeps .claude-backup-* / so backups stay git-ignored.
176
+ */
177
+ export async function cleanGitignore(projectRoot) {
178
+ const gitignorePath = path.join(projectRoot, '.gitignore');
179
+ if (!(await fileExists(gitignorePath))) return false;
180
+
181
+ const content = await readFile(gitignorePath);
182
+ const lines = content.split(/\r?\n/);
183
+
184
+ const REMOVE_LINES = new Set(['# Worclaude (generated workflow files)', '.claude/']);
185
+
186
+ const filtered = lines.filter((line) => !REMOVE_LINES.has(line.trim()));
187
+
188
+ // Collapse consecutive blank lines (max 2 in a row)
189
+ const cleaned = [];
190
+ let blankCount = 0;
191
+ for (const line of filtered) {
192
+ if (line.trim() === '') {
193
+ blankCount++;
194
+ if (blankCount <= 2) cleaned.push(line);
195
+ } else {
196
+ blankCount = 0;
197
+ cleaned.push(line);
198
+ }
199
+ }
200
+
201
+ // Trim trailing blank lines
202
+ while (cleaned.length > 0 && cleaned[cleaned.length - 1].trim() === '') {
203
+ cleaned.pop();
204
+ }
205
+ // Ensure file ends with newline if non-empty
206
+ const newContent = cleaned.length > 0 ? cleaned.join('\n') + '\n' : '';
207
+
208
+ if (newContent !== content) {
209
+ await writeFile(gitignorePath, newContent);
210
+ return true;
211
+ }
212
+ return false;
213
+ }
@@ -168,6 +168,8 @@ export const COMMAND_FILES = [
168
168
  'status',
169
169
  'update-claude-md',
170
170
  'setup',
171
+ 'sync',
172
+ 'conflict-resolver',
171
173
  ];
172
174
 
173
175
  export const UNIVERSAL_SKILLS = [
@@ -217,24 +219,6 @@ export const TECH_STACKS = [
217
219
  { name: 'Other / None', value: 'other' },
218
220
  ];
219
221
 
220
- export const FORMATTER_COMMANDS = {
221
- python: 'ruff format . || true',
222
- node: 'npx prettier --write . || true',
223
- java: "google-java-format -i $(find . -name '*.java' 2>/dev/null) || true",
224
- csharp: 'dotnet format || true',
225
- cpp: "find . -name '*.c' -o -name '*.cpp' -o -name '*.h' -o -name '*.hpp' | xargs clang-format -i || true",
226
- go: 'gofmt -w . || true',
227
- php: 'php-cs-fixer fix . || true',
228
- ruby: 'rubocop -A || true',
229
- kotlin: 'ktlint -F || true',
230
- swift: 'swift-format format -r . -i || true',
231
- rust: 'cargo fmt || true',
232
- dart: 'dart format . || true',
233
- scala: 'scalafmt || true',
234
- elixir: 'mix format || true',
235
- zig: 'zig fmt . || true',
236
- };
237
-
238
222
  export const PROJECT_TYPE_DESCRIPTIONS = {
239
223
  'Full-stack web application': 'Frontend + backend in one repo',
240
224
  'Backend / API': 'Server, REST/GraphQL, no frontend',
package/src/index.js CHANGED
@@ -8,6 +8,7 @@ import { statusCommand } from './commands/status.js';
8
8
  import { backupCommand } from './commands/backup.js';
9
9
  import { restoreCommand } from './commands/restore.js';
10
10
  import { diffCommand } from './commands/diff.js';
11
+ import { deleteCommand } from './commands/delete.js';
11
12
 
12
13
  const program = new Command();
13
14
 
@@ -46,4 +47,9 @@ program
46
47
  .description('Compare current setup against installed workflow version')
47
48
  .action(diffCommand);
48
49
 
50
+ program
51
+ .command('delete')
52
+ .description('Remove worclaude workflow from project')
53
+ .action(deleteCommand);
54
+
49
55
  program.parse();
@@ -1,23 +1,27 @@
1
- 1. Update docs/spec/PROGRESS.md with what was completed this session
2
- 2. **Version bump (only when on `develop` targeting `main`):**
3
- - Check what changed since last version using the versioning policy in git-conventions.md
4
- - Template or CLI behavior changes → patch bump
5
- - New command/feature minor bump
6
- - Breaking change major bump
7
- - Only docs/CI/tests/PROGRESS.md → no bump needed
8
- - If bump needed: update `version` in package.json
9
- 3. Stage all changes: git add -A
10
- 4. Write a clear, conventional commit message
11
- 5. Push to the current branch
12
- 6. Create a PR with:
13
- - Clear title matching conventional commit format
14
- - Description of changes
15
- - Testing done
16
- - Any notes for reviewers
17
-
18
- Branch targeting rules:
19
-
20
- - Feature/bugfix branches → PR targets `develop` (`gh pr create --base develop`)
21
- - When on `develop` → PR targets `main` (`gh pr create --base main`) — release merges only
22
-
23
- Use `gh pr create` for PR creation.
1
+ Determine which branch you're on, then follow the appropriate flow.
2
+
3
+ ## On a feature branch (feature/*, fix/*, chore/*, refactor/*)
4
+
5
+ Feature branches contain ONLY the task changes. Do NOT touch shared-state
6
+ files (see git-conventions.md for the canonical list).
7
+
8
+ 1. Stage all changes: git add -A
9
+ 2. Write a clear, conventional commit message
10
+ 3. Push to the current branch
11
+ 4. Create a PR targeting develop: gh pr create --base develop
12
+ 5. Include in PR description: title, changes, testing done, reviewer notes
13
+
14
+ ## On develop
15
+
16
+ Only used for release merges after /sync has been run.
17
+
18
+ 1. Stage all changes: git add -A
19
+ 2. Write a clear, conventional commit message
20
+ 3. Push to develop
21
+ 4. Create a PR targeting main: gh pr create --base main
22
+
23
+ ## On any other branch
24
+
25
+ Ask the user which base branch to target before creating a PR.
26
+
27
+ Use gh pr create for PR creation.
@@ -0,0 +1,40 @@
1
+ You are resolving merge conflicts. ONLY resolve conflicts — do not
2
+ update PROGRESS.md, SPEC.md, or bump versions. That is /sync's job.
3
+
4
+ ## Step 1: Detect
5
+
6
+ Run: git status
7
+ Identify all files with merge conflicts (listed as "both modified").
8
+ If no conflicts found, report "No conflicts detected" and stop.
9
+
10
+ ## Step 2: Understand
11
+
12
+ For each conflicted file:
13
+ - Read the file and find all <<<<<<< / ======= / >>>>>>> markers
14
+ - Understand what EACH side was trying to do
15
+ - Check git log for both branches to understand the intent
16
+
17
+ ## Step 3: Resolve
18
+
19
+ For each conflict:
20
+ - Changes in DIFFERENT parts of the code → keep both
21
+ - Changes modify the SAME lines → combine intelligently based on intent
22
+ - Changes truly contradict → ask the user which to keep
23
+ - NEVER silently drop changes from either side
24
+
25
+ ## Step 4: Verify clean
26
+
27
+ Search ALL tracked files for remaining conflict markers
28
+ (<<<<<<, =======, >>>>>>>). If any remain, resolve them.
29
+
30
+ ## Step 5: Test
31
+
32
+ Run /verify (or the project's test and lint commands).
33
+ If anything fails, fix it.
34
+
35
+ ## Step 6: Commit resolution only
36
+
37
+ - git add -A
38
+ - git commit -m "merge: resolve conflicts on develop"
39
+
40
+ Do NOT push. Do NOT create a PR. The user will run /sync next.
@@ -1,11 +1,16 @@
1
- Use this command ONLY when stopping mid-task (unfinished work).
2
- If the task is complete, use /commit-push-pr instead — it handles PROGRESS.md updates.
1
+ Use this ONLY when stopping work mid-task without committing.
3
2
 
4
- When stopping mid-task:
5
- 1. Update docs/spec/PROGRESS.md with:
6
- - What's in progress
7
- - Any blockers or decisions needed
8
- - Next steps to resume
9
- 2. Write a handoff document at docs/handoffs/HANDOFF_{date}.md
10
- with enough context for a fresh session to continue seamlessly.
11
- 3. Stage and commit the progress update: git add -A && git commit
3
+ Do NOT update PROGRESS.md — /sync handles that on develop after merging.
4
+
5
+ ## Mid-task handoff
6
+
7
+ 1. Create docs/handoffs/HANDOFF-{branch-name}-{date}.md
8
+ 2. Include:
9
+ - What was being worked on
10
+ - What is done so far
11
+ - What is left to do
12
+ - Decisions or context the next session needs
13
+ - Files that were modified
14
+ 3. git add -A
15
+ 4. git commit -m "wip: handoff for [task description]"
16
+ 5. git push
@@ -1,3 +1,11 @@
1
1
  Read docs/spec/PROGRESS.md to understand current state.
2
+ Read .claude/skills/agent-routing.md for agent usage guidance.
3
+
4
+ Check for handoff files from previous sessions:
5
+ - Look in docs/handoffs/ for any HANDOFF*.md files
6
+ (both HANDOFF-{branch}-{date}.md and legacy HANDOFF_{date}.md)
7
+ - Prioritize files matching the current branch name
8
+ - If found, read them for context and report what was handed off
9
+
2
10
  If an active implementation prompt exists, read it.
3
11
  Report: what was last completed, what's next, any blockers.
@@ -0,0 +1,49 @@
1
+ Update shared-state files after merging feature PRs into develop.
2
+ Run this on the develop branch AFTER all PRs are merged and any
3
+ conflicts are resolved.
4
+
5
+ ONLY update shared-state files — do not resolve conflicts. That is
6
+ /conflict-resolver's job. If you detect unresolved conflicts, stop
7
+ and tell the user to run /conflict-resolver first.
8
+
9
+ ## Pre-check
10
+
11
+ 1. Confirm you are on the develop branch. If not, stop and warn.
12
+ 2. Check for unresolved conflict markers in tracked files.
13
+ If found, stop: "Run /conflict-resolver first."
14
+ 3. Read git log to understand what was merged since the last sync
15
+ or version tag. If nothing was merged, report "Nothing to sync"
16
+ and stop.
17
+
18
+ ## Update PROGRESS.md
19
+
20
+ 4. Update docs/spec/PROGRESS.md:
21
+ - Mark completed items based on merged work
22
+ - Update "Last Updated" date to today
23
+ - Update Stats section by checking actual project values
24
+ (run the test suite, count source files, etc.)
25
+ - Move "In Progress" items to "Completed" if their PRs merged
26
+ - Update "Next Steps" if needed
27
+
28
+ ## Update SPEC.md
29
+
30
+ 5. If any merged PRs added features or changed behavior, update
31
+ docs/spec/SPEC.md to reflect the current state.
32
+ If nothing changed spec-wise, leave it alone.
33
+
34
+ ## Version bump
35
+
36
+ 6. Determine version bump using the Versioning Policy in
37
+ git-conventions.md. If bump needed: update version in package.json.
38
+
39
+ ## Verify
40
+
41
+ 7. Run /verify to confirm tests and lint pass.
42
+ If anything fails, fix it before proceeding.
43
+
44
+ ## Commit, push, and PR
45
+
46
+ 8. git add -A
47
+ 9. git commit -m "chore: sync progress, spec, and version to [new version]"
48
+ 10. git push origin develop
49
+ 11. gh pr create --base main with description of what was merged
@@ -29,7 +29,9 @@ See `.claude/skills/` — load only what's relevant:
29
29
  ## Session Protocol
30
30
  **Start:** Read PROGRESS.md → Read `.claude/skills/agent-routing.md` → Read active implementation prompt if any.
31
31
  **During:** One task at a time. Commit after each. Use subagents per routing guide.
32
- **End:** Use /commit-push-pr (updates PROGRESS.md, commits, pushes, creates PR). Use /end only if stopping mid-task.
32
+ **Feature branch:** /start work /verify /commit-push-pr
33
+ **After merging PRs:** git checkout develop → git pull → /conflict-resolver (if needed) → /sync
34
+ **Mid-task stop:** /end (writes handoff file)
33
35
 
34
36
  ## Critical Rules
35
37
  1. SPEC.md is source of truth. Do not invent features.
@@ -39,6 +41,7 @@ See `.claude/skills/` — load only what's relevant:
39
41
  5. Self-healing: same mistake twice → update CLAUDE.md.
40
42
  6. Use subagents to keep main context clean.
41
43
  7. Mediocre fix → scrap it, implement elegantly.
44
+ 8. Feature branches NEVER modify shared-state files. Those are updated only on develop via /sync after merging PRs. See git-conventions.md Shared-State Files for the canonical list.
42
45
 
43
46
  ## Gotchas
44
47
  [Grows during development]
@@ -56,6 +56,8 @@ The workflow installs a PostCompact hook that runs:
56
56
  cat CLAUDE.md && cat docs/spec/PROGRESS.md 2>/dev/null || true
57
57
  ```
58
58
 
59
+ > On Windows, this command runs in Git Bash (installed with [Git for Windows](https://gitforwindows.org)).
60
+
59
61
  This ensures you never lose your bearings after compaction. The hook fires
60
62
  automatically — you don't need to re-read these files manually.
61
63
 
@@ -97,6 +97,17 @@ When using `git worktree` for parallel work:
97
97
  Agents that use worktree isolation (code-simplifier, test-writer, ci-fixer, etc.)
98
98
  create and clean up their own worktrees automatically.
99
99
 
100
+ ## Shared-State Files
101
+
102
+ These files are modified ONLY on the develop branch (via /sync), never on feature branches:
103
+
104
+ - `docs/spec/PROGRESS.md` — project progress tracker
105
+ - `docs/spec/SPEC.md` — feature specification
106
+ - `README.md` — project documentation
107
+ - `package.json` version field — release versioning
108
+
109
+ This prevents merge conflicts when running parallel feature branches.
110
+
100
111
  ## Versioning Policy
101
112
 
102
113
  Follow [semver](https://semver.org/) when the project publishes releases: