argsbarg 4.0.3 → 4.1.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/CHANGELOG.md +42 -1
- package/README.md +6 -6
- package/docs/ai-skills.md +9 -4
- package/docs/bundled-docs.md +1 -0
- package/docs/cli-program.md +4 -4
- package/docs/config-schema.md +1 -4
- package/docs/developing.md +1 -1
- package/docs/install.md +160 -39
- package/docs/mcp.md +38 -11
- package/docs/templates/cursor/rules/cli-program.mdc +1 -1
- package/examples/config-app/program.ts +0 -3
- package/examples/consumer-app/README.md +1 -1
- package/examples/consumer-app/src/program.ts +5 -13
- package/examples/mcp-test.ts +14 -24
- package/index.d.ts +60 -5
- package/package.json +1 -1
- package/src/builtins/builtins.test.ts +20 -4
- package/src/builtins/config.test.ts +31 -25
- package/src/builtins/config.ts +4 -3
- package/src/builtins/install.ts +50 -57
- package/src/builtins/mcp.ts +1 -1
- package/src/capabilities.ts +4 -4
- package/src/cli.ts +2 -0
- package/src/config/bootstrap.ts +167 -66
- package/src/config/context.test.ts +22 -36
- package/src/config/context.ts +5 -4
- package/src/config/file.test.ts +66 -56
- package/src/config/file.ts +33 -25
- package/src/config/resolve.test.ts +25 -1
- package/src/config/resolve.ts +43 -8
- package/src/config.integration.test.ts +17 -10
- package/src/docs/api-guide.test.ts +1 -1
- package/src/docs/docs.test.ts +1 -1
- package/src/docs/mcp-guide.ts +15 -4
- package/src/docs/mcp-resources.test.ts +63 -0
- package/src/docs/mcp-resources.ts +68 -0
- package/src/hidden-mcpb.test.ts +41 -1
- package/src/index.ts +4 -0
- package/src/install/{binary.ts → app.ts} +13 -13
- package/src/install/bootstrap.ts +22 -0
- package/src/install/detect-installed.ts +2 -97
- package/src/install/index.ts +187 -110
- package/src/install/install-validate.test.ts +61 -0
- package/src/install/install.test.ts +138 -42
- package/src/install/mcp-openclaw.test.ts +40 -0
- package/src/install/mcp-openclaw.ts +106 -0
- package/src/install/normalize.ts +35 -0
- package/src/install/paths.ts +27 -13
- package/src/install/plan.ts +30 -259
- package/src/install/shell.ts +2 -2
- package/src/install/status.test.ts +85 -0
- package/src/install/status.ts +22 -9
- package/src/install/target-base.ts +93 -0
- package/src/install/target-detect.ts +20 -0
- package/src/install/target-effective.ts +131 -0
- package/src/install/target-mcp-cli.ts +149 -0
- package/src/install/target-mcp-json.ts +130 -0
- package/src/install/target-plan-build.ts +67 -0
- package/src/install/target-registry.ts +57 -0
- package/src/install/target-scope.ts +266 -0
- package/src/install/target-skill.ts +104 -0
- package/src/install/target-types.ts +145 -0
- package/src/install/targets/app.ts +69 -0
- package/src/install/targets/chatgpt-mcp.ts +12 -0
- package/src/install/targets/claude-code-mcp.ts +15 -0
- package/src/install/targets/claude-desktop-mcp.ts +12 -0
- package/src/install/targets/claude-skill.ts +16 -0
- package/src/install/targets/codex-mcp.ts +25 -0
- package/src/install/targets/codex-skill.ts +14 -0
- package/src/install/targets/completions.ts +133 -0
- package/src/install/targets/configure.ts +59 -0
- package/src/install/targets/cursor-mcp.ts +15 -0
- package/src/install/targets/cursor-skill.ts +16 -0
- package/src/install/targets/index.ts +53 -0
- package/src/install/targets/openclaw-mcp.ts +25 -0
- package/src/install/targets/openclaw-skill.ts +17 -0
- package/src/install/targets/opencode-mcp.ts +101 -0
- package/src/install/targets/opencode-skill.ts +15 -0
- package/src/install/targets.test.ts +136 -0
- package/src/install/uninstall.ts +16 -152
- package/src/install/update.test.ts +17 -2
- package/src/install/update.ts +2 -5
- package/src/invoke.test.ts +7 -1
- package/src/mcp/bundle.ts +16 -4
- package/src/mcp/claude.test.ts +23 -4
- package/src/mcp/claude.ts +18 -13
- package/src/mcp/tools.ts +4 -1
- package/src/mcp/zip.test.ts +17 -0
- package/src/mcp/zip.ts +62 -9
- package/src/mcp.integration.test.ts +18 -1
- package/src/parse.test.ts +57 -4
- package/src/paths/host.ts +11 -11
- package/src/paths/remove-empty-dir.ts +13 -0
- package/src/skill/generate.ts +89 -5
- package/src/skill/hint.ts +5 -0
- package/src/skill/install.ts +33 -6
- package/src/skill/naming.ts +28 -0
- package/src/types.ts +65 -4
- package/src/validate.ts +79 -4
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
import { resolveCapabilities } from "../capabilities.ts";
|
|
2
|
+
import { appConfigInstalled } from "../config/file.ts";
|
|
3
|
+
import type { CliInstallTargets, CliProgram } from "../types.ts";
|
|
4
|
+
import type { InstallPaths } from "./paths.ts";
|
|
5
|
+
import {
|
|
6
|
+
type InstallPlanMode,
|
|
7
|
+
type InstallScope,
|
|
8
|
+
type InstallTargetPreview,
|
|
9
|
+
resolveAgentIntegration,
|
|
10
|
+
resolveEffectiveInstallTargets,
|
|
11
|
+
resolveInstallPlanMode,
|
|
12
|
+
} from "./target-effective.ts";
|
|
13
|
+
import {
|
|
14
|
+
INSTALL_ARTIFACT_KEYS,
|
|
15
|
+
INSTALL_TARGETS,
|
|
16
|
+
installTargetForKey,
|
|
17
|
+
MCP_KEYS,
|
|
18
|
+
SKILL_KEYS,
|
|
19
|
+
} from "./target-registry.ts";
|
|
20
|
+
import type {
|
|
21
|
+
CliInstallArtifactKey,
|
|
22
|
+
DetectedSnapshot,
|
|
23
|
+
InstalledArtifacts,
|
|
24
|
+
InstallOpts,
|
|
25
|
+
TargetPlanContext,
|
|
26
|
+
} from "./target-types.ts";
|
|
27
|
+
|
|
28
|
+
export { mcpCategoryEnabled } from "./target-effective.ts";
|
|
29
|
+
|
|
30
|
+
function emptyInstalledArtifacts(): InstalledArtifacts {
|
|
31
|
+
return {
|
|
32
|
+
app: false,
|
|
33
|
+
bashCompletion: false,
|
|
34
|
+
zshCompletion: false,
|
|
35
|
+
fishCompletion: false,
|
|
36
|
+
cursorSkill: false,
|
|
37
|
+
claudeSkill: false,
|
|
38
|
+
codexSkill: false,
|
|
39
|
+
opencodeSkill: false,
|
|
40
|
+
openclawSkill: false,
|
|
41
|
+
cursorMcp: false,
|
|
42
|
+
claudeMcp: false,
|
|
43
|
+
claudeDesktopMcp: false,
|
|
44
|
+
opencodeMcp: false,
|
|
45
|
+
codexMcp: false,
|
|
46
|
+
openclawMcp: false,
|
|
47
|
+
chatGptMcp: false,
|
|
48
|
+
bashRcPath: false,
|
|
49
|
+
zshRcFpath: false,
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/** Detects which install artifacts are currently present. */
|
|
54
|
+
export function detectInstalledArtifacts(
|
|
55
|
+
paths: InstallPaths,
|
|
56
|
+
root: CliProgram,
|
|
57
|
+
): InstalledArtifacts {
|
|
58
|
+
const out = emptyInstalledArtifacts();
|
|
59
|
+
for (const target of INSTALL_TARGETS) {
|
|
60
|
+
target.applyDetected(paths, root, out);
|
|
61
|
+
}
|
|
62
|
+
return out;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function isNonInteractiveInstall(opts: InstallOpts): boolean {
|
|
66
|
+
return !!(opts.yes || opts.json || opts.reinstall || opts.update || opts.dry);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/** Scope flags; non-interactive `install --mcp` implicitly includes app. */
|
|
70
|
+
export function resolveInstallScope(opts: InstallOpts): InstallScope {
|
|
71
|
+
const assumeApp =
|
|
72
|
+
!opts.uninstall && opts.mcp && !opts.all && !opts.app && isNonInteractiveInstall(opts);
|
|
73
|
+
return {
|
|
74
|
+
all: opts.all,
|
|
75
|
+
app: opts.app || assumeApp,
|
|
76
|
+
completions: opts.completions,
|
|
77
|
+
skill: opts.skill,
|
|
78
|
+
mcp: opts.mcp,
|
|
79
|
+
configure: opts.configure,
|
|
80
|
+
uninstall: opts.uninstall,
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/** Builds detected snapshot including app config for plan scoping. */
|
|
85
|
+
export function buildDetectedSnapshot(root: CliProgram, paths: InstallPaths): DetectedSnapshot {
|
|
86
|
+
const base = detectInstalledArtifacts(paths, root);
|
|
87
|
+
return {
|
|
88
|
+
...base,
|
|
89
|
+
appConfig: root.appConfig ? appConfigInstalled(root) : false,
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function targetExplicitlyConfigured(
|
|
94
|
+
user: CliInstallTargets | undefined,
|
|
95
|
+
key: CliInstallArtifactKey,
|
|
96
|
+
): boolean {
|
|
97
|
+
return user?.[key] !== undefined;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function agentCategoryInScope(
|
|
101
|
+
key: CliInstallArtifactKey,
|
|
102
|
+
categoryKeys: readonly CliInstallArtifactKey[],
|
|
103
|
+
category: "skill" | "mcp",
|
|
104
|
+
effective: Record<CliInstallArtifactKey, { enabled: boolean; includedInAll: boolean }>,
|
|
105
|
+
targets: CliInstallTargets | undefined,
|
|
106
|
+
root: CliProgram,
|
|
107
|
+
): boolean {
|
|
108
|
+
if (!categoryKeys.includes(key)) return false;
|
|
109
|
+
if (!effective[key].enabled) return false;
|
|
110
|
+
if (category === "mcp" && !resolveCapabilities(root).mcp) return false;
|
|
111
|
+
return effective[key].includedInAll || targetExplicitlyConfigured(targets, key);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/** Whether an artifact is in scope for the current CLI flags and install mode. */
|
|
115
|
+
export function isArtifactInScope(
|
|
116
|
+
key: CliInstallArtifactKey,
|
|
117
|
+
scope: InstallScope,
|
|
118
|
+
effective: Record<CliInstallArtifactKey, { enabled: boolean; includedInAll: boolean }>,
|
|
119
|
+
mode: InstallPlanMode,
|
|
120
|
+
root: CliProgram,
|
|
121
|
+
targets?: CliInstallTargets,
|
|
122
|
+
): boolean {
|
|
123
|
+
if (mode === "uninstall-all") {
|
|
124
|
+
return true;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
if (mode === "refresh") {
|
|
128
|
+
return effective[key].enabled;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
if (mode === "install-all") {
|
|
132
|
+
const t = effective[key];
|
|
133
|
+
return t.enabled && t.includedInAll;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
const scoped =
|
|
137
|
+
scope.app || scope.completions || scope.skill || scope.mcp || scope.configure || false;
|
|
138
|
+
|
|
139
|
+
if (mode === "uninstall-scoped" || mode === "install-scoped") {
|
|
140
|
+
if (!effective[key].enabled) return false;
|
|
141
|
+
if (scope.configure) return key === "configure";
|
|
142
|
+
if (scope.app) return key === "app";
|
|
143
|
+
if (scope.completions) return key === "completions";
|
|
144
|
+
if (scope.skill) {
|
|
145
|
+
return agentCategoryInScope(key, SKILL_KEYS, "skill", effective, targets, root);
|
|
146
|
+
}
|
|
147
|
+
if (scope.mcp) {
|
|
148
|
+
return agentCategoryInScope(key, MCP_KEYS, "mcp", effective, targets, root);
|
|
149
|
+
}
|
|
150
|
+
if (!scoped && mode === "install-scoped") return false;
|
|
151
|
+
return false;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
return false;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/** Whether to include an artifact in the current install/uninstall/refresh plan. */
|
|
158
|
+
export function shouldIncludeArtifact(
|
|
159
|
+
key: CliInstallArtifactKey,
|
|
160
|
+
root: CliProgram,
|
|
161
|
+
paths: InstallPaths,
|
|
162
|
+
scope: InstallScope,
|
|
163
|
+
mode: InstallPlanMode,
|
|
164
|
+
effective: Record<CliInstallArtifactKey, { enabled: boolean; includedInAll: boolean }>,
|
|
165
|
+
detected?: Partial<Record<CliInstallArtifactKey, boolean>>,
|
|
166
|
+
): boolean {
|
|
167
|
+
const target = installTargetForKey(key);
|
|
168
|
+
const targets = root.install?.targets;
|
|
169
|
+
|
|
170
|
+
if (key !== "configure" && target && !target.isAvailable(root, paths)) {
|
|
171
|
+
return false;
|
|
172
|
+
}
|
|
173
|
+
if (key === "configure" && !root.appConfig) {
|
|
174
|
+
return false;
|
|
175
|
+
}
|
|
176
|
+
if (mode === "refresh" && detected) {
|
|
177
|
+
if (!detected[key]) return false;
|
|
178
|
+
}
|
|
179
|
+
if ((mode === "uninstall-all" || mode === "uninstall-scoped") && detected) {
|
|
180
|
+
const det = detected[key];
|
|
181
|
+
if (key === "configure") {
|
|
182
|
+
if (!det) return false;
|
|
183
|
+
} else if (mode === "uninstall-scoped") {
|
|
184
|
+
if (!isArtifactInScope(key, scope, effective, mode, root, targets)) return false;
|
|
185
|
+
if (!det) return false;
|
|
186
|
+
} else if (!det) {
|
|
187
|
+
return false;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
if (mode === "uninstall-all") {
|
|
191
|
+
return true;
|
|
192
|
+
}
|
|
193
|
+
if (mode === "uninstall-scoped") {
|
|
194
|
+
return isArtifactInScope(key, scope, effective, mode, root, targets);
|
|
195
|
+
}
|
|
196
|
+
if (mode === "refresh") {
|
|
197
|
+
return effective[key].enabled;
|
|
198
|
+
}
|
|
199
|
+
return isArtifactInScope(key, scope, effective, mode, root, targets);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/** Builds plan context shared by install and uninstall planners. */
|
|
203
|
+
export function buildTargetPlanContext(
|
|
204
|
+
root: CliProgram,
|
|
205
|
+
paths: InstallPaths,
|
|
206
|
+
opts: InstallOpts,
|
|
207
|
+
detected: DetectedSnapshot,
|
|
208
|
+
): TargetPlanContext {
|
|
209
|
+
const effective = resolveEffectiveInstallTargets(root.install, root);
|
|
210
|
+
const mode = resolveInstallPlanMode(opts);
|
|
211
|
+
const scope = resolveInstallScope(opts);
|
|
212
|
+
const detPartial = Object.fromEntries(
|
|
213
|
+
INSTALL_TARGETS.map((t) => [t.key, t.detectedForSnapshot(detected)]),
|
|
214
|
+
) as Partial<Record<CliInstallArtifactKey, boolean>>;
|
|
215
|
+
|
|
216
|
+
const include = (key: CliInstallArtifactKey) =>
|
|
217
|
+
shouldIncludeArtifact(key, root, paths, scope, mode, effective, detPartial);
|
|
218
|
+
|
|
219
|
+
return {
|
|
220
|
+
root,
|
|
221
|
+
paths,
|
|
222
|
+
opts,
|
|
223
|
+
dry: !!opts.dry,
|
|
224
|
+
detected,
|
|
225
|
+
effective,
|
|
226
|
+
scope,
|
|
227
|
+
mode,
|
|
228
|
+
include,
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/** Maps detected snapshot to artifact key (for tests and legacy callers). */
|
|
233
|
+
export function detectedForArtifact(
|
|
234
|
+
key: CliInstallArtifactKey,
|
|
235
|
+
detected: DetectedSnapshot,
|
|
236
|
+
): boolean {
|
|
237
|
+
const target = installTargetForKey(key);
|
|
238
|
+
if (key === "completions") {
|
|
239
|
+
return detected.bashCompletion || detected.zshCompletion || detected.fishCompletion;
|
|
240
|
+
}
|
|
241
|
+
if (key === "configure") {
|
|
242
|
+
return detected.appConfig ?? false;
|
|
243
|
+
}
|
|
244
|
+
return target?.detectedForSnapshot(detected) ?? false;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/** Resolved artifact keys for `--all`, `--mcp`, and `--skill` (availability-gated). */
|
|
248
|
+
export function resolveInstallTargetPreview(
|
|
249
|
+
program: CliProgram,
|
|
250
|
+
paths: InstallPaths,
|
|
251
|
+
): InstallTargetPreview {
|
|
252
|
+
const effective = resolveEffectiveInstallTargets(program.install, program);
|
|
253
|
+
const integration = resolveAgentIntegration(program.install, program.mcpServer?.enabled === true);
|
|
254
|
+
|
|
255
|
+
const keysForScope = (scope: InstallScope, mode: InstallPlanMode): CliInstallArtifactKey[] =>
|
|
256
|
+
INSTALL_ARTIFACT_KEYS.filter((key) =>
|
|
257
|
+
shouldIncludeArtifact(key, program, paths, scope, mode, effective),
|
|
258
|
+
);
|
|
259
|
+
|
|
260
|
+
return {
|
|
261
|
+
agentIntegration: integration,
|
|
262
|
+
all: keysForScope({ all: true }, "install-all"),
|
|
263
|
+
mcp: keysForScope({ mcp: true }, "install-scoped"),
|
|
264
|
+
skill: keysForScope({ skill: true }, "install-scoped"),
|
|
265
|
+
};
|
|
266
|
+
}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
import type { CliProgram } from "../types.ts";
|
|
3
|
+
import { displayInstallPath, type InstallPaths } from "./paths.ts";
|
|
4
|
+
import { InstallTarget } from "./target-base.ts";
|
|
5
|
+
import type {
|
|
6
|
+
CliInstallArtifactKey,
|
|
7
|
+
DetectedSnapshot,
|
|
8
|
+
InstallAction,
|
|
9
|
+
InstallActionKind,
|
|
10
|
+
InstalledArtifacts,
|
|
11
|
+
InstallStatus,
|
|
12
|
+
TargetPlanContext,
|
|
13
|
+
UninstallAction,
|
|
14
|
+
} from "./target-types.ts";
|
|
15
|
+
|
|
16
|
+
export interface SkillHostSpec {
|
|
17
|
+
key: CliInstallArtifactKey;
|
|
18
|
+
actionKind: InstallActionKind;
|
|
19
|
+
label: string;
|
|
20
|
+
uninstallPrefix: string;
|
|
21
|
+
pairedMcpKey: CliInstallArtifactKey;
|
|
22
|
+
skillDir: (paths: InstallPaths) => string;
|
|
23
|
+
detectedKey: keyof Pick<
|
|
24
|
+
InstalledArtifacts,
|
|
25
|
+
"cursorSkill" | "claudeSkill" | "codexSkill" | "opencodeSkill" | "openclawSkill"
|
|
26
|
+
>;
|
|
27
|
+
statusField: keyof Pick<
|
|
28
|
+
InstallStatus,
|
|
29
|
+
"cursorSkill" | "claudeSkill" | "codexSkill" | "opencodeSkill" | "openclawSkill"
|
|
30
|
+
>;
|
|
31
|
+
isAvailable: (root: CliProgram, paths: InstallPaths) => boolean;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** Agent shell skill directory install. */
|
|
35
|
+
export class SkillInstallTarget extends InstallTarget {
|
|
36
|
+
readonly key: CliInstallArtifactKey;
|
|
37
|
+
readonly actionKind: InstallActionKind;
|
|
38
|
+
readonly category = "skill" as const;
|
|
39
|
+
readonly pairedKey: CliInstallArtifactKey;
|
|
40
|
+
readonly uninstallPrefix: string;
|
|
41
|
+
|
|
42
|
+
private readonly spec: SkillHostSpec;
|
|
43
|
+
|
|
44
|
+
constructor(spec: SkillHostSpec) {
|
|
45
|
+
super();
|
|
46
|
+
this.spec = spec;
|
|
47
|
+
this.key = spec.key;
|
|
48
|
+
this.actionKind = spec.actionKind;
|
|
49
|
+
this.pairedKey = spec.pairedMcpKey;
|
|
50
|
+
this.uninstallPrefix = spec.uninstallPrefix;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
isAvailable(root: CliProgram, paths: InstallPaths): boolean {
|
|
54
|
+
return this.spec.isAvailable(root, paths);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
isDetected(paths: InstallPaths, _root: CliProgram): boolean {
|
|
58
|
+
return existsSync(this.spec.skillDir(paths));
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
applyDetected(paths: InstallPaths, root: CliProgram, out: InstalledArtifacts): void {
|
|
62
|
+
out[this.spec.detectedKey] = this.isDetected(paths, root);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
protected isDetectedFromSnapshot(detected: DetectedSnapshot): boolean {
|
|
66
|
+
return detected[this.spec.detectedKey];
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
protected formatStatusLine(paths: InstallPaths, _root: CliProgram): string {
|
|
70
|
+
return `${displayInstallPath(this.spec.skillDir(paths))}/`;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
protected assignStatusLine(status: InstallStatus, line: string): void {
|
|
74
|
+
status[this.spec.statusField] = line;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
skillDir(paths: InstallPaths): string {
|
|
78
|
+
return this.spec.skillDir(paths);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
protected buildInstallActions(ctx: TargetPlanContext): InstallAction[] {
|
|
82
|
+
const dir = this.spec.skillDir(ctx.paths);
|
|
83
|
+
return [
|
|
84
|
+
{
|
|
85
|
+
kind: this.actionKind,
|
|
86
|
+
summary: `${this.spec.label.toLowerCase()} skill: ${displayInstallPath(dir)}/`,
|
|
87
|
+
message: `Installing ${this.spec.label} skill to ${displayInstallPath(dir)}/`,
|
|
88
|
+
run: () => [],
|
|
89
|
+
},
|
|
90
|
+
];
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
protected buildUninstallActions(ctx: TargetPlanContext): UninstallAction[] {
|
|
94
|
+
const dir = this.spec.skillDir(ctx.paths);
|
|
95
|
+
return [
|
|
96
|
+
{
|
|
97
|
+
kind: this.actionKind,
|
|
98
|
+
summary: `${this.uninstallPrefix}: ${displayInstallPath(dir)}/`,
|
|
99
|
+
message: `Removing ${this.spec.label} skill ${displayInstallPath(dir)}/`,
|
|
100
|
+
run: () => [],
|
|
101
|
+
},
|
|
102
|
+
];
|
|
103
|
+
}
|
|
104
|
+
}
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import type { CliProgram } from "../types.ts";
|
|
2
|
+
import type { InstallPaths } from "./paths.ts";
|
|
3
|
+
|
|
4
|
+
export type { InstallTargetSpec, ResolvedInstallTarget } from "../types.ts";
|
|
5
|
+
|
|
6
|
+
export type InstallPlanMode =
|
|
7
|
+
| "install-all"
|
|
8
|
+
| "install-scoped"
|
|
9
|
+
| "uninstall-all"
|
|
10
|
+
| "uninstall-scoped"
|
|
11
|
+
| "refresh";
|
|
12
|
+
|
|
13
|
+
export interface InstallScope {
|
|
14
|
+
all?: boolean;
|
|
15
|
+
app?: boolean;
|
|
16
|
+
completions?: boolean;
|
|
17
|
+
skill?: boolean;
|
|
18
|
+
mcp?: boolean;
|
|
19
|
+
configure?: boolean;
|
|
20
|
+
uninstall?: boolean;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/** Artifact keys for install.targets. */
|
|
24
|
+
export type CliInstallArtifactKey =
|
|
25
|
+
| "app"
|
|
26
|
+
| "chatgptMcp"
|
|
27
|
+
| "claudeCodeMcp"
|
|
28
|
+
| "claudeDesktopMcp"
|
|
29
|
+
| "claudeSkill"
|
|
30
|
+
| "codexMcp"
|
|
31
|
+
| "codexSkill"
|
|
32
|
+
| "completions"
|
|
33
|
+
| "configure"
|
|
34
|
+
| "cursorMcp"
|
|
35
|
+
| "cursorSkill"
|
|
36
|
+
| "openclawMcp"
|
|
37
|
+
| "openclawSkill"
|
|
38
|
+
| "opencodeMcp"
|
|
39
|
+
| "opencodeSkill";
|
|
40
|
+
|
|
41
|
+
export type InstallActionKind =
|
|
42
|
+
| "app"
|
|
43
|
+
| "completions"
|
|
44
|
+
| "cursor-skill"
|
|
45
|
+
| "claude-skill"
|
|
46
|
+
| "codex-skill"
|
|
47
|
+
| "opencode-skill"
|
|
48
|
+
| "openclaw-skill"
|
|
49
|
+
| "cursor-mcp"
|
|
50
|
+
| "claude-mcp"
|
|
51
|
+
| "claude-desktop-mcp"
|
|
52
|
+
| "opencode-mcp"
|
|
53
|
+
| "codex-mcp"
|
|
54
|
+
| "openclaw-mcp"
|
|
55
|
+
| "chatgpt-desktop-mcp"
|
|
56
|
+
| "configure";
|
|
57
|
+
|
|
58
|
+
export type InstallTargetCategory = "core" | "skill" | "mcp";
|
|
59
|
+
|
|
60
|
+
export interface InstallAction {
|
|
61
|
+
kind: InstallActionKind;
|
|
62
|
+
summary: string;
|
|
63
|
+
message: string;
|
|
64
|
+
run: () => string[];
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export interface UninstallAction {
|
|
68
|
+
kind?: InstallActionKind | "configure";
|
|
69
|
+
summary: string;
|
|
70
|
+
message: string;
|
|
71
|
+
run: () => string[];
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export interface InstalledArtifacts {
|
|
75
|
+
app: boolean;
|
|
76
|
+
bashCompletion: boolean;
|
|
77
|
+
zshCompletion: boolean;
|
|
78
|
+
fishCompletion: boolean;
|
|
79
|
+
cursorSkill: boolean;
|
|
80
|
+
claudeSkill: boolean;
|
|
81
|
+
codexSkill: boolean;
|
|
82
|
+
opencodeSkill: boolean;
|
|
83
|
+
openclawSkill: boolean;
|
|
84
|
+
cursorMcp: boolean;
|
|
85
|
+
claudeMcp: boolean;
|
|
86
|
+
claudeDesktopMcp: boolean;
|
|
87
|
+
opencodeMcp: boolean;
|
|
88
|
+
codexMcp: boolean;
|
|
89
|
+
openclawMcp: boolean;
|
|
90
|
+
chatGptMcp: boolean;
|
|
91
|
+
bashRcPath: boolean;
|
|
92
|
+
zshRcFpath: boolean;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export interface InstallStatus {
|
|
96
|
+
app?: string;
|
|
97
|
+
bashCompletion?: string;
|
|
98
|
+
zshCompletion?: string;
|
|
99
|
+
fishCompletion?: string;
|
|
100
|
+
cursorSkill?: string;
|
|
101
|
+
claudeSkill?: string;
|
|
102
|
+
codexSkill?: string;
|
|
103
|
+
opencodeSkill?: string;
|
|
104
|
+
openclawSkill?: string;
|
|
105
|
+
cursorMcp?: string;
|
|
106
|
+
claudeMcp?: string;
|
|
107
|
+
claudeDesktopMcp?: string;
|
|
108
|
+
opencodeMcp?: string;
|
|
109
|
+
codexMcp?: string;
|
|
110
|
+
openclawMcp?: string;
|
|
111
|
+
chatGptMcp?: string;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export interface DetectedSnapshot extends InstalledArtifacts {
|
|
115
|
+
appConfig?: boolean;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export interface InstallOpts {
|
|
119
|
+
all?: boolean;
|
|
120
|
+
app?: boolean;
|
|
121
|
+
completions?: boolean;
|
|
122
|
+
skill?: boolean;
|
|
123
|
+
mcp?: boolean;
|
|
124
|
+
reinstall?: boolean;
|
|
125
|
+
update?: boolean;
|
|
126
|
+
from?: string;
|
|
127
|
+
status?: boolean;
|
|
128
|
+
uninstall?: boolean;
|
|
129
|
+
configure?: boolean;
|
|
130
|
+
yes?: boolean;
|
|
131
|
+
dry?: boolean;
|
|
132
|
+
json?: boolean;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
export interface TargetPlanContext {
|
|
136
|
+
root: CliProgram;
|
|
137
|
+
paths: InstallPaths;
|
|
138
|
+
opts: InstallOpts;
|
|
139
|
+
dry: boolean;
|
|
140
|
+
detected: DetectedSnapshot;
|
|
141
|
+
effective: Record<CliInstallArtifactKey, { enabled: boolean; includedInAll: boolean }>;
|
|
142
|
+
scope: InstallScope;
|
|
143
|
+
mode: InstallPlanMode;
|
|
144
|
+
include: (key: CliInstallArtifactKey) => boolean;
|
|
145
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
import type { CliProgram } from "../../types.ts";
|
|
3
|
+
import { installApp, uninstallApp } from "../app.ts";
|
|
4
|
+
import { displayInstallPath, type InstallPaths } from "../paths.ts";
|
|
5
|
+
import { InstallTarget } from "../target-base.ts";
|
|
6
|
+
import type {
|
|
7
|
+
DetectedSnapshot,
|
|
8
|
+
InstallAction,
|
|
9
|
+
InstalledArtifacts,
|
|
10
|
+
InstallStatus,
|
|
11
|
+
TargetPlanContext,
|
|
12
|
+
UninstallAction,
|
|
13
|
+
} from "../target-types.ts";
|
|
14
|
+
|
|
15
|
+
/** Installs the compiled app to ~/.local/bin/<key>. */
|
|
16
|
+
class AppInstallTarget extends InstallTarget {
|
|
17
|
+
readonly key = "app" as const;
|
|
18
|
+
readonly actionKind = "app" as const;
|
|
19
|
+
readonly category = "core" as const;
|
|
20
|
+
|
|
21
|
+
isAvailable(_root: CliProgram, _paths: InstallPaths): boolean {
|
|
22
|
+
return true;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
isDetected(paths: InstallPaths, _root: CliProgram): boolean {
|
|
26
|
+
return existsSync(paths.appPath);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
applyDetected(paths: InstallPaths, root: CliProgram, out: InstalledArtifacts): void {
|
|
30
|
+
out.app = this.isDetected(paths, root);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
protected isDetectedFromSnapshot(detected: DetectedSnapshot): boolean {
|
|
34
|
+
return detected.app;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
protected formatStatusLine(paths: InstallPaths, _root: CliProgram): string {
|
|
38
|
+
return displayInstallPath(paths.appPath);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
protected assignStatusLine(status: InstallStatus, line: string): void {
|
|
42
|
+
status.app = line;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
protected buildInstallActions(ctx: TargetPlanContext): InstallAction[] {
|
|
46
|
+
const sourcePath = ctx.opts.from ?? process.execPath;
|
|
47
|
+
return [
|
|
48
|
+
{
|
|
49
|
+
kind: this.actionKind,
|
|
50
|
+
summary: `app: ${displayInstallPath(ctx.paths.appPath)}`,
|
|
51
|
+
message: `Installing app to ${displayInstallPath(ctx.paths.appPath)}`,
|
|
52
|
+
run: () => installApp(ctx.root, ctx.paths, ctx.dry, sourcePath).changedFiles,
|
|
53
|
+
},
|
|
54
|
+
];
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
protected buildUninstallActions(ctx: TargetPlanContext): UninstallAction[] {
|
|
58
|
+
return [
|
|
59
|
+
{
|
|
60
|
+
kind: this.actionKind,
|
|
61
|
+
summary: `app: ${displayInstallPath(ctx.paths.appPath)}`,
|
|
62
|
+
message: `Removing app ${displayInstallPath(ctx.paths.appPath)}`,
|
|
63
|
+
run: () => uninstallApp(ctx.root, ctx.paths, ctx.dry),
|
|
64
|
+
},
|
|
65
|
+
];
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export const appTarget = new AppInstallTarget();
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { chatGptDesktopPresent, userHome } from "../paths.ts";
|
|
2
|
+
import { McpJsonInstallTarget } from "../target-mcp-json.ts";
|
|
3
|
+
|
|
4
|
+
export const chatgptMcpTarget = new McpJsonInstallTarget({
|
|
5
|
+
key: "chatgptMcp",
|
|
6
|
+
actionKind: "chatgpt-desktop-mcp",
|
|
7
|
+
label: "chatgpt desktop mcp",
|
|
8
|
+
configPath: (p) => p.chatGptMcpPath,
|
|
9
|
+
detectedKey: "chatGptMcp",
|
|
10
|
+
statusField: "chatGptMcp",
|
|
11
|
+
isAvailable: (_root, paths) => chatGptDesktopPresent(userHome(), paths.chatGptMcpPath),
|
|
12
|
+
});
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { userHome } from "../paths.ts";
|
|
4
|
+
import { McpJsonInstallTarget } from "../target-mcp-json.ts";
|
|
5
|
+
|
|
6
|
+
export const claudeCodeMcpTarget = new McpJsonInstallTarget({
|
|
7
|
+
key: "claudeCodeMcp",
|
|
8
|
+
actionKind: "claude-mcp",
|
|
9
|
+
label: "claude code mcp",
|
|
10
|
+
pairedSkillKey: "claudeSkill",
|
|
11
|
+
configPath: (p) => p.claudeMcpPath,
|
|
12
|
+
detectedKey: "claudeMcp",
|
|
13
|
+
statusField: "claudeMcp",
|
|
14
|
+
isAvailable: () => existsSync(join(userHome(), ".claude")),
|
|
15
|
+
});
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { claudeDesktopPresent, userHome } from "../paths.ts";
|
|
2
|
+
import { McpJsonInstallTarget } from "../target-mcp-json.ts";
|
|
3
|
+
|
|
4
|
+
export const claudeDesktopMcpTarget = new McpJsonInstallTarget({
|
|
5
|
+
key: "claudeDesktopMcp",
|
|
6
|
+
actionKind: "claude-desktop-mcp",
|
|
7
|
+
label: "claude desktop mcp",
|
|
8
|
+
configPath: (p) => p.claudeDesktopMcpPath,
|
|
9
|
+
detectedKey: "claudeDesktopMcp",
|
|
10
|
+
statusField: "claudeDesktopMcp",
|
|
11
|
+
isAvailable: (_root, paths) => claudeDesktopPresent(userHome(), paths.claudeDesktopMcpPath),
|
|
12
|
+
});
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { userHome } from "../paths.ts";
|
|
4
|
+
import { SkillInstallTarget } from "../target-skill.ts";
|
|
5
|
+
|
|
6
|
+
export const claudeSkillTarget = new SkillInstallTarget({
|
|
7
|
+
key: "claudeSkill",
|
|
8
|
+
actionKind: "claude-skill",
|
|
9
|
+
label: "Claude Code",
|
|
10
|
+
uninstallPrefix: "claude skill",
|
|
11
|
+
pairedMcpKey: "claudeCodeMcp",
|
|
12
|
+
skillDir: (p) => p.claudeSkillDir,
|
|
13
|
+
detectedKey: "claudeSkill",
|
|
14
|
+
statusField: "claudeSkill",
|
|
15
|
+
isAvailable: () => existsSync(join(userHome(), ".claude")),
|
|
16
|
+
});
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { codexOnPath } from "../mcp-codex.ts";
|
|
2
|
+
import {
|
|
3
|
+
checkCodexMcpConflict,
|
|
4
|
+
codexMcpHasServer,
|
|
5
|
+
McpCliInstallTarget,
|
|
6
|
+
mergeCodexMcpConfig,
|
|
7
|
+
removeCodexMcpConfig,
|
|
8
|
+
} from "../target-mcp-cli.ts";
|
|
9
|
+
|
|
10
|
+
export const codexMcpTarget = new McpCliInstallTarget({
|
|
11
|
+
key: "codexMcp",
|
|
12
|
+
actionKind: "codex-mcp",
|
|
13
|
+
label: "codex mcp",
|
|
14
|
+
pairedSkillKey: "codexSkill",
|
|
15
|
+
configPath: (p) => p.codexConfigPath,
|
|
16
|
+
detectedKey: "codexMcp",
|
|
17
|
+
statusField: "codexMcp",
|
|
18
|
+
isAvailable: () => codexOnPath(),
|
|
19
|
+
hasServer: codexMcpHasServer,
|
|
20
|
+
installMessage: 'Registering MCP server "{name}" via codex mcp add',
|
|
21
|
+
uninstallMessage: 'Removing MCP server "{name}" from Codex via codex mcp remove',
|
|
22
|
+
merge: mergeCodexMcpConfig,
|
|
23
|
+
remove: removeCodexMcpConfig,
|
|
24
|
+
preflight: checkCodexMcpConflict,
|
|
25
|
+
});
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { codexOnPath } from "../mcp-codex.ts";
|
|
2
|
+
import { SkillInstallTarget } from "../target-skill.ts";
|
|
3
|
+
|
|
4
|
+
export const codexSkillTarget = new SkillInstallTarget({
|
|
5
|
+
key: "codexSkill",
|
|
6
|
+
actionKind: "codex-skill",
|
|
7
|
+
label: "Codex",
|
|
8
|
+
uninstallPrefix: "codex skill",
|
|
9
|
+
pairedMcpKey: "codexMcp",
|
|
10
|
+
skillDir: (p) => p.codexSkillDir,
|
|
11
|
+
detectedKey: "codexSkill",
|
|
12
|
+
statusField: "codexSkill",
|
|
13
|
+
isAvailable: () => codexOnPath(),
|
|
14
|
+
});
|