@acta-dev/cli 0.1.1 → 1.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/dist/index.js +340 -18
- package/package.json +3 -2
package/dist/index.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
// src/index.ts
|
|
4
4
|
import { realpathSync } from "fs";
|
|
5
5
|
import { fileURLToPath } from "url";
|
|
6
|
-
import { defineCommand as
|
|
6
|
+
import { defineCommand as defineCommand10, runMain } from "citty";
|
|
7
7
|
|
|
8
8
|
// src/commands/build.ts
|
|
9
9
|
import { buildArtifacts } from "@acta-dev/core";
|
|
@@ -226,8 +226,170 @@ import { existsSync as existsSync2 } from "fs";
|
|
|
226
226
|
import { mkdir, writeFile } from "fs/promises";
|
|
227
227
|
import { join as join2, resolve as resolve2 } from "path";
|
|
228
228
|
import { createInterface } from "readline";
|
|
229
|
-
import { resolveConfig as
|
|
229
|
+
import { resolveConfig as resolveConfig3 } from "@acta-dev/core";
|
|
230
230
|
import { defineCommand as defineCommand3 } from "citty";
|
|
231
|
+
|
|
232
|
+
// src/skill.ts
|
|
233
|
+
import {
|
|
234
|
+
adrStatuses,
|
|
235
|
+
documentKinds,
|
|
236
|
+
internalLinkKeys,
|
|
237
|
+
linkKeys,
|
|
238
|
+
resolveConfig as resolveConfig2,
|
|
239
|
+
specStatuses
|
|
240
|
+
} from "@acta-dev/core";
|
|
241
|
+
var SKILL_NAME = "acta-document";
|
|
242
|
+
var LINK_DESCRIPTIONS = {
|
|
243
|
+
related: "Loosely related documents.",
|
|
244
|
+
supersedes: "This document supersedes the target; mirror with `replacedBy`.",
|
|
245
|
+
replacedBy: "This document is replaced by the target.",
|
|
246
|
+
decidedBy: "This spec was decided by an ADR.",
|
|
247
|
+
dependsOn: "This document depends on the target decision.",
|
|
248
|
+
validates: "This spec validates/implements an ADR decision.",
|
|
249
|
+
references: "External URLs only; must be valid http(s)."
|
|
250
|
+
};
|
|
251
|
+
function table(header, rows) {
|
|
252
|
+
const head = `| ${header.join(" | ")} |`;
|
|
253
|
+
const sep = `| ${header.map(() => "---").join(" | ")} |`;
|
|
254
|
+
const body = rows.map((r) => `| ${r.join(" | ")} |`).join("\n");
|
|
255
|
+
return [head, sep, body].join("\n");
|
|
256
|
+
}
|
|
257
|
+
function renderSkill() {
|
|
258
|
+
const config = resolveConfig2({}, { rootDir: process.cwd() });
|
|
259
|
+
const dirFor = {
|
|
260
|
+
adr: config.docs.adrDir,
|
|
261
|
+
spec: config.docs.specDir
|
|
262
|
+
};
|
|
263
|
+
const prefixFor = {
|
|
264
|
+
adr: config.ids.adrPrefix,
|
|
265
|
+
spec: config.ids.specPrefix
|
|
266
|
+
};
|
|
267
|
+
const required = config.validation.requiredSections;
|
|
268
|
+
const kindRows = documentKinds.map((kind) => [
|
|
269
|
+
`\`${kind}\``,
|
|
270
|
+
`\`${prefixFor[kind]}\``,
|
|
271
|
+
`\`${dirFor[kind]}/\``
|
|
272
|
+
]);
|
|
273
|
+
const linkRows = linkKeys.map((key) => [
|
|
274
|
+
`\`${key}\``,
|
|
275
|
+
key === "references" ? "external" : "internal",
|
|
276
|
+
LINK_DESCRIPTIONS[key]
|
|
277
|
+
]);
|
|
278
|
+
const frontmatter = [
|
|
279
|
+
"---",
|
|
280
|
+
`name: ${SKILL_NAME}`,
|
|
281
|
+
"description: >-",
|
|
282
|
+
" Create and maintain Acta ADR/spec documents with the `acta` CLI. Use after",
|
|
283
|
+
" implementing a feature, fixing a notable bug, or making an architectural",
|
|
284
|
+
" decision, or when asked to document a decision, write an ADR, or create a spec.",
|
|
285
|
+
"allowed-tools: Bash",
|
|
286
|
+
"---"
|
|
287
|
+
].join("\n");
|
|
288
|
+
return `${frontmatter}
|
|
289
|
+
|
|
290
|
+
# Acta: document decisions and specs
|
|
291
|
+
|
|
292
|
+
Acta is a docs-as-code CLI that keeps Architecture Decision Records (ADRs) and
|
|
293
|
+
specs validated and linked in Git. Drive it through the \`acta\` binary; every
|
|
294
|
+
data command supports \`--json\` for reliable parsing. **Never scrape human
|
|
295
|
+
output \u2014 always pass \`--json\` and parse stdout.** Human logs go to stderr.
|
|
296
|
+
|
|
297
|
+
## When to use
|
|
298
|
+
|
|
299
|
+
- After implementing a feature or system \u2192 write a \`spec\`.
|
|
300
|
+
- After making an architectural decision (or choosing between options) \u2192 write an \`adr\`.
|
|
301
|
+
- When the user asks to "document this decision", "write an ADR", or "create a spec".
|
|
302
|
+
|
|
303
|
+
## Document model
|
|
304
|
+
|
|
305
|
+
${table(["Kind", "ID prefix", "Directory"], kindRows)}
|
|
306
|
+
|
|
307
|
+
IDs are \`<PREFIX>-<NNNN>\` (zero-padded to width ${config.ids.width}); the CLI allocates the next ID.
|
|
308
|
+
|
|
309
|
+
**ADR statuses:** ${adrStatuses.map((s) => `\`${s}\``).join(", ")} (default \`proposed\`).
|
|
310
|
+
**Spec statuses:** ${specStatuses.map((s) => `\`${s}\``).join(", ")} (default \`draft\`).
|
|
311
|
+
|
|
312
|
+
### Link types (frontmatter \`links:\`)
|
|
313
|
+
|
|
314
|
+
${table(["Key", "Kind", "Meaning"], linkRows)}
|
|
315
|
+
|
|
316
|
+
Internal link keys (${internalLinkKeys.map((k) => `\`${k}\``).join(", ")}) reference other
|
|
317
|
+
document IDs. \`references\` holds external URLs only.
|
|
318
|
+
|
|
319
|
+
### Required sections
|
|
320
|
+
|
|
321
|
+
- **ADR:** ${required.adr.map((s) => `\`# ${s}\``).join(", ")}
|
|
322
|
+
- **Spec:** ${required.spec.map((s) => `\`# ${s}\``).join(", ")}
|
|
323
|
+
|
|
324
|
+
## Procedure
|
|
325
|
+
|
|
326
|
+
1. **Pick the kind.** Architectural decision \u2192 \`adr\`. Feature/system \u2192 \`spec\`.
|
|
327
|
+
2. **Create it** and capture the path from JSON:
|
|
328
|
+
\`\`\`sh
|
|
329
|
+
acta new adr "Short title" --json # or: acta new spec "Short title" --json
|
|
330
|
+
\`\`\`
|
|
331
|
+
Returns \`{ "id", "kind", "title", "status", "path", "relativePath" }\`. Edit \`path\`.
|
|
332
|
+
3. **Fill frontmatter:** set \`status\`, \`tags\`, \`component\`, \`owners\`, a one-line
|
|
333
|
+
\`summary\`, and \`links\` (use the link types above; \`references\` for external URLs).
|
|
334
|
+
4. **Fill required sections** (see above) with real content \u2014 no placeholders.
|
|
335
|
+
5. **Validate and fix-loop:**
|
|
336
|
+
\`\`\`sh
|
|
337
|
+
acta validate --json
|
|
338
|
+
\`\`\`
|
|
339
|
+
Returns \`{ "valid", "errorCount", "warningCount", "issues": [{ severity, code, documentId, message, path, line }] }\`.
|
|
340
|
+
If \`valid\` is \`false\`, fix each \`issues[].message\` and re-run. Repeat until
|
|
341
|
+
\`valid\` is \`true\` (cap at ~5 iterations; if still failing, report the issues).
|
|
342
|
+
6. **Verify links** with \`acta show <id> --json\` (inspect \`links\` and \`backlinks\`).
|
|
343
|
+
|
|
344
|
+
## Command reference (all accept \`--json\` unless noted)
|
|
345
|
+
|
|
346
|
+
- \`acta new adr|spec "<title>" [--status <s>] [--tags a,b] [--id <ID>]\` \u2192 created doc.
|
|
347
|
+
- \`acta list [--kind adr|spec] [--status <s>] [--tag <t>]\` \u2192 array of documents.
|
|
348
|
+
- \`acta show <id>\` \u2192 one normalized document with \`links\` + \`backlinks\`.
|
|
349
|
+
- \`acta validate\` \u2192 validation result; exit \`1\` if invalid.
|
|
350
|
+
- \`acta graph --format json|mermaid|dot\` \u2192 relationship graph.
|
|
351
|
+
- \`acta build\` \u2192 build manifest (\`documentCount\`, \`errorCount\`, \`warningCount\`).
|
|
352
|
+
|
|
353
|
+
Exit codes: \`0\` ok \xB7 \`1\` validation/operation failure \xB7 \`2\` usage error.
|
|
354
|
+
`;
|
|
355
|
+
}
|
|
356
|
+
var AGENTS_BLOCK_START = "<!-- acta:skill:start -->";
|
|
357
|
+
var AGENTS_BLOCK_END = "<!-- acta:skill:end -->";
|
|
358
|
+
function renderAgentsBlock() {
|
|
359
|
+
const adr = adrStatuses.join(", ");
|
|
360
|
+
const spec = specStatuses.join(", ");
|
|
361
|
+
return `${AGENTS_BLOCK_START}
|
|
362
|
+
## Documenting work with Acta
|
|
363
|
+
|
|
364
|
+
After implementing a feature or making an architectural decision, record it with
|
|
365
|
+
the \`acta\` CLI (always use \`--json\` and parse stdout):
|
|
366
|
+
|
|
367
|
+
1. \`acta new adr|spec "Title" --json\` \u2014 \`adr\` for decisions, \`spec\` for features. Capture \`path\`.
|
|
368
|
+
2. Fill frontmatter (\`status\`, \`tags\`, \`component\`, \`owners\`, \`summary\`, \`links\`) and required sections.
|
|
369
|
+
3. \`acta validate --json\` \u2014 fix each \`issues[].message\` and repeat until \`valid\` is \`true\`.
|
|
370
|
+
4. \`acta show <id> --json\` \u2014 verify \`links\`/\`backlinks\`.
|
|
371
|
+
|
|
372
|
+
ADR statuses: ${adr}. Spec statuses: ${spec}. Link keys: ${linkKeys.join(", ")}.
|
|
373
|
+
${AGENTS_BLOCK_END}`;
|
|
374
|
+
}
|
|
375
|
+
function upsertAgentsBlock(existing) {
|
|
376
|
+
const block = renderAgentsBlock();
|
|
377
|
+
const start = existing.indexOf(AGENTS_BLOCK_START);
|
|
378
|
+
const end = existing.indexOf(AGENTS_BLOCK_END);
|
|
379
|
+
if (start !== -1 && end !== -1 && end > start) {
|
|
380
|
+
const before = existing.slice(0, start);
|
|
381
|
+
const after = existing.slice(end + AGENTS_BLOCK_END.length);
|
|
382
|
+
return `${before}${block}${after}`;
|
|
383
|
+
}
|
|
384
|
+
const trimmed = existing.replace(/\s+$/, "");
|
|
385
|
+
return trimmed.length > 0 ? `${trimmed}
|
|
386
|
+
|
|
387
|
+
${block}
|
|
388
|
+
` : `${block}
|
|
389
|
+
`;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
// src/commands/init.ts
|
|
231
393
|
var ADR_TEMPLATE = `---
|
|
232
394
|
id: ADR-0000
|
|
233
395
|
kind: adr
|
|
@@ -440,6 +602,11 @@ var initCommand = defineCommand3({
|
|
|
440
602
|
description: "Install GitHub Actions workflow template",
|
|
441
603
|
default: false
|
|
442
604
|
},
|
|
605
|
+
skill: {
|
|
606
|
+
type: "boolean",
|
|
607
|
+
description: "Install the acta-document agent skill and AGENTS.md guidance",
|
|
608
|
+
default: false
|
|
609
|
+
},
|
|
443
610
|
config: {
|
|
444
611
|
type: "string",
|
|
445
612
|
alias: "c",
|
|
@@ -449,7 +616,7 @@ var initCommand = defineCommand3({
|
|
|
449
616
|
async run({ args }) {
|
|
450
617
|
const cwd = resolve2(process.cwd());
|
|
451
618
|
const yes = args.yes;
|
|
452
|
-
const config =
|
|
619
|
+
const config = resolveConfig3({}, { rootDir: cwd });
|
|
453
620
|
printLine("Initializing Acta docs structure...");
|
|
454
621
|
printLine();
|
|
455
622
|
const configPath = join2(cwd, "acta.config.ts");
|
|
@@ -491,6 +658,18 @@ var initCommand = defineCommand3({
|
|
|
491
658
|
const workflowWritten = await safeWriteFile(workflowPath, GITHUB_ACTION_TEMPLATE, yes);
|
|
492
659
|
if (workflowWritten) printSuccess(`Created ${workflowPath}`);
|
|
493
660
|
}
|
|
661
|
+
if (args.skill) {
|
|
662
|
+
const skillDir = join2(cwd, ".claude", "skills", "acta-document");
|
|
663
|
+
await mkdir(skillDir, { recursive: true });
|
|
664
|
+
const skillPath = join2(skillDir, "SKILL.md");
|
|
665
|
+
const skillWritten = await safeWriteFile(skillPath, renderSkill(), yes);
|
|
666
|
+
if (skillWritten) printSuccess(`Created ${skillPath}`);
|
|
667
|
+
const { readFile: readFile3 } = await import("fs/promises");
|
|
668
|
+
const agentsPath = join2(cwd, "AGENTS.md");
|
|
669
|
+
const existing = existsSync2(agentsPath) ? await readFile3(agentsPath, "utf8") : "";
|
|
670
|
+
await writeFile(agentsPath, upsertAgentsBlock(existing), "utf8");
|
|
671
|
+
printSuccess(`Updated ${agentsPath} with Acta agent guidance`);
|
|
672
|
+
}
|
|
494
673
|
printLine();
|
|
495
674
|
printSuccess("Acta initialized. Run `acta validate` to check your documents.");
|
|
496
675
|
}
|
|
@@ -606,7 +785,7 @@ var listCommand = defineCommand4({
|
|
|
606
785
|
import { existsSync as existsSync3 } from "fs";
|
|
607
786
|
import { writeFile as writeFile2 } from "fs/promises";
|
|
608
787
|
import { join as join4, relative } from "path";
|
|
609
|
-
import { adrStatuses, specStatuses } from "@acta-dev/core";
|
|
788
|
+
import { adrStatuses as adrStatuses2, specStatuses as specStatuses2 } from "@acta-dev/core";
|
|
610
789
|
import { defineCommand as defineCommand5 } from "citty";
|
|
611
790
|
|
|
612
791
|
// src/id.ts
|
|
@@ -668,7 +847,7 @@ async function createDocument(kind, title, opts) {
|
|
|
668
847
|
}
|
|
669
848
|
const defaultStatus = kind === "adr" ? "proposed" : "draft";
|
|
670
849
|
const status = opts.status ?? defaultStatus;
|
|
671
|
-
const validStatuses = kind === "adr" ?
|
|
850
|
+
const validStatuses = kind === "adr" ? adrStatuses2 : specStatuses2;
|
|
672
851
|
if (!validStatuses.includes(status)) {
|
|
673
852
|
exitUsage(`Invalid status "${status}" for ${kind}. Valid: ${validStatuses.join(", ")}`);
|
|
674
853
|
}
|
|
@@ -794,7 +973,7 @@ function parseTags(value) {
|
|
|
794
973
|
// src/commands/renumber.ts
|
|
795
974
|
import { readFile as readFile2, rename, writeFile as writeFile3 } from "fs/promises";
|
|
796
975
|
import { basename, dirname, join as join5 } from "path";
|
|
797
|
-
import { internalLinkKeys, loadProject as loadProject3 } from "@acta-dev/core";
|
|
976
|
+
import { internalLinkKeys as internalLinkKeys2, loadProject as loadProject3 } from "@acta-dev/core";
|
|
798
977
|
import { defineCommand as defineCommand6 } from "citty";
|
|
799
978
|
import kleur4 from "kleur";
|
|
800
979
|
import { parse as parseYaml, stringify as stringifyYaml } from "yaml";
|
|
@@ -818,7 +997,7 @@ function buildRenumberPlan(fromId, toId, project) {
|
|
|
818
997
|
const oldSlug = oldFilename.replace(`${target.id}-`, "").replace(/\.md$/, "");
|
|
819
998
|
const newFilename = `${toId}-${oldSlug}.md`;
|
|
820
999
|
const newPath = join5(dirname(target.file.path), newFilename);
|
|
821
|
-
const affectedDocs = project.documents.filter((d) => d.id !== target.id).filter((d) =>
|
|
1000
|
+
const affectedDocs = project.documents.filter((d) => d.id !== target.id).filter((d) => internalLinkKeys2.some((key) => d.links[key].includes(target.id))).map((d) => ({ doc: d, path: d.file.path }));
|
|
822
1001
|
return {
|
|
823
1002
|
target,
|
|
824
1003
|
oldPath: target.file.path,
|
|
@@ -840,7 +1019,7 @@ async function rewriteDocument(filePath, oldId, newId, isTarget) {
|
|
|
840
1019
|
}
|
|
841
1020
|
const linksObj = fm.links;
|
|
842
1021
|
if (linksObj && typeof linksObj === "object") {
|
|
843
|
-
for (const key of
|
|
1022
|
+
for (const key of internalLinkKeys2) {
|
|
844
1023
|
const arr = linksObj[key];
|
|
845
1024
|
if (Array.isArray(arr)) {
|
|
846
1025
|
linksObj[key] = arr.map((v) => v === oldId ? newId : v);
|
|
@@ -1036,13 +1215,155 @@ function formatShowDate(value) {
|
|
|
1036
1215
|
return `${year}-${month}-${day}`;
|
|
1037
1216
|
}
|
|
1038
1217
|
|
|
1218
|
+
// src/commands/site.ts
|
|
1219
|
+
import { spawn } from "child_process";
|
|
1220
|
+
import { createRequire } from "module";
|
|
1221
|
+
import { dirname as dirname2, join as join6, resolve as resolve3 } from "path";
|
|
1222
|
+
import { buildArtifacts as buildArtifacts2 } from "@acta-dev/core";
|
|
1223
|
+
import { defineCommand as defineCommand8 } from "citty";
|
|
1224
|
+
import kleur6 from "kleur";
|
|
1225
|
+
function resolveSiteOptions(config, args, cwd = process.cwd()) {
|
|
1226
|
+
return {
|
|
1227
|
+
outDir: args.out ? resolve3(cwd, args.out) : config.resolvedSite.outDir,
|
|
1228
|
+
base: args.base ?? config.site.base,
|
|
1229
|
+
site: args.site ?? config.site.url
|
|
1230
|
+
};
|
|
1231
|
+
}
|
|
1232
|
+
function buildSiteEnv(config, options) {
|
|
1233
|
+
const env = {
|
|
1234
|
+
...process.env,
|
|
1235
|
+
ASTRO_TELEMETRY_DISABLED: "1",
|
|
1236
|
+
ACTA_PROJECT_ROOT: config.rootDir,
|
|
1237
|
+
ACTA_DIST_DIR: config.resolvedBuild.outDir,
|
|
1238
|
+
ACTA_SITE_OUT: options.outDir
|
|
1239
|
+
};
|
|
1240
|
+
if (options.base !== void 0) env.ACTA_BASE = options.base;
|
|
1241
|
+
if (options.site !== void 0) env.ACTA_SITE = options.site;
|
|
1242
|
+
return env;
|
|
1243
|
+
}
|
|
1244
|
+
var siteCommand = defineCommand8({
|
|
1245
|
+
meta: {
|
|
1246
|
+
name: "site",
|
|
1247
|
+
description: "Build a deployable static web viewer from your docs into the site output dir"
|
|
1248
|
+
},
|
|
1249
|
+
args: {
|
|
1250
|
+
out: {
|
|
1251
|
+
type: "string",
|
|
1252
|
+
description: "Output directory for the generated site (default: .acta/site)"
|
|
1253
|
+
},
|
|
1254
|
+
base: {
|
|
1255
|
+
type: "string",
|
|
1256
|
+
description: "Base path for hosting under a subpath (e.g. /my-repo for project Pages)"
|
|
1257
|
+
},
|
|
1258
|
+
site: {
|
|
1259
|
+
type: "string",
|
|
1260
|
+
description: "Absolute site URL used for canonical links and sitemaps"
|
|
1261
|
+
},
|
|
1262
|
+
"skip-build": {
|
|
1263
|
+
type: "boolean",
|
|
1264
|
+
description: "Reuse existing artifacts instead of running `acta build` first",
|
|
1265
|
+
default: false
|
|
1266
|
+
},
|
|
1267
|
+
config: {
|
|
1268
|
+
type: "string",
|
|
1269
|
+
alias: "c",
|
|
1270
|
+
description: "Path to acta.config.ts"
|
|
1271
|
+
},
|
|
1272
|
+
json: {
|
|
1273
|
+
type: "boolean",
|
|
1274
|
+
description: "Print the result as JSON",
|
|
1275
|
+
default: false
|
|
1276
|
+
}
|
|
1277
|
+
},
|
|
1278
|
+
async run({ args }) {
|
|
1279
|
+
const { config } = await resolveContext({ config: args.config });
|
|
1280
|
+
const json = Boolean(args.json);
|
|
1281
|
+
let documentCount = 0;
|
|
1282
|
+
if (!args["skip-build"]) {
|
|
1283
|
+
if (!json) printLine("Building artifacts...");
|
|
1284
|
+
const { manifest, validation } = await buildArtifacts2({ config });
|
|
1285
|
+
documentCount = manifest.documentCount;
|
|
1286
|
+
if (validation.errorCount > 0 && !json) {
|
|
1287
|
+
printWarn(
|
|
1288
|
+
`Building site with ${validation.errorCount} validation error${validation.errorCount !== 1 ? "s" : ""}. Run \`acta validate\` for details.`
|
|
1289
|
+
);
|
|
1290
|
+
}
|
|
1291
|
+
}
|
|
1292
|
+
const webDir = resolveWebPackageDir();
|
|
1293
|
+
if (!webDir) {
|
|
1294
|
+
return exitFailure(
|
|
1295
|
+
"Could not locate @acta-dev/web. Install it alongside @acta-dev/cli to use `acta site`."
|
|
1296
|
+
);
|
|
1297
|
+
}
|
|
1298
|
+
const astroBin = resolveAstroBin(webDir);
|
|
1299
|
+
if (!astroBin) {
|
|
1300
|
+
return exitFailure("Could not locate the Astro binary inside @acta-dev/web.");
|
|
1301
|
+
}
|
|
1302
|
+
const options = resolveSiteOptions(config, args);
|
|
1303
|
+
if (!json) printLine("Building static viewer...");
|
|
1304
|
+
const env = buildSiteEnv(config, options);
|
|
1305
|
+
const code = await runAstroBuild(astroBin, webDir, env, json);
|
|
1306
|
+
if (code !== 0) {
|
|
1307
|
+
return exitFailure(`Astro build failed with exit code ${code}.`);
|
|
1308
|
+
}
|
|
1309
|
+
if (json) {
|
|
1310
|
+
printJson({
|
|
1311
|
+
ok: true,
|
|
1312
|
+
outDir: options.outDir,
|
|
1313
|
+
base: options.base ?? null,
|
|
1314
|
+
site: options.site ?? null,
|
|
1315
|
+
documentCount
|
|
1316
|
+
});
|
|
1317
|
+
return;
|
|
1318
|
+
}
|
|
1319
|
+
printLine();
|
|
1320
|
+
printSuccess("Site built");
|
|
1321
|
+
printLine(` ${kleur6.bold("Output:")} ${options.outDir}`);
|
|
1322
|
+
if (options.base) printLine(` ${kleur6.bold("Base:")} ${options.base}`);
|
|
1323
|
+
printLine();
|
|
1324
|
+
printLine(kleur6.dim("Deploy the contents of the output directory to any static host."));
|
|
1325
|
+
}
|
|
1326
|
+
});
|
|
1327
|
+
function resolveWebPackageDir() {
|
|
1328
|
+
const require2 = createRequire(import.meta.url);
|
|
1329
|
+
try {
|
|
1330
|
+
return dirname2(require2.resolve("@acta-dev/web/package.json"));
|
|
1331
|
+
} catch {
|
|
1332
|
+
return void 0;
|
|
1333
|
+
}
|
|
1334
|
+
}
|
|
1335
|
+
function resolveAstroBin(webDir) {
|
|
1336
|
+
const require2 = createRequire(join6(webDir, "package.json"));
|
|
1337
|
+
try {
|
|
1338
|
+
const astroPkgJsonPath = require2.resolve("astro/package.json");
|
|
1339
|
+
const astroPkg = require2("astro/package.json");
|
|
1340
|
+
const binRel = typeof astroPkg.bin === "string" ? astroPkg.bin : astroPkg.bin?.astro;
|
|
1341
|
+
if (!binRel) return void 0;
|
|
1342
|
+
return join6(dirname2(astroPkgJsonPath), binRel);
|
|
1343
|
+
} catch {
|
|
1344
|
+
return void 0;
|
|
1345
|
+
}
|
|
1346
|
+
}
|
|
1347
|
+
function runAstroBuild(astroBin, webDir, env, json) {
|
|
1348
|
+
return new Promise((resolvePromise) => {
|
|
1349
|
+
const child = spawn(process.execPath, [astroBin, "build"], {
|
|
1350
|
+
cwd: webDir,
|
|
1351
|
+
env,
|
|
1352
|
+
// Astro logs go to stderr so JSON on stdout stays clean.
|
|
1353
|
+
stdio: json ? ["ignore", "ignore", "inherit"] : "inherit"
|
|
1354
|
+
});
|
|
1355
|
+
child.on("close", (code) => resolvePromise(code ?? 1));
|
|
1356
|
+
child.on("error", () => resolvePromise(1));
|
|
1357
|
+
});
|
|
1358
|
+
}
|
|
1359
|
+
|
|
1039
1360
|
// src/commands/validate.ts
|
|
1040
1361
|
import { mkdir as mkdir2, writeFile as writeFile4 } from "fs/promises";
|
|
1041
|
-
import { join as
|
|
1362
|
+
import { join as join7 } from "path";
|
|
1042
1363
|
import { validateLoadedProject } from "@acta-dev/core";
|
|
1043
|
-
import { defineCommand as
|
|
1044
|
-
import
|
|
1045
|
-
var validateCommand =
|
|
1364
|
+
import { defineCommand as defineCommand9 } from "citty";
|
|
1365
|
+
import kleur7 from "kleur";
|
|
1366
|
+
var validateCommand = defineCommand9({
|
|
1046
1367
|
meta: {
|
|
1047
1368
|
name: "validate",
|
|
1048
1369
|
description: "Validate frontmatter, IDs, links, sections and repository rules"
|
|
@@ -1074,16 +1395,16 @@ var validateCommand = defineCommand8({
|
|
|
1074
1395
|
}
|
|
1075
1396
|
if (args.ci) {
|
|
1076
1397
|
await mkdir2(config.resolvedBuild.outDir, { recursive: true });
|
|
1077
|
-
const outPath =
|
|
1398
|
+
const outPath = join7(config.resolvedBuild.outDir, "validation.json");
|
|
1078
1399
|
await writeFile4(outPath, `${JSON.stringify(result, null, 2)}
|
|
1079
1400
|
`, "utf8");
|
|
1080
1401
|
if (result.errors.length > 0) {
|
|
1081
1402
|
for (const issue of result.errors) {
|
|
1082
|
-
printLine(`${
|
|
1403
|
+
printLine(`${kleur7.red("error")} ${issue.documentId ?? ""} ${issue.message}`);
|
|
1083
1404
|
}
|
|
1084
1405
|
}
|
|
1085
1406
|
for (const issue of result.warnings) {
|
|
1086
|
-
printLine(`${
|
|
1407
|
+
printLine(`${kleur7.yellow("warn")} ${issue.documentId ?? ""} ${issue.message}`);
|
|
1087
1408
|
}
|
|
1088
1409
|
printLine(`Written ${outPath}`);
|
|
1089
1410
|
process.exit(result.valid ? 0 : 1);
|
|
@@ -1096,12 +1417,12 @@ var validateCommand = defineCommand8({
|
|
|
1096
1417
|
const warnings = result.issues.filter((i) => i.severity === "warning");
|
|
1097
1418
|
if (errors.length > 0) {
|
|
1098
1419
|
printLine();
|
|
1099
|
-
printLine(
|
|
1420
|
+
printLine(kleur7.bold("Errors:"));
|
|
1100
1421
|
printIssues(errors);
|
|
1101
1422
|
}
|
|
1102
1423
|
if (warnings.length > 0) {
|
|
1103
1424
|
printLine();
|
|
1104
|
-
printLine(
|
|
1425
|
+
printLine(kleur7.bold("Warnings:"));
|
|
1105
1426
|
printIssues(warnings);
|
|
1106
1427
|
}
|
|
1107
1428
|
printLine();
|
|
@@ -1112,7 +1433,7 @@ var validateCommand = defineCommand8({
|
|
|
1112
1433
|
});
|
|
1113
1434
|
|
|
1114
1435
|
// src/index.ts
|
|
1115
|
-
var main =
|
|
1436
|
+
var main = defineCommand10({
|
|
1116
1437
|
meta: {
|
|
1117
1438
|
name: "acta",
|
|
1118
1439
|
version: "0.0.0",
|
|
@@ -1126,6 +1447,7 @@ var main = defineCommand9({
|
|
|
1126
1447
|
validate: validateCommand,
|
|
1127
1448
|
graph: graphCommand,
|
|
1128
1449
|
build: buildCommand,
|
|
1450
|
+
site: siteCommand,
|
|
1129
1451
|
renumber: renumberCommand
|
|
1130
1452
|
}
|
|
1131
1453
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@acta-dev/cli",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "Acta CLI — TypeScript-first docs-as-code tooling for ADR and spec documents in Git. Provides the `acta` binary.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"adr",
|
|
@@ -45,7 +45,8 @@
|
|
|
45
45
|
"citty": "^0.2.2",
|
|
46
46
|
"kleur": "^4.1.5",
|
|
47
47
|
"yaml": "^2.8.3",
|
|
48
|
-
"@acta-dev/core": "
|
|
48
|
+
"@acta-dev/core": "1.1.0",
|
|
49
|
+
"@acta-dev/web": "1.0.0"
|
|
49
50
|
},
|
|
50
51
|
"devDependencies": {
|
|
51
52
|
"execa": "^9.6.1",
|