@skillmarkdown/cli 0.2.1 → 0.3.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/README.md CHANGED
@@ -28,7 +28,31 @@ npx @skillmarkdown/cli init
28
28
  skillmd init
29
29
  ```
30
30
 
31
- This scaffolds a spec-aligned skill structure including `SKILL.md` and optional directories (`scripts/`, `references/`, `assets/`), then runs local validation.
31
+ Default `init` creates the minimal filesystem scaffold:
32
+
33
+ - `SKILL.md` only
34
+
35
+ and runs spec validation.
36
+
37
+ To scaffold the full verbose template:
38
+
39
+ ```bash
40
+ skillmd init --template verbose
41
+ ```
42
+
43
+ The `verbose` template includes `SKILL.md`, `.gitignore`, and optional directories (`scripts/`, `references/`, `assets/`) with starter placeholder content and `.gitkeep`, then runs strict validation.
44
+
45
+ Included starter files:
46
+
47
+ - `scripts/README.md`
48
+ - `scripts/extract.py`
49
+ - `references/REFERENCE.md`
50
+ - `references/FORMS.md`
51
+ - `assets/README.md`
52
+ - `assets/report-template.md`
53
+ - `assets/lookup-table.csv`
54
+
55
+ `SKILL.md` content is the same across `minimal` and `verbose` templates. Template selection only changes extra scaffold files/directories around `SKILL.md`.
32
56
 
33
57
  To skip validation during init:
34
58
 
@@ -36,6 +60,11 @@ To skip validation during init:
36
60
  skillmd init --no-validate
37
61
  ```
38
62
 
63
+ Supported templates:
64
+
65
+ - `minimal` (default)
66
+ - `verbose`
67
+
39
68
  ### Validate a skill folder
40
69
 
41
70
  ```bash
@@ -77,7 +106,7 @@ By default, `login` uses the project’s built-in development config. You can ov
77
106
  - `SKILLMD_FIREBASE_PROJECT_ID`
78
107
 
79
108
  See `.env.example` for the expected keys.
80
- Maintainers: built-in defaults are defined in `src/lib/auth-defaults.ts`.
109
+ Maintainers: built-in defaults are defined in `src/lib/auth/defaults.ts`.
81
110
 
82
111
  Example override file:
83
112
 
package/dist/cli.js CHANGED
@@ -5,7 +5,7 @@ const init_1 = require("./commands/init");
5
5
  const login_1 = require("./commands/login");
6
6
  const logout_1 = require("./commands/logout");
7
7
  const validate_1 = require("./commands/validate");
8
- const cli_text_1 = require("./lib/cli-text");
8
+ const cli_text_1 = require("./lib/shared/cli-text");
9
9
  const COMMAND_HANDLERS = {
10
10
  init: init_1.runInitCommand,
11
11
  validate: validate_1.runValidateCommand,
@@ -30,4 +30,8 @@ async function main() {
30
30
  console.error(cli_text_1.ROOT_USAGE);
31
31
  process.exitCode = 1;
32
32
  }
33
- void main();
33
+ void main().catch((error) => {
34
+ const message = error instanceof Error ? error.message : "Unknown error";
35
+ console.error(`skillmd: ${message}`);
36
+ process.exitCode = 1;
37
+ });
@@ -1,26 +1,57 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.runInitCommand = runInitCommand;
4
- const cli_text_1 = require("../lib/cli-text");
5
- const command_output_1 = require("../lib/command-output");
6
- const scaffold_1 = require("../lib/scaffold");
7
- const validator_1 = require("../lib/validator");
4
+ const cli_text_1 = require("../lib/shared/cli-text");
5
+ const command_output_1 = require("../lib/shared/command-output");
6
+ const scaffold_1 = require("../lib/scaffold/scaffold");
7
+ const validator_1 = require("../lib/validation/validator");
8
+ function parseInitArgs(args) {
9
+ let skipValidation = false;
10
+ let template = "minimal";
11
+ for (let index = 0; index < args.length; index += 1) {
12
+ const arg = args[index];
13
+ if (arg === "--no-validate") {
14
+ skipValidation = true;
15
+ continue;
16
+ }
17
+ if (arg === "--template") {
18
+ const nextValue = args[index + 1];
19
+ const resolved = nextValue ? (0, scaffold_1.resolveInitTemplateId)(nextValue) : null;
20
+ if (!resolved) {
21
+ return { skipValidation: false, template: "minimal", valid: false };
22
+ }
23
+ template = resolved;
24
+ index += 1;
25
+ continue;
26
+ }
27
+ if (arg.startsWith("--template=")) {
28
+ const value = arg.slice("--template=".length);
29
+ const resolved = (0, scaffold_1.resolveInitTemplateId)(value);
30
+ if (!resolved) {
31
+ return { skipValidation: false, template: "minimal", valid: false };
32
+ }
33
+ template = resolved;
34
+ continue;
35
+ }
36
+ return { skipValidation: false, template: "minimal", valid: false };
37
+ }
38
+ return { skipValidation, template, valid: true };
39
+ }
8
40
  function runInitCommand(args, options = {}) {
9
41
  const cwd = options.cwd ?? process.cwd();
10
- const validateSkillFn = options.validateSkill ?? ((targetDir) => (0, validator_1.validateSkill)(targetDir, { strict: true }));
11
- const skipValidation = args.includes("--no-validate");
12
- const hasUnsupportedArgs = args.length > 1 || (args.length === 1 && args[0] !== "--no-validate");
13
- if (hasUnsupportedArgs) {
42
+ const validateSkillFn = options.validateSkill ?? validator_1.validateSkill;
43
+ const { skipValidation, template, valid } = parseInitArgs(args);
44
+ if (!valid) {
14
45
  return (0, command_output_1.failWithUsage)("skillmd init: unsupported argument(s)", cli_text_1.INIT_USAGE);
15
46
  }
16
47
  try {
17
- const result = (0, scaffold_1.scaffoldSkillInDirectory)(cwd);
48
+ const result = (0, scaffold_1.scaffoldSkillInDirectory)(cwd, { template });
18
49
  console.log(`Initialized skill '${result.skillName}'.`);
19
50
  if (skipValidation) {
20
51
  console.log("Validation skipped (--no-validate).");
21
52
  return 0;
22
53
  }
23
- const validation = validateSkillFn(cwd);
54
+ const validation = validateSkillFn(cwd, { strict: result.template === "verbose" });
24
55
  (0, command_output_1.printValidationResult)(validation);
25
56
  if (validation.status === "passed") {
26
57
  return 0;
@@ -1,64 +1,18 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.runLoginCommand = runLoginCommand;
4
- const cli_text_1 = require("../lib/cli-text");
5
- const command_output_1 = require("../lib/command-output");
6
- const auth_config_1 = require("../lib/auth-config");
7
- const auth_session_1 = require("../lib/auth-session");
8
- const github_device_flow_1 = require("../lib/github-device-flow");
9
- const firebase_auth_1 = require("../lib/firebase-auth");
10
- function parseFlags(args) {
11
- let status = false;
12
- let reauth = false;
13
- for (const arg of args) {
14
- if (arg === "--status") {
15
- status = true;
16
- continue;
17
- }
18
- if (arg === "--reauth") {
19
- reauth = true;
20
- continue;
21
- }
22
- return { status: false, reauth: false, valid: false };
23
- }
24
- if (status && reauth) {
25
- return { status: false, reauth: false, valid: false };
26
- }
27
- return { status, reauth, valid: true };
28
- }
29
- function formatSessionProject(session, currentConfigProjectId) {
30
- if (!session.projectId) {
31
- if (currentConfigProjectId) {
32
- return { label: `unknown (current config: ${currentConfigProjectId})`, mismatch: false };
33
- }
34
- return { label: "unknown", mismatch: false };
35
- }
36
- return {
37
- label: session.projectId,
38
- mismatch: Boolean(currentConfigProjectId && session.projectId !== currentConfigProjectId),
39
- };
40
- }
41
- function printSessionStatus(session, currentConfigProjectId) {
42
- if (!session) {
43
- console.log("Not logged in.");
44
- return 1;
45
- }
46
- const project = formatSessionProject(session, currentConfigProjectId);
47
- if (session.email) {
48
- console.log(`Logged in with GitHub as ${session.email} (project: ${project.label}).`);
49
- }
50
- else {
51
- console.log(`Logged in with GitHub (uid: ${session.uid}, project: ${project.label}).`);
52
- }
53
- if (project.mismatch && currentConfigProjectId) {
54
- console.log(`Current CLI config targets project '${currentConfigProjectId}'. ` +
55
- "Run 'skillmd login --reauth' to switch projects.");
56
- }
57
- return 0;
58
- }
4
+ const cli_text_1 = require("../lib/shared/cli-text");
5
+ const command_output_1 = require("../lib/shared/command-output");
6
+ const config_1 = require("../lib/auth/config");
7
+ const session_1 = require("../lib/auth/session");
8
+ const github_device_flow_1 = require("../lib/auth/github-device-flow");
9
+ const firebase_auth_1 = require("../lib/auth/firebase-auth");
10
+ const login_flow_1 = require("../lib/auth/login-flow");
11
+ const login_flags_1 = require("../lib/auth/login-flags");
12
+ const login_status_1 = require("../lib/auth/login-status");
59
13
  function requireConfig(env) {
60
14
  try {
61
- return (0, auth_config_1.getLoginEnvConfig)(env);
15
+ return (0, config_1.getLoginEnvConfig)(env);
62
16
  }
63
17
  catch (error) {
64
18
  const message = error instanceof Error ? error.message : "invalid login configuration";
@@ -68,72 +22,27 @@ function requireConfig(env) {
68
22
  }
69
23
  }
70
24
  async function runLoginCommand(args, options = {}) {
71
- const { status, reauth, valid } = parseFlags(args);
25
+ const { status, reauth, valid } = (0, login_flags_1.parseLoginFlags)(args);
72
26
  if (!valid) {
73
27
  return (0, command_output_1.failWithUsage)("skillmd login: unsupported argument(s)", cli_text_1.LOGIN_USAGE);
74
28
  }
75
- const readSessionFn = options.readSession ?? auth_session_1.readAuthSession;
76
- const writeSessionFn = options.writeSession ?? auth_session_1.writeAuthSession;
77
- const clearSessionFn = options.clearSession ?? auth_session_1.clearAuthSession;
29
+ const readSessionFn = options.readSession ?? session_1.readAuthSession;
30
+ const writeSessionFn = options.writeSession ?? session_1.writeAuthSession;
31
+ const clearSessionFn = options.clearSession ?? session_1.clearAuthSession;
78
32
  try {
79
33
  const config = requireConfig(options.env ?? process.env);
80
34
  if (status) {
81
- return printSessionStatus(readSessionFn(), config.firebaseProjectId);
82
- }
83
- const existingSession = readSessionFn();
84
- if (existingSession && !reauth) {
85
- const verifyRefreshTokenFn = options.verifyRefreshToken ?? firebase_auth_1.verifyFirebaseRefreshToken;
86
- try {
87
- const validation = await verifyRefreshTokenFn(config.firebaseApiKey, existingSession.refreshToken);
88
- if (validation.valid) {
89
- const project = formatSessionProject(existingSession, config.firebaseProjectId);
90
- if (existingSession.email) {
91
- console.log(`Already logged in as ${existingSession.email} (project: ${project.label}). ` +
92
- "Run 'skillmd logout' first.");
93
- }
94
- else {
95
- console.log(`Already logged in (uid: ${existingSession.uid}, project: ${project.label}). ` +
96
- "Run 'skillmd logout' first.");
97
- }
98
- if (project.mismatch) {
99
- console.log(`Current CLI config targets project '${config.firebaseProjectId}'. ` +
100
- "Run 'skillmd login --reauth' to switch projects.");
101
- }
102
- return 0;
103
- }
104
- clearSessionFn();
105
- console.log("Existing session is no longer valid. Starting re-authentication.");
106
- }
107
- catch (error) {
108
- const message = error instanceof Error ? error.message : "Unknown error";
109
- console.error(`skillmd login: unable to verify existing session (${message}). ` +
110
- "Keeping current session. Run 'skillmd login --reauth' to force reauthentication.");
111
- return 1;
112
- }
35
+ return (0, login_status_1.printSessionStatus)(readSessionFn(), config.firebaseProjectId);
113
36
  }
114
- const requestDeviceCodeFn = options.requestDeviceCode ?? github_device_flow_1.requestDeviceCode;
115
- const pollForAccessTokenFn = options.pollForAccessToken ?? github_device_flow_1.pollForAccessToken;
116
- const signInFn = options.signInWithGitHubAccessToken ?? firebase_auth_1.signInWithGitHubAccessToken;
117
- const deviceCode = await requestDeviceCodeFn(config.githubClientId);
118
- console.log("Open this URL in your browser to authorize skillmd:");
119
- console.log(deviceCode.verificationUriComplete ?? deviceCode.verificationUri);
120
- console.log(`Then enter code: ${deviceCode.userCode}`);
121
- const token = await pollForAccessTokenFn(config.githubClientId, deviceCode.deviceCode, deviceCode.interval, deviceCode.expiresIn);
122
- const firebaseSession = await signInFn(config.firebaseApiKey, token.accessToken);
123
- writeSessionFn({
124
- provider: "github",
125
- uid: firebaseSession.localId,
126
- email: firebaseSession.email,
127
- refreshToken: firebaseSession.refreshToken,
128
- projectId: config.firebaseProjectId,
37
+ return await (0, login_flow_1.executeLoginFlow)(config, reauth, {
38
+ readSession: readSessionFn,
39
+ writeSession: writeSessionFn,
40
+ clearSession: clearSessionFn,
41
+ requestDeviceCode: options.requestDeviceCode ?? github_device_flow_1.requestDeviceCode,
42
+ pollForAccessToken: options.pollForAccessToken ?? github_device_flow_1.pollForAccessToken,
43
+ signInWithGitHubAccessToken: options.signInWithGitHubAccessToken ?? firebase_auth_1.signInWithGitHubAccessToken,
44
+ verifyRefreshToken: options.verifyRefreshToken ?? firebase_auth_1.verifyFirebaseRefreshToken,
129
45
  });
130
- if (firebaseSession.email) {
131
- console.log(`Login successful. Signed in as ${firebaseSession.email} (project: ${config.firebaseProjectId}).`);
132
- }
133
- else {
134
- console.log(`Login successful (project: ${config.firebaseProjectId}).`);
135
- }
136
- return 0;
137
46
  }
138
47
  catch (error) {
139
48
  const message = error instanceof Error ? error.message : "Unknown error";
@@ -1,19 +1,26 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.runLogoutCommand = runLogoutCommand;
4
- const cli_text_1 = require("../lib/cli-text");
5
- const command_output_1 = require("../lib/command-output");
6
- const auth_session_1 = require("../lib/auth-session");
4
+ const cli_text_1 = require("../lib/shared/cli-text");
5
+ const command_output_1 = require("../lib/shared/command-output");
6
+ const session_1 = require("../lib/auth/session");
7
7
  function runLogoutCommand(args, options = {}) {
8
8
  if (args.length > 0) {
9
9
  return (0, command_output_1.failWithUsage)("skillmd logout: unsupported argument(s)", cli_text_1.LOGOUT_USAGE);
10
10
  }
11
- const clearSessionFn = options.clearSession ?? auth_session_1.clearAuthSession;
12
- const removed = clearSessionFn();
13
- if (removed) {
14
- console.log("Logged out.");
11
+ try {
12
+ const clearSessionFn = options.clearSession ?? session_1.clearAuthSession;
13
+ const removed = clearSessionFn();
14
+ if (removed) {
15
+ console.log("Logged out.");
16
+ return 0;
17
+ }
18
+ console.log("No active session to log out.");
15
19
  return 0;
16
20
  }
17
- console.log("No active session to log out.");
18
- return 0;
21
+ catch (error) {
22
+ const message = error instanceof Error ? error.message : "Unknown error";
23
+ console.error(`skillmd logout: ${message}`);
24
+ return 1;
25
+ }
19
26
  }
@@ -2,61 +2,68 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.runValidateCommand = runValidateCommand;
4
4
  const node_path_1 = require("node:path");
5
- const cli_text_1 = require("../lib/cli-text");
6
- const command_output_1 = require("../lib/command-output");
7
- const upstream_validator_1 = require("../lib/upstream-validator");
8
- const validator_1 = require("../lib/validator");
5
+ const cli_text_1 = require("../lib/shared/cli-text");
6
+ const command_output_1 = require("../lib/shared/command-output");
7
+ const upstream_validator_1 = require("../lib/validation/upstream-validator");
8
+ const validator_1 = require("../lib/validation/validator");
9
9
  function runValidateCommand(args, options = {}) {
10
- const cwd = options.cwd ?? process.cwd();
11
- const validateLocal = options.validateLocal ?? validator_1.validateSkill;
12
- const validateUpstream = options.validateUpstream ?? upstream_validator_1.validateWithSkillsRef;
13
- let strict = false;
14
- let parity = false;
15
- let pathArg;
16
- for (const arg of args) {
17
- if (arg === "--strict") {
18
- strict = true;
19
- continue;
10
+ try {
11
+ const cwd = options.cwd ?? process.cwd();
12
+ const validateLocal = options.validateLocal ?? validator_1.validateSkill;
13
+ const validateUpstream = options.validateUpstream ?? upstream_validator_1.validateWithSkillsRef;
14
+ let strict = false;
15
+ let parity = false;
16
+ let pathArg;
17
+ for (const arg of args) {
18
+ if (arg === "--strict") {
19
+ strict = true;
20
+ continue;
21
+ }
22
+ if (arg === "--parity") {
23
+ parity = true;
24
+ continue;
25
+ }
26
+ if (arg.startsWith("-")) {
27
+ return (0, command_output_1.failWithUsage)(`skillmd validate: unsupported flag '${arg}'`, cli_text_1.VALIDATE_USAGE);
28
+ }
29
+ if (pathArg) {
30
+ return (0, command_output_1.failWithUsage)("skillmd validate: accepts at most one path argument", cli_text_1.VALIDATE_USAGE);
31
+ }
32
+ pathArg = arg;
20
33
  }
21
- if (arg === "--parity") {
22
- parity = true;
23
- continue;
24
- }
25
- if (arg.startsWith("-")) {
26
- return (0, command_output_1.failWithUsage)(`skillmd validate: unsupported flag '${arg}'`, cli_text_1.VALIDATE_USAGE);
27
- }
28
- if (pathArg) {
29
- return (0, command_output_1.failWithUsage)("skillmd validate: accepts at most one path argument", cli_text_1.VALIDATE_USAGE);
30
- }
31
- pathArg = arg;
32
- }
33
- const targetDir = pathArg ? (0, node_path_1.resolve)(cwd, pathArg) : cwd;
34
- const validation = validateLocal(targetDir, { strict });
35
- if (parity) {
36
- const upstream = validateUpstream(targetDir);
37
- if (upstream.status === "unavailable") {
38
- console.error(`Validation parity unavailable: ${upstream.message}. Install skills-ref to use --parity.`);
39
- return 1;
40
- }
41
- if (validation.status === "passed" && upstream.status !== "passed") {
42
- console.error("Validation parity mismatch: local validation passed but skills-ref failed.");
43
- console.error(`skills-ref: ${upstream.message}`);
44
- return 1;
34
+ const targetDir = pathArg ? (0, node_path_1.resolve)(cwd, pathArg) : cwd;
35
+ const validation = validateLocal(targetDir, { strict });
36
+ if (parity) {
37
+ const upstream = validateUpstream(targetDir);
38
+ if (upstream.status === "unavailable") {
39
+ console.error(`Validation parity unavailable: ${upstream.message}. Install skills-ref to use --parity.`);
40
+ return 1;
41
+ }
42
+ if (validation.status === "passed" && upstream.status !== "passed") {
43
+ console.error("Validation parity mismatch: local validation passed but skills-ref failed.");
44
+ console.error(`skills-ref: ${upstream.message}`);
45
+ return 1;
46
+ }
47
+ if (validation.status === "failed" && upstream.status !== "failed") {
48
+ console.error("Validation parity mismatch: local validation failed but skills-ref passed.");
49
+ return 1;
50
+ }
45
51
  }
46
- if (validation.status === "failed" && upstream.status !== "failed") {
47
- console.error("Validation parity mismatch: local validation failed but skills-ref passed.");
48
- return 1;
52
+ (0, command_output_1.printValidationResult)(validation);
53
+ if (validation.status === "passed") {
54
+ if (parity) {
55
+ console.log("Validation parity passed (skills-ref).");
56
+ }
57
+ return 0;
49
58
  }
50
- }
51
- (0, command_output_1.printValidationResult)(validation);
52
- if (validation.status === "passed") {
53
59
  if (parity) {
54
- console.log("Validation parity passed (skills-ref).");
60
+ console.error("Validation parity matched (skills-ref also failed).");
55
61
  }
56
- return 0;
62
+ return 1;
57
63
  }
58
- if (parity) {
59
- console.error("Validation parity matched (skills-ref also failed).");
64
+ catch (error) {
65
+ const message = error instanceof Error ? error.message : "Unknown error";
66
+ console.error(`skillmd validate: ${message}`);
67
+ return 1;
60
68
  }
61
- return 1;
62
69
  }
@@ -5,7 +5,7 @@ exports.getDefaultUserEnvPath = getDefaultUserEnvPath;
5
5
  const node_fs_1 = require("node:fs");
6
6
  const node_os_1 = require("node:os");
7
7
  const node_path_1 = require("node:path");
8
- const auth_defaults_1 = require("./auth-defaults");
8
+ const defaults_1 = require("./defaults");
9
9
  const USER_ENV_RELATIVE_PATH = ".skillmd/.env";
10
10
  function parseDotEnv(content) {
11
11
  const values = {};
@@ -52,9 +52,9 @@ function pickValue(...candidates) {
52
52
  }
53
53
  function getLoginEnvConfig(env = process.env, options = {}) {
54
54
  const dotEnv = loadDotEnv(getDefaultUserEnvPath(options));
55
- const githubClientId = pickValue(env.SKILLMD_GITHUB_CLIENT_ID, dotEnv.SKILLMD_GITHUB_CLIENT_ID, auth_defaults_1.DEFAULT_LOGIN_AUTH_CONFIG.githubClientId);
56
- const firebaseApiKey = pickValue(env.SKILLMD_FIREBASE_API_KEY, dotEnv.SKILLMD_FIREBASE_API_KEY, auth_defaults_1.DEFAULT_LOGIN_AUTH_CONFIG.firebaseApiKey);
57
- const firebaseProjectId = pickValue(env.SKILLMD_FIREBASE_PROJECT_ID, dotEnv.SKILLMD_FIREBASE_PROJECT_ID, auth_defaults_1.DEFAULT_LOGIN_AUTH_CONFIG.firebaseProjectId);
55
+ const githubClientId = pickValue(env.SKILLMD_GITHUB_CLIENT_ID, dotEnv.SKILLMD_GITHUB_CLIENT_ID, defaults_1.DEFAULT_LOGIN_AUTH_CONFIG.githubClientId);
56
+ const firebaseApiKey = pickValue(env.SKILLMD_FIREBASE_API_KEY, dotEnv.SKILLMD_FIREBASE_API_KEY, defaults_1.DEFAULT_LOGIN_AUTH_CONFIG.firebaseApiKey);
57
+ const firebaseProjectId = pickValue(env.SKILLMD_FIREBASE_PROJECT_ID, dotEnv.SKILLMD_FIREBASE_PROJECT_ID, defaults_1.DEFAULT_LOGIN_AUTH_CONFIG.firebaseProjectId);
58
58
  if (!githubClientId || !firebaseApiKey || !firebaseProjectId) {
59
59
  throw new Error("missing login configuration");
60
60
  }
@@ -2,7 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.signInWithGitHubAccessToken = signInWithGitHubAccessToken;
4
4
  exports.verifyFirebaseRefreshToken = verifyFirebaseRefreshToken;
5
- const http_1 = require("./http");
5
+ const http_1 = require("../shared/http");
6
6
  const FIREBASE_HTTP_TIMEOUT_MS = 10000;
7
7
  async function parseJsonApiResponse(response, apiLabel) {
8
8
  const text = await response.text();
@@ -2,7 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.requestDeviceCode = requestDeviceCode;
4
4
  exports.pollForAccessToken = pollForAccessToken;
5
- const http_1 = require("./http");
5
+ const http_1 = require("../shared/http");
6
6
  const GITHUB_DEVICE_CODE_URL = "https://github.com/login/device/code";
7
7
  const GITHUB_ACCESS_TOKEN_URL = "https://github.com/login/oauth/access_token";
8
8
  const GITHUB_HTTP_TIMEOUT_MS = 10000;
@@ -0,0 +1,22 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.parseLoginFlags = parseLoginFlags;
4
+ function parseLoginFlags(args) {
5
+ let status = false;
6
+ let reauth = false;
7
+ for (const arg of args) {
8
+ if (arg === "--status") {
9
+ status = true;
10
+ continue;
11
+ }
12
+ if (arg === "--reauth") {
13
+ reauth = true;
14
+ continue;
15
+ }
16
+ return { status: false, reauth: false, valid: false };
17
+ }
18
+ if (status && reauth) {
19
+ return { status: false, reauth: false, valid: false };
20
+ }
21
+ return { status, reauth, valid: true };
22
+ }
@@ -0,0 +1,56 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.executeLoginFlow = executeLoginFlow;
4
+ const login_status_1 = require("./login-status");
5
+ async function executeLoginFlow(config, reauth, dependencies) {
6
+ const existingSession = dependencies.readSession();
7
+ if (existingSession && !reauth) {
8
+ try {
9
+ const validation = await dependencies.verifyRefreshToken(config.firebaseApiKey, existingSession.refreshToken);
10
+ if (validation.valid) {
11
+ const project = (0, login_status_1.formatSessionProject)(existingSession, config.firebaseProjectId);
12
+ if (existingSession.email) {
13
+ console.log(`Already logged in as ${existingSession.email} (project: ${project.label}). ` +
14
+ "Run 'skillmd logout' first.");
15
+ }
16
+ else {
17
+ console.log(`Already logged in (uid: ${existingSession.uid}, project: ${project.label}). ` +
18
+ "Run 'skillmd logout' first.");
19
+ }
20
+ if (project.mismatch) {
21
+ console.log(`Current CLI config targets project '${config.firebaseProjectId}'. ` +
22
+ "Run 'skillmd login --reauth' to switch projects.");
23
+ }
24
+ return 0;
25
+ }
26
+ dependencies.clearSession();
27
+ console.log("Existing session is no longer valid. Starting re-authentication.");
28
+ }
29
+ catch (error) {
30
+ const message = error instanceof Error ? error.message : "Unknown error";
31
+ console.error(`skillmd login: unable to verify existing session (${message}). ` +
32
+ "Keeping current session. Run 'skillmd login --reauth' to force reauthentication.");
33
+ return 1;
34
+ }
35
+ }
36
+ const deviceCode = await dependencies.requestDeviceCode(config.githubClientId);
37
+ console.log("Open this URL in your browser to authorize skillmd:");
38
+ console.log(deviceCode.verificationUriComplete ?? deviceCode.verificationUri);
39
+ console.log(`Then enter code: ${deviceCode.userCode}`);
40
+ const token = await dependencies.pollForAccessToken(config.githubClientId, deviceCode.deviceCode, deviceCode.interval, deviceCode.expiresIn);
41
+ const firebaseSession = await dependencies.signInWithGitHubAccessToken(config.firebaseApiKey, token.accessToken);
42
+ dependencies.writeSession({
43
+ provider: "github",
44
+ uid: firebaseSession.localId,
45
+ email: firebaseSession.email,
46
+ refreshToken: firebaseSession.refreshToken,
47
+ projectId: config.firebaseProjectId,
48
+ });
49
+ if (firebaseSession.email) {
50
+ console.log(`Login successful. Signed in as ${firebaseSession.email} (project: ${config.firebaseProjectId}).`);
51
+ }
52
+ else {
53
+ console.log(`Login successful (project: ${config.firebaseProjectId}).`);
54
+ }
55
+ return 0;
56
+ }
@@ -0,0 +1,34 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.formatSessionProject = formatSessionProject;
4
+ exports.printSessionStatus = printSessionStatus;
5
+ function formatSessionProject(session, currentConfigProjectId) {
6
+ if (!session.projectId) {
7
+ if (currentConfigProjectId) {
8
+ return { label: `unknown (current config: ${currentConfigProjectId})`, mismatch: false };
9
+ }
10
+ return { label: "unknown", mismatch: false };
11
+ }
12
+ return {
13
+ label: session.projectId,
14
+ mismatch: Boolean(currentConfigProjectId && session.projectId !== currentConfigProjectId),
15
+ };
16
+ }
17
+ function printSessionStatus(session, currentConfigProjectId) {
18
+ if (!session) {
19
+ console.log("Not logged in.");
20
+ return 1;
21
+ }
22
+ const project = formatSessionProject(session, currentConfigProjectId);
23
+ if (session.email) {
24
+ console.log(`Logged in with GitHub as ${session.email} (project: ${project.label}).`);
25
+ }
26
+ else {
27
+ console.log(`Logged in with GitHub (uid: ${session.uid}, project: ${project.label}).`);
28
+ }
29
+ if (project.mismatch && currentConfigProjectId) {
30
+ console.log(`Current CLI config targets project '${currentConfigProjectId}'. ` +
31
+ "Run 'skillmd login --reauth' to switch projects.");
32
+ }
33
+ return 0;
34
+ }
@@ -0,0 +1,79 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.isInitTemplateId = isInitTemplateId;
4
+ exports.resolveInitTemplateId = resolveInitTemplateId;
5
+ exports.scaffoldSkillInDirectory = scaffoldSkillInDirectory;
6
+ const node_fs_1 = require("node:fs");
7
+ const node_path_1 = require("node:path");
8
+ const normalize_name_1 = require("./normalize-name");
9
+ const skill_spec_1 = require("./skill-spec");
10
+ const templates_1 = require("./templates");
11
+ function assertDirectoryEmpty(targetDir) {
12
+ const entries = (0, node_fs_1.readdirSync)(targetDir);
13
+ if (entries.length > 0) {
14
+ throw new Error(`target directory is not empty (${entries.length} item(s) found); run 'skillmd init' in an empty directory`);
15
+ }
16
+ }
17
+ function assertDirectoryNameMatchesNormalized(targetDir) {
18
+ const dirName = (0, node_path_1.basename)(targetDir);
19
+ const normalizedName = (0, normalize_name_1.normalizeSkillName)(dirName);
20
+ if (dirName !== normalizedName) {
21
+ throw new Error(`directory name '${dirName}' must already be normalized. Rename it to '${normalizedName}' and retry`);
22
+ }
23
+ return normalizedName;
24
+ }
25
+ function isInitTemplateId(value) {
26
+ return skill_spec_1.INIT_TEMPLATE_IDS.includes(value);
27
+ }
28
+ function resolveInitTemplateId(value) {
29
+ return isInitTemplateId(value) ? value : null;
30
+ }
31
+ function createDirectory(path, createdPaths) {
32
+ (0, node_fs_1.mkdirSync)(path, { recursive: false, mode: 0o755 });
33
+ createdPaths.push({ kind: "dir", path });
34
+ }
35
+ function createFile(path, content, createdPaths) {
36
+ (0, node_fs_1.writeFileSync)(path, content, { encoding: "utf8", flag: "wx", mode: 0o644 });
37
+ (0, node_fs_1.chmodSync)(path, 0o644);
38
+ createdPaths.push({ kind: "file", path });
39
+ }
40
+ function rollbackCreatedPaths(createdPaths) {
41
+ for (const entry of [...createdPaths].reverse()) {
42
+ if (entry.kind === "file") {
43
+ (0, node_fs_1.rmSync)(entry.path, { force: true });
44
+ continue;
45
+ }
46
+ (0, node_fs_1.rmSync)(entry.path, { recursive: true, force: true });
47
+ }
48
+ }
49
+ function scaffoldSkillInDirectory(targetDir, options = {}) {
50
+ const template = options.template ?? "minimal";
51
+ const skillName = assertDirectoryNameMatchesNormalized(targetDir);
52
+ assertDirectoryEmpty(targetDir);
53
+ const createdPaths = [];
54
+ try {
55
+ if (template === "verbose") {
56
+ for (const directory of skill_spec_1.SCAFFOLD_DIRECTORIES) {
57
+ const fullPath = (0, node_path_1.join)(targetDir, directory);
58
+ createDirectory(fullPath, createdPaths);
59
+ createFile((0, node_path_1.join)(fullPath, ".gitkeep"), "", createdPaths);
60
+ }
61
+ createFile((0, node_path_1.join)(targetDir, ".gitignore"), (0, templates_1.buildGitignore)(), createdPaths);
62
+ createFile((0, node_path_1.join)(targetDir, "SKILL.md"), (0, templates_1.buildVerboseSkillMarkdown)(skillName), createdPaths);
63
+ createFile((0, node_path_1.join)(targetDir, "scripts", "README.md"), (0, templates_1.buildScriptsReadme)(), createdPaths);
64
+ createFile((0, node_path_1.join)(targetDir, "scripts", "extract.py"), (0, templates_1.buildExtractScriptPython)(), createdPaths);
65
+ createFile((0, node_path_1.join)(targetDir, "references", "REFERENCE.md"), (0, templates_1.buildReferenceGuide)(), createdPaths);
66
+ createFile((0, node_path_1.join)(targetDir, "references", "FORMS.md"), (0, templates_1.buildFormsReference)(), createdPaths);
67
+ createFile((0, node_path_1.join)(targetDir, "assets", "README.md"), (0, templates_1.buildAssetsReadme)(), createdPaths);
68
+ createFile((0, node_path_1.join)(targetDir, "assets", "report-template.md"), (0, templates_1.buildReportTemplate)(), createdPaths);
69
+ createFile((0, node_path_1.join)(targetDir, "assets", "lookup-table.csv"), (0, templates_1.buildLookupTableCsv)(), createdPaths);
70
+ return { skillName, template };
71
+ }
72
+ createFile((0, node_path_1.join)(targetDir, "SKILL.md"), (0, templates_1.buildMinimalSkillMarkdown)(skillName), createdPaths);
73
+ return { skillName, template };
74
+ }
75
+ catch (error) {
76
+ rollbackCreatedPaths(createdPaths);
77
+ throw error;
78
+ }
79
+ }
@@ -1,7 +1,8 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.STRICT_SECTION_HEADINGS = exports.STRICT_SECTION_TITLES = exports.STRICT_REQUIRED_FILES = exports.SCAFFOLD_DIRECTORIES = exports.MAX_SKILL_NAME_LENGTH = void 0;
3
+ exports.STRICT_SECTION_HEADINGS = exports.STRICT_SECTION_TITLES = exports.STRICT_REQUIRED_FILES = exports.SCAFFOLD_DIRECTORIES = exports.INIT_TEMPLATE_IDS = exports.MAX_SKILL_NAME_LENGTH = void 0;
4
4
  exports.MAX_SKILL_NAME_LENGTH = 64;
5
+ exports.INIT_TEMPLATE_IDS = ["minimal", "verbose"];
5
6
  exports.SCAFFOLD_DIRECTORIES = ["scripts", "references", "assets"];
6
7
  exports.STRICT_REQUIRED_FILES = [
7
8
  ".gitignore",
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./skill-markdown"), exports);
18
+ __exportStar(require("./resources"), exports);
@@ -0,0 +1,169 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.buildGitignore = buildGitignore;
4
+ exports.buildScriptsReadme = buildScriptsReadme;
5
+ exports.buildExtractScriptPython = buildExtractScriptPython;
6
+ exports.buildReferenceGuide = buildReferenceGuide;
7
+ exports.buildFormsReference = buildFormsReference;
8
+ exports.buildAssetsReadme = buildAssetsReadme;
9
+ exports.buildReportTemplate = buildReportTemplate;
10
+ exports.buildLookupTableCsv = buildLookupTableCsv;
11
+ function buildGitignore() {
12
+ return `node_modules/
13
+ dist/
14
+ .DS_Store
15
+ npm-debug.log*
16
+ `;
17
+ }
18
+ function buildScriptsReadme() {
19
+ return `# scripts/
20
+
21
+ Use this folder for executable code that agents can run.
22
+
23
+ Guidelines:
24
+ - Keep scripts self-contained or clearly document dependencies.
25
+ - Emit actionable error messages.
26
+ - Handle edge cases (missing files, invalid input, empty data).
27
+
28
+ Starter script:
29
+ - \`scripts/extract.py\`
30
+ `;
31
+ }
32
+ function buildExtractScriptPython() {
33
+ return `#!/usr/bin/env python3
34
+ """Example extraction script with defensive error handling.
35
+
36
+ Usage:
37
+ python3 scripts/extract.py --input ./input.txt --output ./output.json
38
+ """
39
+
40
+ from __future__ import annotations
41
+
42
+ import argparse
43
+ import json
44
+ import os
45
+ import sys
46
+
47
+
48
+ def parse_args() -> argparse.Namespace:
49
+ parser = argparse.ArgumentParser(description="Extract line and word statistics.")
50
+ parser.add_argument("--input", required=True, help="Path to input text file.")
51
+ parser.add_argument("--output", required=True, help="Path to output JSON file.")
52
+ return parser.parse_args()
53
+
54
+
55
+ def main() -> int:
56
+ args = parse_args()
57
+
58
+ if not os.path.exists(args.input):
59
+ print(f"error: input file does not exist: {args.input}", file=sys.stderr)
60
+ return 1
61
+
62
+ try:
63
+ with open(args.input, "r", encoding="utf-8") as source:
64
+ content = source.read()
65
+ except OSError as error:
66
+ print(f"error: failed to read input file: {error}", file=sys.stderr)
67
+ return 1
68
+
69
+ lines = [line for line in content.splitlines() if line.strip()]
70
+ result = {
71
+ "line_count": len(lines),
72
+ "word_count": len(content.split()),
73
+ "preview": lines[:3],
74
+ }
75
+
76
+ try:
77
+ with open(args.output, "w", encoding="utf-8") as destination:
78
+ json.dump(result, destination, indent=2)
79
+ destination.write("\\n")
80
+ except OSError as error:
81
+ print(f"error: failed to write output file: {error}", file=sys.stderr)
82
+ return 1
83
+
84
+ print(f"wrote extraction summary to {args.output}")
85
+ return 0
86
+
87
+
88
+ if __name__ == "__main__":
89
+ raise SystemExit(main())
90
+ `;
91
+ }
92
+ function buildReferenceGuide() {
93
+ return `# Reference Guide
94
+
95
+ Use this file for detailed technical reference that should not live in \`SKILL.md\`.
96
+
97
+ Suggested sections:
98
+ - Data model and field definitions
99
+ - Validation rules and constraints
100
+ - External API contract notes
101
+ - Error catalog and recovery guidance
102
+
103
+ Keep this file focused and concise. Add deeper detail in additional reference files when needed.
104
+ `;
105
+ }
106
+ function buildFormsReference() {
107
+ return `# Forms and Structured Templates
108
+
109
+ Use this file to store reusable form structures and payload templates.
110
+
111
+ ## Intake Form (Example)
112
+
113
+ \`\`\`yaml
114
+ request_id: "REQ-0001"
115
+ request_type: "analysis"
116
+ priority: "normal"
117
+ inputs:
118
+ source_path: "./input.txt"
119
+ output_path: "./output.json"
120
+ \`\`\`
121
+
122
+ ## Result Envelope (Example)
123
+
124
+ \`\`\`json
125
+ {
126
+ "status": "ok",
127
+ "summary": "One-line result summary",
128
+ "artifacts": []
129
+ }
130
+ \`\`\`
131
+ `;
132
+ }
133
+ function buildAssetsReadme() {
134
+ return `# assets/
135
+
136
+ Use this folder for static resources that scripts or instructions can reference:
137
+ - templates
138
+ - lookup tables
139
+ - example data
140
+ - diagrams
141
+
142
+ Keep asset files small and task-focused.
143
+ `;
144
+ }
145
+ function buildReportTemplate() {
146
+ return `# Report Template
147
+
148
+ ## Summary
149
+ - Status:
150
+ - Owner:
151
+ - Updated:
152
+
153
+ ## Findings
154
+ 1.
155
+ 2.
156
+ 3.
157
+
158
+ ## Next Steps
159
+ 1.
160
+ 2.
161
+ `;
162
+ }
163
+ function buildLookupTableCsv() {
164
+ return `code,label,description
165
+ E001,missing_input,Required input file is missing
166
+ E002,invalid_format,Input format is invalid or unsupported
167
+ E003,write_failed,Output could not be written
168
+ `;
169
+ }
@@ -0,0 +1,38 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.buildSkillMarkdown = buildSkillMarkdown;
4
+ exports.buildMinimalSkillMarkdown = buildMinimalSkillMarkdown;
5
+ exports.buildVerboseSkillMarkdown = buildVerboseSkillMarkdown;
6
+ const skill_spec_1 = require("../skill-spec");
7
+ const SECTION_PLACEHOLDERS = {
8
+ Scope: "Define the boundaries of this skill: what it should and should not handle.",
9
+ "When to use": "Describe the signals or request patterns that should trigger this skill. Keep SKILL.md focused and move deep details into references/ files.",
10
+ Inputs: "List required and optional inputs, expected formats, and assumptions. Add structured templates to references/FORMS.md when helpful.",
11
+ Outputs: "Describe expected outputs, side effects, and completion criteria. Use assets/report-template.md when a fixed output structure is useful.",
12
+ "Steps / Procedure": "Provide ordered steps the agent should follow, including key decision points. See [the reference guide](references/REFERENCE.md) for detailed rules. Run helper scripts such as scripts/extract.py when needed.",
13
+ Examples: "Add one or two realistic examples of inputs and expected outputs.",
14
+ "Limitations / Failure modes": "Document known limitations, failure cases, and recommended recovery actions.",
15
+ "Security / Tool access": "State required tools/permissions and any security constraints. If needed, set frontmatter allowed-tools to pre-approve tool usage.",
16
+ };
17
+ function buildSkillMarkdown(name) {
18
+ const sections = skill_spec_1.STRICT_SECTION_TITLES.map((title) => `## ${title}\n\n${SECTION_PLACEHOLDERS[title]}`).join("\n\n");
19
+ return `---
20
+ name: ${name}
21
+ description: Explain what this skill does and when an agent should use it.
22
+ # compatibility: "Optional: environment requirements (products, packages, network access)."
23
+ # metadata:
24
+ # author: "your-org"
25
+ # version: "1.0.0"
26
+ # allowed-tools: "Optional: space-delimited pre-approved tools."
27
+ license: Optional. Add a license name or reference to a bundled license file.
28
+ ---
29
+
30
+ ${sections}
31
+ `;
32
+ }
33
+ function buildMinimalSkillMarkdown(name) {
34
+ return buildSkillMarkdown(name);
35
+ }
36
+ function buildVerboseSkillMarkdown(name) {
37
+ return buildSkillMarkdown(name);
38
+ }
@@ -2,7 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.LOGOUT_USAGE = exports.LOGIN_USAGE = exports.VALIDATE_USAGE = exports.INIT_USAGE = exports.ROOT_USAGE = void 0;
4
4
  exports.ROOT_USAGE = "Usage: skillmd <init|validate|login|logout>";
5
- exports.INIT_USAGE = "Usage: skillmd init [--no-validate]";
5
+ exports.INIT_USAGE = "Usage: skillmd init [--no-validate] [--template <minimal|verbose>]";
6
6
  exports.VALIDATE_USAGE = "Usage: skillmd validate [path] [--strict] [--parity]";
7
7
  exports.LOGIN_USAGE = "Usage: skillmd login [--status|--reauth]";
8
8
  exports.LOGOUT_USAGE = "Usage: skillmd logout";
@@ -2,12 +2,14 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.validateWithSkillsRef = validateWithSkillsRef;
4
4
  const node_child_process_1 = require("node:child_process");
5
+ const SKILLS_REF_TIMEOUT_MS = 10000;
5
6
  function formatOutput(stdout, stderr) {
6
7
  return [stdout.trim(), stderr.trim()].filter((part) => part.length > 0).join("\n");
7
8
  }
8
9
  function validateWithSkillsRef(targetDir) {
9
10
  const result = (0, node_child_process_1.spawnSync)("skills-ref", ["validate", targetDir], {
10
11
  encoding: "utf8",
12
+ timeout: SKILLS_REF_TIMEOUT_MS,
11
13
  });
12
14
  if (result.error) {
13
15
  if ("code" in result.error && result.error.code === "ENOENT") {
@@ -16,6 +18,12 @@ function validateWithSkillsRef(targetDir) {
16
18
  message: "skills-ref is not installed or not on PATH",
17
19
  };
18
20
  }
21
+ if ("code" in result.error && result.error.code === "ETIMEDOUT") {
22
+ return {
23
+ status: "unavailable",
24
+ message: `skills-ref timed out after ${SKILLS_REF_TIMEOUT_MS}ms`,
25
+ };
26
+ }
19
27
  return {
20
28
  status: "unavailable",
21
29
  message: `skills-ref execution failed: ${result.error.message}`,
@@ -4,7 +4,7 @@ exports.validateSkill = validateSkill;
4
4
  const node_fs_1 = require("node:fs");
5
5
  const node_path_1 = require("node:path");
6
6
  const yaml_1 = require("yaml");
7
- const skill_spec_1 = require("./skill-spec");
7
+ const skill_spec_1 = require("../scaffold/skill-spec");
8
8
  const SKILL_FILE = "SKILL.md";
9
9
  function stripUtf8Bom(content) {
10
10
  return content.replace(/^\uFEFF/, "");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@skillmarkdown/cli",
3
- "version": "0.2.1",
3
+ "version": "0.3.0",
4
4
  "description": "CLI for scaffolding SKILL.md-based AI skills",
5
5
  "license": "MIT",
6
6
  "type": "commonjs",
@@ -25,7 +25,7 @@
25
25
  "prepublishOnly": "npm run clean && npm run build",
26
26
  "smoke:link": "bash ./scripts/smoke-link.sh",
27
27
  "smoke:pack": "bash ./scripts/smoke-pack.sh",
28
- "test": "npm run build && node --test tests/*.test.js"
28
+ "test": "npm run build && node --test \"tests/**/*.test.js\""
29
29
  },
30
30
  "engines": {
31
31
  "node": ">=18"
@@ -1,34 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.scaffoldSkillInDirectory = scaffoldSkillInDirectory;
4
- const node_fs_1 = require("node:fs");
5
- const node_path_1 = require("node:path");
6
- const normalize_name_1 = require("./normalize-name");
7
- const skill_spec_1 = require("./skill-spec");
8
- const templates_1 = require("./templates");
9
- function assertDirectoryEmpty(targetDir) {
10
- const entries = (0, node_fs_1.readdirSync)(targetDir);
11
- if (entries.length > 0) {
12
- throw new Error(`target directory is not empty (${entries.length} item(s) found); run 'skillmd init' in an empty directory`);
13
- }
14
- }
15
- function assertDirectoryNameMatchesNormalized(targetDir) {
16
- const dirName = (0, node_path_1.basename)(targetDir);
17
- const normalizedName = (0, normalize_name_1.normalizeSkillName)(dirName);
18
- if (dirName !== normalizedName) {
19
- throw new Error(`directory name '${dirName}' must already be normalized. Rename it to '${normalizedName}' and retry`);
20
- }
21
- return normalizedName;
22
- }
23
- function scaffoldSkillInDirectory(targetDir) {
24
- const skillName = assertDirectoryNameMatchesNormalized(targetDir);
25
- assertDirectoryEmpty(targetDir);
26
- for (const directory of skill_spec_1.SCAFFOLD_DIRECTORIES) {
27
- const fullPath = (0, node_path_1.join)(targetDir, directory);
28
- (0, node_fs_1.mkdirSync)(fullPath, { recursive: true });
29
- (0, node_fs_1.writeFileSync)((0, node_path_1.join)(fullPath, ".gitkeep"), "", "utf8");
30
- }
31
- (0, node_fs_1.writeFileSync)((0, node_path_1.join)(targetDir, "SKILL.md"), (0, templates_1.buildSkillMarkdown)(skillName), "utf8");
32
- (0, node_fs_1.writeFileSync)((0, node_path_1.join)(targetDir, ".gitignore"), (0, templates_1.buildGitignore)(), "utf8");
33
- return { skillName };
34
- }
@@ -1,23 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.buildSkillMarkdown = buildSkillMarkdown;
4
- exports.buildGitignore = buildGitignore;
5
- const skill_spec_1 = require("./skill-spec");
6
- function buildSkillMarkdown(name) {
7
- const sections = skill_spec_1.STRICT_SECTION_TITLES.map((title) => `## ${title}\nTODO`).join("\n\n");
8
- return `---
9
- name: ${name}
10
- description: "TODO: Describe what this skill does and when to use it."
11
- license: TODO
12
- ---
13
-
14
- ${sections}
15
- `;
16
- }
17
- function buildGitignore() {
18
- return `node_modules/
19
- dist/
20
- .DS_Store
21
- npm-debug.log*
22
- `;
23
- }
File without changes
File without changes
File without changes