CLI¶
Audience. Daily user and Developer.
condash is the command-line companion to the dashboard. A single binary on PATH dispatches both modes: bare condash (no args, or condash gui) launches the packaged Electron GUI; condash <noun> <verb> [args] runs the bundled CLI script with no window — output goes to stdout / stderr.
The CLI exists because skills (/projects, /knowledge) and shell scripts need a programmatic surface that shares condash's parser, validator, and indexer — without re-implementing them in bash + grep + sed.
At a glance¶
| Invocation | What it does |
|---|---|
condash |
Launch the packaged Electron GUI against the saved conception tree |
condash --help |
Print the top-level CLI help |
condash --version |
Print the CLI version |
condash <noun> <verb> |
Run a CLI verb against the resolved conception path |
For running condash from a source clone (make install, make dev, make package), see Dev launch.
How dispatch works¶
One binary, one launcher: the condash entry on PATH inspects its argv. With no positional argument (or with the literal gui first), it boots the Electron GUI. Anything else runs the bundled CLI script in plain-Node mode (no Chromium, no window) — the dispatcher is deliberately not noun-aware (src/main/dispatch.ts), so it is the CLI itself that reports an unknown noun and exits with code 2 (usage).
CLI nouns (Daily):
Maintenance — hidden from the top-level help's Daily list, fully functional:
projects backfill-closed, projects rewrite-headers, and projects scan-promotions are one-shot migrations marked [internal] in the top-level help; the invocations are unchanged.
A typo (condash projct list) reports an unknown noun and exits with code 2 (usage).
code is not a CLI noun: condash code reports Unknown noun: code and exits 2. The Code pane is GUI-only — the CLI equivalent is worktrees (setup, check, mismatch, remove), repos list, and audit --include worktrees for the same lifecycle checks. And condash --list does not exist either: --list is not a valid flag, so there is no way to ask the CLI to enumerate its nouns — condash help prints them.
Universal flags¶
Available on every noun:
| Flag | Meaning |
|---|---|
--conception <path> |
Override the conception root for this invocation only |
--json |
Emit a single JSON envelope on stdout |
--ndjson |
Emit one JSON object per line (streaming-friendly) |
--quiet, -q |
Suppress diagnostics on stderr |
--no-color |
Disable ANSI styling |
--help, -h |
Show help for this noun / verb |
--version, -v |
Show version |
--json and --ndjson are mutually exclusive. When neither is set, stdout is human-readable text.
Exit codes¶
Code 5 means the CLI could not resolve a conception path — pass --conception <path> or set lastConceptionPath via condash config set lastConceptionPath <path>.
Conception-path resolution¶
The CLI honours the same chain as the GUI, minus the folder picker:
--conception <path>flag.CONDASH_CONCEPTION_PATHenvironment variable (legacy aliasCONDASH_CONCEPTIONstill accepted).CLAUDE_PROJECT_DIRenvironment variable (back-compat for Claude Code sessions).- Walk-up from the current working directory looking for
.condash/settings.json,condash.json, orconfiguration.jsonnext to aprojects/directory. lastConceptionPathin${XDG_CONFIG_HOME:-~/.config}/condash/settings.json(or platform equivalent).- Hard error (exit 5).
condash config conception-path prints the currently resolved conception path. To change it, use condash config set lastConceptionPath <path>. condash init needs no resolved path either — it creates the tree instead.
Nouns¶
init¶
Bootstrap a conception tree from the bundled template — the CLI equivalent of the GUI's first-launch init dialog (Get started — first launch).
condash init # initialise the current directory
condash init --path <dir> # initialise <dir> (created if missing)
init lays down the whole template: AGENTS.md (with the {{ conception_name }} / {{ description }} tokens filled from the target directory name), the shipped skills under .agents/skills/, knowledge/, projects/, and a .condash/settings.json materialised from the shipped .example.
Existing files are never overwritten — the same guarantee as the GUI init. Re-running init on an already-initialised tree creates nothing, reports the paths that were created (none) and that existing files were left untouched, and exits 0. The verb is the CLI cold-start fix: it is the one way to create a conception without launching the GUI.
projects¶
Item lifecycle and reads.
| Verb | What it does |
|---|---|
list |
List items, optionally filtered by --status, --kind, --apps, --branch, --parent, and sorted by --sort (status (default) | slug | date). --parent <slug> lists one plan's spin-off subprojects — the slug is resolved leniently, so a short form works |
read <slug> [--with-notes] |
Read one item by slug or path. --with-notes also returns the contents of every notes/*.md |
activity [--begin <YYYY-MM-DD>] [--end <YYYY-MM-DD>] [--format md] |
Generic project-tree activity over a date range (default: last 7 days): every ## Timeline beat parsed into items + dated events + day/week/month/app indices. --json is the reusable data layer for digest tooling and dashboards; plain output is a one-look summary; --format md emits a no-frills markdown digest |
resolve <slug> |
Resolve a slug to its absolute path |
search <query> |
Full-text search across items, optional --status / --kind / --limit |
validate [<slug>] |
Validate header fields against the schema; pass --all for the whole tree, or --path <readme> to check one specific README (must be inside <conception>/projects/) |
status get <slug> / status set <slug> <new-status> |
Read or change the status field; a done-edge appends the same Closed. / Reopened. timeline entry as the verbs below, annotated with --summary <text> when given |
close <slug> |
Set status to done (or --status <name>) and append a Closed. timeline entry, annotated with --summary <text> when given. --no-touch-dirty skips touching projects/.index-dirty, for a caller that regenerates the indexes itself |
reopen <slug> |
Move done back to now (or --status <s>) and append a Reopened. timeline entry, annotated with --summary <text> when given |
backfill-closed [--dry-run] |
Append a Closed. timeline entry to legacy done items missing one |
index [--dry-run] [--rewrite-aggregated] |
Regenerate every projects/**/index.md from the on-disk tree; clear projects/.index-dirty |
create --kind <k> --slug <s> --title "<t>" [--apps "<a>"] [flags] |
Create a new project / incident / document folder + README from the canonical template. --apps is optional (defaults to [] — the GUI create modal omits the field; a fresh tree with an empty registry gets a one-line stderr nudge pointing at condash applications add). --status accepts now \| review \| later \| backlog (default now); done is rejected — use condash projects close to flip status to done. --date <YYYY-MM-DD> overrides today (it picks both the YYYY-MM/ bucket and the folder's date prefix), --branch <name> and --base <ref> seed the matching header fields, and --parent <slug> records the plan this item spins off from — the slug is resolved against the tree and stored in its canonical dated form, so a short form works and an unknown one exits 4 / 6. Incidents add --severity + --severity-impact + --environment. Every create also emits a Prior art block — the two most recent items sharing an app, and up to five knowledge/ lines matching the title — so what the tree already knows arrives without anyone remembering to go and look for it (priorArt in the JSON envelope; omitted entirely when nothing matches) |
scan-promotions <slug> |
Walk a closed item's notes for "always / never / next time / use X" cues that suggest a knowledge promotion; print suggestions |
check-knowledge <slug> [--record] |
Signal whether a done project still needs a knowledge-promotion check (read-only). --record appends the dated Checked knowledge promotion marker after a real review (the mechanical recorder the /knowledge skill calls — never hand-typed). No mass/backfill writer: the marker is only ever written for a project that was actually reviewed |
rewrite-headers [--dry-run] |
One-shot migration of legacy bold-prose headers to YAML frontmatter; idempotent (already-YAML files are no-ops). Skips any README whose body has unexpected content between the meta block and the first ## heading |
Slug forms accepted:
- Full dated:
2026-04-17-foo— exact folder name, minus the month dir. - Short:
foo— any part of the slug after the date prefix. - Month-qualified:
2026-04/2026-04-17-foo.
--summary (on close, reopen, and status set) must be single-line: the
text is written into one ## Timeline bullet, so a carriage return, newline, or
U+2028 / U+2029 line separator surviving the surrounding whitespace trim is a
hard error and nothing is written. Leading and trailing breaks are trimmed away
and are fine — only an interior one fails. Worth knowing when the value comes
from a shell variable or a paste: --summary "$(…)" and text copied out of a PDF
or web page are the usual sources. The flag is validated on every transition,
including ones that write no timeline entry at all.
knowledge¶
Knowledge-tree operations.
| Verb | What it does |
|---|---|
tree |
Render the knowledge index as a tree, depth-limited via --depth (default: unlimited) |
verify [--max-age <days>] |
Audit verification stamps (**Verified:** YYYY-MM-DD) and report any file whose oldest stamp is older than --max-age days (default 90). A file with several stamps is reported once, on its oldest, with its newest named alongside |
retrieve <query> |
Find relevant knowledge for a topic, by --mode (triage, grep, both; default both). both runs both layers — the index-keyword triage walk and a full-text grep — and never suppresses one because the other matched. Grep scores each line by how many query tokens it carries, weighted by how rare each token is across the tree, with an exact-phrase bonus on top |
stamp <path> --where <text> |
Add or refresh a verification stamp on a knowledge file. --where is required — it is the provenance string written into the stamp (e.g. "condash@abc1234 on main"). --date <iso> overrides today; --line <n> names the 1-based line of the stamp to replace, and is required when the file carries more than one (the verb refuses rather than guess — verify reports the line of the stale stamp); --insert-after "<heading>" names the heading to insert below when the file carries no stamp yet. <path> must resolve inside the conception tree |
index [--dry-run] [--rewrite-aggregated] |
Regenerate every knowledge/**/index.md from the on-disk tree; clear knowledge/.index-dirty |
search¶
Cross-tree full-text search.
--scope accepts all, projects, knowledge, resources, skills, logs. Defaults to all, which forwards the four indexed markdown scopes (projects, knowledge, resources, skills) — terminal-session logs are disk-scanned and only searched under the explicit --scope logs. --limit <n> caps the hit count.
repos¶
List configured repositories from .condash/settings.json (or the legacy condash.json).
condash repos list # configured repos
condash repos list --include-worktrees # … each one's worktrees too (slower)
--include-worktrees runs a git worktree list per repo, so it costs an extra process per entry — off by default. condash worktrees list is an alias for exactly this invocation.
applications¶
The app registry — one canonical #handle per app, with its label + path. The handle is the single reference used in card pills, project README apps: lists, the generated AGENTS.md Apps table, the colour hash, and search. Live apps are the repositories[] entries — submodules included: a submodules[] entry is a first-class app whose handle resolves everywhere a handle is expected, carrying its parent's handle in parent. Defunct handles that closed projects still reference live in retired_apps. Either may carry aliases (legacy spellings that resolve to the handle).
| Verb | What it does |
|---|---|
list |
List every registered app (live + retired), with handle, label, path; submodules render indented under their parent (↳ #child) |
add <handle> --path <p> [--label <l>] [--purpose <t>] |
Register a new live app (top-level only — there is no parent flag, so a submodule cannot be created here) |
set <handle> [--label <l>] [--purpose <t>] [--path <p>] |
Update a registered app — any handle list shows, submodules included. An entry written as a bare string is widened to object form so it can hold the field; its handle is unchanged |
rename <old> <new> |
Rename a handle; records the old as an alias and rewrites every project README apps: reference that pointed at it |
sync-docs |
Regenerate the Apps table in AGENTS.md between the condash:apps sentinels from the registry — App, Repo, Purpose, AGENTS.md, Knowledge; submodule rows render right after their parent with a ↳-prefixed App cell (agent-specific files like CLAUDE.md are virtual agedum renders of AGENTS.md — never written to disk) |
validate [--fix] |
Every project README apps: value must resolve to a known #handle (live or retired) or an existing absolute path; unknown handles exit 3, alias hits are reported with a suggested rewrite. --fix canonicalises every resolvable value to its #handle (bare names and legacy aliases alike) and leaves only the unresolvable ones for a human |
condash applications list --json
condash applications validate # exit 3 on an unresolved reference
condash applications rename fovea fovea-web
worktrees¶
Worktree-centric operations on top of the conception's configured repositories (.condash/settings.json, or legacy condash.json).
| Verb | What it does |
|---|---|
list |
Print every worktree, grouped by primary, with branch + dirty status. An alias for repos list --include-worktrees — same payload, no extra flags of its own |
check <branch> |
Per-branch state: which items declare it, per-repo worktree✓/branch✓/primary-on-branch/pinned flags, missing or orphan dirs |
mismatch |
Report worktrees referenced by an item's branch field that don't exist on disk (or vice versa) |
setup <branch> [--repo <r>...] [--no-env] [--no-install] [--base <ref>] |
Create the worktree for <branch> in every primary (or the listed --repo subset). --no-env skips env wiring; --no-install skips the per-repo install: hook (which runs by default); --base <ref> overrides the base. With no --base, the base comes from the declaring items' base: header fields (which must agree); with no base at all, each repo branches from its own default-branch tip — origin/HEAD, else local main / master, else the primary checkout's HEAD. No fetch is run, so a base ref trailing its upstream only earns a warning. Re-running setup on an already-present worktree backfills only the declared env files that worktree is missing — an existing copy is never overwritten, and install: does not re-run; --no-env suppresses that backfill too. Exit code: 1 (runtime) when any per-repo install: command fails; blocked repos (pinned, primary-on-branch, missing base ref) are expected outcomes reported under blocked and do not affect the exit code |
remove <branch> [--repo <r>...] [--force] [--force-rm] |
Tear down <branch> worktrees and (if safe) the local branch. --force passes through to git worktree remove --force (deletes even if dirty); --force-rm implies --force and rm -rf's the leftover dir if git deregistered the worktree but left files behind (typical with node_modules). Without --force-rm, half-removed entries are reported under partiallyRemoved[] so the caller can distinguish them from genuinely protected repos |
A declaring item's apps: tokens and explicit --repo values resolve to a repo by its #handle, its directory name, or a configured alias — so a repo whose handle differs from its directory (e.g. #vcoeur → vcoeur.com) is matched either way. The worktree directory is always named after the canonical directory name, so every spelling lands on the same <worktrees_path>/<branch>/<dir>/.
remove refuses outright when <branch> matches long_lived_branches (default main + master, glob patterns accepted): every requested repo comes back under protected[] with a "remove manually if really intended" reason, and nothing is deleted — --force does not override it.
audit¶
Tree-wide health checks. CLI-only — the GUI has no audit surface, so this noun (and the /knowledge verify skill action that wraps it) is the whole story.
| Check | What it flags |
|---|---|
lfs |
Files that should probably live in Git LFS but are tracked as blobs |
binaries |
Binary files (PDF, .docx, images > size threshold) that may need migrating |
cross-repo |
Cross-repo wikilinks or relative paths that escape the conception |
worktrees |
Same shape as worktrees mismatch — items declaring a branch with no on-disk worktree, or vice versa |
index |
Structural index.md problems under knowledge/ — missing index, dangling links, orphan body files |
stale-index |
index.md files under projects/ or knowledge/ whose content has drifted from the tree (a regen would rewrite them); autofix re-runs condash <tree> index |
stale-verification |
Knowledge body files whose oldest **Verified:** stamp is older than the freshness threshold (default 90 days). Shares its engine with condash knowledge verify, so the two agree on what counts as stale. Never auto-fixed — a stale stamp means a human must reread the source, not bump the date |
check-knowledge-deferred |
Projects with a deferred knowledge promotion (a [knowledge-recheck:pending] timeline marker) never resolved by a later [knowledge-recheck:done]. Checked across all statuses, done included |
check-knowledge |
done projects whose last timeline entry isn't Checked knowledge promotion — the promotion review is missing or stale. Resolve by doing the real /knowledge review, then projects check-knowledge <slug> --record. Legacy done projects stay flagged until actually reviewed (no backfill shortcut) |
hooks |
A hook script under .claude/hooks/ that no settings file registers (.claude/settings.json, .claude/settings.local.json). The file is present and executable and reads as live, while nothing runs it — so a skill calling it a backstop is promising something the tree does not deliver. Never auto-fixed: registering it or deleting it is a judgment call |
--include <list> restricts to a comma-separated subset (or all).
Each issue in --json mode carries a fix object: { action, autoFix, ...payload }. autoFix: true flags issues a wrapping skill (e.g. /knowledge verify) can mechanically apply once batched confirmation is given; autoFix: false flags items that need human judgment. The same shape is shared with condash knowledge verify --json's issues[] array, so triage skills consume audit + verify uniformly.
dirty¶
Read or touch the dirty-index sentinels (projects/.index-dirty, knowledge/.index-dirty).
| Verb | What it does |
|---|---|
list |
Show which trees have a dirty marker |
touch <tree> |
Mark a tree dirty — <tree> is projects or knowledge |
clear <tree\|all> |
Clear one tree's marker, or all of them |
The skills (/projects index, /knowledge index) clear these after they regenerate.
sync¶
The conception's single writer to git. When several agent sessions work in one checkout, each on its own item, and each commits its own work, they corrupt each other three ways:
- The git index is shared process-wide. Session A runs
git add, session B runsgit commita second later, and B's commit carries A's staged files. Path-scoping B's commit doesn't help — the pathspec scopes what is matched, not what is committed out of the index. index.mdfiles are fan-in.projects/index.mdandprojects/<YYYY-MM>/index.mdare regenerated from every item's front-matter, so whoever commits one commits every other session's status flips. No session considers them theirs, so they sit dirty.- Pushes race. Concurrent pushes reject non-fast-forward, and the reflex
git pull --rebaserewrites the working tree under a session that is mid-edit.
A conception has one author — or a small team, each on their own checkout, all pushing to one remote — and no CI, so if exactly one process ever commits, all three dissolve at once: one writer means no index race, the writer owns index.md and regenerates it before committing, and nothing else writes the remote so every push is a fast-forward.
| Verb | What it does |
|---|---|
run [--dry-run] [--no-push] [--quiet-period <secs>] |
The sweeper — commit settled changes and push. sync run executes; bare condash sync is a dry-run |
commit <item> --message "<subject>" [--dry-run] [--no-push] |
Manual milestone commit for one item, under the same lock |
Bare condash sync (no verb) is a dry-run: it reports exactly what a sweep would commit and push — the would commit … lines, the would-be index regenerations, the skipped paths — and writes no git state. That is the safe default: a bare invocation can never commit-and-push a tree by accident. Use sync run when you mean to execute; the sweeper and the auto-commit engine already call sync run explicitly. --dry-run keeps working on run for an explicit plan.
run, in order:
- Takes an exclusive, non-blocking lock at
<git-dir>/condash-sync.lock. If held, exits 0 — the next tick picks the work up. - Refuses (exit 3) mid-merge, mid-rebase, mid-cherry-pick, mid-revert, or with a conflicted tree.
- Fetches the remote and fast-forwards it (when integration is on and pushing): the fetch is read-only, and a remote that is ahead-only — commits on the remote, none local — is fast-forwarded (
git merge --ff-only) so the sweep's own commits keep the push a fast-forward. A genuine divergence (commits on both sides) or a failed fetch/fast-forward refuses the push but still lets the sweep commit local work.autoSync.integration: 'off'skips this step entirely. - Considers every tracked, non-gitignored change in the tree.
git statusalready omits gitignored paths, so the per-harness generated views (.claude/*,.pi/*,CLAUDE.md,.kimi/, …),resources/local/,projects/**/local/, and the.index-dirtysentinels never even reach the sweeper. Everything that remains gets a committer: item andknowledge/paths as today, and anything outside those two trees — rootAGENTS.md,.agents/**, config files (opencode.json,.claude/settings.json, …),resources/,tasks/— swept into onemeta: synccommit. That is the point: once a checkout adopts "syncis the only committer," any file the sweeper skipped would have no committer at all, so it skips nothing but the gitignored. A malformed path inside a tree (sayprojects/stray.md) is the one exception — reportedunresolved, never committed, because a misplaced file is a mistake worth surfacing. - Skips any path whose mtime is younger than the quiet period (default 90 s). A session mid-write is left alone and swept next tick — the property that makes
runsafe on a timer while sessions are live.--quiet-period 0disables it. - Regenerates a tree's indexes if its
.index-dirtymarker is present or the sweep commits content in that tree — unless step 5 held back a path of that tree that HEAD has never held (an untracked or staged-new item or knowledge file). An index is fan-in over every item, so regenerating one while a brand-new item is still mid-write would commit aprojects/index.mdwhose bullets point at a directory the sweep deliberately didn't commit. When that happens that tree's index step is deferred, the marker stays set, and the next tick that finds the new path settled does it; the report names the path. A held-back path that is already in HEAD cannot dangle, so it never defers: the tree regenerates, its bullet may reflect the mid-write file for one tick, and the marker is kept set until a regeneration finds every path in the tree settled, which re-derives the bullet. (Only quiet-period skips defer, only tree ones, and only new ones — a mid-writeAGENTS.mdis never referenced by an index, and anunresolvedpath never becomes eligible, so neither may wedge the indexes forever.) - Groups eligible paths: one commit per item (
<YYYY-MM-DD-slug>: sync), one for knowledge (knowledge: sync), one for every non-tree change (meta: sync), one for the regenerated indexes (indexes: sync) — the index commit always lands after the item commits it refers to. One exception to the item subject: a sweep that introduces the item README'sClosed.timeline entry (comparing HEAD to the worktree) is that item's close, and gets a synthesizedClose <slug>. Outcome: <summary>.milestone subject — the summary comes from the closing entry itself, so closing an item is write-files-only. - Pushes, when the branch ends up ahead of its upstream — unless the integration step refused it. A diverged tree (or one whose fetch/fast-forward failed) still committed its local work but does not push; see the warning below.
commit takes the same lock and commits just that item's paths under a caller-chosen subject line, and cannot race the sweeper. Two differences from run: no quiet period applies, and a held lock is an error (exit 3) rather than a silent skip — a milestone that quietly did nothing is worse than one that says so. There is no -m short flag; condash's short flags are boolean-only. It is a manual escape hatch for humans: agents never run sync verbs — the sweeper synthesizes the close milestone subject on its own when it sweeps the closing entry.
A rejected push is a warning, not a failure (exit stays 0, pushError is set in --json). The commits are local, and the next run retries because the push condition is "ahead of upstream", not "we just committed". The same contract covers a divergence or a failed integration: the run still exits 0 and commits the local work, but refuses to push, and the --json report carries behind, diverged, and integrateError instead of pushError. The human reconciles a diverged tree with git pull --rebase (or git merge origin/main) once the work is settled — never git reset --hard, which discards the local commits — and the next sweep pushes. sync never rebases and never force-pushes — that would be contention mechanism 3.
Paths under projects/ or knowledge/ that match no known shape (say projects/stray.md) are reported under skipped[] with reason unresolved and are never committed. Every other tracked, non-gitignored change is committed — under its item, knowledge, or meta. To keep a file out of sync, gitignore it.
The CLI has no scheduler — sync run is the deliverable, and headless scheduling is the operator's business. A systemd --user timer:
# ~/.config/systemd/user/condash-sync.service
[Service]
Type=oneshot
ExecStart=%h/.local/bin/condash sync run --conception %h/src/vcoeur/conception
# ~/.config/systemd/user/condash-sync.timer
[Timer]
OnBootSec=2min
OnUnitActiveSec=2min
[Install]
WantedBy=timers.target
The GUI does have one — an opt-in auto-commit engine that runs sync run on a timer while a conception is open (Settings → Auto-commit, off by default). It's the same sweep with the same safety; it just needs the app running. Use the timer above when you want commits to happen headless too. See autoSync in the config reference.
logs¶
Navigate the per-conception terminal-session logs that the GUI writes under .condash/logs/YYYY/MM/DD/HHMMSS-<sid>.txt (a # condash: JSON header line, the rendered xterm buffer, and — once the pty exits — a # condash: footer line). The noun is read-only: it never deletes a log (deletion stays a Logs-pane affordance). Logging is opt-in (terminal.logging.enabled), so the tree is empty until you turn it on.
| Verb | What it does |
|---|---|
days [--month YYYY-MM] [--year YYYY] |
List days that hold sessions (newest first) with session count + size. The default verb — bare condash logs runs it |
list [<day>] [filters] |
List sessions, newest spawn-time first |
read <sid\|day/sid\|path> [selector] |
Output a session transcript |
tail [--sid s,s] [--repo n] [--lines n] [--all] |
Last lines (default 20) of the active tabs |
list filters: --since <when> / --until <when> (by spawn time), --modified-since <when> (by file mtime — catches a long-running session that spawned earlier but is still being written), --repo <name>, --active (only sessions with no footer — still running), --sid <prefix>, --limit <n>. A <when> is an ISO date (2026-05-30) or datetime (2026-05-30T14:00), a relative span (30m, 2h, 3d, 1w — ago from now), or today / yesterday.
Every list row and read result carries a kind: transcript (the in-band OSC agent transcript — append-only, so a --from-byte cursor advanced to nextByte is reliable and lands on a message boundary) or grid (the rendered xterm buffer — a plain shell's scrollback or an alternate-screen TUI's frame). A grid body is appended too, but only up to its frozen watermark: the trailing live-tail region — the viewport, up to ~10 KB — is truncated and rewritten on every flush. A cursor at or below the watermark is stable. A cursor inside the live tail is not: because the tail is rewritten, a later read can both repeat rows it has already returned and, when the tail shrinks (a clear, a smaller TUI frame), skip rows entirely — the cursor sits past content that no longer exists at that offset, and rotated is only reported when the cursor is past EOF, which a rewrite-in-place does not trigger. Nothing exposes the watermark, so treat any grid cursor as approximate and re-read the tail if you need it exactly. The writer stamps kind in the header; logs written before the field fall back to a first-line heuristic.
read selectors are mutually exclusive: --head <n>, --tail <n>, --lines <a-b> (inclusive 1-based; also a- to end, or a for one line), --from-byte <n> (raw bytes from offset n to EOF — the stateless "what changed since I last looked" cursor; the JSON nextByte is the offset to store for next time, and rotated: true flags a janitor-trimmed file), --meta (only the parsed header/footer). --with-meta keeps the # condash: lines in the body. --redact masks obvious secret shapes (provider API keys, bearer tokens, JWTs, secret-named assignments, PEM private keys) in the emitted body — do the masking once here rather than in every consumer that ships a slice off-machine. A bare <sid> is prefix-matched across days, newest first — an ambiguous prefix exits 6.
tail is the "what's live right now" glance: it prints the last lines of every active session (no footer). --all includes ended sessions; --sid / --repo narrow the set; --redact masks secrets as on read.
condash logs list --since today --active
condash logs read t-a1b2c3d4 --tail 40
condash logs read t-a1b2 --from-byte 31044 --json # delta since the stored cursor
condash logs read t-a1b2 --from-byte 31044 --redact # … with secrets masked
condash logs tail --repo condash
skills¶
Install (or refresh) what condash ships into a conception. condash does exactly two things with agent config:
- Ship skill sources under
<conception>/.agents/skills/<name>/—SKILL.mdplus any task.mdfiles and an optionalSKILL.<harness>.mdoverlay, placed verbatim with refuse-on-edit. condash does not compile skills to per-harness directories; the harness launcher renders them per agent at run time. - Maintain the
AGENTS.mdmarker region at the conception root — regenerate everything from line 1 through<!-- end condash agents -->, preserve the## Specificstail verbatim.
condash no longer ships any top-level file — .gitignore was dropped after v4.0.1, so the conception's .gitignore is entirely user-owned.
| Verb | What it does |
|---|---|
list |
Print every shipped skill, with install status |
install [<skill\|AGENTS.md>…] |
Copy shipped skill sources into .agents/skills/ and regenerate the AGENTS.md head. With no positionals, installs everything. Refuses on locally-edited sources without --force |
status |
Per-skill install state (tracked, edited, missing on source) |
validate [<skill>…] |
Lint shipped skills — each must have a SKILL.md carrying a description |
A file that is already present and has never been tracked by condash — the state every conception is in the first time a file joins the shipped set — is reported as left alone and the install still exits 0; --diff compares it with the shipped copy and --force adopts it. A file condash has tracked and the user then edited is refused, which does fail the install until --force or a reconcile.
Install flags: --dest <path> (retarget the install dir; default the resolved conception or cwd), --force (override refuse-on-edit), --diff (show a unified diff per refused item), --dry-run (report without writing), --prune (drop manifest entries whose shipped source has been removed — a whole skill or a single file the current layout no longer ships — and remove the leftover source directory of a dropped skill when every file in it still matches the hash condash recorded; a locally edited or hand-added file keeps the directory, reported with the reason. Harness views are left alone: they are rendered from these sources at launch, so retiring the source retires the view).
Skill sources flow through one manifest at .agents/.condash-skills.json (v3 schema: skills.<name>.source per source file; a files.<path> namespace is retained only to reconcile legacy top-level entries such as a .gitignore shipped by condash ≤ 4.0.1), tracking the shipped version and SHA256 per file so a re-install can detect local edits. A per-skill entry left by an earlier schema (one with no source map) is re-seeded on read, so upgrading condash never crashes the install. AGENTS.md is deterministic (the marker is the boundary) and not manifest-tracked.
mdx¶
Visual-note MDX documents (.mdx notes authored by the /visual skill — see
the visual notes guide).
| Verb | Behaviour |
|---|---|
check <path> |
Validate a .mdx file (or a folder holding plan.mdx) against the block schemas the in-app viewer renders. Errors exit 3 (validation) with per-issue line numbers; unsupported canvas.mdx / prototype.mdx siblings warn |
blocks |
Print the block-vocabulary reference generated from the registry — the same content the /visual skill ships as blocks.md (drift-tested) |
The parser, the zod schemas, the viewer, and this verb are one code path
(src/shared/plan-blocks/), so a green check means the document parses and
matches the viewer by construction. It does not guarantee visible content —
check warns on a block with an empty payload (an unfolded diagram, an empty
code/diff, a wireframe with no html). For an svg block it goes one step
further: a missing ``svg fence or a first element that is not