@tanstack/intent 0.0.32 → 0.0.34
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -7
- package/dist/artifact-coverage-BAN2W6aH.mjs +3 -0
- package/dist/artifact-coverage-wLNVX8yC.mjs +128 -0
- package/dist/cli.mjs +248 -99
- package/dist/{display-DCRCp4-C.mjs → display-B3vkG99D.mjs} +1 -1
- package/dist/index.d.mts +22 -4
- package/dist/index.mjs +10 -8
- package/dist/{install-BmVqcbEi.mjs → install-QjryhQtg.mjs} +54 -18
- package/dist/intent-library.mjs +5 -5
- package/dist/library-scanner.d.mts +1 -1
- package/dist/library-scanner.mjs +1 -1
- package/dist/{project-context-CKG-q4rD.mjs → project-context-alYMNoNa.mjs} +1 -1
- package/dist/{resolver-aFigTqXi.mjs → resolver-Whd12ksO.mjs} +1 -1
- package/dist/scanner-BAZxWeUk.mjs +6 -0
- package/dist/{scanner-DKL8v8is.mjs → scanner-Dav1tzQK.mjs} +79 -4
- package/dist/{setup-JJvjiGkM.mjs → setup-Dp-W8y0Y.mjs} +2 -2
- package/dist/setup.d.mts +1 -1
- package/dist/setup.mjs +3 -3
- package/dist/staleness-DpbmYod4.mjs +5 -0
- package/dist/staleness-PdgakrCQ.mjs +243 -0
- package/dist/{types-CsySN6Vw.d.mts → types-DT7Y6TFz.d.mts} +48 -1
- package/dist/workflow-review-CwkPVIQf.mjs +153 -0
- package/dist/workflow-review-Dz_ofcYQ.mjs +3 -0
- package/dist/{workspace-patterns-U35B5AO-.mjs → workspace-patterns-BN2A_60g.mjs} +6 -1
- package/dist/workspace-patterns-x-dLZxx4.mjs +4 -0
- package/meta/templates/workflows/check-skills.yml +58 -96
- package/package.json +1 -1
- package/dist/scanner-CRZITpcY.mjs +0 -6
- package/dist/staleness-DorwfGrf.mjs +0 -104
- package/dist/staleness-NF-lxfXf.mjs +0 -4
- package/dist/workspace-patterns-DbnA0peB.mjs +0 -4
- package/meta/templates/workflows/notify-intent.yml +0 -51
- package/meta/templates/workflows/validate-skills.yml +0 -52
- /package/dist/{display-DUgtRJkt.mjs → display-CAof6doy.mjs} +0 -0
- /package/dist/{library-scanner-DFFreLjW.mjs → library-scanner-fexXlPXb.mjs} +0 -0
- /package/dist/{setup-DDoOLriA.d.mts → setup-t1i2o2-h.d.mts} +0 -0
- /package/dist/{skill-use-CXOnncWK.mjs → skill-use-BzuuvLM7.mjs} +0 -0
package/README.md
CHANGED
|
@@ -90,10 +90,10 @@ npx @tanstack/intent@latest stale
|
|
|
90
90
|
|
|
91
91
|
From a monorepo root, `intent stale` checks every workspace package that ships skills. To scope it to one package, pass a directory like `intent stale packages/router`.
|
|
92
92
|
|
|
93
|
-
Copy CI workflow templates into your repo so validation and staleness checks run
|
|
93
|
+
Copy CI workflow templates into your repo so validation and staleness checks can run in GitHub Actions:
|
|
94
94
|
|
|
95
95
|
```bash
|
|
96
|
-
npx @tanstack/intent@latest setup
|
|
96
|
+
npx @tanstack/intent@latest setup
|
|
97
97
|
```
|
|
98
98
|
|
|
99
99
|
## Compatibility
|
|
@@ -108,14 +108,14 @@ npx @tanstack/intent@latest setup-github-actions
|
|
|
108
108
|
|
|
109
109
|
## Monorepos
|
|
110
110
|
|
|
111
|
-
- Run `npx @tanstack/intent@latest setup
|
|
112
|
-
- Generated workflows are monorepo-aware: validation loops over workspace packages with skills, staleness checks run from the workspace root
|
|
111
|
+
- Run `npx @tanstack/intent@latest setup` from either the repo root or a package directory. Intent detects the workspace root and writes workflows to the repo-level `.github/workflows/` directory.
|
|
112
|
+
- Generated workflows are monorepo-aware: validation loops over workspace packages with skills, and staleness checks run from the workspace root.
|
|
113
113
|
- Run `npx @tanstack/intent@latest validate packages/<pkg>/skills` from the repo root to validate one package without root-level packaging warnings.
|
|
114
|
-
- Run `npx @tanstack/intent@latest stale` from the repo root to check
|
|
114
|
+
- Run `npx @tanstack/intent@latest stale` from the repo root to check workspace packages with skills and public workspace packages missing skill or `_artifacts` coverage, or `intent stale packages/<pkg>` to check one package.
|
|
115
115
|
|
|
116
116
|
## Keeping skills current
|
|
117
117
|
|
|
118
|
-
The real risk with any derived artifact is staleness. `npx @tanstack/intent@latest stale` flags skills whose source docs have changed, and CI templates catch drift before it ships.
|
|
118
|
+
The real risk with any derived artifact is staleness. `npx @tanstack/intent@latest stale` flags skills whose source docs have changed, generated skills that drift from `_artifacts`, and public workspace packages missing coverage. CI templates catch drift before it ships.
|
|
119
119
|
|
|
120
120
|
The feedback loop runs both directions. `npx @tanstack/intent@latest feedback` lets users submit structured reports when a skill produces wrong output — which skill, which version, what broke. That context flows back to the maintainer, and the fix ships to everyone on the next package update. Every support interaction produces an artifact that prevents the same class of problem for all future users — not just the one who reported it.
|
|
121
121
|
|
|
@@ -129,7 +129,7 @@ The feedback loop runs both directions. `npx @tanstack/intent@latest feedback` l
|
|
|
129
129
|
| `npx @tanstack/intent@latest meta` | List meta-skills for library maintainers |
|
|
130
130
|
| `npx @tanstack/intent@latest scaffold` | Print the guided skill generation prompt |
|
|
131
131
|
| `npx @tanstack/intent@latest validate [dir]` | Validate SKILL.md files |
|
|
132
|
-
| `npx @tanstack/intent@latest setup
|
|
132
|
+
| `npx @tanstack/intent@latest setup` | Copy CI templates into your repo |
|
|
133
133
|
| `npx @tanstack/intent@latest stale [dir] [--json]` | Check skills for version drift |
|
|
134
134
|
| `npx @tanstack/intent@latest feedback` | Submit skill feedback |
|
|
135
135
|
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { existsSync, readFileSync, readdirSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { parse } from "yaml";
|
|
4
|
+
|
|
5
|
+
//#region src/artifact-coverage.ts
|
|
6
|
+
function isRecord(value) {
|
|
7
|
+
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
8
|
+
}
|
|
9
|
+
function stringValue(value) {
|
|
10
|
+
return typeof value === "string" && value.trim() ? value : void 0;
|
|
11
|
+
}
|
|
12
|
+
function stringArray(value) {
|
|
13
|
+
if (!Array.isArray(value)) return [];
|
|
14
|
+
return value.filter((entry) => typeof entry === "string");
|
|
15
|
+
}
|
|
16
|
+
function detectArtifactKind(fileName) {
|
|
17
|
+
if (fileName.endsWith("skill_tree.yaml")) return "skill-tree";
|
|
18
|
+
if (fileName.endsWith("domain_map.yaml")) return "domain-map";
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
21
|
+
function readArtifactYaml(artifactPath, warnings) {
|
|
22
|
+
let parsed;
|
|
23
|
+
try {
|
|
24
|
+
parsed = parse(readFileSync(artifactPath, "utf8"));
|
|
25
|
+
} catch (err) {
|
|
26
|
+
warnings.push({
|
|
27
|
+
artifactPath,
|
|
28
|
+
message: `Invalid YAML: ${err instanceof Error ? err.message : String(err)}`
|
|
29
|
+
});
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
if (!isRecord(parsed)) {
|
|
33
|
+
warnings.push({
|
|
34
|
+
artifactPath,
|
|
35
|
+
message: "Artifact YAML must contain an object at the top level"
|
|
36
|
+
});
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
return parsed;
|
|
40
|
+
}
|
|
41
|
+
function parseArtifactFile(artifactPath, kind, parsed) {
|
|
42
|
+
const library = isRecord(parsed.library) ? parsed.library : {};
|
|
43
|
+
return {
|
|
44
|
+
path: artifactPath,
|
|
45
|
+
kind,
|
|
46
|
+
libraryName: stringValue(library.name),
|
|
47
|
+
libraryVersion: stringValue(library.version)
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
function parseSkills(artifactPath, kind, parsed) {
|
|
51
|
+
if (!Array.isArray(parsed.skills)) return [];
|
|
52
|
+
const skills = [];
|
|
53
|
+
for (const skill of parsed.skills) {
|
|
54
|
+
if (!isRecord(skill)) continue;
|
|
55
|
+
const packagePath = stringValue(skill.package);
|
|
56
|
+
const packages = stringArray(skill.packages);
|
|
57
|
+
if (packagePath) packages.push(packagePath);
|
|
58
|
+
skills.push({
|
|
59
|
+
artifactPath,
|
|
60
|
+
artifactKind: kind,
|
|
61
|
+
name: stringValue(skill.name),
|
|
62
|
+
slug: stringValue(skill.slug),
|
|
63
|
+
path: stringValue(skill.path),
|
|
64
|
+
package: packagePath,
|
|
65
|
+
packages: [...new Set(packages)].sort((a, b) => a.localeCompare(b)),
|
|
66
|
+
sources: stringArray(skill.sources),
|
|
67
|
+
covers: stringArray(skill.covers)
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
return skills;
|
|
71
|
+
}
|
|
72
|
+
function parseCoverageIgnores(artifactPath, parsed) {
|
|
73
|
+
const ignored = (isRecord(parsed.coverage) ? parsed.coverage : null)?.ignored_packages;
|
|
74
|
+
if (!Array.isArray(ignored)) return [];
|
|
75
|
+
const ignores = [];
|
|
76
|
+
for (const entry of ignored) {
|
|
77
|
+
if (typeof entry === "string") {
|
|
78
|
+
if (entry.trim()) ignores.push({
|
|
79
|
+
packageName: entry,
|
|
80
|
+
artifactPath
|
|
81
|
+
});
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
if (!isRecord(entry)) continue;
|
|
85
|
+
const packageName = stringValue(entry.name);
|
|
86
|
+
if (!packageName) continue;
|
|
87
|
+
ignores.push({
|
|
88
|
+
packageName,
|
|
89
|
+
reason: stringValue(entry.reason),
|
|
90
|
+
artifactPath
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
return ignores;
|
|
94
|
+
}
|
|
95
|
+
function readIntentArtifacts(root) {
|
|
96
|
+
const artifactsDir = join(root, "_artifacts");
|
|
97
|
+
if (!existsSync(artifactsDir)) return null;
|
|
98
|
+
const warnings = [];
|
|
99
|
+
const skillTrees = [];
|
|
100
|
+
const domainMaps = [];
|
|
101
|
+
const skills = [];
|
|
102
|
+
const ignoredPackages = [];
|
|
103
|
+
for (const entry of readdirSync(artifactsDir, { withFileTypes: true })) {
|
|
104
|
+
if (!entry.isFile()) continue;
|
|
105
|
+
const kind = detectArtifactKind(entry.name);
|
|
106
|
+
if (!kind) continue;
|
|
107
|
+
const artifactPath = join(artifactsDir, entry.name);
|
|
108
|
+
const parsed = readArtifactYaml(artifactPath, warnings);
|
|
109
|
+
if (!parsed) continue;
|
|
110
|
+
const artifactFile = parseArtifactFile(artifactPath, kind, parsed);
|
|
111
|
+
if (kind === "skill-tree") skillTrees.push(artifactFile);
|
|
112
|
+
else domainMaps.push(artifactFile);
|
|
113
|
+
skills.push(...parseSkills(artifactPath, kind, parsed));
|
|
114
|
+
ignoredPackages.push(...parseCoverageIgnores(artifactPath, parsed));
|
|
115
|
+
}
|
|
116
|
+
return {
|
|
117
|
+
root,
|
|
118
|
+
artifactsDir,
|
|
119
|
+
skillTrees: skillTrees.sort((a, b) => a.path.localeCompare(b.path)),
|
|
120
|
+
domainMaps: domainMaps.sort((a, b) => a.path.localeCompare(b.path)),
|
|
121
|
+
skills: skills.sort((a, b) => `${a.artifactPath}:${a.slug ?? a.name ?? ""}`.localeCompare(`${b.artifactPath}:${b.slug ?? b.name ?? ""}`)),
|
|
122
|
+
ignoredPackages: ignoredPackages.sort((a, b) => a.packageName.localeCompare(b.packageName)),
|
|
123
|
+
warnings
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
//#endregion
|
|
128
|
+
export { readIntentArtifacts as t };
|
package/dist/cli.mjs
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import "./utils-COlDcU72.mjs";
|
|
3
|
-
import "./workspace-patterns-
|
|
4
|
-
import { i as parseSkillUse } from "./skill-use-
|
|
5
|
-
import { r as resolveSkillUse } from "./resolver-
|
|
6
|
-
import { t as resolveProjectContext } from "./project-context-
|
|
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-
|
|
8
|
-
import { existsSync, readFileSync, readdirSync, realpathSync } from "node:fs";
|
|
9
|
-
import { isAbsolute, join, relative, resolve, sep } from "node:path";
|
|
3
|
+
import { n as findWorkspacePackages } from "./workspace-patterns-BN2A_60g.mjs";
|
|
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-QjryhQtg.mjs";
|
|
8
|
+
import { appendFileSync, existsSync, readFileSync, readdirSync, realpathSync } from "node:fs";
|
|
9
|
+
import { basename, dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
|
|
10
10
|
import { fileURLToPath } from "node:url";
|
|
11
11
|
import { cac } from "cac";
|
|
12
12
|
|
|
@@ -43,7 +43,7 @@ async function runListCommand(options, scanIntentsOrFail$1) {
|
|
|
43
43
|
console.log(JSON.stringify(result, null, 2));
|
|
44
44
|
return;
|
|
45
45
|
}
|
|
46
|
-
const { computeSkillNameWidth, printSkillTree, printTable } = await import("./display-
|
|
46
|
+
const { computeSkillNameWidth, printSkillTree, printTable } = await import("./display-B3vkG99D.mjs");
|
|
47
47
|
const scanCoverage = formatScanCoverage(result);
|
|
48
48
|
if (result.packages.length === 0) {
|
|
49
49
|
console.log("No intent-enabled packages found.");
|
|
@@ -245,11 +245,17 @@ async function runSetupGithubActionsCommand(root, metaDir) {
|
|
|
245
245
|
//#endregion
|
|
246
246
|
//#region src/commands/stale.ts
|
|
247
247
|
async function runStaleCommand(targetDir, options, resolveStaleTargets$1) {
|
|
248
|
-
|
|
248
|
+
if (options.githubReview) {
|
|
249
|
+
await runGithubReview(targetDir, options, resolveStaleTargets$1);
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
252
|
+
const { reports, workflowAdvisories = [] } = await resolveStaleTargets$1(targetDir);
|
|
249
253
|
if (options.json) {
|
|
250
254
|
console.log(JSON.stringify(reports, null, 2));
|
|
251
255
|
return;
|
|
252
256
|
}
|
|
257
|
+
for (const advisory of workflowAdvisories) console.log(advisory);
|
|
258
|
+
if (workflowAdvisories.length > 0) console.log();
|
|
253
259
|
if (reports.length === 0) {
|
|
254
260
|
console.log("No intent-enabled packages found.");
|
|
255
261
|
return;
|
|
@@ -259,14 +265,38 @@ async function runStaleCommand(targetDir, options, resolveStaleTargets$1) {
|
|
|
259
265
|
const vLabel = report.skillVersion && report.currentVersion ? ` (${report.skillVersion} → ${report.currentVersion})` : "";
|
|
260
266
|
console.log(`${report.library}${vLabel}${driftLabel}`);
|
|
261
267
|
const stale = report.skills.filter((skill) => skill.needsReview);
|
|
262
|
-
|
|
263
|
-
|
|
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
|
+
}
|
|
264
277
|
console.log();
|
|
265
278
|
}
|
|
266
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
|
+
}
|
|
267
296
|
|
|
268
297
|
//#endregion
|
|
269
298
|
//#region src/commands/validate.ts
|
|
299
|
+
const agentSkillNamePattern = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
270
300
|
function buildValidationFailure(errors, warnings) {
|
|
271
301
|
const lines = [
|
|
272
302
|
"",
|
|
@@ -301,114 +331,230 @@ function collectPackagingWarnings(context) {
|
|
|
301
331
|
}
|
|
302
332
|
return warnings;
|
|
303
333
|
}
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
const
|
|
312
|
-
if (
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
const
|
|
318
|
-
|
|
319
|
-
const match = content.match(/^---\r?\n([\s\S]*?)\r?\n---\r?\n([\s\S]*)/);
|
|
320
|
-
if (!match) {
|
|
321
|
-
errors.push({
|
|
322
|
-
file: rel,
|
|
323
|
-
message: "Missing or invalid frontmatter"
|
|
324
|
-
});
|
|
325
|
-
continue;
|
|
326
|
-
}
|
|
327
|
-
if (!match[1]) {
|
|
328
|
-
errors.push({
|
|
329
|
-
file: rel,
|
|
330
|
-
message: "Missing YAML frontmatter"
|
|
331
|
-
});
|
|
332
|
-
continue;
|
|
333
|
-
}
|
|
334
|
-
let fm;
|
|
335
|
-
try {
|
|
336
|
-
fm = parseYaml(match[1]);
|
|
337
|
-
} catch (err) {
|
|
338
|
-
const detail = err instanceof Error ? err.message : String(err);
|
|
339
|
-
errors.push({
|
|
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({
|
|
340
349
|
file: rel,
|
|
341
|
-
message:
|
|
350
|
+
message: "Agent Skills spec warning: each name segment should use lowercase letters, numbers, and single hyphens only"
|
|
342
351
|
});
|
|
343
|
-
|
|
352
|
+
break;
|
|
344
353
|
}
|
|
345
|
-
|
|
354
|
+
const parentDir = basename(dirname(filePath));
|
|
355
|
+
if (!fm.name.includes("/") && fm.name !== parentDir) warnings.push({
|
|
346
356
|
file: rel,
|
|
347
|
-
message: "
|
|
357
|
+
message: "Agent Skills spec warning: name should match the parent directory name"
|
|
348
358
|
});
|
|
349
|
-
|
|
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({
|
|
350
366
|
file: rel,
|
|
351
|
-
message: "
|
|
367
|
+
message: "Agent Skills spec warning: compatibility should be a non-empty string"
|
|
352
368
|
});
|
|
353
|
-
if (
|
|
354
|
-
const expectedPath = relative(skillsDir, filePath).replace(/[/\\]SKILL\.md$/, "").split(sep).join("/");
|
|
355
|
-
if (fm.name !== expectedPath) errors.push({
|
|
356
|
-
file: rel,
|
|
357
|
-
message: `name "${fm.name}" does not match directory path "${expectedPath}"`
|
|
358
|
-
});
|
|
359
|
-
}
|
|
360
|
-
if (typeof fm.description === "string" && fm.description.length > 1024) errors.push({
|
|
369
|
+
else if (fm.compatibility.length > 500) warnings.push({
|
|
361
370
|
file: rel,
|
|
362
|
-
message: `
|
|
371
|
+
message: `Agent Skills spec warning: compatibility exceeds 500 characters (${fm.compatibility.length} chars)`
|
|
363
372
|
});
|
|
364
|
-
|
|
373
|
+
}
|
|
374
|
+
if (fm.metadata !== void 0) {
|
|
375
|
+
if (!isRecord(fm.metadata)) warnings.push({
|
|
365
376
|
file: rel,
|
|
366
|
-
message: "
|
|
377
|
+
message: "Agent Skills spec warning: metadata should be a mapping"
|
|
367
378
|
});
|
|
368
|
-
|
|
369
|
-
if (lineCount > 500) errors.push({
|
|
379
|
+
else if (Object.values(fm.metadata).some((value) => typeof value !== "string")) warnings.push({
|
|
370
380
|
file: rel,
|
|
371
|
-
message:
|
|
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)
|
|
372
402
|
});
|
|
403
|
+
throw err;
|
|
373
404
|
}
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
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"
|
|
385
461
|
});
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
if (content.trim().length === 0) {
|
|
390
|
-
errors.push({
|
|
391
|
-
file: relative(process.cwd(), artifactPath),
|
|
392
|
-
message: "Artifact file is empty"
|
|
462
|
+
if (!fm.description) errors.push({
|
|
463
|
+
file: rel,
|
|
464
|
+
message: "Missing required field: description"
|
|
393
465
|
});
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
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"
|
|
403
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
|
+
}
|
|
404
523
|
}
|
|
524
|
+
validatedCount += skillFiles.length;
|
|
525
|
+
warnings.push(...collectPackagingWarnings(validateContext));
|
|
405
526
|
}
|
|
406
|
-
const warnings = collectPackagingWarnings(context);
|
|
407
527
|
if (errors.length > 0) fail(buildValidationFailure(errors, warnings));
|
|
408
|
-
console.log(`✅ Validated ${
|
|
528
|
+
console.log(`✅ Validated ${validatedCount} skill files — all passed`);
|
|
409
529
|
if (warnings.length > 0) console.log();
|
|
410
530
|
printWarnings(warnings);
|
|
411
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
|
+
}
|
|
412
558
|
|
|
413
559
|
//#endregion
|
|
414
560
|
//#region src/cli.ts
|
|
@@ -424,8 +570,8 @@ function createCli() {
|
|
|
424
570
|
cli.command("meta [name]", "List meta-skills, or print one by name").usage("meta [name]").example("meta").example("meta domain-discovery").action(async (name) => {
|
|
425
571
|
await runMetaCommand(name, getMetaDir());
|
|
426
572
|
});
|
|
427
|
-
cli.command("validate [dir]", "Validate skill files").usage("validate [dir]").example("validate").example("validate packages/query/skills").action(async (dir) => {
|
|
428
|
-
await runValidateCommand(dir);
|
|
573
|
+
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) => {
|
|
574
|
+
await runValidateCommand(dir, options);
|
|
429
575
|
});
|
|
430
576
|
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) => {
|
|
431
577
|
await runInstallCommand(options, scanIntentsOrFail);
|
|
@@ -433,12 +579,15 @@ function createCli() {
|
|
|
433
579
|
cli.command("scaffold", "Print maintainer scaffold prompt").usage("scaffold").action(() => {
|
|
434
580
|
runScaffoldCommand(getMetaDir());
|
|
435
581
|
});
|
|
436
|
-
cli.command("stale [dir]", "Check skills for staleness in the current package or workspace").usage("stale [dir] [--json]").option("--json", "Output JSON").example("stale").example("stale packages/query").example("stale --json").action(async (targetDir, options) => {
|
|
582
|
+
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) => {
|
|
437
583
|
await runStaleCommand(targetDir, options, resolveStaleTargets);
|
|
438
584
|
});
|
|
439
585
|
cli.command("edit-package-json", "Update package.json files so skills are published").usage("edit-package-json").action(async () => {
|
|
440
586
|
await runEditPackageJsonCommand(process.cwd());
|
|
441
587
|
});
|
|
588
|
+
cli.command("setup", "Copy Intent CI workflow templates into .github/workflows/").usage("setup").action(async () => {
|
|
589
|
+
await runSetupGithubActionsCommand(process.cwd(), getMetaDir());
|
|
590
|
+
});
|
|
442
591
|
cli.command("setup-github-actions", "Copy Intent CI workflow templates into .github/workflows/").usage("setup-github-actions").action(async () => {
|
|
443
592
|
await runSetupGithubActionsCommand(process.cwd(), getMetaDir());
|
|
444
593
|
});
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import "./utils-COlDcU72.mjs";
|
|
2
2
|
import "./skill-paths-8k9K9y26.mjs";
|
|
3
|
-
import { n as printSkillTree, r as printTable, t as computeSkillNameWidth } from "./display-
|
|
3
|
+
import { n as printSkillTree, r as printTable, t as computeSkillNameWidth } from "./display-CAof6doy.mjs";
|
|
4
4
|
|
|
5
5
|
export { computeSkillNameWidth, printSkillTree, printTable };
|
package/dist/index.d.mts
CHANGED
|
@@ -1,11 +1,29 @@
|
|
|
1
|
-
import { a as
|
|
2
|
-
import { i as runEditPackageJson, o as runSetupGithubActions, r as SetupGithubActionsResult, t as EditPackageJsonResult } from "./setup-
|
|
1
|
+
import { _ as StalenessReport, a as IntentArtifactSet, c as IntentConfig, d as MetaFeedbackPayload, f as MetaSkillName, g as SkillStaleness, h as SkillEntry, i as IntentArtifactFile, l as IntentPackage, m as ScanResult, n as FeedbackPayload, o as IntentArtifactSkill, p as ScanOptions, r as IntentArtifactCoverageIgnore, s as IntentArtifactWarning, t as AgentName, u as IntentProjectConfig, v as StalenessSignal, y as VersionConflict } from "./types-DT7Y6TFz.mjs";
|
|
2
|
+
import { i as runEditPackageJson, o as runSetupGithubActions, r as SetupGithubActionsResult, t as EditPackageJsonResult } from "./setup-t1i2o2-h.mjs";
|
|
3
3
|
|
|
4
4
|
//#region src/scanner.d.ts
|
|
5
5
|
declare function scanForIntents(root?: string, options?: ScanOptions): ScanResult;
|
|
6
6
|
//#endregion
|
|
7
7
|
//#region src/staleness.d.ts
|
|
8
|
-
declare function checkStaleness(packageDir: string, packageName?: string): Promise<StalenessReport>;
|
|
8
|
+
declare function checkStaleness(packageDir: string, packageName?: string, artifactRoot?: string): Promise<StalenessReport>;
|
|
9
|
+
//#endregion
|
|
10
|
+
//#region src/artifact-coverage.d.ts
|
|
11
|
+
declare function readIntentArtifacts(root: string): IntentArtifactSet | null;
|
|
12
|
+
//#endregion
|
|
13
|
+
//#region src/workflow-review.d.ts
|
|
14
|
+
interface StaleReviewItem {
|
|
15
|
+
type: string;
|
|
16
|
+
library: string;
|
|
17
|
+
subject: string;
|
|
18
|
+
reasons: Array<string>;
|
|
19
|
+
artifactPath?: string;
|
|
20
|
+
packageName?: string;
|
|
21
|
+
packageRoot?: string;
|
|
22
|
+
skill?: string;
|
|
23
|
+
}
|
|
24
|
+
declare function collectStaleReviewItems(reports: Array<StalenessReport>): Array<StaleReviewItem>;
|
|
25
|
+
declare function createFailedStaleReviewItem(library: string): StaleReviewItem;
|
|
26
|
+
declare function buildStaleReviewBody(items: Array<StaleReviewItem>): string;
|
|
9
27
|
//#endregion
|
|
10
28
|
//#region src/feedback.d.ts
|
|
11
29
|
declare function containsSecrets(text: string): boolean;
|
|
@@ -108,4 +126,4 @@ declare class ResolveSkillUseError extends Error {
|
|
|
108
126
|
declare function isResolveSkillUseError(error: unknown): error is ResolveSkillUseError;
|
|
109
127
|
declare function resolveSkillUse(use: string, scanResult: ScanResult): ResolveSkillResult;
|
|
110
128
|
//#endregion
|
|
111
|
-
export { type AgentName, type EditPackageJsonResult, type FeedbackPayload, type IntentConfig, type IntentPackage, type IntentProjectConfig, type MetaFeedbackPayload, type MetaSkillName, type ResolveSkillResult, ResolveSkillUseError, type ResolveSkillUseErrorCode, type ScanOptions, type ScanResult, type SetupGithubActionsResult, type SkillEntry, type SkillStaleness, type SkillUse, SkillUseParseError, type SkillUseParseErrorCode, type StalenessReport, checkStaleness, containsSecrets, findSkillFiles, formatSkillUse, getDeps, hasGhCli, isResolveSkillUseError, isSkillUseParseError, metaToMarkdown, parseFrontmatter, parseSkillUse, resolveDepDir, resolveFrequency, resolveSkillUse, runEditPackageJson, runSetupGithubActions, scanForIntents, submitFeedback, submitMetaFeedback, toMarkdown, validateMetaPayload, validatePayload };
|
|
129
|
+
export { type AgentName, type EditPackageJsonResult, type FeedbackPayload, type IntentArtifactCoverageIgnore, type IntentArtifactFile, type IntentArtifactSet, type IntentArtifactSkill, type IntentArtifactWarning, type IntentConfig, type IntentPackage, type IntentProjectConfig, type MetaFeedbackPayload, type MetaSkillName, type ResolveSkillResult, ResolveSkillUseError, type ResolveSkillUseErrorCode, type ScanOptions, type ScanResult, type SetupGithubActionsResult, type SkillEntry, type SkillStaleness, type SkillUse, SkillUseParseError, type SkillUseParseErrorCode, type StaleReviewItem, type StalenessReport, type StalenessSignal, buildStaleReviewBody, checkStaleness, collectStaleReviewItems, containsSecrets, createFailedStaleReviewItem, findSkillFiles, formatSkillUse, getDeps, hasGhCli, isResolveSkillUseError, isSkillUseParseError, metaToMarkdown, parseFrontmatter, parseSkillUse, readIntentArtifacts, resolveDepDir, resolveFrequency, resolveSkillUse, runEditPackageJson, runSetupGithubActions, scanForIntents, submitFeedback, submitMetaFeedback, toMarkdown, validateMetaPayload, validatePayload };
|