docks-kit 0.16.13 → 0.16.15
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 +10 -2
- package/cli/docs/flags.md +8 -1
- package/cli/docs/install.md +20 -1
- package/cli/docs/overview.md +1 -0
- package/cli/docs/plugins.md +11 -4
- package/cli/docs/toolchain.md +24 -0
- package/cli/src/commands/docs.ts +4 -3
- package/cli/src/commands/harnesses.ts +1 -1
- package/cli/src/commands/model.ts +9 -7
- package/cli/src/commands/models.ts +4 -3
- package/cli/src/commands/plugins.ts +4 -3
- package/cli/src/commands/skills.ts +4 -3
- package/cli/src/commands/status.ts +3 -2
- package/cli/src/commands/sync.ts +31 -25
- package/cli/src/commands/toolchain.ts +5 -4
- package/cli/src/commands/update.ts +3 -2
- package/cli/src/generated/sotPayload.ts +3 -3
- package/cli/tsconfig.json +1 -1
- package/package.json +6 -6
package/AGENTS.md
CHANGED
|
@@ -84,7 +84,7 @@ For per-tool SoT layouts (`SoT/.claude/`, `SoT/.codex/`, `SoT/.omp/`), see the m
|
|
|
84
84
|
- **Effect skill routing.** Effect work in this checkout must verify migration and API call shapes against the installed declarations under `node_modules/effect/dist/unstable/cli/`, never from memory or a mutable dist-tag. The `effect-ts-setup`, `effect-ts-port`, and `effect-ts-specialist` skills target Effect 3.x and do not apply.
|
|
85
85
|
- **Targeted syncs.** `./docks-kit sync` accepts positional targets: `claude`, `codex`, `agents`, and `omp`. Use the narrowest target that matches the SoT change (for example, `./docks-kit sync omp` for omp-only config edits); targets can be combined with `--dry-run`, `--skip-bubblewrap` (skip optional bubblewrap bootstrap for the Codex Linux sandbox), `--skip-plugin-refresh` (install missing plugins without refreshing existing caches), `--reconcile`, `--prune`, 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).
|
|
86
86
|
- **Per-machine harness selection.** `~/.docks-kit/state.json` drives a flag-less sync. A missing file selects `claude`, `codex`, and `agents`; it never selects `omp` implicitly. `sync` never prompts and never writes the selection file. `docks-kit harnesses` is the only command that writes the selection.
|
|
87
|
-
- **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 (`
|
|
87
|
+
- **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 (`claudeSync.ts REMOVED_MANIFEST, baseline removal inventory`), a curated list of unambiguous kit-owned artifacts that `claudeSync.ts syncRemovals, baseline artifact prune` force-prunes on every sync, including the home-relative `~/.local/bin/session-relay` artifact installed outside `~/.claude`; see `CLAUDE.md` § Pruning stale artifacts.
|
|
88
88
|
- **`--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.
|
|
89
89
|
- **SOLID-aligned modules.** `cli/src/engine-native/parseArgs.ts` owns flag parsing and validation. `toolchain.ts` owns verified-version floor reporting 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 their tool-specific sync logic. `ompSync.ts` owns omp file and plugin sync. `ompYaml.ts` owns the omp YAML merge. `harnesses.ts` owns per-machine harness selection. `index.ts` is the thin orchestrator. The public CLI seam is `cli/src/engine.ts`.
|
|
90
90
|
- **Small, reviewable changes.** Bundled multi-concern PRs are harder to review and revert. Split an engine/CLI change and a per-tool config change unless the change requires atomicity.
|
|
@@ -101,7 +101,7 @@ For per-tool SoT layouts (`SoT/.claude/`, `SoT/.codex/`, `SoT/.omp/`), see the m
|
|
|
101
101
|
|
|
102
102
|
- No secrets in SoT. The kit's SoT directories are committed; treat them as declarative config only.
|
|
103
103
|
- Treat external plugin marketplaces and installer downloads as untrusted input. Prefer download-then-run over `curl … | bash` so a truncated stream cannot execute a partial script.
|
|
104
|
-
- **Pin, never float.** Every kit-driven install of third-party software is pinned to a `SoT/toolchain.json` `verified` version — no `@latest` npm/bun installs (Shai-Hulud-class worm surface), no mutable action tags in workflows (commit SHAs only), installer scripts fetched from version tags where upstream supports it. New third-party install surface ⇒ manifest pin first. The rule also covers a transitive dependency whose declared range is a caret or tilde over a prerelease: `^4.0.0-rc.109` admits every later prerelease of the same version tuple, and a published install resolves that range fresh because `bun.lock` is not packaged, so a runtime pinned in `package.json` meets a newer sibling and the process fails at startup. The fix is an exact root pin plus both guards: the offline `cli/test/unit/dependencyPins.test.ts` and the clean-room `bun run smoke:package`. Details: the `toolchain-context` skill and `cli/docs/toolchain.md`.
|
|
104
|
+
- **Pin, never float.** Every kit-driven install of third-party software is pinned to a `SoT/toolchain.json` `verified` version — no `@latest` npm/bun installs (Shai-Hulud-class worm surface), no mutable action tags in workflows (commit SHAs only), installer scripts fetched from version tags where upstream supports it. New third-party install surface ⇒ manifest pin first. The rule also covers a transitive dependency whose declared range is a caret or tilde over a prerelease: `^4.0.0-rc.109` admits every later prerelease of the same version tuple, and a published install resolves that range fresh because `bun.lock` is not packaged, so a runtime pinned in `package.json` meets a newer sibling and the process fails at startup. The fix is an exact root pin plus both guards: the offline `cli/test/unit/dependencyPins.test.ts` and the clean-room `bun run smoke:package`. Moving the Effect release is therefore one lockstep commit: set `effect`, `@effect/platform-bun`, `@effect/platform-node-shared`, and `@effect/vitest` to the same version together, then run `bun run smoke:package`; the guard reports `unsatisfied` while one of them lags. Details: the `toolchain-context` skill and `cli/docs/toolchain.md`.
|
|
105
105
|
- **One exemption: the kit's own package.** `install.sh` and `install.ps1` end with `bun add -g docks-kit@latest`, because a global installer that pinned itself would install a fixed old kit forever, and pinning it to `package.json` would request an unpublished version between the release-prep commit and the npm publish. The exemption covers `docks-kit` alone. Both installers still pin the Bun installer they download to the manifest's verified version, and `cli/test/unit/install.test.ts` asserts that pin in all four launcher and installer scripts.
|
|
106
106
|
## Testing
|
|
107
107
|
|
|
@@ -109,6 +109,14 @@ Automated coverage includes `bun run test:unit`, `bun run golden:dryrun`, and `b
|
|
|
109
109
|
|
|
110
110
|
Use direct acceptance and focused regressions while iterating, then run the full unit/golden gate once at the pre-commit or release boundary. Reuse still-matching evidence; a later relevant edit invalidates only the affected rung and final gate, not every prior check.
|
|
111
111
|
|
|
112
|
+
The spawning suites launch a prebuilt bundle of `cli/src/main.ts`. The build runs once per run, into the ignored `cli/dist-test/`. Bun otherwise transpiles the whole CLI on every child spawn. Set `DOCKS_KIT_TEST_CLI_ENTRY=<repo>/cli/src/main.ts` to run those suites against the TypeScript entry. Use that variable to read a real stack trace. See `cli/test/lib/cliEntry.ts`.
|
|
113
|
+
|
|
114
|
+
## Landing changes
|
|
115
|
+
|
|
116
|
+
Open a pull request when the change can alter what CI proves: `cli/`, `SoT/`, `.github/`, the launchers, the installers, `package.json`, `bun.lock`. Wait for every required check, then ask before merge. Push direct to `main` only for prose: root Markdown and code comments.
|
|
117
|
+
|
|
118
|
+
Never rewrite published `main` history to correct a routing mistake. Use a follow-up commit and say what happened.
|
|
119
|
+
|
|
112
120
|
## Skills
|
|
113
121
|
|
|
114
122
|
**Project-skill scope.** Keep project skills within two classes: kit-mechanic skills and the `unslop` repo-prose skill.
|
package/cli/docs/flags.md
CHANGED
|
@@ -6,11 +6,18 @@ deployed config is `--<tool>-<setting>`.
|
|
|
6
6
|
## Targets (positional)
|
|
7
7
|
|
|
8
8
|
```
|
|
9
|
-
docks-kit sync #
|
|
9
|
+
docks-kit sync # stored harness selection
|
|
10
10
|
docks-kit sync claude # one
|
|
11
11
|
docks-kit sync claude agents # two
|
|
12
|
+
docks-kit sync omp # opt-in harness
|
|
12
13
|
```
|
|
13
14
|
|
|
15
|
+
Positional targets are `claude`, `codex`, `agents`, and `omp`. A flag-less
|
|
16
|
+
`sync` deploys the per-machine harness selection stored in
|
|
17
|
+
`~/.docks-kit/state.json`. A missing or invalid state file selects `claude`,
|
|
18
|
+
`codex`, and `agents`, and never selects `omp`. Choose the stored selection
|
|
19
|
+
with `docks-kit harnesses`.
|
|
20
|
+
|
|
14
21
|
## Global flags
|
|
15
22
|
|
|
16
23
|
| Flag | Effect |
|
package/cli/docs/install.md
CHANGED
|
@@ -122,7 +122,26 @@ bun rm -g docks-kit && bun add -g docks-kit@latest
|
|
|
122
122
|
A plain reinstall over the broken tree is not enough. Bun keeps the previously
|
|
123
123
|
resolved copy nested under
|
|
124
124
|
`node_modules/@effect/platform-bun/node_modules/@effect/platform-node-shared`,
|
|
125
|
-
and that stale copy still loads.
|
|
125
|
+
and that stale copy still loads.
|
|
126
|
+
|
|
127
|
+
If a plain reinstall was already attempted, the remove-then-add above no longer
|
|
128
|
+
clears that nested copy either. Force a full re-resolve of the global tree:
|
|
129
|
+
|
|
130
|
+
```
|
|
131
|
+
bun rm -g docks-kit && bun add -g docks-kit@latest --force
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
`--force` without the remove does not repair it. Other global packages survive
|
|
135
|
+
both sequences.
|
|
136
|
+
|
|
137
|
+
An npm global install repairs itself with a plain upgrade, because npm rebuilds
|
|
138
|
+
the tree it owns:
|
|
139
|
+
|
|
140
|
+
```
|
|
141
|
+
npm install -g docks-kit@latest
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
Verified on Bun 1.4.2 and npm 11, each in a clean HOME.
|
|
126
145
|
|
|
127
146
|
## No-Bun recovery
|
|
128
147
|
|
package/cli/docs/overview.md
CHANGED
|
@@ -12,6 +12,7 @@ AI-assisted dev environment on every machine.
|
|
|
12
12
|
| `SoT/.claude/` | Claude Code config (settings template, Bun runtime programs, CLAUDE.md) |
|
|
13
13
|
| `SoT/.codex/` | Codex config (config.toml, rules, AGENTS.md, marketplace) |
|
|
14
14
|
| `SoT/.agents/` | Universal agent skills manifest (agentskills.io standard) |
|
|
15
|
+
| `SoT/.omp/` | omp config deployed to `~/.omp/agent/` (AGENTS.md, config.yml, models.yml, mcp.json) plus `intercom.json` for pi intercom |
|
|
15
16
|
| `SoT/models.json` | Kit-verified model catalog (see `docks-kit docs models`) |
|
|
16
17
|
| `SoT/toolchain.json` | Verified-version floors for external tools (see `docks-kit docs toolchain`) |
|
|
17
18
|
| `cli/src/generated/sotPayload.ts` | Deterministic generated payload embedded in standalone/npm execution |
|
package/cli/docs/plugins.md
CHANGED
|
@@ -15,10 +15,17 @@ is absent from user settings.
|
|
|
15
15
|
## Sync passes (idempotent, via the `claude plugin` CLI)
|
|
16
16
|
|
|
17
17
|
1. Add SoT marketplaces not yet known
|
|
18
|
-
2. Install SoT-declared plugins missing at user scope
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
18
|
+
2. Install SoT-declared plugins missing at user scope, refreshing each
|
|
19
|
+
plugin's source marketplace once first
|
|
20
|
+
3. Refresh the remaining kit-owned marketplaces (skipped by
|
|
21
|
+
`--skip-plugin-refresh`)
|
|
22
|
+
4. Update the kit-owned installed plugins (skipped by `--skip-plugin-refresh`)
|
|
23
|
+
5. (`--prune` only) uninstall user-scope plugins that are neither
|
|
24
|
+
SoT-declared nor opted in on this run
|
|
25
|
+
6. (`--prune` only) remove marketplaces no kit-owned plugin needs (built-in
|
|
26
|
+
`claude-plugins-official` and marketplaces behind project- or local-scope
|
|
27
|
+
installs are never removed)
|
|
28
|
+
7. Re-assert SoT enabled-state (undoes install's enable side effect on
|
|
22
29
|
false-keyed plugins)
|
|
23
30
|
|
|
24
31
|
## Failed operations fail the sync
|
package/cli/docs/toolchain.md
CHANGED
|
@@ -28,6 +28,30 @@ jq and curl are `check` rows, not global prerequisites. jq is not consumed by
|
|
|
28
28
|
normal sync. curl is checked only when a requested Bun bootstrap must download
|
|
29
29
|
an installer. An already-present Bun skips that download on every host.
|
|
30
30
|
|
|
31
|
+
## Node floor
|
|
32
|
+
|
|
33
|
+
`node` stays a `check` row and now carries a floor of 22.22.2. The floor comes
|
|
34
|
+
from `typescript-language-server` 6's `engines` field. The kit installs that
|
|
35
|
+
server as an npm global for the `typescript-lsp` plugin, so an older Node runs
|
|
36
|
+
a server its own manifest rejects. `docks-kit toolchain check` prints
|
|
37
|
+
`below-floor` for a Node older than 22.22.2 and changes nothing else. No sync
|
|
38
|
+
pass, install, or launcher consults the Node floor. A host below the floor
|
|
39
|
+
still runs every kit operation.
|
|
40
|
+
|
|
41
|
+
## Language-server upgrades
|
|
42
|
+
|
|
43
|
+
`claudeSync syncLspServers` installs `intelephense`, `typescript-language-server`,
|
|
44
|
+
and `typescript` only when the binary is missing. It never upgrades a server
|
|
45
|
+
that is already present. A `verified` bump therefore reaches a fresh host
|
|
46
|
+
immediately and leaves an existing install alone.
|
|
47
|
+
|
|
48
|
+
A lagging server shows as `below-floor` in `docks-kit toolchain check`. To move
|
|
49
|
+
it, install the pinned version by hand:
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
npm install -g typescript-language-server@6.0.0
|
|
53
|
+
```
|
|
54
|
+
|
|
31
55
|
## Supply-chain stance
|
|
32
56
|
|
|
33
57
|
Every kit-driven install of third-party software is pinned to a
|
package/cli/src/commands/docs.ts
CHANGED
|
@@ -25,12 +25,13 @@ const TOPICS: Record<string, { summary: string; body: string }> = {
|
|
|
25
25
|
"omp-models": { summary: "omp role map and the Artificial Analysis snapshot behind it", body: ompModels }
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
-
const topic = Argument.
|
|
28
|
+
const topic = Argument.String("topic").pipe(
|
|
29
29
|
Argument.withDescription(`One of: ${Object.keys(TOPICS).join(", ")}`),
|
|
30
30
|
Argument.optional
|
|
31
31
|
)
|
|
32
|
-
const json = Flag.
|
|
33
|
-
Flag.withDescription("List topics as JSON")
|
|
32
|
+
const json = Flag.Boolean("json").pipe(
|
|
33
|
+
Flag.withDescription("List topics as JSON"),
|
|
34
|
+
Flag.withDefault(false)
|
|
34
35
|
)
|
|
35
36
|
|
|
36
37
|
export const docsCommand = Command.make("docs", { topic, json }, (config) =>
|
|
@@ -38,7 +38,7 @@ export const harnessesCommand = Command.make(
|
|
|
38
38
|
|
|
39
39
|
if (!process.stdout.isTTY) return
|
|
40
40
|
|
|
41
|
-
const answer = yield* Prompt.
|
|
41
|
+
const answer = yield* Prompt.MultiSelect({
|
|
42
42
|
message: "Choose the harnesses for a flag-less docks-kit sync",
|
|
43
43
|
choices: HARNESSES.map((harness) => ({
|
|
44
44
|
title: harness,
|
|
@@ -4,19 +4,21 @@ import { bail, engine } from "../engine"
|
|
|
4
4
|
import { modelCatalog } from "../engine-native/models"
|
|
5
5
|
import type { Tool } from "../manifests"
|
|
6
6
|
|
|
7
|
-
const tool = Argument.
|
|
7
|
+
const tool = Argument.String("tool").pipe(
|
|
8
8
|
Argument.withDescription("Which tool: claude | codex")
|
|
9
9
|
)
|
|
10
|
-
const value = Argument.
|
|
10
|
+
const value = Argument.String("value").pipe(
|
|
11
11
|
Argument.withDescription("Model to set (omit to view current + pick interactively on a TTY)"),
|
|
12
12
|
Argument.optional
|
|
13
13
|
)
|
|
14
|
-
const dryRun = Flag.
|
|
15
|
-
Flag.withDescription("Preview without applying")
|
|
14
|
+
const dryRun = Flag.Boolean("dry-run").pipe(
|
|
15
|
+
Flag.withDescription("Preview without applying"),
|
|
16
|
+
Flag.withDefault(false)
|
|
16
17
|
)
|
|
17
|
-
const verbose = Flag.
|
|
18
|
+
const verbose = Flag.Boolean("verbose").pipe(
|
|
18
19
|
Flag.withAlias("v"),
|
|
19
|
-
Flag.withDescription("Also print no-op confirmations (already in sync, up to date)")
|
|
20
|
+
Flag.withDescription("Also print no-op confirmations (already in sync, up to date)"),
|
|
21
|
+
Flag.withDefault(false)
|
|
20
22
|
)
|
|
21
23
|
|
|
22
24
|
const KEEP = "__keep__"
|
|
@@ -46,7 +48,7 @@ export const modelCommand = Command.make(
|
|
|
46
48
|
if (!process.stdin.isTTY || !process.stdout.isTTY) return
|
|
47
49
|
|
|
48
50
|
const catalog = modelCatalog(t)
|
|
49
|
-
const chosen = yield* Prompt.
|
|
51
|
+
const chosen = yield* Prompt.Select({
|
|
50
52
|
message: `Set the deployed ${t} model (deployed config only; a flag-less sync reverts to SoT)`,
|
|
51
53
|
choices: [
|
|
52
54
|
{ title: "(keep current)", value: KEEP },
|
|
@@ -4,12 +4,13 @@ import { bail } from "../engine"
|
|
|
4
4
|
import { modelCatalog } from "../engine-native/models"
|
|
5
5
|
import type { Tool } from "../manifests"
|
|
6
6
|
|
|
7
|
-
const tool = Argument.
|
|
7
|
+
const tool = Argument.String("tool").pipe(
|
|
8
8
|
Argument.withDescription("claude | codex (omit for both tool catalogs)"),
|
|
9
9
|
Argument.optional
|
|
10
10
|
)
|
|
11
|
-
const json = Flag.
|
|
12
|
-
Flag.withDescription("Machine-readable output")
|
|
11
|
+
const json = Flag.Boolean("json").pipe(
|
|
12
|
+
Flag.withDescription("Machine-readable output"),
|
|
13
|
+
Flag.withDefault(false)
|
|
13
14
|
)
|
|
14
15
|
|
|
15
16
|
const renderTool = (t: Tool) =>
|
|
@@ -3,12 +3,13 @@ import { Console, Effect, Option } from "effect"
|
|
|
3
3
|
import { bail } from "../engine"
|
|
4
4
|
import { pluginsView } from "../manifests"
|
|
5
5
|
|
|
6
|
-
const action = Argument.
|
|
6
|
+
const action = Argument.String("action").pipe(
|
|
7
7
|
Argument.withDescription("list (default)"),
|
|
8
8
|
Argument.optional
|
|
9
9
|
)
|
|
10
|
-
const json = Flag.
|
|
11
|
-
Flag.withDescription("Machine-readable output")
|
|
10
|
+
const json = Flag.Boolean("json").pipe(
|
|
11
|
+
Flag.withDescription("Machine-readable output"),
|
|
12
|
+
Flag.withDefault(false)
|
|
12
13
|
)
|
|
13
14
|
|
|
14
15
|
export const pluginsCommand = Command.make("plugins", { action, json }, (config) =>
|
|
@@ -3,12 +3,13 @@ import { Console, Effect, Option } from "effect"
|
|
|
3
3
|
import { bail } from "../engine"
|
|
4
4
|
import { skillsView } from "../manifests"
|
|
5
5
|
|
|
6
|
-
const action = Argument.
|
|
6
|
+
const action = Argument.String("action").pipe(
|
|
7
7
|
Argument.withDescription("list (default)"),
|
|
8
8
|
Argument.optional
|
|
9
9
|
)
|
|
10
|
-
const json = Flag.
|
|
11
|
-
Flag.withDescription("Machine-readable output")
|
|
10
|
+
const json = Flag.Boolean("json").pipe(
|
|
11
|
+
Flag.withDescription("Machine-readable output"),
|
|
12
|
+
Flag.withDefault(false)
|
|
12
13
|
)
|
|
13
14
|
|
|
14
15
|
export const skillsCommand = Command.make("skills", { action, json }, (config) =>
|
|
@@ -11,8 +11,9 @@ import {
|
|
|
11
11
|
} from "../manifests"
|
|
12
12
|
import { kitHome } from "../kitHome"
|
|
13
13
|
|
|
14
|
-
const json = Flag.
|
|
15
|
-
Flag.withDescription("Machine-readable output")
|
|
14
|
+
const json = Flag.Boolean("json").pipe(
|
|
15
|
+
Flag.withDescription("Machine-readable output"),
|
|
16
|
+
Flag.withDefault(false)
|
|
16
17
|
)
|
|
17
18
|
|
|
18
19
|
interface Drift {
|
package/cli/src/commands/sync.ts
CHANGED
|
@@ -31,61 +31,67 @@ const updateNudge = (logger: Logger): void => {
|
|
|
31
31
|
|
|
32
32
|
const VALID_TARGETS = ["claude", "codex", "agents", "omp"]
|
|
33
33
|
|
|
34
|
-
const targets = Argument.
|
|
35
|
-
Argument.
|
|
36
|
-
|
|
37
|
-
)
|
|
34
|
+
const targets = Argument.String("target").pipe(
|
|
35
|
+
Argument.withDescription("Sync targets: claude, codex, agents, omp (default: selected harnesses)"),
|
|
36
|
+
Argument.variadic()
|
|
38
37
|
)
|
|
39
38
|
|
|
40
|
-
const dryRun = Flag.
|
|
41
|
-
Flag.withDescription("Preview without applying")
|
|
39
|
+
const dryRun = Flag.Boolean("dry-run").pipe(
|
|
40
|
+
Flag.withDescription("Preview without applying"),
|
|
41
|
+
Flag.withDefault(false)
|
|
42
42
|
)
|
|
43
|
-
const reconcile = Flag.
|
|
44
|
-
Flag.withDescription("Reconcile kit-owned settings with SoT (SoT keys win; user-only keys preserved; permissions arrays replaced)")
|
|
43
|
+
const reconcile = Flag.Boolean("reconcile").pipe(
|
|
44
|
+
Flag.withDescription("Reconcile kit-owned settings with SoT (SoT keys win; user-only keys preserved; permissions arrays replaced)"),
|
|
45
|
+
Flag.withDefault(false)
|
|
45
46
|
)
|
|
46
|
-
const prune = Flag.
|
|
47
|
-
Flag.withDescription("Uninstall kit-managed installs not in SoT (plugins, marketplaces, universal skills)")
|
|
47
|
+
const prune = Flag.Boolean("prune").pipe(
|
|
48
|
+
Flag.withDescription("Uninstall kit-managed installs not in SoT (plugins, marketplaces, universal skills)"),
|
|
49
|
+
Flag.withDefault(false)
|
|
48
50
|
)
|
|
49
|
-
const skipBubblewrap = Flag.
|
|
50
|
-
Flag.withDescription("Skip optional bubblewrap bootstrap (Codex Linux sandbox)")
|
|
51
|
+
const skipBubblewrap = Flag.Boolean("skip-bubblewrap").pipe(
|
|
52
|
+
Flag.withDescription("Skip optional bubblewrap bootstrap (Codex Linux sandbox)"),
|
|
53
|
+
Flag.withDefault(false)
|
|
51
54
|
)
|
|
52
|
-
const skipPluginRefresh = Flag.
|
|
53
|
-
Flag.withDescription("Install missing plugins but skip refresh-only updates for existing plugins")
|
|
55
|
+
const skipPluginRefresh = Flag.Boolean("skip-plugin-refresh").pipe(
|
|
56
|
+
Flag.withDescription("Install missing plugins but skip refresh-only updates for existing plugins"),
|
|
57
|
+
Flag.withDefault(false)
|
|
54
58
|
)
|
|
55
|
-
const verbose = Flag.
|
|
59
|
+
const verbose = Flag.Boolean("verbose").pipe(
|
|
56
60
|
Flag.withAlias("v"),
|
|
57
|
-
Flag.withDescription("Also print no-op confirmations (already in sync, up to date, left as-is)")
|
|
61
|
+
Flag.withDescription("Also print no-op confirmations (already in sync, up to date, left as-is)"),
|
|
62
|
+
Flag.withDefault(false)
|
|
58
63
|
)
|
|
59
|
-
const claudeModel = Flag.
|
|
64
|
+
const claudeModel = Flag.String("claude-model").pipe(
|
|
60
65
|
Flag.withDescription("Deploy-time modifier: set deployed Claude model (see `docks-kit models claude`)"),
|
|
61
66
|
Flag.optional
|
|
62
67
|
)
|
|
63
|
-
const claudeEffort = Flag.
|
|
68
|
+
const claudeEffort = Flag.String("claude-effort").pipe(
|
|
64
69
|
Flag.withDescription("Deploy-time modifier: set Claude effortLevel (bare flag shows valid levels)"),
|
|
65
70
|
Flag.optional
|
|
66
71
|
)
|
|
67
|
-
const claudeAdvisor = Flag.
|
|
72
|
+
const claudeAdvisor = Flag.String("claude-advisor").pipe(
|
|
68
73
|
Flag.withDescription("Deploy-time modifier: set Claude advisor on/off/default"),
|
|
69
74
|
Flag.optional
|
|
70
75
|
)
|
|
71
|
-
const claudeCompactWindow = Flag.
|
|
76
|
+
const claudeCompactWindow = Flag.String("claude-compact-window").pipe(
|
|
72
77
|
Flag.withDescription("Deploy-time modifier: set deployed autocompact window in tokens (e.g. 680000 or 680k)"),
|
|
73
78
|
Flag.optional
|
|
74
79
|
)
|
|
75
|
-
const claudePermissive = Flag.
|
|
76
|
-
Flag.withDescription("Deploy-time modifier: empty permissions.ask/deny in deployed settings (sandboxes)")
|
|
80
|
+
const claudePermissive = Flag.Boolean("claude-permissive").pipe(
|
|
81
|
+
Flag.withDescription("Deploy-time modifier: empty permissions.ask/deny in deployed settings (sandboxes)"),
|
|
82
|
+
Flag.withDefault(false)
|
|
77
83
|
)
|
|
78
|
-
const claudePlugin = Flag.
|
|
84
|
+
const claudePlugin = Flag.String("claude-plugin").pipe(
|
|
79
85
|
Flag.withDescription(
|
|
80
86
|
"Sticky opt-in plugin(s); repeatable and/or comma-separated (known: supabase, n8n)"
|
|
81
87
|
),
|
|
82
88
|
Flag.atLeast(0)
|
|
83
89
|
)
|
|
84
|
-
const codexModel = Flag.
|
|
90
|
+
const codexModel = Flag.String("codex-model").pipe(
|
|
85
91
|
Flag.withDescription("Deploy-time modifier: set deployed Codex model (see `docks-kit models codex`)"),
|
|
86
92
|
Flag.optional
|
|
87
93
|
)
|
|
88
|
-
const codexEffort = Flag.
|
|
94
|
+
const codexEffort = Flag.String("codex-effort").pipe(
|
|
89
95
|
Flag.withDescription("Deploy-time modifier: set Codex model_reasoning_effort (bare flag shows valid levels)"),
|
|
90
96
|
Flag.optional
|
|
91
97
|
)
|
|
@@ -4,17 +4,18 @@ import { bail, engine } from "../engine"
|
|
|
4
4
|
|
|
5
5
|
const MANAGED = ["bun"]
|
|
6
6
|
|
|
7
|
-
const op = Argument.
|
|
7
|
+
const op = Argument.String("op").pipe(
|
|
8
8
|
Argument.withDescription("check (default) | ensure <tool>"),
|
|
9
9
|
Argument.optional
|
|
10
10
|
)
|
|
11
|
-
const tool = Argument.
|
|
11
|
+
const tool = Argument.String("tool").pipe(
|
|
12
12
|
Argument.withDescription(`Managed tool for ensure: ${MANAGED.join(", ")}`),
|
|
13
13
|
Argument.optional
|
|
14
14
|
)
|
|
15
|
-
const verbose = Flag.
|
|
15
|
+
const verbose = Flag.Boolean("verbose").pipe(
|
|
16
16
|
Flag.withAlias("v"),
|
|
17
|
-
Flag.withDescription("Also print no-op confirmations (present, up to date)")
|
|
17
|
+
Flag.withDescription("Also print no-op confirmations (present, up to date)"),
|
|
18
|
+
Flag.withDefault(false)
|
|
18
19
|
)
|
|
19
20
|
|
|
20
21
|
export const toolchainCommand = Command.make("toolchain", { op, tool, verbose }, (config) =>
|
|
@@ -7,8 +7,9 @@ import { kitHome } from "../kitHome"
|
|
|
7
7
|
import { p, which } from "../engine-native/exec"
|
|
8
8
|
import { hostOs, type HostOs, type Invocation } from "../engine-native/os"
|
|
9
9
|
|
|
10
|
-
const noSync = Flag.
|
|
11
|
-
Flag.withDescription("Update the kit only; skip the chained flag-less sync")
|
|
10
|
+
const noSync = Flag.Boolean("no-sync").pipe(
|
|
11
|
+
Flag.withDescription("Update the kit only; skip the chained flag-less sync"),
|
|
12
|
+
Flag.withDefault(false)
|
|
12
13
|
)
|
|
13
14
|
|
|
14
15
|
/** A tool this host cannot resolve, shaped like the failed spawn it replaces. */
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
// Generated by cli/scripts/generate-sot-payload.ts. DO NOT EDIT.
|
|
2
2
|
// Edit SoT/, notification.mp3, or package.json, then run: bun cli/scripts/generate-sot-payload.ts
|
|
3
3
|
|
|
4
|
-
export const GENERATED_PACKAGE_VERSION = "0.16.
|
|
4
|
+
export const GENERATED_PACKAGE_VERSION = "0.16.15"
|
|
5
5
|
|
|
6
6
|
export const GENERATED_PAYLOAD_TEXT = {
|
|
7
7
|
"SoT/.agents/skills.txt": "# Universal AI-agent skill manifest intentionally empty.\n# Global skill discovery is opt-in: add one <owner>/<repo> slug per line.\n# EngineNative ignores comments and blank lines.\n",
|
|
8
8
|
"SoT/models.json": "{\n \"$comment\": \"Kit-verified model catalog — single source for EngineNative validators, the docks-kit CLI (models/model commands, pickers, and bare-flag help), and docs. Entries are research-proofed: update an entry and its tool-level `verified` date when a model ships or retires. Deploy-time model flags remain permissive.\",\n \"claude\": {\n \"verified\": \"2026-07-27\",\n \"models\": [\n { \"id\": \"best\", \"kind\": \"alias\", \"note\": \"Fable 5 where the org has access, latest Opus otherwise (Claude Code >=2.1.170)\" },\n { \"id\": \"opus\", \"kind\": \"alias\", \"note\": \"latest Opus — the kit SoT default (Opus 5 on the Anthropic API from Claude Code >=2.1.219; Opus 4.6 on Microsoft Foundry)\" },\n { \"id\": \"fable\", \"kind\": \"alias\", \"note\": \"Fable 5 — advisor opt-in default; needs org access + Claude Code >=2.1.170\" },\n { \"id\": \"sonnet\", \"kind\": \"alias\", \"note\": \"latest Sonnet (currently Sonnet 5)\" },\n { \"id\": \"haiku\", \"kind\": \"alias\", \"note\": \"latest Haiku (currently Haiku 4.5)\" },\n { \"id\": \"default\", \"kind\": \"alias\", \"note\": \"engine pseudo-value: deletes the deployed model key so the account default applies\" },\n { \"id\": \"claude-fable-5\", \"kind\": \"id\", \"note\": \"Fable 5\" },\n { \"id\": \"claude-opus-5\", \"kind\": \"id\", \"note\": \"Opus 5 — needs Claude Code >=2.1.219\" },\n { \"id\": \"claude-opus-4-8\", \"kind\": \"id\", \"note\": \"Opus 4.8\" },\n { \"id\": \"claude-sonnet-5\", \"kind\": \"id\", \"note\": \"Sonnet 5\" },\n { \"id\": \"claude-haiku-4-5-20251001\", \"kind\": \"id\", \"note\": \"Haiku 4.5\" }\n ]\n },\n \"codex\": {\n \"verified\": \"2026-07-16\",\n \"models\": [\n { \"id\": \"gpt-5.6-sol\", \"kind\": \"id\", \"note\": \"GPT-5.6 Sol — frontier, recommended default; the kit SoT pin\" },\n { \"id\": \"gpt-5.6-terra\", \"kind\": \"id\", \"note\": \"GPT-5.6 Terra — balanced tier\" },\n { \"id\": \"gpt-5.6-luna\", \"kind\": \"id\", \"note\": \"GPT-5.6 Luna — fast/light tier\" },\n { \"id\": \"gpt-5.5\", \"kind\": \"id\", \"note\": \"previous generation\" },\n { \"id\": \"gpt-5.5-codex\", \"kind\": \"id\", \"note\": \"codex-tuned gpt-5.5\" },\n { \"id\": \"gpt-5.1\", \"kind\": \"id\", \"note\": \"previous generation\" },\n { \"id\": \"gpt-5\", \"kind\": \"id\", \"note\": \"previous generation\" },\n { \"id\": \"gpt-5-codex\", \"kind\": \"id\", \"note\": \"codex-tuned gpt-5\" }\n ]\n }\n}\n",
|
|
9
|
-
"SoT/toolchain.json": "{\n \"$comment\": \"Kit toolchain manifest - DATA only (versions, floors, policy); version probing and the doctor report live in cli/src/engine-native/toolchain.ts, and the one managed install lives in cli/src/engine-native/bun.ts bunBootstrap. kind: check (doctor visibility only) | managed (kit installs it when missing) | pin (no binary probe - a version pin for a package the kit installs through another tool, such as npx or `omp install`). policy (managed only): present (install when missing, never upgrade). `verified` = last kit-tested version; `pinnable` marks a tool whose `verified` release the bootstrap can install by exact tag. Supply-chain stance: every kit-driven install is pinned to `verified` - never floating @latest (npm-worm/Shai-Hulud surface). Update `verified` after testing a new release.\",\n \"tools\": {\n \"jq\": { \"kind\": \"check\", \"note\": \"optional operator CLI; EngineNative JSON and Claude runtime do not invoke it\" },\n \"curl\": { \"kind\": \"check\", \"note\": \"POSIX installer transport for the Bun bootstrap\" },\n \"git\": { \"kind\": \"check\", \"note\": \"plugin marketplaces (claude/codex clone them) + kit checkout updates\" },\n \"node\": { \"kind\": \"check\", \"note\": \"hosts the npm globals installed for the Claude LSP plugins\" },\n \"npm\": { \"kind\": \"check\", \"note\": \"npm-global installer; also backs the intelephense version probe (`npm ls -g`)\" },\n \"claude\": { \"kind\": \"check\", \"floor\": \"2.1.219\", \"note\": \"kit floor — lets the `opus` alias select the newest Opus its provider offers (Anthropic API: 5; Foundry: 4.6) and subsumes Fable 5's >=2.1.170 requirement (mirrors settings minimumVersion)\" },\n \"codex\": { \"kind\": \"check\", \"note\": \"upstream-owned; standalone installer prints when missing\" },\n \"omp\": { \"kind\": \"check\", \"floor\": \"18.0.8\", \"verified\": \"18.0.8\",\n \"note\": \"Oh My Pi harness (https://github.com/can1357/oh-my-pi); upstream-owned and self-updating through `omp update`, so sync never installs or upgrades it. `sync omp` needs the CLI only for the marketplace and plugin passes; the file deploys proceed without it\" },\n \"ffplay\": { \"kind\": \"check\", \"note\": \"Notification hook sound; distro-installed, so no kit floor applies\" },\n \"bwrap\": { \"kind\": \"check\", \"os\": \"linux\", \"floor\": \"0.9.0\",\n \"note\": \"Codex Linux sandbox runtime; sync installs it via the distro package manager, so the floor is the kit-tested baseline (Ubuntu 24.04 LTS) and no verified pin applies\" },\n \"intelephense\": { \"kind\": \"check\", \"floor\": \"1.18.5\", \"verified\": \"1.18.5\",\n \"note\": \"php-lsp server; `verified` pins claudeSync syncLspServers' npm install. Version comes from `npm ls -g` — its own --version prints minified source\" },\n \"typescript-language-server\": { \"kind\": \"check\", \"floor\": \"
|
|
9
|
+
"SoT/toolchain.json": "{\n \"$comment\": \"Kit toolchain manifest - DATA only (versions, floors, policy); version probing and the doctor report live in cli/src/engine-native/toolchain.ts, and the one managed install lives in cli/src/engine-native/bun.ts bunBootstrap. kind: check (doctor visibility only) | managed (kit installs it when missing) | pin (no binary probe - a version pin for a package the kit installs through another tool, such as npx or `omp install`). policy (managed only): present (install when missing, never upgrade). `verified` = last kit-tested version; `pinnable` marks a tool whose `verified` release the bootstrap can install by exact tag. Supply-chain stance: every kit-driven install is pinned to `verified` - never floating @latest (npm-worm/Shai-Hulud surface). Update `verified` after testing a new release.\",\n \"tools\": {\n \"jq\": { \"kind\": \"check\", \"note\": \"optional operator CLI; EngineNative JSON and Claude runtime do not invoke it\" },\n \"curl\": { \"kind\": \"check\", \"note\": \"POSIX installer transport for the Bun bootstrap\" },\n \"git\": { \"kind\": \"check\", \"note\": \"plugin marketplaces (claude/codex clone them) + kit checkout updates\" },\n \"node\": { \"kind\": \"check\", \"floor\": \"22.22.2\",\n \"note\": \"hosts the npm globals installed for the Claude LSP plugins; the floor is typescript-language-server 6's engines requirement, so an older Node reports `below-floor` in `docks-kit toolchain` instead of silently installing an LSP server that cannot start\" },\n \"npm\": { \"kind\": \"check\", \"note\": \"npm-global installer; also backs the intelephense version probe (`npm ls -g`)\" },\n \"claude\": { \"kind\": \"check\", \"floor\": \"2.1.219\", \"note\": \"kit floor — lets the `opus` alias select the newest Opus its provider offers (Anthropic API: 5; Foundry: 4.6) and subsumes Fable 5's >=2.1.170 requirement (mirrors settings minimumVersion)\" },\n \"codex\": { \"kind\": \"check\", \"note\": \"upstream-owned; standalone installer prints when missing\" },\n \"omp\": { \"kind\": \"check\", \"floor\": \"18.0.8\", \"verified\": \"18.0.8\",\n \"note\": \"Oh My Pi harness (https://github.com/can1357/oh-my-pi); upstream-owned and self-updating through `omp update`, so sync never installs or upgrades it. `sync omp` needs the CLI only for the marketplace and plugin passes; the file deploys proceed without it\" },\n \"ffplay\": { \"kind\": \"check\", \"note\": \"Notification hook sound; distro-installed, so no kit floor applies\" },\n \"bwrap\": { \"kind\": \"check\", \"os\": \"linux\", \"floor\": \"0.9.0\",\n \"note\": \"Codex Linux sandbox runtime; sync installs it via the distro package manager, so the floor is the kit-tested baseline (Ubuntu 24.04 LTS) and no verified pin applies\" },\n \"intelephense\": { \"kind\": \"check\", \"floor\": \"1.18.5\", \"verified\": \"1.18.5\",\n \"note\": \"php-lsp server; `verified` pins claudeSync syncLspServers' npm install. Version comes from `npm ls -g` — its own --version prints minified source\" },\n \"typescript-language-server\": { \"kind\": \"check\", \"floor\": \"6.0.0\", \"verified\": \"6.0.0\",\n \"note\": \"typescript-lsp server binary; `verified` pins claudeSync syncLspServers' npm install. Version 6 requires Node >=22.22.2 (see the `node` floor)\" },\n \"tsc\": { \"kind\": \"check\", \"floor\": \"6.0.3\", \"verified\": \"6.0.3\", \"note\": \"typescript-lsp dependency (npm package `typescript`); `verified` pins claudeSync syncLspServers' npm install. Deliberately on the 6.x line: typescript-language-server embeds TypeScript's programmatic API, which TS7 (native) doesn't yet expose — the repo's own devDependency runs TS7 for tsc --noEmit\" },\n \"bun\": { \"kind\": \"managed\", \"policy\": \"present\", \"floor\": \"1.4.0\", \"verified\": \"1.4.2\", \"pinnable\": true,\n \"note\": \"runtime for the docks-kit CLI and the Claude statusline/hook programs; bootstrap installs the verified release (installer takes bun-vX.Y.Z); self-updates via `bun upgrade` when wanted\" },\n \"skills-cli\": { \"kind\": \"pin\", \"verified\": \"1.5.25\",\n \"note\": \"the `skills` npm package the kit runs via `npx skills@<verified>` when SoT/.agents/skills.txt names a slug (it is empty by default) — pinned, never @latest\" },\n \"pi-intercom\": { \"kind\": \"pin\", \"verified\": \"0.13.0\",\n \"note\": \"the cross-session messaging plugin ompSync installs with `omp install pi-intercom@<verified>` - pinned, never floating. Its broker runs under Bun because omp's flat plugin store cannot resolve the default `npx --no-install tsx` launcher\" }\n }\n}\n",
|
|
10
10
|
"SoT/.claude/CLAUDE.md": "## Research Before Implementation\n\nBefore writing or modifying code that uses an API, hook, method, or config surface you have not verified in this session, research current documentation first.\n\nResearch workflow:\n1. Prefer official documentation and primary sources for the specific library, framework, or API.\n2. If a local docs or MCP tool is available, use it before broad web search.\n3. Only then proceed to implementation.\n\nResearch when:\n- Installing or configuring a dependency.\n- Using an API, hook, method, or pattern not verified in this session.\n- Upgrading or migrating between versions.\n- Any task where relying on memory could cause stale syntax or behavior.\n\nDo not:\n- Assume API signatures, method names, or config options from memory.\n- Generate framework code without checking current docs first.\n- Skip research because the library seems familiar.\n\n<constraint>\nResearch the codebase before editing. Never change code you have not read.\n</constraint>\n\n## Agentic Harness Heuristics\n\n**1. Persistence.** Keep going until the user's query is completely resolved. Only yield when sure the problem is solved. Before ending a turn, check the last paragraph: if it is a plan, a question you can answer yourself, or a promise of work not done (\"I'll…\"), do that work now.\n\n**2. Default to parallel.** Whenever you have multiple independent operations (reads, greps, web fetches, independent edits), invoke them in a single response with multiple tool-use blocks. Sequential calls only when output of one operation is required as input to the next.\n\n**3. Multi-pass search.** First-pass search often misses — vary the wording (colleague-questions over keywords) before concluding something doesn't exist.\n\n**4. Trace symbols.** Before modifying a symbol, trace it to its definitions and all usages. Don't assume a function's behavior or a type's shape from the call site alone.\n\n**5. Linter-loop 3-strike rule.** Don't loop more than 3 times fixing linter errors on the same file. On the third attempt, stop and ask the user — repeated failure usually means the diagnosis is wrong, not the code.\n\n**6. Read-before-Edit TTL.** If you haven't read a file with the Read tool in the last ~5 messages, re-read it before editing. Cached file content goes stale silently when the user edits between turns.\n\n**7. Big-file rule.** For files >1000 lines, prefer Grep + scoped Read (`offset` + `limit`) over reading the entire file. Whole-file reads bloat context; targeted reads keep the working set small.\n\n**8. Todo hygiene.** Use TaskCreate for items with meaningful outcome (≥5 min, distinct deliverable). Never include operational sub-actions (linting, testing, searching, examining the codebase) as their own todos — those are sub-steps in service of higher-level tasks. Mark complete immediately when done, never in batches.\n\n**9. Literal-instruction rule.** Current frontier models follow instructions literally — they do not silently generalize from intent. Phrase requirements as explicit checklists with success criteria, not narrative.\n\n**10. Context hygiene.** Prefer `/clear` at task boundaries and `/rewind` for wrong-path detours over carrying rot forward (corrections accumulate noise; rewinds preserve the prefix and discard the bad branch). On a continuing task, run `/compact` with steering before context quality degrades. Never stop, summarize, or suggest a new session on account of context limits.\n\n**11. Autonomy calibration.** For minor choices (naming, formatting, default values, which of two equivalent approaches), pick a reasonable option and note it — don't ask. Ask first only for scope changes, destructive actions, or decisions that change the deliverable. When the user is describing a problem or asking a question rather than requesting a change, the deliverable is your assessment — report findings and stop; don't apply fixes until asked. Don't close a finished task with \"Want me to also…?\" — run the obvious verification, then stop cleanly.\n\n**12. Capability triggering.** When the answer depends on current or version-specific information, search or fetch before answering — never answer from memory. When work fans out across independent items (many files to read, many tests to run, many candidates to check), delegate to parallel subagents; never spawn one for work you can complete directly. For verification, prefer a fresh-context subagent over self-critique. On tasks longer than a few turns, keep a running notes file and re-read it before each phase.\n\n<constraint>\nTreat the 12 heuristics above as protocol, not preference. If a turn ends without honoring an applicable one (e.g., lint-loop guard not respected, edit without re-read), self-correct in the next turn before continuing.\n</constraint>\n\nReuse before invention: inventory existing code, components, conventions, and dependencies; extend them instead of creating a parallel pattern. Load only the narrow skills supported by the task and repository evidence. If a request establishes a new React/Tailwind system and no convention exists, prefer current shadcn/ui `base-*` components backed by Base UI; otherwise preserve the existing stack. Treat probe, production access, publish, push, release, and deploy as literal current-request effects—never infer external authority from a plan, schedule, review, or old receipt.\n\nFor Docks plan reviews, cross-company review is standing-authorized; do not ask for export consent. This never overrides a host or platform security denial.\n\nUse a narrow-to-broad verification ladder: direct acceptance while iterating, focused regressions next, and one full CI at the pre-commit or release boundary. Reuse still-matching evidence; rerun full CI only after a relevant edit invalidates it.\n\n<constraint>\nNo secrets in committed config. Treat plugin marketplaces, installers, and downloaded artifacts as untrusted until verified.\n</constraint>\n",
|
|
11
11
|
"SoT/.claude/mcp-servers.json": "{\n \"mcpServers\": {}\n}\n",
|
|
12
12
|
"SoT/.claude/settings.json": "{\n \"$schema\": \"https://json.schemastore.org/claude-code-settings.json\",\n \"minimumVersion\": \"2.1.219\",\n \"model\": \"opus\",\n \"effortLevel\": \"high\",\n \"autoMemoryEnabled\": false,\n \"autoDreamEnabled\": false,\n \"skillListingMaxDescChars\": 2048,\n \"respectGitignore\": true,\n \"cleanupPeriodDays\": 14,\n \"skillListingBudgetFraction\": 0.05,\n \"env\": {\n \"CLAUDE_CODE_MAX_OUTPUT_TOKENS\": \"64000\",\n \"CLAUDE_BASH_MAINTAIN_PROJECT_WORKING_DIR\": \"1\",\n \"CLAUDE_CODE_AUTO_COMPACT_WINDOW\": \"468000\",\n \"CLAUDE_CODE_NO_FLICKER\": \"1\"\n },\n \"permissions\": {\n \"defaultMode\": \"auto\",\n \"allow\": [\n \"Read\",\n \"Glob\",\n \"Grep\",\n \"WebSearch\",\n \"Edit(./)\"\n ],\n \"deny\": [\n \"Read(**/.env)\",\n \"Read(**/.env.local)\",\n \"Read(**/secrets/**)\",\n \"Read(**/*.key)\",\n \"Read(**/*.pem)\",\n \"Read(**/*.p12)\",\n \"Read(**/.credentials*)\",\n \"Edit(**/.env)\",\n \"Edit(**/.env.local)\",\n \"Edit(**/secrets/**)\",\n \"Bash(sudo *)\",\n \"Bash(rm -rf /)\",\n \"Bash(rm -rf / *)\",\n \"Bash(rm -rf ~)\",\n \"Bash(rm -rf ~ *)\",\n \"Bash(rm -rf $HOME)\",\n \"Bash(rm -rf $HOME *)\",\n \"Bash(> /dev *)\",\n \"Bash(dd if= *)\",\n \"Bash(mkfs *)\",\n \"Bash(eval *)\",\n \"Bash(chmod 777 *)\",\n \"Bash(chmod -R 777 *)\",\n \"Bash(git push --force origin main *)\",\n \"Bash(git push --force origin master *)\",\n \"Bash(git push -f origin main *)\",\n \"Bash(git push -f origin master *)\",\n \"PowerShell(sudo *)\",\n \"PowerShell(rm -rf /)\",\n \"PowerShell(rm -rf / *)\",\n \"PowerShell(rm -rf ~)\",\n \"PowerShell(rm -rf ~ *)\",\n \"PowerShell(rm -rf $HOME)\",\n \"PowerShell(rm -rf $HOME *)\",\n \"PowerShell(> /dev *)\",\n \"PowerShell(dd if= *)\",\n \"PowerShell(mkfs *)\",\n \"PowerShell(eval *)\",\n \"PowerShell(chmod 777 *)\",\n \"PowerShell(chmod -R 777 *)\",\n \"PowerShell(git push --force origin main *)\",\n \"PowerShell(git push --force origin master *)\",\n \"PowerShell(git push -f origin main *)\",\n \"PowerShell(git push -f origin master *)\",\n \"PowerShell(Remove-Item *-Recurse* /)\",\n \"PowerShell(Remove-Item *-Recurse* / *)\",\n \"PowerShell(Remove-Item / *-Recurse*)\",\n \"PowerShell(Remove-Item -Path / *-Recurse*)\",\n \"PowerShell(Remove-Item -LiteralPath / *-Recurse*)\",\n \"PowerShell(Remove-Item *-Recurse* ~)\",\n \"PowerShell(Remove-Item *-Recurse* ~ *)\",\n \"PowerShell(Remove-Item ~ *-Recurse*)\",\n \"PowerShell(Remove-Item -Path ~ *-Recurse*)\",\n \"PowerShell(Remove-Item -LiteralPath ~ *-Recurse*)\",\n \"PowerShell(Remove-Item *-Recurse* $HOME)\",\n \"PowerShell(Remove-Item *-Recurse* $HOME *)\",\n \"PowerShell(Remove-Item $HOME *-Recurse*)\",\n \"PowerShell(Remove-Item -Path $HOME *-Recurse*)\",\n \"PowerShell(Remove-Item -LiteralPath $HOME *-Recurse*)\",\n \"PowerShell(Remove-Item *-Recurse* $env:USERPROFILE)\",\n \"PowerShell(Remove-Item *-Recurse* $env:USERPROFILE *)\",\n \"PowerShell(Remove-Item $env:USERPROFILE *-Recurse*)\",\n \"PowerShell(Remove-Item -Path $env:USERPROFILE *-Recurse*)\",\n \"PowerShell(Remove-Item -LiteralPath $env:USERPROFILE *-Recurse*)\",\n \"PowerShell(Remove-Item *-Recurse* \\\\\\\\)\",\n \"PowerShell(Remove-Item *-Recurse* \\\\ *)\",\n \"PowerShell(Remove-Item \\\\ *-Recurse*)\",\n \"PowerShell(Remove-Item -Path \\\\ *-Recurse*)\",\n \"PowerShell(Remove-Item -LiteralPath \\\\ *-Recurse*)\",\n \"PowerShell(Remove-Item *-Recurse* *:\\\\\\\\)\",\n \"PowerShell(Remove-Item *-Recurse* *:\\\\ *)\",\n \"PowerShell(Remove-Item *:\\\\ *-Recurse*)\",\n \"PowerShell(Remove-Item -Path *:\\\\ *-Recurse*)\",\n \"PowerShell(Remove-Item -LiteralPath *:\\\\ *-Recurse*)\",\n \"PowerShell(del *-Recurse* /)\",\n \"PowerShell(del *-Recurse* / *)\",\n \"PowerShell(del / *-Recurse*)\",\n \"PowerShell(del -Path / *-Recurse*)\",\n \"PowerShell(del -LiteralPath / *-Recurse*)\",\n \"PowerShell(del *-Recurse* ~)\",\n \"PowerShell(del *-Recurse* ~ *)\",\n \"PowerShell(del ~ *-Recurse*)\",\n \"PowerShell(del -Path ~ *-Recurse*)\",\n \"PowerShell(del -LiteralPath ~ *-Recurse*)\",\n \"PowerShell(del *-Recurse* $HOME)\",\n \"PowerShell(del *-Recurse* $HOME *)\",\n \"PowerShell(del $HOME *-Recurse*)\",\n \"PowerShell(del -Path $HOME *-Recurse*)\",\n \"PowerShell(del -LiteralPath $HOME *-Recurse*)\",\n \"PowerShell(del *-Recurse* $env:USERPROFILE)\",\n \"PowerShell(del *-Recurse* $env:USERPROFILE *)\",\n \"PowerShell(del $env:USERPROFILE *-Recurse*)\",\n \"PowerShell(del -Path $env:USERPROFILE *-Recurse*)\",\n \"PowerShell(del -LiteralPath $env:USERPROFILE *-Recurse*)\",\n \"PowerShell(del *-Recurse* \\\\\\\\)\",\n \"PowerShell(del *-Recurse* \\\\ *)\",\n \"PowerShell(del \\\\ *-Recurse*)\",\n \"PowerShell(del -Path \\\\ *-Recurse*)\",\n \"PowerShell(del -LiteralPath \\\\ *-Recurse*)\",\n \"PowerShell(del *-Recurse* *:\\\\\\\\)\",\n \"PowerShell(del *-Recurse* *:\\\\ *)\",\n \"PowerShell(del *:\\\\ *-Recurse*)\",\n \"PowerShell(del -Path *:\\\\ *-Recurse*)\",\n \"PowerShell(del -LiteralPath *:\\\\ *-Recurse*)\",\n \"PowerShell(erase *-Recurse* /)\",\n \"PowerShell(erase *-Recurse* / *)\",\n \"PowerShell(erase / *-Recurse*)\",\n \"PowerShell(erase -Path / *-Recurse*)\",\n \"PowerShell(erase -LiteralPath / *-Recurse*)\",\n \"PowerShell(erase *-Recurse* ~)\",\n \"PowerShell(erase *-Recurse* ~ *)\",\n \"PowerShell(erase ~ *-Recurse*)\",\n \"PowerShell(erase -Path ~ *-Recurse*)\",\n \"PowerShell(erase -LiteralPath ~ *-Recurse*)\",\n \"PowerShell(erase *-Recurse* $HOME)\",\n \"PowerShell(erase *-Recurse* $HOME *)\",\n \"PowerShell(erase $HOME *-Recurse*)\",\n \"PowerShell(erase -Path $HOME *-Recurse*)\",\n \"PowerShell(erase -LiteralPath $HOME *-Recurse*)\",\n \"PowerShell(erase *-Recurse* $env:USERPROFILE)\",\n \"PowerShell(erase *-Recurse* $env:USERPROFILE *)\",\n \"PowerShell(erase $env:USERPROFILE *-Recurse*)\",\n \"PowerShell(erase -Path $env:USERPROFILE *-Recurse*)\",\n \"PowerShell(erase -LiteralPath $env:USERPROFILE *-Recurse*)\",\n \"PowerShell(erase *-Recurse* \\\\\\\\)\",\n \"PowerShell(erase *-Recurse* \\\\ *)\",\n \"PowerShell(erase \\\\ *-Recurse*)\",\n \"PowerShell(erase -Path \\\\ *-Recurse*)\",\n \"PowerShell(erase -LiteralPath \\\\ *-Recurse*)\",\n \"PowerShell(erase *-Recurse* *:\\\\\\\\)\",\n \"PowerShell(erase *-Recurse* *:\\\\ *)\",\n \"PowerShell(erase *:\\\\ *-Recurse*)\",\n \"PowerShell(erase -Path *:\\\\ *-Recurse*)\",\n \"PowerShell(erase -LiteralPath *:\\\\ *-Recurse*)\",\n \"PowerShell(rd *-Recurse* /)\",\n \"PowerShell(rd *-Recurse* / *)\",\n \"PowerShell(rd / *-Recurse*)\",\n \"PowerShell(rd -Path / *-Recurse*)\",\n \"PowerShell(rd -LiteralPath / *-Recurse*)\",\n \"PowerShell(rd *-Recurse* ~)\",\n \"PowerShell(rd *-Recurse* ~ *)\",\n \"PowerShell(rd ~ *-Recurse*)\",\n \"PowerShell(rd -Path ~ *-Recurse*)\",\n \"PowerShell(rd -LiteralPath ~ *-Recurse*)\",\n \"PowerShell(rd *-Recurse* $HOME)\",\n \"PowerShell(rd *-Recurse* $HOME *)\",\n \"PowerShell(rd $HOME *-Recurse*)\",\n \"PowerShell(rd -Path $HOME *-Recurse*)\",\n \"PowerShell(rd -LiteralPath $HOME *-Recurse*)\",\n \"PowerShell(rd *-Recurse* $env:USERPROFILE)\",\n \"PowerShell(rd *-Recurse* $env:USERPROFILE *)\",\n \"PowerShell(rd $env:USERPROFILE *-Recurse*)\",\n \"PowerShell(rd -Path $env:USERPROFILE *-Recurse*)\",\n \"PowerShell(rd -LiteralPath $env:USERPROFILE *-Recurse*)\",\n \"PowerShell(rd *-Recurse* \\\\\\\\)\",\n \"PowerShell(rd *-Recurse* \\\\ *)\",\n \"PowerShell(rd \\\\ *-Recurse*)\",\n \"PowerShell(rd -Path \\\\ *-Recurse*)\",\n \"PowerShell(rd -LiteralPath \\\\ *-Recurse*)\",\n \"PowerShell(rd *-Recurse* *:\\\\\\\\)\",\n \"PowerShell(rd *-Recurse* *:\\\\ *)\",\n \"PowerShell(rd *:\\\\ *-Recurse*)\",\n \"PowerShell(rd -Path *:\\\\ *-Recurse*)\",\n \"PowerShell(rd -LiteralPath *:\\\\ *-Recurse*)\",\n \"PowerShell(ri *-Recurse* /)\",\n \"PowerShell(ri *-Recurse* / *)\",\n \"PowerShell(ri / *-Recurse*)\",\n \"PowerShell(ri -Path / *-Recurse*)\",\n \"PowerShell(ri -LiteralPath / *-Recurse*)\",\n \"PowerShell(ri *-Recurse* ~)\",\n \"PowerShell(ri *-Recurse* ~ *)\",\n \"PowerShell(ri ~ *-Recurse*)\",\n \"PowerShell(ri -Path ~ *-Recurse*)\",\n \"PowerShell(ri -LiteralPath ~ *-Recurse*)\",\n \"PowerShell(ri *-Recurse* $HOME)\",\n \"PowerShell(ri *-Recurse* $HOME *)\",\n \"PowerShell(ri $HOME *-Recurse*)\",\n \"PowerShell(ri -Path $HOME *-Recurse*)\",\n \"PowerShell(ri -LiteralPath $HOME *-Recurse*)\",\n \"PowerShell(ri *-Recurse* $env:USERPROFILE)\",\n \"PowerShell(ri *-Recurse* $env:USERPROFILE *)\",\n \"PowerShell(ri $env:USERPROFILE *-Recurse*)\",\n \"PowerShell(ri -Path $env:USERPROFILE *-Recurse*)\",\n \"PowerShell(ri -LiteralPath $env:USERPROFILE *-Recurse*)\",\n \"PowerShell(ri *-Recurse* \\\\\\\\)\",\n \"PowerShell(ri *-Recurse* \\\\ *)\",\n \"PowerShell(ri \\\\ *-Recurse*)\",\n \"PowerShell(ri -Path \\\\ *-Recurse*)\",\n \"PowerShell(ri -LiteralPath \\\\ *-Recurse*)\",\n \"PowerShell(ri *-Recurse* *:\\\\\\\\)\",\n \"PowerShell(ri *-Recurse* *:\\\\ *)\",\n \"PowerShell(ri *:\\\\ *-Recurse*)\",\n \"PowerShell(ri -Path *:\\\\ *-Recurse*)\",\n \"PowerShell(ri -LiteralPath *:\\\\ *-Recurse*)\",\n \"PowerShell(rm *-Recurse* /)\",\n \"PowerShell(rm *-Recurse* / *)\",\n \"PowerShell(rm / *-Recurse*)\",\n \"PowerShell(rm -Path / *-Recurse*)\",\n \"PowerShell(rm -LiteralPath / *-Recurse*)\",\n \"PowerShell(rm *-Recurse* ~)\",\n \"PowerShell(rm *-Recurse* ~ *)\",\n \"PowerShell(rm ~ *-Recurse*)\",\n \"PowerShell(rm -Path ~ *-Recurse*)\",\n \"PowerShell(rm -LiteralPath ~ *-Recurse*)\",\n \"PowerShell(rm *-Recurse* $HOME)\",\n \"PowerShell(rm *-Recurse* $HOME *)\",\n \"PowerShell(rm $HOME *-Recurse*)\",\n \"PowerShell(rm -Path $HOME *-Recurse*)\",\n \"PowerShell(rm -LiteralPath $HOME *-Recurse*)\",\n \"PowerShell(rm *-Recurse* $env:USERPROFILE)\",\n \"PowerShell(rm *-Recurse* $env:USERPROFILE *)\",\n \"PowerShell(rm $env:USERPROFILE *-Recurse*)\",\n \"PowerShell(rm -Path $env:USERPROFILE *-Recurse*)\",\n \"PowerShell(rm -LiteralPath $env:USERPROFILE *-Recurse*)\",\n \"PowerShell(rm *-Recurse* \\\\\\\\)\",\n \"PowerShell(rm *-Recurse* \\\\ *)\",\n \"PowerShell(rm \\\\ *-Recurse*)\",\n \"PowerShell(rm -Path \\\\ *-Recurse*)\",\n \"PowerShell(rm -LiteralPath \\\\ *-Recurse*)\",\n \"PowerShell(rm *-Recurse* *:\\\\\\\\)\",\n \"PowerShell(rm *-Recurse* *:\\\\ *)\",\n \"PowerShell(rm *:\\\\ *-Recurse*)\",\n \"PowerShell(rm -Path *:\\\\ *-Recurse*)\",\n \"PowerShell(rm -LiteralPath *:\\\\ *-Recurse*)\",\n \"PowerShell(rmdir *-Recurse* /)\",\n \"PowerShell(rmdir *-Recurse* / *)\",\n \"PowerShell(rmdir / *-Recurse*)\",\n \"PowerShell(rmdir -Path / *-Recurse*)\",\n \"PowerShell(rmdir -LiteralPath / *-Recurse*)\",\n \"PowerShell(rmdir *-Recurse* ~)\",\n \"PowerShell(rmdir *-Recurse* ~ *)\",\n \"PowerShell(rmdir ~ *-Recurse*)\",\n \"PowerShell(rmdir -Path ~ *-Recurse*)\",\n \"PowerShell(rmdir -LiteralPath ~ *-Recurse*)\",\n \"PowerShell(rmdir *-Recurse* $HOME)\",\n \"PowerShell(rmdir *-Recurse* $HOME *)\",\n \"PowerShell(rmdir $HOME *-Recurse*)\",\n \"PowerShell(rmdir -Path $HOME *-Recurse*)\",\n \"PowerShell(rmdir -LiteralPath $HOME *-Recurse*)\",\n \"PowerShell(rmdir *-Recurse* $env:USERPROFILE)\",\n \"PowerShell(rmdir *-Recurse* $env:USERPROFILE *)\",\n \"PowerShell(rmdir $env:USERPROFILE *-Recurse*)\",\n \"PowerShell(rmdir -Path $env:USERPROFILE *-Recurse*)\",\n \"PowerShell(rmdir -LiteralPath $env:USERPROFILE *-Recurse*)\",\n \"PowerShell(rmdir *-Recurse* \\\\\\\\)\",\n \"PowerShell(rmdir *-Recurse* \\\\ *)\",\n \"PowerShell(rmdir \\\\ *-Recurse*)\",\n \"PowerShell(rmdir -Path \\\\ *-Recurse*)\",\n \"PowerShell(rmdir -LiteralPath \\\\ *-Recurse*)\",\n \"PowerShell(rmdir *-Recurse* *:\\\\\\\\)\",\n \"PowerShell(rmdir *-Recurse* *:\\\\ *)\",\n \"PowerShell(rmdir *:\\\\ *-Recurse*)\",\n \"PowerShell(rmdir -Path *:\\\\ *-Recurse*)\",\n \"PowerShell(rmdir -LiteralPath *:\\\\ *-Recurse*)\",\n \"PowerShell(Start-Process *-Verb RunAs*)\",\n \"PowerShell(Invoke-Expression *)\",\n \"PowerShell(iex *)\",\n \"PowerShell(Format-Volume *)\",\n \"PowerShell(icacls */grant*Everyone:F*)\",\n \"PowerShell(icacls */grant*Everyone:(F)*)\"\n ],\n \"ask\": [\n \"Bash(git clean *)\",\n \"Bash(docker volume rm *)\",\n \"Bash(docker system prune *)\",\n \"PowerShell(git clean *)\",\n \"PowerShell(docker volume rm *)\",\n \"PowerShell(docker system prune *)\"\n ]\n },\n \"hooks\": {\n \"SessionStart\": [\n {\n \"hooks\": [\n {\n \"type\": \"command\",\n \"command\": \"__DOCKS_KIT_BUN__\",\n \"args\": [\"__DOCKS_KIT_SESSION_START__\"],\n \"timeout\": 5\n }\n ]\n }\n ],\n \"Notification\": [\n {\n \"hooks\": [\n {\n \"type\": \"command\",\n \"command\": \"__DOCKS_KIT_BUN__\",\n \"args\": [\"__DOCKS_KIT_NOTIFY__\"],\n \"timeout\": 10,\n \"async\": true\n }\n ]\n }\n ],\n \"PostToolUseFailure\": [\n {\n \"matcher\": \"Bash|PowerShell\",\n \"hooks\": [\n {\n \"type\": \"command\",\n \"command\": \"echo '{\\\"hookSpecificOutput\\\":{\\\"hookEventName\\\":\\\"PostToolUseFailure\\\",\\\"additionalContext\\\":\\\"Last bash command failed. Repository / file state may have shifted \\u2014 re-read affected files before retrying. If the failure is a missing dependency or env mismatch, surface it to the user rather than retrying blindly.\\\"}}'\",\n \"timeout\": 5\n }\n ]\n }\n ],\n \"SubagentStop\": [\n {\n \"hooks\": [\n {\n \"type\": \"prompt\",\n \"prompt\": \"You are a quality gate for subagent outputs in a multi-agent code-analysis pipeline.\\n\\nEvaluate the subagent's `last_assistant_message` field (in the JSON below) against these requirements:\\n\\n1. ALLOW (return `{}`): Mode-selection or no-issues responses. Examples: \\\"Which mode do you prefer\\\", \\\"select an option\\\", \\\"no issues / problems / violations / blockers found\\\".\\n\\n2. ALLOW (return `{}`): Output contains at least one concrete file:line citation \\u2014 e.g. `src/auth.ts:42`, `lib/db.ts:100-115`, or path references that include line numbers.\\n\\n3. BLOCK (return `{\\\"decision\\\":\\\"block\\\",\\\"reason\\\":\\\"<one-line explanation>\\\"}`): Output claims about code or findings WITHOUT concrete file:line citations. Vague references like \\\"the auth handler\\\" or \\\"near the database code\\\" are not acceptable as the only evidence.\\n\\nSubagent invocation JSON:\\n$ARGUMENTS\\n\\nReturn ONLY the JSON decision (no commentary, no markdown fences).\",\n \"timeout\": 30\n }\n ]\n }\n ]\n },\n \"statusLine\": {\n \"type\": \"command\",\n \"command\": \"__DOCKS_KIT_STATUSLINE__\",\n \"refreshInterval\": 5\n },\n \"enabledPlugins\": {\n \"docks@docks\": true,\n \"plan-lifecycle@docks\": true,\n \"php-lsp@claude-plugins-official\": true,\n \"typescript-lsp@claude-plugins-official\": true\n },\n \"extraKnownMarketplaces\": {\n \"docks\": {\n \"source\": {\n \"source\": \"github\",\n \"repo\": \"DocksDocks/docks\"\n }\n }\n },\n \"alwaysThinkingEnabled\": true,\n \"showThinkingSummaries\": true,\n \"viewMode\": \"default\",\n \"theme\": \"dark-daltonized\",\n \"skipDangerousModePermissionPrompt\": true\n}\n",
|
|
@@ -50,4 +50,4 @@ export const GENERATED_PAYLOAD_PATHS = [
|
|
|
50
50
|
"notification.mp3"
|
|
51
51
|
] as const
|
|
52
52
|
|
|
53
|
-
export const GENERATED_PAYLOAD_HASH = "
|
|
53
|
+
export const GENERATED_PAYLOAD_HASH = "4a7afde17fb96a6673cf56670a60c549a3460d075636d82e02d6d960e95ef944"
|
package/cli/tsconfig.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "docks-kit",
|
|
3
|
-
"version": "0.16.
|
|
3
|
+
"version": "0.16.15",
|
|
4
4
|
"description": "Portable AI coding agent config kit — SoT sync engine + typed CLI for Claude Code, Codex, and universal agent skills",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -40,15 +40,15 @@
|
|
|
40
40
|
"test:ci": "bun run check:generated && bun run --parallel typecheck test:unit golden:dryrun golden:mutation && bun run test:runtime:posix"
|
|
41
41
|
},
|
|
42
42
|
"dependencies": {
|
|
43
|
-
"@effect/platform-bun": "4.0.0-rc.
|
|
44
|
-
"@effect/platform-node-shared": "4.0.0-rc.
|
|
45
|
-
"effect": "4.0.0-rc.
|
|
43
|
+
"@effect/platform-bun": "4.0.0-rc.115",
|
|
44
|
+
"@effect/platform-node-shared": "4.0.0-rc.115",
|
|
45
|
+
"effect": "4.0.0-rc.115",
|
|
46
46
|
"yaml": "2.9.0"
|
|
47
47
|
},
|
|
48
48
|
"devDependencies": {
|
|
49
|
-
"@effect/vitest": "4.0.0-rc.
|
|
49
|
+
"@effect/vitest": "4.0.0-rc.115",
|
|
50
50
|
"@types/bun": "^1.4.0",
|
|
51
51
|
"typescript": "7.0.2",
|
|
52
|
-
"vitest": "
|
|
52
|
+
"vitest": "5.0.0"
|
|
53
53
|
}
|
|
54
54
|
}
|