@bossmissing/agent-meta 0.1.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/LICENSE +21 -0
- package/README.md +91 -0
- package/dist/analyzers/duplicate-rule-analyzer.d.ts +31 -0
- package/dist/analyzers/duplicate-rule-analyzer.js +80 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +177 -0
- package/dist/commands/audit.d.ts +7 -0
- package/dist/commands/audit.js +187 -0
- package/dist/commands/check.d.ts +35 -0
- package/dist/commands/check.js +109 -0
- package/dist/commands/config.d.ts +2 -0
- package/dist/commands/config.js +59 -0
- package/dist/commands/init.d.ts +8 -0
- package/dist/commands/init.js +179 -0
- package/dist/commands/new-adr.d.ts +8 -0
- package/dist/commands/new-adr.js +65 -0
- package/dist/commands/new-domain.d.ts +13 -0
- package/dist/commands/new-domain.js +106 -0
- package/dist/commands/sync.d.ts +7 -0
- package/dist/commands/sync.js +46 -0
- package/dist/core/config.d.ts +15 -0
- package/dist/core/config.js +40 -0
- package/dist/core/errors.d.ts +12 -0
- package/dist/core/errors.js +16 -0
- package/dist/core/filesystem.d.ts +33 -0
- package/dist/core/filesystem.js +92 -0
- package/dist/core/frontmatter.d.ts +19 -0
- package/dist/core/frontmatter.js +84 -0
- package/dist/core/logger.d.ts +8 -0
- package/dist/core/logger.js +22 -0
- package/dist/core/paths.d.ts +8 -0
- package/dist/core/paths.js +39 -0
- package/dist/core/risk.d.ts +3 -0
- package/dist/core/risk.js +8 -0
- package/dist/core/templates.d.ts +10 -0
- package/dist/core/templates.js +31 -0
- package/dist/core/types.d.ts +21 -0
- package/dist/core/types.js +7 -0
- package/dist/generators/adr-generator.d.ts +11 -0
- package/dist/generators/adr-generator.js +25 -0
- package/dist/generators/index-generator.d.ts +6 -0
- package/dist/generators/index-generator.js +87 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.js +10 -0
- package/dist/schemas/config.schema.d.ts +208 -0
- package/dist/schemas/config.schema.js +83 -0
- package/dist/templates/index.d.ts +23 -0
- package/dist/templates/index.js +445 -0
- package/dist/validators/adr-validator.d.ts +3 -0
- package/dist/validators/adr-validator.js +182 -0
- package/dist/validators/agents-validator.d.ts +3 -0
- package/dist/validators/agents-validator.js +82 -0
- package/dist/validators/context.d.ts +12 -0
- package/dist/validators/context.js +1 -0
- package/dist/validators/docs-validator.d.ts +3 -0
- package/dist/validators/docs-validator.js +129 -0
- package/dist/validators/domain-validator.d.ts +3 -0
- package/dist/validators/domain-validator.js +132 -0
- package/dist/validators/openspec-validator.d.ts +2 -0
- package/dist/validators/openspec-validator.js +76 -0
- package/dist/validators/project-validator.d.ts +2 -0
- package/dist/validators/project-validator.js +41 -0
- package/package.json +64 -0
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { MetaConfig } from "../schemas/config.schema.js";
|
|
2
|
+
import type { MetaIssue } from "../core/types.js";
|
|
3
|
+
export interface ValidatorContext {
|
|
4
|
+
root: string;
|
|
5
|
+
config: MetaConfig;
|
|
6
|
+
/** Whether agent-meta.config.yaml was found on disk. */
|
|
7
|
+
configFound: boolean;
|
|
8
|
+
}
|
|
9
|
+
export interface ValidatorResult {
|
|
10
|
+
issues: MetaIssue[];
|
|
11
|
+
checkedFiles: number;
|
|
12
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import fg from "fast-glob";
|
|
3
|
+
import { parseDocFile } from "../core/frontmatter.js";
|
|
4
|
+
import { dateValueToString, daysSince } from "../core/paths.js";
|
|
5
|
+
export const VALID_DOC_STATUSES = ["draft", "active", "deprecated", "archived"];
|
|
6
|
+
const ARRAY_FIELDS = ["applies_to", "supersedes", "superseded_by", "tags"];
|
|
7
|
+
export function validateDocs(ctx) {
|
|
8
|
+
const issues = [];
|
|
9
|
+
const { root, config } = ctx;
|
|
10
|
+
if (!config.features.docs_frontmatter)
|
|
11
|
+
return { issues, checkedFiles: 0 };
|
|
12
|
+
// AGENTS.md files are rule files, not documents — they have their own
|
|
13
|
+
// validator and the standard template carries no frontmatter.
|
|
14
|
+
const files = fg.sync(config.validation.require_doc_frontmatter, {
|
|
15
|
+
cwd: root,
|
|
16
|
+
dot: false,
|
|
17
|
+
ignore: ["**/AGENTS.md"],
|
|
18
|
+
});
|
|
19
|
+
for (const rel of files.sort()) {
|
|
20
|
+
const abs = path.join(root, rel);
|
|
21
|
+
let doc;
|
|
22
|
+
try {
|
|
23
|
+
doc = parseDocFile(abs);
|
|
24
|
+
}
|
|
25
|
+
catch (e) {
|
|
26
|
+
issues.push({
|
|
27
|
+
code: "DOC_UNREADABLE",
|
|
28
|
+
severity: "error",
|
|
29
|
+
file: rel,
|
|
30
|
+
message: `Cannot read file: ${e.message}`,
|
|
31
|
+
});
|
|
32
|
+
continue;
|
|
33
|
+
}
|
|
34
|
+
if (!doc.hasFrontmatter) {
|
|
35
|
+
issues.push({
|
|
36
|
+
code: "DOC_FRONTMATTER_MISSING",
|
|
37
|
+
severity: "error",
|
|
38
|
+
file: rel,
|
|
39
|
+
message: "Document is missing YAML frontmatter",
|
|
40
|
+
suggestion: "Add frontmatter with title, status, owner and last_reviewed.",
|
|
41
|
+
});
|
|
42
|
+
continue;
|
|
43
|
+
}
|
|
44
|
+
if (doc.parseError) {
|
|
45
|
+
issues.push({
|
|
46
|
+
code: "DOC_FRONTMATTER_INVALID",
|
|
47
|
+
severity: "error",
|
|
48
|
+
file: rel,
|
|
49
|
+
message: `Frontmatter YAML cannot be parsed: ${doc.parseError}`,
|
|
50
|
+
});
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
const data = doc.data;
|
|
54
|
+
for (const field of config.validation.required_frontmatter) {
|
|
55
|
+
const v = data[field];
|
|
56
|
+
const missing = v === undefined || v === null || (typeof v === "string" && v.trim() === "");
|
|
57
|
+
if (missing) {
|
|
58
|
+
issues.push({
|
|
59
|
+
code: `DOC_FRONTMATTER_MISSING_${field.toUpperCase()}`,
|
|
60
|
+
severity: "error",
|
|
61
|
+
file: rel,
|
|
62
|
+
message: `Required frontmatter field '${field}' is missing`,
|
|
63
|
+
suggestion: `Add '${field}' to the document frontmatter.`,
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
const status = data["status"];
|
|
68
|
+
if (typeof status === "string" && !VALID_DOC_STATUSES.includes(status)) {
|
|
69
|
+
issues.push({
|
|
70
|
+
code: "DOC_FRONTMATTER_INVALID",
|
|
71
|
+
severity: "error",
|
|
72
|
+
file: rel,
|
|
73
|
+
message: `Invalid status '${status}'`,
|
|
74
|
+
suggestion: `Use one of: ${VALID_DOC_STATUSES.join(", ")}.`,
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
if (data["last_reviewed"] !== undefined && data["last_reviewed"] !== null) {
|
|
78
|
+
const dateStr = dateValueToString(data["last_reviewed"]);
|
|
79
|
+
if (!dateStr) {
|
|
80
|
+
issues.push({
|
|
81
|
+
code: "DOC_FRONTMATTER_INVALID",
|
|
82
|
+
severity: "error",
|
|
83
|
+
file: rel,
|
|
84
|
+
message: `'last_reviewed' is not a valid YYYY-MM-DD date`,
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
else if (typeof status === "string") {
|
|
88
|
+
const staleDays = status === "active"
|
|
89
|
+
? config.validation.stale_review_days.active
|
|
90
|
+
: status === "draft"
|
|
91
|
+
? config.validation.stale_review_days.draft
|
|
92
|
+
: null;
|
|
93
|
+
if (staleDays !== null) {
|
|
94
|
+
const age = daysSince(dateStr);
|
|
95
|
+
if (Number.isFinite(age) && age > staleDays) {
|
|
96
|
+
issues.push({
|
|
97
|
+
code: "DOC_STALE",
|
|
98
|
+
severity: "warning",
|
|
99
|
+
file: rel,
|
|
100
|
+
message: `Document has not been reviewed for ${age} days (expected interval: ${staleDays} days)`,
|
|
101
|
+
suggestion: "Review the document and update 'last_reviewed'.",
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
for (const field of ARRAY_FIELDS) {
|
|
108
|
+
const v = data[field];
|
|
109
|
+
if (v !== undefined && v !== null && !Array.isArray(v)) {
|
|
110
|
+
issues.push({
|
|
111
|
+
code: "DOC_FRONTMATTER_INVALID",
|
|
112
|
+
severity: "error",
|
|
113
|
+
file: rel,
|
|
114
|
+
message: `'${field}' must be an array`,
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
const sot = data["source_of_truth"];
|
|
119
|
+
if (sot !== undefined && sot !== null && typeof sot !== "boolean") {
|
|
120
|
+
issues.push({
|
|
121
|
+
code: "DOC_FRONTMATTER_INVALID",
|
|
122
|
+
severity: "error",
|
|
123
|
+
file: rel,
|
|
124
|
+
message: `'source_of_truth' must be a boolean`,
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
return { issues, checkedFiles: files.length };
|
|
129
|
+
}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { parseDocFile, hasSection } from "../core/frontmatter.js";
|
|
4
|
+
import { matchRiskKeywords } from "../core/risk.js";
|
|
5
|
+
export function listDomainDirs(root, domainDocs) {
|
|
6
|
+
const abs = path.join(root, domainDocs);
|
|
7
|
+
if (!fs.existsSync(abs))
|
|
8
|
+
return [];
|
|
9
|
+
return fs
|
|
10
|
+
.readdirSync(abs, { withFileTypes: true })
|
|
11
|
+
.filter((d) => d.isDirectory())
|
|
12
|
+
.map((d) => d.name)
|
|
13
|
+
.sort();
|
|
14
|
+
}
|
|
15
|
+
export function validateDomains(ctx) {
|
|
16
|
+
const issues = [];
|
|
17
|
+
const { root, config } = ctx;
|
|
18
|
+
const domainDirs = listDomainDirs(root, config.paths.domain_docs);
|
|
19
|
+
let checkedFiles = 0;
|
|
20
|
+
for (const domain of domainDirs) {
|
|
21
|
+
const relDir = path.join(config.paths.domain_docs, domain);
|
|
22
|
+
const absDir = path.join(root, relDir);
|
|
23
|
+
checkedFiles++;
|
|
24
|
+
const readmeAbs = path.join(absDir, "README.md");
|
|
25
|
+
const rulesAbs = path.join(absDir, "rules.md");
|
|
26
|
+
const agentsAbs = path.join(absDir, "AGENTS.md");
|
|
27
|
+
const stateMachineAbs = path.join(absDir, "state-machine.md");
|
|
28
|
+
const riskHits = matchRiskKeywords(domain, config.risk_keywords);
|
|
29
|
+
if (!fs.existsSync(readmeAbs)) {
|
|
30
|
+
issues.push({
|
|
31
|
+
code: "DOMAIN_README_MISSING",
|
|
32
|
+
severity: "error",
|
|
33
|
+
file: relDir,
|
|
34
|
+
message: `Domain directory has no README.md`,
|
|
35
|
+
suggestion: `Run \`meta new-domain ${domain} --force\` to create missing files.`,
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
const readme = parseDocFile(readmeAbs);
|
|
40
|
+
// A linked state-machine.md must exist.
|
|
41
|
+
if (/\]\(\.\/?state-machine\.md\)/.test(readme.content) && !fs.existsSync(stateMachineAbs)) {
|
|
42
|
+
issues.push({
|
|
43
|
+
code: "STATE_MACHINE_LINK_BROKEN",
|
|
44
|
+
severity: "error",
|
|
45
|
+
file: path.join(relDir, "README.md"),
|
|
46
|
+
message: "README links to state-machine.md but the file does not exist",
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
const owner = readme.data["owner"];
|
|
50
|
+
const ownerEmpty = owner === undefined ||
|
|
51
|
+
owner === null ||
|
|
52
|
+
(typeof owner === "string" && owner.trim() === "") ||
|
|
53
|
+
(Array.isArray(owner) && owner.length === 0);
|
|
54
|
+
if (ownerEmpty) {
|
|
55
|
+
issues.push({
|
|
56
|
+
code: "DOMAIN_NO_OWNER",
|
|
57
|
+
severity: "warning",
|
|
58
|
+
file: path.join(relDir, "README.md"),
|
|
59
|
+
message: "Domain README has no owner",
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
if (fs.existsSync(agentsAbs)) {
|
|
64
|
+
const content = fs.readFileSync(agentsAbs, "utf8");
|
|
65
|
+
if (!hasSection(content, ["scope"])) {
|
|
66
|
+
issues.push({
|
|
67
|
+
code: "LOCAL_AGENTS_MISSING_SCOPE",
|
|
68
|
+
severity: "error",
|
|
69
|
+
file: path.join(relDir, "AGENTS.md"),
|
|
70
|
+
message: 'Local AGENTS.md is missing a "Scope" section',
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
if (!hasSection(content, ["required reading"])) {
|
|
74
|
+
issues.push({
|
|
75
|
+
code: "LOCAL_AGENTS_MISSING_REQUIRED_READING",
|
|
76
|
+
severity: "error",
|
|
77
|
+
file: path.join(relDir, "AGENTS.md"),
|
|
78
|
+
message: 'Local AGENTS.md is missing a "Required Reading" section',
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
if (fs.existsSync(stateMachineAbs)) {
|
|
83
|
+
const sm = parseDocFile(stateMachineAbs);
|
|
84
|
+
if (!hasSection(sm.content, ["状态列表", "states"])) {
|
|
85
|
+
issues.push({
|
|
86
|
+
code: "STATE_MACHINE_MISSING_STATES",
|
|
87
|
+
severity: "error",
|
|
88
|
+
file: path.join(relDir, "state-machine.md"),
|
|
89
|
+
message: 'state-machine.md is missing a "状态列表" section',
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
if (!hasSection(sm.content, ["状态流转", "transitions"])) {
|
|
93
|
+
issues.push({
|
|
94
|
+
code: "STATE_MACHINE_MISSING_TRANSITIONS",
|
|
95
|
+
severity: "error",
|
|
96
|
+
file: path.join(relDir, "state-machine.md"),
|
|
97
|
+
message: 'state-machine.md is missing a "状态流转" section',
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
if (riskHits.length > 0) {
|
|
102
|
+
if (!fs.existsSync(rulesAbs)) {
|
|
103
|
+
issues.push({
|
|
104
|
+
code: "HIGH_RISK_DOMAIN_MISSING_RULES",
|
|
105
|
+
severity: "warning",
|
|
106
|
+
file: relDir,
|
|
107
|
+
message: `High-risk domain '${domain}' (matched: ${riskHits.join(", ")}) has no rules.md`,
|
|
108
|
+
suggestion: `Run \`meta new-domain ${domain} --force\` to create it.`,
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
if (!fs.existsSync(agentsAbs)) {
|
|
112
|
+
issues.push({
|
|
113
|
+
code: "HIGH_RISK_DOMAIN_MISSING_AGENTS",
|
|
114
|
+
severity: "warning",
|
|
115
|
+
file: relDir,
|
|
116
|
+
message: `High-risk domain '${domain}' has no local AGENTS.md`,
|
|
117
|
+
suggestion: `Run \`meta new-domain ${domain} --force --with-local-agents\`.`,
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
if (!fs.existsSync(stateMachineAbs)) {
|
|
121
|
+
issues.push({
|
|
122
|
+
code: "HIGH_RISK_DOMAIN_MISSING_STATE_MACHINE",
|
|
123
|
+
severity: "warning",
|
|
124
|
+
file: relDir,
|
|
125
|
+
message: `High-risk domain '${domain}' may need a state machine but has none`,
|
|
126
|
+
suggestion: `Run \`meta new-domain ${domain} --force --with-state-machine\`.`,
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
return { issues, checkedFiles };
|
|
132
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
export function validateOpenSpec(ctx) {
|
|
4
|
+
const issues = [];
|
|
5
|
+
const { root, config } = ctx;
|
|
6
|
+
const openspecRel = config.paths.openspec;
|
|
7
|
+
const openspecAbs = path.join(root, openspecRel);
|
|
8
|
+
if (config.features.openspec === false)
|
|
9
|
+
return { issues, checkedFiles: 0 };
|
|
10
|
+
if (!fs.existsSync(openspecAbs)) {
|
|
11
|
+
if (config.features.openspec === true) {
|
|
12
|
+
issues.push({
|
|
13
|
+
code: "OPENSPEC_CONFIG_MISSING",
|
|
14
|
+
severity: "error",
|
|
15
|
+
file: openspecRel,
|
|
16
|
+
message: "features.openspec is enabled but the openspec/ directory does not exist",
|
|
17
|
+
});
|
|
18
|
+
return { issues, checkedFiles: 0 };
|
|
19
|
+
}
|
|
20
|
+
return { issues, checkedFiles: 0 };
|
|
21
|
+
}
|
|
22
|
+
let checkedFiles = 1;
|
|
23
|
+
if (!fs.existsSync(path.join(openspecAbs, "config.yaml"))) {
|
|
24
|
+
issues.push({
|
|
25
|
+
code: "OPENSPEC_CONFIG_MISSING",
|
|
26
|
+
severity: "error",
|
|
27
|
+
file: path.join(openspecRel, "config.yaml"),
|
|
28
|
+
message: "OpenSpec directory detected but openspec/config.yaml is missing",
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
for (const sub of ["specs", "changes"]) {
|
|
32
|
+
if (!fs.existsSync(path.join(openspecAbs, sub))) {
|
|
33
|
+
issues.push({
|
|
34
|
+
code: "OPENSPEC_STRUCTURE_INCOMPLETE",
|
|
35
|
+
severity: "warning",
|
|
36
|
+
file: path.join(openspecRel, sub),
|
|
37
|
+
message: `OpenSpec directory is missing ${sub}/`,
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
// Root AGENTS.md should reference OpenSpec entry points.
|
|
42
|
+
const rootAgentsAbs = path.join(root, config.paths.root_agents);
|
|
43
|
+
if (fs.existsSync(rootAgentsAbs)) {
|
|
44
|
+
checkedFiles++;
|
|
45
|
+
const content = fs.readFileSync(rootAgentsAbs, "utf8");
|
|
46
|
+
if (!content.includes("openspec")) {
|
|
47
|
+
issues.push({
|
|
48
|
+
code: "AGENTS_MISSING_OPENSPEC_ENTRY",
|
|
49
|
+
severity: "warning",
|
|
50
|
+
file: config.paths.root_agents,
|
|
51
|
+
message: "Root AGENTS.md does not reference openspec/ documentation entry points",
|
|
52
|
+
suggestion: "Add links to openspec/specs/ and openspec/changes/ in AGENTS.md.",
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
// Each change directory should contain at least a proposal or tasks file.
|
|
57
|
+
const changesAbs = path.join(openspecAbs, "changes");
|
|
58
|
+
if (fs.existsSync(changesAbs)) {
|
|
59
|
+
for (const entry of fs.readdirSync(changesAbs, { withFileTypes: true })) {
|
|
60
|
+
if (!entry.isDirectory())
|
|
61
|
+
continue;
|
|
62
|
+
checkedFiles++;
|
|
63
|
+
const changeDir = path.join(changesAbs, entry.name);
|
|
64
|
+
const hasCore = ["proposal.md", "tasks.md", "design.md"].some((f) => fs.existsSync(path.join(changeDir, f)));
|
|
65
|
+
if (!hasCore) {
|
|
66
|
+
issues.push({
|
|
67
|
+
code: "OPENSPEC_CHANGE_INCOMPLETE",
|
|
68
|
+
severity: "warning",
|
|
69
|
+
file: path.join(openspecRel, "changes", entry.name),
|
|
70
|
+
message: "Change directory has none of proposal.md / design.md / tasks.md",
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return { issues, checkedFiles };
|
|
76
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { CONFIG_FILENAME } from "../core/config.js";
|
|
4
|
+
export function validateProject(ctx) {
|
|
5
|
+
const issues = [];
|
|
6
|
+
const { root, config } = ctx;
|
|
7
|
+
if (!ctx.configFound) {
|
|
8
|
+
issues.push({
|
|
9
|
+
code: "CONFIG_MISSING",
|
|
10
|
+
severity: "error",
|
|
11
|
+
file: CONFIG_FILENAME,
|
|
12
|
+
message: `${CONFIG_FILENAME} not found`,
|
|
13
|
+
suggestion: "Run `meta init` to create it.",
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
if (!fs.existsSync(path.join(root, config.paths.root_agents))) {
|
|
17
|
+
issues.push({
|
|
18
|
+
code: "ROOT_AGENTS_MISSING",
|
|
19
|
+
severity: "error",
|
|
20
|
+
file: config.paths.root_agents,
|
|
21
|
+
message: `Root ${config.paths.root_agents} is missing`,
|
|
22
|
+
suggestion: "Run `meta init` to generate the root AGENTS.md.",
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
for (const [key, rel] of [
|
|
26
|
+
["paths.docs", config.paths.docs],
|
|
27
|
+
["paths.domain_docs", config.paths.domain_docs],
|
|
28
|
+
["paths.adr_docs", config.paths.adr_docs],
|
|
29
|
+
]) {
|
|
30
|
+
if (!fs.existsSync(path.join(root, rel))) {
|
|
31
|
+
issues.push({
|
|
32
|
+
code: "PATH_MISSING",
|
|
33
|
+
severity: "error",
|
|
34
|
+
file: rel,
|
|
35
|
+
message: `Configured path does not exist: ${rel} (${key})`,
|
|
36
|
+
suggestion: `Create the directory or update ${key} in ${CONFIG_FILENAME}.`,
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return { issues, checkedFiles: 1 };
|
|
41
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@bossmissing/agent-meta",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Agent Meta CLI - manage AI agent collaboration metadata (AGENTS.md, domain docs, ADRs, frontmatter checks)",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"cli",
|
|
7
|
+
"agents",
|
|
8
|
+
"agents-md",
|
|
9
|
+
"adr",
|
|
10
|
+
"documentation",
|
|
11
|
+
"ai-coding",
|
|
12
|
+
"openspec",
|
|
13
|
+
"frontmatter",
|
|
14
|
+
"metadata"
|
|
15
|
+
],
|
|
16
|
+
"license": "MIT",
|
|
17
|
+
"type": "module",
|
|
18
|
+
"bin": {
|
|
19
|
+
"meta": "./dist/cli.js"
|
|
20
|
+
},
|
|
21
|
+
"main": "./dist/index.js",
|
|
22
|
+
"types": "./dist/index.d.ts",
|
|
23
|
+
"files": [
|
|
24
|
+
"dist",
|
|
25
|
+
"README.md",
|
|
26
|
+
"LICENSE"
|
|
27
|
+
],
|
|
28
|
+
"publishConfig": {
|
|
29
|
+
"access": "public",
|
|
30
|
+
"registry": "https://registry.npmjs.org"
|
|
31
|
+
},
|
|
32
|
+
"engines": {
|
|
33
|
+
"node": ">=20"
|
|
34
|
+
},
|
|
35
|
+
"scripts": {
|
|
36
|
+
"build": "tsc -p tsconfig.build.json",
|
|
37
|
+
"dev": "tsx src/cli.ts",
|
|
38
|
+
"test": "vitest run",
|
|
39
|
+
"test:watch": "vitest",
|
|
40
|
+
"typecheck": "tsc --noEmit",
|
|
41
|
+
"prepublishOnly": "pnpm build"
|
|
42
|
+
},
|
|
43
|
+
"dependencies": {
|
|
44
|
+
"@inquirer/prompts": "^7.0.0",
|
|
45
|
+
"commander": "^12.1.0",
|
|
46
|
+
"fast-glob": "^3.3.2",
|
|
47
|
+
"gray-matter": "^4.0.3",
|
|
48
|
+
"picocolors": "^1.1.0",
|
|
49
|
+
"yaml": "^2.5.0",
|
|
50
|
+
"zod": "^3.23.8"
|
|
51
|
+
},
|
|
52
|
+
"devDependencies": {
|
|
53
|
+
"@types/node": "^20.14.0",
|
|
54
|
+
"tsx": "^4.19.0",
|
|
55
|
+
"typescript": "^5.5.0",
|
|
56
|
+
"vitest": "^2.1.0"
|
|
57
|
+
},
|
|
58
|
+
"packageManager": "pnpm@10.0.0",
|
|
59
|
+
"pnpm": {
|
|
60
|
+
"onlyBuiltDependencies": [
|
|
61
|
+
"esbuild"
|
|
62
|
+
]
|
|
63
|
+
}
|
|
64
|
+
}
|