skills-layer 0.1.8 → 0.2.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.
@@ -1,15 +1,56 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- import { executePublicCli } from "../public/skills-layer-public.mjs";
3
+ import { createCliProgress, createRevealSkipController, executePublicCli, prepareCliPresentation, writeCliResult } from "../public/skills-layer-public.mjs";
4
4
 
5
- const result = await executePublicCli(process.argv.slice(2));
6
-
7
- if (result.stdout) {
8
- process.stdout.write(`${result.stdout}\n`);
5
+ const args = process.argv.slice(2);
6
+ const capabilities = {
7
+ stdinIsTty: Boolean(process.stdin.isTTY),
8
+ stdoutIsTty: Boolean(process.stdout.isTTY),
9
+ stderrIsTty: Boolean(process.stderr.isTTY)
10
+ };
11
+ let presentation;
12
+ try {
13
+ presentation = prepareCliPresentation(args, process.env, capabilities);
14
+ } catch {
15
+ const outputFormatIndex = args.indexOf("--output-format");
16
+ const inlineOutputFormat = args.find((argument) => argument.startsWith("--output-format="))?.slice("--output-format=".length);
17
+ const requestedOutputFormat = outputFormatIndex >= 0 ? args[outputFormatIndex + 1] : inlineOutputFormat;
18
+ const kind = requestedOutputFormat === "stream-json" ? "json-stream" : args.includes("--json") || requestedOutputFormat === "json" ? "json-static" : "human-static";
19
+ presentation = { args, mode: { kind, color: false, motion: false, interactive: false, unicode: true, screenReader: false, classic: true, detail: "standard" } };
20
+ }
21
+ const human = presentation.mode.kind === "human-interactive" || presentation.mode.kind === "human-static";
22
+ const commandName = presentation.args?.find((argument) => !argument.startsWith("-")) ?? "skills-layer";
23
+ const lightweight = args.length === 0 || args.includes("--help") || args.includes("-h") || args.includes("--version") || args.includes("-v") || commandName === "help" || commandName === "version";
24
+ const progress = lightweight
25
+ ? { info() {}, pause() {}, resume() {}, finish() {} }
26
+ : createCliProgress({ mode: presentation.mode, commandName, stream: process.stderr });
27
+ if (human && !lightweight) {
28
+ progress.info(`Running ${commandName}…`);
9
29
  }
30
+ const result = await executePublicCli(args, {
31
+ stdinIsTty: capabilities.stdinIsTty,
32
+ stdoutIsTty: capabilities.stdoutIsTty,
33
+ stderrIsTty: capabilities.stderrIsTty,
34
+ ...(presentation.mode.kind === "json-stream" ? {
35
+ emitCliEvent: (event) => process.stdout.write(`${JSON.stringify(event)}\n`)
36
+ } : {}),
37
+ ...(human ? { emitInfo: progress.info } : {}),
38
+ ...(human ? { onPromptStart: progress.pause, onPromptEnd: progress.resume } : {})
39
+ });
40
+ progress.finish(result);
10
41
 
11
- if (result.stderr) {
12
- process.stderr.write(`${result.stderr}\n`);
42
+ const revealSkip = createRevealSkipController(process.stdin);
43
+ try {
44
+ await writeCliResult(result, {
45
+ mode: presentation.mode,
46
+ writeStdout: (value) => process.stdout.write(value),
47
+ writeStderr: (value) => process.stderr.write(value),
48
+ sleep: (milliseconds) => new Promise((resolve) => setTimeout(resolve, milliseconds)),
49
+ shouldSkip: revealSkip.shouldSkip,
50
+ width: process.stdout.columns
51
+ });
52
+ } finally {
53
+ revealSkip.dispose();
13
54
  }
14
55
 
15
56
  process.exitCode = result.exitCode;
@@ -1,7 +0,0 @@
1
- version: 2
2
- updates:
3
- - package-ecosystem: npm
4
- directory: /
5
- schedule:
6
- interval: weekly
7
- open-pull-requests-limit: 5
@@ -1,22 +0,0 @@
1
- name: CLI package CI
2
-
3
- on:
4
- pull_request:
5
- push:
6
- branches:
7
- - main
8
-
9
- permissions:
10
- contents: read
11
-
12
- jobs:
13
- verify:
14
- runs-on: ubuntu-latest
15
- steps:
16
- - uses: actions/checkout@v6
17
- - uses: actions/setup-node@v6
18
- with:
19
- node-version: "24"
20
- package-manager-cache: false
21
- - run: npm run typecheck
22
- - run: npm run test:coverage
@@ -1,24 +0,0 @@
1
- name: Publish skills-layer to npm
2
-
3
- on:
4
- push:
5
- tags:
6
- - "v*"
7
-
8
- permissions:
9
- contents: read
10
- id-token: write
11
-
12
- jobs:
13
- publish:
14
- runs-on: ubuntu-latest
15
- steps:
16
- - uses: actions/checkout@v6
17
- - uses: actions/setup-node@v6
18
- with:
19
- node-version: "24"
20
- registry-url: "https://registry.npmjs.org"
21
- package-manager-cache: false
22
- - run: npm run typecheck
23
- - run: npm run test:coverage
24
- - run: npm publish --access public --provenance
@@ -1,18 +0,0 @@
1
- # Code Of Conduct
2
-
3
- We want Skills Layer CLI to be a respectful, practical, and welcoming project for users and contributors.
4
-
5
- ## Expected Behavior
6
-
7
- - Be respectful and constructive.
8
- - Assume good intent while being precise about risks and tradeoffs.
9
- - Keep security reports private until they are resolved or responsibly disclosed.
10
- - Avoid harassment, insults, discrimination, or repeated disruptive behavior.
11
-
12
- ## Scope
13
-
14
- This applies to repository issues, pull requests, discussions, and related project communication.
15
-
16
- ## Enforcement
17
-
18
- Maintainers may edit, hide, or remove content and may restrict participation when behavior harms users, contributors, or project safety.
package/CONTRIBUTING.md DELETED
@@ -1,24 +0,0 @@
1
- # Contributing
2
-
3
- Thanks for helping improve Skills Layer CLI.
4
-
5
- This public repository is intentionally limited to the CLI, MCP setup, local agent integration, packaging, docs, and package-level tests. Do not submit backend service code, Cloud admin tooling, Guardian scoring internals, credentials, or private release material here.
6
-
7
- ## Local Checks
8
-
9
- Run these from the repository root before opening a pull request:
10
-
11
- ```bash
12
- npm run typecheck
13
- npm run test:coverage
14
- ```
15
-
16
- The package has no runtime dependencies and no npm lifecycle scripts. Please keep it that way unless a public CLI feature has a clear need.
17
-
18
- ## Pull Request Checklist
19
-
20
- - Keep user-facing CLI output readable and agent-friendly.
21
- - Preserve predictable `--json` behavior for agent workflows.
22
- - Avoid leaking internal service details or private operational commands.
23
- - Update README/help text when behavior changes.
24
- - Add focused tests for changed behavior.
@@ -1,311 +0,0 @@
1
- import { execFile } from "node:child_process";
2
- import test from "node:test";
3
- import assert from "node:assert/strict";
4
- import { mkdtemp, readFile, writeFile } from "node:fs/promises";
5
- import { tmpdir } from "node:os";
6
- import { join } from "node:path";
7
- import { fileURLToPath } from "node:url";
8
-
9
- const packageUrl = new URL("../package.json", import.meta.url);
10
- const runtimeUrl = new URL("../public/skills-layer-public.mjs", import.meta.url);
11
- const readmeUrl = new URL("../README.md", import.meta.url);
12
- const licenseUrl = new URL("../LICENSE", import.meta.url);
13
- const wrapperUrl = new URL("../scripts/skills-layer.mjs", import.meta.url);
14
- const securityUrl = new URL("../SECURITY.md", import.meta.url);
15
- const contributingUrl = new URL("../CONTRIBUTING.md", import.meta.url);
16
- const codeOfConductUrl = new URL("../CODE_OF_CONDUCT.md", import.meta.url);
17
- const dependabotUrl = new URL("../.github/dependabot.yml", import.meta.url);
18
- const ciWorkflowUrl = new URL("../.github/workflows/ci.yml", import.meta.url);
19
- const trustedPublishWorkflowUrl = new URL("../.github/workflows/publish.yml", import.meta.url);
20
- const binPath = fileURLToPath(new URL("../bin/skills-layer.js", import.meta.url));
21
-
22
- async function loadRuntime() {
23
- return await import(runtimeUrl.href);
24
- }
25
-
26
- async function makeHome() {
27
- return await mkdtemp(join(tmpdir(), "skills-layer-cli-package-home-"));
28
- }
29
-
30
- function packageEnv(home) {
31
- const env = {
32
- ...process.env,
33
- HOME: home,
34
- SKILLS_LAYER_NO_BANNER: "1"
35
- };
36
- delete env.SKILLS_LAYER_API_KEY;
37
- delete env.SKILLS_LAYER_BASE_URL;
38
- delete env.SKILLS_LAYER_MCP_TOKEN;
39
- return env;
40
- }
41
-
42
- async function runPublic(args, options = {}) {
43
- const runtime = await loadRuntime();
44
- return await runtime.executePublicCli(args, {
45
- env: packageEnv(options.home ?? await makeHome()),
46
- cwd: options.cwd,
47
- stdoutIsTty: false,
48
- fetch: options.fetch ?? (async () => { throw new Error("Unexpected network call from package contract test."); })
49
- });
50
- }
51
-
52
- async function runBin(args) {
53
- const home = await makeHome();
54
- return await new Promise((resolve) => {
55
- execFile(process.execPath, [binPath, ...args], { env: packageEnv(home) }, (error, stdout, stderr) => {
56
- resolve({
57
- exitCode: typeof error?.code === "number" ? error.code : 0,
58
- stdout,
59
- stderr
60
- });
61
- });
62
- });
63
- }
64
-
65
- function parseJsonOutput(result) {
66
- assert.equal(result.stderr, "");
67
- return JSON.parse(result.stdout);
68
- }
69
-
70
- function parseJsonError(result) {
71
- assert.equal(result.stdout, "");
72
- return JSON.parse(result.stderr);
73
- }
74
-
75
- function jsonResponse(body, status = 200) {
76
- return new Response(JSON.stringify(body), {
77
- status,
78
- headers: { "content-type": "application/json" }
79
- });
80
- }
81
-
82
- test("public package metadata keeps the npm tree narrow and non-lifecycle", async () => {
83
- const packageJson = JSON.parse(await readFile(packageUrl, "utf8"));
84
- assert.equal(packageJson.name, "skills-layer");
85
- assert.equal(packageJson.private, false);
86
- assert.equal(packageJson.type, "module");
87
- assert.equal(packageJson.bin["skills-layer"], "bin/skills-layer.js");
88
- assert.equal(packageJson.dependencies, undefined);
89
- assert.equal(packageJson.devDependencies, undefined);
90
- assert.equal(packageJson.license, "Apache-2.0");
91
- assert.deepEqual(packageJson.repository, {
92
- type: "git",
93
- url: "git+https://github.com/ankurdhoom/skills-layer.git"
94
- });
95
- assert.deepEqual(packageJson.bugs, { url: "https://github.com/ankurdhoom/skills-layer/issues" });
96
- assert.deepEqual(packageJson.files, [
97
- "CODE_OF_CONDUCT.md",
98
- "CONTRIBUTING.md",
99
- "SECURITY.md",
100
- ".github/dependabot.yml",
101
- ".github/workflows/*.yml",
102
- "bin/skills-layer.js",
103
- "public/skills-layer-public.mjs",
104
- "scripts/skills-layer.mjs",
105
- "docs/assets/*.svg",
106
- "tests/public-cli-package.test.mjs"
107
- ]);
108
- assert.deepEqual(Object.keys(packageJson.scripts).sort(), ["test", "test:coverage", "typecheck"]);
109
- for (const lifecycleScript of ["preinstall", "install", "postinstall", "prepare", "prepack", "postpack", "prepublishOnly"]) {
110
- assert.equal(packageJson.scripts[lifecycleScript], undefined);
111
- }
112
-
113
- const workflow = await readFile(trustedPublishWorkflowUrl, "utf8");
114
- assert.match(workflow, /name: Publish skills-layer to npm/u);
115
- assert.match(workflow, /id-token: write/u);
116
- assert.match(workflow, /npm publish --access public --provenance/u);
117
- assert.doesNotMatch(workflow, /NPM_TOKEN/u);
118
-
119
- const ciWorkflow = await readFile(ciWorkflowUrl, "utf8");
120
- assert.match(ciWorkflow, /name: CLI package CI/u);
121
- assert.match(ciWorkflow, /npm run typecheck/u);
122
- assert.match(ciWorkflow, /npm run test:coverage/u);
123
- assert.doesNotMatch(ciWorkflow, /NPM_TOKEN/u);
124
-
125
- const dependabot = await readFile(dependabotUrl, "utf8");
126
- assert.match(dependabot, /package-ecosystem: npm/u);
127
-
128
- const [security, contributing, codeOfConduct] = await Promise.all([
129
- readFile(securityUrl, "utf8"),
130
- readFile(contributingUrl, "utf8"),
131
- readFile(codeOfConductUrl, "utf8")
132
- ]);
133
- assert.match(security, /Please do not open public issues for active vulnerabilities/u);
134
- assert.match(security, /hello@skills-layer\.com/u);
135
- assert.match(contributing, /Do not submit backend service code/u);
136
- assert.match(codeOfConduct, /Expected Behavior/u);
137
- });
138
-
139
- test("public package text stays limited to public CLI surface", async () => {
140
- const [packageText, readme, license, runtime, wrapper] = await Promise.all([
141
- readFile(packageUrl, "utf8"),
142
- readFile(readmeUrl, "utf8"),
143
- readFile(licenseUrl, "utf8"),
144
- readFile(runtimeUrl, "utf8"),
145
- readFile(wrapperUrl, "utf8")
146
- ]);
147
- const combined = `${packageText}\n${readme}\n${license}\n${runtime}\n${wrapper}`;
148
- assert.match(combined, /Skills Layer/u);
149
- assert.match(combined, /skills-layer/u);
150
- assert.match(readme, /open-source CLI, MCP setup, and local agent integration layer/u);
151
- assert.match(readme, /hosted backend code, marketplace services, Guardian scoring internals, and service operations remain proprietary/u);
152
- assert.match(readme, /actions\/workflows\/ci\.yml\/badge\.svg/u);
153
- assert.match(readme, /https:\/\/unpkg\.com\/skills-layer\/docs\/assets\/skills-layer-logo\.svg/u);
154
- assert.match(readme, /https:\/\/unpkg\.com\/skills-layer\/docs\/assets\/skills-layer-hero\.svg/u);
155
- assert.match(readme, /https:\/\/unpkg\.com\/skills-layer\/docs\/assets\/skills-layer-flow\.svg/u);
156
- assert.match(readme, /https:\/\/unpkg\.com\/skills-layer\/docs\/assets\/skills-layer-marketplace-flow\.svg/u);
157
- assert.doesNotMatch(readme, /src="docs\/assets\//u);
158
- for (const forbidden of [
159
- new RegExp(["", "api", "v1", "admin"].join("/"), "u"),
160
- new RegExp(["x", "skills", "layer", "admin", "key"].join("-"), "u"),
161
- new RegExp(["SKILLS", "LAYER", "ADMIN", "API", "KEY"].join("_"), "u"),
162
- new RegExp(["--", "admin", "api", "key"].join("-"), "u"),
163
- new RegExp(["github.com", "openai", "openskills"].join("/"), "u")
164
- ]) {
165
- assert.doesNotMatch(combined, forbidden);
166
- }
167
- });
168
-
169
- test("public bin wrapper and runtime expose deterministic package smoke commands", async () => {
170
- const packageJson = JSON.parse(await readFile(packageUrl, "utf8"));
171
- const expectedVersionLine = `skills-layer ${packageJson.version}`;
172
- const runtime = await loadRuntime();
173
- const binVersion = await runBin(["--version"]);
174
- assert.equal(binVersion.exitCode, 0);
175
- assert.equal(binVersion.stderr, "");
176
- assert.equal(binVersion.stdout.trim(), expectedVersionLine);
177
-
178
- const version = await runtime.executePublicCli(["--version"]);
179
- assert.equal(version.exitCode, 0);
180
- assert.equal(version.stdout.trim(), expectedVersionLine);
181
-
182
- const jsonVersion = await runPublic(["version", "--json"]);
183
- assert.equal(jsonVersion.exitCode, 0);
184
- assert.equal(parseJsonOutput(jsonVersion).data.version, packageJson.version);
185
-
186
- const loginHelp = await runtime.executePublicCli(["login", "--help"]);
187
- assert.equal(loginHelp.exitCode, 0);
188
- assert.match(loginHelp.stdout, /Skills Layer login help/u);
189
- assert.match(loginHelp.stdout, /skills-layer mcp setup/u);
190
-
191
- const fullHelp = await runtime.executePublicCli(["help", "full"]);
192
- assert.equal(fullHelp.exitCode, 0);
193
- assert.match(fullHelp.stdout, /Start here:/u);
194
- assert.match(fullHelp.stdout, /skills-layer help full/u);
195
- });
196
-
197
- test("public runtime persists backend preference without network access", async () => {
198
- const home = await makeHome();
199
- const update = await runPublic(["backend", "--base-url", "https://staging.findfigg.co.uk", "--json"], { home });
200
- assert.equal(update.exitCode, 0);
201
- assert.equal(parseJsonOutput(update).data.selectedBaseUrl, "https://staging.findfigg.co.uk");
202
-
203
- const preference = JSON.parse(await readFile(join(home, ".skills-layer", "backend-preference.json"), "utf8"));
204
- assert.equal(preference.preferredBaseUrl, "https://staging.findfigg.co.uk");
205
-
206
- const status = await runPublic(["backend", "status", "--json"], { home });
207
- assert.equal(status.exitCode, 0);
208
- const statusPayload = parseJsonOutput(status);
209
- assert.equal(statusPayload.data.selectedBaseUrl, "https://staging.findfigg.co.uk");
210
- assert.equal(statusPayload.data.loggedIn, false);
211
- });
212
-
213
- test("public runtime posts buyer eval summary envelopes with reviews", async () => {
214
- const home = await makeHome();
215
- const cwd = await mkdtemp(join(tmpdir(), "skills-layer-cli-package-work-"));
216
- const summaryPath = join(cwd, "buyer-eval-summary.json");
217
- const artifactDigest = `sha256:${"a".repeat(64)}`;
218
- await writeFile(summaryPath, JSON.stringify({
219
- schemaVersion: "skills-layer.eval.summary.v1",
220
- status: "passed",
221
- skillSlug: "project-planner",
222
- version: "1.0.0",
223
- artifactDigest,
224
- evalPackDigest: `sha256:${"b".repeat(64)}`,
225
- generatedAt: "2026-07-03T00:00:00.000Z",
226
- counts: {
227
- cases: 1,
228
- assertions: 2,
229
- passed: 2,
230
- failed: 0,
231
- skipped: 0
232
- },
233
- redaction: {
234
- mode: "content_free",
235
- rawContentIncluded: false
236
- }
237
- }), "utf8");
238
-
239
- const fetch = async (input, init = {}) => {
240
- const url = String(input);
241
- if (url === "https://api.skills-layer.local/api/v1/me") {
242
- assert.equal(init.headers.authorization, "Bearer package-test-key");
243
- return jsonResponse({
244
- userId: "user_package",
245
- email: "package@example.com",
246
- role: "buyer",
247
- roles: ["buyer"]
248
- });
249
- }
250
- if (url === "https://api.skills-layer.local/api/v1/capabilities") {
251
- assert.equal(init.headers.authorization, "Bearer package-test-key");
252
- return jsonResponse({ edition: "saas", capabilities: ["marketplace"] });
253
- }
254
- if (url === "https://api.skills-layer.local/api/v1/me/library/project-planner/review") {
255
- assert.equal(init.method, "POST");
256
- assert.equal(init.headers.authorization, "Bearer package-test-key");
257
- const body = JSON.parse(String(init.body));
258
- assert.deepEqual(body, {
259
- rating: 5,
260
- feedback: "Useful",
261
- evalSummary: {
262
- schemaVersion: "skills-layer.eval.summary.v1",
263
- status: "passed",
264
- skillSlug: "project-planner",
265
- version: "1.0.0",
266
- artifactDigest,
267
- evalPackDigest: `sha256:${"b".repeat(64)}`,
268
- generatedAt: "2026-07-03T00:00:00.000Z",
269
- counts: {
270
- cases: 1,
271
- assertions: 2,
272
- passed: 2,
273
- failed: 0,
274
- skipped: 0
275
- },
276
- redaction: {
277
- mode: "content_free",
278
- rawContentIncluded: false
279
- }
280
- }
281
- });
282
- return jsonResponse({ status: "reviewed", reviewId: "review_package", buyerEval: { status: "recorded" } });
283
- }
284
- throw new Error(`Unexpected package contract request: ${url}`);
285
- };
286
-
287
- const login = await runPublic(["login", "--api-key", "package-test-key", "--base-url", "https://api.skills-layer.local", "--no-mcp", "--json"], { home, cwd, fetch });
288
- assert.equal(login.exitCode, 0);
289
- assert.equal(parseJsonOutput(login).data.email, "package@example.com");
290
-
291
- const review = await runPublic(["add-review", "project-planner", "--rating", "5", "--feedback", "Useful", "--eval-summary", summaryPath, "--json"], { home, cwd, fetch });
292
- assert.equal(review.exitCode, 0);
293
- assert.equal(parseJsonOutput(review).data.buyerEval.status, "recorded");
294
- });
295
-
296
- test("public runtime fails closed for internal and login-required commands", async () => {
297
- const admin = await runPublic(["admin", "status", "--json"]);
298
- assert.equal(admin.exitCode, 1);
299
- const adminPayload = parseJsonError(admin);
300
- assert.equal(adminPayload.status, "error");
301
- assert.match(adminPayload.summary, /does not include internal commands/u);
302
-
303
- const whoami = await runPublic(["whoami", "--json"]);
304
- assert.equal(whoami.exitCode, 1);
305
- const whoamiPayload = parseJsonError(whoami);
306
- assert.equal(whoamiPayload.status, "login_required");
307
- assert.equal(whoamiPayload.reasonCode, "login_required");
308
- assert.equal(whoamiPayload.nextAction, "login");
309
- assert.match(whoamiPayload.nextStep, /skills-layer login/u);
310
- assert.doesNotMatch(whoamiPayload.summary, /--api-key/u);
311
- });