devrites 5.10.0 → 5.10.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +13 -0
- package/README.md +5 -2
- package/docs/architecture.md +8 -4
- package/docs/cli.md +4 -1
- package/docs/command-map.md +3 -2
- package/docs/engine/commands.md +7 -5
- package/docs/orchestration.md +5 -2
- package/docs/skills.md +1 -1
- package/docs/usage.md +5 -3
- package/engine/commands.go +3 -0
- package/engine/help.go +190 -0
- package/engine/help_test.go +98 -0
- package/engine/internal/parallel/cli.go +90 -32
- package/engine/internal/parallel/cli_test.go +32 -0
- package/engine/main.go +15 -8
- package/engine/root_routing_test.go +4 -0
- package/pack/.claude/skills/devrites-lib/reference/standards/afk-hitl.md +27 -9
- package/pack/.claude/skills/rite-autocomplete/SKILL.md +20 -9
- package/pack/.claude/skills/rite-autocomplete/reference/decision-policy.md +5 -2
- package/pack/.claude/skills/rite-autocomplete/reference/loop.md +25 -14
- package/pack/.claude/skills/rite-autocomplete/reference/stop-conditions.md +10 -3
- package/pack/.claude/skills/rite-build/SKILL.md +3 -1
- package/pack/.claude/skills/rite-build/reference/afk-discipline.md +4 -3
- package/pack/.claude/skills/rite-build/reference/parallel-batch.md +3 -1
- package/pack/.claude/skills/rite-define/reference/gates.md +5 -0
- package/pack/.claude/skills/rite-define/reference/plan-template.md +1 -1
- package/pack/.claude/skills/rite-resolve/SKILL.md +5 -2
- package/pack/generated/claude/skills/devrites-lib/reference/standards/afk-hitl.md +27 -9
- package/pack/generated/claude/skills/rite-autocomplete/SKILL.md +20 -9
- package/pack/generated/claude/skills/rite-autocomplete/reference/decision-policy.md +5 -2
- package/pack/generated/claude/skills/rite-autocomplete/reference/loop.md +25 -14
- package/pack/generated/claude/skills/rite-autocomplete/reference/stop-conditions.md +10 -3
- package/pack/generated/claude/skills/rite-build/SKILL.md +3 -1
- package/pack/generated/claude/skills/rite-build/reference/afk-discipline.md +4 -3
- package/pack/generated/claude/skills/rite-build/reference/parallel-batch.md +3 -1
- package/pack/generated/claude/skills/rite-define/reference/gates.md +5 -0
- package/pack/generated/claude/skills/rite-define/reference/plan-template.md +1 -1
- package/pack/generated/claude/skills/rite-resolve/SKILL.md +5 -2
- package/pack/generated/codex/skills/devrites-lib/reference/standards/afk-hitl.md +27 -9
- package/pack/generated/codex/skills/rite-autocomplete/SKILL.md +20 -9
- package/pack/generated/codex/skills/rite-autocomplete/reference/decision-policy.md +5 -2
- package/pack/generated/codex/skills/rite-autocomplete/reference/loop.md +25 -14
- package/pack/generated/codex/skills/rite-autocomplete/reference/stop-conditions.md +10 -3
- package/pack/generated/codex/skills/rite-build/SKILL.md +3 -1
- package/pack/generated/codex/skills/rite-build/reference/afk-discipline.md +4 -3
- package/pack/generated/codex/skills/rite-build/reference/parallel-batch.md +3 -1
- package/pack/generated/codex/skills/rite-define/reference/gates.md +5 -0
- package/pack/generated/codex/skills/rite-define/reference/plan-template.md +1 -1
- package/pack/generated/codex/skills/rite-resolve/SKILL.md +5 -2
- package/pack/generated/omp/skills/devrites-lib/reference/standards/afk-hitl.md +27 -9
- package/pack/generated/omp/skills/rite-autocomplete/SKILL.md +20 -9
- package/pack/generated/omp/skills/rite-autocomplete/reference/decision-policy.md +5 -2
- package/pack/generated/omp/skills/rite-autocomplete/reference/loop.md +25 -14
- package/pack/generated/omp/skills/rite-autocomplete/reference/stop-conditions.md +10 -3
- package/pack/generated/omp/skills/rite-build/SKILL.md +3 -1
- package/pack/generated/omp/skills/rite-build/reference/afk-discipline.md +4 -3
- package/pack/generated/omp/skills/rite-build/reference/parallel-batch.md +3 -1
- package/pack/generated/omp/skills/rite-define/reference/gates.md +5 -0
- package/pack/generated/omp/skills/rite-define/reference/plan-template.md +1 -1
- package/pack/generated/omp/skills/rite-resolve/SKILL.md +5 -2
- package/pack/generated/pi/skills/devrites-lib/reference/standards/afk-hitl.md +27 -9
- package/pack/generated/pi/skills/rite-autocomplete/SKILL.md +20 -9
- package/pack/generated/pi/skills/rite-autocomplete/reference/decision-policy.md +5 -2
- package/pack/generated/pi/skills/rite-autocomplete/reference/loop.md +25 -14
- package/pack/generated/pi/skills/rite-autocomplete/reference/stop-conditions.md +10 -3
- package/pack/generated/pi/skills/rite-build/SKILL.md +3 -1
- package/pack/generated/pi/skills/rite-build/reference/afk-discipline.md +4 -3
- package/pack/generated/pi/skills/rite-build/reference/parallel-batch.md +3 -1
- package/pack/generated/pi/skills/rite-define/reference/gates.md +5 -0
- package/pack/generated/pi/skills/rite-define/reference/plan-template.md +1 -1
- package/pack/generated/pi/skills/rite-resolve/SKILL.md +5 -2
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,19 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to DevRites are documented here. The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and DevRites adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). Releases are generated automatically by [semantic-release](https://semantic-release.gitbook.io/) from Conventional Commits on `main`.
|
|
4
4
|
|
|
5
|
+
## [5.10.2](https://github.com/ViktorsBaikers/DevRites/compare/v5.10.1...v5.10.2) (2026-09-15)
|
|
6
|
+
|
|
7
|
+
### Fixed
|
|
8
|
+
|
|
9
|
+
* **ci:** stop gosec false positive on secret-scan help ([ac8ef15](https://github.com/ViktorsBaikers/DevRites/commit/ac8ef1530f5121cf192fb9e60acef781b0e3fbb8))
|
|
10
|
+
* **rite:** honor --parallel over leftover AFK cap ([#78](https://github.com/ViktorsBaikers/DevRites/issues/78)) ([db6c299](https://github.com/ViktorsBaikers/DevRites/commit/db6c299e24be5e1b8c5739e45542f2ef8b572e3b))
|
|
11
|
+
|
|
12
|
+
## [5.10.1](https://github.com/ViktorsBaikers/DevRites/compare/v5.10.0...v5.10.1) (2026-09-15)
|
|
13
|
+
|
|
14
|
+
### Fixed
|
|
15
|
+
|
|
16
|
+
* **rite:** auto-resolve recommended blocking questions in autocomplete ([#77](https://github.com/ViktorsBaikers/DevRites/issues/77)) ([cd11a59](https://github.com/ViktorsBaikers/DevRites/commit/cd11a59c01b9b1cd48df5a19a7b8ad3c4f1fb2d4))
|
|
17
|
+
|
|
5
18
|
## [5.10.0](https://github.com/ViktorsBaikers/DevRites/compare/v5.9.0...v5.10.0) (2026-09-15)
|
|
6
19
|
|
|
7
20
|
### Added
|
package/README.md
CHANGED
|
@@ -33,7 +33,7 @@ project-conventional push, tag, or PR action, and archive the workspace.
|
|
|
33
33
|
Unattended runs may create local WIP checkpoint commits along the way, but they
|
|
34
34
|
remain local unless Ship's disclosed plan includes an approved remote action.
|
|
35
35
|
|
|
36
|
-
**Status:** [`v5.10.
|
|
36
|
+
**Status:** [`v5.10.2`](https://github.com/ViktorsBaikers/DevRites/releases/tag/v5.10.2): see [`CHANGELOG.md`](CHANGELOG.md) for release notes.
|
|
37
37
|
|
|
38
38
|
This is the latest published release; `main` may contain unreleased work.
|
|
39
39
|
|
|
@@ -252,7 +252,10 @@ ignored and never rewritten. Delete `.devrites/AFK` to
|
|
|
252
252
|
return to HITL.
|
|
253
253
|
|
|
254
254
|
AFK still pauses for product, scope, or policy choices, irreversible risk,
|
|
255
|
-
and access or actions available only to a human.
|
|
255
|
+
and access or actions available only to a human. `/rite-autocomplete` is the
|
|
256
|
+
exception that auto-resolves blocking questions already naming a ranked
|
|
257
|
+
recommended option; escalating, irreversible-risk, access, and blocking with
|
|
258
|
+
no recommended option still pause. Agents use bounded recovery
|
|
256
259
|
for red tests, type or lint errors, runtime failures, and missing technical
|
|
257
260
|
coverage. If that recovery budget runs out, they record a technical blocker
|
|
258
261
|
instead of asking a question. The full pause and gate contract is in
|
package/docs/architecture.md
CHANGED
|
@@ -39,7 +39,9 @@ cursor readers, traceability rules, and phase-relative completeness model, see
|
|
|
39
39
|
through Seal GO and stops. Explicit `--ship` enters Ship preflight but still
|
|
40
40
|
waits for a fresh literal `GO` and native approval before mutation. It
|
|
41
41
|
chooses the recommended option at each soft gate and pauses for hard
|
|
42
|
-
irreversible-risk,
|
|
42
|
+
irreversible-risk, escalating, unanswered blocking with no recommended
|
|
43
|
+
option, or a remaining NO-GO. Open blocking questions that already name a
|
|
44
|
+
ranked recommended option auto-resolve; they are not a user handoff. The
|
|
43
45
|
`devrites-` prefix prevents collisions with bundled Claude Code skill names
|
|
44
46
|
such as `prototype`, `handoff`, `triage`, and `diagnose`; it does not mark
|
|
45
47
|
visibility. `rite-pressure-test` needs no prefix because it does not collide.
|
|
@@ -336,9 +338,11 @@ workflow. It carries state through workspace files rather than chat. A vague
|
|
|
336
338
|
prompt starts `devrites-interview`; `/rite-spec` and `/rite-clarify` then
|
|
337
339
|
finish the only interactive window. After decision coverage is CLEAR it runs unattended,
|
|
338
340
|
choosing the recommended option at each soft gate and recording the rationale
|
|
339
|
-
in `decisions.md`. It does **not** weaken
|
|
340
|
-
gates: human-only access/actions, those gates,
|
|
341
|
-
|
|
341
|
+
in `decisions.md`. It does **not** weaken escalating or irreversible-risk
|
|
342
|
+
gates: human-only access/actions, those gates, unanswered blocking with no
|
|
343
|
+
recommended option, a remaining NO-GO, or low confidence still pause. Open
|
|
344
|
+
blocking questions that already name a ranked recommended option auto-resolve
|
|
345
|
+
through `devrites-engine state resolve`. Temper expand, validating gates, and default slice/agent/time/review-queue
|
|
342
346
|
caps do not. `--max-slices` is ignored the same way. Agents use bounded recovery for red tests, runtime failures, and
|
|
343
347
|
missing technical coverage. By default the workflow stops at Seal GO with `/rite-ship` as the next step.
|
|
344
348
|
The `--ship` flag (alias `--yolo`) continues through Ship preflight, discloses
|
package/docs/cli.md
CHANGED
|
@@ -8,7 +8,10 @@ cross-host primitives; it never dispatches an agent or grades reviewer prose.
|
|
|
8
8
|
## Command surface
|
|
9
9
|
|
|
10
10
|
`devrites-engine help` lists the exhaustive operational surface. Standard
|
|
11
|
-
`help`, `-h`, `--help`, `version`, and `--version` forms also remain supported
|
|
11
|
+
`help`, `-h`, `--help`, `version`, and `--version` forms also remain supported.
|
|
12
|
+
Each operational command and subcommand accepts `-h` / `--help` and prints that
|
|
13
|
+
command's usage to stdout (exit 0) without requiring a workspace. Missing
|
|
14
|
+
required arguments still print usage to stderr and exit 2.
|
|
12
15
|
|
|
13
16
|
```text
|
|
14
17
|
devrites-engine install [flags]
|
package/docs/command-map.md
CHANGED
|
@@ -49,7 +49,8 @@ The Go surface is intentionally closed and deterministic:
|
|
|
49
49
|
|
|
50
50
|
`devrites-engine help` exhaustively lists the operational engine commands.
|
|
51
51
|
Standard `help`, `-h`, `--help`, `version`, and `--version` forms remain
|
|
52
|
-
available;
|
|
52
|
+
available; each operational command and subcommand also accepts `-h` / `--help`.
|
|
53
|
+
There are no legacy operational aliases. `check candidate <slug>`
|
|
53
54
|
prints exactly
|
|
54
55
|
`candidate-sha256: <64 lowercase hex>` and `candidate-files: <row count>` on a
|
|
55
56
|
pass; usage/root errors exit `2` and candidate blocks exit `3`. There are no
|
|
@@ -99,7 +100,7 @@ See [`cli.md`](cli.md).
|
|
|
99
100
|
| [`/rite-review`](../pack/.claude/skills/rite-review/SKILL.md) | review | `[scope: slice N \| feature] [--full]` | Feature-scoped parallel Spec + Standards review of the closed candidate; binds `review.md` to its digest. | workspace + candidate | `review.md`, `evidence.md`, `state.md` |
|
|
100
101
|
| [`/rite-seal`](../pack/.claude/skills/rite-seal/SKILL.md) | seal | `[feature-slug] [--full]` | Candidate-bound GO / NO-GO **decision**. Rechecks exact proof/review bindings and runs no git; on GO sets `Next step: /rite-ship`. | all artifacts + candidate | `seal.md`, `state.md` |
|
|
101
102
|
| [`/rite-ship`](../pack/.claude/skills/rite-ship/SKILL.md) | ship | `[slug]` | Starts with a read-only Seal/candidate check and exact plan disclosure, then requires fresh literal `GO`. Only then may it collapse checkpoints, stage exact candidate paths, validate staged scope/bytes/bindings/secrets, commit and reverify, perform optional separately approved push/tag/PR actions, and archive/close. | `seal.md` + candidate + Git index | `ship.md`, `state.md`, archive |
|
|
102
|
-
| [`/rite-autocomplete`](../pack/.claude/skills/rite-autocomplete/SKILL.md) | (orchestrator) | `[idea] [--ship\|--yolo] [--max-slices N] [--full] [--cross-model]` | Full lifecycle (spec → clarify → … → seal → ship). Spec + clarify form the one interactive window; AFK/checkpoint mode arms only after decision coverage is CLEAR. Pauses for irreversible risk,
|
|
103
|
+
| [`/rite-autocomplete`](../pack/.claude/skills/rite-autocomplete/SKILL.md) | (orchestrator) | `[idea] [--ship\|--yolo] [--max-slices N] [--full] [--cross-model]` | Full lifecycle (spec → clarify → … → seal → ship). Spec + clarify form the one interactive window; AFK/checkpoint mode arms only after decision coverage is CLEAR. Pauses for irreversible risk, escalating, unanswered blocking with no recommended option, human-only access, or remaining NO-GO — not recommended-option blocking questions, temper expand, validating gates, `--max-slices`, or default resource caps. Objective red checks use bounded technical recovery. Default stops at Seal GO; `--ship` (`--yolo`) reaches Ship preflight but still requires fresh literal `GO` and native approval; `--full` selects the Full profile and `--cross-model` arms Vet's second opinion. | idea + workspace | whole workspace (drives every phase) |
|
|
103
104
|
| [`/rite-quick`](../pack/.claude/skills/rite-quick/SKILL.md) | (express) | `<change>` | Express lane for a **small, reversible, unambiguous** change: one-line contract → TDD build → scoped prove → review-lite → ship, no full artifact tree. **Significance gate first**: auth / migration / public-API / destructive / multi-slice / ambiguous → escalates to `/rite-spec`. Triggers: "quick fix", "small change", "tiny tweak", "just do X". | the change + codebase | code + commit (optional `brief.md` / `evidence.md`) |
|
|
104
105
|
| [`/rite-frame`](../pack/.claude/skills/rite-frame/SKILL.md) | lens | `[ask \| diff]` | Pre-flight + self-audit lens for ad-hoc work the lifecycle gates never see: **FRAME** turns an imperative ask into a falsifiable success criterion + verify command before code; **AUDIT** checks a raw diff against the four LLM coding failure modes (silent assumption / overcomplication / out-of-scope edit / unverifiable goal). Top of `/rite-quick` or before a plain "just do X". | the ask / a raw diff | success criterion + verify command (inline) |
|
|
105
106
|
| [`/rite-adopt`](../pack/.claude/skills/rite-adopt/SKILL.md) | onboard | `[path or area to adopt] [+ what you want to build next]` | Reverse-derive the existing baseline and next objective; propose any durable project guidance in the nearest native instruction file. | codebase + project instructions | workspace spec/state artifacts + optional guidance proposal |
|
package/docs/engine/commands.md
CHANGED
|
@@ -27,11 +27,13 @@ workflow strategy.
|
|
|
27
27
|
| `open-visual <path-or-name> [--slug <slug>] [--no-open]` | Resolve a local visual HTML file, optionally open it in the OS browser, warn if the sibling outline is missing or inventory ids are absent from HTML, and print agent path tips. No network. |
|
|
28
28
|
| `version` | Print the engine version. |
|
|
29
29
|
|
|
30
|
-
`help`, `-h`, and `--help` print this operational inventory.
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
30
|
+
`help`, `-h`, and `--help` print this operational inventory. Each operational
|
|
31
|
+
command and subcommand also accepts `-h` / `--help` and prints that command's
|
|
32
|
+
usage to stdout without requiring a workspace. `version` and `--version` print
|
|
33
|
+
the binary version. Other unlisted command forms are rejected as unknown; the
|
|
34
|
+
engine has no compatibility aliases or tombstones. The `add`/`upgrade`/`remove`
|
|
35
|
+
conveniences belong only to the `npx devrites` adapter, not to the engine
|
|
36
|
+
command namespace.
|
|
35
37
|
|
|
36
38
|
## Check boundary
|
|
37
39
|
|
package/docs/orchestration.md
CHANGED
|
@@ -128,8 +128,11 @@ preserved. No ad hoc copy/cherry-pick/merge occurs from the read-only root.
|
|
|
128
128
|
|
|
129
129
|
Isolation does not authorize same-worktree throughput. Parallel writers are allowed
|
|
130
130
|
**only** under the `/rite-build` batch path when path-disjoint eligibility, abort-batch,
|
|
131
|
-
and a control `parallel-lease.md` apply. `--parallel N`
|
|
132
|
-
|
|
131
|
+
and a control `parallel-lease.md` apply. `--parallel N` on `/rite-autocomplete`
|
|
132
|
+
or `/rite-build` is the batch cap for that run and wins over leftover sentinel
|
|
133
|
+
`max_parallel`; `/rite-autocomplete --parallel N` also writes or replaces only
|
|
134
|
+
that field. Absent the flag, the sentinel (default cap 10 under
|
|
135
|
+
`/rite-autocomplete`) is a **cap**: each round runs
|
|
133
136
|
the largest eligible set, recomputes after every completed round, and repeats until no
|
|
134
137
|
pending slice remains; a one-slice round is serial for that round only (see
|
|
135
138
|
[`parallel-batch.md`](../pack/.claude/skills/rite-build/reference/parallel-batch.md)).
|
package/docs/skills.md
CHANGED
|
@@ -222,7 +222,7 @@ and `Shipped`. Utility commands keep the same compact labels and one-next-action
|
|
|
222
222
|
|
|
223
223
|
| Skill | What It Does | Use When |
|
|
224
224
|
|---|---|---|
|
|
225
|
-
| [`rite-autocomplete`](../pack/.claude/skills/rite-autocomplete/SKILL.md) | Runs the lifecycle unattended (spec → clarify → … → seal → ship), choosing the recommended option at each soft gate and recording the rationale in `decisions.md`.<br>A vague prompt gets one up-front spec/clarify window; after CLEAR it does not iterate per phase.<br>It pauses only for irreversible risk,
|
|
225
|
+
| [`rite-autocomplete`](../pack/.claude/skills/rite-autocomplete/SKILL.md) | Runs the lifecycle unattended (spec → clarify → … → seal → ship), choosing the recommended option at each soft gate and recording the rationale in `decisions.md`.<br>A vague prompt gets one up-front spec/clarify window; after CLEAR it does not iterate per phase.<br>It pauses only for irreversible risk, escalating gates, unanswered blocking with no recommended option, human-only access, or remaining NO-GO — not recommended-option blocking questions, temper expand, validating gates, or default slice/agent/time/review-queue caps.<br>Objective red checks use bounded technical recovery, including cold-resume from retained one-shot evidence and one-time migration of stale missing-writer stops when an upgraded contract supplies the first supported writer. That migration applies once; the first real materializer failure is attempt one under normal bounded recovery. Internal `NEEDS_REPLAN` and new agent-owned review findings stay caller-owned backward edges, not user-facing stops. Spending an action authorization blocks another real execution, not offline repair.<br>Default stops at Seal GO. `--ship` (alias `--yolo`) reaches Ship preflight but still requires a fresh literal `GO` and native approval. | "Autocomplete", "do the whole thing", "run the full cycle", "one-shot this feature". |
|
|
226
226
|
| [`rite-zoom-out`](../pack/.claude/skills/rite-zoom-out/SKILL.md) | Map the modules, callers, callees, and decisions in an unfamiliar area using the project's domain glossary. | Explicit-only: `/rite-zoom-out` / `/rite zoom-out`. |
|
|
227
227
|
| [`rite-prototype`](../pack/.claude/skills/rite-prototype/SKILL.md) | Throwaway code answering ONE design question: logic harness OR 2 to 4 UI variations on one route. | Explicit-only: `/rite-prototype` / `/rite prototype`. |
|
|
228
228
|
| [`rite-handoff`](../pack/.claude/skills/rite-handoff/SKILL.md) | Compact chat session → handoff doc. References existing `.devrites/work/<slug>/` artifacts by path. | Explicit-only: `/rite-handoff` / `/rite handoff`. |
|
package/docs/usage.md
CHANGED
|
@@ -384,8 +384,8 @@ You: GO # → commit, optional approved push/tag/PR, then
|
|
|
384
384
|
|
|
385
385
|
Add `--ship` (alias `--yolo`) to continue through `/rite-ship` preflight.
|
|
386
386
|
Autocomplete discloses the exact Git plan, then still stops for a fresh literal
|
|
387
|
-
`GO` and native host approval. It also pauses for irreversible risk, human-only access/actions,
|
|
388
|
-
|
|
387
|
+
`GO` and native host approval. It also pauses for irreversible risk, human-only access/actions, escalating
|
|
388
|
+
gates, unanswered blocking with no recommended option, a remaining NO-GO, or low confidence — not for recommended-option blocking questions, temper expand, validating
|
|
389
389
|
gates, `--max-slices`, or default slice/agent/time/review-queue caps.
|
|
390
390
|
Objective red checks use bounded recovery instead. Args:
|
|
391
391
|
`[idea] [--ship|--yolo] [--max-slices N] [--full] [--cross-model]`.
|
|
@@ -413,7 +413,9 @@ remaining from the pending-slice count and ignores leftover remaining and
|
|
|
413
413
|
- **`.devrites/AFK` is per-developer, not per-repo**: gitignore it (or commit
|
|
414
414
|
it deliberately if the team agrees on AFK defaults). The sentinel is
|
|
415
415
|
read-only config: it toggles your local session mode and sets the initial
|
|
416
|
-
`max_slices` budget
|
|
416
|
+
`max_slices` budget. `/rite-autocomplete --parallel N` is the one exception:
|
|
417
|
+
it writes or replaces only `max_parallel: N` so a leftover `1` cannot keep
|
|
418
|
+
later ticks serial. The mutable remaining-slice count lives
|
|
417
419
|
in `state.md` (`AFK slices remaining`), never in the sentinel. The root
|
|
418
420
|
charges it once with each green pending → built transition and stops before
|
|
419
421
|
another dispatch at zero; malformed values fail closed.
|
package/engine/commands.go
CHANGED
|
@@ -25,6 +25,9 @@ const (
|
|
|
25
25
|
// workspace or Git state, which must never fall back after an unsafe root
|
|
26
26
|
// selection.
|
|
27
27
|
func rootModeFor(command string, args []string) rootMode {
|
|
28
|
+
if hasHelpFlag(args) || (len(args) > 0 && isHelpToken(args[0])) {
|
|
29
|
+
return rootUnused
|
|
30
|
+
}
|
|
28
31
|
subcommand := firstRootOperand(args)
|
|
29
32
|
switch command {
|
|
30
33
|
case "secret-scan", "open-visual":
|
package/engine/help.go
ADDED
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
package main
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"fmt"
|
|
5
|
+
"io"
|
|
6
|
+
"strings"
|
|
7
|
+
|
|
8
|
+
"github.com/devrites/devrites/internal/parallel"
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
const (
|
|
12
|
+
checkUsage = `usage: devrites-engine check <candidate|readiness|seal|path-disjoint|task-graph|skill-trust|indexes> ...
|
|
13
|
+
|
|
14
|
+
check candidate <slug>
|
|
15
|
+
check readiness <slug>
|
|
16
|
+
check readiness --emit-binding <slug>
|
|
17
|
+
check seal <slug>
|
|
18
|
+
check path-disjoint [--root <dir>] [<json-file>|-]
|
|
19
|
+
check task-graph <slug>
|
|
20
|
+
check skill-trust <path>
|
|
21
|
+
check indexes [--root <dir>]
|
|
22
|
+
`
|
|
23
|
+
|
|
24
|
+
stateUsage = `usage: devrites-engine state <resolve|merge-manifest|close> ...
|
|
25
|
+
|
|
26
|
+
state resolve <qid> "<answer>"
|
|
27
|
+
state resolve --drop <qid> ["<reason>"]
|
|
28
|
+
state resolve --batch <file>
|
|
29
|
+
state merge-manifest <slug> [<predecessor>...]
|
|
30
|
+
state close <slug>
|
|
31
|
+
`
|
|
32
|
+
|
|
33
|
+
observeUsage = `usage: devrites-engine observe summary <slug> | observe slice <slug> <SLICE-ID>
|
|
34
|
+
|
|
35
|
+
observe summary [slug]
|
|
36
|
+
observe slice <slug> <SLICE-ID>
|
|
37
|
+
`
|
|
38
|
+
|
|
39
|
+
orientUsage = "usage: devrites-engine orient [slug]"
|
|
40
|
+
migrateUsage = "usage: devrites-engine migrate <slug> [--dry-run] [--answer id=choice]"
|
|
41
|
+
// scanCmdUsage is help text for `secret-scan`. The identifier must not
|
|
42
|
+
// contain "secret": gosec G101 treats secret* string consts as credentials.
|
|
43
|
+
scanCmdUsage = "usage: devrites-engine secret-scan [--staged] [--stdin] [slug]"
|
|
44
|
+
openVisualUsage = "usage: devrites-engine open-visual <path-or-name> [--slug <slug>] [--no-open]"
|
|
45
|
+
versionUsage = "usage: devrites-engine version"
|
|
46
|
+
candidateUsage = "usage: devrites-engine check candidate <slug>"
|
|
47
|
+
readinessUsage = "usage: devrites-engine check readiness <slug>\n devrites-engine check readiness --emit-binding <slug>"
|
|
48
|
+
sealUsage = "usage: devrites-engine check seal <slug>"
|
|
49
|
+
taskGraphUsage = "usage: devrites-engine check task-graph <slug>"
|
|
50
|
+
skillTrustUsage = "usage: devrites-engine check skill-trust <path>"
|
|
51
|
+
indexesUsage = "usage: devrites-engine check indexes [--root <dir>]"
|
|
52
|
+
resolveUsage = `usage: devrites-engine state resolve <qid> "<answer>" | state resolve --drop <qid> ["<reason>"] | state resolve --batch <file>`
|
|
53
|
+
mergeManifestUsage = "usage: devrites-engine state merge-manifest <slug> [<predecessor>...]"
|
|
54
|
+
closeUsage = "usage: devrites-engine state close <slug>"
|
|
55
|
+
observeSliceUsage = "usage: devrites-engine observe slice <slug> <SLICE-ID>"
|
|
56
|
+
observeSummaryUsage = "usage: devrites-engine observe summary [slug]"
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
func isHelpFlag(arg string) bool {
|
|
60
|
+
switch arg {
|
|
61
|
+
case "-h", "-help", "--help":
|
|
62
|
+
return true
|
|
63
|
+
default:
|
|
64
|
+
return false
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
func isHelpToken(arg string) bool {
|
|
69
|
+
return isHelpFlag(arg) || arg == "help"
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
func hasHelpFlag(args []string) bool {
|
|
73
|
+
for _, arg := range args {
|
|
74
|
+
if isHelpFlag(arg) {
|
|
75
|
+
return true
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return false
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
func shouldPrintHelp(args []string) bool {
|
|
82
|
+
if len(args) == 0 {
|
|
83
|
+
return false
|
|
84
|
+
}
|
|
85
|
+
if isHelpToken(args[0]) {
|
|
86
|
+
return true
|
|
87
|
+
}
|
|
88
|
+
if hasHelpFlag(args) {
|
|
89
|
+
return true
|
|
90
|
+
}
|
|
91
|
+
switch args[0] {
|
|
92
|
+
case "check", "state", "observe", "parallel":
|
|
93
|
+
return len(args) >= 2 && args[1] == "help"
|
|
94
|
+
default:
|
|
95
|
+
return false
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
func firstNonHelp(args []string) string {
|
|
100
|
+
for _, arg := range args {
|
|
101
|
+
if isHelpToken(arg) || strings.HasPrefix(arg, "-") {
|
|
102
|
+
continue
|
|
103
|
+
}
|
|
104
|
+
return arg
|
|
105
|
+
}
|
|
106
|
+
return ""
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
func writeHelp(stdout io.Writer, text string) int {
|
|
110
|
+
fmt.Fprintln(stdout, strings.TrimRight(text, "\n"))
|
|
111
|
+
return exitOK
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// commandHelp returns the usage text for a help request on a known command.
|
|
115
|
+
// ok is false when args are not a help request, or the command is unknown so
|
|
116
|
+
// the existing unknown-command path should run.
|
|
117
|
+
func commandHelp(args []string) (string, bool) {
|
|
118
|
+
if !shouldPrintHelp(args) {
|
|
119
|
+
return "", false
|
|
120
|
+
}
|
|
121
|
+
switch args[0] {
|
|
122
|
+
case "-h", "-help", "--help", "help":
|
|
123
|
+
return usage, true
|
|
124
|
+
case "check":
|
|
125
|
+
return helpCheck(args[1:]), true
|
|
126
|
+
case "state":
|
|
127
|
+
return helpState(args[1:]), true
|
|
128
|
+
case "observe":
|
|
129
|
+
return helpObserve(args[1:]), true
|
|
130
|
+
case "orient":
|
|
131
|
+
return orientUsage, true
|
|
132
|
+
case "parallel":
|
|
133
|
+
return parallel.CommandUsage(firstNonHelp(args[1:])), true
|
|
134
|
+
case "migrate":
|
|
135
|
+
return migrateUsage, true
|
|
136
|
+
case "secret-scan":
|
|
137
|
+
return scanCmdUsage, true
|
|
138
|
+
case "open-visual":
|
|
139
|
+
return openVisualUsage, true
|
|
140
|
+
case "version", "--version":
|
|
141
|
+
return versionUsage, true
|
|
142
|
+
default:
|
|
143
|
+
return "", false
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
func helpCheck(rest []string) string {
|
|
148
|
+
switch firstNonHelp(rest) {
|
|
149
|
+
case "candidate":
|
|
150
|
+
return candidateUsage
|
|
151
|
+
case "readiness":
|
|
152
|
+
return readinessUsage
|
|
153
|
+
case "seal":
|
|
154
|
+
return sealUsage
|
|
155
|
+
case "path-disjoint":
|
|
156
|
+
return parallel.CommandUsage("path-disjoint")
|
|
157
|
+
case "task-graph":
|
|
158
|
+
return taskGraphUsage
|
|
159
|
+
case "skill-trust":
|
|
160
|
+
return skillTrustUsage
|
|
161
|
+
case "indexes":
|
|
162
|
+
return indexesUsage
|
|
163
|
+
default:
|
|
164
|
+
return checkUsage
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
func helpState(rest []string) string {
|
|
169
|
+
switch firstNonHelp(rest) {
|
|
170
|
+
case "resolve":
|
|
171
|
+
return resolveUsage
|
|
172
|
+
case "merge-manifest":
|
|
173
|
+
return mergeManifestUsage
|
|
174
|
+
case "close":
|
|
175
|
+
return closeUsage
|
|
176
|
+
default:
|
|
177
|
+
return stateUsage
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
func helpObserve(rest []string) string {
|
|
182
|
+
switch firstNonHelp(rest) {
|
|
183
|
+
case "summary":
|
|
184
|
+
return observeSummaryUsage
|
|
185
|
+
case "slice":
|
|
186
|
+
return observeSliceUsage
|
|
187
|
+
default:
|
|
188
|
+
return observeUsage
|
|
189
|
+
}
|
|
190
|
+
}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
package main
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"bytes"
|
|
5
|
+
"strings"
|
|
6
|
+
"testing"
|
|
7
|
+
)
|
|
8
|
+
|
|
9
|
+
func TestCommandHelp(t *testing.T) {
|
|
10
|
+
t.Setenv("DEVRITES_ROOT", "")
|
|
11
|
+
tests := []struct {
|
|
12
|
+
args []string
|
|
13
|
+
want string
|
|
14
|
+
}{
|
|
15
|
+
{args: []string{"--help"}, want: "Usage:"},
|
|
16
|
+
{args: []string{"-h"}, want: "Usage:"},
|
|
17
|
+
{args: []string{"help"}, want: "Usage:"},
|
|
18
|
+
{args: []string{"check", "--help"}, want: "check candidate <slug>"},
|
|
19
|
+
{args: []string{"check", "help"}, want: "check <candidate|readiness|seal|path-disjoint|task-graph|skill-trust|indexes>"},
|
|
20
|
+
{args: []string{"check", "candidate", "--help"}, want: "check candidate <slug>"},
|
|
21
|
+
{args: []string{"check", "readiness", "-h"}, want: "check readiness --emit-binding <slug>"},
|
|
22
|
+
{args: []string{"check", "seal", "--help"}, want: "check seal <slug>"},
|
|
23
|
+
{args: []string{"check", "path-disjoint", "--help"}, want: "check path-disjoint"},
|
|
24
|
+
{args: []string{"check", "task-graph", "--help"}, want: "check task-graph <slug>"},
|
|
25
|
+
{args: []string{"check", "skill-trust", "--help"}, want: "check skill-trust <path>"},
|
|
26
|
+
{args: []string{"check", "indexes", "--help"}, want: "check indexes [--root <dir>]"},
|
|
27
|
+
{args: []string{"state", "--help"}, want: "state <resolve|merge-manifest|close>"},
|
|
28
|
+
{args: []string{"state", "-h"}, want: "state resolve"},
|
|
29
|
+
{args: []string{"state", "help"}, want: "state close <slug>"},
|
|
30
|
+
{args: []string{"state", "resolve", "--help"}, want: `state resolve <qid> "<answer>"`},
|
|
31
|
+
{args: []string{"state", "merge-manifest", "--help"}, want: "state merge-manifest <slug>"},
|
|
32
|
+
{args: []string{"state", "close", "--help"}, want: "state close <slug>"},
|
|
33
|
+
{args: []string{"parallel", "--help"}, want: "usage: parallel <subcommand>"},
|
|
34
|
+
{args: []string{"parallel", "create", "--help"}, want: "usage: parallel create --root --slug --batch --base --json"},
|
|
35
|
+
{args: []string{"parallel", "create", "-h"}, want: "parallel create"},
|
|
36
|
+
{args: []string{"parallel", "select", "--help"}, want: "parallel select --cap"},
|
|
37
|
+
{args: []string{"observe", "--help"}, want: "observe summary"},
|
|
38
|
+
{args: []string{"observe", "summary", "--help"}, want: "observe summary"},
|
|
39
|
+
{args: []string{"observe", "slice", "--help"}, want: "observe slice <slug> <SLICE-ID>"},
|
|
40
|
+
{args: []string{"orient", "--help"}, want: "orient [slug]"},
|
|
41
|
+
{args: []string{"migrate", "--help"}, want: "migrate <slug>"},
|
|
42
|
+
{args: []string{"secret-scan", "--help"}, want: "secret-scan [--staged] [--stdin]"},
|
|
43
|
+
{args: []string{"open-visual", "--help"}, want: "open-visual <path-or-name>"},
|
|
44
|
+
{args: []string{"version", "--help"}, want: "devrites-engine version"},
|
|
45
|
+
{args: []string{"install", "--help"}, want: "usage: devrites-engine install"},
|
|
46
|
+
{args: []string{"update", "--help"}, want: "usage: devrites-engine update"},
|
|
47
|
+
{args: []string{"uninstall", "--help"}, want: "usage: devrites-engine uninstall"},
|
|
48
|
+
}
|
|
49
|
+
for _, test := range tests {
|
|
50
|
+
t.Run(strings.Join(test.args, " "), func(t *testing.T) {
|
|
51
|
+
var stdout, stderr bytes.Buffer
|
|
52
|
+
code := run(test.args, strings.NewReader(""), &stdout, &stderr)
|
|
53
|
+
if code != exitOK {
|
|
54
|
+
t.Fatalf("run(%q) = %d, want %d; stdout=%q stderr=%q", test.args, code, exitOK, stdout.String(), stderr.String())
|
|
55
|
+
}
|
|
56
|
+
if stderr.Len() != 0 {
|
|
57
|
+
t.Fatalf("run(%q) wrote stderr %q", test.args, stderr.String())
|
|
58
|
+
}
|
|
59
|
+
if !strings.Contains(stdout.String(), test.want) {
|
|
60
|
+
t.Fatalf("run(%q) stdout=%q, want %q", test.args, stdout.String(), test.want)
|
|
61
|
+
}
|
|
62
|
+
if strings.Contains(stdout.String(), "unknown") {
|
|
63
|
+
t.Fatalf("run(%q) treated help as unknown: %q", test.args, stdout.String())
|
|
64
|
+
}
|
|
65
|
+
})
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
func TestCommandHelpDoesNotRequireWorkspace(t *testing.T) {
|
|
70
|
+
t.Setenv("DEVRITES_ROOT", "")
|
|
71
|
+
for _, args := range [][]string{
|
|
72
|
+
{"state", "--help"},
|
|
73
|
+
{"check", "candidate", "--help"},
|
|
74
|
+
{"orient", "--help"},
|
|
75
|
+
{"migrate", "--help"},
|
|
76
|
+
{"parallel", "create", "--help"},
|
|
77
|
+
} {
|
|
78
|
+
var stdout, stderr bytes.Buffer
|
|
79
|
+
code := run(args, strings.NewReader(""), &stdout, &stderr)
|
|
80
|
+
if code != exitOK || stderr.Len() != 0 || !strings.Contains(stdout.String(), "usage:") {
|
|
81
|
+
t.Fatalf("run(%q) code=%d stdout=%q stderr=%q", args, code, stdout.String(), stderr.String())
|
|
82
|
+
}
|
|
83
|
+
if strings.Contains(stdout.String()+stderr.String(), "root selection") {
|
|
84
|
+
t.Fatalf("run(%q) required a workspace for help", args)
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
func TestUnknownCommandHelpStillUnknown(t *testing.T) {
|
|
90
|
+
var stdout, stderr bytes.Buffer
|
|
91
|
+
args := []string{"frobnicate", "--help"}
|
|
92
|
+
if code := run(args, strings.NewReader(""), &stdout, &stderr); code != exitUsage {
|
|
93
|
+
t.Fatalf("run(%q) = %d, want %d", args, code, exitUsage)
|
|
94
|
+
}
|
|
95
|
+
if !strings.Contains(stderr.String(), `unknown command "frobnicate"`) {
|
|
96
|
+
t.Fatalf("stderr = %q, want unknown-command diagnostic", stderr.String())
|
|
97
|
+
}
|
|
98
|
+
}
|