Skip to content

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.

condash <noun> <verb> [args] [--flags]

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):

init   projects   knowledge   search   repos   applications   worktrees   audit   sync   logs   skills   mdx   config   help

Maintenance — hidden from the top-level help's Daily list, fully functional:

dirty

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

0  ok
1  runtime
2  usage
3  validation
4  not-found
5  no-conception
6  ambiguous

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:

  1. --conception <path> flag.
  2. CONDASH_CONCEPTION_PATH environment variable (legacy alias CONDASH_CONCEPTION still accepted).
  3. CLAUDE_PROJECT_DIR environment variable (back-compat for Claude Code sessions).
  4. Walk-up from the current working directory looking for .condash/settings.json, condash.json, or configuration.json next to a projects/ directory.
  5. lastConceptionPath in ${XDG_CONFIG_HOME:-~/.config}/condash/settings.json (or platform equivalent).
  6. 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

Cross-tree full-text search.

condash search "session cookie" --scope all

--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. #vcoeurvcoeur.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.

condash audit                       # run every check
condash audit --include lfs,binaries
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:

  1. The git index is shared process-wide. Session A runs git add, session B runs git commit a 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.
  2. index.md files are fan-in. projects/index.md and projects/<YYYY-MM>/index.md are 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.
  3. Pushes race. Concurrent pushes reject non-fast-forward, and the reflex git pull --rebase rewrites 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:

  1. Takes an exclusive, non-blocking lock at <git-dir>/condash-sync.lock. If held, exits 0 — the next tick picks the work up.
  2. Refuses (exit 3) mid-merge, mid-rebase, mid-cherry-pick, mid-revert, or with a conflicted tree.
  3. 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.
  4. Considers every tracked, non-gitignored change in the tree. git status already omits gitignored paths, so the per-harness generated views (.claude/*, .pi/*, CLAUDE.md, .kimi/, …), resources/local/, projects/**/local/, and the .index-dirty sentinels never even reach the sweeper. Everything that remains gets a committer: item and knowledge/ paths as today, and anything outside those two trees — root AGENTS.md, .agents/**, config files (opencode.json, .claude/settings.json, …), resources/, tasks/ — swept into one meta: sync commit. That is the point: once a checkout adopts "sync is 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 (say projects/stray.md) is the one exception — reported unresolved, never committed, because a misplaced file is a mistake worth surfacing.
  5. 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 run safe on a timer while sessions are live. --quiet-period 0 disables it.
  6. Regenerates a tree's indexes if its .index-dirty marker 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 a projects/index.md whose 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-write AGENTS.md is never referenced by an index, and an unresolved path never becomes eligible, so neither may wedge the indexes forever.)
  7. 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's Closed. timeline entry (comparing HEAD to the worktree) is that item's close, and gets a synthesized Close <slug>. Outcome: <summary>. milestone subject — the summary comes from the closing entry itself, so closing an item is write-files-only.
  8. 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 schedulersync 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.md plus any task .md files and an optional SKILL.<harness>.md overlay, 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.md marker region at the conception root — regenerate everything from line 1 through <!-- end condash agents -->, preserve the ## Specifics tail 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 notis an error (an XML prolog, DOCTYPE or comment before the root is accepted, as a tool export carries), and it warns on a root withoutviewBox, a block withoutalt, and any element the viewer's sanitizer will strip —