@tanstack/intent 0.0.21 → 0.0.23

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.d.mts CHANGED
@@ -1,6 +1,5 @@
1
1
  #!/usr/bin/env node
2
2
  //#region src/cli.d.ts
3
- declare const USAGE = "TanStack Intent CLI\n\nUsage:\n intent list [--json] Discover intent-enabled packages\n intent meta [name] List meta-skills, or print one by name\n intent validate [<dir>] Validate skill files (default: skills/)\n intent install Print a skill that guides your coding agent to set up skill-to-task mappings\n intent scaffold Print maintainer scaffold prompt\n intent edit-package-json Wire package.json (files, keywords) for skill publishing\n intent setup-github-actions Copy CI workflow templates to .github/workflows/\n intent stale [dir] [--json] Check skills for staleness";
4
3
  declare function main(argv?: Array<string>): Promise<number>;
5
4
  //#endregion
6
- export { USAGE, main };
5
+ export { main };
package/dist/cli.mjs CHANGED
@@ -1,35 +1,72 @@
1
1
  #!/usr/bin/env node
2
- import { t as INSTALL_PROMPT } from "./install-prompt-C0M-U3WZ.mjs";
2
+ import { n as runInstallCommand } from "./install-BHbA_wkb.mjs";
3
3
  import { existsSync, readFileSync, readdirSync, realpathSync } from "node:fs";
4
4
  import { dirname, join, relative, sep } from "node:path";
5
5
  import { fileURLToPath } from "node:url";
6
+ import { cac } from "cac";
6
7
 
7
- //#region src/cli.ts
8
- function getMetaDir() {
9
- return join(dirname(fileURLToPath(import.meta.url)), "..", "meta");
10
- }
8
+ //#region src/cli-error.ts
9
+ const CLI_FAILURE = Symbol("CliFailure");
11
10
  function fail(message, exitCode = 1) {
12
11
  throw {
12
+ [CLI_FAILURE]: true,
13
13
  message,
14
14
  exitCode
15
15
  };
16
16
  }
17
17
  function isCliFailure(value) {
18
- return !!value && typeof value === "object" && "message" in value && typeof value.message === "string" && "exitCode" in value && typeof value.exitCode === "number";
18
+ return !!value && typeof value === "object" && CLI_FAILURE in value;
19
+ }
20
+
21
+ //#endregion
22
+ //#region src/cli-support.ts
23
+ function printWarnings(warnings) {
24
+ if (warnings.length === 0) return;
25
+ console.log("Warnings:");
26
+ for (const warning of warnings) console.log(` ⚠ ${warning}`);
27
+ }
28
+ function getMetaDir() {
29
+ return join(dirname(fileURLToPath(import.meta.url)), "..", "meta");
19
30
  }
20
31
  async function scanIntentsOrFail() {
21
- const { scanForIntents } = await import("./scanner-RWHFrJ_C.mjs");
32
+ const { scanForIntents } = await import("./scanner-CsDGCRDm.mjs");
22
33
  try {
23
- return await scanForIntents();
34
+ return scanForIntents();
24
35
  } catch (err) {
25
- fail(err.message);
36
+ fail(err instanceof Error ? err.message : String(err));
26
37
  }
27
38
  }
28
- function printWarnings(warnings) {
29
- if (warnings.length === 0) return;
30
- console.log("Warnings:");
31
- for (const warning of warnings) console.log(` ⚠ ${warning}`);
39
+ function readPackageName(root) {
40
+ try {
41
+ const pkgJson = JSON.parse(readFileSync(join(root, "package.json"), "utf8"));
42
+ return typeof pkgJson.name === "string" ? pkgJson.name : relative(process.cwd(), root) || "unknown";
43
+ } catch {
44
+ return relative(process.cwd(), root) || "unknown";
45
+ }
46
+ }
47
+ async function resolveStaleTargets(targetDir) {
48
+ const resolvedRoot = targetDir ? join(process.cwd(), targetDir) : process.cwd();
49
+ const { checkStaleness } = await import("./staleness-Dr5-5wj5.mjs");
50
+ if (existsSync(join(resolvedRoot, "skills"))) return { reports: [await checkStaleness(resolvedRoot, readPackageName(resolvedRoot))] };
51
+ const { findPackagesWithSkills, findWorkspaceRoot } = await import("./setup.mjs");
52
+ const workspaceRoot = findWorkspaceRoot(resolvedRoot);
53
+ if (workspaceRoot) {
54
+ const packageDirs = findPackagesWithSkills(workspaceRoot);
55
+ if (packageDirs.length > 0) return { reports: await Promise.all(packageDirs.map((packageDir) => checkStaleness(packageDir, readPackageName(packageDir)))) };
56
+ }
57
+ const staleResult = await scanIntentsOrFail();
58
+ return { reports: await Promise.all(staleResult.packages.map((pkg) => checkStaleness(pkg.packageRoot, pkg.name))) };
32
59
  }
60
+
61
+ //#endregion
62
+ //#region src/commands/edit-package-json.ts
63
+ async function runEditPackageJsonCommand(root) {
64
+ const { runEditPackageJsonAll } = await import("./setup.mjs");
65
+ runEditPackageJsonAll(root);
66
+ }
67
+
68
+ //#endregion
69
+ //#region src/commands/list.ts
33
70
  function formatScanCoverage(result) {
34
71
  const coverage = [];
35
72
  if (result.nodeModules.local.scanned) coverage.push("project node_modules");
@@ -49,24 +86,10 @@ function printVersionConflicts(result) {
49
86
  console.log();
50
87
  }
51
88
  }
52
- function buildValidationFailure(errors, warnings) {
53
- const lines = [
54
- "",
55
- `❌ Validation failed with ${errors.length} error(s):`,
56
- ""
57
- ];
58
- for (const { file, message } of errors) lines.push(` ${file}: ${message}`);
59
- if (warnings.length > 0) {
60
- lines.push("", "⚠ Packaging warnings:");
61
- for (const warning of warnings) lines.push(` ${warning}`);
62
- }
63
- return lines.join("\n");
64
- }
65
- async function cmdList(args) {
89
+ async function runListCommand(options, scanIntentsOrFail$1) {
66
90
  const { computeSkillNameWidth, printSkillTree, printTable } = await import("./display-CuCDLPP_.mjs");
67
- const jsonOutput = args.includes("--json");
68
- const result = await scanIntentsOrFail();
69
- if (jsonOutput) {
91
+ const result = await scanIntentsOrFail$1();
92
+ if (options.json) {
70
93
  console.log(JSON.stringify(result, null, 2));
71
94
  return;
72
95
  }
@@ -80,7 +103,7 @@ async function cmdList(args) {
80
103
  }
81
104
  return;
82
105
  }
83
- const totalSkills = result.packages.reduce((sum, p) => sum + p.skills.length, 0);
106
+ const totalSkills = result.packages.reduce((sum, pkg) => sum + pkg.skills.length, 0);
84
107
  console.log(`\n${result.packages.length} intent-enabled packages, ${totalSkills} skills (${result.packageManager})\n`);
85
108
  if (scanCoverage) console.log(`Scanned: ${scanCoverage}${result.nodeModules.global.scanned ? " (local packages take precedence)" : ""}\n`);
86
109
  printTable([
@@ -95,8 +118,8 @@ async function cmdList(args) {
95
118
  pkg.intent.requires?.join(", ") || "–"
96
119
  ]));
97
120
  printVersionConflicts(result);
98
- const nameWidth = computeSkillNameWidth(result.packages.map((p) => p.skills));
99
- const showTypes = result.packages.some((p) => p.skills.some((s) => s.type));
121
+ const nameWidth = computeSkillNameWidth(result.packages.map((pkg) => pkg.skills));
122
+ const showTypes = result.packages.some((pkg) => pkg.skills.some((skill) => skill.type));
100
123
  console.log(`\nSkills:\n`);
101
124
  for (const pkg of result.packages) {
102
125
  console.log(` ${pkg.name}`);
@@ -106,18 +129,18 @@ async function cmdList(args) {
106
129
  });
107
130
  console.log();
108
131
  }
109
- console.log(`Feedback:`);
110
- console.log(` Submit feedback on skill usage to help maintainers improve the skills.`);
111
- console.log(` Load: node_modules/@tanstack/intent/meta/feedback-collection/SKILL.md`);
132
+ console.log("Feedback:");
133
+ console.log(" Submit feedback on skill usage to help maintainers improve the skills.");
134
+ console.log(" Load: node_modules/@tanstack/intent/meta/feedback-collection/SKILL.md");
112
135
  console.log();
113
136
  printWarnings(result.warnings);
114
137
  }
115
- async function cmdMeta(args) {
116
- const { parseFrontmatter } = await import("./utils-D7OKi0Rn.mjs");
117
- const metaDir = getMetaDir();
138
+
139
+ //#endregion
140
+ //#region src/commands/meta.ts
141
+ async function runMetaCommand(name, metaDir) {
118
142
  if (!existsSync(metaDir)) fail("Meta-skills directory not found.");
119
- if (args.length > 0) {
120
- const name = args[0];
143
+ if (name) {
121
144
  if (name.includes("..") || name.includes("/") || name.includes("\\")) fail(`Invalid meta-skill name: "${name}"`);
122
145
  const skillFile = join(metaDir, name, "SKILL.md");
123
146
  if (!existsSync(skillFile)) fail(`Meta-skill "${name}" not found. Run \`intent meta\` to list available meta-skills.`);
@@ -128,7 +151,8 @@ async function cmdMeta(args) {
128
151
  }
129
152
  return;
130
153
  }
131
- const entries = readdirSync(metaDir, { withFileTypes: true }).filter((e) => e.isDirectory()).filter((e) => existsSync(join(metaDir, e.name, "SKILL.md")));
154
+ const { parseFrontmatter } = await import("./utils-D7OKi0Rn.mjs");
155
+ const entries = readdirSync(metaDir, { withFileTypes: true }).filter((entry) => entry.isDirectory()).filter((entry) => existsSync(join(metaDir, entry.name, "SKILL.md")));
132
156
  if (entries.length === 0) {
133
157
  console.log("No meta-skills found.");
134
158
  return;
@@ -138,11 +162,145 @@ async function cmdMeta(args) {
138
162
  const fm = parseFrontmatter(join(metaDir, entry.name, "SKILL.md"));
139
163
  let description = "";
140
164
  if (typeof fm?.description === "string") description = fm.description.replace(/\s+/g, " ").trim();
141
- const shortDesc = description.length > 60 ? description.slice(0, 57) + "..." : description;
165
+ const shortDesc = description.length > 60 ? `${description.slice(0, 57)}...` : description;
142
166
  console.log(` ${entry.name.padEnd(28)} ${shortDesc}`);
143
167
  }
144
- console.log(`\nUsage: load the SKILL.md into your AI agent conversation.`);
145
- console.log(`Path: node_modules/@tanstack/intent/meta/<name>/SKILL.md`);
168
+ console.log("\nUsage: load the SKILL.md into your AI agent conversation.");
169
+ console.log("Path: node_modules/@tanstack/intent/meta/<name>/SKILL.md");
170
+ }
171
+
172
+ //#endregion
173
+ //#region src/commands/scaffold.ts
174
+ function runScaffoldCommand(metaDir) {
175
+ function metaSkillPath(name) {
176
+ return join(metaDir, name, "SKILL.md");
177
+ }
178
+ const prompt = `You are helping a library maintainer scaffold Intent skills.
179
+
180
+ Run the three meta skills below **one at a time, in order**. For each step:
181
+ 1. Load the SKILL.md file specified
182
+ 2. Follow its instructions completely
183
+ 3. Present outputs to the maintainer for review
184
+ 4. Do NOT proceed to the next step until the maintainer confirms
185
+
186
+ ## Before you start
187
+
188
+ Gather this context yourself (do not ask the maintainer — agents should never
189
+ ask for information they can discover):
190
+ 1. Read package.json for library name, repository URL, and homepage/docs URL
191
+ 2. Detect if this is a monorepo (look for workspaces field, packages/ directory, lerna.json)
192
+ 3. Use skills/ as the default skills root
193
+ 4. For monorepos:
194
+ - Domain map artifacts go at the REPO ROOT: _artifacts/
195
+ - Skills go INSIDE EACH PACKAGE: packages/<pkg>/skills/
196
+ - Identify which packages are client-facing (usually client SDKs and primary framework adapters)
197
+
198
+ ---
199
+
200
+ ## Step 1 — Domain Discovery
201
+
202
+ Load and follow: ${metaSkillPath("domain-discovery")}
203
+
204
+ This produces: domain_map.yaml and skill_spec.md in the artifacts directory.
205
+ Domain discovery covers the WHOLE library (one domain map even for monorepos).
206
+
207
+ **STOP. Review outputs with the maintainer before continuing.**
208
+
209
+ ---
210
+
211
+ ## Step 2 — Tree Generator
212
+
213
+ Load and follow: ${metaSkillPath("tree-generator")}
214
+
215
+ This produces: skill_tree.yaml in the artifacts directory.
216
+ For monorepos, each skill entry should include a \`package\` field.
217
+
218
+ **STOP. Review outputs with the maintainer before continuing.**
219
+
220
+ ---
221
+
222
+ ## Step 3 — Generate Skills
223
+
224
+ Load and follow: ${metaSkillPath("generate-skill")}
225
+
226
+ This produces: individual SKILL.md files.
227
+ - Single-repo: skills/<domain>/<skill>/SKILL.md
228
+ - Monorepo: packages/<pkg>/skills/<domain>/<skill>/SKILL.md
229
+
230
+ ---
231
+
232
+ ## After all skills are generated
233
+
234
+ 1. Run \`intent validate\` in each package directory
235
+ 2. Commit skills/ and artifacts
236
+ 3. For each publishable package, run: \`npx @tanstack/intent edit-package-json\`
237
+ 4. Ensure each package has \`@tanstack/intent\` as a devDependency
238
+ 5. Create a \`skill:<skill-name>\` label on the GitHub repo for each skill (use \`gh label create\`)
239
+ 6. Add a README note: "If you use an AI agent, run \`npx @tanstack/intent@latest install\`"
240
+ `;
241
+ console.log(prompt);
242
+ }
243
+
244
+ //#endregion
245
+ //#region src/commands/setup-github-actions.ts
246
+ async function runSetupGithubActionsCommand(root, metaDir) {
247
+ const { runSetupGithubActions } = await import("./setup.mjs");
248
+ runSetupGithubActions(root, metaDir);
249
+ }
250
+
251
+ //#endregion
252
+ //#region src/commands/stale.ts
253
+ async function runStaleCommand(targetDir, options, resolveStaleTargets$1) {
254
+ const { reports } = await resolveStaleTargets$1(targetDir);
255
+ if (reports.length === 0) {
256
+ console.log("No intent-enabled packages found.");
257
+ return;
258
+ }
259
+ if (options.json) {
260
+ console.log(JSON.stringify(reports, null, 2));
261
+ return;
262
+ }
263
+ for (const report of reports) {
264
+ const driftLabel = report.versionDrift ? ` [${report.versionDrift} drift]` : "";
265
+ const vLabel = report.skillVersion && report.currentVersion ? ` (${report.skillVersion} → ${report.currentVersion})` : "";
266
+ console.log(`${report.library}${vLabel}${driftLabel}`);
267
+ const stale = report.skills.filter((skill) => skill.needsReview);
268
+ if (stale.length === 0) console.log(" All skills up-to-date");
269
+ else for (const skill of stale) console.log(` ⚠ ${skill.name}: ${skill.reasons.join(", ")}`);
270
+ console.log();
271
+ }
272
+ }
273
+
274
+ //#endregion
275
+ //#region src/commands/validate.ts
276
+ function buildValidationFailure(errors, warnings) {
277
+ const lines = [
278
+ "",
279
+ `❌ Validation failed with ${errors.length} error(s):`,
280
+ ""
281
+ ];
282
+ for (const { file, message } of errors) lines.push(` ${file}: ${message}`);
283
+ if (warnings.length > 0) {
284
+ lines.push("", "⚠ Packaging warnings:");
285
+ for (const warning of warnings) lines.push(` ${warning}`);
286
+ }
287
+ return lines.join("\n");
288
+ }
289
+ function isInsideMonorepo(root) {
290
+ let dir = join(root, "..");
291
+ for (let i = 0; i < 5; i++) {
292
+ const parentPkg = join(dir, "package.json");
293
+ if (existsSync(parentPkg)) try {
294
+ const parent = JSON.parse(readFileSync(parentPkg, "utf8"));
295
+ return Array.isArray(parent.workspaces) || parent.workspaces?.packages;
296
+ } catch {
297
+ return false;
298
+ }
299
+ const next = dirname(dir);
300
+ if (next === dir) break;
301
+ dir = next;
302
+ }
303
+ return false;
146
304
  }
147
305
  function collectPackagingWarnings(root) {
148
306
  const pkgJsonPath = join(root, "package.json");
@@ -160,22 +318,7 @@ function collectPackagingWarnings(root) {
160
318
  const files = pkgJson.files;
161
319
  if (Array.isArray(files)) {
162
320
  if (!files.includes("skills")) warnings.push("\"skills\" is not in the \"files\" array — skills won't be published");
163
- if (!(() => {
164
- let dir = join(root, "..");
165
- for (let i = 0; i < 5; i++) {
166
- const parentPkg = join(dir, "package.json");
167
- if (existsSync(parentPkg)) try {
168
- const parent = JSON.parse(readFileSync(parentPkg, "utf8"));
169
- return Array.isArray(parent.workspaces) || parent.workspaces?.packages;
170
- } catch {
171
- return false;
172
- }
173
- const next = dirname(dir);
174
- if (next === dir) break;
175
- dir = next;
176
- }
177
- return false;
178
- })() && !files.includes("!skills/_artifacts")) warnings.push("\"!skills/_artifacts\" is not in the \"files\" array — artifacts will be published unnecessarily");
321
+ if (!isInsideMonorepo(root) && !files.includes("!skills/_artifacts")) warnings.push("\"!skills/_artifacts\" is not in the \"files\" array — artifacts will be published unnecessarily");
179
322
  }
180
323
  return warnings;
181
324
  }
@@ -188,30 +331,9 @@ function resolvePackageRoot(startDir) {
188
331
  dir = next;
189
332
  }
190
333
  }
191
- function readPackageName(root) {
192
- try {
193
- const pkgJson = JSON.parse(readFileSync(join(root, "package.json"), "utf8"));
194
- return typeof pkgJson.name === "string" ? pkgJson.name : relative(process.cwd(), root) || "unknown";
195
- } catch {
196
- return relative(process.cwd(), root) || "unknown";
197
- }
198
- }
199
- async function resolveStaleTargets(targetDir) {
200
- const resolvedRoot = targetDir ? join(process.cwd(), targetDir) : process.cwd();
201
- const { checkStaleness } = await import("./staleness-Dr5-5wj5.mjs");
202
- if (existsSync(join(resolvedRoot, "skills"))) return { reports: [await checkStaleness(resolvedRoot, readPackageName(resolvedRoot))] };
203
- const { findPackagesWithSkills, findWorkspaceRoot } = await import("./setup.mjs");
204
- const workspaceRoot = findWorkspaceRoot(resolvedRoot);
205
- if (workspaceRoot) {
206
- const packageDirs = findPackagesWithSkills(workspaceRoot);
207
- if (packageDirs.length > 0) return { reports: await Promise.all(packageDirs.map((packageDir) => checkStaleness(packageDir, readPackageName(packageDir)))) };
208
- }
209
- const staleResult = await scanIntentsOrFail();
210
- return { reports: await Promise.all(staleResult.packages.map((pkg) => checkStaleness(pkg.packageRoot, pkg.name))) };
211
- }
212
- async function cmdValidate(args) {
334
+ async function runValidateCommand(dir) {
213
335
  const [{ parse: parseYaml }, { findSkillFiles }] = await Promise.all([import("yaml"), import("./utils-D7OKi0Rn.mjs")]);
214
- const targetDir = args[0] ?? "skills";
336
+ const targetDir = dir ?? "skills";
215
337
  const skillsDir = join(process.cwd(), targetDir);
216
338
  const packageRoot = resolvePackageRoot(skillsDir);
217
339
  if (!existsSync(skillsDir)) fail(`Skills directory not found: ${skillsDir}`);
@@ -239,10 +361,11 @@ async function cmdValidate(args) {
239
361
  let fm;
240
362
  try {
241
363
  fm = parseYaml(match[1]);
242
- } catch {
364
+ } catch (err) {
365
+ const detail = err instanceof Error ? err.message : String(err);
243
366
  errors.push({
244
367
  file: rel,
245
- message: "Invalid YAML frontmatter"
368
+ message: `Invalid YAML frontmatter: ${detail}`
246
369
  });
247
370
  continue;
248
371
  }
@@ -299,10 +422,11 @@ async function cmdValidate(args) {
299
422
  }
300
423
  if (fileName.endsWith(".yaml")) try {
301
424
  parseYaml(content);
302
- } catch {
425
+ } catch (err) {
426
+ const detail = err instanceof Error ? err.message : String(err);
303
427
  errors.push({
304
428
  file: relative(process.cwd(), artifactPath),
305
- message: "Invalid YAML in artifact file"
429
+ message: `Invalid YAML in artifact file: ${detail}`
306
430
  });
307
431
  }
308
432
  }
@@ -312,211 +436,76 @@ async function cmdValidate(args) {
312
436
  if (warnings.length > 0) console.log();
313
437
  printWarnings(warnings);
314
438
  }
315
- function cmdScaffold() {
316
- const metaDir = getMetaDir();
317
- const metaSkillPath = (name) => join(metaDir, name, "SKILL.md");
318
- const prompt = `You are helping a library maintainer scaffold Intent skills.
319
439
 
320
- Run the three meta skills below **one at a time, in order**. For each step:
321
- 1. Load the SKILL.md file specified
322
- 2. Follow its instructions completely
323
- 3. Present outputs to the maintainer for review
324
- 4. Do NOT proceed to the next step until the maintainer confirms
325
-
326
- ## Before you start
327
-
328
- Gather this context yourself (do not ask the maintainer agents should never
329
- ask for information they can discover):
330
- 1. Read package.json for library name, repository URL, and homepage/docs URL
331
- 2. Detect if this is a monorepo (look for workspaces field, packages/ directory, lerna.json)
332
- 3. Use skills/ as the default skills root
333
- 4. For monorepos:
334
- - Domain map artifacts go at the REPO ROOT: _artifacts/
335
- - Skills go INSIDE EACH PACKAGE: packages/<pkg>/skills/
336
- - Identify which packages are client-facing (usually client SDKs and primary framework adapters)
337
-
338
- ---
339
-
340
- ## Step 1 Domain Discovery
341
-
342
- Load and follow: ${metaSkillPath("domain-discovery")}
343
-
344
- This produces: domain_map.yaml and skill_spec.md in the artifacts directory.
345
- Domain discovery covers the WHOLE library (one domain map even for monorepos).
346
-
347
- **STOP. Review outputs with the maintainer before continuing.**
348
-
349
- ---
350
-
351
- ## Step 2 — Tree Generator
352
-
353
- Load and follow: ${metaSkillPath("tree-generator")}
354
-
355
- This produces: skill_tree.yaml in the artifacts directory.
356
- For monorepos, each skill entry should include a \`package\` field.
357
-
358
- **STOP. Review outputs with the maintainer before continuing.**
359
-
360
- ---
361
-
362
- ## Step 3 — Generate Skills
363
-
364
- Load and follow: ${metaSkillPath("generate-skill")}
365
-
366
- This produces: individual SKILL.md files.
367
- - Single-repo: skills/<domain>/<skill>/SKILL.md
368
- - Monorepo: packages/<pkg>/skills/<domain>/<skill>/SKILL.md
369
-
370
- ---
371
-
372
- ## After all skills are generated
373
-
374
- 1. Run \`intent validate\` in each package directory
375
- 2. Commit skills/ and artifacts
376
- 3. For each publishable package, run: \`npx @tanstack/intent edit-package-json\`
377
- 4. Ensure each package has \`@tanstack/intent\` as a devDependency
378
- 5. Create a \`skill:<skill-name>\` label on the GitHub repo for each skill (use \`gh label create\`)
379
- 6. Add a README note: "If you use an AI agent, run \`npx @tanstack/intent@latest install\`"
380
- `;
381
- console.log(prompt);
382
- }
383
- const USAGE = `TanStack Intent CLI
384
-
385
- Usage:
386
- intent list [--json] Discover intent-enabled packages
387
- intent meta [name] List meta-skills, or print one by name
388
- intent validate [<dir>] Validate skill files (default: skills/)
389
- intent install Print a skill that guides your coding agent to set up skill-to-task mappings
390
- intent scaffold Print maintainer scaffold prompt
391
- intent edit-package-json Wire package.json (files, keywords) for skill publishing
392
- intent setup-github-actions Copy CI workflow templates to .github/workflows/
393
- intent stale [dir] [--json] Check skills for staleness`;
394
- const HELP_BY_COMMAND = {
395
- list: `${USAGE}
396
-
397
- Examples:
398
- intent list
399
- intent list --json`,
400
- meta: `intent meta [name]
401
-
402
- List shipped meta-skills, or print a single meta-skill by name.
403
-
404
- Examples:
405
- intent meta
406
- intent meta domain-discovery`,
407
- validate: `intent validate [dir]
408
-
409
- Validate SKILL.md files in the target directory.
410
-
411
- Examples:
412
- intent validate
413
- intent validate packages/query/skills`,
414
- install: `intent install
415
-
416
- Print the install prompt used to set up skill-to-task mappings.`,
417
- scaffold: `intent scaffold
418
-
419
- Print the guided maintainer prompt for generating skills.`,
420
- stale: `intent stale [dir] [--json]
421
-
422
- Check installed skills for version and source drift.
423
-
424
- Examples:
425
- intent stale
426
- intent stale packages/query
427
- intent stale --json`,
428
- "edit-package-json": `intent edit-package-json
429
-
430
- Update package.json files so skills are published.`,
431
- "setup-github-actions": `intent setup-github-actions
432
-
433
- Copy Intent CI workflow templates into .github/workflows/.`
434
- };
435
- function isHelpFlag(arg) {
436
- return arg === "-h" || arg === "--help";
437
- }
438
- function printHelp(command) {
439
- if (!command) {
440
- console.log(`${USAGE}
441
-
442
- Run \`intent help <command>\` for details on a specific command.`);
443
- return;
444
- }
445
- console.log(HELP_BY_COMMAND[command] ?? USAGE);
440
+ //#endregion
441
+ //#region src/cli.ts
442
+ function createCli() {
443
+ const cli = cac("intent");
444
+ cli.usage("<command> [options]");
445
+ cli.command("list", "Discover intent-enabled packages").usage("list [--json]").option("--json", "Output JSON").example("list").example("list --json").action(async (options) => {
446
+ await runListCommand(options, scanIntentsOrFail);
447
+ });
448
+ cli.command("meta [name]", "List meta-skills, or print one by name").usage("meta [name]").example("meta").example("meta domain-discovery").action(async (name) => {
449
+ await runMetaCommand(name, getMetaDir());
450
+ });
451
+ cli.command("validate [dir]", "Validate skill files").usage("validate [dir]").example("validate").example("validate packages/query/skills").action(async (dir) => {
452
+ await runValidateCommand(dir);
453
+ });
454
+ cli.command("install", "Print a skill that guides your coding agent to set up skill-to-task mappings").usage("install").action(() => {
455
+ runInstallCommand();
456
+ });
457
+ cli.command("scaffold", "Print maintainer scaffold prompt").usage("scaffold").action(() => {
458
+ runScaffoldCommand(getMetaDir());
459
+ });
460
+ cli.command("stale [dir]", "Check skills for staleness").usage("stale [dir] [--json]").option("--json", "Output JSON").example("stale").example("stale packages/query").example("stale --json").action(async (targetDir, options) => {
461
+ await runStaleCommand(targetDir, options, resolveStaleTargets);
462
+ });
463
+ cli.command("edit-package-json", "Update package.json files so skills are published").usage("edit-package-json").action(async () => {
464
+ await runEditPackageJsonCommand(process.cwd());
465
+ });
466
+ cli.command("setup-github-actions", "Copy Intent CI workflow templates into .github/workflows/").usage("setup-github-actions").action(async () => {
467
+ await runSetupGithubActionsCommand(process.cwd(), getMetaDir());
468
+ });
469
+ cli.command("help [command]", "Display help for a command").action((commandName) => {
470
+ if (!commandName) {
471
+ cli.outputHelp();
472
+ return;
473
+ }
474
+ const command = cli.commands.find((candidate) => candidate.isMatched(commandName));
475
+ if (!command) fail(`Unknown command: ${commandName}`);
476
+ command.outputHelp();
477
+ });
478
+ cli.help();
479
+ return cli;
446
480
  }
447
481
  async function main(argv = process.argv.slice(2)) {
448
- const command = argv[0];
449
- const commandArgs = argv.slice(1);
450
482
  try {
451
- if (!command || isHelpFlag(command)) {
452
- printHelp();
453
- return 0;
454
- }
455
- if (command === "help") {
456
- printHelp(commandArgs[0]);
483
+ const cli = createCli();
484
+ if (argv.length === 0) {
485
+ cli.outputHelp();
457
486
  return 0;
458
487
  }
459
- if (isHelpFlag(commandArgs[0])) {
460
- printHelp(command);
461
- return 0;
462
- }
463
- switch (command) {
464
- case "list":
465
- await cmdList(commandArgs);
466
- return 0;
467
- case "meta":
468
- await cmdMeta(commandArgs);
469
- return 0;
470
- case "validate":
471
- await cmdValidate(commandArgs);
472
- return 0;
473
- case "install":
474
- console.log(INSTALL_PROMPT);
475
- return 0;
476
- case "scaffold":
477
- cmdScaffold();
478
- return 0;
479
- case "stale": {
480
- const jsonStale = commandArgs.includes("--json");
481
- const { reports } = await resolveStaleTargets(commandArgs.find((arg) => !arg.startsWith("-")));
482
- if (reports.length === 0) {
483
- console.log("No intent-enabled packages found.");
484
- return 0;
485
- }
486
- if (jsonStale) {
487
- console.log(JSON.stringify(reports, null, 2));
488
- return 0;
489
- }
490
- for (const report of reports) {
491
- const driftLabel = report.versionDrift ? ` [${report.versionDrift} drift]` : "";
492
- const vLabel = report.skillVersion && report.currentVersion ? ` (${report.skillVersion} → ${report.currentVersion})` : "";
493
- console.log(`${report.library}${vLabel}${driftLabel}`);
494
- const stale = report.skills.filter((s) => s.needsReview);
495
- if (stale.length === 0) console.log(" All skills up-to-date");
496
- else for (const skill of stale) console.log(` ⚠ ${skill.name}: ${skill.reasons.join(", ")}`);
497
- console.log();
498
- }
499
- return 0;
500
- }
501
- case "edit-package-json": {
502
- const { runEditPackageJsonAll } = await import("./setup.mjs");
503
- runEditPackageJsonAll(process.cwd());
504
- return 0;
505
- }
506
- case "setup-github-actions": {
507
- const { runSetupGithubActions } = await import("./setup.mjs");
508
- runSetupGithubActions(process.cwd(), getMetaDir());
509
- return 0;
510
- }
511
- default:
512
- printHelp();
513
- return command ? 1 : 0;
488
+ cli.parse([
489
+ "intent",
490
+ "intent",
491
+ ...argv
492
+ ], { run: false });
493
+ if (cli.options.help) return 0;
494
+ if (!cli.matchedCommand) {
495
+ cli.outputHelp();
496
+ return 1;
514
497
  }
498
+ await cli.runMatchedCommand();
499
+ return 0;
515
500
  } catch (err) {
516
501
  if (isCliFailure(err)) {
517
502
  console.error(err.message);
518
503
  return err.exitCode;
519
504
  }
505
+ if (err instanceof Error) {
506
+ console.error(err.message);
507
+ return 1;
508
+ }
520
509
  throw err;
521
510
  }
522
511
  }
@@ -530,4 +519,4 @@ if (isMain) {
530
519
  }
531
520
 
532
521
  //#endregion
533
- export { USAGE, main };
522
+ export { main };
package/dist/index.d.mts CHANGED
@@ -2,7 +2,7 @@ import { a as IntentProjectConfig, c as ScanResult, d as StalenessReport, i as I
2
2
  import { c as runEditPackageJson, r as SetupGithubActionsResult, t as EditPackageJsonResult, u as runSetupGithubActions } from "./setup-BNBVotfR.mjs";
3
3
 
4
4
  //#region src/scanner.d.ts
5
- declare function scanForIntents(root?: string): Promise<ScanResult>;
5
+ declare function scanForIntents(root?: string): ScanResult;
6
6
  //#endregion
7
7
  //#region src/staleness.d.ts
8
8
  declare function checkStaleness(packageDir: string, packageName?: string): Promise<StalenessReport>;
package/dist/index.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
  import { a as parseFrontmatter, n as findSkillFiles, o as resolveDepDir, r as getDeps } from "./utils-BfjM1mQe.mjs";
2
- import { a as runEditPackageJson, s as runSetupGithubActions } from "./setup-J7F7DIP2.mjs";
3
- import { t as scanForIntents } from "./scanner-SHzOJKcz.mjs";
2
+ import { a as runEditPackageJson, s as runSetupGithubActions } from "./setup-Nyfl1KTS.mjs";
3
+ import { t as scanForIntents } from "./scanner-BCgNt-oI.mjs";
4
4
  import { t as checkStaleness } from "./staleness-DZKvsLVq.mjs";
5
5
  import { readFileSync, writeFileSync } from "node:fs";
6
6
  import { join } from "node:path";
@@ -1,4 +1,4 @@
1
- //#region src/install-prompt.ts
1
+ //#region src/commands/install.ts
2
2
  const INSTALL_PROMPT = `You are an AI assistant helping a developer set up skill-to-task mappings for their project.
3
3
 
4
4
  Follow these steps in order:
@@ -54,6 +54,9 @@ skills:
54
54
  - Keep entries concise - this block is read on every agent task
55
55
  - Preserve all content outside the block tags unchanged
56
56
  - If the user is on Deno, note that this setup is best-effort today and relies on npm interop`;
57
+ function runInstallCommand() {
58
+ console.log(INSTALL_PROMPT);
59
+ }
57
60
 
58
61
  //#endregion
59
- export { INSTALL_PROMPT as t };
62
+ export { runInstallCommand as n, INSTALL_PROMPT as t };
@@ -1,14 +1,14 @@
1
1
  #!/usr/bin/env node
2
2
  import "./utils-BfjM1mQe.mjs";
3
- import { t as INSTALL_PROMPT } from "./install-prompt-C0M-U3WZ.mjs";
3
+ import { t as INSTALL_PROMPT } from "./install-BHbA_wkb.mjs";
4
4
  import { n as printSkillTree, r as printTable, t as computeSkillNameWidth } from "./display-DhsUxNJW.mjs";
5
- import { t as scanLibrary } from "./library-scanner-CU3ZHPYU.mjs";
5
+ import { t as scanLibrary } from "./library-scanner-DlWy8VeZ.mjs";
6
6
 
7
7
  //#region src/intent-library.ts
8
- async function cmdList() {
8
+ function cmdList() {
9
9
  let result;
10
10
  try {
11
- result = await scanLibrary(process.argv[1]);
11
+ result = scanLibrary(process.argv[1]);
12
12
  } catch (err) {
13
13
  console.error(err.message);
14
14
  process.exit(1);
@@ -65,7 +65,7 @@ const command = process.argv[2];
65
65
  switch (command) {
66
66
  case "list":
67
67
  case void 0:
68
- await cmdList();
68
+ cmdList();
69
69
  break;
70
70
  case "install":
71
71
  cmdInstall();
@@ -59,7 +59,7 @@ function discoverSkills(skillsDir) {
59
59
  walk(skillsDir);
60
60
  return skills;
61
61
  }
62
- async function scanLibrary(scriptPath, _projectRoot) {
62
+ function scanLibrary(scriptPath, _projectRoot) {
63
63
  const packages = [];
64
64
  const warnings = [];
65
65
  const visited = /* @__PURE__ */ new Set();
@@ -11,6 +11,6 @@ interface LibraryScanResult {
11
11
  packages: Array<LibraryPackage>;
12
12
  warnings: Array<string>;
13
13
  }
14
- declare function scanLibrary(scriptPath: string, _projectRoot?: string): Promise<LibraryScanResult>;
14
+ declare function scanLibrary(scriptPath: string, _projectRoot?: string): LibraryScanResult;
15
15
  //#endregion
16
16
  export { LibraryPackage, LibraryScanResult, scanLibrary };
@@ -1,4 +1,4 @@
1
1
  import "./utils-BfjM1mQe.mjs";
2
- import { t as scanLibrary } from "./library-scanner-CU3ZHPYU.mjs";
2
+ import { t as scanLibrary } from "./library-scanner-DlWy8VeZ.mjs";
3
3
 
4
4
  export { scanLibrary };
@@ -1,5 +1,5 @@
1
1
  import { a as parseFrontmatter, i as listNodeModulesPackageDirs, o as resolveDepDir, r as getDeps, t as detectGlobalNodeModules } from "./utils-BfjM1mQe.mjs";
2
- import { i as resolveWorkspacePackages, n as findWorkspaceRoot, r as readWorkspacePatterns } from "./setup-J7F7DIP2.mjs";
2
+ import { i as resolveWorkspacePackages, n as findWorkspaceRoot, r as readWorkspacePatterns } from "./setup-Nyfl1KTS.mjs";
3
3
  import { existsSync, readFileSync, readdirSync } from "node:fs";
4
4
  import { join, relative, sep } from "node:path";
5
5
 
@@ -169,7 +169,7 @@ function toVersionConflict(packageName, variants, chosen) {
169
169
  variants
170
170
  };
171
171
  }
172
- async function scanForIntents(root) {
172
+ function scanForIntents(root) {
173
173
  const projectRoot = root ?? process.cwd();
174
174
  const packageManager = detectPackageManager(projectRoot);
175
175
  const nodeModulesDir = join(projectRoot, "node_modules");
@@ -0,0 +1,5 @@
1
+ import "./utils-BfjM1mQe.mjs";
2
+ import "./setup-Nyfl1KTS.mjs";
3
+ import { t as scanForIntents } from "./scanner-BCgNt-oI.mjs";
4
+
5
+ export { scanForIntents };
@@ -4,6 +4,19 @@ import { basename, join, relative } from "node:path";
4
4
  import { parse } from "yaml";
5
5
 
6
6
  //#region src/setup.ts
7
+ function isGenericWorkspaceName(name, root) {
8
+ const normalized = name.trim().toLowerCase();
9
+ return normalized.length === 0 || normalized === "unknown" || normalized === "root" || normalized === "workspace" || normalized === "monorepo" || normalized === basename(root).toLowerCase();
10
+ }
11
+ function deriveWorkspacePackageName(root, repo, packageDirs) {
12
+ const repoName = repo.split("/").filter(Boolean).pop() || basename(root);
13
+ for (const packageDir of packageDirs) {
14
+ const pkgJson = readPackageJson(packageDir);
15
+ const pkgName = typeof pkgJson.name === "string" ? pkgJson.name : null;
16
+ if (pkgName?.startsWith("@")) return `${pkgName.split("/")[0]}/${repoName}`;
17
+ }
18
+ return repoName;
19
+ }
7
20
  function readPackageJson(root) {
8
21
  const pkgPath = join(root, "package.json");
9
22
  try {
@@ -41,19 +54,22 @@ function buildWatchPaths(root, packageDirs) {
41
54
  }
42
55
  function detectVars(root, packageDirs) {
43
56
  const pkgJson = readPackageJson(root);
44
- const name = typeof pkgJson.name === "string" ? pkgJson.name : "unknown";
57
+ const rawName = typeof pkgJson.name === "string" ? pkgJson.name : "unknown";
45
58
  const docs = typeof pkgJson.intent?.docs === "string" ? pkgJson.intent.docs : "docs/";
46
- const repo = detectRepo(pkgJson, name.replace(/^@/, "").replace(/\//, "/"));
47
59
  const isMonorepo = packageDirs !== void 0;
48
- const packageLabel = isMonorepo && name === "unknown" ? `${basename(root)} workspace` : name;
49
- let srcPath = `packages/${name.replace(/^@[^/]+\//, "")}/src/**`;
50
- if (existsSync(join(root, "src"))) srcPath = "src/**";
60
+ const repo = detectRepo(pkgJson, detectRepo((packageDirs?.[0] ? readPackageJson(packageDirs[0]) : null) ?? {}, basename(root)));
61
+ let packageName = rawName;
62
+ if (isMonorepo && isGenericWorkspaceName(rawName, root)) packageName = deriveWorkspacePackageName(root, repo, packageDirs);
63
+ const shortName = packageName.replace(/^@[^/]+\//, "");
64
+ let srcPath = isMonorepo ? "packages/*/src/**" : `packages/${shortName}/src/**`;
65
+ if (!isMonorepo && existsSync(join(root, "src"))) srcPath = "src/**";
66
+ const docsPath = isMonorepo ? "packages/*/docs/**" : docs;
51
67
  return {
52
- PACKAGE_NAME: name,
53
- PACKAGE_LABEL: packageLabel,
54
- PAYLOAD_PACKAGE: packageLabel,
68
+ PACKAGE_NAME: packageName,
69
+ PACKAGE_LABEL: packageName,
70
+ PAYLOAD_PACKAGE: packageName,
55
71
  REPO: repo,
56
- DOCS_PATH: docs.endsWith("**") ? docs : docs.replace(/\/$/, "") + "/**",
72
+ DOCS_PATH: docsPath.endsWith("**") ? docsPath : docsPath.replace(/\/$/, "") + "/**",
57
73
  SRC_PATH: srcPath,
58
74
  WATCH_PATHS: isMonorepo ? buildWatchPaths(root, packageDirs) : ` - '${docs.endsWith("**") ? docs : docs.replace(/\/$/, "") + "/**"}'\n - '${srcPath}'`
59
75
  };
package/dist/setup.mjs CHANGED
@@ -1,4 +1,4 @@
1
1
  import "./utils-BfjM1mQe.mjs";
2
- import { a as runEditPackageJson, i as resolveWorkspacePackages, n as findWorkspaceRoot, o as runEditPackageJsonAll, r as readWorkspacePatterns, s as runSetupGithubActions, t as findPackagesWithSkills } from "./setup-J7F7DIP2.mjs";
2
+ import { a as runEditPackageJson, i as resolveWorkspacePackages, n as findWorkspaceRoot, o as runEditPackageJsonAll, r as readWorkspacePatterns, s as runSetupGithubActions, t as findPackagesWithSkills } from "./setup-Nyfl1KTS.mjs";
3
3
 
4
4
  export { findPackagesWithSkills, findWorkspaceRoot, readWorkspacePatterns, resolveWorkspacePackages, runEditPackageJson, runEditPackageJsonAll, runSetupGithubActions };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tanstack/intent",
3
- "version": "0.0.21",
3
+ "version": "0.0.23",
4
4
  "description": "Ship compositional knowledge for AI coding agents alongside your npm packages",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -27,6 +27,7 @@
27
27
  "meta"
28
28
  ],
29
29
  "dependencies": {
30
+ "cac": "^6.7.14",
30
31
  "yaml": "^2.7.0"
31
32
  },
32
33
  "devDependencies": {
@@ -37,6 +38,7 @@
37
38
  "scripts": {
38
39
  "prepack": "npm run build",
39
40
  "build": "tsdown src/index.ts src/cli.ts src/setup.ts src/intent-library.ts src/library-scanner.ts --format esm --dts",
41
+ "test:smoke": "pnpm run build && node dist/cli.mjs --help > /dev/null",
40
42
  "test:lib": "vitest run --exclude 'tests/integration/**'",
41
43
  "test:integration": "vitest run tests/integration/",
42
44
  "test:types": "tsc --noEmit"
@@ -1,5 +0,0 @@
1
- import "./utils-BfjM1mQe.mjs";
2
- import "./setup-J7F7DIP2.mjs";
3
- import { t as scanForIntents } from "./scanner-SHzOJKcz.mjs";
4
-
5
- export { scanForIntents };