@tanstack/intent 0.0.33 → 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/dist/cli.mjs +233 -96
- package/dist/{display-DvLbcWzq.mjs → display-B3vkG99D.mjs} +1 -1
- package/dist/index.d.mts +2 -2
- package/dist/index.mjs +6 -121
- package/dist/{install-PUnIfBNC.mjs → install-QjryhQtg.mjs} +4 -4
- package/dist/intent-library.mjs +4 -4
- package/dist/library-scanner.d.mts +1 -1
- package/dist/library-scanner.mjs +1 -1
- package/dist/{resolver-D2CgIYGg.mjs → resolver-Whd12ksO.mjs} +1 -1
- package/dist/{scanner-DlkcbVye.mjs → scanner-BAZxWeUk.mjs} +1 -1
- package/dist/{scanner-CW59cxE_.mjs → scanner-Dav1tzQK.mjs} +78 -3
- package/dist/{setup-DfLsziXU.mjs → setup-Dp-W8y0Y.mjs} +1 -1
- package/dist/setup.d.mts +1 -1
- package/dist/setup.mjs +2 -2
- package/dist/workflow-review-CwkPVIQf.mjs +153 -0
- package/dist/workflow-review-Dz_ofcYQ.mjs +3 -0
- package/meta/templates/workflows/check-skills.yml +34 -180
- package/package.json +1 -1
- package/meta/templates/workflows/validate-skills.yml +0 -52
- /package/dist/{display-BTZWCjzT.mjs → display-CAof6doy.mjs} +0 -0
- /package/dist/{library-scanner-Cl-XPEMf.mjs → library-scanner-fexXlPXb.mjs} +0 -0
- /package/dist/{project-context-IDLpJU3S.mjs → project-context-alYMNoNa.mjs} +0 -0
- /package/dist/{setup-D2CGdTsx.d.mts → setup-t1i2o2-h.d.mts} +0 -0
- /package/dist/{skill-use-uwGleSOz.mjs → skill-use-BzuuvLM7.mjs} +0 -0
- /package/dist/{types-_y9b00bI.d.mts → types-DT7Y6TFz.d.mts} +0 -0
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-BN2A_60g.mjs";
|
|
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,6 +245,10 @@ 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
|
+
if (options.githubReview) {
|
|
249
|
+
await runGithubReview(targetDir, options, resolveStaleTargets$1);
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
248
252
|
const { reports, workflowAdvisories = [] } = await resolveStaleTargets$1(targetDir);
|
|
249
253
|
if (options.json) {
|
|
250
254
|
console.log(JSON.stringify(reports, null, 2));
|
|
@@ -273,9 +277,26 @@ async function runStaleCommand(targetDir, options, resolveStaleTargets$1) {
|
|
|
273
277
|
console.log();
|
|
274
278
|
}
|
|
275
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
|
+
}
|
|
276
296
|
|
|
277
297
|
//#endregion
|
|
278
298
|
//#region src/commands/validate.ts
|
|
299
|
+
const agentSkillNamePattern = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
279
300
|
function buildValidationFailure(errors, warnings) {
|
|
280
301
|
const lines = [
|
|
281
302
|
"",
|
|
@@ -310,114 +331,230 @@ function collectPackagingWarnings(context) {
|
|
|
310
331
|
}
|
|
311
332
|
return warnings;
|
|
312
333
|
}
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
const
|
|
321
|
-
if (
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
const
|
|
327
|
-
|
|
328
|
-
const match = content.match(/^---\r?\n([\s\S]*?)\r?\n---\r?\n([\s\S]*)/);
|
|
329
|
-
if (!match) {
|
|
330
|
-
errors.push({
|
|
331
|
-
file: rel,
|
|
332
|
-
message: "Missing or invalid frontmatter"
|
|
333
|
-
});
|
|
334
|
-
continue;
|
|
335
|
-
}
|
|
336
|
-
if (!match[1]) {
|
|
337
|
-
errors.push({
|
|
338
|
-
file: rel,
|
|
339
|
-
message: "Missing YAML frontmatter"
|
|
340
|
-
});
|
|
341
|
-
continue;
|
|
342
|
-
}
|
|
343
|
-
let fm;
|
|
344
|
-
try {
|
|
345
|
-
fm = parseYaml(match[1]);
|
|
346
|
-
} catch (err) {
|
|
347
|
-
const detail = err instanceof Error ? err.message : String(err);
|
|
348
|
-
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({
|
|
349
349
|
file: rel,
|
|
350
|
-
message:
|
|
350
|
+
message: "Agent Skills spec warning: each name segment should use lowercase letters, numbers, and single hyphens only"
|
|
351
351
|
});
|
|
352
|
-
|
|
352
|
+
break;
|
|
353
353
|
}
|
|
354
|
-
|
|
354
|
+
const parentDir = basename(dirname(filePath));
|
|
355
|
+
if (!fm.name.includes("/") && fm.name !== parentDir) warnings.push({
|
|
355
356
|
file: rel,
|
|
356
|
-
message: "
|
|
357
|
+
message: "Agent Skills spec warning: name should match the parent directory name"
|
|
357
358
|
});
|
|
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({
|
|
359
366
|
file: rel,
|
|
360
|
-
message: "
|
|
367
|
+
message: "Agent Skills spec warning: compatibility should be a non-empty string"
|
|
361
368
|
});
|
|
362
|
-
if (
|
|
363
|
-
const expectedPath = relative(skillsDir, filePath).replace(/[/\\]SKILL\.md$/, "").split(sep).join("/");
|
|
364
|
-
if (fm.name !== expectedPath) errors.push({
|
|
365
|
-
file: rel,
|
|
366
|
-
message: `name "${fm.name}" does not match directory path "${expectedPath}"`
|
|
367
|
-
});
|
|
368
|
-
}
|
|
369
|
-
if (typeof fm.description === "string" && fm.description.length > 1024) errors.push({
|
|
369
|
+
else if (fm.compatibility.length > 500) warnings.push({
|
|
370
370
|
file: rel,
|
|
371
|
-
message: `
|
|
371
|
+
message: `Agent Skills spec warning: compatibility exceeds 500 characters (${fm.compatibility.length} chars)`
|
|
372
372
|
});
|
|
373
|
-
|
|
373
|
+
}
|
|
374
|
+
if (fm.metadata !== void 0) {
|
|
375
|
+
if (!isRecord(fm.metadata)) warnings.push({
|
|
374
376
|
file: rel,
|
|
375
|
-
message: "
|
|
377
|
+
message: "Agent Skills spec warning: metadata should be a mapping"
|
|
376
378
|
});
|
|
377
|
-
|
|
378
|
-
if (lineCount > 500) errors.push({
|
|
379
|
+
else if (Object.values(fm.metadata).some((value) => typeof value !== "string")) warnings.push({
|
|
379
380
|
file: rel,
|
|
380
|
-
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)
|
|
381
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;
|
|
382
422
|
}
|
|
383
|
-
const
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
const
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
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"
|
|
394
461
|
});
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
if (content.trim().length === 0) {
|
|
399
|
-
errors.push({
|
|
400
|
-
file: relative(process.cwd(), artifactPath),
|
|
401
|
-
message: "Artifact file is empty"
|
|
462
|
+
if (!fm.description) errors.push({
|
|
463
|
+
file: rel,
|
|
464
|
+
message: "Missing required field: description"
|
|
402
465
|
});
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
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.`
|
|
412
490
|
});
|
|
413
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));
|
|
414
526
|
}
|
|
415
|
-
const warnings = collectPackagingWarnings(context);
|
|
416
527
|
if (errors.length > 0) fail(buildValidationFailure(errors, warnings));
|
|
417
|
-
console.log(`✅ Validated ${
|
|
528
|
+
console.log(`✅ Validated ${validatedCount} skill files — all passed`);
|
|
418
529
|
if (warnings.length > 0) console.log();
|
|
419
530
|
printWarnings(warnings);
|
|
420
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
|
+
}
|
|
421
558
|
|
|
422
559
|
//#endregion
|
|
423
560
|
//#region src/cli.ts
|
|
@@ -433,8 +570,8 @@ function createCli() {
|
|
|
433
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) => {
|
|
434
571
|
await runMetaCommand(name, getMetaDir());
|
|
435
572
|
});
|
|
436
|
-
cli.command("validate [dir]", "Validate skill files").usage("validate [dir]").example("validate").example("validate packages/query/skills").action(async (dir) => {
|
|
437
|
-
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);
|
|
438
575
|
});
|
|
439
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) => {
|
|
440
577
|
await runInstallCommand(options, scanIntentsOrFail);
|
|
@@ -442,7 +579,7 @@ function createCli() {
|
|
|
442
579
|
cli.command("scaffold", "Print maintainer scaffold prompt").usage("scaffold").action(() => {
|
|
443
580
|
runScaffoldCommand(getMetaDir());
|
|
444
581
|
});
|
|
445
|
-
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) => {
|
|
446
583
|
await runStaleCommand(targetDir, options, resolveStaleTargets);
|
|
447
584
|
});
|
|
448
585
|
cli.command("edit-package-json", "Update package.json files so skills are published").usage("edit-package-json").action(async () => {
|
|
@@ -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,5 +1,5 @@
|
|
|
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-
|
|
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;
|
package/dist/index.mjs
CHANGED
|
@@ -1,133 +1,18 @@
|
|
|
1
1
|
import { a as parseFrontmatter, n as findSkillFiles, o as resolveDepDir, r as getDeps } from "./utils-COlDcU72.mjs";
|
|
2
2
|
import "./skill-paths-8k9K9y26.mjs";
|
|
3
|
-
import { t as scanForIntents } from "./scanner-
|
|
3
|
+
import { t as scanForIntents } from "./scanner-Dav1tzQK.mjs";
|
|
4
4
|
import "./workspace-patterns-BN2A_60g.mjs";
|
|
5
5
|
import { t as readIntentArtifacts } from "./artifact-coverage-wLNVX8yC.mjs";
|
|
6
6
|
import { n as checkStaleness } from "./staleness-PdgakrCQ.mjs";
|
|
7
|
-
import {
|
|
8
|
-
import { n as
|
|
9
|
-
import "./
|
|
10
|
-
import
|
|
7
|
+
import { n as collectStaleReviewItems, r as createFailedStaleReviewItem, t as buildStaleReviewBody } from "./workflow-review-CwkPVIQf.mjs";
|
|
8
|
+
import { i as parseSkillUse, n as formatSkillUse, r as isSkillUseParseError, t as SkillUseParseError } from "./skill-use-BzuuvLM7.mjs";
|
|
9
|
+
import { n as isResolveSkillUseError, r as resolveSkillUse, t as ResolveSkillUseError } from "./resolver-Whd12ksO.mjs";
|
|
10
|
+
import "./project-context-alYMNoNa.mjs";
|
|
11
|
+
import { r as runSetupGithubActions, t as runEditPackageJson } from "./setup-Dp-W8y0Y.mjs";
|
|
11
12
|
import { readFileSync, writeFileSync } from "node:fs";
|
|
12
13
|
import { join } from "node:path";
|
|
13
14
|
import { execFileSync, execSync } from "node:child_process";
|
|
14
15
|
|
|
15
|
-
//#region src/workflow-review.ts
|
|
16
|
-
function collectStaleReviewItems(reports) {
|
|
17
|
-
const items = [];
|
|
18
|
-
for (const report of reports) {
|
|
19
|
-
for (const skill of report.skills ?? []) {
|
|
20
|
-
if (!skill?.needsReview) continue;
|
|
21
|
-
items.push({
|
|
22
|
-
type: "stale-skill",
|
|
23
|
-
library: report.library,
|
|
24
|
-
subject: skill.name,
|
|
25
|
-
reasons: skill.reasons ?? []
|
|
26
|
-
});
|
|
27
|
-
}
|
|
28
|
-
for (const signal of report.signals ?? []) {
|
|
29
|
-
if (signal?.needsReview === false) continue;
|
|
30
|
-
items.push({
|
|
31
|
-
type: signal?.type ?? "review-signal",
|
|
32
|
-
library: signal?.library ?? report.library,
|
|
33
|
-
subject: signal?.packageName ?? signal?.packageRoot ?? signal?.skill ?? signal?.artifactPath ?? signal?.subject ?? report.library,
|
|
34
|
-
reasons: signal?.reasons ?? [],
|
|
35
|
-
artifactPath: signal?.artifactPath,
|
|
36
|
-
packageName: signal?.packageName,
|
|
37
|
-
packageRoot: signal?.packageRoot,
|
|
38
|
-
skill: signal?.skill
|
|
39
|
-
});
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
return items;
|
|
43
|
-
}
|
|
44
|
-
function createFailedStaleReviewItem(library) {
|
|
45
|
-
return {
|
|
46
|
-
type: "stale-check-failed",
|
|
47
|
-
library,
|
|
48
|
-
subject: "intent stale --json",
|
|
49
|
-
reasons: ["The stale check command failed. Review the workflow logs before updating skills."]
|
|
50
|
-
};
|
|
51
|
-
}
|
|
52
|
-
function buildStaleReviewBody(items) {
|
|
53
|
-
const grouped = /* @__PURE__ */ new Map();
|
|
54
|
-
for (const item of items) grouped.set(item.type, (grouped.get(item.type) ?? 0) + 1);
|
|
55
|
-
const signalRows = [...grouped.entries()].sort(([a], [b]) => a.localeCompare(b)).map(([type, count]) => `| \`${type}\` | ${count} |`);
|
|
56
|
-
const itemRows = items.map((item) => {
|
|
57
|
-
const subject = item.subject ? `\`${item.subject}\`` : "-";
|
|
58
|
-
const reasons = item.reasons?.length ? item.reasons.join("; ") : "-";
|
|
59
|
-
return `| \`${item.type}\` | ${subject} | \`${item.library}\` | ${reasons} |`;
|
|
60
|
-
});
|
|
61
|
-
const prompt = [
|
|
62
|
-
"You are helping maintain Intent skills for this repository.",
|
|
63
|
-
"",
|
|
64
|
-
"Goal:",
|
|
65
|
-
"Resolve the Intent skill review signals below while preserving the existing scope, taxonomy, and maintainer-reviewed artifacts.",
|
|
66
|
-
"",
|
|
67
|
-
"Review signals:",
|
|
68
|
-
JSON.stringify(items, null, 2),
|
|
69
|
-
"",
|
|
70
|
-
"Required workflow:",
|
|
71
|
-
"1. Read the existing `_artifacts/*domain_map.yaml`, `_artifacts/*skill_tree.yaml`, and generated `skills/**/SKILL.md` files.",
|
|
72
|
-
"2. Read each flagged package package.json, public exports, README/docs if present, and source entry points.",
|
|
73
|
-
"3. Compare flagged packages against the existing domains, skills, tasks, packages, covers, sources, tensions, and cross-references in the artifacts.",
|
|
74
|
-
"4. For each signal, decide whether it means existing skill coverage, a missing generated skill, a new skill candidate, out-of-scope coverage, or deferred work.",
|
|
75
|
-
"",
|
|
76
|
-
"Maintainer questions:",
|
|
77
|
-
"Before editing skills or artifacts, ask the maintainer:",
|
|
78
|
-
"1. For each flagged package, is this package user-facing enough to need agent guidance?",
|
|
79
|
-
"2. If yes, should it extend an existing skill or become a new skill?",
|
|
80
|
-
"3. If it extends an existing skill, which current skill should own it?",
|
|
81
|
-
"4. If it is out of scope, what short reason should be recorded in artifact coverage ignores?",
|
|
82
|
-
"5. Are any of these packages experimental or unstable enough to exclude for now?",
|
|
83
|
-
"",
|
|
84
|
-
"Decision rules:",
|
|
85
|
-
"- Do not auto-generate skills.",
|
|
86
|
-
"- Do not create broad new skill areas without maintainer confirmation.",
|
|
87
|
-
"- Prefer adding package coverage to an existing skill when the package is an implementation variant of an existing domain.",
|
|
88
|
-
"- Create a new skill only when the package introduces a distinct developer task or failure mode.",
|
|
89
|
-
"- Preserve current naming, path, and package layout conventions.",
|
|
90
|
-
"- Keep generated skills under the package-local `skills/` directory.",
|
|
91
|
-
"- Keep repo-root `_artifacts` as the reviewed plan.",
|
|
92
|
-
"",
|
|
93
|
-
"If maintainer confirms updates:",
|
|
94
|
-
"1. Update the relevant `_artifacts/*domain_map.yaml` or `_artifacts/*skill_tree.yaml`.",
|
|
95
|
-
"2. Update or create `SKILL.md` files only for confirmed coverage changes.",
|
|
96
|
-
"3. Keep `sources` aligned between artifact skill entries and SKILL frontmatter.",
|
|
97
|
-
"4. Bump `library_version` only for skills whose covered source package version changed.",
|
|
98
|
-
"5. Run `npx @tanstack/intent@latest validate` on touched skill directories.",
|
|
99
|
-
"6. Summarize every package as one of: existing-skill coverage, new skill, ignored, or deferred."
|
|
100
|
-
].join("\n");
|
|
101
|
-
return [
|
|
102
|
-
"## Intent Skill Review Needed",
|
|
103
|
-
"",
|
|
104
|
-
"Intent found skills, artifact coverage, or workspace package coverage that need maintainer review.",
|
|
105
|
-
"",
|
|
106
|
-
"### Summary",
|
|
107
|
-
"",
|
|
108
|
-
"| Signal | Count |",
|
|
109
|
-
"| --- | ---: |",
|
|
110
|
-
...signalRows,
|
|
111
|
-
"",
|
|
112
|
-
"### Review Items",
|
|
113
|
-
"",
|
|
114
|
-
"| Signal | Subject | Library | Reason |",
|
|
115
|
-
"| --- | --- | --- | --- |",
|
|
116
|
-
...itemRows,
|
|
117
|
-
"",
|
|
118
|
-
"### Agent Prompt",
|
|
119
|
-
"",
|
|
120
|
-
"Paste this into your coding agent:",
|
|
121
|
-
"",
|
|
122
|
-
"```text",
|
|
123
|
-
prompt,
|
|
124
|
-
"```",
|
|
125
|
-
"",
|
|
126
|
-
"This PR is a review reminder only. It does not update skills automatically."
|
|
127
|
-
].join("\n");
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
//#endregion
|
|
131
16
|
//#region src/feedback.ts
|
|
132
17
|
const META_FEEDBACK_REPO = "TanStack/intent";
|
|
133
18
|
const SECRET_PATTERNS = [
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { i as parseSkillUse, n as formatSkillUse } from "./skill-use-
|
|
2
|
-
import { t as resolveProjectContext } from "./project-context-
|
|
1
|
+
import { i as parseSkillUse, n as formatSkillUse } from "./skill-use-BzuuvLM7.mjs";
|
|
2
|
+
import { t as resolveProjectContext } from "./project-context-alYMNoNa.mjs";
|
|
3
3
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
4
4
|
import { dirname, join, relative, resolve } from "node:path";
|
|
5
5
|
import { parse } from "yaml";
|
|
@@ -28,7 +28,7 @@ function printWarnings(warnings) {
|
|
|
28
28
|
|
|
29
29
|
//#endregion
|
|
30
30
|
//#region src/cli-support.ts
|
|
31
|
-
const INTENT_CHECK_SKILLS_WORKFLOW_VERSION =
|
|
31
|
+
const INTENT_CHECK_SKILLS_WORKFLOW_VERSION = 3;
|
|
32
32
|
function getMetaDir() {
|
|
33
33
|
return join(dirname(fileURLToPath(import.meta.url)), "..", "meta");
|
|
34
34
|
}
|
|
@@ -46,7 +46,7 @@ function getCheckSkillsWorkflowAdvisories(root) {
|
|
|
46
46
|
return [`Intent workflow update available: run \`npx @tanstack/intent@latest setup\` to refresh ${relative(process.cwd(), workflowPath) || workflowPath}.`];
|
|
47
47
|
}
|
|
48
48
|
async function scanIntentsOrFail(options) {
|
|
49
|
-
const { scanForIntents } = await import("./scanner-
|
|
49
|
+
const { scanForIntents } = await import("./scanner-BAZxWeUk.mjs");
|
|
50
50
|
try {
|
|
51
51
|
return scanForIntents(void 0, options);
|
|
52
52
|
} catch (err) {
|
package/dist/intent-library.mjs
CHANGED
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
import "./utils-COlDcU72.mjs";
|
|
3
3
|
import "./skill-paths-8k9K9y26.mjs";
|
|
4
4
|
import "./workspace-patterns-BN2A_60g.mjs";
|
|
5
|
-
import "./project-context-
|
|
6
|
-
import { t as INSTALL_PROMPT } from "./install-
|
|
7
|
-
import { n as printSkillTree, r as printTable, t as computeSkillNameWidth } from "./display-
|
|
8
|
-
import { t as scanLibrary } from "./library-scanner-
|
|
5
|
+
import "./project-context-alYMNoNa.mjs";
|
|
6
|
+
import { t as INSTALL_PROMPT } from "./install-QjryhQtg.mjs";
|
|
7
|
+
import { n as printSkillTree, r as printTable, t as computeSkillNameWidth } from "./display-CAof6doy.mjs";
|
|
8
|
+
import { t as scanLibrary } from "./library-scanner-fexXlPXb.mjs";
|
|
9
9
|
|
|
10
10
|
//#region src/intent-library.ts
|
|
11
11
|
function cmdList() {
|
package/dist/library-scanner.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import "./utils-COlDcU72.mjs";
|
|
2
2
|
import "./skill-paths-8k9K9y26.mjs";
|
|
3
|
-
import { t as scanForIntents } from "./scanner-
|
|
3
|
+
import { t as scanForIntents } from "./scanner-Dav1tzQK.mjs";
|
|
4
4
|
import "./workspace-patterns-BN2A_60g.mjs";
|
|
5
5
|
|
|
6
6
|
export { scanForIntents };
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { a as parseFrontmatter, i as listNodeModulesPackageDirs, o as resolveDepDir, r as getDeps, s as toPosixPath, t as detectGlobalNodeModules } from "./utils-COlDcU72.mjs";
|
|
2
2
|
import { r as rewriteSkillLoadPaths } from "./skill-paths-8k9K9y26.mjs";
|
|
3
3
|
import { a as resolveWorkspacePackages, i as readWorkspacePatterns, r as findWorkspaceRoot } from "./workspace-patterns-BN2A_60g.mjs";
|
|
4
|
+
import { createRequire } from "node:module";
|
|
4
5
|
import { existsSync, readFileSync, readdirSync } from "node:fs";
|
|
5
|
-
import { join, relative, sep } from "node:path";
|
|
6
|
+
import { dirname, join, relative, resolve, sep } from "node:path";
|
|
6
7
|
|
|
7
8
|
//#region src/discovery/register.ts
|
|
8
9
|
function isLocalToProject(dirPath, projectRoot) {
|
|
@@ -132,13 +133,31 @@ function createDependencyWalker(opts) {
|
|
|
132
133
|
|
|
133
134
|
//#endregion
|
|
134
135
|
//#region src/scanner.ts
|
|
135
|
-
|
|
136
|
-
|
|
136
|
+
const requireFromHere = createRequire(import.meta.url);
|
|
137
|
+
function findPnpFile(start) {
|
|
138
|
+
let dir = resolve(start);
|
|
139
|
+
while (true) {
|
|
140
|
+
for (const fileName of [".pnp.cjs", ".pnp.js"]) {
|
|
141
|
+
const pnpPath = join(dir, fileName);
|
|
142
|
+
if (existsSync(pnpPath)) return pnpPath;
|
|
143
|
+
}
|
|
144
|
+
const next = dirname(dir);
|
|
145
|
+
if (next === dir) return null;
|
|
146
|
+
dir = next;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
function isYarnPnpProject(root) {
|
|
150
|
+
return findPnpFile(root) !== null;
|
|
151
|
+
}
|
|
152
|
+
function assertLocalNodeModulesSupported(root) {
|
|
137
153
|
if (existsSync(join(root, "deno.json")) && !existsSync(join(root, "node_modules"))) throw new Error("Deno without node_modules is not yet supported. Add `\"nodeModulesDir\": \"auto\"` to your deno.json to use intent.");
|
|
154
|
+
}
|
|
155
|
+
function detectPackageManager(root) {
|
|
138
156
|
const dirsToCheck = [root];
|
|
139
157
|
const wsRoot = findWorkspaceRoot(root);
|
|
140
158
|
if (wsRoot && wsRoot !== root) dirsToCheck.push(wsRoot);
|
|
141
159
|
for (const dir of dirsToCheck) {
|
|
160
|
+
if (isYarnPnpProject(dir)) return "yarn";
|
|
142
161
|
if (existsSync(join(dir, "pnpm-lock.yaml"))) return "pnpm";
|
|
143
162
|
if (existsSync(join(dir, "bun.lockb")) || existsSync(join(dir, "bun.lock"))) return "bun";
|
|
144
163
|
if (existsSync(join(dir, "yarn.lock"))) return "yarn";
|
|
@@ -146,6 +165,35 @@ function detectPackageManager(root) {
|
|
|
146
165
|
}
|
|
147
166
|
return "unknown";
|
|
148
167
|
}
|
|
168
|
+
function loadPnpApi(root) {
|
|
169
|
+
const pnpPath = findPnpFile(root);
|
|
170
|
+
if (!pnpPath) return null;
|
|
171
|
+
try {
|
|
172
|
+
const foundApi = requireFromHere("node:module").findPnpApi?.(root);
|
|
173
|
+
if (foundApi) return foundApi;
|
|
174
|
+
const pnpModule = requireFromHere(pnpPath);
|
|
175
|
+
if (typeof pnpModule.setup === "function") pnpModule.setup();
|
|
176
|
+
if (typeof pnpModule.getDependencyTreeRoots === "function" && typeof pnpModule.getPackageInformation === "function") return pnpModule;
|
|
177
|
+
return createRequire(join(dirname(pnpPath), "package.json"))("pnpapi");
|
|
178
|
+
} catch (err) {
|
|
179
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
180
|
+
throw new Error(`Yarn PnP project detected, but Intent could not load Yarn's PnP API from ${pnpPath}: ${msg}`);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
function getPnpLocatorKey(locator) {
|
|
184
|
+
return `${locator.name ?? "<top>"}@${locator.reference ?? "<top>"}`;
|
|
185
|
+
}
|
|
186
|
+
function getPnpDependencyLocator(dependencyName, target) {
|
|
187
|
+
if (target === null) return null;
|
|
188
|
+
if (Array.isArray(target)) return {
|
|
189
|
+
name: target[0],
|
|
190
|
+
reference: target[1]
|
|
191
|
+
};
|
|
192
|
+
return {
|
|
193
|
+
name: dependencyName,
|
|
194
|
+
reference: target
|
|
195
|
+
};
|
|
196
|
+
}
|
|
149
197
|
function validateIntentField(_pkgName, intent) {
|
|
150
198
|
if (!intent || typeof intent !== "object") return null;
|
|
151
199
|
const pb = intent;
|
|
@@ -304,6 +352,7 @@ function scanForIntents(root, options = {}) {
|
|
|
304
352
|
const projectRoot = root ?? process.cwd();
|
|
305
353
|
const scanScope = getScanScope(options);
|
|
306
354
|
const packageManager = detectPackageManager(projectRoot);
|
|
355
|
+
const pnpApi = scanScope === "global" ? null : loadPnpApi(projectRoot);
|
|
307
356
|
const nodeModulesDir = join(projectRoot, "node_modules");
|
|
308
357
|
const explicitGlobalNodeModules = process.env.INTENT_GLOBAL_NODE_MODULES?.trim() || null;
|
|
309
358
|
const packages = [];
|
|
@@ -378,7 +427,33 @@ function scanForIntents(root, options = {}) {
|
|
|
378
427
|
tryRegister,
|
|
379
428
|
warnings
|
|
380
429
|
});
|
|
430
|
+
function scanPnpPackages() {
|
|
431
|
+
if (!pnpApi) return;
|
|
432
|
+
const api = pnpApi;
|
|
433
|
+
const visited = /* @__PURE__ */ new Set();
|
|
434
|
+
const workspaceRoot = findWorkspaceRoot(projectRoot);
|
|
435
|
+
const projectLocator = api.findPackageLocator?.(projectRoot.endsWith(sep) ? projectRoot : `${projectRoot}${sep}`);
|
|
436
|
+
const roots = workspaceRoot && workspaceRoot !== projectRoot && projectLocator ? [projectLocator] : api.getDependencyTreeRoots?.() ?? (api.topLevel ? [api.topLevel] : []);
|
|
437
|
+
function visit(locator) {
|
|
438
|
+
const key = getPnpLocatorKey(locator);
|
|
439
|
+
if (visited.has(key)) return;
|
|
440
|
+
visited.add(key);
|
|
441
|
+
const info = api.getPackageInformation(locator);
|
|
442
|
+
if (!info) return;
|
|
443
|
+
tryRegister(info.packageLocation.replace(/[\\/]$/, ""), locator.name ?? "unknown");
|
|
444
|
+
for (const [dependencyName, target] of info.packageDependencies) {
|
|
445
|
+
const dependencyLocator = getPnpDependencyLocator(dependencyName, target);
|
|
446
|
+
if (dependencyLocator) visit(dependencyLocator);
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
for (const locator of roots) visit(locator);
|
|
450
|
+
}
|
|
381
451
|
function scanLocalPackages() {
|
|
452
|
+
if (pnpApi && !nodeModules.local.exists) {
|
|
453
|
+
scanPnpPackages();
|
|
454
|
+
return;
|
|
455
|
+
}
|
|
456
|
+
assertLocalNodeModulesSupported(projectRoot);
|
|
382
457
|
scanTarget(nodeModules.local);
|
|
383
458
|
walkWorkspacePackages();
|
|
384
459
|
walkKnownPackages();
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { i as readWorkspacePatterns, r as findWorkspaceRoot, t as findPackagesWithSkills } from "./workspace-patterns-BN2A_60g.mjs";
|
|
2
|
-
import { t as resolveProjectContext } from "./project-context-
|
|
2
|
+
import { t as resolveProjectContext } from "./project-context-alYMNoNa.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-t1i2o2-h.mjs";
|
|
2
2
|
export { EditPackageJsonResult, MonorepoResult, SetupGithubActionsResult, findPackagesWithSkills, findWorkspaceRoot, readWorkspacePatterns, resolveWorkspacePackages, runEditPackageJson, runEditPackageJsonAll, runSetupGithubActions };
|
package/dist/setup.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import "./utils-COlDcU72.mjs";
|
|
2
2
|
import { a as resolveWorkspacePackages, i as readWorkspacePatterns, r as findWorkspaceRoot, t as findPackagesWithSkills } from "./workspace-patterns-BN2A_60g.mjs";
|
|
3
|
-
import "./project-context-
|
|
4
|
-
import { n as runEditPackageJsonAll, r as runSetupGithubActions, t as runEditPackageJson } from "./setup-
|
|
3
|
+
import "./project-context-alYMNoNa.mjs";
|
|
4
|
+
import { n as runEditPackageJsonAll, r as runSetupGithubActions, t as runEditPackageJson } from "./setup-Dp-W8y0Y.mjs";
|
|
5
5
|
|
|
6
6
|
export { findPackagesWithSkills, findWorkspaceRoot, readWorkspacePatterns, resolveWorkspacePackages, runEditPackageJson, runEditPackageJsonAll, runSetupGithubActions };
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import { appendFileSync, writeFileSync } from "node:fs";
|
|
2
|
+
|
|
3
|
+
//#region src/workflow-review.ts
|
|
4
|
+
function collectStaleReviewItems(reports) {
|
|
5
|
+
const items = [];
|
|
6
|
+
for (const report of reports) {
|
|
7
|
+
for (const skill of report.skills ?? []) {
|
|
8
|
+
if (!skill?.needsReview) continue;
|
|
9
|
+
items.push({
|
|
10
|
+
type: "stale-skill",
|
|
11
|
+
library: report.library,
|
|
12
|
+
subject: skill.name,
|
|
13
|
+
reasons: skill.reasons ?? []
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
for (const signal of report.signals ?? []) {
|
|
17
|
+
if (signal?.needsReview === false) continue;
|
|
18
|
+
items.push({
|
|
19
|
+
type: signal?.type ?? "review-signal",
|
|
20
|
+
library: signal?.library ?? report.library,
|
|
21
|
+
subject: signal?.packageName ?? signal?.packageRoot ?? signal?.skill ?? signal?.artifactPath ?? signal?.subject ?? report.library,
|
|
22
|
+
reasons: signal?.reasons ?? [],
|
|
23
|
+
artifactPath: signal?.artifactPath,
|
|
24
|
+
packageName: signal?.packageName,
|
|
25
|
+
packageRoot: signal?.packageRoot,
|
|
26
|
+
skill: signal?.skill
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
return items;
|
|
31
|
+
}
|
|
32
|
+
function createFailedStaleReviewItem(library) {
|
|
33
|
+
return {
|
|
34
|
+
type: "stale-check-failed",
|
|
35
|
+
library,
|
|
36
|
+
subject: "intent stale --json",
|
|
37
|
+
reasons: ["The stale check command failed. Review the workflow logs before updating skills."]
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
function createWorkflowAdvisoryReviewItems(library, advisories) {
|
|
41
|
+
return advisories.map((advisory) => ({
|
|
42
|
+
type: "workflow-advisory",
|
|
43
|
+
library,
|
|
44
|
+
subject: "check-skills.yml",
|
|
45
|
+
reasons: [advisory]
|
|
46
|
+
}));
|
|
47
|
+
}
|
|
48
|
+
function buildStaleReviewBody(items) {
|
|
49
|
+
const grouped = /* @__PURE__ */ new Map();
|
|
50
|
+
for (const item of items) grouped.set(item.type, (grouped.get(item.type) ?? 0) + 1);
|
|
51
|
+
const signalRows = [...grouped.entries()].sort(([a], [b]) => a.localeCompare(b)).map(([type, count]) => `| \`${type}\` | ${count} |`);
|
|
52
|
+
const itemRows = items.map((item) => {
|
|
53
|
+
const subject = item.subject ? `\`${item.subject}\`` : "-";
|
|
54
|
+
const reasons = item.reasons?.length ? item.reasons.join("; ") : "-";
|
|
55
|
+
return `| \`${item.type}\` | ${subject} | \`${item.library}\` | ${reasons} |`;
|
|
56
|
+
});
|
|
57
|
+
const reasonBullets = items.map((item) => {
|
|
58
|
+
const subject = item.subject ? `\`${item.subject}\`` : "`unknown`";
|
|
59
|
+
const reasons = item.reasons?.length ? item.reasons.join("; ") : "Intent did not emit a detailed reason for this signal.";
|
|
60
|
+
return `- \`${item.type}\` for ${subject}: ${reasons}`;
|
|
61
|
+
});
|
|
62
|
+
const prompt = [
|
|
63
|
+
"You are helping maintain Intent skills for this repository.",
|
|
64
|
+
"",
|
|
65
|
+
"Goal:",
|
|
66
|
+
"Resolve the Intent skill review signals below while preserving the existing scope, taxonomy, and maintainer-reviewed artifacts.",
|
|
67
|
+
"",
|
|
68
|
+
"Review signals:",
|
|
69
|
+
JSON.stringify(items, null, 2),
|
|
70
|
+
"",
|
|
71
|
+
"Required workflow:",
|
|
72
|
+
"1. Read the existing `_artifacts/*domain_map.yaml`, `_artifacts/*skill_tree.yaml`, and generated `skills/**/SKILL.md` files.",
|
|
73
|
+
"2. Read each flagged package package.json, public exports, README/docs if present, and source entry points.",
|
|
74
|
+
"3. Compare flagged packages against the existing domains, skills, tasks, packages, covers, sources, tensions, and cross-references in the artifacts.",
|
|
75
|
+
"4. For each signal, decide whether it means existing skill coverage, a missing generated skill, a new skill candidate, out-of-scope coverage, or deferred work.",
|
|
76
|
+
"",
|
|
77
|
+
"Maintainer questions:",
|
|
78
|
+
"Before editing skills or artifacts, ask the maintainer:",
|
|
79
|
+
"1. For each flagged package, is this package user-facing enough to need agent guidance?",
|
|
80
|
+
"2. If yes, should it extend an existing skill or become a new skill?",
|
|
81
|
+
"3. If it extends an existing skill, which current skill should own it?",
|
|
82
|
+
"4. If it is out of scope, what short reason should be recorded in artifact coverage ignores?",
|
|
83
|
+
"5. Are any of these packages experimental or unstable enough to exclude for now?",
|
|
84
|
+
"",
|
|
85
|
+
"Decision rules:",
|
|
86
|
+
"- Do not auto-generate skills.",
|
|
87
|
+
"- Do not create broad new skill areas without maintainer confirmation.",
|
|
88
|
+
"- Prefer adding package coverage to an existing skill when the package is an implementation variant of an existing domain.",
|
|
89
|
+
"- Create a new skill only when the package introduces a distinct developer task or failure mode.",
|
|
90
|
+
"- Preserve current naming, path, and package layout conventions.",
|
|
91
|
+
"- Keep generated skills under the package-local `skills/` directory.",
|
|
92
|
+
"- Keep repo-root `_artifacts` as the reviewed plan.",
|
|
93
|
+
"",
|
|
94
|
+
"If maintainer confirms updates:",
|
|
95
|
+
"1. Update the relevant `_artifacts/*domain_map.yaml` or `_artifacts/*skill_tree.yaml`.",
|
|
96
|
+
"2. Update or create `SKILL.md` files only for confirmed coverage changes.",
|
|
97
|
+
"3. Keep `sources` aligned between artifact skill entries and SKILL frontmatter.",
|
|
98
|
+
"4. Bump `library_version` only for skills whose covered source package version changed.",
|
|
99
|
+
"5. Run `npx @tanstack/intent@latest validate` on touched skill directories.",
|
|
100
|
+
"6. Summarize every package as one of: existing-skill coverage, new skill, ignored, or deferred."
|
|
101
|
+
].join("\n");
|
|
102
|
+
return [
|
|
103
|
+
"## Intent Skill Review Needed",
|
|
104
|
+
"",
|
|
105
|
+
"Intent found skills, artifact coverage, or workspace package coverage that need maintainer review.",
|
|
106
|
+
"",
|
|
107
|
+
"### Summary",
|
|
108
|
+
"",
|
|
109
|
+
"| Signal | Count |",
|
|
110
|
+
"| --- | ---: |",
|
|
111
|
+
...signalRows,
|
|
112
|
+
"",
|
|
113
|
+
"### Why This PR Opened",
|
|
114
|
+
"",
|
|
115
|
+
...reasonBullets,
|
|
116
|
+
"",
|
|
117
|
+
"### Review Items",
|
|
118
|
+
"",
|
|
119
|
+
"| Signal | Subject | Library | Reason |",
|
|
120
|
+
"| --- | --- | --- | --- |",
|
|
121
|
+
...itemRows,
|
|
122
|
+
"",
|
|
123
|
+
"### Agent Prompt",
|
|
124
|
+
"",
|
|
125
|
+
"Paste this into your coding agent:",
|
|
126
|
+
"",
|
|
127
|
+
"```text",
|
|
128
|
+
prompt,
|
|
129
|
+
"```",
|
|
130
|
+
"",
|
|
131
|
+
"This PR is a review reminder only. It does not update skills automatically."
|
|
132
|
+
].join("\n");
|
|
133
|
+
}
|
|
134
|
+
function writeStaleReviewWorkflowFiles(items, options = {}) {
|
|
135
|
+
const outputPath = options.outputPath ?? process.env.GITHUB_OUTPUT;
|
|
136
|
+
const summaryPath = options.summaryPath ?? process.env.GITHUB_STEP_SUMMARY;
|
|
137
|
+
const reviewItemsPath = options.reviewItemsPath ?? "review-items.json";
|
|
138
|
+
const prBodyPath = options.prBodyPath ?? "pr-body.md";
|
|
139
|
+
const hasReview = items.length > 0;
|
|
140
|
+
writeFileSync(reviewItemsPath, JSON.stringify(items, null, 2) + "\n");
|
|
141
|
+
if (outputPath) appendFileSync(outputPath, `has_review=${hasReview ? "true" : "false"}\n`);
|
|
142
|
+
const summary = hasReview ? buildStaleReviewBody(items) + "\n" : [
|
|
143
|
+
"### Intent skill review",
|
|
144
|
+
"",
|
|
145
|
+
"No stale skills or coverage gaps found.",
|
|
146
|
+
""
|
|
147
|
+
].join("\n");
|
|
148
|
+
if (hasReview) writeFileSync(prBodyPath, summary);
|
|
149
|
+
if (summaryPath) appendFileSync(summaryPath, summary);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
//#endregion
|
|
153
|
+
export { writeStaleReviewWorkflowFiles as a, createWorkflowAdvisoryReviewItems as i, collectStaleReviewItems as n, createFailedStaleReviewItem as r, buildStaleReviewBody as t };
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import { a as writeStaleReviewWorkflowFiles, i as createWorkflowAdvisoryReviewItems, n as collectStaleReviewItems, r as createFailedStaleReviewItem, t as buildStaleReviewBody } from "./workflow-review-CwkPVIQf.mjs";
|
|
2
|
+
|
|
3
|
+
export { collectStaleReviewItems, createFailedStaleReviewItem, createWorkflowAdvisoryReviewItems, writeStaleReviewWorkflowFiles };
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
# check-skills.yml — Drop this into your library repo's .github/workflows/
|
|
2
2
|
#
|
|
3
|
-
#
|
|
4
|
-
# existing skills, artifact coverage, or workspace
|
|
3
|
+
# Validates intent skills on PRs. After a release or manual run, opens or
|
|
4
|
+
# updates one review PR when existing skills, artifact coverage, or workspace
|
|
5
|
+
# package coverage need review.
|
|
5
6
|
#
|
|
6
|
-
# Triggers: new release published, or
|
|
7
|
+
# Triggers: pull requests touching skills/artifacts, new release published, or
|
|
8
|
+
# manual workflow_dispatch.
|
|
7
9
|
#
|
|
8
|
-
# intent-workflow-version:
|
|
10
|
+
# intent-workflow-version: 3
|
|
9
11
|
#
|
|
10
12
|
# Template variables (replaced by `intent setup`):
|
|
11
13
|
# {{PACKAGE_LABEL}} — e.g. @tanstack/query or my-workspace workspace
|
|
@@ -13,6 +15,12 @@
|
|
|
13
15
|
name: Check Skills
|
|
14
16
|
|
|
15
17
|
on:
|
|
18
|
+
pull_request:
|
|
19
|
+
paths:
|
|
20
|
+
- 'skills/**'
|
|
21
|
+
- '**/skills/**'
|
|
22
|
+
- '_artifacts/**'
|
|
23
|
+
- '**/_artifacts/**'
|
|
16
24
|
release:
|
|
17
25
|
types: [published]
|
|
18
26
|
workflow_dispatch: {}
|
|
@@ -22,8 +30,28 @@ permissions:
|
|
|
22
30
|
pull-requests: write
|
|
23
31
|
|
|
24
32
|
jobs:
|
|
25
|
-
|
|
33
|
+
validate:
|
|
34
|
+
name: Validate intent skills
|
|
35
|
+
if: github.event_name == 'pull_request'
|
|
36
|
+
runs-on: ubuntu-latest
|
|
37
|
+
steps:
|
|
38
|
+
- name: Checkout
|
|
39
|
+
uses: actions/checkout@v4
|
|
40
|
+
|
|
41
|
+
- name: Setup Node
|
|
42
|
+
uses: actions/setup-node@v4
|
|
43
|
+
with:
|
|
44
|
+
node-version: 20
|
|
45
|
+
|
|
46
|
+
- name: Install intent
|
|
47
|
+
run: npm install -g @tanstack/intent
|
|
48
|
+
|
|
49
|
+
- name: Validate skills
|
|
50
|
+
run: intent validate --github-summary
|
|
51
|
+
|
|
52
|
+
review:
|
|
26
53
|
name: Check intent skill coverage
|
|
54
|
+
if: github.event_name != 'pull_request'
|
|
27
55
|
runs-on: ubuntu-latest
|
|
28
56
|
steps:
|
|
29
57
|
- name: Checkout
|
|
@@ -42,181 +70,7 @@ jobs:
|
|
|
42
70
|
- name: Check skills
|
|
43
71
|
id: stale
|
|
44
72
|
run: |
|
|
45
|
-
|
|
46
|
-
intent stale --json > stale.json
|
|
47
|
-
STATUS=$?
|
|
48
|
-
set -e
|
|
49
|
-
|
|
50
|
-
cat stale.json
|
|
51
|
-
|
|
52
|
-
if [ "$STATUS" -ne 0 ]; then
|
|
53
|
-
echo "has_review=true" >> "$GITHUB_OUTPUT"
|
|
54
|
-
echo "check_failed=true" >> "$GITHUB_OUTPUT"
|
|
55
|
-
cat > review-items.json <<'JSON'
|
|
56
|
-
[
|
|
57
|
-
{
|
|
58
|
-
"type": "stale-check-failed",
|
|
59
|
-
"library": "{{PACKAGE_LABEL}}",
|
|
60
|
-
"subject": "intent stale --json",
|
|
61
|
-
"reasons": ["The stale check command failed. Review the workflow logs before updating skills."]
|
|
62
|
-
}
|
|
63
|
-
]
|
|
64
|
-
JSON
|
|
65
|
-
else
|
|
66
|
-
node <<'NODE'
|
|
67
|
-
const fs = require('fs')
|
|
68
|
-
const reports = JSON.parse(fs.readFileSync('stale.json', 'utf8'))
|
|
69
|
-
const items = []
|
|
70
|
-
|
|
71
|
-
for (const report of reports) {
|
|
72
|
-
for (const skill of report.skills ?? []) {
|
|
73
|
-
if (!skill?.needsReview) continue
|
|
74
|
-
items.push({
|
|
75
|
-
type: 'stale-skill',
|
|
76
|
-
library: report.library,
|
|
77
|
-
subject: skill.name,
|
|
78
|
-
reasons: skill.reasons ?? [],
|
|
79
|
-
})
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
for (const signal of report.signals ?? []) {
|
|
83
|
-
if (signal?.needsReview === false) continue
|
|
84
|
-
items.push({
|
|
85
|
-
type: signal?.type ?? 'review-signal',
|
|
86
|
-
library: signal?.library ?? report.library,
|
|
87
|
-
subject:
|
|
88
|
-
signal?.packageName ??
|
|
89
|
-
signal?.packageRoot ??
|
|
90
|
-
signal?.skill ??
|
|
91
|
-
signal?.artifactPath ??
|
|
92
|
-
signal?.subject ??
|
|
93
|
-
report.library,
|
|
94
|
-
reasons: signal?.reasons ?? [],
|
|
95
|
-
artifactPath: signal?.artifactPath,
|
|
96
|
-
packageName: signal?.packageName,
|
|
97
|
-
packageRoot: signal?.packageRoot,
|
|
98
|
-
skill: signal?.skill,
|
|
99
|
-
})
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
fs.writeFileSync('review-items.json', JSON.stringify(items, null, 2) + '\n')
|
|
104
|
-
fs.appendFileSync(
|
|
105
|
-
process.env.GITHUB_OUTPUT,
|
|
106
|
-
`has_review=${items.length > 0 ? 'true' : 'false'}\n`,
|
|
107
|
-
)
|
|
108
|
-
NODE
|
|
109
|
-
fi
|
|
110
|
-
|
|
111
|
-
{
|
|
112
|
-
echo "review_items<<EOF"
|
|
113
|
-
cat review-items.json
|
|
114
|
-
echo "EOF"
|
|
115
|
-
} >> "$GITHUB_OUTPUT"
|
|
116
|
-
|
|
117
|
-
- name: Write clean summary
|
|
118
|
-
if: steps.stale.outputs.has_review == 'false'
|
|
119
|
-
run: |
|
|
120
|
-
{
|
|
121
|
-
echo "### Intent skill review"
|
|
122
|
-
echo ""
|
|
123
|
-
echo "No stale skills or coverage gaps found."
|
|
124
|
-
} >> "$GITHUB_STEP_SUMMARY"
|
|
125
|
-
|
|
126
|
-
- name: Build review PR body
|
|
127
|
-
if: steps.stale.outputs.has_review == 'true'
|
|
128
|
-
run: |
|
|
129
|
-
node <<'NODE'
|
|
130
|
-
const fs = require('fs')
|
|
131
|
-
const items = JSON.parse(fs.readFileSync('review-items.json', 'utf8'))
|
|
132
|
-
const grouped = new Map()
|
|
133
|
-
|
|
134
|
-
for (const item of items) {
|
|
135
|
-
grouped.set(item.type, (grouped.get(item.type) ?? 0) + 1)
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
const signalRows = [...grouped.entries()]
|
|
139
|
-
.sort(([a], [b]) => a.localeCompare(b))
|
|
140
|
-
.map(([type, count]) => `| \`${type}\` | ${count} |`)
|
|
141
|
-
|
|
142
|
-
const itemRows = items.map((item) => {
|
|
143
|
-
const subject = item.subject ? `\`${item.subject}\`` : '-'
|
|
144
|
-
const reasons = item.reasons?.length ? item.reasons.join('; ') : '-'
|
|
145
|
-
return `| \`${item.type}\` | ${subject} | \`${item.library}\` | ${reasons} |`
|
|
146
|
-
})
|
|
147
|
-
|
|
148
|
-
const prompt = [
|
|
149
|
-
'You are helping maintain Intent skills for this repository.',
|
|
150
|
-
'',
|
|
151
|
-
'Goal:',
|
|
152
|
-
'Resolve the Intent skill review signals below while preserving the existing scope, taxonomy, and maintainer-reviewed artifacts.',
|
|
153
|
-
'',
|
|
154
|
-
'Review signals:',
|
|
155
|
-
JSON.stringify(items, null, 2),
|
|
156
|
-
'',
|
|
157
|
-
'Required workflow:',
|
|
158
|
-
'1. Read the existing `_artifacts/*domain_map.yaml`, `_artifacts/*skill_tree.yaml`, and generated `skills/**/SKILL.md` files.',
|
|
159
|
-
'2. Read each flagged package package.json, public exports, README/docs if present, and source entry points.',
|
|
160
|
-
'3. Compare flagged packages against the existing domains, skills, tasks, packages, covers, sources, tensions, and cross-references in the artifacts.',
|
|
161
|
-
'4. For each signal, decide whether it means existing skill coverage, a missing generated skill, a new skill candidate, out-of-scope coverage, or deferred work.',
|
|
162
|
-
'',
|
|
163
|
-
'Maintainer questions:',
|
|
164
|
-
'Before editing skills or artifacts, ask the maintainer:',
|
|
165
|
-
'1. For each flagged package, is this package user-facing enough to need agent guidance?',
|
|
166
|
-
'2. If yes, should it extend an existing skill or become a new skill?',
|
|
167
|
-
'3. If it extends an existing skill, which current skill should own it?',
|
|
168
|
-
'4. If it is out of scope, what short reason should be recorded in artifact coverage ignores?',
|
|
169
|
-
'5. Are any of these packages experimental or unstable enough to exclude for now?',
|
|
170
|
-
'',
|
|
171
|
-
'Decision rules:',
|
|
172
|
-
'- Do not auto-generate skills.',
|
|
173
|
-
'- Do not create broad new skill areas without maintainer confirmation.',
|
|
174
|
-
'- Prefer adding package coverage to an existing skill when the package is an implementation variant of an existing domain.',
|
|
175
|
-
'- Create a new skill only when the package introduces a distinct developer task or failure mode.',
|
|
176
|
-
'- Preserve current naming, path, and package layout conventions.',
|
|
177
|
-
'- Keep generated skills under the package-local `skills/` directory.',
|
|
178
|
-
'- Keep repo-root `_artifacts` as the reviewed plan.',
|
|
179
|
-
'',
|
|
180
|
-
'If maintainer confirms updates:',
|
|
181
|
-
'1. Update the relevant `_artifacts/*domain_map.yaml` or `_artifacts/*skill_tree.yaml`.',
|
|
182
|
-
'2. Update or create `SKILL.md` files only for confirmed coverage changes.',
|
|
183
|
-
'3. Keep `sources` aligned between artifact skill entries and SKILL frontmatter.',
|
|
184
|
-
'4. Bump `library_version` only for skills whose covered source package version changed.',
|
|
185
|
-
'5. Run `npx @tanstack/intent@latest validate` on touched skill directories.',
|
|
186
|
-
'6. Summarize every package as one of: existing-skill coverage, new skill, ignored, or deferred.',
|
|
187
|
-
].join('\n')
|
|
188
|
-
|
|
189
|
-
const body = [
|
|
190
|
-
'## Intent Skill Review Needed',
|
|
191
|
-
'',
|
|
192
|
-
'Intent found skills, artifact coverage, or workspace package coverage that need maintainer review.',
|
|
193
|
-
'',
|
|
194
|
-
'### Summary',
|
|
195
|
-
'',
|
|
196
|
-
'| Signal | Count |',
|
|
197
|
-
'| --- | ---: |',
|
|
198
|
-
...signalRows,
|
|
199
|
-
'',
|
|
200
|
-
'### Review Items',
|
|
201
|
-
'',
|
|
202
|
-
'| Signal | Subject | Library | Reason |',
|
|
203
|
-
'| --- | --- | --- | --- |',
|
|
204
|
-
...itemRows,
|
|
205
|
-
'',
|
|
206
|
-
'### Agent Prompt',
|
|
207
|
-
'',
|
|
208
|
-
'Paste this into your coding agent:',
|
|
209
|
-
'',
|
|
210
|
-
'```text',
|
|
211
|
-
prompt,
|
|
212
|
-
'```',
|
|
213
|
-
'',
|
|
214
|
-
'This PR is a review reminder only. It does not update skills automatically.',
|
|
215
|
-
].join('\n')
|
|
216
|
-
|
|
217
|
-
fs.writeFileSync('pr-body.md', body + '\n')
|
|
218
|
-
fs.writeFileSync(process.env.GITHUB_STEP_SUMMARY, body + '\n')
|
|
219
|
-
NODE
|
|
73
|
+
intent stale --github-review --package-label "{{PACKAGE_LABEL}}"
|
|
220
74
|
|
|
221
75
|
- name: Open or update review PR
|
|
222
76
|
if: steps.stale.outputs.has_review == 'true'
|
package/package.json
CHANGED
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
# validate-skills.yml — Drop this into your library repo's .github/workflows/
|
|
2
|
-
#
|
|
3
|
-
# Validates skill files on PRs that touch the skills/ directory.
|
|
4
|
-
# Ensures frontmatter is correct, names match paths, and files stay under
|
|
5
|
-
# the 500-line limit.
|
|
6
|
-
|
|
7
|
-
name: Validate Skills
|
|
8
|
-
|
|
9
|
-
on:
|
|
10
|
-
pull_request:
|
|
11
|
-
paths:
|
|
12
|
-
- 'skills/**'
|
|
13
|
-
- '**/skills/**'
|
|
14
|
-
|
|
15
|
-
jobs:
|
|
16
|
-
validate:
|
|
17
|
-
name: Validate skill files
|
|
18
|
-
runs-on: ubuntu-latest
|
|
19
|
-
steps:
|
|
20
|
-
- name: Checkout
|
|
21
|
-
uses: actions/checkout@v4
|
|
22
|
-
|
|
23
|
-
- name: Setup Node
|
|
24
|
-
uses: actions/setup-node@v4
|
|
25
|
-
with:
|
|
26
|
-
node-version: 20
|
|
27
|
-
|
|
28
|
-
- name: Install intent CLI
|
|
29
|
-
run: npm install -g @tanstack/intent
|
|
30
|
-
|
|
31
|
-
- name: Find and validate skills
|
|
32
|
-
run: |
|
|
33
|
-
# Find all directories containing SKILL.md files
|
|
34
|
-
SKILLS_DIR=""
|
|
35
|
-
if [ -d "skills" ]; then
|
|
36
|
-
SKILLS_DIR="skills"
|
|
37
|
-
elif [ -d "packages" ]; then
|
|
38
|
-
# Monorepo — find skills/ under packages
|
|
39
|
-
for dir in packages/*/skills; do
|
|
40
|
-
if [ -d "$dir" ]; then
|
|
41
|
-
echo "Validating $dir..."
|
|
42
|
-
intent validate "$dir"
|
|
43
|
-
fi
|
|
44
|
-
done
|
|
45
|
-
exit 0
|
|
46
|
-
fi
|
|
47
|
-
|
|
48
|
-
if [ -n "$SKILLS_DIR" ]; then
|
|
49
|
-
intent validate "$SKILLS_DIR"
|
|
50
|
-
else
|
|
51
|
-
echo "No skills/ directory found — skipping validation."
|
|
52
|
-
fi
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|