@tanstack/intent 0.0.35 → 0.0.37
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/artifact-coverage-DA26utB1.mjs +3 -0
- package/dist/cli-error-BrMXlbtx.mjs +15 -0
- package/dist/cli-support-CHEYZ9xj.mjs +6 -0
- package/dist/cli-support-Dk2rgYli.mjs +114 -0
- package/dist/cli.mjs +17 -560
- package/dist/core-BlJqkv0a.mjs +497 -0
- package/dist/core.d.mts +85 -0
- package/dist/core.mjs +9 -0
- package/dist/display-COlw5FaB.mjs +5 -0
- package/dist/{display-CAof6doy.mjs → display-CVeoAwBd.mjs} +1 -1
- package/dist/edit-package-json-CzWlMXOf.mjs +8 -0
- package/dist/index.d.mts +5 -2
- package/dist/index.mjs +11 -11
- package/dist/install-BAUG1FD8.mjs +7 -0
- package/dist/{install-2_wkomiT.mjs → install-DA19Vg17.mjs} +7 -111
- package/dist/intent-library.mjs +8 -7
- package/dist/{library-scanner-fexXlPXb.mjs → library-scanner-CHepLJQJ.mjs} +2 -2
- package/dist/library-scanner.d.mts +1 -1
- package/dist/library-scanner.mjs +3 -3
- package/dist/list-D6osUZl7.mjs +107 -0
- package/dist/load-CzvLUFur.mjs +70 -0
- package/dist/meta-CYV9EzM8.mjs +38 -0
- package/dist/{project-context-alYMNoNa.mjs → project-context-CfSZGJFC.mjs} +1 -1
- package/dist/resolver-BTeG2oyZ.mjs +196 -0
- package/dist/scaffold-D2vwv9ls.mjs +75 -0
- package/dist/scanner-DgRSm4y0.mjs +6 -0
- package/dist/{scanner-B1j-wDhj.mjs → scanner-W6797hWs.mjs} +101 -19
- package/dist/{setup-Dp-W8y0Y.mjs → setup-B-1hsdmA.mjs} +2 -2
- package/dist/setup-github-actions-emXSyGy3.mjs +8 -0
- package/dist/setup.d.mts +1 -1
- package/dist/setup.mjs +4 -4
- package/dist/{skill-paths-8k9K9y26.mjs → skill-paths-DNOHrvL5.mjs} +1 -1
- package/dist/stale-flPZnWfI.mjs +53 -0
- package/dist/{staleness-PdgakrCQ.mjs → staleness-CFr3W-RI.mjs} +2 -2
- package/dist/staleness-DpyuNdd5.mjs +5 -0
- package/dist/{types-DT7Y6TFz.d.mts → types-S2zpibHp.d.mts} +1 -1
- package/dist/{utils-dkVvY7D7.mjs → utils-BHzSyNeJ.mjs} +1 -1
- package/dist/validate-ugk2DZBd.mjs +271 -0
- package/dist/{workflow-review-Dz_ofcYQ.mjs → workflow-review-CIdJXmKP.mjs} +1 -1
- package/dist/{workspace-patterns-x-dLZxx4.mjs → workspace-patterns-BGQcoSy7.mjs} +2 -2
- package/dist/{workspace-patterns-BN2A_60g.mjs → workspace-patterns-CNhdqCO4.mjs} +1 -1
- package/package.json +7 -3
- package/dist/artifact-coverage-BAN2W6aH.mjs +0 -3
- package/dist/display-B3vkG99D.mjs +0 -5
- package/dist/resolver-Whd12ksO.mjs +0 -70
- package/dist/scanner-C2YjF4w_.mjs +0 -6
- package/dist/staleness-DpbmYod4.mjs +0 -5
- /package/dist/{artifact-coverage-wLNVX8yC.mjs → artifact-coverage-DgWuVqUp.mjs} +0 -0
- /package/dist/{setup-t1i2o2-h.d.mts → setup-ldoQOQot.d.mts} +0 -0
- /package/dist/{skill-use-BzuuvLM7.mjs → skill-use-umYvZl94.mjs} +0 -0
- /package/dist/{utils-COlDcU72.mjs → utils-mdb4i6VA.mjs} +0 -0
- /package/dist/{workflow-review-CwkPVIQf.mjs → workflow-review-CwcR2ge4.mjs} +0 -0
package/dist/cli.mjs
CHANGED
|
@@ -1,594 +1,51 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import "./
|
|
3
|
-
import {
|
|
4
|
-
import { i as parseSkillUse } from "./skill-use-BzuuvLM7.mjs";
|
|
5
|
-
import { r as resolveSkillUse } from "./resolver-Whd12ksO.mjs";
|
|
6
|
-
import { t as resolveProjectContext } from "./project-context-alYMNoNa.mjs";
|
|
7
|
-
import { a as scanIntentsOrFail, c as fail, i as resolveStaleTargets, l as isCliFailure, n as runInstallCommand, o as scanOptionsFromGlobalFlags, r as getMetaDir, s as printWarnings } from "./install-2_wkomiT.mjs";
|
|
8
|
-
import { appendFileSync, existsSync, readFileSync, readdirSync, realpathSync } from "node:fs";
|
|
9
|
-
import { basename, dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
|
|
2
|
+
import { n as isCliFailure, t as fail } from "./cli-error-BrMXlbtx.mjs";
|
|
3
|
+
import { realpathSync } from "node:fs";
|
|
10
4
|
import { fileURLToPath } from "node:url";
|
|
11
5
|
import { cac } from "cac";
|
|
12
6
|
|
|
13
|
-
//#region src/commands/edit-package-json.ts
|
|
14
|
-
async function runEditPackageJsonCommand(root) {
|
|
15
|
-
const { runEditPackageJsonAll } = await import("./setup.mjs");
|
|
16
|
-
runEditPackageJsonAll(root);
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
//#endregion
|
|
20
|
-
//#region src/commands/list.ts
|
|
21
|
-
function formatScanCoverage(result) {
|
|
22
|
-
const coverage = [];
|
|
23
|
-
if (result.nodeModules.local.scanned) coverage.push("project node_modules");
|
|
24
|
-
if (result.nodeModules.global.scanned) coverage.push("global node_modules");
|
|
25
|
-
return coverage.join(", ");
|
|
26
|
-
}
|
|
27
|
-
function printVersionConflicts(result) {
|
|
28
|
-
if (result.conflicts.length === 0) return;
|
|
29
|
-
console.log("\nVersion conflicts:\n");
|
|
30
|
-
for (const conflict of result.conflicts) {
|
|
31
|
-
console.log(` ${conflict.packageName} -> using ${conflict.chosen.version}`);
|
|
32
|
-
console.log(` chosen: ${conflict.chosen.packageRoot}`);
|
|
33
|
-
for (const variant of conflict.variants) {
|
|
34
|
-
if (variant.packageRoot === conflict.chosen.packageRoot) continue;
|
|
35
|
-
console.log(` also found: ${variant.version} at ${variant.packageRoot}`);
|
|
36
|
-
}
|
|
37
|
-
console.log();
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
async function runListCommand(options, scanIntentsOrFail$1) {
|
|
41
|
-
const result = await scanIntentsOrFail$1(scanOptionsFromGlobalFlags(options));
|
|
42
|
-
if (options.json) {
|
|
43
|
-
console.log(JSON.stringify(result, null, 2));
|
|
44
|
-
return;
|
|
45
|
-
}
|
|
46
|
-
const { computeSkillNameWidth, printSkillTree, printTable } = await import("./display-B3vkG99D.mjs");
|
|
47
|
-
const scanCoverage = formatScanCoverage(result);
|
|
48
|
-
if (result.packages.length === 0) {
|
|
49
|
-
console.log("No intent-enabled packages found.");
|
|
50
|
-
if (scanCoverage) console.log(`Scanned: ${scanCoverage}`);
|
|
51
|
-
if (result.warnings.length > 0) {
|
|
52
|
-
console.log();
|
|
53
|
-
printWarnings(result.warnings);
|
|
54
|
-
}
|
|
55
|
-
return;
|
|
56
|
-
}
|
|
57
|
-
const totalSkills = result.packages.reduce((sum, pkg) => sum + pkg.skills.length, 0);
|
|
58
|
-
console.log(`\n${result.packages.length} intent-enabled packages, ${totalSkills} skills (${result.packageManager})\n`);
|
|
59
|
-
if (scanCoverage) console.log(`Scanned: ${scanCoverage}${result.nodeModules.global.scanned ? " (local packages take precedence)" : ""}\n`);
|
|
60
|
-
printTable([
|
|
61
|
-
"PACKAGE",
|
|
62
|
-
"SOURCE",
|
|
63
|
-
"VERSION",
|
|
64
|
-
"SKILLS",
|
|
65
|
-
"REQUIRES"
|
|
66
|
-
], result.packages.map((pkg) => [
|
|
67
|
-
pkg.name,
|
|
68
|
-
pkg.source,
|
|
69
|
-
pkg.version,
|
|
70
|
-
String(pkg.skills.length),
|
|
71
|
-
pkg.intent.requires?.join(", ") || "–"
|
|
72
|
-
]));
|
|
73
|
-
printVersionConflicts(result);
|
|
74
|
-
const nameWidth = computeSkillNameWidth(result.packages.map((pkg) => pkg.skills));
|
|
75
|
-
const showTypes = result.packages.some((pkg) => pkg.skills.some((skill) => skill.type));
|
|
76
|
-
console.log(`\nSkills:\n`);
|
|
77
|
-
for (const pkg of result.packages) {
|
|
78
|
-
console.log(` ${pkg.name}`);
|
|
79
|
-
printSkillTree(pkg.skills, {
|
|
80
|
-
nameWidth,
|
|
81
|
-
packageName: pkg.name,
|
|
82
|
-
showTypes
|
|
83
|
-
});
|
|
84
|
-
console.log();
|
|
85
|
-
}
|
|
86
|
-
console.log("Feedback:");
|
|
87
|
-
console.log(" Submit feedback on skill usage to help maintainers improve the skills.");
|
|
88
|
-
console.log(" Load: node_modules/@tanstack/intent/meta/feedback-collection/SKILL.md");
|
|
89
|
-
console.log();
|
|
90
|
-
printWarnings(result.warnings);
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
//#endregion
|
|
94
|
-
//#region src/commands/load.ts
|
|
95
|
-
function resolveFromCwd(path) {
|
|
96
|
-
return resolve(process.cwd(), path);
|
|
97
|
-
}
|
|
98
|
-
function isPathInsidePackageRoot(path, packageRoot) {
|
|
99
|
-
const relativePath = relative(resolveFromCwd(packageRoot), resolveFromCwd(path));
|
|
100
|
-
return relativePath === "" || !relativePath.startsWith("..") && !isAbsolute(relativePath);
|
|
101
|
-
}
|
|
102
|
-
async function runLoadCommand(use, options, scanIntentsOrFail$1) {
|
|
103
|
-
if (!use) fail("Missing skill use. Expected: intent load <package>#<skill>");
|
|
104
|
-
if (options.json && options.path) fail("Use either --json or --path, not both.");
|
|
105
|
-
parseSkillUse(use);
|
|
106
|
-
const resolved = resolveSkillUse(use, await scanIntentsOrFail$1(scanOptionsFromGlobalFlags(options)));
|
|
107
|
-
const resolvedPath = resolveFromCwd(resolved.path);
|
|
108
|
-
if (!isPathInsidePackageRoot(resolved.path, resolved.packageRoot)) fail(`Resolved skill path for "${use}" is outside package root: ${resolved.path}`);
|
|
109
|
-
if (!existsSync(resolvedPath)) fail(`Resolved skill file was not found: ${resolved.path}`);
|
|
110
|
-
if (options.path) {
|
|
111
|
-
console.log(resolved.path);
|
|
112
|
-
for (const warning of resolved.warnings) console.error(`Warning: ${warning}`);
|
|
113
|
-
return;
|
|
114
|
-
}
|
|
115
|
-
const content = readFileSync(resolvedPath, "utf8");
|
|
116
|
-
if (options.json) {
|
|
117
|
-
console.log(JSON.stringify({
|
|
118
|
-
package: resolved.packageName,
|
|
119
|
-
skill: resolved.skillName,
|
|
120
|
-
path: resolved.path,
|
|
121
|
-
packageRoot: resolved.packageRoot,
|
|
122
|
-
source: resolved.source,
|
|
123
|
-
version: resolved.version,
|
|
124
|
-
content,
|
|
125
|
-
warnings: resolved.warnings
|
|
126
|
-
}, null, 2));
|
|
127
|
-
return;
|
|
128
|
-
}
|
|
129
|
-
process.stdout.write(content);
|
|
130
|
-
for (const warning of resolved.warnings) console.error(`Warning: ${warning}`);
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
//#endregion
|
|
134
|
-
//#region src/commands/meta.ts
|
|
135
|
-
async function runMetaCommand(name, metaDir) {
|
|
136
|
-
if (!existsSync(metaDir)) fail("Meta-skills directory not found.");
|
|
137
|
-
if (name) {
|
|
138
|
-
if (name.includes("..") || name.includes("/") || name.includes("\\")) fail(`Invalid meta-skill name: "${name}"`);
|
|
139
|
-
const skillFile = join(metaDir, name, "SKILL.md");
|
|
140
|
-
if (!existsSync(skillFile)) fail(`Meta-skill "${name}" not found. Run \`intent meta\` to list available meta-skills.`);
|
|
141
|
-
try {
|
|
142
|
-
console.log(readFileSync(skillFile, "utf8"));
|
|
143
|
-
} catch (err) {
|
|
144
|
-
fail(`Failed to read meta-skill "${name}": ${err instanceof Error ? err.message : String(err)}`);
|
|
145
|
-
}
|
|
146
|
-
return;
|
|
147
|
-
}
|
|
148
|
-
const { parseFrontmatter } = await import("./utils-dkVvY7D7.mjs");
|
|
149
|
-
const entries = readdirSync(metaDir, { withFileTypes: true }).filter((entry) => entry.isDirectory()).filter((entry) => existsSync(join(metaDir, entry.name, "SKILL.md")));
|
|
150
|
-
if (entries.length === 0) {
|
|
151
|
-
console.log("No meta-skills found.");
|
|
152
|
-
return;
|
|
153
|
-
}
|
|
154
|
-
console.log("Meta-skills (for library maintainers):\n");
|
|
155
|
-
for (const entry of entries) {
|
|
156
|
-
const fm = parseFrontmatter(join(metaDir, entry.name, "SKILL.md"));
|
|
157
|
-
let description = "";
|
|
158
|
-
if (typeof fm?.description === "string") description = fm.description.replace(/\s+/g, " ").trim();
|
|
159
|
-
const shortDesc = description.length > 60 ? `${description.slice(0, 57)}...` : description;
|
|
160
|
-
console.log(` ${entry.name.padEnd(28)} ${shortDesc}`);
|
|
161
|
-
}
|
|
162
|
-
console.log("\nUsage: load the SKILL.md into your AI agent conversation.");
|
|
163
|
-
console.log("Path: node_modules/@tanstack/intent/meta/<name>/SKILL.md");
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
//#endregion
|
|
167
|
-
//#region src/commands/scaffold.ts
|
|
168
|
-
function runScaffoldCommand(metaDir) {
|
|
169
|
-
function metaSkillPath(name) {
|
|
170
|
-
return join(metaDir, name, "SKILL.md");
|
|
171
|
-
}
|
|
172
|
-
const prompt = `You are helping a library maintainer scaffold Intent skills.
|
|
173
|
-
|
|
174
|
-
Run the three meta skills below **one at a time, in order**. For each step:
|
|
175
|
-
1. Load the SKILL.md file specified
|
|
176
|
-
2. Follow its instructions completely
|
|
177
|
-
3. Present outputs to the maintainer for review
|
|
178
|
-
4. Do NOT proceed to the next step until the maintainer confirms
|
|
179
|
-
|
|
180
|
-
## Before you start
|
|
181
|
-
|
|
182
|
-
Gather this context yourself (do not ask the maintainer — agents should never
|
|
183
|
-
ask for information they can discover):
|
|
184
|
-
1. Read package.json for library name, repository URL, and homepage/docs URL
|
|
185
|
-
2. Detect if this is a monorepo (look for workspaces field, packages/ directory, lerna.json)
|
|
186
|
-
3. Use skills/ as the default skills root
|
|
187
|
-
4. For monorepos:
|
|
188
|
-
- Domain map artifacts go at the REPO ROOT: _artifacts/
|
|
189
|
-
- Skills go INSIDE EACH PACKAGE: packages/<pkg>/skills/
|
|
190
|
-
- Identify which packages are client-facing (usually client SDKs and primary framework adapters)
|
|
191
|
-
|
|
192
|
-
---
|
|
193
|
-
|
|
194
|
-
## Step 1 — Domain Discovery
|
|
195
|
-
|
|
196
|
-
Load and follow: ${metaSkillPath("domain-discovery")}
|
|
197
|
-
|
|
198
|
-
This produces: domain_map.yaml and skill_spec.md in the artifacts directory.
|
|
199
|
-
Domain discovery covers the WHOLE library (one domain map even for monorepos).
|
|
200
|
-
|
|
201
|
-
**STOP. Review outputs with the maintainer before continuing.**
|
|
202
|
-
|
|
203
|
-
---
|
|
204
|
-
|
|
205
|
-
## Step 2 — Tree Generator
|
|
206
|
-
|
|
207
|
-
Load and follow: ${metaSkillPath("tree-generator")}
|
|
208
|
-
|
|
209
|
-
This produces: skill_tree.yaml in the artifacts directory.
|
|
210
|
-
For monorepos, each skill entry should include a \`package\` field.
|
|
211
|
-
|
|
212
|
-
**STOP. Review outputs with the maintainer before continuing.**
|
|
213
|
-
|
|
214
|
-
---
|
|
215
|
-
|
|
216
|
-
## Step 3 — Generate Skills
|
|
217
|
-
|
|
218
|
-
Load and follow: ${metaSkillPath("generate-skill")}
|
|
219
|
-
|
|
220
|
-
This produces: individual SKILL.md files.
|
|
221
|
-
- Single-repo: skills/<domain>/<skill>/SKILL.md
|
|
222
|
-
- Monorepo: packages/<pkg>/skills/<domain>/<skill>/SKILL.md
|
|
223
|
-
|
|
224
|
-
---
|
|
225
|
-
|
|
226
|
-
## After all skills are generated
|
|
227
|
-
|
|
228
|
-
1. Run \`intent validate\` in each package directory
|
|
229
|
-
2. Commit skills/ and artifacts
|
|
230
|
-
3. For each publishable package, run: \`npx @tanstack/intent edit-package-json\`
|
|
231
|
-
4. Ensure each package has \`@tanstack/intent\` as a devDependency
|
|
232
|
-
5. Create a \`skill:<skill-name>\` label on the GitHub repo for each skill (use \`gh label create\`)
|
|
233
|
-
6. Add a README note: "If you use an AI agent, run \`npx @tanstack/intent@latest install\`"
|
|
234
|
-
`;
|
|
235
|
-
console.log(prompt);
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
//#endregion
|
|
239
|
-
//#region src/commands/setup-github-actions.ts
|
|
240
|
-
async function runSetupGithubActionsCommand(root, metaDir) {
|
|
241
|
-
const { runSetupGithubActions } = await import("./setup.mjs");
|
|
242
|
-
runSetupGithubActions(root, metaDir);
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
//#endregion
|
|
246
|
-
//#region src/commands/stale.ts
|
|
247
|
-
async function runStaleCommand(targetDir, options, resolveStaleTargets$1) {
|
|
248
|
-
if (options.githubReview) {
|
|
249
|
-
await runGithubReview(targetDir, options, resolveStaleTargets$1);
|
|
250
|
-
return;
|
|
251
|
-
}
|
|
252
|
-
const { reports, workflowAdvisories = [] } = await resolveStaleTargets$1(targetDir);
|
|
253
|
-
if (options.json) {
|
|
254
|
-
console.log(JSON.stringify(reports, null, 2));
|
|
255
|
-
return;
|
|
256
|
-
}
|
|
257
|
-
for (const advisory of workflowAdvisories) console.log(advisory);
|
|
258
|
-
if (workflowAdvisories.length > 0) console.log();
|
|
259
|
-
if (reports.length === 0) {
|
|
260
|
-
console.log("No intent-enabled packages found.");
|
|
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
|
-
const signals = report.signals.filter((signal) => signal.needsReview);
|
|
269
|
-
if (stale.length === 0 && signals.length === 0) console.log(" All skills up-to-date");
|
|
270
|
-
else {
|
|
271
|
-
for (const skill of stale) console.log(` ⚠ ${skill.name}: ${skill.reasons.join(", ")}`);
|
|
272
|
-
for (const signal of signals) {
|
|
273
|
-
const subject = signal.packageName ?? signal.packageRoot ?? signal.skill ?? signal.artifactPath ?? signal.subject ?? report.library;
|
|
274
|
-
console.log(` ⚠ ${subject}: ${signal.reasons.join(", ")}`);
|
|
275
|
-
}
|
|
276
|
-
}
|
|
277
|
-
console.log();
|
|
278
|
-
}
|
|
279
|
-
}
|
|
280
|
-
async function runGithubReview(targetDir, options, resolveStaleTargets$1) {
|
|
281
|
-
const { collectStaleReviewItems, createFailedStaleReviewItem, createWorkflowAdvisoryReviewItems, writeStaleReviewWorkflowFiles } = await import("./workflow-review-Dz_ofcYQ.mjs");
|
|
282
|
-
const packageLabel = options.packageLabel ?? "workspace";
|
|
283
|
-
try {
|
|
284
|
-
const { reports, workflowAdvisories = [] } = await resolveStaleTargets$1(targetDir);
|
|
285
|
-
const items = [...collectStaleReviewItems(reports), ...createWorkflowAdvisoryReviewItems(packageLabel, workflowAdvisories)];
|
|
286
|
-
writeStaleReviewWorkflowFiles(items);
|
|
287
|
-
if (items.length === 0) console.log("No stale skills or coverage gaps found.");
|
|
288
|
-
else console.log(`Wrote ${items.length} intent skill review item(s).`);
|
|
289
|
-
} catch (err) {
|
|
290
|
-
writeStaleReviewWorkflowFiles([createFailedStaleReviewItem(packageLabel)]);
|
|
291
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
292
|
-
console.log(`Intent stale check failed: ${message}`);
|
|
293
|
-
console.log("Wrote a review PR body so maintainers can inspect the logs.");
|
|
294
|
-
}
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
//#endregion
|
|
298
|
-
//#region src/commands/validate.ts
|
|
299
|
-
const agentSkillNamePattern = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
300
|
-
function buildValidationFailure(errors, warnings) {
|
|
301
|
-
const lines = [
|
|
302
|
-
"",
|
|
303
|
-
`❌ Validation failed with ${errors.length} error(s):`,
|
|
304
|
-
""
|
|
305
|
-
];
|
|
306
|
-
for (const { file, message } of errors) lines.push(` ${file}: ${message}`);
|
|
307
|
-
if (warnings.length > 0) {
|
|
308
|
-
lines.push("", "⚠ Packaging warnings:");
|
|
309
|
-
for (const warning of warnings) lines.push(` ${warning}`);
|
|
310
|
-
}
|
|
311
|
-
return lines.join("\n");
|
|
312
|
-
}
|
|
313
|
-
function collectPackagingWarnings(context) {
|
|
314
|
-
if (!context.packageRoot || !context.targetPackageJsonPath) return [];
|
|
315
|
-
const pkgJsonPath = context.targetPackageJsonPath;
|
|
316
|
-
if (!existsSync(pkgJsonPath)) return [];
|
|
317
|
-
let pkgJson;
|
|
318
|
-
try {
|
|
319
|
-
pkgJson = JSON.parse(readFileSync(pkgJsonPath, "utf8"));
|
|
320
|
-
} catch (err) {
|
|
321
|
-
return [`Could not parse package.json: ${err instanceof Error ? err.message : String(err)}`];
|
|
322
|
-
}
|
|
323
|
-
const warnings = [];
|
|
324
|
-
if (!pkgJson.devDependencies?.["@tanstack/intent"]) warnings.push("@tanstack/intent is not in devDependencies");
|
|
325
|
-
const keywords = pkgJson.keywords;
|
|
326
|
-
if (!Array.isArray(keywords) || !keywords.includes("tanstack-intent")) warnings.push("Missing \"tanstack-intent\" in keywords array");
|
|
327
|
-
const files = pkgJson.files;
|
|
328
|
-
if (Array.isArray(files)) {
|
|
329
|
-
if (!files.includes("skills")) warnings.push("\"skills\" is not in the \"files\" array — skills won't be published");
|
|
330
|
-
if (!context.isMonorepo && !files.includes("!skills/_artifacts")) warnings.push("\"!skills/_artifacts\" is not in the \"files\" array — artifacts will be published unnecessarily");
|
|
331
|
-
}
|
|
332
|
-
return warnings;
|
|
333
|
-
}
|
|
334
|
-
function formatWarning({ file, message }) {
|
|
335
|
-
return `${file}: ${message}`;
|
|
336
|
-
}
|
|
337
|
-
function isRecord(value) {
|
|
338
|
-
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
339
|
-
}
|
|
340
|
-
function collectAgentSkillSpecWarnings({ filePath, fm, rel }) {
|
|
341
|
-
const warnings = [];
|
|
342
|
-
if (typeof fm.name === "string") {
|
|
343
|
-
if (fm.name.length > 64) warnings.push({
|
|
344
|
-
file: rel,
|
|
345
|
-
message: `Agent Skills spec warning: name exceeds 64 characters (${fm.name.length} chars)`
|
|
346
|
-
});
|
|
347
|
-
for (const segment of fm.name.split("/")) if (!agentSkillNamePattern.test(segment)) {
|
|
348
|
-
warnings.push({
|
|
349
|
-
file: rel,
|
|
350
|
-
message: "Agent Skills spec warning: each name segment should use lowercase letters, numbers, and single hyphens only"
|
|
351
|
-
});
|
|
352
|
-
break;
|
|
353
|
-
}
|
|
354
|
-
const parentDir = basename(dirname(filePath));
|
|
355
|
-
if (!fm.name.includes("/") && fm.name !== parentDir) warnings.push({
|
|
356
|
-
file: rel,
|
|
357
|
-
message: "Agent Skills spec warning: name should match the parent directory name"
|
|
358
|
-
});
|
|
359
|
-
}
|
|
360
|
-
if (fm.license !== void 0 && (typeof fm.license !== "string" || fm.license.trim().length === 0)) warnings.push({
|
|
361
|
-
file: rel,
|
|
362
|
-
message: "Agent Skills spec warning: license should be a non-empty string"
|
|
363
|
-
});
|
|
364
|
-
if (fm.compatibility !== void 0) {
|
|
365
|
-
if (typeof fm.compatibility !== "string" || fm.compatibility.trim().length === 0) warnings.push({
|
|
366
|
-
file: rel,
|
|
367
|
-
message: "Agent Skills spec warning: compatibility should be a non-empty string"
|
|
368
|
-
});
|
|
369
|
-
else if (fm.compatibility.length > 500) warnings.push({
|
|
370
|
-
file: rel,
|
|
371
|
-
message: `Agent Skills spec warning: compatibility exceeds 500 characters (${fm.compatibility.length} chars)`
|
|
372
|
-
});
|
|
373
|
-
}
|
|
374
|
-
if (fm.metadata !== void 0) {
|
|
375
|
-
if (!isRecord(fm.metadata)) warnings.push({
|
|
376
|
-
file: rel,
|
|
377
|
-
message: "Agent Skills spec warning: metadata should be a mapping"
|
|
378
|
-
});
|
|
379
|
-
else if (Object.values(fm.metadata).some((value) => typeof value !== "string")) warnings.push({
|
|
380
|
-
file: rel,
|
|
381
|
-
message: "Agent Skills spec warning: metadata values should be strings"
|
|
382
|
-
});
|
|
383
|
-
}
|
|
384
|
-
if (fm["allowed-tools"] !== void 0 && typeof fm["allowed-tools"] !== "string") warnings.push({
|
|
385
|
-
file: rel,
|
|
386
|
-
message: "Agent Skills spec warning: allowed-tools should be a space-separated string"
|
|
387
|
-
});
|
|
388
|
-
return warnings;
|
|
389
|
-
}
|
|
390
|
-
async function runValidateCommand(dir, options = {}) {
|
|
391
|
-
if (!options.githubSummary) {
|
|
392
|
-
await runValidateCommandInternal(dir);
|
|
393
|
-
return;
|
|
394
|
-
}
|
|
395
|
-
try {
|
|
396
|
-
await runValidateCommandInternal(dir);
|
|
397
|
-
writeGithubValidationSummary({ ok: true });
|
|
398
|
-
} catch (err) {
|
|
399
|
-
writeGithubValidationSummary({
|
|
400
|
-
ok: false,
|
|
401
|
-
message: validationErrorMessage(err)
|
|
402
|
-
});
|
|
403
|
-
throw err;
|
|
404
|
-
}
|
|
405
|
-
}
|
|
406
|
-
async function runValidateCommandInternal(dir) {
|
|
407
|
-
const [{ parse: parseYaml }, { findSkillFiles }] = await Promise.all([import("yaml"), import("./utils-dkVvY7D7.mjs")]);
|
|
408
|
-
const context = resolveProjectContext({
|
|
409
|
-
cwd: process.cwd(),
|
|
410
|
-
targetPath: dir
|
|
411
|
-
});
|
|
412
|
-
const explicitDir = dir !== void 0;
|
|
413
|
-
const skillsDirs = explicitDir ? [context.targetSkillsDir ?? resolve(process.cwd(), dir)] : collectDefaultSkillsDirs(context, findSkillFiles);
|
|
414
|
-
if (explicitDir && !existsSync(skillsDirs[0])) fail(`Skills directory not found: ${skillsDirs[0]}`);
|
|
415
|
-
const errors = [];
|
|
416
|
-
const warnings = [];
|
|
417
|
-
let validatedCount = 0;
|
|
418
|
-
if (explicitDir && findSkillFiles(skillsDirs[0]).length === 0) fail("No SKILL.md files found");
|
|
419
|
-
if (skillsDirs.length === 0) {
|
|
420
|
-
console.log("No skills/ directory found — skipping validation.");
|
|
421
|
-
return;
|
|
422
|
-
}
|
|
423
|
-
for (const skillsDir of skillsDirs) {
|
|
424
|
-
const skillFiles = findSkillFiles(skillsDir);
|
|
425
|
-
const validateContext = resolveProjectContext({
|
|
426
|
-
cwd: process.cwd(),
|
|
427
|
-
targetPath: skillsDir
|
|
428
|
-
});
|
|
429
|
-
for (const filePath of skillFiles) {
|
|
430
|
-
const rel = relative(process.cwd(), filePath);
|
|
431
|
-
const content = readFileSync(filePath, "utf8");
|
|
432
|
-
const match = content.match(/^---\r?\n([\s\S]*?)\r?\n---\r?\n([\s\S]*)/);
|
|
433
|
-
if (!match) {
|
|
434
|
-
errors.push({
|
|
435
|
-
file: rel,
|
|
436
|
-
message: "Missing or invalid frontmatter"
|
|
437
|
-
});
|
|
438
|
-
continue;
|
|
439
|
-
}
|
|
440
|
-
if (!match[1]) {
|
|
441
|
-
errors.push({
|
|
442
|
-
file: rel,
|
|
443
|
-
message: "Missing YAML frontmatter"
|
|
444
|
-
});
|
|
445
|
-
continue;
|
|
446
|
-
}
|
|
447
|
-
let fm;
|
|
448
|
-
try {
|
|
449
|
-
fm = parseYaml(match[1]);
|
|
450
|
-
} catch (err) {
|
|
451
|
-
const detail = err instanceof Error ? err.message : String(err);
|
|
452
|
-
errors.push({
|
|
453
|
-
file: rel,
|
|
454
|
-
message: `Invalid YAML frontmatter: ${detail}`
|
|
455
|
-
});
|
|
456
|
-
continue;
|
|
457
|
-
}
|
|
458
|
-
if (!fm.name) errors.push({
|
|
459
|
-
file: rel,
|
|
460
|
-
message: "Missing required field: name"
|
|
461
|
-
});
|
|
462
|
-
if (!fm.description) errors.push({
|
|
463
|
-
file: rel,
|
|
464
|
-
message: "Missing required field: description"
|
|
465
|
-
});
|
|
466
|
-
if (typeof fm.name === "string") {
|
|
467
|
-
const expectedPath = relative(skillsDir, filePath).replace(/[/\\]SKILL\.md$/, "").split(sep).join("/");
|
|
468
|
-
if (fm.name !== expectedPath) errors.push({
|
|
469
|
-
file: rel,
|
|
470
|
-
message: `name "${fm.name}" does not match directory path "${expectedPath}"`
|
|
471
|
-
});
|
|
472
|
-
}
|
|
473
|
-
if (typeof fm.description === "string" && fm.description.length > 1024) errors.push({
|
|
474
|
-
file: rel,
|
|
475
|
-
message: `Description exceeds 1024 character limit (${fm.description.length} chars)`
|
|
476
|
-
});
|
|
477
|
-
if (fm.type === "framework" && !Array.isArray(fm.requires)) errors.push({
|
|
478
|
-
file: rel,
|
|
479
|
-
message: "Framework skills must have a \"requires\" field"
|
|
480
|
-
});
|
|
481
|
-
warnings.push(...collectAgentSkillSpecWarnings({
|
|
482
|
-
filePath,
|
|
483
|
-
fm,
|
|
484
|
-
rel
|
|
485
|
-
}).map(formatWarning));
|
|
486
|
-
const lineCount = content.split(/\r?\n/).length;
|
|
487
|
-
if (lineCount > 500) errors.push({
|
|
488
|
-
file: rel,
|
|
489
|
-
message: `Exceeds 500 line limit (${lineCount} lines). Rewrite for conciseness: move API tables to references/, trim verbose examples, and remove content an agent already knows. Do not simply raise the limit.`
|
|
490
|
-
});
|
|
491
|
-
}
|
|
492
|
-
const artifactsDir = join(skillsDir, "_artifacts");
|
|
493
|
-
if (!validateContext.isMonorepo && existsSync(artifactsDir)) for (const fileName of [
|
|
494
|
-
"domain_map.yaml",
|
|
495
|
-
"skill_spec.md",
|
|
496
|
-
"skill_tree.yaml"
|
|
497
|
-
]) {
|
|
498
|
-
const artifactPath = join(artifactsDir, fileName);
|
|
499
|
-
if (!existsSync(artifactPath)) {
|
|
500
|
-
errors.push({
|
|
501
|
-
file: relative(process.cwd(), artifactPath),
|
|
502
|
-
message: "Missing required artifact"
|
|
503
|
-
});
|
|
504
|
-
continue;
|
|
505
|
-
}
|
|
506
|
-
const content = readFileSync(artifactPath, "utf8");
|
|
507
|
-
if (content.trim().length === 0) {
|
|
508
|
-
errors.push({
|
|
509
|
-
file: relative(process.cwd(), artifactPath),
|
|
510
|
-
message: "Artifact file is empty"
|
|
511
|
-
});
|
|
512
|
-
continue;
|
|
513
|
-
}
|
|
514
|
-
if (fileName.endsWith(".yaml")) try {
|
|
515
|
-
parseYaml(content);
|
|
516
|
-
} catch (err) {
|
|
517
|
-
const detail = err instanceof Error ? err.message : String(err);
|
|
518
|
-
errors.push({
|
|
519
|
-
file: relative(process.cwd(), artifactPath),
|
|
520
|
-
message: `Invalid YAML in artifact file: ${detail}`
|
|
521
|
-
});
|
|
522
|
-
}
|
|
523
|
-
}
|
|
524
|
-
validatedCount += skillFiles.length;
|
|
525
|
-
warnings.push(...collectPackagingWarnings(validateContext));
|
|
526
|
-
}
|
|
527
|
-
if (errors.length > 0) fail(buildValidationFailure(errors, warnings));
|
|
528
|
-
console.log(`✅ Validated ${validatedCount} skill files — all passed`);
|
|
529
|
-
if (warnings.length > 0) console.log();
|
|
530
|
-
printWarnings(warnings);
|
|
531
|
-
}
|
|
532
|
-
function validationErrorMessage(err) {
|
|
533
|
-
if (isCliFailure(err)) return err.message;
|
|
534
|
-
if (err instanceof Error) return err.message;
|
|
535
|
-
return String(err);
|
|
536
|
-
}
|
|
537
|
-
function writeGithubValidationSummary({ message, ok }) {
|
|
538
|
-
const summaryPath = process.env.GITHUB_STEP_SUMMARY;
|
|
539
|
-
if (!summaryPath) return;
|
|
540
|
-
const lines = ["### Intent skill validation", ""];
|
|
541
|
-
if (ok) lines.push("Skill validation passed.", "");
|
|
542
|
-
else lines.push("Skill validation failed.", "", "Why this failed:", "", "Intent validates SKILL.md frontmatter, skill names, required fields, size limits, framework requirements, and artifact files.", "The command output below contains the exact file-level reasons to fix.", "", "Run locally:", "", "```bash", "npx @tanstack/intent@latest validate", "```", "", "Command output:", "", "```text", message ?? "Unknown validation error.", "```", "");
|
|
543
|
-
appendFileSync(summaryPath, lines.join("\n"));
|
|
544
|
-
}
|
|
545
|
-
function collectDefaultSkillsDirs(context, findSkillFiles) {
|
|
546
|
-
const skillsDirs = [];
|
|
547
|
-
const addSkillsDir = (skillsDir) => {
|
|
548
|
-
if (existsSync(skillsDir) && findSkillFiles(skillsDir).length > 0) skillsDirs.push(skillsDir);
|
|
549
|
-
};
|
|
550
|
-
if (context.workspaceRoot && context.cwd === context.workspaceRoot) {
|
|
551
|
-
addSkillsDir(join(context.workspaceRoot, "skills"));
|
|
552
|
-
for (const packageDir of findWorkspacePackages(context.workspaceRoot)) addSkillsDir(join(packageDir, "skills"));
|
|
553
|
-
return [...new Set(skillsDirs)].sort((a, b) => a.localeCompare(b));
|
|
554
|
-
}
|
|
555
|
-
addSkillsDir(context.targetSkillsDir ?? (context.packageRoot ? join(context.packageRoot, "skills") : resolve(context.cwd, "skills")));
|
|
556
|
-
return skillsDirs;
|
|
557
|
-
}
|
|
558
|
-
|
|
559
|
-
//#endregion
|
|
560
7
|
//#region src/cli.ts
|
|
561
8
|
function createCli() {
|
|
562
9
|
const cli = cac("intent");
|
|
563
10
|
cli.usage("<command> [options]");
|
|
564
|
-
cli.command("list", "Discover intent-enabled packages from the project or workspace").usage("list [--json] [--global] [--global-only]").option("--json", "Output JSON").option("--global", "Include global packages after project packages").option("--global-only", "List global packages only").example("list").example("list --json").example("list --global").action(async (options) => {
|
|
565
|
-
await
|
|
11
|
+
cli.command("list", "Discover intent-enabled packages from the project or workspace").usage("list [--json] [--debug] [--exclude <pattern>] [--global] [--global-only]").option("--json", "Output JSON").option("--debug", "Print discovery debug details to stderr").option("--exclude <pattern>", "Exclude package name glob").option("--global", "Include global packages after project packages").option("--global-only", "List global packages only").example("list").example("list --json").example("list --global").action(async (options) => {
|
|
12
|
+
const { runListCommand } = await import("./list-D6osUZl7.mjs");
|
|
13
|
+
await runListCommand(options);
|
|
566
14
|
});
|
|
567
|
-
cli.command("load [use]", "Load a compact skill use and print its SKILL.md").usage("load <use> [--path] [--json] [--global] [--global-only]").option("--path", "Print the resolved skill path instead of file content").option("--json", "Output JSON").option("--global", "Load from project packages, then global packages").option("--global-only", "Load from global packages only").example("load @tanstack/query#core").example("load @tanstack/query#core --path").action(async (use, options) => {
|
|
568
|
-
|
|
15
|
+
cli.command("load [use]", "Load a compact skill use and print its SKILL.md").usage("load <use> [--path] [--json] [--debug] [--exclude <pattern>] [--global] [--global-only]").option("--path", "Print the resolved skill path instead of file content").option("--json", "Output JSON").option("--debug", "Print resolution debug details to stderr").option("--exclude <pattern>", "Exclude package name glob").option("--global", "Load from project packages, then global packages").option("--global-only", "Load from global packages only").example("load @tanstack/query#core").example("load @tanstack/query#core --path").action(async (use, options) => {
|
|
16
|
+
const { runLoadCommand } = await import("./load-CzvLUFur.mjs");
|
|
17
|
+
await runLoadCommand(use, options);
|
|
569
18
|
});
|
|
570
19
|
cli.command("meta [name]", "List meta-skills, or print one by name").usage("meta [name]").example("meta").example("meta domain-discovery").action(async (name) => {
|
|
20
|
+
const [{ getMetaDir }, { runMetaCommand }] = await Promise.all([import("./cli-support-CHEYZ9xj.mjs"), import("./meta-CYV9EzM8.mjs")]);
|
|
571
21
|
await runMetaCommand(name, getMetaDir());
|
|
572
22
|
});
|
|
573
23
|
cli.command("validate [dir]", "Validate skill files").usage("validate [dir] [--github-summary]").option("--github-summary", "Write a GitHub Actions step summary").example("validate").example("validate packages/query/skills").action(async (dir, options) => {
|
|
24
|
+
const { runValidateCommand } = await import("./validate-ugk2DZBd.mjs");
|
|
574
25
|
await runValidateCommand(dir, options);
|
|
575
26
|
});
|
|
576
27
|
cli.command("install", "Create or update skill loading guidance in an agent config file").usage("install [--map] [--dry-run] [--print-prompt] [--global] [--global-only]").option("--map", "Write explicit skill-to-task mappings").option("--dry-run", "Print the generated block without writing").option("--print-prompt", "Print the legacy agent setup prompt instead of writing").option("--global", "Include global packages after project packages").option("--global-only", "Install mappings from global packages only").example("install").example("install --map").example("install --dry-run").example("install --print-prompt").example("install --global").action(async (options) => {
|
|
28
|
+
const [{ scanIntentsOrFail }, { runInstallCommand }] = await Promise.all([import("./cli-support-CHEYZ9xj.mjs"), import("./install-BAUG1FD8.mjs")]);
|
|
577
29
|
await runInstallCommand(options, scanIntentsOrFail);
|
|
578
30
|
});
|
|
579
|
-
cli.command("scaffold", "Print maintainer scaffold prompt").usage("scaffold").action(() => {
|
|
31
|
+
cli.command("scaffold", "Print maintainer scaffold prompt").usage("scaffold").action(async () => {
|
|
32
|
+
const [{ getMetaDir }, { runScaffoldCommand }] = await Promise.all([import("./cli-support-CHEYZ9xj.mjs"), import("./scaffold-D2vwv9ls.mjs")]);
|
|
580
33
|
runScaffoldCommand(getMetaDir());
|
|
581
34
|
});
|
|
582
35
|
cli.command("stale [dir]", "Check skills for staleness in the current package or workspace").usage("stale [dir] [--json] [--github-review]").option("--json", "Output JSON").option("--github-review", "Write GitHub Actions review PR files").option("--package-label <label>", "Fallback package label for review PRs").example("stale").example("stale packages/query").example("stale --json").action(async (targetDir, options) => {
|
|
36
|
+
const [{ resolveStaleTargets }, { runStaleCommand }] = await Promise.all([import("./cli-support-CHEYZ9xj.mjs"), import("./stale-flPZnWfI.mjs")]);
|
|
583
37
|
await runStaleCommand(targetDir, options, resolveStaleTargets);
|
|
584
38
|
});
|
|
585
39
|
cli.command("edit-package-json", "Update package.json files so skills are published").usage("edit-package-json").action(async () => {
|
|
40
|
+
const { runEditPackageJsonCommand } = await import("./edit-package-json-CzWlMXOf.mjs");
|
|
586
41
|
await runEditPackageJsonCommand(process.cwd());
|
|
587
42
|
});
|
|
588
43
|
cli.command("setup", "Copy Intent CI workflow templates into .github/workflows/").usage("setup").action(async () => {
|
|
44
|
+
const [{ getMetaDir }, { runSetupGithubActionsCommand }] = await Promise.all([import("./cli-support-CHEYZ9xj.mjs"), import("./setup-github-actions-emXSyGy3.mjs")]);
|
|
589
45
|
await runSetupGithubActionsCommand(process.cwd(), getMetaDir());
|
|
590
46
|
});
|
|
591
47
|
cli.command("setup-github-actions", "Copy Intent CI workflow templates into .github/workflows/").usage("setup-github-actions").action(async () => {
|
|
48
|
+
const [{ getMetaDir }, { runSetupGithubActionsCommand }] = await Promise.all([import("./cli-support-CHEYZ9xj.mjs"), import("./setup-github-actions-emXSyGy3.mjs")]);
|
|
592
49
|
await runSetupGithubActionsCommand(process.cwd(), getMetaDir());
|
|
593
50
|
});
|
|
594
51
|
cli.command("help [command]", "Display help for a command").action((commandName) => {
|