@tanstack/intent 0.0.29 → 0.0.32

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.
Files changed (33) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +18 -5
  3. package/dist/cli.d.mts +0 -0
  4. package/dist/cli.mjs +62 -73
  5. package/dist/display-DCRCp4-C.mjs +5 -0
  6. package/dist/{display-hdsqb4w-.mjs → display-DUgtRJkt.mjs} +10 -1
  7. package/dist/index.d.mts +57 -4
  8. package/dist/index.mjs +9 -6
  9. package/dist/install-BmVqcbEi.mjs +506 -0
  10. package/dist/intent-library.mjs +9 -5
  11. package/dist/{library-scanner-B51qV5aX.mjs → library-scanner-DFFreLjW.mjs} +10 -3
  12. package/dist/library-scanner.d.mts +2 -2
  13. package/dist/library-scanner.mjs +2 -1
  14. package/dist/{project-context-D6A5sBBO.mjs → project-context-CKG-q4rD.mjs} +1 -1
  15. package/dist/resolver-aFigTqXi.mjs +70 -0
  16. package/dist/scanner-CRZITpcY.mjs +6 -0
  17. package/dist/{scanner-B-bbXBLY.mjs → scanner-DKL8v8is.mjs} +176 -117
  18. package/dist/{setup-B4ZwN5Hg.mjs → setup-JJvjiGkM.mjs} +2 -2
  19. package/dist/setup.d.mts +1 -1
  20. package/dist/setup.mjs +3 -3
  21. package/dist/skill-paths-8k9K9y26.mjs +33 -0
  22. package/dist/skill-use-CXOnncWK.mjs +42 -0
  23. package/dist/staleness-NF-lxfXf.mjs +4 -0
  24. package/dist/{types-BTQ9efv-.d.mts → types-CsySN6Vw.d.mts} +7 -1
  25. package/dist/{workspace-patterns-D_y6rlqX.mjs → workspace-patterns-DbnA0peB.mjs} +1 -1
  26. package/package.json +3 -4
  27. package/dist/display-DdmZXLZm.mjs +0 -3
  28. package/dist/install-BzDmD5yI.mjs +0 -69
  29. package/dist/scanner-CP4U8F_n.mjs +0 -5
  30. package/dist/staleness-LRbiWWZK.mjs +0 -4
  31. /package/dist/{setup-BA9RkENh.d.mts → setup-DDoOLriA.d.mts} +0 -0
  32. /package/dist/{staleness-SY7-mZMH.mjs → staleness-DorwfGrf.mjs} +0 -0
  33. /package/dist/{workspace-patterns-Cndd-7vB.mjs → workspace-patterns-U35B5AO-.mjs} +0 -0
@@ -0,0 +1,506 @@
1
+ import { i as parseSkillUse, n as formatSkillUse } from "./skill-use-CXOnncWK.mjs";
2
+ import { t as resolveProjectContext } from "./project-context-CKG-q4rD.mjs";
3
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
4
+ import { dirname, join, relative, resolve } from "node:path";
5
+ import { parse } from "yaml";
6
+ import { fileURLToPath } from "node:url";
7
+
8
+ //#region src/cli-error.ts
9
+ const CLI_FAILURE = Symbol("CliFailure");
10
+ function fail(message, exitCode = 1) {
11
+ throw {
12
+ [CLI_FAILURE]: true,
13
+ message,
14
+ exitCode
15
+ };
16
+ }
17
+ function isCliFailure(value) {
18
+ return !!value && typeof value === "object" && CLI_FAILURE in value;
19
+ }
20
+
21
+ //#endregion
22
+ //#region src/cli-output.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
+
29
+ //#endregion
30
+ //#region src/cli-support.ts
31
+ function getMetaDir() {
32
+ return join(dirname(fileURLToPath(import.meta.url)), "..", "meta");
33
+ }
34
+ async function scanIntentsOrFail(options) {
35
+ const { scanForIntents } = await import("./scanner-CRZITpcY.mjs");
36
+ try {
37
+ return scanForIntents(void 0, options);
38
+ } catch (err) {
39
+ fail(err instanceof Error ? err.message : String(err));
40
+ }
41
+ }
42
+ function scanOptionsFromGlobalFlags(options) {
43
+ if (options.global && options.globalOnly) fail("Use either --global or --global-only, not both.");
44
+ if (options.globalOnly) return { scope: "global" };
45
+ if (options.global) return { scope: "local-and-global" };
46
+ return { scope: "local" };
47
+ }
48
+ function readPackageName(root) {
49
+ try {
50
+ const pkgJson = JSON.parse(readFileSync(join(root, "package.json"), "utf8"));
51
+ return typeof pkgJson.name === "string" ? pkgJson.name : relative(process.cwd(), root) || "unknown";
52
+ } catch {
53
+ return relative(process.cwd(), root) || "unknown";
54
+ }
55
+ }
56
+ async function resolveStaleTargets(targetDir) {
57
+ const resolvedRoot = targetDir ? resolve(process.cwd(), targetDir) : process.cwd();
58
+ const context = resolveProjectContext({
59
+ cwd: process.cwd(),
60
+ targetPath: targetDir
61
+ });
62
+ const { checkStaleness } = await import("./staleness-NF-lxfXf.mjs");
63
+ if (context.packageRoot && (context.targetSkillsDir !== null || resolvedRoot !== context.workspaceRoot)) return { reports: [await checkStaleness(context.packageRoot, readPackageName(context.packageRoot))] };
64
+ if (existsSync(join(resolvedRoot, "skills"))) return { reports: [await checkStaleness(resolvedRoot, readPackageName(resolvedRoot))] };
65
+ const { findPackagesWithSkills, findWorkspaceRoot } = await import("./workspace-patterns-DbnA0peB.mjs");
66
+ const workspaceRoot = findWorkspaceRoot(resolvedRoot);
67
+ if (workspaceRoot) {
68
+ const packageDirs = findPackagesWithSkills(workspaceRoot);
69
+ if (packageDirs.length > 0) return { reports: await Promise.all(packageDirs.map((packageDir) => checkStaleness(packageDir, readPackageName(packageDir)))) };
70
+ }
71
+ const staleResult = await scanIntentsOrFail();
72
+ return { reports: await Promise.all(staleResult.packages.map((pkg) => checkStaleness(pkg.packageRoot, pkg.name))) };
73
+ }
74
+
75
+ //#endregion
76
+ //#region src/commands/install-writer.ts
77
+ const INTENT_SKILLS_START = "<!-- intent-skills:start -->";
78
+ const INTENT_SKILLS_END = "<!-- intent-skills:end -->";
79
+ const SUPPORTED_AGENT_CONFIG_FILES = [
80
+ "AGENTS.md",
81
+ "CLAUDE.md",
82
+ ".cursorrules",
83
+ ".github/copilot-instructions.md"
84
+ ];
85
+ const NON_ACTIONABLE_SKILL_TYPES = new Set([
86
+ "maintainer",
87
+ "maintainer-only",
88
+ "meta",
89
+ "reference"
90
+ ]);
91
+ function normalizeBlock(content) {
92
+ return content.replace(/\r\n/g, "\n").trimEnd();
93
+ }
94
+ function readManagedBlock(content) {
95
+ const start = content.indexOf(INTENT_SKILLS_START);
96
+ const errors = [];
97
+ if (start === -1) errors.push("Missing intent-skills start marker.");
98
+ const endMarkerStart = start === -1 ? content.indexOf(INTENT_SKILLS_END) : content.indexOf(INTENT_SKILLS_END, start);
99
+ if (endMarkerStart === -1) errors.push("Missing intent-skills end marker.");
100
+ const hasMarker = start !== -1 || endMarkerStart !== -1;
101
+ if (errors.length > 0 || start === -1 || endMarkerStart === -1) return {
102
+ errors,
103
+ hasMarker,
104
+ managedBlock: null
105
+ };
106
+ const end = endMarkerStart + 26;
107
+ return {
108
+ errors,
109
+ hasMarker,
110
+ managedBlock: {
111
+ end,
112
+ start,
113
+ text: content.slice(start, end)
114
+ }
115
+ };
116
+ }
117
+ function parseSkillsList(block) {
118
+ const yamlBody = normalizeBlock(block).split("\n").filter((line) => line !== INTENT_SKILLS_START && line !== INTENT_SKILLS_END).join("\n");
119
+ try {
120
+ const parsed = parse(yamlBody);
121
+ if (!parsed || !Array.isArray(parsed.skills)) return {
122
+ errors: ["Managed block must contain a skills list."],
123
+ skills: []
124
+ };
125
+ return {
126
+ errors: [],
127
+ skills: parsed.skills
128
+ };
129
+ } catch (err) {
130
+ return {
131
+ errors: [`Managed block contains invalid YAML: ${err instanceof Error ? err.message : String(err)}`],
132
+ skills: []
133
+ };
134
+ }
135
+ }
136
+ function verifyIntentSkillsBlockFile({ expectedBlock, expectedMappingCount, targetPath }) {
137
+ const errors = [];
138
+ if (!existsSync(targetPath)) return {
139
+ errors: [`Agent config file was not created: ${targetPath}`],
140
+ ok: false
141
+ };
142
+ const { managedBlock, errors: markerErrors } = readManagedBlock(readFileSync(targetPath, "utf8"));
143
+ errors.push(...markerErrors);
144
+ if (!managedBlock) return {
145
+ errors,
146
+ ok: false
147
+ };
148
+ const block = managedBlock.text;
149
+ if (normalizeBlock(block) !== normalizeBlock(expectedBlock)) errors.push("Managed block does not match generated mappings.");
150
+ if (expectedMappingCount === void 0) return {
151
+ errors,
152
+ ok: errors.length === 0
153
+ };
154
+ const { skills, errors: parseErrors } = parseSkillsList(block);
155
+ errors.push(...parseErrors);
156
+ if (skills.length !== expectedMappingCount) errors.push(`Expected ${expectedMappingCount} skill mappings, found ${skills.length}.`);
157
+ for (const skill of skills) {
158
+ if (!skill || typeof skill !== "object") {
159
+ errors.push("Each skill mapping must be an object.");
160
+ continue;
161
+ }
162
+ const mapping = skill;
163
+ if (mapping.load !== void 0) errors.push("Skill mappings must use compact `use` entries, not `load`.");
164
+ if (typeof mapping.when !== "string" || mapping.when.trim() === "") errors.push("Each skill mapping must include a non-empty `when` field.");
165
+ if (typeof mapping.use !== "string") errors.push("Each skill mapping must include a `use` field.");
166
+ else try {
167
+ parseSkillUse(mapping.use);
168
+ } catch (err) {
169
+ errors.push(err instanceof Error ? err.message : String(err));
170
+ }
171
+ }
172
+ return {
173
+ errors,
174
+ ok: errors.length === 0
175
+ };
176
+ }
177
+ function resolveIntentSkillsBlockTargetPath(root, mappingCount) {
178
+ if (mappingCount === 0) return null;
179
+ return findExistingConfigWithManagedBlock(root)?.filePath ?? join(root, "AGENTS.md");
180
+ }
181
+ function compareNames(a, b) {
182
+ return a.name.localeCompare(b.name);
183
+ }
184
+ function quoteYamlString(value) {
185
+ return `"${value.replace(/\\/g, "\\\\").replace(/"/g, "\\\"").replace(/\n/g, "\\n").replace(/\r/g, "\\r").replace(/\t/g, "\\t")}"`;
186
+ }
187
+ function isActionableSkill(skill) {
188
+ const type = skill.type?.trim().toLowerCase();
189
+ return !type || !NON_ACTIONABLE_SKILL_TYPES.has(type);
190
+ }
191
+ function formatWhen(packageName, skill) {
192
+ return skill.description.replace(/\s+/g, " ").trim() || `Use ${packageName} ${skill.name}`;
193
+ }
194
+ function buildIntentSkillsBlock(scanResult) {
195
+ const lines = [
196
+ INTENT_SKILLS_START,
197
+ "# Skill mappings - load `use` with `npx @tanstack/intent@latest load <use>`.",
198
+ "skills:"
199
+ ];
200
+ let mappingCount = 0;
201
+ for (const pkg of [...scanResult.packages].sort(compareNames)) for (const skill of [...pkg.skills].sort(compareNames)) {
202
+ if (!isActionableSkill(skill)) continue;
203
+ mappingCount++;
204
+ lines.push(` - when: ${quoteYamlString(formatWhen(pkg.name, skill))}`);
205
+ lines.push(` use: ${quoteYamlString(formatSkillUse(pkg.name, skill.name))}`);
206
+ }
207
+ if (mappingCount === 0) lines[2] = "skills: []";
208
+ lines.push(INTENT_SKILLS_END);
209
+ return {
210
+ block: `${lines.join("\n")}\n`,
211
+ mappingCount
212
+ };
213
+ }
214
+ function buildIntentSkillGuidanceBlock() {
215
+ return {
216
+ block: `${[
217
+ INTENT_SKILLS_START,
218
+ "## Skill Loading",
219
+ "",
220
+ "Before substantial work:",
221
+ "- Skill check: run `npx @tanstack/intent@latest list`, or use skills already listed in context.",
222
+ "- Skill guidance: if one local skill clearly matches the task, run `npx @tanstack/intent@latest load <package>#<skill>` and follow the returned `SKILL.md`.",
223
+ "- Monorepos: when working across packages, run the skill check from the workspace root and prefer the local skill for the package being changed.",
224
+ "- Multiple matches: prefer the most specific local skill for the package or concern you are changing; load additional skills only when the task spans multiple packages or concerns.",
225
+ INTENT_SKILLS_END
226
+ ].join("\n")}\n`,
227
+ mappingCount: 0
228
+ };
229
+ }
230
+ function detectNewline(content) {
231
+ return content.includes("\r\n") ? "\r\n" : "\n";
232
+ }
233
+ function withNewlineStyle(content, newline) {
234
+ return newline === "\n" ? content : content.replace(/\n/g, newline);
235
+ }
236
+ function findExistingConfigWithManagedBlock(root) {
237
+ for (const file of SUPPORTED_AGENT_CONFIG_FILES) {
238
+ const filePath = join(root, file);
239
+ if (!existsSync(filePath)) continue;
240
+ const content = readFileSync(filePath, "utf8");
241
+ const { managedBlock, errors, hasMarker } = readManagedBlock(content);
242
+ if (managedBlock) return {
243
+ content,
244
+ filePath,
245
+ managedBlock
246
+ };
247
+ if (hasMarker) throw new Error(`Invalid intent-skills block in ${filePath}: ${errors.join(" ")}`);
248
+ }
249
+ return null;
250
+ }
251
+ function replaceManagedBlock(content, managedBlock, block) {
252
+ const newline = detectNewline(content);
253
+ const styledBlock = withNewlineStyle(block.trimEnd(), newline);
254
+ return `${content.slice(0, managedBlock.start)}${styledBlock}${content.slice(managedBlock.end)}`;
255
+ }
256
+ function writeIntentSkillsBlock({ block, mappingCount, root, skipWhenEmpty = true }) {
257
+ if (mappingCount === 0 && skipWhenEmpty) return {
258
+ mappingCount,
259
+ status: "skipped",
260
+ targetPath: null
261
+ };
262
+ const existingTarget = findExistingConfigWithManagedBlock(root);
263
+ const targetPath = existingTarget?.filePath ?? join(root, "AGENTS.md");
264
+ if (existingTarget) {
265
+ const nextContent = replaceManagedBlock(existingTarget.content, existingTarget.managedBlock, block);
266
+ if (nextContent === existingTarget.content) return {
267
+ mappingCount,
268
+ status: "unchanged",
269
+ targetPath
270
+ };
271
+ writeFileSync(targetPath, nextContent);
272
+ return {
273
+ mappingCount,
274
+ status: "updated",
275
+ targetPath
276
+ };
277
+ }
278
+ if (existsSync(targetPath)) {
279
+ const currentContent = readFileSync(targetPath, "utf8");
280
+ const newline = detectNewline(currentContent);
281
+ const separator = currentContent === "" ? "" : newline;
282
+ writeFileSync(targetPath, `${withNewlineStyle(block, newline)}${separator}${currentContent}`);
283
+ return {
284
+ mappingCount,
285
+ status: "updated",
286
+ targetPath
287
+ };
288
+ }
289
+ mkdirSync(dirname(targetPath), { recursive: true });
290
+ writeFileSync(targetPath, block);
291
+ return {
292
+ mappingCount,
293
+ status: "created",
294
+ targetPath
295
+ };
296
+ }
297
+
298
+ //#endregion
299
+ //#region src/commands/install.ts
300
+ const INSTALL_PROMPT = `You are an AI assistant helping a developer set up skill-to-task mappings for their project.
301
+
302
+ Goal: create or update one agent config file with an intent-skills mapping block.
303
+
304
+ Hard rules:
305
+ - Do not report success until a file was created or updated, or an existing mapping block was confirmed.
306
+ - If skills are discovered and no mapping block exists, create AGENTS.md unless the user asks for another supported config file.
307
+ - If a mapping block already exists in a supported config file, update that file.
308
+ - Preserve all content outside the managed block unchanged.
309
+ - Store compact \`use\` values in the managed block; do not write \`load\` paths.
310
+ - Never write absolute local file paths, node_modules paths, or package-manager-internal paths in the managed block.
311
+ - Verify the target file before your final response.
312
+
313
+ Follow these steps in order:
314
+
315
+ 1. CHECK FOR EXISTING MAPPINGS
316
+ Search the project's agent config files (AGENTS.md, CLAUDE.md, .cursorrules,
317
+ .github/copilot-instructions.md) for a block delimited by:
318
+ <!-- intent-skills:start -->
319
+ <!-- intent-skills:end -->
320
+ - If found: show the user the current mappings, keep that file as the source of truth,
321
+ and ask "What would you like to update?" Then skip to step 4 with their requested changes.
322
+ - If not found: continue to step 2.
323
+
324
+ 2. DISCOVER AVAILABLE SKILLS
325
+ Run: \`npx @tanstack/intent@latest list\`
326
+ This scans project-local node_modules by default and outputs each package and skill's name,
327
+ description, and source.
328
+ If the user explicitly wants globally installed skills included, run:
329
+ \`npx @tanstack/intent@latest list --global\`
330
+ This works best in Node-compatible environments (npm, pnpm, Bun, or Deno npm interop
331
+ with node_modules enabled).
332
+ If no skills are found, do not create a config file. Report: "No intent-enabled skills found."
333
+
334
+ 3. SCAN THE REPOSITORY
335
+ Build a picture of the project's structure and patterns:
336
+ - Read package.json for library dependencies
337
+ - Survey the directory layout (src/, app/, routes/, components/, api/, etc.)
338
+ - Note recurring patterns (routing, data fetching, auth, UI components, etc.)
339
+
340
+ Mapping coverage rule:
341
+ - Create mappings for all discovered actionable skills.
342
+ - Do not omit an actionable skill only because the repo does not currently appear to use it.
343
+ - Do not map reference, meta, maintainer, or maintainer-only skills by default.
344
+ - Include slash-named sub-skills when no parent mapping exists, or when they describe distinct user tasks.
345
+ - If the proposed block would exceed 12 mappings, show the full discovered list and ask which packages
346
+ or skill groups to include before writing.
347
+ - Add one fallback note telling the agent to run \`npx @tanstack/intent@latest list\` for less common local skills.
348
+
349
+ Based on the repository scan and the coverage rule, propose the skill-to-task mappings.
350
+ For each one explain:
351
+ - The task or code area (in plain language the user would recognise)
352
+ - Which skill applies and why
353
+
354
+ Then ask: "What other tasks do you commonly use AI coding agents for?
355
+ I'll create mappings for those too."
356
+ Also ask: "I'll default to AGENTS.md unless you want another supported config file.
357
+ Do you have a preference?"
358
+
359
+ 4. WRITE THE MAPPINGS BLOCK
360
+ Once you have the full set of mappings, write or update the agent config file.
361
+ - If you found an existing intent-skills block, update that file in place.
362
+ - Otherwise prefer AGENTS.md by default, unless the user asked for another supported file.
363
+ - Do not stop after discovery. If skills were found, the task is incomplete until this file exists
364
+ and contains the managed block.
365
+
366
+ Use this exact block:
367
+
368
+ <!-- intent-skills:start -->
369
+ # Skill mappings - load \`use\` with \`npx @tanstack/intent@latest load <use>\`.
370
+ skills:
371
+ - when: "describe the task or code area here"
372
+ use: "@scope/package#skill-name"
373
+ <!-- intent-skills:end -->
374
+
375
+ Rules:
376
+ - Use the user's own words for \`when\` descriptions
377
+ - Use compact \`use\` values in \`<package>#<skill>\` format
378
+ - Do not include \`load\`
379
+ - Do not include machine-specific directories such as \`/Users/...\`, \`/home/...\`, \`/private/...\`,
380
+ drive letters, temp workspace paths, \`.pnpm/\`, \`.bun/\`, or \`.yarn/\`.
381
+ - Agents should load \`use\` at runtime with \`npx @tanstack/intent@latest load <use>\`
382
+ - Keep entries concise - this block is read on every agent task
383
+ - Preserve all content outside the block tags unchanged
384
+ - If the user is on Deno, note that this setup is best-effort today and relies on npm interop
385
+
386
+ 5. VERIFY AND REPORT
387
+ Before reporting completion:
388
+ - Confirm the target file exists
389
+ - Confirm it contains both managed block markers
390
+ - Confirm every mapping has \`when\` and \`use\`
391
+ - Confirm every \`use\` parses as \`<package>#<skill>\`
392
+ - Confirm no mapping includes \`load\`
393
+ - Confirm no path-like machine-specific values are stored in the managed block
394
+ - Confirm every discovered actionable skill is mapped, skipped by rule, or deferred by user choice
395
+
396
+ Final response must include:
397
+ - The target file path
398
+ - Whether it was created, updated, or already contained a valid block
399
+ - The number of mappings
400
+ - The verification result`;
401
+ function formatTargetPath(targetPath) {
402
+ return relative(process.cwd(), targetPath) || targetPath;
403
+ }
404
+ function formatMappingCount(mappingCount) {
405
+ return `${mappingCount} ${mappingCount === 1 ? "mapping" : "mappings"}`;
406
+ }
407
+ function printNoActionableSkills(warnings) {
408
+ console.log("No intent-enabled skills found.");
409
+ printWarnings(warnings);
410
+ }
411
+ function printPlacementTip(targetPath) {
412
+ console.log(`Tip: Keep the intent-skills block near the top of ${formatTargetPath(targetPath)} so agents read it before task-specific instructions.`);
413
+ }
414
+ function printWriteResult({ mappingCount, status, targetPath }) {
415
+ const target = formatTargetPath(targetPath);
416
+ if (mappingCount === 0) {
417
+ switch (status) {
418
+ case "created":
419
+ console.log(`Created ${target} with skill loading guidance.`);
420
+ break;
421
+ case "updated":
422
+ console.log(`Updated ${target} with skill loading guidance.`);
423
+ break;
424
+ case "unchanged":
425
+ console.log(`No changes to ${target}; skill loading guidance already current.`);
426
+ break;
427
+ }
428
+ return;
429
+ }
430
+ switch (status) {
431
+ case "created":
432
+ console.log(`Created ${target} with ${formatMappingCount(mappingCount)}.`);
433
+ break;
434
+ case "updated":
435
+ console.log(`Updated ${target} with ${formatMappingCount(mappingCount)}.`);
436
+ break;
437
+ case "unchanged":
438
+ console.log(`No changes to ${target}; ${formatMappingCount(mappingCount)} already current.`);
439
+ break;
440
+ }
441
+ }
442
+ async function runInstallCommand(options, scanIntentsOrFail$1) {
443
+ if (options.printPrompt) {
444
+ console.log(INSTALL_PROMPT);
445
+ return;
446
+ }
447
+ scanOptionsFromGlobalFlags(options);
448
+ if (!options.map) {
449
+ const generated$1 = buildIntentSkillGuidanceBlock();
450
+ if (options.dryRun) {
451
+ const targetPath = resolveIntentSkillsBlockTargetPath(process.cwd(), 1);
452
+ console.log(`Generated skill loading guidance for ${formatTargetPath(targetPath)}.`);
453
+ console.log(generated$1.block);
454
+ return;
455
+ }
456
+ const result$1 = writeIntentSkillsBlock({
457
+ ...generated$1,
458
+ root: process.cwd(),
459
+ skipWhenEmpty: false
460
+ });
461
+ if (!result$1.targetPath) fail("Install guidance target was not created.");
462
+ const verification$1 = verifyIntentSkillsBlockFile({
463
+ expectedBlock: generated$1.block,
464
+ targetPath: result$1.targetPath
465
+ });
466
+ const target$1 = formatTargetPath(result$1.targetPath);
467
+ if (!verification$1.ok) fail([`Install verification failed for ${target$1}:`, ...verification$1.errors.map((error) => `- ${error}`)].join("\n"));
468
+ printWriteResult(result$1);
469
+ printPlacementTip(result$1.targetPath);
470
+ return;
471
+ }
472
+ const scanResult = await scanIntentsOrFail$1(scanOptionsFromGlobalFlags(options));
473
+ const generated = buildIntentSkillsBlock(scanResult);
474
+ if (options.dryRun) {
475
+ const targetPath = resolveIntentSkillsBlockTargetPath(process.cwd(), generated.mappingCount);
476
+ if (!targetPath) {
477
+ printNoActionableSkills(scanResult.warnings);
478
+ return;
479
+ }
480
+ console.log(`Generated ${formatMappingCount(generated.mappingCount)} for ${formatTargetPath(targetPath)}.`);
481
+ console.log(generated.block);
482
+ printWarnings(scanResult.warnings);
483
+ return;
484
+ }
485
+ const result = writeIntentSkillsBlock({
486
+ ...generated,
487
+ root: process.cwd()
488
+ });
489
+ if (!result.targetPath) {
490
+ printNoActionableSkills(scanResult.warnings);
491
+ return;
492
+ }
493
+ const target = formatTargetPath(result.targetPath);
494
+ const verification = verifyIntentSkillsBlockFile({
495
+ expectedBlock: generated.block,
496
+ expectedMappingCount: generated.mappingCount,
497
+ targetPath: result.targetPath
498
+ });
499
+ if (!verification.ok) fail([`Install verification failed for ${target}:`, ...verification.errors.map((error) => `- ${error}`)].join("\n"));
500
+ printWriteResult(result);
501
+ printPlacementTip(result.targetPath);
502
+ printWarnings(scanResult.warnings);
503
+ }
504
+
505
+ //#endregion
506
+ export { scanIntentsOrFail as a, fail as c, resolveStaleTargets as i, isCliFailure as l, runInstallCommand as n, scanOptionsFromGlobalFlags as o, getMetaDir as r, printWarnings as s, INSTALL_PROMPT as t };
@@ -1,14 +1,17 @@
1
1
  #!/usr/bin/env node
2
2
  import "./utils-COlDcU72.mjs";
3
- import { t as INSTALL_PROMPT } from "./install-BzDmD5yI.mjs";
4
- import { n as printSkillTree, r as printTable, t as computeSkillNameWidth } from "./display-hdsqb4w-.mjs";
5
- import { t as scanLibrary } from "./library-scanner-B51qV5aX.mjs";
3
+ import "./skill-paths-8k9K9y26.mjs";
4
+ import "./workspace-patterns-U35B5AO-.mjs";
5
+ import "./project-context-CKG-q4rD.mjs";
6
+ import { t as INSTALL_PROMPT } from "./install-BmVqcbEi.mjs";
7
+ import { n as printSkillTree, r as printTable, t as computeSkillNameWidth } from "./display-DUgtRJkt.mjs";
8
+ import { t as scanLibrary } from "./library-scanner-DFFreLjW.mjs";
6
9
 
7
10
  //#region src/intent-library.ts
8
11
  function cmdList() {
9
12
  let result;
10
13
  try {
11
- result = scanLibrary(process.argv[1]);
14
+ result = scanLibrary(process.argv[1], process.cwd());
12
15
  } catch (err) {
13
16
  console.error(err.message);
14
17
  process.exit(1);
@@ -39,6 +42,7 @@ function cmdList() {
39
42
  console.log(` ${pkg.name}`);
40
43
  printSkillTree(pkg.skills, {
41
44
  nameWidth,
45
+ packageName: pkg.name,
42
46
  showTypes
43
47
  });
44
48
  console.log();
@@ -60,7 +64,7 @@ const USAGE = `TanStack Intent
60
64
  Usage:
61
65
  intent list List all available skills from this library and its dependencies
62
66
  intent install Print a skill that guides your coding agent to scan the project
63
- and set up skill-to-task mappings in your agent config`;
67
+ and set up skill loading guidance in your agent config`;
64
68
  const command = process.argv[2];
65
69
  switch (command) {
66
70
  case "list":
@@ -1,4 +1,5 @@
1
1
  import { a as parseFrontmatter, o as resolveDepDir, r as getDeps, s as toPosixPath } from "./utils-COlDcU72.mjs";
2
+ import { r as rewriteSkillLoadPaths } from "./skill-paths-8k9K9y26.mjs";
2
3
  import { existsSync, readFileSync, readdirSync } from "node:fs";
3
4
  import { dirname, join, relative } from "node:path";
4
5
 
@@ -52,14 +53,14 @@ function discoverSkills(skillsDir) {
52
53
  type: typeof fm?.type === "string" ? fm.type : void 0,
53
54
  framework: typeof fm?.framework === "string" ? fm.framework : void 0
54
55
  });
55
- walk(childDir);
56
56
  }
57
+ walk(childDir);
57
58
  }
58
59
  }
59
60
  walk(skillsDir);
60
61
  return skills;
61
62
  }
62
- function scanLibrary(scriptPath, _projectRoot) {
63
+ function scanLibrary(scriptPath, projectRoot) {
63
64
  const packages = [];
64
65
  const warnings = [];
65
66
  const visited = /* @__PURE__ */ new Set();
@@ -74,6 +75,7 @@ function scanLibrary(scriptPath, _projectRoot) {
74
75
  warnings: ["Could not read home package.json"]
75
76
  };
76
77
  const homeName = typeof homePkg.name === "string" ? homePkg.name : "";
78
+ const scanRoot = projectRoot ?? homeDir;
77
79
  function processPackage(name, dir) {
78
80
  if (visited.has(name)) return;
79
81
  visited.add(name);
@@ -84,7 +86,12 @@ function scanLibrary(scriptPath, _projectRoot) {
84
86
  }
85
87
  const skillsDir = join(dir, "skills");
86
88
  const skills = existsSync(skillsDir) ? discoverSkills(skillsDir) : [];
87
- if (name) for (const skill of skills) skill.path = `node_modules/${name}/${toPosixPath(relative(dir, skill.path))}`;
89
+ rewriteSkillLoadPaths({
90
+ packageName: name,
91
+ packageRoot: dir,
92
+ projectRoot: scanRoot,
93
+ skills
94
+ });
88
95
  packages.push({
89
96
  name,
90
97
  version: typeof pkg.version === "string" ? pkg.version : "0.0.0",
@@ -1,4 +1,4 @@
1
- import { l as SkillEntry } from "./types-BTQ9efv-.mjs";
1
+ import { u as SkillEntry } from "./types-CsySN6Vw.mjs";
2
2
 
3
3
  //#region src/library-scanner.d.ts
4
4
  interface LibraryPackage {
@@ -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): LibraryScanResult;
14
+ declare function scanLibrary(scriptPath: string, projectRoot?: string): LibraryScanResult;
15
15
  //#endregion
16
16
  export { LibraryPackage, LibraryScanResult, scanLibrary };
@@ -1,4 +1,5 @@
1
1
  import "./utils-COlDcU72.mjs";
2
- import { t as scanLibrary } from "./library-scanner-B51qV5aX.mjs";
2
+ import "./skill-paths-8k9K9y26.mjs";
3
+ import { t as scanLibrary } from "./library-scanner-DFFreLjW.mjs";
3
4
 
4
5
  export { scanLibrary };
@@ -1,4 +1,4 @@
1
- import { n as findWorkspaceRoot, r as readWorkspacePatterns } from "./workspace-patterns-Cndd-7vB.mjs";
1
+ import { n as findWorkspaceRoot, r as readWorkspacePatterns } from "./workspace-patterns-U35B5AO-.mjs";
2
2
  import { existsSync, statSync } from "node:fs";
3
3
  import { dirname, join, relative, resolve } from "node:path";
4
4
 
@@ -0,0 +1,70 @@
1
+ import { i as parseSkillUse } from "./skill-use-CXOnncWK.mjs";
2
+
3
+ //#region src/resolver.ts
4
+ var ResolveSkillUseError = class extends Error {
5
+ constructor({ availablePackages = [], availableSkills = [], code, packageName, skillName, use }) {
6
+ super(formatResolveSkillUseErrorMessage({
7
+ availablePackages,
8
+ availableSkills,
9
+ code,
10
+ packageName,
11
+ skillName,
12
+ use
13
+ }));
14
+ this.name = "ResolveSkillUseError";
15
+ this.availablePackages = availablePackages;
16
+ this.availableSkills = availableSkills;
17
+ this.code = code;
18
+ this.packageName = packageName;
19
+ this.skillName = skillName;
20
+ this.use = use;
21
+ }
22
+ };
23
+ function isResolveSkillUseError(error) {
24
+ return error instanceof ResolveSkillUseError;
25
+ }
26
+ function resolveSkillUse(use, scanResult) {
27
+ const { packageName, skillName } = parseSkillUse(use);
28
+ const packages = scanResult.packages.filter((pkg$1) => pkg$1.name === packageName);
29
+ const pkg = packages.find((candidate) => candidate.source === "local") ?? packages[0];
30
+ if (!pkg) throw new ResolveSkillUseError({
31
+ availablePackages: scanResult.packages.map((candidate) => candidate.name),
32
+ code: "package-not-found",
33
+ packageName,
34
+ skillName,
35
+ use
36
+ });
37
+ const skill = pkg.skills.find((candidate) => candidate.name === skillName);
38
+ if (!skill) throw new ResolveSkillUseError({
39
+ availableSkills: pkg.skills.map((candidate) => candidate.name),
40
+ code: "skill-not-found",
41
+ packageName,
42
+ skillName,
43
+ use
44
+ });
45
+ const conflict = scanResult.conflicts.find((candidate) => candidate.packageName === packageName) ?? null;
46
+ return {
47
+ packageName,
48
+ skillName,
49
+ path: skill.path,
50
+ source: pkg.source,
51
+ version: pkg.version,
52
+ packageRoot: pkg.packageRoot,
53
+ warnings: scanResult.warnings.filter((warning) => {
54
+ const idx = warning.indexOf(packageName);
55
+ if (idx === -1) return false;
56
+ const after = warning[idx + packageName.length];
57
+ return after === void 0 || /[^a-zA-Z0-9_-]/.test(after);
58
+ }),
59
+ conflict
60
+ };
61
+ }
62
+ function formatResolveSkillUseErrorMessage({ availablePackages, availableSkills, code, packageName, skillName, use }) {
63
+ switch (code) {
64
+ case "package-not-found": return `Cannot resolve skill use "${use}": package "${packageName}" was not found.${availablePackages.length > 0 ? ` Available packages: ${availablePackages.join(", ")}.` : ""}`;
65
+ case "skill-not-found": return `Cannot resolve skill use "${use}": skill "${skillName}" was not found in package "${packageName}".${availableSkills.length > 0 ? ` Available skills: ${availableSkills.join(", ")}.` : ""}`;
66
+ }
67
+ }
68
+
69
+ //#endregion
70
+ export { isResolveSkillUseError as n, resolveSkillUse as r, ResolveSkillUseError as t };