@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
|
@@ -96,11 +96,11 @@ For the codebase:
|
|
|
96
96
|
- Read actual implementations, not just existing docs about them
|
|
97
97
|
|
|
98
98
|
For shared context:
|
|
99
|
-
- Walk `workspace-context/` for handoffs, braindumps, locked team knowledge
|
|
99
|
+
- Walk `workspace-context/` for handoffs, braindumps, and locked team knowledge
|
|
100
100
|
|
|
101
101
|
For work-session history:
|
|
102
|
-
- Walk `work-sessions/*/workspace/session.md` for any currently-active session trackers — their bodies may contain decisions not yet
|
|
103
|
-
-
|
|
102
|
+
- Walk `work-sessions/*/workspace/session.md` for any currently-active session trackers — their bodies may contain decisions not yet captured anywhere durable
|
|
103
|
+
- Walk the chat drawers `workspace-scratchpad/chats/*/` for in-progress task-model material — designs, plans, braindumps, research not yet promoted into `workspace-context/` — same rationale: it is thinking the site may need that no merged artifact carries
|
|
104
104
|
|
|
105
105
|
For existing project documentation (from Phase 1 Q2):
|
|
106
106
|
- Read every file the user pointed at
|
|
@@ -312,11 +312,11 @@ Produce a final report:
|
|
|
312
312
|
- **Files needing user decision.** Any old docs with content that didn't migrate cleanly — coverage check found gaps the user needs to resolve.
|
|
313
313
|
- **URLs that may need redirects.** If the old docs had live URLs, list them. The skill flags but does not set up redirects.
|
|
314
314
|
|
|
315
|
-
Update the session tracker with the final state. The skill's work ends here. `/complete-work` handles the merge,
|
|
315
|
+
Update the session tracker with the final state. The skill's work ends here. `/complete-work` handles the merge, the PR, and cleanup.
|
|
316
316
|
|
|
317
317
|
## Notes
|
|
318
318
|
|
|
319
|
-
- Specs and plans live at the project worktree root, not inflight (per workspace-structure rule). They are consumed by `/complete-work` into
|
|
319
|
+
- Specs and plans live at the project worktree root, not inflight (per workspace-structure rule). They are consumed by `/complete-work` when it builds the PR body, or promoted into `workspace-context/`.
|
|
320
320
|
- Diagrams primitives are deliberately fixed (no project customization) because the class-based fill pattern is load-bearing for theme support.
|
|
321
321
|
- The leak grep is project-accurate because it derives the list from the project's own dependency manifests. Do not maintain a hardcoded master list — it would over-trigger for projects whose docs legitimately discuss their own dependencies.
|
|
322
322
|
- The forbidden-word grep is per-project — each project supplies its own list from Phase 1 Q5. There is no default.
|
|
@@ -116,4 +116,4 @@ Terms that came from brainstorming sessions and need to be translated to descrip
|
|
|
116
116
|
|
|
117
117
|
## Notes
|
|
118
118
|
|
|
119
|
-
This spec
|
|
119
|
+
This spec feeds the PR body when `/complete-work` finishes the session (or is promoted into `workspace-context/`). Update it as decisions change during writing.
|
|
@@ -0,0 +1,452 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: complete-work
|
|
3
|
+
description: Finalize a work session — rebase, PR, merge, and close the linked issue. Handles all project repos and the 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, 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. If it is present, this chat runs inside a session worktree: continue with this flow unchanged.
|
|
15
|
+
|
|
16
|
+
If no pointer is present, run work-model detection — this covers the task model, whose chats run at the workspace root (the launcher), not inside a worktree:
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
node "{launcher-root}/.claude/scripts/task-worktree.mjs" --root "{launcher-root}" --detect --chat "{chat}"
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
- `{launcher-root}` is the absolute path on the `Workspace root:` line the SessionStart hook injects. If that line is absent, derive it from git: run `git rev-parse --git-common-dir` (when it prints a relative path, resolve it against the cwd) and take its parent directory. That derivation lands on the source clone `…/repos/{repo}` when run from inside a **project** task worktree — there the launcher is two levels up; from inside a `.` worktree (`.claude/worktrees/{slug}`) the parent already is the launcher.
|
|
23
|
+
- `{chat}` is the name from the `Chat record:` line the SessionStart hook injects. If that line is absent, run `node .claude/scripts/chat-record.mjs --whoami --root "{launcher-root}"` first — compaction can drop the hook line, and this recovers the name by matching the chat's session id against the records. When that too prints nothing (exit 1), omit `--chat` — detection then relies on cwd alone.
|
|
24
|
+
- `model: session` → continue with this flow (read the session tracker as below), taking `{session-name}` from the detect result's `sessionName`.
|
|
25
|
+
- `model: task` → go to **Task completion (session model v2)**. The result's `tasks` come from the chat record; if several are open, ask the user which one to complete — group by branch, a multi-repo task is several entries sharing a branch.
|
|
26
|
+
- `model: none` → "No active work session. Nothing to complete."
|
|
27
|
+
|
|
28
|
+
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).
|
|
29
|
+
|
|
30
|
+
Determine paths:
|
|
31
|
+
- Session folder: `work-sessions/{session-name}/`
|
|
32
|
+
- Workspace worktree: `work-sessions/{session-name}/workspace/`
|
|
33
|
+
- Project worktrees: `work-sessions/{session-name}/workspace/repos/{repo}/` for each repo in the tracker's `repos:` list
|
|
34
|
+
- Read each repo's default branch from workspace.json (`repos.{repo}.branch`)
|
|
35
|
+
|
|
36
|
+
### Step 2: Rebase project repos
|
|
37
|
+
|
|
38
|
+
For each repo in the tracker's `repos:`:
|
|
39
|
+
```bash
|
|
40
|
+
# {repo-branch} = repos.{repo}.branch from workspace.json
|
|
41
|
+
cd "work-sessions/{session-name}/workspace/repos/{repo}"
|
|
42
|
+
git fetch origin
|
|
43
|
+
git rebase "origin/{repo-branch}"
|
|
44
|
+
```
|
|
45
|
+
If conflicts arise in any repo, STOP and present them to the user. Do not auto-resolve.
|
|
46
|
+
|
|
47
|
+
### Step 3: Capture final discussion state
|
|
48
|
+
|
|
49
|
+
Run `/braindump` to capture any final discussion/reasoning to the session tracker body.
|
|
50
|
+
If the user declines or there's nothing to capture, skip.
|
|
51
|
+
|
|
52
|
+
### Step 4: Flush task list to session.md
|
|
53
|
+
|
|
54
|
+
Before reading sources for the PR body, flush current `TodoWrite` state to `## Tasks` per the `task-list-mirroring` rule. This ensures the body written in Step 9 sees the final state:
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
cd "work-sessions/{session-name}/workspace"
|
|
58
|
+
echo '<JSON-of-current-todos>' | node .claude/scripts/sync-tasks.mjs --write session.md
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Mark `Complete work` as `in_progress` in the JSON before flushing — the rest of this skill IS the act of completing.
|
|
62
|
+
|
|
63
|
+
### Step 5: Gather source material
|
|
64
|
+
|
|
65
|
+
Formally read ALL sources before writing the PR body — do not summarize from memory alone:
|
|
66
|
+
|
|
67
|
+
1. **Session tracker** at `work-sessions/{session-name}/workspace/session.md` — read the full body (frontmatter is machine state, body is human content)
|
|
68
|
+
|
|
69
|
+
2. **Session-scoped specs/plans/goal artifacts** at the top of the session worktree:
|
|
70
|
+
- `work-sessions/{session-name}/workspace/design-*.md` files
|
|
71
|
+
- `work-sessions/{session-name}/workspace/plan-*.md` files
|
|
72
|
+
- `work-sessions/{session-name}/workspace/goal-*.md` files
|
|
73
|
+
- `work-sessions/{session-name}/workspace/research-*.md` files
|
|
74
|
+
- `work-sessions/{session-name}/workspace/crossref-*.md` files
|
|
75
|
+
- Read each one fully
|
|
76
|
+
|
|
77
|
+
3. **Handoffs** — any workspace-context entries referencing this branch:
|
|
78
|
+
```bash
|
|
79
|
+
grep -rl "branch: {branch}" workspace-context/
|
|
80
|
+
```
|
|
81
|
+
Read each matching file.
|
|
82
|
+
|
|
83
|
+
4. **Branch commit logs** (per repo):
|
|
84
|
+
```bash
|
|
85
|
+
# For each repo in the tracker's repos list:
|
|
86
|
+
cd "work-sessions/{session-name}/workspace/repos/{repo}"
|
|
87
|
+
git log "origin/{repo-branch}..HEAD" --oneline
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
5. **The linked issue** — if the tracker has a `workItem:` field and `workspace.tracker` is configured, read the issue through the tracker adapter so the PR body can speak to what was asked.
|
|
91
|
+
|
|
92
|
+
These sources feed the PR bodies in Step 9: a short summary of what changed and why, plus a Verification section.
|
|
93
|
+
|
|
94
|
+
### Step 6: Remove session artifacts from the workspace branch
|
|
95
|
+
|
|
96
|
+
The entire `work-sessions/{session-name}/` folder is removed by the cleanup script in Step 11. Before that happens, decide what deserves to survive: the tracker, specs, plans, and goal artifacts hold the session's reasoning, and the commands below delete them from the branch.
|
|
97
|
+
|
|
98
|
+
**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:
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
cd "work-sessions/{session-name}/workspace/repos/{repo}" # repeat for the workspace worktree too
|
|
102
|
+
session_branch=$(git rev-parse --abbrev-ref HEAD)
|
|
103
|
+
for sub in $(git branch --format='%(refname:short)' --list "${session_branch}-*"); do
|
|
104
|
+
git merge-base --is-ancestor "$sub" "$session_branch" || echo "UNMERGED: $sub"
|
|
105
|
+
done
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
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.
|
|
109
|
+
|
|
110
|
+
**Decide the fate of the artifacts — runs after the pre-flight, never before it.** Before stripping, list the session artifacts present at the top of the workspace worktree — `session.md` plus every `design-*.md`, `plan-*.md`, `goal-*.md`, `research-*.md`, and `crossref-*.md` — and offer two choices:
|
|
111
|
+
|
|
112
|
+
1. **Promote first (default).** Run `/promote` on the artifacts so they land in
|
|
113
|
+
`workspace-context/` as durable team knowledge, then strip and continue. This is the
|
|
114
|
+
right answer in every case: reasoning that is worth keeping belongs in workspace-context,
|
|
115
|
+
which is where it will actually be read.
|
|
116
|
+
2. **Discard.** Strip anyway, with an explicit confirmation that names each file.
|
|
117
|
+
|
|
118
|
+
Never choose on the user's behalf. There is deliberately no "keep them on the branch"
|
|
119
|
+
option: `session.md` would merge to the workspace repo root, which is exactly the path the
|
|
120
|
+
next `/start-work` worktree writes its own tracker to, and `createSessionTracker` overwrites
|
|
121
|
+
unconditionally. Keeping artifacts on the branch does not preserve them — it contaminates
|
|
122
|
+
`main` and then loses them anyway on the next session.
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
Session content lives at the top of the workspace worktree on the session branch. Once the pre-flight passes and the artifacts' fate is decided, remove these files from the branch before the final push so main's top level stays free of session artifacts:
|
|
126
|
+
|
|
127
|
+
```bash
|
|
128
|
+
cd "work-sessions/{session-name}/workspace"
|
|
129
|
+
git rm -f session.md 2>/dev/null || true
|
|
130
|
+
git rm -f design-*.md 2>/dev/null || true
|
|
131
|
+
git rm -f plan-*.md 2>/dev/null || true
|
|
132
|
+
git rm -f goal-*.md 2>/dev/null || true
|
|
133
|
+
git rm -f research-*.md 2>/dev/null || true
|
|
134
|
+
git rm -f crossref-*.md 2>/dev/null || true
|
|
135
|
+
git commit -m "chore: remove session artifacts before PR" 2>/dev/null || true
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
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.
|
|
139
|
+
|
|
140
|
+
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.
|
|
141
|
+
|
|
142
|
+
> **No version bump here.** Versions are bumped at release time by `/release`, which tags the merge and cuts a forge release whose notes are generated from merged PRs. `/complete-work` does not modify any project repo's `package.json`.
|
|
143
|
+
|
|
144
|
+
### Step 7: Detect remote type per repo
|
|
145
|
+
|
|
146
|
+
For each repo in the tracker's `repos:` plus the workspace repo, determine the remote type. This drives how Step 8 and Step 9 push and merge.
|
|
147
|
+
|
|
148
|
+
```bash
|
|
149
|
+
cd "work-sessions/{session-name}/workspace/repos/{repo}"
|
|
150
|
+
git remote get-url origin 2>&1
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
Classify the result:
|
|
154
|
+
|
|
155
|
+
- **GitHub remote** — URL contains `github.com` or `gh repo view` succeeds against origin → use the PR flow (Step 8a, Step 9a).
|
|
156
|
+
- **Local / bare remote** — URL is a filesystem path (starts with `/`, `./`, `file://`, or points at a `.git` bare mirror) → use the local merge flow (Step 8b, Step 9b).
|
|
157
|
+
- **Other remote** (e.g., GitLab, Bitbucket, self-hosted) — no `gh` support → fall back to the local merge flow (Step 8b, Step 9b), and mention it in the final summary.
|
|
158
|
+
- **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.
|
|
159
|
+
|
|
160
|
+
### Step 8: Push all repos
|
|
161
|
+
|
|
162
|
+
#### Step 8a: GitHub remotes
|
|
163
|
+
|
|
164
|
+
```bash
|
|
165
|
+
# Each project repo with a GitHub remote
|
|
166
|
+
cd "work-sessions/{session-name}/workspace/repos/{repo}"
|
|
167
|
+
git push -u origin "{branch}"
|
|
168
|
+
|
|
169
|
+
# Workspace repo — from the workspace worktree
|
|
170
|
+
cd "work-sessions/{session-name}/workspace"
|
|
171
|
+
git add .
|
|
172
|
+
git commit -m "chore: finalize context for {session-name}"
|
|
173
|
+
git push -u origin "{branch}"
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
#### Step 8b: Local/bare remotes
|
|
177
|
+
|
|
178
|
+
```bash
|
|
179
|
+
# Push the feature branch to the bare remote so it exists there
|
|
180
|
+
cd "work-sessions/{session-name}/workspace/repos/{repo}"
|
|
181
|
+
git push -u origin "{branch}"
|
|
182
|
+
|
|
183
|
+
# Workspace repo — same commit + push pattern
|
|
184
|
+
cd "work-sessions/{session-name}/workspace"
|
|
185
|
+
git add .
|
|
186
|
+
git commit -m "chore: finalize context for {session-name}"
|
|
187
|
+
git push -u origin "{branch}"
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
The push shape is the same as 8a — what differs is the merge mechanics in Step 9b.
|
|
191
|
+
|
|
192
|
+
### Step 9: Merge and present unified summary
|
|
193
|
+
|
|
194
|
+
#### Step 9a: GitHub remotes — create PRs, unified summary, merge
|
|
195
|
+
|
|
196
|
+
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.
|
|
197
|
+
|
|
198
|
+
Each PR body is built from the material gathered in Step 5 — the session tracker body, the linked issue, and the commits: a short summary of what changed and why, then a **Verification** section stating how the change was checked (tests run, commands executed, results).
|
|
199
|
+
|
|
200
|
+
```javascript
|
|
201
|
+
import { createForge } from './.claude/scripts/forges/interface.mjs';
|
|
202
|
+
import { readFileSync } from 'node:fs';
|
|
203
|
+
|
|
204
|
+
const ws = JSON.parse(readFileSync('workspace.json', 'utf-8'));
|
|
205
|
+
const forge = createForge(ws.workspace?.forge);
|
|
206
|
+
|
|
207
|
+
// For each repo in the tracker's repos with a GitHub remote, from
|
|
208
|
+
// work-sessions/{session-name}/workspace/repos/{repo}:
|
|
209
|
+
const projectPr = await forge.prCreate({
|
|
210
|
+
title: `${type}: ${description}`,
|
|
211
|
+
body: prBody, // short summary + Verification section
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
// Workspace PR — from the workspace worktree:
|
|
215
|
+
const workspacePr = await forge.prCreate({
|
|
216
|
+
title: `context: ${sessionName} work session`,
|
|
217
|
+
body: workspacePrBody,
|
|
218
|
+
});
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
Present unified summary:
|
|
222
|
+
```
|
|
223
|
+
Work session complete:
|
|
224
|
+
|
|
225
|
+
PROJECT: {repo-1}
|
|
226
|
+
PR #{n}: {type}: {description}
|
|
227
|
+
Branch: {branch} → {repo-1-branch}
|
|
228
|
+
Changes:
|
|
229
|
+
- {bullet points from the PR body}
|
|
230
|
+
|
|
231
|
+
PROJECT: {repo-2}
|
|
232
|
+
PR #{m}: {type}: {description}
|
|
233
|
+
Branch: {branch} → {repo-2-branch}
|
|
234
|
+
Changes:
|
|
235
|
+
- {bullet points from the PR body}
|
|
236
|
+
|
|
237
|
+
WORKSPACE: {workspace-name}
|
|
238
|
+
PR #{p}: context: {session-name} work session
|
|
239
|
+
Branch: {branch} → main
|
|
240
|
+
|
|
241
|
+
Merge all? [Y/n]
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
If yes — merge all PRs atomically through the forge adapter:
|
|
245
|
+
|
|
246
|
+
```javascript
|
|
247
|
+
// For each project PR returned from Step 9a's prCreate calls:
|
|
248
|
+
await forge.prMerge({ id: projectPr.id, strategy: 'squash', deleteBranch: true });
|
|
249
|
+
|
|
250
|
+
// Workspace PR:
|
|
251
|
+
await forge.prMerge({ id: workspacePr.id, strategy: 'squash', deleteBranch: true });
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
`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.
|
|
255
|
+
|
|
256
|
+
Then pull all repos to their default branches (still plain git):
|
|
257
|
+
|
|
258
|
+
```bash
|
|
259
|
+
# For each repo in the tracker's repos:
|
|
260
|
+
cd "repos/{repo}" && git pull origin "{repo-branch}"
|
|
261
|
+
cd "{main-workspace-root}" && git pull origin main
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
#### Step 9b: Local / bare / other remotes — local merge flow
|
|
265
|
+
|
|
266
|
+
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:
|
|
267
|
+
|
|
268
|
+
```
|
|
269
|
+
Work session complete:
|
|
270
|
+
|
|
271
|
+
PROJECT: {repo-1} (local remote)
|
|
272
|
+
Branch: {branch} → {repo-1-branch}
|
|
273
|
+
Changes:
|
|
274
|
+
- {bullet points from the PR-body material}
|
|
275
|
+
|
|
276
|
+
PROJECT: {repo-2} (local remote)
|
|
277
|
+
Branch: {branch} → {repo-2-branch}
|
|
278
|
+
Changes:
|
|
279
|
+
- {bullet points from the PR-body material}
|
|
280
|
+
|
|
281
|
+
WORKSPACE: {workspace-name} (local remote)
|
|
282
|
+
Branch: {branch} → main
|
|
283
|
+
|
|
284
|
+
Merge all locally? [Y/n]
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
If yes — fast-forward merge on each remote, delete the feature branch, pull the source clone:
|
|
288
|
+
```bash
|
|
289
|
+
# For each repo in the tracker's repos with a local/bare remote:
|
|
290
|
+
cd "work-sessions/{session-name}/workspace/repos/{repo}"
|
|
291
|
+
git push origin "HEAD:{repo-branch}" # fast-forward the default branch
|
|
292
|
+
git push origin --delete "{branch}" # remove the feature branch from the remote
|
|
293
|
+
cd "repos/{repo}" && git checkout "{repo-branch}" && git pull origin "{repo-branch}"
|
|
294
|
+
|
|
295
|
+
# Workspace repo — same pattern from the workspace worktree
|
|
296
|
+
cd "work-sessions/{session-name}/workspace"
|
|
297
|
+
git push origin HEAD:main
|
|
298
|
+
git push origin --delete "{branch}"
|
|
299
|
+
cd "{main-workspace-root}" && git pull origin main
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
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.
|
|
303
|
+
|
|
304
|
+
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:
|
|
305
|
+
```bash
|
|
306
|
+
cd "repos/{repo}" && git merge --ff-only "{branch}"
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
### Step 10: Close the linked issue on the tracker
|
|
310
|
+
|
|
311
|
+
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:
|
|
312
|
+
|
|
313
|
+
```javascript
|
|
314
|
+
import { createTracker } from './.claude/scripts/trackers/interface.mjs';
|
|
315
|
+
import { readFileSync } from 'node:fs';
|
|
316
|
+
const ws = JSON.parse(readFileSync('workspace.json', 'utf-8'));
|
|
317
|
+
if (ws.workspace?.tracker) {
|
|
318
|
+
const tracker = createTracker(ws.workspace.tracker);
|
|
319
|
+
const comment = [
|
|
320
|
+
`**Completed by @${currentUser}**`,
|
|
321
|
+
'',
|
|
322
|
+
'Merged PRs:',
|
|
323
|
+
...mergedPrs.map(p => `- ${p.repo}: ${p.url}`),
|
|
324
|
+
'',
|
|
325
|
+
releaseSummary, // 1-3 sentence synthesis of what shipped, drawn from the session tracker
|
|
326
|
+
].join('\n');
|
|
327
|
+
await tracker.closeIssue(workItem, { comment });
|
|
328
|
+
}
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
If `workItem:` is unset, skip the close — this was a blank session.
|
|
332
|
+
|
|
333
|
+
If the close call fails (tracker unreachable, auth expired), report the error in the unified summary but do not block Step 11 cleanup. The issue can be closed manually via the GitHub UI; no data is at risk.
|
|
334
|
+
|
|
335
|
+
### Step 11: Cleanup
|
|
336
|
+
|
|
337
|
+
Run the cleanup helper script from the main workspace root:
|
|
338
|
+
```bash
|
|
339
|
+
node .claude/scripts/cleanup-work-session.mjs --session-name "{session-name}"
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
The script tears down in the **mandatory** order:
|
|
343
|
+
1. Remove each nested project worktree from its project repo
|
|
344
|
+
2. Remove the workspace worktree from the workspace repo
|
|
345
|
+
3. `git worktree prune` on each project repo (belt-and-suspenders for orphan records)
|
|
346
|
+
4. Delete local branches in all repos
|
|
347
|
+
5. `rm -rf work-sessions/{session-name}/` — the tracker, specs, plans, and any local-only artifacts vanish. Anything worth preserving was promoted into `workspace-context/` in Step 6.
|
|
348
|
+
|
|
349
|
+
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.
|
|
350
|
+
|
|
351
|
+
Verify workspace root is still on main:
|
|
352
|
+
```bash
|
|
353
|
+
git branch --show-current # should be "main"
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
## Handling Unformal Work Sessions
|
|
357
|
+
|
|
358
|
+
If /complete-work is called but changes were made without a formal work session (no branch, changes on default branch):
|
|
359
|
+
|
|
360
|
+
Ask: "These changes weren't part of a formal work session. What do you want to do?"
|
|
361
|
+
- **Accept as work** — create a session retroactively, proceed with normal completion
|
|
362
|
+
- **Stash for later** — create a user-scoped handoff describing what was done, stash the changes
|
|
363
|
+
- **Hand off to someone** — create a team-visible handoff at root workspace-context/ for another member to pick up
|
|
364
|
+
- **Revert** — undo the changes (with confirmation)
|
|
365
|
+
|
|
366
|
+
## Task completion (session model v2)
|
|
367
|
+
|
|
368
|
+
Reached from Step 1 when detection says `model: task`. The state is the branch, the chat record's task entries, and the linked issue — there is no session folder and no `session.md`. This chat normally runs at the workspace root (the launcher) but may be running inside one of the worktrees; either way, every input comes from the detect result, never from cwd. Run `cd "{launcher-root}"` first — steps 2–5 and every relative path in them are anchored there:
|
|
369
|
+
|
|
370
|
+
- `{chat}` — the `Chat record:` line injected by the SessionStart hook (the same value Step 1 passed as `--chat`)
|
|
371
|
+
- `{tasks}` — the detect result's task entries from the chat record, each carrying `{workItem}`, `{branch}`, `{repo}`; `repo: "."` is the workspace repo itself. When detection came from cwd alone (`source: 'worktree'`, no chat record entry — e.g. a no-tracker task) there are no task entries: take `{repo}` and `{branch}` from the detect result itself and treat `{workItem}` as absent
|
|
372
|
+
- `{worktree}` — `{launcher-root}/repos/{repo}/.claude/worktrees/{slug}` for a project repo, or `{launcher-root}/.claude/worktrees/{slug}` for the workspace repo (`.`), where `{slug}` is the branch with `/` replaced by `-`
|
|
373
|
+
- `{workspace-worktree}` — `{launcher-root}/.claude/worktrees/{slug}`: the workspace repo's own task worktree, created in step 2 only when drawer items are promoted (Claude Code's native worktree location — the two converge on it)
|
|
374
|
+
- `{defaultBranch}` — `workspace.json` → `repos.{repo}.branch`, default `main`; for `.` it is the workspace origin's HEAD with `main` as the fallback — exactly what `task-worktree.mjs`'s `defaultBranchFor` resolves
|
|
375
|
+
|
|
376
|
+
If several tasks are open, ask the user which one to complete — group by branch; a multi-repo task is several entries sharing a branch — and complete one branch at a time.
|
|
377
|
+
|
|
378
|
+
0. **Pre-flight: the worktrees must be clean.** For each `{worktree}` of the chosen task, `git -C "{worktree}" status --porcelain` must be empty. If it is not, stop here — before the rebase — and ask the user to commit or discard: rebasing over uncommitted work silently invalidates it.
|
|
379
|
+
|
|
380
|
+
1. **Rebase each task worktree onto `origin/{defaultBranch}`** (`git -C "{worktree}" fetch origin`, then `git -C "{worktree}" rebase "origin/{defaultBranch}"`). Freshness first: the PR in step 3 must describe the branch as it will merge. If conflicts arise, STOP and present them — do not auto-resolve.
|
|
381
|
+
|
|
382
|
+
2. **Route durable thinking.** List the chat drawer `{launcher-root}/workspace-scratchpad/chats/{chat}/` and ask which items should graduate into `workspace-context/`. The offer is filtered by task: an item whose frontmatter carries a `workItem:` is listed only when it names this task's `{workItem}`, an item with no `workItem:` is always listed, and an item tagged for another open task is not this completion's to promote — it belongs to that task's completion. Leftovers of an earlier completion attempt are never offered: the `pr-{slug}-*.md` bodies and the `prs-{slug}.json` state file are step 3–4 machinery, not thinking. Drafting skills (`/braindump`, `/handoff`, designs and plans) tag their drawer writes with `workItem:` while a task is active so this filter has something to filter on. If nothing is chosen, skip this step — no `.` worktree is needed yet. Do NOT invoke `/promote`: it writes relative to cwd and commits per item, which at the launcher lands on the default branch — exactly the hole this flow closes, and it does not know about drawer items. For the chosen items, first create the workspace worktree on the task's branch and record it alongside the project entries:
|
|
383
|
+
|
|
384
|
+
```bash
|
|
385
|
+
node "{launcher-root}/.claude/scripts/task-worktree.mjs" --root "{launcher-root}" --create --repo "." --branch "{branch}"
|
|
386
|
+
node "{launcher-root}/.claude/scripts/chat-record.mjs" --root "{launcher-root}" --add-task --chat "{chat}" --work-item "{workItem}" --branch "{branch}" --repo "."
|
|
387
|
+
```
|
|
388
|
+
|
|
389
|
+
Skip the record line when there is no `{workItem}`. Then copy each chosen item to `{workspace-worktree}/workspace-context/shared/{item}` or `{workspace-worktree}/workspace-context/team-member/{user}/{item}` — ask which level; `shared/locked/` only if explicitly requested — keeping the filename and making sure the file carries a `description:` frontmatter (add one if the drawer item has none). Never write under `{launcher-root}/workspace-context/` at the launcher. Rebuild the indexes and commit once, and only when something is staged:
|
|
390
|
+
|
|
391
|
+
```bash
|
|
392
|
+
node "{launcher-root}/.claude/scripts/build-workspace-context.mjs" --write --root "{workspace-worktree}"
|
|
393
|
+
git -C "{workspace-worktree}" add workspace-context/
|
|
394
|
+
git -C "{workspace-worktree}" diff --cached --quiet || git -C "{workspace-worktree}" commit -m "context: promote durable thinking for {branch}"
|
|
395
|
+
```
|
|
396
|
+
|
|
397
|
+
The drawer is per-chat, so it survives task teardown — but it is machine-local and backed up nowhere, and this review, with the work fresh in mind, is the moment to decide what graduates. Items left behind are not lost, only unreviewed.
|
|
398
|
+
|
|
399
|
+
3. **Write one PR body per repo into the drawer, then create every PR with `task-pr.mjs --create`** — never `gh pr` directly. For each repo of the task, `{worktree}` included when it exists, write `{launcher-root}/workspace-scratchpad/chats/{chat}/pr-{slug}-{repo}.md` (with `{repo}` rendered as `workspace` for `.`). Each body carries a short summary of what changed and why, then a `## Verification` section stating how the change was checked — the commands run and their results. The drawer is machine-local and gitignored, so these files never touch a branch, and `--create` requires one for every repo whose branch has commits. Then one command does the rest:
|
|
400
|
+
|
|
401
|
+
```bash
|
|
402
|
+
node "{launcher-root}/.claude/scripts/task-pr.mjs" --create --root "{launcher-root}" --branch "{branch}" \
|
|
403
|
+
--work-item "{workItem}" --chat "{chat}" \
|
|
404
|
+
--body-file "{repo}={launcher-root}/workspace-scratchpad/chats/{chat}/pr-{slug}-{repo}.md" \
|
|
405
|
+
--out "{launcher-root}/workspace-scratchpad/chats/{chat}/prs-{slug}.json"
|
|
406
|
+
```
|
|
407
|
+
|
|
408
|
+
Omit `--work-item` when the task has none, and repeat `--body-file` once per repo. `--chat` resolves the task's repos from this chat's record entries for the branch; when detection came from cwd alone, pass `--repo "{repo}"` once per repo instead. The script skips repos whose branch has no commits over `origin/{defaultBranch}` (reporting them as `empty` — no push, no PR, no body file needed, torn down in step 5 like any other; a workspace branch that collected no promotions gets no PR), parses each worktree's own origin into `{owner}/{name}` and stops **before pushing anything** if any origin is not forge-hosted — such a repo is completed under the session model — then pushes `-u origin {branch}` and opens one PR per remaining repo through a per-repo forge aimed at that worktree's own remote, never the launcher's. The workspace repo (`.`) is handled exactly like a project repo. The PR title is the linked issue's title, or the branch's first commit subject without a `{workItem}`; the body is the drawer file with `Closes <ref>` appended, where `<ref>` is `#N` when the PR's repo is the tracker's repo and `{tracker-repo}#N` otherwise — only the first form closes an issue in the PR's own repo. If the push is rejected as non-fast-forward — the branch already existed on origin and step 1's rebase rewrote it — the script stops and says so; ask the user, and only on explicit confirmation re-run the same command with `--force-with-lease` (the script never forces on its own). If `workspace.forge` is `false` in `workspace.json`, the script refuses before pushing anything: forge operations are disabled in this workspace and the PR is opened by hand. `--out` writes the run's JSON to `prs-{slug}.json` for step 4 — `{ prs: [{ repo, owner, name, number, id, url, isWorkspace }], empty: [repo…], pushed: [repo…] }`, printed to stdout as well — instead of shell redirection, so the command is identical on every shell. The run is idempotent: a repo whose branch already has an open PR against `{defaultBranch}` gets that PR reused, never duplicated, and if the run fails partway the file still records what was pushed and opened so far — fix the cause and re-run the same command; it completes the set.
|
|
409
|
+
|
|
410
|
+
4. **Ask before merging, then run `task-pr.mjs --merge`, which also closes the linked issue.** When step 3 reported `prs: []` — every repo was empty — there is nothing to merge: skip this step, say so, and leave the linked issue open for the user to decide (close it by hand, or keep the task going); `--merge` itself refuses an empty PRs file for exactly that reason. Otherwise present a summary per repo that got a PR — the workspace repo included — built from `--create`'s output, and ask once:
|
|
411
|
+
|
|
412
|
+
```
|
|
413
|
+
Task complete:
|
|
414
|
+
|
|
415
|
+
PROJECT: {owner}/{name}
|
|
416
|
+
PR: {pr.url}
|
|
417
|
+
Branch: {branch} → {defaultBranch}
|
|
418
|
+
Commits: {n} # git -C "{worktree}" rev-list --count "origin/{defaultBranch}..{branch}"
|
|
419
|
+
|
|
420
|
+
WORKSPACE: {ws-owner}/{ws-name} # from the workspace worktree's origin
|
|
421
|
+
PR: {ws-pr.url}
|
|
422
|
+
Branch: {branch} → {defaultBranch}
|
|
423
|
+
|
|
424
|
+
Merge all? [Y/n]
|
|
425
|
+
```
|
|
426
|
+
|
|
427
|
+
On "n", stop: the PRs stay open and the worktrees, branches, and record entries stay in place — say so.
|
|
428
|
+
|
|
429
|
+
On "y":
|
|
430
|
+
|
|
431
|
+
```bash
|
|
432
|
+
node "{launcher-root}/.claude/scripts/task-pr.mjs" --merge --root "{launcher-root}" \
|
|
433
|
+
--prs "{launcher-root}/workspace-scratchpad/chats/{chat}/prs-{slug}.json" --work-item "{workItem}"
|
|
434
|
+
```
|
|
435
|
+
|
|
436
|
+
The script checks each PR's state first, then merges the project PRs (squash, delete branch), then the workspace PR only when every project merge succeeded — the workspace branch's promoted context describes the project merges and must never merge ahead of them. A PR the forge already reports as merged counts as done and is never merged twice, which is what makes a re-run safe: the run that finally gets every PR merged also does the pull and the close. The launcher is pulled `--ff-only` only when it sits on the workspace default branch — it waited there on the workspace merge, and a project-only task pulls after the project merges; when it sits on some other branch the JSON reports `pullSkipped: "launcher on <branch>"`, and a failed pull is reported as `pullFailed: true` rather than an error, because the merges stand and the issue still closes — in both cases tell the user to pull the launcher by hand before step 5. The close comes last, with a one-line comment naming the merged PR URL(s): merge precedes close because an issue closed before its PR merges points at work that never landed; without a `{workItem}` (no tracker, or the task was never recorded) the close is skipped and the script says so. On any merge failure the script stops, names the PRs still open, and exits non-zero — nothing is torn down, and re-running `--merge` once the failure is fixed is safe. After a successful merge, delete the drawer's `pr-{slug}-*.md` body files and the spent `prs-{slug}.json`.
|
|
437
|
+
|
|
438
|
+
5. **Tear down only what finished — worktree first, then the record entry**, and only for a repo whose PR merged in step 4, or whose branch was empty and never pushed in step 3; the workspace repo included (`--repo "."`). If a repo's push, PR, or merge failed, or the user declined the merge, leave that repo's worktree, branch, and record entry exactly in place and say so: `--delete-branch` would otherwise `branch -D` commits that exist nowhere but the local worktree. For `.` the script never deletes the branch checked out at the launcher root.
|
|
439
|
+
|
|
440
|
+
```bash
|
|
441
|
+
node "{launcher-root}/.claude/scripts/task-worktree.mjs" --root "{launcher-root}" --remove --repo "{repo}" --branch "{branch}" --delete-branch
|
|
442
|
+
node "{launcher-root}/.claude/scripts/chat-record.mjs" --root "{launcher-root}" --remove-task --chat "{chat}" --work-item "{workItem}" --repo "{repo}"
|
|
443
|
+
```
|
|
444
|
+
|
|
445
|
+
Skip the `--remove-task` line when there is no record entry (no `{workItem}`). Worktree first because a refusal (dirty worktree, slug collision) then leaves both the worktree and its record entry in place — nothing orphaned, safe to retry. `--delete-branch` also removes the local branch: the forge's `deleteBranch` removed only the remote one, so post-merge teardown passes it to clean the local clone; this is the only step that ever passes it. Never pass `--force` without asking the user.
|
|
446
|
+
|
|
447
|
+
## Notes
|
|
448
|
+
- The session tracker's body is the primary source for PR-body synthesis — it captures the full session history alongside specs and plans
|
|
449
|
+
- All repos get PRed and merged together — one approval for all
|
|
450
|
+
- Version bumps, tags, and publish happen in `/release`, not `/complete-work` — this avoids version drift when multiple feature branches land between releases
|
|
451
|
+
- The teardown order is mandatory: project worktrees first, then workspace worktree, then prune, then delete the session folder
|
|
452
|
+
- Context promotion and cleanup are intentional workflow behavior — they bypass normal commit conventions by design
|