@acta-dev/cli 1.1.0 → 1.3.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 +336 -58
- package/package.json +1 -1
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 defineCommand11, runMain } from "citty";
|
|
7
7
|
|
|
8
8
|
// src/commands/build.ts
|
|
9
9
|
import { buildArtifacts } from "@acta-dev/core";
|
|
@@ -222,14 +222,17 @@ var graphCommand = defineCommand2({
|
|
|
222
222
|
});
|
|
223
223
|
|
|
224
224
|
// src/commands/init.ts
|
|
225
|
-
import { existsSync as
|
|
226
|
-
import { mkdir, writeFile } from "fs/promises";
|
|
227
|
-
import { join as
|
|
225
|
+
import { existsSync as existsSync3 } from "fs";
|
|
226
|
+
import { mkdir as mkdir2, writeFile as writeFile2 } from "fs/promises";
|
|
227
|
+
import { join as join3, resolve as resolve2 } from "path";
|
|
228
228
|
import { createInterface } from "readline";
|
|
229
229
|
import { resolveConfig as resolveConfig3 } from "@acta-dev/core";
|
|
230
230
|
import { defineCommand as defineCommand3 } from "citty";
|
|
231
231
|
|
|
232
232
|
// src/skill.ts
|
|
233
|
+
import { existsSync as existsSync2 } from "fs";
|
|
234
|
+
import { mkdir, readFile, writeFile } from "fs/promises";
|
|
235
|
+
import { join as join2 } from "path";
|
|
233
236
|
import {
|
|
234
237
|
adrStatuses,
|
|
235
238
|
documentKinds,
|
|
@@ -239,6 +242,7 @@ import {
|
|
|
239
242
|
specStatuses
|
|
240
243
|
} from "@acta-dev/core";
|
|
241
244
|
var SKILL_NAME = "acta-document";
|
|
245
|
+
var skillFormats = ["codex", "claude", "both"];
|
|
242
246
|
var LINK_DESCRIPTIONS = {
|
|
243
247
|
related: "Loosely related documents.",
|
|
244
248
|
supersedes: "This document supersedes the target; mirror with `replacedBy`.",
|
|
@@ -250,9 +254,9 @@ var LINK_DESCRIPTIONS = {
|
|
|
250
254
|
};
|
|
251
255
|
function table(header, rows) {
|
|
252
256
|
const head = `| ${header.join(" | ")} |`;
|
|
253
|
-
const
|
|
257
|
+
const sep2 = `| ${header.map(() => "---").join(" | ")} |`;
|
|
254
258
|
const body = rows.map((r) => `| ${r.join(" | ")} |`).join("\n");
|
|
255
|
-
return [head,
|
|
259
|
+
return [head, sep2, body].join("\n");
|
|
256
260
|
}
|
|
257
261
|
function renderSkill() {
|
|
258
262
|
const config = resolveConfig2({}, { rootDir: process.cwd() });
|
|
@@ -388,6 +392,31 @@ ${block}
|
|
|
388
392
|
` : `${block}
|
|
389
393
|
`;
|
|
390
394
|
}
|
|
395
|
+
async function installAgentSkill(cwd, format) {
|
|
396
|
+
const skillPaths = [];
|
|
397
|
+
const skillContent = renderSkill();
|
|
398
|
+
if (format === "codex" || format === "both") {
|
|
399
|
+
const skillDir = join2(cwd, ".agents", "skills", SKILL_NAME);
|
|
400
|
+
await mkdir(skillDir, { recursive: true });
|
|
401
|
+
const skillPath = join2(skillDir, "SKILL.md");
|
|
402
|
+
await writeFile(skillPath, skillContent, "utf8");
|
|
403
|
+
skillPaths.push(skillPath);
|
|
404
|
+
const agentsPath = join2(cwd, "AGENTS.md");
|
|
405
|
+
const existing = existsSync2(agentsPath) ? await readFile(agentsPath, "utf8") : "";
|
|
406
|
+
await writeFile(agentsPath, upsertAgentsBlock(existing), "utf8");
|
|
407
|
+
if (format === "codex") {
|
|
408
|
+
return { skillPaths, agentsPath };
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
if (format === "claude" || format === "both") {
|
|
412
|
+
const skillDir = join2(cwd, ".claude", "skills", SKILL_NAME);
|
|
413
|
+
await mkdir(skillDir, { recursive: true });
|
|
414
|
+
const skillPath = join2(skillDir, "SKILL.md");
|
|
415
|
+
await writeFile(skillPath, skillContent, "utf8");
|
|
416
|
+
skillPaths.push(skillPath);
|
|
417
|
+
}
|
|
418
|
+
return format === "both" ? { skillPaths, agentsPath: join2(cwd, "AGENTS.md") } : { skillPaths };
|
|
419
|
+
}
|
|
391
420
|
|
|
392
421
|
// src/commands/init.ts
|
|
393
422
|
var ADR_TEMPLATE = `---
|
|
@@ -566,7 +595,7 @@ async function confirm(message) {
|
|
|
566
595
|
});
|
|
567
596
|
}
|
|
568
597
|
async function safeWriteFile(filePath, content, yes) {
|
|
569
|
-
if (
|
|
598
|
+
if (existsSync3(filePath)) {
|
|
570
599
|
if (!yes) {
|
|
571
600
|
const ok = await confirm(` Overwrite ${filePath}?`);
|
|
572
601
|
if (!ok) {
|
|
@@ -577,7 +606,7 @@ async function safeWriteFile(filePath, content, yes) {
|
|
|
577
606
|
printWarn(`Overwriting ${filePath}`);
|
|
578
607
|
}
|
|
579
608
|
}
|
|
580
|
-
await
|
|
609
|
+
await writeFile2(filePath, content, "utf8");
|
|
581
610
|
return true;
|
|
582
611
|
}
|
|
583
612
|
var initCommand = defineCommand3({
|
|
@@ -604,7 +633,7 @@ var initCommand = defineCommand3({
|
|
|
604
633
|
},
|
|
605
634
|
skill: {
|
|
606
635
|
type: "boolean",
|
|
607
|
-
description: "
|
|
636
|
+
description: "Compatibility alias for `acta skill --init` after scaffolding",
|
|
608
637
|
default: false
|
|
609
638
|
},
|
|
610
639
|
config: {
|
|
@@ -619,7 +648,7 @@ var initCommand = defineCommand3({
|
|
|
619
648
|
const config = resolveConfig3({}, { rootDir: cwd });
|
|
620
649
|
printLine("Initializing Acta docs structure...");
|
|
621
650
|
printLine();
|
|
622
|
-
const configPath =
|
|
651
|
+
const configPath = join3(cwd, "acta.config.ts");
|
|
623
652
|
const configWritten = await safeWriteFile(configPath, CONFIG_TEMPLATE, yes);
|
|
624
653
|
if (configWritten) printSuccess(`Created ${configPath}`);
|
|
625
654
|
const dirs = [
|
|
@@ -628,47 +657,44 @@ var initCommand = defineCommand3({
|
|
|
628
657
|
config.resolvedDocs.templatesDir
|
|
629
658
|
];
|
|
630
659
|
for (const dir of dirs) {
|
|
631
|
-
await
|
|
660
|
+
await mkdir2(dir, { recursive: true });
|
|
632
661
|
printSuccess(`Created dir ${dir}`);
|
|
633
662
|
}
|
|
634
|
-
const adrTplPath =
|
|
635
|
-
const specTplPath =
|
|
663
|
+
const adrTplPath = join3(config.resolvedDocs.templatesDir, "adr.md");
|
|
664
|
+
const specTplPath = join3(config.resolvedDocs.templatesDir, "spec.md");
|
|
636
665
|
const adrWritten = await safeWriteFile(adrTplPath, ADR_TEMPLATE, yes);
|
|
637
666
|
if (adrWritten) printSuccess(`Created ${adrTplPath}`);
|
|
638
667
|
const specWritten = await safeWriteFile(specTplPath, SPEC_TEMPLATE, yes);
|
|
639
668
|
if (specWritten) printSuccess(`Created ${specTplPath}`);
|
|
640
|
-
const gitignorePath =
|
|
641
|
-
if (
|
|
642
|
-
const { readFile:
|
|
643
|
-
const content = await
|
|
669
|
+
const gitignorePath = join3(cwd, ".gitignore");
|
|
670
|
+
if (existsSync3(gitignorePath)) {
|
|
671
|
+
const { readFile: readFile4, appendFile } = await import("fs/promises");
|
|
672
|
+
const content = await readFile4(gitignorePath, "utf8");
|
|
644
673
|
if (!content.includes(".acta/")) {
|
|
645
674
|
await appendFile(gitignorePath, "\n# Acta build artifacts\n.acta/\n");
|
|
646
675
|
printSuccess(`Added .acta/ to .gitignore`);
|
|
647
676
|
}
|
|
648
677
|
}
|
|
649
678
|
if (args.hooks) {
|
|
650
|
-
const lefthookPath =
|
|
679
|
+
const lefthookPath = join3(cwd, "lefthook.yml");
|
|
651
680
|
const lefthookWritten = await safeWriteFile(lefthookPath, LEFTHOOK_TEMPLATE, yes);
|
|
652
681
|
if (lefthookWritten) printSuccess(`Created ${lefthookPath}`);
|
|
653
682
|
}
|
|
654
683
|
if (args["github-action"]) {
|
|
655
|
-
const workflowsDir =
|
|
656
|
-
await
|
|
657
|
-
const workflowPath =
|
|
684
|
+
const workflowsDir = join3(cwd, ".github", "workflows");
|
|
685
|
+
await mkdir2(workflowsDir, { recursive: true });
|
|
686
|
+
const workflowPath = join3(workflowsDir, "acta-ci.yml");
|
|
658
687
|
const workflowWritten = await safeWriteFile(workflowPath, GITHUB_ACTION_TEMPLATE, yes);
|
|
659
688
|
if (workflowWritten) printSuccess(`Created ${workflowPath}`);
|
|
660
689
|
}
|
|
661
690
|
if (args.skill) {
|
|
662
|
-
const
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
if (
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
const existing = existsSync2(agentsPath) ? await readFile3(agentsPath, "utf8") : "";
|
|
670
|
-
await writeFile(agentsPath, upsertAgentsBlock(existing), "utf8");
|
|
671
|
-
printSuccess(`Updated ${agentsPath} with Acta agent guidance`);
|
|
691
|
+
const result = await installAgentSkill(cwd, "both");
|
|
692
|
+
for (const skillPath of result.skillPaths) {
|
|
693
|
+
printSuccess(`Installed ${skillPath}`);
|
|
694
|
+
}
|
|
695
|
+
if (result.agentsPath) {
|
|
696
|
+
printSuccess(`Updated ${result.agentsPath} with Acta agent guidance`);
|
|
697
|
+
}
|
|
672
698
|
}
|
|
673
699
|
printLine();
|
|
674
700
|
printSuccess("Acta initialized. Run `acta validate` to check your documents.");
|
|
@@ -782,9 +808,9 @@ var listCommand = defineCommand4({
|
|
|
782
808
|
});
|
|
783
809
|
|
|
784
810
|
// src/commands/new.ts
|
|
785
|
-
import { existsSync as
|
|
786
|
-
import { writeFile as
|
|
787
|
-
import { join as
|
|
811
|
+
import { existsSync as existsSync4 } from "fs";
|
|
812
|
+
import { writeFile as writeFile3 } from "fs/promises";
|
|
813
|
+
import { join as join5, relative } from "path";
|
|
788
814
|
import { adrStatuses as adrStatuses2, specStatuses as specStatuses2 } from "@acta-dev/core";
|
|
789
815
|
import { defineCommand as defineCommand5 } from "citty";
|
|
790
816
|
|
|
@@ -805,11 +831,11 @@ function titleToSlug(title) {
|
|
|
805
831
|
}
|
|
806
832
|
|
|
807
833
|
// src/template.ts
|
|
808
|
-
import { readFile } from "fs/promises";
|
|
809
|
-
import { join as
|
|
834
|
+
import { readFile as readFile2 } from "fs/promises";
|
|
835
|
+
import { join as join4 } from "path";
|
|
810
836
|
async function renderTemplate(kind, vars, config) {
|
|
811
|
-
const templateFile =
|
|
812
|
-
const raw = await
|
|
837
|
+
const templateFile = join4(config.resolvedDocs.templatesDir, `${kind}.md`);
|
|
838
|
+
const raw = await readFile2(templateFile, "utf8");
|
|
813
839
|
return interpolate(raw, vars);
|
|
814
840
|
}
|
|
815
841
|
function interpolate(raw, vars) {
|
|
@@ -854,8 +880,8 @@ async function createDocument(kind, title, opts) {
|
|
|
854
880
|
const slug = titleToSlug(title.trim());
|
|
855
881
|
const filename = `${id}-${slug}.md`;
|
|
856
882
|
const dir = kind === "adr" ? config.resolvedDocs.adrDir : config.resolvedDocs.specDir;
|
|
857
|
-
const destPath =
|
|
858
|
-
if (
|
|
883
|
+
const destPath = join5(dir, filename);
|
|
884
|
+
if (existsSync4(destPath)) {
|
|
859
885
|
exitFailure(`File already exists: ${destPath}`);
|
|
860
886
|
}
|
|
861
887
|
const content = await renderTemplate(
|
|
@@ -863,7 +889,7 @@ async function createDocument(kind, title, opts) {
|
|
|
863
889
|
{ id, title: title.trim(), date: nowIsoDateTime(), status, tags: parseTags(opts.tags) },
|
|
864
890
|
config
|
|
865
891
|
);
|
|
866
|
-
await
|
|
892
|
+
await writeFile3(destPath, content, "utf8");
|
|
867
893
|
if (opts.json) {
|
|
868
894
|
printJson({
|
|
869
895
|
id,
|
|
@@ -971,8 +997,8 @@ function parseTags(value) {
|
|
|
971
997
|
}
|
|
972
998
|
|
|
973
999
|
// src/commands/renumber.ts
|
|
974
|
-
import { readFile as
|
|
975
|
-
import { basename, dirname, join as
|
|
1000
|
+
import { readFile as readFile3, rename, writeFile as writeFile4 } from "fs/promises";
|
|
1001
|
+
import { basename, dirname, join as join6 } from "path";
|
|
976
1002
|
import { internalLinkKeys as internalLinkKeys2, loadProject as loadProject3 } from "@acta-dev/core";
|
|
977
1003
|
import { defineCommand as defineCommand6 } from "citty";
|
|
978
1004
|
import kleur4 from "kleur";
|
|
@@ -996,7 +1022,7 @@ function buildRenumberPlan(fromId, toId, project) {
|
|
|
996
1022
|
const oldFilename = basename(target.file.path);
|
|
997
1023
|
const oldSlug = oldFilename.replace(`${target.id}-`, "").replace(/\.md$/, "");
|
|
998
1024
|
const newFilename = `${toId}-${oldSlug}.md`;
|
|
999
|
-
const newPath =
|
|
1025
|
+
const newPath = join6(dirname(target.file.path), newFilename);
|
|
1000
1026
|
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 }));
|
|
1001
1027
|
return {
|
|
1002
1028
|
target,
|
|
@@ -1007,7 +1033,7 @@ function buildRenumberPlan(fromId, toId, project) {
|
|
|
1007
1033
|
};
|
|
1008
1034
|
}
|
|
1009
1035
|
async function rewriteDocument(filePath, oldId, newId, isTarget) {
|
|
1010
|
-
const raw = await
|
|
1036
|
+
const raw = await readFile3(filePath, "utf8");
|
|
1011
1037
|
const match = raw.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);
|
|
1012
1038
|
if (!match) {
|
|
1013
1039
|
throw new Error(`Cannot parse frontmatter in ${filePath}`);
|
|
@@ -1091,11 +1117,11 @@ var renumberCommand = defineCommand6({
|
|
|
1091
1117
|
printLine();
|
|
1092
1118
|
for (const { doc, path } of plan.affectedDocs) {
|
|
1093
1119
|
const rewritten = await rewriteDocument(path, fromId, toId, false);
|
|
1094
|
-
await
|
|
1120
|
+
await writeFile4(path, rewritten, "utf8");
|
|
1095
1121
|
printSuccess(`Updated links in ${doc.id}`);
|
|
1096
1122
|
}
|
|
1097
1123
|
const rewrittenTarget = await rewriteDocument(plan.oldPath, fromId, toId, true);
|
|
1098
|
-
await
|
|
1124
|
+
await writeFile4(plan.oldPath, rewrittenTarget, "utf8");
|
|
1099
1125
|
await rename(plan.oldPath, plan.newPath);
|
|
1100
1126
|
printSuccess(`Renamed ${basename(plan.oldPath)} \u2192 ${plan.newFilename}`);
|
|
1101
1127
|
printLine();
|
|
@@ -1217,8 +1243,11 @@ function formatShowDate(value) {
|
|
|
1217
1243
|
|
|
1218
1244
|
// src/commands/site.ts
|
|
1219
1245
|
import { spawn } from "child_process";
|
|
1246
|
+
import { createReadStream } from "fs";
|
|
1247
|
+
import { stat } from "fs/promises";
|
|
1248
|
+
import { createServer } from "http";
|
|
1220
1249
|
import { createRequire } from "module";
|
|
1221
|
-
import { dirname as dirname2, join as
|
|
1250
|
+
import { dirname as dirname2, extname, join as join7, relative as relative2, resolve as resolve3, sep } from "path";
|
|
1222
1251
|
import { buildArtifacts as buildArtifacts2 } from "@acta-dev/core";
|
|
1223
1252
|
import { defineCommand as defineCommand8 } from "citty";
|
|
1224
1253
|
import kleur6 from "kleur";
|
|
@@ -1229,6 +1258,16 @@ function resolveSiteOptions(config, args, cwd = process.cwd()) {
|
|
|
1229
1258
|
site: args.site ?? config.site.url
|
|
1230
1259
|
};
|
|
1231
1260
|
}
|
|
1261
|
+
function resolveSiteServeOptions(args) {
|
|
1262
|
+
const port = args.port === void 0 ? 4321 : Number(args.port);
|
|
1263
|
+
if (!Number.isInteger(port) || port < 0 || port > 65535) {
|
|
1264
|
+
throw new Error("Expected --port to be an integer between 0 and 65535.");
|
|
1265
|
+
}
|
|
1266
|
+
return {
|
|
1267
|
+
host: args.host ?? "127.0.0.1",
|
|
1268
|
+
port
|
|
1269
|
+
};
|
|
1270
|
+
}
|
|
1232
1271
|
function buildSiteEnv(config, options) {
|
|
1233
1272
|
const env = {
|
|
1234
1273
|
...process.env,
|
|
@@ -1264,6 +1303,19 @@ var siteCommand = defineCommand8({
|
|
|
1264
1303
|
description: "Reuse existing artifacts instead of running `acta build` first",
|
|
1265
1304
|
default: false
|
|
1266
1305
|
},
|
|
1306
|
+
serve: {
|
|
1307
|
+
type: "boolean",
|
|
1308
|
+
description: "Serve the generated site locally after building it",
|
|
1309
|
+
default: false
|
|
1310
|
+
},
|
|
1311
|
+
host: {
|
|
1312
|
+
type: "string",
|
|
1313
|
+
description: "Host for --serve (default: 127.0.0.1)"
|
|
1314
|
+
},
|
|
1315
|
+
port: {
|
|
1316
|
+
type: "string",
|
|
1317
|
+
description: "Port for --serve (default: 4321)"
|
|
1318
|
+
},
|
|
1267
1319
|
config: {
|
|
1268
1320
|
type: "string",
|
|
1269
1321
|
alias: "c",
|
|
@@ -1278,6 +1330,18 @@ var siteCommand = defineCommand8({
|
|
|
1278
1330
|
async run({ args }) {
|
|
1279
1331
|
const { config } = await resolveContext({ config: args.config });
|
|
1280
1332
|
const json = Boolean(args.json);
|
|
1333
|
+
const serve = Boolean(args.serve);
|
|
1334
|
+
if (json && serve) {
|
|
1335
|
+
return exitUsage("`acta site --serve` cannot be combined with --json.");
|
|
1336
|
+
}
|
|
1337
|
+
let serveOptions;
|
|
1338
|
+
if (serve) {
|
|
1339
|
+
try {
|
|
1340
|
+
serveOptions = resolveSiteServeOptions(args);
|
|
1341
|
+
} catch (error) {
|
|
1342
|
+
return exitUsage(error instanceof Error ? error.message : String(error));
|
|
1343
|
+
}
|
|
1344
|
+
}
|
|
1281
1345
|
let documentCount = 0;
|
|
1282
1346
|
if (!args["skip-build"]) {
|
|
1283
1347
|
if (!json) printLine("Building artifacts...");
|
|
@@ -1320,6 +1384,23 @@ var siteCommand = defineCommand8({
|
|
|
1320
1384
|
printSuccess("Site built");
|
|
1321
1385
|
printLine(` ${kleur6.bold("Output:")} ${options.outDir}`);
|
|
1322
1386
|
if (options.base) printLine(` ${kleur6.bold("Base:")} ${options.base}`);
|
|
1387
|
+
if (serveOptions) {
|
|
1388
|
+
try {
|
|
1389
|
+
const preview = await serveStaticSite({
|
|
1390
|
+
root: options.outDir,
|
|
1391
|
+
base: options.base,
|
|
1392
|
+
...serveOptions
|
|
1393
|
+
});
|
|
1394
|
+
printLine(` ${kleur6.bold("Serving:")} ${preview.url}`);
|
|
1395
|
+
printLine();
|
|
1396
|
+
printLine(kleur6.dim("Press Ctrl+C to stop the preview server."));
|
|
1397
|
+
await waitForShutdown(preview);
|
|
1398
|
+
return;
|
|
1399
|
+
} catch (error) {
|
|
1400
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1401
|
+
return exitFailure(message);
|
|
1402
|
+
}
|
|
1403
|
+
}
|
|
1323
1404
|
printLine();
|
|
1324
1405
|
printLine(kleur6.dim("Deploy the contents of the output directory to any static host."));
|
|
1325
1406
|
}
|
|
@@ -1333,13 +1414,13 @@ function resolveWebPackageDir() {
|
|
|
1333
1414
|
}
|
|
1334
1415
|
}
|
|
1335
1416
|
function resolveAstroBin(webDir) {
|
|
1336
|
-
const require2 = createRequire(
|
|
1417
|
+
const require2 = createRequire(join7(webDir, "package.json"));
|
|
1337
1418
|
try {
|
|
1338
1419
|
const astroPkgJsonPath = require2.resolve("astro/package.json");
|
|
1339
1420
|
const astroPkg = require2("astro/package.json");
|
|
1340
1421
|
const binRel = typeof astroPkg.bin === "string" ? astroPkg.bin : astroPkg.bin?.astro;
|
|
1341
1422
|
if (!binRel) return void 0;
|
|
1342
|
-
return
|
|
1423
|
+
return join7(dirname2(astroPkgJsonPath), binRel);
|
|
1343
1424
|
} catch {
|
|
1344
1425
|
return void 0;
|
|
1345
1426
|
}
|
|
@@ -1356,14 +1437,210 @@ function runAstroBuild(astroBin, webDir, env, json) {
|
|
|
1356
1437
|
child.on("error", () => resolvePromise(1));
|
|
1357
1438
|
});
|
|
1358
1439
|
}
|
|
1440
|
+
var mimeTypes = {
|
|
1441
|
+
".css": "text/css; charset=utf-8",
|
|
1442
|
+
".html": "text/html; charset=utf-8",
|
|
1443
|
+
".ico": "image/x-icon",
|
|
1444
|
+
".jpeg": "image/jpeg",
|
|
1445
|
+
".jpg": "image/jpeg",
|
|
1446
|
+
".js": "text/javascript; charset=utf-8",
|
|
1447
|
+
".json": "application/json; charset=utf-8",
|
|
1448
|
+
".mjs": "text/javascript; charset=utf-8",
|
|
1449
|
+
".png": "image/png",
|
|
1450
|
+
".svg": "image/svg+xml",
|
|
1451
|
+
".txt": "text/plain; charset=utf-8",
|
|
1452
|
+
".wasm": "application/wasm",
|
|
1453
|
+
".webp": "image/webp"
|
|
1454
|
+
};
|
|
1455
|
+
function previewUrl(host, port, base) {
|
|
1456
|
+
const basePath = normalizeBasePath(base);
|
|
1457
|
+
const path = basePath === "/" ? "/" : `${basePath}/`;
|
|
1458
|
+
return `http://${host}:${port}${path}`;
|
|
1459
|
+
}
|
|
1460
|
+
async function serveStaticSite(options) {
|
|
1461
|
+
const root = resolve3(options.root);
|
|
1462
|
+
const basePath = normalizeBasePath(options.base);
|
|
1463
|
+
const server = createServer(async (request, response) => {
|
|
1464
|
+
const result = await resolveStaticSiteResponse({
|
|
1465
|
+
root,
|
|
1466
|
+
base: basePath,
|
|
1467
|
+
method: request.method ?? "GET",
|
|
1468
|
+
url: request.url ?? "/"
|
|
1469
|
+
});
|
|
1470
|
+
if (!result.path) {
|
|
1471
|
+
if (request.method === "HEAD") {
|
|
1472
|
+
response.statusCode = result.status;
|
|
1473
|
+
response.setHeader("Content-Type", result.contentType);
|
|
1474
|
+
response.end();
|
|
1475
|
+
return;
|
|
1476
|
+
}
|
|
1477
|
+
sendText(response, result.status, result.text ?? "Not Found");
|
|
1478
|
+
return;
|
|
1479
|
+
}
|
|
1480
|
+
response.statusCode = result.status;
|
|
1481
|
+
response.setHeader("Content-Type", result.contentType);
|
|
1482
|
+
if (result.contentLength !== void 0) {
|
|
1483
|
+
response.setHeader("Content-Length", String(result.contentLength));
|
|
1484
|
+
}
|
|
1485
|
+
if (request.method === "HEAD") {
|
|
1486
|
+
response.end();
|
|
1487
|
+
return;
|
|
1488
|
+
}
|
|
1489
|
+
createReadStream(result.path).pipe(response);
|
|
1490
|
+
});
|
|
1491
|
+
await new Promise((resolvePromise, reject) => {
|
|
1492
|
+
server.once("error", reject);
|
|
1493
|
+
server.listen(options.port, options.host, () => {
|
|
1494
|
+
server.off("error", reject);
|
|
1495
|
+
resolvePromise();
|
|
1496
|
+
});
|
|
1497
|
+
}).catch((error) => {
|
|
1498
|
+
if (isAddressInUse(error)) {
|
|
1499
|
+
throw new Error(
|
|
1500
|
+
`Port ${options.port} is already in use on ${options.host}. Try a different --port.`
|
|
1501
|
+
);
|
|
1502
|
+
}
|
|
1503
|
+
throw error;
|
|
1504
|
+
});
|
|
1505
|
+
const address = server.address();
|
|
1506
|
+
const port = typeof address === "object" && address ? address.port : options.port;
|
|
1507
|
+
return {
|
|
1508
|
+
server,
|
|
1509
|
+
url: previewUrl(options.host, port, options.base),
|
|
1510
|
+
close: () => new Promise((resolvePromise, reject) => {
|
|
1511
|
+
server.close((error) => error ? reject(error) : resolvePromise());
|
|
1512
|
+
})
|
|
1513
|
+
};
|
|
1514
|
+
}
|
|
1515
|
+
async function resolveStaticSiteResponse(options) {
|
|
1516
|
+
if (options.method !== "GET" && options.method !== "HEAD") {
|
|
1517
|
+
return {
|
|
1518
|
+
status: 405,
|
|
1519
|
+
contentType: "text/plain; charset=utf-8",
|
|
1520
|
+
text: "Method Not Allowed"
|
|
1521
|
+
};
|
|
1522
|
+
}
|
|
1523
|
+
const root = resolve3(options.root);
|
|
1524
|
+
const basePath = normalizeBasePath(options.base);
|
|
1525
|
+
const filePath = await resolveRequestPath(root, basePath, options.url);
|
|
1526
|
+
if (!filePath) {
|
|
1527
|
+
return { status: 404, contentType: "text/plain; charset=utf-8", text: "Not Found" };
|
|
1528
|
+
}
|
|
1529
|
+
try {
|
|
1530
|
+
const fileStat = await stat(filePath);
|
|
1531
|
+
const finalPath = fileStat.isDirectory() ? join7(filePath, "index.html") : filePath;
|
|
1532
|
+
const finalStat = fileStat.isDirectory() ? await stat(finalPath) : fileStat;
|
|
1533
|
+
if (!finalStat.isFile()) {
|
|
1534
|
+
return { status: 404, contentType: "text/plain; charset=utf-8", text: "Not Found" };
|
|
1535
|
+
}
|
|
1536
|
+
return {
|
|
1537
|
+
status: 200,
|
|
1538
|
+
contentType: mimeTypes[extname(finalPath)] ?? "application/octet-stream",
|
|
1539
|
+
contentLength: finalStat.size,
|
|
1540
|
+
path: finalPath
|
|
1541
|
+
};
|
|
1542
|
+
} catch {
|
|
1543
|
+
return { status: 404, contentType: "text/plain; charset=utf-8", text: "Not Found" };
|
|
1544
|
+
}
|
|
1545
|
+
}
|
|
1546
|
+
async function resolveRequestPath(root, basePath, requestUrl) {
|
|
1547
|
+
let pathname;
|
|
1548
|
+
try {
|
|
1549
|
+
pathname = decodeURIComponent(new URL(requestUrl, "http://localhost").pathname);
|
|
1550
|
+
} catch {
|
|
1551
|
+
return void 0;
|
|
1552
|
+
}
|
|
1553
|
+
if (basePath !== "/") {
|
|
1554
|
+
if (pathname === basePath) {
|
|
1555
|
+
pathname = "/";
|
|
1556
|
+
} else if (pathname.startsWith(`${basePath}/`)) {
|
|
1557
|
+
pathname = pathname.slice(basePath.length);
|
|
1558
|
+
} else {
|
|
1559
|
+
return void 0;
|
|
1560
|
+
}
|
|
1561
|
+
}
|
|
1562
|
+
const normalized = pathname.startsWith("/") ? pathname : `/${pathname}`;
|
|
1563
|
+
const candidate = resolve3(root, `.${normalized}`);
|
|
1564
|
+
const rel = relative2(root, candidate);
|
|
1565
|
+
if (rel === ".." || rel.startsWith(`..${sep}`) || rel === "" || rel.startsWith("/") || rel === ".") {
|
|
1566
|
+
return rel === "." || rel === "" ? root : void 0;
|
|
1567
|
+
}
|
|
1568
|
+
return candidate;
|
|
1569
|
+
}
|
|
1570
|
+
function sendText(response, status, text) {
|
|
1571
|
+
response.statusCode = status;
|
|
1572
|
+
response.setHeader("Content-Type", "text/plain; charset=utf-8");
|
|
1573
|
+
response.end(text);
|
|
1574
|
+
}
|
|
1575
|
+
function normalizeBasePath(base) {
|
|
1576
|
+
if (!base || base === "/") return "/";
|
|
1577
|
+
const withLeading = base.startsWith("/") ? base : `/${base}`;
|
|
1578
|
+
return withLeading.replace(/\/+$/, "") || "/";
|
|
1579
|
+
}
|
|
1580
|
+
function waitForShutdown(preview) {
|
|
1581
|
+
return new Promise((resolvePromise) => {
|
|
1582
|
+
const shutdown = async () => {
|
|
1583
|
+
process.off("SIGINT", shutdown);
|
|
1584
|
+
process.off("SIGTERM", shutdown);
|
|
1585
|
+
await preview.close();
|
|
1586
|
+
resolvePromise();
|
|
1587
|
+
};
|
|
1588
|
+
process.once("SIGINT", shutdown);
|
|
1589
|
+
process.once("SIGTERM", shutdown);
|
|
1590
|
+
});
|
|
1591
|
+
}
|
|
1592
|
+
function isAddressInUse(error) {
|
|
1593
|
+
return typeof error === "object" && error !== null && "code" in error && error.code === "EADDRINUSE";
|
|
1594
|
+
}
|
|
1595
|
+
|
|
1596
|
+
// src/commands/skill.ts
|
|
1597
|
+
import { resolve as resolve4 } from "path";
|
|
1598
|
+
import { defineCommand as defineCommand9 } from "citty";
|
|
1599
|
+
function parseSkillFormat(value) {
|
|
1600
|
+
const format = value ?? "both";
|
|
1601
|
+
if (!skillFormats.includes(format)) {
|
|
1602
|
+
exitUsage(`Unknown skill format "${format}". Use: codex, claude, both`);
|
|
1603
|
+
}
|
|
1604
|
+
return format;
|
|
1605
|
+
}
|
|
1606
|
+
var skillCommand = defineCommand9({
|
|
1607
|
+
meta: {
|
|
1608
|
+
name: "skill",
|
|
1609
|
+
description: "Install or refresh the bundled acta-document agent skill"
|
|
1610
|
+
},
|
|
1611
|
+
args: {
|
|
1612
|
+
init: {
|
|
1613
|
+
type: "boolean",
|
|
1614
|
+
description: "Install or overwrite the bundled acta-document skill",
|
|
1615
|
+
default: false
|
|
1616
|
+
},
|
|
1617
|
+
format: {
|
|
1618
|
+
type: "string",
|
|
1619
|
+
description: "Skill target format: codex | claude | both (default: both)"
|
|
1620
|
+
}
|
|
1621
|
+
},
|
|
1622
|
+
async run({ args }) {
|
|
1623
|
+
if (!args.init) {
|
|
1624
|
+
exitUsage("Usage: acta skill --init [--format codex|claude|both]");
|
|
1625
|
+
}
|
|
1626
|
+
const cwd = resolve4(process.cwd());
|
|
1627
|
+
const result = await installAgentSkill(cwd, parseSkillFormat(args.format));
|
|
1628
|
+
for (const skillPath of result.skillPaths) {
|
|
1629
|
+
printSuccess(`Installed ${skillPath}`);
|
|
1630
|
+
}
|
|
1631
|
+
if (result.agentsPath) {
|
|
1632
|
+
printSuccess(`Updated ${result.agentsPath} with Acta agent guidance`);
|
|
1633
|
+
}
|
|
1634
|
+
}
|
|
1635
|
+
});
|
|
1359
1636
|
|
|
1360
1637
|
// src/commands/validate.ts
|
|
1361
|
-
import { mkdir as
|
|
1362
|
-
import { join as
|
|
1638
|
+
import { mkdir as mkdir3, writeFile as writeFile5 } from "fs/promises";
|
|
1639
|
+
import { join as join8 } from "path";
|
|
1363
1640
|
import { validateLoadedProject } from "@acta-dev/core";
|
|
1364
|
-
import { defineCommand as
|
|
1641
|
+
import { defineCommand as defineCommand10 } from "citty";
|
|
1365
1642
|
import kleur7 from "kleur";
|
|
1366
|
-
var validateCommand =
|
|
1643
|
+
var validateCommand = defineCommand10({
|
|
1367
1644
|
meta: {
|
|
1368
1645
|
name: "validate",
|
|
1369
1646
|
description: "Validate frontmatter, IDs, links, sections and repository rules"
|
|
@@ -1394,9 +1671,9 @@ var validateCommand = defineCommand9({
|
|
|
1394
1671
|
return;
|
|
1395
1672
|
}
|
|
1396
1673
|
if (args.ci) {
|
|
1397
|
-
await
|
|
1398
|
-
const outPath =
|
|
1399
|
-
await
|
|
1674
|
+
await mkdir3(config.resolvedBuild.outDir, { recursive: true });
|
|
1675
|
+
const outPath = join8(config.resolvedBuild.outDir, "validation.json");
|
|
1676
|
+
await writeFile5(outPath, `${JSON.stringify(result, null, 2)}
|
|
1400
1677
|
`, "utf8");
|
|
1401
1678
|
if (result.errors.length > 0) {
|
|
1402
1679
|
for (const issue of result.errors) {
|
|
@@ -1433,7 +1710,7 @@ var validateCommand = defineCommand9({
|
|
|
1433
1710
|
});
|
|
1434
1711
|
|
|
1435
1712
|
// src/index.ts
|
|
1436
|
-
var main =
|
|
1713
|
+
var main = defineCommand11({
|
|
1437
1714
|
meta: {
|
|
1438
1715
|
name: "acta",
|
|
1439
1716
|
version: "0.0.0",
|
|
@@ -1448,6 +1725,7 @@ var main = defineCommand10({
|
|
|
1448
1725
|
graph: graphCommand,
|
|
1449
1726
|
build: buildCommand,
|
|
1450
1727
|
site: siteCommand,
|
|
1728
|
+
skill: skillCommand,
|
|
1451
1729
|
renumber: renumberCommand
|
|
1452
1730
|
}
|
|
1453
1731
|
});
|