@namewta/speculo 0.4.0 → 0.5.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/README.md +3 -4
- package/package.json +1 -1
- package/template/canonical/canonical-specdev-engineering-cognitive-mentor.md +5 -0
- package/template/canonical/canonical-specdev-goal-plan.md +366 -59
- package/template/canonical/canonical-specdev-grill-with-docs.md +153 -104
- package/template/canonical/canonical-specdev-spec.md +5 -0
- package/template/canonical/canonical-specdev-tickets.md +5 -0
- package/template/canonical/canonical-specdev-wayfinder.md +171 -249
- package/template/commands/docs-sync.md +3 -3
- package/template/skills/docs-sync/SKILL.md +4 -3
- package/template/skills/docs-sync/assets/report-template.md +1 -0
- package/template/skills/docs-sync/references/agents/agent-writing.md +75 -0
- package/template/skills/{agents-md-builder/references → docs-sync/references/agents}/claude-redirect.md +1 -1
- package/template/skills/docs-sync/references/agents-contract.md +23 -1
- package/template/workflows/specdev/G-grill-with-docs/G-grill-with-docs.md +78 -73
- package/template/workflows/specdev/G-grill-with-docs/design-tree-template.json +9 -0
- package/template/workflows/specdev/G-grill-with-docs/grilling-protocol.md +16 -35
- package/template/workflows/specdev/G-grill-with-docs/log-format.md +2 -0
- package/template/workflows/specdev/I-implement/I-implement.md +12 -10
- package/template/workflows/specdev/I-implement/design-it-twice.md +45 -6
- package/template/workflows/specdev/I-implement/evidence-template.md +7 -0
- package/template/workflows/specdev/I-implement/execution-preflight.md +6 -0
- package/template/workflows/specdev/INDEX.md +11 -4
- package/template/workflows/specdev/P-goal-plan/P-goal-plan.md +23 -10
- package/template/workflows/specdev/P-goal-plan/completion-control.md +20 -7
- package/template/workflows/specdev/P-goal-plan/goal-plan-template.md +55 -3
- package/template/workflows/specdev/P-goal-plan/orchestration-protocol.md +42 -38
- package/template/workflows/specdev/P-goal-plan/planning-modes.md +36 -2
- package/template/workflows/specdev/R-review-architecture/R-review-architecture.md +67 -80
- package/template/workflows/specdev/R-review-architecture/architecture-report-contract.md +123 -0
- package/template/workflows/specdev/R-review-architecture/architecture-review-report-template.html +103 -55
- package/template/workflows/specdev/R-review-architecture/architecture-review-template.md +20 -29
- package/template/workflows/specdev/W-wayfinder/W-wayfinder.md +76 -147
- package/template/workflows/specdev/W-wayfinder/investigation-ticket-template.md +8 -53
- package/template/workflows/specdev/W-wayfinder/local-tracker-contract.md +36 -0
- package/template/workflows/specdev/W-wayfinder/solution-comment-template.md +17 -0
- package/template/workflows/specdev/W-wayfinder/wayfinder-map-template.md +12 -65
- package/template/workflows/specdev/common/README.md +4 -0
- package/template/workflows/specdev/common/rules/artifact-contract.md +5 -0
- package/template/workflows/specdev/common/rules/codebase-design.md +148 -0
- package/template/workflows/specdev/common/schemas/design-tree.schema.json +35 -0
- package/template/workflows/specdev/common/schemas/wayfinder-ticket.schema.json +19 -0
- package/template/workflows/specdev/common/skills/subagent-delivery/SKILL.md +61 -0
- package/template/workflows/specdev/common/skills/subagent-delivery/references/external-web-subagent.md +32 -0
- package/template/workflows/specdev/common/skills/subagent-delivery/references/github-checkpoints.md +24 -0
- package/template/workflows/specdev/common/skills/subagent-delivery/references/native-subagent.md +35 -0
- package/template/workflows/specdev/common/skills/subagent-delivery/references/source-package.md +17 -0
- package/template/workflows/specdev/common/tools/validate-specdev.mjs +226 -5
- package/template/skills/agents-md-builder/SKILL.md +0 -30
- package/template/workflows/specdev/I-implement/codebase-design-glossary.md +0 -12
- package/template/workflows/specdev/I-implement/deepening.md +0 -17
- /package/template/skills/{agents-md-builder/references → docs-sync/references/agents}/content-contract.md +0 -0
- /package/template/skills/{agents-md-builder/references → docs-sync/references/agents}/evidence-collection.md +0 -0
- /package/template/skills/{agents-md-builder/references → docs-sync/references/agents}/manifest-discovery.md +0 -0
- /package/template/skills/{agents-md-builder/references → docs-sync/references/agents}/role-classification.md +0 -0
- /package/template/skills/{agents-md-builder/references → docs-sync/references/agents}/templates/aggregator-AGENTS.md +0 -0
- /package/template/skills/{agents-md-builder/references → docs-sync/references/agents}/templates/capability-module-AGENTS.md +0 -0
- /package/template/skills/{agents-md-builder/references → docs-sync/references/agents}/templates/contract-module-AGENTS.md +0 -0
- /package/template/skills/{agents-md-builder/references → docs-sync/references/agents}/templates/repo-root-AGENTS.md +0 -0
- /package/template/skills/{agents-md-builder/references → docs-sync/references/agents}/templates/runnable-app-AGENTS.md +0 -0
- /package/template/skills/{agents-md-builder/references → docs-sync/references/agents}/templates/scripts-docs-AGENTS.md +0 -0
- /package/template/skills/{agents-md-builder/references → docs-sync/references/agents}/writing-style.md +0 -0
|
@@ -56,6 +56,22 @@ const VALID_PLAN_MODES = new Set([
|
|
|
56
56
|
"reference-conformance",
|
|
57
57
|
"release-coordination",
|
|
58
58
|
]);
|
|
59
|
+
const VALID_DESIGN_TREE_STATUS = new Set(["active", "consensus", "blocked"]);
|
|
60
|
+
const VALID_DESIGN_NODE_STATUS = new Set(["open", "answered", "deferred", "rejected"]);
|
|
61
|
+
const VALID_WAYFINDER_LABEL = new Set([
|
|
62
|
+
"wayfinder:research",
|
|
63
|
+
"wayfinder:prototype",
|
|
64
|
+
"wayfinder:grilling",
|
|
65
|
+
"wayfinder:task",
|
|
66
|
+
]);
|
|
67
|
+
const VALID_WAYFINDER_STATUS = new Set(["open", "closed"]);
|
|
68
|
+
const VALID_WAYFINDER_RESOLUTION = new Set([
|
|
69
|
+
null,
|
|
70
|
+
"answered",
|
|
71
|
+
"out-of-scope",
|
|
72
|
+
"superseded",
|
|
73
|
+
"cancelled",
|
|
74
|
+
]);
|
|
59
75
|
const REQUIRED_TICKET_KEYS = new Set([
|
|
60
76
|
"schema_version",
|
|
61
77
|
"artifact",
|
|
@@ -108,6 +124,7 @@ const STATE_ARTIFACT_BASENAMES = new Set([
|
|
|
108
124
|
"architecture-review.md",
|
|
109
125
|
"architecture-review.html",
|
|
110
126
|
"wayfinder-map.md",
|
|
127
|
+
"design-tree.json",
|
|
111
128
|
]);
|
|
112
129
|
const FORBIDDEN_OBSOLETE_BASENAMES = new Set([
|
|
113
130
|
"input-validation.md",
|
|
@@ -522,28 +539,38 @@ function capabilityChecks(root) {
|
|
|
522
539
|
"goal-plan",
|
|
523
540
|
[
|
|
524
541
|
join(root, "P-goal-plan", "P-goal-plan.md"),
|
|
525
|
-
[
|
|
542
|
+
[
|
|
543
|
+
"Outcome",
|
|
544
|
+
"Authority",
|
|
545
|
+
"DAG",
|
|
546
|
+
"Gate",
|
|
547
|
+
"Wave",
|
|
548
|
+
"Lead",
|
|
549
|
+
"Dispatch Packet",
|
|
550
|
+
"subagent-delivery",
|
|
551
|
+
"Definition of Done",
|
|
552
|
+
],
|
|
526
553
|
],
|
|
527
554
|
],
|
|
528
555
|
[
|
|
529
556
|
"implement",
|
|
530
557
|
[
|
|
531
558
|
join(root, "I-implement", "I-implement.md"),
|
|
532
|
-
["design-it-twice", "TDD", "双轴审查", "Evidence", "提交"],
|
|
559
|
+
["codebase-design", "design-it-twice", "TDD", "双轴审查", "Evidence", "delegated execution", "subagent-delivery", "提交"],
|
|
533
560
|
],
|
|
534
561
|
],
|
|
535
562
|
[
|
|
536
563
|
"wayfinder",
|
|
537
564
|
[
|
|
538
565
|
join(root, "W-wayfinder", "W-wayfinder.md"),
|
|
539
|
-
["共享地图", "claimed_investigations", "
|
|
566
|
+
["共享地图", "claimed_investigations", "wayfinder:prototype", "wayfinder:grilling", "solution comment", "每个会话"],
|
|
540
567
|
],
|
|
541
568
|
],
|
|
542
569
|
[
|
|
543
570
|
"architecture-review",
|
|
544
571
|
[
|
|
545
572
|
join(root, "R-review-architecture", "R-review-architecture.md"),
|
|
546
|
-
["
|
|
573
|
+
["shallow", "interface", "locality", "Tailwind CDN", "Mermaid CDN", "最佳推荐"],
|
|
547
574
|
],
|
|
548
575
|
],
|
|
549
576
|
[
|
|
@@ -557,7 +584,7 @@ function capabilityChecks(root) {
|
|
|
557
584
|
"grill",
|
|
558
585
|
[
|
|
559
586
|
join(root, "G-grill-with-docs", "G-grill-with-docs.md"),
|
|
560
|
-
["
|
|
587
|
+
["完整 frontier", "design-tree.json", "LOG.md", "CONTEXT.md", "ADR.md", "推荐答案"],
|
|
561
588
|
],
|
|
562
589
|
],
|
|
563
590
|
[
|
|
@@ -583,6 +610,28 @@ function capabilityChecks(root) {
|
|
|
583
610
|
}
|
|
584
611
|
if (!isFile(join(root, "R-review-architecture", "architecture-review-report-template.html"))) {
|
|
585
612
|
errors.push("architecture review lost its visual HTML report template");
|
|
613
|
+
} else {
|
|
614
|
+
const html = readText(join(root, "R-review-architecture", "architecture-review-report-template.html"));
|
|
615
|
+
for (const marker of ["cdn.tailwindcss.com", "mermaid", "Before", "After", "top-recommendation"]) {
|
|
616
|
+
if (!html.includes(marker)) errors.push(`architecture HTML template lost marker ${marker}`);
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
for (const required of [
|
|
621
|
+
"common/rules/codebase-design.md",
|
|
622
|
+
"common/schemas/design-tree.schema.json",
|
|
623
|
+
"common/schemas/wayfinder-ticket.schema.json",
|
|
624
|
+
"G-grill-with-docs/design-tree-template.json",
|
|
625
|
+
"W-wayfinder/local-tracker-contract.md",
|
|
626
|
+
"W-wayfinder/solution-comment-template.md",
|
|
627
|
+
"R-review-architecture/architecture-report-contract.md",
|
|
628
|
+
]) {
|
|
629
|
+
if (!isFile(join(root, required))) errors.push(`missing architecture/wayfinding contract ${required}`);
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
const mapTemplate = join(root, "W-wayfinder", "wayfinder-map-template.md");
|
|
633
|
+
if (isFile(mapTemplate) && /^## (?:开放 Tickets|调查清单)/m.test(readText(mapTemplate))) {
|
|
634
|
+
errors.push("Wayfinder map template must query open Tickets instead of caching them");
|
|
586
635
|
}
|
|
587
636
|
return errors;
|
|
588
637
|
}
|
|
@@ -777,6 +826,176 @@ function validateGoalPlan(path, errors) {
|
|
|
777
826
|
return { path, meta, body };
|
|
778
827
|
}
|
|
779
828
|
|
|
829
|
+
function validateDesignTree(path, change, errors) {
|
|
830
|
+
if (!isFile(path)) return null;
|
|
831
|
+
let data;
|
|
832
|
+
try {
|
|
833
|
+
data = JSON.parse(readText(path));
|
|
834
|
+
} catch (error) {
|
|
835
|
+
errors.push(`${basename(path)}: invalid JSON: ${error.message}`);
|
|
836
|
+
return null;
|
|
837
|
+
}
|
|
838
|
+
|
|
839
|
+
if (data.schema_version !== 1 || data.artifact !== "design-tree") {
|
|
840
|
+
errors.push(`${basename(path)}: artifact/schema_version must be design-tree/1`);
|
|
841
|
+
}
|
|
842
|
+
if (data.change !== basename(change)) {
|
|
843
|
+
errors.push(`${basename(path)}: change must equal directory name ${basename(change)}`);
|
|
844
|
+
}
|
|
845
|
+
if (!VALID_DESIGN_TREE_STATUS.has(data.status)) {
|
|
846
|
+
errors.push(`${basename(path)}: invalid status ${data.status}`);
|
|
847
|
+
}
|
|
848
|
+
if (!Number.isInteger(data.round) || data.round < 0) {
|
|
849
|
+
errors.push(`${basename(path)}: round must be a non-negative integer`);
|
|
850
|
+
}
|
|
851
|
+
if (!Array.isArray(data.nodes)) {
|
|
852
|
+
errors.push(`${basename(path)}: nodes must be a list`);
|
|
853
|
+
return data;
|
|
854
|
+
}
|
|
855
|
+
|
|
856
|
+
const nodes = new Map();
|
|
857
|
+
const graph = new Map();
|
|
858
|
+
for (const node of data.nodes) {
|
|
859
|
+
const id = String(node?.id ?? "");
|
|
860
|
+
if (!/^D-\d{3,}$/.test(id)) errors.push(`${basename(path)}: invalid design node id ${id}`);
|
|
861
|
+
if (nodes.has(id)) errors.push(`${basename(path)}: duplicate design node id ${id}`);
|
|
862
|
+
nodes.set(id, node);
|
|
863
|
+
const dependencies = Array.isArray(node?.depends_on) ? node.depends_on.map(String) : [];
|
|
864
|
+
graph.set(id, dependencies);
|
|
865
|
+
if (!Array.isArray(node?.depends_on)) {
|
|
866
|
+
errors.push(`${basename(path)}: ${id} depends_on must be a list`);
|
|
867
|
+
}
|
|
868
|
+
for (const key of ["title", "question", "recommendation"]) {
|
|
869
|
+
if (!String(node?.[key] ?? "").trim()) errors.push(`${basename(path)}: ${id} ${key} is required`);
|
|
870
|
+
}
|
|
871
|
+
if (!VALID_DESIGN_NODE_STATUS.has(node?.status)) {
|
|
872
|
+
errors.push(`${basename(path)}: ${id} has invalid status ${node?.status}`);
|
|
873
|
+
}
|
|
874
|
+
if (node?.round !== null && (!Number.isInteger(node?.round) || node.round < 1)) {
|
|
875
|
+
errors.push(`${basename(path)}: ${id} round must be null or a positive integer`);
|
|
876
|
+
}
|
|
877
|
+
if (node?.status !== "open") {
|
|
878
|
+
if (!String(node?.answer ?? "").trim()) errors.push(`${basename(path)}: ${id} closed node needs an answer`);
|
|
879
|
+
if (!/^LOG-\d{3,}$/.test(String(node?.log_ref ?? ""))) {
|
|
880
|
+
errors.push(`${basename(path)}: ${id} closed node needs a LOG-### reference`);
|
|
881
|
+
}
|
|
882
|
+
}
|
|
883
|
+
}
|
|
884
|
+
|
|
885
|
+
for (const [id, dependencies] of graph) {
|
|
886
|
+
for (const dependency of dependencies) {
|
|
887
|
+
if (!nodes.has(dependency)) errors.push(`${basename(path)}: ${id} depends on missing ${dependency}`);
|
|
888
|
+
if (dependency === id) errors.push(`${basename(path)}: ${id} cannot depend on itself`);
|
|
889
|
+
}
|
|
890
|
+
}
|
|
891
|
+
const cycle = findCycle(graph);
|
|
892
|
+
if (cycle) errors.push(`Design tree dependency cycle: ${cycle.join(" -> ")}`);
|
|
893
|
+
if (
|
|
894
|
+
data.status === "consensus" &&
|
|
895
|
+
data.nodes.some((node) => new Set(["open", "deferred"]).has(node?.status))
|
|
896
|
+
) {
|
|
897
|
+
errors.push(`${basename(path)}: consensus design tree cannot contain open or deferred nodes`);
|
|
898
|
+
}
|
|
899
|
+
|
|
900
|
+
const logPath = join(change, "LOG.md");
|
|
901
|
+
const logText = isFile(logPath) ? readText(logPath) : "";
|
|
902
|
+
for (const node of data.nodes) {
|
|
903
|
+
if (node?.log_ref && !logText.includes(String(node.log_ref))) {
|
|
904
|
+
errors.push(`${basename(path)}: ${node.id} points to missing ${node.log_ref} in LOG`);
|
|
905
|
+
}
|
|
906
|
+
}
|
|
907
|
+
return data;
|
|
908
|
+
}
|
|
909
|
+
|
|
910
|
+
function validateWayfinder(change, errors) {
|
|
911
|
+
const mapPath = join(change, "wayfinder-map.md");
|
|
912
|
+
const investigationDir = join(change, "investigation");
|
|
913
|
+
if (!isFile(mapPath) && !isDirectory(investigationDir)) return null;
|
|
914
|
+
if (!isFile(mapPath)) errors.push("Wayfinder investigations exist but the map is missing");
|
|
915
|
+
if (!isDirectory(investigationDir)) {
|
|
916
|
+
errors.push("Wayfinder map exists but the investigation directory is missing");
|
|
917
|
+
return null;
|
|
918
|
+
}
|
|
919
|
+
|
|
920
|
+
if (isFile(mapPath)) {
|
|
921
|
+
const { meta } = parseFrontmatter(mapPath);
|
|
922
|
+
if (meta.artifact !== "wayfinder-map") errors.push("wayfinder-map.md: artifact must be wayfinder-map");
|
|
923
|
+
if (meta.change !== basename(change)) {
|
|
924
|
+
errors.push(`wayfinder-map.md: change must equal directory name ${basename(change)}`);
|
|
925
|
+
}
|
|
926
|
+
}
|
|
927
|
+
|
|
928
|
+
const ticketPaths = readdirSync(investigationDir, { withFileTypes: true })
|
|
929
|
+
.filter((entry) => entry.isFile() && entry.name.endsWith(".md"))
|
|
930
|
+
.map((entry) => join(investigationDir, entry.name))
|
|
931
|
+
.sort();
|
|
932
|
+
const tickets = new Map();
|
|
933
|
+
const graph = new Map();
|
|
934
|
+
for (const path of ticketPaths) {
|
|
935
|
+
const { meta } = parseFrontmatter(path);
|
|
936
|
+
const id = String(meta.id ?? "");
|
|
937
|
+
if (meta.artifact !== "wayfinder-ticket") {
|
|
938
|
+
errors.push(`${basename(path)}: artifact must be wayfinder-ticket`);
|
|
939
|
+
}
|
|
940
|
+
if (!/^INV-\d{2,}$/.test(id)) errors.push(`${basename(path)}: invalid Wayfinder Ticket id ${id}`);
|
|
941
|
+
if (tickets.has(id)) errors.push(`duplicate Wayfinder Ticket id ${id}`);
|
|
942
|
+
tickets.set(id, { path, meta });
|
|
943
|
+
if (!basename(path).startsWith(id)) errors.push(`${basename(path)}: filename must start with ${id}`);
|
|
944
|
+
if (!String(meta.name ?? "").trim()) errors.push(`${basename(path)}: name is required`);
|
|
945
|
+
const parentMap = typeof meta.parent_map === "string" ? stripPathTag(meta.parent_map) : null;
|
|
946
|
+
if (parentMap === null || !parentMap.endsWith("/wayfinder-map.md")) {
|
|
947
|
+
errors.push(`${basename(path)}: parent_map must be a rooted Wayfinder map Path`);
|
|
948
|
+
}
|
|
949
|
+
if (!VALID_WAYFINDER_LABEL.has(meta.label)) {
|
|
950
|
+
errors.push(`${basename(path)}: invalid Wayfinder label ${meta.label}`);
|
|
951
|
+
}
|
|
952
|
+
if (!VALID_WAYFINDER_STATUS.has(meta.status)) {
|
|
953
|
+
errors.push(`${basename(path)}: invalid Wayfinder status ${meta.status}`);
|
|
954
|
+
}
|
|
955
|
+
const dependencies = Array.isArray(meta.blocked_by) ? meta.blocked_by.map(String) : [];
|
|
956
|
+
if (!Array.isArray(meta.blocked_by)) errors.push(`${basename(path)}: blocked_by must be a list`);
|
|
957
|
+
graph.set(id, dependencies);
|
|
958
|
+
if (!VALID_WAYFINDER_RESOLUTION.has(meta.resolution)) {
|
|
959
|
+
errors.push(`${basename(path)}: invalid Wayfinder resolution ${meta.resolution}`);
|
|
960
|
+
}
|
|
961
|
+
if (meta.status === "open" && meta.resolution !== null) {
|
|
962
|
+
errors.push(`${basename(path)}: open Wayfinder Ticket must have resolution: null`);
|
|
963
|
+
}
|
|
964
|
+
if (meta.status === "closed" && meta.resolution === null) {
|
|
965
|
+
errors.push(`${basename(path)}: closed Wayfinder Ticket needs a resolution`);
|
|
966
|
+
}
|
|
967
|
+
}
|
|
968
|
+
|
|
969
|
+
for (const [id, dependencies] of graph) {
|
|
970
|
+
for (const dependency of dependencies) {
|
|
971
|
+
if (!tickets.has(dependency)) errors.push(`${id}: blocked_by references missing ${dependency}`);
|
|
972
|
+
if (dependency === id) errors.push(`${id}: Wayfinder Ticket cannot block itself`);
|
|
973
|
+
}
|
|
974
|
+
}
|
|
975
|
+
const cycle = findCycle(graph);
|
|
976
|
+
if (cycle) errors.push(`Wayfinder dependency cycle: ${cycle.join(" -> ")}`);
|
|
977
|
+
|
|
978
|
+
const commentsRoot = join(investigationDir, "comments");
|
|
979
|
+
for (const [id, ticket] of tickets) {
|
|
980
|
+
if (ticket.meta.status !== "closed") continue;
|
|
981
|
+
const commentDir = join(commentsRoot, id);
|
|
982
|
+
const comments = isDirectory(commentDir)
|
|
983
|
+
? readdirSync(commentDir).filter((name) => /^\d{2,}-solution\.md$/.test(name)).sort()
|
|
984
|
+
: [];
|
|
985
|
+
if (!comments.length) {
|
|
986
|
+
errors.push(`${id}: closed Wayfinder Ticket has no solution comment`);
|
|
987
|
+
continue;
|
|
988
|
+
}
|
|
989
|
+
for (const name of comments) {
|
|
990
|
+
const { meta } = parseFrontmatter(join(commentDir, name));
|
|
991
|
+
if (meta.artifact !== "wayfinder-solution-comment" || meta.ticket !== id) {
|
|
992
|
+
errors.push(`${toPosix(relative(change, join(commentDir, name)))}: invalid solution comment identity`);
|
|
993
|
+
}
|
|
994
|
+
}
|
|
995
|
+
}
|
|
996
|
+
return tickets;
|
|
997
|
+
}
|
|
998
|
+
|
|
780
999
|
function validateTicket(path, errors) {
|
|
781
1000
|
const { meta, body } = parseFrontmatter(path);
|
|
782
1001
|
const missing = [...REQUIRED_TICKET_KEYS].filter((key) => !(key in meta));
|
|
@@ -930,6 +1149,8 @@ function validateChange(change) {
|
|
|
930
1149
|
const spec = validateSpec(join(change, "spec.md"), errors, warnings);
|
|
931
1150
|
const ticketsMap = validateMap(join(change, "tickets-map.md"), errors);
|
|
932
1151
|
const goalPlan = validateGoalPlan(join(change, "goal-plan.md"), errors);
|
|
1152
|
+
validateDesignTree(join(change, "design-tree.json"), change, errors);
|
|
1153
|
+
validateWayfinder(change, errors);
|
|
933
1154
|
for (const artifact of [spec, ticketsMap, goalPlan]) {
|
|
934
1155
|
if (artifact && artifact.meta.change !== basename(change)) {
|
|
935
1156
|
errors.push(`${basename(artifact.path)}: change must equal directory name ${basename(change)}`);
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
id: agents-md-builder
|
|
3
|
-
type: skill
|
|
4
|
-
name: AGENTS.md Builder
|
|
5
|
-
description: 扫描 manifest 目录并生成有证据、极简且形成父子导航树的 AGENTS.md 与 CLAUDE.md。
|
|
6
|
-
---
|
|
7
|
-
|
|
8
|
-
# AGENTS.md Builder
|
|
9
|
-
|
|
10
|
-
每份 AGENTS.md 是模块导读。逐行执行删除测试:删除后不会使代理犯错的内容不进入产物。
|
|
11
|
-
|
|
12
|
-
## 输入
|
|
13
|
-
|
|
14
|
-
- 项目根(默认 runtime context 的 project root)与可选目标子树。
|
|
15
|
-
- 可选 manifest 类型过滤和输出语言(默认中文)。
|
|
16
|
-
|
|
17
|
-
## 流程
|
|
18
|
-
|
|
19
|
-
1. 读取 `references/manifest-discovery.md`,发现 manifest 目录、忽略目录和父子树;同时列出无 manifest 却存在代理手册的目录。完成标准:扫描范围内每个 manifest 恰好出现一次,所有孤立手册已列为候选而未直接删除。
|
|
20
|
-
2. 读取 `references/role-classification.md`,按优先级为每个目录判定唯一角色。完成标准:所有目录都有角色及支持证据,没有多重分类。
|
|
21
|
-
3. 读取 `references/evidence-collection.md`,收集 manifest、入口、依赖、消费方和测试。完成标准:每个将写入的结论都能指向真实文件。
|
|
22
|
-
4. 读取 `references/content-contract.md`、`references/writing-style.md` 和对应 `references/templates/*`,自底向上生成或更新 AGENTS.md。完成标准:父子 routing 完整、内容不跨模块重复且逐行通过删除测试。
|
|
23
|
-
5. 读取 `references/claude-redirect.md` 生成同层 CLAUDE.md;展示创建、更新和删除候选,确认后再写入。完成标准:每个合法 AGENTS.md 有唯一重定向,非法候选的处置有记录。
|
|
24
|
-
|
|
25
|
-
## 输出
|
|
26
|
-
|
|
27
|
-
- 生成/更新的 AGENTS.md 与 CLAUDE.md 清单。
|
|
28
|
-
- 按角色统计、证据缺口和经确认清理的孤立文件清单。
|
|
29
|
-
|
|
30
|
-
本 skill 不自行选择 Speculo 报告路径;需要持久化时由调用方提供。
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
# 代码库设计术语
|
|
2
|
-
|
|
3
|
-
- **模块**:隐藏复杂度并提供稳定接口的单元,不等于目录或类。
|
|
4
|
-
- **深度**:接口简单度相对于模块内部承载复杂度的杠杆。
|
|
5
|
-
- **接口**:调用者必须知道的全部,包括类型、不变量、顺序、错误和性能语义。
|
|
6
|
-
- **接缝**:可以替换、观察或验证行为的位置。
|
|
7
|
-
- **适配器**:把外部变化隔离在稳定接缝外的实现。
|
|
8
|
-
- **信息隐藏**:设计决定只存在于一个权威位置。
|
|
9
|
-
- **局部性**:完成一个行为所需理解和修改的范围尽可能集中。
|
|
10
|
-
- **时间耦合**:调用必须按隐含顺序发生;应显式化或封装。
|
|
11
|
-
|
|
12
|
-
检查目标:减少调用者认知,而不是单纯减少行数或文件数。
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
# 深化与依赖策略
|
|
2
|
-
|
|
3
|
-
## 依赖分类
|
|
4
|
-
|
|
5
|
-
1. 进程内稳定依赖:直接调用通常足够。
|
|
6
|
-
2. 本地可替换依赖:在真实替换/测试需要处建立接缝。
|
|
7
|
-
3. 远程但自有:封装协议、超时、重试、幂等和可观测性。
|
|
8
|
-
4. 真正外部:适配器隔离版本、失败、限流和不可控变化。
|
|
9
|
-
|
|
10
|
-
不要为了“可测试”给每个类加接口。接缝应服务真实变化轴或高价值验证。
|
|
11
|
-
|
|
12
|
-
## 深化问题
|
|
13
|
-
|
|
14
|
-
- 能否让调用者少知道一个状态、顺序或错误分支?
|
|
15
|
-
- 能否把重复策略收进单一模块?
|
|
16
|
-
- 是否把外部 SDK 类型泄漏到领域层?
|
|
17
|
-
- 是否有至少两个真实消费者/适配器证明抽象成立?
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|