docks-kit 0.8.2 → 0.10.0
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 +11 -5
- package/README.md +26 -15
- package/cli/docs/install.md +11 -20
- package/cli/docs/platforms.md +16 -44
- package/cli/docs/sync-layers.md +10 -2
- package/cli/docs/toolchain.md +17 -5
- package/cli/src/commands/docs.ts +1 -1
- package/cli/src/commands/toolchain.ts +1 -1
- package/cli/src/engine-native/DESIGN.md +19 -27
- package/cli/src/engine-native/bun.ts +7 -25
- package/cli/src/engine-native/claudeRuntime.ts +6 -15
- package/cli/src/engine-native/claudeSync.ts +5 -34
- package/cli/src/engine-native/codexSync.ts +3 -1
- package/cli/src/engine-native/deps.ts +34 -49
- package/cli/src/engine-native/exec.ts +6 -13
- package/cli/src/engine-native/modes.ts +4 -1
- package/cli/src/engine-native/os.ts +7 -12
- package/cli/src/engine-native/services.ts +2 -4
- package/cli/src/engine-native/sessionRelayCli.ts +257 -0
- package/cli/src/engine-native/skillsSync.ts +13 -38
- package/cli/src/engine-native/toolchain.ts +2 -0
- package/cli/src/engine.ts +23 -15
- package/cli/src/generated/sotPayload.ts +3 -3
- package/docks-kit +11 -10
- package/package.json +5 -1
- package/cli/src/engine-native/powershell.ts +0 -11
|
@@ -5,12 +5,12 @@
|
|
|
5
5
|
* and the snapshot write.
|
|
6
6
|
*/
|
|
7
7
|
import { spawnSync } from "node:child_process"
|
|
8
|
-
import { cpSync, existsSync, lstatSync, mkdirSync, readFileSync, readlinkSync,
|
|
8
|
+
import { cpSync, existsSync, lstatSync, mkdirSync, readFileSync, readlinkSync, rmSync, statSync, symlinkSync } from "node:fs"
|
|
9
9
|
import { p, writeFileIfChanged } from "./exec"
|
|
10
10
|
import { bunBootstrap } from "./bun"
|
|
11
11
|
import type { Ctx } from "./index"
|
|
12
12
|
import { compareCodepoints } from "./jq"
|
|
13
|
-
import type { EngineServices
|
|
13
|
+
import type { EngineServices } from "./services"
|
|
14
14
|
import { ensure, field } from "./toolchain"
|
|
15
15
|
import { payloadText } from "../payload"
|
|
16
16
|
|
|
@@ -139,9 +139,6 @@ function healClaudeSymlink(ctx: Ctx, skillsDir: string, base: string): boolean {
|
|
|
139
139
|
if (linkStat?.isSymbolicLink() === true) {
|
|
140
140
|
const current = safeReadlink(claudeLink)
|
|
141
141
|
if (current === relTarget) return false
|
|
142
|
-
// win32: `npx skills add` creates absolute symlinks/junctions — any link
|
|
143
|
-
// that RESOLVES to the canonical dir is healthy, not stale.
|
|
144
|
-
if (ctx.services.platform.isWindows() && realpathEquals(claudeLink, canonical)) return false
|
|
145
142
|
if (ctx.dryRun) {
|
|
146
143
|
echo(`[dry-run] would replace stale Claude symlink: ~/.claude/skills/${base} -> ${current} (correct: ${relTarget})`)
|
|
147
144
|
return true
|
|
@@ -178,36 +175,22 @@ function safeReadlink(path: string): string {
|
|
|
178
175
|
}
|
|
179
176
|
}
|
|
180
177
|
|
|
181
|
-
function realpathEquals(a: string, b: string): boolean {
|
|
182
|
-
try {
|
|
183
|
-
return realpathSync(a) === realpathSync(b)
|
|
184
|
-
} catch {
|
|
185
|
-
return false
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
178
|
|
|
189
|
-
/**
|
|
190
|
-
* Remove a symlink/junction without crashing: Bun's rmSync throws EFAULT on
|
|
191
|
-
* win32 directory symlinks. unlink handles file symlinks, rmdir handles
|
|
192
|
-
* dir symlinks/junctions, rmSync is the POSIX catch-all.
|
|
193
|
-
*/
|
|
179
|
+
/** Remove a symlink without touching a real directory. */
|
|
194
180
|
function removeLink(path: string): boolean {
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
// try the next removal shape
|
|
201
|
-
}
|
|
181
|
+
try {
|
|
182
|
+
rmSync(path, { force: true })
|
|
183
|
+
return true
|
|
184
|
+
} catch {
|
|
185
|
+
return lstat(path) === undefined
|
|
202
186
|
}
|
|
203
|
-
return lstat(path) === undefined
|
|
204
187
|
}
|
|
205
188
|
|
|
206
|
-
/** skills::_link_or_copy — real symlink preferred, copy fallback
|
|
207
|
-
export function linkOrCopy(target: string, link: string
|
|
189
|
+
/** skills::_link_or_copy — real symlink preferred, copy fallback. */
|
|
190
|
+
export function linkOrCopy(target: string, link: string): boolean {
|
|
208
191
|
removeLink(link)
|
|
209
192
|
try {
|
|
210
|
-
symlinkSync(target, link
|
|
193
|
+
symlinkSync(target, link)
|
|
211
194
|
} catch {
|
|
212
195
|
// fall through to the copy fallback below
|
|
213
196
|
}
|
|
@@ -223,11 +206,11 @@ export function linkOrCopy(target: string, link: string, platform: Platform): bo
|
|
|
223
206
|
}
|
|
224
207
|
|
|
225
208
|
function linkOrCopyWithWarnings(target: string, link: string, services: EngineServices): boolean {
|
|
226
|
-
const linked = linkOrCopy(target, link
|
|
209
|
+
const linked = linkOrCopy(target, link)
|
|
227
210
|
if (!linked) {
|
|
228
211
|
services.logger.warn(`could not create ${link} (symlink and copy both failed)`)
|
|
229
212
|
} else if (lstat(link)?.isSymbolicLink() !== true) {
|
|
230
|
-
services.logger.warn(`symlinks unsupported here — ${link} is a copy
|
|
213
|
+
services.logger.warn(`symlinks unsupported here — ${link} is a copy refreshed on sync`)
|
|
231
214
|
}
|
|
232
215
|
return linked
|
|
233
216
|
}
|
|
@@ -298,14 +281,6 @@ export function effectSolutionsInstall(
|
|
|
298
281
|
|
|
299
282
|
const location = services.deps.location("effect-solutions")
|
|
300
283
|
const gbin = location.binDir
|
|
301
|
-
// win32: bun writes an .exe shim (not the bare Unix name), and the
|
|
302
|
-
// ~/.local/bin link step below is Unix-only plumbing (non-interactive
|
|
303
|
-
// agent PATH) — bun's global bin is already the Windows PATH entry.
|
|
304
|
-
if (services.platform.isWindows()) {
|
|
305
|
-
if (location.path !== "") change(`effect-solutions CLI ready (${gbin})`)
|
|
306
|
-
else warn(`effect-solutions installed but no shim found under '${gbin !== "" ? gbin : "<unknown>"}' — check bun pm -g bin`)
|
|
307
|
-
return 0
|
|
308
|
-
}
|
|
309
284
|
if (location.path !== "") {
|
|
310
285
|
mkdirSync(p(ctx.home, ".local", "bin"), { recursive: true })
|
|
311
286
|
linkOrCopyWithWarnings(bun, p(ctx.home, ".local", "bin", "bun"), services)
|
|
@@ -57,6 +57,8 @@ export function installedVersion(ctx: Ctx, tool: ToolId): string {
|
|
|
57
57
|
case "codex":
|
|
58
58
|
case "agent-browser":
|
|
59
59
|
return firstLineField(version(), -1)
|
|
60
|
+
case "session-relay":
|
|
61
|
+
return firstLineField(version(), 1)
|
|
60
62
|
case "git":
|
|
61
63
|
return firstLineField(version(), 2)
|
|
62
64
|
case "node":
|
package/cli/src/engine.ts
CHANGED
|
@@ -5,28 +5,32 @@ import { makeEngineServices } from "./engine-native/services"
|
|
|
5
5
|
import { kitHome } from "./kitHome"
|
|
6
6
|
import { DependencyManagerService, LoggerService, PlatformService } from "./services"
|
|
7
7
|
|
|
8
|
-
// Same factory as the Effect rim's live layers — this path runs outside the
|
|
9
|
-
// runtime (child-spawn capture), so it takes the services directly.
|
|
10
|
-
const services = makeEngineServices()
|
|
11
|
-
|
|
12
8
|
/**
|
|
13
9
|
* The single seam between the typed CLI and EngineNative. Engine execution
|
|
14
10
|
* stays in-process after @effect/cli has parsed pickers and flag spellings.
|
|
15
11
|
*/
|
|
16
12
|
const bashRemovedMessage = "bash engine removed — recover at tag bash-engine-final"
|
|
17
13
|
const bashEngineRequested = (): boolean => process.env["DOCKS_KIT_ENGINE"] === "bash"
|
|
14
|
+
const requireSupportedHost = () => {
|
|
15
|
+
const platform = process.platform
|
|
16
|
+
const arch = process.arch
|
|
17
|
+
return (platform === "linux" || platform === "darwin") && (arch === "x64" || arch === "arm64")
|
|
18
|
+
? Effect.void
|
|
19
|
+
: bail(
|
|
20
|
+
`unsupported host ${platform}/${arch}; docks-kit supports only Linux and macOS on x64 or arm64`,
|
|
21
|
+
2
|
|
22
|
+
)
|
|
23
|
+
}
|
|
18
24
|
|
|
19
|
-
// bun build --compile runs the embedded entry from a virtual path
|
|
20
|
-
// ("/$bunfs/root/…"
|
|
21
|
-
//
|
|
22
|
-
// The Windows check is anchored to the drive-rooted "~BUN" segment so a
|
|
23
|
-
// real checkout under a ~BUN-named directory can't false-positive.
|
|
25
|
+
// bun build --compile runs the embedded entry from a virtual POSIX path
|
|
26
|
+
// ("/$bunfs/root/…"). There process.execPath IS the CLI, so a re-spawn must
|
|
27
|
+
// not pass main.ts.
|
|
24
28
|
export const compiled =
|
|
25
|
-
process.argv[1] !== undefined &&
|
|
26
|
-
(process.argv[1].startsWith("/$bunfs/") || /^[A-Za-z]:[\\/]~BUN[\\/]/i.test(process.argv[1]))
|
|
29
|
+
process.argv[1] !== undefined && process.argv[1].startsWith("/$bunfs/")
|
|
27
30
|
|
|
28
31
|
export const engine = (args: ReadonlyArray<string>) =>
|
|
29
32
|
Effect.gen(function* () {
|
|
33
|
+
yield* requireSupportedHost()
|
|
30
34
|
if (bashEngineRequested()) {
|
|
31
35
|
yield* bail(bashRemovedMessage, 2)
|
|
32
36
|
}
|
|
@@ -41,9 +45,12 @@ export const engine = (args: ReadonlyArray<string>) =>
|
|
|
41
45
|
|
|
42
46
|
/** Run the engine capturing stdout (engine logs/warns go to stderr and pass through). */
|
|
43
47
|
export const engineCapture = (args: ReadonlyArray<string>) =>
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
48
|
+
Effect.gen(function* () {
|
|
49
|
+
yield* requireSupportedHost()
|
|
50
|
+
if (bashEngineRequested()) {
|
|
51
|
+
return yield* bail(bashRemovedMessage, 2)
|
|
52
|
+
}
|
|
53
|
+
return yield* Effect.sync(() => {
|
|
47
54
|
// Child process (raw channel): runEngineNative writes straight to
|
|
48
55
|
// process.stdout, so in-process capture isn't possible.
|
|
49
56
|
const res = spawnSync(process.execPath, compiled ? [...args] : [`${kitHome()}/cli/src/main.ts`, ...args], {
|
|
@@ -52,10 +59,11 @@ export const engineCapture = (args: ReadonlyArray<string>) =>
|
|
|
52
59
|
stdio: ["ignore", "pipe", "inherit"]
|
|
53
60
|
})
|
|
54
61
|
if (res.error !== undefined || res.status !== 0) {
|
|
55
|
-
|
|
62
|
+
makeEngineServices().logger.warn(`engine capture failed (${args.join(" ")} exited ${res.status ?? "spawn-error"})`)
|
|
56
63
|
}
|
|
57
64
|
return res.stdout ?? ""
|
|
58
65
|
})
|
|
66
|
+
})
|
|
59
67
|
|
|
60
68
|
/** Print a message to stderr and exit — for CLI-side validation failures. */
|
|
61
69
|
export const bail = (message: string, code = 2) =>
|
|
@@ -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.
|
|
4
|
+
export const GENERATED_PACKAGE_VERSION = "0.10.0"
|
|
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, workflow selectors, pickers, 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; workflow selectors are strict.\",\n \"claude\": {\n \"verified\": \"2026-07-08\",\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 (currently Opus 4.8)\" },\n { \"id\": \"fable\", \"kind\": \"alias\", \"note\": \"Fable 5 — the kit SoT 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-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 \"workflow\": {\n \"schema\": 2,\n \"profiles\": {\n \"claude-best\": {\n \"candidates\": [\n { \"company\": \"anthropic\", \"tool\": \"claude\", \"model\": \"fable\", \"effort\": \"high\" },\n { \"company\": \"anthropic\", \"tool\": \"claude\", \"model\": \"opus\", \"effort\": \"xhigh\" }\n ]\n }\n },\n \"defaults\": {\n \"orchestrator\": \"profile:claude-best\",\n \"reviewer\": \"codex:gpt-5.6-sol@high\",\n \"implementer\": \"codex:gpt-5.6-sol@high\",\n \"review\": {\n \"minimum_score\": 90,\n \"max_rounds\": 3\n }\n },\n \"exact_target_grammar\": \"<tool>:<model>@<effort>[+fast]\",\n \"availability\": \"checked_when_used\"\n }\n}\n",
|
|
9
|
-
"SoT/toolchain.json": "{\n \"$comment\": \"Kit toolchain manifest — DATA only (versions, floors, policy); check/install logic lives in cli/src/engine-native/toolchain.ts with per-surface sync callbacks in cli/src/engine-native/. kind: check (doctor visibility only) | managed (kit installs/upgrades it) | pin (no binary probe — a version pin for a tool the kit invokes via npx). policy (managed only): track (upgrade toward latest, gated by `verified`) | present (install when missing, never upgrade). `verified` = last kit-tested version — anything above it prompts before install (--yes auto-accepts; non-TTY declines and falls back to the pinned `verified` when pinnable). Supply-chain stance: every kit-driven install is pinned to `verified` or gated by it — 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\": \"contextual POSIX installer transport for RTK/Bun bootstrap\" },\n \"git\": { \"kind\": \"check\", \"note\": \"plugin marketplaces (claude/codex clone them) + kit checkout updates\" },\n \"node\": { \"kind\": \"check\", \"note\": \"hosts npm globals (agent-browser, LSP servers)\" },\n \"npm\": { \"kind\": \"check\", \"note\": \"npm-global installer\" },\n \"claude\": { \"kind\": \"check\", \"floor\": \"2.1.170\", \"note\": \"kit floor — `best` alias + Fable 5 need >=2.1.170 (mirrors settings minimumVersion)\" },\n \"codex\": { \"kind\": \"check\", \"note\": \"upstream-owned; standalone installer prints when missing\" },\n \"ffplay\": { \"kind\": \"check\", \"note\": \"Notification hook sound (previously unchecked)\" },\n \"bwrap\": { \"kind\": \"check\", \"os\": \"linux\", \"note\": \"Codex Linux sandbox runtime\" },\n \"intelephense\": { \"kind\": \"check\", \"verified\": \"1.18.5\", \"note\": \"php-lsp server binary; `verified` pins claude::sync_lsp_servers' npm install\" },\n \"typescript-language-server\": { \"kind\": \"check\", \"verified\": \"5.3.0\", \"note\": \"typescript-lsp server binary; `verified` pins claude::sync_lsp_servers' npm install\" },\n \"tsc\": { \"kind\": \"check\", \"verified\": \"6.0.3\", \"note\": \"typescript-lsp dependency (npm package `typescript`); `verified` pins claude::sync_lsp_servers' 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 \"rtk\": { \"kind\": \"managed\", \"policy\": \"track\", \"floor\": \"0.43.0\", \"verified\": \"0.43.0\", \"pinnable\": true,\n \"note\": \"PreToolUse hook — supply-chain review before unverified upgrades; installer honors RTK_VERSION=vX.Y.Z pin\" },\n \"bun\": { \"kind\": \"managed\", \"policy\": \"present\", \"verified\": \"1.3.14\", \"pinnable\": true,\n \"note\": \"runtime for effect-solutions + the docks-kit CLI; bootstrap installs the verified release (installer takes bun-vX.Y.Z); self-updates via `bun upgrade` when wanted\" },\n \"effect-solutions\": { \"kind\": \"managed\", \"policy\": \"track\", \"verified\": \"0.5.3\", \"pinnable\": true,\n \"note\": \"Effect docs CLI (bun global) — track keeps it self-upgrading, gated by the verified pin\" },\n \"agent-browser\": { \"kind\": \"managed\", \"policy\": \"track\", \"verified\": \"0.32.0\", \"pinnable\": true,\n \"note\": \"browser-automation CLI (npm global), gated by the verified pin; first install also downloads Chrome for Testing\" },\n \"skills-cli\": { \"kind\": \"pin\", \"verified\": \"1.5.15\",\n \"note\": \"the `skills` npm package the kit runs via `npx skills@<verified>` on every agents sync (universal-skill install/remove) — pinned, never @latest\" }\n }\n}\n",
|
|
9
|
+
"SoT/toolchain.json": "{\n \"$comment\": \"Kit toolchain manifest — DATA only (versions, floors, policy); check/install logic lives in cli/src/engine-native/toolchain.ts with per-surface sync callbacks in cli/src/engine-native/. kind: check (doctor visibility only) | managed (kit installs/upgrades it) | managed-release (dedicated source-pinned release installer) | pin (no binary probe — a version pin for a tool the kit invokes via npx). policy (managed only): track (upgrade toward latest, gated by `verified`) | present (install when missing, never upgrade); managed-release uses exact. `verified` = last kit-tested version — anything above it prompts before install (--yes auto-accepts; non-TTY declines and falls back to the pinned `verified` when pinnable). Supply-chain stance: every kit-driven install is pinned to `verified` or gated by it — 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\": \"contextual POSIX installer transport for RTK/Bun bootstrap\" },\n \"git\": { \"kind\": \"check\", \"note\": \"plugin marketplaces (claude/codex clone them) + kit checkout updates\" },\n \"node\": { \"kind\": \"check\", \"note\": \"hosts npm globals (agent-browser, LSP servers)\" },\n \"npm\": { \"kind\": \"check\", \"note\": \"npm-global installer\" },\n \"claude\": { \"kind\": \"check\", \"floor\": \"2.1.170\", \"note\": \"kit floor — `best` alias + Fable 5 need >=2.1.170 (mirrors settings minimumVersion)\" },\n \"codex\": { \"kind\": \"check\", \"note\": \"upstream-owned; standalone installer prints when missing\" },\n \"ffplay\": { \"kind\": \"check\", \"note\": \"Notification hook sound (previously unchecked)\" },\n \"bwrap\": { \"kind\": \"check\", \"os\": \"linux\", \"note\": \"Codex Linux sandbox runtime\" },\n \"intelephense\": { \"kind\": \"check\", \"verified\": \"1.18.5\", \"note\": \"php-lsp server binary; `verified` pins claude::sync_lsp_servers' npm install\" },\n \"typescript-language-server\": { \"kind\": \"check\", \"verified\": \"5.3.0\", \"note\": \"typescript-lsp server binary; `verified` pins claude::sync_lsp_servers' npm install\" },\n \"tsc\": { \"kind\": \"check\", \"verified\": \"6.0.3\", \"note\": \"typescript-lsp dependency (npm package `typescript`); `verified` pins claude::sync_lsp_servers' 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 \"rtk\": { \"kind\": \"managed\", \"policy\": \"track\", \"floor\": \"0.43.0\", \"verified\": \"0.43.0\", \"pinnable\": true,\n \"note\": \"PreToolUse hook — supply-chain review before unverified upgrades; installer honors RTK_VERSION=vX.Y.Z pin\" },\n \"bun\": { \"kind\": \"managed\", \"policy\": \"present\", \"verified\": \"1.3.14\", \"pinnable\": true,\n \"note\": \"runtime for effect-solutions + the docks-kit CLI; bootstrap installs the verified release (installer takes bun-vX.Y.Z); self-updates via `bun upgrade` when wanted\" },\n \"effect-solutions\": { \"kind\": \"managed\", \"policy\": \"track\", \"verified\": \"0.5.3\", \"pinnable\": true,\n \"note\": \"Effect docs CLI (bun global) — track keeps it self-upgrading, gated by the verified pin\" },\n \"agent-browser\": { \"kind\": \"managed\", \"policy\": \"track\", \"verified\": \"0.32.0\", \"pinnable\": true,\n \"note\": \"browser-automation CLI (npm global), gated by the verified pin; first install also downloads Chrome for Testing\" },\n \"session-relay\": { \"kind\": \"managed-release\", \"policy\": \"exact\", \"verified\": \"0.12.0\", \"repository\": \"DocksDocks/docks\", \"tag\": \"session-relay--v0.12.0\", \"plugin_id\": \"session-relay@docks\", \"plugin_version\": \"0.12.0\", \"install_path\": \"~/.local/bin/session-relay\",\n \"assets\": {\n \"x86_64-unknown-linux-musl\": \"ead7faead73ba5835879e4823bc4bca6b6d1003d8a9bcbdbea6cf9f266ce5b42\",\n \"aarch64-unknown-linux-musl\": \"d7171bbaa33c4da8b0a9e15f9bfe7a3fb31930a1fad95cc9f682f153369b421b\",\n \"x86_64-apple-darwin\": \"be12a6f782453d8cc90d98ab77f408f0e7c61b55f8ccdf36bf0584c8cec2f1d8\",\n \"aarch64-apple-darwin\": \"5022354025d0c639406cf8b027d824724d8366f74e2b778c37d705e7e9f53889\"\n } },\n \"skills-cli\": { \"kind\": \"pin\", \"verified\": \"1.5.15\",\n \"note\": \"the `skills` npm package the kit runs via `npx skills@<verified>` on every agents sync (universal-skill install/remove) — pinned, never @latest\" }\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\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\nDocks-workflow-models: {\"implementer\":{\"candidates\":[{\"company\":\"openai\",\"effort\":\"high\",\"model\":\"gpt-5.6-sol\",\"tool\":\"codex\"}],\"selector\":\"codex:gpt-5.6-sol@high\"},\"orchestrator\":{\"candidates\":[{\"company\":\"anthropic\",\"effort\":\"high\",\"model\":\"fable\",\"tool\":\"claude\"},{\"company\":\"anthropic\",\"effort\":\"xhigh\",\"model\":\"opus\",\"tool\":\"claude\"}],\"selector\":\"profile:claude-best\"},\"review\":{\"max_rounds\":3,\"minimum_score\":90},\"reviewer\":{\"candidates\":[{\"company\":\"openai\",\"effort\":\"high\",\"model\":\"gpt-5.6-sol\",\"tool\":\"codex\"}],\"selector\":\"codex:gpt-5.6-sol@high\"},\"schema\":1}\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.170\",\n \"model\": \"fable\",\n \"effortLevel\": \"high\",\n \"autoMemoryEnabled\": true,\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 \"WebFetch\",\n \"WebSearch\",\n \"Edit(./)\",\n \"Bash(git *)\",\n \"Bash(git add *)\",\n \"Bash(git commit *)\",\n \"Bash(git status *)\",\n \"Bash(git diff *)\",\n \"Bash(git log *)\",\n \"Bash(git branch *)\",\n \"Bash(git checkout *)\",\n \"Bash(git switch *)\",\n \"Bash(git stash *)\",\n \"Bash(git fetch *)\",\n \"Bash(git pull *)\",\n \"Bash(git tag *)\",\n \"Bash(git show *)\",\n \"Bash(git blame *)\",\n \"Bash(git worktree *)\",\n \"Bash(gh *)\",\n \"Bash(pnpm *)\",\n \"Bash(npm *)\",\n \"Bash(npx *)\",\n \"Bash(node *)\",\n \"Bash(docker *)\",\n \"Bash(docker-compose *)\",\n \"Bash(rtk *)\",\n \"Bash(ls *)\",\n \"Bash(cat *)\",\n \"Bash(find *)\",\n \"Bash(grep *)\",\n \"Bash(head *)\",\n \"Bash(tail *)\",\n \"Bash(wc *)\",\n \"Bash(sort *)\",\n \"Bash(uniq *)\",\n \"Bash(diff *)\",\n \"Bash(which *)\",\n \"Bash(pwd *)\",\n \"Bash(date *)\",\n \"Bash(mkdir *)\",\n \"Bash(basename *)\",\n \"Bash(dirname *)\",\n \"Bash(realpath *)\",\n \"Bash(jq *)\",\n \"Bash(curl *)\",\n \"Bash(tree *)\",\n \"Bash(sed *)\",\n \"Bash(awk *)\",\n \"Bash(cut *)\",\n \"Bash(tr *)\",\n \"Bash(tee *)\",\n \"Bash(echo *)\",\n \"Bash(printf *)\",\n \"Bash(env *)\",\n \"Bash(printenv *)\",\n \"Bash(uname *)\",\n \"Bash(file *)\",\n \"Bash(stat *)\",\n \"Bash(du *)\",\n \"Bash(id *)\",\n \"Bash(whoami *)\",\n \"Bash(php *)\",\n \"Bash(composer *)\",\n \"Bash(python3 *)\",\n \"Bash(python *)\",\n \"Bash(pip *)\",\n \"Bash(pip3 *)\"\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 ],\n \"ask\": [\n \"Bash(git clean *)\",\n \"Bash(docker volume rm *)\",\n \"Bash(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 \"PreToolUse\": [\n {\n \"matcher\": \"Bash\",\n \"hooks\": [\n {\n \"type\": \"command\",\n \"command\": \"rtk hook claude\"\n }\n ]\n }\n ],\n \"PostToolUseFailure\": [\n {\n \"matcher\": \"Bash\",\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 \"session-relay@docks\": true,\n \"effect-kit@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",
|
|
@@ -40,4 +40,4 @@ export const GENERATED_PAYLOAD_PATHS = [
|
|
|
40
40
|
"notification.mp3"
|
|
41
41
|
] as const
|
|
42
42
|
|
|
43
|
-
export const GENERATED_PAYLOAD_HASH = "
|
|
43
|
+
export const GENERATED_PAYLOAD_HASH = "a549edb9fd7a7c9a1684ea31bc3b252c66c358e45efe6e7eea9d7ce66dacf753"
|
package/docks-kit
CHANGED
|
@@ -11,17 +11,18 @@ REPO_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
|
11
11
|
BUN_PIN="1.3.14"
|
|
12
12
|
# END GENERATED BUN PIN
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
Linux-
|
|
17
|
-
|
|
18
|
-
Darwin-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
14
|
+
HOST="$(uname -s)-$(uname -m)"
|
|
15
|
+
case "$HOST" in
|
|
16
|
+
Linux-x86_64) KIT_BIN="docks-kit-linux-x64" ;;
|
|
17
|
+
Linux-aarch64) KIT_BIN="docks-kit-linux-arm64" ;;
|
|
18
|
+
Darwin-x86_64) KIT_BIN="docks-kit-darwin-x64" ;;
|
|
19
|
+
Darwin-arm64) KIT_BIN="docks-kit-darwin-arm64" ;;
|
|
20
|
+
*)
|
|
21
|
+
echo "[docks-kit] unsupported host $HOST; docks-kit supports only Linux and macOS on x64 or arm64." >&2
|
|
22
|
+
exit 1
|
|
23
|
+
;;
|
|
23
24
|
esac
|
|
24
|
-
if [[ -
|
|
25
|
+
if [[ -x "$REPO_DIR/cli/dist/$KIT_BIN" ]]; then
|
|
25
26
|
CHECKOUT_VERSION=""
|
|
26
27
|
while IFS= read -r line; do
|
|
27
28
|
if [[ "$line" =~ ^[[:space:]]*\"version\"[[:space:]]*:[[:space:]]*\"([^\"]+)\" ]]; then
|
package/package.json
CHANGED
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "docks-kit",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.10.0",
|
|
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",
|
|
7
|
+
"os": [
|
|
8
|
+
"linux",
|
|
9
|
+
"darwin"
|
|
10
|
+
],
|
|
7
11
|
"repository": {
|
|
8
12
|
"type": "git",
|
|
9
13
|
"url": "git+https://github.com/DocksDocks/public.git"
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
export function powerShellLiteral(value: string): string {
|
|
2
|
-
return `'${value.replaceAll("'", "''")}'`
|
|
3
|
-
}
|
|
4
|
-
|
|
5
|
-
export function encodePowerShellCommand(script: string): string {
|
|
6
|
-
return Buffer.from(script, "utf16le").toString("base64")
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
export function decodePowerShellCommand(encoded: string): string {
|
|
10
|
-
return Buffer.from(encoded, "base64").toString("utf16le")
|
|
11
|
-
}
|