@ulysses-ai/create-workspace 0.16.0-beta.1 → 0.18.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 +5 -5
- package/lib/init.mjs +19 -0
- package/package.json +1 -1
- package/template/.claude/hooks/_utils.mjs +1 -1
- package/template/.claude/hooks/repo-write-detection.mjs +161 -64
- package/template/.claude/hooks/session-end.mjs +68 -2
- package/template/.claude/hooks/session-start.mjs +35 -1
- package/template/.claude/hooks/subagent-start.mjs +89 -22
- package/template/.claude/lib/session-frontmatter.mjs +28 -0
- package/template/.claude/rules/coherent-revisions.md +1 -1
- package/template/.claude/rules/config-review.md.skip +29 -0
- package/template/.claude/rules/forge-operations.md +51 -0
- package/template/.claude/rules/git-conventions.md +16 -11
- package/template/.claude/rules/goal-driven-work.md +8 -403
- package/template/.claude/rules/honest-pushback.md +37 -37
- package/template/.claude/rules/memory-guidance.md +43 -90
- package/template/.claude/rules/superpowers-workflow.md.skip +1 -1
- package/template/.claude/rules/work-item-tracking.md +30 -72
- package/template/.claude/rules/workspace-structure.md +49 -69
- package/template/.claude/scripts/build-workspace-context.mjs +61 -16
- package/template/.claude/scripts/chat-record.mjs +282 -0
- package/template/.claude/scripts/cleanup-work-session.mjs +363 -36
- package/template/.claude/scripts/context-footprint.mjs +282 -0
- package/template/.claude/scripts/forges/github.mjs +255 -0
- package/template/.claude/scripts/forges/gitlab.mjs +20 -0
- package/template/.claude/scripts/forges/interface.mjs +125 -0
- package/template/.claude/scripts/generate-claude-local.mjs +21 -2
- package/template/.claude/scripts/migrate-sessions.mjs +1571 -0
- package/template/.claude/scripts/migrate-to-workspace-context.mjs +7 -2
- package/template/.claude/scripts/task-worktree.mjs +525 -0
- package/template/.claude/scripts/workspace-diagnostics.mjs +654 -0
- package/template/.claude/settings.json +5 -13
- package/template/.claude/skills/braindump/SKILL.md +11 -4
- package/template/.claude/skills/build-docs-site/SKILL.md +5 -5
- package/template/.claude/skills/build-docs-site/templates/spec.md.tmpl +1 -1
- package/template/.claude/skills/complete-work/SKILL.md +255 -215
- package/template/.claude/skills/context-placement/SKILL.md +199 -0
- package/template/.claude/skills/goal-driven-work/SKILL.md +459 -0
- package/template/.claude/skills/handoff/SKILL.md +11 -4
- package/template/.claude/skills/maintenance/SKILL.md +39 -6
- package/template/.claude/skills/migrate-sessions/SKILL.md +70 -0
- package/template/.claude/skills/pause-work/SKILL.md +33 -8
- package/template/.claude/skills/release/SKILL.md +44 -108
- package/template/.claude/skills/start-work/SKILL.md +89 -7
- package/template/.claude/skills/workspace-init/SKILL.md +34 -0
- package/template/.claude/skills/workspace-update/SKILL.md +4 -0
- package/template/.claudeignore +3 -0
- package/template/CLAUDE.md.tmpl +20 -2
- package/template/CODEBASE.md.tmpl +13 -0
- package/template/_gitignore +9 -0
- package/template/repo-claude.md.tmpl +10 -0
- package/template/workspace.json.tmpl +5 -3
- package/template/.claude/hooks/worktree-create.mjs +0 -53
|
@@ -11,7 +11,7 @@ Save structured workstream state to workspace-context. Usable anytime, any numbe
|
|
|
11
11
|
- `/handoff {name}` — create or update a named handoff
|
|
12
12
|
- `/handoff` (no param) — analyze session and suggest name(s)
|
|
13
13
|
|
|
14
|
-
##
|
|
14
|
+
## Lifecycle-Aware Behavior
|
|
15
15
|
|
|
16
16
|
When called within an active work session (the active-session pointer at `.claude/.active-session.json` exists inside the current worktree):
|
|
17
17
|
|
|
@@ -26,9 +26,16 @@ When called within an active work session (the active-session pointer at `.claud
|
|
|
26
26
|
git commit -m "handoff: update {session-name} tracker"
|
|
27
27
|
```
|
|
28
28
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
-
|
|
29
|
+
Under the task model — `workspace.sessionModel` is `"task"` in `workspace.json` AND the SessionStart hook injected a `Chat record:` line (`{chat}` is its name):
|
|
30
|
+
|
|
31
|
+
- Default behavior: write `handoff_{topic}.md` directly into that chat's drawer at `workspace-scratchpad/chats/{chat}/` — the drawer sits outside `workspace-context/`, so `capture-context.mjs` is not involved
|
|
32
|
+
- No commit for drawer writes: the drawer is gitignored and machine-local; `/complete-work` lists it and asks what to promote into `workspace-context/`
|
|
33
|
+
|
|
34
|
+
When called from the workspace root with no active session — every other case, including a `sessionModel: "session"` workspace (the `Chat record:` line is injected in every chat, so it alone does not select the drawer):
|
|
35
|
+
|
|
36
|
+
- Use `--local-only` so the captured file is gitignored (the root only allows local-only writes), landing in `team-member/{user}/`
|
|
37
|
+
- If the task model applies but the `Chat record:` line is absent, say the drawer destination is unavailable for that reason
|
|
38
|
+
- Suggest starting work (`/start-work`) first if the handoff is about actionable work
|
|
32
39
|
|
|
33
40
|
The flows below apply when NOT in an active work session, or when the user explicitly asks for a standalone handoff file.
|
|
34
41
|
|
|
@@ -44,6 +44,10 @@ For each workspace-context `.md` file and each `work-sessions/*/workspace/sessio
|
|
|
44
44
|
- Worktrees whose branch has already been merged? (cleanup candidates)
|
|
45
45
|
- Workspace repo on expected branch?
|
|
46
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.
|
|
47
|
+
- Task-model state (gh:146), three checks:
|
|
48
|
+
- **Unrecorded task worktrees** — list `repos/*/.claude/worktrees/*` and `.claude/worktrees/*`, read each candidate's branch (`git -C "{path}" rev-parse --abbrev-ref HEAD`), and keep only those on a task-prefixed branch (`feature/`, `bugfix/`, `chore/`) — Claude Code's own worktrees carry other branch names, so the prefix filter skips them without guessing a name convention. Cross-reference the chat records (`node .claude/scripts/chat-record.mjs --root . --list`): a task-prefixed worktree no record entry claims is *unrecorded* — it may be a legitimate no-tracker task (those are never recorded), so present it and ask before suggesting `node .claude/scripts/task-worktree.mjs --root . --remove --repo "{repo}" --branch "{branch}"`.
|
|
49
|
+
- **Stale record entries** — a record task entry whose worktree is gone (neither `repos/{repo}/.claude/worktrees/{slug}/` nor, for `repo: "."`, `.claude/worktrees/{slug}/` exists). Suggest `node .claude/scripts/chat-record.mjs --root . --remove-task --chat "{chat}" --work-item "{workItem}" --repo "{repo}"` (omit `--repo` when the entry has none).
|
|
50
|
+
- **Merged but never completed** — a recorded task branch that already merged. Judge merged-ness by the forge's merged PRs for that repo, matching on head branch — never `git branch --merged`, which a squash merge (never an ancestor) silently misses. `/complete-work` never ran. Suggest running `/complete-work` for that branch (detection from the chat record finds it).
|
|
47
51
|
|
|
48
52
|
### 5. Workspace-context auto-file integrity
|
|
49
53
|
|
|
@@ -108,22 +112,39 @@ Report one of:
|
|
|
108
112
|
|
|
109
113
|
Active recommendations. Flags problems and suggests fixes, but asks before acting.
|
|
110
114
|
|
|
111
|
-
### 7.
|
|
115
|
+
### 7. Component age check
|
|
116
|
+
|
|
117
|
+
Scan the following file sets for a YAML frontmatter `updated:` field:
|
|
118
|
+
- `.claude/rules/*.md` (active rules only — `.md.skip` files are included too, since the rule content can still drift)
|
|
119
|
+
- `.claude/skills/*/SKILL.md`
|
|
120
|
+
- `.claude/agents/*.md`
|
|
121
|
+
- `.claude/hooks/*.mjs`
|
|
122
|
+
|
|
123
|
+
For each file that has an `updated:` field, compute the age in days from today. If the age exceeds 180 days, flag the file as a stale component candidate. Print the file name, the `updated:` date, and the age in days so the contributor knows how far the file has drifted.
|
|
124
|
+
|
|
125
|
+
Files without an `updated:` field are skipped — the check is opt-in and activates the discipline incrementally as contributors add frontmatter to the files they own. To start tracking a file, add `updated: <today>` to its frontmatter; the check will surface it if it goes stale.
|
|
126
|
+
|
|
127
|
+
When stale candidates are found, surface them as warnings in the output format and link to `config-review.md.skip` (in `.claude/rules/`) as the opt-in rule that documents the review cadence and rationale.
|
|
128
|
+
|
|
129
|
+
### 8. Stale context
|
|
112
130
|
- Ephemeral files not updated in 7+ days — suggest resolve, update, or archive
|
|
113
131
|
- `work-sessions/{name}/` folders whose worktrees are gone — suggest cleanup
|
|
114
132
|
- Session trackers whose branches have been merged — suggest `/complete-work` post-flight cleanup
|
|
133
|
+
- Unrecorded task-prefixed worktrees (no chat-record entry claims them; may be no-tracker tasks) — ask, then suggest `task-worktree.mjs --remove`
|
|
134
|
+
- Chat-record task entries whose worktree is gone — suggest `chat-record.mjs --remove-task`
|
|
135
|
+
- Recorded task branches already merged (per the forge's merged PRs, not `git branch --merged`) — suggest `/complete-work`
|
|
115
136
|
- Braindumps that overlap significantly — suggest merging (e.g., "workspace-branching.md and persistent-work-sessions.md cover the same topic")
|
|
116
137
|
- Handoffs referencing deleted branches — suggest resolve or remove
|
|
117
138
|
|
|
118
|
-
###
|
|
139
|
+
### 9. Context reconciliation
|
|
119
140
|
- Read recent workspace-context writes (last session or last N files by updated date)
|
|
120
141
|
- For each, scan other workspace-context files for references that are now stale
|
|
121
142
|
- Surface: "{file} says X but {newer-file} now says Y. Update {file}?"
|
|
122
143
|
- This is the capture-time cross-check, run retroactively instead of inline
|
|
123
144
|
|
|
124
|
-
###
|
|
145
|
+
### 10. Canonical budget triage
|
|
125
146
|
|
|
126
|
-
This step runs only when the post-regen `--check` from step
|
|
147
|
+
This step runs only when the post-regen `--check` from step 9 still reports `selectionStatus: 'over-budget'`. If the regular regen pass cleared the budget — or if `--check` was already `ok`, `trimmed`, or `stubbed` after step 9 — skip this step entirely.
|
|
127
148
|
|
|
128
149
|
The rest of cleanup is suggestion-list-with-confirmation: surface a candidate, ask before applying, move on. Triage is the one meaningfully more interactive surface in `/maintenance`. It runs as a small REPL: present the budget state and a triage menu, take one action, re-run `--check`, present the menu again with the new state. No suggestion is auto-applied; every action is the user's choice.
|
|
129
150
|
|
|
@@ -168,7 +189,19 @@ For each chosen action:
|
|
|
168
189
|
|
|
169
190
|
Trim markers and demotions only matter for `priority: reference` files — `<!-- canonical:trim -->` spans on a `priority: critical` file are inert until the file is demoted. The triage flow never auto-decides which file to demote or which section to wrap; it surfaces the data, presents options, and waits.
|
|
170
191
|
|
|
171
|
-
###
|
|
192
|
+
### 11. Forge configuration
|
|
193
|
+
|
|
194
|
+
Read `workspace.json`. If `workspace.tracker?.type === 'github-issues'` and `workspace.forge` is unset, emit a notice (not an error):
|
|
195
|
+
|
|
196
|
+
```
|
|
197
|
+
ℹ workspace.json has tracker.type='github-issues' but no workspace.forge field.
|
|
198
|
+
Skills default to GitHub forge operations; add `"forge": {"type": "github"}`
|
|
199
|
+
to workspace.json to make the choice explicit. See .claude/rules/forge-operations.md.
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
This is migration guidance for workspaces created before the `forge` field landed — the field is back-compat with a sensible default, so the unset case is not a bug, just an opportunity to make the implicit explicit. If `workspace.forge.type` is set to a value with no adapter at `.claude/scripts/forges/{type}.mjs`, that IS an error and goes in the Issues section.
|
|
203
|
+
|
|
204
|
+
### 12. Health metrics
|
|
172
205
|
- Canonical budget — read from the same `--check` invocation as step 5. Reported as `current / budget` bytes with the selection status (e.g., `full`, `2 reference files trimmed`). Over-budget cases are deferred to the cleanup triage flow rather than re-reported here.
|
|
173
206
|
- Number of ephemeral files — flag if accumulating without resolution
|
|
174
207
|
- Session log stats (if `workspace-scratchpad/session-log.jsonl` exists):
|
|
@@ -215,7 +248,7 @@ OK (5):
|
|
|
215
248
|
5. Check git state (worktrees, branches, remotes)
|
|
216
249
|
6. Run `node .claude/scripts/build-workspace-context.mjs --check --root .` — capture status. Exit `0` = clean and within budget, `1` = artifact missing or stale, `2` = artifacts current but canonical body over budget. The `canonical` block in the JSON output drives both the audit budget line and the cleanup triage decision.
|
|
217
250
|
7. Read session-log.jsonl if it exists
|
|
218
|
-
8. If cleanup mode: regenerate the workspace-context auto-files if stale (index.md, canonical.md, per-user team-member indexes); compare files pairwise for overlap; scan for stale cross-references. If post-regen `--check` reports `over-budget`, enter the canonical-budget triage flow described in cleanup step
|
|
251
|
+
8. If cleanup mode: regenerate the workspace-context auto-files if stale (index.md, canonical.md, per-user team-member indexes); compare files pairwise for overlap; scan for stale cross-references. If post-regen `--check` reports `over-budget`, enter the canonical-budget triage flow described in cleanup step 10.
|
|
219
252
|
9. Compile and present findings grouped by severity
|
|
220
253
|
|
|
221
254
|
## Notes
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: migrate-sessions
|
|
3
|
+
description: Migrate this workspace from the session lifecycle to the task lifecycle — inventory old work sessions, decide each one with the operator, finish or archive them, and switch workspace.json to the task model. Runs only inside the current workspace; never deletes anything.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Migrate Sessions
|
|
7
|
+
|
|
8
|
+
Drain a workspace's accumulated session entries and switch new work to the task lifecycle. The script is `.claude/scripts/migrate-sessions.mjs`; this skill is the operator procedure around it.
|
|
9
|
+
|
|
10
|
+
**Scope rule, before anything else: this skill acts only on the workspace it is run in.** Never read, inventory, or act on any other workspace or directory — even if asked to "do them all." Each workspace runs its own migration from its own root, by its own operator, on its own schedule.
|
|
11
|
+
|
|
12
|
+
**Run from the launcher root only** — the workspace root itself, never a session folder or any other worktree. The script refuses a linked-worktree `--root` on its own (one exception, the Switch step below), and it refuses to back up or archive the session that hosts the current chat, so there is no way to drain the session you are sitting in from inside it.
|
|
13
|
+
|
|
14
|
+
## 1. Inventory
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
node .claude/scripts/migrate-sessions.mjs --inventory
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Read-only. Present the stderr table plus each session's proposal with its reasons and warnings. Say plainly that the proposals are proposals — evidence and a starting point, not decisions. Pay particular attention to the per-remote state shown per worktree (`same`, `ahead +N`, `behind -N`, `diverged +N/-M`, `not-fetched`, `unknown`) and to `unbacked` warnings: they change what Finish and Archive mean for that session. Entries shown as `foreign` (symlinked) are never acted on — surface them for manual reconciliation.
|
|
21
|
+
|
|
22
|
+
## 2. Decide per session, with the operator — one at a time
|
|
23
|
+
|
|
24
|
+
For each session, lay out its evidence and ask the operator which way to go. Never infer the decision from the proposal. The options:
|
|
25
|
+
|
|
26
|
+
- **Finish** (typical for MERGEABLE) — resume the session with `/start-work`, then run `/complete-work`; its own merge confirmation applies there. But if the inventory shows a **diverged** remote for that session, say so *before* the operator chooses Finish: `/complete-work`'s plain push will be rejected, and pushing the rewritten history needs `--force-with-lease` — which you run only on the operator's explicit yes naming the branch. Never force silently.
|
|
27
|
+
- **Archive** (typical for ABANDONED, a broken shell, or a MERGEABLE the operator gives up on) — take it out of the active lifecycle without destroying anything. Two steps, each its own decision:
|
|
28
|
+
1. **Offer a backup.** Archiving keeps everything on this machine; a backup adds an off-machine copy of the session's commits, and it is what makes a later deletion safe. It creates `drain/{session}/…` tags and pushes them to the resolved remote(s) — tags may land in a public repository, so show the plan first: `node .claude/scripts/migrate-sessions.mjs --backup --session {name} --dry-run` (add `--remote <name>` to aim somewhere other than the resolved default). It lists, per tip, the tag, the remote, and which tips a remote branch or tag already holds exactly, with no side effects. On the operator's yes, run it without `--dry-run` and show the tags it created. Declining is fine — the archive still keeps everything locally. A repo with no remote at all is refused by backup; say so.
|
|
29
|
+
2. **Archive after an explicit yes naming the session:** `node .claude/scripts/migrate-sessions.mjs --archive --session {name}`. The whole session folder moves to `{sessions}/.archived/{name}--{timestamp}/` and git's worktree links are repaired to follow it — every commit, uncommitted edit, untracked or ignored file, and embedded repository comes along. If the move or the repair fails, the session is put back and the result says whether every link was verified. The session's branches stay checked out in the archived worktrees, so a new task cannot reuse those branch names until the archive is deleted. The archive refuses, touching nothing, when a directory in the folder cannot be read, when the folder holds a worktree of a repository outside this workspace, or when it holds a submodule checkout (its link cannot be repaired) — surface the reason; for a submodule the options are Finish or Keep. Relay any `warnings` (relative symlinks that pointed outside the session no longer resolve after the move).
|
|
30
|
+
- **Keep** (typical for ACTIVE, and the only sane answer for UNKNOWN) — leave it; it completes later under the session lifecycle.
|
|
31
|
+
|
|
32
|
+
Unbacked commits (present on no remote) are safe in an archive — they are only ever at risk when someone deletes one. Say so when you archive such a session, and offer the backup.
|
|
33
|
+
|
|
34
|
+
## 3. Switch — never write the launcher's tracked `workspace.json` directly
|
|
35
|
+
|
|
36
|
+
The switch procedure:
|
|
37
|
+
|
|
38
|
+
1. Create a workspace task worktree for the change:
|
|
39
|
+
```bash
|
|
40
|
+
node .claude/scripts/task-worktree.mjs --root . --create --repo . --branch chore/enable-task-model
|
|
41
|
+
```
|
|
42
|
+
2. Run the switch against that worktree (the one mode that accepts a linked-worktree root — it only edits `workspace.json`):
|
|
43
|
+
```bash
|
|
44
|
+
node .claude/scripts/migrate-sessions.mjs --enable-task-model --root .claude/worktrees/chore-enable-task-model
|
|
45
|
+
```
|
|
46
|
+
3. Commit there, open a PR through the workspace's normal flow, and pull the launcher after merge. The launcher root never commits to its default branch.
|
|
47
|
+
|
|
48
|
+
The switch output reports `remainingSessions: null` when run from the worktree — the real remaining-sessions list comes from a separate `--inventory` at the launcher root. Remaining sessions are fine either way: they keep resuming and completing under the session lifecycle after the switch.
|
|
49
|
+
|
|
50
|
+
## 4. Verify
|
|
51
|
+
|
|
52
|
+
Re-run `--inventory` at the launcher root and report what remains and why — kept sessions, anything the operator deferred, foreign entries, or an empty list.
|
|
53
|
+
|
|
54
|
+
## 5. Afterwards
|
|
55
|
+
|
|
56
|
+
The first new piece of work starts with `/start-work` under the task lifecycle.
|
|
57
|
+
|
|
58
|
+
## Deleting an archive — the operator's call, never this skill's
|
|
59
|
+
|
|
60
|
+
Archives are meant to be kept until the operator has looked at them. When the operator asks to delete one, first show them — for the whole archive, not just its top level — everything that deletion would destroy, per worktree (list them with `git -C {repo} worktree list --porcelain` in the workspace repo and each `repos/{name}`, filtered to paths under the archive):
|
|
61
|
+
|
|
62
|
+
- commits no remote holds: `git -C {worktree} log --oneline --branches --not --remotes` and whether a `drain/*` tag covers the tip;
|
|
63
|
+
- uncommitted, untracked and ignored files: `git -C {worktree} status --porcelain --ignored`;
|
|
64
|
+
- edits hidden from status: `git -C {worktree} ls-files -v` — any lowercase tag (assume-unchanged) or `S` (skip-worktree) whose file differs from the index;
|
|
65
|
+
- per-worktree refs that die with the worktree: `git -C {worktree} for-each-ref refs/worktree refs/bisect`;
|
|
66
|
+
- an operation in progress: `MERGE_HEAD`, `rebase-merge`, `rebase-apply` under `git -C {worktree} rev-parse --git-dir`;
|
|
67
|
+
- embedded repositories (any `.git` directory under the archive) and their unpushed branches;
|
|
68
|
+
- files in the archive outside the worktrees (e.g. beside `workspace/`).
|
|
69
|
+
|
|
70
|
+
Only on their explicit yes naming the archive: remove the worktrees deepest-first (`git -C {repo} worktree remove --force {path}`), delete each branch they confirm (`git -C {repo} branch -D {branch}`), then delete the archive folder. Nothing in this workspace does this automatically.
|
|
@@ -28,7 +28,15 @@ This is a coherent rewrite of the Progress section, not an append (coherent-revi
|
|
|
28
28
|
|
|
29
29
|
### Step 3: Update frontmatter status and post pause comment on tracker
|
|
30
30
|
|
|
31
|
-
|
|
31
|
+
Set `status: paused` in the tracker's frontmatter. `.claude/lib/session-frontmatter.mjs` is
|
|
32
|
+
a library, not a CLI — running it with flags does nothing and exits 2:
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
node --input-type=module -e '
|
|
36
|
+
import { updateSessionFile } from "./.claude/lib/session-frontmatter.mjs";
|
|
37
|
+
updateSessionFile("session.md", { status: "paused" });
|
|
38
|
+
'
|
|
39
|
+
```
|
|
32
40
|
|
|
33
41
|
If the session tracker has a `workItem:` field AND `workspace.tracker` is configured, post a pause comment on the linked issue via the adapter:
|
|
34
42
|
|
|
@@ -95,16 +103,33 @@ git push -u origin {branch}
|
|
|
95
103
|
|
|
96
104
|
### Step 7: Create draft PRs
|
|
97
105
|
|
|
98
|
-
|
|
99
|
-
# For each repo in the tracker's repos:
|
|
100
|
-
cd work-sessions/{session-name}/workspace/repos/{repo}
|
|
101
|
-
gh pr create --draft --title "WIP: {description}" --body "Work in progress. Session paused."
|
|
106
|
+
PR creation goes through the forge adapter (`.claude/scripts/forges/interface.mjs`), not directly through `gh` — see `.claude/rules/forge-operations.md` for the contract and why. The adapter resolves the target repo from `workspace.forge.repo` or the local git remote.
|
|
102
107
|
|
|
103
|
-
|
|
104
|
-
|
|
108
|
+
```javascript
|
|
109
|
+
import { createForge } from './.claude/scripts/forges/interface.mjs';
|
|
110
|
+
import { readFileSync } from 'node:fs';
|
|
111
|
+
|
|
112
|
+
const ws = JSON.parse(readFileSync('workspace.json', 'utf-8'));
|
|
113
|
+
const forge = createForge(ws.workspace?.forge);
|
|
114
|
+
|
|
115
|
+
// For each repo in the tracker's repos, from work-sessions/{session-name}/workspace/repos/{repo}:
|
|
116
|
+
const projectPr = await forge.prCreate({
|
|
117
|
+
title: `WIP: ${description}`,
|
|
118
|
+
body: 'Work in progress. Session paused.',
|
|
119
|
+
draft: true,
|
|
120
|
+
});
|
|
121
|
+
console.log(projectPr.url);
|
|
122
|
+
|
|
123
|
+
// Workspace repo — from the workspace worktree:
|
|
124
|
+
const workspacePr = await forge.prCreate({
|
|
125
|
+
title: `context: ${sessionName} (paused)`,
|
|
126
|
+
body: 'Workspace context for paused session.',
|
|
127
|
+
draft: true,
|
|
128
|
+
});
|
|
129
|
+
console.log(workspacePr.url);
|
|
105
130
|
```
|
|
106
131
|
|
|
107
|
-
If PRs already exist, update them to draft status if needed.
|
|
132
|
+
If PRs already exist, update them to draft status if needed (use `gh pr ready --undo` directly until a `forge.prSetDraft` method lands — that's tracked as a future forge adapter extension, not blocking here).
|
|
108
133
|
|
|
109
134
|
### Step 8: Confirm
|
|
110
135
|
|
|
@@ -1,151 +1,87 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: release
|
|
3
|
-
description:
|
|
3
|
+
description: Cut a versioned release of one project repo — bump the version, merge it through a PR, tag it, and publish a forge release whose notes are generated from merged PRs. No release-notes files.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Release
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
Cut a versioned release of one project repo. The version bump travels through a PR like any other change; the tag marks the merge commit; the forge generates the release notes from merged PR titles. The workspace keeps no release-notes files of its own.
|
|
9
9
|
|
|
10
10
|
## Why this shape
|
|
11
11
|
|
|
12
|
-
|
|
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.
|
|
12
|
+
Release notes come from the forge. GitHub's generated notes (merged PR titles) are what users of a published release actually read; the detail behind each PR already lives in the issue and the PR body, so duplicating it into workspace files buys nothing. Projects that use changesets, semantic-release, or their own release tooling simply don't use this skill. A repo's `CHANGELOG.md`, if it has one, is historical — this skill never writes it.
|
|
15
13
|
|
|
16
14
|
## Parameters
|
|
17
|
-
|
|
15
|
+
|
|
16
|
+
- `/release {version}` — release a specific version
|
|
18
17
|
- `/release` — ask for the version
|
|
19
18
|
|
|
20
19
|
## Flow
|
|
21
20
|
|
|
22
21
|
**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
22
|
|
|
30
|
-
|
|
23
|
+
Ask which repo to release — read `repos` from `workspace.json` and default to the entry with `"primary": true`. If no version was given, ask the bump kind (patch/minor/major) after showing the merged PRs since the last tag, so the operator can judge the impact:
|
|
31
24
|
|
|
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
25
|
```bash
|
|
35
|
-
|
|
26
|
+
git -C repos/{repo} describe --tags --abbrev=0
|
|
36
27
|
```
|
|
37
|
-
Read all `branch-release-notes-*.md` and `branch-release-questions-*.md` files.
|
|
38
28
|
|
|
39
|
-
|
|
29
|
+
Then call the forge adapter's `prList` with a merged-after search bounded by that tag's date (e.g. `merged:>{date}`). Pre-v1.0 breaking changes are a minor bump.
|
|
40
30
|
|
|
41
|
-
|
|
31
|
+
**Step 2: Preflight the tag**
|
|
42
32
|
|
|
43
|
-
|
|
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.
|
|
33
|
+
If `v{version}` already exists on origin, stop and ask — reuse it, investigate with `forge.releaseView`, or pick another version. Never force-push a tag.
|
|
45
34
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
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
|
|
35
|
+
```bash
|
|
36
|
+
git -C repos/{repo} ls-remote --exit-code origin refs/tags/v{version}
|
|
37
|
+
```
|
|
52
38
|
|
|
53
|
-
**Step
|
|
39
|
+
**Step 3: Bump on a branch**
|
|
54
40
|
|
|
55
|
-
|
|
41
|
+
Create a task worktree for the release branch:
|
|
56
42
|
|
|
57
|
-
|
|
43
|
+
```bash
|
|
44
|
+
node .claude/scripts/task-worktree.mjs --root . --create --repo "{repo}" --branch "release/v{version}"
|
|
45
|
+
```
|
|
58
46
|
|
|
59
|
-
|
|
60
|
-
## v{version} — {YYYY-MM-DD}
|
|
47
|
+
If the repo has a `package.json` with a `version`, set it to `{version}` (edit the JSON; keep formatting) and update `package-lock.json`'s top-level version fields if that file is present. Commit `chore: release v{version}`. If the repo has no version file, skip the commit — step 5 tags the current default-branch head instead.
|
|
61
48
|
|
|
62
|
-
|
|
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.}
|
|
49
|
+
**Step 4: Merge**
|
|
67
50
|
|
|
68
|
-
|
|
69
|
-
- {Deferred questions from Step 4, if any. Omit this subsection when empty.}
|
|
70
|
-
```
|
|
51
|
+
Push the branch and open a PR through the forge adapter — per-repo `createForge({ ...ws.workspace?.forge, repo: '{owner}/{name}' })` with head `release/v{version}`. Ask `Merge? [Y/n]`, then merge (squash, delete branch).
|
|
71
52
|
|
|
72
|
-
|
|
53
|
+
**Step 5: Tag and publish**
|
|
73
54
|
|
|
74
|
-
|
|
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.
|
|
55
|
+
Pull the merge, tag it, push the tag, and publish the forge release:
|
|
81
56
|
|
|
82
|
-
**Step 7: Commit the CHANGELOG entry to the project repo**
|
|
83
57
|
```bash
|
|
84
|
-
|
|
85
|
-
git
|
|
86
|
-
git
|
|
58
|
+
git -C repos/{repo} pull --ff-only
|
|
59
|
+
git -C repos/{repo} tag v{version}
|
|
60
|
+
git -C repos/{repo} push origin v{version}
|
|
87
61
|
```
|
|
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
62
|
|
|
90
|
-
|
|
91
|
-
|
|
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}"
|
|
63
|
+
```js
|
|
64
|
+
await forge.releaseCreate({ tag: 'v{version}', repo, generateNotes: true });
|
|
97
65
|
```
|
|
98
|
-
Skip this step if the repo has no package.json or no version field.
|
|
99
66
|
|
|
100
|
-
|
|
67
|
+
If the repo has `.github/workflows/publish.yml`, find and watch its run with `workflowRunFind` / `workflowRunWatch` — retry the find up to 5 times with 3 s backoff (the run may not be registered the moment the tag lands). A failed run is reported to the operator, not thrown.
|
|
68
|
+
|
|
69
|
+
**Step 6: Tear down and report**
|
|
70
|
+
|
|
71
|
+
Remove the release worktree:
|
|
72
|
+
|
|
101
73
|
```bash
|
|
102
|
-
|
|
103
|
-
git add {releaseNotesDir}/unreleased/
|
|
104
|
-
git commit -m "release: consume {repo} branch notes for v{version}"
|
|
74
|
+
node .claude/scripts/task-worktree.mjs --root . --remove --repo "{repo}" --branch "release/v{version}" --delete-branch
|
|
105
75
|
```
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
-
|
|
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."
|
|
76
|
+
|
|
77
|
+
Report the PR, the tag, the release URL, and the publish status.
|
|
78
|
+
|
|
79
|
+
**Step 7: Update workspace release state**
|
|
80
|
+
|
|
81
|
+
If the workspace keeps release state in `workspace-context/` (for example a current-release line in a status file under `shared/locked/`), offer to update it — through a workspace task worktree and PR, never on the launcher.
|
|
139
82
|
|
|
140
83
|
## Notes
|
|
141
84
|
|
|
142
|
-
-
|
|
143
|
-
-
|
|
144
|
-
-
|
|
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.
|
|
85
|
+
- Never run `npm publish` locally. The publish workflow is the only path that exercises OIDC trusted publishing; a local publish requires a 2FA OTP and bypasses it. If the workflow fails, investigate via `gh run view` — do not fall back to a local publish.
|
|
86
|
+
- Recovery from a failed publish: transient failures rerun via `gh run rerun {run_id}`; content failures mean deleting the tag and redoing the release. Once a version is published to a registry, that version number is committed — bump and release a new version instead.
|
|
87
|
+
- Pre-v1.0 breaking changes are a minor bump, not major.
|
|
@@ -5,6 +5,8 @@ description: Begin or resume a work session. Creates a self-contained work-sessi
|
|
|
5
5
|
|
|
6
6
|
# Start Work
|
|
7
7
|
|
|
8
|
+
Two lifecycles share this skill. `workspace.sessionModel` in `workspace.json` selects for new work: `"task"` routes new work to **Flow: Task** (session model v2 — no session folder, no `session.md`); absent or `"session"` keeps the existing flows below, unchanged. Resuming an existing `work-sessions/` session always uses the existing Resume flow regardless of the setting.
|
|
9
|
+
|
|
8
10
|
Begin or resume a persistent work session. Each session lives in its own `work-sessions/{name}/` folder containing one workspace worktree, nested project worktrees, and a unified `session.md` tracker. Sessions can run in parallel from separate terminals.
|
|
9
11
|
|
|
10
12
|
## Parameters
|
|
@@ -13,6 +15,55 @@ Begin or resume a persistent work session. Each session lives in its own `work-s
|
|
|
13
15
|
- `/start-work handoff` — list shared context to resume from
|
|
14
16
|
- `/start-work all` — list active sessions across all users (for shared debugging or multi-user workspaces)
|
|
15
17
|
|
|
18
|
+
## Flow: Task (session model v2)
|
|
19
|
+
|
|
20
|
+
New work as a task: one tracker issue, one branch, one worktree per repo the work touches. This flow creates no `work-sessions/` folder, no `session.md`, and seeds no task list — the issue, the branch, and the chat record are the entire state. The chat stays at the workspace root — `{launcher-root}`, the absolute path on the `Workspace root:` line the SessionStart hook injects (at /start-work time you are normally already there); the worktrees are reached by path.
|
|
21
|
+
|
|
22
|
+
If `workspace.tracker` is absent, say tracking is off and skip step 1 — but still ask for the type (`bug` / `feat` / `chore`) and a one-line description, because the type picks the branch prefix — then continue with steps 2–6. Tell the user plainly what that costs: without a tracker there is no `workItem`, the task is not recorded on the chat record, and `/complete-work` cannot find it from the launcher. It is completed either by running `/complete-work` from inside the worktree (cwd detection) or by opening the PR by hand.
|
|
23
|
+
|
|
24
|
+
1. **Identify or create the tracker issue and claim it** — the same adapter calls as Flow: Blank steps 3–6:
|
|
25
|
+
|
|
26
|
+
```javascript
|
|
27
|
+
import { createTracker } from './.claude/scripts/trackers/interface.mjs';
|
|
28
|
+
import { readFileSync } from 'node:fs';
|
|
29
|
+
const ws = JSON.parse(readFileSync('workspace.json', 'utf-8'));
|
|
30
|
+
const tracker = createTracker(ws.workspace.tracker);
|
|
31
|
+
const assigned = await tracker.listAssignedToMe();
|
|
32
|
+
const candidates = assigned.length > 0 ? assigned : await tracker.listUnassigned();
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Present the list as Flow: Blank step 4 does. When the pick came from the unassigned fallback, claim it atomically and re-fetch on `ALREADY_ASSIGNED` exactly as Blank step 5 shows; for "something new", create and self-assign per Blank step 6:
|
|
36
|
+
|
|
37
|
+
```javascript
|
|
38
|
+
const newIssue = await tracker.createIssue({
|
|
39
|
+
title: description,
|
|
40
|
+
body: `Created at /start-work by ${user}.`,
|
|
41
|
+
labels: [type, priority],
|
|
42
|
+
milestone: milestone || null,
|
|
43
|
+
});
|
|
44
|
+
await tracker.claim(newIssue.id);
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Remember `workItem: {issue.id}`.
|
|
48
|
+
|
|
49
|
+
2. **Pick repo(s)** — the same numbered multi-select as Blank step 7 (e.g. `1,3` or `all`), defaulting to the repo marked `"primary": true` under `repos` in `workspace.json`, falling back to the first entry when none is marked. The list also offers the **workspace repo itself**, shown as `workspace (this repo)` and addressed as `.`. Include it when the task changes anything tracked in the workspace repo — `workspace-context/`, the workspace's own `.claude/` (rules, hooks, scripts, skills), or, in a dogfood workspace, mirrors of template changes. Steps 4 and 5 take `.` like any other repo name (`--repo "."`).
|
|
50
|
+
|
|
51
|
+
3. **Propose the branch** — `{prefix}/{slug}` with the prefix from type (`feature/`, `bugfix/`, `chore/`), per the branch-naming step in Flow: Blank.
|
|
52
|
+
|
|
53
|
+
4. **Create one worktree per repo the work touches:**
|
|
54
|
+
```bash
|
|
55
|
+
node .claude/scripts/task-worktree.mjs --root . --create --repo "{repo}" --branch "{branch}"
|
|
56
|
+
```
|
|
57
|
+
The script fetches origin best-effort (offline is fine) before choosing the base. A project repo's worktree lands at `repos/{repo}/.claude/worktrees/{slug}/` — Claude Code's native worktree location — based on `origin/{defaultBranch}` when that ref exists, and never tracking it. For `.` the worktree lands at `.claude/worktrees/{slug}/` — the same native location, one level up — based on the workspace origin's HEAD (falling back to `main`); the workspace's own `.gitignore` already covers the path.
|
|
58
|
+
|
|
59
|
+
5. **Record the task on this chat's record** (only when a `workItem` exists — see the no-tracker note above):
|
|
60
|
+
```bash
|
|
61
|
+
node .claude/scripts/chat-record.mjs --root . --add-task --chat "{chat}" --work-item "{workItem}" --branch "{branch}" --repo "{repo}"
|
|
62
|
+
```
|
|
63
|
+
`{chat}` is the name from the `Chat record:` line the SessionStart hook injected into this conversation. If there is no such line, say so and skip recording rather than guessing a name.
|
|
64
|
+
|
|
65
|
+
6. **Tell the user where the work happens:** the worktree path(s) above — edits belong there, not in the source clones at `repos/{repo}/`. Work continues from this chat by path. A chat started inside a **project** worktree would not load the workspace's conventions or hooks (a worktree is a context boundary), so staying here is the default. A `.` worktree does load a copy of the workspace's `CLAUDE.md`/`.claude/` — but with the worktree as root, so its chat records land in the worktree's own scratchpad rather than the launcher's; the task still belongs to this chat.
|
|
66
|
+
|
|
16
67
|
## Flow: No Parameter
|
|
17
68
|
|
|
18
69
|
1. Read the current user from `.claude/settings.local.json` → `workspace.user`. If unset, behave as `/start-work all` (no user filter). If the user invoked `/start-work all`, also skip filtering.
|
|
@@ -40,7 +91,7 @@ Begin or resume a persistent work session. Each session lives in its own `work-s
|
|
|
40
91
|
- Workspace: `work-sessions/{name}/workspace/`
|
|
41
92
|
- For each repo in `repos:` frontmatter: `work-sessions/{name}/workspace/repos/{repo}/`
|
|
42
93
|
- If any are missing, recreate from the branch
|
|
43
|
-
3. The session-start hook automatically registers each chat in the session tracker's `chatSessions` frontmatter when Claude opens in a worktree. Verify the current chat is registered — if not (e.g., the hook didn't fire),
|
|
94
|
+
3. The session-start hook automatically registers each chat in the session tracker's `chatSessions` frontmatter when Claude opens in a worktree. Verify the current chat is registered — if not (e.g., the hook didn't fire), append an entry using the invocation shown under "Create work session" below (the helper is an importable library, not a CLI).
|
|
44
95
|
|
|
45
96
|
Each `chatSessions` entry has this shape:
|
|
46
97
|
```yaml
|
|
@@ -55,7 +106,7 @@ Begin or resume a persistent work session. Each session lives in its own `work-s
|
|
|
55
106
|
4. Update the tracker `status:` to `active` if it was `paused`
|
|
56
107
|
5. Restore the task list from `## Tasks` per the `task-list-mirroring` rule:
|
|
57
108
|
```bash
|
|
58
|
-
cd work-sessions/{name}/workspace
|
|
109
|
+
cd "work-sessions/{name}/workspace"
|
|
59
110
|
node .claude/scripts/sync-tasks.mjs --read session.md
|
|
60
111
|
```
|
|
61
112
|
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.
|
|
@@ -165,9 +216,40 @@ The script creates:
|
|
|
165
216
|
- Active-session pointer at `work-sessions/{session-name}/workspace/.claude/.active-session.json`
|
|
166
217
|
- Copies `settings.local.json` into the worktree if it exists at the workspace root
|
|
167
218
|
|
|
168
|
-
If a `workItem:` was set in step 5 or 6, write it into the tracker's frontmatter
|
|
219
|
+
If a `workItem:` was set in step 5 or 6, write it into the tracker's frontmatter after
|
|
220
|
+
creation. `/pause-work` and `/complete-work` both use this to locate the linked issue, and
|
|
221
|
+
a session created without it looks fine until one of them silently cannot find the ticket.
|
|
222
|
+
|
|
223
|
+
`.claude/lib/session-frontmatter.mjs` is a **library, not a CLI** — running it with flags
|
|
224
|
+
does nothing and exits 2. Import it:
|
|
225
|
+
|
|
226
|
+
```bash
|
|
227
|
+
cd "work-sessions/{session-name}/workspace"
|
|
228
|
+
node --input-type=module -e '
|
|
229
|
+
import { updateSessionFile, readSessionFields } from "./.claude/lib/session-frontmatter.mjs";
|
|
230
|
+
updateSessionFile("session.md", { workItem: "{workItem}" });
|
|
231
|
+
console.log("workItem =", readSessionFields("session.md").workItem);
|
|
232
|
+
'
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
Read the value back, as above, and confirm it before moving on — this write has failed
|
|
236
|
+
silently before (gh:143).
|
|
237
|
+
|
|
238
|
+
Register this chat in the tracker's `chatSessions` frontmatter. For new sessions, the session-start hook has already fired (before /start-work was invoked) but the session folder didn't exist yet. Find the current chat's UUID from the most recently modified `.jsonl` file in
|
|
239
|
+
`~/.claude/projects/{project-path}/` and append the entry with the same library:
|
|
240
|
+
|
|
241
|
+
```bash
|
|
242
|
+
node --input-type=module -e '
|
|
243
|
+
import { updateSessionFile, readSessionFields } from "./.claude/lib/session-frontmatter.mjs";
|
|
244
|
+
const existing = readSessionFields("session.md").chatSessions || [];
|
|
245
|
+
updateSessionFile("session.md", {
|
|
246
|
+
chatSessions: [...existing, { id: "{uuid}", names: [], started: new Date().toISOString(), ended: null }],
|
|
247
|
+
});
|
|
248
|
+
'
|
|
249
|
+
```
|
|
169
250
|
|
|
170
|
-
|
|
251
|
+
Append — never replace: the list is the session'"'"'s whole chat history. Subsequent chats are
|
|
252
|
+
registered automatically by the hook.
|
|
171
253
|
|
|
172
254
|
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`.
|
|
173
255
|
|
|
@@ -177,7 +259,7 @@ After session creation, seed the `## Tasks` section in the new tracker so `TodoW
|
|
|
177
259
|
|
|
178
260
|
```bash
|
|
179
261
|
# Build the seed from inside the worktree so the helper resolves workspace.json correctly.
|
|
180
|
-
cd work-sessions/{session-name}/workspace
|
|
262
|
+
cd "work-sessions/{session-name}/workspace"
|
|
181
263
|
echo '{"todos": []}' | node .claude/scripts/sync-tasks.mjs --write session.md
|
|
182
264
|
```
|
|
183
265
|
|
|
@@ -199,7 +281,7 @@ The auto-commit at the end of "Capture prior conversation context" picks up the
|
|
|
199
281
|
|
|
200
282
|
### Capture prior conversation context
|
|
201
283
|
|
|
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
|
|
284
|
+
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 write a thin PR body.
|
|
203
285
|
|
|
204
286
|
Check: has the current conversation included substantive discussion (design decisions, requirements exploration, approach selection) before this point?
|
|
205
287
|
|
|
@@ -208,7 +290,7 @@ If yes:
|
|
|
208
290
|
2. Write the summary into `work-sessions/{session-name}/workspace/session.md`'s body, in a `## Pre-session context` or `## Progress` section
|
|
209
291
|
3. Auto-commit from inside the worktree so the capture lands on the session branch:
|
|
210
292
|
```bash
|
|
211
|
-
cd work-sessions/{session-name}/workspace
|
|
293
|
+
cd "work-sessions/{session-name}/workspace"
|
|
212
294
|
git add session.md
|
|
213
295
|
git commit -m "chore: capture pre-session discussion for {session-name}"
|
|
214
296
|
```
|