@intentius/chant 0.78.0 → 0.80.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/audit/core.d.ts.map +1 -1
- package/dist/audit/discover.d.ts +38 -0
- package/dist/audit/discover.d.ts.map +1 -1
- package/dist/audit/terraform-state.d.ts +14 -0
- package/dist/audit/terraform-state.d.ts.map +1 -1
- package/dist/cli/command-group.d.ts +6 -0
- package/dist/cli/command-group.d.ts.map +1 -1
- package/dist/cli/commands/audit.d.ts +27 -2
- package/dist/cli/commands/audit.d.ts.map +1 -1
- package/dist/cli/commands/check-lexicon-examples.d.ts.map +1 -1
- package/dist/cli/commands/doctor.d.ts.map +1 -1
- package/dist/cli/commands/import-agents.d.ts.map +1 -1
- package/dist/cli/commands/init.d.ts +6 -0
- package/dist/cli/commands/init.d.ts.map +1 -1
- package/dist/cli/commands/lint.d.ts.map +1 -1
- package/dist/cli/commands/onboard.d.ts.map +1 -1
- package/dist/cli/commands/update.d.ts.map +1 -1
- package/dist/cli/conflict-check.d.ts +6 -0
- package/dist/cli/conflict-check.d.ts.map +1 -1
- package/dist/cli/handlers/dev.d.ts.map +1 -1
- package/dist/cli/handlers/lifecycle.d.ts.map +1 -1
- package/dist/cli/handlers/misc.d.ts.map +1 -1
- package/dist/cli/handlers/operator.d.ts +17 -0
- package/dist/cli/handlers/operator.d.ts.map +1 -1
- package/dist/cli/handlers/promote.d.ts +17 -0
- package/dist/cli/handlers/promote.d.ts.map +1 -0
- package/dist/cli/handlers/run-generate.d.ts +39 -0
- package/dist/cli/handlers/run-generate.d.ts.map +1 -0
- package/dist/cli/handlers/run.d.ts.map +1 -1
- package/dist/cli/main.d.ts.map +1 -1
- package/dist/cli/mcp/op-tools.d.ts.map +1 -1
- package/dist/cli/plugins.d.ts +9 -0
- package/dist/cli/plugins.d.ts.map +1 -1
- package/dist/cli/registry.d.ts +18 -1
- package/dist/cli/registry.d.ts.map +1 -1
- package/dist/components/capability-plugin-loader.d.ts.map +1 -1
- package/dist/components/cli-support.d.ts.map +1 -1
- package/dist/components/discover.d.ts.map +1 -1
- package/dist/components/driver.d.ts.map +1 -1
- package/dist/components/fan-out-support.d.ts.map +1 -1
- package/dist/components/pilots/alb-ecs.pilot.d.ts +7 -1
- package/dist/components/pilots/alb-ecs.pilot.d.ts.map +1 -1
- package/dist/components/presets/ecs-fargate.d.ts +10 -4
- package/dist/components/presets/ecs-fargate.d.ts.map +1 -1
- package/dist/components/promote.d.ts +211 -0
- package/dist/components/promote.d.ts.map +1 -0
- package/dist/components/unbound-gate-approval.d.ts +24 -0
- package/dist/components/unbound-gate-approval.d.ts.map +1 -0
- package/dist/config.d.ts +82 -5
- package/dist/config.d.ts.map +1 -1
- package/dist/discovery/convergence.d.ts +97 -0
- package/dist/discovery/convergence.d.ts.map +1 -0
- package/dist/discovery/files.d.ts +41 -2
- package/dist/discovery/files.d.ts.map +1 -1
- package/dist/discovery/fold-import.d.ts +15 -0
- package/dist/discovery/fold-import.d.ts.map +1 -1
- package/dist/graph-ir.d.ts +13 -0
- package/dist/graph-ir.d.ts.map +1 -1
- package/dist/lexicon-module.d.ts +65 -0
- package/dist/lexicon-module.d.ts.map +1 -0
- package/dist/lexicon.d.ts +24 -0
- package/dist/lexicon.d.ts.map +1 -1
- package/dist/lifecycle/build-ledger-store.d.ts.map +1 -1
- package/dist/lifecycle/gate-ledger.d.ts +22 -0
- package/dist/lifecycle/gate-ledger.d.ts.map +1 -1
- package/dist/lifecycle/legacy-digest.d.ts +26 -0
- package/dist/lifecycle/legacy-digest.d.ts.map +1 -0
- package/dist/lifecycle/release-ledger.d.ts +25 -0
- package/dist/lifecycle/release-ledger.d.ts.map +1 -1
- package/dist/lint/rules/comp/comp003-mutating-no-rollback.d.ts +2 -3
- package/dist/lint/rules/comp/comp003-mutating-no-rollback.d.ts.map +1 -1
- package/dist/lint/rules/op/index.d.ts +2 -1
- package/dist/lint/rules/op/index.d.ts.map +1 -1
- package/dist/lint/rules/op/ops015-gate-approval.d.ts +14 -0
- package/dist/lint/rules/op/ops015-gate-approval.d.ts.map +1 -0
- package/dist/op/activity-contract-registry.d.ts.map +1 -1
- package/dist/op/activity-registry.d.ts.map +1 -1
- package/dist/op/builders.d.ts +4 -0
- package/dist/op/builders.d.ts.map +1 -1
- package/dist/op/discover.d.ts.map +1 -1
- package/dist/op/gate-approval.d.ts +142 -0
- package/dist/op/gate-approval.d.ts.map +1 -0
- package/dist/op/gate.d.ts +54 -0
- package/dist/op/gate.d.ts.map +1 -1
- package/dist/op/generate-pipeline.d.ts.map +1 -1
- package/dist/op/index.d.ts +4 -1
- package/dist/op/index.d.ts.map +1 -1
- package/dist/op/local-executor.d.ts +4 -0
- package/dist/op/local-executor.d.ts.map +1 -1
- package/dist/op/op-ir.d.ts +2 -0
- package/dist/op/op-ir.d.ts.map +1 -1
- package/dist/op/runtimes/local.d.ts.map +1 -1
- package/dist/op/types.d.ts +7 -0
- package/dist/op/types.d.ts.map +1 -1
- package/dist/workspace/record-source.d.ts +22 -0
- package/dist/workspace/record-source.d.ts.map +1 -0
- package/dist/workspace/records-cli.d.ts +57 -0
- package/dist/workspace/records-cli.d.ts.map +1 -0
- package/dist/workspace/records.d.ts +118 -0
- package/dist/workspace/records.d.ts.map +1 -0
- package/package.json +3 -3
- package/src/audit/core.ts +13 -2
- package/src/audit/discover.ts +90 -12
- package/src/audit/terraform-state.test.ts +86 -1
- package/src/audit/terraform-state.ts +26 -0
- package/src/cli/command-group.ts +7 -0
- package/src/cli/commands/audit-walk-warnings.test.ts +156 -0
- package/src/cli/commands/audit.ts +93 -19
- package/src/cli/commands/check-lexicon-examples.ts +2 -1
- package/src/cli/commands/doctor.ts +3 -0
- package/src/cli/commands/import-agents.ts +11 -4
- package/src/cli/commands/init.ts +57 -25
- package/src/cli/commands/lint.test.ts +58 -1
- package/src/cli/commands/lint.ts +48 -18
- package/src/cli/commands/onboard.ts +15 -0
- package/src/cli/commands/update.ts +5 -1
- package/src/cli/conflict-check.test.ts +18 -1
- package/src/cli/conflict-check.ts +13 -0
- package/src/cli/discovery-skip.test.ts +168 -0
- package/src/cli/handlers/build.test.ts +11 -0
- package/src/cli/handlers/build.ts +1 -1
- package/src/cli/handlers/dev.ts +3 -0
- package/src/cli/handlers/graph.test.ts +20 -0
- package/src/cli/handlers/graph.ts +4 -2
- package/src/cli/handlers/lifecycle.ts +2 -1
- package/src/cli/handlers/misc.ts +16 -0
- package/src/cli/handlers/operator.test.ts +85 -0
- package/src/cli/handlers/operator.ts +118 -2
- package/src/cli/handlers/promote.test.ts +261 -0
- package/src/cli/handlers/promote.ts +325 -0
- package/src/cli/handlers/run-generate.test.ts +160 -0
- package/src/cli/handlers/run-generate.ts +133 -0
- package/src/cli/handlers/run.ts +13 -1
- package/src/cli/main.test.ts +33 -1
- package/src/cli/main.ts +78 -16
- package/src/cli/mcp/op-tools.ts +2 -1
- package/src/cli/path-lexicon-messages.test.ts +152 -0
- package/src/cli/plugins.ts +44 -6
- package/src/cli/registry.ts +18 -1
- package/src/components/__fixtures__/alb-ecs-service.json +0 -8
- package/src/components/capability-plugin-loader.ts +3 -1
- package/src/components/cli-support.ts +3 -2
- package/src/components/discover.ts +26 -2
- package/src/components/driver.test.ts +12 -11
- package/src/components/driver.ts +2 -0
- package/src/components/fan-out-support.ts +2 -1
- package/src/components/pilots/README.md +1 -1
- package/src/components/pilots/alb-ecs.pilot.ts +7 -7
- package/src/components/presets/ecs-fargate.ts +10 -5
- package/src/components/promote.test.ts +371 -0
- package/src/components/promote.ts +537 -0
- package/src/components/unbound-gate-approval.test.ts +82 -0
- package/src/components/unbound-gate-approval.ts +51 -0
- package/src/config.test.ts +40 -0
- package/src/config.ts +128 -11
- package/src/discovery/convergence.test.ts +231 -0
- package/src/discovery/convergence.ts +399 -0
- package/src/discovery/files.test.ts +127 -1
- package/src/discovery/files.ts +97 -9
- package/src/discovery/fold-import.ts +83 -9
- package/src/discovery/sandbox/path-lexicon-parity.test.ts +162 -0
- package/src/graph-ir.ts +14 -0
- package/src/lexicon-module.test.ts +203 -0
- package/src/lexicon-module.ts +117 -0
- package/src/lexicon.ts +26 -0
- package/src/lifecycle/build-ledger-store.ts +11 -1
- package/src/lifecycle/gate-ledger.ts +22 -0
- package/src/lifecycle/legacy-digest.test.ts +114 -0
- package/src/lifecycle/legacy-digest.ts +62 -0
- package/src/lifecycle/release-ledger.ts +34 -0
- package/src/lint/rules/comp/comp003-mutating-no-rollback.ts +2 -3
- package/src/lint/rules/op/index.ts +4 -2
- package/src/lint/rules/op/ops015-gate-approval.test.ts +67 -0
- package/src/lint/rules/op/ops015-gate-approval.ts +57 -0
- package/src/op/activity-contract-registry.ts +18 -4
- package/src/op/activity-registry.ts +26 -0
- package/src/op/builders.ts +10 -1
- package/src/op/discover-convergence.test.ts +70 -0
- package/src/op/discover.ts +4 -0
- package/src/op/gate-approval.test.ts +258 -0
- package/src/op/gate-approval.ts +260 -0
- package/src/op/gate.ts +148 -10
- package/src/op/generate-pipeline.ts +2 -1
- package/src/op/index.ts +9 -1
- package/src/op/local-executor.test.ts +66 -0
- package/src/op/local-executor.ts +44 -1
- package/src/op/op-ir.ts +4 -0
- package/src/op/runtimes/local.ts +2 -1
- package/src/op/types.ts +7 -0
- package/src/workspace/record-source.ts +117 -0
- package/src/workspace/records-cli.ts +126 -0
- package/src/workspace/records-contract.test.ts +88 -0
- package/src/workspace/records.schema.json +120 -0
- package/src/workspace/records.test.ts +243 -0
- package/src/workspace/records.ts +383 -0
package/src/cli/main.test.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { describe, test, expect } from "vitest";
|
|
2
2
|
import { EventEmitter } from "node:events";
|
|
3
|
-
import {
|
|
3
|
+
import { readFileSync } from "node:fs";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
import { commandRegistry, parseArgs, waitForStreamDrain } from "./main";
|
|
4
6
|
import { resolveCommand, type CommandDef, type ParsedArgs } from "./registry";
|
|
5
7
|
|
|
6
8
|
describe("parseArgs", () => {
|
|
@@ -298,6 +300,12 @@ describe("parseArgs", () => {
|
|
|
298
300
|
|
|
299
301
|
// ── --no-release-record (#597) ───────────────────────────────────────────
|
|
300
302
|
|
|
303
|
+
test("--promote-to <env> sets promoteTo, and a missing value is refused (#2575)", () => {
|
|
304
|
+
expect(parseArgs(["build", "--components", "--generate", "github", "--promote-to", "prod"]).promoteTo).toBe("prod");
|
|
305
|
+
expect(() => parseArgs(["build", "--components", "--generate", "github", "--promote-to"])).toThrow(/--promote-to needs an environment/);
|
|
306
|
+
expect(() => parseArgs(["build", "--promote-to", "--env", "x"])).toThrow(/--promote-to needs an environment/);
|
|
307
|
+
});
|
|
308
|
+
|
|
301
309
|
test("parses --no-release-record for run --components", () => {
|
|
302
310
|
const result = parseArgs(["run", "--components", "search-service", "--env", "staging", "--no-release-record"]);
|
|
303
311
|
expect(result.components).toBe(true);
|
|
@@ -633,6 +641,30 @@ describe("parseArgs — run flags", () => {
|
|
|
633
641
|
});
|
|
634
642
|
});
|
|
635
643
|
|
|
644
|
+
describe("workspace records (#2546)", () => {
|
|
645
|
+
test("parses --kind, --current, --at and --json", () => {
|
|
646
|
+
const args = parseArgs(["workspace", "records", "--kind", "docs/k.kind.mjs", "--current", "--at", "HEAD~1", "--json"]);
|
|
647
|
+
expect(args).toMatchObject({ command: "workspace", path: "records", kind: "docs/k.kind.mjs", current: true, at: "HEAD~1", json: true });
|
|
648
|
+
expect(parseArgs(["workspace", "records", "--kind=docs/k.kind.mjs"]).kind).toBe("docs/k.kind.mjs");
|
|
649
|
+
expect(parseArgs(["workspace", "records"]).current).toBeUndefined();
|
|
650
|
+
expect(() => parseArgs(["workspace", "records", "--kind"])).toThrow(/--kind needs a kind file/);
|
|
651
|
+
expect(() => parseArgs(["workspace", "records", "--kind", "--json"])).toThrow(/--kind needs a kind file/);
|
|
652
|
+
});
|
|
653
|
+
|
|
654
|
+
test("resolves to the records command, and anything else under workspace to its fallback", () => {
|
|
655
|
+
expect(resolveCommand(parseArgs(["workspace", "records", "--kind", "k.mjs"]), commandRegistry)?.def.name).toBe("workspace records");
|
|
656
|
+
expect(resolveCommand(parseArgs(["workspace", "ls"]), commandRegistry)?.def.name).toBe("workspace");
|
|
657
|
+
});
|
|
658
|
+
|
|
659
|
+
// #2525 rule 5: the workspace code loads only when a workspace command runs.
|
|
660
|
+
// The level-0 goldens catch it at run time; this catches it in review.
|
|
661
|
+
test("main.ts never imports workspace code statically", () => {
|
|
662
|
+
const source = readFileSync(join(import.meta.dirname, "main.ts"), "utf-8");
|
|
663
|
+
expect(source).not.toMatch(/^import[^;]*from\s+["'][^"']*workspace/m);
|
|
664
|
+
expect(source).toMatch(/await import\("\.\.\/workspace\/records-cli"\)/);
|
|
665
|
+
});
|
|
666
|
+
});
|
|
667
|
+
|
|
636
668
|
describe("waitForStreamDrain", () => {
|
|
637
669
|
// Minimal writable stub — just the surface waitForStreamDrain reads.
|
|
638
670
|
function fakeStream(len: number): NodeJS.WriteStream & { writableLength: number } {
|
package/src/cli/main.ts
CHANGED
|
@@ -27,6 +27,7 @@ import { runCarveStatus } from "./handlers/carve-status";
|
|
|
27
27
|
import { runLifecycleSnapshot, runLifecycleShow, runLifecycleDiff, runLifecycleRollback, runLifecyclePlan, runLifecycleAffected, runLifecycleLog, runLifecycleTeardown, runLifecycleWhoami, runLifecycleUnknown } from "./handlers/lifecycle";
|
|
28
28
|
import { runComponentsStatus, runComponentsReleaseRecord, runComponentsExport, runComponentsUnknown } from "./handlers/components";
|
|
29
29
|
import { runComponentsFanOut } from "./handlers/fan-out";
|
|
30
|
+
import { runComponentsPromote, runComponentsRollback } from "./handlers/promote";
|
|
30
31
|
import { runScenarioCheck, runScenarioUnknown } from "./handlers/scenario";
|
|
31
32
|
import { runGraph } from "./handlers/graph";
|
|
32
33
|
import { runExplain } from "./handlers/explain";
|
|
@@ -51,6 +52,7 @@ import type { LexiconPlugin } from "../lexicon";
|
|
|
51
52
|
const BOOLEAN_FLAGS = new Set([
|
|
52
53
|
"--help",
|
|
53
54
|
"--agents",
|
|
55
|
+
"--agent",
|
|
54
56
|
"--all-projects",
|
|
55
57
|
"--force",
|
|
56
58
|
"--fix",
|
|
@@ -92,6 +94,7 @@ const BOOLEAN_FLAGS = new Set([
|
|
|
92
94
|
"--fail-on-drift",
|
|
93
95
|
"--durable-requests",
|
|
94
96
|
"--skip-mcp",
|
|
97
|
+
"--current",
|
|
95
98
|
]);
|
|
96
99
|
|
|
97
100
|
/**
|
|
@@ -204,8 +207,9 @@ export function parseArgs(args: string[]): ParsedArgs {
|
|
|
204
207
|
if (v !== "source" && v !== "live") throw new Error(`--overlay-anchor must be 'source' or 'live', got '${v}'`);
|
|
205
208
|
result.overlayAnchor = v;
|
|
206
209
|
} else if (arg === "--from") {
|
|
207
|
-
// Shared by `migrate --from <lexicon
|
|
208
|
-
//
|
|
210
|
+
// Shared by `migrate --from <lexicon>`, `import --from <env>` and
|
|
211
|
+
// `components promote --from <env>`; the commands never run together,
|
|
212
|
+
// so one field carries all three.
|
|
209
213
|
result.migrateFrom = args[++i];
|
|
210
214
|
} else if (arg === "--kustomize") {
|
|
211
215
|
// `chant import --kustomize <dir>` (#1548): render the overlay, import
|
|
@@ -264,6 +268,9 @@ export function parseArgs(args: string[]): ParsedArgs {
|
|
|
264
268
|
result.src = args[++i];
|
|
265
269
|
} else if (arg === "--env") {
|
|
266
270
|
result.env = args[++i];
|
|
271
|
+
} else if (arg === "--promote-to") {
|
|
272
|
+
result.promoteTo = args[++i];
|
|
273
|
+
if (!result.promoteTo || result.promoteTo.startsWith("-")) throw new Error("--promote-to needs an environment: --promote-to <env>");
|
|
267
274
|
} else if (arg === "--tier") {
|
|
268
275
|
result.tier = args[++i];
|
|
269
276
|
} else if (arg === "--agents") {
|
|
@@ -276,6 +283,9 @@ export function parseArgs(args: string[]): ParsedArgs {
|
|
|
276
283
|
result.runtime = args[++i];
|
|
277
284
|
} else if (arg === "--fail-on") {
|
|
278
285
|
result.failOn = args[++i];
|
|
286
|
+
} else if (arg === "--max-files") {
|
|
287
|
+
// Validated by the audit handler, like `--limit` below.
|
|
288
|
+
result.maxFiles = Number(args[++i]);
|
|
279
289
|
} else if (arg === "--theme") {
|
|
280
290
|
result.theme = args[++i];
|
|
281
291
|
} else if (arg === "--stacks") {
|
|
@@ -284,6 +294,8 @@ export function parseArgs(args: string[]): ParsedArgs {
|
|
|
284
294
|
result.components = true;
|
|
285
295
|
} else if (arg === "--generate") {
|
|
286
296
|
result.generate = args[++i];
|
|
297
|
+
} else if (arg === "--spec") {
|
|
298
|
+
result.opsSpec = args[++i];
|
|
287
299
|
} else if (arg === "--dump-outputs") {
|
|
288
300
|
result.dumpOutputs = args[++i];
|
|
289
301
|
} else if (arg === "--seed-outputs") {
|
|
@@ -341,6 +353,12 @@ export function parseArgs(args: string[]): ParsedArgs {
|
|
|
341
353
|
result.deep = true;
|
|
342
354
|
} else if (arg === "--at") {
|
|
343
355
|
result.at = args[++i];
|
|
356
|
+
} else if (arg === "--kind") {
|
|
357
|
+
// `chant workspace records --kind <kind file>` (#2546)
|
|
358
|
+
result.kind = args[++i];
|
|
359
|
+
if (!result.kind || result.kind.startsWith("-")) throw new Error("--kind needs a kind file: --kind <path>");
|
|
360
|
+
} else if (arg === "--current") {
|
|
361
|
+
result.current = true;
|
|
344
362
|
} else if (arg === "--ambient") {
|
|
345
363
|
result.ambient = true;
|
|
346
364
|
} else if (arg === "--check-live") {
|
|
@@ -419,6 +437,14 @@ export function parseArgs(args: string[]): ParsedArgs {
|
|
|
419
437
|
result.note = args[++i];
|
|
420
438
|
} else if (arg === "--expire") {
|
|
421
439
|
result.expire = true;
|
|
440
|
+
} else if (arg === "--role") {
|
|
441
|
+
// #2508 — a role the approver holds, for a gate whose quorum names
|
|
442
|
+
// roles. Repeatable, and a comma list works too.
|
|
443
|
+
const value = args[++i] ?? "";
|
|
444
|
+
result.roles = [...(result.roles ?? []), ...value.split(",").map((r) => r.trim()).filter(Boolean)];
|
|
445
|
+
} else if (arg === "--agent") {
|
|
446
|
+
// #2508 — record the approval as an agent's rather than a person's.
|
|
447
|
+
result.agent = true;
|
|
422
448
|
} else if (arg === "--allow-same-origin") {
|
|
423
449
|
// chant#2384 — record a resolution the same-origin rule would refuse,
|
|
424
450
|
// deliberately. Flagged on the record, not just accepted quietly.
|
|
@@ -482,10 +508,11 @@ Commands:
|
|
|
482
508
|
init Initialize a new chant project
|
|
483
509
|
init lexicon <name> Scaffold a new lexicon plugin project
|
|
484
510
|
build Build infrastructure from specification files
|
|
485
|
-
(--components --generate gitlab:
|
|
486
|
-
|
|
487
|
-
discovered component's
|
|
488
|
-
instead of a normal
|
|
511
|
+
(--components --generate github|gitlab|forgejo:
|
|
512
|
+
generate mode (#563), which synthesizes a thin CI
|
|
513
|
+
pipeline that triggers each discovered component's
|
|
514
|
+
own deploy in wave order, instead of a normal
|
|
515
|
+
lexicon build)
|
|
489
516
|
lint Check specifications for issues
|
|
490
517
|
list List discovered entities
|
|
491
518
|
describe Show the effective config for one component
|
|
@@ -500,7 +527,8 @@ Commands:
|
|
|
500
527
|
audit [path|url] Audit a repo's CI YAML for security issues
|
|
501
528
|
(--format stylish|json|sarif|markdown|html, -o <file>,
|
|
502
529
|
--tier merge-worthy|all, --fail-on merge-worthy|warning|error|none,
|
|
503
|
-
--template <file> / --theme <file> for the html report
|
|
530
|
+
--template <file> / --theme <file> for the html report,
|
|
531
|
+
--max-files <n> to walk more than 1000 files)
|
|
504
532
|
--agents audits this machine's agent configuration —
|
|
505
533
|
instruction files, MCP servers, skills, plugins,
|
|
506
534
|
permissions — instead of a repository
|
|
@@ -547,6 +575,11 @@ Ops:
|
|
|
547
575
|
run approve <op> <gate> Record a gate's resolution and wake the runtime
|
|
548
576
|
run cancel <name> Cancel the active run (requires --force)
|
|
549
577
|
run log <name> Show run history for an Op
|
|
578
|
+
run --generate <provider> Write one CI pipeline file per scheduled Op for
|
|
579
|
+
github, gitlab or forgejo (#2533), from every Op that
|
|
580
|
+
declares a schedule or from --spec <file.json>.
|
|
581
|
+
Files go to the forge's own directory unless
|
|
582
|
+
--output <dir>; --format json prints them instead
|
|
550
583
|
run --components <name|all> Run discovered Component(s) through the interpret
|
|
551
584
|
driver on the local executor (--env <env>; #585).
|
|
552
585
|
On success, auto-emits a release-ledger record per
|
|
@@ -606,6 +639,13 @@ Ops:
|
|
|
606
639
|
predicts the account. The level reaches the engine
|
|
607
640
|
verbatim; without the flag nothing is asked (#2377)
|
|
608
641
|
|
|
642
|
+
Workspace (first-test slice, #2546):
|
|
643
|
+
workspace records --kind <kind file> [--current] [--at <rev>] [--json]
|
|
644
|
+
Read the records a record kind locates, validated
|
|
645
|
+
against its schema, with reason codes for invalid
|
|
646
|
+
ones. --current leaves out superseded records; --at
|
|
647
|
+
reads a commit's git objects. Needs no workspace file
|
|
648
|
+
|
|
609
649
|
Lifecycle (alias: lc):
|
|
610
650
|
lifecycle snapshot <env> Query API, save metadata to orphan branch
|
|
611
651
|
--deep: also record each resource's property tree,
|
|
@@ -661,6 +701,15 @@ Component release ledger + status:
|
|
|
661
701
|
[--digest <manifestDigest>] -o <dir> [--json]);
|
|
662
702
|
copies every image/template/asset/sbom entry
|
|
663
703
|
byte-for-byte plus a self-describing manifest.json
|
|
704
|
+
components promote Deploy the digests one environment's release
|
|
705
|
+
ledger records to another, without a build
|
|
706
|
+
(--from <env> --to <env> [--component <name>
|
|
707
|
+
[--digest <sha256:...>]]; --dry-run prints the
|
|
708
|
+
plan; the target's gates still apply)
|
|
709
|
+
components rollback <env> Redeploy an earlier release of an environment
|
|
710
|
+
from its recorded digest (--component <name>
|
|
711
|
+
[--digest <sha256:...>]; defaults to the release
|
|
712
|
+
before the current one; --dry-run prints the plan)
|
|
664
713
|
|
|
665
714
|
Lexicon development:
|
|
666
715
|
dev generate Generate lexicon artifacts (+ validate + coverage)
|
|
@@ -735,9 +784,14 @@ Options:
|
|
|
735
784
|
--use-composites Rewrite to composite calls when patterns match (migrate)
|
|
736
785
|
--components Target discovered Component declarations instead of
|
|
737
786
|
lexicon resources (list, describe, graph, build, run)
|
|
738
|
-
--generate <lexicon> Generate mode
|
|
739
|
-
|
|
740
|
-
|
|
787
|
+
--generate <lexicon> Generate mode: synthesize CI YAML for <lexicon> (github,
|
|
788
|
+
gitlab or forgejo) instead of running. build
|
|
789
|
+
--components: one pipeline for the components; run:
|
|
790
|
+
one pipeline file per scheduled Op
|
|
791
|
+
--spec <file> (run --generate) JSON Op specs: an array, or
|
|
792
|
+
{ ops, options }
|
|
793
|
+
--promote-to <env> (build --components --generate) Add a job that
|
|
794
|
+
promotes the deployed releases to <env>
|
|
741
795
|
--no-release-record Skip auto-emitting a release-ledger record after a
|
|
742
796
|
successful \`run --components\` deploy (default: on;
|
|
743
797
|
also settable via chant.config.ts's
|
|
@@ -769,18 +823,18 @@ Options:
|
|
|
769
823
|
project source; network egress is NOT blocked (see
|
|
770
824
|
docs). Default: off (also settable via
|
|
771
825
|
chant.config.ts's build.sandbox: true; #1045)
|
|
772
|
-
--param <name=value> (build, graph, run --components, components fan-out
|
|
773
|
-
Bind a
|
|
774
|
-
build-time parameter (chant.config.ts's buildParams)
|
|
826
|
+
--param <name=value> (build, graph, run --components, components fan-out,
|
|
827
|
+
components promote, components rollback) Bind a
|
|
828
|
+
declared build-time parameter (chant.config.ts's buildParams)
|
|
775
829
|
to a value, for source to read as params.<name>
|
|
776
830
|
(#1064) instead of process.env — repeatable.
|
|
777
831
|
Distinct from the AWS lexicon's deploy-time
|
|
778
832
|
Parameter(): this resolves before synthesis, so it
|
|
779
833
|
can change which resources are produced at all.
|
|
780
834
|
Highest precedence.
|
|
781
|
-
--params-file <path> (build, graph, run --components, components fan-out
|
|
782
|
-
JSON file
|
|
783
|
-
{ "name": value } build-time parameter values
|
|
835
|
+
--params-file <path> (build, graph, run --components, components fan-out,
|
|
836
|
+
components promote, components rollback) JSON file
|
|
837
|
+
of { "name": value } build-time parameter values
|
|
784
838
|
(#1064). Second precedence, after --param.
|
|
785
839
|
|
|
786
840
|
Examples:
|
|
@@ -791,6 +845,7 @@ Examples:
|
|
|
791
845
|
chant build ./infra/ --fold
|
|
792
846
|
chant build ./infra/ --components --generate gitlab
|
|
793
847
|
chant build ./infra/ --components --generate gitlab --output .gitlab-ci.yml
|
|
848
|
+
chant run --generate github
|
|
794
849
|
chant run --components search-service --env staging
|
|
795
850
|
chant run --components all --env production
|
|
796
851
|
chant components export prod --component search-service -o ./dist/search-service
|
|
@@ -985,6 +1040,10 @@ export const commandRegistry: CommandDef[] = [
|
|
|
985
1040
|
{ name: "graph", handler: runGraph },
|
|
986
1041
|
{ name: "vendor", handler: runVendor },
|
|
987
1042
|
|
|
1043
|
+
// Workspace reads (#2524). Imported on first use, so a level-0 command never
|
|
1044
|
+
// loads anything under workspace/ (#2525 rule 5, pinned by #2526's goldens).
|
|
1045
|
+
{ name: "workspace records", handler: async (ctx) => (await import("../workspace/records-cli")).runWorkspaceRecords(ctx) },
|
|
1046
|
+
|
|
988
1047
|
// State subcommands
|
|
989
1048
|
{ name: "lifecycle snapshot", requiresPlugins: true, handler: runLifecycleSnapshot },
|
|
990
1049
|
{ name: "lifecycle show", handler: runLifecycleShow },
|
|
@@ -1006,6 +1065,8 @@ export const commandRegistry: CommandDef[] = [
|
|
|
1006
1065
|
{ name: "components status", requiresPlugins: true, handler: runComponentsStatus },
|
|
1007
1066
|
{ name: "components release", handler: runComponentsReleaseRecord },
|
|
1008
1067
|
{ name: "components export", handler: runComponentsExport },
|
|
1068
|
+
{ name: "components promote", handler: runComponentsPromote },
|
|
1069
|
+
{ name: "components rollback", handler: runComponentsRollback },
|
|
1009
1070
|
|
|
1010
1071
|
// Local emulators of configured lexicons (#920). Compound so the action word
|
|
1011
1072
|
// lands in args.path (not consumed as a project dir) and projectPath is forced ".".
|
|
@@ -1025,6 +1086,7 @@ export const commandRegistry: CommandDef[] = [
|
|
|
1025
1086
|
{ name: "dev", handler: runDevUnknown },
|
|
1026
1087
|
{ name: "serve", handler: runServeUnknown },
|
|
1027
1088
|
{ name: "components", handler: runComponentsUnknown },
|
|
1089
|
+
{ name: "workspace", handler: async (ctx) => (await import("../workspace/records-cli")).runWorkspaceUnknown(ctx) },
|
|
1028
1090
|
];
|
|
1029
1091
|
|
|
1030
1092
|
/**
|
package/src/cli/mcp/op-tools.ts
CHANGED
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
* run.
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
|
+
import { lexiconNames } from "../../lexicon-module";
|
|
17
18
|
import { resolve } from "node:path";
|
|
18
19
|
import { discoverOps } from "../../op/discover";
|
|
19
20
|
import { createLocalOpRuntime } from "../../op/runtimes/local";
|
|
@@ -39,7 +40,7 @@ async function runtimeFor(name: unknown): Promise<OpRuntimeProvider> {
|
|
|
39
40
|
}
|
|
40
41
|
|
|
41
42
|
const configured = await loadChantConfig(resolve("."))
|
|
42
|
-
.then(({ config }) => config.lexicons ?? [])
|
|
43
|
+
.then(({ config }) => lexiconNames(config.lexicons ?? []))
|
|
43
44
|
.catch(() => [] as string[]);
|
|
44
45
|
if (!configured.includes(wanted)) {
|
|
45
46
|
throw new Error(
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* chant#2578 — init, onboard, audit and import-agents name a lexicon declared
|
|
3
|
+
* by path (#2520) by that path, and print no install line for it. A lexicon
|
|
4
|
+
* named by package keeps its package name and `npm i` line.
|
|
5
|
+
*/
|
|
6
|
+
import { describe, test, expect, beforeEach, afterEach } from "vitest";
|
|
7
|
+
import { existsSync, mkdirSync, mkdtempSync, readFileSync, realpathSync, rmSync, writeFileSync } from "node:fs";
|
|
8
|
+
import { tmpdir } from "node:os";
|
|
9
|
+
import { join } from "node:path";
|
|
10
|
+
import {
|
|
11
|
+
lexiconPackagesToInstall,
|
|
12
|
+
lexiconSourceLabel,
|
|
13
|
+
registerLexiconDeclarations,
|
|
14
|
+
resetLexiconModules,
|
|
15
|
+
} from "../lexicon-module";
|
|
16
|
+
import { initCommand } from "./commands/init";
|
|
17
|
+
import { onboardCommand } from "./commands/onboard";
|
|
18
|
+
import { installLine } from "./commands/audit";
|
|
19
|
+
import { importAgentsCommand } from "./commands/import-agents";
|
|
20
|
+
import { auditFiles, MissingLexiconError, type AuditInput } from "../audit/core";
|
|
21
|
+
|
|
22
|
+
let dir: string;
|
|
23
|
+
|
|
24
|
+
beforeEach(() => {
|
|
25
|
+
dir = realpathSync(mkdtempSync(join(tmpdir(), "chant-2578-")));
|
|
26
|
+
resetLexiconModules();
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
afterEach(() => {
|
|
30
|
+
resetLexiconModules();
|
|
31
|
+
rmSync(dir, { recursive: true, force: true });
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
describe("the shared helpers", () => {
|
|
35
|
+
test("a path lexicon is labelled by its path and has no package to install", () => {
|
|
36
|
+
registerLexiconDeclarations(["aws", { name: "site", module: "./lexicon/index.ts" }], dir);
|
|
37
|
+
expect(lexiconSourceLabel("site", dir)).toBe("./lexicon/index.ts");
|
|
38
|
+
expect(lexiconSourceLabel("aws", dir)).toBe("@intentius/chant-lexicon-aws");
|
|
39
|
+
expect(lexiconPackagesToInstall(["aws", "site"])).toEqual(["@intentius/chant-lexicon-aws"]);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
test("a path outside the directory is shown absolute", () => {
|
|
43
|
+
registerLexiconDeclarations([{ name: "site", module: "/elsewhere/site.ts" }], dir);
|
|
44
|
+
expect(lexiconSourceLabel("site", dir)).toBe("/elsewhere/site.ts");
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
describe("init", () => {
|
|
49
|
+
function writeConfig(entry: string): void {
|
|
50
|
+
writeFileSync(join(dir, "chant.config.ts"), `export default { lexicons: [${entry}] };\n`);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
test("a lexicon the existing config declares by path gets no dependency and no type stub", async () => {
|
|
54
|
+
writeConfig(`{ name: "site", module: "./lexicon/index.ts" }`);
|
|
55
|
+
const result = await initCommand({ path: dir, lexicon: "site", force: true, skipMcp: true, skipInstall: true });
|
|
56
|
+
|
|
57
|
+
expect(result.success).toBe(true);
|
|
58
|
+
expect(result.lexiconModule).toBe("./lexicon/index.ts");
|
|
59
|
+
const pkg = JSON.parse(readFileSync(join(dir, "package.json"), "utf-8"));
|
|
60
|
+
expect(Object.keys(pkg.dependencies)).toEqual(["@intentius/chant"]);
|
|
61
|
+
expect(existsSync(join(dir, ".chant", "types", "lexicon-site"))).toBe(false);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
test("a package lexicon is unchanged", async () => {
|
|
65
|
+
writeConfig(`"aws"`);
|
|
66
|
+
const result = await initCommand({ path: dir, lexicon: "aws", force: true, skipMcp: true, skipInstall: true });
|
|
67
|
+
|
|
68
|
+
expect(result.success).toBe(true);
|
|
69
|
+
expect(result.lexiconModule).toBeUndefined();
|
|
70
|
+
const pkg = JSON.parse(readFileSync(join(dir, "package.json"), "utf-8"));
|
|
71
|
+
expect(Object.keys(pkg.dependencies)).toEqual(["@intentius/chant", "@intentius/chant-lexicon-aws"]);
|
|
72
|
+
expect(existsSync(join(dir, ".chant", "types", "lexicon-aws", "package.json"))).toBe(true);
|
|
73
|
+
});
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
describe("onboard", () => {
|
|
77
|
+
test("a lexicon declared by path is named by its path, with nothing patched", () => {
|
|
78
|
+
registerLexiconDeclarations([{ name: "site", module: "./lexicon/index.ts" }], process.cwd());
|
|
79
|
+
const result = onboardCommand({ name: "site", root: dir });
|
|
80
|
+
|
|
81
|
+
expect(result.success).toBe(false);
|
|
82
|
+
expect(result.error).toContain("./lexicon/index.ts");
|
|
83
|
+
expect(result.error).not.toMatch(/npm (i|install)\b/);
|
|
84
|
+
expect(result.patched).toEqual([]);
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
test("a package lexicon still patches", () => {
|
|
88
|
+
writeFileSync(join(dir, "package.json"), JSON.stringify({ name: "root", dependencies: {} }));
|
|
89
|
+
const result = onboardCommand({ name: "site", root: dir });
|
|
90
|
+
|
|
91
|
+
expect(result.success).toBe(true);
|
|
92
|
+
expect(result.patched.some((p) => p.startsWith("package.json"))).toBe(true);
|
|
93
|
+
});
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
describe("audit", () => {
|
|
97
|
+
test("the npx line leaves out a lexicon declared by path", () => {
|
|
98
|
+
registerLexiconDeclarations([{ name: "github", module: "./gh/index.ts" }], dir);
|
|
99
|
+
expect(installLine(["github", "gitlab"], ".")).toBe(
|
|
100
|
+
"npx -p @intentius/chant -p @intentius/chant-lexicon-gitlab chant audit .",
|
|
101
|
+
);
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
test("a path lexicon that fails to load is named by its path, with no npm line", async () => {
|
|
105
|
+
registerLexiconDeclarations([{ name: "helm", module: join(dir, "missing.ts") }], dir);
|
|
106
|
+
const input = { lexicon: "helm", path: "Chart.yaml", content: "" } as AuditInput;
|
|
107
|
+
|
|
108
|
+
const error = await auditFiles([input]).then(
|
|
109
|
+
() => undefined,
|
|
110
|
+
(err: unknown) => err,
|
|
111
|
+
);
|
|
112
|
+
expect(error).toBeInstanceOf(MissingLexiconError);
|
|
113
|
+
expect((error as Error).message).toContain(join(dir, "missing.ts"));
|
|
114
|
+
expect((error as Error).message).not.toContain("npm i");
|
|
115
|
+
});
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
describe("import-agents", () => {
|
|
119
|
+
function seedAgentConfig(home: string): void {
|
|
120
|
+
mkdirSync(join(home, ".claude"), { recursive: true });
|
|
121
|
+
writeFileSync(join(home, ".claude", "mcp.json"), JSON.stringify({ mcpServers: { a: { command: "x" } } }));
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
test("a path lexicon that fails to load is named by its path, with no npm line", async () => {
|
|
125
|
+
const home = join(dir, "home");
|
|
126
|
+
seedAgentConfig(home);
|
|
127
|
+
registerLexiconDeclarations([{ name: "fountain", module: "./agents/missing.ts" }], dir);
|
|
128
|
+
|
|
129
|
+
const result = await importAgentsCommand({ home, platform: "linux", projectRoots: [], output: join(dir, "out") });
|
|
130
|
+
|
|
131
|
+
expect(result.success).toBe(false);
|
|
132
|
+
expect(result.error).toContain("declared by path");
|
|
133
|
+
expect(result.error).toContain("missing.ts");
|
|
134
|
+
expect(result.error).not.toContain("npm i");
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
test("a package lexicon still gets its npm line", async () => {
|
|
138
|
+
const home = join(dir, "home");
|
|
139
|
+
seedAgentConfig(home);
|
|
140
|
+
|
|
141
|
+
const result = await importAgentsCommand({
|
|
142
|
+
home,
|
|
143
|
+
platform: "linux",
|
|
144
|
+
projectRoots: [],
|
|
145
|
+
output: join(dir, "out"),
|
|
146
|
+
lexicon: "no-such-lexicon",
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
expect(result.success).toBe(false);
|
|
150
|
+
expect(result.error).toContain("npm i @intentius/chant-lexicon-no-such-lexicon");
|
|
151
|
+
});
|
|
152
|
+
});
|
package/src/cli/plugins.ts
CHANGED
|
@@ -1,10 +1,30 @@
|
|
|
1
|
+
import { importLexiconModule, lexiconModulePath, lexiconNames } from "../lexicon-module";
|
|
1
2
|
import { createRequire } from "node:module";
|
|
2
3
|
import { dirname, join } from "node:path";
|
|
3
4
|
import { existsSync, readFileSync } from "node:fs";
|
|
4
5
|
import { isLexiconPlugin, type LexiconPlugin } from "../lexicon";
|
|
5
|
-
import { loadChantConfigUpward } from "../config";
|
|
6
|
+
import { loadChantConfig, loadChantConfigUpward } from "../config";
|
|
7
|
+
import { findProjectConfig } from "../project-root";
|
|
6
8
|
import { findInfraFiles, detectLexicons } from "../index";
|
|
7
|
-
import { checkConflicts } from "./conflict-check";
|
|
9
|
+
import { checkConflicts, describeConflict } from "./conflict-check";
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* chant#2578 — load the chant.config nearest `dir` (walking up) so the
|
|
13
|
+
* lexicons it declares by path are recorded (../lexicon-module.ts), for a
|
|
14
|
+
* command that does not otherwise read the config. With no config there is
|
|
15
|
+
* nothing to record. A config that fails to load records nothing either, and
|
|
16
|
+
* the command keeps its package behaviour; the commands that need a valid
|
|
17
|
+
* config report its errors themselves.
|
|
18
|
+
*/
|
|
19
|
+
export async function recordProjectLexicons(dir: string): Promise<void> {
|
|
20
|
+
const { dir: projectDir, configPath } = findProjectConfig(dir);
|
|
21
|
+
if (configPath === undefined) return;
|
|
22
|
+
try {
|
|
23
|
+
await loadChantConfig(projectDir);
|
|
24
|
+
} catch {
|
|
25
|
+
// See above: an unloadable config changes nothing here.
|
|
26
|
+
}
|
|
27
|
+
}
|
|
8
28
|
|
|
9
29
|
/**
|
|
10
30
|
* Load a single lexicon plugin by lexicon name.
|
|
@@ -13,6 +33,23 @@ import { checkConflicts } from "./conflict-check";
|
|
|
13
33
|
* LexiconPlugin export. Falls back to wrapping a raw Serializer export.
|
|
14
34
|
*/
|
|
15
35
|
export async function loadPlugin(lexiconName: string): Promise<LexiconPlugin> {
|
|
36
|
+
// chant #2520 — a lexicon declared by module path loads from that module.
|
|
37
|
+
const modulePath = lexiconModulePath(lexiconName);
|
|
38
|
+
if (modulePath !== undefined) {
|
|
39
|
+
const mod = (await importLexiconModule(lexiconName))!;
|
|
40
|
+
for (const value of Object.values(mod)) {
|
|
41
|
+
if (isLexiconPlugin(value)) {
|
|
42
|
+
if (value.name !== lexiconName) {
|
|
43
|
+
throw new Error(
|
|
44
|
+
`lexicon "${lexiconName}" is declared with module ${modulePath}, but the plugin it exports is named "${value.name}"`,
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
return value;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
throw new Error(`Module ${modulePath} (lexicon "${lexiconName}") does not export a LexiconPlugin`);
|
|
51
|
+
}
|
|
52
|
+
|
|
16
53
|
const packageName = `@intentius/chant-lexicon-${lexiconName}`;
|
|
17
54
|
const mod = await import(packageName);
|
|
18
55
|
|
|
@@ -67,6 +104,9 @@ export function resolveLexiconVersions(lexiconNames: readonly string[]): Record<
|
|
|
67
104
|
const versions: Record<string, string> = {};
|
|
68
105
|
|
|
69
106
|
for (const name of lexiconNames) {
|
|
107
|
+
// chant #2520 — a lexicon declared by module path is not an installed
|
|
108
|
+
// package, so it has no package version to report.
|
|
109
|
+
if (lexiconModulePath(name) !== undefined) continue;
|
|
70
110
|
const packageName = `@intentius/chant-lexicon-${name}`;
|
|
71
111
|
try {
|
|
72
112
|
let dir = dirname(require_.resolve(packageName));
|
|
@@ -177,9 +217,7 @@ export async function loadPlugins(lexiconNames: string[]): Promise<LexiconPlugin
|
|
|
177
217
|
}
|
|
178
218
|
|
|
179
219
|
if (report.conflicts.length > 0) {
|
|
180
|
-
const details = report.conflicts
|
|
181
|
-
.map((c) => ` ${c.type} "${c.key}" from: ${c.plugins.join(", ")}`)
|
|
182
|
-
.join("\n");
|
|
220
|
+
const details = report.conflicts.map(describeConflict).join("\n");
|
|
183
221
|
throw new Error(
|
|
184
222
|
`Cross-lexicon conflicts detected:\n${details}`,
|
|
185
223
|
);
|
|
@@ -222,7 +260,7 @@ export async function resolveProjectLexicons(projectPath: string): Promise<strin
|
|
|
222
260
|
const { config } = await loadChantConfigUpward(projectPath);
|
|
223
261
|
|
|
224
262
|
if (config.lexicons && config.lexicons.length > 0) {
|
|
225
|
-
return config.lexicons;
|
|
263
|
+
return lexiconNames(config.lexicons);
|
|
226
264
|
}
|
|
227
265
|
|
|
228
266
|
// Fallback: detect from source imports — a pure text scan, no execution.
|
package/src/cli/registry.ts
CHANGED
|
@@ -145,6 +145,8 @@ export interface ParsedArgs {
|
|
|
145
145
|
/** `--env <name>` — active environment: sets CHANT_ENV so env-aware source
|
|
146
146
|
* re-evaluates for that environment (`build` + `graph`), and drives policy. */
|
|
147
147
|
env?: string;
|
|
148
|
+
/** `chant build --components --generate <lexicon> --promote-to <env>` (#2575) — add a promote job to the generated pipeline. */
|
|
149
|
+
promoteTo?: string;
|
|
148
150
|
/** `chant graph --stacks` — render the cross-stack apply-ordering graph */
|
|
149
151
|
stacks?: boolean;
|
|
150
152
|
/** `chant graph --live --overlay` — classify the provisioned graph against
|
|
@@ -175,8 +177,13 @@ export interface ParsedArgs {
|
|
|
175
177
|
components?: boolean;
|
|
176
178
|
/** `chant build --components --generate <lexicon>` — generate mode (#563):
|
|
177
179
|
* synthesize CI YAML from discovered components instead of running a normal
|
|
178
|
-
* lexicon build.
|
|
180
|
+
* lexicon build. Also `chant run --generate <provider>` (#2533): one CI
|
|
181
|
+
* pipeline file per scheduled Op, through `generateOpsPipeline`. */
|
|
179
182
|
generate?: string;
|
|
183
|
+
/** `chant run --generate <provider> --spec <file>` (#2533): a JSON file of
|
|
184
|
+
* `ScheduledOpSpec`s (or `{ ops, options }`) to generate instead of every
|
|
185
|
+
* discovered Op that declares a `schedule`. */
|
|
186
|
+
opsSpec?: string;
|
|
180
187
|
/** `chant graph --format ir --detail <0..3>` — graph IR detail tier */
|
|
181
188
|
detail?: number;
|
|
182
189
|
/** `chant graph --lens <kind>:<target>` — focus the graph IR on a slice */
|
|
@@ -242,6 +249,8 @@ export interface ParsedArgs {
|
|
|
242
249
|
allProjects?: boolean;
|
|
243
250
|
/** `chant audit --fail-on merge-worthy|warning|error|none` */
|
|
244
251
|
failOn?: string;
|
|
252
|
+
/** `chant audit --max-files <n>` (#2528) — files the local walk takes before it stops (default 1000). */
|
|
253
|
+
maxFiles?: number;
|
|
245
254
|
/** `chant audit --theme <file>` — JSON theme knobs for the HTML report */
|
|
246
255
|
theme?: string;
|
|
247
256
|
/** `chant dev surface-diff --update-snapshot` — write the fresh snapshot as the new baseline */
|
|
@@ -270,6 +279,10 @@ export interface ParsedArgs {
|
|
|
270
279
|
* should win.
|
|
271
280
|
*/
|
|
272
281
|
at?: string;
|
|
282
|
+
/** `chant workspace records --kind <path>` (#2546): the record kind file to read records through. */
|
|
283
|
+
kind?: string;
|
|
284
|
+
/** `chant workspace records --current` (#2546): leave out records a closed record supersedes. */
|
|
285
|
+
current?: boolean;
|
|
273
286
|
/**
|
|
274
287
|
* `chant search "<q>" --ambient --live --env <name>` (#1278) — also report
|
|
275
288
|
* resources of a kind this estate manages that exist in the account without
|
|
@@ -364,6 +377,10 @@ export interface ParsedArgs {
|
|
|
364
377
|
* on a model-authored channel.
|
|
365
378
|
*/
|
|
366
379
|
allowSameOrigin?: boolean;
|
|
380
|
+
/** `chant approve --role <name>` (#2508) — roles the approver claims, for a gate whose quorum counts only some roles. Repeatable; a comma list also works. */
|
|
381
|
+
roles?: string[];
|
|
382
|
+
/** `chant approve --agent` (#2508) — record the approval as an agent's. An agent never counts toward a quorum; it passes a gate only through a policy permit in `enforce` mode. */
|
|
383
|
+
agent?: boolean;
|
|
367
384
|
/** `chant approve <op> <gate> --plan <digest>` (#2300) — the plan this approval is for, as `sha256:<64 hex>`. Omitted, the digest is taken from the gate's standing pending fact, which is the plan the run that stopped at the gate actually produced; pass it to approve a plan explicitly, or to approve one before any run has recorded a pending fact. */
|
|
368
385
|
plan?: string;
|
|
369
386
|
/** `chant operator log --op <name>` (#2029) — restrict the tick history to one ConvergeOp by name. Omitted, every discovered ConvergeOp's ticks are merged into one timeline. */
|
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
*/
|
|
23
23
|
|
|
24
24
|
import { CapabilityRegistry } from "./capability";
|
|
25
|
+
import { lexiconModulePath } from "../lexicon-module";
|
|
25
26
|
import type { CapabilityPlugin } from "./capability-plugin";
|
|
26
27
|
import { isCapabilityPlugin } from "./capability-plugin";
|
|
27
28
|
import { starterCapabilityPlugin } from "./starter-plugin";
|
|
@@ -113,7 +114,8 @@ export async function loadCapabilityPlugins(names: string[]): Promise<Capability
|
|
|
113
114
|
* where the package's whole reason to exist is the plugin.
|
|
114
115
|
*/
|
|
115
116
|
export async function loadCapabilityPluginFromLexicon(name: string): Promise<CapabilityPlugin | null> {
|
|
116
|
-
|
|
117
|
+
// chant #2520 — a lexicon declared by module path is imported from that path.
|
|
118
|
+
const packageName = lexiconModulePath(name) ?? `@intentius/chant-lexicon-${name}`;
|
|
117
119
|
let mod: Record<string, unknown>;
|
|
118
120
|
try {
|
|
119
121
|
mod = (await import(packageName)) as Record<string, unknown>;
|
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
* executor, the CLI entrypoint the driver (#556) never had.
|
|
23
23
|
*/
|
|
24
24
|
|
|
25
|
+
import { lexiconModulePath, lexiconNames } from "../lexicon-module";
|
|
25
26
|
import { discoverComponents } from "./discover";
|
|
26
27
|
import type { BuildParamProvenance } from "../provenance";
|
|
27
28
|
import { projectToJson, type Archetype } from "./component";
|
|
@@ -237,7 +238,7 @@ export interface GenerateComponentsResult {
|
|
|
237
238
|
async function loadLexiconPlugin(name: string): Promise<LexiconPlugin | null> {
|
|
238
239
|
let mod: Record<string, unknown>;
|
|
239
240
|
try {
|
|
240
|
-
mod = (await import(`@intentius/chant-lexicon-${name}`)) as Record<string, unknown>;
|
|
241
|
+
mod = (await import(lexiconModulePath(name) ?? `@intentius/chant-lexicon-${name}`)) as Record<string, unknown>;
|
|
241
242
|
} catch {
|
|
242
243
|
return null;
|
|
243
244
|
}
|
|
@@ -550,7 +551,7 @@ export async function runComponents(
|
|
|
550
551
|
options.registry ??
|
|
551
552
|
(await buildCapabilityRegistry({
|
|
552
553
|
plugins: options.capabilityPlugins ?? config.capabilities,
|
|
553
|
-
lexicons: config.lexicons,
|
|
554
|
+
lexicons: lexiconNames(config.lexicons),
|
|
554
555
|
}));
|
|
555
556
|
const env = options.env ?? "local";
|
|
556
557
|
const selected = targets.map((c) => c.name);
|