docks-kit 0.17.1 → 0.17.2
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 +36 -32
- package/cli/docs/omp-models.md +88 -53
- package/cli/src/argv.ts +38 -469
- package/cli/src/argvSurface.ts +371 -0
- package/cli/src/argvValidate.ts +122 -0
- package/cli/src/commands/docs.ts +57 -42
- package/cli/src/commands/harnesses.ts +38 -45
- package/cli/src/commands/model.ts +48 -53
- package/cli/src/commands/models.ts +24 -26
- package/cli/src/commands/omp.ts +132 -112
- package/cli/src/commands/plugins.ts +20 -20
- package/cli/src/commands/skills.ts +22 -20
- package/cli/src/commands/status.ts +87 -80
- package/cli/src/commands/sync.ts +113 -87
- package/cli/src/commands/toolchain.ts +21 -21
- package/cli/src/commands/update.ts +139 -117
- package/cli/src/efforts.ts +35 -34
- package/cli/src/engine-native/DESIGN.md +30 -30
- package/cli/src/engine-native/bun.ts +77 -55
- package/cli/src/engine-native/claudeLsp.ts +154 -0
- package/cli/src/engine-native/claudeOptionalPlugins.ts +113 -0
- package/cli/src/engine-native/claudePluginPasses.ts +348 -0
- package/cli/src/engine-native/claudeRemovals.ts +195 -0
- package/cli/src/engine-native/claudeRetired.ts +4 -4
- package/cli/src/engine-native/claudeRuntime.ts +94 -68
- package/cli/src/engine-native/claudeSettings.ts +174 -0
- package/cli/src/engine-native/claudeSettingsModifiers.ts +50 -54
- package/cli/src/engine-native/claudeSync.ts +176 -456
- package/cli/src/engine-native/codexConfig.ts +158 -0
- package/cli/src/engine-native/codexHooks.ts +91 -0
- package/cli/src/engine-native/codexPlugins.ts +290 -0
- package/cli/src/engine-native/codexStatus.ts +24 -0
- package/cli/src/engine-native/codexSync.ts +110 -568
- package/cli/src/engine-native/codexToml.ts +140 -138
- package/cli/src/engine-native/deps.ts +144 -107
- package/cli/src/engine-native/engineCtx.ts +126 -0
- package/cli/src/engine-native/exec.ts +98 -87
- package/cli/src/engine-native/failures.ts +3 -3
- package/cli/src/engine-native/harnesses.ts +62 -72
- package/cli/src/engine-native/index.ts +34 -315
- package/cli/src/engine-native/jq.ts +25 -24
- package/cli/src/engine-native/logger.ts +98 -100
- package/cli/src/engine-native/models.ts +50 -45
- package/cli/src/engine-native/modes.ts +83 -80
- package/cli/src/engine-native/ompFileDeploy.ts +111 -0
- package/cli/src/engine-native/ompMarketplace.ts +136 -0
- package/cli/src/engine-native/ompOverlay.ts +67 -64
- package/cli/src/engine-native/ompPaths.ts +49 -38
- package/cli/src/engine-native/ompPlugins.ts +170 -0
- package/cli/src/engine-native/ompSync.ts +64 -408
- package/cli/src/engine-native/ompYaml.ts +43 -41
- package/cli/src/engine-native/os/darwin.ts +5 -5
- package/cli/src/engine-native/os/index.ts +15 -15
- package/cli/src/engine-native/os/linux.ts +5 -5
- package/cli/src/engine-native/os/posix.ts +28 -24
- package/cli/src/engine-native/os/targets.ts +17 -17
- package/cli/src/engine-native/os/types.ts +31 -31
- package/cli/src/engine-native/os/windows.ts +61 -61
- package/cli/src/engine-native/parseArgs.ts +122 -372
- package/cli/src/engine-native/parseHelp.ts +66 -0
- package/cli/src/engine-native/parseModifiers.ts +222 -0
- package/cli/src/engine-native/services.ts +34 -34
- package/cli/src/engine-native/settings.ts +15 -15
- package/cli/src/engine-native/sharedTypes.d.ts +46 -0
- package/cli/src/engine-native/skillsInstall.ts +105 -0
- package/cli/src/engine-native/skillsLinks.ts +203 -0
- package/cli/src/engine-native/skillsManifest.ts +16 -0
- package/cli/src/engine-native/skillsPrune.ts +108 -0
- package/cli/src/engine-native/skillsSync.ts +28 -362
- package/cli/src/engine-native/syncConcurrency.ts +55 -0
- package/cli/src/engine-native/syncDispatch.ts +141 -0
- package/cli/src/engine-native/toolchain.ts +65 -62
- package/cli/src/engine.ts +65 -57
- package/cli/src/generated/sotPayload.ts +7 -7
- package/cli/src/kitHome.ts +44 -40
- package/cli/src/main.ts +63 -56
- package/cli/src/manifests.ts +53 -54
- package/cli/src/md.d.ts +2 -2
- package/cli/src/payload.ts +9 -9
- package/cli/src/services.ts +21 -13
- package/cli/tsconfig.json +4 -1
- package/package.json +9 -2
- package/cli/src/engine-native/claudePlugins.ts +0 -510
package/cli/src/commands/omp.ts
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
import { Argument, Command, Flag, Prompt } from "effect/unstable/cli"
|
|
2
|
-
import { Console, Effect, Option } from "effect"
|
|
3
|
-
import { chmodSync, mkdirSync, writeFileSync } from "node:fs"
|
|
4
|
-
import { bail } from "../engine"
|
|
5
|
-
import { capture, p, spawnHost, which } from "../engine-native/exec"
|
|
1
|
+
import { Argument, Command, Flag, Prompt } from "effect/unstable/cli";
|
|
2
|
+
import { Console, Effect, Option } from "effect";
|
|
3
|
+
import { chmodSync, mkdirSync, writeFileSync } from "node:fs";
|
|
4
|
+
import { bail } from "../engine";
|
|
5
|
+
import { capture, p, spawnHost, which } from "../engine-native/exec";
|
|
6
6
|
import {
|
|
7
7
|
DEFAULT_OMP_SESSION_MODEL,
|
|
8
8
|
engineHome,
|
|
9
9
|
readOmpSessionModel,
|
|
10
10
|
writeOmpSessionModel,
|
|
11
|
-
type OmpSessionModel
|
|
12
|
-
} from "../engine-native/harnesses"
|
|
11
|
+
type OmpSessionModel,
|
|
12
|
+
} from "../engine-native/harnesses";
|
|
13
13
|
import {
|
|
14
14
|
advisorLevelFor,
|
|
15
15
|
advisorRecommendation,
|
|
@@ -19,25 +19,25 @@ import {
|
|
|
19
19
|
planEffortChoice,
|
|
20
20
|
parseFreeModels,
|
|
21
21
|
renderFreeOverlay,
|
|
22
|
-
type CatalogModel
|
|
23
|
-
} from "../engine-native/ompOverlay"
|
|
22
|
+
type CatalogModel,
|
|
23
|
+
} from "../engine-native/ompOverlay";
|
|
24
24
|
|
|
25
25
|
const model = Flag.String("model").pipe(
|
|
26
26
|
Flag.withDescription("Free model selector to use and remember for omp sessions"),
|
|
27
|
-
Flag.optional
|
|
28
|
-
)
|
|
27
|
+
Flag.optional,
|
|
28
|
+
);
|
|
29
29
|
const pick = Flag.Boolean("pick").pipe(
|
|
30
30
|
Flag.withDescription("Choose the free model interactively"),
|
|
31
|
-
Flag.withDefault(false)
|
|
32
|
-
)
|
|
31
|
+
Flag.withDefault(false),
|
|
32
|
+
);
|
|
33
33
|
const args: Argument.Argument<ReadonlyArray<string>> = Argument.String("args").pipe(
|
|
34
34
|
Argument.withDescription("Arguments forwarded verbatim to omp"),
|
|
35
|
-
Argument.variadic()
|
|
36
|
-
)
|
|
35
|
+
Argument.variadic(),
|
|
36
|
+
);
|
|
37
37
|
|
|
38
38
|
export type FreeSelectorResolution =
|
|
39
39
|
| { readonly ok: true; readonly model: CatalogModel }
|
|
40
|
-
| { readonly ok: false; readonly message: string }
|
|
40
|
+
| { readonly ok: false; readonly message: string };
|
|
41
41
|
|
|
42
42
|
// Pure selector matching so the picker rule stays testable without spawning omp.
|
|
43
43
|
// An exact selector always wins; otherwise one case-insensitive substring hit
|
|
@@ -45,53 +45,55 @@ export type FreeSelectorResolution =
|
|
|
45
45
|
// to mistype and the catalog is small enough to disambiguate safely.
|
|
46
46
|
export function resolveFreeSelector(
|
|
47
47
|
input: string,
|
|
48
|
-
free: ReadonlyArray<CatalogModel
|
|
48
|
+
free: ReadonlyArray<CatalogModel>,
|
|
49
49
|
): FreeSelectorResolution {
|
|
50
|
-
const trimmed = input.trim()
|
|
50
|
+
const trimmed = input.trim();
|
|
51
51
|
if (trimmed === "") {
|
|
52
|
-
return { ok: false, message: "Model selector must not be empty or blank" }
|
|
52
|
+
return { ok: false, message: "Model selector must not be empty or blank" };
|
|
53
53
|
}
|
|
54
|
-
const exact = free.find((candidate) => candidate.selector === trimmed)
|
|
55
|
-
if (exact !== undefined) return { ok: true, model: exact }
|
|
56
|
-
const lowered = trimmed.toLowerCase()
|
|
57
|
-
const matches = free.filter((candidate) => candidate.selector.toLowerCase().includes(lowered))
|
|
58
|
-
const list = free.map((candidate) => candidate.selector).join(", ")
|
|
54
|
+
const exact = free.find((candidate) => candidate.selector === trimmed);
|
|
55
|
+
if (exact !== undefined) return { ok: true, model: exact };
|
|
56
|
+
const lowered = trimmed.toLowerCase();
|
|
57
|
+
const matches = free.filter((candidate) => candidate.selector.toLowerCase().includes(lowered));
|
|
58
|
+
const list = free.map((candidate) => candidate.selector).join(", ");
|
|
59
59
|
if (matches.length === 0) {
|
|
60
60
|
return {
|
|
61
61
|
ok: false,
|
|
62
|
-
message: `Model '${trimmed}' is not in the free catalog. Free models: ${list}
|
|
63
|
-
}
|
|
62
|
+
message: `Model '${trimmed}' is not in the free catalog. Free models: ${list}`,
|
|
63
|
+
};
|
|
64
64
|
}
|
|
65
|
-
const only = matches[0]
|
|
66
|
-
if (matches.length === 1 && only !== undefined) return { ok: true, model: only }
|
|
65
|
+
const only = matches[0];
|
|
66
|
+
if (matches.length === 1 && only !== undefined) return { ok: true, model: only };
|
|
67
67
|
return {
|
|
68
68
|
ok: false,
|
|
69
|
-
message: `Model '${trimmed}' is ambiguous; matches: ${matches.map((candidate) => candidate.selector).join(", ")}. Pass the exact selector
|
|
70
|
-
}
|
|
69
|
+
message: `Model '${trimmed}' is ambiguous; matches: ${matches.map((candidate) => candidate.selector).join(", ")}. Pass the exact selector.`,
|
|
70
|
+
};
|
|
71
71
|
}
|
|
72
72
|
|
|
73
73
|
const loadFreeCatalog = (omp: string): Effect.Effect<ReadonlyArray<CatalogModel>> =>
|
|
74
74
|
Effect.gen(function* () {
|
|
75
|
-
const raw = yield* Effect.promise(() => capture(omp, ["models", "--json"]))
|
|
75
|
+
const raw = yield* Effect.promise(() => capture(omp, ["models", "--json"]));
|
|
76
76
|
if (raw === "") {
|
|
77
77
|
return yield* bail(
|
|
78
|
-
"'omp models --json' returned no output; verify omp runs on this host, then retry"
|
|
79
|
-
)
|
|
78
|
+
"'omp models --json' returned no output; verify omp runs on this host, then retry",
|
|
79
|
+
);
|
|
80
80
|
}
|
|
81
|
-
let parsed: unknown
|
|
81
|
+
let parsed: unknown;
|
|
82
82
|
try {
|
|
83
|
-
parsed = JSON.parse(raw) as unknown
|
|
83
|
+
parsed = JSON.parse(raw) as unknown;
|
|
84
84
|
} catch {
|
|
85
85
|
return yield* bail(
|
|
86
|
-
"'omp models --json' returned output that is not JSON; verify the omp version, then retry"
|
|
87
|
-
)
|
|
86
|
+
"'omp models --json' returned output that is not JSON; verify the omp version, then retry",
|
|
87
|
+
);
|
|
88
88
|
}
|
|
89
|
-
const free = parseFreeModels(parsed)
|
|
89
|
+
const free = parseFreeModels(parsed);
|
|
90
90
|
if (free.length === 0) {
|
|
91
|
-
return yield* bail(
|
|
91
|
+
return yield* bail(
|
|
92
|
+
"'omp models --json' listed no free models; verify the omp login, then retry",
|
|
93
|
+
);
|
|
92
94
|
}
|
|
93
|
-
return free
|
|
94
|
-
})
|
|
95
|
+
return free;
|
|
96
|
+
});
|
|
95
97
|
|
|
96
98
|
/**
|
|
97
99
|
* Ask for the thinking levels of one model. Every choice comes from that
|
|
@@ -99,36 +101,46 @@ const loadFreeCatalog = (omp: string): Effect.Effect<ReadonlyArray<CatalogModel>
|
|
|
99
101
|
* publish. A model with no ladder and a model with one level are settled
|
|
100
102
|
* without a question.
|
|
101
103
|
*/
|
|
102
|
-
const pickLevels = (
|
|
104
|
+
const pickLevels = (catalogModel: CatalogModel) =>
|
|
103
105
|
Effect.gen(function* () {
|
|
104
|
-
const plan = planEffortChoice(
|
|
106
|
+
const plan = planEffortChoice(catalogModel.thinking);
|
|
105
107
|
if (plan.kind === "none") {
|
|
106
|
-
yield* Console.error(
|
|
107
|
-
|
|
108
|
+
yield* Console.error(
|
|
109
|
+
`${catalogModel.name} publishes no thinking levels; the model default applies`,
|
|
110
|
+
);
|
|
111
|
+
return { thinking: undefined, advisorThinking: undefined };
|
|
108
112
|
}
|
|
109
113
|
if (plan.kind === "fixed") {
|
|
110
|
-
yield* Console.error(`${
|
|
111
|
-
return { thinking: plan.level, advisorThinking: plan.level }
|
|
114
|
+
yield* Console.error(`${catalogModel.name} publishes one thinking level: ${plan.level}`);
|
|
115
|
+
return { thinking: plan.level, advisorThinking: plan.level };
|
|
112
116
|
}
|
|
113
117
|
const uniform = yield* Prompt.Select({
|
|
114
118
|
message: "Use the same thinking level for every role?",
|
|
115
119
|
choices: [
|
|
116
|
-
{
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
120
|
+
{
|
|
121
|
+
title: "Yes",
|
|
122
|
+
value: true,
|
|
123
|
+
description: `every role runs at ${plan.highest}, the highest this model offers`,
|
|
124
|
+
},
|
|
125
|
+
{ title: "No", value: false, description: "set the advisor apart from the other roles" },
|
|
126
|
+
],
|
|
127
|
+
});
|
|
128
|
+
if (uniform) return { thinking: plan.highest, advisorThinking: plan.highest };
|
|
121
129
|
const ladderChoices = plan.levels.map((level) => ({
|
|
122
130
|
title: level,
|
|
123
131
|
value: level,
|
|
124
132
|
description:
|
|
125
|
-
level === plan.highest
|
|
126
|
-
|
|
133
|
+
level === plan.highest
|
|
134
|
+
? "highest this model offers"
|
|
135
|
+
: level === plan.levels[0]
|
|
136
|
+
? "lowest this model offers"
|
|
137
|
+
: "",
|
|
138
|
+
}));
|
|
127
139
|
const thinking = yield* Prompt.Select({
|
|
128
140
|
message: "Thinking level for all roles except the advisor",
|
|
129
|
-
choices: ladderChoices
|
|
130
|
-
})
|
|
131
|
-
const suggested = advisorRecommendation(plan.levels, thinking)
|
|
141
|
+
choices: ladderChoices,
|
|
142
|
+
});
|
|
143
|
+
const suggested = advisorRecommendation(plan.levels, thinking);
|
|
132
144
|
// The list leads with the suggestion, because Prompt.Select always starts
|
|
133
145
|
// on the first entry and offers no initial index. The full ladder follows
|
|
134
146
|
// in ladder order, so a different level stays one keypress away. A
|
|
@@ -140,120 +152,128 @@ const pickLevels = (model: CatalogModel) =>
|
|
|
140
152
|
{
|
|
141
153
|
title: suggested,
|
|
142
154
|
value: suggested,
|
|
143
|
-
description: `recommended, two levels below ${thinking}
|
|
155
|
+
description: `recommended, two levels below ${thinking}`,
|
|
144
156
|
},
|
|
145
|
-
...ladderChoices
|
|
146
|
-
]
|
|
157
|
+
...ladderChoices,
|
|
158
|
+
];
|
|
147
159
|
const advisorThinking = yield* Prompt.Select({
|
|
148
160
|
message: "Thinking level for the advisor role",
|
|
149
|
-
choices: advisorChoices
|
|
150
|
-
})
|
|
151
|
-
return { thinking, advisorThinking }
|
|
152
|
-
})
|
|
161
|
+
choices: advisorChoices,
|
|
162
|
+
});
|
|
163
|
+
return { thinking, advisorThinking };
|
|
164
|
+
});
|
|
153
165
|
|
|
154
166
|
export const ompCommand = Command.make("omp", { model, pick, args }, (config) =>
|
|
155
167
|
Effect.gen(function* () {
|
|
156
168
|
if (Option.isSome(config.model) && config.pick) {
|
|
157
|
-
return yield* bail("Pass either --model <selector> or --pick, not both")
|
|
169
|
+
return yield* bail("Pass either --model <selector> or --pick, not both");
|
|
158
170
|
}
|
|
159
171
|
|
|
160
|
-
const omp = yield* Effect.sync(() => which("omp"))
|
|
172
|
+
const omp = yield* Effect.sync(() => which("omp"));
|
|
161
173
|
if (omp === "") {
|
|
162
|
-
return yield* bail("omp not found on PATH; install omp, then retry", 1)
|
|
174
|
+
return yield* bail("omp not found on PATH; install omp, then retry", 1);
|
|
163
175
|
}
|
|
164
176
|
|
|
165
|
-
const home = engineHome(process.env)
|
|
177
|
+
const home = engineHome(process.env);
|
|
166
178
|
if (Option.isSome(config.model)) {
|
|
167
|
-
const free = yield* loadFreeCatalog(omp)
|
|
168
|
-
const resolved = resolveFreeSelector(config.model.value, free)
|
|
169
|
-
if (!resolved.ok) return yield* bail(resolved.message)
|
|
179
|
+
const free = yield* loadFreeCatalog(omp);
|
|
180
|
+
const resolved = resolveFreeSelector(config.model.value, free);
|
|
181
|
+
if (!resolved.ok) return yield* bail(resolved.message);
|
|
170
182
|
// Compute both levels once from the catalog row so a plain launch
|
|
171
183
|
// never needs the catalog again.
|
|
172
|
-
const thinking = ladderCeiling(resolved.model.thinking)
|
|
173
|
-
const advisorThinking = advisorLevelFor(resolved.model.thinking)
|
|
184
|
+
const thinking = ladderCeiling(resolved.model.thinking);
|
|
185
|
+
const advisorThinking = advisorLevelFor(resolved.model.thinking);
|
|
174
186
|
yield* Effect.sync(() =>
|
|
175
|
-
writeOmpSessionModel(home, {
|
|
176
|
-
|
|
187
|
+
writeOmpSessionModel(home, {
|
|
188
|
+
selector: resolved.model.selector,
|
|
189
|
+
thinking,
|
|
190
|
+
advisorThinking,
|
|
191
|
+
}),
|
|
192
|
+
);
|
|
177
193
|
}
|
|
178
194
|
|
|
179
195
|
if (config.pick) {
|
|
180
196
|
// Check the terminal before the catalog spawn, so a non-interactive run
|
|
181
197
|
// fails with the actionable message instead of an omp subprocess first.
|
|
182
198
|
if (!process.stdin.isTTY || !process.stdout.isTTY) {
|
|
183
|
-
return yield* bail("Picking needs a terminal; pass --model <selector> instead")
|
|
199
|
+
return yield* bail("Picking needs a terminal; pass --model <selector> instead");
|
|
184
200
|
}
|
|
185
|
-
const free = yield* loadFreeCatalog(omp)
|
|
201
|
+
const free = yield* loadFreeCatalog(omp);
|
|
186
202
|
const chosen = yield* Prompt.Select({
|
|
187
203
|
message: "Choose the free model for this omp session",
|
|
188
204
|
choices: free.map((candidate) => {
|
|
189
|
-
const ceiling = ladderCeiling(candidate.thinking)
|
|
205
|
+
const ceiling = ladderCeiling(candidate.thinking);
|
|
190
206
|
return {
|
|
191
207
|
title: `${candidate.name} — ${candidate.selector}`,
|
|
192
208
|
value: candidate.selector,
|
|
193
209
|
description:
|
|
194
210
|
ceiling === undefined
|
|
195
211
|
? `model default thinking; ${candidate.contextWindow} context`
|
|
196
|
-
: `thinking to ${ceiling}; ${candidate.contextWindow} context
|
|
197
|
-
}
|
|
198
|
-
})
|
|
199
|
-
})
|
|
200
|
-
const match = free.find((candidate) => candidate.selector === chosen)
|
|
201
|
-
if (match === undefined) return yield* bail(`Model '${chosen}' is not in the free catalog`)
|
|
202
|
-
const chosenLevels = yield* pickLevels(match)
|
|
212
|
+
: `thinking to ${ceiling}; ${candidate.contextWindow} context`,
|
|
213
|
+
};
|
|
214
|
+
}),
|
|
215
|
+
});
|
|
216
|
+
const match = free.find((candidate) => candidate.selector === chosen);
|
|
217
|
+
if (match === undefined) return yield* bail(`Model '${chosen}' is not in the free catalog`);
|
|
218
|
+
const chosenLevels = yield* pickLevels(match);
|
|
203
219
|
yield* Effect.sync(() =>
|
|
204
220
|
writeOmpSessionModel(home, {
|
|
205
221
|
selector: match.selector,
|
|
206
222
|
thinking: chosenLevels.thinking,
|
|
207
|
-
advisorThinking: chosenLevels.advisorThinking
|
|
208
|
-
})
|
|
209
|
-
)
|
|
223
|
+
advisorThinking: chosenLevels.advisorThinking,
|
|
224
|
+
}),
|
|
225
|
+
);
|
|
210
226
|
}
|
|
211
227
|
|
|
212
|
-
const session: OmpSessionModel =
|
|
213
|
-
DEFAULT_OMP_SESSION_MODEL
|
|
228
|
+
const session: OmpSessionModel =
|
|
229
|
+
(yield* Effect.sync(() => readOmpSessionModel(home))) ?? DEFAULT_OMP_SESSION_MODEL;
|
|
214
230
|
|
|
215
231
|
// A stable cache path, not a temp file deleted at exit, because omp may
|
|
216
232
|
// re-read the overlay during a live session. The name carries the model,
|
|
217
233
|
// so a second launcher on another model cannot rewrite the file a running
|
|
218
234
|
// session still reads.
|
|
219
|
-
const directory = p(home, ".cache", "docks-kit")
|
|
220
|
-
const overlayPath = p(directory, overlayFileName(session.selector))
|
|
235
|
+
const directory = p(home, ".cache", "docks-kit");
|
|
236
|
+
const overlayPath = p(directory, overlayFileName(session.selector));
|
|
221
237
|
yield* Effect.sync(() => {
|
|
222
238
|
// `mode` applies only when mkdir creates the path, so an existing
|
|
223
239
|
// permissive cache directory keeps its mode until the chmod below.
|
|
224
|
-
mkdirSync(directory, { recursive: true, mode: 0o700 })
|
|
225
|
-
chmodSync(directory, 0o700)
|
|
226
|
-
writeFileSync(overlayPath, renderFreeOverlay(session), { mode: 0o600 })
|
|
227
|
-
chmodSync(overlayPath, 0o600)
|
|
228
|
-
})
|
|
240
|
+
mkdirSync(directory, { recursive: true, mode: 0o700 });
|
|
241
|
+
chmodSync(directory, 0o700);
|
|
242
|
+
writeFileSync(overlayPath, renderFreeOverlay(session), { mode: 0o600 });
|
|
243
|
+
chmodSync(overlayPath, 0o600);
|
|
244
|
+
});
|
|
229
245
|
|
|
230
246
|
// A level-free model states the model default so the line never shows
|
|
231
247
|
// an undefined level. The advisor appears only when it differs, because
|
|
232
248
|
// a uniform session has nothing extra to report.
|
|
233
|
-
const level =
|
|
249
|
+
const level =
|
|
250
|
+
typeof session.thinking === "string" && session.thinking.trim() !== ""
|
|
251
|
+
? session.thinking
|
|
252
|
+
: undefined;
|
|
234
253
|
const advisor =
|
|
235
254
|
typeof session.advisorThinking === "string" && session.advisorThinking.trim() !== ""
|
|
236
255
|
? session.advisorThinking
|
|
237
|
-
: undefined
|
|
238
|
-
const levelText =
|
|
239
|
-
|
|
256
|
+
: undefined;
|
|
257
|
+
const levelText =
|
|
258
|
+
level === undefined ? "the model default thinking level" : `${level} thinking`;
|
|
259
|
+
const advisorText = advisor !== undefined && advisor !== level ? `, advisor at ${advisor}` : "";
|
|
240
260
|
yield* Console.error(
|
|
241
|
-
`Starting omp with ${session.selector} at ${levelText}${advisorText} (session only; deployed config unchanged)
|
|
242
|
-
)
|
|
261
|
+
`Starting omp with ${session.selector} at ${levelText}${advisorText} (session only; deployed config unchanged)`,
|
|
262
|
+
);
|
|
243
263
|
|
|
244
264
|
const child = yield* Effect.sync(() =>
|
|
245
|
-
spawnHost("omp", buildOmpArgs(overlayPath, config.args), { stdio: "inherit" })
|
|
246
|
-
)
|
|
265
|
+
spawnHost("omp", buildOmpArgs(overlayPath, config.args), { stdio: "inherit" }),
|
|
266
|
+
);
|
|
247
267
|
if (child.error !== undefined) {
|
|
248
268
|
return yield* bail(
|
|
249
|
-
`Failed to start omp: ${child.error instanceof Error ? child.error.message : String(child.error)}
|
|
250
|
-
)
|
|
269
|
+
`Failed to start omp: ${child.error instanceof Error ? child.error.message : String(child.error)}`,
|
|
270
|
+
);
|
|
251
271
|
}
|
|
252
272
|
// A signalled child reports a null status, which must not look successful.
|
|
253
|
-
yield* Effect.sync(() => process.exit(child.status ?? 1))
|
|
254
|
-
})
|
|
273
|
+
yield* Effect.sync(() => process.exit(child.status ?? 1));
|
|
274
|
+
}),
|
|
255
275
|
).pipe(
|
|
256
276
|
Command.withDescription(
|
|
257
|
-
"Start omp with every model role overridden to the remembered free model for this run only (no deployed configuration is changed; --model or --pick remembers a new free default)."
|
|
258
|
-
)
|
|
259
|
-
)
|
|
277
|
+
"Start omp with every model role overridden to the remembered free model for this run only (no deployed configuration is changed; --model or --pick remembers a new free default).",
|
|
278
|
+
),
|
|
279
|
+
);
|
|
@@ -1,35 +1,35 @@
|
|
|
1
|
-
import { Argument, Command, Flag } from "effect/unstable/cli"
|
|
2
|
-
import { Console, Effect, Option } from "effect"
|
|
3
|
-
import { bail } from "../engine"
|
|
4
|
-
import { pluginsView } from "../manifests"
|
|
1
|
+
import { Argument, Command, Flag } from "effect/unstable/cli";
|
|
2
|
+
import { Console, Effect, Option } from "effect";
|
|
3
|
+
import { bail } from "../engine";
|
|
4
|
+
import { pluginsView } from "../manifests";
|
|
5
5
|
|
|
6
6
|
const action = Argument.String("action").pipe(
|
|
7
7
|
Argument.withDescription("list (default)"),
|
|
8
|
-
Argument.optional
|
|
9
|
-
)
|
|
8
|
+
Argument.optional,
|
|
9
|
+
);
|
|
10
10
|
const json = Flag.Boolean("json").pipe(
|
|
11
11
|
Flag.withDescription("Machine-readable output"),
|
|
12
|
-
Flag.withDefault(false)
|
|
13
|
-
)
|
|
12
|
+
Flag.withDefault(false),
|
|
13
|
+
);
|
|
14
14
|
|
|
15
15
|
export const pluginsCommand = Command.make("plugins", { action, json }, (config) =>
|
|
16
16
|
Effect.gen(function* () {
|
|
17
|
-
const act = Option.getOrElse(config.action, () => "list")
|
|
17
|
+
const act = Option.getOrElse(config.action, () => "list");
|
|
18
18
|
if (act !== "list") {
|
|
19
19
|
return yield* bail(
|
|
20
|
-
`Unknown plugins action '${act}' (valid: list). Install/removal runs through sync: --claude-plugin=<name> opts in, --prune reconciles
|
|
21
|
-
)
|
|
20
|
+
`Unknown plugins action '${act}' (valid: list). Install/removal runs through sync: --claude-plugin=<name> opts in, --prune reconciles.`,
|
|
21
|
+
);
|
|
22
22
|
}
|
|
23
|
-
const view = pluginsView()
|
|
23
|
+
const view = pluginsView();
|
|
24
24
|
if (config.json) {
|
|
25
|
-
return yield* Console.log(JSON.stringify(view, null, 2))
|
|
25
|
+
return yield* Console.log(JSON.stringify(view, null, 2));
|
|
26
26
|
}
|
|
27
|
-
yield* Console.log(
|
|
28
|
-
|
|
27
|
+
yield* Console.log(
|
|
28
|
+
"SoT tri-state: true = enabled everywhere, false = installed-but-disabled (per-project enable), absent = not kit-managed\n",
|
|
29
|
+
);
|
|
30
|
+
yield* Console.log(`${"PLUGIN".padEnd(44)} ${"SOT".padEnd(7)} INSTALLED`);
|
|
29
31
|
for (const p of view) {
|
|
30
|
-
yield* Console.log(`${p.plugin.padEnd(44)} ${p.sot.padEnd(7)} ${p.installed ? "yes" : "no"}`)
|
|
32
|
+
yield* Console.log(`${p.plugin.padEnd(44)} ${p.sot.padEnd(7)} ${p.installed ? "yes" : "no"}`);
|
|
31
33
|
}
|
|
32
|
-
})
|
|
33
|
-
).pipe(
|
|
34
|
-
Command.withDescription("Installed plugins vs SoT enabledPlugins tri-state (read-only).")
|
|
35
|
-
)
|
|
34
|
+
}),
|
|
35
|
+
).pipe(Command.withDescription("Installed plugins vs SoT enabledPlugins tri-state (read-only)."));
|
|
@@ -1,38 +1,40 @@
|
|
|
1
|
-
import { Argument, Command, Flag } from "effect/unstable/cli"
|
|
2
|
-
import { Console, Effect, Option } from "effect"
|
|
3
|
-
import { bail } from "../engine"
|
|
4
|
-
import { skillsView } from "../manifests"
|
|
1
|
+
import { Argument, Command, Flag } from "effect/unstable/cli";
|
|
2
|
+
import { Console, Effect, Option } from "effect";
|
|
3
|
+
import { bail } from "../engine";
|
|
4
|
+
import { skillsView } from "../manifests";
|
|
5
5
|
|
|
6
6
|
const action = Argument.String("action").pipe(
|
|
7
7
|
Argument.withDescription("list (default)"),
|
|
8
|
-
Argument.optional
|
|
9
|
-
)
|
|
8
|
+
Argument.optional,
|
|
9
|
+
);
|
|
10
10
|
const json = Flag.Boolean("json").pipe(
|
|
11
11
|
Flag.withDescription("Machine-readable output"),
|
|
12
|
-
Flag.withDefault(false)
|
|
13
|
-
)
|
|
12
|
+
Flag.withDefault(false),
|
|
13
|
+
);
|
|
14
14
|
|
|
15
15
|
export const skillsCommand = Command.make("skills", { action, json }, (config) =>
|
|
16
16
|
Effect.gen(function* () {
|
|
17
|
-
const act = Option.getOrElse(config.action, () => "list")
|
|
17
|
+
const act = Option.getOrElse(config.action, () => "list");
|
|
18
18
|
if (act !== "list") {
|
|
19
19
|
return yield* bail(
|
|
20
|
-
`Unknown skills action '${act}' (valid: list). Declare skills in SoT/.agents/skills.txt and run sync; --prune removes undeclared kit-managed ones
|
|
21
|
-
)
|
|
20
|
+
`Unknown skills action '${act}' (valid: list). Declare skills in SoT/.agents/skills.txt and run sync; --prune removes undeclared kit-managed ones.`,
|
|
21
|
+
);
|
|
22
22
|
}
|
|
23
|
-
const view = skillsView()
|
|
23
|
+
const view = skillsView();
|
|
24
24
|
if (config.json) {
|
|
25
|
-
return yield* Console.log(JSON.stringify(view, null, 2))
|
|
25
|
+
return yield* Console.log(JSON.stringify(view, null, 2));
|
|
26
26
|
}
|
|
27
|
-
yield* Console.log(
|
|
27
|
+
yield* Console.log(
|
|
28
|
+
`${"SKILL".padEnd(28)} ${"DECLARED".padEnd(9)} ${"INSTALLED".padEnd(10)} CLAUDE-SYMLINK`,
|
|
29
|
+
);
|
|
28
30
|
for (const s of view) {
|
|
29
31
|
yield* Console.log(
|
|
30
|
-
`${s.skill.padEnd(28)} ${(s.declared ? "yes" : "no").padEnd(9)} ${(s.installed ? "yes" : "no").padEnd(10)} ${s.claudeSymlink ? "ok" : "-"}
|
|
31
|
-
)
|
|
32
|
+
`${s.skill.padEnd(28)} ${(s.declared ? "yes" : "no").padEnd(9)} ${(s.installed ? "yes" : "no").padEnd(10)} ${s.claudeSymlink ? "ok" : "-"}`,
|
|
33
|
+
);
|
|
32
34
|
}
|
|
33
|
-
})
|
|
35
|
+
}),
|
|
34
36
|
).pipe(
|
|
35
37
|
Command.withDescription(
|
|
36
|
-
"Universal agent skills: SoT/.agents/skills.txt vs ~/.agents/skills + the ~/.claude/skills symlink health (read-only)."
|
|
37
|
-
)
|
|
38
|
-
)
|
|
38
|
+
"Universal agent skills: SoT/.agents/skills.txt vs ~/.agents/skills + the ~/.claude/skills symlink health (read-only).",
|
|
39
|
+
),
|
|
40
|
+
);
|