--- name: trekcontinue description: Resume the next session in a multi-session trekplan project. Reads .session-state.local.json and immediately begins the next session. argument-hint: "[ | --help]" model: opus --- # Ultracontinue Local v1.0 Zero-friction multi-session resumption. In a fresh Claude Code session, type `/trekcontinue` — the command reads the per-project state file (`.claude/projects//.session-state.local.json`), shows a 3-line summary, and immediately begins executing the next session. The state file is the contract. Any session-end mechanism may write it (`/trekexecute` Phase 8 / Phase 2.55 / Phase 4, the `/trekendsession` helper, or — in the future — `graceful-handoff`). This command only reads. Pipeline position: ``` /trekplan → plan.md /trekexecute → progress.json + .session-state.local.json ... session boundary, fresh chat ... /trekcontinue → reads .session-state.local.json, starts next session ``` See **Handover 7** in `docs/HANDOVER-CONTRACTS.md` for the full schema. ## Phase 0 — `--help` handling Parse `$ARGUMENTS` with `parseArgs($ARGUMENTS, 'trekcontinue')` from `lib/parsers/arg-parser.mjs`. Dispatch the usage block ONLY when one of these two conditions equals exactly true (no substring search, no "contains" check): - `flags['--help'] === true`, OR - `positional[0] === '-h'` (single-dash short form — the parser keeps it as positional because the schema does not declare an alias). In every other case — including when `$ARGUMENTS` is empty, whitespace-only, the literal empty string `""`, or a positional project-dir — fall through to Phase 1. Do NOT print the usage block on empty args. ``` /trekcontinue — Resume the next session in a multi-session trekplan project. Usage: /trekcontinue # auto-discover state file under cwd /trekcontinue # explicit project directory /trekcontinue --cleanup # dry-run: list stale files /trekcontinue --cleanup --confirm # actually delete (requires status: completed) /trekcontinue --help # this message Reads .claude/projects//.session-state.local.json (per-project, gitignored). On a valid resumable state, prints a 3-line summary and begins executing the next session immediately. No interactive confirmation prompt. State-file schema (v1): schema_version 1 project string next_session_brief_path string (validator soft-checks file existence) next_session_label string status in_progress | partial | failed | stopped | completed (completed → no further sessions to resume) updated_at ISO-8601 timestamp (unknown top-level keys are tolerated — forward-compat for graceful-handoff v2.2) Typical flow: /trekbrief # produces brief.md /trekplan --project ... # produces plan.md /trekexecute --project .. # writes session-state on session-end ... (fresh Claude chat) ... /trekcontinue # reads session-state, runs next session ``` ## Phase 0.5 — Cleanup mode dispatch After `parseArgs` has resolved `$ARGUMENTS`, check the parsed `flags` object directly (NOT a string contains-check on raw `$ARGUMENTS` — that substring pattern was the root cause of Bug 1). If `flags['--cleanup'] === true`, switch into the terminal cleanup flow and do NOT proceed to Phase 1 or any later phase. **Required positional:** an explicit `` (`positional[0]`). There is no "clean all" mode — accidental wholesale deletion would be irreversible. If `positional[0]` is missing, empty, or starts with `-`, print this usage block to stderr and exit non-zero: ``` Error: /trekcontinue --cleanup requires . Usage: /trekcontinue --cleanup # dry-run: list stale files /trekcontinue --cleanup --confirm # actually delete (status: completed) ``` **Compute mode from parsed flags:** ``` dryRun = (flags['--confirm'] !== true) confirm = (flags['--confirm'] === true) ``` **Invoke cleanup inline.** Emit the concrete project-dir path as a literal token in the Bash command — never a template placeholder — same anti-substitution rule as Phase 2: ``` node --input-type=module -e "import {cleanupProject} from './lib/util/cleanup.mjs'; const [, dir, mode] = process.argv; const r = cleanupProject(dir, {dryRun: mode !== 'confirm', confirm: mode === 'confirm'}); console.log(JSON.stringify(r, null, 2)); process.exit(r.valid ? 0 : 1)" '' '' ``` Substitute `` with the literal `positional[0]` value you have in your working context, and `` with either the literal string `dryrun` or the literal string `confirm` based on the booleans above. The validator emits a `{valid, errors, warnings, parsed}` JSON record. Print it to stdout. Exit with the validator's exit code. **Cleanup is a terminal mode.** It must not fall through to Phase 1/2/3/4. Operators who want to resume after cleanup must invoke `/trekcontinue` again without `--cleanup`. ## Phase 1 — Resolve project directory The parsed `positional[0]` from Phase 0 is the explicit project-dir argument, when present. Otherwise (empty `$ARGUMENTS` or whitespace-only) auto-discover. ### Step 1.a — Reject `.md` positional argument (SC-2) If `positional[0]` is non-empty AND ends in `.md`, the user almost certainly pasted a `NEXT-SESSION-PROMPT.local.md` path instead of a project directory. Print the following diagnostic to stderr and exit non-zero. Do NOT proceed. ``` Error: expected , got a markdown file path: Did you mean to paste the file path as a project directory? Usage: /trekcontinue ``` ### Step 1.b — Auto-discover candidates When no explicit project-dir was given, enumerate `.claude/projects/*/.session-state.local.json` paths with `node -e` (NOT shell glob — harness-mode safety) and emit each as one JSON line of `{"path": ..., "updated_at": ...}` so Phase 1 can sort numerically: ```bash !`node -e "const fs=require('fs'),path=require('path');const root='.claude/projects';if(!fs.existsSync(root))process.exit(0);for(const d of fs.readdirSync(root)){const p=path.join(root,d,'.session-state.local.json');if(!fs.existsSync(p))continue;let u='';try{u=(JSON.parse(fs.readFileSync(p,'utf8'))||{}).updated_at||''}catch(_){};process.stdout.write(JSON.stringify({path:p,updated_at:u})+'\\n');}"` ``` Sort the emitted candidates by `Date.parse(updated_at)` descending (newest first) — numeric comparison, NOT lexicographic string compare. The newest resumable state wins. ### Step 1.c — Decision tree - **0 candidates and no explicit arg:** print SC-2 cold-start message and exit: ``` No active multi-session project here. Start with /trekbrief or /trekplan. ``` - **1 candidate (or explicit non-`.md` arg):** continue to Phase 2 with that path. - **>1 candidates and no explicit arg:** with the Date.parse sort applied, the newest resumable state wins automatically and the command continues to Phase 2 with that path. (Operators who want a different candidate re-invoke as `/trekcontinue `.) ## Phase 1.5 — Frontmatter consistency check Bug 3 contract: producers (`/trekexecute`, `/trekendsession`) write `NEXT-SESSION-PROMPT.local.md` with YAML frontmatter (`produced_by:`, `produced_at:`). Multiple producers may have written candidates in different locations; this phase refuses ambiguity before validating the state file. After resolving the project directory and state-file path, look for two `NEXT-SESSION-PROMPT.local.md` candidates: a. `/NEXT-SESSION-PROMPT.local.md` — operator-managed master file b. `/NEXT-SESSION-PROMPT.local.md` — producer-written sibling **If both exist:** - Read both via the **Read tool** (NOT Bash — same anti-substitution rule as Phase 2). - Invoke the consistency validator with both paths emitted as concrete literal tokens (no template substitution at the Bash boundary): ``` node lib/validators/next-session-prompt-validator.mjs --json --consistency ``` Replace `` and `` with the two concrete filesystem paths you have in your working context. The validator emits `{valid, errors, warnings}` JSON on stdout. - **If `valid: false`** (typically `NEXT_SESSION_PROMPT_PRODUCER_MISMATCH`): print the structured `errors[]` (each `[code] message` on its own line), list both candidate paths, and exit non-zero. Do NOT proceed to Phase 2. Resolve the conflict by deleting the stale candidate (run `/trekcontinue --cleanup --confirm ` after the current session closes, or remove by hand). - **If `valid: true` with a `NEXT_SESSION_PROMPT_WALL_CLOCK_DRIFT` warning** (one of the candidates is more than 24h old): print the warning to stderr but continue — long pauses (weekend, vacation) are not failures. - **If `valid: true` with a `NEXT_SESSION_PROMPT_STALE_IGNORED` warning** (one candidate is older than the state file's `updated_at`): print the warning and continue. The state-anchored check is the primary refusal signal; staleness simply rejects the older candidate. **If only one exists:** continue to Phase 2. No comparison needed. **If neither exists:** continue to Phase 2. Legacy projects and first-run flows have no NEXT-SESSION-PROMPT files. ## Phase 2 — Validate the state file Phase 1 resolved a concrete state-file path. That path is a real string in your working context — never a template. Phase 2 must read and validate the state file without any placeholder substitution. ### Step 2.a — Read the file with the Read tool (no Bash) Use the **Read tool** on the resolved state-file path from Phase 1. Do NOT use Bash for the read. The Read tool is deterministic and not subject to shell-substitution errors. Parse the returned JSON body programmatically. ### Step 2.b — Schema-validate the parsed object Verify the schema by invoking the existing validator CLI shim. Emit the resolved absolute path as a literal string token in the Bash command — the exact same string you just passed to the Read tool. The validator accepts `--json ` and prints a `{valid, errors, warnings}` JSON record: ``` node lib/validators/session-state-validator.mjs --json ``` Replace `` with the actual path string at the time you issue the Bash call. There is no template engine; the string is substituted by you, the model, before the Bash tool sees the command. **Anti-substitution invariant.** If you ever find yourself about to emit a literal angle-bracket placeholder, or any other unresolved variable name, to the Bash tool — STOP. The resolved path is a concrete value you already have from Phase 1; emit the value, not a placeholder for it. ### Step 2.c — Interpret the result - **Validator exit code != 0 OR `valid: false` in JSON output:** print the structured `errors[]` (each `[code] message` on its own line) and exit. Do not proceed to narration. Suggest running the validator directly for follow-up: `node lib/validators/session-state-validator.mjs `. - **`valid: true` AND any warning has `code: SESSION_STATE_NOT_RESUMABLE`** (i.e. `status: completed`): print "no further sessions to resume; project complete" and exit cleanly. - **`valid: true` AND status is one of `in_progress | partial | failed | stopped`:** proceed to Phase 3. ## Phase 3 — Narrate 3-line summary Print this exact template (using values from the validated `parsed` object): ``` Project: {project} Next session: {next_session_label} Brief: {next_session_brief_path} ``` No interactive confirmation prompt — per the brief NFR ("ingen prompts, men la informasjon synes"). The 3-line block is informational only. ## Phase 4 — Begin execution Read the file at `next_session_brief_path` (it is the brief that the next session is supposed to execute — typically the same `brief.md` for single-brief multi-session plans, or a session-specific spec for parallel session decomposition). Understand the task and begin executing per the standard trekplan pipeline. The user did not type a separate "start" command — `/trekcontinue` is the start. If the brief file does not exist (validator emits a warning but does not fail), print: `Warning: next_session_brief_path "{path}" does not exist on disk. Cannot continue automatically.` and exit. Do not guess. ## Phase 5 — Stats tracking Append a one-line JSON record to `${CLAUDE_PLUGIN_DATA}/trekcontinue-stats.jsonl` if the env var is set; silently skip otherwise. ```json {"ts":"","project":"","next_session_label":"