@skilly-hand/skilly-hand 0.21.0 → 0.22.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 +32 -0
- package/README.md +1 -0
- package/package.json +1 -1
- package/packages/catalog/package.json +1 -1
- package/packages/cli/package.json +1 -1
- package/packages/cli/src/bin.js +339 -140
- package/packages/cli/src/ink-ui.js +304 -27
- package/packages/cli/src/result-doc.js +58 -0
- package/packages/core/package.json +1 -1
- package/packages/core/src/index.js +312 -14
- package/packages/core/src/terminal.js +5 -3
- package/packages/core/src/ui/layout.js +11 -7
- package/packages/core/src/ui/theme.js +16 -2
- package/packages/detectors/package.json +1 -1
package/packages/cli/src/bin.js
CHANGED
|
@@ -9,11 +9,22 @@ import {
|
|
|
9
9
|
installProject,
|
|
10
10
|
resolveSkillSelection,
|
|
11
11
|
runDoctor,
|
|
12
|
+
setupNativeProject,
|
|
12
13
|
uninstallProject
|
|
13
14
|
} from "../../core/src/index.js";
|
|
14
15
|
import { createTerminalRenderer } from "../../core/src/terminal.js";
|
|
15
16
|
import { detectProject } from "../../detectors/src/index.js";
|
|
16
17
|
import { confirmWithInk, launchInkApp } from "./ink-ui.js";
|
|
18
|
+
import {
|
|
19
|
+
createResultDoc,
|
|
20
|
+
kvBlock,
|
|
21
|
+
listBlock,
|
|
22
|
+
renderResultDocText,
|
|
23
|
+
section,
|
|
24
|
+
statusBlock,
|
|
25
|
+
tableBlock,
|
|
26
|
+
textBlock
|
|
27
|
+
} from "./result-doc.js";
|
|
17
28
|
|
|
18
29
|
const require = createRequire(import.meta.url);
|
|
19
30
|
const { version } = require("../../../package.json");
|
|
@@ -75,13 +86,14 @@ export function parseArgs(argv) {
|
|
|
75
86
|
else throw new Error(`Unknown flag: ${token}`);
|
|
76
87
|
}
|
|
77
88
|
|
|
78
|
-
return { command: positional[0], flags };
|
|
89
|
+
return { command: positional[0], subcommand: positional[1], flags };
|
|
79
90
|
}
|
|
80
91
|
|
|
81
92
|
function buildHelpText(renderer, appVersion) {
|
|
82
93
|
const usage = renderer.section("Usage", renderer.list([
|
|
83
94
|
"npx skilly-hand # interactive launcher when running in a TTY",
|
|
84
95
|
"npx skilly-hand install",
|
|
96
|
+
"npx skilly-hand native setup",
|
|
85
97
|
"npx skilly-hand detect",
|
|
86
98
|
"npx skilly-hand list",
|
|
87
99
|
"npx skilly-hand doctor",
|
|
@@ -104,6 +116,7 @@ function buildHelpText(renderer, appVersion) {
|
|
|
104
116
|
const examples = renderer.section("Examples", renderer.list([
|
|
105
117
|
"npx skilly-hand",
|
|
106
118
|
"npx skilly-hand install --dry-run",
|
|
119
|
+
"npx skilly-hand native setup --agent codex",
|
|
107
120
|
"npx skilly-hand detect --json",
|
|
108
121
|
"npx skilly-hand install --agent antigravity --agent windsurf",
|
|
109
122
|
"npx skilly-hand uninstall --yes"
|
|
@@ -117,151 +130,216 @@ function buildHelpText(renderer, appVersion) {
|
|
|
117
130
|
]);
|
|
118
131
|
}
|
|
119
132
|
|
|
120
|
-
function
|
|
133
|
+
function buildInstallResultDoc(result, flags, detectionGridText = "") {
|
|
121
134
|
const mode = flags.dryRun ? "dry-run" : "apply";
|
|
122
|
-
|
|
123
|
-
"Install Preflight",
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
135
|
+
return createResultDoc("Install", [
|
|
136
|
+
section("Install Preflight", [
|
|
137
|
+
kvBlock([
|
|
138
|
+
["Project", result.plan.cwd],
|
|
139
|
+
["Install root", result.plan.installRoot],
|
|
140
|
+
["Agents", result.plan.agents.join(", ") || "none"],
|
|
141
|
+
["Include tags", flags.include.join(", ") || "none"],
|
|
142
|
+
["Exclude tags", flags.exclude.join(", ") || "none"],
|
|
143
|
+
["Mode", mode]
|
|
144
|
+
])
|
|
145
|
+
]),
|
|
146
|
+
section("Detected Technologies", [
|
|
147
|
+
result.plan.detections.length > 0
|
|
148
|
+
? textBlock(detectionGridText)
|
|
149
|
+
: statusBlock("warn", "No technology signals were detected.", "Only core skills will be selected.")
|
|
150
|
+
]),
|
|
151
|
+
section("Skill Plan", [
|
|
152
|
+
result.plan.skills.length > 0
|
|
153
|
+
? tableBlock(
|
|
154
|
+
[
|
|
155
|
+
{ key: "id", header: "Skill ID" },
|
|
156
|
+
{ key: "title", header: "Title" },
|
|
157
|
+
{ key: "tags", header: "Tags" }
|
|
158
|
+
],
|
|
159
|
+
result.plan.skills.map((skill) => ({
|
|
160
|
+
id: skill.id,
|
|
161
|
+
title: skill.title,
|
|
162
|
+
tags: skill.tags.join(", ")
|
|
163
|
+
}))
|
|
164
|
+
)
|
|
165
|
+
: statusBlock("warn", "No skills selected.")
|
|
166
|
+
]),
|
|
167
|
+
section("Status", [
|
|
168
|
+
result.applied
|
|
169
|
+
? statusBlock("success", "Installation completed.", "Managed files and symlinks are in place.")
|
|
170
|
+
: statusBlock("info", "Dry run complete.", "No files were written.")
|
|
171
|
+
]),
|
|
172
|
+
section("Next Steps", [
|
|
173
|
+
listBlock(
|
|
174
|
+
result.applied
|
|
175
|
+
? [
|
|
176
|
+
"Review generated AGENTS and assistant instruction files.",
|
|
177
|
+
"Run `npx skilly-hand native setup` to scaffold native instruction/rule adapters.",
|
|
178
|
+
"Run `npx skilly-hand doctor` to validate installation health.",
|
|
179
|
+
"Use `npx skilly-hand uninstall` to restore backed-up files if needed."
|
|
180
|
+
]
|
|
181
|
+
: [
|
|
182
|
+
"Run `npx skilly-hand install` to apply this plan.",
|
|
183
|
+
"Adjust `--include` and `--exclude` tags to tune skill selection."
|
|
184
|
+
]
|
|
185
|
+
)
|
|
131
186
|
])
|
|
132
|
-
);
|
|
187
|
+
]);
|
|
188
|
+
}
|
|
133
189
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
190
|
+
function buildNativeSetupResultDoc(result, flags) {
|
|
191
|
+
const mode = flags.dryRun ? "dry-run" : "apply";
|
|
192
|
+
return createResultDoc("Native Setup", [
|
|
193
|
+
section("Native Setup Preflight", [
|
|
194
|
+
kvBlock([
|
|
195
|
+
["Project", result.plan.cwd],
|
|
196
|
+
["Install root", result.plan.installRoot],
|
|
197
|
+
["Agents", result.plan.agents.join(", ") || "none"],
|
|
198
|
+
["Mode", mode]
|
|
199
|
+
])
|
|
200
|
+
]),
|
|
201
|
+
section("Native Coverage", [
|
|
202
|
+
tableBlock(
|
|
203
|
+
[
|
|
204
|
+
{ key: "agent", header: "Agent" },
|
|
205
|
+
{ key: "status", header: "Status" },
|
|
206
|
+
{ key: "target", header: "Target" },
|
|
207
|
+
{ key: "remediation", header: "Remediation" }
|
|
208
|
+
],
|
|
209
|
+
(result.nativeStatus || result.plan.nativeStatus || []).map((row) => ({
|
|
210
|
+
agent: row.agent,
|
|
211
|
+
status: row.status,
|
|
212
|
+
target: row.target || "-",
|
|
213
|
+
remediation: row.remediation
|
|
214
|
+
}))
|
|
215
|
+
)
|
|
216
|
+
]),
|
|
217
|
+
section("Status", [
|
|
218
|
+
result.applied
|
|
219
|
+
? statusBlock("success", "Native setup completed.", "Native rule/instruction files are synchronized.")
|
|
220
|
+
: statusBlock("info", "Native setup dry run complete.", "No files were written.")
|
|
221
|
+
]),
|
|
222
|
+
section("Next Steps", [
|
|
223
|
+
listBlock(
|
|
224
|
+
result.applied
|
|
225
|
+
? [
|
|
226
|
+
"Run `npx skilly-hand doctor` to verify native coverage.",
|
|
227
|
+
"Re-run `npx skilly-hand native setup` after changing agent targets."
|
|
228
|
+
]
|
|
229
|
+
: [
|
|
230
|
+
"Run `npx skilly-hand native setup` to apply these native adapter changes."
|
|
231
|
+
]
|
|
232
|
+
)
|
|
233
|
+
])
|
|
234
|
+
]);
|
|
235
|
+
}
|
|
140
236
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
? renderer.table(
|
|
145
|
-
[
|
|
146
|
-
{ key: "id", header: "Skill ID" },
|
|
147
|
-
{ key: "title", header: "Title" },
|
|
148
|
-
{ key: "tags", header: "Tags" }
|
|
149
|
-
],
|
|
150
|
-
result.plan.skills.map((skill) => ({
|
|
151
|
-
id: skill.id,
|
|
152
|
-
title: skill.title,
|
|
153
|
-
tags: skill.tags.join(", ")
|
|
154
|
-
}))
|
|
155
|
-
)
|
|
156
|
-
: renderer.status("warn", "No skills selected.")
|
|
157
|
-
);
|
|
237
|
+
function renderResultDoc(renderer, appVersion, doc, options = {}) {
|
|
238
|
+
return renderResultDocText(renderer, appVersion, doc, options);
|
|
239
|
+
}
|
|
158
240
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
241
|
+
function buildInstallResultBlock(renderer, appVersion, result, flags, options = {}) {
|
|
242
|
+
const doc = buildInstallResultDoc(result, flags, renderer.detectionGrid(result.plan.detections));
|
|
243
|
+
return renderResultDoc(renderer, appVersion, doc, options);
|
|
244
|
+
}
|
|
162
245
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
"Run `npx skilly-hand doctor` to validate installation health.",
|
|
167
|
-
"Use `npx skilly-hand uninstall` to restore backed-up files if needed."
|
|
168
|
-
])
|
|
169
|
-
: renderer.nextSteps([
|
|
170
|
-
"Run `npx skilly-hand install` to apply this plan.",
|
|
171
|
-
"Adjust `--include` and `--exclude` tags to tune skill selection."
|
|
172
|
-
]);
|
|
246
|
+
function buildNativeSetupResultBlock(renderer, appVersion, result, flags, options = {}) {
|
|
247
|
+
return renderResultDoc(renderer, appVersion, buildNativeSetupResultDoc(result, flags), options);
|
|
248
|
+
}
|
|
173
249
|
|
|
174
|
-
|
|
250
|
+
function printNativeSetupResult(renderer, appVersion, result, flags) {
|
|
251
|
+
renderer.write(buildNativeSetupResultBlock(renderer, appVersion, result, flags, { includeBanner: true }));
|
|
175
252
|
}
|
|
176
253
|
|
|
177
254
|
function printInstallResult(renderer, appVersion, result, flags) {
|
|
178
|
-
renderer.write(buildInstallResultBlock(renderer, appVersion, result, flags));
|
|
255
|
+
renderer.write(buildInstallResultBlock(renderer, appVersion, result, flags, { includeBanner: true }));
|
|
179
256
|
}
|
|
180
257
|
|
|
181
|
-
function
|
|
182
|
-
|
|
183
|
-
"Detection Summary",
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
258
|
+
function buildDetectResultDoc(cwd, detections, detectionGridText = "") {
|
|
259
|
+
return createResultDoc("Detect", [
|
|
260
|
+
section("Detection Summary", [
|
|
261
|
+
kvBlock([
|
|
262
|
+
["Project", cwd],
|
|
263
|
+
["Signals found", String(detections.length)]
|
|
264
|
+
])
|
|
265
|
+
]),
|
|
266
|
+
section("Findings", [
|
|
267
|
+
detections.length > 0
|
|
268
|
+
? textBlock(detectionGridText)
|
|
269
|
+
: statusBlock("warn", "No technology signals were detected.", "Only core skills will be selected.")
|
|
187
270
|
])
|
|
188
|
-
);
|
|
189
|
-
|
|
190
|
-
const findings = renderer.section(
|
|
191
|
-
"Findings",
|
|
192
|
-
detections.length > 0
|
|
193
|
-
? renderer.detectionGrid(detections)
|
|
194
|
-
: renderer.status("warn", "No technology signals were detected.", "Only core skills will be selected.")
|
|
195
|
-
);
|
|
271
|
+
]);
|
|
272
|
+
}
|
|
196
273
|
|
|
197
|
-
|
|
274
|
+
function buildDetectResultBlock(renderer, cwd, detections) {
|
|
275
|
+
const doc = buildDetectResultDoc(cwd, detections, renderer.detectionGrid(detections));
|
|
276
|
+
return renderResultDoc(renderer, "", doc, { includeBanner: false });
|
|
198
277
|
}
|
|
199
278
|
|
|
200
279
|
function printDetectResult(renderer, cwd, detections) {
|
|
201
280
|
renderer.write(buildDetectResultBlock(renderer, cwd, detections));
|
|
202
281
|
}
|
|
203
282
|
|
|
204
|
-
function
|
|
205
|
-
|
|
206
|
-
"Catalog Summary",
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
)
|
|
226
|
-
);
|
|
283
|
+
function buildListResultDoc(skills) {
|
|
284
|
+
return createResultDoc("List", [
|
|
285
|
+
section("Catalog Summary", [kvBlock([["Skills available", String(skills.length)]])]),
|
|
286
|
+
section("Skills", [
|
|
287
|
+
tableBlock(
|
|
288
|
+
[
|
|
289
|
+
{ key: "id", header: "Skill ID" },
|
|
290
|
+
{ key: "title", header: "Title" },
|
|
291
|
+
{ key: "tags", header: "Tags" },
|
|
292
|
+
{ key: "agents", header: "Agents" }
|
|
293
|
+
],
|
|
294
|
+
skills.map((skill) => ({
|
|
295
|
+
id: skill.id,
|
|
296
|
+
title: skill.title,
|
|
297
|
+
tags: skill.tags.join(", "),
|
|
298
|
+
agents: skill.agentSupport.join(", ")
|
|
299
|
+
}))
|
|
300
|
+
)
|
|
301
|
+
])
|
|
302
|
+
]);
|
|
303
|
+
}
|
|
227
304
|
|
|
228
|
-
|
|
305
|
+
function buildListResultBlock(renderer, skills) {
|
|
306
|
+
return renderResultDoc(renderer, "", buildListResultDoc(skills), { includeBanner: false });
|
|
229
307
|
}
|
|
230
308
|
|
|
231
309
|
function printListResult(renderer, skills) {
|
|
232
310
|
renderer.write(buildListResultBlock(renderer, skills));
|
|
233
311
|
}
|
|
234
312
|
|
|
235
|
-
function
|
|
236
|
-
const
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
313
|
+
function buildDoctorResultDoc(result, healthBadgeText = "") {
|
|
314
|
+
const sections = [
|
|
315
|
+
section("Health", [textBlock(healthBadgeText)]),
|
|
316
|
+
section("Doctor Summary", [
|
|
317
|
+
kvBlock([
|
|
318
|
+
["Project", result.cwd],
|
|
319
|
+
["Installed", result.installed ? "yes" : "no"],
|
|
320
|
+
["Catalog issues", String(result.catalogIssues.length)]
|
|
321
|
+
])
|
|
244
322
|
])
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
: "";
|
|
323
|
+
];
|
|
324
|
+
|
|
325
|
+
if (result.lock) {
|
|
326
|
+
sections.push(section("Lock Metadata", [
|
|
327
|
+
kvBlock([
|
|
328
|
+
["Generated at", result.lock.generatedAt],
|
|
329
|
+
["Agents", result.lock.agents.join(", ")],
|
|
330
|
+
["Skills", result.lock.skills.join(", ")]
|
|
331
|
+
])
|
|
332
|
+
]));
|
|
333
|
+
}
|
|
257
334
|
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
335
|
+
sections.push(section("Catalog Issues", [
|
|
336
|
+
result.catalogIssues.length
|
|
337
|
+
? listBlock(result.catalogIssues)
|
|
338
|
+
: statusBlock("success", "No catalog issues found.")
|
|
339
|
+
]));
|
|
261
340
|
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
renderer.table(
|
|
341
|
+
sections.push(section("Project Probes", [
|
|
342
|
+
tableBlock(
|
|
265
343
|
[
|
|
266
344
|
{ key: "path", header: "Path" },
|
|
267
345
|
{ key: "exists", header: "Exists" },
|
|
@@ -273,32 +351,60 @@ function buildDoctorResultBlock(renderer, result) {
|
|
|
273
351
|
type: item.type || "-"
|
|
274
352
|
}))
|
|
275
353
|
)
|
|
276
|
-
);
|
|
354
|
+
]));
|
|
277
355
|
|
|
278
|
-
|
|
356
|
+
sections.push(section("Native Coverage", [
|
|
357
|
+
tableBlock(
|
|
358
|
+
[
|
|
359
|
+
{ key: "agent", header: "Agent" },
|
|
360
|
+
{ key: "status", header: "Status" },
|
|
361
|
+
{ key: "target", header: "Target" },
|
|
362
|
+
{ key: "remediation", header: "Remediation" }
|
|
363
|
+
],
|
|
364
|
+
(result.nativeStatus || []).map((row) => ({
|
|
365
|
+
agent: row.agent,
|
|
366
|
+
status: row.status,
|
|
367
|
+
target: row.target || "-",
|
|
368
|
+
remediation: row.remediation
|
|
369
|
+
}))
|
|
370
|
+
)
|
|
371
|
+
]));
|
|
372
|
+
|
|
373
|
+
return createResultDoc("Doctor", sections);
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
function buildDoctorResultBlock(renderer, result) {
|
|
377
|
+
const doc = buildDoctorResultDoc(result, renderer.healthBadge(result.installed));
|
|
378
|
+
return renderResultDoc(renderer, "", doc, { includeBanner: false });
|
|
279
379
|
}
|
|
280
380
|
|
|
281
381
|
function printDoctorResult(renderer, result) {
|
|
282
382
|
renderer.write(buildDoctorResultBlock(renderer, result));
|
|
283
383
|
}
|
|
284
384
|
|
|
285
|
-
function
|
|
385
|
+
function buildUninstallResultDoc(result) {
|
|
286
386
|
if (result.removed) {
|
|
287
|
-
return
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
387
|
+
return createResultDoc("Uninstall", [
|
|
388
|
+
section("Status", [statusBlock("success", "skilly-hand installation removed.")]),
|
|
389
|
+
section("Next Steps", [
|
|
390
|
+
listBlock([
|
|
391
|
+
"Run `npx skilly-hand install` if you want to reinstall managed files.",
|
|
392
|
+
"Run `npx skilly-hand doctor` to confirm the project state."
|
|
393
|
+
])
|
|
292
394
|
])
|
|
293
395
|
]);
|
|
294
396
|
}
|
|
295
397
|
|
|
296
|
-
return
|
|
297
|
-
|
|
298
|
-
|
|
398
|
+
return createResultDoc("Uninstall", [
|
|
399
|
+
section("Status", [statusBlock("warn", "Nothing to uninstall.", result.reason)]),
|
|
400
|
+
section("Next Steps", [listBlock(["Run `npx skilly-hand install` to create a managed installation first."])])
|
|
299
401
|
]);
|
|
300
402
|
}
|
|
301
403
|
|
|
404
|
+
function buildUninstallResultBlock(renderer, result) {
|
|
405
|
+
return renderResultDoc(renderer, "", buildUninstallResultDoc(result), { includeBanner: false });
|
|
406
|
+
}
|
|
407
|
+
|
|
302
408
|
function printUninstallResult(renderer, result) {
|
|
303
409
|
renderer.write(buildUninstallResultBlock(renderer, result));
|
|
304
410
|
}
|
|
@@ -307,6 +413,9 @@ export function buildErrorHint(message) {
|
|
|
307
413
|
if (message.startsWith("Unknown command:")) {
|
|
308
414
|
return "Run `npx skilly-hand --help` to see available commands.";
|
|
309
415
|
}
|
|
416
|
+
if (message.startsWith("Unknown native subcommand:")) {
|
|
417
|
+
return "Use `npx skilly-hand native setup`.";
|
|
418
|
+
}
|
|
310
419
|
if (message.startsWith("Unknown flag:") || message.startsWith("Missing value")) {
|
|
311
420
|
return "Check command flags with `npx skilly-hand --help`.";
|
|
312
421
|
}
|
|
@@ -319,6 +428,7 @@ function createServices(overrides = {}) {
|
|
|
319
428
|
installProject,
|
|
320
429
|
resolveSkillSelection,
|
|
321
430
|
runDoctor,
|
|
431
|
+
setupNativeProject,
|
|
322
432
|
uninstallProject,
|
|
323
433
|
detectProject,
|
|
324
434
|
defaultAgents: DEFAULT_AGENTS,
|
|
@@ -367,53 +477,113 @@ async function runInteractiveSession({
|
|
|
367
477
|
await interactiveUi.launch({
|
|
368
478
|
appVersion,
|
|
369
479
|
actions: {
|
|
370
|
-
async
|
|
480
|
+
async runCommandBundle(command) {
|
|
481
|
+
if (command === "native-setup") {
|
|
482
|
+
const result = await services.setupNativeProject({ cwd, dryRun: false });
|
|
483
|
+
const doc = buildNativeSetupResultDoc(result, { dryRun: false });
|
|
484
|
+
return {
|
|
485
|
+
doc,
|
|
486
|
+
text: renderResultDoc(renderer, appVersion, doc, { includeBanner: false })
|
|
487
|
+
};
|
|
488
|
+
}
|
|
371
489
|
if (command === "detect") {
|
|
372
490
|
const detections = await services.detectProject(cwd);
|
|
373
|
-
|
|
491
|
+
const doc = buildDetectResultDoc(cwd, detections, renderer.detectionGrid(detections));
|
|
492
|
+
return {
|
|
493
|
+
doc,
|
|
494
|
+
text: renderResultDoc(renderer, "", doc, { includeBanner: false })
|
|
495
|
+
};
|
|
374
496
|
}
|
|
375
497
|
if (command === "list") {
|
|
376
498
|
const skills = await services.loadAllSkills();
|
|
377
|
-
|
|
499
|
+
const doc = buildListResultDoc(skills);
|
|
500
|
+
return {
|
|
501
|
+
doc,
|
|
502
|
+
text: renderResultDoc(renderer, "", doc, { includeBanner: false })
|
|
503
|
+
};
|
|
378
504
|
}
|
|
379
505
|
if (command === "doctor") {
|
|
380
506
|
const result = await services.runDoctor(cwd);
|
|
381
|
-
|
|
507
|
+
const doc = buildDoctorResultDoc(result, renderer.healthBadge(result.installed));
|
|
508
|
+
return {
|
|
509
|
+
doc,
|
|
510
|
+
text: renderResultDoc(renderer, "", doc, { includeBanner: false })
|
|
511
|
+
};
|
|
382
512
|
}
|
|
383
513
|
if (command === "uninstall") {
|
|
384
514
|
const result = await services.uninstallProject(cwd);
|
|
385
|
-
|
|
515
|
+
const doc = buildUninstallResultDoc(result);
|
|
516
|
+
return {
|
|
517
|
+
doc,
|
|
518
|
+
text: renderResultDoc(renderer, "", doc, { includeBanner: false })
|
|
519
|
+
};
|
|
386
520
|
}
|
|
387
|
-
|
|
521
|
+
const doc = createResultDoc("Result", [section("Status", [statusBlock("warn", `Unknown command: ${command}`)])]);
|
|
522
|
+
return {
|
|
523
|
+
doc,
|
|
524
|
+
text: renderResultDoc(renderer, "", doc, { includeBanner: false })
|
|
525
|
+
};
|
|
526
|
+
},
|
|
527
|
+
async runCommandDoc(command) {
|
|
528
|
+
const bundle = await this.runCommandBundle(command);
|
|
529
|
+
return bundle.doc;
|
|
530
|
+
},
|
|
531
|
+
async runCommand(command) {
|
|
532
|
+
const bundle = await this.runCommandBundle(command);
|
|
533
|
+
return bundle.text;
|
|
388
534
|
},
|
|
389
535
|
async prepareInstall() {
|
|
390
536
|
return getInteractiveInstallContext({ cwd, services });
|
|
391
537
|
},
|
|
392
|
-
async
|
|
538
|
+
async previewInstallBundle({ selectedSkillIds, selectedAgents }) {
|
|
393
539
|
const preview = await services.installProject({
|
|
394
540
|
cwd,
|
|
395
541
|
agents: selectedAgents,
|
|
396
542
|
dryRun: true,
|
|
397
543
|
selectedSkillIds
|
|
398
544
|
});
|
|
399
|
-
|
|
545
|
+
const doc = buildInstallResultDoc(preview, {
|
|
400
546
|
dryRun: true,
|
|
401
547
|
include: [],
|
|
402
548
|
exclude: []
|
|
403
|
-
});
|
|
549
|
+
}, renderer.detectionGrid(preview.plan.detections));
|
|
550
|
+
return {
|
|
551
|
+
doc,
|
|
552
|
+
text: renderResultDoc(renderer, appVersion, doc, { includeBanner: false })
|
|
553
|
+
};
|
|
404
554
|
},
|
|
405
|
-
async
|
|
555
|
+
async previewInstall({ selectedSkillIds, selectedAgents }) {
|
|
556
|
+
const bundle = await this.previewInstallBundle({ selectedSkillIds, selectedAgents });
|
|
557
|
+
return bundle.text;
|
|
558
|
+
},
|
|
559
|
+
async previewInstallDoc({ selectedSkillIds, selectedAgents }) {
|
|
560
|
+
const bundle = await this.previewInstallBundle({ selectedSkillIds, selectedAgents });
|
|
561
|
+
return bundle.doc;
|
|
562
|
+
},
|
|
563
|
+
async applyInstallBundle({ selectedSkillIds, selectedAgents }) {
|
|
406
564
|
const applied = await services.installProject({
|
|
407
565
|
cwd,
|
|
408
566
|
agents: selectedAgents,
|
|
409
567
|
dryRun: false,
|
|
410
568
|
selectedSkillIds
|
|
411
569
|
});
|
|
412
|
-
|
|
570
|
+
const doc = buildInstallResultDoc(applied, {
|
|
413
571
|
dryRun: false,
|
|
414
572
|
include: [],
|
|
415
573
|
exclude: []
|
|
416
|
-
});
|
|
574
|
+
}, renderer.detectionGrid(applied.plan.detections));
|
|
575
|
+
return {
|
|
576
|
+
doc,
|
|
577
|
+
text: renderResultDoc(renderer, appVersion, doc, { includeBanner: false })
|
|
578
|
+
};
|
|
579
|
+
},
|
|
580
|
+
async applyInstall({ selectedSkillIds, selectedAgents }) {
|
|
581
|
+
const bundle = await this.applyInstallBundle({ selectedSkillIds, selectedAgents });
|
|
582
|
+
return bundle.text;
|
|
583
|
+
},
|
|
584
|
+
async applyInstallDoc({ selectedSkillIds, selectedAgents }) {
|
|
585
|
+
const bundle = await this.applyInstallBundle({ selectedSkillIds, selectedAgents });
|
|
586
|
+
return bundle.doc;
|
|
417
587
|
}
|
|
418
588
|
}
|
|
419
589
|
});
|
|
@@ -421,6 +591,7 @@ async function runInteractiveSession({
|
|
|
421
591
|
|
|
422
592
|
async function runCommand({
|
|
423
593
|
command,
|
|
594
|
+
subcommand,
|
|
424
595
|
flags,
|
|
425
596
|
cwd,
|
|
426
597
|
stdin,
|
|
@@ -530,6 +701,32 @@ async function runCommand({
|
|
|
530
701
|
return;
|
|
531
702
|
}
|
|
532
703
|
|
|
704
|
+
if (command === "native") {
|
|
705
|
+
if (subcommand && subcommand !== "setup") {
|
|
706
|
+
throw new Error(`Unknown native subcommand: ${subcommand}`);
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
const result = await services.setupNativeProject({
|
|
710
|
+
cwd,
|
|
711
|
+
agents: flags.agents,
|
|
712
|
+
dryRun: flags.dryRun
|
|
713
|
+
});
|
|
714
|
+
|
|
715
|
+
if (flags.json) {
|
|
716
|
+
renderer.writeJson({
|
|
717
|
+
command: "native setup",
|
|
718
|
+
applied: result.applied,
|
|
719
|
+
plan: result.plan,
|
|
720
|
+
nativeStatus: result.nativeStatus || result.plan.nativeStatus || [],
|
|
721
|
+
lockPath: result.lockPath || null
|
|
722
|
+
});
|
|
723
|
+
return;
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
printNativeSetupResult(renderer, appVersion, result, flags);
|
|
727
|
+
return;
|
|
728
|
+
}
|
|
729
|
+
|
|
533
730
|
throw new Error(`Unknown command: ${command}`);
|
|
534
731
|
}
|
|
535
732
|
|
|
@@ -550,7 +747,7 @@ export async function runCli({
|
|
|
550
747
|
} = {}) {
|
|
551
748
|
const renderer = createTerminalRenderer({ stdout, stderr, env, platform });
|
|
552
749
|
const services = createServices(providedServices);
|
|
553
|
-
const { command, flags } = parseArgs(argv);
|
|
750
|
+
const { command, subcommand, flags } = parseArgs(argv);
|
|
554
751
|
|
|
555
752
|
if (flags.help) {
|
|
556
753
|
if (flags.json) {
|
|
@@ -560,6 +757,7 @@ export async function runCli({
|
|
|
560
757
|
usage: [
|
|
561
758
|
"npx skilly-hand",
|
|
562
759
|
"npx skilly-hand install",
|
|
760
|
+
"npx skilly-hand native setup",
|
|
563
761
|
"npx skilly-hand detect",
|
|
564
762
|
"npx skilly-hand list",
|
|
565
763
|
"npx skilly-hand doctor",
|
|
@@ -597,6 +795,7 @@ export async function runCli({
|
|
|
597
795
|
const effectiveCommand = command || "install";
|
|
598
796
|
await runCommand({
|
|
599
797
|
command: effectiveCommand,
|
|
798
|
+
subcommand,
|
|
600
799
|
flags,
|
|
601
800
|
cwd,
|
|
602
801
|
stdin,
|