vskill 1.0.13 → 1.0.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +63 -2
- package/agents.json +1 -1
- package/dist/bin.js +0 -0
- package/dist/clone/github-scaffold.d.ts +38 -0
- package/dist/clone/github-scaffold.js +108 -0
- package/dist/clone/github-scaffold.js.map +1 -0
- package/dist/clone/provenance-fork.d.ts +34 -0
- package/dist/clone/provenance-fork.js +97 -0
- package/dist/clone/provenance-fork.js.map +1 -0
- package/dist/clone/reference-scanner.d.ts +19 -0
- package/dist/clone/reference-scanner.js +144 -0
- package/dist/clone/reference-scanner.js.map +1 -0
- package/dist/clone/skill-locator.d.ts +26 -0
- package/dist/clone/skill-locator.js +248 -0
- package/dist/clone/skill-locator.js.map +1 -0
- package/dist/clone/target-router.d.ts +73 -0
- package/dist/clone/target-router.js +200 -0
- package/dist/clone/target-router.js.map +1 -0
- package/dist/clone/types.d.ts +82 -0
- package/dist/clone/types.js +11 -0
- package/dist/clone/types.js.map +1 -0
- package/dist/commands/add.js +96 -32
- package/dist/commands/add.js.map +1 -1
- package/dist/commands/auth.d.ts +23 -0
- package/dist/commands/auth.js +273 -0
- package/dist/commands/auth.js.map +1 -0
- package/dist/commands/check.d.ts +55 -0
- package/dist/commands/check.js +279 -0
- package/dist/commands/check.js.map +1 -0
- package/dist/commands/clone-prompts.d.ts +13 -0
- package/dist/commands/clone-prompts.js +67 -0
- package/dist/commands/clone-prompts.js.map +1 -0
- package/dist/commands/clone.d.ts +70 -0
- package/dist/commands/clone.js +649 -0
- package/dist/commands/clone.js.map +1 -0
- package/dist/commands/eval/serve.js +8 -1
- package/dist/commands/eval/serve.js.map +1 -1
- package/dist/commands/keys.js +54 -2
- package/dist/commands/keys.js.map +1 -1
- package/dist/core/agent-prompts.d.ts +35 -0
- package/dist/core/agent-prompts.js +201 -0
- package/dist/core/agent-prompts.js.map +1 -0
- package/dist/core/skill-generator.d.ts +25 -3
- package/dist/core/skill-generator.js +131 -0
- package/dist/core/skill-generator.js.map +1 -1
- package/dist/eval/skill-scanner.d.ts +2 -12
- package/dist/eval/skill-scanner.js +27 -5
- package/dist/eval/skill-scanner.js.map +1 -1
- package/dist/eval-server/api-routes.d.ts +14 -0
- package/dist/eval-server/api-routes.js +376 -31
- package/dist/eval-server/api-routes.js.map +1 -1
- package/dist/eval-server/data-events.d.ts +1 -1
- package/dist/eval-server/data-events.js.map +1 -1
- package/dist/eval-server/install-engine-routes-helpers.d.ts +1 -3
- package/dist/eval-server/install-engine-routes-helpers.js +6 -14
- package/dist/eval-server/install-engine-routes-helpers.js.map +1 -1
- package/dist/eval-server/origin-resolver.d.ts +42 -0
- package/dist/eval-server/origin-resolver.js +168 -0
- package/dist/eval-server/origin-resolver.js.map +1 -0
- package/dist/eval-server/platform-proxy.d.ts +10 -0
- package/dist/eval-server/platform-proxy.js +58 -2
- package/dist/eval-server/platform-proxy.js.map +1 -1
- package/dist/eval-server/skill-create-routes.d.ts +8 -0
- package/dist/eval-server/skill-create-routes.js +96 -0
- package/dist/eval-server/skill-create-routes.js.map +1 -1
- package/dist/eval-server/skill-resolver.js +40 -0
- package/dist/eval-server/skill-resolver.js.map +1 -1
- package/dist/eval-server/utils/resolve-editor.d.ts +18 -0
- package/dist/eval-server/utils/resolve-editor.js +77 -0
- package/dist/eval-server/utils/resolve-editor.js.map +1 -0
- package/dist/eval-server/utils/scan-install-locations.d.ts +7 -0
- package/dist/eval-server/utils/scan-install-locations.js +20 -0
- package/dist/eval-server/utils/scan-install-locations.js.map +1 -1
- package/dist/eval-server/utils/which.d.ts +15 -0
- package/dist/eval-server/utils/which.js +76 -0
- package/dist/eval-server/utils/which.js.map +1 -0
- package/dist/eval-ui/assets/{CreateSkillPage-T0YWZWw-.js → CreateSkillPage-BmbvQEzE.js} +1 -1
- package/dist/eval-ui/assets/{FindSkillsPalette-KcFM32hZ.js → FindSkillsPalette-D0Zjhm31.js} +2 -2
- package/dist/eval-ui/assets/{SearchPaletteCore-EhBtr4Xx.js → SearchPaletteCore-EhcN1xEa.js} +1 -1
- package/dist/eval-ui/assets/SkillDetailPanel-B5J60ffv.js +1 -0
- package/dist/eval-ui/assets/{UpdateDropdown-pjFhHTi6.js → UpdateDropdown-Celf0_Cr.js} +1 -1
- package/dist/eval-ui/assets/index-BV7k6fdk.js +124 -0
- package/dist/eval-ui/assets/{index-BKAvJDDF.css → index-CKLqBL52.css} +1 -1
- package/dist/eval-ui/index.html +2 -2
- package/dist/index.js +47 -0
- package/dist/index.js.map +1 -1
- package/dist/installer/frontmatter.d.ts +26 -0
- package/dist/installer/frontmatter.js +90 -0
- package/dist/installer/frontmatter.js.map +1 -1
- package/dist/lib/github-fetch.d.ts +22 -0
- package/dist/lib/github-fetch.js +152 -0
- package/dist/lib/github-fetch.js.map +1 -0
- package/dist/lib/keychain.d.ts +41 -0
- package/dist/lib/keychain.js +232 -0
- package/dist/lib/keychain.js.map +1 -0
- package/dist/studio/types.d.ts +13 -0
- package/dist/utils/claude-plugin.d.ts +26 -0
- package/dist/utils/claude-plugin.js +60 -0
- package/dist/utils/claude-plugin.js.map +1 -1
- package/package.json +2 -1
- package/dist/eval-ui/assets/SkillDetailPanel-cyzLsLcK.js +0 -1
- package/dist/eval-ui/assets/index-C3S9iHnq.js +0 -122
package/README.md
CHANGED
|
@@ -9,8 +9,8 @@
|
|
|
9
9
|
<a href="https://www.npmjs.com/package/vskill"><img src="https://img.shields.io/npm/v/vskill?color=cb3837&logo=npm" alt="npm" /></a>
|
|
10
10
|
<a href="https://www.npmjs.com/package/vskill"><img src="https://img.shields.io/npm/dw/vskill?color=cb3837&logo=npm&label=downloads" alt="downloads" /></a>
|
|
11
11
|
<img src="https://img.shields.io/badge/agents-53_platforms-0969DA" alt="53 agents" />
|
|
12
|
-
<img src="https://img.shields.io/badge/plugins-
|
|
13
|
-
<img src="https://img.shields.io/badge/skills-
|
|
12
|
+
<img src="https://img.shields.io/badge/plugins-9-8B5CF6" alt="9 plugins" />
|
|
13
|
+
<img src="https://img.shields.io/badge/skills-16-10B981" alt="16 skills" />
|
|
14
14
|
<a href="https://verified-skill.com"><img src="https://img.shields.io/badge/registry-verified--skill.com-F59E0B" alt="registry" /></a>
|
|
15
15
|
<img src="https://img.shields.io/badge/license-MIT-green" alt="MIT" />
|
|
16
16
|
</p>
|
|
@@ -133,12 +133,73 @@ npx vskill@latest skill new # create a new skill (AI-assisted)
|
|
|
133
133
|
npx vskill@latest eval sweep <skill> # benchmark across models
|
|
134
134
|
npx vskill@latest audit --ci # SARIF v2.1.0 for CI
|
|
135
135
|
npx vskill@latest keys set anthropic # store API keys in ~/.vskill/keys.env
|
|
136
|
+
npx vskill@latest auth login # sign in to GitHub for private skills
|
|
136
137
|
```
|
|
137
138
|
|
|
138
139
|
Full reference → [verified-skill.com/docs/cli-reference](https://verified-skill.com/docs/cli-reference)
|
|
139
140
|
|
|
140
141
|
<br/>
|
|
141
142
|
|
|
143
|
+
## Authentication (GitHub, for private skills)
|
|
144
|
+
|
|
145
|
+
`vskill auth` signs you in to GitHub using the official Device Flow so the CLI
|
|
146
|
+
can install skills from private repos and the local Studio can list your org's
|
|
147
|
+
private catalog.
|
|
148
|
+
|
|
149
|
+
```bash
|
|
150
|
+
npx vskill@latest auth login # interactive Device Flow — copy code, visit URL
|
|
151
|
+
npx vskill@latest auth status # show the current GitHub identity
|
|
152
|
+
npx vskill@latest auth logout # forget the GitHub token
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
How it works:
|
|
156
|
+
|
|
157
|
+
1. `vskill auth login` requests a device + user code from `github.com/login/device/code`.
|
|
158
|
+
2. You visit `https://github.com/login/device` and enter the 8-character code (rendered as `XXXX-XXXX`).
|
|
159
|
+
3. The CLI polls `github.com/login/oauth/access_token` until you authorize, then validates against `api.github.com/user`.
|
|
160
|
+
4. The resulting token is stored in your **OS keychain** (macOS Keychain / Windows DPAPI / libsecret).
|
|
161
|
+
On systems without a keyring daemon, the token falls back to `~/.vskill/keys.env` with mode `0600` and a startup warning.
|
|
162
|
+
|
|
163
|
+
Where the token is used:
|
|
164
|
+
|
|
165
|
+
- **`vskill install <github-url>`** — added as `Authorization: Bearer …` on every fetch to `api.github.com` and `raw.githubusercontent.com`. Public skills still install anonymously.
|
|
166
|
+
- **`vskill studio`** — the local eval-server proxies private routes (`/api/v1/private/*`, `/api/v1/tenants/*`) to verified-skill.com with the bearer header injected at the proxy boundary. Your browser never holds the token.
|
|
167
|
+
|
|
168
|
+
Configuration:
|
|
169
|
+
|
|
170
|
+
- `VSKILL_GITHUB_CLIENT_ID` — the OAuth/App `client_id` used during Device Flow. Defaults are baked in for the public Skill Studio App; set this only if you are running a self-hosted variant.
|
|
171
|
+
|
|
172
|
+
Inspect status of all credentials in one place:
|
|
173
|
+
|
|
174
|
+
```bash
|
|
175
|
+
npx vskill@latest keys list # shows AI provider keys + the github slot
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
### Private skill workflow
|
|
179
|
+
|
|
180
|
+
Once authenticated, installing a private org skill is identical to a public one — the CLI silently attaches the keychain token to every `api.github.com` and `raw.githubusercontent.com` request:
|
|
181
|
+
|
|
182
|
+
```bash
|
|
183
|
+
npx vskill@latest auth login # one-time setup
|
|
184
|
+
npx vskill@latest add https://github.com/<org>/<repo> # private skill installs same as public
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
The local skill bundle on disk **never contains** your GitHub token — the token is used only at fetch time. Your project's `vskill.lock` records `source: "private"` and the org name so future updates re-authenticate correctly.
|
|
188
|
+
|
|
189
|
+
Customer-facing setup walkthrough → [`.specweave/docs/external/private-repos-quickstart.md`](../../../.specweave/docs/external/private-repos-quickstart.md) (in the umbrella repo).
|
|
190
|
+
|
|
191
|
+
## Security & Compliance
|
|
192
|
+
|
|
193
|
+
Skill Studio private-repo support (increment 0826) ships with documented threat model, verification checklist, SOC 2 evidence map, and operational runbooks — all in the umbrella repo under `.specweave/docs/`:
|
|
194
|
+
|
|
195
|
+
- **Customer quickstart** — `.specweave/docs/external/private-repos-quickstart.md`
|
|
196
|
+
- **Vendor security questionnaire** — `.specweave/docs/external/security-questionnaire-template.md`
|
|
197
|
+
- **Threat model & verification checklist** — `.specweave/docs/internal/security/0826-*.md`
|
|
198
|
+
- **SOC 2 evidence map** — `.specweave/docs/internal/compliance/0826-soc2-evidence-map.md`
|
|
199
|
+
- **Operations + rotation runbooks** — `.specweave/docs/internal/runbooks/0826-*.md`
|
|
200
|
+
|
|
201
|
+
<br/>
|
|
202
|
+
|
|
142
203
|
## Recent highlights (0.5.x)
|
|
143
204
|
|
|
144
205
|
- **0.5.129** — Studio Publish: one-click `git push` + open verified-skill.com submit pre-filled
|
package/agents.json
CHANGED
package/dist/bin.js
CHANGED
|
File without changes
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
export interface RunGhResult {
|
|
2
|
+
stdout: string;
|
|
3
|
+
stderr: string;
|
|
4
|
+
code: number;
|
|
5
|
+
}
|
|
6
|
+
export type RunGh = (args: string[], opts?: {
|
|
7
|
+
cwd?: string;
|
|
8
|
+
}) => Promise<RunGhResult>;
|
|
9
|
+
export interface ScaffoldGitHubArgs {
|
|
10
|
+
/** Local path to the directory that will become the GitHub repo root. */
|
|
11
|
+
pluginDir: string;
|
|
12
|
+
/** Repo name (without owner). The owner is whatever `gh` is currently authenticated as. */
|
|
13
|
+
repoName: string;
|
|
14
|
+
/** Optional repo description (passed to `gh repo create -d`). */
|
|
15
|
+
description?: string;
|
|
16
|
+
/** Whether the new repo should be public. Defaults to true. */
|
|
17
|
+
public?: boolean;
|
|
18
|
+
/** Injectable `gh` adapter — defaults to spawning the local `gh` binary. */
|
|
19
|
+
runGh?: RunGh;
|
|
20
|
+
}
|
|
21
|
+
export interface ScaffoldGitHubResult {
|
|
22
|
+
skipped: boolean;
|
|
23
|
+
/** When skipped, a short human-readable reason. */
|
|
24
|
+
reason?: string;
|
|
25
|
+
/** Repo URL when the create step succeeded. */
|
|
26
|
+
repoUrl?: string;
|
|
27
|
+
/** Argv recorded for each `gh` invocation — useful for debugging and tests. */
|
|
28
|
+
invocations: string[][];
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Run `gh repo create <name> --public/--private --source <pluginDir> --push`.
|
|
32
|
+
*
|
|
33
|
+
* The `--source <dir> --push` form initializes a git repo if needed and pushes
|
|
34
|
+
* the initial commit in one step. When `gh` is missing or the create step
|
|
35
|
+
* fails, the function resolves with `skipped: true` and a clear reason —
|
|
36
|
+
* never throws.
|
|
37
|
+
*/
|
|
38
|
+
export declare function scaffoldGitHub(args: ScaffoldGitHubArgs): Promise<ScaffoldGitHubResult>;
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
// ---------------------------------------------------------------------------
|
|
2
|
+
// vskill clone — github-scaffold
|
|
3
|
+
// ---------------------------------------------------------------------------
|
|
4
|
+
// Optional `gh repo create` + initial commit/push for a freshly-scaffolded
|
|
5
|
+
// new-plugin directory. The `runGh` adapter is injectable so unit tests can
|
|
6
|
+
// substitute a recording fake; the default adapter shells out to the local
|
|
7
|
+
// `gh` binary via node:child_process.
|
|
8
|
+
//
|
|
9
|
+
// Failure modes:
|
|
10
|
+
// - `gh` not installed (ENOENT) → resolve with { skipped: true, reason }
|
|
11
|
+
// - `gh repo create` rejects (non-zero exit) → resolve with { skipped: true, reason }
|
|
12
|
+
//
|
|
13
|
+
// In both cases the on-disk plugin remains intact — the orchestrator surfaces
|
|
14
|
+
// the warning but reports the overall clone as a success (AC-US3-02).
|
|
15
|
+
//
|
|
16
|
+
// Sequencing: this function is called ONLY after the plugin's `.tmp` has been
|
|
17
|
+
// atomically renamed into place (AC-US3-03), so a partial-state failure here
|
|
18
|
+
// can never corrupt the destination plugin.
|
|
19
|
+
// ---------------------------------------------------------------------------
|
|
20
|
+
import { spawn } from "node:child_process";
|
|
21
|
+
/**
|
|
22
|
+
* Default `runGh` adapter — spawn the local `gh` binary.
|
|
23
|
+
*
|
|
24
|
+
* Surfacing semantics: never throws on a non-zero exit. ENOENT (binary not on
|
|
25
|
+
* PATH) is forwarded as a thrown error so the caller can convert it to a
|
|
26
|
+
* graceful skip with a clear "gh not installed" message.
|
|
27
|
+
*/
|
|
28
|
+
function defaultRunGh(args, opts = {}) {
|
|
29
|
+
return new Promise((resolve, reject) => {
|
|
30
|
+
const child = spawn("gh", args, {
|
|
31
|
+
cwd: opts.cwd,
|
|
32
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
33
|
+
});
|
|
34
|
+
let stdout = "";
|
|
35
|
+
let stderr = "";
|
|
36
|
+
child.stdout?.on("data", (chunk) => {
|
|
37
|
+
stdout += chunk.toString();
|
|
38
|
+
});
|
|
39
|
+
child.stderr?.on("data", (chunk) => {
|
|
40
|
+
stderr += chunk.toString();
|
|
41
|
+
});
|
|
42
|
+
child.on("error", (err) => {
|
|
43
|
+
reject(err);
|
|
44
|
+
});
|
|
45
|
+
child.on("close", (code) => {
|
|
46
|
+
resolve({ stdout, stderr, code: code ?? 0 });
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
function isENOENT(err) {
|
|
51
|
+
return Boolean(err) && err.code === "ENOENT";
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Run `gh repo create <name> --public/--private --source <pluginDir> --push`.
|
|
55
|
+
*
|
|
56
|
+
* The `--source <dir> --push` form initializes a git repo if needed and pushes
|
|
57
|
+
* the initial commit in one step. When `gh` is missing or the create step
|
|
58
|
+
* fails, the function resolves with `skipped: true` and a clear reason —
|
|
59
|
+
* never throws.
|
|
60
|
+
*/
|
|
61
|
+
export async function scaffoldGitHub(args) {
|
|
62
|
+
const runGh = args.runGh ?? defaultRunGh;
|
|
63
|
+
const isPublic = args.public !== false;
|
|
64
|
+
const visibilityFlag = isPublic ? "--public" : "--private";
|
|
65
|
+
const invocations = [];
|
|
66
|
+
const createArgv = ["repo", "create", args.repoName, visibilityFlag];
|
|
67
|
+
if (args.description) {
|
|
68
|
+
createArgv.push("--description", args.description);
|
|
69
|
+
}
|
|
70
|
+
// --source + --push will initialize a git repo (if absent), commit, and push.
|
|
71
|
+
createArgv.push("--source", args.pluginDir, "--push");
|
|
72
|
+
let createResult;
|
|
73
|
+
try {
|
|
74
|
+
invocations.push([...createArgv]);
|
|
75
|
+
createResult = await runGh(createArgv, { cwd: args.pluginDir });
|
|
76
|
+
}
|
|
77
|
+
catch (err) {
|
|
78
|
+
if (isENOENT(err)) {
|
|
79
|
+
return {
|
|
80
|
+
skipped: true,
|
|
81
|
+
reason: "gh CLI not installed (binary not found on PATH)",
|
|
82
|
+
invocations,
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
return {
|
|
86
|
+
skipped: true,
|
|
87
|
+
reason: `gh invocation failed: ${err.message}`,
|
|
88
|
+
invocations,
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
if (createResult.code !== 0) {
|
|
92
|
+
return {
|
|
93
|
+
skipped: true,
|
|
94
|
+
reason: `gh repo create exited ${createResult.code}: ${createResult.stderr.trim() || createResult.stdout.trim()}`,
|
|
95
|
+
invocations,
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
// `gh repo create --source --push` prints the repo URL to stdout. The exact
|
|
99
|
+
// line varies but always contains a `https://github.com/<owner>/<name>`
|
|
100
|
+
// segment — extract the first such occurrence as the repoUrl.
|
|
101
|
+
const urlMatch = createResult.stdout.match(/https:\/\/github\.com\/[\w.-]+\/[\w.-]+/);
|
|
102
|
+
return {
|
|
103
|
+
skipped: false,
|
|
104
|
+
repoUrl: urlMatch ? urlMatch[0] : undefined,
|
|
105
|
+
invocations,
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
//# sourceMappingURL=github-scaffold.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"github-scaffold.js","sourceRoot":"","sources":["../../src/clone/github-scaffold.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { Provenance } from "../studio/types.js";
|
|
2
|
+
import type { ForkProvenance } from "./types.js";
|
|
3
|
+
export interface WriteForkProvenanceArgs {
|
|
4
|
+
/** Where to write the .vskill-meta.json (typically the .tmp staging dir). */
|
|
5
|
+
targetSkillDir: string;
|
|
6
|
+
/** New `forkedFrom` record describing the immediate parent. */
|
|
7
|
+
forkedFrom: ForkProvenance;
|
|
8
|
+
/** Optional pre-read source provenance — when omitted, the helper reads it from `sourceSkillDir`. */
|
|
9
|
+
sourceProvenance?: Provenance | null;
|
|
10
|
+
/** Source skill directory. Used to read prior sidecar when `sourceProvenance` is not supplied. */
|
|
11
|
+
sourceSkillDir?: string;
|
|
12
|
+
/** Source absolute path — recorded as `sourcePath` for compatibility with the existing schema. */
|
|
13
|
+
sourcePath: string;
|
|
14
|
+
/** Source version — recorded as `sourceSkillVersion`. */
|
|
15
|
+
sourceVersion?: string;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Compute the merged Provenance object for a fresh fork without writing.
|
|
19
|
+
* Exposed for unit tests and dry-run mode.
|
|
20
|
+
*/
|
|
21
|
+
export declare function computeForkProvenance(args: {
|
|
22
|
+
forkedFrom: ForkProvenance;
|
|
23
|
+
sourceProvenance: Provenance | null;
|
|
24
|
+
sourcePath: string;
|
|
25
|
+
sourceVersion?: string;
|
|
26
|
+
}): Provenance;
|
|
27
|
+
/**
|
|
28
|
+
* Read the source skill's existing `.vskill-meta.json` (if any), compute the
|
|
29
|
+
* merged fork provenance, and write it to the target directory's sidecar.
|
|
30
|
+
*
|
|
31
|
+
* The write is delegated to `writeProvenance` so the on-disk JSON shape is
|
|
32
|
+
* identical to every other vskill flow that produces a sidecar.
|
|
33
|
+
*/
|
|
34
|
+
export declare function writeForkProvenance(args: WriteForkProvenanceArgs): Promise<Provenance>;
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
// ---------------------------------------------------------------------------
|
|
2
|
+
// vskill clone — provenance-fork
|
|
3
|
+
// ---------------------------------------------------------------------------
|
|
4
|
+
// Thin helper around the existing `writeProvenance` / `readProvenance`
|
|
5
|
+
// primitives in src/studio/lib/provenance.ts. Computes the `forkedFrom`,
|
|
6
|
+
// `originalSource`, and `forkChain` fork-lineage fields for a cloned skill,
|
|
7
|
+
// then delegates the write to `writeProvenance` so the on-disk format is
|
|
8
|
+
// unchanged.
|
|
9
|
+
//
|
|
10
|
+
// Lineage rules (settled in plan.md §3 + spec AC-US5-02):
|
|
11
|
+
// - `forkedFrom` always refers to the IMMEDIATE parent we just copied from.
|
|
12
|
+
// - `forkChain[]` is the ancestry trail. When the source was already a fork
|
|
13
|
+
// (i.e. its existing sidecar has a `forkedFrom`), append the source's
|
|
14
|
+
// `forkedFrom.source` to the source's prior `forkChain[]`. When the
|
|
15
|
+
// source was a root (no sidecar or no `forkedFrom`), `forkChain` is left
|
|
16
|
+
// undefined.
|
|
17
|
+
// - `originalSource` is carried forward unchanged from the source's sidecar
|
|
18
|
+
// when present (it always points to the chain's deepest known ancestor).
|
|
19
|
+
//
|
|
20
|
+
// Existing `Provenance` fields written by other vskill flows
|
|
21
|
+
// (`promotedFrom`, `sourcePath`, `promotedAt`, `sourceSkillVersion`) are
|
|
22
|
+
// preserved when present on the source's sidecar, with sensible defaults
|
|
23
|
+
// for fresh clones.
|
|
24
|
+
//
|
|
25
|
+
// See ADR 0688-02 (sidecar provenance) for the underlying contract.
|
|
26
|
+
// ---------------------------------------------------------------------------
|
|
27
|
+
import { readProvenance, writeProvenance } from "../studio/lib/provenance.js";
|
|
28
|
+
/**
|
|
29
|
+
* Compute the merged Provenance object for a fresh fork without writing.
|
|
30
|
+
* Exposed for unit tests and dry-run mode.
|
|
31
|
+
*/
|
|
32
|
+
export function computeForkProvenance(args) {
|
|
33
|
+
const { forkedFrom, sourceProvenance, sourcePath, sourceVersion } = args;
|
|
34
|
+
const next = {
|
|
35
|
+
// The existing schema requires these — for a brand-new fork the sensible
|
|
36
|
+
// defaults are "global" (the cloned skill is now globally-authored by the
|
|
37
|
+
// new author) and a fresh timestamp. When the source has its own sidecar,
|
|
38
|
+
// we DO NOT inherit its `promotedFrom`/`sourcePath` because those refer
|
|
39
|
+
// to the source's own promotion lineage, not the fork's.
|
|
40
|
+
promotedFrom: "global",
|
|
41
|
+
sourcePath,
|
|
42
|
+
promotedAt: Date.now(),
|
|
43
|
+
sourceSkillVersion: sourceVersion ?? forkedFrom.version,
|
|
44
|
+
forkedFrom: { ...forkedFrom },
|
|
45
|
+
};
|
|
46
|
+
if (!sourceProvenance) {
|
|
47
|
+
// Source is a root (no prior sidecar) — no forkChain, no originalSource.
|
|
48
|
+
return next;
|
|
49
|
+
}
|
|
50
|
+
// Carry forward originalSource unchanged when present. It always points to
|
|
51
|
+
// the chain's deepest known ancestor, so it is invariant under forking.
|
|
52
|
+
if (sourceProvenance.originalSource) {
|
|
53
|
+
next.originalSource = { ...sourceProvenance.originalSource };
|
|
54
|
+
}
|
|
55
|
+
if (sourceProvenance.forkedFrom) {
|
|
56
|
+
// Source itself was a fork — extend the chain by its `forkedFrom.source`.
|
|
57
|
+
// Order: oldest-first (the deepest ancestor is `forkChain[0]`, the most
|
|
58
|
+
// recent intermediate parent — i.e. the source's own forkedFrom — is the
|
|
59
|
+
// last entry). This ordering is consistent across rounds because each
|
|
60
|
+
// clone appends only one entry.
|
|
61
|
+
const priorChain = Array.isArray(sourceProvenance.forkChain)
|
|
62
|
+
? [...sourceProvenance.forkChain]
|
|
63
|
+
: [];
|
|
64
|
+
priorChain.push(sourceProvenance.forkedFrom.source);
|
|
65
|
+
next.forkChain = priorChain;
|
|
66
|
+
// If the source had no `originalSource` recorded but did have a chain,
|
|
67
|
+
// synthesize one pointing at the chain head (best available ancestor).
|
|
68
|
+
if (!next.originalSource && priorChain.length > 0) {
|
|
69
|
+
next.originalSource = { skillPath: priorChain[0] };
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return next;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Read the source skill's existing `.vskill-meta.json` (if any), compute the
|
|
76
|
+
* merged fork provenance, and write it to the target directory's sidecar.
|
|
77
|
+
*
|
|
78
|
+
* The write is delegated to `writeProvenance` so the on-disk JSON shape is
|
|
79
|
+
* identical to every other vskill flow that produces a sidecar.
|
|
80
|
+
*/
|
|
81
|
+
export async function writeForkProvenance(args) {
|
|
82
|
+
let sourceProvenance = args.sourceProvenance ?? null;
|
|
83
|
+
if (sourceProvenance === undefined || (sourceProvenance === null && args.sourceSkillDir)) {
|
|
84
|
+
if (args.sourceSkillDir) {
|
|
85
|
+
sourceProvenance = await readProvenance(args.sourceSkillDir);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
const merged = computeForkProvenance({
|
|
89
|
+
forkedFrom: args.forkedFrom,
|
|
90
|
+
sourceProvenance,
|
|
91
|
+
sourcePath: args.sourcePath,
|
|
92
|
+
sourceVersion: args.sourceVersion,
|
|
93
|
+
});
|
|
94
|
+
await writeProvenance(args.targetSkillDir, merged);
|
|
95
|
+
return merged;
|
|
96
|
+
}
|
|
97
|
+
//# sourceMappingURL=provenance-fork.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"provenance-fork.js","sourceRoot":"","sources":["../../src/clone/provenance-fork.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { ReferenceMatch } from "./types.js";
|
|
2
|
+
export interface ScanOptions {
|
|
3
|
+
/** Old fully-qualified skill name (e.g. "sw/ado-mapper" or "ado-mapper"). */
|
|
4
|
+
oldSkillName: string;
|
|
5
|
+
/** Logical filename (relative to skill root) — recorded in each match. */
|
|
6
|
+
file?: string;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Scan SKILL.md content for cross-skill references and self-name occurrences.
|
|
10
|
+
* Returns matches in document order with line numbers (1-indexed).
|
|
11
|
+
*/
|
|
12
|
+
export declare function scanReferences(content: string, opts: ScanOptions): ReferenceMatch[];
|
|
13
|
+
/**
|
|
14
|
+
* Scan for literal occurrences of the OLD skill name in prose.
|
|
15
|
+
* Suppressed inside fenced code blocks. Returned as a separate array because
|
|
16
|
+
* it's informational at a different level (manual rename candidates, not
|
|
17
|
+
* cross-skill dependencies).
|
|
18
|
+
*/
|
|
19
|
+
export declare function scanSelfNameOccurrences(content: string, opts: ScanOptions): ReferenceMatch[];
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
// ---------------------------------------------------------------------------
|
|
2
|
+
// vskill clone — reference-scanner (READ-ONLY)
|
|
3
|
+
// ---------------------------------------------------------------------------
|
|
4
|
+
// Detects cross-skill reference patterns and self-name occurrences in
|
|
5
|
+
// SKILL.md prose. **Reports only — never rewrites anything.** The orchestrator
|
|
6
|
+
// appends the report to the success summary so the user can decide what to do.
|
|
7
|
+
//
|
|
8
|
+
// Patterns:
|
|
9
|
+
// - backtick: `sw:foo` (single-backtick inline code)
|
|
10
|
+
// - skill-call: Skill({ skill: "sw:foo" }) / Skill({ skill: 'sw:foo' })
|
|
11
|
+
// - slash-command: /sw:foo
|
|
12
|
+
// - self-name: literal occurrence of the OLD skill name in prose
|
|
13
|
+
//
|
|
14
|
+
// False-positive guard: matches inside fenced code blocks (```...```) are
|
|
15
|
+
// excluded for the slash-command and self-name patterns. Backtick and
|
|
16
|
+
// Skill({...}) patterns are themselves code-shaped, so they are reported
|
|
17
|
+
// unconditionally — that's the user's signal that an actual cross-skill
|
|
18
|
+
// dependency exists in the source.
|
|
19
|
+
//
|
|
20
|
+
// See spec.md AC-US1-04, plan.md §6.
|
|
21
|
+
// ---------------------------------------------------------------------------
|
|
22
|
+
const BACKTICK_RE = /`([a-z][a-z0-9-]*:[a-z][a-z0-9-]*)`/g;
|
|
23
|
+
const SKILL_CALL_RE = /Skill\s*\(\s*\{\s*skill\s*:\s*["']([^"']+)["']/g;
|
|
24
|
+
const SLASH_COMMAND_RE = /(?:^|\s)(\/[a-z][a-z0-9-]*:[a-z][a-z0-9-]*)\b/g;
|
|
25
|
+
/**
|
|
26
|
+
* Build a regex that matches the OLD skill name as a whole "word", where word
|
|
27
|
+
* boundaries here mean the surrounding chars are not [A-Za-z0-9-/] (so
|
|
28
|
+
* "ado-mapper" matches but "ado-mapperx" or "my-ado-mapper-thing" do not).
|
|
29
|
+
*
|
|
30
|
+
* The `oldSkillName` is escaped before being embedded in the pattern so any
|
|
31
|
+
* regex metacharacters in the skill name are treated literally.
|
|
32
|
+
*/
|
|
33
|
+
function escapeRegex(s) {
|
|
34
|
+
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Detect lines covered by fenced code blocks (triple-backtick). Returns a Set
|
|
38
|
+
* of 1-indexed line numbers that are INSIDE a fence. Used to suppress
|
|
39
|
+
* slash-command and self-name false positives that live in code samples.
|
|
40
|
+
*
|
|
41
|
+
* The opening/closing fence lines themselves are also considered "inside"
|
|
42
|
+
* for the purpose of suppression — they don't contain meaningful prose
|
|
43
|
+
* references, so this is a deliberate slight overreach.
|
|
44
|
+
*/
|
|
45
|
+
function computeFencedLines(lines) {
|
|
46
|
+
const fenced = new Set();
|
|
47
|
+
let inFence = false;
|
|
48
|
+
for (let i = 0; i < lines.length; i++) {
|
|
49
|
+
const trimmed = lines[i].trimStart();
|
|
50
|
+
if (trimmed.startsWith("```")) {
|
|
51
|
+
// The fence-opening line and fence-closing line are both fenced.
|
|
52
|
+
fenced.add(i + 1);
|
|
53
|
+
inFence = !inFence;
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
if (inFence)
|
|
57
|
+
fenced.add(i + 1);
|
|
58
|
+
}
|
|
59
|
+
return fenced;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Scan SKILL.md content for cross-skill references and self-name occurrences.
|
|
63
|
+
* Returns matches in document order with line numbers (1-indexed).
|
|
64
|
+
*/
|
|
65
|
+
export function scanReferences(content, opts) {
|
|
66
|
+
const file = opts.file ?? "SKILL.md";
|
|
67
|
+
const normalized = content.replace(/^/, "").replace(/\r\n/g, "\n");
|
|
68
|
+
const lines = normalized.split("\n");
|
|
69
|
+
const fenced = computeFencedLines(lines);
|
|
70
|
+
const out = [];
|
|
71
|
+
// Pattern 1: backtick `sw:foo` — reported unconditionally (already code-shaped).
|
|
72
|
+
for (let i = 0; i < lines.length; i++) {
|
|
73
|
+
const line = lines[i];
|
|
74
|
+
BACKTICK_RE.lastIndex = 0;
|
|
75
|
+
let m;
|
|
76
|
+
while ((m = BACKTICK_RE.exec(line)) !== null) {
|
|
77
|
+
out.push({ file, line: i + 1, match: m[1], kind: "backtick" });
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
// Pattern 2: Skill({ skill: "sw:foo" }) — reported unconditionally.
|
|
81
|
+
for (let i = 0; i < lines.length; i++) {
|
|
82
|
+
const line = lines[i];
|
|
83
|
+
SKILL_CALL_RE.lastIndex = 0;
|
|
84
|
+
let m;
|
|
85
|
+
while ((m = SKILL_CALL_RE.exec(line)) !== null) {
|
|
86
|
+
out.push({ file, line: i + 1, match: m[1], kind: "skill-call" });
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
// Pattern 3: /sw:foo — slash-command. Suppressed inside fenced code blocks.
|
|
90
|
+
for (let i = 0; i < lines.length; i++) {
|
|
91
|
+
if (fenced.has(i + 1))
|
|
92
|
+
continue;
|
|
93
|
+
const line = lines[i];
|
|
94
|
+
SLASH_COMMAND_RE.lastIndex = 0;
|
|
95
|
+
let m;
|
|
96
|
+
while ((m = SLASH_COMMAND_RE.exec(line)) !== null) {
|
|
97
|
+
// Strip the leading "/" so the recorded match is the bare slash-command name.
|
|
98
|
+
const matched = m[1];
|
|
99
|
+
out.push({ file, line: i + 1, match: matched, kind: "slash-command" });
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
return out;
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Scan for literal occurrences of the OLD skill name in prose.
|
|
106
|
+
* Suppressed inside fenced code blocks. Returned as a separate array because
|
|
107
|
+
* it's informational at a different level (manual rename candidates, not
|
|
108
|
+
* cross-skill dependencies).
|
|
109
|
+
*/
|
|
110
|
+
export function scanSelfNameOccurrences(content, opts) {
|
|
111
|
+
const file = opts.file ?? "SKILL.md";
|
|
112
|
+
const normalized = content.replace(/^/, "").replace(/\r\n/g, "\n");
|
|
113
|
+
const lines = normalized.split("\n");
|
|
114
|
+
const fenced = computeFencedLines(lines);
|
|
115
|
+
// The full name may contain "/" (e.g. "sw/ado-mapper") — we scan for both
|
|
116
|
+
// the bare skill segment and the full name. Bare segment first ensures we
|
|
117
|
+
// catch occurrences like "the ado-mapper does X" in prose.
|
|
118
|
+
const slashIdx = opts.oldSkillName.indexOf("/");
|
|
119
|
+
const bareSkill = slashIdx >= 0 ? opts.oldSkillName.slice(slashIdx + 1) : opts.oldSkillName;
|
|
120
|
+
const patterns = new Set();
|
|
121
|
+
patterns.add(opts.oldSkillName);
|
|
122
|
+
if (bareSkill !== opts.oldSkillName)
|
|
123
|
+
patterns.add(bareSkill);
|
|
124
|
+
const out = [];
|
|
125
|
+
for (const pattern of patterns) {
|
|
126
|
+
const escaped = escapeRegex(pattern);
|
|
127
|
+
// Word-boundary that treats letters, digits, hyphen, slash as part of the "word".
|
|
128
|
+
// (?<![A-Za-z0-9-/]) — preceded by neither a name char nor a slash
|
|
129
|
+
// (?![A-Za-z0-9-/]) — followed by neither a name char nor a slash
|
|
130
|
+
const re = new RegExp(`(?<![A-Za-z0-9\\-/])${escaped}(?![A-Za-z0-9\\-/])`, "g");
|
|
131
|
+
for (let i = 0; i < lines.length; i++) {
|
|
132
|
+
if (fenced.has(i + 1))
|
|
133
|
+
continue;
|
|
134
|
+
const line = lines[i];
|
|
135
|
+
re.lastIndex = 0;
|
|
136
|
+
let m;
|
|
137
|
+
while ((m = re.exec(line)) !== null) {
|
|
138
|
+
out.push({ file, line: i + 1, match: m[0], kind: "self-name" });
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
return out;
|
|
143
|
+
}
|
|
144
|
+
//# sourceMappingURL=reference-scanner.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reference-scanner.js","sourceRoot":"","sources":["../../src/clone/reference-scanner.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { SkillSource } from "./types.js";
|
|
2
|
+
export interface LocateOptions {
|
|
3
|
+
/** Working directory to use as the project root (defaults to process.cwd()). */
|
|
4
|
+
cwd?: string;
|
|
5
|
+
/** Home directory (defaults to os.homedir() — explicit for testability). */
|
|
6
|
+
home: string;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Split a source identifier like "sw/ado-mapper" into namespace + skill, or
|
|
10
|
+
* return only the skill when no namespace is present.
|
|
11
|
+
*/
|
|
12
|
+
export declare function parseSourceIdent(source: string): {
|
|
13
|
+
namespace?: string;
|
|
14
|
+
skill: string;
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* Discover a skill across project / personal / cache. Returns every match in
|
|
18
|
+
* search order — the orchestrator handles disambiguation when there is more
|
|
19
|
+
* than one and `--source` was not explicitly provided.
|
|
20
|
+
*/
|
|
21
|
+
export declare function locateSkill(source: string, opts: LocateOptions): Promise<SkillSource[]>;
|
|
22
|
+
/**
|
|
23
|
+
* Enumerate every skill inside a cached plugin. Used by the whole-plugin
|
|
24
|
+
* clone path (T-010). Returns a list of SkillSource entries (one per skill).
|
|
25
|
+
*/
|
|
26
|
+
export declare function enumeratePluginSkills(pluginName: string, opts: LocateOptions): Promise<SkillSource[]>;
|