@skill-map/cli 0.44.0 → 0.45.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/dist/cli/tutorial/sm-tutorial/SKILL.md +91 -114
- package/dist/cli.js +244 -70
- package/dist/cli.js.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/kernel/index.d.ts +35 -0
- package/dist/kernel/index.js +1 -1
- package/dist/kernel/index.js.map +1 -1
- package/dist/ui/{chunk-DL5EA245.js → chunk-CBI77N5U.js} +3 -3
- package/dist/ui/index.html +1 -1
- package/dist/ui/{main-O3CWFYKV.js → main-ERCTR2PR.js} +1 -1
- package/package.json +2 -2
package/dist/cli.js
CHANGED
|
@@ -462,6 +462,11 @@ var claudeProvider = {
|
|
|
462
462
|
// a Claude Code project. Provider-owned (replaces the old central
|
|
463
463
|
// detection table in `src/core/config/active-provider.ts`).
|
|
464
464
|
detect: { markers: [".claude"] },
|
|
465
|
+
// Authoring target for `sm tutorial`: Claude Code discovers skills under
|
|
466
|
+
// `.claude/skills/<name>/SKILL.md`, so a materialised tutorial folder
|
|
467
|
+
// lands there. This is the WRITE side of the territory the `classify`
|
|
468
|
+
// below READS.
|
|
469
|
+
scaffold: { skillDir: ".claude/skills" },
|
|
465
470
|
// Vendor provider: Claude Code only reads its own `.claude/` territory
|
|
466
471
|
// and ignores `.codex/` / Antigravity layouts at runtime. Gating the
|
|
467
472
|
// classifier behind the active lens prevents the walker from inventing
|
|
@@ -1147,6 +1152,13 @@ var agentSkillsProvider = {
|
|
|
1147
1152
|
// (Antigravity adopted the open standard), so such projects auto-detect
|
|
1148
1153
|
// as this universal lens. Provider-owned.
|
|
1149
1154
|
detect: { markers: [".agents"] },
|
|
1155
|
+
// Authoring target for `sm tutorial`: the open standard discovers skills
|
|
1156
|
+
// under `.agents/skills/<name>/SKILL.md`. The same path is consumed by
|
|
1157
|
+
// Antigravity (adopted the standard rather than a `.gemini/` layout) and
|
|
1158
|
+
// OpenAI Codex (skills mirror the open standard), so `aka` surfaces both
|
|
1159
|
+
// names in the destination prompt to orient testers on those agents.
|
|
1160
|
+
// `aka` is display-only, `--for` still matches the `agent-skills` id.
|
|
1161
|
+
scaffold: { skillDir: ".agents/skills", aka: ["Antigravity", "OpenAI Codex"] },
|
|
1150
1162
|
read: { extensions: [".md"], parser: "frontmatter-yaml" },
|
|
1151
1163
|
kinds: {
|
|
1152
1164
|
skill: {
|
|
@@ -3948,7 +3960,7 @@ var UPDATE_CHECK_TEXTS = {
|
|
|
3948
3960
|
// package.json
|
|
3949
3961
|
var package_default = {
|
|
3950
3962
|
name: "@skill-map/cli",
|
|
3951
|
-
version: "0.
|
|
3963
|
+
version: "0.45.0",
|
|
3952
3964
|
description: "skill-map reference implementation \u2014 kernel + CLI + adapters.",
|
|
3953
3965
|
license: "MIT",
|
|
3954
3966
|
type: "module",
|
|
@@ -4418,40 +4430,40 @@ var updateCheckHook = {
|
|
|
4418
4430
|
};
|
|
4419
4431
|
|
|
4420
4432
|
// plugins/built-ins.ts
|
|
4421
|
-
var claudeProvider2 = { ...claudeProvider, pluginId: "claude", version: "0.
|
|
4422
|
-
var atDirectiveExtractor2 = { ...atDirectiveExtractor, pluginId: "claude", version: "0.
|
|
4423
|
-
var slashCommandExtractor2 = { ...slashCommandExtractor, pluginId: "claude", version: "0.
|
|
4424
|
-
var antigravityProvider2 = { ...antigravityProvider, pluginId: "antigravity", version: "0.
|
|
4425
|
-
var openaiProvider2 = { ...openaiProvider, pluginId: "openai", version: "0.
|
|
4426
|
-
var agentSkillsProvider2 = { ...agentSkillsProvider, pluginId: "agent-skills", version: "0.
|
|
4427
|
-
var coreMarkdownProvider2 = { ...coreMarkdownProvider, pluginId: "core", version: "0.
|
|
4428
|
-
var annotationsExtractor2 = { ...annotationsExtractor, pluginId: "core", version: "0.
|
|
4429
|
-
var externalUrlCounterExtractor2 = { ...externalUrlCounterExtractor, pluginId: "core", version: "0.
|
|
4430
|
-
var markdownLinkExtractor2 = { ...markdownLinkExtractor, pluginId: "core", version: "0.
|
|
4431
|
-
var mcpToolsExtractor2 = { ...mcpToolsExtractor, pluginId: "core", version: "0.
|
|
4432
|
-
var toolsCounterExtractor2 = { ...toolsCounterExtractor, pluginId: "core", version: "0.
|
|
4433
|
-
var annotationFieldUnknownAnalyzer2 = { ...annotationFieldUnknownAnalyzer, pluginId: "core", version: "0.
|
|
4434
|
-
var annotationOrphanAnalyzer2 = { ...annotationOrphanAnalyzer, pluginId: "core", version: "0.
|
|
4435
|
-
var annotationStaleAnalyzer2 = { ...annotationStaleAnalyzer, pluginId: "core", version: "0.
|
|
4436
|
-
var contributionOrphanAnalyzer2 = { ...contributionOrphanAnalyzer, pluginId: "core", version: "0.
|
|
4437
|
-
var issueCounterAnalyzer2 = { ...issueCounterAnalyzer, pluginId: "core", version: "0.
|
|
4438
|
-
var jobFileOrphanAnalyzer2 = { ...jobFileOrphanAnalyzer, pluginId: "core", version: "0.
|
|
4439
|
-
var linkConflictAnalyzer2 = { ...linkConflictAnalyzer, pluginId: "core", version: "0.
|
|
4440
|
-
var linkCounterAnalyzer2 = { ...linkCounterAnalyzer, pluginId: "core", version: "0.
|
|
4441
|
-
var linkSelfLoopAnalyzer2 = { ...linkSelfLoopAnalyzer, pluginId: "core", version: "0.
|
|
4442
|
-
var nameReservedAnalyzer2 = { ...nameReservedAnalyzer, pluginId: "core", version: "0.
|
|
4443
|
-
var nodeStabilityAnalyzer2 = { ...nodeStabilityAnalyzer, pluginId: "core", version: "0.
|
|
4444
|
-
var nodeSupersededAnalyzer2 = { ...nodeSupersededAnalyzer, pluginId: "core", version: "0.
|
|
4445
|
-
var referenceBrokenAnalyzer2 = { ...referenceBrokenAnalyzer, pluginId: "core", version: "0.
|
|
4446
|
-
var referenceRedundantAnalyzer2 = { ...referenceRedundantAnalyzer, pluginId: "core", version: "0.
|
|
4447
|
-
var schemaViolationAnalyzer2 = { ...schemaViolationAnalyzer, pluginId: "core", version: "0.
|
|
4448
|
-
var signalCollisionAnalyzer2 = { ...signalCollisionAnalyzer, pluginId: "core", version: "0.
|
|
4449
|
-
var triggerCollisionAnalyzer2 = { ...triggerCollisionAnalyzer, pluginId: "core", version: "0.
|
|
4450
|
-
var asciiFormatter2 = { ...asciiFormatter, pluginId: "core", version: "0.
|
|
4451
|
-
var jsonFormatter2 = { ...jsonFormatter, pluginId: "core", version: "0.
|
|
4452
|
-
var nodeBumpAction2 = { ...nodeBumpAction, pluginId: "core", version: "0.
|
|
4453
|
-
var nodeSupersedeAction2 = { ...nodeSupersedeAction, pluginId: "core", version: "0.
|
|
4454
|
-
var updateCheckHook2 = { ...updateCheckHook, pluginId: "core", version: "0.
|
|
4433
|
+
var claudeProvider2 = { ...claudeProvider, pluginId: "claude", version: "0.45.0" };
|
|
4434
|
+
var atDirectiveExtractor2 = { ...atDirectiveExtractor, pluginId: "claude", version: "0.45.0" };
|
|
4435
|
+
var slashCommandExtractor2 = { ...slashCommandExtractor, pluginId: "claude", version: "0.45.0" };
|
|
4436
|
+
var antigravityProvider2 = { ...antigravityProvider, pluginId: "antigravity", version: "0.45.0" };
|
|
4437
|
+
var openaiProvider2 = { ...openaiProvider, pluginId: "openai", version: "0.45.0" };
|
|
4438
|
+
var agentSkillsProvider2 = { ...agentSkillsProvider, pluginId: "agent-skills", version: "0.45.0" };
|
|
4439
|
+
var coreMarkdownProvider2 = { ...coreMarkdownProvider, pluginId: "core", version: "0.45.0" };
|
|
4440
|
+
var annotationsExtractor2 = { ...annotationsExtractor, pluginId: "core", version: "0.45.0" };
|
|
4441
|
+
var externalUrlCounterExtractor2 = { ...externalUrlCounterExtractor, pluginId: "core", version: "0.45.0" };
|
|
4442
|
+
var markdownLinkExtractor2 = { ...markdownLinkExtractor, pluginId: "core", version: "0.45.0" };
|
|
4443
|
+
var mcpToolsExtractor2 = { ...mcpToolsExtractor, pluginId: "core", version: "0.45.0" };
|
|
4444
|
+
var toolsCounterExtractor2 = { ...toolsCounterExtractor, pluginId: "core", version: "0.45.0" };
|
|
4445
|
+
var annotationFieldUnknownAnalyzer2 = { ...annotationFieldUnknownAnalyzer, pluginId: "core", version: "0.45.0" };
|
|
4446
|
+
var annotationOrphanAnalyzer2 = { ...annotationOrphanAnalyzer, pluginId: "core", version: "0.45.0" };
|
|
4447
|
+
var annotationStaleAnalyzer2 = { ...annotationStaleAnalyzer, pluginId: "core", version: "0.45.0" };
|
|
4448
|
+
var contributionOrphanAnalyzer2 = { ...contributionOrphanAnalyzer, pluginId: "core", version: "0.45.0" };
|
|
4449
|
+
var issueCounterAnalyzer2 = { ...issueCounterAnalyzer, pluginId: "core", version: "0.45.0" };
|
|
4450
|
+
var jobFileOrphanAnalyzer2 = { ...jobFileOrphanAnalyzer, pluginId: "core", version: "0.45.0" };
|
|
4451
|
+
var linkConflictAnalyzer2 = { ...linkConflictAnalyzer, pluginId: "core", version: "0.45.0" };
|
|
4452
|
+
var linkCounterAnalyzer2 = { ...linkCounterAnalyzer, pluginId: "core", version: "0.45.0" };
|
|
4453
|
+
var linkSelfLoopAnalyzer2 = { ...linkSelfLoopAnalyzer, pluginId: "core", version: "0.45.0" };
|
|
4454
|
+
var nameReservedAnalyzer2 = { ...nameReservedAnalyzer, pluginId: "core", version: "0.45.0" };
|
|
4455
|
+
var nodeStabilityAnalyzer2 = { ...nodeStabilityAnalyzer, pluginId: "core", version: "0.45.0" };
|
|
4456
|
+
var nodeSupersededAnalyzer2 = { ...nodeSupersededAnalyzer, pluginId: "core", version: "0.45.0" };
|
|
4457
|
+
var referenceBrokenAnalyzer2 = { ...referenceBrokenAnalyzer, pluginId: "core", version: "0.45.0" };
|
|
4458
|
+
var referenceRedundantAnalyzer2 = { ...referenceRedundantAnalyzer, pluginId: "core", version: "0.45.0" };
|
|
4459
|
+
var schemaViolationAnalyzer2 = { ...schemaViolationAnalyzer, pluginId: "core", version: "0.45.0" };
|
|
4460
|
+
var signalCollisionAnalyzer2 = { ...signalCollisionAnalyzer, pluginId: "core", version: "0.45.0" };
|
|
4461
|
+
var triggerCollisionAnalyzer2 = { ...triggerCollisionAnalyzer, pluginId: "core", version: "0.45.0" };
|
|
4462
|
+
var asciiFormatter2 = { ...asciiFormatter, pluginId: "core", version: "0.45.0" };
|
|
4463
|
+
var jsonFormatter2 = { ...jsonFormatter, pluginId: "core", version: "0.45.0" };
|
|
4464
|
+
var nodeBumpAction2 = { ...nodeBumpAction, pluginId: "core", version: "0.45.0" };
|
|
4465
|
+
var nodeSupersedeAction2 = { ...nodeSupersedeAction, pluginId: "core", version: "0.45.0" };
|
|
4466
|
+
var updateCheckHook2 = { ...updateCheckHook, pluginId: "core", version: "0.45.0" };
|
|
4455
4467
|
var builtInBundles = [
|
|
4456
4468
|
{
|
|
4457
4469
|
id: "claude",
|
|
@@ -17000,13 +17012,13 @@ var SCAN_RUNNER_TEXTS = {
|
|
|
17000
17012
|
* Active-provider bootstrap: filesystem auto-detect found exactly
|
|
17001
17013
|
* one marker and persisted the detected id to project settings.
|
|
17002
17014
|
*/
|
|
17003
|
-
activeProviderAutodetected: "Auto-detected activeProvider = {{id}} from filesystem markers; persisted to .skill-map/settings.json
|
|
17015
|
+
activeProviderAutodetected: "Auto-detected activeProvider = {{id}} from filesystem markers; persisted to .skill-map/settings.json.\n",
|
|
17004
17016
|
/**
|
|
17005
17017
|
* Active-provider bootstrap: persistence of the auto-detected id
|
|
17006
17018
|
* failed (permission, disk full, etc). Non-fatal; the scan
|
|
17007
17019
|
* continues with the value in memory for this run.
|
|
17008
17020
|
*/
|
|
17009
|
-
activeProviderPersistFailed: "Auto-detected activeProvider = {{id}}, but persisting to .skill-map/settings.json failed: {{message}}. Run `sm config set activeProvider {{id}}` manually to make the choice sticky
|
|
17021
|
+
activeProviderPersistFailed: "Auto-detected activeProvider = {{id}}, but persisting to .skill-map/settings.json failed: {{message}}. Run `sm config set activeProvider {{id}}` manually to make the choice sticky.\n",
|
|
17010
17022
|
/**
|
|
17011
17023
|
* Active-provider bootstrap: ambiguous detection (2+ markers
|
|
17012
17024
|
* present), interactive prompt header. Follows
|
|
@@ -17699,7 +17711,7 @@ var INIT_TEXTS = {
|
|
|
17699
17711
|
* scan never inherits stale rows from a pre-current schema.
|
|
17700
17712
|
*/
|
|
17701
17713
|
removedPriorDb: "{{glyph}} Removed prior DB at {{path}} (--force reset)\n",
|
|
17702
|
-
runningFirstScan: "
|
|
17714
|
+
runningFirstScan: "\nRunning first scan...\n",
|
|
17703
17715
|
configLoadFailure: "{{glyph}} sm init: {{message}}\n",
|
|
17704
17716
|
scanFailed: "{{glyph}} sm init: scan failed: {{message}}\n",
|
|
17705
17717
|
firstScanSummary: "{{glyph}} First scan: {{nodes}} node{{nodesPlural}}, {{links}} link{{linksPlural}}, {{issues}} issue{{issuesPlural}}.\n",
|
|
@@ -28035,31 +28047,54 @@ var STUB_COMMANDS = [
|
|
|
28035
28047
|
];
|
|
28036
28048
|
|
|
28037
28049
|
// cli/commands/tutorial.ts
|
|
28038
|
-
import { cpSync as cpSync2, existsSync as existsSync31, mkdirSync as mkdirSync6, rmSync as rmSync2, statSync as statSync11 } from "fs";
|
|
28050
|
+
import { cpSync as cpSync2, existsSync as existsSync31, mkdirSync as mkdirSync6, readdirSync as readdirSync10, rmSync as rmSync2, statSync as statSync11 } from "fs";
|
|
28039
28051
|
import { dirname as dirname19, join as join21, resolve as resolve39 } from "path";
|
|
28052
|
+
import { createInterface as createInterface5 } from "readline";
|
|
28040
28053
|
import { fileURLToPath as fileURLToPath7 } from "url";
|
|
28041
28054
|
import { Command as Command37, Option as Option35 } from "clipanion";
|
|
28042
28055
|
|
|
28043
28056
|
// cli/i18n/tutorial.texts.ts
|
|
28044
28057
|
var TUTORIAL_TEXTS = {
|
|
28045
28058
|
// Success, written to stdout after `<cwd>/{{target}}` is created.
|
|
28046
|
-
// The skill
|
|
28059
|
+
// The skill lives at `<skillDir>/<slug>/`; the tester's agent
|
|
28047
28060
|
// auto-discovers it on the next boot, so the tester invokes by
|
|
28048
28061
|
// speaking a trigger phrase rather than referencing the file path.
|
|
28049
|
-
//
|
|
28050
|
-
//
|
|
28051
|
-
//
|
|
28052
|
-
|
|
28062
|
+
// Message stays host-agnostic ("your coding agent") because the
|
|
28063
|
+
// destination Provider varies. English / Spanish triggers are
|
|
28064
|
+
// surfaced side by side and the first phrase the tester types sets
|
|
28065
|
+
// the tutorial language for the rest of the session.
|
|
28066
|
+
written: " {{glyph}} Skill `{{slug}}` materialized at {{target}} (under {{cwd}}, for {{provider}})\n\n Open your coding agent in this directory. The skill is auto-\n discovered; invoke it with one of its trigger phrases. The\n first message you type sets the tutorial language for the\n rest of the session:\n\n {{enLabel}} {{enTrigger}}\n {{esLabel}} {{esTrigger}}\n",
|
|
28053
28067
|
writtenLabelEn: "English",
|
|
28054
28068
|
writtenLabelEs: "Espa\xF1ol",
|
|
28055
|
-
//
|
|
28056
|
-
//
|
|
28057
|
-
//
|
|
28058
|
-
//
|
|
28059
|
-
|
|
28060
|
-
|
|
28069
|
+
// Destination-provider prompt (interactive stdin, no `--for`). Header
|
|
28070
|
+
// uses a yellow `?` glyph; options are a numbered list of provider
|
|
28071
|
+
// label (with any `aka` agents in parentheses) + skill directory, with
|
|
28072
|
+
// a `(default)` marker on the first option (Claude). The input line
|
|
28073
|
+
// accepts a number, a provider id, or an empty answer (which takes the
|
|
28074
|
+
// default).
|
|
28075
|
+
promptHeader: "{{glyph}} Which agent should host the tutorial skill?",
|
|
28076
|
+
promptOption: " {{index}}) {{label}}: {{skillDir}}{{marker}}",
|
|
28077
|
+
promptDefaultMarker: " (default)",
|
|
28078
|
+
promptInput: " Enter the number or provider id [default {{index}}]: ",
|
|
28079
|
+
// Prompt answer matched neither an index nor an id. Goes to stderr,
|
|
28080
|
+
// exit code 2. Mirrors the error shape: glyph + headline + dim hint.
|
|
28081
|
+
promptInvalid: "{{glyph}} sm tutorial: that is not one of the listed providers\n {{hint}}\n",
|
|
28082
|
+
// `--for` named a provider that does not exist or declares no
|
|
28083
|
+
// `scaffold.skillDir`. Goes to stderr, exit code 2.
|
|
28084
|
+
forUnknown: "{{glyph}} sm tutorial: unknown provider '{{provider}}' for --for\n {{hint}}\n",
|
|
28085
|
+
forUnknownHint: "Valid providers: {{ids}}.",
|
|
28086
|
+
// Defensive: no built-in provider declares a scaffold target. Should
|
|
28087
|
+
// never happen (claude always does). Goes to stderr, exit code 2.
|
|
28088
|
+
noTargets: "{{glyph}} sm tutorial: no provider declares a skill scaffold target.\n",
|
|
28089
|
+
// Refusal, the cwd is not empty and `--force` was not set. Goes to
|
|
28090
|
+
// stderr, exit code 2 (operational error per spec § Exit codes). The
|
|
28091
|
+
// tutorial seeds a self-contained scenario into the cwd, so it needs
|
|
28092
|
+
// an empty directory; the hint spells the two ways forward. Mirrors
|
|
28093
|
+
// the error shape: glyph + headline + dim hint.
|
|
28094
|
+
notEmpty: "{{glyph}} sm tutorial: the current directory is not empty (found {{entries}})\n {{hint}}\n",
|
|
28095
|
+
notEmptyHint: "sm tutorial seeds a self-contained scenario; run it in a fresh empty directory, or pass `--force` to use this one anyway.",
|
|
28061
28096
|
// Invalid `variant` positional argument. Goes to stderr, exit code 2.
|
|
28062
|
-
// Mirrors
|
|
28097
|
+
// Mirrors the error shape: glyph + headline + dim hint enumerating the
|
|
28063
28098
|
// valid values.
|
|
28064
28099
|
invalidVariant: "{{glyph}} sm tutorial: unknown variant '{{variant}}'\n {{hint}}\n",
|
|
28065
28100
|
invalidVariantHint: "Valid values: tutorial (default), master.",
|
|
@@ -28111,6 +28146,13 @@ var TutorialCommand = class extends SmCommand {
|
|
|
28111
28146
|
]
|
|
28112
28147
|
});
|
|
28113
28148
|
variant = Option35.String({ required: false });
|
|
28149
|
+
// Named `forProvider`, NOT `for` (reserved word). The CLI surface stays
|
|
28150
|
+
// `--for`; selects the destination Provider whose `scaffold.skillDir`
|
|
28151
|
+
// the skill is materialised under, skipping the interactive prompt.
|
|
28152
|
+
forProvider = Option35.String("--for", {
|
|
28153
|
+
required: false,
|
|
28154
|
+
description: "Destination provider id (e.g. claude, agent-skills). Skips the prompt."
|
|
28155
|
+
});
|
|
28114
28156
|
force = Option35.Boolean("--force", false, {
|
|
28115
28157
|
description: "Overwrite an existing target directory without prompting."
|
|
28116
28158
|
});
|
|
@@ -28119,32 +28161,24 @@ var TutorialCommand = class extends SmCommand {
|
|
|
28119
28161
|
const stderr = this.context.stderr;
|
|
28120
28162
|
const stderrAnsi = this.ansiFor("stderr");
|
|
28121
28163
|
const errGlyph = stderrAnsi.red("\u2715");
|
|
28122
|
-
const
|
|
28123
|
-
if (
|
|
28124
|
-
this.printer.error(
|
|
28125
|
-
tx(TUTORIAL_TEXTS.invalidVariant, {
|
|
28126
|
-
glyph: errGlyph,
|
|
28127
|
-
variant: rawVariant,
|
|
28128
|
-
hint: stderrAnsi.dim(TUTORIAL_TEXTS.invalidVariantHint)
|
|
28129
|
-
})
|
|
28130
|
-
);
|
|
28131
|
-
return ExitCode.Error;
|
|
28132
|
-
}
|
|
28133
|
-
const variant = rawVariant ?? DEFAULT_VARIANT;
|
|
28164
|
+
const variant = this.resolveVariantArg(errGlyph, stderrAnsi);
|
|
28165
|
+
if (variant === null) return ExitCode.Error;
|
|
28134
28166
|
const spec = VARIANT_SPECS[variant];
|
|
28135
|
-
|
|
28136
|
-
const targetDisplay = `.claude/skills/${spec.slug}/`;
|
|
28137
|
-
if (existsSync31(targetDir) && !this.force) {
|
|
28167
|
+
if (!this.force && !isDirEmpty(ctx.cwd)) {
|
|
28138
28168
|
this.printer.error(
|
|
28139
|
-
tx(TUTORIAL_TEXTS.
|
|
28169
|
+
tx(TUTORIAL_TEXTS.notEmpty, {
|
|
28140
28170
|
glyph: errGlyph,
|
|
28141
|
-
|
|
28142
|
-
|
|
28143
|
-
hint: stderrAnsi.dim(TUTORIAL_TEXTS.alreadyExistsHint)
|
|
28171
|
+
entries: listCwdEntries(ctx.cwd),
|
|
28172
|
+
hint: stderrAnsi.dim(TUTORIAL_TEXTS.notEmptyHint)
|
|
28144
28173
|
})
|
|
28145
28174
|
);
|
|
28146
28175
|
return ExitCode.Error;
|
|
28147
28176
|
}
|
|
28177
|
+
const targets = listScaffoldTargets();
|
|
28178
|
+
const target = await this.resolveScaffoldTarget(targets, stderrAnsi, errGlyph);
|
|
28179
|
+
if (target === null) return ExitCode.Error;
|
|
28180
|
+
const targetDir = join21(ctx.cwd, target.skillDir, spec.slug);
|
|
28181
|
+
const targetDisplay = `${target.skillDir}/${spec.slug}/`;
|
|
28148
28182
|
let sourceDir;
|
|
28149
28183
|
try {
|
|
28150
28184
|
sourceDir = resolveSkillSourceDir(variant);
|
|
@@ -28184,6 +28218,7 @@ var TutorialCommand = class extends SmCommand {
|
|
|
28184
28218
|
glyph: ansi.green("\u2713"),
|
|
28185
28219
|
slug: spec.slug,
|
|
28186
28220
|
target: targetDisplay,
|
|
28221
|
+
provider: ansi.dim(target.label),
|
|
28187
28222
|
cwd: ansi.dim(displayCwd(ctx.cwd)),
|
|
28188
28223
|
enLabel: ansi.dim(TUTORIAL_TEXTS.writtenLabelEn),
|
|
28189
28224
|
esLabel: ansi.dim(TUTORIAL_TEXTS.writtenLabelEs),
|
|
@@ -28193,15 +28228,154 @@ var TutorialCommand = class extends SmCommand {
|
|
|
28193
28228
|
);
|
|
28194
28229
|
return ExitCode.Ok;
|
|
28195
28230
|
}
|
|
28231
|
+
/**
|
|
28232
|
+
* Validate the positional `variant` arg against the closed catalog.
|
|
28233
|
+
* Returns the resolved variant, or `null` after printing the
|
|
28234
|
+
* `invalidVariant` error (caller exits non-zero). Extracted from
|
|
28235
|
+
* `run()` to keep its cyclomatic complexity within the lint budget.
|
|
28236
|
+
*/
|
|
28237
|
+
resolveVariantArg(errGlyph, stderrAnsi) {
|
|
28238
|
+
const rawVariant = this.variant;
|
|
28239
|
+
if (rawVariant !== void 0 && !isTutorialVariant(rawVariant)) {
|
|
28240
|
+
this.printer.error(
|
|
28241
|
+
tx(TUTORIAL_TEXTS.invalidVariant, {
|
|
28242
|
+
glyph: errGlyph,
|
|
28243
|
+
variant: rawVariant,
|
|
28244
|
+
hint: stderrAnsi.dim(TUTORIAL_TEXTS.invalidVariantHint)
|
|
28245
|
+
})
|
|
28246
|
+
);
|
|
28247
|
+
return null;
|
|
28248
|
+
}
|
|
28249
|
+
return rawVariant ?? DEFAULT_VARIANT;
|
|
28250
|
+
}
|
|
28251
|
+
/**
|
|
28252
|
+
* Resolve the destination Provider. Precedence:
|
|
28253
|
+
* 1. `--for <id>` (validated against the scaffold-capable catalog).
|
|
28254
|
+
* 2. Interactive stdin → numbered prompt defaulting to Claude (the
|
|
28255
|
+
* first entry); an empty answer accepts it.
|
|
28256
|
+
* 3. Non-interactive stdin → Claude (the first entry), so the verb
|
|
28257
|
+
* stays scriptable.
|
|
28258
|
+
* The verb requires an empty cwd, so there is no marker to detect: the
|
|
28259
|
+
* default is always the first scaffold-capable Provider (Claude).
|
|
28260
|
+
* Returns `null` after printing an error (caller exits non-zero).
|
|
28261
|
+
*/
|
|
28262
|
+
async resolveScaffoldTarget(targets, stderrAnsi, errGlyph) {
|
|
28263
|
+
if (targets.length === 0) {
|
|
28264
|
+
this.printer.error(tx(TUTORIAL_TEXTS.noTargets, { glyph: errGlyph }));
|
|
28265
|
+
return null;
|
|
28266
|
+
}
|
|
28267
|
+
const requested = this.forProvider;
|
|
28268
|
+
if (requested !== void 0) {
|
|
28269
|
+
const found = targets.find((t) => t.id === requested);
|
|
28270
|
+
if (found === void 0) {
|
|
28271
|
+
this.printer.error(
|
|
28272
|
+
tx(TUTORIAL_TEXTS.forUnknown, {
|
|
28273
|
+
glyph: errGlyph,
|
|
28274
|
+
provider: requested,
|
|
28275
|
+
hint: stderrAnsi.dim(
|
|
28276
|
+
tx(TUTORIAL_TEXTS.forUnknownHint, { ids: targets.map((t) => t.id).join(", ") })
|
|
28277
|
+
)
|
|
28278
|
+
})
|
|
28279
|
+
);
|
|
28280
|
+
return null;
|
|
28281
|
+
}
|
|
28282
|
+
return found;
|
|
28283
|
+
}
|
|
28284
|
+
const defaultIndex = 0;
|
|
28285
|
+
const def = targets[defaultIndex];
|
|
28286
|
+
const stdin = this.context.stdin;
|
|
28287
|
+
if (targets.length === 1 || stdin.isTTY !== true) return def;
|
|
28288
|
+
const stderr = this.context.stderr;
|
|
28289
|
+
const picked = await promptForTarget(
|
|
28290
|
+
targets,
|
|
28291
|
+
defaultIndex,
|
|
28292
|
+
stdin,
|
|
28293
|
+
stderr,
|
|
28294
|
+
stderrAnsi.yellow("?")
|
|
28295
|
+
);
|
|
28296
|
+
if (picked === null) {
|
|
28297
|
+
this.printer.error(
|
|
28298
|
+
tx(TUTORIAL_TEXTS.promptInvalid, {
|
|
28299
|
+
glyph: errGlyph,
|
|
28300
|
+
hint: stderrAnsi.dim(
|
|
28301
|
+
tx(TUTORIAL_TEXTS.forUnknownHint, { ids: targets.map((t) => t.id).join(", ") })
|
|
28302
|
+
)
|
|
28303
|
+
})
|
|
28304
|
+
);
|
|
28305
|
+
return null;
|
|
28306
|
+
}
|
|
28307
|
+
return picked;
|
|
28308
|
+
}
|
|
28196
28309
|
};
|
|
28197
28310
|
function isTutorialVariant(value) {
|
|
28198
28311
|
return VALID_VARIANTS.includes(value);
|
|
28199
28312
|
}
|
|
28313
|
+
function toScaffoldTarget(provider) {
|
|
28314
|
+
const scaffold = provider.scaffold;
|
|
28315
|
+
if (!scaffold || !scaffold.skillDir) return null;
|
|
28316
|
+
return {
|
|
28317
|
+
id: provider.id,
|
|
28318
|
+
label: provider.presentation.label,
|
|
28319
|
+
skillDir: scaffold.skillDir,
|
|
28320
|
+
aka: scaffold.aka ?? []
|
|
28321
|
+
};
|
|
28322
|
+
}
|
|
28323
|
+
function listScaffoldTargets() {
|
|
28324
|
+
const out = [];
|
|
28325
|
+
for (const provider of builtIns().providers) {
|
|
28326
|
+
const target = toScaffoldTarget(provider);
|
|
28327
|
+
if (target !== null) out.push(target);
|
|
28328
|
+
}
|
|
28329
|
+
return out;
|
|
28330
|
+
}
|
|
28331
|
+
function labelWithAka(target) {
|
|
28332
|
+
return target.aka.length > 0 ? `${target.label} (${target.aka.join(", ")})` : target.label;
|
|
28333
|
+
}
|
|
28334
|
+
async function promptForTarget(targets, defaultIndex, stdin, stderr, glyph) {
|
|
28335
|
+
const lines = [tx(TUTORIAL_TEXTS.promptHeader, { glyph })];
|
|
28336
|
+
for (let i = 0; i < targets.length; i += 1) {
|
|
28337
|
+
const t = targets[i];
|
|
28338
|
+
lines.push(
|
|
28339
|
+
tx(TUTORIAL_TEXTS.promptOption, {
|
|
28340
|
+
index: i + 1,
|
|
28341
|
+
label: labelWithAka(t),
|
|
28342
|
+
skillDir: `${t.skillDir}/`,
|
|
28343
|
+
marker: i === defaultIndex ? TUTORIAL_TEXTS.promptDefaultMarker : ""
|
|
28344
|
+
})
|
|
28345
|
+
);
|
|
28346
|
+
}
|
|
28347
|
+
stderr.write(lines.join("\n") + "\n");
|
|
28348
|
+
const rl = createInterface5({ input: stdin, output: stderr });
|
|
28349
|
+
try {
|
|
28350
|
+
const answer = await new Promise(
|
|
28351
|
+
(resolveP) => rl.question(tx(TUTORIAL_TEXTS.promptInput, { index: defaultIndex + 1 }), resolveP)
|
|
28352
|
+
);
|
|
28353
|
+
const trimmed = answer.trim();
|
|
28354
|
+
if (trimmed === "") return targets[defaultIndex];
|
|
28355
|
+
const asNumber = Number.parseInt(trimmed, 10);
|
|
28356
|
+
if (!Number.isNaN(asNumber) && asNumber >= 1 && asNumber <= targets.length) {
|
|
28357
|
+
return targets[asNumber - 1];
|
|
28358
|
+
}
|
|
28359
|
+
const byId = targets.find((t) => t.id.toLowerCase() === trimmed.toLowerCase());
|
|
28360
|
+
return byId ?? null;
|
|
28361
|
+
} finally {
|
|
28362
|
+
rl.close();
|
|
28363
|
+
}
|
|
28364
|
+
}
|
|
28200
28365
|
function displayCwd(cwd) {
|
|
28201
28366
|
const segments = cwd.split("/").filter((s) => s.length > 0);
|
|
28202
28367
|
if (segments.length === 0) return "./";
|
|
28203
28368
|
return `./${segments[segments.length - 1]}/`;
|
|
28204
28369
|
}
|
|
28370
|
+
function isDirEmpty(dir) {
|
|
28371
|
+
return readdirSync10(dir).length === 0;
|
|
28372
|
+
}
|
|
28373
|
+
function listCwdEntries(dir) {
|
|
28374
|
+
const entries = readdirSync10(dir).sort();
|
|
28375
|
+
const shown = entries.slice(0, 5);
|
|
28376
|
+
const more = entries.length > shown.length ? ", ..." : "";
|
|
28377
|
+
return shown.join(", ") + more;
|
|
28378
|
+
}
|
|
28205
28379
|
var cachedSourceDirs = /* @__PURE__ */ new Map();
|
|
28206
28380
|
function resolveSkillSourceDir(variant) {
|
|
28207
28381
|
const cached = cachedSourceDirs.get(variant);
|