renma 0.15.2 → 0.17.0
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/CHANGELOG.md +54 -1
- package/README.md +216 -655
- package/architecture.md +523 -0
- package/design.md +458 -0
- package/dist/agent-skills.d.ts +77 -0
- package/dist/agent-skills.d.ts.map +1 -0
- package/dist/agent-skills.js +468 -0
- package/dist/agent-skills.js.map +1 -0
- package/dist/catalog.d.ts.map +1 -1
- package/dist/catalog.js +38 -5
- package/dist/catalog.js.map +1 -1
- package/dist/cli-help.d.ts +13 -11
- package/dist/cli-help.d.ts.map +1 -1
- package/dist/cli-help.js +36 -11
- package/dist/cli-help.js.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +139 -40
- package/dist/cli.js.map +1 -1
- package/dist/commands/catalog.d.ts.map +1 -1
- package/dist/commands/catalog.js +4 -3
- package/dist/commands/catalog.js.map +1 -1
- package/dist/commands/graph.d.ts.map +1 -1
- package/dist/commands/graph.js +7 -12
- package/dist/commands/graph.js.map +1 -1
- package/dist/commands/readiness.d.ts.map +1 -1
- package/dist/commands/readiness.js +7 -6
- package/dist/commands/readiness.js.map +1 -1
- package/dist/commands/scaffold.d.ts.map +1 -1
- package/dist/commands/scaffold.js +59 -16
- package/dist/commands/scaffold.js.map +1 -1
- package/dist/commands/suggest-metadata.d.ts +3 -1
- package/dist/commands/suggest-metadata.d.ts.map +1 -1
- package/dist/commands/suggest-metadata.js +226 -7
- package/dist/commands/suggest-metadata.js.map +1 -1
- package/dist/commands/suggest-semantic-split.js +3 -3
- package/dist/commands/suggest-semantic-split.js.map +1 -1
- package/dist/dependency-resolution.d.ts +6 -0
- package/dist/dependency-resolution.d.ts.map +1 -0
- package/dist/dependency-resolution.js +11 -0
- package/dist/dependency-resolution.js.map +1 -0
- package/dist/diagnostic-ids.d.ts +29 -0
- package/dist/diagnostic-ids.d.ts.map +1 -1
- package/dist/diagnostic-ids.js +28 -0
- package/dist/diagnostic-ids.js.map +1 -1
- package/dist/discovery.d.ts +50 -0
- package/dist/discovery.d.ts.map +1 -1
- package/dist/discovery.js +183 -25
- package/dist/discovery.js.map +1 -1
- package/dist/metadata.d.ts +3 -1
- package/dist/metadata.d.ts.map +1 -1
- package/dist/metadata.js +166 -29
- package/dist/metadata.js.map +1 -1
- package/dist/model.d.ts +1 -0
- package/dist/model.d.ts.map +1 -1
- package/dist/report.d.ts.map +1 -1
- package/dist/report.js +16 -1
- package/dist/report.js.map +1 -1
- package/dist/repository-paths.d.ts +13 -0
- package/dist/repository-paths.d.ts.map +1 -1
- package/dist/repository-paths.js +60 -2
- package/dist/repository-paths.js.map +1 -1
- package/dist/rules.d.ts.map +1 -1
- package/dist/rules.js +97 -107
- package/dist/rules.js.map +1 -1
- package/dist/scanner.d.ts.map +1 -1
- package/dist/scanner.js +3 -1
- package/dist/scanner.js.map +1 -1
- package/dist/security-diagnostics.d.ts.map +1 -1
- package/dist/security-diagnostics.js +98 -35
- package/dist/security-diagnostics.js.map +1 -1
- package/dist/security-policy-inventory.js +11 -4
- package/dist/security-policy-inventory.js.map +1 -1
- package/dist/security-policy.d.ts +27 -1
- package/dist/security-policy.d.ts.map +1 -1
- package/dist/security-policy.js +247 -34
- package/dist/security-policy.js.map +1 -1
- package/dist/skill-migration.d.ts +30 -0
- package/dist/skill-migration.d.ts.map +1 -0
- package/dist/skill-migration.js +566 -0
- package/dist/skill-migration.js.map +1 -0
- package/dist/trust-graph.d.ts.map +1 -1
- package/dist/trust-graph.js +7 -11
- package/dist/trust-graph.js.map +1 -1
- package/dist/types.d.ts +3 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/yaml-frontmatter.d.ts +26 -0
- package/dist/yaml-frontmatter.d.ts.map +1 -0
- package/dist/yaml-frontmatter.js +114 -0
- package/dist/yaml-frontmatter.js.map +1 -0
- package/docs/README.md +55 -0
- package/docs/advanced-skill-authoring.md +140 -0
- package/docs/agent-skills-compatibility.md +500 -0
- package/docs/authoring-guide.md +324 -0
- package/docs/context-conflict-diagnostics.md +32 -0
- package/docs/context-language-diagnostics.md +99 -0
- package/docs/context-lens.md +253 -0
- package/docs/context-lifecycle-diagnostics.md +55 -0
- package/docs/diagnostics.md +373 -0
- package/docs/metadata-budget.md +14 -0
- package/docs/repository-context-bom.md +113 -0
- package/docs/security-policy.md +338 -0
- package/docs/user-manual.md +813 -0
- package/examples/context-lens/README.md +101 -0
- package/examples/context-lens/contexts/testing/boundary-value-analysis.md +23 -0
- package/examples/context-lens/lenses/testing/spec-review-boundary-values.md +29 -0
- package/examples/context-lens/lenses/testing/test-design-boundary-values.md +29 -0
- package/examples/context-lens/skills/testing/spec-review/SKILL.md +49 -0
- package/examples/context-repo/README.md +149 -0
- package/examples/context-repo/contexts/domain/payment/idempotency.md +20 -0
- package/examples/context-repo/contexts/testing/boundary-value-analysis.md +19 -0
- package/examples/context-repo/contexts/testing/negative-testing.md +19 -0
- package/examples/context-repo/lenses/testing/spec-review-boundary-values.md +35 -0
- package/examples/context-repo/renma.config.json +12 -0
- package/examples/context-repo/skills/testing/spec-review/SKILL.md +66 -0
- package/examples/context-repo/tools/appium/README.md +14 -0
- package/examples/github-actions/renma-ci-report.yml +36 -0
- package/examples/interactive-placeholder/README.md +104 -0
- package/examples/interactive-placeholder/assets/template.txt +1 -0
- package/examples/interactive-placeholder/renma.config.json +6 -0
- package/examples/interactive-placeholder/skills/replace-placeholder/SKILL.md +54 -0
- package/examples/interactive-placeholder/tools/README.md +48 -0
- package/examples/interactive-placeholder/tools/placeholder-demo.mjs +99 -0
- package/package.json +11 -1
- package/plan-discovery.md +740 -0
- package/plan.md +150 -0
- package/scripts/verify-package.mjs +97 -0
package/dist/rules.js
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
|
+
import { resolvedAgentSkillDescription } from "./agent-skills.js";
|
|
2
3
|
import { addDaysIsoDate, isIsoDate, parseDayDuration, todayIsoDate, } from "./freshness.js";
|
|
3
4
|
import { DIAGNOSTIC_IDS } from "./diagnostic-ids.js";
|
|
4
|
-
import {
|
|
5
|
+
import { classifyRepositorySkillEntrypointPath, classifyRepositorySkillPath, normalizeRepositoryRelativePath, } from "./discovery.js";
|
|
6
|
+
import { helperScriptPath, resolveHelperScriptPath, } from "./repository-paths.js";
|
|
7
|
+
import { parseAssetMetadata } from "./metadata.js";
|
|
5
8
|
import { runRuleRegistry } from "./rule-engine.js";
|
|
6
9
|
const SECRET_PATTERN = /\b(?:password|passwd|token|api[_-]?key|secret|credential|private[_-]?key)\b\s*[:=]\s*["']?([A-Za-z0-9_./+=-]{8,})/i;
|
|
7
10
|
const PRIVATE_KEY_PATTERN = /-----BEGIN (?:RSA |OPENSSH |EC |DSA )?PRIVATE KEY-----/;
|
|
@@ -131,7 +134,7 @@ function rulesForEvaluationDate(evaluationDate, repositoryPaths) {
|
|
|
131
134
|
return [
|
|
132
135
|
{
|
|
133
136
|
id: "strict-layout-policy",
|
|
134
|
-
run: (context) => strictLayoutPolicyFindings(context.documents, context.
|
|
137
|
+
run: (context) => strictLayoutPolicyFindings(context.documents, context.catalog, repositoryPaths),
|
|
135
138
|
},
|
|
136
139
|
{
|
|
137
140
|
id: "security",
|
|
@@ -434,7 +437,7 @@ function orphanedContextLensFindings(entries, resolver) {
|
|
|
434
437
|
confidence: "medium",
|
|
435
438
|
evidence: metadataFindingEvidence(entry.sourcePath, "Active context lens has no incoming requires_lens or optional_lens references from skills."),
|
|
436
439
|
whyItMatters: "Context lenses are easier to review when a skill declares how the purpose-oriented interpretation is used. An unreferenced active lens may be newly created, intentionally staged, or stale.",
|
|
437
|
-
remediation: "If the lens should be used, reference it from a
|
|
440
|
+
remediation: "If the lens should be used, reference it from a canonical Skill with metadata.renma.requires-lens or metadata.renma.optional-lens JSON-array metadata. Pre-0.16-only Skills use requires_lens or optional_lens only during migration. If the lens is not ready or no longer needed, update its lifecycle status after review.",
|
|
438
441
|
constraints: [
|
|
439
442
|
"Do not make Renma select runtime lenses.",
|
|
440
443
|
"Do not rank or retrieve lenses semantically.",
|
|
@@ -446,7 +449,7 @@ function orphanedContextLensFindings(entries, resolver) {
|
|
|
446
449
|
"Run renma catalog.",
|
|
447
450
|
"Run renma graph focused on the lens or owning skill.",
|
|
448
451
|
],
|
|
449
|
-
llmHint: `Search for
|
|
452
|
+
llmHint: `Search for Skills that should declare "${entry.id}" as a required or optional lens. For canonical Skills, update metadata.renma.requires-lens or metadata.renma.optional-lens as a JSON-array string; use requires_lens or optional_lens only for pre-0.16 migration inputs. Do not add runtime selection logic or prompt assembly.`,
|
|
450
453
|
details: {
|
|
451
454
|
assetId: entry.id,
|
|
452
455
|
sourcePath: entry.sourcePath,
|
|
@@ -640,7 +643,9 @@ function shapeFindings(document) {
|
|
|
640
643
|
return [];
|
|
641
644
|
const text = document.artifact.content.toLowerCase();
|
|
642
645
|
const findings = [];
|
|
643
|
-
const description = document.
|
|
646
|
+
const description = document.artifact.kind === "skill"
|
|
647
|
+
? (resolvedAgentSkillDescription(document) ?? "")
|
|
648
|
+
: (document.metadata.description ?? "");
|
|
644
649
|
const tokenCount = approximateTokenCount(document.artifact.content);
|
|
645
650
|
if (!description) {
|
|
646
651
|
findings.push(documentFinding(document, DIAGNOSTIC_IDS.QUAL_MISSING_DESCRIPTION, "Skill is missing an explicit description", "quality", "medium", "Add frontmatter description so agents can route to the skill intentionally."));
|
|
@@ -804,7 +809,10 @@ function reusableContextCandidateFinding(document, tokenCount) {
|
|
|
804
809
|
function supportSharedContextCandidateFindings(document) {
|
|
805
810
|
if (document.artifact.kind !== "reference")
|
|
806
811
|
return [];
|
|
807
|
-
|
|
812
|
+
const skillPath = classifyRepositorySkillPath(document.artifact.path);
|
|
813
|
+
if (skillPath?.kind !== "support" ||
|
|
814
|
+
skillPath.supportDirectory !== "references" ||
|
|
815
|
+
!document.artifact.path.endsWith(".md")) {
|
|
808
816
|
return [];
|
|
809
817
|
}
|
|
810
818
|
const tokenCount = approximateTokenCount(document.artifact.content);
|
|
@@ -936,7 +944,8 @@ function contextPathNonSemanticFindings(document) {
|
|
|
936
944
|
function skillContextReferenceNotDeclaredFindings(document) {
|
|
937
945
|
if (document.artifact.kind !== "skill")
|
|
938
946
|
return [];
|
|
939
|
-
const
|
|
947
|
+
const operationalMetadata = parseAssetMetadata(document).metadata;
|
|
948
|
+
const declaredContexts = new Set(operationalMetadata.requiresContext);
|
|
940
949
|
const bodyLineIndexes = markdownBodyLineIndexes(document);
|
|
941
950
|
const matches = new Map();
|
|
942
951
|
for (const index of bodyLineIndexes) {
|
|
@@ -961,7 +970,7 @@ function skillContextReferenceNotDeclaredFindings(document) {
|
|
|
961
970
|
confidence: "high",
|
|
962
971
|
evidence: evidence(document, match.line, match.text),
|
|
963
972
|
whyItMatters: "Declared context references make skill/context relationships visible to catalog, graph, and validation reports. If a skill only mentions a context in prose, humans may see the dependency but repository tooling cannot validate it.",
|
|
964
|
-
remediation: "Add the referenced shared context asset to
|
|
973
|
+
remediation: "Add the referenced shared context asset to metadata.renma.requires-context as a JSON-array string, or remove the prose reference if it is no longer needed. Pre-0.16 requires_context is migration input only and is not operational in Renma 0.16.0.",
|
|
965
974
|
constraints: [
|
|
966
975
|
"Do not select runtime context.",
|
|
967
976
|
"Do not assemble prompt packages.",
|
|
@@ -973,9 +982,9 @@ function skillContextReferenceNotDeclaredFindings(document) {
|
|
|
973
982
|
"Run renma catalog.",
|
|
974
983
|
"Confirm the skill/context relationship appears in metadata and catalog output.",
|
|
975
984
|
],
|
|
976
|
-
llmHint: `Find context paths mentioned in the
|
|
985
|
+
llmHint: `Find context paths mentioned in the Skill body and add the missing declaration using metadata.renma.requires-context as a JSON-array string. Pre-0.16 requires_context is accepted only by suggest-metadata and is not operational. Missing declaration: ${referencedPath}`,
|
|
977
986
|
details: {
|
|
978
|
-
source:
|
|
987
|
+
source: operationalMetadata.id ?? document.artifact.path,
|
|
979
988
|
target: referencedPath,
|
|
980
989
|
referenceKind: "requires_context",
|
|
981
990
|
sourcePath: document.artifact.path,
|
|
@@ -1002,6 +1011,7 @@ function skillReferencesSupersededAssetFindings(documents) {
|
|
|
1002
1011
|
const skill = skillsByPath.get(skillPath);
|
|
1003
1012
|
if (!skill)
|
|
1004
1013
|
return [];
|
|
1014
|
+
const skillMetadata = parseAssetMetadata(skill).metadata;
|
|
1005
1015
|
const referencedFrom = skillReferenceLine(skill, document.artifact.path);
|
|
1006
1016
|
if (!referencedFrom)
|
|
1007
1017
|
return [];
|
|
@@ -1042,7 +1052,7 @@ function skillReferencesSupersededAssetFindings(documents) {
|
|
|
1042
1052
|
],
|
|
1043
1053
|
llmHint: "Inspect the deprecated local support file and its superseded_by or canonical_context metadata. If the shared context asset is now canonical, update skill guidance and metadata to reference the shared context directly. Keep the local reference only if it contains truly local notes or is intentionally preserved as a compatibility shim.",
|
|
1044
1054
|
details: {
|
|
1045
|
-
source:
|
|
1055
|
+
source: skillMetadata.id ?? skill.artifact.path,
|
|
1046
1056
|
target: metadataText(document.metadata.id) ?? document.artifact.path,
|
|
1047
1057
|
referenceKind: "body_reference",
|
|
1048
1058
|
sourcePath: skill.artifact.path,
|
|
@@ -1055,20 +1065,24 @@ function skillReferencesSupersededAssetFindings(documents) {
|
|
|
1055
1065
|
});
|
|
1056
1066
|
}
|
|
1057
1067
|
function isSkillLocalReference(document) {
|
|
1068
|
+
const skillPath = classifyRepositorySkillPath(document.artifact.path);
|
|
1058
1069
|
return (document.artifact.kind === "reference" &&
|
|
1059
|
-
|
|
1070
|
+
skillPath?.kind === "support" &&
|
|
1071
|
+
skillPath.supportDirectory === "references" &&
|
|
1072
|
+
document.artifact.path.endsWith(".md"));
|
|
1060
1073
|
}
|
|
1061
1074
|
function sharedContextTargets(document) {
|
|
1075
|
+
const operationalMetadata = parseAssetMetadata(document).metadata;
|
|
1062
1076
|
return [
|
|
1063
|
-
...
|
|
1077
|
+
...operationalMetadata.supersededBy,
|
|
1064
1078
|
...listMetadataValue(document.metadata.canonical_context),
|
|
1065
1079
|
].filter((target, index, targets) => /^contexts?\//u.test(target) && targets.indexOf(target) === index);
|
|
1066
1080
|
}
|
|
1067
1081
|
function parentSkillPath(referencePath) {
|
|
1068
|
-
const
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1082
|
+
const skillPath = classifyRepositorySkillPath(referencePath);
|
|
1083
|
+
return skillPath?.kind === "support"
|
|
1084
|
+
? `${skillPath.skillDirectory}/SKILL.md`
|
|
1085
|
+
: undefined;
|
|
1072
1086
|
}
|
|
1073
1087
|
function skillReferenceLine(skill, referencePath) {
|
|
1074
1088
|
const skillDir = path.posix.dirname(skill.artifact.path);
|
|
@@ -1084,18 +1098,23 @@ function skillReferenceLine(skill, referencePath) {
|
|
|
1084
1098
|
}
|
|
1085
1099
|
function assetReferencesSupersededAssetFindings(documents) {
|
|
1086
1100
|
const supersededAssets = documents
|
|
1087
|
-
.map((document) =>
|
|
1088
|
-
document
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1101
|
+
.map((document) => {
|
|
1102
|
+
const metadata = parseAssetMetadata(document).metadata;
|
|
1103
|
+
return {
|
|
1104
|
+
document,
|
|
1105
|
+
metadata,
|
|
1106
|
+
canonicalTargets: sharedContextTargets(document),
|
|
1107
|
+
};
|
|
1108
|
+
})
|
|
1109
|
+
.filter(({ metadata, canonicalTargets }) => metadata.status === "deprecated" ||
|
|
1110
|
+
metadata.status === "archived" ||
|
|
1093
1111
|
canonicalTargets.length > 0)
|
|
1094
1112
|
.filter(({ canonicalTargets }) => canonicalTargets.length > 0);
|
|
1095
1113
|
return documents.flatMap((referencingDocument) => {
|
|
1096
1114
|
if (referencingDocument.artifact.kind === "skill")
|
|
1097
1115
|
return [];
|
|
1098
|
-
|
|
1116
|
+
const referencingMetadata = parseAssetMetadata(referencingDocument).metadata;
|
|
1117
|
+
return supersededAssets.flatMap(({ document, metadata, canonicalTargets }) => {
|
|
1099
1118
|
if (document.artifact.path === referencingDocument.artifact.path) {
|
|
1100
1119
|
return [];
|
|
1101
1120
|
}
|
|
@@ -1139,13 +1158,12 @@ function assetReferencesSupersededAssetFindings(documents) {
|
|
|
1139
1158
|
],
|
|
1140
1159
|
llmHint: "Inspect the referenced deprecated asset and its superseded_by or canonical context metadata. If the canonical shared context is the intended source of truth, update this asset to reference that context directly. Keep the superseded file only when it serves a deliberate compatibility or migration role.",
|
|
1141
1160
|
details: {
|
|
1142
|
-
source:
|
|
1143
|
-
|
|
1144
|
-
target: metadataText(document.metadata.id) ?? document.artifact.path,
|
|
1161
|
+
source: referencingMetadata.id ?? referencingDocument.artifact.path,
|
|
1162
|
+
target: metadata.id ?? document.artifact.path,
|
|
1145
1163
|
referenceKind: "body_reference",
|
|
1146
1164
|
sourcePath: referencingDocument.artifact.path,
|
|
1147
1165
|
targetPath: document.artifact.path,
|
|
1148
|
-
targetStatus:
|
|
1166
|
+
targetStatus: metadata.status,
|
|
1149
1167
|
replacementTargets: canonicalTargets,
|
|
1150
1168
|
},
|
|
1151
1169
|
},
|
|
@@ -1336,14 +1354,13 @@ function matchingLineFindings(document, matcher) {
|
|
|
1336
1354
|
];
|
|
1337
1355
|
});
|
|
1338
1356
|
}
|
|
1339
|
-
function strictLayoutPolicyFindings(documents,
|
|
1357
|
+
function strictLayoutPolicyFindings(documents, catalog, repositoryPaths) {
|
|
1340
1358
|
const findings = [];
|
|
1341
1359
|
const paths = repositoryPaths ??
|
|
1342
1360
|
new Set(documents.map((document) => document.artifact.path));
|
|
1343
1361
|
for (const document of documents) {
|
|
1344
|
-
findings.push(...disallowedSkillAssetFindings(document, config));
|
|
1345
1362
|
findings.push(...thinSkillLayoutFindings(document));
|
|
1346
|
-
findings.push(...helperCommandFindings(document, paths
|
|
1363
|
+
findings.push(...helperCommandFindings(document, paths));
|
|
1347
1364
|
findings.push(...layoutConsistencyFindings(document));
|
|
1348
1365
|
findings.push(...contextRootFindings(document));
|
|
1349
1366
|
findings.push(...helperRootFindings(document));
|
|
@@ -1353,34 +1370,9 @@ function strictLayoutPolicyFindings(documents, config, catalog, repositoryPaths)
|
|
|
1353
1370
|
}
|
|
1354
1371
|
return findings;
|
|
1355
1372
|
}
|
|
1356
|
-
function disallowedSkillAssetFindings(document, config) {
|
|
1357
|
-
const match = document.artifact.path.match(/^skills\/([^/]+)\/(references|profiles|examples|scripts)\/(.+)$/);
|
|
1358
|
-
if (!match)
|
|
1359
|
-
return [];
|
|
1360
|
-
const [, skillName = "", assetRoot = "", rest = ""] = match;
|
|
1361
|
-
const target = canonicalSkillAssetTarget(config, skillName, assetRoot, rest);
|
|
1362
|
-
return [
|
|
1363
|
-
documentFinding(document, DIAGNOSTIC_IDS.LAYOUT_DISALLOWED_SKILL_ASSET, `Disallowed skill-local ${assetRoot} asset`, "structure", assetRoot === "scripts" ? "medium" : "low", `Move this file to \`${target}\` and update every repo-local reference to the new canonical path.`, {
|
|
1364
|
-
whyItMatters: "Strict layout keeps skills as thin entrypoints, shared context under contexts/, and executable helpers under tools/ so agents can migrate repositories deterministically.",
|
|
1365
|
-
verificationSteps: [
|
|
1366
|
-
`Move ${document.artifact.path} to ${target}.`,
|
|
1367
|
-
"Run renma scan and renma readiness again.",
|
|
1368
|
-
],
|
|
1369
|
-
llmHint: `Move ${document.artifact.path} to ${target}, then rewrite references from the old path to the new path. Do not leave a compatibility shim unless another Renma finding requires it.`,
|
|
1370
|
-
}),
|
|
1371
|
-
];
|
|
1372
|
-
}
|
|
1373
|
-
function canonicalSkillAssetTarget(config, skillName, assetRoot, rest) {
|
|
1374
|
-
const workflow = canonicalWorkflowName(config, skillName);
|
|
1375
|
-
if (assetRoot === "scripts")
|
|
1376
|
-
return helperAssetPath(config, workflow, `scripts/${rest}`);
|
|
1377
|
-
return contextAssetPath(config, workflow, `${assetRoot}/${rest}`);
|
|
1378
|
-
}
|
|
1379
|
-
function canonicalWorkflowName(config, skillName) {
|
|
1380
|
-
return config.layout.workflowAliases[skillName] ?? skillName;
|
|
1381
|
-
}
|
|
1382
1373
|
function thinSkillLayoutFindings(document) {
|
|
1383
|
-
if (
|
|
1374
|
+
if (classifyRepositorySkillEntrypointPath(document.artifact.path)?.kind !==
|
|
1375
|
+
"canonical")
|
|
1384
1376
|
return [];
|
|
1385
1377
|
const findings = [];
|
|
1386
1378
|
const wordCount = words(document.artifact.content).length;
|
|
@@ -1391,42 +1383,50 @@ function thinSkillLayoutFindings(document) {
|
|
|
1391
1383
|
if (wordCount > 450 ||
|
|
1392
1384
|
procedureSignals.some((pattern) => pattern.test(document.artifact.content))) {
|
|
1393
1385
|
findings.push(documentFinding(document, DIAGNOSTIC_IDS.LAYOUT_SKILL_NOT_THIN, "Skill entrypoint contains procedure content", "structure", wordCount > 700 ? "medium" : "low", "Move reusable procedure content into contexts/** and keep SKILL.md as a concise router that points to the required context assets.", {
|
|
1394
|
-
whyItMatters: "
|
|
1386
|
+
whyItMatters: "Canonical Skill entrypoints should route agents instead of owning reusable knowledge, helper implementations, or detailed procedures.",
|
|
1395
1387
|
verificationSteps: [
|
|
1396
1388
|
"Confirm SKILL.md contains routing guidance and required context references only.",
|
|
1397
1389
|
"Run renma readiness and check layout.skills_thin.",
|
|
1398
1390
|
],
|
|
1399
|
-
llmHint: "Extract long procedure sections into contexts/**, add
|
|
1391
|
+
llmHint: "Extract long procedure sections into contexts/**, add metadata.renma.requires-context or metadata.renma.optional-context JSON-array strings, and keep SKILL.md focused on when to use or not use the Skill. Pre-0.16 top-level references are migration input only.",
|
|
1400
1392
|
}));
|
|
1401
1393
|
}
|
|
1402
1394
|
const command = firstExecutableCommand(document);
|
|
1403
1395
|
if (command) {
|
|
1404
|
-
findings.push(findingAt(document, DIAGNOSTIC_IDS.LAYOUT_SKILL_EXECUTABLE_COMMAND, "Skill entrypoint contains executable command", "structure", "low", command.line, command.command, "Move executable
|
|
1405
|
-
whyItMatters: "Executable setup commands in SKILL.md make the entrypoint procedural
|
|
1396
|
+
findings.push(findingAt(document, DIAGNOSTIC_IDS.LAYOUT_SKILL_EXECUTABLE_COMMAND, "Skill entrypoint contains executable command", "structure", "low", command.line, command.command, "Move executable command detail into Skill-local references or governed contexts/** procedures, and keep SKILL.md as a router.", {
|
|
1397
|
+
whyItMatters: "Executable setup commands in SKILL.md make the entrypoint procedural instead of a concise routing layer.",
|
|
1406
1398
|
verificationSteps: [
|
|
1407
1399
|
"Move the command guidance to a context/reference/procedure.",
|
|
1408
|
-
"If the command invokes a helper script, ensure it
|
|
1400
|
+
"If the command invokes a helper script, ensure it resolves to local scripts/** in the owning Skill or a shared tools/** helper.",
|
|
1409
1401
|
],
|
|
1410
1402
|
}));
|
|
1411
1403
|
}
|
|
1412
1404
|
return findings;
|
|
1413
1405
|
}
|
|
1414
|
-
function helperCommandFindings(document, paths
|
|
1406
|
+
function helperCommandFindings(document, paths) {
|
|
1415
1407
|
const findings = [];
|
|
1416
1408
|
for (const command of executableCommands(document)) {
|
|
1417
|
-
const
|
|
1418
|
-
if (!
|
|
1409
|
+
const commandPath = helperScriptPath(command.command);
|
|
1410
|
+
if (!commandPath)
|
|
1419
1411
|
continue;
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
],
|
|
1412
|
+
const resolution = resolveHelperScriptPath(document.artifact.path, commandPath);
|
|
1413
|
+
if (resolution.kind === "unscoped")
|
|
1414
|
+
continue;
|
|
1415
|
+
if (resolution.kind === "unsafe") {
|
|
1416
|
+
findings.push(unresolvedHelperCommandFinding(document, command, commandPath, {
|
|
1417
|
+
details: { unsafePath: true },
|
|
1427
1418
|
}));
|
|
1428
1419
|
continue;
|
|
1429
1420
|
}
|
|
1421
|
+
const scriptPath = resolution.path;
|
|
1422
|
+
const skillPath = classifyRepositorySkillPath(scriptPath);
|
|
1423
|
+
if (skillPath?.kind === "support" &&
|
|
1424
|
+
skillPath.supportDirectory === "scripts") {
|
|
1425
|
+
if (!paths.has(scriptPath)) {
|
|
1426
|
+
findings.push(unresolvedHelperCommandFinding(document, command, scriptPath));
|
|
1427
|
+
}
|
|
1428
|
+
continue;
|
|
1429
|
+
}
|
|
1430
1430
|
if (scriptPath.includes("/scripts/") && !scriptPath.startsWith("tools/")) {
|
|
1431
1431
|
findings.push(findingAt(document, DIAGNOSTIC_IDS.PATH_HELPER_COMMAND_NON_TOOLS, "Helper command does not use tools root", "structure", "low", command.line, command.command, "Update helper script commands to reference scripts under tools/**.", {
|
|
1432
1432
|
whyItMatters: "Helper commands should resolve to non-context helper assets in tools/** so contexts remain LLM-readable guidance.",
|
|
@@ -1434,9 +1434,7 @@ function helperCommandFindings(document, paths, config) {
|
|
|
1434
1434
|
continue;
|
|
1435
1435
|
}
|
|
1436
1436
|
if (scriptPath.startsWith("tools/") && !paths.has(scriptPath)) {
|
|
1437
|
-
findings.push(
|
|
1438
|
-
whyItMatters: "Agents need helper commands in markdown procedures to resolve deterministically before running them.",
|
|
1439
|
-
}));
|
|
1437
|
+
findings.push(unresolvedHelperCommandFinding(document, command, scriptPath));
|
|
1440
1438
|
}
|
|
1441
1439
|
}
|
|
1442
1440
|
return findings;
|
|
@@ -1456,10 +1454,6 @@ function layoutConsistencyFindings(document) {
|
|
|
1456
1454
|
pattern: /Each skill includes a self-improvement prompt/i,
|
|
1457
1455
|
message: "Describe self-improvement prompts as loaded context/reference/procedure workflow prompts instead of SKILL.md-only content.",
|
|
1458
1456
|
},
|
|
1459
|
-
{
|
|
1460
|
-
pattern: /skills\/[^/\s`]+\/(?:references|profiles|examples|scripts)\//,
|
|
1461
|
-
message: "Update docs to point to canonical contexts/** assets or tools/** helper scripts instead of skill-local support directories.",
|
|
1462
|
-
},
|
|
1463
1457
|
{
|
|
1464
1458
|
pattern: /\]\(contexts\/tools\//,
|
|
1465
1459
|
message: "Use backtick repo-root paths for contexts/tools/... references instead of file-relative Markdown links.",
|
|
@@ -1470,10 +1464,10 @@ function layoutConsistencyFindings(document) {
|
|
|
1470
1464
|
if (!match)
|
|
1471
1465
|
continue;
|
|
1472
1466
|
const line = lineForOffset(text, match.index ?? 0);
|
|
1473
|
-
findings.push(findingAt(document, DIAGNOSTIC_IDS.DOCS_LAYOUT_INCONSISTENT, "Repository docs
|
|
1474
|
-
whyItMatters: "README.md and AGENTS.md should
|
|
1467
|
+
findings.push(findingAt(document, DIAGNOSTIC_IDS.DOCS_LAYOUT_INCONSISTENT, "Repository docs contradict the supported layout", "maintenance", "low", line, lineText(document, line), stale.message, {
|
|
1468
|
+
whyItMatters: "README.md and AGENTS.md should describe the current repository model without treating valid Skill-local support as stale.",
|
|
1475
1469
|
verificationSteps: [
|
|
1476
|
-
"
|
|
1470
|
+
"Confirm docs distinguish canonical Skill roots and valid local support from governed contexts/** assets and shared tools/** helpers.",
|
|
1477
1471
|
"Run renma scan again.",
|
|
1478
1472
|
],
|
|
1479
1473
|
}));
|
|
@@ -1491,6 +1485,11 @@ function contextRootFindings(document) {
|
|
|
1491
1485
|
return [];
|
|
1492
1486
|
}
|
|
1493
1487
|
function helperRootFindings(document) {
|
|
1488
|
+
const skillPath = classifyRepositorySkillPath(document.artifact.path);
|
|
1489
|
+
if (skillPath?.kind === "support" &&
|
|
1490
|
+
skillPath.supportDirectory === "scripts") {
|
|
1491
|
+
return [];
|
|
1492
|
+
}
|
|
1494
1493
|
if (document.artifact.path.includes("/scripts/") &&
|
|
1495
1494
|
!document.artifact.path.startsWith("tools/")) {
|
|
1496
1495
|
return [
|
|
@@ -1511,7 +1510,7 @@ function declaredDependencyLayoutFindings(catalog, paths) {
|
|
|
1511
1510
|
continue;
|
|
1512
1511
|
}
|
|
1513
1512
|
if (target.startsWith("contexts/") ||
|
|
1514
|
-
target
|
|
1513
|
+
normalizeRepositoryRelativePath(target) !== undefined ||
|
|
1515
1514
|
target.startsWith("tools/")) {
|
|
1516
1515
|
continue;
|
|
1517
1516
|
}
|
|
@@ -1525,8 +1524,8 @@ function declaredDependencyLayoutFindings(catalog, paths) {
|
|
|
1525
1524
|
severity: "low",
|
|
1526
1525
|
confidence: "medium",
|
|
1527
1526
|
evidence: metadataFindingEvidence(source.sourcePath, target),
|
|
1528
|
-
whyItMatters: "Declared context references should resolve through canonical contexts/**, skills/**, or tools/**
|
|
1529
|
-
remediation: "Rewrite declared
|
|
1527
|
+
whyItMatters: "Declared context references should resolve through canonical contexts/**, skills/**, .agents/skills/**, or tools/** repository paths.",
|
|
1528
|
+
remediation: "Rewrite declared required or optional context dependency values to canonical repo-root paths without changing the Skill's operational metadata format.",
|
|
1530
1529
|
verificationSteps: ["Run renma graph and confirm all edges resolve."],
|
|
1531
1530
|
details: {
|
|
1532
1531
|
source: dependency.from,
|
|
@@ -1550,27 +1549,18 @@ function executableCommands(document) {
|
|
|
1550
1549
|
function firstExecutableCommand(document) {
|
|
1551
1550
|
return executableCommands(document)[0];
|
|
1552
1551
|
}
|
|
1553
|
-
function
|
|
1554
|
-
const
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
}
|
|
1566
|
-
function helperAssetPath(config, workflow, rest) {
|
|
1567
|
-
const namespace = config.layout.toolNamespace;
|
|
1568
|
-
if (namespace)
|
|
1569
|
-
return `tools/${namespace}/${workflow}/${rest}`;
|
|
1570
|
-
return `tools/${workflow}/${rest}`;
|
|
1571
|
-
}
|
|
1572
|
-
function isCanonicalSkillEntrypoint(pathValue) {
|
|
1573
|
-
return /^skills\/[^/]+\/SKILL\.md$/.test(pathValue);
|
|
1552
|
+
function unresolvedHelperCommandFinding(document, command, scriptPath, details = {}) {
|
|
1553
|
+
const unsafePath = details.details?.unsafePath === true;
|
|
1554
|
+
return findingAt(document, DIAGNOSTIC_IDS.PATH_HELPER_COMMAND_UNRESOLVED, "Helper command target does not resolve", "structure", "medium", command.line, command.command, unsafePath
|
|
1555
|
+
? "Update this command to a path inside the owning Skill's scripts/** directory or to a repository-root tools/** helper; do not escape the Skill with `..`."
|
|
1556
|
+
: `Create \`${scriptPath}\` or update this command to the correct local helper path.`, {
|
|
1557
|
+
whyItMatters: "Agents need shared tools/** helpers and Skill-local scripts to resolve deterministically before running them.",
|
|
1558
|
+
verificationSteps: [
|
|
1559
|
+
"Confirm the command resolves to an existing tools/** helper or to scripts/** inside its owning Skill.",
|
|
1560
|
+
"Run renma readiness and check paths.helper_commands.",
|
|
1561
|
+
],
|
|
1562
|
+
...details,
|
|
1563
|
+
});
|
|
1574
1564
|
}
|
|
1575
1565
|
function findingAt(document, id, title, category, severity, line, snippet, remediation, details = {}) {
|
|
1576
1566
|
return {
|