Skip to main content
Version: 0.1.124

Repave CLI

This document defines the target contract for a first-party repave CLI that replaces the copied helper scripts currently used by agents and also gives human developers a stable command-line interface.

Goals​

  • Give agents and human developers the same command surface.
  • Avoid requiring users to remember or paste project IDs during normal use.
  • Replace many copied workspace helper scripts with one maintained CLI.
  • Keep authentication explicit for humans and non-interactive for agents.
  • Keep project context separate from credentials.
  • Preserve a migration bridge for existing prompts and scripts while the CLI rolls out.

Implementation Language​

The repave CLI should be implemented in Go.

Rationale:

  • The CLI is primarily a thin HTTP client with local project-context resolution, token storage, JSON output, and a local browser-login callback server.
  • Go gives straightforward cross-platform single-binary distribution for macOS, Linux, Windows, Docker, and Kubernetes agent images.
  • Go keeps the CLI operationally simple while Repave's server remains the source of truth for authorization, validation, and persistence.
  • Rust is not required for this workload because the CLI is not CPU-bound and should avoid duplicating complex domain validation locally.

The CLI should not port all helper-script business logic into Go. Canonical validation should live behind Repave API endpoints wherever feasible, and the CLI should call those endpoints.

Suggested layout:

cli/repave/
go.mod
cmd/repave/main.go
internal/api/
internal/auth/
internal/config/
internal/context/
internal/output/

Initial implementation should be runnable from the repository root with:

npm run repave:test
npm run repave:build

Current implementation status:

  • repave auth login/status/logout, repave project current/settings/link, repave projects list, repave features list/search/read/create/update/delete/import, repave scenarios search/read/add/replace/delete, and repave validation-rules list exist.

  • repave project settings returns the project's implementation gate configuration — unitTestsEnabled, unitTestCoverageThreshold, unitTestFalsePositiveCheckEnabled, uiCheckEnabled, apiCheckEnabled, dbSchemaCheckEnabled, scenarioDriftCheckEnabled, implementationPlanningEnabled, scenarioByScenarioEnabled, dbModernizationPolicyEnabled, and maxReviewCycles — the same flags the server-side implementation orchestrator gates on. It also returns the two accessibility flags: prototypeAccessibilityCheckEnabled, which governs the deterministic axe-core scan run at prototype generation time , and implementationAccessibilityCheckEnabled, which governs the Stage-2 gate over the axe results of the feature's BDD run . It also returns the two prototype axes : prototypeMode, the design source (html or figma), and targetStackPreviewEnabled, whether that design is also built and run on the project's own stack — independent settings, both read by the prototype loop's first phase . unitTestFalsePositiveCheckEnabled governs the Stage 3+4 reviewer that inspects passing unit tests for false positives; it defaults off, so a project that has not turned it on runs Stage 3+4 to completion on green tests plus coverage alone . The Repave plugin's /repave:code-review reads this endpoint to run exactly the checks the project has enabled instead of inferring them from the worktree. Requires project:read scope.

  • repave project test-config returns how this project's unit tests are run and measured: per suite, its working directory, test command, optional separate coverage command, coverage report path and format, plus the commit the configuration was verified against. Populated only for a provided modernized codebase, by the UNIT_TEST_CONFIG_GENERATION job, and only after every discovered suite was executed and produced a report the platform could parse . Empty for a scaffolded project, whose runners follow from its modernized stack, and for a provided project that has not been detected yet — an agent asked to write or run unit tests in a provided codebase should read this rather than guessing from the file layout. Paths and commands are relative to the modernized root, so they work unchanged from a feature worktree. Requires project:read scope.

  • repave project bdd-config reports where this checkout keeps its BDD assets — the runner, its features/ directory, its bdd-runs/ directory and the most recent run — every path relative to the checkout root. The runner is the project's own bddTestScript, which may sit at any depth under any name (test/uat/run-bdd.sh), asked of GET /api/cli/projects/:id and used when that file is in the checkout . It falls back to scanning the disk for run-bdd-tests.sh at the root and one level down, which covers the single-repository layout and a combined workspace's shared test folder (Project.sharedWorkspaceDir, default e2e). The fallback applies when the project cannot be asked (offline, no token) or the configured file is not in this checkout. The runner cds to its own directory before running, so its runs land in bdd-runs/ beside it. With --json it also reports:

    • runnerSource: project or discovered;
    • runsDirs: every runs directory that exists, the runner's first, so runs left beside an older runner location stay visible;
    • configuredRunner: the project's script when this checkout lacks it;
    • projectLookupError: why the project could not be asked, when it could not.

    Exits non-zero when the checkout has no runner, naming the project's script, or else the directories it looked in. A caller asked to run or submit the BDD suite reads this rather than hardcoding ./run-bdd-tests.sh.

  • repave features submit-tests <feature-id> [--run-dir <dir>] pushes a BDD run's cucumber report and Playwright traces. With no --run-dir it finds the most recent run wherever this checkout's runner writes it (the same layout bdd-config reports, the project's own runner first) and always sends the path relative to the checkout root, which is the frame the server resolves it in. --run-dir is accepted relative to the checkout root or to the working directory, whichever exists, so any spelling of a run you can see on disk resolves to the same submission. Requires features:write scope and obeys the feature-write lock.

  • repave architecture save, repave er save, repave business-requirements save, repave personas list/add, repave views list/add/update, repave apis list/add/update, repave messaging-destinations list/add/update, repave external-systems list/add/update/delete (--realm legacy|modernized on every subcommand; add/update accept [legacy|modernized], default legacy; renaming/deleting cascades to every scenario's # @external-systems annotation that referenced the old name), repave legacy-data-stores list/add, repave batch-jobs list/add/update, repave reports list/show/add, repave db tables search/list/add, repave db stored-procs search/read, and repave db policy get/set-defaults/set-object exist.

  • repave navigation-map read/validate/import, repave navigation-map nodes list, and repave navigation-map path-for-view manage the project Navigation Map . read returns the active map for a realm, import validates and persists a canonical nav-map JSON file in one call, and path-for-view resolves the best root-first navigation path that opens or contains a registered view.

  • repave application-shell read and repave ui-prototype read <feature-title-or-id> return the selected Application Shell and feature UI prototype. In HTML mode the default output is the selected HTML; in Figma mode the default output is the selected file/node metadata. Use --json for structured metadata in either mode. Validation agents may pass --option-number <n> to read the exact persisted prototype option they are checking; without it, the command returns the selected/opted option.

  • repave ui-prototype list <feature-title-or-id> answers the whole stored option set rather than the active design's content: option number, label, what the option last validated as, and which one is active. read tells you what the design is; list tells you what there is to choose between, which is what a caller needs before it can name an option at all. Requires features:read scope.

  • Writing a prototype . These exist so a web IDE session can produce a prototype itself and persist the result, instead of firing a server-side generation job and waiting. All three require features:write scope and obey the feature-write lock — a feature-scoped session token writes one feature's prototype and is refused (403) elsewhere.

    • repave ui-prototype submit <feature> --manifest FILE [--append] stores an option set. The manifest is a JSON array; each entry carries a label and either inline html, an htmlFile path resolved relative to the manifest, or a figmaNodeId. mode is inferred from the content when omitted. Numbering belongs to the server, so entries carry no option number: they are numbered in manifest order, or appended after the existing options with --append. The feature's active design follows the write — the previously active option keeps the slot when its content survives, otherwise the first option takes it.
    • repave ui-prototype update <feature> --option N (--html FILE | --figma-node-id ID) replaces one option's content and leaves the others byte-identical. Exactly one content flag: both, or neither, is a usage error rather than a guess. The feature's active design moves only when the updated option was the active one.
    • repave ui-prototype submit-validation <feature> --option N (--issue TEXT ... | --clean) records what the project's UI_PROTOTYPE-cadence rules found. Repeat --issue for several; --clean records that the option validates with nothing open. One of the two is required — a missing --issue is not read as a clean verdict, so a caller that forgot the flag cannot silently clear a recorded failure. Writes only the verdict: never the option's content, and never the staleness stamps.
    • There is deliberately no select verb. Selecting which option is the design is a human act in the web UI, so a CLI verb for it would ship with no caller.
  • repave ui-prototype save-draft <feature-title-or-id> [--option N] --html FILE and repave application-shell save-draft --option N --html FILE are how a platform prototype agent delivers its result . The document is stored as a draft of the agent's own job — the server takes the job from the agent token (agent:<jobId>), never from the request — and the service that ran the agent reads it back when the call ends; the file is only the agent's working copy. A save made by any agent call in the job counts for the rest of it, and a later save of the same option replaces an earlier one. --option omitted saves the feature's active design (refine, Figma pull); the shell always names its option. Only an agent token is accepted: a person's token is refused with a pointer to ui-prototype update, and a save into a job that is no longer processing, into another project, or of an empty document is refused with the reason. Nothing the user sees moves on a save — the service stores the validated result at the end of the run, as before. Drafts are deleted when the run ends. Requires features:write scope.

  • repave ui-prototype references <feature-title-or-id> --out DIR exports the design reference images a user attached to a feature so a prototype or implementation agent can open them with its Read tool — agents have no multimodal input path, so an image only reaches a model as a file on disk. Images are written to DIR/<feature-id>/ alongside a references.json manifest carrying each image's caption; the feature-id segment matters because the prototype working directory is shared by every feature in the project. The target directory is cleared first, so an image the user deleted cannot survive as a stale export. A feature with no references prints No reference images are attached to this feature. and exits 0 rather than failing. Requires features:read scope. repave ui-prototype references --discovery-job JOB_ID --out DIR exports a feature-discovery run's images instead into DIR/_job-<job-id>/; pass exactly one of a feature or --discovery-job.

  • repave decisions list (docs/implementation-preflight.md §9) is a read-only surface over the project-decision aggregate and per-launch immutable decision record: with no flags it returns current project decision heads; --feature-id <id> returns that feature's implementation-launch history (rounds, immutable handoff baseline, append-only stage-boundary adoptions); --launch <id> (requires --feature-id) narrows to exactly one launch; --all-features returns the feature-scoped answers from every feature's preflight in the project, each attributed to its feature, launch, and launch status, with --query <text> filtering case-insensitively across question, answer, and category. Superseded launches are excluded — their replacement re-ran preflight — and the scan is bounded at 200 launches, printing an explicit note rather than presenting a partial list as the whole project. Feature IDs are required — feature titles are not unique. No decisions subcommand creates, edits, revokes, or deletes a decision; that only happens through project settings.

  • repave preflight status [feature-id] reports whether the platform's implementation preflight has concluded for a feature, which is what gates local implementation in a Web IDE session. The feature id may be positional, --feature-id, or $REPAVE_FEATURE_ID (see The Session's Feature; this command had that fallback first, and now shares it). The exit code is the contract: 0 the gate is satisfied (or the project does not require preflight), 3 a definite negative verdict, 1 the verdict could not be determined. The plugin's PreToolUse hook denies writes on 3 and fails open on 1, so a shell hook never has to parse JSON to tell a denial from an unreachable server. The verdict itself — satisfied, a stable reason, and a message — is computed server-side, so the policy is not frozen at whatever CLI version a client installed. Requires features:read scope.

  • repave architecture read returns the stored C4 artifacts. With no --level it is an inventory — which levels exist, their format, and their size — so a caller can pick before pulling tens of KB; --level c1|c2|c3 (plus c4 under --scope legacy) prints that artifact to stdout verbatim, inline SVG or mermaid included, so it redirects and pipes cleanly. --scope defaults to modernized; the legacy scope falls back to the project's stored architecture columns when the best-effort file write never landed, so it never reports an architecture the Architecture page renders as missing. Requires project:read scope.

  • repave architecture decisions returns the clarification Q&A that shaped the To-Be architecture: the thread's seeding instruction and, per answered round, each question, the rationale it was asked with, and the user's answer. Defaults to the most recent thread; --all-threads returns the full history oldest-first. Unanswered rounds are reported as a count and never as content — an open question is not a decision. Requires project:read scope.

  • Feature/scenario read and write commands use /api/cli/* endpoints with bearer auth.

  • Human CLI calls enforce the same project read/edit access as the web app; viewer access can read but cannot mutate features, scenarios, or saved artifacts.

  • Agent CLI calls use short-lived project-scoped tokens and are rejected when a token accesses a different project.

  • Migrated helper entrypoints have been removed from templates/legacy; prompts and workspace instructions should call repave ... directly.

  • ZIP, Git, and local clone workspace setup write .repave/project.json through src/lib/repave-project-context.ts.

  • Docker and Kubernetes agent runs receive REPAVE_AGENT_TOKEN, REPAVE_API_BASE_URL, and REPAVE_PROJECT_ID.

  • Agent tokens are short-lived, project-scoped bearer tokens signed by the server.

  • Dockerfile.agent-runner builds and installs the Go repave binary into the agent image.

  • Kubernetes agent setup writes /workspace/.repave/project.json so repave project current works from /workspace/legacy and /workspace/modernized.

Command Shape​

The CLI should use grouped commands with a stable repave <domain> <action> shape:

repave project current
repave project settings
repave project test-config
repave project bdd-config
repave features list
repave features read "Feature title or id"
repave scenarios search "checkout|payment"
repave scenarios read <feature-id> "Scenario name"
repave scenarios add --rule "Rule name" [--dry-run] <feature-id> scenario.txt
repave scenarios replace [--dry-run] <feature-id> "Scenario name" scenario.txt
repave rules read <feature-id> "Rule name"
repave rules add [--dry-run] <feature-id> rule-block.txt
repave rules replace [--dry-run] <feature-id> "Rule name" rule-block.txt
repave features write-tobe-gherkin [--confirm-reopen] [--dry-run] <feature-id> feature.feature
repave validation-rules list --cadence per_feature --enabled-only --format markdown
repave features create feature.feature
repave features update <feature-id> feature.feature
repave validate html c1.html
repave architecture save c1.html
repave architecture save modernized-architecture/c1.html --scope modernized
repave architecture read
repave architecture read --level c2
repave architecture read --scope legacy --level c4
repave architecture decisions
repave decisions list --all-features --query "pagination"
repave preflight status
repave design save DESIGN.md
repave application-shell read
repave navigation-map read --realm legacy --include-bindings
repave navigation-map validate nav-map.json --realm legacy
repave navigation-map import nav-map.json --realm legacy --source static-scan --activate
repave navigation-map activate <map-id> --replace-active
repave navigation-map nodes list --realm legacy --kind MAIN_TAB
repave navigation-map path-for-view "Customer Search Page" --realm legacy
repave ui-prototype read "Feature title or id"
repave ui-prototype read --option-number 2 --json "Feature title or id"
repave ui-prototype references "Feature title or id" --out .ui-references
repave ui-prototype references --discovery-job <job-id> --out .ui-references
repave ui-prototype list "Feature title or id"
repave ui-prototype submit "Feature title or id" --manifest options.json
repave ui-prototype update "Feature title or id" --option 2 --html revised.html
repave ui-prototype submit-validation "Feature title or id" --option 2 --issue "Shell chrome is reimplemented"
repave ui-prototype submit-validation "Feature title or id" --option 3 --clean
repave ui-prototype save-draft "Feature id" --option 2 --html modernized/ui-prototype/orders.html # platform agents only
repave application-shell save-draft --option 1 --html modernized/ui-prototype/_application-shell-option-1.html
repave trace list "Feature title or id"
repave trace frames "Feature title or id" --scenario "Scenario name" --out .ui-verification/frames
repave verification request --job-id <job-id> --context "..." --questions questions.json
repave verification poll <verification-id>
repave user-journeys save journey.json
repave user-journeys test-feature regenerate --journey-id <journey-id> --cuj-id <cuj-id> --job-id <job-id>
repave scenario-proposals create <feature-id> "Scenario name" scenario.txt --rationale "..."
repave code-tags list --file codebase/path/File.java
repave code-tags tag --file codebase/path/File.java --lines 10-45 --tag no-business-logic --reason "..."

--dry-run on the Gherkin write commands (scenarios add|replace, rules add|replace, features write-tobe-gherkin) runs the identical validation chain, reports the same findings, saves nothing, and prints the document that would have been stored. Its response is labelled Dry run: instead of Success:, and a Note: line names any feature lock the real write would still hit — distinguishing a lock --confirm-reopen clears from in_review, which it does not.

A To-Be write carrying # @code-ref is refused . features write-tobe-gherkin (including --dry-run) and features update on a feature that has To-Be Gherkin return a 400 naming every scenario or rule that carries one; remove the line — legacy evidence stays on the As-Is scenario, linked with # @from-asis — and retry. If the document you were saving is As-Is content, the feature was the wrong target for features update: save it with features write-as-is-gherkin and keep its # @code-ref lines. The CLI is the only place this is refused: the UI editors, repository adoption, feature-file sync and PR merges strip the annotation instead. features update on a To-Be feature also keeps the feature's stored source-file list, since a To-Be document carries no @code-ref to derive one from.

Normal project-scoped commands must not require a project ID argument. A --project-id <id> override may exist as an explicit escape hatch for debugging, scripts, and support cases, but prompts and user-facing docs should prefer automatic project resolution.

Every command that mutates server state should support:

  • --json for machine-readable output.
  • Stable non-zero exit codes on validation, auth, API, and network failures.
  • Human-readable output by default, with a final JSON object where compatibility with current agent behavior is needed.

Scenario proposal commands are disabled by default. Set ENABLE_SCENARIO_PROPOSALS=true for server/agent access and NEXT_PUBLIC_ENABLE_SCENARIO_PROPOSALS=true for UI visibility before using repave scenario-proposals ... or saving user journeys with proposalId steps.

The Session's Feature​

In a feature-scoped web IDE session the container sets REPAVE_FEATURE_ID to the one feature that session was opened for (code-server-container-service.ts), and the session's token is locked to it. Commands whose feature id is the sole positional argument resolve that variable when the caller omits an id, so a session can say repave features read and mean its own feature:

repave features read # this session's feature
repave features write-feature-file # …into this checkout
repave features submit-tests
repave preflight status
repave trace list
repave trace frames --scenario "…" --out ./frames
repave ui-prototype read
repave ui-prototype list
repave ui-prototype submit --manifest options.json
repave ui-prototype update --option 1 --html option-1.html
repave ui-prototype submit-validation --option 1 --clean
repave ui-prototype save-draft --html draft.html

An explicit id — positional, or --feature-id where the command has it — always wins. With neither an argument nor the variable the command exits 2 and says No feature id: pass one, or set REPAVE_FEATURE_ID.; that is the normal state of a project-, module- or worktree-scoped session, which has no single feature to assume.

The exclusions. A command that takes a file as well (features update, features write-tobe-gherkin, features write-as-is-gherkin, scenarios *, rules *, scenario-proposals create) keeps requiring its id: an optional leading positional would make repave features update foo.feature ambiguous with a missing second argument. Two more are sole-positional and still excluded: repave features delete, because inferring the target of a delete from the environment is not a convenience worth having (even though the token's feature claim would confine it to this session's own feature), and repave ui-prototype references, whose positional is already an either/or with --job-id.

Note the ordering on ui-prototype submit and save-draft: both read and validate their input file before resolving the feature, so a bad manifest or a missing HTML file is reported instead of a missing id.

Input Contract​

Commands that save substantial content should take a file path, not raw multi-line content as an argument:

repave features create checkout.feature
repave features update <feature-id> checkout.feature
repave scenarios add --rule "Rule name" <feature-id> new-scenario.txt
repave scenarios replace <feature-id> "Scenario name" updated-scenario.txt
repave rules replace <feature-id> "Rule name" updated-rule.txt
repave architecture save c1.html
repave design save DESIGN.md
repave er save er-diagram.html
repave business-requirements save requirements.json

A # @code-ref whose file does not exist is rejected . The path is resolved against the project's registered source roots, so write the exact path you read the file at — copy it, do not reconstruct it. Every intermediate segment must be present: for a ZIP-uploaded codebase that is codebase/<zip-name>/path/to/File.ext, and dropping <zip-name> points at nothing. A cloned repository has no codebase/ segment at all, which is why there is no fixed prefix to prepend. Declared line ranges are checked against the file's real length. When the basename matches exactly one file in the source, the error names it (— did you mean "…"?); when several match it says so rather than guessing. Every as-is write path enforces this — features create, features update, features write-as-is-gherkin, scenarios add|replace, and rules add|replace — and a whole-document write is only refused for a reference it introduces, so a document that already carried a broken one stays repairable.

Scenario add/replace files are scenario-owned blocks only: supported leading # @... annotations, one Scenario: or Scenario Outline: line, steps, tables, doc strings, and examples. repave scenarios add requires --rule "Rule name" and inserts the block at the end of that rule. Each saved scenario must be governed by a rule and resolve to exactly one # @entrypoint from either the governing rule or the scenario. The evidence/traceability annotation required before the Scenario: line depends on the feature's realm: a legacy (As-Is) feature requires # @code-ref or # @new-scenario; a feature with a generated To-Be Gherkin requires # @from-asis or # @new-scenario and must never carry # @code-ref (legacy evidence stays on the As-Is scenario). Supported scenario annotations include # @new-scenario, # @persona, and # @dependency; do not write deprecated # @ui, # @api, # @batch, or # @interface. Use # @entrypoint plus typed resource annotations such as # @views, # @apis, # @messaging, # @batch-jobs, and # @reports (report slugs, from repave reports add). Each scenario must contain at most one explicit When step and one explicit Then step; use And/But for continuations and split a second action/assertion pair into another scenario. Do not include Feature: or Rule: lines in scenario files. To update a Rule: and all scenarios governed by it, use repave rules read and repave rules replace.

Rule replace files are whole rule blocks: optional rule-level annotations, one Rule: line, optional rule description/background, and all scenarios governed by that rule. The same one-When, one-Then scenario flow validation applies to every scenario inside the rule. Do not include Feature: lines or more than one Rule: line.

repave validation-rules list reads project validation-rule guidance from the server. It is distinct from repave rules, which reads or replaces Gherkin Rule: blocks inside a feature. Agents should use --format markdown for compact implementation guidance and --json for structured metadata.

Small selectors and options should remain command arguments, such as feature IDs, scenario names, regexes, architecture levels, job IDs, and --project-id overrides.

This contract is intentional for both human and agent callers:

  • It avoids shell quoting bugs with Gherkin, Markdown, SVG/HTML, Mermaid, and JSON bodies.
  • It avoids exposing large business content through process arguments.
  • It matches existing agent workflows that already write intermediate .feature, .txt, .md, and .json files.
  • It keeps server-side validation as the source of truth while letting the CLI do lightweight local checks for missing or empty files.

A future --stdin option may be added for pipe-friendly usage, but prompts should prefer file paths.

Save commands must validate and persist in one call. Agents and humans should not need to run a separate helper script before saving:

  • repave features create/update validates Gherkin syntax and referenced DB objects before saving.
  • A Feature: heading with no Latin letter or digit (a Chinese, Japanese, Cyrillic or Greek title) needs an English name: features create, features create-from-requirements, and an update that changes the heading are refused without --english-title "<name>"; features import takes an englishTitle per entry. An update that keeps the heading never needs it. The English name names the feature's git branch .
  • repave scenarios add/replace/delete validates the resulting feature Gherkin before saving.
  • repave architecture save validates that the HTML artifact contains a primary inline SVG diagram and zoom controls before saving. Legacy Markdown Mermaid artifacts are still accepted. Use --scope modernized to save target architecture design artifacts separately under modernized-architecture/; modernized scope accepts C4 levels 1-3.
  • repave er save validates that the HTML artifact contains a primary inline SVG diagram and zoom controls before saving. Legacy Markdown Mermaid ER artifacts are still accepted.
  • repave business-requirements save validates JSON shape, required fields, category, and priority before saving.
  • repave design save validates the design document structure before saving.

Automatic Project Resolution​

repave should resolve the current project automatically for project-scoped commands.

Resolution order:

  1. Explicit --project-id <id> override.
  2. Nearest .repave/project.json, found by walking upward from the current working directory.
  3. Workspace path inference for known layouts, such as:
    • .../app-rewrite-work-dir/<projectId>/
    • .../app-rewrite-work-dir/<projectId>/legacy/
    • .../app-rewrite-work-dir/<projectId>/legacy/codebase/
    • .../app-rewrite-work-dir/<projectId>/<modernizedDirName>/
  4. API path resolution, where the CLI sends the absolute current directory to Repave and the server matches it against known workspace, legacy, codebase, and modernized paths.
  5. REPAVE_PROJECT_ID as a final non-interactive fallback for CI or unusual environments.

If resolution fails, the CLI should print a direct recovery message:

No Repave project context found.

Run one of:
repave project link <project-id>
repave projects list
repave features list --project-id <project-id>

If more than one project matches, the CLI must fail and ask the user to disambiguate with repave project link <project-id> or --project-id.

Project Metadata​

Workspace setup should create .repave/project.json with non-secret context:

{
"projectId": "cmf123abc456",
"projectName": "Student Housing",
"apiBaseUrl": "http://localhost:3000",
"workspaceRoot": "/Users/example/app-rewrite-work-dir/cmf123abc456",
"legacyDir": "/Users/example/app-rewrite-work-dir/cmf123abc456/legacy",
"modernizedDir": "/Users/example/app-rewrite-work-dir/cmf123abc456/modernized",
"modernizedDirName": "modernized",
"featuresPath": "features"
}

This file must not contain tokens, API keys, refresh tokens, cookies, or other secrets.

The preferred placement is the workspace root:

<workspace>/<projectId>/.repave/project.json

The upward search should make this discoverable from legacy/, legacy/codebase/, and the modernized directory. If a future workspace layout prevents that, setup may copy the same non-secret metadata file into child roots.

Human Authentication​

Humans authenticate with:

repave auth login
repave auth status
repave auth logout

repave auth login should:

  1. Start a local callback server on 127.0.0.1:<random-port>.
  2. Open the browser to the Repave app login page with a CLI auth challenge.
  3. Let the user sign in through the normal Repave web flow.
  4. Receive a one-time callback code.
  5. Exchange the code for a user-scoped CLI token.
  6. Store CLI auth state in ~/.repave/config.json.

The config file should be created with owner-only permissions (0600) where the OS supports it.

REPAVE_CONFIG_DIR overrides the config directory. Inside a Repave web IDE session there is no login at all: the app writes the session's credential into the container and renews it for as long as the session runs. Sessions set REPAVE_WEB_IDE_SESSION=1, and with it repave auth login refuses at once (its loopback callback cannot reach a container), while a 401 and an expired repave auth status say to reopen the session from the project's View Source Code screen instead of suggesting a login (design/web-ide-session-cli-credential-refresh.md).

Example shape:

{
"repavePath": "/Users/dev/workspaces/app-rewrite/cli/repave/bin/repave",
"currentProfile": "local",
"profiles": {
"local": {
"baseUrl": "http://localhost:3000",
"accessToken": "<user-scoped-token>",
"refreshToken": "<optional-refresh-token>",
"expiresAt": "2026-05-20T12:00:00.000Z"
}
}
}

Human CLI tokens must enforce the same project permissions as the web app. If the logged-in user cannot view or edit a project in Repave, the CLI must not allow that action either.

Human Access Control​

Human CLI access must respect the Repave project permission model.

  • repave projects list should only return projects the logged-in user can access.
  • Read commands, such as repave features list, repave scenarios read, and repave project current, require project read access.
  • Write commands, such as repave scenarios add, repave scenarios replace, repave architecture save, and repave design save, require project edit access.
  • Destructive commands, such as deleting a feature or scenario, require edit access and should keep explicit confirmation behavior unless a non-interactive flag is intentionally supported.
  • If a user is read-only on a project, the CLI may resolve and read the project context, but every mutation must fail with a clear authorization error.
  • If a user has no access to a project, automatic project resolution may identify the local project context, but API calls must still fail as unauthorized or forbidden.

The server is the source of truth for authorization. The CLI may do preflight checks for better error messages, but it must not rely on local metadata or cached project context to grant access.

Example read-only mutation failure:

Forbidden: your Repave account has read-only access to this project.
Required permission: project:edit
Command: repave scenarios replace

Agent Authentication​

Agents must not run browser login.

Agent runtimes should receive short-lived non-interactive credentials through environment variables:

REPAVE_AGENT_TOKEN=<short-lived-token>
REPAVE_API_BASE_URL=http://localhost:3000

Agent tokens should be scoped to:

  • One project.
  • One job or session when feasible.
  • A small permission set, such as features:read, features:write, verifications:create, or artifacts:write.
  • A short TTL.
  • An audit identity such as agent:<jobId>.

Auth resolution should prefer agent credentials before human credentials:

  1. REPAVE_AGENT_TOKEN with REPAVE_API_BASE_URL.
  2. Human profile from ~/.repave/config.json.
  3. Existing INTERNAL_API_KEY environment variable as a temporary migration bridge only.

Long term, both human and agent calls should use:

Authorization: Bearer <token>

X-Internal-API-Key should remain available only until existing helper scripts and prompts are migrated.

For local agent execution, the app also reads ~/.repave/config.json and prepends the directory containing repavePath to the Claude SDK PATH. This lets prompts call repave ... even when the web server process was started from an environment that does not load the developer's shell profile.

Base URL Resolution​

The CLI should resolve the Repave API base URL in this order:

  1. --base-url <url>.
  2. REPAVE_API_BASE_URL.
  3. .repave/project.json apiBaseUrl.
  4. Current profile in ~/.repave/config.json.
  5. Default local development URL: http://localhost:3000.

repave auth login --base-url <url> should store the selected base URL in the chosen profile.

Migration From Helper Scripts​

Helper entrypoints are no longer copied into agent workspaces. Agents and humans should call repave ... directly. templates/legacy no longer contains JavaScript or Python helper implementations.

Existing INTERNAL_API_KEY-based calls should continue only for unmigrated helpers. New CLI-backed workflows use bearer auth through REPAVE_AGENT_TOKEN or repave auth login.

Preferred replacements:

repave features list
repave features search "checkout|payment"
repave features by-file codebase/path/File.java
repave features read "Feature Title"
repave features read --gherkin "Feature Title"
repave features read --gherkin --as-is "Feature Title" # always the As-Is/legacy Gherkin, even after a To-Be Gherkin has been generated
repave scenarios search "checkout|payment"
repave scenarios search --content "SELECT.*FROM"
repave scenarios read <feature-id> "Scenario name" ["Scenario name" ...]
repave scenarios read --as-is --id ASIS-001,ASIS-002 <feature-id> # As-Is scenarios by their own # @id, e.g. from a To-Be scenario's # @from-asis
repave scenarios replace <feature-id> "Scenario name" scenario.txt
repave scenarios add --rule "Rule name" <feature-id> scenario.txt
repave scenarios delete --yes <feature-id> "Scenario name"
repave rules read <feature-id> "Rule name"
repave rules replace <feature-id> "Rule name" rule-block.txt
repave validation-rules list --cadence per_feature --enabled-only --format markdown
repave features create feature.feature
repave features update <feature-id> feature.feature
repave features delete --yes <feature-id>
repave features import discovered-features.json
repave architecture save c1.html
repave architecture save modernized-architecture/c2.html --scope modernized
repave design save DESIGN.md
repave application-shell read
repave ui-prototype read "Feature Title"
repave er save er-diagram.html
repave business-requirements save requirements.json
repave validate html c1.html
repave validate html ui-prototype.html
repave validate openapi openapi.yaml
repave verification request --job-id <job-id> --context "..." --questions questions.json
repave verification poll <verification-id>
repave user-journeys save journey.json
repave user-journeys save journey.json --journey-id <journey-id>
repave user-journeys test-feature regenerate --journey-id <journey-id> [--cuj-id <cuj-id>] [--job-id <job-id>] [--model <model>] [--reuse-alignment]
repave scenario-proposals create <feature-id> "Scenario name" scenario.txt --rationale "..."
repave scenario-proposals update <scenario-id> "Scenario name" scenario.txt --rationale "..."
repave scenario-proposals delete <scenario-id> --rationale "..."
repave code-tags list --file codebase/path/File.java
repave code-tags tag --file codebase/path/File.java --lines 10-45 --tag no-business-logic --reason "..."
repave code-tags retag <tag-id> --tag no-business-logic --reason "..."
repave code-tags untag <tag-id>
repave personas list
repave personas add "Property Manager" "Reviews occupancy and room status"
repave views list
repave views list --realm modernized
repave views list --search "account" # name substring, case-insensitive; also on apis/batch-jobs/legacy-data-stores/nfrs list
repave views add --type page --route /accounts/:id --legacy-file account-summary.jsp "Account Summary View" "Customer account overview"
repave views add --realm modernized --legacy-ref "Account Summary View" --type page --route /accounts/:id "Account Summary Page" "To-be account overview" # --type, --route, and a description are required
repave views update --status CONFIRMED <view-id>
repave apis list
repave apis list --realm modernized
repave apis add --type SOAP --protocol "SOAP over HTTP" --method GetCustomer --path CustomerService.GetCustomer --contract customer.wsdl "Customer Lookup SOAP API" "Looks up customer data"
repave validate openapi modernized/api/customers.openapi.yaml
repave apis import-openapi --realm modernized modernized/api/customers.openapi.yaml
repave apis update --status CONFIRMED <api-id>
repave messaging-destinations list
repave messaging-destinations list --realm modernized
repave messaging-destinations add --platform KAFKA --destination-type TOPIC --destination-name payments.approved --direction PUBLISHES --payload-format JSON --contract asyncapi.yaml "Payment Approved Event" "Published after approval"
repave messaging-destinations add --realm modernized --legacy-ref "Payment Approved Event" --platform KAFKA --destination-type TOPIC --destination-name payments.v2.approved --direction PUBLISHES --payload-format JSON "Payment Approved Event V2" "To-be payment approval event" # --platform, --destination-type, --direction, --payload-format, and a description are required
repave messaging-destinations update --status CONFIRMED <destination-id>
repave external-systems list # legacy entries include the modernization disposition and, for REPLACE, the replacement system name
repave external-systems list --realm modernized
repave external-systems add "SMTP Email" "Sends outbound notifications"
repave external-systems add --realm modernized "Payment Service" "Modern payment processor"
repave external-systems update --name "Email Service" <system-id> # cascades to every scenario's # @external-systems annotation
repave external-systems update --realm modernized --description "" <system-id> # pass an empty string to clear the description
repave external-systems delete --realm modernized <system-id>
repave legacy-data-stores list
repave legacy-data-stores add "ACCTDAT VSAM File" VSAM_KSDS "Account master file"
repave batch-jobs list
repave batch-jobs list --realm modernized
repave batch-jobs add --entry-point jcl/settle.jcl --frequency nightly --inputs "ACCTDAT VSAM File" --outputs SETTLEDGER "Nightly Settlement" "Settles obligations"
repave batch-jobs add --realm modernized --legacy-ref "Nightly Settlement" --frequency nightly --inputs settlements --outputs settlement_ledger "Nightly Settlement Job" "To-be settlement job" # a description, --trigger or --frequency, and at least one of --inputs/--outputs are required
repave batch-jobs update --command ./settle.sh <batch-job-id>
repave reports list
repave reports list --unused # reports no scenario names yet
repave reports add --engine CRYSTAL --trigger SCHEDULED --schedule nightly --formats PDF --sources src/reports/EXPDEP01.rpt "Expected Departure" "Nightly departure manifest"
repave reports add --realm modernized --legacy-ref expected-departure "Expected Departure Report" # the template is mapped in the app, not here
repave reports show expected-departure-report # template path (or where to write it) and the parameter/dataset contract
repave db tables search booking # each match includes the effective database modernization policy disposition when the policy is enabled
repave db tables list --realm modernized
repave db tables add --realm modernized --schema public --columns '[{"name":"id","type":"uuid","primaryKey":true},{"name":"customer_id","type":"uuid","references":{"table":"public.customers","column":"id"}}]' --description "Bookings placed by customers" --legacy-ref "dbo.BOOKING" "bookings"
repave db stored-procs search booking # also includes the effective disposition when the policy is enabled
repave db stored-procs read dbo.usp_CreateBooking
repave db policy get
repave db policy set-defaults --schema-disposition RETAIN --stored-proc-disposition RETAIN
repave db policy set-object --kind TABLE --disposition MODERNIZE dbo.BOOKING
repave db policy set-object --kind STORED_PROC --disposition RETAIN dbo.usp_CreateBooking

The views, apis, batch-jobs, and messaging-destinations registries are split by realm: LEGACY rows are the discovered as-is inventory of the legacy system, and MODERNIZED rows are the to-be catalog for the modernized application. add defaults to the legacy realm; pass --realm modernized when registering to-be rows, and use --legacy-ref "<legacy row name>" to link a modernized row back to the legacy row it replaces. list accepts --realm legacy|modernized to filter by realm (case-insensitive).

Modernized rows must be fully specified at creation; both the CLI and the server reject incomplete modernized adds:

  • Modernized REST API Catalog rows are imported from OpenAPI group files with repave apis import-openapi --realm modernized <openapi-file>. Do not use repave apis add/update --realm modernized. The imported OpenAPI document is stored as the source of truth; API Catalog rows are a derived index for search, validation, and # @apis references.
  • views add --realm modernized requires --type, --route, and a description.
  • batch-jobs add --realm modernized requires a description, --trigger or --frequency, and at least one of --inputs/--outputs.
  • messaging-destinations add --realm modernized requires --platform, --destination-type, --direction, --payload-format, and a description.

repave db policy controls database modernization strategy when clients want retained schemas/procedures or a hybrid path. set-defaults enables the policy and sets project defaults for schema objects and stored procedures/functions. set-object adds per-object overrides. To-be validation and generated validation rules use the effective disposition: RETAIN objects may remain runtime dependencies; MODERNIZE objects must use the modernized schema/service contract; REPLACE requires --replacement-ref; RETIRE forbids runtime use.

Navigation maps are imported from a canonical JSON document ( --> for the shape). Import validates and persists in one call; validate runs the same checks without writing:

repave navigation-map validate nav-map.json --realm legacy
repave navigation-map import nav-map.json --realm legacy --source static-scan --activate

Import upserts a DRAFT map (re-importing the same version replaces its nodes), resolves each node's viewBindings by viewName or viewRegistryItemId against the View Registry in the same realm, recomputes node depth and sibling order from parent links, and stores payload omissions in the map's metadata. --activate activates the imported map when no map is active for the realm; if one is already active, the import still succeeds as a DRAFT with an ACTIVATION_SKIPPED warning, and repave navigation-map activate <map-id> --replace-active supersedes the previous active map. --dry-run reports the counts and version a real import would produce without writing, and --strict fails the import on unresolved view bindings instead of downgrading them to warnings.

repave navigation-map path-for-view "<View Name>" --realm legacy returns the best root-first navigation path for a view from the active map, ranked by binding confidence, binding type, then depth.

Modernized API OpenAPI import​

Modernized REST API Catalog entries are sourced from OpenAPI 3.x group files under modernized/api/. Each file represents one API group, where the group is the operation's primary OpenAPI tag. Import stores the full OpenAPI file and derives REST operation rows from it; Swagger UI defaults to a combined view built from the stored documents and also lets users switch to each original imported file.

repave validate openapi modernized/api/orders.openapi.yaml
repave apis import-openapi --realm modernized modernized/api/orders.openapi.yaml

Import stores or replaces the OpenAPI document, upserts each REST operation by method and path in the MODERNIZED realm, and deletes derived rows for operations removed from the re-imported file. Catalog names are canonicalized as <METHOD> <path>, and contractReference points back to the imported file.

Legacy API inventory can still use repave apis add and repave apis update.

Legacy API request/response specs​

Legacy API rows may still store OpenAPI-compatible request/response fragments through repave apis add and repave apis update:

repave apis add \
--type REST --method GET --path /legacy/orders/{id} --auth "session cookie" \
--handler legacy/OrderController.java \
--response-spec '{"200":{"description":"Order record"},"404":{"description":"Order not found"}}' \
"Legacy Order Lookup API" "Looks up an order in the legacy application"

repave apis update --status CONFIRMED <api-id>

Legacy-only provenance fields are never set on modernized rows and are stripped server-side: --handler/--source-refs/--disposition/--target-notes for APIs, --legacy-file/--source-refs/--disposition/--target-notes for views, --entry-point/--command/--source-files/--disposition/--target-notes for batch jobs, and --source-refs/--disposition/--target-notes for messaging destinations.

db tables is realm-split differently: legacy tables come only from DB discovery (repave db tables search), while the DB Table Catalog (modernized to-be tables for RDBMS targets) is managed with repave db tables list --realm modernized and repave db tables add --realm modernized — both require --realm modernized. add requires --description and --columns as a JSON array of {"name","type"} objects (optionally nullable, primaryKey, default, and references: {table, column}), defaults --schema to public, and is idempotent on (schema, name).

  • Tests should shift from duplicated helper-script logic to shared CLI/library modules.

Rendered Evidence (repave trace)​

repave trace reads the Playwright traces a feature's most recent BDD run collected, and turns them into images an agent can look at. It exists for the UI verification gate: comparing a prototype or Figma frame against the implementation's source cannot show a control that renders but is clipped, overlapped, zero-height, or never reached.

repave trace list "Book Return"
repave trace list "Book Return" --scenario "Returning a borrowed book" --json
repave trace frames "Book Return" --scenario "Returning a borrowed book" --out .ui-verification/frames --max 8
  • list shows one row per scenario trace from the newest test execution — scenario name, artifact id, capture time, size, and collection status. A feature whose run captured no trace says so; a trace whose collection recorded it as anything but valid is listed with its status and cannot be exported.
  • frames writes one JPEG per Gherkin step into <out>/<scenario-slug>/, plus a frames.json manifest. The exported frame is the last one painted inside that step's tracing group — the state the user is left in when the step finishes. When a scenario was retried inside one execution, the newest trace is the one exported.
  • Flags may come before or after the feature reference; -- ends the flag section for a feature title that starts with a dash.

The step attribution comes from the Cucumber support hooks, which wrap each step in tracing.group("Step N: <step text>") (see prompt/stage-1-implement-feature.md). A run whose hooks do not group by step has no step boundaries, and frames refuses it rather than exporting an arbitrary slice of the screencast.

frames.json explains every step it did not export, so a gap in the manifest is never read as "nothing to check here":

skipped reasonMeaning
no-frameThe step's window holds no screencast frame — the step is unverified.
blank-onlyEvery frame in the window was a blank page mid-navigation — unverified.
no-repaintThe step ended on the frame the previous step already showed — normal.
over-capThe step is past --max (server default 12).

Reads require the features:read scope and a token scoped to the feature's project, which the agent token issued to every job already carries.

BDD And Characterization Runners​

run-bdd-tests.sh, bdd-project.config.sh, and run-char-tests.* should be migrated after the API helper commands.

Target commands:

repave bdd run <feature-path>
repave bdd run --all
repave char run

These runners have project lifecycle behavior, shell integration, artifact paths, and platform-specific process handling, so they should be treated as a separate migration slice rather than bundled into the first CLI cut.

Implementation Plan​

Phase 1 - Server Contracts​

Add or harden the API contracts the CLI needs before moving command behavior out of helper scripts.

  • Add token-based CLI auth endpoints:
    • Start CLI auth challenge.
    • Complete browser callback.
    • Exchange one-time code for user-scoped CLI tokens.
    • Refresh and revoke CLI tokens if refresh tokens are supported.
  • Add agent-token issuance for job runners:
    • Project-scoped.
    • Job/session-scoped when available.
    • Short TTL.
    • Explicit permission scopes.
    • Auditable identity like agent:<jobId>.
  • Add path resolution endpoint:
    • Input: absolute local path plus optional hostname/user metadata.
    • Output: matching project context if the authenticated caller has access.
    • Return disambiguation details when multiple projects match.
  • Ensure all CLI-targeted write endpoints enforce server-side edit access.
  • Fix or replace any endpoint that currently depends only on global middleware for authorization.
  • Normalize error responses so CLI can render useful messages for auth, forbidden, validation, and not-found cases.

Phase 2 - Go CLI Foundation​

Build the CLI skeleton and shared client behavior.

  • Create cli/repave Go module.
  • Implement command routing with grouped domains.
  • Implement ~/.repave/config.json profile loading and writing with owner-only permissions where supported.
  • Implement base URL resolution:
    • --base-url
    • REPAVE_API_BASE_URL
    • .repave/project.json
    • ~/.repave/config.json
    • http://localhost:3000
  • Implement project context resolution:
    • --project-id
    • upward .repave/project.json search
    • workspace path inference
    • server path resolution
    • REPAVE_PROJECT_ID fallback
  • Implement auth resolution:
    • REPAVE_AGENT_TOKEN
    • human profile token
    • temporary INTERNAL_API_KEY bridge
  • Implement output helpers:
    • default human output
    • --json
    • stable exit codes
    • clear forbidden/read-only errors

Phase 3 - Human Auth Commands​

Implement human login and account state.

  • repave auth login
  • repave auth status
  • repave auth logout
  • Browser login with local callback on 127.0.0.1:<random-port>.
  • Store auth state in ~/.repave/config.json.
  • Validate that repave projects list only returns accessible projects.
  • Validate read-only users can run read commands but cannot run mutations.

Phase 4 - Project Metadata​

Wire project context into workspace setup.

  • Write .repave/project.json during ZIP, Git, and local clone workspace setup.
  • Include non-secret context only.
  • Ensure project context is discoverable from:
    • workspace root
    • legacy/
    • legacy/codebase/
    • modernized root
  • Add repave project current.
  • Add repave project link <project-id> for manual context repair.

Phase 5 - API Helper Commands​

Implement the API-backed helper surface first.

  • Features:
    • list
    • read
    • read-gherkin
    • search
    • create
    • write/update
    • delete
  • Scenarios:
    • search
    • read
    • add
    • replace
    • delete
  • Registries:
    • personas
    • views
    • APIs
    • external systems
    • legacy data stores
    • batch jobs
  • Database object lookup:
    • tables search
    • stored procs search
    • stored proc read
  • Code segment tags:
    • list
    • tag
    • retag
    • untag
  • Artifacts:
    • architecture save
    • ER save
    • design save
    • business requirements save
  • Verification:
    • request
    • poll
  • User journeys and scenario proposals.

For this phase, validation should primarily happen server-side. The CLI may perform lightweight input checks, such as missing file, malformed local JSON, or empty arguments, but should not become the canonical validator for Gherkin, Mermaid, DB objects, or project authorization.

Phase 6 - Agent Runtime Integration​

Make agents use the CLI without browser login.

  • Generate short-lived REPAVE_AGENT_TOKEN for each agent job.
  • Inject REPAVE_AGENT_TOKEN, REPAVE_API_BASE_URL, REPAVE_PROJECT_ID, and the configured local repavePath into local agent environments.
  • Inject REPAVE_AGENT_TOKEN and REPAVE_API_BASE_URL into Docker and Kubernetes agent environments.
  • Package the Go binary into the app container and agent-runner image.
  • Update K8s setup to copy or expose the repave binary instead of copying every helper script implementation.
  • Keep .repave/project.json non-secret and rely on env vars for credentials.

Phase 7 - Direct Prompt Migration​

Move migrated workflows directly to repave commands.

  • Remove migrated helper entrypoints instead of preserving wrapper shims.
  • Keep final JSON output compatible in the CLI where agents parse or inspect command output.
  • Update prompts and workspace instructions in the same change as each command migration.
  • Do not copy helper scripts into agent workspaces.

Phase 8 - Prompt, Docs, And Tests Migration​

Move the codebase from script names to repave commands.

  • Update prompt templates to use repave ....
  • Update templates/CLAUDE-legacy.md.
  • Update docs/claude-reference.md to replace the "helper scripts must live in templates/legacy" rule with the CLI contract.
  • Update service tests that assert helper-script copying.
  • Replace duplicated helper-script tests with CLI and shared API-contract tests.
  • Add integration tests for:
    • human read access
    • human read-only mutation denial
    • inaccessible project denial
    • agent token project scoping
    • automatic project resolution from workspace paths

Phase 9 - BDD And Characterization Commands​

Migrate lifecycle runners after the API helper commands are stable.

  • Implement repave bdd run <feature-path>.
  • Implement repave bdd run --all.
  • Implement repave char run.
  • Preserve artifact paths and run tracking behavior expected by existing services.
  • Update BddTestService and characterization services to call the CLI.
  • Keep shell or PowerShell shims only as temporary compatibility wrappers if needed.

Phase 10 - Remaining Helper Removal​

Remove remaining helper-script implementations after their CLI commands exist.

  • Delete implementation-heavy templates/legacy/*.js and templates/legacy/*.py files after prompts and services no longer reference them.
  • Remove stale tests for duplicated helper-script logic.
  • Remove INTERNAL_API_KEY usage from agent CLI paths once bearer-token auth is fully deployed.
  • Update Docker/K8s images so they no longer bake copied helper scripts.