docks-kit 0.19.0 → 0.19.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -2
- package/cli/docs/overview.md +1 -1
- package/cli/src/commands/docs.ts +8 -2
- package/cli/src/commands/toolchain.ts +9 -2
- package/cli/src/commands/update.ts +6 -6
- package/cli/src/engine-native/claudePluginPasses.ts +26 -11
- package/cli/src/engine-native/codexConfig.ts +6 -6
- package/cli/src/engine-native/codexPlugins.ts +6 -5
- package/cli/src/engine-native/codexToml.ts +10 -6
- package/cli/src/engine-native/modes.ts +7 -11
- package/cli/src/generated/sotPayload.ts +1 -1
- package/cli/src/main.ts +2 -2
- package/cli/src/manifests.ts +2 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -53,7 +53,7 @@ docks-kit toolchain [check|ensure <tool>|outdated] verified-version floors for
|
|
|
53
53
|
docks-kit status [--json] deployed-vs-SoT drift + toolchain + counts
|
|
54
54
|
docks-kit plugins list [--json] enabledPlugins tri-state vs installed
|
|
55
55
|
docks-kit skills list [--json] universal skills vs manifest
|
|
56
|
-
docks-kit docs [topic] self-documentation (
|
|
56
|
+
docks-kit docs [topic] self-documentation (11 topics)
|
|
57
57
|
--help --version --wizard --completions built-in
|
|
58
58
|
```
|
|
59
59
|
|
|
@@ -100,7 +100,8 @@ and a later flag-less sync reverts them. Full reference: `docks-kit docs flags`
|
|
|
100
100
|
the full doctor table. Bun is the one managed install and is pinned to its
|
|
101
101
|
verified version.
|
|
102
102
|
- **Model catalog** — enabled harnesses provide live Claude, Codex, or omp
|
|
103
|
-
model IDs; `SoT/models.json` supplies aliases, notes, and the
|
|
103
|
+
model IDs; `SoT/models.json` supplies Claude and Codex aliases, notes, and the
|
|
104
|
+
offline fallback. omp has no curated fallback list.
|
|
104
105
|
- **Claude runtime** — sync materializes three dependency-free Bun `.mjs`
|
|
105
106
|
programs for statusline, SessionStart, and Notification. Quota display uses
|
|
106
107
|
Claude's native `rate_limits`; there is no OAuth fetch, shared usage cache,
|
package/cli/docs/overview.md
CHANGED
|
@@ -13,7 +13,7 @@ AI-assisted dev environment on every machine.
|
|
|
13
13
|
| `SoT/.codex/` | Codex config (config.toml, rules, AGENTS.md, marketplace) |
|
|
14
14
|
| `SoT/.agents/` | Universal agent skills manifest (agentskills.io standard) |
|
|
15
15
|
| `SoT/.omp/` | omp config deployed to `~/.omp/agent/` (AGENTS.md, config.yml, models.yml, mcp.json) plus `intercom.json` for pi intercom |
|
|
16
|
-
| `SoT/models.json` |
|
|
16
|
+
| `SoT/models.json` | Curated Claude and Codex aliases, notes, and offline fallback for the live model lists (see `docks-kit docs models`) |
|
|
17
17
|
| `SoT/toolchain.json` | Verified-version floors for external tools (see `docks-kit docs toolchain`) |
|
|
18
18
|
| `cli/src/generated/sotPayload.ts` | Deterministic generated payload embedded in standalone/npm execution |
|
|
19
19
|
| `cli/src/engine-native/` | EngineNative mutation logic for sync/model/toolchain and the `docks-kit omp` session overlay |
|
package/cli/src/commands/docs.ts
CHANGED
|
@@ -24,8 +24,14 @@ const TOPICS: Record<string, { summary: string; body: string }> = {
|
|
|
24
24
|
summary: "Deploy-time modifiers and the flag-less-sync-reverts contract",
|
|
25
25
|
body: modifiers,
|
|
26
26
|
},
|
|
27
|
-
models: {
|
|
28
|
-
|
|
27
|
+
models: {
|
|
28
|
+
summary: "Live model lists, the curated overlay, validation, model get/set",
|
|
29
|
+
body: models,
|
|
30
|
+
},
|
|
31
|
+
toolchain: {
|
|
32
|
+
summary: "Verified-version floors, the doctor table, and the upstream outdated report",
|
|
33
|
+
body: toolchain,
|
|
34
|
+
},
|
|
29
35
|
plugins: { summary: "enabledPlugins tri-state + optional plugin opt-ins", body: plugins },
|
|
30
36
|
install: {
|
|
31
37
|
summary: "Install paths: repo checkout, bun add -g, POSIX/Windows installers",
|
|
@@ -29,10 +29,12 @@ export const toolchainCommand = Command.make(
|
|
|
29
29
|
Effect.gen(function* () {
|
|
30
30
|
const operation = Option.getOrElse(config.op, () => "check");
|
|
31
31
|
const flags = config.verbose ? ["--verbose"] : [];
|
|
32
|
+
const operand = Option.getOrUndefined(config.tool);
|
|
33
|
+
const words = operand === undefined ? [] : [operand];
|
|
32
34
|
|
|
33
35
|
switch (operation) {
|
|
34
36
|
case "check":
|
|
35
|
-
return yield* engine(["toolchain", "check", ...
|
|
37
|
+
return yield* engine(["toolchain", "check", ...words, ...flags]);
|
|
36
38
|
case "ensure": {
|
|
37
39
|
const t = Option.getOrUndefined(config.tool);
|
|
38
40
|
if (t === undefined || !MANAGED.includes(t)) {
|
|
@@ -41,7 +43,12 @@ export const toolchainCommand = Command.make(
|
|
|
41
43
|
return yield* engine(["toolchain", "ensure", t, ...flags]);
|
|
42
44
|
}
|
|
43
45
|
case "outdated":
|
|
44
|
-
return yield* engine([
|
|
46
|
+
return yield* engine([
|
|
47
|
+
"toolchain",
|
|
48
|
+
"outdated",
|
|
49
|
+
...words,
|
|
50
|
+
...(config.refresh ? ["--refresh"] : []),
|
|
51
|
+
]);
|
|
45
52
|
default:
|
|
46
53
|
return yield* bail(
|
|
47
54
|
`Unknown toolchain op '${operation}' (valid: check, ensure, outdated)`,
|
|
@@ -60,9 +60,8 @@ const capturePackageRoot: CapturePackageRoot = (command, args) => {
|
|
|
60
60
|
|
|
61
61
|
/**
|
|
62
62
|
* A Bun global home is `<root>/.bun/install/global/node_modules/<pkg>`. Windows
|
|
63
|
-
* reports that path with backslashes
|
|
64
|
-
*
|
|
65
|
-
* character and must never be read as a separator there.
|
|
63
|
+
* reports that path with backslashes and compares it without case sensitivity.
|
|
64
|
+
* On POSIX, a backslash remains a legal filename character, not a separator.
|
|
66
65
|
*/
|
|
67
66
|
export const packageManagerForHome = (
|
|
68
67
|
home: string,
|
|
@@ -70,13 +69,14 @@ export const packageManagerForHome = (
|
|
|
70
69
|
host: HostOs = hostOs(),
|
|
71
70
|
): PackageManager => {
|
|
72
71
|
const normalize = (value: string): string =>
|
|
73
|
-
host.id === "windows" ? value.replaceAll("\\", "/") : value;
|
|
72
|
+
host.id === "windows" ? value.replaceAll("\\", "/").toLowerCase() : value;
|
|
74
73
|
const normalizedHome = normalize(home);
|
|
75
74
|
const underEnvironmentRoot = (name: "BUN_INSTALL_GLOBAL_DIR" | "BUN_INSTALL"): boolean => {
|
|
76
75
|
const root = environment[name]?.trim();
|
|
77
76
|
if (root === undefined || root === "") return false;
|
|
78
|
-
const normalizedRoot = normalize(root);
|
|
79
|
-
|
|
77
|
+
const normalizedRoot = normalize(root).replace(/\/+$/, "") || "/";
|
|
78
|
+
const prefix = normalizedRoot.endsWith("/") ? normalizedRoot : `${normalizedRoot}/`;
|
|
79
|
+
return normalizedHome === normalizedRoot || normalizedHome.startsWith(prefix);
|
|
80
80
|
};
|
|
81
81
|
return normalizedHome.includes("/.bun/") ||
|
|
82
82
|
underEnvironmentRoot("BUN_INSTALL_GLOBAL_DIR") ||
|
|
@@ -19,6 +19,9 @@ import {
|
|
|
19
19
|
import { payloadText } from "../payload";
|
|
20
20
|
import type { JsonObject } from "./sharedTypes";
|
|
21
21
|
|
|
22
|
+
/** Claude Code's built-in marketplace: always present, never added or pruned by the kit. */
|
|
23
|
+
const OFFICIAL_MARKETPLACE = "claude-plugins-official";
|
|
24
|
+
|
|
22
25
|
export async function cli(
|
|
23
26
|
args: Array<string>,
|
|
24
27
|
): Promise<{ ok: boolean; out: string; detail: string }> {
|
|
@@ -113,20 +116,31 @@ export async function syncPlugins(ctx: Ctx, claudeDir: string): Promise<void> {
|
|
|
113
116
|
? repoObj["extraKnownMarketplaces"]
|
|
114
117
|
: {};
|
|
115
118
|
const sotPlugins = isObject(repoObj["enabledPlugins"]) ? repoObj["enabledPlugins"] : {};
|
|
116
|
-
|
|
117
|
-
// Pass 1 — add missing marketplaces (SoT insertion order, like to_entries).
|
|
118
|
-
let addedMp = 0;
|
|
119
|
-
let f1 = 0;
|
|
120
|
-
for (const [mpName, mpValue] of Object.entries(sotMarketplaces)) {
|
|
119
|
+
const knownMarketplace = (mpName: string): boolean => {
|
|
121
120
|
const known = readJsonFile(knownMarketplaces);
|
|
122
|
-
|
|
121
|
+
return (
|
|
123
122
|
known !== undefined &&
|
|
124
123
|
isObject(known) &&
|
|
125
124
|
known[mpName] !== undefined &&
|
|
126
125
|
known[mpName] !== null &&
|
|
127
126
|
known[mpName] !== false
|
|
128
|
-
)
|
|
129
|
-
|
|
127
|
+
);
|
|
128
|
+
};
|
|
129
|
+
const addedMarketplaces = new Set<string>();
|
|
130
|
+
// The official marketplace is built in; a successful add is usable before its inventory is written.
|
|
131
|
+
// A missing inventory proves absence (fresh home); an unreadable one does not, so refresh as before.
|
|
132
|
+
const marketplaceReady = (mpName: string): boolean => {
|
|
133
|
+
if (mpName === OFFICIAL_MARKETPLACE || addedMarketplaces.has(mpName)) return true;
|
|
134
|
+
if (!existsSync(knownMarketplaces)) return false;
|
|
135
|
+
const known = readJsonFile(knownMarketplaces);
|
|
136
|
+
return known === undefined || !isObject(known) || knownMarketplace(mpName);
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
// Pass 1 — add missing marketplaces (SoT insertion order, like to_entries).
|
|
140
|
+
let addedMp = 0;
|
|
141
|
+
let f1 = 0;
|
|
142
|
+
for (const [mpName, mpValue] of Object.entries(sotMarketplaces)) {
|
|
143
|
+
if (knownMarketplace(mpName)) continue;
|
|
130
144
|
const repo =
|
|
131
145
|
isObject(mpValue) && isObject(mpValue["source"])
|
|
132
146
|
? String((mpValue["source"] as JsonObject)["repo"] ?? "")
|
|
@@ -136,6 +150,7 @@ export async function syncPlugins(ctx: Ctx, claudeDir: string): Promise<void> {
|
|
|
136
150
|
clearProgress();
|
|
137
151
|
if (marketplaceResult.ok) {
|
|
138
152
|
addedMp++;
|
|
153
|
+
addedMarketplaces.add(mpName);
|
|
139
154
|
} else {
|
|
140
155
|
recordFailure(
|
|
141
156
|
ctx,
|
|
@@ -156,7 +171,7 @@ export async function syncPlugins(ctx: Ctx, claudeDir: string): Promise<void> {
|
|
|
156
171
|
if (pluginUserScopeInstalled(installedPlugins, pluginId)) continue;
|
|
157
172
|
const separator = pluginId.lastIndexOf("@");
|
|
158
173
|
const mpName = separator > 0 ? pluginId.slice(separator + 1) : "";
|
|
159
|
-
if (mpName !== "" && !refreshedMarketplaces.has(mpName)) {
|
|
174
|
+
if (mpName !== "" && marketplaceReady(mpName) && !refreshedMarketplaces.has(mpName)) {
|
|
160
175
|
progress(`Refreshing marketplace ${mpName}...`);
|
|
161
176
|
const refreshResult = await cli(["plugin", "marketplace", "update", mpName]);
|
|
162
177
|
clearProgress();
|
|
@@ -207,7 +222,7 @@ export async function syncPlugins(ctx: Ctx, claudeDir: string): Promise<void> {
|
|
|
207
222
|
// would duplicate one failure in the ledger and in the failed-operation count.
|
|
208
223
|
if (!ctx.skipPluginRefresh) {
|
|
209
224
|
for (const mpName of [...kitMarketplaces].sort(compareCodepoints)) {
|
|
210
|
-
if (refreshedMarketplaces.has(mpName)) continue;
|
|
225
|
+
if (refreshedMarketplaces.has(mpName) || !marketplaceReady(mpName)) continue;
|
|
211
226
|
progress(`Refreshing marketplace ${mpName}...`);
|
|
212
227
|
const refreshResult = await cli(["plugin", "marketplace", "update", mpName]);
|
|
213
228
|
clearProgress();
|
|
@@ -263,7 +278,7 @@ export async function syncPlugins(ctx: Ctx, claudeDir: string): Promise<void> {
|
|
|
263
278
|
// Pass 6 — prune-gated marketplace removal.
|
|
264
279
|
const known = readJsonFile(knownMarketplaces);
|
|
265
280
|
for (const mpName of sortedKeys(known)) {
|
|
266
|
-
if (mpName ===
|
|
281
|
+
if (mpName === OFFICIAL_MARKETPLACE) continue;
|
|
267
282
|
if (nonUserMarketplaces.has(mpName)) continue;
|
|
268
283
|
if (kitMarketplaces.has(mpName)) continue;
|
|
269
284
|
progress(`Removing marketplace ${mpName}...`);
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
import { copyFileSync, existsSync, readFileSync, renameSync, rmSync, writeFileSync } from "node:fs";
|
|
8
8
|
|
|
9
9
|
import { payloadDisplayPath } from "../payload";
|
|
10
|
-
import { mergeTableSettings, mergeTopLevelSettings } from "./codexToml";
|
|
10
|
+
import { isTomlHeaderLine, mergeTableSettings, mergeTopLevelSettings } from "./codexToml";
|
|
11
11
|
import type { Ctx } from "./index";
|
|
12
12
|
|
|
13
13
|
// --------------------------------------------------------------- config ----
|
|
@@ -68,14 +68,14 @@ function scrubDeprecatedFeaturesText(content: string): string {
|
|
|
68
68
|
let changed = false;
|
|
69
69
|
for (const line of lines) {
|
|
70
70
|
if (inFeatures) {
|
|
71
|
-
if (line
|
|
71
|
+
if (isTomlHeaderLine(line)) {
|
|
72
72
|
inFeatures = false;
|
|
73
73
|
if (keep) out += `${header}\n${body}`;
|
|
74
74
|
else changed = true;
|
|
75
75
|
out += `${line}\n`;
|
|
76
76
|
continue;
|
|
77
77
|
}
|
|
78
|
-
if (/^use_legacy_landlock[ \t]*=/.test(line)) {
|
|
78
|
+
if (/^[ \t]*use_legacy_landlock[ \t]*=/.test(line)) {
|
|
79
79
|
changed = true;
|
|
80
80
|
continue;
|
|
81
81
|
}
|
|
@@ -83,7 +83,7 @@ function scrubDeprecatedFeaturesText(content: string): string {
|
|
|
83
83
|
if (/[^ \t\f\v\r]/.test(line)) keep = true;
|
|
84
84
|
continue;
|
|
85
85
|
}
|
|
86
|
-
if (
|
|
86
|
+
if (/^[ \t]*\[features\][ \t]*$/.test(line)) {
|
|
87
87
|
inFeatures = true;
|
|
88
88
|
header = line;
|
|
89
89
|
body = "";
|
|
@@ -111,7 +111,7 @@ function scrubDeprecatedFeatures(ctx: Ctx, userConfig: string): void {
|
|
|
111
111
|
change("Codex: scrubbed deprecated [features].use_legacy_landlock");
|
|
112
112
|
}
|
|
113
113
|
|
|
114
|
-
export const PLUGIN_TABLE_HEADER =
|
|
114
|
+
export const PLUGIN_TABLE_HEADER = /^[ \t]*\[plugins\."([^"]+)"\][ \t]*$/;
|
|
115
115
|
/** Plugin ids the kit retired; their deployed tables are stripped on every sync. */
|
|
116
116
|
const RETIRED_PLUGIN_IDS: Readonly<Record<string, true>> = {
|
|
117
117
|
"effect-kit@docks": true,
|
|
@@ -130,7 +130,7 @@ export function removeRetiredPluginTablesText(content: string): string {
|
|
|
130
130
|
skipping = RETIRED_PLUGIN_IDS[header[1]!] === true;
|
|
131
131
|
if (skipping) continue;
|
|
132
132
|
} else if (skipping) {
|
|
133
|
-
if (!line
|
|
133
|
+
if (!isTomlHeaderLine(line)) continue;
|
|
134
134
|
skipping = false;
|
|
135
135
|
}
|
|
136
136
|
out += `${line}\n`;
|
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
|
|
15
15
|
import { payloadDisplayPath } from "../payload";
|
|
16
16
|
import { PLUGIN_TABLE_HEADER } from "./codexConfig";
|
|
17
|
+
import { isTomlHeaderLine } from "./codexToml";
|
|
17
18
|
import { p, spawnProcess } from "./exec";
|
|
18
19
|
import { recordFailure } from "./failures";
|
|
19
20
|
import type { Ctx } from "./index";
|
|
@@ -107,20 +108,20 @@ function mergeMarketplace(repo: Json, user: Json): Json {
|
|
|
107
108
|
// -------------------------------------------------------------- plugins ----
|
|
108
109
|
|
|
109
110
|
/** codex::_marketplace_source — first `source =` inside [marketplaces.<name>]. */
|
|
110
|
-
function marketplaceSource(marketplace: string, configFile: string): string {
|
|
111
|
+
export function marketplaceSource(marketplace: string, configFile: string): string {
|
|
111
112
|
if (!existsSync(configFile)) return "";
|
|
112
113
|
let inMarketplace = false;
|
|
113
114
|
for (const line of readFileSync(configFile, "utf8").split("\n")) {
|
|
114
|
-
if (line === `[marketplaces.${marketplace}]`) {
|
|
115
|
+
if (line.trim() === `[marketplaces.${marketplace}]`) {
|
|
115
116
|
inMarketplace = true;
|
|
116
117
|
continue;
|
|
117
118
|
}
|
|
118
|
-
if (line
|
|
119
|
+
if (isTomlHeaderLine(line)) inMarketplace = false;
|
|
119
120
|
if (inMarketplace && /^[ \t]*source[ \t]*=/.test(line)) {
|
|
120
121
|
return line
|
|
121
122
|
.replace(/^[^=]+=[ \t]*/, "")
|
|
122
123
|
.replace(/[ \t]*#.*/, "")
|
|
123
|
-
.replace(/^"
|
|
124
|
+
.replace(/^(["'])(.*)\1$/, "$2");
|
|
124
125
|
}
|
|
125
126
|
}
|
|
126
127
|
return "";
|
|
@@ -175,7 +176,7 @@ function enabledPluginIdsFromText(configText: string): Array<string> {
|
|
|
175
176
|
enabled = false;
|
|
176
177
|
continue;
|
|
177
178
|
}
|
|
178
|
-
if (line
|
|
179
|
+
if (isTomlHeaderLine(line)) {
|
|
179
180
|
flush();
|
|
180
181
|
plugin = "";
|
|
181
182
|
enabled = false;
|
|
@@ -12,15 +12,19 @@ import { resolveEffort } from "../efforts";
|
|
|
12
12
|
import type { Ctx } from "./index";
|
|
13
13
|
import type { SettingEdit } from "./sharedTypes";
|
|
14
14
|
|
|
15
|
+
export function isTomlHeaderLine(line: string): boolean {
|
|
16
|
+
return /^[ \t]*\[/.test(line);
|
|
17
|
+
}
|
|
18
|
+
|
|
15
19
|
export function replaceTopLevelSetting(content: string, key: string, replacement: string): string {
|
|
16
20
|
const lines = content.split("\n");
|
|
17
21
|
if (lines[lines.length - 1] === "") lines.pop(); // awk records exclude a trailing empty split artifact
|
|
18
|
-
const keyRe = new RegExp(
|
|
22
|
+
const keyRe = new RegExp(`^[ \\t]*${key}[ \\t]*=`);
|
|
19
23
|
const out: Array<string> = [];
|
|
20
24
|
let inTable = false;
|
|
21
25
|
let replaced = false;
|
|
22
26
|
for (const line of lines) {
|
|
23
|
-
if (line
|
|
27
|
+
if (isTomlHeaderLine(line)) {
|
|
24
28
|
if (!replaced) {
|
|
25
29
|
out.push(replacement);
|
|
26
30
|
replaced = true;
|
|
@@ -113,11 +117,11 @@ export function syncCodexEffort(ctx: Ctx, effort: string): void {
|
|
|
113
117
|
|
|
114
118
|
export function mergeTopLevelSettings(sotConfigText: string, userConfig: string): void {
|
|
115
119
|
for (const line of sotConfigText.split("\n")) {
|
|
116
|
-
if (line
|
|
120
|
+
if (isTomlHeaderLine(line)) break;
|
|
117
121
|
if (/^[ \t]*($|#)/.test(line)) continue;
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
replaceTopLevelSettingInFile(userConfig,
|
|
122
|
+
const setting = /^[ \t]*([A-Za-z0-9_.-]+)[ \t]*=/.exec(line);
|
|
123
|
+
if (setting === null) continue;
|
|
124
|
+
replaceTopLevelSettingInFile(userConfig, setting[1]!, line);
|
|
121
125
|
}
|
|
122
126
|
}
|
|
123
127
|
|
|
@@ -14,6 +14,7 @@ import { printModels, validateClaudeModel, validateCodexModel } from "./models";
|
|
|
14
14
|
import { defaultLiveInputs, resolveCatalog } from "./liveModels";
|
|
15
15
|
import { bunBootstrap } from "./bun";
|
|
16
16
|
import { installedVersion, outdatedReport, present, report } from "./toolchain";
|
|
17
|
+
import { topLevelTomlString } from "../manifests";
|
|
17
18
|
|
|
18
19
|
export async function modeModel(ctx: Ctx, args: ReadonlyArray<string>): Promise<number> {
|
|
19
20
|
const { echo, err, warn } = ctx.services.logger;
|
|
@@ -59,8 +60,8 @@ export async function modeModel(ctx: Ctx, args: ReadonlyArray<string>): Promise<
|
|
|
59
60
|
err(`Failed to read ~/.codex/config.toml: ${String(result.error)}`);
|
|
60
61
|
return 1;
|
|
61
62
|
}
|
|
62
|
-
echo(`deployed: ${
|
|
63
|
-
echo(`SoT: ${
|
|
63
|
+
echo(`deployed: ${topLevelTomlString(result.data, "model") ?? ""}`);
|
|
64
|
+
echo(`SoT: ${topLevelTomlString(payloadText("SoT/.codex/config.toml"), "model") ?? ""}`);
|
|
64
65
|
}
|
|
65
66
|
printModels(ctx, catalog);
|
|
66
67
|
return 0;
|
|
@@ -112,15 +113,6 @@ function jsonModelText(text: string): string {
|
|
|
112
113
|
return typeof v === "string" ? v : JSON.stringify(v);
|
|
113
114
|
}
|
|
114
115
|
|
|
115
|
-
/** `awk -F'"' '/^model[[:space:]]*=/{print $2; exit}'`. */
|
|
116
|
-
|
|
117
|
-
function tomlModelText(text: string): string {
|
|
118
|
-
for (const line of text.split("\n")) {
|
|
119
|
-
if (/^model[ \t]*=/.test(line)) return line.split('"')[1] ?? "";
|
|
120
|
-
}
|
|
121
|
-
return "";
|
|
122
|
-
}
|
|
123
|
-
|
|
124
116
|
export async function modeToolchain(ctx: Ctx, args: ReadonlyArray<string>): Promise<number> {
|
|
125
117
|
const { echo, err, verbose } = ctx.services.logger;
|
|
126
118
|
const words = args.filter((arg) => !arg.startsWith("--"));
|
|
@@ -129,6 +121,10 @@ export async function modeToolchain(ctx: Ctx, args: ReadonlyArray<string>): Prom
|
|
|
129
121
|
for (const arg of args) {
|
|
130
122
|
if (arg === "--verbose") ctx.verbose = true;
|
|
131
123
|
}
|
|
124
|
+
if (words.length > (op === "ensure" ? 2 : 1)) {
|
|
125
|
+
err("Usage: toolchain [check|ensure <tool>|outdated [--refresh]]");
|
|
126
|
+
return 2;
|
|
127
|
+
}
|
|
132
128
|
|
|
133
129
|
if (op === "check") {
|
|
134
130
|
await report(ctx);
|
|
@@ -1,7 +1,7 @@
|
|
|
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.19.
|
|
4
|
+
export const GENERATED_PACKAGE_VERSION = "0.19.1"
|
|
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",
|
package/cli/src/main.ts
CHANGED
|
@@ -32,8 +32,8 @@ const root = Command.make("docks-kit", {}, () =>
|
|
|
32
32
|
" docks-kit update [--no-sync] self-update the kit, then sync",
|
|
33
33
|
);
|
|
34
34
|
yield* Console.log(" docks-kit model <claude|codex> [value] get/set the deployed model");
|
|
35
|
-
yield* Console.log(" docks-kit models [tool]
|
|
36
|
-
yield* Console.log(" docks-kit toolchain [check|ensure
|
|
35
|
+
yield* Console.log(" docks-kit models [tool] [--refresh] live model lists per harness");
|
|
36
|
+
yield* Console.log(" docks-kit toolchain [check|ensure|outdated] verified-version floors");
|
|
37
37
|
yield* Console.log(" docks-kit status deployed-vs-SoT doctor view");
|
|
38
38
|
yield* Console.log(" docks-kit plugins list plugin tri-state");
|
|
39
39
|
yield* Console.log(" docks-kit skills list universal skills");
|
package/cli/src/manifests.ts
CHANGED
|
@@ -23,8 +23,8 @@ export const deployedClaudeSettings = (): any | undefined => {
|
|
|
23
23
|
export function topLevelTomlString(text: string, setting: string): string | undefined {
|
|
24
24
|
for (const line of text.split(/\r?\n/)) {
|
|
25
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];
|
|
26
|
+
const assignment = line.match(/^\s*([A-Za-z0-9_-]+)\s*=\s*(?:"([^"]+)"|'([^']*)')/);
|
|
27
|
+
if (assignment?.[1] === setting) return assignment[2] ?? assignment[3];
|
|
28
28
|
}
|
|
29
29
|
return undefined;
|
|
30
30
|
}
|
package/package.json
CHANGED