@ulysses-ai/create-workspace 0.17.0-beta.0 → 0.19.0-beta.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 +3 -3
- package/lib/init.mjs +4 -1
- package/lib/payload.mjs +18 -1
- package/lib/payload.test.mjs +55 -0
- package/lib/scaffold.mjs +23 -6
- package/lib/scaffold.test.mjs +59 -0
- package/package.json +1 -1
- package/template/CLAUDE.md.tmpl +19 -2
- package/template/{.claude → _claude}/hooks/_utils.mjs +1 -1
- package/template/_claude/hooks/repo-write-detection.mjs +204 -0
- package/template/{.claude → _claude}/hooks/session-start.mjs +35 -1
- package/template/_claude/hooks/subagent-start.mjs +111 -0
- package/template/{.claude → _claude}/lib/session-frontmatter.mjs +28 -0
- package/template/{.claude → _claude}/rules/coherent-revisions.md +1 -1
- package/template/_claude/rules/forge-operations.md +57 -0
- package/template/_claude/rules/git-conventions.md +39 -0
- package/template/_claude/rules/goal-driven-work.md +24 -0
- package/template/_claude/rules/honest-pushback.md +56 -0
- package/template/_claude/rules/memory-guidance.md +66 -0
- package/template/{.claude → _claude}/rules/superpowers-workflow.md.skip +1 -1
- package/template/{.claude → _claude}/rules/task-list-mirroring.md +6 -0
- package/template/_claude/rules/work-item-tracking.md +48 -0
- package/template/_claude/rules/workspace-structure.md +79 -0
- package/template/{.claude → _claude}/scripts/build-workspace-context.mjs +86 -30
- package/template/_claude/scripts/chat-record.mjs +315 -0
- package/template/_claude/scripts/cleanup-work-session.mjs +436 -0
- package/template/_claude/scripts/context-footprint.mjs +391 -0
- package/template/{.claude → _claude}/scripts/forges/github.mjs +46 -0
- package/template/{.claude → _claude}/scripts/forges/gitlab.mjs +3 -2
- package/template/{.claude → _claude}/scripts/forges/interface.mjs +13 -0
- package/template/{.claude → _claude}/scripts/generate-claude-local.mjs +21 -2
- package/template/_claude/scripts/migrate-sessions.mjs +1571 -0
- package/template/{.claude → _claude}/scripts/migrate-to-workspace-context.mjs +7 -2
- package/template/_claude/scripts/task-pr.mjs +447 -0
- package/template/_claude/scripts/task-worktree.mjs +525 -0
- package/template/{.claude → _claude}/scripts/trackers/github-issues.mjs +11 -0
- package/template/{.claude → _claude}/scripts/trackers/interface.mjs +8 -0
- package/template/_claude/scripts/workspace-diagnostics.mjs +654 -0
- package/template/{.claude → _claude}/skills/braindump/SKILL.md +12 -4
- package/template/{.claude → _claude}/skills/build-docs-site/SKILL.md +5 -5
- package/template/{.claude → _claude}/skills/build-docs-site/templates/spec.md.tmpl +1 -1
- package/template/_claude/skills/complete-work/SKILL.md +452 -0
- package/template/_claude/skills/context-placement/SKILL.md +202 -0
- package/template/{.claude/rules/goal-driven-work.md → _claude/skills/goal-driven-work/SKILL.md} +46 -19
- package/template/{.claude → _claude}/skills/handoff/SKILL.md +12 -4
- package/template/{.claude → _claude}/skills/maintenance/SKILL.md +56 -17
- package/template/_claude/skills/migrate-sessions/SKILL.md +70 -0
- package/template/{.claude → _claude}/skills/pause-work/SKILL.md +9 -1
- package/template/_claude/skills/release/SKILL.md +91 -0
- package/template/{.claude → _claude}/skills/start-work/SKILL.md +89 -7
- package/template/{.claude → _claude}/skills/workspace-init/SKILL.md +3 -1
- package/template/{.claude → _claude}/skills/workspace-update/SKILL.md +4 -0
- package/template/_gitignore +9 -0
- package/template/workspace.json.tmpl +4 -3
- package/template/.claude/hooks/repo-write-detection.mjs +0 -107
- package/template/.claude/hooks/subagent-start.mjs +0 -44
- package/template/.claude/rules/forge-operations.md +0 -107
- package/template/.claude/rules/git-conventions.md +0 -34
- package/template/.claude/rules/honest-pushback.md +0 -56
- package/template/.claude/rules/memory-guidance.md +0 -109
- package/template/.claude/rules/work-item-tracking.md +0 -90
- package/template/.claude/rules/workspace-structure.md +0 -137
- package/template/.claude/scripts/cleanup-work-session.mjs +0 -247
- package/template/.claude/skills/complete-work/SKILL.md +0 -498
- package/template/.claude/skills/release/SKILL.md +0 -151
- /package/template/{.claude → _claude}/agents/aside-researcher.md +0 -0
- /package/template/{.claude → _claude}/agents/implementer.md +0 -0
- /package/template/{.claude → _claude}/agents/researcher.md +0 -0
- /package/template/{.claude → _claude}/agents/reviewer.md +0 -0
- /package/template/{.claude → _claude}/hooks/bash-output-advisory.mjs +0 -0
- /package/template/{.claude → _claude}/hooks/post-compact.mjs +0 -0
- /package/template/{.claude → _claude}/hooks/pre-compact.mjs +0 -0
- /package/template/{.claude → _claude}/hooks/session-end.mjs +0 -0
- /package/template/{.claude → _claude}/hooks/version-freshness-check.mjs +0 -0
- /package/template/{.claude → _claude}/hooks/workspace-update-check.mjs +0 -0
- /package/template/{.claude → _claude}/lib/freshness.mjs +0 -0
- /package/template/{.claude → _claude}/lib/registry-check.mjs +0 -0
- /package/template/{.claude → _claude}/lib/require-node.mjs +0 -0
- /package/template/{.claude → _claude}/recipes/migrate-from-notion.md +0 -0
- /package/template/{.claude → _claude}/rules/agent-rules.md.skip +0 -0
- /package/template/{.claude → _claude}/rules/cloud-infrastructure.md.skip +0 -0
- /package/template/{.claude → _claude}/rules/config-review.md.skip +0 -0
- /package/template/{.claude → _claude}/rules/documentation.md.skip +0 -0
- /package/template/{.claude → _claude}/rules/local-dev-environment.md.skip +0 -0
- /package/template/{.claude → _claude}/rules/product-integrity.md.skip +0 -0
- /package/template/{.claude → _claude}/rules/scope-guard.md.skip +0 -0
- /package/template/{.claude → _claude}/rules/token-economics.md.skip +0 -0
- /package/template/{.claude → _claude}/scripts/add-repo-to-session.mjs +0 -0
- /package/template/{.claude → _claude}/scripts/capture-context.mjs +0 -0
- /package/template/{.claude → _claude}/scripts/create-work-session.mjs +0 -0
- /package/template/{.claude → _claude}/scripts/migrate-canonical-priority.mjs +0 -0
- /package/template/{.claude → _claude}/scripts/migrate-claude-md-freshness-include.mjs +0 -0
- /package/template/{.claude → _claude}/scripts/migrate-open-work.mjs +0 -0
- /package/template/{.claude → _claude}/scripts/migrate-session-layout.mjs +0 -0
- /package/template/{.claude → _claude}/scripts/sweep-references.mjs +0 -0
- /package/template/{.claude → _claude}/scripts/sync-tasks.mjs +0 -0
- /package/template/{.claude → _claude}/settings.json +0 -0
- /package/template/{.claude → _claude}/skills/aside/SKILL.md +0 -0
- /package/template/{.claude → _claude}/skills/build-docs-site/checklists/framing.md +0 -0
- /package/template/{.claude → _claude}/skills/build-docs-site/checklists/pitfalls.md +0 -0
- /package/template/{.claude → _claude}/skills/build-docs-site/checklists/review.md +0 -0
- /package/template/{.claude → _claude}/skills/build-docs-site/scripts/bulk-fill-migration.py +0 -0
- /package/template/{.claude → _claude}/skills/build-docs-site/scripts/forbidden-word-grep.mjs +0 -0
- /package/template/{.claude → _claude}/skills/build-docs-site/scripts/leak-grep.mjs +0 -0
- /package/template/{.claude → _claude}/skills/build-docs-site/templates/custom.css.tmpl +0 -0
- /package/template/{.claude → _claude}/skills/build-docs-site/templates/docusaurus.config.ts.tmpl +0 -0
- /package/template/{.claude → _claude}/skills/build-docs-site/templates/primitives/Arrow.tsx +0 -0
- /package/template/{.claude → _claude}/skills/build-docs-site/templates/primitives/Box.tsx +0 -0
- /package/template/{.claude → _claude}/skills/build-docs-site/templates/primitives/DiagramContainer.tsx +0 -0
- /package/template/{.claude → _claude}/skills/build-docs-site/templates/primitives/Region.tsx +0 -0
- /package/template/{.claude → _claude}/skills/build-docs-site/templates/primitives/SectionTitle.tsx +0 -0
- /package/template/{.claude → _claude}/skills/build-docs-site/templates/primitives/tokens.ts +0 -0
- /package/template/{.claude → _claude}/skills/build-docs-site/templates/sidebars.ts.tmpl +0 -0
- /package/template/{.claude → _claude}/skills/promote/SKILL.md +0 -0
- /package/template/{.claude → _claude}/skills/setup-tracker/SKILL.md +0 -0
- /package/template/{.claude → _claude}/skills/sync-work/SKILL.md +0 -0
- /package/template/{.mcp.json → _mcp.json} +0 -0
|
@@ -1,498 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: complete-work
|
|
3
|
-
description: Finalize a work session — rebase, synthesize release notes from spec/plan/session tracker/commits, create PRs with unified presentation. Handles all project repos and workspace repo. Use when work on a session is done.
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# Complete Work
|
|
7
|
-
|
|
8
|
-
Finalize the active work session. Handles all project repos (code changes, release notes, PRs) and the workspace repo (context processing, PR). Presents a unified summary with a single merge approval, then tears down the session folder.
|
|
9
|
-
|
|
10
|
-
## Flow
|
|
11
|
-
|
|
12
|
-
### Step 1: Detect context
|
|
13
|
-
|
|
14
|
-
Read the active-session pointer from `.claude/.active-session.json` in the current worktree.
|
|
15
|
-
If no active session: "No active work session. Nothing to complete."
|
|
16
|
-
|
|
17
|
-
Read the full session tracker at `work-sessions/{session-name}/workspace/session.md` (use the frontmatter helper in `.claude/lib/session-frontmatter.mjs` — scripts and hooks use `_utils.mjs` which wraps it).
|
|
18
|
-
|
|
19
|
-
Determine paths:
|
|
20
|
-
- Session folder: `work-sessions/{session-name}/`
|
|
21
|
-
- Workspace worktree: `work-sessions/{session-name}/workspace/`
|
|
22
|
-
- Project worktrees: `work-sessions/{session-name}/workspace/repos/{repo}/` for each repo in the tracker's `repos:` list
|
|
23
|
-
- Read each repo's default branch from workspace.json (`repos.{repo}.branch`)
|
|
24
|
-
- **Release-notes base directory.** Read `workspace.releaseNotesDir` from `workspace.json` (default `workspace-context/release-notes` if the field is absent). Throughout this skill `{releaseNotesDir}` refers to that resolved value; every branch-note path is `{releaseNotesDir}/unreleased/{repo}/…`. Never use a bare `release-notes/`.
|
|
25
|
-
|
|
26
|
-
### Step 2: Rebase project repos
|
|
27
|
-
|
|
28
|
-
For each repo in the tracker's `repos:`:
|
|
29
|
-
```bash
|
|
30
|
-
# {repo-branch} = repos.{repo}.branch from workspace.json
|
|
31
|
-
cd work-sessions/{session-name}/workspace/repos/{repo}
|
|
32
|
-
git fetch origin
|
|
33
|
-
git rebase origin/{repo-branch}
|
|
34
|
-
```
|
|
35
|
-
If conflicts arise in any repo, STOP and present them to the user. Do not auto-resolve.
|
|
36
|
-
|
|
37
|
-
### Step 3: Capture final discussion state
|
|
38
|
-
|
|
39
|
-
Run `/braindump` to capture any final discussion/reasoning to the session tracker body.
|
|
40
|
-
If the user declines or there's nothing to capture, skip.
|
|
41
|
-
|
|
42
|
-
### Step 4: Flush task list to session.md
|
|
43
|
-
|
|
44
|
-
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:
|
|
45
|
-
|
|
46
|
-
```bash
|
|
47
|
-
cd work-sessions/{session-name}/workspace
|
|
48
|
-
echo '<JSON-of-current-todos>' | node .claude/scripts/sync-tasks.mjs --write session.md
|
|
49
|
-
```
|
|
50
|
-
|
|
51
|
-
Mark `Complete work` as `in_progress` in the JSON before flushing — the rest of this skill IS the act of completing.
|
|
52
|
-
|
|
53
|
-
### Step 5: Gather source material
|
|
54
|
-
|
|
55
|
-
Formally read ALL sources before synthesizing — do not write release notes from memory alone:
|
|
56
|
-
|
|
57
|
-
1. **Session tracker** at `work-sessions/{session-name}/workspace/session.md` — read the full body (frontmatter is machine state, body is human content)
|
|
58
|
-
|
|
59
|
-
2. **Session-scoped specs/plans/goal artifacts** at the top of the session worktree:
|
|
60
|
-
- `work-sessions/{session-name}/workspace/design-*.md` files
|
|
61
|
-
- `work-sessions/{session-name}/workspace/plan-*.md` files
|
|
62
|
-
- `work-sessions/{session-name}/workspace/goal-*.md` files
|
|
63
|
-
- `work-sessions/{session-name}/workspace/research-*.md` files
|
|
64
|
-
- `work-sessions/{session-name}/workspace/crossref-*.md` files
|
|
65
|
-
- Read each one fully
|
|
66
|
-
|
|
67
|
-
3. **Handoffs** — any workspace-context entries referencing this branch:
|
|
68
|
-
```bash
|
|
69
|
-
grep -rl "branch: {branch}" workspace-context/
|
|
70
|
-
```
|
|
71
|
-
Read each matching file.
|
|
72
|
-
|
|
73
|
-
4. **Branch commit logs** (per repo):
|
|
74
|
-
```bash
|
|
75
|
-
# For each repo in the tracker's repos list:
|
|
76
|
-
cd work-sessions/{session-name}/workspace/repos/{repo}
|
|
77
|
-
git log origin/{repo-branch}..HEAD --oneline
|
|
78
|
-
```
|
|
79
|
-
|
|
80
|
-
### Step 6: Synthesize release notes
|
|
81
|
-
|
|
82
|
-
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.
|
|
83
|
-
|
|
84
|
-
For each repo in the tracker's `repos:` list that has commits beyond the base branch:
|
|
85
|
-
|
|
86
|
-
```bash
|
|
87
|
-
cd work-sessions/{session-name}/workspace/repos/{repo}
|
|
88
|
-
COMMIT_ID=$(git rev-parse --short HEAD)
|
|
89
|
-
cd ../.. # back to the workspace worktree
|
|
90
|
-
mkdir -p {releaseNotesDir}/unreleased/{repo-name}
|
|
91
|
-
```
|
|
92
|
-
|
|
93
|
-
**File 1: `{releaseNotesDir}/unreleased/{repo-name}/branch-release-notes-{COMMIT_ID}.md`** (relative to the workspace worktree)
|
|
94
|
-
```markdown
|
|
95
|
-
---
|
|
96
|
-
branch: {branch}
|
|
97
|
-
repo: {repo-name}
|
|
98
|
-
type: {feature|fix|chore}
|
|
99
|
-
author: {user}
|
|
100
|
-
date: {YYYY-MM-DD}
|
|
101
|
-
---
|
|
102
|
-
|
|
103
|
-
## {Human-readable title}
|
|
104
|
-
|
|
105
|
-
{Coherent narrative synthesized from tracker + spec + plan + commits.
|
|
106
|
-
Written from scratch per coherent-revisions rule.}
|
|
107
|
-
```
|
|
108
|
-
|
|
109
|
-
**File 2: `{releaseNotesDir}/unreleased/{repo-name}/branch-release-questions-{COMMIT_ID}.md`**
|
|
110
|
-
```markdown
|
|
111
|
-
---
|
|
112
|
-
branch: {branch}
|
|
113
|
-
repo: {repo-name}
|
|
114
|
-
author: {user}
|
|
115
|
-
date: {YYYY-MM-DD}
|
|
116
|
-
---
|
|
117
|
-
|
|
118
|
-
## Open Questions
|
|
119
|
-
|
|
120
|
-
{Only genuinely open questions — not things resolved during implementation.}
|
|
121
|
-
```
|
|
122
|
-
|
|
123
|
-
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.
|
|
124
|
-
|
|
125
|
-
After all repos are processed, commit once on the workspace branch:
|
|
126
|
-
```bash
|
|
127
|
-
cd work-sessions/{session-name}/workspace
|
|
128
|
-
git add {releaseNotesDir}/unreleased/
|
|
129
|
-
git commit -m "docs: add release notes for {branch}"
|
|
130
|
-
```
|
|
131
|
-
|
|
132
|
-
If a repo has no commits beyond the base, skip release notes for it.
|
|
133
|
-
|
|
134
|
-
### Step 7: Remove session artifacts from the workspace branch
|
|
135
|
-
|
|
136
|
-
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, plans, and goal artifacts have served their purpose.
|
|
137
|
-
|
|
138
|
-
**Goal sub-branch pre-flight (only when a `goal-*.md` artifact is present).** A `/goal`-driven session can produce per-phase sub-branches for code phases (any phase declaring `integration.strategy: sub-branch` in the goal artifact). Those sub-branches must be merged into the session branch before completion, or their work is lost when the session folder is torn down. Before stripping anything, check each repo in the session — the workspace worktree itself and every `repos/{repo}/` project worktree:
|
|
139
|
-
|
|
140
|
-
```bash
|
|
141
|
-
cd work-sessions/{session-name}/workspace/repos/{repo} # repeat for the workspace worktree too
|
|
142
|
-
session_branch=$(git rev-parse --abbrev-ref HEAD)
|
|
143
|
-
for sub in $(git branch --format='%(refname:short)' --list "${session_branch}-*"); do
|
|
144
|
-
git merge-base --is-ancestor "$sub" "$session_branch" || echo "UNMERGED: $sub"
|
|
145
|
-
done
|
|
146
|
-
```
|
|
147
|
-
|
|
148
|
-
If any `UNMERGED:` lines print, abort completion and show the list. The user merges the intended sub-branches into the session branch, or closes abandoned ones, then re-runs `/complete-work`. When no `goal-*.md` artifact is present, this check is a no-op and completion proceeds normally.
|
|
149
|
-
|
|
150
|
-
Session content lives at the top of the workspace worktree on the session branch. Once the pre-flight passes, remove these files from the branch before the final push so main's top level stays free of session artifacts:
|
|
151
|
-
|
|
152
|
-
```bash
|
|
153
|
-
cd work-sessions/{session-name}/workspace
|
|
154
|
-
git rm -f session.md 2>/dev/null || true
|
|
155
|
-
git rm -f design-*.md 2>/dev/null || true
|
|
156
|
-
git rm -f plan-*.md 2>/dev/null || true
|
|
157
|
-
git rm -f goal-*.md 2>/dev/null || true
|
|
158
|
-
git rm -f research-*.md 2>/dev/null || true
|
|
159
|
-
git rm -f crossref-*.md 2>/dev/null || true
|
|
160
|
-
git commit -m "chore: remove session artifacts before PR" 2>/dev/null || true
|
|
161
|
-
```
|
|
162
|
-
|
|
163
|
-
The `|| true` guards keep this idempotent — if a file is already gone (e.g., a session without specs or goals), the step is a no-op. The commit is skipped when there's nothing staged.
|
|
164
|
-
|
|
165
|
-
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.
|
|
166
|
-
|
|
167
|
-
> **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.
|
|
168
|
-
|
|
169
|
-
### Step 8: Detect remote type per repo
|
|
170
|
-
|
|
171
|
-
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.
|
|
172
|
-
|
|
173
|
-
```bash
|
|
174
|
-
cd work-sessions/{session-name}/workspace/repos/{repo}
|
|
175
|
-
git remote get-url origin 2>&1
|
|
176
|
-
```
|
|
177
|
-
|
|
178
|
-
Classify the result:
|
|
179
|
-
|
|
180
|
-
- **GitHub remote** — URL contains `github.com` or `gh repo view` succeeds against origin → use the PR flow (Step 9a, Step 10a).
|
|
181
|
-
- **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).
|
|
182
|
-
- **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.
|
|
183
|
-
- **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.
|
|
184
|
-
|
|
185
|
-
### Step 9: Push all repos
|
|
186
|
-
|
|
187
|
-
#### Step 9a: GitHub remotes
|
|
188
|
-
|
|
189
|
-
```bash
|
|
190
|
-
# Each project repo with a GitHub remote
|
|
191
|
-
cd work-sessions/{session-name}/workspace/repos/{repo}
|
|
192
|
-
git push -u origin {branch}
|
|
193
|
-
|
|
194
|
-
# Workspace repo — from the workspace worktree
|
|
195
|
-
cd work-sessions/{session-name}/workspace
|
|
196
|
-
git add .
|
|
197
|
-
git commit -m "chore: finalize context for {session-name}"
|
|
198
|
-
git push -u origin {branch}
|
|
199
|
-
```
|
|
200
|
-
|
|
201
|
-
#### Step 9b: Local/bare remotes
|
|
202
|
-
|
|
203
|
-
```bash
|
|
204
|
-
# Push the feature branch to the bare remote so it exists there
|
|
205
|
-
cd work-sessions/{session-name}/workspace/repos/{repo}
|
|
206
|
-
git push -u origin {branch}
|
|
207
|
-
|
|
208
|
-
# Workspace repo — same commit + push pattern
|
|
209
|
-
cd work-sessions/{session-name}/workspace
|
|
210
|
-
git add .
|
|
211
|
-
git commit -m "chore: finalize context for {session-name}"
|
|
212
|
-
git push -u origin {branch}
|
|
213
|
-
```
|
|
214
|
-
|
|
215
|
-
The push shape is the same as 9a — what differs is the merge mechanics in Step 10b.
|
|
216
|
-
|
|
217
|
-
### Step 10: Merge and present unified summary
|
|
218
|
-
|
|
219
|
-
#### Step 10a: GitHub remotes — create PRs, unified summary, merge
|
|
220
|
-
|
|
221
|
-
Create one PR per project repo plus one workspace PR. PR operations go through the forge adapter (`.claude/scripts/forges/interface.mjs`), not `gh` directly — see `.claude/rules/forge-operations.md` for the contract. The adapter resolves the target repo from `workspace.forge.repo` or the local git remote.
|
|
222
|
-
|
|
223
|
-
```javascript
|
|
224
|
-
import { createForge } from './.claude/scripts/forges/interface.mjs';
|
|
225
|
-
import { readFileSync } from 'node:fs';
|
|
226
|
-
|
|
227
|
-
const ws = JSON.parse(readFileSync('workspace.json', 'utf-8'));
|
|
228
|
-
const forge = createForge(ws.workspace?.forge);
|
|
229
|
-
|
|
230
|
-
// For each repo in the tracker's repos with a GitHub remote, from
|
|
231
|
-
// work-sessions/{session-name}/workspace/repos/{repo}:
|
|
232
|
-
const projectPr = await forge.prCreate({
|
|
233
|
-
title: `${type}: ${description}`,
|
|
234
|
-
body: prBody, // synthesized release notes + verification section
|
|
235
|
-
});
|
|
236
|
-
|
|
237
|
-
// Workspace PR — from the workspace worktree:
|
|
238
|
-
const workspacePr = await forge.prCreate({
|
|
239
|
-
title: `context: ${sessionName} work session`,
|
|
240
|
-
body: workspacePrBody,
|
|
241
|
-
});
|
|
242
|
-
```
|
|
243
|
-
|
|
244
|
-
Present unified summary:
|
|
245
|
-
```
|
|
246
|
-
Work session complete:
|
|
247
|
-
|
|
248
|
-
PROJECT: {repo-1}
|
|
249
|
-
PR #{n}: {type}: {description}
|
|
250
|
-
Branch: {branch} → {repo-1-branch}
|
|
251
|
-
Changes:
|
|
252
|
-
- {bullet points from release notes}
|
|
253
|
-
Release notes: branch-release-notes-{COMMIT_ID}.md
|
|
254
|
-
|
|
255
|
-
PROJECT: {repo-2}
|
|
256
|
-
PR #{m}: {type}: {description}
|
|
257
|
-
Branch: {branch} → {repo-2-branch}
|
|
258
|
-
Changes:
|
|
259
|
-
- {bullet points from release notes}
|
|
260
|
-
|
|
261
|
-
WORKSPACE: {workspace-name}
|
|
262
|
-
PR #{p}: context: {session-name} work session
|
|
263
|
-
Branch: {branch} → main
|
|
264
|
-
|
|
265
|
-
Merge all? [Y/n]
|
|
266
|
-
```
|
|
267
|
-
|
|
268
|
-
If yes — merge all PRs atomically through the forge adapter:
|
|
269
|
-
|
|
270
|
-
```javascript
|
|
271
|
-
// For each project PR returned from Step 10a's prCreate calls:
|
|
272
|
-
await forge.prMerge({ id: projectPr.id, strategy: 'squash', deleteBranch: true });
|
|
273
|
-
|
|
274
|
-
// Workspace PR:
|
|
275
|
-
await forge.prMerge({ id: workspacePr.id, strategy: 'squash', deleteBranch: true });
|
|
276
|
-
```
|
|
277
|
-
|
|
278
|
-
`strategy: 'squash'` matches the workspace convention from `post-release-discipline` (`create-ulysses-workspace` requires linear history, so squash is the only strategy that merges cleanly; squash also lifts the PR body into the commit message). `deleteBranch: true` cleans the remote feature branch on success.
|
|
279
|
-
|
|
280
|
-
Then pull all repos to their default branches (still plain git):
|
|
281
|
-
|
|
282
|
-
```bash
|
|
283
|
-
# For each repo in the tracker's repos:
|
|
284
|
-
cd repos/{repo} && git pull origin {repo-branch}
|
|
285
|
-
cd {main-workspace-root} && git pull origin main
|
|
286
|
-
```
|
|
287
|
-
|
|
288
|
-
**Step 10a.1: Tag the merge commit (release sessions only, project repos with `package.json`)**
|
|
289
|
-
|
|
290
|
-
The next three sub-substeps run only when the session branch starts with `release/` — the convention for release sessions (e.g., `release/v0.15.0-beta.0`). For feature, bugfix, and chore sessions, skip 10a.1, 10a.2, and 10a.3 entirely; non-release sessions don't trigger publishes. Detection is purely by branch prefix.
|
|
291
|
-
|
|
292
|
-
Derive the version tag from the branch name by stripping the `release/` prefix (so `release/v0.15.0-beta.0` yields `v0.15.0-beta.0`). For each project repo whose `package.json` declares a `version` field, verify that version matches the derived tag. The workspace repo is **never** tagged — only project repos with publishable `package.json` files get tagged, since the tag triggers `.github/workflows/publish.yml` in that project repo. If a project repo's `package.json` version doesn't match the release tag, skip that repo with a warning rather than failing the whole completion flow — the mismatch usually means `/release` was run against a different version than the branch name suggests, and the user needs to investigate before publishing.
|
|
293
|
-
|
|
294
|
-
Before tagging, preflight against origin: if `v{version}` already exists remotely, surface the conflict to the user with three explicit recovery options — **Reuse** (skip to 10a.2 if the existing tag points at the right commit), **Replace** (`git push origin --delete v{version}` then re-run 10a.1), or **Investigate** (`forge.releaseView({ tag: 'v{version}', repo: '{org}/{repo}' })` — or `gh release view v{version}` as a manual fallback — to see what shipped). Do **not** silently force-push the tag; an existing tag means a published artifact, and overwriting it without confirmation can corrupt the npm registry's view of the release history.
|
|
295
|
-
|
|
296
|
-
If the tag is absent on origin, tag the merge commit (HEAD on `{default-branch}` after the prior `git pull origin {default-branch}`) and push the tag. The tag push triggers `.github/workflows/publish.yml`.
|
|
297
|
-
|
|
298
|
-
```bash
|
|
299
|
-
# Detect: only run for release sessions.
|
|
300
|
-
if [[ ! "$branch" =~ ^release/ ]]; then
|
|
301
|
-
# Not a release session — skip 10a.1, 10a.2, 10a.3.
|
|
302
|
-
return
|
|
303
|
-
fi
|
|
304
|
-
|
|
305
|
-
# Extract the version from the branch name (release/v{X} → v{X}).
|
|
306
|
-
version_tag="${branch#release/}" # e.g. "v0.15.0-beta.0"
|
|
307
|
-
|
|
308
|
-
# For each project repo with a package.json containing a version field:
|
|
309
|
-
for repo in {project-repos-with-package-json}; do
|
|
310
|
-
cd repos/{repo}
|
|
311
|
-
|
|
312
|
-
# Verify package.json version matches the tag.
|
|
313
|
-
pkg_version=$(node -p "require('./package.json').version")
|
|
314
|
-
expected_version="${version_tag#v}"
|
|
315
|
-
if [ "$pkg_version" != "$expected_version" ]; then
|
|
316
|
-
echo "Skipping {repo}: package.json version ($pkg_version) does not match release tag ($expected_version)."
|
|
317
|
-
continue
|
|
318
|
-
fi
|
|
319
|
-
|
|
320
|
-
# Preflight: does the tag already exist on origin?
|
|
321
|
-
if git ls-remote --exit-code origin "refs/tags/$version_tag" >/dev/null 2>&1; then
|
|
322
|
-
# Tag exists. Surface to user with three options:
|
|
323
|
-
# 1. Reuse — skip to 10a.2 if the existing tag points at the right commit.
|
|
324
|
-
# 2. Replace — `git push origin --delete $version_tag` then re-run 10a.1.
|
|
325
|
-
# 3. Investigate — call forge.releaseView({ tag: '$version_tag' }) — or
|
|
326
|
-
# `gh release view $version_tag` as a manual fallback — to see what shipped.
|
|
327
|
-
# Do NOT silently force-push.
|
|
328
|
-
echo "Tag $version_tag already exists on origin. Aborting with recovery options."
|
|
329
|
-
return 1
|
|
330
|
-
fi
|
|
331
|
-
|
|
332
|
-
# Tag the merge commit (HEAD on default branch after the prior `git pull`).
|
|
333
|
-
git tag "$version_tag"
|
|
334
|
-
git push origin "$version_tag" # Triggers .github/workflows/publish.yml
|
|
335
|
-
done
|
|
336
|
-
```
|
|
337
|
-
|
|
338
|
-
**Step 10a.2: Watch the publish workflow (release sessions only)**
|
|
339
|
-
|
|
340
|
-
For each project repo tagged in 10a.1, find and follow the `publish.yml` workflow run on GitHub. The workflow takes a moment to register against the new tag — poll up to 5 times with a 3-second backoff before giving up. Once the run is found, attach with `workflowRunWatch` so the maintainer sees progress live alongside the unified summary. The adapter's `exitStatus: true` makes the underlying `gh run watch --exit-status` exit non-zero on workflow failure; the adapter returns the exit code via `res.exitCode` instead of throwing, so a failure does **not** abort the rest of `/complete-work` — the maintainer still needs to see the unified summary, including the failure URL, to decide whether to rerun, redo the release, or roll the tag back. If no run registers within the retry window, log a warning with the manual investigation command and continue.
|
|
341
|
-
|
|
342
|
-
```javascript
|
|
343
|
-
import { createForge } from './.claude/scripts/forges/interface.mjs';
|
|
344
|
-
import { readFileSync } from 'node:fs';
|
|
345
|
-
|
|
346
|
-
const ws = JSON.parse(readFileSync('workspace.json', 'utf-8'));
|
|
347
|
-
const forge = createForge(ws.workspace?.forge);
|
|
348
|
-
|
|
349
|
-
// Retry up to 5 times with 3-second backoff — the run takes a moment to register.
|
|
350
|
-
let run = null;
|
|
351
|
-
for (let i = 0; i < 5; i++) {
|
|
352
|
-
run = await forge.workflowRunFind({
|
|
353
|
-
workflow: 'publish.yml',
|
|
354
|
-
branch: versionTag, // e.g. 'v0.15.0-beta.0'
|
|
355
|
-
repo: `${org}/${repo}`,
|
|
356
|
-
limit: 1,
|
|
357
|
-
});
|
|
358
|
-
if (run) break;
|
|
359
|
-
await new Promise(r => setTimeout(r, 3000));
|
|
360
|
-
}
|
|
361
|
-
|
|
362
|
-
if (!run) {
|
|
363
|
-
console.warn(`Warning: no publish workflow run found for ${versionTag} after 15s. Investigate via 'gh run list --workflow publish.yml --branch ${versionTag}'.`);
|
|
364
|
-
} else {
|
|
365
|
-
const result = await forge.workflowRunWatch({
|
|
366
|
-
runId: run.runId,
|
|
367
|
-
repo: `${org}/${repo}`,
|
|
368
|
-
exitStatus: true,
|
|
369
|
-
});
|
|
370
|
-
// result.exitCode === 0 on success; non-zero on workflow failure (NOT thrown).
|
|
371
|
-
// result.exitCode and run.url feed into the unified summary in Step 10a.3.
|
|
372
|
-
}
|
|
373
|
-
```
|
|
374
|
-
|
|
375
|
-
**Step 10a.3: Update the unified summary (release sessions only)**
|
|
376
|
-
|
|
377
|
-
The unified summary block presented earlier in Step 10a already has a section per project repo. For release sessions, append a `PUBLISH` section per tagged project repo to the same summary — this goes inside the existing summary, not in a new location, so the maintainer sees one consolidated report covering merges, tags, and npm publishes:
|
|
378
|
-
|
|
379
|
-
```
|
|
380
|
-
PUBLISH ({repo}):
|
|
381
|
-
Tag: v{version}
|
|
382
|
-
Workflow: {run-url}
|
|
383
|
-
Status: success | failure
|
|
384
|
-
Published: {dist-tag}@{version} on npm
|
|
385
|
-
```
|
|
386
|
-
|
|
387
|
-
Pull `Status` from the watch result's `exitCode` (success when `result.exitCode === 0`, failure otherwise). Pull `Workflow` from `run.url` captured in 10a.2. Pull `Published: {dist-tag}@{version}` from the workflow's published-package output if available; if the workflow failed before publishing, omit the `Published:` line and rely on `Status: failure` plus the workflow URL to point the maintainer at the failure.
|
|
388
|
-
|
|
389
|
-
#### Step 10b: Local / bare / other remotes — local merge flow
|
|
390
|
-
|
|
391
|
-
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:
|
|
392
|
-
|
|
393
|
-
```
|
|
394
|
-
Work session complete:
|
|
395
|
-
|
|
396
|
-
PROJECT: {repo-1} (local remote)
|
|
397
|
-
Branch: {branch} → {repo-1-branch}
|
|
398
|
-
Changes:
|
|
399
|
-
- {bullet points from release notes}
|
|
400
|
-
Release notes: branch-release-notes-{COMMIT_ID}.md
|
|
401
|
-
|
|
402
|
-
PROJECT: {repo-2} (local remote)
|
|
403
|
-
Branch: {branch} → {repo-2-branch}
|
|
404
|
-
Changes:
|
|
405
|
-
- {bullet points from release notes}
|
|
406
|
-
|
|
407
|
-
WORKSPACE: {workspace-name} (local remote)
|
|
408
|
-
Branch: {branch} → main
|
|
409
|
-
|
|
410
|
-
Merge all locally? [Y/n]
|
|
411
|
-
```
|
|
412
|
-
|
|
413
|
-
If yes — fast-forward merge on each remote, delete the feature branch, pull the source clone:
|
|
414
|
-
```bash
|
|
415
|
-
# For each repo in the tracker's repos with a local/bare remote:
|
|
416
|
-
cd work-sessions/{session-name}/workspace/repos/{repo}
|
|
417
|
-
git push origin HEAD:{repo-branch} # fast-forward the default branch
|
|
418
|
-
git push origin --delete {branch} # remove the feature branch from the remote
|
|
419
|
-
cd repos/{repo} && git checkout {repo-branch} && git pull origin {repo-branch}
|
|
420
|
-
|
|
421
|
-
# Workspace repo — same pattern from the workspace worktree
|
|
422
|
-
cd work-sessions/{session-name}/workspace
|
|
423
|
-
git push origin HEAD:main
|
|
424
|
-
git push origin --delete {branch}
|
|
425
|
-
cd {main-workspace-root} && git pull origin main
|
|
426
|
-
```
|
|
427
|
-
|
|
428
|
-
If the fast-forward push fails because the remote's default branch has moved ahead, STOP and present the divergence — the user decides whether to rebase and retry or handle it another way. Do not auto-resolve.
|
|
429
|
-
|
|
430
|
-
For repos with no remote at all (user chose "keep local"): skip push entirely. The branch lives only in the source clone after cleanup merges it:
|
|
431
|
-
```bash
|
|
432
|
-
cd repos/{repo} && git merge --ff-only {branch}
|
|
433
|
-
```
|
|
434
|
-
|
|
435
|
-
### Step 11: Close the linked issue on the tracker
|
|
436
|
-
|
|
437
|
-
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:
|
|
438
|
-
|
|
439
|
-
```javascript
|
|
440
|
-
import { createTracker } from './.claude/scripts/trackers/interface.mjs';
|
|
441
|
-
import { readFileSync } from 'node:fs';
|
|
442
|
-
const ws = JSON.parse(readFileSync('workspace.json', 'utf-8'));
|
|
443
|
-
if (ws.workspace?.tracker) {
|
|
444
|
-
const tracker = createTracker(ws.workspace.tracker);
|
|
445
|
-
const comment = [
|
|
446
|
-
`**Completed by @${currentUser}**`,
|
|
447
|
-
'',
|
|
448
|
-
'Merged PRs:',
|
|
449
|
-
...mergedPrs.map(p => `- ${p.repo}: ${p.url}`),
|
|
450
|
-
'',
|
|
451
|
-
releaseSummary, // 1-3 sentence synthesis of what shipped, drawn from release notes
|
|
452
|
-
].join('\n');
|
|
453
|
-
await tracker.closeIssue(workItem, { comment });
|
|
454
|
-
}
|
|
455
|
-
```
|
|
456
|
-
|
|
457
|
-
If `workItem:` is unset, skip the close — this was a blank session.
|
|
458
|
-
|
|
459
|
-
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.
|
|
460
|
-
|
|
461
|
-
### Step 12: Cleanup
|
|
462
|
-
|
|
463
|
-
Run the cleanup helper script from the main workspace root:
|
|
464
|
-
```bash
|
|
465
|
-
node .claude/scripts/cleanup-work-session.mjs --session-name "{session-name}"
|
|
466
|
-
```
|
|
467
|
-
|
|
468
|
-
The script tears down in the **mandatory** order:
|
|
469
|
-
1. Remove each nested project worktree from its project repo
|
|
470
|
-
2. Remove the workspace worktree from the workspace repo
|
|
471
|
-
3. `git worktree prune` on each project repo (belt-and-suspenders for orphan records)
|
|
472
|
-
4. Delete local branches in all repos
|
|
473
|
-
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.
|
|
474
|
-
|
|
475
|
-
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.
|
|
476
|
-
|
|
477
|
-
Verify workspace root is still on main:
|
|
478
|
-
```bash
|
|
479
|
-
git branch --show-current # should be "main"
|
|
480
|
-
```
|
|
481
|
-
|
|
482
|
-
## Handling Unformal Work Sessions
|
|
483
|
-
|
|
484
|
-
If /complete-work is called but changes were made without a formal work session (no branch, changes on default branch):
|
|
485
|
-
|
|
486
|
-
Ask: "These changes weren't part of a formal work session. What do you want to do?"
|
|
487
|
-
- **Accept as work** — create a session retroactively, proceed with normal completion
|
|
488
|
-
- **Stash for later** — create a user-scoped handoff describing what was done, stash the changes
|
|
489
|
-
- **Hand off to someone** — create a team-visible handoff at root workspace-context/ for another member to pick up
|
|
490
|
-
- **Revert** — undo the changes (with confirmation)
|
|
491
|
-
|
|
492
|
-
## Notes
|
|
493
|
-
- Branch release notes live in the WORKSPACE repo at `{releaseNotesDir}/unreleased/{repo-name}/` (resolved from `workspace.json` → `workspace.releaseNotesDir`, default `workspace-context/release-notes`) — never in project repos. Project repos only ever see code commits and (at release time) `CHANGELOG.md` entries written by `/release`.
|
|
494
|
-
- The session tracker's body is the primary source for release note synthesis — it captures the full session history alongside specs and plans
|
|
495
|
-
- All repos get PRed and merged together — one approval for all
|
|
496
|
-
- Version bumps happen in `/release`, not `/complete-work` — this avoids version drift when multiple feature branches land between releases
|
|
497
|
-
- The teardown order is mandatory: project worktrees first, then workspace worktree, then prune, then delete the session folder
|
|
498
|
-
- Context consumption, cleanup, and auto-committing release notes are intentional workflow behavior — these bypass normal commit conventions by design
|
|
@@ -1,151 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: release
|
|
3
|
-
description: Prepend a new CHANGELOG.md entry per project repo by synthesizing unreleased branch notes. Deletes consumed branch notes and synthesizes workspace-context into canonical (locked) entries. Use at release time.
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# Release
|
|
7
|
-
|
|
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-context into canonical (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.
|
|
15
|
-
|
|
16
|
-
## Parameters
|
|
17
|
-
- `/release {version}` — create a release entry for a specific version
|
|
18
|
-
- `/release` — ask for the version
|
|
19
|
-
|
|
20
|
-
## Flow
|
|
21
|
-
|
|
22
|
-
**Step 1: Determine version and repo**
|
|
23
|
-
If no version parameter: ask "What version is this release? (e.g., 1.2.0)"
|
|
24
|
-
|
|
25
|
-
Check `workspace.json` for `releaseMode`:
|
|
26
|
-
- **per-repo** (default): ask which repo to release
|
|
27
|
-
- **workspace**: process all repos together
|
|
28
|
-
- **ask**: "Process all repos together or individually?"
|
|
29
|
-
|
|
30
|
-
**Release-notes base directory.** Read `workspace.releaseNotesDir` from `workspace.json` (default `workspace-context/release-notes` if the field is absent). Throughout this skill `{releaseNotesDir}` refers to that resolved value; every branch-note path is `{releaseNotesDir}/unreleased/{repo}/…`. Never use a bare `release-notes/`.
|
|
31
|
-
|
|
32
|
-
**Step 2: Read unreleased notes**
|
|
33
|
-
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:
|
|
34
|
-
```bash
|
|
35
|
-
ls {releaseNotesDir}/unreleased/{repo}/
|
|
36
|
-
```
|
|
37
|
-
Read all `branch-release-notes-*.md` and `branch-release-questions-*.md` files.
|
|
38
|
-
|
|
39
|
-
If no unreleased files exist for a target repo: "No unreleased notes found for {repo}. Nothing to release."
|
|
40
|
-
|
|
41
|
-
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.
|
|
42
|
-
|
|
43
|
-
**Step 3: Group and organize**
|
|
44
|
-
Group notes by `type:` frontmatter (feature, fix, chore). Within each group, order chronologically by date. This ordering drives bullet sequence in the synthesized entry.
|
|
45
|
-
|
|
46
|
-
**Step 4: Handle questions**
|
|
47
|
-
Present all open questions from `branch-release-questions-*.md` files:
|
|
48
|
-
"These questions are still open from development. For each one:"
|
|
49
|
-
- **Answer** — provide the answer, remove from questions
|
|
50
|
-
- **Defer** — keep as a "Known issues" sub-bullet in the CHANGELOG entry
|
|
51
|
-
- **Discard** — no longer relevant
|
|
52
|
-
|
|
53
|
-
**Step 5: Synthesize the CHANGELOG entry**
|
|
54
|
-
|
|
55
|
-
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
|
-
|
|
57
|
-
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:
|
|
58
|
-
|
|
59
|
-
```markdown
|
|
60
|
-
## v{version} — {YYYY-MM-DD}
|
|
61
|
-
|
|
62
|
-
- {Concise bullet per meaningful change. Features, fixes, and chores interleaved
|
|
63
|
-
by significance, not by category. Each bullet is one sentence or short paragraph
|
|
64
|
-
in plain user-facing language: "the CLI now supports X", "corrected Y behavior
|
|
65
|
-
on Z", not "we decided" or "the team merged." Deduplicate related items.
|
|
66
|
-
Write from scratch per the coherent-revisions rule.}
|
|
67
|
-
|
|
68
|
-
### Known issues
|
|
69
|
-
- {Deferred questions from Step 4, if any. Omit this subsection when empty.}
|
|
70
|
-
```
|
|
71
|
-
|
|
72
|
-
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.
|
|
73
|
-
|
|
74
|
-
**Step 6: Delete consumed branch notes from the workspace**
|
|
75
|
-
```bash
|
|
76
|
-
rm {releaseNotesDir}/unreleased/{repo}/branch-release-*
|
|
77
|
-
# If the directory is now empty, remove it too:
|
|
78
|
-
rmdir {releaseNotesDir}/unreleased/{repo} 2>/dev/null || true
|
|
79
|
-
```
|
|
80
|
-
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.
|
|
81
|
-
|
|
82
|
-
**Step 7: Commit the CHANGELOG entry to the project repo**
|
|
83
|
-
```bash
|
|
84
|
-
cd repos/{repo}
|
|
85
|
-
git add CHANGELOG.md
|
|
86
|
-
git commit -m "docs: v{version} changelog entry"
|
|
87
|
-
```
|
|
88
|
-
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.
|
|
89
|
-
|
|
90
|
-
**Step 7b: Bump package.json version (project repo)**
|
|
91
|
-
If the project repo has a `package.json` with a `version` field, update it to match the release version:
|
|
92
|
-
```bash
|
|
93
|
-
cd repos/{repo}
|
|
94
|
-
# Update "version": "..." in package.json to the release version
|
|
95
|
-
git add package.json
|
|
96
|
-
git commit -m "chore: bump version to v{version}"
|
|
97
|
-
```
|
|
98
|
-
Skip this step if the repo has no package.json or no version field.
|
|
99
|
-
|
|
100
|
-
**Step 7c: Commit the consumed-notes deletion in the workspace**
|
|
101
|
-
```bash
|
|
102
|
-
# From the workspace root
|
|
103
|
-
git add {releaseNotesDir}/unreleased/
|
|
104
|
-
git commit -m "release: consume {repo} branch notes for v{version}"
|
|
105
|
-
```
|
|
106
|
-
Workspace and project repos have separate commits — they are separate git histories.
|
|
107
|
-
|
|
108
|
-
**Step 8: Consume project-scoped specs**
|
|
109
|
-
Project-scoped specs and plans in `workspace-context/team-member/{user}/` (ongoing) that are fully covered by this release:
|
|
110
|
-
- Consume into the CHANGELOG entry (their content is now captured there)
|
|
111
|
-
- Remove the source files
|
|
112
|
-
- If partially covered: rewrite the spec to reflect only what remains unimplemented
|
|
113
|
-
|
|
114
|
-
**Step 9: Synthesize workspace-context for canonical promotion**
|
|
115
|
-
Process ephemeral workspace-context entries:
|
|
116
|
-
|
|
117
|
-
1. List all ephemeral entries with `lifecycle: resolved` (across `shared/` and any `team-member/{user}/`).
|
|
118
|
-
2. For each, determine:
|
|
119
|
-
- Does an existing locked entry cover this topic? → Merge into it (enrich)
|
|
120
|
-
- Are there related resolved entries? → Combine into a new locked entry
|
|
121
|
-
- Is it stale/fully consumed by release notes? → Archive or delete
|
|
122
|
-
- Is it unresolvable but still valuable? → Move to `team-member/{user}/` ongoing or keep at `shared/` root ephemeral
|
|
123
|
-
3. For merged/new locked entries:
|
|
124
|
-
- Set `state: locked`, `type: synthesized` (or `type: reference` for clean truths)
|
|
125
|
-
- Write to `workspace-context/shared/locked/{bare-name}.md` — locked files use bare names (location signals the type), so strip any `braindump_/handoff_/research_` prefix when promoting
|
|
126
|
-
- Write concise, focused content — team truths, not session history
|
|
127
|
-
4. Regenerate auto-files so `canonical.md` and `index.md` reflect the new locked content:
|
|
128
|
-
```bash
|
|
129
|
-
node .claude/scripts/build-workspace-context.mjs --write --root .
|
|
130
|
-
```
|
|
131
|
-
5. Commit:
|
|
132
|
-
```bash
|
|
133
|
-
git add workspace-context/
|
|
134
|
-
git commit -m "release: synthesize workspace-context for v{version}"
|
|
135
|
-
```
|
|
136
|
-
|
|
137
|
-
**Step 10: Report**
|
|
138
|
-
"Release v{version} complete for {repo}. {N} branch notes consumed into CHANGELOG.md. {M} context entries synthesized into {K} locked entries."
|
|
139
|
-
|
|
140
|
-
## Notes
|
|
141
|
-
|
|
142
|
-
- 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/`.
|
|
143
|
-
- Branch notes live in the WORKSPACE at `{releaseNotesDir}/unreleased/{repo}/` (resolved from `workspace.json` → `workspace.releaseNotesDir`, default `workspace-context/release-notes`). `/complete-work` writes them; `/release` consumes and deletes them. They never reach project repos.
|
|
144
|
-
- Versions are bumped here, not in `/complete-work`. This keeps the version semantics aligned with what actually shipped (accumulated changes since last release).
|
|
145
|
-
- 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.
|
|
146
|
-
- Context synthesis happens in the WORKSPACE repo — Step 7c (consumed-notes) and Step 9 (workspace-context synthesis) are separate workspace commits.
|
|
147
|
-
- Per-repo is the default — each project repo has its own release cadence.
|
|
148
|
-
- The coherent-revisions rule applies: write the CHANGELOG entry from scratch, don't concatenate branch notes.
|
|
149
|
-
- Tagging happens in `/complete-work`, not here. When the session branch starts with `release/`, `/complete-work` tags the merge commit on the project repo's default branch and pushes the tag, which triggers `.github/workflows/publish.yml` to publish to npm. `/release` produces the synthesis (CHANGELOG entry + version bump + consumed-notes deletion); `/complete-work` does the push, PR, merge, and tag.
|
|
150
|
-
- Do not run `npm publish` locally. The publish workflow is the only path that exercises OIDC trusted publishing — local publish requires 2FA OTP and bypasses that. If the workflow fails, investigate via `gh run view`; do not fall back to local publish.
|
|
151
|
-
- Recovery from a failed publish. Transient failure: rerun via `gh run rerun {run_id}`. Content failure: delete the tag (`git push origin --delete v{version} && git tag -d v{version}`), then redo the release in a new release session — `/start-work`, then `/release v{version}`, then `/complete-work`. Once a version is published to npm, that version is committed on the registry; bump and start a new release.
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|