@ulysses-ai/create-workspace 0.13.0-beta.2 → 0.14.0-beta.3
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 +15 -3
- package/package.json +1 -1
- package/template/.claude/hooks/_bash-output-advisory.test.mjs +88 -0
- package/template/.claude/hooks/bash-output-advisory.mjs +77 -0
- package/template/.claude/hooks/version-freshness-check.mjs +30 -0
- package/template/.claude/lib/freshness.mjs +75 -0
- package/template/.claude/lib/freshness.test.mjs +175 -0
- package/template/.claude/lib/registry-check.mjs +106 -0
- package/template/.claude/lib/registry-check.test.mjs +130 -0
- package/template/.claude/rules/memory-guidance.md +47 -0
- package/template/.claude/rules/task-list-mirroring.md +69 -0
- package/template/.claude/rules/token-economics.md.skip +23 -8
- package/template/.claude/rules/workspace-structure.md +2 -0
- package/template/.claude/scripts/build-shared-context-index.mjs +212 -0
- package/template/.claude/scripts/build-shared-context-index.test.mjs +318 -0
- package/template/.claude/scripts/migrate-claude-md-freshness-include.mjs +30 -0
- package/template/.claude/scripts/migrate-claude-md-freshness-include.test.mjs +54 -0
- package/template/.claude/scripts/sync-tasks.mjs +234 -0
- package/template/.claude/scripts/sync-tasks.test.mjs +350 -0
- package/template/.claude/settings.json +20 -9
- package/template/.claude/skills/braindump/SKILL.md +15 -0
- package/template/.claude/skills/build-docs-site/SKILL.md +1 -1
- package/template/.claude/skills/build-docs-site/checklists/pitfalls.md +4 -4
- package/template/.claude/skills/complete-work/SKILL.md +47 -55
- package/template/.claude/skills/handoff/SKILL.md +15 -0
- package/template/.claude/skills/maintenance/SKILL.md +49 -7
- package/template/.claude/skills/pause-work/SKILL.md +25 -4
- package/template/.claude/skills/release/SKILL.md +59 -43
- package/template/.claude/skills/start-work/SKILL.md +34 -2
- package/template/.claude/skills/workspace-update/SKILL.md +16 -0
- package/template/CLAUDE.md.tmpl +1 -0
- package/template/_gitignore +2 -3
- package/template/workspace.json.tmpl +1 -0
|
@@ -38,7 +38,18 @@ If conflicts arise in any repo, STOP and present them to the user. Do not auto-r
|
|
|
38
38
|
Run `/braindump` to capture any final discussion/reasoning to the session tracker body.
|
|
39
39
|
If the user declines or there's nothing to capture, skip.
|
|
40
40
|
|
|
41
|
-
### Step 4:
|
|
41
|
+
### Step 4: Flush task list to session.md
|
|
42
|
+
|
|
43
|
+
Before reading sources for synthesis, flush current `TodoWrite` state to `## Tasks` per the `task-list-mirroring` rule. This ensures the synthesis in Step 6 sees the final state:
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
cd work-sessions/{session-name}/workspace
|
|
47
|
+
echo '<JSON-of-current-todos>' | node .claude/scripts/sync-tasks.mjs --write session.md
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Mark `Complete work` as `in_progress` in the JSON before flushing — the rest of this skill IS the act of completing.
|
|
51
|
+
|
|
52
|
+
### Step 5: Gather source material
|
|
42
53
|
|
|
43
54
|
Formally read ALL sources before synthesizing — do not write release notes from memory alone:
|
|
44
55
|
|
|
@@ -62,20 +73,24 @@ Formally read ALL sources before synthesizing — do not write release notes fro
|
|
|
62
73
|
git log origin/{repo-branch}..HEAD --oneline
|
|
63
74
|
```
|
|
64
75
|
|
|
65
|
-
### Step
|
|
76
|
+
### Step 6: Synthesize release notes
|
|
77
|
+
|
|
78
|
+
Branch notes are written to the **workspace** repo, not the project repo. They are an internal retrospection artifact consumed by `/release` at release time; the project repo only ever receives a `CHANGELOG.md` entry. This separation keeps dogfood content out of public project repos between feature merge and the next release cut.
|
|
66
79
|
|
|
67
80
|
For each repo in the tracker's `repos:` list that has commits beyond the base branch:
|
|
68
81
|
|
|
69
82
|
```bash
|
|
70
83
|
cd work-sessions/{session-name}/workspace/repos/{repo}
|
|
71
84
|
COMMIT_ID=$(git rev-parse --short HEAD)
|
|
72
|
-
|
|
85
|
+
cd ../.. # back to the workspace worktree
|
|
86
|
+
mkdir -p release-notes/unreleased/{repo-name}
|
|
73
87
|
```
|
|
74
88
|
|
|
75
|
-
**File 1: `release-notes/unreleased/branch-release-notes-{COMMIT_ID}.md`**
|
|
89
|
+
**File 1: `release-notes/unreleased/{repo-name}/branch-release-notes-{COMMIT_ID}.md`** (relative to the workspace worktree)
|
|
76
90
|
```markdown
|
|
77
91
|
---
|
|
78
92
|
branch: {branch}
|
|
93
|
+
repo: {repo-name}
|
|
79
94
|
type: {feature|fix|chore}
|
|
80
95
|
author: {user}
|
|
81
96
|
date: {YYYY-MM-DD}
|
|
@@ -87,10 +102,11 @@ date: {YYYY-MM-DD}
|
|
|
87
102
|
Written from scratch per coherent-revisions rule.}
|
|
88
103
|
```
|
|
89
104
|
|
|
90
|
-
**File 2: `release-notes/unreleased/branch-release-questions-{COMMIT_ID}.md`**
|
|
105
|
+
**File 2: `release-notes/unreleased/{repo-name}/branch-release-questions-{COMMIT_ID}.md`**
|
|
91
106
|
```markdown
|
|
92
107
|
---
|
|
93
108
|
branch: {branch}
|
|
109
|
+
repo: {repo-name}
|
|
94
110
|
author: {user}
|
|
95
111
|
date: {YYYY-MM-DD}
|
|
96
112
|
---
|
|
@@ -100,23 +116,22 @@ date: {YYYY-MM-DD}
|
|
|
100
116
|
{Only genuinely open questions — not things resolved during implementation.}
|
|
101
117
|
```
|
|
102
118
|
|
|
103
|
-
|
|
119
|
+
The `repo:` frontmatter field is what `/release` uses to know which project repo's `CHANGELOG.md` should consume each note. The directory name is the same as the field for redundancy.
|
|
120
|
+
|
|
121
|
+
After all repos are processed, commit once on the workspace branch:
|
|
104
122
|
```bash
|
|
123
|
+
cd work-sessions/{session-name}/workspace
|
|
105
124
|
git add release-notes/unreleased/
|
|
106
125
|
git commit -m "docs: add release notes for {branch}"
|
|
107
126
|
```
|
|
108
127
|
|
|
109
128
|
If a repo has no commits beyond the base, skip release notes for it.
|
|
110
129
|
|
|
111
|
-
### Step
|
|
112
|
-
|
|
113
|
-
The entire `work-sessions/{session-name}/` folder is removed by the cleanup script in Step 11. Before that happens, make sure everything worth preserving has landed in release notes.
|
|
114
|
-
|
|
115
|
-
No separate "consume spec and plan" commit is needed for the project repos — specs and plans now live in the session folder, not in the project worktrees. The project worktrees only carry source code changes.
|
|
130
|
+
### Step 7: Remove session artifacts from the workspace branch
|
|
116
131
|
|
|
117
|
-
|
|
132
|
+
The entire `work-sessions/{session-name}/` folder is removed by the cleanup script in Step 12. Before that happens, make sure everything worth preserving has landed in release notes (Step 6) — once Step 6 has run, the tracker, specs, and plans have served their purpose.
|
|
118
133
|
|
|
119
|
-
Session content
|
|
134
|
+
Session content lives at the top of the workspace worktree on the session branch. Remove these files from the branch before the final push so main's top level stays free of session artifacts:
|
|
120
135
|
|
|
121
136
|
```bash
|
|
122
137
|
cd work-sessions/{session-name}/workspace
|
|
@@ -130,35 +145,11 @@ The `|| true` guards keep this idempotent — if a file is already gone (e.g., a
|
|
|
130
145
|
|
|
131
146
|
This commit persists in the branch's history. On squash merge or rebase merge, branch history collapses to one clean commit on main with no session artifacts. On merge commits, branch history is reachable but the final tree on main shows no session content.
|
|
132
147
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
For each repo in the tracker's `repos:`, check if the repo has versioning:
|
|
136
|
-
```bash
|
|
137
|
-
cd work-sessions/{session-name}/workspace/repos/{repo}
|
|
138
|
-
cat package.json 2>/dev/null | grep '"version"'
|
|
139
|
-
```
|
|
140
|
-
|
|
141
|
-
If no `package.json` or no `version` field: skip this repo — no versioning to manage.
|
|
142
|
-
|
|
143
|
-
If the repo has a version, determine the appropriate bump from the release notes just written:
|
|
144
|
-
|
|
145
|
-
1. Read the `type:` field from the branch release notes created in Step 5
|
|
146
|
-
2. Determine the bump:
|
|
147
|
-
- `type: fix` or `type: chore` → **patch** (auto-bump, no confirmation needed)
|
|
148
|
-
- `type: feature` → **minor** (present to user: "This session adds new functionality. Suggested bump: {current} → {next-minor}. Confirm or adjust?")
|
|
149
|
-
- Breaking changes detected (schema changes, removed APIs, convention changes) → **major** (present to user: "This session includes breaking changes. Suggested bump: {current} → {next-major}. Confirm or adjust?")
|
|
150
|
-
|
|
151
|
-
3. Apply the bump:
|
|
152
|
-
```bash
|
|
153
|
-
git add package.json
|
|
154
|
-
git commit -m "chore: bump version to {new-version}"
|
|
155
|
-
```
|
|
156
|
-
|
|
157
|
-
The user can override any suggestion. Accept their decision.
|
|
148
|
+
> **No version bump here.** Versions are bumped at release time by `/release`, which consumes accumulated unreleased branch notes into a single `CHANGELOG.md` entry per project repo. `/complete-work` only writes branch notes; it does not modify any project repo's `package.json`. This avoids version drift when multiple feature branches land between releases.
|
|
158
149
|
|
|
159
|
-
### Step
|
|
150
|
+
### Step 8: Detect remote type per repo
|
|
160
151
|
|
|
161
|
-
For each repo in the tracker's `repos:` plus the workspace repo, determine the remote type. This drives how Step
|
|
152
|
+
For each repo in the tracker's `repos:` plus the workspace repo, determine the remote type. This drives how Step 9 and Step 10 push and merge.
|
|
162
153
|
|
|
163
154
|
```bash
|
|
164
155
|
cd work-sessions/{session-name}/workspace/repos/{repo}
|
|
@@ -167,14 +158,14 @@ git remote get-url origin 2>&1
|
|
|
167
158
|
|
|
168
159
|
Classify the result:
|
|
169
160
|
|
|
170
|
-
- **GitHub remote** — URL contains `github.com` or `gh repo view` succeeds against origin → use the PR flow (Step
|
|
171
|
-
- **Local / bare remote** — URL is a filesystem path (starts with `/`, `./`, `file://`, or points at a `.git` bare mirror) → use the local merge flow (Step
|
|
172
|
-
- **Other remote** (e.g., GitLab, Bitbucket, self-hosted) — no `gh` support → fall back to the local merge flow (Step
|
|
161
|
+
- **GitHub remote** — URL contains `github.com` or `gh repo view` succeeds against origin → use the PR flow (Step 9a, Step 10a).
|
|
162
|
+
- **Local / bare remote** — URL is a filesystem path (starts with `/`, `./`, `file://`, or points at a `.git` bare mirror) → use the local merge flow (Step 9b, Step 10b).
|
|
163
|
+
- **Other remote** (e.g., GitLab, Bitbucket, self-hosted) — no `gh` support → fall back to the local merge flow (Step 9b, Step 10b), and mention it in the final summary.
|
|
173
164
|
- **No remote at all** — "No remote configured for {repo}. Want me to create one on GitHub, add an existing URL, or keep the session local (push/merge inside the local clone only)?" Act on the user's choice. Never silently skip push.
|
|
174
165
|
|
|
175
|
-
### Step
|
|
166
|
+
### Step 9: Push all repos
|
|
176
167
|
|
|
177
|
-
#### Step
|
|
168
|
+
#### Step 9a: GitHub remotes
|
|
178
169
|
|
|
179
170
|
```bash
|
|
180
171
|
# Each project repo with a GitHub remote
|
|
@@ -188,7 +179,7 @@ git commit -m "chore: finalize context for {session-name}"
|
|
|
188
179
|
git push -u origin {branch}
|
|
189
180
|
```
|
|
190
181
|
|
|
191
|
-
#### Step
|
|
182
|
+
#### Step 9b: Local/bare remotes
|
|
192
183
|
|
|
193
184
|
```bash
|
|
194
185
|
# Push the feature branch to the bare remote so it exists there
|
|
@@ -202,11 +193,11 @@ git commit -m "chore: finalize context for {session-name}"
|
|
|
202
193
|
git push -u origin {branch}
|
|
203
194
|
```
|
|
204
195
|
|
|
205
|
-
The push shape is the same as
|
|
196
|
+
The push shape is the same as 9a — what differs is the merge mechanics in Step 10b.
|
|
206
197
|
|
|
207
|
-
### Step
|
|
198
|
+
### Step 10: Merge and present unified summary
|
|
208
199
|
|
|
209
|
-
#### Step
|
|
200
|
+
#### Step 10a: GitHub remotes — create PRs, unified summary, merge
|
|
210
201
|
|
|
211
202
|
Create one PR per project repo plus one workspace PR:
|
|
212
203
|
|
|
@@ -258,7 +249,7 @@ cd repos/{repo} && git pull origin {repo-branch}
|
|
|
258
249
|
cd {main-workspace-root} && git pull origin main
|
|
259
250
|
```
|
|
260
251
|
|
|
261
|
-
#### Step
|
|
252
|
+
#### Step 10b: Local / bare / other remotes — local merge flow
|
|
262
253
|
|
|
263
254
|
No PRs are created — these remotes don't have a PR concept (or we don't have a client wired up for them). Present an adjusted summary:
|
|
264
255
|
|
|
@@ -304,7 +295,7 @@ For repos with no remote at all (user chose "keep local"): skip push entirely. T
|
|
|
304
295
|
cd repos/{repo} && git merge --ff-only {branch}
|
|
305
296
|
```
|
|
306
297
|
|
|
307
|
-
### Step
|
|
298
|
+
### Step 11: Close the linked issue on the tracker
|
|
308
299
|
|
|
309
300
|
If the session tracker has a `workItem:` field AND `workspace.tracker` is configured, close the linked issue via the adapter after all PRs have merged:
|
|
310
301
|
|
|
@@ -328,9 +319,9 @@ if (ws.workspace?.tracker) {
|
|
|
328
319
|
|
|
329
320
|
If `workItem:` is unset, skip the close — this was a blank session.
|
|
330
321
|
|
|
331
|
-
If the close call fails (tracker unreachable, auth expired), report the error in the unified summary but do not block Step
|
|
322
|
+
If the close call fails (tracker unreachable, auth expired), report the error in the unified summary but do not block Step 12 cleanup. The issue can be closed manually via the GitHub UI; no data is at risk.
|
|
332
323
|
|
|
333
|
-
### Step
|
|
324
|
+
### Step 12: Cleanup
|
|
334
325
|
|
|
335
326
|
Run the cleanup helper script from the main workspace root:
|
|
336
327
|
```bash
|
|
@@ -342,7 +333,7 @@ The script tears down in the **mandatory** order:
|
|
|
342
333
|
2. Remove the workspace worktree from the workspace repo
|
|
343
334
|
3. `git worktree prune` on each project repo (belt-and-suspenders for orphan records)
|
|
344
335
|
4. Delete local branches in all repos
|
|
345
|
-
5. `rm -rf work-sessions/{session-name}/` — the tracker, specs, plans, and any local-only artifacts vanish. Their content was already archived into release notes in Step
|
|
336
|
+
5. `rm -rf work-sessions/{session-name}/` — the tracker, specs, plans, and any local-only artifacts vanish. Their content was already archived into release notes in Step 6.
|
|
346
337
|
|
|
347
338
|
Workspace-first removal silently deletes the nested project worktrees' `.git` files and leaves orphan worktree records in the project repos. The script enforces the safe order.
|
|
348
339
|
|
|
@@ -362,8 +353,9 @@ Ask: "These changes weren't part of a formal work session. What do you want to d
|
|
|
362
353
|
- **Revert** — undo the changes (with confirmation)
|
|
363
354
|
|
|
364
355
|
## Notes
|
|
365
|
-
-
|
|
356
|
+
- Branch release notes live in the WORKSPACE repo at `release-notes/unreleased/{repo-name}/` — never in project repos. Project repos only ever see code commits and (at release time) `CHANGELOG.md` entries written by `/release`.
|
|
366
357
|
- The session tracker's body is the primary source for release note synthesis — it captures the full session history alongside specs and plans
|
|
367
358
|
- All repos get PRed and merged together — one approval for all
|
|
359
|
+
- Version bumps happen in `/release`, not `/complete-work` — this avoids version drift when multiple feature branches land between releases
|
|
368
360
|
- The teardown order is mandatory: project worktrees first, then workspace worktree, then prune, then delete the session folder
|
|
369
361
|
- Context consumption, cleanup, and auto-committing release notes are intentional workflow behavior — these bypass normal commit conventions by design
|
|
@@ -84,6 +84,21 @@ updated: {YYYY-MM-DD}
|
|
|
84
84
|
- If no: ask for a single name that covers both
|
|
85
85
|
4. Proceed with the named flow for each handoff
|
|
86
86
|
|
|
87
|
+
## Include task snapshot
|
|
88
|
+
|
|
89
|
+
If an active session exists (detected via `.claude/.active-session.json`), include a `## Tasks at capture time` section in the handoff artifact with a snapshot of the current `TodoWrite` state:
|
|
90
|
+
|
|
91
|
+
```markdown
|
|
92
|
+
## Tasks at capture time
|
|
93
|
+
|
|
94
|
+
- [x] Start work
|
|
95
|
+
- [x] Reproduce on iOS Safari
|
|
96
|
+
- [ ] Identify race condition
|
|
97
|
+
- [ ] Complete work
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
Use the same GFM checkbox format as `session.md`'s `## Tasks` section (just `content` and `status` per task — no `activeForm` field, no blockquote line) and render it inline in the handoff. Do NOT call `sync-tasks.mjs --write` — handoffs are snapshots, not the canonical store.
|
|
101
|
+
|
|
87
102
|
## Updating Existing Handoffs
|
|
88
103
|
|
|
89
104
|
When updating an existing handoff, rewrite it as a fresh snapshot of current understanding (coherent-revisions rule). Don't append below the old content. The updated handoff should read as if written in one pass reflecting the current state.
|
|
@@ -30,6 +30,7 @@ For each shared-context `.md` file and each `work-sessions/*/workspace/session.m
|
|
|
30
30
|
- `lifecycle: active` on a file not updated in 7+ days? (stale candidate)
|
|
31
31
|
- `lifecycle: resolved` files that should have been processed by /complete-work?
|
|
32
32
|
- Session tracker `status: active` but the workspace worktree at `work-sessions/{name}/workspace/` is missing? (orphaned)
|
|
33
|
+
- `confidence` field present? Must be one of `high`, `medium`, `low` if set.
|
|
33
34
|
|
|
34
35
|
### 3. Workspace structure
|
|
35
36
|
- Actual directory layout matches what workspace-structure rule describes?
|
|
@@ -44,24 +45,63 @@ For each shared-context `.md` file and each `work-sessions/*/workspace/session.m
|
|
|
44
45
|
- Workspace repo on expected branch?
|
|
45
46
|
- Orphan worktree records in project repos — run `git -C repos/{repo} worktree list` for each repo and flag any `prunable` markers. These usually come from a workspace-first teardown (the unsafe order) leaving stale admin records behind. Suggest `git worktree prune` on the affected repo.
|
|
46
47
|
|
|
48
|
+
### 5. Shared-context index integrity
|
|
49
|
+
|
|
50
|
+
`shared-context/index.md` is auto-generated from frontmatter. Run the check:
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
node .claude/scripts/build-shared-context-index.mjs --check --root .
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Three failure modes (script exits 1 with a JSON status):
|
|
57
|
+
|
|
58
|
+
- `missing` — `shared-context/` exists but `index.md` does not. Run `--write` to create.
|
|
59
|
+
- `stale` — `index.md` exists but its entries no longer match the filesystem. Causes: a file was added or deleted, a `description:` was changed, a `.indexignore` rule was added. Run `--write` to regenerate.
|
|
60
|
+
- (no failure) — `current` with the entry count.
|
|
61
|
+
|
|
62
|
+
Audit mode reports the status. Cleanup mode runs `--write` if stale or missing, then re-checks.
|
|
63
|
+
|
|
64
|
+
While the index is being read, also flag entries with weak fallbacks: filename-slug-only descriptions (e.g., "project status" with no period) usually indicate the underlying file is missing a `description:` or has no usable opening sentence. Suggest adding `description:` to those source files — the index will pick it up on the next regeneration.
|
|
65
|
+
|
|
66
|
+
### 6. Template freshness
|
|
67
|
+
|
|
68
|
+
Compare the workspace's pinned template version against the latest published on npm.
|
|
69
|
+
|
|
70
|
+
Always invoke `refreshIfStale` from the audit (regardless of `workspace.versionCheck.ambient` — the user explicitly ran `/maintenance`):
|
|
71
|
+
|
|
72
|
+
```javascript
|
|
73
|
+
import { refreshIfStale } from './.claude/lib/freshness.mjs';
|
|
74
|
+
const result = await refreshIfStale({
|
|
75
|
+
workspaceRoot: process.cwd(),
|
|
76
|
+
ttlMs: 24 * 60 * 60 * 1000,
|
|
77
|
+
});
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Report one of:
|
|
81
|
+
- `outdated` → `✗ Template v{current} → v{latest} available. Run npx @ulysses-ai/create-workspace --upgrade.`
|
|
82
|
+
- `current` → `✓ Template is up to date (v{latest}).`
|
|
83
|
+
- `unknown` (with cache) → `⚠ Could not reach npm registry; last cached latest was v{latest} as of {checkedAt}.`
|
|
84
|
+
- `unknown` (no cache) → `⚠ Could not reach npm registry; no cached version on file. Try again when online.`
|
|
85
|
+
- `skipped: 'uninitialized'` → `⚠ Workspace not initialized; freshness check unavailable.`
|
|
86
|
+
|
|
47
87
|
## Cleanup
|
|
48
88
|
|
|
49
89
|
Active recommendations. Flags problems and suggests fixes, but asks before acting.
|
|
50
90
|
|
|
51
|
-
###
|
|
91
|
+
### 7. Stale context
|
|
52
92
|
- Ephemeral files not updated in 7+ days — suggest resolve, update, or archive
|
|
53
93
|
- `work-sessions/{name}/` folders whose worktrees are gone — suggest cleanup
|
|
54
94
|
- Session trackers whose branches have been merged — suggest `/complete-work` post-flight cleanup
|
|
55
95
|
- Braindumps that overlap significantly — suggest merging (e.g., "workspace-branching.md and persistent-work-sessions.md cover the same topic")
|
|
56
96
|
- Handoffs referencing deleted branches — suggest resolve or remove
|
|
57
97
|
|
|
58
|
-
###
|
|
98
|
+
### 8. Context reconciliation
|
|
59
99
|
- Read recent shared-context writes (last session or last N files by updated date)
|
|
60
100
|
- For each, scan other shared-context files for references that are now stale
|
|
61
101
|
- Surface: "{file} says X but {newer-file} now says Y. Update {file}?"
|
|
62
102
|
- This is the capture-time cross-check, run retroactively instead of inline
|
|
63
103
|
|
|
64
|
-
###
|
|
104
|
+
### 9. Health metrics
|
|
65
105
|
- Size of `shared-context/locked/` relative to the active model's context window — flag if over 5% (yellow) or 15% (red). Absolute byte count is a weak proxy; contradictions, stale references, and duplicated coverage across files matter more than total size.
|
|
66
106
|
- Number of ephemeral files — flag if accumulating without resolution
|
|
67
107
|
- Session log stats (if `workspace-scratchpad/session-log.jsonl` exists):
|
|
@@ -90,11 +130,12 @@ Cleanup suggestions (2):
|
|
|
90
130
|
⊕ migration-recipes.md still says "/sync handles dogfood" but
|
|
91
131
|
/sync was replaced by /sync-work — update?
|
|
92
132
|
|
|
93
|
-
OK (
|
|
133
|
+
OK (5):
|
|
94
134
|
✓ All CLAUDE.md skill references valid
|
|
95
135
|
✓ Workspace structure matches rule
|
|
96
136
|
✓ workspace.json repos all present
|
|
97
137
|
✓ No frontmatter errors
|
|
138
|
+
✓ Template is up to date (v0.14.0)
|
|
98
139
|
```
|
|
99
140
|
|
|
100
141
|
## Flow
|
|
@@ -104,9 +145,10 @@ OK (4):
|
|
|
104
145
|
3. Read workspace.json — extract repo manifest
|
|
105
146
|
4. Check `.claude/rules/`, `.claude/skills/`, `.claude/agents/` against references
|
|
106
147
|
5. Check git state (worktrees, branches, remotes)
|
|
107
|
-
6.
|
|
108
|
-
7.
|
|
109
|
-
8.
|
|
148
|
+
6. Run `node .claude/scripts/build-shared-context-index.mjs --check --root .` — capture status
|
|
149
|
+
7. Read session-log.jsonl if it exists
|
|
150
|
+
8. If cleanup mode: regenerate the shared-context index if stale; compare files pairwise for overlap; scan for stale cross-references
|
|
151
|
+
9. Compile and present findings grouped by severity
|
|
110
152
|
|
|
111
153
|
## Notes
|
|
112
154
|
- Audit mode is always read-only — never modifies files
|
|
@@ -54,7 +54,28 @@ If `workItem:` is unset, skip the comment — this is a blank session with no tr
|
|
|
54
54
|
|
|
55
55
|
If the comment fails (tracker unreachable, auth expired), report the error but do not block the pause. The pause state lives locally in the session tracker regardless.
|
|
56
56
|
|
|
57
|
-
### Step 4:
|
|
57
|
+
### Step 4: Flush task list to session.md
|
|
58
|
+
|
|
59
|
+
Before the commit picks it up, flush current `TodoWrite` state to `## Tasks` per the `task-list-mirroring` rule:
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
cd work-sessions/{session-name}/workspace
|
|
63
|
+
echo '<JSON-of-current-todos>' | node .claude/scripts/sync-tasks.mjs --write session.md
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
The `<JSON-of-current-todos>` is the same shape Claude has been maintaining via `TodoWrite`:
|
|
67
|
+
|
|
68
|
+
```json
|
|
69
|
+
{
|
|
70
|
+
"todos": [
|
|
71
|
+
{ "content": "...", "activeForm": "...", "status": "pending|in_progress|completed" }
|
|
72
|
+
]
|
|
73
|
+
}
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
The helper enforces the bookend invariant — pass whatever current state you have, including any missing or misplaced bookends, and the helper will normalize.
|
|
77
|
+
|
|
78
|
+
### Step 5: Commit and push workspace
|
|
58
79
|
|
|
59
80
|
```bash
|
|
60
81
|
# From the workspace worktree
|
|
@@ -64,7 +85,7 @@ git commit -m "handoff: pause {session-name}"
|
|
|
64
85
|
git push -u origin {branch}
|
|
65
86
|
```
|
|
66
87
|
|
|
67
|
-
### Step
|
|
88
|
+
### Step 6: Push project repos
|
|
68
89
|
|
|
69
90
|
```bash
|
|
70
91
|
# For each repo in the tracker's repos:
|
|
@@ -72,7 +93,7 @@ cd work-sessions/{session-name}/workspace/repos/{repo}
|
|
|
72
93
|
git push -u origin {branch}
|
|
73
94
|
```
|
|
74
95
|
|
|
75
|
-
### Step
|
|
96
|
+
### Step 7: Create draft PRs
|
|
76
97
|
|
|
77
98
|
```bash
|
|
78
99
|
# For each repo in the tracker's repos:
|
|
@@ -85,7 +106,7 @@ gh pr create --draft --title "context: {session-name} (paused)" --body "Workspac
|
|
|
85
106
|
|
|
86
107
|
If PRs already exist, update them to draft status if needed.
|
|
87
108
|
|
|
88
|
-
### Step
|
|
109
|
+
### Step 8: Confirm
|
|
89
110
|
|
|
90
111
|
"Session '{session-name}' paused. Resume anytime with /start-work."
|
|
91
112
|
|
|
@@ -1,14 +1,20 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: release
|
|
3
|
-
description:
|
|
3
|
+
description: Prepend a new CHANGELOG.md entry per project repo by synthesizing unreleased branch notes. Deletes consumed branch notes and synthesizes workspace shared-context into locked entries. Use at release time.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Release
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
Synthesize unreleased branch notes (in the **workspace** repo) into a concise, user-facing entry at the top of each project repo's `CHANGELOG.md`. Delete the consumed branch notes from the workspace. Bump the project repo's `package.json` version. In parallel, promote resolved workspace shared-context into locked team knowledge.
|
|
9
|
+
|
|
10
|
+
## Why this shape
|
|
11
|
+
|
|
12
|
+
Branch notes are detailed dogfood-retrospective artifacts that should not bloat public project repos. By keeping them in the workspace repo until release time, the project repo stays lean — it only ever sees code commits and `CHANGELOG.md` entries. A single `CHANGELOG.md` with one concise entry per version is what users of a published package actually want. Branch notes remain the input format for `/complete-work` (they capture per-session detail at the right moment), but they live in the workspace and are consumed-then-deleted by `/release`.
|
|
13
|
+
|
|
14
|
+
Versions are bumped here, not in `/complete-work`, because version semantics describe what shipped — accumulated changes since the last release — not the timing of any individual feature merge.
|
|
9
15
|
|
|
10
16
|
## Parameters
|
|
11
|
-
- `/release {version}` — create release
|
|
17
|
+
- `/release {version}` — create a release entry for a specific version
|
|
12
18
|
- `/release` — ask for the version
|
|
13
19
|
|
|
14
20
|
## Flow
|
|
@@ -22,66 +28,65 @@ Check `workspace.json` for `releaseMode`:
|
|
|
22
28
|
- **ask**: "Process all repos together or individually?"
|
|
23
29
|
|
|
24
30
|
**Step 2: Read unreleased notes**
|
|
25
|
-
For each target repo:
|
|
31
|
+
Branch notes live in the **workspace** repo, written there by `/complete-work`. For each target repo, list the workspace's unreleased subdirectory for that project:
|
|
26
32
|
```bash
|
|
27
|
-
ls
|
|
33
|
+
ls release-notes/unreleased/{repo}/
|
|
28
34
|
```
|
|
29
35
|
Read all `branch-release-notes-*.md` and `branch-release-questions-*.md` files.
|
|
30
36
|
|
|
31
|
-
If no unreleased files exist: "No unreleased notes found for {repo}. Nothing to release."
|
|
37
|
+
If no unreleased files exist for a target repo: "No unreleased notes found for {repo}. Nothing to release."
|
|
38
|
+
|
|
39
|
+
The frontmatter `repo:` field on each branch-notes file confirms which project repo the notes belong to — match that to the directory name as a sanity check. Notes mismatched on `repo:` are a sign of manual file moves; surface to the user.
|
|
32
40
|
|
|
33
41
|
**Step 3: Group and organize**
|
|
34
|
-
Group notes by
|
|
35
|
-
- Features first, then fixes, then chores
|
|
36
|
-
- Within each group, order chronologically by date
|
|
42
|
+
Group notes by `type:` frontmatter (feature, fix, chore). Within each group, order chronologically by date. This ordering drives bullet sequence in the synthesized entry.
|
|
37
43
|
|
|
38
44
|
**Step 4: Handle questions**
|
|
39
45
|
Present all open questions from `branch-release-questions-*.md` files:
|
|
40
46
|
"These questions are still open from development. For each one:"
|
|
41
47
|
- **Answer** — provide the answer, remove from questions
|
|
42
|
-
- **Defer** — keep
|
|
48
|
+
- **Defer** — keep as a "Known issues" sub-bullet in the CHANGELOG entry
|
|
43
49
|
- **Discard** — no longer relevant
|
|
44
50
|
|
|
45
|
-
**Step 5: Synthesize
|
|
46
|
-
Write `repos/{repo}/release-notes/v{version}.md`:
|
|
47
|
-
```markdown
|
|
48
|
-
# v{version} Release Notes
|
|
49
|
-
|
|
50
|
-
**Date:** {YYYY-MM-DD}
|
|
51
|
+
**Step 5: Synthesize the CHANGELOG entry**
|
|
51
52
|
|
|
52
|
-
|
|
53
|
-
{Coherent narrative combining all feature branch-release-notes.
|
|
54
|
-
Deduplicate related items. Credit authors.
|
|
55
|
-
Write from scratch — don't concatenate. Coherent-revisions rule applies.}
|
|
53
|
+
Read the current `repos/{repo}/CHANGELOG.md` (if it exists) so the new entry matches the existing voice and structure. If no CHANGELOG exists, create one with a short header explaining that entries are written for package users, not contributors.
|
|
56
54
|
|
|
57
|
-
|
|
58
|
-
{Same treatment for bugfix branches.}
|
|
55
|
+
Prepend a new section at the top of the changelog body (after the header, before any existing version entries). Write it user-facing — what shipped, not how it shipped:
|
|
59
56
|
|
|
60
|
-
|
|
61
|
-
{
|
|
57
|
+
```markdown
|
|
58
|
+
## v{version} — {YYYY-MM-DD}
|
|
62
59
|
|
|
63
|
-
|
|
64
|
-
|
|
60
|
+
- {Concise bullet per meaningful change. Features, fixes, and chores interleaved
|
|
61
|
+
by significance, not by category. Each bullet is one sentence or short paragraph
|
|
62
|
+
in plain user-facing language: "the CLI now supports X", "corrected Y behavior
|
|
63
|
+
on Z", not "we decided" or "the team merged." Deduplicate related items.
|
|
64
|
+
Write from scratch per the coherent-revisions rule.}
|
|
65
65
|
|
|
66
|
-
|
|
67
|
-
{
|
|
66
|
+
### Known issues
|
|
67
|
+
- {Deferred questions from Step 4, if any. Omit this subsection when empty.}
|
|
68
68
|
```
|
|
69
69
|
|
|
70
|
-
|
|
70
|
+
The entry stays short. If a change needs more detail, reference the repo's docs or a dedicated design doc — do not inline session-level retrospection into the public changelog.
|
|
71
|
+
|
|
72
|
+
**Step 6: Delete consumed branch notes from the workspace**
|
|
71
73
|
```bash
|
|
72
|
-
|
|
73
|
-
|
|
74
|
+
rm release-notes/unreleased/{repo}/branch-release-*
|
|
75
|
+
# If the directory is now empty, remove it too:
|
|
76
|
+
rmdir release-notes/unreleased/{repo} 2>/dev/null || true
|
|
74
77
|
```
|
|
78
|
+
The branch notes were an intermediate capture; their content is now in the CHANGELOG entry and their raw form in git history. They do not survive into the project repo.
|
|
75
79
|
|
|
76
|
-
**Step 7: Commit
|
|
80
|
+
**Step 7: Commit the CHANGELOG entry to the project repo**
|
|
77
81
|
```bash
|
|
78
82
|
cd repos/{repo}
|
|
79
|
-
git add
|
|
80
|
-
git commit -m "docs: v{version}
|
|
83
|
+
git add CHANGELOG.md
|
|
84
|
+
git commit -m "docs: v{version} changelog entry"
|
|
81
85
|
```
|
|
86
|
+
This commit lands on the project repo's source clone (which stays on its default branch). The user pushes it when ready — `/release` does not push automatically.
|
|
82
87
|
|
|
83
|
-
**Step 7b: Bump package.json version**
|
|
84
|
-
If the project repo has a `package.json` with a `version` field
|
|
88
|
+
**Step 7b: Bump package.json version (project repo)**
|
|
89
|
+
If the project repo has a `package.json` with a `version` field, update it to match the release version:
|
|
85
90
|
```bash
|
|
86
91
|
cd repos/{repo}
|
|
87
92
|
# Update "version": "..." in package.json to the release version
|
|
@@ -90,9 +95,17 @@ git commit -m "chore: bump version to v{version}"
|
|
|
90
95
|
```
|
|
91
96
|
Skip this step if the repo has no package.json or no version field.
|
|
92
97
|
|
|
98
|
+
**Step 7c: Commit the consumed-notes deletion in the workspace**
|
|
99
|
+
```bash
|
|
100
|
+
# From the workspace root
|
|
101
|
+
git add release-notes/unreleased/
|
|
102
|
+
git commit -m "release: consume {repo} branch notes for v{version}"
|
|
103
|
+
```
|
|
104
|
+
Workspace and project repos have separate commits — they are separate git histories.
|
|
105
|
+
|
|
93
106
|
**Step 8: Consume project-scoped specs**
|
|
94
107
|
Project-scoped specs and plans in `shared-context/{user}/` (ongoing) that are fully covered by this release:
|
|
95
|
-
- Consume into the
|
|
108
|
+
- Consume into the CHANGELOG entry (their content is now captured there)
|
|
96
109
|
- Remove the source files
|
|
97
110
|
- If partially covered: rewrite the spec to reflect only what remains unimplemented
|
|
98
111
|
|
|
@@ -116,11 +129,14 @@ Process ephemeral shared-context entries:
|
|
|
116
129
|
```
|
|
117
130
|
|
|
118
131
|
**Step 10: Report**
|
|
119
|
-
"Release v{version} complete for {repo}. {N} branch notes
|
|
132
|
+
"Release v{version} complete for {repo}. {N} branch notes consumed into CHANGELOG.md. {M} context entries synthesized into {K} locked entries."
|
|
120
133
|
|
|
121
134
|
## Notes
|
|
122
|
-
|
|
123
|
-
-
|
|
124
|
-
-
|
|
125
|
-
-
|
|
126
|
-
- The
|
|
135
|
+
|
|
136
|
+
- Release entries live in `CHANGELOG.md` at the project repo root — one file, one concise entry per version. No `release-notes/v*.md`, no `release-notes/archive/`.
|
|
137
|
+
- Branch notes live in the WORKSPACE at `release-notes/unreleased/{repo}/`. `/complete-work` writes them; `/release` consumes and deletes them. They never reach project repos.
|
|
138
|
+
- Versions are bumped here, not in `/complete-work`. This keeps the version semantics aligned with what actually shipped (accumulated changes since last release).
|
|
139
|
+
- The public repo stays lean. Detailed per-branch retrospection exists in workspace git history (the consumed-notes commit) but is not surfaced as standalone files in either repo.
|
|
140
|
+
- Context synthesis happens in the WORKSPACE repo — Step 7c (consumed-notes) and Step 9 (shared-context synthesis) are separate workspace commits.
|
|
141
|
+
- Per-repo is the default — each project repo has its own release cadence.
|
|
142
|
+
- The coherent-revisions rule applies: write the CHANGELOG entry from scratch, don't concatenate branch notes.
|
|
@@ -53,8 +53,14 @@ Begin or resume a persistent work session. Each session lives in its own `work-s
|
|
|
53
53
|
- `names` is a list of all names the chat has had (users can rename). Append, never replace.
|
|
54
54
|
- `ended` is set by the session-end hook when the chat closes.
|
|
55
55
|
4. Update the tracker `status:` to `active` if it was `paused`
|
|
56
|
-
5.
|
|
57
|
-
|
|
56
|
+
5. Restore the task list from `## Tasks` per the `task-list-mirroring` rule:
|
|
57
|
+
```bash
|
|
58
|
+
cd work-sessions/{name}/workspace
|
|
59
|
+
node .claude/scripts/sync-tasks.mjs --read session.md
|
|
60
|
+
```
|
|
61
|
+
Pass the parsed `todos` array to `TodoWrite` so the live UI matches the durable state. If the section is missing (legacy session predating this feature), seed it first via `--write` with an empty `todos` array — the helper will insert the bookends.
|
|
62
|
+
6. Run history reconstruction (see below)
|
|
63
|
+
7. Tell user: "Resuming {name}. Work from `work-sessions/{name}/workspace/`."
|
|
58
64
|
|
|
59
65
|
### History Reconstruction
|
|
60
66
|
|
|
@@ -165,6 +171,32 @@ Register this chat in the tracker's `chatSessions` frontmatter. For new sessions
|
|
|
165
171
|
|
|
166
172
|
The tracker already reflects the correct state — assignment happened in step 5 or 6 via `adapter.claim()`. Do not write to any local file mirror. There is no `open-work.md`.
|
|
167
173
|
|
|
174
|
+
### Seed the task list
|
|
175
|
+
|
|
176
|
+
After session creation, seed the `## Tasks` section in the new tracker so `TodoWrite` has something to mirror. See the `task-list-mirroring` rule for the schema.
|
|
177
|
+
|
|
178
|
+
```bash
|
|
179
|
+
# Build the seed from inside the worktree so the helper resolves workspace.json correctly.
|
|
180
|
+
cd work-sessions/{session-name}/workspace
|
|
181
|
+
echo '{"todos": []}' | node .claude/scripts/sync-tasks.mjs --write session.md
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
The helper auto-inserts the `Start work` (completed) and `Complete work` (pending) bookends, and resolves the tracker title from `workItem:` if set.
|
|
185
|
+
|
|
186
|
+
Then call `TodoWrite` with the same seed so the live UI matches:
|
|
187
|
+
|
|
188
|
+
```javascript
|
|
189
|
+
// Pseudocode — call the actual TodoWrite tool.
|
|
190
|
+
TodoWrite({
|
|
191
|
+
todos: [
|
|
192
|
+
{ content: 'Start work', activeForm: 'Starting work', status: 'completed' },
|
|
193
|
+
{ content: 'Complete work', activeForm: 'Completing work', status: 'pending' },
|
|
194
|
+
]
|
|
195
|
+
});
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
The auto-commit at the end of "Capture prior conversation context" picks up the new section — no separate commit needed.
|
|
199
|
+
|
|
168
200
|
### Capture prior conversation context
|
|
169
201
|
|
|
170
202
|
If brainstorming, spec writing, or design discussion happened in this conversation before `/start-work` was called, that reasoning needs to be captured into the session tracker body. Otherwise it will be lost when the conversation ends and `/complete-work` will produce thin release notes.
|
|
@@ -75,6 +75,22 @@ Also handle these non-component files from the payload:
|
|
|
75
75
|
|
|
76
76
|
Read `toVersion` from `.workspace-update/.manifest.json` and update `templateVersion` in `workspace.json` to match.
|
|
77
77
|
|
|
78
|
+
### Step 4a: Run idempotent migrators
|
|
79
|
+
|
|
80
|
+
The payload may include migrator scripts at `.workspace-update/.claude/scripts/migrate-*.mjs` that bring older workspaces forward in shape. They are idempotent — safe to re-run on already-migrated workspaces. Run each one and surface its action in the upgrade summary.
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
node .workspace-update/.claude/scripts/migrate-claude-md-freshness-include.mjs
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
Output is JSON: `{"action":"appended"|"unchanged"|"skipped"}`.
|
|
87
|
+
|
|
88
|
+
- `appended` — the workspace's `CLAUDE.md` got the `@local-only-template-freshness.md` include line added at the end.
|
|
89
|
+
- `unchanged` — the line was already present.
|
|
90
|
+
- `skipped` — no `CLAUDE.md` exists at the workspace root (rare; surface to the user).
|
|
91
|
+
|
|
92
|
+
Add other migrators here as the template ships them.
|
|
93
|
+
|
|
78
94
|
### Step 5: Post-update verification
|
|
79
95
|
|
|
80
96
|
Run `/maintenance audit` again to verify the update didn't introduce:
|