docks-kit 0.17.1 → 0.17.3

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.
Files changed (87) hide show
  1. package/AGENTS.md +41 -33
  2. package/cli/docs/omp-context.md +104 -0
  3. package/cli/docs/omp-models.md +106 -56
  4. package/cli/src/argv.ts +38 -469
  5. package/cli/src/argvSurface.ts +371 -0
  6. package/cli/src/argvValidate.ts +122 -0
  7. package/cli/src/commands/docs.ts +62 -42
  8. package/cli/src/commands/harnesses.ts +38 -45
  9. package/cli/src/commands/model.ts +48 -53
  10. package/cli/src/commands/models.ts +24 -26
  11. package/cli/src/commands/omp.ts +132 -112
  12. package/cli/src/commands/plugins.ts +20 -20
  13. package/cli/src/commands/skills.ts +22 -20
  14. package/cli/src/commands/status.ts +87 -80
  15. package/cli/src/commands/sync.ts +113 -87
  16. package/cli/src/commands/toolchain.ts +21 -21
  17. package/cli/src/commands/update.ts +139 -117
  18. package/cli/src/efforts.ts +35 -34
  19. package/cli/src/engine-native/DESIGN.md +30 -30
  20. package/cli/src/engine-native/bun.ts +77 -55
  21. package/cli/src/engine-native/claudeLsp.ts +154 -0
  22. package/cli/src/engine-native/claudeOptionalPlugins.ts +113 -0
  23. package/cli/src/engine-native/claudePluginPasses.ts +348 -0
  24. package/cli/src/engine-native/claudeRemovals.ts +195 -0
  25. package/cli/src/engine-native/claudeRetired.ts +4 -4
  26. package/cli/src/engine-native/claudeRuntime.ts +94 -68
  27. package/cli/src/engine-native/claudeSettings.ts +174 -0
  28. package/cli/src/engine-native/claudeSettingsModifiers.ts +50 -54
  29. package/cli/src/engine-native/claudeSync.ts +176 -456
  30. package/cli/src/engine-native/codexConfig.ts +158 -0
  31. package/cli/src/engine-native/codexHooks.ts +91 -0
  32. package/cli/src/engine-native/codexPlugins.ts +290 -0
  33. package/cli/src/engine-native/codexStatus.ts +24 -0
  34. package/cli/src/engine-native/codexSync.ts +110 -568
  35. package/cli/src/engine-native/codexToml.ts +140 -138
  36. package/cli/src/engine-native/deps.ts +144 -107
  37. package/cli/src/engine-native/engineCtx.ts +126 -0
  38. package/cli/src/engine-native/exec.ts +98 -87
  39. package/cli/src/engine-native/failures.ts +3 -3
  40. package/cli/src/engine-native/harnesses.ts +62 -72
  41. package/cli/src/engine-native/index.ts +34 -315
  42. package/cli/src/engine-native/jq.ts +25 -24
  43. package/cli/src/engine-native/logger.ts +98 -100
  44. package/cli/src/engine-native/models.ts +50 -45
  45. package/cli/src/engine-native/modes.ts +83 -80
  46. package/cli/src/engine-native/ompFileDeploy.ts +111 -0
  47. package/cli/src/engine-native/ompMarketplace.ts +136 -0
  48. package/cli/src/engine-native/ompOverlay.ts +67 -64
  49. package/cli/src/engine-native/ompPaths.ts +49 -38
  50. package/cli/src/engine-native/ompPlugins.ts +170 -0
  51. package/cli/src/engine-native/ompRemovals.ts +159 -0
  52. package/cli/src/engine-native/ompSync.ts +69 -409
  53. package/cli/src/engine-native/ompYaml.ts +43 -41
  54. package/cli/src/engine-native/os/darwin.ts +5 -5
  55. package/cli/src/engine-native/os/index.ts +15 -15
  56. package/cli/src/engine-native/os/linux.ts +5 -5
  57. package/cli/src/engine-native/os/posix.ts +28 -24
  58. package/cli/src/engine-native/os/targets.ts +17 -17
  59. package/cli/src/engine-native/os/types.ts +31 -31
  60. package/cli/src/engine-native/os/windows.ts +61 -61
  61. package/cli/src/engine-native/parseArgs.ts +122 -372
  62. package/cli/src/engine-native/parseHelp.ts +66 -0
  63. package/cli/src/engine-native/parseModifiers.ts +222 -0
  64. package/cli/src/engine-native/services.ts +34 -34
  65. package/cli/src/engine-native/settings.ts +15 -15
  66. package/cli/src/engine-native/sharedTypes.d.ts +46 -0
  67. package/cli/src/engine-native/skillsInstall.ts +105 -0
  68. package/cli/src/engine-native/skillsLinks.ts +203 -0
  69. package/cli/src/engine-native/skillsManifest.ts +16 -0
  70. package/cli/src/engine-native/skillsPrune.ts +108 -0
  71. package/cli/src/engine-native/skillsSync.ts +28 -362
  72. package/cli/src/engine-native/syncConcurrency.ts +55 -0
  73. package/cli/src/engine-native/syncDispatch.ts +141 -0
  74. package/cli/src/engine-native/toolchain.ts +65 -62
  75. package/cli/src/engine.ts +65 -57
  76. package/cli/src/generated/sotPayload.ts +7 -7
  77. package/cli/src/kitHome.ts +44 -40
  78. package/cli/src/main.ts +63 -56
  79. package/cli/src/manifests.ts +53 -54
  80. package/cli/src/md.d.ts +2 -2
  81. package/cli/src/payload.ts +9 -9
  82. package/cli/src/services.ts +21 -13
  83. package/cli/tsconfig.json +4 -1
  84. package/docks-kit +13 -2
  85. package/docks-kit.ps1 +21 -6
  86. package/package.json +10 -2
  87. package/cli/src/engine-native/claudePlugins.ts +0 -510
@@ -1,41 +1,45 @@
1
- import { readFileSync } from "node:fs"
2
- import { dirname, join, resolve } from "node:path"
1
+ import { readFileSync } from "node:fs";
2
+ import { dirname, join, resolve } from "node:path";
3
3
 
4
4
  const isKitHome = (dir: string): boolean => {
5
5
  try {
6
- const manifest = JSON.parse(readFileSync(join(dir, "package.json"), "utf8")) as { name?: unknown }
7
- return manifest.name === "docks-kit"
6
+ const manifest = JSON.parse(readFileSync(join(dir, "package.json"), "utf8")) as {
7
+ name?: unknown;
8
+ };
9
+ return manifest.name === "docks-kit";
8
10
  } catch {
9
- return false
11
+ return false;
10
12
  }
11
- }
13
+ };
12
14
 
13
15
  const explicitKitHome = (dir: string, source: string): string => {
14
- const packageJson = join(dir, "package.json")
15
- let text: string
16
+ const packageJson = join(dir, "package.json");
17
+ let text: string;
16
18
  try {
17
- text = readFileSync(packageJson, "utf8")
19
+ text = readFileSync(packageJson, "utf8");
18
20
  } catch (error) {
19
21
  const code =
20
22
  typeof error === "object" && error !== null && "code" in error
21
23
  ? String(error.code)
22
- : undefined
24
+ : undefined;
23
25
  if (code === "ENOENT") {
24
- throw new Error(`DOCKS_KIT_HOME=${source} does not contain package.json`)
26
+ throw new Error(`DOCKS_KIT_HOME=${source} does not contain package.json`, { cause: error });
25
27
  }
26
- const detail = error instanceof Error ? error.message : String(error)
27
- const category = code === undefined ? "" : ` (${code})`
28
- throw new Error(
29
- `DOCKS_KIT_HOME=${source} package.json cannot be read${category}: ${detail}`
30
- )
28
+ const detail = error instanceof Error ? error.message : String(error);
29
+ const category = code === undefined ? "" : ` (${code})`;
30
+ throw new Error(`DOCKS_KIT_HOME=${source} package.json cannot be read${category}: ${detail}`, {
31
+ cause: error,
32
+ });
31
33
  }
32
34
 
33
- let manifest: unknown
35
+ let manifest: unknown;
34
36
  try {
35
- manifest = JSON.parse(text)
37
+ manifest = JSON.parse(text);
36
38
  } catch (error) {
37
- const detail = error instanceof Error ? error.message : String(error)
38
- throw new Error(`DOCKS_KIT_HOME=${source} package.json contains invalid JSON: ${detail}`)
39
+ const detail = error instanceof Error ? error.message : String(error);
40
+ throw new Error(`DOCKS_KIT_HOME=${source} package.json contains invalid JSON: ${detail}`, {
41
+ cause: error,
42
+ });
39
43
  }
40
44
 
41
45
  if (
@@ -45,29 +49,29 @@ const explicitKitHome = (dir: string, source: string): string => {
45
49
  manifest.name !== "docks-kit"
46
50
  ) {
47
51
  throw new Error(
48
- `DOCKS_KIT_HOME=${source} is not a docks-kit package root (package.json name must be "docks-kit")`
49
- )
52
+ `DOCKS_KIT_HOME=${source} is not a docks-kit package root (package.json name must be "docks-kit")`,
53
+ );
50
54
  }
51
- return dir
52
- }
55
+ return dir;
56
+ };
53
57
 
54
58
  const findKitHome = (start: string | undefined): string | undefined => {
55
- if (start === undefined || start === "") return undefined
56
- let dir = resolve(start)
59
+ if (start === undefined || start === "") return undefined;
60
+ let dir = resolve(start);
57
61
  for (;;) {
58
- if (isKitHome(dir)) return dir
59
- const parent = dirname(dir)
60
- if (parent === dir) return undefined
61
- dir = parent
62
+ if (isKitHome(dir)) return dir;
63
+ const parent = dirname(dir);
64
+ if (parent === dir) return undefined;
65
+ dir = parent;
62
66
  }
63
- }
67
+ };
64
68
 
65
69
  export interface KitHomeSources {
66
- readonly env: string | undefined
70
+ readonly env: string | undefined;
67
71
  /** `import.meta.dir` is a Bun extension, so a non-Bun loader leaves it undefined. */
68
- readonly moduleDir: string | undefined
69
- readonly execPath: string
70
- readonly cwd: string
72
+ readonly moduleDir: string | undefined;
73
+ readonly execPath: string;
74
+ readonly cwd: string;
71
75
  }
72
76
 
73
77
  /**
@@ -78,7 +82,7 @@ export interface KitHomeSources {
78
82
  */
79
83
  export const resolveKitHome = (sources: KitHomeSources): string => {
80
84
  if (sources.env !== undefined && sources.env !== "") {
81
- return explicitKitHome(resolve(sources.env), sources.env)
85
+ return explicitKitHome(resolve(sources.env), sources.env);
82
86
  }
83
87
 
84
88
  return (
@@ -86,13 +90,13 @@ export const resolveKitHome = (sources: KitHomeSources): string => {
86
90
  findKitHome(dirname(sources.execPath)) ??
87
91
  findKitHome(sources.cwd) ??
88
92
  dirname(sources.execPath)
89
- )
90
- }
93
+ );
94
+ };
91
95
 
92
96
  export const kitHome = (): string =>
93
97
  resolveKitHome({
94
98
  env: process.env["DOCKS_KIT_HOME"],
95
99
  moduleDir: import.meta.dir,
96
100
  execPath: process.execPath,
97
- cwd: process.cwd()
98
- })
101
+ cwd: process.cwd(),
102
+ });
package/cli/src/main.ts CHANGED
@@ -1,46 +1,53 @@
1
1
  #!/usr/bin/env bun
2
- import { Command, CliOutput } from "effect/unstable/cli"
3
- import { BunRuntime, BunServices } from "@effect/platform-bun"
4
- import { Console, Effect, Layer } from "effect"
5
- import { EngineServicesLive } from "./services"
6
- import { docsCommand } from "./commands/docs"
7
- import { harnessesCommand } from "./commands/harnesses"
8
- import { modelCommand } from "./commands/model"
9
- import { modelsCommand } from "./commands/models"
10
- import { ompCommand } from "./commands/omp"
11
- import { pluginsCommand } from "./commands/plugins"
12
- import { skillsCommand } from "./commands/skills"
13
- import { statusCommand } from "./commands/status"
14
- import { syncCommand } from "./commands/sync"
15
- import { toolchainCommand } from "./commands/toolchain"
16
- import { updateCommand } from "./commands/update"
17
- import { GENERATED_PACKAGE_VERSION } from "./generated/sotPayload"
18
- import { prepareArgv } from "./argv"
19
- import { runEngineNative } from "./engine-native"
20
-
2
+ import { Command, CliOutput } from "effect/unstable/cli";
3
+ import { BunRuntime, BunServices } from "@effect/platform-bun";
4
+ import { Console, Effect, Layer } from "effect";
5
+ import { EngineServicesLive } from "./services";
6
+ import { docsCommand } from "./commands/docs";
7
+ import { harnessesCommand } from "./commands/harnesses";
8
+ import { modelCommand } from "./commands/model";
9
+ import { modelsCommand } from "./commands/models";
10
+ import { ompCommand } from "./commands/omp";
11
+ import { pluginsCommand } from "./commands/plugins";
12
+ import { skillsCommand } from "./commands/skills";
13
+ import { statusCommand } from "./commands/status";
14
+ import { syncCommand } from "./commands/sync";
15
+ import { toolchainCommand } from "./commands/toolchain";
16
+ import { updateCommand } from "./commands/update";
17
+ import { GENERATED_PACKAGE_VERSION } from "./generated/sotPayload";
18
+ import { prepareArgv } from "./argv";
19
+ import { runEngineNative } from "./engine-native";
21
20
 
22
21
  const root = Command.make("docks-kit", {}, () =>
23
22
  Effect.gen(function* () {
24
- yield* Console.log("docks-kit — portable AI coding agent config kit")
25
- yield* Console.log("")
26
- yield* Console.log(" docks-kit sync [claude|codex|agents|omp] deploy the SoT to this machine")
27
- yield* Console.log(" docks-kit harnesses choose the flag-less sync selection")
28
- yield* Console.log(" docks-kit update [--no-sync] self-update the kit, then sync")
29
- yield* Console.log(" docks-kit model <claude|codex> [value] get/set the deployed model")
30
- yield* Console.log(" docks-kit models [tool] kit-verified model catalog")
31
- yield* Console.log(" docks-kit toolchain [check|ensure <tool>] verified-version floors")
32
- yield* Console.log(" docks-kit status deployed-vs-SoT doctor view")
33
- yield* Console.log(" docks-kit plugins list plugin tri-state")
34
- yield* Console.log(" docks-kit skills list universal skills")
35
- yield* Console.log(" docks-kit docs [topic] self-documentation")
36
- yield* Console.log(" docks-kit omp [--model|--pick] [...] free-model omp session")
37
- yield* Console.log("")
38
- yield* Console.log("Run 'docks-kit --help' for full option listings (also: --wizard, --completions).")
39
- yield* Console.log("No-Bun recovery path: use a platform release binary.")
40
- })
23
+ yield* Console.log("docks-kit — portable AI coding agent config kit");
24
+ yield* Console.log("");
25
+ yield* Console.log(
26
+ " docks-kit sync [claude|codex|agents|omp] deploy the SoT to this machine",
27
+ );
28
+ yield* Console.log(
29
+ " docks-kit harnesses choose the flag-less sync selection",
30
+ );
31
+ yield* Console.log(
32
+ " docks-kit update [--no-sync] self-update the kit, then sync",
33
+ );
34
+ yield* Console.log(" docks-kit model <claude|codex> [value] get/set the deployed model");
35
+ yield* Console.log(" docks-kit models [tool] kit-verified model catalog");
36
+ yield* Console.log(" docks-kit toolchain [check|ensure <tool>] verified-version floors");
37
+ yield* Console.log(" docks-kit status deployed-vs-SoT doctor view");
38
+ yield* Console.log(" docks-kit plugins list plugin tri-state");
39
+ yield* Console.log(" docks-kit skills list universal skills");
40
+ yield* Console.log(" docks-kit docs [topic] self-documentation");
41
+ yield* Console.log(" docks-kit omp [--model|--pick] [...] free-model omp session");
42
+ yield* Console.log("");
43
+ yield* Console.log(
44
+ "Run 'docks-kit --help' for full option listings (also: --wizard, --completions).",
45
+ );
46
+ yield* Console.log("No-Bun recovery path: use a platform release binary.");
47
+ }),
41
48
  ).pipe(
42
49
  Command.withDescription(
43
- "Portable AI coding agent config kit — SoT sync engine + helpers for Claude Code, Codex, and universal agent skills."
50
+ "Portable AI coding agent config kit — SoT sync engine + helpers for Claude Code, Codex, and universal agent skills.",
44
51
  ),
45
52
  Command.withSubcommands([
46
53
  syncCommand,
@@ -53,17 +60,17 @@ const root = Command.make("docks-kit", {}, () =>
53
60
  pluginsCommand,
54
61
  skillsCommand,
55
62
  docsCommand,
56
- ompCommand
57
- ])
58
- )
63
+ ompCommand,
64
+ ]),
65
+ );
59
66
 
60
67
  // v4's default formatter renders `name vversion`, but the `docks-kit` launcher
61
68
  // compares `--version` against the bare `package.json` version. The trailing
62
69
  // newline reproduces the characterized byte-for-byte output of the v3 CLI.
63
70
  const bareVersionFormatter: CliOutput.Formatter = {
64
71
  ...CliOutput.defaultFormatter(),
65
- formatVersion: (_name, version) => `${version}\n`
66
- }
72
+ formatVersion: (_name, version) => `${version}\n`,
73
+ };
67
74
 
68
75
  // Harness-private raw channel:
69
76
  // `DOCKS_KIT_ENGINE=native-raw` bypasses effect/unstable/cli and hands the raw engine
@@ -71,31 +78,31 @@ const bareVersionFormatter: CliOutput.Formatter = {
71
78
  // PUBLIC engine execution lives at the engine.ts seam after the CLI has
72
79
  // parsed/normalized pickers, --flag value forms, and non-engine commands.
73
80
  if (process.env["DOCKS_KIT_ENGINE"] === "native-raw") {
74
- const rawArgs = process.argv.slice(2)
75
- const rawOperation = rawArgs.join(" ") || "default"
81
+ const rawArgs = process.argv.slice(2);
82
+ const rawOperation = rawArgs.join(" ") || "default";
76
83
  try {
77
- process.exit(await runEngineNative(rawArgs))
84
+ process.exit(await runEngineNative(rawArgs));
78
85
  } catch (error) {
79
- let detail = "unknown error"
80
- if (error instanceof Error && error.message !== "") detail = error.message
81
- else if (typeof error === "string" && error !== "") detail = error
82
- process.stderr.write(`docks-kit raw operation '${rawOperation}' failed: ${detail}\n`)
83
- process.exit(1)
86
+ let detail = "unknown error";
87
+ if (error instanceof Error && error.message !== "") detail = error.message;
88
+ else if (typeof error === "string" && error !== "") detail = error;
89
+ process.stderr.write(`docks-kit raw operation '${rawOperation}' failed: ${detail}\n`);
90
+ process.exit(1);
84
91
  }
85
92
  }
86
93
 
87
94
  // Validate and normalize before parsing because the kit refuses to guess at unrecognized
88
95
  // or duplicated flags, and Effect 4 would otherwise negate `--no-<flag>` into a real
89
96
  // mutating run. This seam owns argument normalization.
90
- const prepared = prepareArgv(process.argv.slice(2))
97
+ const prepared = prepareArgv(process.argv.slice(2));
91
98
  if (prepared.kind === "reject") {
92
- process.stderr.write(`${prepared.message}\n`)
93
- process.exit(prepared.exitCode)
99
+ process.stderr.write(`${prepared.message}\n`);
100
+ process.exit(prepared.exitCode);
94
101
  }
95
102
 
96
103
  Command.runWith(root, { version: GENERATED_PACKAGE_VERSION })(prepared.args).pipe(
97
104
  Effect.provide(
98
- Layer.mergeAll(BunServices.layer, EngineServicesLive, CliOutput.layer(bareVersionFormatter))
105
+ Layer.mergeAll(BunServices.layer, EngineServicesLive, CliOutput.layer(bareVersionFormatter)),
99
106
  ),
100
- BunRuntime.runMain
101
- )
107
+ BunRuntime.runMain,
108
+ );
@@ -1,99 +1,98 @@
1
- import { existsSync, lstatSync, readFileSync, readdirSync, readlinkSync } from "node:fs"
2
- import { homedir } from "node:os"
3
- import { dirname, join, resolve } from "node:path"
4
- import { pluginUserScopeInstalled } from "./engine-native/claudePlugins"
5
- import { COPY_MARKER } from "./engine-native/skillsSync"
6
- import { payloadText } from "./payload"
1
+ import { existsSync, lstatSync, readFileSync, readdirSync, readlinkSync } from "node:fs";
2
+ import { homedir } from "node:os";
3
+ import { dirname, join, resolve } from "node:path";
4
+ import { pluginUserScopeInstalled } from "./engine-native/claudePluginPasses";
5
+ import { COPY_MARKER } from "./engine-native/skillsSync";
6
+ import { payloadText } from "./payload";
7
7
 
8
- export { homedir }
8
+ export { homedir };
9
9
 
10
- export type Tool = "claude" | "codex"
10
+ export type Tool = "claude" | "codex";
11
11
 
12
- const readJson = (path: string): any => JSON.parse(readFileSync(path, "utf8"))
13
- const readJsonText = (text: string): any => JSON.parse(text)
12
+ const readJson = (path: string): any => JSON.parse(readFileSync(path, "utf8"));
13
+ const readJsonText = (text: string): any => JSON.parse(text);
14
14
 
15
15
  /** SoT settings (claude) — model/effort/env for drift display. */
16
- export const sotClaudeSettings = (): any =>
17
- readJsonText(payloadText("SoT/.claude/settings.json"))
16
+ export const sotClaudeSettings = (): any => readJsonText(payloadText("SoT/.claude/settings.json"));
18
17
 
19
18
  export const deployedClaudeSettings = (): any | undefined => {
20
- const p = join(homedir(), ".claude", "settings.json")
21
- return existsSync(p) ? readJson(p) : undefined
22
- }
19
+ const p = join(homedir(), ".claude", "settings.json");
20
+ return existsSync(p) ? readJson(p) : undefined;
21
+ };
23
22
 
24
23
  export function topLevelTomlString(text: string, setting: string): string | undefined {
25
24
  for (const line of text.split(/\r?\n/)) {
26
- if (/^\s*\[/.test(line)) return undefined
27
- const assignment = line.match(/^\s*([A-Za-z0-9_-]+)\s*=\s*"([^"]+)"/)
28
- if (assignment?.[1] === setting) return assignment[2]
25
+ if (/^\s*\[/.test(line)) return undefined;
26
+ const assignment = line.match(/^\s*([A-Za-z0-9_-]+)\s*=\s*"([^"]+)"/);
27
+ if (assignment?.[1] === setting) return assignment[2];
29
28
  }
30
- return undefined
29
+ return undefined;
31
30
  }
32
31
 
33
32
  const tomlModel = (path: string): string | undefined => {
34
- if (!existsSync(path)) return undefined
35
- return topLevelTomlString(readFileSync(path, "utf8"), "model")
36
- }
33
+ if (!existsSync(path)) return undefined;
34
+ return topLevelTomlString(readFileSync(path, "utf8"), "model");
35
+ };
37
36
 
38
37
  export const sotCodexModel = (): string | undefined =>
39
- topLevelTomlString(payloadText("SoT/.codex/config.toml"), "model")
38
+ topLevelTomlString(payloadText("SoT/.codex/config.toml"), "model");
40
39
 
41
40
  export const deployedCodexModel = (): string | undefined =>
42
- tomlModel(join(homedir(), ".codex", "config.toml"))
41
+ tomlModel(join(homedir(), ".codex", "config.toml"));
43
42
 
44
43
  /** Installed plugins (user scope) vs SoT enabledPlugins tri-state. */
45
44
  export const pluginsView = (): Array<{
46
- plugin: string
47
- sot: "true" | "false" | "absent"
48
- installed: boolean
45
+ plugin: string;
46
+ sot: "true" | "false" | "absent";
47
+ installed: boolean;
49
48
  }> => {
50
- const sot: Record<string, boolean> = sotClaudeSettings().enabledPlugins ?? {}
51
- const installedPath = join(homedir(), ".claude", "plugins", "installed_plugins.json")
49
+ const sot: Record<string, boolean> = sotClaudeSettings().enabledPlugins ?? {};
50
+ const installedPath = join(homedir(), ".claude", "plugins", "installed_plugins.json");
52
51
  const installed: Record<string, unknown> = existsSync(installedPath)
53
- ? readJson(installedPath).plugins ?? {}
54
- : {}
55
- const names = new Set([...Object.keys(sot), ...Object.keys(installed)])
52
+ ? (readJson(installedPath).plugins ?? {})
53
+ : {};
54
+ const names = new Set([...Object.keys(sot), ...Object.keys(installed)]);
56
55
  return [...names].sort().map((plugin) => ({
57
56
  plugin,
58
57
  sot: plugin in sot ? (sot[plugin] ? "true" : "false") : "absent",
59
- installed: pluginUserScopeInstalled(installedPath, plugin)
60
- }))
61
- }
58
+ installed: pluginUserScopeInstalled(installedPath, plugin),
59
+ }));
60
+ };
62
61
 
63
62
  /** Universal skills: SoT manifest slugs vs ~/.agents/skills contents. */
64
63
  export const skillsView = (): Array<{
65
- skill: string
66
- declared: boolean
67
- installed: boolean
64
+ skill: string;
65
+ declared: boolean;
66
+ installed: boolean;
68
67
  /** Claude entry resolves to the canonical skill by symlink, junction, or kit-created copy. */
69
- claudeSymlink: boolean
68
+ claudeSymlink: boolean;
70
69
  }> => {
71
70
  const declared = payloadText("SoT/.agents/skills.txt")
72
71
  .split("\n")
73
72
  .map((l) => l.replace(/#.*$/, "").trim())
74
73
  .filter((l) => l.length > 0)
75
- .map((slug) => slug.split("/").pop() as string)
76
- const home = homedir()
77
- const skillsDir = join(home, ".agents", "skills")
74
+ .map((slug) => slug.split("/").pop() as string);
75
+ const home = homedir();
76
+ const skillsDir = join(home, ".agents", "skills");
78
77
  const installed = existsSync(skillsDir)
79
78
  ? readdirSync(skillsDir, { withFileTypes: true })
80
79
  .filter((e) => e.isDirectory())
81
80
  .map((e) => e.name)
82
- : []
83
- const names = new Set([...declared, ...installed])
81
+ : [];
82
+ const names = new Set([...declared, ...installed]);
84
83
  return [...names].sort().map((skill) => {
85
- const link = join(home, ".claude", "skills", skill)
86
- const canonical = resolve(skillsDir, skill)
87
- let claudeSymlink = false
84
+ const link = join(home, ".claude", "skills", skill);
85
+ const canonical = resolve(skillsDir, skill);
86
+ let claudeSymlink = false;
88
87
  try {
89
- const target = resolve(dirname(link), readlinkSync(link))
90
- claudeSymlink = target === canonical && existsSync(target)
88
+ const target = resolve(dirname(link), readlinkSync(link));
89
+ claudeSymlink = target === canonical && existsSync(target);
91
90
  } catch {
92
91
  /* not a symlink or missing */
93
92
  }
94
93
  if (!claudeSymlink && installed.includes(skill)) {
95
94
  try {
96
- claudeSymlink = lstatSync(link).isDirectory() && existsSync(join(link, COPY_MARKER))
95
+ claudeSymlink = lstatSync(link).isDirectory() && existsSync(join(link, COPY_MARKER));
97
96
  } catch {
98
97
  /* missing Claude entry */
99
98
  }
@@ -102,7 +101,7 @@ export const skillsView = (): Array<{
102
101
  skill,
103
102
  declared: declared.includes(skill),
104
103
  installed: installed.includes(skill),
105
- claudeSymlink
106
- }
107
- })
108
- }
104
+ claudeSymlink,
105
+ };
106
+ });
107
+ };
package/cli/src/md.d.ts CHANGED
@@ -1,4 +1,4 @@
1
1
  declare module "*.md" {
2
- const content: string
3
- export default content
2
+ const content: string;
3
+ export default content;
4
4
  }
@@ -1,25 +1,25 @@
1
1
  import {
2
2
  GENERATED_PAYLOAD_BASE64,
3
3
  GENERATED_PAYLOAD_PATHS,
4
- GENERATED_PAYLOAD_TEXT
5
- } from "./generated/sotPayload"
4
+ GENERATED_PAYLOAD_TEXT,
5
+ } from "./generated/sotPayload";
6
6
 
7
- export type PayloadPath = typeof GENERATED_PAYLOAD_PATHS[number]
8
- type TextPayloadPath = Exclude<PayloadPath, "notification.mp3">
7
+ export type PayloadPath = (typeof GENERATED_PAYLOAD_PATHS)[number];
8
+ type TextPayloadPath = Exclude<PayloadPath, "notification.mp3">;
9
9
 
10
10
  export function payloadText(path: TextPayloadPath): string {
11
- return GENERATED_PAYLOAD_TEXT[path]
11
+ return GENERATED_PAYLOAD_TEXT[path];
12
12
  }
13
13
 
14
14
  export function payloadBytes(path: PayloadPath): Buffer {
15
- if (path === "notification.mp3") return Buffer.from(GENERATED_PAYLOAD_BASE64[path], "base64")
16
- return Buffer.from(payloadText(path))
15
+ if (path === "notification.mp3") return Buffer.from(GENERATED_PAYLOAD_BASE64[path], "base64");
16
+ return Buffer.from(payloadText(path));
17
17
  }
18
18
 
19
19
  export function payloadPaths(prefix: string): ReadonlyArray<PayloadPath> {
20
- return GENERATED_PAYLOAD_PATHS.filter((path) => path.startsWith(prefix))
20
+ return GENERATED_PAYLOAD_PATHS.filter((path) => path.startsWith(prefix));
21
21
  }
22
22
 
23
23
  export function payloadDisplayPath(path: PayloadPath): string {
24
- return `embedded:${path}`
24
+ return `embedded:${path}`;
25
25
  }
@@ -4,9 +4,14 @@
4
4
  * named test-layer constructors. Composed ONCE at main.ts — command code
5
5
  * accesses services via `yield*`, never by re-providing.
6
6
  */
7
- import { Context, Layer } from "effect"
8
- import { makeLogger, type Logger, type LoggerSinks } from "./engine-native/logger"
9
- import { makeEngineServices, makePlatform, type DependencyManager, type Platform } from "./engine-native/services"
7
+ import { Context, Layer } from "effect";
8
+ import { makeLogger, type Logger, type LoggerSinks } from "./engine-native/logger";
9
+ import {
10
+ makeEngineServices,
11
+ makePlatform,
12
+ type DependencyManager,
13
+ type Platform,
14
+ } from "./engine-native/services";
10
15
 
11
16
  export class LoggerService extends Context.Service<LoggerService, Logger>()("docks-kit/Logger") {}
12
17
 
@@ -15,20 +20,23 @@ export class DependencyManagerService extends Context.Service<
15
20
  DependencyManager
16
21
  >()("docks-kit/DependencyManager") {}
17
22
 
18
- export class PlatformService extends Context.Service<PlatformService, Platform>()("docks-kit/Platform") {}
23
+ export class PlatformService extends Context.Service<PlatformService, Platform>()(
24
+ "docks-kit/Platform",
25
+ ) {}
19
26
 
20
- const live = makeEngineServices()
27
+ const live = makeEngineServices();
21
28
 
22
- export const LoggerLive = Layer.succeed(LoggerService, live.logger)
23
- export const DependencyManagerLive = Layer.succeed(DependencyManagerService, live.deps)
24
- export const PlatformLive = Layer.succeed(PlatformService, live.platform)
25
- export const EngineServicesLive = Layer.mergeAll(LoggerLive, DependencyManagerLive, PlatformLive)
29
+ export const LoggerLive = Layer.succeed(LoggerService, live.logger);
30
+ export const DependencyManagerLive = Layer.succeed(DependencyManagerService, live.deps);
31
+ export const PlatformLive = Layer.succeed(PlatformService, live.platform);
32
+ export const EngineServicesLive = Layer.mergeAll(LoggerLive, DependencyManagerLive, PlatformLive);
26
33
 
27
34
  export const LoggerTest = (sinks: LoggerSinks): Layer.Layer<LoggerService> =>
28
- Layer.succeed(LoggerService, makeLogger(sinks))
35
+ Layer.succeed(LoggerService, makeLogger(sinks));
29
36
 
30
37
  export const PlatformTest = (pf: NodeJS.Platform): Layer.Layer<PlatformService> =>
31
- Layer.succeed(PlatformService, makePlatform(pf))
38
+ Layer.succeed(PlatformService, makePlatform(pf));
32
39
 
33
- export const DependencyManagerTest = (impl: DependencyManager): Layer.Layer<DependencyManagerService> =>
34
- Layer.succeed(DependencyManagerService, impl)
40
+ export const DependencyManagerTest = (
41
+ impl: DependencyManager,
42
+ ): Layer.Layer<DependencyManagerService> => Layer.succeed(DependencyManagerService, impl);
package/cli/tsconfig.json CHANGED
@@ -6,9 +6,12 @@
6
6
  "types": ["bun"],
7
7
  "strict": true,
8
8
  "noEmit": true,
9
+ "allowJs": true,
10
+ "checkJs": true,
11
+ "allowImportingTsExtensions": true,
9
12
  "skipLibCheck": true,
10
13
  "forceConsistentCasingInFileNames": true,
11
14
  "verbatimModuleSyntax": true
12
15
  },
13
- "include": ["src/**/*.ts", "src/**/*.d.ts", "test/**/*.ts", "scripts/**/*.ts"]
16
+ "include": ["src/**/*.ts", "src/**/*.d.ts", "test/**/*.ts", "scripts/**/*.ts", "test/statusline-runtime-smoke.mjs", "../SoT/.claude/bin/*.mjs"]
14
17
  }
package/docks-kit CHANGED
@@ -37,9 +37,20 @@ if [[ -x "$REPO_DIR/cli/dist/$KIT_BIN" ]]; then
37
37
  BIN_VERSION="$("$REPO_DIR/cli/dist/$KIT_BIN" --version 2>/dev/null || true)"
38
38
  BIN_VERSION="${BIN_VERSION%$'\r'}"
39
39
  if [[ -n "$CHECKOUT_VERSION" && "$BIN_VERSION" == "$CHECKOUT_VERSION" ]]; then
40
- exec "$REPO_DIR/cli/dist/$KIT_BIN" "$@"
40
+ # A matching version does not prove matching content. Unreleased work does
41
+ # not move package.json, so a binary compiled before it still reports the
42
+ # checkout version and would silently shadow the source it predates.
43
+ NEWER_SOURCE=""
44
+ if [[ -d "$REPO_DIR/cli/src" ]]; then
45
+ NEWER_SOURCE="$(find "$REPO_DIR/cli/src" -type f -newer "$REPO_DIR/cli/dist/$KIT_BIN" -print -quit 2>/dev/null || true)"
46
+ fi
47
+ if [[ -z "$NEWER_SOURCE" ]]; then
48
+ exec "$REPO_DIR/cli/dist/$KIT_BIN" "$@"
49
+ fi
50
+ echo "[docks-kit] ignoring cli/dist/$KIT_BIN; ${NEWER_SOURCE#"$REPO_DIR/"} is newer than the binary — running from source; run 'bun run build:binaries' to refresh it or delete it" >&2
51
+ else
52
+ echo "[docks-kit] ignoring stale cli/dist/$KIT_BIN ${BIN_VERSION:-<unknown>}; checkout is ${CHECKOUT_VERSION:-<unknown>} — running from source; run 'bun run build:binaries' to refresh it or delete it" >&2
41
53
  fi
42
- echo "[docks-kit] ignoring stale cli/dist/$KIT_BIN ${BIN_VERSION:-<unknown>}; checkout is ${CHECKOUT_VERSION:-<unknown>} — running from source" >&2
43
54
  fi
44
55
 
45
56
  # Locate bun: PATH, then the known install locations that sit off the
package/docks-kit.ps1 CHANGED
@@ -56,13 +56,28 @@ if (Test-Path -LiteralPath $KitPath -PathType Leaf) {
56
56
  }
57
57
 
58
58
  if (-not [string]::IsNullOrEmpty($CheckoutVersion) -and $BinVersion -eq $CheckoutVersion) {
59
- & $KitPath @args
60
- exit $LASTEXITCODE
59
+ # A matching version does not prove matching content. Unreleased work does
60
+ # not move package.json, so a binary compiled before it still reports the
61
+ # checkout version and would silently shadow the source it predates.
62
+ $NewerSource = $null
63
+ $SrcDir = Join-Path $RepoDir 'cli\src'
64
+ if (Test-Path -LiteralPath $SrcDir -PathType Container) {
65
+ $BinWritten = (Get-Item -LiteralPath $KitPath).LastWriteTimeUtc
66
+ $NewerSource = Get-ChildItem -LiteralPath $SrcDir -Recurse -File -ErrorAction SilentlyContinue |
67
+ Where-Object { $_.LastWriteTimeUtc -gt $BinWritten } |
68
+ Select-Object -First 1
69
+ }
70
+ if ($null -eq $NewerSource) {
71
+ & $KitPath @args
72
+ exit $LASTEXITCODE
73
+ }
74
+ $NewerRelative = $NewerSource.FullName.Substring($RepoDir.Length).TrimStart('\', '/')
75
+ [Console]::Error.WriteLine("[docks-kit] ignoring cli/dist/$KitBin; $NewerRelative is newer than the binary — running from source; run 'bun run build:binaries' to refresh it or delete it")
76
+ } else {
77
+ $DisplayedBinVersion = if ([string]::IsNullOrEmpty($BinVersion)) { '<unknown>' } else { $BinVersion }
78
+ $DisplayedCheckoutVersion = if ([string]::IsNullOrEmpty($CheckoutVersion)) { '<unknown>' } else { $CheckoutVersion }
79
+ [Console]::Error.WriteLine("[docks-kit] ignoring stale cli/dist/$KitBin $DisplayedBinVersion; checkout is $DisplayedCheckoutVersion — running from source; run 'bun run build:binaries' to refresh it or delete it")
61
80
  }
62
-
63
- $DisplayedBinVersion = if ([string]::IsNullOrEmpty($BinVersion)) { '<unknown>' } else { $BinVersion }
64
- $DisplayedCheckoutVersion = if ([string]::IsNullOrEmpty($CheckoutVersion)) { '<unknown>' } else { $CheckoutVersion }
65
- [Console]::Error.WriteLine("[docks-kit] ignoring stale cli/dist/$KitBin $DisplayedBinVersion; checkout is $DisplayedCheckoutVersion — running from source")
66
81
  }
67
82
 
68
83
  function Find-Bun {