feat(ultraplan-local): add /ultraplan-end-session helper for informal multi-session flows [skip-docs]
Tiny helper command for ad-hoc multi-session flows that don't run through /ultraexecute-local. Writes .session-state.local.json so /ultracontinue can resume in a fresh chat. Required args (next-brief-path, next-label) — no inline prompt, headless-safe. Validates via session-state-validator and prints the same 3-line narration that /ultracontinue Phase 3 uses (SC-8 cross-project consistency). Step 9 of /ultracontinue v3.3.0. README/CLAUDE updates land in Step 11.
This commit is contained in:
parent
5688512898
commit
2690ab501f
1 changed files with 156 additions and 0 deletions
156
plugins/ultraplan-local/commands/ultraplan-end-session-local.md
Normal file
156
plugins/ultraplan-local/commands/ultraplan-end-session-local.md
Normal file
|
|
@ -0,0 +1,156 @@
|
|||
---
|
||||
name: ultraplan-end-session-local
|
||||
description: Mark the current session as complete and write session-state pointing at the next session. Helper for informal multi-session flows.
|
||||
argument-hint: "<next-brief-path> <next-label> | --help"
|
||||
model: sonnet
|
||||
---
|
||||
|
||||
# Ultraplan End-Session Local v1.0
|
||||
|
||||
Tiny helper for **informal** multi-session flows (no formal plan with
|
||||
Execution Strategy). Writes a `.session-state.local.json` pointing at the
|
||||
next session so `/ultracontinue` can resume in a fresh Claude chat.
|
||||
|
||||
For formal flows (a plan produced by `/ultraplan-local --project`),
|
||||
`/ultraexecute-local` Phase 8 already writes the state file — this helper
|
||||
is unnecessary there. Use this command for ad-hoc release runs, manual
|
||||
multi-session handovers, or any flow that does not run through
|
||||
`/ultraexecute-local`.
|
||||
|
||||
Pipeline position:
|
||||
|
||||
```
|
||||
... session N work ...
|
||||
/ultraplan-end-session-local <brief> "<next-label>" → writes state
|
||||
... session boundary, fresh chat ...
|
||||
/ultracontinue → reads state, starts session N+1
|
||||
```
|
||||
|
||||
See **Handover 7** in `docs/HANDOVER-CONTRACTS.md` for the schema.
|
||||
|
||||
## Phase 0 — `--help` handling
|
||||
|
||||
If `$ARGUMENTS` contains `--help` or `-h`, print the usage block below and exit
|
||||
cleanly. Do NOT proceed to any further phase.
|
||||
|
||||
```
|
||||
/ultraplan-end-session-local — Mark current session done; point at next session.
|
||||
|
||||
Usage:
|
||||
/ultraplan-end-session-local <next-brief-path> <next-label>
|
||||
/ultraplan-end-session-local --help
|
||||
|
||||
Both arguments are REQUIRED. No interactive prompt — headless-safe.
|
||||
|
||||
Writes <project-dir>/.session-state.local.json with:
|
||||
schema_version 1
|
||||
project <auto-resolved from cwd>
|
||||
next_session_brief_path <next-brief-path argument>
|
||||
next_session_label <next-label argument>
|
||||
status in_progress
|
||||
updated_at <now, ISO-8601>
|
||||
|
||||
Then validates via lib/validators/session-state-validator.mjs and prints
|
||||
the same 3-line narration that /ultracontinue will show in the next session.
|
||||
|
||||
Example:
|
||||
/ultraplan-end-session-local .claude/projects/2026-05-01-feature/brief.md "Session 2 of 3"
|
||||
```
|
||||
|
||||
## Phase 1 — Resolve project directory
|
||||
|
||||
Resolve the nearest `.claude/projects/*/brief.md` from cwd (the current working
|
||||
directory). Use `node -e` enumeration (NOT shell glob — harness-mode safety):
|
||||
|
||||
```bash
|
||||
!`node -e "const fs=require('fs'),path=require('path');const root='.claude/projects';if(!fs.existsSync(root)){process.exit(0)}const dirs=fs.readdirSync(root).filter(d=>fs.existsSync(path.join(root,d,'brief.md'))).map(d=>path.join(root,d));dirs.forEach(p=>process.stdout.write(p+'\\n'));"`
|
||||
```
|
||||
|
||||
Decision tree:
|
||||
|
||||
- **0 candidates:** print error to stderr — "no `.claude/projects/<dir>/brief.md`
|
||||
found under cwd; cannot determine project directory" — and exit 1. Do NOT
|
||||
fall back to a synthesized path.
|
||||
- **1 candidate:** use it as `<project-dir>`. Continue.
|
||||
- **>1 candidates:** print all paths and ask the operator to `cd` into the
|
||||
intended project directory before retrying. Exit 1.
|
||||
|
||||
## Phase 2 — Required args check (headless-safe)
|
||||
|
||||
Read `$ARGUMENTS`. Both `<next-brief-path>` and `<next-label>` are required.
|
||||
If either is missing or empty:
|
||||
|
||||
```
|
||||
Error: missing required args.
|
||||
Usage: /ultraplan-end-session-local <next-brief-path> '<next-label>'
|
||||
```
|
||||
|
||||
Print to stderr and exit 1. **No interactive prompt** — this keeps the helper
|
||||
headless-safe (per brief NFR; addresses adversarial-review major #11). If you
|
||||
want an interactive flow, use `/ultracontinue --help` to see the full pipeline.
|
||||
|
||||
## Phase 3 — Atomically write `.session-state.local.json`
|
||||
|
||||
Write `<project-dir>/.session-state.local.json` with the schema-v1 object:
|
||||
|
||||
```json
|
||||
{
|
||||
"schema_version": 1,
|
||||
"project": "<project-dir>",
|
||||
"next_session_brief_path": "<arg 1>",
|
||||
"next_session_label": "<arg 2>",
|
||||
"status": "in_progress",
|
||||
"updated_at": "<now, ISO-8601>"
|
||||
}
|
||||
```
|
||||
|
||||
Use the atomic-write util — write to `<path>.tmp`, then `rename` into place —
|
||||
to avoid partial-state on crash. Inline-call pattern:
|
||||
|
||||
```bash
|
||||
!`node -e "
|
||||
const path=require('path');
|
||||
const {atomicWriteJson}=require('./lib/util/atomic-write.mjs');
|
||||
const obj={schema_version:1,project:process.argv[1],next_session_brief_path:process.argv[2],next_session_label:process.argv[3],status:'in_progress',updated_at:new Date().toISOString()};
|
||||
atomicWriteJson(path.join(process.argv[1],'.session-state.local.json'),obj);
|
||||
console.log(path.join(process.argv[1],'.session-state.local.json'));
|
||||
" '<project-dir>' '<next-brief-path>' '<next-label>'`
|
||||
```
|
||||
|
||||
(Note: `atomic-write.mjs` is ESM; if the inline `require()` form fails in your
|
||||
Node version, fall back to `node --input-type=module -e "..."` with `import`.)
|
||||
|
||||
## Phase 4 — Validate + narrate
|
||||
|
||||
Validate the freshly-written state file:
|
||||
|
||||
```bash
|
||||
!`node lib/validators/session-state-validator.mjs --json <project-dir>/.session-state.local.json`
|
||||
```
|
||||
|
||||
If `valid: true`, print the success block matching `/ultracontinue` Phase 3
|
||||
narration (SC-8 cross-project consistency — same template both sides):
|
||||
|
||||
```
|
||||
Session state written: <project-dir>/.session-state.local.json
|
||||
|
||||
Project: <project-dir>
|
||||
Next session: <next-label>
|
||||
Brief: <next-brief-path>
|
||||
|
||||
In a fresh Claude session, run /ultracontinue to resume.
|
||||
```
|
||||
|
||||
If `valid: false`, print the structured `errors[]` and exit 1. Investigate
|
||||
before retrying — usually means a bad path or label argument.
|
||||
|
||||
## Hard rules
|
||||
|
||||
- **Required args, no defaults.** Never invent a brief path or session label.
|
||||
If args are missing, fail loud.
|
||||
- **Atomic write only.** Tmp + rename — no partial state files on disk.
|
||||
- **Zero secrets.** Status, paths, labels — never API keys, never user content
|
||||
beyond filenames.
|
||||
- **NEVER auto-invoke this command.** It is operator-typed only at session-end.
|
||||
- **Idempotent within a session.** Running twice with the same args
|
||||
overwrites cleanly (atomic rename); does not double-advance.
|
||||
Loading…
Add table
Add a link
Reference in a new issue