@intentius/chant 0.124.45 → 0.124.46
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/capabilities/aws.paths +148 -0
- package/dist/capabilities/base-mappings.json +122 -0
- package/dist/capabilities/catalog.d.ts +89 -0
- package/dist/capabilities/catalog.d.ts.map +1 -0
- package/dist/capabilities/catalog.js +204 -0
- package/dist/capabilities/language.d.ts +103 -0
- package/dist/capabilities/language.d.ts.map +1 -0
- package/dist/capabilities/language.js +335 -0
- package/dist/capabilities/matcher.d.ts +65 -0
- package/dist/capabilities/matcher.d.ts.map +1 -0
- package/dist/capabilities/matcher.js +299 -0
- package/dist/capabilities/view.d.ts +207 -0
- package/dist/capabilities/view.d.ts.map +1 -0
- package/dist/capabilities/view.js +484 -0
- package/dist/cli/commands/lint.d.ts.map +1 -1
- package/dist/cli/commands/lint.js +1 -0
- package/dist/cli/handlers/declared-graph.d.ts +44 -0
- package/dist/cli/handlers/declared-graph.d.ts.map +1 -0
- package/dist/cli/handlers/declared-graph.js +87 -0
- package/dist/cli/handlers/graph.d.ts +25 -0
- package/dist/cli/handlers/graph.d.ts.map +1 -1
- package/dist/cli/handlers/graph.js +93 -87
- package/dist/cli/handlers/pr-env.d.ts.map +1 -1
- package/dist/cli/handlers/pr-env.js +2 -1
- package/dist/cli/handlers/search-count.d.ts +89 -0
- package/dist/cli/handlers/search-count.d.ts.map +1 -0
- package/dist/cli/handlers/search-count.js +132 -0
- package/dist/cli/handlers/search.d.ts +358 -6
- package/dist/cli/handlers/search.d.ts.map +1 -1
- package/dist/cli/handlers/search.js +1065 -155
- package/dist/cli/main.d.ts.map +1 -1
- package/dist/cli/main.js +23 -3
- package/dist/cli/mcp/server.d.ts.map +1 -1
- package/dist/cli/mcp/server.js +3 -0
- package/dist/cli/mcp/tools/capabilities.d.ts +7 -0
- package/dist/cli/mcp/tools/capabilities.d.ts.map +1 -0
- package/dist/cli/mcp/tools/capabilities.js +27 -0
- package/dist/cli/mcp/workspace-tools.d.ts +3 -1
- package/dist/cli/mcp/workspace-tools.d.ts.map +1 -1
- package/dist/cli/mcp/workspace-tools.js +47 -2
- package/dist/cli/parse-args.d.ts.map +1 -1
- package/dist/cli/parse-args.js +64 -17
- package/dist/cli/registry.d.ts +22 -0
- package/dist/cli/registry.d.ts.map +1 -1
- package/dist/components/pr-env-ports.d.ts +29 -0
- package/dist/components/pr-env-ports.d.ts.map +1 -0
- package/dist/components/pr-env-ports.js +77 -0
- package/dist/components/pr-env-run.d.ts +5 -21
- package/dist/components/pr-env-run.d.ts.map +1 -1
- package/dist/components/pr-env-run.js +6 -70
- package/dist/components/pr-environment.d.ts +2 -1
- package/dist/components/pr-environment.d.ts.map +1 -1
- package/dist/components/pr-environment.js +1 -1
- package/dist/config-environments.d.ts +92 -0
- package/dist/config-environments.d.ts.map +1 -0
- package/dist/config-environments.js +102 -0
- package/dist/config.d.ts +24 -84
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +9 -96
- package/dist/graph-detail.d.ts.map +1 -1
- package/dist/graph-detail.js +15 -3
- package/dist/graph-effective.d.ts +33 -2
- package/dist/graph-effective.d.ts.map +1 -1
- package/dist/graph-effective.js +215 -68
- package/dist/graph-ir.d.ts +23 -0
- package/dist/graph-ir.d.ts.map +1 -1
- package/dist/graph-ir.js +130 -0
- package/dist/graph-sub-template.d.ts +11 -0
- package/dist/graph-sub-template.d.ts.map +1 -0
- package/dist/graph-sub-template.js +19 -0
- package/dist/lexicon.d.ts +9 -0
- package/dist/lexicon.d.ts.map +1 -1
- package/dist/lifecycle/observe.d.ts +28 -0
- package/dist/lifecycle/observe.d.ts.map +1 -1
- package/dist/lifecycle/observe.js +23 -1
- package/dist/lint/rule.d.ts +6 -0
- package/dist/lint/rule.d.ts.map +1 -1
- package/dist/live-endpoint.d.ts +1 -1
- package/dist/live-endpoint.d.ts.map +1 -1
- package/dist/live-endpoint.js +1 -1
- package/dist/observation.d.ts +48 -1
- package/dist/observation.d.ts.map +1 -1
- package/dist/observation.js +21 -5
- package/dist/op/activities/activity-contracts.d.ts +4 -4
- package/dist/wave-runtime.mjs +250 -249
- package/dist/workspace/capabilities-contract.d.ts +66 -0
- package/dist/workspace/capabilities-contract.d.ts.map +1 -0
- package/dist/workspace/capabilities-contract.js +107 -0
- package/dist/workspace/capabilities.schema.json +229 -0
- package/dist/workspace/compose-graph.d.ts +9 -0
- package/dist/workspace/compose-graph.d.ts.map +1 -1
- package/dist/workspace/compose-graph.js +3 -1
- package/dist/workspace/conformance/index.d.ts +1 -1
- package/dist/workspace/conformance/index.d.ts.map +1 -1
- package/dist/workspace/conformance/index.js +39 -2
- package/dist/workspace/graph-cache.d.ts +8 -0
- package/dist/workspace/graph-cache.d.ts.map +1 -1
- package/dist/workspace/graph-cache.js +27 -3
- package/dist/workspace/graph-cli.d.ts +39 -2
- package/dist/workspace/graph-cli.d.ts.map +1 -1
- package/dist/workspace/graph-cli.js +66 -24
- package/dist/workspace/graph.schema.json +148 -0
- package/dist/workspace/kind-readers.d.ts +3 -0
- package/dist/workspace/kind-readers.d.ts.map +1 -1
- package/dist/workspace/kind-readers.js +7 -1
- package/dist/workspace/member-commands.d.ts +10 -1
- package/dist/workspace/member-commands.d.ts.map +1 -1
- package/dist/workspace/member-commands.js +17 -1
- package/dist/workspace/nested-graph.d.ts +2 -0
- package/dist/workspace/nested-graph.d.ts.map +1 -1
- package/dist/workspace/nested-graph.js +32 -1
- package/dist/workspace/reason-codes.d.ts +10 -0
- package/dist/workspace/reason-codes.d.ts.map +1 -1
- package/dist/workspace/reason-codes.js +12 -0
- package/dist/workspace/search-cli.d.ts +204 -0
- package/dist/workspace/search-cli.d.ts.map +1 -0
- package/dist/workspace/search-cli.js +466 -0
- package/dist/workspace/search-contract.d.ts +56 -0
- package/dist/workspace/search-contract.d.ts.map +1 -0
- package/dist/workspace/search-contract.js +70 -0
- package/dist/workspace/search.schema.json +822 -0
- package/package.json +1 -1
- package/src/__snapshots__/okf.test.ts.snap +10 -0
- package/src/capabilities/aws.paths +148 -0
- package/src/capabilities/base-mappings.json +122 -0
- package/src/capabilities/catalog.ts +230 -0
- package/src/capabilities/language.test.ts +191 -0
- package/src/capabilities/language.ts +373 -0
- package/src/capabilities/matcher.ts +315 -0
- package/src/capabilities/view-compose.test.ts +76 -0
- package/src/capabilities/view.ts +631 -0
- package/src/cli/commands/lint.ts +1 -0
- package/src/cli/handlers/declared-graph.ts +99 -0
- package/src/cli/handlers/graph.test.ts +94 -3
- package/src/cli/handlers/graph.ts +123 -96
- package/src/cli/handlers/pr-env.ts +1 -2
- package/src/cli/handlers/search-build-roots.test.ts +144 -0
- package/src/cli/handlers/search-count.test.ts +191 -0
- package/src/cli/handlers/search-count.ts +165 -0
- package/src/cli/handlers/search-json.test.ts +333 -0
- package/src/cli/handlers/search-live.test.ts +387 -0
- package/src/cli/handlers/search-paths.test.ts +235 -0
- package/src/cli/handlers/search.ts +1312 -166
- package/src/cli/main.test.ts +28 -3
- package/src/cli/main.ts +23 -3
- package/src/cli/mcp/server.test.ts +3 -3
- package/src/cli/mcp/server.ts +3 -0
- package/src/cli/mcp/tools/capabilities.ts +32 -0
- package/src/cli/mcp/workspace-tools.e2e.test.ts +21 -1
- package/src/cli/mcp/workspace-tools.ts +47 -2
- package/src/cli/parse-args.ts +52 -13
- package/src/cli/registry.ts +22 -0
- package/src/components/pr-env-ports.ts +89 -0
- package/src/components/pr-env-run-bundle.test.ts +54 -0
- package/src/components/pr-env-run.ts +5 -76
- package/src/components/pr-environment.ts +2 -1
- package/src/config-environments.ts +133 -0
- package/src/config.ts +37 -124
- package/src/graph-detail.ts +15 -3
- package/src/graph-effective.ts +226 -75
- package/src/graph-ir.test.ts +163 -0
- package/src/graph-ir.ts +147 -0
- package/src/graph-sub-template.ts +22 -0
- package/src/lexicon.ts +10 -0
- package/src/lifecycle/observe.test.ts +27 -1
- package/src/lifecycle/observe.ts +43 -1
- package/src/lint/rule.ts +6 -0
- package/src/live-endpoint.ts +1 -1
- package/src/observation.test.ts +25 -0
- package/src/observation.ts +69 -5
- package/src/workspace/capabilities-contract.test.ts +88 -0
- package/src/workspace/capabilities-contract.ts +132 -0
- package/src/workspace/capabilities.schema.json +229 -0
- package/src/workspace/compose-graph.ts +10 -1
- package/src/workspace/conformance/conformance.e2e.test.ts +14 -1
- package/src/workspace/conformance/index.ts +31 -2
- package/src/workspace/graph-cache.ts +27 -3
- package/src/workspace/graph-cli.ts +96 -24
- package/src/workspace/graph.schema.json +148 -0
- package/src/workspace/kind-readers.e2e.test.ts +28 -0
- package/src/workspace/kind-readers.test.ts +1 -1
- package/src/workspace/kind-readers.ts +7 -1
- package/src/workspace/member-commands.ts +22 -1
- package/src/workspace/nested-graph.ts +32 -1
- package/src/workspace/reason-codes.test.ts +7 -0
- package/src/workspace/reason-codes.ts +12 -0
- package/src/workspace/search-cli.e2e.test.ts +305 -0
- package/src/workspace/search-cli.ts +591 -0
- package/src/workspace/search-contract.ts +95 -0
- package/src/workspace/search.schema.json +822 -0
package/src/cli/main.test.ts
CHANGED
|
@@ -208,9 +208,33 @@ describe("parseArgs", () => {
|
|
|
208
208
|
expect(() => parseArgs(["build", "--unknown=value"])).toThrow(/Unknown flag: --unknown/);
|
|
209
209
|
});
|
|
210
210
|
|
|
211
|
-
test("unknown short
|
|
212
|
-
|
|
213
|
-
|
|
211
|
+
test("an unknown short flag is refused like an unknown long one (#4369)", () => {
|
|
212
|
+
// Was "unknown short flags are still silently ignored": `-x` vanished and
|
|
213
|
+
// the next token became the path. #4369 refuses it with the #1127 message.
|
|
214
|
+
expect(() => parseArgs(["build", "-x", "value"])).toThrow(/Unknown flag: -x\nRun "chant --help"/);
|
|
215
|
+
expect(() => parseArgs(["search", "kind:X", "-q"])).toThrow(/Unknown flag: -q/);
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
test("a search query that opens with -> is the positional, not a dropped flag (#4369)", () => {
|
|
219
|
+
expect(parseArgs(["search", "->kind:Bucket"]).path).toBe("->kind:Bucket");
|
|
220
|
+
const explained = parseArgs(["search", "->kind:Bucket", "--explain"]);
|
|
221
|
+
expect(explained.path).toBe("->kind:Bucket");
|
|
222
|
+
expect(explained.explain).toBe(true);
|
|
223
|
+
expect(parseArgs(["search", "--explain", "->*[via=subnet_id]kind:aws_vpc"]).path).toBe("->*[via=subnet_id]kind:aws_vpc");
|
|
224
|
+
// Unchanged: the in-edge spelling and a leading space always worked.
|
|
225
|
+
expect(parseArgs(["search", "<-kind:Bucket"]).path).toBe("<-kind:Bucket");
|
|
226
|
+
expect(parseArgs(["search", " ->kind:Bucket"]).path).toBe(" ->kind:Bucket");
|
|
227
|
+
// A known short flag still parses as one.
|
|
228
|
+
expect(parseArgs(["search", "->kind:Bucket", "-v"]).verbose).toBe(true);
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
test("-- ends flag parsing: what follows is positional, unsplit (#4369)", () => {
|
|
232
|
+
const r = parseArgs(["search", "--json", "--", "-kind:X", "--explain"]);
|
|
233
|
+
expect(r.path).toBe("-kind:X");
|
|
234
|
+
expect(r.extraPositional).toBe("--explain");
|
|
235
|
+
expect(r.explain).toBeUndefined();
|
|
236
|
+
expect(r.json).toBe(true);
|
|
237
|
+
expect(parseArgs(["search", "--", "--x=y"]).path).toBe("--x=y");
|
|
214
238
|
});
|
|
215
239
|
|
|
216
240
|
test("parses --watch flag", () => {
|
|
@@ -846,6 +870,7 @@ describe("workspace init and ls (#2534)", () => {
|
|
|
846
870
|
"workspace points",
|
|
847
871
|
"workspace records",
|
|
848
872
|
"workspace runs",
|
|
873
|
+
"workspace search",
|
|
849
874
|
"workspace signers",
|
|
850
875
|
"workspace status",
|
|
851
876
|
"workspace trust",
|
package/src/cli/main.ts
CHANGED
|
@@ -170,9 +170,15 @@ Commands:
|
|
|
170
170
|
bundle tree, otherwise JSON path→content on stdout)
|
|
171
171
|
search "<query>" Answer an estate question with a few matching rows
|
|
172
172
|
instead of a graph dump (kind:, tag:, attr:, ->/<- edge
|
|
173
|
-
terms
|
|
174
|
-
|
|
175
|
-
|
|
173
|
+
terms, ->* / ->{1,3} for depth, ->[via=attr] / [to=attr]
|
|
174
|
+
by attribute; --live or --snapshot <ref> (also --at
|
|
175
|
+
<ref>) with --env <name> reads the estate or a
|
|
176
|
+
recorded snapshot; --show <attr>,
|
|
177
|
+
--count-by kind|module|attr:<name>, --explain prints
|
|
178
|
+
paths, --json, --check-live,
|
|
179
|
+
--check-snapshot, --fail-on-drift; cap:<name> and
|
|
180
|
+
->[cap=<name>] ask with a capability, and
|
|
181
|
+
--capabilities prints the catalog)
|
|
176
182
|
query "<sql>" [dir] Run one read-only SQL statement over chant/lifecycle:
|
|
177
183
|
the resources and snapshots each lexicon observed, Op
|
|
178
184
|
runs, gates, deploys, releases and cuts, loaded into
|
|
@@ -580,6 +586,9 @@ Ops:
|
|
|
580
586
|
--layout-engine graphviz to use dot instead;
|
|
581
587
|
--detail 0..3: stacks|composites|declarables|attributes;
|
|
582
588
|
--lens lexicon:<n>|stack:<n>|blast:<node> (--up/--down))
|
|
589
|
+
--format ir --capability-view: add the lexicon-neutral
|
|
590
|
+
view capability rules read, as this project's lexicons
|
|
591
|
+
map it, to the IR as capabilityView (#4378)
|
|
583
592
|
--components --format ir --projection gitlab|github|forgejo:
|
|
584
593
|
add the CI/pipeline projection (stages/jobs/needs) to
|
|
585
594
|
the component-graph IR, from the same generator
|
|
@@ -934,6 +943,15 @@ Workspace (level 1, #2524):
|
|
|
934
943
|
source and toolchain are unchanged is served from
|
|
935
944
|
the cache in $CHANT_CACHE_DIR or ~/.cache/chant;
|
|
936
945
|
--no-cache reads every member
|
|
946
|
+
--capability-view: each member's chant adds its
|
|
947
|
+
capability view, mapped by its own lexicons
|
|
948
|
+
workspace search "<query>" [--member <name>] [--env <name>] [--snapshot <ref> | --live] [--at <rev>] [--explain] [--count-by ...] [--json]
|
|
949
|
+
chant search in every chant member, each under its own
|
|
950
|
+
chant, as one read-contract document: ids are
|
|
951
|
+
<member>/<id> as workspace graph prints them, each
|
|
952
|
+
member is searched alone, and a member that can't be
|
|
953
|
+
searched has a reason code. --at <rev> is the git
|
|
954
|
+
revision; --snapshot <ref> replays a recorded snapshot
|
|
937
955
|
workspace graph --composites [--at <rev>] [--member <name>] [-o <file>]
|
|
938
956
|
Each composite instance the members declare, with the
|
|
939
957
|
components whose contract can deploy it; an instance
|
|
@@ -1610,6 +1628,8 @@ export const commandRegistry: CommandDef[] = [
|
|
|
1610
1628
|
// chant#2618 — audit runs no project code, at the workspace level too.
|
|
1611
1629
|
{ name: "workspace audit", runsNoConfig: true, handler: async (ctx) => (await import("../workspace/member-commands")).runWorkspaceMembers(ctx, "audit") },
|
|
1612
1630
|
{ name: "workspace graph", handler: async (ctx) => (await import("../workspace/member-commands")).runWorkspaceMembers(ctx, "graph") },
|
|
1631
|
+
// #4374 — chant search in every member, under its own chant, composed with workspace graph's ids: the search document.
|
|
1632
|
+
{ name: "workspace search", runsNoConfig: true, handler: async (ctx) => (await import("../workspace/search-cli")).runWorkspaceSearch(ctx) },
|
|
1613
1633
|
// Each unit decides for itself whether its config is loaded (runCommandInProcess).
|
|
1614
1634
|
{ name: "workspace member-run", runsNoConfig: true, handler: async (ctx) => (await import("../workspace/member-run")).runWorkspaceMemberRun(ctx, runCommandInProcess) },
|
|
1615
1635
|
// #3308 — a box's listing, written through chant so a tool never edits the declaration itself (ws-074).
|
|
@@ -1630,9 +1630,9 @@ describe("McpServer", () => {
|
|
|
1630
1630
|
|
|
1631
1631
|
const toolsRes = await s.handleRequest({ jsonrpc: "2.0", id: 2, method: "tools/list" });
|
|
1632
1632
|
const tools = (toolsRes.result as { tools: Array<{ name: string }> }).tools;
|
|
1633
|
-
expect(tools).toHaveLength(
|
|
1633
|
+
expect(tools).toHaveLength(15);
|
|
1634
1634
|
expect(tools.map((t) => t.name).sort()).toEqual([
|
|
1635
|
-
"build", "composites", "explain", "import", "lifecycle-diff", "lifecycle-snapshot", "lint",
|
|
1635
|
+
"build", "capabilities", "composites", "explain", "import", "lifecycle-diff", "lifecycle-snapshot", "lint",
|
|
1636
1636
|
"op-approve", "op-list", "op-report", "op-run", "op-status",
|
|
1637
1637
|
"scaffold", "search",
|
|
1638
1638
|
]);
|
|
@@ -1646,7 +1646,7 @@ describe("McpServer", () => {
|
|
|
1646
1646
|
const s = new McpServer([]);
|
|
1647
1647
|
const toolsRes = await s.handleRequest({ jsonrpc: "2.0", id: 1, method: "tools/list" });
|
|
1648
1648
|
const tools = (toolsRes.result as { tools: Array<{ name: string }> }).tools;
|
|
1649
|
-
expect(tools).toHaveLength(
|
|
1649
|
+
expect(tools).toHaveLength(15);
|
|
1650
1650
|
});
|
|
1651
1651
|
});
|
|
1652
1652
|
});
|
package/src/cli/mcp/server.ts
CHANGED
|
@@ -7,6 +7,7 @@ import { explainTool, handleExplain } from "./tools/explain";
|
|
|
7
7
|
import { scaffoldTool, createScaffoldHandler } from "./tools/scaffold";
|
|
8
8
|
import { searchTool, createSearchHandler } from "./tools/search";
|
|
9
9
|
import { compositesTool, createCompositesHandler } from "./tools/composites";
|
|
10
|
+
import { capabilitiesTool, createCapabilitiesHandler } from "./tools/capabilities";
|
|
10
11
|
import type { LexiconPlugin } from "../../lexicon";
|
|
11
12
|
import type { McpClientInfo, McpRequest, McpResponse, McpRequestMeta, ToolDefinition, ToolHandler, ResourceDefinition } from "./types";
|
|
12
13
|
import { findWorkspaceRoot } from "../../project-root";
|
|
@@ -126,6 +127,8 @@ export class McpServer {
|
|
|
126
127
|
this.registerTool(scaffoldTool, createScaffoldHandler(plugins ?? []));
|
|
127
128
|
this.registerTool(searchTool, createSearchHandler(plugins ?? []));
|
|
128
129
|
this.registerTool(compositesTool, createCompositesHandler(plugins ?? []));
|
|
130
|
+
// The capability catalog (#4375), the read contract's capabilities document.
|
|
131
|
+
this.registerTool(capabilitiesTool, createCapabilitiesHandler(options.workspace?.cwd ?? process.cwd(), options.workspace?.chantCommand));
|
|
129
132
|
|
|
130
133
|
// Register state tools
|
|
131
134
|
const snapshot = createSnapshotTool(plugins ?? []);
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `capabilities` tool of `chant serve mcp` (#4375): the capability
|
|
3
|
+
* catalog `chant search` answers `cap:` terms with, as the read contract's
|
|
4
|
+
* `capabilities` document (capabilities.schema.json).
|
|
5
|
+
*
|
|
6
|
+
* Like the workspace reads, it runs the command and returns the document it
|
|
7
|
+
* printed, unchanged: `chant workspace search --capabilities --json` inside a
|
|
8
|
+
* declared workspace, where the workspace's chant runs the rules, and `chant
|
|
9
|
+
* search --capabilities --json` anywhere else. It is served everywhere, since
|
|
10
|
+
* a plain project's agent asks the same questions.
|
|
11
|
+
*/
|
|
12
|
+
import { findWorkspaceRoot } from "../../../project-root";
|
|
13
|
+
import type { ToolDefinition, ToolHandler } from "../types";
|
|
14
|
+
import { ownChantCommand, runRead, type ChantCommand } from "../workspace-tools";
|
|
15
|
+
|
|
16
|
+
export const capabilitiesTool: ToolDefinition = {
|
|
17
|
+
name: "capabilities",
|
|
18
|
+
description:
|
|
19
|
+
"The capability catalog for chant search (capabilities.schema.json, #4375): each capability's name, meaning, parameters, the search term that asks for it (cap:<name> for a set, ->[cap=<name>] for an edge), the questions it answers, whether each lexicon can supply it, what it does not evaluate, and its rule. " +
|
|
20
|
+
"Read it once per session, then ask estate questions with those terms, such as cap:internet-reachable(22) or cap:stores-data <-[cap=reaches]<role>. Not the resource-type catalog: that is the search tool. " +
|
|
21
|
+
"Returns the document chant search --capabilities --json prints (chant workspace search --capabilities --json inside a declared workspace), unchanged.",
|
|
22
|
+
inputSchema: { type: "object", properties: {} },
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
/** The argv the tool runs in `cwd`: the workspace form inside a declared workspace. */
|
|
26
|
+
export function capabilitiesArgv(cwd: string): string[] {
|
|
27
|
+
return findWorkspaceRoot(cwd) ? ["workspace", "search", "--capabilities", "--json"] : ["search", "--capabilities", "--json"];
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function createCapabilitiesHandler(cwd: string, command: ChantCommand = ownChantCommand()): ToolHandler {
|
|
31
|
+
return async () => runRead(command, capabilitiesArgv(cwd), cwd);
|
|
32
|
+
}
|
|
@@ -85,7 +85,7 @@ describe("which servers have the workspace tools", () => {
|
|
|
85
85
|
const { tools } = (await rpc(server(cwd), "tools/list")) as { tools: { name: string }[] };
|
|
86
86
|
expect(tools.map((t) => t.name)).toEqual(expect.arrayContaining(names));
|
|
87
87
|
}
|
|
88
|
-
expect(names).toEqual(["workspace-ls", "workspace-status", "workspace-graph", "workspace-changes", "workspace-records", "workspace-points", "workspace-agent", "workspace-work-history", "workspace-runs", "records-new", "records-amend", "records-review", "records-close", "points-answer", "points-retract", "points-withdraw"]);
|
|
88
|
+
expect(names).toEqual(["workspace-ls", "workspace-status", "workspace-graph", "workspace-search", "workspace-changes", "workspace-records", "workspace-points", "workspace-agent", "workspace-work-history", "workspace-runs", "records-new", "records-amend", "records-review", "records-close", "points-answer", "points-retract", "points-withdraw"]);
|
|
89
89
|
});
|
|
90
90
|
|
|
91
91
|
test("a server outside any workspace, or given none, does not", async () => {
|
|
@@ -159,7 +159,27 @@ describe("reads", () => {
|
|
|
159
159
|
expect(flag.content[0].text).toMatch(/at may not start with -/);
|
|
160
160
|
}, 120_000);
|
|
161
161
|
|
|
162
|
+
test("workspace-search returns exactly the document chant workspace search --json prints (#4374)", async () => {
|
|
163
|
+
const s = server();
|
|
164
|
+
const res = await call(s, "workspace-search", { query: "app" });
|
|
165
|
+
expect(res.isError).toBeUndefined();
|
|
166
|
+
expect(res.structuredContent).toEqual(cli(["workspace", "search", "app", "--json"]));
|
|
167
|
+
expect(res.structuredContent).toMatchObject({ contract: 1, query: "app", at: null, source: { kind: "declared" } });
|
|
168
|
+
// A query that opens with an edge term is passed after --, and a refusal is a document.
|
|
169
|
+
const edge = await call(s, "workspace-search", { query: "->kind:Nothing", countBy: "kind" });
|
|
170
|
+
expect(edge.structuredContent).toEqual(cli(["workspace", "search", "--count-by", "kind", "--json", "--", "->kind:Nothing"]));
|
|
171
|
+
const refused = await call(s, "workspace-search", { query: "app", at: "latest" });
|
|
172
|
+
expect(refused.structuredContent).toMatchObject({ error: { code: "revision-unknown", hint: expect.stringContaining("--snapshot latest") } });
|
|
173
|
+
}, 240_000);
|
|
174
|
+
|
|
162
175
|
test("the conformance suite's argv maps to the tool that answers it", () => {
|
|
176
|
+
expect(mcpToolCall(["workspace", "search", "app", "--member", "delivery", "--snapshot", "latest", "--env", "dev", "--json"])).toEqual({
|
|
177
|
+
name: "workspace-search",
|
|
178
|
+
arguments: { query: "app", member: ["delivery"], snapshot: "latest", env: "dev" },
|
|
179
|
+
});
|
|
180
|
+
expect(readArgv("workspace-search", { query: "->kind:X", member: ["delivery"], snapshot: "latest", env: "dev" })).toEqual([
|
|
181
|
+
"workspace", "search", "--member", "delivery", "--env", "dev", "--snapshot", "latest", "--json", "--", "->kind:X",
|
|
182
|
+
]);
|
|
163
183
|
expect(mcpToolCall(["workspace", "records", "--kind", KIND, "--json"])).toEqual({ name: "workspace-records", arguments: { kind: KIND } });
|
|
164
184
|
expect(mcpToolCall(["workspace", "records", "--uncommitted", "--kind", KIND, "--json"])).toEqual({ name: "workspace-records", arguments: { uncommitted: true, kind: KIND } });
|
|
165
185
|
expect(readArgv("workspace-records", { kind: KIND, uncommitted: true })).toEqual(["workspace", "records", "--kind", KIND, "--uncommitted", "--json"]);
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* Served when the server starts at or inside a declared workspace. Each tool
|
|
6
6
|
* is a thin call into the code the CLI runs:
|
|
7
7
|
*
|
|
8
|
-
* - The reads (`workspace-ls`, `workspace-status`, `workspace-graph`,
|
|
8
|
+
* - The reads (`workspace-ls`, `workspace-status`, `workspace-graph`, `workspace-search`,
|
|
9
9
|
* `workspace-changes`, `workspace-records`, `workspace-points`,
|
|
10
10
|
* `workspace-work-history`, `workspace-runs`) run `chant workspace <command>
|
|
11
11
|
* ... --json` with the chant this server runs as, in the server's
|
|
@@ -123,6 +123,27 @@ export const workspaceReadTools: ToolDefinition[] = [
|
|
|
123
123
|
},
|
|
124
124
|
},
|
|
125
125
|
},
|
|
126
|
+
{
|
|
127
|
+
name: "workspace-search",
|
|
128
|
+
description:
|
|
129
|
+
"Answer an estate question over every chant member of the workspace with a few rows instead of the whole graph: chant workspace search <query> --json (search.schema.json, #4374). Returns that document unchanged. Each member is searched under its own chant and alone, ids are <member>/<id> as workspace-graph names the same nodes, and a member that can't be searched is listed with a reason code, never as zero matches. " +
|
|
130
|
+
"The query is space-separated terms that must all match: a bare word (substring of id, kind or attributes), kind:<substr> (a Terraform resource matches its type, kind:aws_instance), attr:<name>=<value>, tag:<key>=<value>, and edge terms ->X (this node references a node matching X) and <-X (referenced by one); ->*X is any depth, ->{2}X or ->{1,3}X a number of hops, ->[via=attr]X and ->[to=attr]X pick the edge by attribute, and !term negates. " +
|
|
131
|
+
"at is the git revision of the source; snapshot (with env) answers from a recorded lifecycle snapshot such as latest. They are two different times, and at refuses a snapshot ref.",
|
|
132
|
+
inputSchema: {
|
|
133
|
+
type: "object",
|
|
134
|
+
properties: {
|
|
135
|
+
query: { type: "string", description: "The search terms. May start with -> or <-." },
|
|
136
|
+
member: { type: "array", items: { type: "string" }, description: "Only these members (--member)." },
|
|
137
|
+
env: { type: "string", description: "The environment a snapshot was recorded for (--env)." },
|
|
138
|
+
snapshot: { type: "string", description: "Answer from this recorded lifecycle snapshot, such as latest (--snapshot). Needs env." },
|
|
139
|
+
countBy: { type: "string", description: "Group the matches: kind, module or attr:<name> (--count-by). The query may then be empty." },
|
|
140
|
+
show: { type: "string", description: "Comma-separated attributes to carry on each row (--show)." },
|
|
141
|
+
explain: { type: "boolean", description: "Add each member's universe and near misses (--explain)." },
|
|
142
|
+
at: atProp,
|
|
143
|
+
},
|
|
144
|
+
required: ["query"],
|
|
145
|
+
},
|
|
146
|
+
},
|
|
126
147
|
{
|
|
127
148
|
name: "workspace-changes",
|
|
128
149
|
description:
|
|
@@ -417,6 +438,30 @@ export function readArgv(tool: string, params: Record<string, unknown>): string[
|
|
|
417
438
|
"--json",
|
|
418
439
|
];
|
|
419
440
|
}
|
|
441
|
+
case "workspace-search": {
|
|
442
|
+
// The query is one argument after `--`, so one that opens with -> stays a query.
|
|
443
|
+
const query = params.query;
|
|
444
|
+
if (typeof query !== "string") throw new ToolInputError("query must be a string");
|
|
445
|
+
const members = params.member === undefined || params.member === null ? [] : Array.isArray(params.member) ? params.member : [params.member];
|
|
446
|
+
const memberArgs = members.flatMap((m, i) => ["--member", str({ [`member[${i}]`]: m }, `member[${i}]`, true)!]);
|
|
447
|
+
const flags = (["env", "snapshot", "show"] as const).flatMap((f) => {
|
|
448
|
+
const v = str(params, f);
|
|
449
|
+
return v !== undefined ? [`--${f}`, v] : [];
|
|
450
|
+
});
|
|
451
|
+
const countBy = str(params, "countBy");
|
|
452
|
+
return [
|
|
453
|
+
"workspace",
|
|
454
|
+
"search",
|
|
455
|
+
...memberArgs,
|
|
456
|
+
...flags,
|
|
457
|
+
...(countBy !== undefined ? ["--count-by", countBy] : []),
|
|
458
|
+
...(bool(params, "explain") ? ["--explain"] : []),
|
|
459
|
+
...atArgs,
|
|
460
|
+
"--json",
|
|
461
|
+
"--",
|
|
462
|
+
query,
|
|
463
|
+
];
|
|
464
|
+
}
|
|
420
465
|
case "workspace-agent": {
|
|
421
466
|
const name = str(params, "name", true)!;
|
|
422
467
|
return ["workspace", "agent", name, "--json"];
|
|
@@ -458,7 +503,7 @@ export function readArgv(tool: string, params: Record<string, unknown>): string[
|
|
|
458
503
|
}
|
|
459
504
|
|
|
460
505
|
/** Run chant and parse the one JSON document it printed. The exit code does not matter: an error document is a document. */
|
|
461
|
-
function runRead(command: ChantCommand, argv: string[], cwd: string): Promise<unknown> {
|
|
506
|
+
export function runRead(command: ChantCommand, argv: string[], cwd: string): Promise<unknown> {
|
|
462
507
|
return new Promise((settle, fail) => {
|
|
463
508
|
const child = spawn(command[0], [...command.slice(1), ...argv], { cwd, env: { ...process.env, NO_COLOR: "1" }, stdio: ["ignore", "pipe", "pipe"] });
|
|
464
509
|
let stdout = "";
|
package/src/cli/parse-args.ts
CHANGED
|
@@ -153,13 +153,36 @@ export function parseArgs(args: string[]): ParsedArgs {
|
|
|
153
153
|
// whose own value legitimately contains `=` (`--param=tier=production`
|
|
154
154
|
// splits to flag `--param`, value `tier=production`, not further split on
|
|
155
155
|
// the second `=`).
|
|
156
|
-
|
|
156
|
+
//
|
|
157
|
+
// Tokens after a bare `--` are positionals (#4369), so they are not split.
|
|
158
|
+
const end = args.indexOf("--");
|
|
159
|
+
args = end < 0 ? splitJoinedFlags(args, BOOLEAN_FLAGS) : [...splitJoinedFlags(args.slice(0, end), BOOLEAN_FLAGS), ...args.slice(end)];
|
|
160
|
+
|
|
161
|
+
// chant #4369 — every token after a bare `--` is a positional, whatever it
|
|
162
|
+
// starts with: `chant search -- "-kind:X"` is the spelling that always works.
|
|
163
|
+
let onlyPositionals = false;
|
|
164
|
+
const positional = (arg: string): void => {
|
|
165
|
+
if (result.command) (result.positionals ??= []).push(arg);
|
|
166
|
+
if (!result.command) {
|
|
167
|
+
result.command = arg;
|
|
168
|
+
} else if (result.path === ".") {
|
|
169
|
+
result.path = arg;
|
|
170
|
+
} else if (!result.extraPositional) {
|
|
171
|
+
result.extraPositional = arg;
|
|
172
|
+
} else {
|
|
173
|
+
result.extraPositional2 = arg;
|
|
174
|
+
}
|
|
175
|
+
};
|
|
157
176
|
|
|
158
177
|
let i = 0;
|
|
159
178
|
while (i < args.length) {
|
|
160
179
|
const arg = args[i];
|
|
161
180
|
|
|
162
|
-
if (
|
|
181
|
+
if (onlyPositionals) {
|
|
182
|
+
positional(arg);
|
|
183
|
+
} else if (arg === "--") {
|
|
184
|
+
onlyPositionals = true;
|
|
185
|
+
} else if (arg === "--help" || arg === "-h") {
|
|
163
186
|
result.help = true;
|
|
164
187
|
} else if (arg === "--version" || arg === "-V") {
|
|
165
188
|
result.version = true;
|
|
@@ -339,6 +362,10 @@ export function parseArgs(args: string[]): ParsedArgs {
|
|
|
339
362
|
result.explain = true;
|
|
340
363
|
} else if (arg === "--show") {
|
|
341
364
|
result.show = args[++i];
|
|
365
|
+
} else if (arg === "--count-by") {
|
|
366
|
+
// `chant search --count-by kind|module|attr:<name>` (#4360)
|
|
367
|
+
result.countBy = args[++i];
|
|
368
|
+
if (!result.countBy || result.countBy.startsWith("-")) throw new Error("--count-by needs a grouping: --count-by kind, --count-by module or --count-by attr:<name>");
|
|
342
369
|
} else if (arg === "--up") {
|
|
343
370
|
result.up = true;
|
|
344
371
|
} else if (arg === "--down") {
|
|
@@ -508,6 +535,16 @@ export function parseArgs(args: string[]): ParsedArgs {
|
|
|
508
535
|
result.deep = true;
|
|
509
536
|
} else if (arg === "--at") {
|
|
510
537
|
result.at = args[++i];
|
|
538
|
+
} else if (arg === "--capability-view") {
|
|
539
|
+
// `chant graph --format ir --capability-view` and `chant workspace graph --capability-view` (#4378)
|
|
540
|
+
result.capabilityView = true;
|
|
541
|
+
} else if (arg === "--capabilities") {
|
|
542
|
+
// `chant search --capabilities` and `chant workspace search --capabilities` (#4375)
|
|
543
|
+
result.capabilities = true;
|
|
544
|
+
} else if (arg === "--snapshot") {
|
|
545
|
+
// `chant search` and `chant workspace search --snapshot <ref>` (#4374)
|
|
546
|
+
result.snapshot = args[++i];
|
|
547
|
+
if (!result.snapshot || result.snapshot.startsWith("-")) throw new Error("--snapshot needs a snapshot ref: --snapshot latest");
|
|
511
548
|
} else if (arg === "--kind") {
|
|
512
549
|
// `chant workspace records|graph|check --kind <kind file>` (#2546, #2549)
|
|
513
550
|
result.kind = args[++i];
|
|
@@ -972,17 +1009,19 @@ export function parseArgs(args: string[]): ParsedArgs {
|
|
|
972
1009
|
// every command, not scoped per-command, so "the command's known
|
|
973
1010
|
// flags" isn't something this loop can name in isolation.
|
|
974
1011
|
throw new Error(`Unknown flag: ${arg}\nRun "chant --help" to see supported flags.`);
|
|
975
|
-
} else if (
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
}
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
1012
|
+
} else if (/^-[A-Za-z]/.test(arg)) {
|
|
1013
|
+
// chant #4369 — an unrecognised short flag (`-x`) is refused like an
|
|
1014
|
+
// unrecognised `--flag` (#1127). It used to be dropped without a word,
|
|
1015
|
+
// the same silent misparse #1127 closed for long flags. A lexicon's own
|
|
1016
|
+
// mounted command still gets its short flags: `main` tries the plugin
|
|
1017
|
+
// commands before rethrowing a parse error.
|
|
1018
|
+
throw new Error(`Unknown flag: ${arg}\nRun "chant --help" to see supported flags.`);
|
|
1019
|
+
} else {
|
|
1020
|
+
// Anything else is a positional, including a token that starts with `-`
|
|
1021
|
+
// but is not flag-shaped (#4369): `->kind:Bucket` is a search query that
|
|
1022
|
+
// opens with an outgoing edge term, and dropping it made `chant search`
|
|
1023
|
+
// answer for "." instead. A bare `-` is a positional too.
|
|
1024
|
+
positional(arg);
|
|
986
1025
|
}
|
|
987
1026
|
|
|
988
1027
|
i++;
|
package/src/cli/registry.ts
CHANGED
|
@@ -246,6 +246,8 @@ export interface ParsedArgs {
|
|
|
246
246
|
show?: string;
|
|
247
247
|
/** `chant search --explain` — append a footer: universe count + why non-matches were excluded (#1139). */
|
|
248
248
|
explain?: boolean;
|
|
249
|
+
/** `chant search --count-by kind|module|attr:<name>`: print one count per group instead of one row per match (#4360). */
|
|
250
|
+
countBy?: string;
|
|
249
251
|
/** `chant graph --lens blast:<node> --up` — include upstream producers */
|
|
250
252
|
up?: boolean;
|
|
251
253
|
/** `chant graph --lens blast:<node> --down` — include downstream dependents */
|
|
@@ -446,6 +448,26 @@ export interface ParsedArgs {
|
|
|
446
448
|
* should win.
|
|
447
449
|
*/
|
|
448
450
|
at?: string;
|
|
451
|
+
/**
|
|
452
|
+
* `chant search "<q>" --snapshot <ref> --env <name>` and `chant workspace
|
|
453
|
+
* search "<q>" --snapshot <ref>` (#4374): the recorded snapshot to answer
|
|
454
|
+
* from. Plain search also takes it as `--at <ref>`, for compatibility;
|
|
455
|
+
* everywhere else in the read contract `--at` is a git revision.
|
|
456
|
+
*/
|
|
457
|
+
snapshot?: string;
|
|
458
|
+
/**
|
|
459
|
+
* `chant search --capabilities` and `chant workspace search --capabilities`
|
|
460
|
+
* (#4375): print the capability catalog, the `capabilities` document of the
|
|
461
|
+
* read contract, instead of answering a query.
|
|
462
|
+
*/
|
|
463
|
+
capabilities?: boolean;
|
|
464
|
+
/**
|
|
465
|
+
* `chant graph --format ir --capability-view` (#4378): add the lexicon-neutral
|
|
466
|
+
* view capability rules read, mapped by this chant's lexicons, to the IR as
|
|
467
|
+
* `capabilityView`. `chant workspace graph --capability-view` asks each
|
|
468
|
+
* member's chant for it and carries it on the member.
|
|
469
|
+
*/
|
|
470
|
+
capabilityView?: boolean;
|
|
449
471
|
/** `chant workspace records --kind <path>` (#2546): the record kind file to read records through. */
|
|
450
472
|
kind?: string;
|
|
451
473
|
/** Every `--kind` given, in order: `chant workspace graph --intent` reads each (#2651). */
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The ports `chant components pr-env` runs ./pr-env-run.ts with: the records
|
|
3
|
+
* on chant/lifecycle (`_pr-envs/records.jsonl`), the gate ledger there, and
|
|
4
|
+
* the note and status on the forge. They sit apart from ./pr-env-run.ts
|
|
5
|
+
* because the gate port loads ./pr-run.ts and ../op/gate.ts, which bring
|
|
6
|
+
* Ajv, js-yaml and the component driver. A product with its own ports
|
|
7
|
+
* imports `@intentius/chant/components/pr-env-run` and never loads these.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import type { PrForge } from "../pr-forge";
|
|
11
|
+
import type { PrEnvPorts } from "./pr-env-run";
|
|
12
|
+
import {
|
|
13
|
+
PR_ENV_LEDGER_DIR,
|
|
14
|
+
PR_ENV_LEDGER_FILE,
|
|
15
|
+
parsePrEnvRecords,
|
|
16
|
+
prEnvNoteMarker,
|
|
17
|
+
prEnvStatusContext,
|
|
18
|
+
prEnvStatusDescription,
|
|
19
|
+
prEnvStatusState,
|
|
20
|
+
renderPrEnvNote,
|
|
21
|
+
type PrEnvReport,
|
|
22
|
+
} from "./pr-environment";
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* The note and the `chant/pr-env` status on the forge. A refusal from the
|
|
26
|
+
* forge warns and never fails the run.
|
|
27
|
+
*/
|
|
28
|
+
export function forgePrEnvPublisher(forge: PrForge, warn: (message: string) => void = () => {}): (report: PrEnvReport) => Promise<void> {
|
|
29
|
+
return async (report) => {
|
|
30
|
+
const { forgePrincipalOf } = await import("../pr-forge");
|
|
31
|
+
const note = renderPrEnvNote(report, forgePrincipalOf(forge.kind, forge.host, "<your-login>"));
|
|
32
|
+
if (report.head) {
|
|
33
|
+
try {
|
|
34
|
+
await forge.setStatus(report.head, { context: prEnvStatusContext(report.member), state: prEnvStatusState(report), description: prEnvStatusDescription(report), ...(forge.runUrl ? { url: forge.runUrl } : {}) });
|
|
35
|
+
} catch (err) {
|
|
36
|
+
warn(`could not set the ${prEnvStatusContext(report.member)} status: ${messageOf(err)}`);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
try {
|
|
40
|
+
await forge.upsertNote(report.pr, prEnvNoteMarker(report.env, report.member), note);
|
|
41
|
+
} catch (err) {
|
|
42
|
+
warn(`could not post the note on #${report.pr}: ${messageOf(err)}`);
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* The ports over chant/lifecycle in `cwd` (default the working directory):
|
|
49
|
+
* the records at `_pr-envs/records.jsonl`, appended and pushed, and the gate
|
|
50
|
+
* ledger read without writing a pending fact. With `forge`, the note and
|
|
51
|
+
* status are posted and the sweep asks it whether a pull request is open.
|
|
52
|
+
*/
|
|
53
|
+
export function lifecyclePrEnvPorts(opts: { cwd?: string; forge?: PrForge; log?: (line: string) => void; warn?: (message: string) => void } = {}): PrEnvPorts {
|
|
54
|
+
const at = opts.cwd ? { cwd: opts.cwd } : undefined;
|
|
55
|
+
const warn = opts.warn ?? (() => {});
|
|
56
|
+
const forge = opts.forge;
|
|
57
|
+
return {
|
|
58
|
+
async readRecords() {
|
|
59
|
+
const { fetchLifecycle, readBlobFromPath } = await import("../lifecycle/git");
|
|
60
|
+
await fetchLifecycle(at).catch(() => false);
|
|
61
|
+
return parsePrEnvRecords(await readBlobFromPath(PR_ENV_LEDGER_DIR, PR_ENV_LEDGER_FILE, at));
|
|
62
|
+
},
|
|
63
|
+
async appendRecord(record) {
|
|
64
|
+
const { appendLedgerLine, fetchLifecycle, pushLifecycleReport } = await import("../lifecycle/git");
|
|
65
|
+
try {
|
|
66
|
+
await fetchLifecycle(at).catch(() => false);
|
|
67
|
+
await appendLedgerLine(PR_ENV_LEDGER_DIR, PR_ENV_LEDGER_FILE, JSON.stringify(record), `Pull request environment ${record.env} ${record.kind} (${record.result})`, at);
|
|
68
|
+
const pushed = await pushLifecycleReport(at);
|
|
69
|
+
if (pushed.pushWarning) warn(pushed.pushWarning);
|
|
70
|
+
} catch (err) {
|
|
71
|
+
warn(`could not record ${record.env}'s ${record.kind} on chant/lifecycle: ${messageOf(err)}`);
|
|
72
|
+
}
|
|
73
|
+
},
|
|
74
|
+
async decideGate(input) {
|
|
75
|
+
const { approvalOf, decidePrGate, readOnlyLedger } = await import("./pr-run");
|
|
76
|
+
const { gitGateLedgerPort } = await import("../op/gate");
|
|
77
|
+
const approval = approvalOf(await decidePrGate(readOnlyLedger(gitGateLedgerPort(at)), input));
|
|
78
|
+
return { status: approval.status, ...(approval.approvedBy ? { approvedBy: approval.approvedBy } : {}) };
|
|
79
|
+
},
|
|
80
|
+
...(forge ? { publish: forgePrEnvPublisher(forge, warn) } : {}),
|
|
81
|
+
...(forge?.pullRequestState ? { pullRequestState: (pr: number) => forge.pullRequestState!(pr) } : {}),
|
|
82
|
+
...(opts.log ? { log: opts.log } : {}),
|
|
83
|
+
warn,
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function messageOf(err: unknown): string {
|
|
88
|
+
return err instanceof Error ? err.message : String(err);
|
|
89
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The pull request environment runs bundle alone (#4330 follow-up): a
|
|
3
|
+
* product that brings its own adapter and ports imports
|
|
4
|
+
* `@intentius/chant/components/pr-env-run` into one bundled file, without the
|
|
5
|
+
* gate, Ajv, js-yaml or the config loader. The ports chant uses live in
|
|
6
|
+
* ./pr-env-ports.ts. This bundles the module and reads esbuild's list of what
|
|
7
|
+
* went in.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { join } from "node:path";
|
|
11
|
+
import { build } from "esbuild";
|
|
12
|
+
import { describe, expect, test } from "vitest";
|
|
13
|
+
import { REPO } from "../workspace/__fixtures__/contract-repo";
|
|
14
|
+
|
|
15
|
+
describe("the pr-env-run bundle", () => {
|
|
16
|
+
test("holds the runs and the records, and no gate, schema checks, YAML or config loader", async () => {
|
|
17
|
+
const result = await build({
|
|
18
|
+
entryPoints: [join(REPO, "packages", "core", "src", "components", "pr-env-run.ts")],
|
|
19
|
+
bundle: true,
|
|
20
|
+
platform: "node",
|
|
21
|
+
format: "esm",
|
|
22
|
+
write: false,
|
|
23
|
+
metafile: true,
|
|
24
|
+
outdir: "out",
|
|
25
|
+
logLevel: "silent",
|
|
26
|
+
});
|
|
27
|
+
const inputs = Object.keys(result.metafile.inputs).map((i) => i.replace(/^.*?packages\//, "packages/")).sort();
|
|
28
|
+
expect(inputs).toEqual([
|
|
29
|
+
"packages/core/src/change-set.ts",
|
|
30
|
+
"packages/core/src/ci-handover.ts",
|
|
31
|
+
"packages/core/src/ci-jobs.ts",
|
|
32
|
+
"packages/core/src/components/changed-files.ts",
|
|
33
|
+
"packages/core/src/components/pr-env-run.ts",
|
|
34
|
+
"packages/core/src/components/pr-environment.ts",
|
|
35
|
+
"packages/core/src/components/pr-pipeline.ts",
|
|
36
|
+
"packages/core/src/config-environments.ts",
|
|
37
|
+
"packages/core/src/content-digest.ts",
|
|
38
|
+
"packages/core/src/declarable.ts",
|
|
39
|
+
"packages/core/src/effect-receipt.ts",
|
|
40
|
+
"packages/core/src/gated-waves.ts",
|
|
41
|
+
"packages/core/src/intrinsic.ts",
|
|
42
|
+
"packages/core/src/lifecycle/plan-digest.ts",
|
|
43
|
+
"packages/core/src/lifecycle/teardown.ts",
|
|
44
|
+
"packages/core/src/live-endpoint.ts",
|
|
45
|
+
"packages/core/src/observation.ts",
|
|
46
|
+
"packages/core/src/op/activity-runtime.ts",
|
|
47
|
+
"packages/core/src/op/emulator-lifecycle.ts",
|
|
48
|
+
"packages/core/src/plan-summary.ts",
|
|
49
|
+
"packages/core/src/pr-loop.ts",
|
|
50
|
+
]);
|
|
51
|
+
expect(inputs.some((i) => /node_modules\/(ajv|js-yaml)\//.test(i) || i.endsWith("/op/gate.ts") || i.endsWith("/src/config.ts") || i.endsWith("/ts-loader.ts"))).toBe(false);
|
|
52
|
+
expect(result.outputFiles.reduce((n, f) => n + f.contents.byteLength, 0)).toBeLessThan(40 * 1024);
|
|
53
|
+
});
|
|
54
|
+
});
|