7.9 KiB
| name | description | argument-hint | disable-model-invocation |
|---|---|---|---|
| trekendsession | Mark the current session as complete and write session-state pointing at the next session. Helper for informal multi-session flows. | <next-brief-path> <next-label> | --help | true |
Voyage 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 /trekcontinue can resume in a fresh Claude chat.
For formal flows (a plan produced by /trekplan --project),
/trekexecute 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
/trekexecute.
Pipeline position:
... session N work ...
/trekendsession <brief> "<next-label>" → writes state
... session boundary, fresh chat ...
/trekcontinue → reads state, starts session N+1
See Handover 7 in docs/HANDOVER-CONTRACTS.md for the schema.
Phase 0 — --help handling
Parse $ARGUMENTS with the arg parser. Run this via the Bash tool at runtime
(NOT eager-exec):
setopt nonomatch 2>/dev/null || true
node ${CLAUDE_PLUGIN_ROOT}/lib/parsers/arg-parser.mjs --command trekendsession -- $ARGUMENTS
It prints the parse result as JSON. On exit 1, print its errors and stop.
Print the usage block below and exit cleanly ONLY when one of these two conditions equals exactly true (no substring search, no "contains" check):
flags['--help'] === true, ORpositional[0] === '-h'(single-dash short form — the parser keeps it as positional because the schema declares no alias).
Then do NOT proceed to any further phase. In every other case — a slug or a
label that holds -h or help too — go on to Phase 1.
/trekendsession — Mark current session done; point at next session.
Usage:
/trekendsession <next-brief-path> <next-label>
/trekendsession --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 /trekcontinue will show in the next session.
Example:
/trekendsession .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):
!`node --input-type=module -e "import {existsSync, readdirSync} from 'node:fs'; import {join} from 'node:path'; const root='.claude/projects'; if(!existsSync(root)) process.exit(0); readdirSync(root).filter(d=>existsSync(join(root,d,'brief.md'))).forEach(d=>process.stdout.write(join(root,d)+'\\n'));"`
Decision tree:
- 0 candidates: print error to stderr — "no
.claude/projects/<dir>/brief.mdfound 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
cdinto the intended project directory before retrying. Exit 1.
Phase 2 — Required args check (headless-safe)
Read the parse result from Phase 0: <next-brief-path> is positional[0]
and <next-label> is positional[1]. Both are required. If either is
missing or empty:
Error: missing required args.
Usage: /trekendsession <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 /trekcontinue --help to see the full pipeline.
Phase 3 — Atomically write .session-state.local.json + sibling NEXT-SESSION-PROMPT.local.md
Write {project_dir}/.session-state.local.json with the schema-v1 object:
{
"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. The util is ESM, so invoke via
node --input-type=module -e with an import statement (a CommonJS shim
would throw ERR_REQUIRE_ESM on Node 18+ since atomic-write.mjs is ESM).
Under node --input-type=module -e "<script>" arg1 arg2 arg3, Node sets
process.argv[0] to the node binary path and user args start at
process.argv[1]. Adjust the destructure if your Node version differs.
This phase ALSO writes a sibling NEXT-SESSION-PROMPT.local.md in the
project directory with YAML frontmatter (produced_by: trekendsession,
produced_at: {ISO-8601}, project: {project_dir}). Both files are written
in a single ESM block so the writes succeed or fail together.
Run the block below via the Bash tool at runtime, substituting the resolved
values for the {curly} placeholders (Phase 1 gives {project_dir}, Phase 2
gives {next_brief_path} and {next_label}). This is NOT an eager-exec
block — the values do not exist at command-load time. The import path must
stay absolute via ${CLAUDE_PLUGIN_ROOT} — your Bash cwd is the user's
repo, not the plugin root, so a cwd-relative import throws
ERR_MODULE_NOT_FOUND:
node --input-type=module -e "
import path from 'node:path';
import { writeFileSync } from 'node:fs';
import { atomicWriteJson } from '${CLAUDE_PLUGIN_ROOT}/lib/util/atomic-write.mjs';
const [, dir, brief, label] = process.argv;
const now = new Date().toISOString();
const stateObj = { schema_version: 1, project: dir, next_session_brief_path: brief, next_session_label: label, status: 'in_progress', updated_at: now };
const stateFile = path.join(dir, '.session-state.local.json');
atomicWriteJson(stateFile, stateObj);
const promptFile = path.join(dir, 'NEXT-SESSION-PROMPT.local.md');
const promptBody = '---\\nproduced_by: trekendsession\\nproduced_at: ' + now + '\\nproject: ' + dir + '\\n---\\n\\n# ' + label + '\\n\\nResume via /trekcontinue.\\n';
writeFileSync(promptFile, promptBody);
console.log(stateFile);
console.log(promptFile);
" '{project_dir}' '{next_brief_path}' '{next_label}'
Phase 4 — Validate + narrate
Validate the freshly-written state file via the Bash tool at runtime,
substituting the resolved {project_dir} (NOT eager-exec — the file does
not exist at command-load time):
node ${CLAUDE_PLUGIN_ROOT}/lib/validators/session-state-validator.mjs --json {project_dir}/.session-state.local.json
If valid: true, print the success block matching /trekcontinue 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 /trekcontinue 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 (the frontmatter field keeps the model from starting it; the operator types it).
- Idempotent within a session. Running twice with the same args overwrites cleanly (atomic rename); does not double-advance.