@tanstack/intent 0.0.36 → 0.0.38
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-BON5hn4L.mjs +114 -0
- package/dist/cli-support-BlSsqyx7.mjs +6 -0
- package/dist/cli.mjs +17 -763
- package/dist/core-baYs6H7Z.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-2_wkomiT.mjs → install-BPWtaOuo.mjs} +7 -111
- package/dist/install-RZpz279M.mjs +7 -0
- 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-iLIL-EO3.mjs +107 -0
- package/dist/load-8868G50-.mjs +70 -0
- package/dist/meta-CYV9EzM8.mjs +38 -0
- package/dist/{project-context-alYMNoNa.mjs → project-context-Dx6XIJJo.mjs} +1 -1
- package/dist/resolver-CP0S8UjZ.mjs +196 -0
- package/dist/scaffold-D2vwv9ls.mjs +75 -0
- package/dist/scanner-ByG70-E3.mjs +6 -0
- package/dist/{scanner-B1j-wDhj.mjs → scanner-DwYsjXzI.mjs} +101 -19
- package/dist/{setup-t1i2o2-h.d.mts → setup-2SE9zYJk.d.mts} +1 -0
- package/dist/setup-github-actions-emXSyGy3.mjs +8 -0
- package/dist/{setup-Dp-W8y0Y.mjs → setup-kvWN4xCr.mjs} +2 -2
- 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-BL1SYAwh.mjs +271 -0
- package/dist/{workflow-review-Dz_ofcYQ.mjs → workflow-review-CIdJXmKP.mjs} +1 -1
- package/dist/workspace-patterns-BWMz7TsI.mjs +4 -0
- package/dist/workspace-patterns-BZLEaMcS.mjs +195 -0
- package/package.json +9 -4
- 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/workspace-patterns-BN2A_60g.mjs +0 -172
- package/dist/workspace-patterns-x-dLZxx4.mjs +0 -4
- /package/dist/{artifact-coverage-wLNVX8yC.mjs → artifact-coverage-DgWuVqUp.mjs} +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
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { join } from "node:path";
|
|
2
|
+
|
|
3
|
+
//#region src/commands/scaffold.ts
|
|
4
|
+
function runScaffoldCommand(metaDir) {
|
|
5
|
+
function metaSkillPath(name) {
|
|
6
|
+
return join(metaDir, name, "SKILL.md");
|
|
7
|
+
}
|
|
8
|
+
const prompt = `You are helping a library maintainer scaffold Intent skills.
|
|
9
|
+
|
|
10
|
+
Run the three meta skills below **one at a time, in order**. For each step:
|
|
11
|
+
1. Load the SKILL.md file specified
|
|
12
|
+
2. Follow its instructions completely
|
|
13
|
+
3. Present outputs to the maintainer for review
|
|
14
|
+
4. Do NOT proceed to the next step until the maintainer confirms
|
|
15
|
+
|
|
16
|
+
## Before you start
|
|
17
|
+
|
|
18
|
+
Gather this context yourself (do not ask the maintainer — agents should never
|
|
19
|
+
ask for information they can discover):
|
|
20
|
+
1. Read package.json for library name, repository URL, and homepage/docs URL
|
|
21
|
+
2. Detect if this is a monorepo (look for workspaces field, packages/ directory, lerna.json)
|
|
22
|
+
3. Use skills/ as the default skills root
|
|
23
|
+
4. For monorepos:
|
|
24
|
+
- Domain map artifacts go at the REPO ROOT: _artifacts/
|
|
25
|
+
- Skills go INSIDE EACH PACKAGE: packages/<pkg>/skills/
|
|
26
|
+
- Identify which packages are client-facing (usually client SDKs and primary framework adapters)
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
## Step 1 — Domain Discovery
|
|
31
|
+
|
|
32
|
+
Load and follow: ${metaSkillPath("domain-discovery")}
|
|
33
|
+
|
|
34
|
+
This produces: domain_map.yaml and skill_spec.md in the artifacts directory.
|
|
35
|
+
Domain discovery covers the WHOLE library (one domain map even for monorepos).
|
|
36
|
+
|
|
37
|
+
**STOP. Review outputs with the maintainer before continuing.**
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
## Step 2 — Tree Generator
|
|
42
|
+
|
|
43
|
+
Load and follow: ${metaSkillPath("tree-generator")}
|
|
44
|
+
|
|
45
|
+
This produces: skill_tree.yaml in the artifacts directory.
|
|
46
|
+
For monorepos, each skill entry should include a \`package\` field.
|
|
47
|
+
|
|
48
|
+
**STOP. Review outputs with the maintainer before continuing.**
|
|
49
|
+
|
|
50
|
+
---
|
|
51
|
+
|
|
52
|
+
## Step 3 — Generate Skills
|
|
53
|
+
|
|
54
|
+
Load and follow: ${metaSkillPath("generate-skill")}
|
|
55
|
+
|
|
56
|
+
This produces: individual SKILL.md files.
|
|
57
|
+
- Single-repo: skills/<domain>/<skill>/SKILL.md
|
|
58
|
+
- Monorepo: packages/<pkg>/skills/<domain>/<skill>/SKILL.md
|
|
59
|
+
|
|
60
|
+
---
|
|
61
|
+
|
|
62
|
+
## After all skills are generated
|
|
63
|
+
|
|
64
|
+
1. Run \`intent validate\` in each package directory
|
|
65
|
+
2. Commit skills/ and artifacts
|
|
66
|
+
3. For each publishable package, run: \`npx @tanstack/intent edit-package-json\`
|
|
67
|
+
4. Ensure each package has \`@tanstack/intent\` as a devDependency
|
|
68
|
+
5. Create a \`skill:<skill-name>\` label on the GitHub repo for each skill (use \`gh label create\`)
|
|
69
|
+
6. Add a README note: "If you use an AI agent, run \`npx @tanstack/intent@latest install\`"
|
|
70
|
+
`;
|
|
71
|
+
console.log(prompt);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
//#endregion
|
|
75
|
+
export { runScaffoldCommand };
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { a as parseFrontmatter, i as listNodeModulesPackageDirs, o as resolveDepDir, r as getDeps, s as toPosixPath, t as detectGlobalNodeModules } from "./utils-
|
|
2
|
-
import { r as rewriteSkillLoadPaths } from "./skill-paths-
|
|
3
|
-
import { a as
|
|
1
|
+
import { a as parseFrontmatter, i as listNodeModulesPackageDirs, o as resolveDepDir, r as getDeps, s as toPosixPath, t as detectGlobalNodeModules } from "./utils-mdb4i6VA.mjs";
|
|
2
|
+
import { r as rewriteSkillLoadPaths } from "./skill-paths-DNOHrvL5.mjs";
|
|
3
|
+
import { a as readWorkspacePatterns, o as resolveWorkspacePackages, r as findWorkspaceRoot } from "./workspace-patterns-BZLEaMcS.mjs";
|
|
4
4
|
import { createRequire } from "node:module";
|
|
5
5
|
import { existsSync, readFileSync, readdirSync } from "node:fs";
|
|
6
|
-
import { dirname, join, relative, resolve, sep } from "node:path";
|
|
6
|
+
import { dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
|
|
7
7
|
|
|
8
8
|
//#region src/discovery/register.ts
|
|
9
9
|
function isLocalToProject(dirPath, projectRoot) {
|
|
@@ -232,7 +232,35 @@ function deriveIntentConfig(pkgJson) {
|
|
|
232
232
|
requires
|
|
233
233
|
};
|
|
234
234
|
}
|
|
235
|
-
function
|
|
235
|
+
function readSkillEntry(skillsDir, childDir, skillFile) {
|
|
236
|
+
const fm = parseFrontmatter(skillFile);
|
|
237
|
+
const relName = toPosixPath(relative(skillsDir, childDir));
|
|
238
|
+
const desc = typeof fm?.description === "string" ? fm.description.replace(/\s+/g, " ").trim() : "";
|
|
239
|
+
return {
|
|
240
|
+
name: typeof fm?.name === "string" ? fm.name : relName,
|
|
241
|
+
path: skillFile,
|
|
242
|
+
description: desc,
|
|
243
|
+
type: typeof fm?.type === "string" ? fm.type : void 0,
|
|
244
|
+
framework: typeof fm?.framework === "string" ? fm.framework : void 0
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
function discoverSkillByNameHint(skillsDir, packageName, skillNameHint) {
|
|
248
|
+
const skills = [];
|
|
249
|
+
const seen = /* @__PURE__ */ new Set();
|
|
250
|
+
const skillNameHints = getSkillNameHints(packageName, skillNameHint);
|
|
251
|
+
for (const hint of skillNameHints) {
|
|
252
|
+
const resolvedHint = resolveSkillNameHintPath(skillsDir, hint);
|
|
253
|
+
if (!resolvedHint) continue;
|
|
254
|
+
const { childDir, skillFile } = resolvedHint;
|
|
255
|
+
if (!existsSync(skillFile)) continue;
|
|
256
|
+
const skill = readSkillEntry(skillsDir, childDir, skillFile);
|
|
257
|
+
if (skill.name !== hint || seen.has(skill.name)) continue;
|
|
258
|
+
seen.add(skill.name);
|
|
259
|
+
skills.push(skill);
|
|
260
|
+
}
|
|
261
|
+
return skills;
|
|
262
|
+
}
|
|
263
|
+
function discoverSkills(skillsDir) {
|
|
236
264
|
const skills = [];
|
|
237
265
|
function walk(dir) {
|
|
238
266
|
let entries;
|
|
@@ -248,24 +276,37 @@ function discoverSkills(skillsDir, _baseName) {
|
|
|
248
276
|
if (!entry.isDirectory()) continue;
|
|
249
277
|
const childDir = join(dir, entry.name);
|
|
250
278
|
const skillFile = join(childDir, "SKILL.md");
|
|
251
|
-
if (existsSync(skillFile))
|
|
252
|
-
const fm = parseFrontmatter(skillFile);
|
|
253
|
-
const relName = toPosixPath(relative(skillsDir, childDir));
|
|
254
|
-
const desc = typeof fm?.description === "string" ? fm.description.replace(/\s+/g, " ").trim() : "";
|
|
255
|
-
skills.push({
|
|
256
|
-
name: typeof fm?.name === "string" ? fm.name : relName,
|
|
257
|
-
path: skillFile,
|
|
258
|
-
description: desc,
|
|
259
|
-
type: typeof fm?.type === "string" ? fm.type : void 0,
|
|
260
|
-
framework: typeof fm?.framework === "string" ? fm.framework : void 0
|
|
261
|
-
});
|
|
262
|
-
}
|
|
279
|
+
if (existsSync(skillFile)) skills.push(readSkillEntry(skillsDir, childDir, skillFile));
|
|
263
280
|
walk(childDir);
|
|
264
281
|
}
|
|
265
282
|
}
|
|
266
283
|
walk(skillsDir);
|
|
267
284
|
return skills;
|
|
268
285
|
}
|
|
286
|
+
function getPackageShortName(packageName) {
|
|
287
|
+
return packageName.split("/").pop() ?? packageName;
|
|
288
|
+
}
|
|
289
|
+
function isWithinOrEqual(path, parentDir) {
|
|
290
|
+
const rel = relative(parentDir, path);
|
|
291
|
+
return rel === "" || !rel.startsWith("..") && !isAbsolute(rel);
|
|
292
|
+
}
|
|
293
|
+
function resolveSkillNameHintPath(skillsDir, hint) {
|
|
294
|
+
if (hint.startsWith("/") || hint.startsWith("\\")) return null;
|
|
295
|
+
const parts = hint.split("/");
|
|
296
|
+
if (parts.some((part) => part === "" || part === "." || part === ".." || part.includes("\\"))) return null;
|
|
297
|
+
const resolvedSkillsDir = resolve(skillsDir);
|
|
298
|
+
const childDir = resolve(resolvedSkillsDir, ...parts);
|
|
299
|
+
if (!isWithinOrEqual(childDir, resolvedSkillsDir)) return null;
|
|
300
|
+
return {
|
|
301
|
+
childDir,
|
|
302
|
+
skillFile: join(childDir, "SKILL.md")
|
|
303
|
+
};
|
|
304
|
+
}
|
|
305
|
+
function getSkillNameHints(packageName, skillNameHint) {
|
|
306
|
+
const packageShortName = getPackageShortName(packageName);
|
|
307
|
+
if (skillNameHint.startsWith(`${packageShortName}/`)) return [skillNameHint];
|
|
308
|
+
return [skillNameHint, `${packageShortName}/${skillNameHint}`];
|
|
309
|
+
}
|
|
269
310
|
function topoSort(packages) {
|
|
270
311
|
const byName = new Map(packages.map((p) => [p.name, p]));
|
|
271
312
|
const visited = /* @__PURE__ */ new Set();
|
|
@@ -460,10 +501,15 @@ function scanForIntents(root, options = {}) {
|
|
|
460
501
|
}
|
|
461
502
|
}
|
|
462
503
|
assertLocalNodeModulesSupported(projectRoot);
|
|
463
|
-
scanTarget(nodeModules.local);
|
|
464
504
|
walkWorkspacePackages();
|
|
505
|
+
const packageCountBeforeDependencyDiscovery = packages.length;
|
|
506
|
+
scanTarget(nodeModules.local);
|
|
465
507
|
walkKnownPackages();
|
|
466
508
|
walkProjectDeps();
|
|
509
|
+
if (packages.length === packageCountBeforeDependencyDiscovery) {
|
|
510
|
+
const api = getPnpApi();
|
|
511
|
+
if (api) scanPnpPackages(api);
|
|
512
|
+
}
|
|
467
513
|
}
|
|
468
514
|
function scanGlobalPackages() {
|
|
469
515
|
ensureGlobalNodeModules();
|
|
@@ -506,6 +552,42 @@ function scanForIntents(root, options = {}) {
|
|
|
506
552
|
nodeModules
|
|
507
553
|
};
|
|
508
554
|
}
|
|
555
|
+
function scanIntentPackageAtRoot(packageRoot, options = {}) {
|
|
556
|
+
const projectRoot = options.projectRoot ?? packageRoot;
|
|
557
|
+
const packages = [];
|
|
558
|
+
const warnings = [];
|
|
559
|
+
const packageIndexes = /* @__PURE__ */ new Map();
|
|
560
|
+
const packageJsonCache = /* @__PURE__ */ new Map();
|
|
561
|
+
function readPkgJson(dirPath) {
|
|
562
|
+
if (packageJsonCache.has(dirPath)) return packageJsonCache.get(dirPath) ?? null;
|
|
563
|
+
try {
|
|
564
|
+
const pkgJson = JSON.parse(readFileSync(join(dirPath, "package.json"), "utf8"));
|
|
565
|
+
packageJsonCache.set(dirPath, pkgJson);
|
|
566
|
+
return pkgJson;
|
|
567
|
+
} catch {
|
|
568
|
+
packageJsonCache.set(dirPath, null);
|
|
569
|
+
return null;
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
const { tryRegister } = createPackageRegistrar({
|
|
573
|
+
comparePackageVersions,
|
|
574
|
+
deriveIntentConfig,
|
|
575
|
+
discoverSkills: options.skillNameHint ? (skillsDir, packageName) => discoverSkillByNameHint(skillsDir, packageName, options.skillNameHint) : discoverSkills,
|
|
576
|
+
getPackageDepth,
|
|
577
|
+
packageIndexes,
|
|
578
|
+
packages,
|
|
579
|
+
projectRoot,
|
|
580
|
+
readPkgJson,
|
|
581
|
+
rememberVariant() {},
|
|
582
|
+
validateIntentField,
|
|
583
|
+
warnings
|
|
584
|
+
});
|
|
585
|
+
tryRegister(packageRoot, options.fallbackName ?? "unknown", options.source ?? "local");
|
|
586
|
+
return {
|
|
587
|
+
package: packages[0] ?? null,
|
|
588
|
+
warnings
|
|
589
|
+
};
|
|
590
|
+
}
|
|
509
591
|
|
|
510
592
|
//#endregion
|
|
511
|
-
export { scanForIntents as t };
|
|
593
|
+
export { scanIntentPackageAtRoot as n, scanForIntents as t };
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
//#region src/workspace-patterns.d.ts
|
|
2
|
+
|
|
2
3
|
declare function readWorkspacePatterns(root: string): Array<string> | null;
|
|
3
4
|
declare function resolveWorkspacePackages(root: string, patterns: Array<string>): Array<string>;
|
|
4
5
|
declare function findWorkspaceRoot(start: string): string | null;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
//#region src/commands/setup-github-actions.ts
|
|
2
|
+
async function runSetupGithubActionsCommand(root, metaDir) {
|
|
3
|
+
const { runSetupGithubActions } = await import("./setup.mjs");
|
|
4
|
+
runSetupGithubActions(root, metaDir);
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
//#endregion
|
|
8
|
+
export { runSetupGithubActionsCommand };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { t as resolveProjectContext } from "./project-context-
|
|
1
|
+
import { a as readWorkspacePatterns, r as findWorkspaceRoot, t as findPackagesWithSkills } from "./workspace-patterns-BZLEaMcS.mjs";
|
|
2
|
+
import { t as resolveProjectContext } from "./project-context-Dx6XIJJo.mjs";
|
|
3
3
|
import { existsSync, mkdirSync, readFileSync, readdirSync, writeFileSync } from "node:fs";
|
|
4
4
|
import { basename, join, relative } from "node:path";
|
|
5
5
|
|
package/dist/setup.d.mts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as runEditPackageJsonAll, c as findWorkspaceRoot, i as runEditPackageJson, l as readWorkspacePatterns, n as MonorepoResult, o as runSetupGithubActions, r as SetupGithubActionsResult, s as findPackagesWithSkills, t as EditPackageJsonResult, u as resolveWorkspacePackages } from "./setup-
|
|
1
|
+
import { a as runEditPackageJsonAll, c as findWorkspaceRoot, i as runEditPackageJson, l as readWorkspacePatterns, n as MonorepoResult, o as runSetupGithubActions, r as SetupGithubActionsResult, s as findPackagesWithSkills, t as EditPackageJsonResult, u as resolveWorkspacePackages } from "./setup-2SE9zYJk.mjs";
|
|
2
2
|
export { EditPackageJsonResult, MonorepoResult, SetupGithubActionsResult, findPackagesWithSkills, findWorkspaceRoot, readWorkspacePatterns, resolveWorkspacePackages, runEditPackageJson, runEditPackageJsonAll, runSetupGithubActions };
|
package/dist/setup.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import "./utils-
|
|
2
|
-
import { a as
|
|
3
|
-
import "./project-context-
|
|
4
|
-
import { n as runEditPackageJsonAll, r as runSetupGithubActions, t as runEditPackageJson } from "./setup-
|
|
1
|
+
import "./utils-mdb4i6VA.mjs";
|
|
2
|
+
import { a as readWorkspacePatterns, o as resolveWorkspacePackages, r as findWorkspaceRoot, t as findPackagesWithSkills } from "./workspace-patterns-BZLEaMcS.mjs";
|
|
3
|
+
import "./project-context-Dx6XIJJo.mjs";
|
|
4
|
+
import { n as runEditPackageJsonAll, r as runSetupGithubActions, t as runEditPackageJson } from "./setup-kvWN4xCr.mjs";
|
|
5
5
|
|
|
6
6
|
export { findPackagesWithSkills, findWorkspaceRoot, readWorkspacePatterns, resolveWorkspacePackages, runEditPackageJson, runEditPackageJsonAll, runSetupGithubActions };
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
//#region src/commands/stale.ts
|
|
2
|
+
async function runStaleCommand(targetDir, options, resolveStaleTargets) {
|
|
3
|
+
if (options.githubReview) {
|
|
4
|
+
await runGithubReview(targetDir, options, resolveStaleTargets);
|
|
5
|
+
return;
|
|
6
|
+
}
|
|
7
|
+
const { reports, workflowAdvisories = [] } = await resolveStaleTargets(targetDir);
|
|
8
|
+
if (options.json) {
|
|
9
|
+
console.log(JSON.stringify(reports, null, 2));
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
12
|
+
for (const advisory of workflowAdvisories) console.log(advisory);
|
|
13
|
+
if (workflowAdvisories.length > 0) console.log();
|
|
14
|
+
if (reports.length === 0) {
|
|
15
|
+
console.log("No intent-enabled packages found.");
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
for (const report of reports) {
|
|
19
|
+
const driftLabel = report.versionDrift ? ` [${report.versionDrift} drift]` : "";
|
|
20
|
+
const vLabel = report.skillVersion && report.currentVersion ? ` (${report.skillVersion} → ${report.currentVersion})` : "";
|
|
21
|
+
console.log(`${report.library}${vLabel}${driftLabel}`);
|
|
22
|
+
const stale = report.skills.filter((skill) => skill.needsReview);
|
|
23
|
+
const signals = report.signals.filter((signal) => signal.needsReview);
|
|
24
|
+
if (stale.length === 0 && signals.length === 0) console.log(" All skills up-to-date");
|
|
25
|
+
else {
|
|
26
|
+
for (const skill of stale) console.log(` ⚠ ${skill.name}: ${skill.reasons.join(", ")}`);
|
|
27
|
+
for (const signal of signals) {
|
|
28
|
+
const subject = signal.packageName ?? signal.packageRoot ?? signal.skill ?? signal.artifactPath ?? signal.subject ?? report.library;
|
|
29
|
+
console.log(` ⚠ ${subject}: ${signal.reasons.join(", ")}`);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
console.log();
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
async function runGithubReview(targetDir, options, resolveStaleTargets) {
|
|
36
|
+
const { collectStaleReviewItems, createFailedStaleReviewItem, createWorkflowAdvisoryReviewItems, writeStaleReviewWorkflowFiles } = await import("./workflow-review-CIdJXmKP.mjs");
|
|
37
|
+
const packageLabel = options.packageLabel ?? "workspace";
|
|
38
|
+
try {
|
|
39
|
+
const { reports, workflowAdvisories = [] } = await resolveStaleTargets(targetDir);
|
|
40
|
+
const items = [...collectStaleReviewItems(reports), ...createWorkflowAdvisoryReviewItems(packageLabel, workflowAdvisories)];
|
|
41
|
+
writeStaleReviewWorkflowFiles(items);
|
|
42
|
+
if (items.length === 0) console.log("No stale skills or coverage gaps found.");
|
|
43
|
+
else console.log(`Wrote ${items.length} intent skill review item(s).`);
|
|
44
|
+
} catch (err) {
|
|
45
|
+
writeStaleReviewWorkflowFiles([createFailedStaleReviewItem(packageLabel)]);
|
|
46
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
47
|
+
console.log(`Intent stale check failed: ${message}`);
|
|
48
|
+
console.log("Wrote a review PR body so maintainers can inspect the logs.");
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
//#endregion
|
|
53
|
+
export { runStaleCommand };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { a as parseFrontmatter, n as findSkillFiles } from "./utils-
|
|
2
|
-
import { t as readIntentArtifacts } from "./artifact-coverage-
|
|
1
|
+
import { a as parseFrontmatter, n as findSkillFiles } from "./utils-mdb4i6VA.mjs";
|
|
2
|
+
import { t as readIntentArtifacts } from "./artifact-coverage-DgWuVqUp.mjs";
|
|
3
3
|
import { existsSync, readFileSync } from "node:fs";
|
|
4
4
|
import { isAbsolute, join, relative, resolve, sep } from "node:path";
|
|
5
5
|
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import "./utils-mdb4i6VA.mjs";
|
|
2
|
+
import "./artifact-coverage-DgWuVqUp.mjs";
|
|
3
|
+
import { n as checkStaleness, r as readPackageName, t as buildWorkspaceCoverageSignals } from "./staleness-CFr3W-RI.mjs";
|
|
4
|
+
|
|
5
|
+
export { buildWorkspaceCoverageSignals, checkStaleness, readPackageName };
|
|
@@ -143,4 +143,4 @@ interface IntentProjectConfig {
|
|
|
143
143
|
};
|
|
144
144
|
}
|
|
145
145
|
//#endregion
|
|
146
|
-
export {
|
|
146
|
+
export { SkillStaleness as _, IntentArtifactSet as a, VersionConflict as b, IntentConfig as c, MetaFeedbackPayload as d, MetaSkillName as f, SkillEntry as g, ScanScope as h, IntentArtifactFile as i, IntentPackage as l, ScanResult as m, FeedbackPayload as n, IntentArtifactSkill as o, ScanOptions as p, IntentArtifactCoverageIgnore as r, IntentArtifactWarning as s, AgentName as t, IntentProjectConfig as u, StalenessReport as v, StalenessSignal as y };
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { a as parseFrontmatter, i as listNodeModulesPackageDirs, n as findSkillFiles, o as resolveDepDir, r as getDeps, s as toPosixPath, t as detectGlobalNodeModules } from "./utils-
|
|
1
|
+
import { a as parseFrontmatter, i as listNodeModulesPackageDirs, n as findSkillFiles, o as resolveDepDir, r as getDeps, s as toPosixPath, t as detectGlobalNodeModules } from "./utils-mdb4i6VA.mjs";
|
|
2
2
|
|
|
3
3
|
export { detectGlobalNodeModules, findSkillFiles, getDeps, listNodeModulesPackageDirs, parseFrontmatter, resolveDepDir, toPosixPath };
|
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
import "./utils-mdb4i6VA.mjs";
|
|
2
|
+
import { n as findWorkspacePackages } from "./workspace-patterns-BZLEaMcS.mjs";
|
|
3
|
+
import { t as resolveProjectContext } from "./project-context-Dx6XIJJo.mjs";
|
|
4
|
+
import { n as isCliFailure, t as fail } from "./cli-error-BrMXlbtx.mjs";
|
|
5
|
+
import { l as printWarnings } from "./cli-support-BON5hn4L.mjs";
|
|
6
|
+
import { appendFileSync, existsSync, readFileSync } from "node:fs";
|
|
7
|
+
import { basename, dirname, join, relative, resolve, sep } from "node:path";
|
|
8
|
+
|
|
9
|
+
//#region src/commands/validate.ts
|
|
10
|
+
const agentSkillNamePattern = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
11
|
+
function buildValidationFailure(errors, warnings) {
|
|
12
|
+
const lines = [
|
|
13
|
+
"",
|
|
14
|
+
`❌ Validation failed with ${errors.length} error(s):`,
|
|
15
|
+
""
|
|
16
|
+
];
|
|
17
|
+
for (const { file, message } of errors) lines.push(` ${file}: ${message}`);
|
|
18
|
+
if (warnings.length > 0) {
|
|
19
|
+
lines.push("", "⚠ Packaging warnings:");
|
|
20
|
+
for (const warning of warnings) lines.push(` ${warning}`);
|
|
21
|
+
}
|
|
22
|
+
return lines.join("\n");
|
|
23
|
+
}
|
|
24
|
+
function collectPackagingWarnings(context) {
|
|
25
|
+
if (!context.packageRoot || !context.targetPackageJsonPath) return [];
|
|
26
|
+
const pkgJsonPath = context.targetPackageJsonPath;
|
|
27
|
+
if (!existsSync(pkgJsonPath)) return [];
|
|
28
|
+
let pkgJson;
|
|
29
|
+
try {
|
|
30
|
+
pkgJson = JSON.parse(readFileSync(pkgJsonPath, "utf8"));
|
|
31
|
+
} catch (err) {
|
|
32
|
+
return [`Could not parse package.json: ${err instanceof Error ? err.message : String(err)}`];
|
|
33
|
+
}
|
|
34
|
+
const warnings = [];
|
|
35
|
+
if (!pkgJson.devDependencies?.["@tanstack/intent"]) warnings.push("@tanstack/intent is not in devDependencies");
|
|
36
|
+
const keywords = pkgJson.keywords;
|
|
37
|
+
if (!Array.isArray(keywords) || !keywords.includes("tanstack-intent")) warnings.push("Missing \"tanstack-intent\" in keywords array");
|
|
38
|
+
const files = pkgJson.files;
|
|
39
|
+
if (Array.isArray(files)) {
|
|
40
|
+
if (!files.includes("skills")) warnings.push("\"skills\" is not in the \"files\" array — skills won't be published");
|
|
41
|
+
if (!context.isMonorepo && !files.includes("!skills/_artifacts")) warnings.push("\"!skills/_artifacts\" is not in the \"files\" array — artifacts will be published unnecessarily");
|
|
42
|
+
}
|
|
43
|
+
return warnings;
|
|
44
|
+
}
|
|
45
|
+
function formatWarning({ file, message }) {
|
|
46
|
+
return `${file}: ${message}`;
|
|
47
|
+
}
|
|
48
|
+
function isRecord(value) {
|
|
49
|
+
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
50
|
+
}
|
|
51
|
+
function collectAgentSkillSpecWarnings({ filePath, fm, rel }) {
|
|
52
|
+
const warnings = [];
|
|
53
|
+
if (typeof fm.name === "string") {
|
|
54
|
+
if (fm.name.length > 64) warnings.push({
|
|
55
|
+
file: rel,
|
|
56
|
+
message: `Agent Skills spec warning: name exceeds 64 characters (${fm.name.length} chars)`
|
|
57
|
+
});
|
|
58
|
+
for (const segment of fm.name.split("/")) if (!agentSkillNamePattern.test(segment)) {
|
|
59
|
+
warnings.push({
|
|
60
|
+
file: rel,
|
|
61
|
+
message: "Agent Skills spec warning: each name segment should use lowercase letters, numbers, and single hyphens only"
|
|
62
|
+
});
|
|
63
|
+
break;
|
|
64
|
+
}
|
|
65
|
+
const parentDir = basename(dirname(filePath));
|
|
66
|
+
if (!fm.name.includes("/") && fm.name !== parentDir) warnings.push({
|
|
67
|
+
file: rel,
|
|
68
|
+
message: "Agent Skills spec warning: name should match the parent directory name"
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
if (fm.license !== void 0 && (typeof fm.license !== "string" || fm.license.trim().length === 0)) warnings.push({
|
|
72
|
+
file: rel,
|
|
73
|
+
message: "Agent Skills spec warning: license should be a non-empty string"
|
|
74
|
+
});
|
|
75
|
+
if (fm.compatibility !== void 0) {
|
|
76
|
+
if (typeof fm.compatibility !== "string" || fm.compatibility.trim().length === 0) warnings.push({
|
|
77
|
+
file: rel,
|
|
78
|
+
message: "Agent Skills spec warning: compatibility should be a non-empty string"
|
|
79
|
+
});
|
|
80
|
+
else if (fm.compatibility.length > 500) warnings.push({
|
|
81
|
+
file: rel,
|
|
82
|
+
message: `Agent Skills spec warning: compatibility exceeds 500 characters (${fm.compatibility.length} chars)`
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
if (fm.metadata !== void 0) {
|
|
86
|
+
if (!isRecord(fm.metadata)) warnings.push({
|
|
87
|
+
file: rel,
|
|
88
|
+
message: "Agent Skills spec warning: metadata should be a mapping"
|
|
89
|
+
});
|
|
90
|
+
else if (Object.values(fm.metadata).some((value) => typeof value !== "string")) warnings.push({
|
|
91
|
+
file: rel,
|
|
92
|
+
message: "Agent Skills spec warning: metadata values should be strings"
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
if (fm["allowed-tools"] !== void 0 && typeof fm["allowed-tools"] !== "string") warnings.push({
|
|
96
|
+
file: rel,
|
|
97
|
+
message: "Agent Skills spec warning: allowed-tools should be a space-separated string"
|
|
98
|
+
});
|
|
99
|
+
return warnings;
|
|
100
|
+
}
|
|
101
|
+
async function runValidateCommand(dir, options = {}) {
|
|
102
|
+
if (!options.githubSummary) {
|
|
103
|
+
await runValidateCommandInternal(dir);
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
try {
|
|
107
|
+
await runValidateCommandInternal(dir);
|
|
108
|
+
writeGithubValidationSummary({ ok: true });
|
|
109
|
+
} catch (err) {
|
|
110
|
+
writeGithubValidationSummary({
|
|
111
|
+
ok: false,
|
|
112
|
+
message: validationErrorMessage(err)
|
|
113
|
+
});
|
|
114
|
+
throw err;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
async function runValidateCommandInternal(dir) {
|
|
118
|
+
const [{ parse: parseYaml }, { findSkillFiles }] = await Promise.all([import("yaml"), import("./utils-BHzSyNeJ.mjs")]);
|
|
119
|
+
const context = resolveProjectContext({
|
|
120
|
+
cwd: process.cwd(),
|
|
121
|
+
targetPath: dir
|
|
122
|
+
});
|
|
123
|
+
const explicitDir = dir !== void 0;
|
|
124
|
+
const skillsDirs = explicitDir ? [context.targetSkillsDir ?? resolve(process.cwd(), dir)] : collectDefaultSkillsDirs(context, findSkillFiles);
|
|
125
|
+
if (explicitDir && !existsSync(skillsDirs[0])) fail(`Skills directory not found: ${skillsDirs[0]}`);
|
|
126
|
+
const errors = [];
|
|
127
|
+
const warnings = [];
|
|
128
|
+
let validatedCount = 0;
|
|
129
|
+
if (explicitDir && findSkillFiles(skillsDirs[0]).length === 0) fail("No SKILL.md files found");
|
|
130
|
+
if (skillsDirs.length === 0) {
|
|
131
|
+
console.log("No skills/ directory found — skipping validation.");
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
for (const skillsDir of skillsDirs) {
|
|
135
|
+
const skillFiles = findSkillFiles(skillsDir);
|
|
136
|
+
const validateContext = resolveProjectContext({
|
|
137
|
+
cwd: process.cwd(),
|
|
138
|
+
targetPath: skillsDir
|
|
139
|
+
});
|
|
140
|
+
for (const filePath of skillFiles) {
|
|
141
|
+
const rel = relative(process.cwd(), filePath);
|
|
142
|
+
const content = readFileSync(filePath, "utf8");
|
|
143
|
+
const match = content.match(/^---\r?\n([\s\S]*?)\r?\n---\r?\n([\s\S]*)/);
|
|
144
|
+
if (!match) {
|
|
145
|
+
errors.push({
|
|
146
|
+
file: rel,
|
|
147
|
+
message: "Missing or invalid frontmatter"
|
|
148
|
+
});
|
|
149
|
+
continue;
|
|
150
|
+
}
|
|
151
|
+
if (!match[1]) {
|
|
152
|
+
errors.push({
|
|
153
|
+
file: rel,
|
|
154
|
+
message: "Missing YAML frontmatter"
|
|
155
|
+
});
|
|
156
|
+
continue;
|
|
157
|
+
}
|
|
158
|
+
let fm;
|
|
159
|
+
try {
|
|
160
|
+
fm = parseYaml(match[1]);
|
|
161
|
+
} catch (err) {
|
|
162
|
+
const detail = err instanceof Error ? err.message : String(err);
|
|
163
|
+
errors.push({
|
|
164
|
+
file: rel,
|
|
165
|
+
message: `Invalid YAML frontmatter: ${detail}`
|
|
166
|
+
});
|
|
167
|
+
continue;
|
|
168
|
+
}
|
|
169
|
+
if (!fm.name) errors.push({
|
|
170
|
+
file: rel,
|
|
171
|
+
message: "Missing required field: name"
|
|
172
|
+
});
|
|
173
|
+
if (!fm.description) errors.push({
|
|
174
|
+
file: rel,
|
|
175
|
+
message: "Missing required field: description"
|
|
176
|
+
});
|
|
177
|
+
if (typeof fm.name === "string") {
|
|
178
|
+
const expectedPath = relative(skillsDir, filePath).replace(/[/\\]SKILL\.md$/, "").split(sep).join("/");
|
|
179
|
+
if (fm.name !== expectedPath) errors.push({
|
|
180
|
+
file: rel,
|
|
181
|
+
message: `name "${fm.name}" does not match directory path "${expectedPath}"`
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
if (typeof fm.description === "string" && fm.description.length > 1024) errors.push({
|
|
185
|
+
file: rel,
|
|
186
|
+
message: `Description exceeds 1024 character limit (${fm.description.length} chars)`
|
|
187
|
+
});
|
|
188
|
+
if (fm.type === "framework" && !Array.isArray(fm.requires)) errors.push({
|
|
189
|
+
file: rel,
|
|
190
|
+
message: "Framework skills must have a \"requires\" field"
|
|
191
|
+
});
|
|
192
|
+
warnings.push(...collectAgentSkillSpecWarnings({
|
|
193
|
+
filePath,
|
|
194
|
+
fm,
|
|
195
|
+
rel
|
|
196
|
+
}).map(formatWarning));
|
|
197
|
+
const lineCount = content.split(/\r?\n/).length;
|
|
198
|
+
if (lineCount > 500) errors.push({
|
|
199
|
+
file: rel,
|
|
200
|
+
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.`
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
const artifactsDir = join(skillsDir, "_artifacts");
|
|
204
|
+
if (!validateContext.isMonorepo && existsSync(artifactsDir)) for (const fileName of [
|
|
205
|
+
"domain_map.yaml",
|
|
206
|
+
"skill_spec.md",
|
|
207
|
+
"skill_tree.yaml"
|
|
208
|
+
]) {
|
|
209
|
+
const artifactPath = join(artifactsDir, fileName);
|
|
210
|
+
if (!existsSync(artifactPath)) {
|
|
211
|
+
errors.push({
|
|
212
|
+
file: relative(process.cwd(), artifactPath),
|
|
213
|
+
message: "Missing required artifact"
|
|
214
|
+
});
|
|
215
|
+
continue;
|
|
216
|
+
}
|
|
217
|
+
const content = readFileSync(artifactPath, "utf8");
|
|
218
|
+
if (content.trim().length === 0) {
|
|
219
|
+
errors.push({
|
|
220
|
+
file: relative(process.cwd(), artifactPath),
|
|
221
|
+
message: "Artifact file is empty"
|
|
222
|
+
});
|
|
223
|
+
continue;
|
|
224
|
+
}
|
|
225
|
+
if (fileName.endsWith(".yaml")) try {
|
|
226
|
+
parseYaml(content);
|
|
227
|
+
} catch (err) {
|
|
228
|
+
const detail = err instanceof Error ? err.message : String(err);
|
|
229
|
+
errors.push({
|
|
230
|
+
file: relative(process.cwd(), artifactPath),
|
|
231
|
+
message: `Invalid YAML in artifact file: ${detail}`
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
validatedCount += skillFiles.length;
|
|
236
|
+
warnings.push(...collectPackagingWarnings(validateContext));
|
|
237
|
+
}
|
|
238
|
+
if (errors.length > 0) fail(buildValidationFailure(errors, warnings));
|
|
239
|
+
console.log(`✅ Validated ${validatedCount} skill files — all passed`);
|
|
240
|
+
if (warnings.length > 0) console.log();
|
|
241
|
+
printWarnings(warnings);
|
|
242
|
+
}
|
|
243
|
+
function validationErrorMessage(err) {
|
|
244
|
+
if (isCliFailure(err)) return err.message;
|
|
245
|
+
if (err instanceof Error) return err.message;
|
|
246
|
+
return String(err);
|
|
247
|
+
}
|
|
248
|
+
function writeGithubValidationSummary({ message, ok }) {
|
|
249
|
+
const summaryPath = process.env.GITHUB_STEP_SUMMARY;
|
|
250
|
+
if (!summaryPath) return;
|
|
251
|
+
const lines = ["### Intent skill validation", ""];
|
|
252
|
+
if (ok) lines.push("Skill validation passed.", "");
|
|
253
|
+
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.", "```", "");
|
|
254
|
+
appendFileSync(summaryPath, lines.join("\n"));
|
|
255
|
+
}
|
|
256
|
+
function collectDefaultSkillsDirs(context, findSkillFiles) {
|
|
257
|
+
const skillsDirs = [];
|
|
258
|
+
const addSkillsDir = (skillsDir) => {
|
|
259
|
+
if (existsSync(skillsDir) && findSkillFiles(skillsDir).length > 0) skillsDirs.push(skillsDir);
|
|
260
|
+
};
|
|
261
|
+
if (context.workspaceRoot && context.cwd === context.workspaceRoot) {
|
|
262
|
+
addSkillsDir(join(context.workspaceRoot, "skills"));
|
|
263
|
+
for (const packageDir of findWorkspacePackages(context.workspaceRoot)) addSkillsDir(join(packageDir, "skills"));
|
|
264
|
+
return [...new Set(skillsDirs)].sort((a, b) => a.localeCompare(b));
|
|
265
|
+
}
|
|
266
|
+
addSkillsDir(context.targetSkillsDir ?? (context.packageRoot ? join(context.packageRoot, "skills") : resolve(context.cwd, "skills")));
|
|
267
|
+
return skillsDirs;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
//#endregion
|
|
271
|
+
export { runValidateCommand };
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { a as writeStaleReviewWorkflowFiles, i as createWorkflowAdvisoryReviewItems, n as collectStaleReviewItems, r as createFailedStaleReviewItem, t as buildStaleReviewBody } from "./workflow-review-
|
|
1
|
+
import { a as writeStaleReviewWorkflowFiles, i as createWorkflowAdvisoryReviewItems, n as collectStaleReviewItems, r as createFailedStaleReviewItem, t as buildStaleReviewBody } from "./workflow-review-CwcR2ge4.mjs";
|
|
2
2
|
|
|
3
3
|
export { collectStaleReviewItems, createFailedStaleReviewItem, createWorkflowAdvisoryReviewItems, writeStaleReviewWorkflowFiles };
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import "./utils-mdb4i6VA.mjs";
|
|
2
|
+
import { a as readWorkspacePatterns, i as getWorkspaceInfo, n as findWorkspacePackages, o as resolveWorkspacePackages, r as findWorkspaceRoot, t as findPackagesWithSkills } from "./workspace-patterns-BZLEaMcS.mjs";
|
|
3
|
+
|
|
4
|
+
export { findWorkspaceRoot, getWorkspaceInfo };
|