docks-kit 0.6.0 → 0.7.1
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/AGENTS.md +7 -6
- package/README.md +23 -5
- package/cli/docs/flags.md +18 -0
- package/cli/docs/install.md +9 -6
- package/cli/docs/models.md +19 -0
- package/cli/docs/modifiers.md +23 -0
- package/cli/docs/overview.md +1 -1
- package/cli/src/commands/models.ts +7 -3
- package/cli/src/commands/sync.ts +5 -0
- package/cli/src/commands/update.ts +10 -4
- package/cli/src/engine-native/claudeSync.ts +53 -8
- package/cli/src/engine-native/codexSync.ts +37 -4
- package/cli/src/engine-native/index.ts +19 -1
- package/cli/src/engine-native/parseArgs.ts +68 -0
- package/cli/src/engine-native/workflowDeploy.ts +139 -0
- package/cli/src/generated/sotPayload.ts +7 -7
- package/cli/src/main.ts +58 -5
- package/cli/src/workflowModels.ts +395 -0
- package/docks-kit +14 -2
- package/package.json +1 -1
package/AGENTS.md
CHANGED
|
@@ -14,10 +14,10 @@ Tool-specific instructions live alongside this file:
|
|
|
14
14
|
|
|
15
15
|
| Path | Purpose |
|
|
16
16
|
|------|---------|
|
|
17
|
-
| `docks-kit` | CLI launcher: runs the
|
|
18
|
-
| `cli/src/engine-native/` | EngineNative implementation for `sync`, `model`, and `toolchain`; idempotent, flag-gated for destructive reconciliation |
|
|
17
|
+
| `docks-kit` | CLI launcher: runs the platform binary in `cli/dist/` only when its `--version` matches `package.json`, otherwise Bun-from-source (auto-installs Bun + `node_modules`). No-Bun recovery is the standalone platform release binary |
|
|
18
|
+
| `cli/src/engine-native/` | EngineNative implementation for `sync`, `model`, `workflow`, and `toolchain`; idempotent, flag-gated for destructive reconciliation |
|
|
19
19
|
| `cli/` | Effect-TS CLI + bundled docs topics |
|
|
20
|
-
| `SoT/models.json` | Kit-verified model catalog |
|
|
20
|
+
| `SoT/models.json` | Kit-verified model catalog plus the strict Docks workflow-role registry |
|
|
21
21
|
| `SoT/toolchain.json` | Toolchain floors manifest (verified pins consumed by EngineNative) |
|
|
22
22
|
| `SoT/.claude/bin/` | Dependency-free Bun runtime programs for Claude's statusline, SessionStart, and Notification |
|
|
23
23
|
| `install.sh` | Global installer |
|
|
@@ -36,6 +36,7 @@ Codex SoT notes:
|
|
|
36
36
|
- `SoT/.codex/rules/*.rules` deploys to `~/.codex/rules/` as kit-managed Codex command policy. This is Codex's equivalent of permission allow/prompt/block rules; user-learned approvals in `~/.codex/rules/default.rules` are preserved.
|
|
37
37
|
- `SoT/.codex/plugins/marketplace.json` deploys to Codex's personal marketplace path at `~/.agents/plugins/marketplace.json`; when the `codex` CLI is available, sync reruns `codex plugin add <plugin@marketplace>` for enabled SoT plugins so stale cached installs are refreshed.
|
|
38
38
|
- `docks-kit status` verifies Session Relay only through the supported `codex plugin list --json` inventory. `ready` means installed and enabled for a newly started Codex session; it is not evidence about an old process, lifecycle state, receive-path health, or worker quiescence. The global prompt SoTs carry the owner's standing authorization for Docks cross-company plan review, which never overrides host or platform denial.
|
|
39
|
+
- Both global prompt SoTs carry one byte-identical compact `Docks-workflow-models:` record. Root `--model-orchestrator` / `--model-reviewer` / `--model-implementer` / review-bound flags update only that deployed record; `docks-kit models workflow` lists the closed selectors, and a flag-less sync restores defaults.
|
|
39
40
|
- The `codex` CLI binary is upstream-owned, not kit-owned. The official standalone installer keeps package metadata under `$CODEX_HOME/packages/standalone` and places the `codex` symlink in `~/.local/bin` by default; sync only warns with a download-then-run installer command when the CLI is missing. Existing installs can self-update with `codex update`; npm and Homebrew remain upstream alternatives.
|
|
40
41
|
- `SoT/.codex/AGENTS.md` deliberately does not import `@RTK.md`: RTK's published Codex integration is prompt-file based rather than hook based, so importing it leaks implementation detail into agent-visible context. Use Codex hooks for RTK only after the kit installs a hook-backed Codex integration.
|
|
41
42
|
- Claude runtime settings are an authoring template with sentinels. `claudeRuntime.ts` materializes absolute Bun/script paths only after the shared `bun.ts` bootstrap is ready; `claudeSync.ts` writes all runtime assets before atomically committing settings, then prunes the legacy shell scripts and Stop hook. Native `rate_limits` is the sole quota source, so jq/curl/OAuth caches are not runtime dependencies. A missing Bun defers only this cutover and preserves legacy pointers/files.
|
|
@@ -47,7 +48,7 @@ For per-tool SoT layouts (`SoT/.claude/`, `SoT/.codex/`), see the matching SoT d
|
|
|
47
48
|
|
|
48
49
|
- **Idempotent operations.** Every EngineNative sync step must be safe to re-run. Settings merges, plugin installs, and marketplace adds are all idempotent — re-running with no SoT changes is a no-op.
|
|
49
50
|
- **Removed bash engine.** The bash engine was removed after the `bash-engine-final` tag. `DOCKS_KIT_ENGINE=bash` must fail with the removed-engine message; engine bugs are fixed forward in EngineNative.
|
|
50
|
-
- **Targeted syncs.** `./docks-kit sync` accepts positional targets: `claude`, `codex`, and `agents`. Use the narrowest target that matches the SoT change (for example, `./docks-kit sync codex` for Codex-only config edits); targets can be combined with `--dry-run`, `--skip-rtk`, `--reconcile`, `--prune`, `--yes` (auto-accept toolchain prompts), and the deploy-time modifiers `--claude-compact-window=<tokens>` / `--claude-permissive` / `--claude-model=<m>` / `--claude-effort=<level>` / `--claude-advisor=<on|off|default>` / `--codex-model=<m>` / `--codex-effort=<level>` (see `CLAUDE.md` § Deploy-time modifiers).
|
|
51
|
+
- **Targeted syncs.** `./docks-kit sync` accepts positional targets: `claude`, `codex`, and `agents`. Use the narrowest target that matches the SoT change (for example, `./docks-kit sync codex` for Codex-only config edits); targets can be combined with `--dry-run`, `--skip-rtk`, `--skip-plugin-refresh` (install missing plugins without refreshing existing caches; used by `docks-kit update`), `--reconcile`, `--prune`, `--yes` (auto-accept toolchain prompts), and the deploy-time modifiers `--claude-compact-window=<tokens>` / `--claude-permissive` / `--claude-model=<m>` / `--claude-effort=<level>` / `--claude-advisor=<on|off|default>` / `--codex-model=<m>` / `--codex-effort=<level>` (see `CLAUDE.md` § Deploy-time modifiers).
|
|
51
52
|
- **Additive by default.** Keys present in deployed config but absent from SoT are preserved on default sync. This protects user-only additions, but means drift accumulates — neither flag-less reset can clean it up. The one exception is the Claude `removed` manifest (`claude::_removed_manifest`), a curated list of unambiguous kit-owned artifacts that `claude::sync_removals` force-prunes on every sync; see `CLAUDE.md` § Pruning stale artifacts.
|
|
52
53
|
- **`--reconcile` / `--prune` are the kit-owned reconcile flags.** Orthogonal — `--reconcile` reconciles the settings layer (SoT-declared keys/tables/arrays win; user-only keys and nested objects are preserved; permissions arrays are replaced wholesale by SoT). `--prune` uninstalls kit-managed installations not in the SoT (plugins, marketplaces, and `~/.agents/skills/*` entries tracked in `~/.agents/.kit-managed-skills`). Combine for a full reset to SoT's kit-managed scope. User-only additions outside the kit's scope (custom env vars, mcpServers, manually-installed skills, third-party plugins not declared in SoT) are always preserved. Each tool's per-tool file documents the specific paths and diff recipes.
|
|
53
54
|
- **SOLID-aligned modules.** `cli/src/engine-native/parseArgs.ts` owns flag parsing/validation. `toolchain.ts` owns the verified-version gate over `SoT/toolchain.json`; `bun.ts` owns the shared, memoized Bun bootstrap; `claudeRuntime.ts` owns Claude settings materialization. `claudeSync.ts`, `codexSync.ts`, and `skillsSync.ts` own tool-specific sync logic. `index.ts` is the thin orchestrator. The public CLI seam is `cli/src/engine.ts`.
|
|
@@ -86,10 +87,10 @@ When a kit-mechanic skill, its `references/`, or a wrapper agent (`.claude/agent
|
|
|
86
87
|
## Plans
|
|
87
88
|
|
|
88
89
|
<constraint>
|
|
89
|
-
Multi-commit work plans live in `docs/plans/active/` (status is a frontmatter field) and `docs/plans/finished/` (archive). Every plan file is a complete handoff document —
|
|
90
|
+
Multi-commit work plans live in `docs/plans/active/` (status is a frontmatter field) and `docs/plans/finished/` (archive). Every plan file is a complete cold-handoff document — goal, context & rationale, environment & how-to-run, steps with exact paths, executable acceptance criteria, and a binary cold-handoff checklist — so any agent (or a weaker model) can pick one up cold without guessing. Skills handle every operation: `plan-init` (bootstrap/migrate), `plan-manager` (list/show/start/block/ship/new, auto-commit on transition, self-review on draft), `plan-review` (verification). Trigger by natural language or the matching `plan-*` skill. `active/` is multi-occupancy.
|
|
90
91
|
</constraint>
|
|
91
92
|
|
|
92
|
-
The full convention (frontmatter schema, body sections, self-review
|
|
93
|
+
The full convention (frontmatter schema, body sections, one-pass local self-review, bounded strong-default X/S review receipts, workflow roles, open-questions, age tokens) lives in `docs/plans/AGENTS.md`. `docs/plans/CLAUDE.md` is a one-line `@AGENTS.md` import for Claude Code's nested discovery. If `.codex/agents/plan-manager.toml` and `.codex/agents/plan-review.toml` exist, Codex may use them for explicit subagent delegation; otherwise run the matching `plan-*` skill inline.
|
|
93
94
|
|
|
94
95
|
Distinct from per-tool **Open Concerns** sections (wait-on-upstream
|
|
95
96
|
blockers tied to a vendor shipping a fix — these live inside the per-tool
|
package/README.md
CHANGED
|
@@ -17,8 +17,10 @@ cd ~/projects/public
|
|
|
17
17
|
./docks-kit status # doctor view: drift, toolchain, plugins, skills
|
|
18
18
|
```
|
|
19
19
|
|
|
20
|
-
The `./docks-kit` launcher prefers a compiled binary (`cli/dist/`)
|
|
21
|
-
from source via Bun — auto-installing
|
|
20
|
+
The `./docks-kit` launcher prefers a compiled binary (`cli/dist/`) only when
|
|
21
|
+
its version matches the checkout, then runs from source via Bun — auto-installing
|
|
22
|
+
Bun and dependencies when missing. Stale ignored build artifacts cannot mask
|
|
23
|
+
newer checkout code.
|
|
22
24
|
|
|
23
25
|
Other install paths (global `bun add -g docks-kit`, curl installer) —
|
|
24
26
|
see `./docks-kit docs install`.
|
|
@@ -39,7 +41,7 @@ RTK/Bun download boundaries, not as a global sync prerequisite.
|
|
|
39
41
|
docks-kit sync [claude] [codex] [agents] deploy the SoT (default: all three)
|
|
40
42
|
docks-kit update [--no-sync] self-update the kit (autodetects checkout vs global install), then sync
|
|
41
43
|
docks-kit model <claude|codex> [value] get/set the DEPLOYED model (TTY picker)
|
|
42
|
-
docks-kit models [
|
|
44
|
+
docks-kit models [claude|codex|workflow] model and workflow-role catalogs (`--json`)
|
|
43
45
|
docks-kit toolchain [check|ensure <tool>] verified-version floors for external tools
|
|
44
46
|
docks-kit status [--json] deployed-vs-SoT drift + toolchain + counts
|
|
45
47
|
docks-kit plugins list [--json] enabledPlugins tri-state vs installed
|
|
@@ -65,12 +67,28 @@ golden-regression coverage for dry-run output, mutation snapshots, and argv logs
|
|
|
65
67
|
| `--claude-plugin=<name>` | Sticky opt-in plugin (supabase, n8n) |
|
|
66
68
|
| `--codex-model=<m>` | Deploy-time modifier: deployed Codex model |
|
|
67
69
|
| `--skip-rtk` | Skip optional tool bootstrap |
|
|
70
|
+
| `--skip-plugin-refresh` | Install missing plugins but skip refresh-only updates (used automatically by `docks-kit update`) |
|
|
68
71
|
| `--yes` | Auto-accept toolchain prompts (CI/containers) |
|
|
69
72
|
|
|
70
73
|
**Deploy-time modifiers** change deployed config only — the SoT is untouched
|
|
71
74
|
and a later flag-less sync reverts them. Full reference: `docks-kit docs flags`
|
|
72
75
|
(includes the old→new rename table for the pre-CLI `sync.sh` flags).
|
|
73
76
|
|
|
77
|
+
### Docks workflow roles (root flags)
|
|
78
|
+
|
|
79
|
+
| Flag | Effect |
|
|
80
|
+
|------|--------|
|
|
81
|
+
| `--model-orchestrator=<selector>` | Override the Docks orchestrator candidate/profile |
|
|
82
|
+
| `--model-reviewer=<selector>` | Override the Docks reviewer candidate/profile |
|
|
83
|
+
| `--model-implementer=<selector>` | Override the Docks implementer candidate/profile |
|
|
84
|
+
| `--review-min-score=<0..100>` | Override the completion-review target |
|
|
85
|
+
| `--review-max-rounds=<1..10>` | Bound completion-review batches |
|
|
86
|
+
|
|
87
|
+
Selectors are strict `profile:<name>` or `<tool>:<model>@<effort>` values from
|
|
88
|
+
`docks-kit models workflow`. These root flags update only the identical compact
|
|
89
|
+
record in `~/.claude/CLAUDE.md` and `~/.codex/AGENTS.md`; omitted fields retain
|
|
90
|
+
their current valid values, while a later flag-less sync restores all defaults.
|
|
91
|
+
|
|
74
92
|
## How syncing works
|
|
75
93
|
|
|
76
94
|
- **Additive by default** — user-only settings keys, plugins, and skills
|
|
@@ -83,7 +101,7 @@ and a later flag-less sync reverts them. Full reference: `docks-kit docs flags`
|
|
|
83
101
|
(`--yes` accepts; non-TTY falls back to the pinned verified version
|
|
84
102
|
when possible). `docks-kit toolchain check` shows the full table.
|
|
85
103
|
- **Model catalog** — `SoT/models.json` is the research-verified source for
|
|
86
|
-
model validation, listings, and
|
|
104
|
+
model validation, listings, pickers, and the Docks workflow-role registry.
|
|
87
105
|
- **Claude runtime** — sync materializes three dependency-free Bun `.mjs`
|
|
88
106
|
programs for statusline, SessionStart, and Notification. Quota display uses
|
|
89
107
|
Claude's native `rate_limits`; there is no OAuth fetch, shared usage cache,
|
|
@@ -97,7 +115,7 @@ and a later flag-less sync reverts them. Full reference: `docks-kit docs flags`
|
|
|
97
115
|
| `SoT/.claude/` | Claude Code SoT (settings template, Bun runtime programs, CLAUDE.md) |
|
|
98
116
|
| `SoT/.codex/` | Codex SoT (config.toml, rules, AGENTS.md, marketplace) |
|
|
99
117
|
| `SoT/.agents/` | Universal-skill manifest |
|
|
100
|
-
| `SoT/models.json` | Kit-verified model catalog |
|
|
118
|
+
| `SoT/models.json` | Kit-verified model and Docks workflow-role catalog |
|
|
101
119
|
| `SoT/toolchain.json` | Verified-version floors |
|
|
102
120
|
| `cli/src/engine-native/` | EngineNative sync/model/toolchain implementation |
|
|
103
121
|
| `cli/src/generated/sotPayload.ts` | Generated in-memory payload used by standalone and npm installs |
|
package/cli/docs/flags.md
CHANGED
|
@@ -19,6 +19,7 @@ docks-kit sync claude agents # two
|
|
|
19
19
|
| `--reconcile` | Settings layer reconciled toward SoT (SoT keys win; user-only keys preserved; permissions arrays replaced) |
|
|
20
20
|
| `--prune` | Uninstall kit-managed installs not in SoT: plugins, marketplaces, universal skills |
|
|
21
21
|
| `--skip-rtk` | Skip optional tool bootstrap (RTK, bubblewrap) |
|
|
22
|
+
| `--skip-plugin-refresh` | Install missing Claude/Codex plugins but skip refresh-only updates; `docks-kit update` uses this automatically |
|
|
22
23
|
| `--yes` | Auto-accept toolchain above-verified prompts (containers/CI) |
|
|
23
24
|
| `--verbose` / `-v` | Also print no-op confirmations (already in sync, up to date, left as-is); accepted on `sync`, `model`, and `toolchain` |
|
|
24
25
|
|
|
@@ -39,6 +40,23 @@ Bare model, effort, or advisor modifiers print the relevant valid-value catalog
|
|
|
39
40
|
and exit 2. A modifier for a target not selected by the positional arguments is
|
|
40
41
|
ignored with a warning; Claude modifiers never touch Codex config and vice versa.
|
|
41
42
|
|
|
43
|
+
## Docks workflow root flags
|
|
44
|
+
|
|
45
|
+
These flags belong at the root (`docks-kit --model-reviewer=...`), not after
|
|
46
|
+
`sync`. They update only the shared deployed workflow record.
|
|
47
|
+
|
|
48
|
+
| Flag | Accepted value |
|
|
49
|
+
|------|----------------|
|
|
50
|
+
| `--model-orchestrator=<selector>` | `profile:<name>` or `<tool>:<model>@<effort>` |
|
|
51
|
+
| `--model-reviewer=<selector>` | same strict selector grammar |
|
|
52
|
+
| `--model-implementer=<selector>` | same strict selector grammar |
|
|
53
|
+
| `--review-min-score=<n>` | base-10 integer `0..100` |
|
|
54
|
+
| `--review-max-rounds=<n>` | base-10 integer `1..10` |
|
|
55
|
+
|
|
56
|
+
Use `docks-kit models workflow [--json]` for the closed registry. Bare, empty,
|
|
57
|
+
unknown, or malformed values print that helper and exit 2 before either prompt
|
|
58
|
+
file changes.
|
|
59
|
+
|
|
42
60
|
## Renamed legacy flags (pre-CLI sync.sh)
|
|
43
61
|
|
|
44
62
|
Old flags exit with a rename hint — there is no compat behavior.
|
package/cli/docs/install.md
CHANGED
|
@@ -8,9 +8,10 @@ cd ~/projects/public
|
|
|
8
8
|
./docks-kit sync
|
|
9
9
|
```
|
|
10
10
|
|
|
11
|
-
The `./docks-kit` launcher prefers a compiled binary in `cli/dist
|
|
12
|
-
falls back to Bun-from-source
|
|
13
|
-
`bun install --frozen-lockfile`
|
|
11
|
+
The `./docks-kit` launcher prefers a compiled binary in `cli/dist/` only when
|
|
12
|
+
its reported version matches `package.json`, then falls back to Bun-from-source
|
|
13
|
+
(auto-installing Bun via download-then-run and `bun install --frozen-lockfile`
|
|
14
|
+
when needed). An ignored stale build is reported and bypassed.
|
|
14
15
|
|
|
15
16
|
## 2. Global via Bun (effect-solutions-style)
|
|
16
17
|
|
|
@@ -78,7 +79,7 @@ Two supported paths (CI-verified on windows-2025, native PowerShell):
|
|
|
78
79
|
## Keeping the kit up to date
|
|
79
80
|
|
|
80
81
|
```
|
|
81
|
-
docks-kit update # autodetect + update +
|
|
82
|
+
docks-kit update # autodetect + update + install-missing-only sync
|
|
82
83
|
docks-kit update --no-sync # update only
|
|
83
84
|
```
|
|
84
85
|
|
|
@@ -86,8 +87,10 @@ Autodetection: a kit home with `.git` is a checkout (requires a clean
|
|
|
86
87
|
worktree and an upstream; `git pull --ff-only`, re-runs
|
|
87
88
|
`bun install --frozen-lockfile` when the lockfile changed); a kit home
|
|
88
89
|
under `node_modules` is a global package (`bun add -g` /
|
|
89
|
-
`npm install -g docks-kit@latest`).
|
|
90
|
-
|
|
90
|
+
`npm install -g docks-kit@latest`). The chained sync skips refresh-only work
|
|
91
|
+
for already-installed Claude/Codex plugins but still installs missing ones.
|
|
92
|
+
A compiled binary inside a checkout updates the checkout; on the next invocation
|
|
93
|
+
the launcher bypasses that now-stale binary and uses updated source until rebuilt.
|
|
91
94
|
Every `docks-kit sync` also does a best-effort behind-upstream check and
|
|
92
95
|
nudges when the checkout is stale (silent offline / detached / no git).
|
|
93
96
|
|
package/cli/docs/models.md
CHANGED
|
@@ -31,8 +31,27 @@ docks-kit models claude --json # machine-readable
|
|
|
31
31
|
docks-kit model claude # current deployed + SoT + picker (TTY)
|
|
32
32
|
docks-kit model claude opus # per-machine override from the Fable SoT
|
|
33
33
|
docks-kit sync claude --claude-model=opus # same, as part of a sync
|
|
34
|
+
docks-kit models workflow # Docks workflow profiles and defaults
|
|
35
|
+
docks-kit models workflow --json # closed machine-readable registry
|
|
34
36
|
```
|
|
35
37
|
|
|
38
|
+
## Docks workflow-role registry
|
|
39
|
+
|
|
40
|
+
The `workflow` section is strict even though ordinary tool-model modifiers are
|
|
41
|
+
forward-compatible. It accepts only known `profile:<name>` values or exact
|
|
42
|
+
`<tool>:<model>@<effort>` targets whose model and effort both appear in the
|
|
43
|
+
catalog. The defaults are:
|
|
44
|
+
|
|
45
|
+
- orchestrator: `profile:claude-best`, ordered as `claude:fable@high` then
|
|
46
|
+
`claude:opus@xhigh`;
|
|
47
|
+
- reviewer and implementer: `codex:gpt-5.6-sol@xhigh`;
|
|
48
|
+
- review minimum score 90 and maximum 3 rounds.
|
|
49
|
+
|
|
50
|
+
`claude:best@high` is Claude's native one-model alias. It is deliberately not
|
|
51
|
+
the same as the Docks-managed `profile:claude-best` candidate chain.
|
|
52
|
+
Availability is `checked_when_used`: Docks classifies each launch attempt;
|
|
53
|
+
docks-kit neither probes providers nor promises provider-wide fallback.
|
|
54
|
+
|
|
36
55
|
## Advisor pairing note (Claude)
|
|
37
56
|
|
|
38
57
|
The SoT ships `model: fable` with advisor off (`advisorModel` unset).
|
package/cli/docs/modifiers.md
CHANGED
|
@@ -44,6 +44,29 @@ docks-kit model codex gpt-5.5 # codex flavor
|
|
|
44
44
|
The set path calls the same engine functions as the sync flags —
|
|
45
45
|
one implementation, two entry points.
|
|
46
46
|
|
|
47
|
+
## Workflow-role overrides (root, no full sync)
|
|
48
|
+
|
|
49
|
+
The root workflow flags are deployed modifiers for Docks rather than one tool:
|
|
50
|
+
|
|
51
|
+
```text
|
|
52
|
+
docks-kit --model-orchestrator=profile:claude-best \
|
|
53
|
+
--model-reviewer=codex:gpt-5.6-terra@high \
|
|
54
|
+
--review-min-score=80 --review-max-rounds=5
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
They atomically write one byte-identical complete record to
|
|
58
|
+
`~/.claude/CLAUDE.md` and `~/.codex/AGENTS.md`. Omitted fields retain the
|
|
59
|
+
current valid record; a missing side is repaired. Invalid or conflicting state
|
|
60
|
+
stops before mutation, a repeated request is a no-op, and failure on the second
|
|
61
|
+
atomic write restores both snapshots. A later flag-less sync restores the
|
|
62
|
+
embedded defaults. Start fresh sessions after changing the record.
|
|
63
|
+
|
|
64
|
+
Selectors are strict `profile:<name>` or `<tool>:<model>@<effort>` entries from
|
|
65
|
+
`docks-kit models workflow`. `profile:claude-best` is the ordered Docks
|
|
66
|
+
Fable-high then Opus-xhigh chain; `claude:best@high` is instead Claude's native
|
|
67
|
+
single-model alias. Docks checks each candidate when used—there is no docks-kit
|
|
68
|
+
provider preflight.
|
|
69
|
+
|
|
47
70
|
## Sticky opt-ins are NOT modifiers
|
|
48
71
|
|
|
49
72
|
`--claude-plugin=<name>` installs+enables an optional plugin and it STAYS
|
package/cli/docs/overview.md
CHANGED
|
@@ -17,7 +17,7 @@ AI-assisted dev environment on every machine.
|
|
|
17
17
|
| `cli/src/generated/sotPayload.ts` | Deterministic generated payload embedded in standalone/npm execution |
|
|
18
18
|
| `cli/src/engine-native/` | EngineNative mutation logic for sync/model/toolchain |
|
|
19
19
|
| `cli/` | This CLI (Effect-TS on Bun) plus bundled docs |
|
|
20
|
-
| `docks-kit` | Launcher: compiled binary → bun-from-source, with Bun auto-install |
|
|
20
|
+
| `docks-kit` | Launcher: version-matching compiled binary → bun-from-source, with Bun auto-install |
|
|
21
21
|
|
|
22
22
|
## Design rules
|
|
23
23
|
|
|
@@ -2,9 +2,10 @@ import { Args, Command, Options } from "@effect/cli"
|
|
|
2
2
|
import { Console, Effect, Option } from "effect"
|
|
3
3
|
import { bail } from "../engine"
|
|
4
4
|
import { modelCatalog, type Tool } from "../manifests"
|
|
5
|
+
import { workflowCatalog, workflowRegistryJson } from "../workflowModels"
|
|
5
6
|
|
|
6
7
|
const tool = Args.text({ name: "tool" }).pipe(
|
|
7
|
-
Args.withDescription("claude | codex (omit for both)"),
|
|
8
|
+
Args.withDescription("claude | codex | workflow (omit for both tool catalogs)"),
|
|
8
9
|
Args.optional
|
|
9
10
|
)
|
|
10
11
|
const json = Options.boolean("json").pipe(
|
|
@@ -24,8 +25,11 @@ const renderTool = (t: Tool) =>
|
|
|
24
25
|
export const modelsCommand = Command.make("models", { tool, json }, (config) =>
|
|
25
26
|
Effect.gen(function* () {
|
|
26
27
|
const requested = Option.getOrUndefined(config.tool)
|
|
28
|
+
if (requested === "workflow") {
|
|
29
|
+
return yield* Console.log(config.json ? workflowRegistryJson() : workflowCatalog())
|
|
30
|
+
}
|
|
27
31
|
if (requested !== undefined && requested !== "claude" && requested !== "codex") {
|
|
28
|
-
return yield* bail(`Unknown tool '${requested}' (valid: claude, codex)`)
|
|
32
|
+
return yield* bail(`Unknown tool '${requested}' (valid: claude, codex, workflow)`)
|
|
29
33
|
}
|
|
30
34
|
const tools: Array<Tool> = requested !== undefined ? [requested as Tool] : ["claude", "codex"]
|
|
31
35
|
|
|
@@ -42,5 +46,5 @@ export const modelsCommand = Command.make("models", { tool, json }, (config) =>
|
|
|
42
46
|
)
|
|
43
47
|
})
|
|
44
48
|
).pipe(
|
|
45
|
-
Command.withDescription("List
|
|
49
|
+
Command.withDescription("List kit-verified tool models or the Docks workflow role registry (SoT/models.json).")
|
|
46
50
|
)
|
package/cli/src/commands/sync.ts
CHANGED
|
@@ -80,6 +80,9 @@ const prune = Options.boolean("prune").pipe(
|
|
|
80
80
|
const skipRtk = Options.boolean("skip-rtk").pipe(
|
|
81
81
|
Options.withDescription("Skip optional tool bootstrap (RTK, bubblewrap)")
|
|
82
82
|
)
|
|
83
|
+
const skipPluginRefresh = Options.boolean("skip-plugin-refresh").pipe(
|
|
84
|
+
Options.withDescription("Install missing plugins but skip refresh-only updates for existing plugins")
|
|
85
|
+
)
|
|
83
86
|
const yes = Options.boolean("yes").pipe(
|
|
84
87
|
Options.withDescription("Auto-accept toolchain prompts (containers/CI)")
|
|
85
88
|
)
|
|
@@ -129,6 +132,7 @@ export const syncCommand = Command.make(
|
|
|
129
132
|
reconcile,
|
|
130
133
|
prune,
|
|
131
134
|
skipRtk,
|
|
135
|
+
skipPluginRefresh,
|
|
132
136
|
yes,
|
|
133
137
|
verbose,
|
|
134
138
|
claudeModel,
|
|
@@ -172,6 +176,7 @@ export const syncCommand = Command.make(
|
|
|
172
176
|
if (config.reconcile) args.push("--reconcile")
|
|
173
177
|
if (config.prune) args.push("--prune")
|
|
174
178
|
if (config.skipRtk) args.push("--skip-rtk")
|
|
179
|
+
if (config.skipPluginRefresh) args.push("--skip-plugin-refresh")
|
|
175
180
|
if (config.yes) args.push("--yes")
|
|
176
181
|
if (config.verbose) args.push("--verbose")
|
|
177
182
|
if (config.claudePermissive) args.push("--claude-permissive")
|
|
@@ -26,6 +26,12 @@ const chainSync = (argv0: string, args: Array<string>): Effect.Effect<void> =>
|
|
|
26
26
|
if (res.error !== undefined || res.status !== 0) process.exit(res.status ?? 1)
|
|
27
27
|
})
|
|
28
28
|
|
|
29
|
+
export const updateSyncArgs = (home: string): Array<string> => [
|
|
30
|
+
join(home, "cli/src/main.ts"),
|
|
31
|
+
"sync",
|
|
32
|
+
"--skip-plugin-refresh"
|
|
33
|
+
]
|
|
34
|
+
|
|
29
35
|
const updateCheckout = (home: string, skipSync: boolean) =>
|
|
30
36
|
Effect.gen(function* () {
|
|
31
37
|
if (spawnSync("git", ["--version"], { stdio: "ignore" }).status !== 0) {
|
|
@@ -62,12 +68,12 @@ const updateCheckout = (home: string, skipSync: boolean) =>
|
|
|
62
68
|
|
|
63
69
|
if (compiled) {
|
|
64
70
|
return yield* Console.log(
|
|
65
|
-
"This compiled binary still runs the previous version - rebuild
|
|
71
|
+
"This compiled binary still runs the previous version - the checkout launcher will use updated source next time. Run: ./docks-kit sync (rebuild with bash cli/build-binaries.sh to restore the binary fast path)."
|
|
66
72
|
)
|
|
67
73
|
}
|
|
68
74
|
if (skipSync) return yield* Console.log("Kit updated. Run: docks-kit sync")
|
|
69
75
|
yield* Console.log("Kit updated - running sync with the new version...")
|
|
70
|
-
return yield* chainSync(process.execPath,
|
|
76
|
+
return yield* chainSync(process.execPath, updateSyncArgs(home))
|
|
71
77
|
})
|
|
72
78
|
|
|
73
79
|
const updatePackage = (home: string, skipSync: boolean) =>
|
|
@@ -93,7 +99,7 @@ const updatePackage = (home: string, skipSync: boolean) =>
|
|
|
93
99
|
yield* Console.log("Kit updated - running sync with the new version...")
|
|
94
100
|
// Chain through the package dir just updated (global installs update in
|
|
95
101
|
// place) — a bare `docks-kit` PATH lookup could hit a different shim.
|
|
96
|
-
return yield* chainSync(process.execPath,
|
|
102
|
+
return yield* chainSync(process.execPath, updateSyncArgs(home))
|
|
97
103
|
})
|
|
98
104
|
|
|
99
105
|
export const updateCommand = Command.make("update", { noSync }, (config) =>
|
|
@@ -111,6 +117,6 @@ export const updateCommand = Command.make("update", { noSync }, (config) =>
|
|
|
111
117
|
})
|
|
112
118
|
).pipe(
|
|
113
119
|
Command.withDescription(
|
|
114
|
-
"Self-update the kit: autodetects the install (git checkout -> ff-only pull; bun/npm global -> @latest) and chains
|
|
120
|
+
"Self-update the kit: autodetects the install (git checkout -> ff-only pull; bun/npm global -> @latest) and chains an install-missing-only sync with the new version (--no-sync to skip)."
|
|
115
121
|
)
|
|
116
122
|
)
|
|
@@ -31,6 +31,7 @@ import { ExitError } from "./parseArgs"
|
|
|
31
31
|
import { mergeSettings, reconcileSettings } from "./settings"
|
|
32
32
|
import { ensure, field } from "./toolchain"
|
|
33
33
|
import { payloadBytes, payloadDisplayPath, payloadText } from "../payload"
|
|
34
|
+
import { renderDefaultWorkflowInstructions } from "./workflowDeploy"
|
|
34
35
|
|
|
35
36
|
export type ClaudeRuntimeState =
|
|
36
37
|
| { readonly kind: "ready"; readonly paths: ClaudeRuntimePaths }
|
|
@@ -222,7 +223,7 @@ function syncClaudeMd(ctx: Ctx, claudeDir: string): void {
|
|
|
222
223
|
return
|
|
223
224
|
}
|
|
224
225
|
|
|
225
|
-
const source = payloadText("SoT/.claude/CLAUDE.md")
|
|
226
|
+
const source = renderDefaultWorkflowInstructions(payloadText("SoT/.claude/CLAUDE.md"))
|
|
226
227
|
const stripReason = ctx.skipRtk ? "--skip-rtk" : rtkMdAbsent ? "~/.claude/RTK.md absent (rtk not initialized)" : ""
|
|
227
228
|
if (stripReason !== "") {
|
|
228
229
|
const stripped = source
|
|
@@ -503,6 +504,10 @@ const REMOVED_MANIFEST = {
|
|
|
503
504
|
"env.CLAUDE_CODE_FORK_SUBAGENT",
|
|
504
505
|
"env.CLAUDE_CODE_EFFORT_LEVEL"
|
|
505
506
|
],
|
|
507
|
+
permissionRules: {
|
|
508
|
+
allow: ["Write(./)"],
|
|
509
|
+
deny: ["Write(**/.env)", "Write(**/.env.local)", "Write(**/secrets/**)"]
|
|
510
|
+
},
|
|
506
511
|
claudeJsonKeys: [] as Array<string>,
|
|
507
512
|
runtimeReady: {
|
|
508
513
|
hooks: ["notify.sh"],
|
|
@@ -544,6 +549,30 @@ function pruneJsonKeys(ctx: Ctx, file: string, keys: Array<string>): number {
|
|
|
544
549
|
return presentKeys.length
|
|
545
550
|
}
|
|
546
551
|
|
|
552
|
+
function prunePermissionRules(
|
|
553
|
+
ctx: Ctx,
|
|
554
|
+
file: string,
|
|
555
|
+
rules: Readonly<Record<"allow" | "deny", ReadonlyArray<string>>>
|
|
556
|
+
): number {
|
|
557
|
+
if (!existsSync(file)) return 0
|
|
558
|
+
const doc = parseJson(readFileSync(file, "utf8"))
|
|
559
|
+
if (doc === undefined || !isObject(doc) || !isObject(doc["permissions"])) return 0
|
|
560
|
+
const permissions = doc["permissions"]
|
|
561
|
+
let present = 0
|
|
562
|
+
for (const key of ["allow", "deny"] as const) {
|
|
563
|
+
const values = permissions[key]
|
|
564
|
+
if (!Array.isArray(values)) continue
|
|
565
|
+
const removed = new Set(rules[key])
|
|
566
|
+
present += rules[key].filter((rule) => values.includes(rule)).length
|
|
567
|
+
if (!ctx.dryRun) permissions[key] = values.filter((value) => typeof value !== "string" || !removed.has(value))
|
|
568
|
+
}
|
|
569
|
+
if (present > 0 && !ctx.dryRun) {
|
|
570
|
+
writeFileSync(`${file}.tmp`, jqStringify(doc))
|
|
571
|
+
renameSync(`${file}.tmp`, file)
|
|
572
|
+
}
|
|
573
|
+
return present
|
|
574
|
+
}
|
|
575
|
+
|
|
547
576
|
function syncRemovals(ctx: Ctx, claudeDir: string, runtime: ClaudeRuntimeState): void {
|
|
548
577
|
const { change, echo } = ctx.services.logger
|
|
549
578
|
let hooksRemoved = 0
|
|
@@ -591,16 +620,25 @@ function syncRemovals(ctx: Ctx, claudeDir: string, runtime: ClaudeRuntimeState):
|
|
|
591
620
|
}
|
|
592
621
|
|
|
593
622
|
const skeys = pruneJsonKeys(ctx, p(claudeDir, "settings.json"), settingsKeys)
|
|
623
|
+
const permissionRules = prunePermissionRules(
|
|
624
|
+
ctx,
|
|
625
|
+
p(claudeDir, "settings.json"),
|
|
626
|
+
REMOVED_MANIFEST.permissionRules
|
|
627
|
+
)
|
|
594
628
|
const cjkeys = pruneJsonKeys(ctx, p(ctx.home, ".claude.json"), REMOVED_MANIFEST.claudeJsonKeys)
|
|
595
629
|
|
|
596
630
|
if (ctx.dryRun) {
|
|
597
631
|
if (skeys > 0) echo(`[dry-run] del ${skeys} stale key(s) from ${p(claudeDir, "settings.json")}`)
|
|
632
|
+
if (permissionRules > 0) {
|
|
633
|
+
echo(`[dry-run] del ${permissionRules} stale permission rule(s) from ${p(claudeDir, "settings.json")}`)
|
|
634
|
+
}
|
|
598
635
|
if (cjkeys > 0) echo(`[dry-run] del ${cjkeys} stale key(s) from ${p(ctx.home, ".claude.json")}`)
|
|
599
636
|
return
|
|
600
637
|
}
|
|
601
638
|
|
|
602
|
-
if (hooksRemoved + filesRemoved + skeys + cjkeys > 0) {
|
|
603
|
-
|
|
639
|
+
if (hooksRemoved + filesRemoved + skeys + permissionRules + cjkeys > 0) {
|
|
640
|
+
const permissionSummary = permissionRules > 0 ? `, permission rules: ${permissionRules}` : ""
|
|
641
|
+
change(`Pruned stale artifacts (hooks: ${hooksRemoved}, files: ${filesRemoved}, settings keys: ${skeys}, claude.json keys: ${cjkeys}${permissionSummary})`)
|
|
604
642
|
ctx.nextStepTriggers.claudeRestart = true
|
|
605
643
|
}
|
|
606
644
|
}
|
|
@@ -636,7 +674,11 @@ function syncPlugins(ctx: Ctx, claudeDir: string): void {
|
|
|
636
674
|
const installedPlugins = p(claudeDir, "plugins", "installed_plugins.json")
|
|
637
675
|
|
|
638
676
|
if (ctx.dryRun) {
|
|
639
|
-
echo(
|
|
677
|
+
echo(
|
|
678
|
+
ctx.skipPluginRefresh
|
|
679
|
+
? "[dry-run] bootstrap + install missing plugins from SoT; skip refresh-only plugin updates"
|
|
680
|
+
: "[dry-run] bootstrap + update plugin marketplaces + plugins from SoT"
|
|
681
|
+
)
|
|
640
682
|
if (ctx.prune) {
|
|
641
683
|
echo("[dry-run] (--prune) would also uninstall plugins not in SoT and remove extra marketplaces")
|
|
642
684
|
}
|
|
@@ -694,13 +736,16 @@ function syncPlugins(ctx: Ctx, claudeDir: string): void {
|
|
|
694
736
|
}
|
|
695
737
|
}
|
|
696
738
|
|
|
697
|
-
// Pass 3 — refresh every installed plugin.
|
|
698
|
-
cli(["plugin", "marketplace", "update"])
|
|
699
739
|
let updatedPl = 0
|
|
700
740
|
const installedDoc = readJsonFile(installedPlugins)
|
|
701
741
|
const installedKeys = installedDoc !== undefined && isObject(installedDoc) ? sortedKeys(installedDoc["plugins"]) : []
|
|
702
|
-
|
|
703
|
-
|
|
742
|
+
// Pass 3 — refresh every installed plugin unless the update command
|
|
743
|
+
// selected its install-missing-only fast path.
|
|
744
|
+
if (!ctx.skipPluginRefresh) {
|
|
745
|
+
cli(["plugin", "marketplace", "update"])
|
|
746
|
+
for (const pluginId of installedKeys) {
|
|
747
|
+
if (cli(["plugin", "update", pluginId]).out.includes("Successfully updated")) updatedPl++
|
|
748
|
+
}
|
|
704
749
|
}
|
|
705
750
|
|
|
706
751
|
// Passes 4 + 5 — prune-gated uninstall + marketplace removal.
|
|
@@ -12,6 +12,7 @@ import type { Ctx } from "./index"
|
|
|
12
12
|
import { compareCodepoints, isObject, jqStringify, parseJson, type Json } from "./jq"
|
|
13
13
|
import { sessionRelayReadiness } from "./sessionRelayReadiness"
|
|
14
14
|
import { payloadBytes, payloadDisplayPath, payloadPaths, payloadText, type PayloadPath } from "../payload"
|
|
15
|
+
import { renderDefaultWorkflowInstructions } from "./workflowDeploy"
|
|
15
16
|
|
|
16
17
|
export function codexSync(ctx: Ctx): void {
|
|
17
18
|
const codexDir = p(ctx.home, ".codex")
|
|
@@ -24,7 +25,7 @@ export function codexSync(ctx: Ctx): void {
|
|
|
24
25
|
syncCodexModel(ctx, ctx.codexModel)
|
|
25
26
|
syncCodexEffort(ctx, ctx.codexEffort)
|
|
26
27
|
syncRules(ctx, payloadPaths("SoT/.codex/rules/"), p(codexDir, "rules"))
|
|
27
|
-
syncAgentsMd(ctx, payloadText("SoT/.codex/AGENTS.md"), p(codexDir, "AGENTS.md"))
|
|
28
|
+
syncAgentsMd(ctx, renderDefaultWorkflowInstructions(payloadText("SoT/.codex/AGENTS.md")), p(codexDir, "AGENTS.md"))
|
|
28
29
|
syncMarketplace(ctx, payloadText("SoT/.codex/plugins/marketplace.json"), p(ctx.agentsDir, "plugins", "marketplace.json"))
|
|
29
30
|
removeLegacyDocksMarketplace(ctx, userConfig)
|
|
30
31
|
syncPlugins(ctx, sotConfig)
|
|
@@ -431,10 +432,30 @@ function manualPluginRefreshCommand(sotConfigText: string): string {
|
|
|
431
432
|
return first !== undefined ? `codex plugin add ${first}` : "codex plugin add <plugin@marketplace>"
|
|
432
433
|
}
|
|
433
434
|
|
|
435
|
+
function installedPluginIdsFromCli(): Set<string> | undefined {
|
|
436
|
+
const result = spawnSync("codex", ["plugin", "list", "--json"], {
|
|
437
|
+
encoding: "utf8",
|
|
438
|
+
stdio: ["ignore", "pipe", "ignore"]
|
|
439
|
+
})
|
|
440
|
+
if (result.error !== undefined || result.status !== 0) return undefined
|
|
441
|
+
const value = parseJson(result.stdout ?? "")
|
|
442
|
+
if (value === undefined || !isObject(value) || !Array.isArray(value["installed"])) return undefined
|
|
443
|
+
const ids = new Set<string>()
|
|
444
|
+
for (const row of value["installed"]) {
|
|
445
|
+
if (!isObject(row) || row["installed"] !== true || typeof row["pluginId"] !== "string") continue
|
|
446
|
+
ids.add(row["pluginId"])
|
|
447
|
+
}
|
|
448
|
+
return ids
|
|
449
|
+
}
|
|
450
|
+
|
|
434
451
|
function syncPlugins(ctx: Ctx, sotConfigText: string): void {
|
|
435
|
-
const { change, echo, warn } = ctx.services.logger
|
|
452
|
+
const { change, echo, verbose, warn } = ctx.services.logger
|
|
436
453
|
if (ctx.dryRun) {
|
|
437
|
-
echo(
|
|
454
|
+
echo(
|
|
455
|
+
ctx.skipPluginRefresh
|
|
456
|
+
? "[dry-run] add missing enabled Codex plugins from SoT; skip refresh-only plugin updates"
|
|
457
|
+
: "[dry-run] add enabled Codex plugins from SoT"
|
|
458
|
+
)
|
|
438
459
|
return
|
|
439
460
|
}
|
|
440
461
|
|
|
@@ -453,9 +474,20 @@ function syncPlugins(ctx: Ctx, sotConfigText: string): void {
|
|
|
453
474
|
return
|
|
454
475
|
}
|
|
455
476
|
|
|
477
|
+
const desiredPluginIds = enabledPluginIdsFromText(sotConfigText)
|
|
478
|
+
let pluginIds = desiredPluginIds
|
|
479
|
+
if (ctx.skipPluginRefresh) {
|
|
480
|
+
const installedPluginIds = installedPluginIdsFromCli()
|
|
481
|
+
if (installedPluginIds === undefined) {
|
|
482
|
+
warn("Codex plugin inventory unavailable — falling back to the full refresh path")
|
|
483
|
+
} else {
|
|
484
|
+
pluginIds = desiredPluginIds.filter((pluginId) => !installedPluginIds.has(pluginId))
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
|
|
456
488
|
let refreshed = 0
|
|
457
489
|
let failed = 0
|
|
458
|
-
for (const pluginId of
|
|
490
|
+
for (const pluginId of pluginIds) {
|
|
459
491
|
const res = spawnSync("codex", ["plugin", "add", pluginId], { encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] })
|
|
460
492
|
const addOut = `${res.stdout ?? ""}${res.stderr ?? ""}`
|
|
461
493
|
if (res.error === undefined && res.status === 0) {
|
|
@@ -482,6 +514,7 @@ function syncPlugins(ctx: Ctx, sotConfigText: string): void {
|
|
|
482
514
|
warn(`Session Relay readiness unavailable after refresh: ${readiness.reason}`)
|
|
483
515
|
}
|
|
484
516
|
}
|
|
517
|
+
if (ctx.skipPluginRefresh && pluginIds.length === 0) verbose("Codex plugins already installed; refresh-only updates skipped")
|
|
485
518
|
if (failed > 0) warn(`${failed} Codex plugin operation(s) failed — re-run sync or install manually`)
|
|
486
519
|
}
|
|
487
520
|
|
|
@@ -15,7 +15,8 @@ import { claudeNextSteps, claudeSummary, claudeSync } from "./claudeSync"
|
|
|
15
15
|
import { codexNextSteps, codexSummary, codexSync } from "./codexSync"
|
|
16
16
|
import { skillsNextSteps, skillsSummary, skillsSync } from "./skillsSync"
|
|
17
17
|
import { modeModel, modeToolchain } from "./modes"
|
|
18
|
-
import { ExitError, parseArgs, validateModifierFlags } from "./parseArgs"
|
|
18
|
+
import { ExitError, parseArgs, parseWorkflowArgs, printWorkflowUsage, validateModifierFlags } from "./parseArgs"
|
|
19
|
+
import { deployWorkflowOverrides } from "./workflowDeploy"
|
|
19
20
|
|
|
20
21
|
export type ModifierFlag =
|
|
21
22
|
| "--claude-model"
|
|
@@ -31,6 +32,7 @@ export interface Ctx {
|
|
|
31
32
|
dryRun: boolean
|
|
32
33
|
verbose: boolean
|
|
33
34
|
skipRtk: boolean
|
|
35
|
+
skipPluginRefresh?: boolean
|
|
34
36
|
reconcile: boolean
|
|
35
37
|
prune: boolean
|
|
36
38
|
assumeYes: boolean
|
|
@@ -71,6 +73,7 @@ function makeCtx(services: EngineServices): Ctx {
|
|
|
71
73
|
dryRun: env["DRY_RUN"] === "1",
|
|
72
74
|
verbose: env["DOCKS_KIT_VERBOSE"] === "1",
|
|
73
75
|
skipRtk: env["SKIP_RTK"] === "1",
|
|
76
|
+
skipPluginRefresh: false,
|
|
74
77
|
reconcile: env["RECONCILE"] === "1",
|
|
75
78
|
prune: env["PRUNE"] === "1",
|
|
76
79
|
assumeYes: env["ASSUME_YES"] === "1",
|
|
@@ -124,6 +127,19 @@ function engineSync(ctx: Ctx, args: ReadonlyArray<string>): number {
|
|
|
124
127
|
return 0
|
|
125
128
|
}
|
|
126
129
|
|
|
130
|
+
function engineWorkflow(ctx: Ctx, args: ReadonlyArray<string>): number {
|
|
131
|
+
try {
|
|
132
|
+
const overrides = parseWorkflowArgs(ctx, args)
|
|
133
|
+
deployWorkflowOverrides(ctx, overrides)
|
|
134
|
+
return 0
|
|
135
|
+
} catch (error) {
|
|
136
|
+
if (error instanceof ExitError) throw error
|
|
137
|
+
printWorkflowUsage(ctx)
|
|
138
|
+
ctx.services.logger.err(error instanceof Error ? error.message : String(error))
|
|
139
|
+
throw new ExitError(2)
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
127
143
|
export function runEngineNative(argv: ReadonlyArray<string>, services?: EngineServices): number {
|
|
128
144
|
let ctx!: Ctx
|
|
129
145
|
const baseServices = services ?? makeEngineServices()
|
|
@@ -149,6 +165,8 @@ export function runEngineNative(argv: ReadonlyArray<string>, services?: EngineSe
|
|
|
149
165
|
return modeModel(ctx, argv.slice(1))
|
|
150
166
|
case "toolchain":
|
|
151
167
|
return modeToolchain(ctx, argv.slice(1))
|
|
168
|
+
case "workflow":
|
|
169
|
+
return engineWorkflow(ctx, argv.slice(1))
|
|
152
170
|
case "sync":
|
|
153
171
|
return engineSync(ctx, argv.slice(1))
|
|
154
172
|
default:
|