cherry-works 0.4.0 → 0.5.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/README.md +18 -15
- package/dist/cw.js +1 -3
- package/dist/main.js +1209 -1233
- package/dist/portal/main.js +3 -3
- package/dist/portal/styles.css +1 -1
- package/package.json +1 -3
package/dist/main.js
CHANGED
|
@@ -6,7 +6,7 @@ import { pathToFileURL as pathToFileURL2 } from "url";
|
|
|
6
6
|
// package.json
|
|
7
7
|
var package_default = {
|
|
8
8
|
name: "cherry-works",
|
|
9
|
-
version: "0.
|
|
9
|
+
version: "0.5.0",
|
|
10
10
|
description: "The charter engine: resolves a charter and compiles it into what a coding agent reads.",
|
|
11
11
|
keywords: [
|
|
12
12
|
"claude-code",
|
|
@@ -48,8 +48,6 @@ var package_default = {
|
|
|
48
48
|
test: 'pnpm run lint:deps && pnpm run build && node --import tsx --test "test/**/*.test.ts"',
|
|
49
49
|
cw: "node --import tsx main.ts",
|
|
50
50
|
"lint:deps": "depcruise src main.ts",
|
|
51
|
-
"specs:link": "node --import tsx scripts/link-specs.ts",
|
|
52
|
-
"specs:check": "node --import tsx scripts/link-specs.ts --check",
|
|
53
51
|
release: "node --import tsx scripts/release.ts",
|
|
54
52
|
prepublishOnly: `node -e "console.error('Publish with \\"pnpm release <version>\\", which checks the package first.'); process.exit(1)"`
|
|
55
53
|
},
|
|
@@ -474,21 +472,21 @@ var DoctorCommand = class {
|
|
|
474
472
|
|
|
475
473
|
// src/driver/cli/commands/EditCommand.ts
|
|
476
474
|
var OPTIONS2 = {
|
|
477
|
-
|
|
475
|
+
id: {
|
|
478
476
|
type: "string",
|
|
479
|
-
describe: 'What the primitive is named by across the whole charter: "
|
|
477
|
+
describe: 'What the primitive is named by across the whole charter, its id: "no-any"'
|
|
480
478
|
}
|
|
481
479
|
};
|
|
482
480
|
var EditCommand = class {
|
|
483
|
-
name = "edit <
|
|
481
|
+
name = "edit <id>";
|
|
484
482
|
summary = "Open the file of one primitive in your editor";
|
|
485
483
|
options = OPTIONS2;
|
|
486
|
-
async run({ cwd, charterAuthoringApp: charterAuthoringApp2 }, {
|
|
487
|
-
const primitive = (await charterAuthoringApp2.open(
|
|
484
|
+
async run({ cwd, charterAuthoringApp: charterAuthoringApp2 }, { id }) {
|
|
485
|
+
const primitive = (await charterAuthoringApp2.open(id ?? "")).data;
|
|
488
486
|
if (primitive.layerName === BUILTIN_LAYER)
|
|
489
487
|
throw new DomainFault(
|
|
490
|
-
`${primitive.
|
|
491
|
-
`To differ from it, run "cw add" for a primitive of your own under an
|
|
488
|
+
`${primitive.id} is built into cw, and there is no file of it in this repository to edit.`,
|
|
489
|
+
`To differ from it, run "cw add" for a primitive of your own under an id of its own.`
|
|
492
490
|
);
|
|
493
491
|
const problem = await editInEditor(cwd, primitive.file);
|
|
494
492
|
if (problem !== void 0) return { code: EXIT_FAILURE, problem };
|
|
@@ -501,17 +499,17 @@ var EditCommand = class {
|
|
|
501
499
|
|
|
502
500
|
// src/driver/cli/commands/ExplainCommand.ts
|
|
503
501
|
var OPTIONS3 = {
|
|
504
|
-
|
|
502
|
+
id: {
|
|
505
503
|
type: "string",
|
|
506
|
-
describe: 'What the primitive is named by across the whole charter: "
|
|
504
|
+
describe: 'What the primitive is named by across the whole charter, its id: "no-any"'
|
|
507
505
|
}
|
|
508
506
|
};
|
|
509
507
|
var ExplainCommand = class {
|
|
510
|
-
name = "explain <
|
|
511
|
-
summary = "Say which file declares an
|
|
508
|
+
name = "explain <id>";
|
|
509
|
+
summary = "Say which file declares an id, and which layer it came from";
|
|
512
510
|
options = OPTIONS3;
|
|
513
|
-
async run({ charterAuthoringApp: charterAuthoringApp2 }, {
|
|
514
|
-
const explanationOutcomeDTO2 = await charterAuthoringApp2.explain(
|
|
511
|
+
async run({ charterAuthoringApp: charterAuthoringApp2 }, { id }) {
|
|
512
|
+
const explanationOutcomeDTO2 = await charterAuthoringApp2.explain(id ?? "");
|
|
515
513
|
if (explanationOutcomeDTO2.type === "FaultsByFile") {
|
|
516
514
|
const noOfFiles = Object.keys(explanationOutcomeDTO2.data.files).length;
|
|
517
515
|
return {
|
|
@@ -527,16 +525,16 @@ Run "cw doctor" to see what is wrong with them.
|
|
|
527
525
|
return {
|
|
528
526
|
code: EXIT_OK,
|
|
529
527
|
result: [
|
|
530
|
-
`${primitive.data.
|
|
528
|
+
`${primitive.data.id} ${primitive.data.description}`,
|
|
531
529
|
` ${primitive.data.file}`,
|
|
532
530
|
` ${layer(primitive.data.layerName)}`,
|
|
533
531
|
` comes up when ${activatesWhen}`,
|
|
534
532
|
...declaredHeaders.map(([name, value]) => ` declares ${name}: ${typeof value === "string" ? value : value.join(", ")}`),
|
|
535
|
-
...useMixins.map(({ data }) => ` uses mixin ${data.
|
|
536
|
-
...rationale !== void 0 ? [` rationale ${rationale.data.
|
|
537
|
-
...hosts.map(({ data }) => ` mixin of ${data.
|
|
538
|
-
...citers.map(({ data }) => ` rationale of ${data.
|
|
539
|
-
...mentioners.map(({ data }) => ` mentioned in ${data.
|
|
533
|
+
...useMixins.map(({ data }) => ` uses mixin ${data.id}`),
|
|
534
|
+
...rationale !== void 0 ? [` rationale ${rationale.data.id}`] : citedRationale !== void 0 ? [` rationale ${String(citedRationale)} (does not resolve)`] : [],
|
|
535
|
+
...hosts.map(({ data }) => ` mixin of ${data.id}`),
|
|
536
|
+
...citers.map(({ data }) => ` rationale of ${data.id}`),
|
|
537
|
+
...mentioners.map(({ data }) => ` mentioned in ${data.id}`),
|
|
540
538
|
...Object.entries(testCasesByFile.data).flatMap(
|
|
541
539
|
([file, situations]) => situations.map((situation) => ` pinned down by ${file}: ${situation}`)
|
|
542
540
|
),
|
|
@@ -677,7 +675,7 @@ var LISTING = {
|
|
|
677
675
|
kind: { type: "string", describe: "Only the primitives of this kind" },
|
|
678
676
|
min: {
|
|
679
677
|
type: "boolean",
|
|
680
|
-
describe: "What an agent surveys by:
|
|
678
|
+
describe: "What an agent surveys by: id and description, nothing else",
|
|
681
679
|
default: false
|
|
682
680
|
}
|
|
683
681
|
};
|
|
@@ -711,8 +709,8 @@ Run "cw doctor" to see what is wrong with them.
|
|
|
711
709
|
};
|
|
712
710
|
}
|
|
713
711
|
};
|
|
714
|
-
function surveyed({ data: {
|
|
715
|
-
return `${
|
|
712
|
+
function surveyed({ data: { id, description } }) {
|
|
713
|
+
return `${id} ${description}`;
|
|
716
714
|
}
|
|
717
715
|
function inFull(entry) {
|
|
718
716
|
const { file, globs } = entry.data;
|
|
@@ -722,9 +720,9 @@ function inFull(entry) {
|
|
|
722
720
|
// src/driver/cli/commands/McpAuthCommand.ts
|
|
723
721
|
import prompts3 from "prompts";
|
|
724
722
|
var OPTIONS6 = {
|
|
725
|
-
|
|
723
|
+
id: {
|
|
726
724
|
type: "string",
|
|
727
|
-
describe: 'One mcp whose address to sign in to again, whatever you were signed in as: "
|
|
725
|
+
describe: 'One mcp whose address to sign in to again, whatever you were signed in as: "github/billing"'
|
|
728
726
|
},
|
|
729
727
|
status: {
|
|
730
728
|
type: "boolean",
|
|
@@ -733,28 +731,28 @@ var OPTIONS6 = {
|
|
|
733
731
|
}
|
|
734
732
|
};
|
|
735
733
|
var McpAuthCommand = class {
|
|
736
|
-
name = "mcp auth [
|
|
734
|
+
name = "mcp auth [id]";
|
|
737
735
|
summary = "Sign in, as yourself, to every place the charter reaches";
|
|
738
736
|
options = OPTIONS6;
|
|
739
|
-
async run({ mcpConnectingApp: mcpConnectingApp2 }, {
|
|
737
|
+
async run({ mcpConnectingApp: mcpConnectingApp2 }, { id, status }) {
|
|
740
738
|
const signInStatuses = (await mcpConnectingApp2.signInStatus()).map((one) => one.data);
|
|
741
739
|
if (status)
|
|
742
740
|
return {
|
|
743
741
|
code: EXIT_OK,
|
|
744
742
|
result: signInStatuses.length === 0 ? "No place the last build listed takes a sign-in.\n" : signInStatuses.map(
|
|
745
|
-
({ address,
|
|
743
|
+
({ address, ids, signedIn, method }) => [address, ` ${ids.join(", ")}`, ` ${signedIn ? `signed in by ${method}` : "not signed in"}`, ""].join("\n")
|
|
746
744
|
).join("")
|
|
747
745
|
};
|
|
748
746
|
let unsignedStatuses;
|
|
749
|
-
if (
|
|
747
|
+
if (id === void 0) unsignedStatuses = signInStatuses.filter((one) => !one.signedIn);
|
|
750
748
|
else {
|
|
751
|
-
const
|
|
752
|
-
if (
|
|
749
|
+
const signInStatus = signInStatuses.find((one) => one.ids.includes(id));
|
|
750
|
+
if (signInStatus === void 0)
|
|
753
751
|
throw new DomainFault(
|
|
754
|
-
`No place the last build listed is declared by ${
|
|
752
|
+
`No place the last build listed is declared by ${id}.`,
|
|
755
753
|
'Run "cw mcp auth --status" to see every mcp, or "cw build" if the charter changed.'
|
|
756
754
|
);
|
|
757
|
-
unsignedStatuses = [
|
|
755
|
+
unsignedStatuses = [signInStatus];
|
|
758
756
|
}
|
|
759
757
|
if (unsignedStatuses.length === 0)
|
|
760
758
|
return {
|
|
@@ -772,9 +770,9 @@ var McpAuthCommand = class {
|
|
|
772
770
|
""
|
|
773
771
|
].join("\n")
|
|
774
772
|
};
|
|
775
|
-
for (const { address,
|
|
773
|
+
for (const { address, ids, auth: auth2 } of unsignedStatuses) {
|
|
776
774
|
process.stderr.write(`${address}
|
|
777
|
-
${
|
|
775
|
+
${ids.join(", ")}
|
|
778
776
|
`);
|
|
779
777
|
const { method } = auth2.length > 1 ? await prompts3({
|
|
780
778
|
type: "select",
|
|
@@ -858,7 +856,7 @@ import { serveStatic } from "@hono/node-server/serve-static";
|
|
|
858
856
|
// src/driver/portal/routes.ts
|
|
859
857
|
import { createRoute, OpenAPIHono } from "@hono/zod-openapi";
|
|
860
858
|
import { HTTPException } from "hono/http-exception";
|
|
861
|
-
import { z as
|
|
859
|
+
import { z as z8 } from "zod";
|
|
862
860
|
|
|
863
861
|
// src/hexagon/port/driver/dtos/dto.ts
|
|
864
862
|
import { z } from "zod";
|
|
@@ -894,7 +892,7 @@ var PrimitiveKindsSchema = dto("PrimitiveKinds", z2.record(z2.string(), z2.strin
|
|
|
894
892
|
var CatalogueEntrySchema = dto(
|
|
895
893
|
"CatalogueEntry",
|
|
896
894
|
z2.object({
|
|
897
|
-
|
|
895
|
+
id: z2.string(),
|
|
898
896
|
kind: z2.string(),
|
|
899
897
|
description: z2.string(),
|
|
900
898
|
file: z2.string(),
|
|
@@ -910,7 +908,7 @@ var CatalogueSchema = dto(
|
|
|
910
908
|
var PrimitiveSchema = dto(
|
|
911
909
|
"Primitive",
|
|
912
910
|
z2.object({
|
|
913
|
-
|
|
911
|
+
id: z2.string(),
|
|
914
912
|
kind: z2.string(),
|
|
915
913
|
description: z2.string(),
|
|
916
914
|
file: z2.string(),
|
|
@@ -972,7 +970,7 @@ var TestCaseSchema = dto(
|
|
|
972
970
|
z2.object({
|
|
973
971
|
situation: z2.string(),
|
|
974
972
|
expectation: z2.string(),
|
|
975
|
-
|
|
973
|
+
id: z2.string().optional()
|
|
976
974
|
})
|
|
977
975
|
);
|
|
978
976
|
var TestSuiteSchema = dto(
|
|
@@ -995,7 +993,7 @@ var SignInStatusSchema = dto(
|
|
|
995
993
|
"SignInStatus",
|
|
996
994
|
z2.object({
|
|
997
995
|
address: z2.string(),
|
|
998
|
-
|
|
996
|
+
ids: StringsSchema,
|
|
999
997
|
auth: z2.array(z2.enum(["oauth", "token"])).readonly(),
|
|
1000
998
|
signedIn: z2.boolean(),
|
|
1001
999
|
method: z2.enum(["oauth", "token"]).optional()
|
|
@@ -1078,10 +1076,10 @@ var ExplanationOutcomeSchema = dto(
|
|
|
1078
1076
|
var OutcomeDTOs = byType(DoctorOutcomeSchema, ExplanationOutcomeSchema);
|
|
1079
1077
|
|
|
1080
1078
|
// src/hexagon/domain/models/test/TestSuite.ts
|
|
1081
|
-
import { z as
|
|
1079
|
+
import { z as z7 } from "zod";
|
|
1082
1080
|
|
|
1083
1081
|
// src/hexagon/domain/models/test/TestCase.ts
|
|
1084
|
-
import { z as
|
|
1082
|
+
import { z as z6 } from "zod";
|
|
1085
1083
|
|
|
1086
1084
|
// src/hexagon/domain/models/charter/primitive/SensorPrimitive.ts
|
|
1087
1085
|
import { z as z5 } from "zod";
|
|
@@ -1100,7 +1098,7 @@ function shortenStringsOf(strings) {
|
|
|
1100
1098
|
const shortStrings = {};
|
|
1101
1099
|
const takenShortStrings = /* @__PURE__ */ new Set();
|
|
1102
1100
|
for (const string of [...new Set(strings)].sort()) {
|
|
1103
|
-
const firstSegment = string.
|
|
1101
|
+
const firstSegment = string.split("/")[0];
|
|
1104
1102
|
let hash = 2166136261;
|
|
1105
1103
|
for (const char of string) hash = Math.imul(hash ^ char.charCodeAt(0), 16777619);
|
|
1106
1104
|
const hashedString = `${firstSegment}_${(hash >>> 0).toString(16).padStart(8, "0").slice(0, 4)}`;
|
|
@@ -1118,11 +1116,14 @@ function contentHashOf(content) {
|
|
|
1118
1116
|
}
|
|
1119
1117
|
|
|
1120
1118
|
// src/hexagon/domain/models/charter/primitive/BasePrimitive.ts
|
|
1119
|
+
var ID_SOURCE = /[a-z0-9](?:[a-z0-9-]*[a-z0-9])?(?:\/[a-z0-9](?:[a-z0-9-]*[a-z0-9])?)*/.source;
|
|
1120
|
+
var REFERENCE = new RegExp(`\\[\\[(${ID_SOURCE})\\]\\]`, "g");
|
|
1121
|
+
var TOOL_REFERENCE = new RegExp(`^\\[\\[(${ID_SOURCE})\\]\\](?::([^:\\s]+))?$`);
|
|
1121
1122
|
var DELIMITER = "---";
|
|
1122
1123
|
var GoodLineSchema = z4.string().regex(/\S/);
|
|
1123
1124
|
var GoodArraySchema = z4.array(GoodLineSchema).min(1).readonly();
|
|
1124
1125
|
var CommonHeadersSchema = z4.object({
|
|
1125
|
-
/** Slugs joined by `/`, so
|
|
1126
|
+
/** Slugs joined by `/`, so ids can be grouped by team or domain
|
|
1126
1127
|
* (FR-141). The `/` is written `-` in the file's name and nothing else:
|
|
1127
1128
|
* nothing is read from what a file is called. At most 40 characters, so every name a host
|
|
1128
1129
|
* is given for it — an mcp's tools among them, `mcp__cw__mcp-<id>__<tool>` —
|
|
@@ -1135,7 +1136,8 @@ var CommonHeadersSchema = z4.object({
|
|
|
1135
1136
|
* are what decide when its body is loaded; a mixin may name them to say how
|
|
1136
1137
|
* far what it lends reaches (FR-006). */
|
|
1137
1138
|
globs: z4.array(GoodLineSchema).readonly().optional(),
|
|
1138
|
-
/**
|
|
1139
|
+
/** The id of the corpus this primitive cites as its reasoning (FR-005,
|
|
1140
|
+
* FR-172). */
|
|
1139
1141
|
rationale: GoodLineSchema.optional(),
|
|
1140
1142
|
mixins: z4.array(GoodLineSchema).readonly().optional()
|
|
1141
1143
|
});
|
|
@@ -1177,7 +1179,7 @@ var BasePrimitive = class _BasePrimitive {
|
|
|
1177
1179
|
return `${this.charterFolder}/${this.primitiveFolder}/${this.index}`;
|
|
1178
1180
|
}
|
|
1179
1181
|
/** Whether a word someone searched for is anywhere a reader would look for
|
|
1180
|
-
* it: the
|
|
1182
|
+
* it: the id, the kind, what it is for, when its kind comes up, its
|
|
1181
1183
|
* file, or any header it declared — ignoring case, since whoever typed it
|
|
1182
1184
|
* did not know how it was written (FR-114). */
|
|
1183
1185
|
search(word) {
|
|
@@ -1185,7 +1187,7 @@ var BasePrimitive = class _BasePrimitive {
|
|
|
1185
1187
|
(value) => value === void 0 ? [] : Array.isArray(value) ? value : [String(value)]
|
|
1186
1188
|
);
|
|
1187
1189
|
const loweredWord = word.toLowerCase();
|
|
1188
|
-
return [this.
|
|
1190
|
+
return [this.headers.id, this.kind, this.activatesWhen, this.file, ...headerValues].some((one) => one.toLowerCase().includes(loweredWord));
|
|
1189
1191
|
}
|
|
1190
1192
|
/** Its folder, from its charter folder: its kind, then each segment of its
|
|
1191
1193
|
* id (FR-141). */
|
|
@@ -1208,16 +1210,12 @@ var BasePrimitive = class _BasePrimitive {
|
|
|
1208
1210
|
get hash() {
|
|
1209
1211
|
return contentHashOf(this.toMarkdown());
|
|
1210
1212
|
}
|
|
1211
|
-
/**
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
* once, so nothing that names a host's file or a place's tools spells it
|
|
1218
|
-
* again. */
|
|
1219
|
-
get normIdentity() {
|
|
1220
|
-
return this.identity.replace(/[:/]/g, "-");
|
|
1213
|
+
/** Its id as every name given to a host writes it, `/` as `-`:
|
|
1214
|
+
* `mfbs-no-any` for `mfbs/no-any`, so a skill is invoked as `/mfbs-no-any`
|
|
1215
|
+
* (FR-141, FR-171). Made here, once, so nothing that names a host's file
|
|
1216
|
+
* spells it again. */
|
|
1217
|
+
get normId() {
|
|
1218
|
+
return this.headers.id.replace(/\//g, "-");
|
|
1221
1219
|
}
|
|
1222
1220
|
/**
|
|
1223
1221
|
* This primitive as the file it is authored in: its headers between the
|
|
@@ -1328,664 +1326,139 @@ var SensorPrimitive = class _SensorPrimitive extends BasePrimitive {
|
|
|
1328
1326
|
}
|
|
1329
1327
|
};
|
|
1330
1328
|
|
|
1331
|
-
// src/hexagon/domain/models/
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
})
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1329
|
+
// src/hexagon/domain/models/test/TestCase.ts
|
|
1330
|
+
var ExpectedIdSchema = CommonHeadersSchema.shape.id;
|
|
1331
|
+
var TestCaseSchema2 = z6.union([
|
|
1332
|
+
z6.strictObject({
|
|
1333
|
+
do: z6.strictObject({ touchFile: z6.string().min(1) }),
|
|
1334
|
+
expect: z6.union([
|
|
1335
|
+
z6.strictObject({ activate: ExpectedIdSchema }),
|
|
1336
|
+
z6.strictObject({ allow: z6.boolean() })
|
|
1337
|
+
])
|
|
1338
|
+
}),
|
|
1339
|
+
z6.strictObject({
|
|
1340
|
+
when: z6.enum(SIGNALS),
|
|
1341
|
+
expect: z6.strictObject({ run: ExpectedIdSchema })
|
|
1342
|
+
})
|
|
1343
|
+
]);
|
|
1344
|
+
var TestCase = class {
|
|
1345
|
+
constructor(written) {
|
|
1346
|
+
this.written = written;
|
|
1347
|
+
}
|
|
1348
|
+
written;
|
|
1349
|
+
/** The situation it puts, said as a line, and what it expects of it: what a
|
|
1350
|
+
* run reports it under, and what an explanation names it by (FR-048,
|
|
1351
|
+
* FR-014). */
|
|
1352
|
+
describe() {
|
|
1353
|
+
return `${this.situation} ${this.expectation}`;
|
|
1354
|
+
}
|
|
1355
|
+
/** The situation it puts: the file it touches, or the event it raises. */
|
|
1356
|
+
get situation() {
|
|
1357
|
+
return "when" in this.written ? `firing event ${this.written.when}` : `touching ${this.written.do.touchFile}`;
|
|
1358
|
+
}
|
|
1359
|
+
/** What it expects the charter to make of its situation. */
|
|
1360
|
+
get expectation() {
|
|
1361
|
+
const written = this.written;
|
|
1362
|
+
if ("when" in written) return `runs ${written.expect.run}`;
|
|
1363
|
+
if ("activate" in written.expect) return `activates ${written.expect.activate}`;
|
|
1364
|
+
return `is ${written.expect.allow ? "allowed" : "denied"}`;
|
|
1365
|
+
}
|
|
1366
|
+
/**
|
|
1367
|
+
* Which primitive this case expects to be activated: the sensor a raised event
|
|
1368
|
+
* is expected to run, or the guide a touched file is expected to bring up
|
|
1369
|
+
* (FR-014).
|
|
1370
|
+
*
|
|
1371
|
+
* Nothing for the third shape. That one expects a touched file to be allowed
|
|
1372
|
+
* or denied, which every posture in the charter answers between them, so it
|
|
1373
|
+
* activates no one primitive by name.
|
|
1374
|
+
*/
|
|
1375
|
+
get activatedId() {
|
|
1376
|
+
if ("when" in this.written) return this.written.expect.run;
|
|
1377
|
+
if ("activate" in this.written.expect) return this.written.expect.activate;
|
|
1378
|
+
return void 0;
|
|
1363
1379
|
}
|
|
1364
1380
|
};
|
|
1365
1381
|
|
|
1366
|
-
// src/hexagon/domain/models/
|
|
1367
|
-
var
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
* or not (FR-004). */
|
|
1377
|
-
static schema = CorpusHeadersSchema;
|
|
1378
|
-
/** When a reader of this charter is to open this kind at all, said
|
|
1379
|
-
* where the kind's contract is: the neutral surface lists one
|
|
1380
|
-
* line per kind and none of them is written down twice (FR-002). */
|
|
1381
|
-
static activatesWhen = "a primitive's `rationale` cites it \u2014 the reasoning, read when someone asks why";
|
|
1382
|
-
/** A corpus as an author writes one: what a refused header is fixed with. */
|
|
1383
|
-
static sample = {
|
|
1384
|
-
id: "type-safety",
|
|
1385
|
-
description: "Why the charter insists on types."
|
|
1386
|
-
};
|
|
1387
|
-
/** One corpus, or every fault its headers have (FR-004). */
|
|
1388
|
-
static of(record, body, assetFiles = [], primitiveLayer) {
|
|
1389
|
-
return new _CorpusPrimitive(headersOf(_CorpusPrimitive, CorpusHeadersSchema, record), body, assetFiles, primitiveLayer);
|
|
1382
|
+
// src/hexagon/domain/models/test/TestSuite.ts
|
|
1383
|
+
var TestSuiteSchema2 = z7.strictObject({
|
|
1384
|
+
description: z7.string().min(1).optional(),
|
|
1385
|
+
cases: z7.array(TestCaseSchema2.transform((written) => new TestCase(written))).min(1)
|
|
1386
|
+
});
|
|
1387
|
+
var TestSuite = class {
|
|
1388
|
+
constructor(description, cases, body) {
|
|
1389
|
+
this.description = description;
|
|
1390
|
+
this.cases = cases;
|
|
1391
|
+
this.body = body;
|
|
1390
1392
|
}
|
|
1393
|
+
description;
|
|
1394
|
+
cases;
|
|
1395
|
+
body;
|
|
1391
1396
|
};
|
|
1397
|
+
var SAMPLE_CASES = [
|
|
1398
|
+
{ do: { touchFile: "src/one.ts" }, expect: { activate: "no-any" } },
|
|
1399
|
+
{ do: { touchFile: ".env" }, expect: { allow: false } },
|
|
1400
|
+
{ when: "PreToolUse", expect: { run: "no-secrets" } }
|
|
1401
|
+
];
|
|
1402
|
+
var SAMPLE = JSON.stringify({ cases: SAMPLE_CASES }, void 0, 1).replace(/\n\s*/g, " ");
|
|
1403
|
+
var DEFAULT_TEST_SUITE_BODY = `${JSON.stringify({ description: "What these cases pin down.", cases: [SAMPLE_CASES[0]] }, null, 2)}
|
|
1404
|
+
`;
|
|
1405
|
+
function testSuiteOf(text) {
|
|
1406
|
+
let written;
|
|
1407
|
+
try {
|
|
1408
|
+
written = JSON.parse(text);
|
|
1409
|
+
} catch {
|
|
1410
|
+
throw new TestSuiteFault(
|
|
1411
|
+
`This file is not JSON, so nothing can read the cases it was meant to hold.`,
|
|
1412
|
+
`Write it as ${SAMPLE}`
|
|
1413
|
+
);
|
|
1414
|
+
}
|
|
1415
|
+
const read = TestSuiteSchema2.safeParse(written);
|
|
1416
|
+
if (!read.success) throw new TestSuiteFault(`This file is not written as a suite of cases.`, `Write it as ${SAMPLE}`);
|
|
1417
|
+
return new TestSuite(read.data.description, read.data.cases, text);
|
|
1418
|
+
}
|
|
1392
1419
|
|
|
1393
|
-
// src/hexagon/domain/models/
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
};
|
|
1417
|
-
/** One guide, or every fault its headers have (FR-004). */
|
|
1418
|
-
static of(record, body, assetFiles = [], primitiveLayer) {
|
|
1419
|
-
return new _GuidePrimitive(headersOf(_GuidePrimitive, GuideHeadersSchema, record), body, assetFiles, primitiveLayer);
|
|
1420
|
+
// src/hexagon/domain/models/test/TestRoot.ts
|
|
1421
|
+
function testSuiteNameOf(path) {
|
|
1422
|
+
return path.slice(`${TEST_DIRECTORY}/`.length);
|
|
1423
|
+
}
|
|
1424
|
+
var TestRoot = class {
|
|
1425
|
+
constructor(suitesByFile, faultsByFiles) {
|
|
1426
|
+
this.suitesByFile = suitesByFile;
|
|
1427
|
+
this.faultsByFiles = faultsByFiles;
|
|
1428
|
+
}
|
|
1429
|
+
suitesByFile;
|
|
1430
|
+
faultsByFiles;
|
|
1431
|
+
/** Whether a test file is under this name, whether it reads or not. A name
|
|
1432
|
+
* reaching outside `.cw/test/` is no name of one. */
|
|
1433
|
+
hasTestSuiteNamed(name) {
|
|
1434
|
+
const path = `${TEST_DIRECTORY}/${name}`;
|
|
1435
|
+
return path in this.suitesByFile || path in this.faultsByFiles.files;
|
|
1436
|
+
}
|
|
1437
|
+
/** What a new test file is named: `untitled-<n>.json` for the first `n` no
|
|
1438
|
+
* test file has (FR-091). */
|
|
1439
|
+
get untitledName() {
|
|
1440
|
+
let untitledNumber = 1;
|
|
1441
|
+
while (this.hasTestSuiteNamed(`untitled-${untitledNumber}.json`)) untitledNumber++;
|
|
1442
|
+
return `untitled-${untitledNumber}.json`;
|
|
1420
1443
|
}
|
|
1421
1444
|
};
|
|
1445
|
+
function testRootOf(testSuiteFiles) {
|
|
1446
|
+
const suitesByFile = {};
|
|
1447
|
+
const faultsByFiles = {};
|
|
1448
|
+
for (const { path, contents } of [...testSuiteFiles].sort((one, another) => one.path.localeCompare(another.path))) {
|
|
1449
|
+
try {
|
|
1450
|
+
suitesByFile[path] = testSuiteOf(contents);
|
|
1451
|
+
} catch (raised) {
|
|
1452
|
+
if (!(raised instanceof DomainFault)) throw raised;
|
|
1453
|
+
faultsByFiles[path] = [raised];
|
|
1454
|
+
}
|
|
1455
|
+
}
|
|
1456
|
+
return new TestRoot(suitesByFile, new FaultsByFile(faultsByFiles));
|
|
1457
|
+
}
|
|
1422
1458
|
|
|
1423
|
-
// src/hexagon/
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
// src/hexagon/domain/models/McpAuthMethod.ts
|
|
1427
|
-
var MCP_AUTH_METHODS = ["oauth", "token"];
|
|
1428
|
-
|
|
1429
|
-
// src/hexagon/domain/models/charter/primitive/McpPrimitive.ts
|
|
1430
|
-
function isSecureEndpoint(endpoint) {
|
|
1431
|
-
if (!URL.canParse(endpoint)) return false;
|
|
1432
|
-
const endpointURL = new URL(endpoint);
|
|
1433
|
-
return endpointURL.protocol === "https:" || endpointURL.protocol === "http:" && ["127.0.0.1", "localhost"].includes(endpointURL.hostname);
|
|
1434
|
-
}
|
|
1435
|
-
var MCP_MENTION = /\bmcp:([a-z0-9](?:[a-z0-9-]*[a-z0-9])?(?:\/[a-z0-9](?:[a-z0-9-]*[a-z0-9])?)*)/g;
|
|
1436
|
-
var MCP_TOOL_REFERENCE = /^(mcp:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?(?:\/[a-z0-9](?:[a-z0-9-]*[a-z0-9])?)*)(?::([^:\s]+))?$/;
|
|
1437
|
-
var McpHeadersSchema = CommonHeadersSchema.extend({
|
|
1438
|
-
/** Reached over HTTP. Plain `http` only to this machine, for a server under
|
|
1439
|
-
* test: anything else would send a developer's credential in the clear. */
|
|
1440
|
-
endpoint: GoodLineSchema.refine(isSecureEndpoint, {
|
|
1441
|
-
message: `"endpoint" is an https:// URL, or http:// to 127.0.0.1 or localhost for a server under test.`
|
|
1442
|
-
}).optional(),
|
|
1443
|
-
/** Started as a local process, spoken to over its standard input and output. */
|
|
1444
|
-
command: GoodLineSchema.optional(),
|
|
1445
|
-
args: z6.array(GoodLineSchema).readonly().optional(),
|
|
1446
|
-
auth: z6.array(z6.enum(MCP_AUTH_METHODS)).min(1).readonly().optional(),
|
|
1447
|
-
/** The environment variable a command reads its token from. */
|
|
1448
|
-
tokenEnv: z6.string().regex(/^[A-Za-z_][A-Za-z0-9_]*$/).optional(),
|
|
1449
|
-
/** The place inside that server: a repository, a database, a channel. */
|
|
1450
|
-
path: GoodLineSchema.optional(),
|
|
1451
|
-
/** The server's tools the agent may use there, by the server's own names. */
|
|
1452
|
-
tools: GoodArraySchema
|
|
1453
|
-
}).superRefine((headers, context) => {
|
|
1454
|
-
const addFault = (message) => context.addIssue({ code: "custom", message });
|
|
1455
|
-
const { endpoint, command, args, auth: auth2, tokenEnv } = headers;
|
|
1456
|
-
if (endpoint !== void 0 && command !== void 0)
|
|
1457
|
-
return addFault(`This declares both "endpoint" and "command"; an mcp is one server, reached one way.`);
|
|
1458
|
-
if (endpoint === void 0 && command === void 0)
|
|
1459
|
-
return addFault(`This declares neither "endpoint" nor "command", so there is no server to reach.`);
|
|
1460
|
-
if (endpoint !== void 0) {
|
|
1461
|
-
if (auth2 === void 0) addFault(`An endpoint says how a developer signs in to it: "auth" of oauth, token or both.`);
|
|
1462
|
-
if (args !== void 0) addFault(`"args" are a command's; an endpoint takes none.`);
|
|
1463
|
-
if (tokenEnv !== void 0) addFault(`"tokenEnv" is where a command reads its token; an endpoint takes none.`);
|
|
1464
|
-
return;
|
|
1465
|
-
}
|
|
1466
|
-
const takesToken = auth2 !== void 0;
|
|
1467
|
-
if (takesToken && (auth2.length !== 1 || auth2[0] !== "token"))
|
|
1468
|
-
addFault(`A command signs in by token alone: "auth: [token]", or no "auth" for one that takes none.`);
|
|
1469
|
-
if (takesToken && tokenEnv === void 0) addFault(`A command taking a token names where it reads it: "tokenEnv".`);
|
|
1470
|
-
if (!takesToken && tokenEnv !== void 0) addFault(`"tokenEnv" is read only by a command taking a token: add "auth: [token]", or drop it.`);
|
|
1471
|
-
});
|
|
1472
|
-
var McpPrimitive = class _McpPrimitive extends BasePrimitive {
|
|
1473
|
-
static kind = "mcp";
|
|
1474
|
-
kind = _McpPrimitive.kind;
|
|
1475
|
-
/** What this kind requires beyond the common headers. `endpoint` or
|
|
1476
|
-
* `command` is required too, but which depends on the shape, so the schema's
|
|
1477
|
-
* refinement says it rather than this (FR-004, FR-039). */
|
|
1478
|
-
static requires = { tools: "list" };
|
|
1479
|
-
/** The schema its headers are read by: what `headersOf` refuses a file
|
|
1480
|
-
* against, and what says the shape of every header this kind takes, required
|
|
1481
|
-
* or not (FR-004). */
|
|
1482
|
-
static schema = McpHeadersSchema;
|
|
1483
|
-
/** When a reader of this charter is to open this kind at all, said
|
|
1484
|
-
* where the kind's contract is: the neutral surface lists one
|
|
1485
|
-
* line per kind and none of them is written down twice (FR-002). */
|
|
1486
|
-
static activatesWhen = "a primitive's body names it as `mcp:<id>` \u2014 a place outside the repository, reached through `cw mcp serve`";
|
|
1487
|
-
/** An mcp reached at an endpoint, as an author writes one: what a refused
|
|
1488
|
-
* header is fixed with. */
|
|
1489
|
-
static sample = {
|
|
1490
|
-
id: "mfbs/billing",
|
|
1491
|
-
description: "Billing service code and pull requests.",
|
|
1492
|
-
endpoint: "https://api.githubcopilot.com/mcp/",
|
|
1493
|
-
path: "moneyforward/billing-service",
|
|
1494
|
-
auth: ["oauth", "token"],
|
|
1495
|
-
tools: ["get_file_contents", "search_code", "list_pull_requests"]
|
|
1496
|
-
};
|
|
1497
|
-
/** The same place started as a local process: what a refused file declaring
|
|
1498
|
-
* `command` is held up against, since that is the shape it was nearer. */
|
|
1499
|
-
static commandSample = {
|
|
1500
|
-
id: "mfbs/billing",
|
|
1501
|
-
description: "Billing service code and pull requests.",
|
|
1502
|
-
command: "npx",
|
|
1503
|
-
args: ["-y", "@modelcontextprotocol/server-github"],
|
|
1504
|
-
tokenEnv: "GITHUB_PERSONAL_ACCESS_TOKEN",
|
|
1505
|
-
auth: ["token"],
|
|
1506
|
-
path: "moneyforward/billing-service",
|
|
1507
|
-
tools: ["get_file_contents", "search_code", "list_pull_requests"]
|
|
1508
|
-
};
|
|
1509
|
-
/** What a developer signs in to, and what places are told apart by beside
|
|
1510
|
-
* their `path`: the endpoint, or the command followed by each of its
|
|
1511
|
-
* arguments. The refinement has already held that one of the two is here
|
|
1512
|
-
* (FR-142). */
|
|
1513
|
-
get address() {
|
|
1514
|
-
const { endpoint, command, args = [] } = this.headers;
|
|
1515
|
-
return endpoint ?? [command, ...args].join(" ");
|
|
1516
|
-
}
|
|
1517
|
-
/** One mcp, or every fault its headers have, shown the sample of the shape
|
|
1518
|
-
* it declared (FR-004, FR-142). */
|
|
1519
|
-
static of(record, body, assetFiles = [], primitiveLayer) {
|
|
1520
|
-
const nearerSample = record.command === void 0 ? _McpPrimitive.sample : _McpPrimitive.commandSample;
|
|
1521
|
-
return new _McpPrimitive(headersOf({ kind: _McpPrimitive.kind, sample: nearerSample }, McpHeadersSchema, record), body, assetFiles, primitiveLayer);
|
|
1522
|
-
}
|
|
1523
|
-
};
|
|
1524
|
-
|
|
1525
|
-
// src/hexagon/domain/models/charter/primitive/MixinPrimitive.ts
|
|
1526
|
-
import { z as z7 } from "zod";
|
|
1527
|
-
var MixinHeadersSchema = CommonHeadersSchema.extend({
|
|
1528
|
-
/** Never: reading refuses a mixin that declares one. */
|
|
1529
|
-
mixins: z7.undefined().optional()
|
|
1530
|
-
});
|
|
1531
|
-
var MixinPrimitive = class _MixinPrimitive extends BasePrimitive {
|
|
1532
|
-
static kind = "mixin";
|
|
1533
|
-
kind = _MixinPrimitive.kind;
|
|
1534
|
-
/** What this kind requires beyond the common headers: nothing. Declared all
|
|
1535
|
-
* the same, since what a file is scaffolded with is read off this (FR-039). */
|
|
1536
|
-
static requires = {};
|
|
1537
|
-
/** The schema its headers are read by: what `headersOf` refuses a file
|
|
1538
|
-
* against, and what says the shape of every header this kind takes, required
|
|
1539
|
-
* or not (FR-004). */
|
|
1540
|
-
static schema = MixinHeadersSchema;
|
|
1541
|
-
/** When a reader of this charter is to open this kind at all, said
|
|
1542
|
-
* where the kind's contract is: the neutral surface lists one
|
|
1543
|
-
* line per kind and none of them is written down twice (FR-002). */
|
|
1544
|
-
static activatesWhen = "never on its own: its body is lent to the primitives that pull it in";
|
|
1545
|
-
/** A mixin as an author writes one: what a refused header is fixed with. */
|
|
1546
|
-
static sample = {
|
|
1547
|
-
id: "typescript-scope",
|
|
1548
|
-
description: "What counts as TypeScript application code.",
|
|
1549
|
-
globs: ["src/**/*.ts"]
|
|
1550
|
-
};
|
|
1551
|
-
/** One mixin, or every fault its headers have (FR-004, FR-006). */
|
|
1552
|
-
static of(record, body, assetFiles = [], primitiveLayer) {
|
|
1553
|
-
return new _MixinPrimitive(headersOf(_MixinPrimitive, MixinHeadersSchema, record), body, assetFiles, primitiveLayer);
|
|
1554
|
-
}
|
|
1555
|
-
};
|
|
1556
|
-
|
|
1557
|
-
// src/hexagon/domain/models/charter/primitive/PlaybookPrimitive.ts
|
|
1558
|
-
var PlaybookHeadersSchema = CommonHeadersSchema.extend({
|
|
1559
|
-
triggers: GoodArraySchema
|
|
1560
|
-
});
|
|
1561
|
-
var PlaybookPrimitive = class _PlaybookPrimitive extends BasePrimitive {
|
|
1562
|
-
static kind = "playbook";
|
|
1563
|
-
kind = _PlaybookPrimitive.kind;
|
|
1564
|
-
/** What this kind requires beyond the common headers, declared where its
|
|
1565
|
-
* contract is: it is read both where a file is checked and where one is
|
|
1566
|
-
* scaffolded (FR-004, FR-039). */
|
|
1567
|
-
static requires = { triggers: "list" };
|
|
1568
|
-
/** The schema its headers are read by: what `headersOf` refuses a file
|
|
1569
|
-
* against, and what says the shape of every header this kind takes, required
|
|
1570
|
-
* or not (FR-004). */
|
|
1571
|
-
static schema = PlaybookHeadersSchema;
|
|
1572
|
-
/** When a reader of this charter is to open this kind at all, said
|
|
1573
|
-
* where the kind's contract is: the neutral surface lists one
|
|
1574
|
-
* line per kind and none of them is written down twice (FR-002). */
|
|
1575
|
-
static activatesWhen = "one of its `triggers` matches; the body is a sequence of skills";
|
|
1576
|
-
/** A playbook as an author writes one: what a refused header is fixed with. */
|
|
1577
|
-
static sample = {
|
|
1578
|
-
id: "ship-a-feature",
|
|
1579
|
-
description: "Take a finished branch from review to release.",
|
|
1580
|
-
triggers: ["ship this", "release this feature"]
|
|
1581
|
-
};
|
|
1582
|
-
/** What this is found by: what it is for, and the requests its author said
|
|
1583
|
-
* bring it up. */
|
|
1584
|
-
description() {
|
|
1585
|
-
return `${this.headers.description} Use when: ${this.headers.triggers.join("; ")}.`;
|
|
1586
|
-
}
|
|
1587
|
-
/** One playbook, or every fault its headers have (FR-004). */
|
|
1588
|
-
static of(record, body, assetFiles = [], primitiveLayer) {
|
|
1589
|
-
return new _PlaybookPrimitive(headersOf(_PlaybookPrimitive, PlaybookHeadersSchema, record), body, assetFiles, primitiveLayer);
|
|
1590
|
-
}
|
|
1591
|
-
};
|
|
1592
|
-
|
|
1593
|
-
// src/hexagon/domain/models/charter/primitive/PosturePrimitive.ts
|
|
1594
|
-
var PostureHeadersSchema = CommonHeadersSchema.extend({
|
|
1595
|
-
allow: GoodArraySchema,
|
|
1596
|
-
deny: GoodArraySchema
|
|
1597
|
-
});
|
|
1598
|
-
var PosturePrimitive = class _PosturePrimitive extends BasePrimitive {
|
|
1599
|
-
static kind = "posture";
|
|
1600
|
-
kind = _PosturePrimitive.kind;
|
|
1601
|
-
/** What this kind requires beyond the common headers, declared where its
|
|
1602
|
-
* contract is: it is read both where a file is checked and where one is
|
|
1603
|
-
* scaffolded (FR-004, FR-039). */
|
|
1604
|
-
static requires = { allow: "list", deny: "list" };
|
|
1605
|
-
/** The schema its headers are read by: what `headersOf` refuses a file
|
|
1606
|
-
* against, and what says the shape of every header this kind takes, required
|
|
1607
|
-
* or not (FR-004). */
|
|
1608
|
-
static schema = PostureHeadersSchema;
|
|
1609
|
-
/** When a reader of this charter is to open this kind at all, said
|
|
1610
|
-
* where the kind's contract is: the neutral surface lists one
|
|
1611
|
-
* line per kind and none of them is written down twice (FR-002). */
|
|
1612
|
-
static activatesWhen = "always, wherever the host can be told what to `allow` and what to `deny`";
|
|
1613
|
-
/** A posture as an author writes one: what a refused header is fixed with. */
|
|
1614
|
-
static sample = {
|
|
1615
|
-
id: "no-force-push",
|
|
1616
|
-
description: "Never rewrite history someone else may have pulled.",
|
|
1617
|
-
allow: ["Bash(git push:*)"],
|
|
1618
|
-
deny: ["Bash(git push --force:*)"]
|
|
1619
|
-
};
|
|
1620
|
-
/** One posture, or every fault its headers have (FR-004). */
|
|
1621
|
-
static of(record, body, assetFiles = [], primitiveLayer) {
|
|
1622
|
-
return new _PosturePrimitive(headersOf(_PosturePrimitive, PostureHeadersSchema, record), body, assetFiles, primitiveLayer);
|
|
1623
|
-
}
|
|
1624
|
-
};
|
|
1625
|
-
|
|
1626
|
-
// src/hexagon/domain/models/charter/primitive/SkillPrimitive.ts
|
|
1627
|
-
var SkillHeadersSchema = CommonHeadersSchema.extend({
|
|
1628
|
-
triggers: GoodArraySchema
|
|
1629
|
-
});
|
|
1630
|
-
var SkillPrimitive = class _SkillPrimitive extends BasePrimitive {
|
|
1631
|
-
static kind = "skill";
|
|
1632
|
-
kind = _SkillPrimitive.kind;
|
|
1633
|
-
/** What this kind requires beyond the common headers, declared where its
|
|
1634
|
-
* contract is: it is read both where a file is checked and where one is
|
|
1635
|
-
* scaffolded (FR-004, FR-039). */
|
|
1636
|
-
static requires = { triggers: "list" };
|
|
1637
|
-
/** The schema its headers are read by: what `headersOf` refuses a file
|
|
1638
|
-
* against, and what says the shape of every header this kind takes, required
|
|
1639
|
-
* or not (FR-004). */
|
|
1640
|
-
static schema = SkillHeadersSchema;
|
|
1641
|
-
/** When a reader of this charter is to open this kind at all, said
|
|
1642
|
-
* where the kind's contract is: the neutral surface lists one
|
|
1643
|
-
* line per kind and none of them is written down twice (FR-002). */
|
|
1644
|
-
static activatesWhen = "one of its `triggers` matches what is being asked";
|
|
1645
|
-
/** A skill as an author writes one: what a refused header is fixed with. */
|
|
1646
|
-
static sample = {
|
|
1647
|
-
id: "refactoring",
|
|
1648
|
-
description: "Change how code is structured without changing what it does.",
|
|
1649
|
-
triggers: ["refactor this", "clean this up"]
|
|
1650
|
-
};
|
|
1651
|
-
/** What this is found by: what it is for, and the requests its author said
|
|
1652
|
-
* bring it up. */
|
|
1653
|
-
description() {
|
|
1654
|
-
return `${this.headers.description} Use when: ${this.headers.triggers.join("; ")}.`;
|
|
1655
|
-
}
|
|
1656
|
-
/** One skill, or every fault its headers have (FR-004). */
|
|
1657
|
-
static of(record, body, assetFiles = [], primitiveLayer) {
|
|
1658
|
-
return new _SkillPrimitive(headersOf(_SkillPrimitive, SkillHeadersSchema, record), body, assetFiles, primitiveLayer);
|
|
1659
|
-
}
|
|
1660
|
-
};
|
|
1661
|
-
|
|
1662
|
-
// src/hexagon/domain/models/charter/primitive/ScriptPrimitive.ts
|
|
1663
|
-
import { z as z8 } from "zod";
|
|
1664
|
-
var SCRIPT_MENTION = /\bscript:([a-z0-9](?:[a-z0-9-]*[a-z0-9])?(?:\/[a-z0-9](?:[a-z0-9-]*[a-z0-9])?)*)/g;
|
|
1665
|
-
var ScriptHeadersSchema = CommonHeadersSchema.extend({
|
|
1666
|
-
/** The one asset that runs, from the script's `index.md`: `./` and a path
|
|
1667
|
-
* inside its folder, no segment of which climbs out again. Letters, digits,
|
|
1668
|
-
* `.`, `_` and `-` only: it is written into commands a shell reads
|
|
1669
|
-
* (FR-169). */
|
|
1670
|
-
executionPath: z8.string().regex(/^\.\/[A-Za-z0-9._-]+(\/[A-Za-z0-9._-]+)*$/).refine((executionPath) => !executionPath.split("/").slice(1).some((segment) => segment === "." || segment === ".."), {
|
|
1671
|
-
message: `"executionPath" stays inside the script's own folder: no segment of it is "." or "..".`
|
|
1672
|
-
})
|
|
1673
|
-
});
|
|
1674
|
-
var ScriptPrimitive = class _ScriptPrimitive extends BasePrimitive {
|
|
1675
|
-
static kind = "script";
|
|
1676
|
-
kind = _ScriptPrimitive.kind;
|
|
1677
|
-
/** What this kind requires beyond the common headers, declared where its
|
|
1678
|
-
* contract is (FR-004, FR-039). */
|
|
1679
|
-
static requires = { executionPath: "line" };
|
|
1680
|
-
/** The schema its headers are read by (FR-004). */
|
|
1681
|
-
static schema = ScriptHeadersSchema;
|
|
1682
|
-
/** When a reader of this charter is to open this kind at all (FR-002). */
|
|
1683
|
-
static activatesWhen = "a primitive names it as `script:<id>`, or a sensor runs it \u2014 its assets, copied to `.cw/out/script/<id>/`, of which its `executionPath` names the one to run";
|
|
1684
|
-
/** A script as an author writes one: what a refused header is fixed with. */
|
|
1685
|
-
static sample = {
|
|
1686
|
-
id: "check-changelog",
|
|
1687
|
-
description: "Fail when CHANGELOG.md holds no entry for the version being released.",
|
|
1688
|
-
executionPath: "./run.sh"
|
|
1689
|
-
};
|
|
1690
|
-
/** One script, or every fault its headers have (FR-004). */
|
|
1691
|
-
static of(record, body, assetFiles = [], primitiveLayer) {
|
|
1692
|
-
return new _ScriptPrimitive(headersOf(_ScriptPrimitive, ScriptHeadersSchema, record), body, assetFiles, primitiveLayer);
|
|
1693
|
-
}
|
|
1694
|
-
/** The asset that runs, as a path inside its folder: `run.sh` of
|
|
1695
|
-
* `./run.sh`. */
|
|
1696
|
-
get executionPathInFolder() {
|
|
1697
|
-
return this.headers.executionPath.slice("./".length);
|
|
1698
|
-
}
|
|
1699
|
-
/** Whether an asset of it is the one its `executionPath` names: the one
|
|
1700
|
-
* run by its path, and no other (FR-160). */
|
|
1701
|
-
isExecutionFile(file) {
|
|
1702
|
-
return file === this.executionPathInFolder;
|
|
1703
|
-
}
|
|
1704
|
-
};
|
|
1705
|
-
|
|
1706
|
-
// src/hexagon/domain/models/charter/primitive/TemplatePrimitive.ts
|
|
1707
|
-
var TEMPLATE_MENTION = /\btemplate:([a-z0-9](?:[a-z0-9-]*[a-z0-9])?(?:\/[a-z0-9](?:[a-z0-9-]*[a-z0-9])?)*)/g;
|
|
1708
|
-
var TemplateHeadersSchema = CommonHeadersSchema.extend({});
|
|
1709
|
-
var TemplatePrimitive = class _TemplatePrimitive extends BasePrimitive {
|
|
1710
|
-
static kind = "template";
|
|
1711
|
-
kind = _TemplatePrimitive.kind;
|
|
1712
|
-
/** What this kind requires beyond the common headers: nothing. Declared all
|
|
1713
|
-
* the same, since what a file is scaffolded with is read off this (FR-039). */
|
|
1714
|
-
static requires = {};
|
|
1715
|
-
/** The schema its headers are read by (FR-004). */
|
|
1716
|
-
static schema = TemplateHeadersSchema;
|
|
1717
|
-
/** When a reader of this charter is to open this kind at all (FR-002). */
|
|
1718
|
-
static activatesWhen = "a primitive names it as `template:<id>` \u2014 a file the agent fills or copies, written as its body";
|
|
1719
|
-
/** A template as an author writes one: what a refused header is fixed with. */
|
|
1720
|
-
static sample = {
|
|
1721
|
-
id: "release-note",
|
|
1722
|
-
description: "The shape every release note follows."
|
|
1723
|
-
};
|
|
1724
|
-
/** One template, or every fault its headers have (FR-004). */
|
|
1725
|
-
static of(record, body, assetFiles = [], primitiveLayer) {
|
|
1726
|
-
return new _TemplatePrimitive(headersOf(_TemplatePrimitive, TemplateHeadersSchema, record), body, assetFiles, primitiveLayer);
|
|
1727
|
-
}
|
|
1728
|
-
};
|
|
1729
|
-
|
|
1730
|
-
// src/hexagon/domain/models/charter/primitive/Primitive.ts
|
|
1731
|
-
var PRIMITIVE_CLASSES = [
|
|
1732
|
-
GuidePrimitive,
|
|
1733
|
-
SensorPrimitive,
|
|
1734
|
-
SkillPrimitive,
|
|
1735
|
-
PlaybookPrimitive,
|
|
1736
|
-
AgentPrimitive,
|
|
1737
|
-
PosturePrimitive,
|
|
1738
|
-
CorpusPrimitive,
|
|
1739
|
-
MixinPrimitive,
|
|
1740
|
-
McpPrimitive,
|
|
1741
|
-
ScriptPrimitive,
|
|
1742
|
-
TemplatePrimitive
|
|
1743
|
-
];
|
|
1744
|
-
var KINDS = Object.freeze(PRIMITIVE_CLASSES.map((one) => one.kind));
|
|
1745
|
-
var PrimitiveIdentitySchema = z9.string().regex(/^[a-z0-9-]+:[a-z0-9-]+(\/[a-z0-9-]+)*$/).transform((identity) => identity);
|
|
1746
|
-
function identityOf(kindAndId) {
|
|
1747
|
-
if (typeof kindAndId !== "string") return `${kindAndId.kind}:${kindAndId.id}`;
|
|
1748
|
-
const parsed = PrimitiveIdentitySchema.safeParse(kindAndId);
|
|
1749
|
-
if (!parsed.success)
|
|
1750
|
-
throw new CharterPrimitiveFault(
|
|
1751
|
-
`"${kindAndId}" is not an identity: one is a kind and an id with a colon between them.`,
|
|
1752
|
-
'Write it as <kind>:<id>, as in "guide:no-any".'
|
|
1753
|
-
);
|
|
1754
|
-
return parsed.data;
|
|
1755
|
-
}
|
|
1756
|
-
function isKind(value) {
|
|
1757
|
-
return typeof value === "string" && KINDS.includes(value);
|
|
1758
|
-
}
|
|
1759
|
-
var CLASS_OF = new Map(
|
|
1760
|
-
PRIMITIVE_CLASSES.map((one) => [one.kind, one])
|
|
1761
|
-
);
|
|
1762
|
-
var PrimitiveHeader = class {
|
|
1763
|
-
constructor(field, shape, required, allowedValues) {
|
|
1764
|
-
this.field = field;
|
|
1765
|
-
this.shape = shape;
|
|
1766
|
-
this.required = required;
|
|
1767
|
-
this.allowedValues = allowedValues;
|
|
1768
|
-
}
|
|
1769
|
-
field;
|
|
1770
|
-
shape;
|
|
1771
|
-
required;
|
|
1772
|
-
allowedValues;
|
|
1773
|
-
};
|
|
1774
|
-
var PrimitiveRequirements = class {
|
|
1775
|
-
constructor(headers, sample) {
|
|
1776
|
-
this.headers = headers;
|
|
1777
|
-
this.sample = sample;
|
|
1778
|
-
}
|
|
1779
|
-
headers;
|
|
1780
|
-
sample;
|
|
1781
|
-
};
|
|
1782
|
-
function primitiveHeadersOf(kind) {
|
|
1783
|
-
const kindClass = CLASS_OF.get(kind);
|
|
1784
|
-
const required = { ...BasePrimitive.requires, ...kindClass.requires };
|
|
1785
|
-
return Object.entries(kindClass.schema.shape).filter(([field]) => field !== "id").map(([field, type]) => {
|
|
1786
|
-
let heldType = type;
|
|
1787
|
-
while (!(heldType instanceof z9.ZodArray) && typeof heldType?.unwrap === "function")
|
|
1788
|
-
heldType = heldType.unwrap();
|
|
1789
|
-
return new PrimitiveHeader(
|
|
1790
|
-
field,
|
|
1791
|
-
heldType instanceof z9.ZodArray ? "list" : "line",
|
|
1792
|
-
field in required,
|
|
1793
|
-
heldType instanceof z9.ZodEnum ? heldType.options : void 0
|
|
1794
|
-
);
|
|
1795
|
-
});
|
|
1796
|
-
}
|
|
1797
|
-
function primitiveSampleOf(kind) {
|
|
1798
|
-
return Object.entries({ kind, ...CLASS_OF.get(kind).sample }).map(([field, value]) => `${field}: ${formatFrontmatterValue(value)}`).join("\n");
|
|
1799
|
-
}
|
|
1800
|
-
function primitiveOf(input, parser, assetFiles = [], primitiveLayer) {
|
|
1801
|
-
let headers;
|
|
1802
|
-
let body;
|
|
1803
|
-
if (typeof input === "string") {
|
|
1804
|
-
const lines3 = input.replace(/^/, "").split(/\r?\n/);
|
|
1805
|
-
if (lines3[0]?.trim() !== DELIMITER)
|
|
1806
|
-
throwAggregateError([
|
|
1807
|
-
new CharterPrimitiveFault(
|
|
1808
|
-
`This file opens with no "${DELIMITER}" frontmatter block.`,
|
|
1809
|
-
`Add one at the top declaring at least "id" and "description".`
|
|
1810
|
-
)
|
|
1811
|
-
]);
|
|
1812
|
-
const end = lines3.findIndex((line, index) => index > 0 && line.trim() === DELIMITER);
|
|
1813
|
-
if (end === -1)
|
|
1814
|
-
throwAggregateError([
|
|
1815
|
-
new CharterPrimitiveFault(
|
|
1816
|
-
`The frontmatter block is never closed.`,
|
|
1817
|
-
`Add a "${DELIMITER}" line after the headers.`
|
|
1818
|
-
)
|
|
1819
|
-
]);
|
|
1820
|
-
headers = parser.parse(lines3.slice(1, end).join("\n"));
|
|
1821
|
-
body = lines3.slice(end + 1).join("\n").replace(/^(\s*\n)+/, "").trimEnd();
|
|
1822
|
-
} else {
|
|
1823
|
-
headers = input.headers;
|
|
1824
|
-
body = input.body;
|
|
1825
|
-
}
|
|
1826
|
-
if (!isKind(headers.kind))
|
|
1827
|
-
throwAggregateError([
|
|
1828
|
-
headers.kind === void 0 ? new CharterPrimitiveFault(
|
|
1829
|
-
`This file declares no "kind", so nothing knows what to read it as.`,
|
|
1830
|
-
`Add "kind: <one the charter knows>" to the frontmatter.`
|
|
1831
|
-
) : new CharterPrimitiveFault(
|
|
1832
|
-
`"${String(headers.kind)}" is not a kind the charter knows.`,
|
|
1833
|
-
`Declare a kind the charter knows: ${KINDS.join(", ")}.`
|
|
1834
|
-
)
|
|
1835
|
-
]);
|
|
1836
|
-
const kind = headers.kind;
|
|
1837
|
-
if (typeof input === "string") return CLASS_OF.get(kind).of(headers, body, assetFiles, primitiveLayer);
|
|
1838
|
-
const headerFields = primitiveHeadersOf(kind).map((header) => header.field);
|
|
1839
|
-
const unknownHeaderFaults = Object.keys(headers).filter((field) => field !== "kind" && field !== "id" && !headerFields.includes(field)).map(
|
|
1840
|
-
(field) => new CharterPrimitiveFault(
|
|
1841
|
-
`A ${kind} holds no "${field}".`,
|
|
1842
|
-
`Answer one of what a ${kind} holds: ${headerFields.join(", ")}.`
|
|
1843
|
-
)
|
|
1844
|
-
);
|
|
1845
|
-
let primitive;
|
|
1846
|
-
let schemaFaults = [];
|
|
1847
|
-
try {
|
|
1848
|
-
primitive = CLASS_OF.get(kind).of(headers, body);
|
|
1849
|
-
} catch (raised) {
|
|
1850
|
-
if (!(raised instanceof AggregateError)) throw raised;
|
|
1851
|
-
schemaFaults = raised.errors;
|
|
1852
|
-
}
|
|
1853
|
-
if (primitive === void 0 || unknownHeaderFaults.length > 0) throwAggregateError([...unknownHeaderFaults, ...schemaFaults]);
|
|
1854
|
-
return primitive;
|
|
1855
|
-
}
|
|
1856
|
-
|
|
1857
|
-
// src/hexagon/domain/models/test/TestCase.ts
|
|
1858
|
-
var TestCaseSchema2 = z10.union([
|
|
1859
|
-
z10.strictObject({
|
|
1860
|
-
do: z10.strictObject({ touchFile: z10.string().min(1) }),
|
|
1861
|
-
expect: z10.union([
|
|
1862
|
-
z10.strictObject({ activate: PrimitiveIdentitySchema }),
|
|
1863
|
-
z10.strictObject({ allow: z10.boolean() })
|
|
1864
|
-
])
|
|
1865
|
-
}),
|
|
1866
|
-
z10.strictObject({
|
|
1867
|
-
when: z10.enum(SIGNALS),
|
|
1868
|
-
expect: z10.strictObject({ run: PrimitiveIdentitySchema })
|
|
1869
|
-
})
|
|
1870
|
-
]);
|
|
1871
|
-
var TestCase = class {
|
|
1872
|
-
constructor(written) {
|
|
1873
|
-
this.written = written;
|
|
1874
|
-
}
|
|
1875
|
-
written;
|
|
1876
|
-
/** The situation it puts, said as a line, and what it expects of it: what a
|
|
1877
|
-
* run reports it under, and what an explanation names it by (FR-048,
|
|
1878
|
-
* FR-014). */
|
|
1879
|
-
describe() {
|
|
1880
|
-
return `${this.situation} ${this.expectation}`;
|
|
1881
|
-
}
|
|
1882
|
-
/** The situation it puts: the file it touches, or the event it raises. */
|
|
1883
|
-
get situation() {
|
|
1884
|
-
return "when" in this.written ? `firing event ${this.written.when}` : `touching ${this.written.do.touchFile}`;
|
|
1885
|
-
}
|
|
1886
|
-
/** What it expects the charter to make of its situation. */
|
|
1887
|
-
get expectation() {
|
|
1888
|
-
const written = this.written;
|
|
1889
|
-
if ("when" in written) return `runs ${written.expect.run}`;
|
|
1890
|
-
if ("activate" in written.expect) return `activates ${written.expect.activate}`;
|
|
1891
|
-
return `is ${written.expect.allow ? "allowed" : "denied"}`;
|
|
1892
|
-
}
|
|
1893
|
-
/**
|
|
1894
|
-
* Which primitive this case expects to be activated: the sensor a raised event
|
|
1895
|
-
* is expected to run, or the guide a touched file is expected to bring up
|
|
1896
|
-
* (FR-014).
|
|
1897
|
-
*
|
|
1898
|
-
* Nothing for the third shape. That one expects a touched file to be allowed
|
|
1899
|
-
* or denied, which every posture in the charter answers between them, so it
|
|
1900
|
-
* activates no one primitive by name.
|
|
1901
|
-
*/
|
|
1902
|
-
get activatedIdentity() {
|
|
1903
|
-
if ("when" in this.written) return this.written.expect.run;
|
|
1904
|
-
if ("activate" in this.written.expect) return this.written.expect.activate;
|
|
1905
|
-
return void 0;
|
|
1906
|
-
}
|
|
1907
|
-
};
|
|
1908
|
-
|
|
1909
|
-
// src/hexagon/domain/models/test/TestSuite.ts
|
|
1910
|
-
var TestSuiteSchema2 = z11.strictObject({
|
|
1911
|
-
description: z11.string().min(1).optional(),
|
|
1912
|
-
cases: z11.array(TestCaseSchema2.transform((written) => new TestCase(written))).min(1)
|
|
1913
|
-
});
|
|
1914
|
-
var TestSuite = class {
|
|
1915
|
-
constructor(description, cases, body) {
|
|
1916
|
-
this.description = description;
|
|
1917
|
-
this.cases = cases;
|
|
1918
|
-
this.body = body;
|
|
1919
|
-
}
|
|
1920
|
-
description;
|
|
1921
|
-
cases;
|
|
1922
|
-
body;
|
|
1923
|
-
};
|
|
1924
|
-
var SAMPLE_CASES = [
|
|
1925
|
-
{ do: { touchFile: "src/one.ts" }, expect: { activate: "guide:no-any" } },
|
|
1926
|
-
{ do: { touchFile: ".env" }, expect: { allow: false } },
|
|
1927
|
-
{ when: "PreToolUse", expect: { run: "sensor:no-secrets" } }
|
|
1928
|
-
];
|
|
1929
|
-
var SAMPLE = JSON.stringify({ cases: SAMPLE_CASES }, void 0, 1).replace(/\n\s*/g, " ");
|
|
1930
|
-
var DEFAULT_TEST_SUITE_BODY = `${JSON.stringify({ description: "What these cases pin down.", cases: [SAMPLE_CASES[0]] }, null, 2)}
|
|
1931
|
-
`;
|
|
1932
|
-
function testSuiteOf(text) {
|
|
1933
|
-
let written;
|
|
1934
|
-
try {
|
|
1935
|
-
written = JSON.parse(text);
|
|
1936
|
-
} catch {
|
|
1937
|
-
throw new TestSuiteFault(
|
|
1938
|
-
`This file is not JSON, so nothing can read the cases it was meant to hold.`,
|
|
1939
|
-
`Write it as ${SAMPLE}`
|
|
1940
|
-
);
|
|
1941
|
-
}
|
|
1942
|
-
const read = TestSuiteSchema2.safeParse(written);
|
|
1943
|
-
if (!read.success) throw new TestSuiteFault(`This file is not written as a suite of cases.`, `Write it as ${SAMPLE}`);
|
|
1944
|
-
return new TestSuite(read.data.description, read.data.cases, text);
|
|
1945
|
-
}
|
|
1946
|
-
|
|
1947
|
-
// src/hexagon/domain/models/test/TestRoot.ts
|
|
1948
|
-
function testSuiteNameOf(path) {
|
|
1949
|
-
return path.slice(`${TEST_DIRECTORY}/`.length);
|
|
1950
|
-
}
|
|
1951
|
-
var TestRoot = class {
|
|
1952
|
-
constructor(suitesByFile, faultsByFiles) {
|
|
1953
|
-
this.suitesByFile = suitesByFile;
|
|
1954
|
-
this.faultsByFiles = faultsByFiles;
|
|
1955
|
-
}
|
|
1956
|
-
suitesByFile;
|
|
1957
|
-
faultsByFiles;
|
|
1958
|
-
/** Whether a test file is under this name, whether it reads or not. A name
|
|
1959
|
-
* reaching outside `.cw/test/` is no name of one. */
|
|
1960
|
-
hasTestSuiteNamed(name) {
|
|
1961
|
-
const path = `${TEST_DIRECTORY}/${name}`;
|
|
1962
|
-
return path in this.suitesByFile || path in this.faultsByFiles.files;
|
|
1963
|
-
}
|
|
1964
|
-
/** What a new test file is named: `untitled-<n>.json` for the first `n` no
|
|
1965
|
-
* test file has (FR-091). */
|
|
1966
|
-
get untitledName() {
|
|
1967
|
-
let untitledNumber = 1;
|
|
1968
|
-
while (this.hasTestSuiteNamed(`untitled-${untitledNumber}.json`)) untitledNumber++;
|
|
1969
|
-
return `untitled-${untitledNumber}.json`;
|
|
1970
|
-
}
|
|
1971
|
-
};
|
|
1972
|
-
function testRootOf(testSuiteFiles) {
|
|
1973
|
-
const suitesByFile = {};
|
|
1974
|
-
const faultsByFiles = {};
|
|
1975
|
-
for (const { path, contents } of [...testSuiteFiles].sort((one, another) => one.path.localeCompare(another.path))) {
|
|
1976
|
-
try {
|
|
1977
|
-
suitesByFile[path] = testSuiteOf(contents);
|
|
1978
|
-
} catch (raised) {
|
|
1979
|
-
if (!(raised instanceof DomainFault)) throw raised;
|
|
1980
|
-
faultsByFiles[path] = [raised];
|
|
1981
|
-
}
|
|
1982
|
-
}
|
|
1983
|
-
return new TestRoot(suitesByFile, new FaultsByFile(faultsByFiles));
|
|
1984
|
-
}
|
|
1985
|
-
|
|
1986
|
-
// src/hexagon/application/dtos.ts
|
|
1987
|
-
function catalogueDTO(catalogue) {
|
|
1988
|
-
return { type: "Catalogue", data: { entries: catalogue.entries.map((one) => ({ type: "CatalogueEntry", data: one })) } };
|
|
1459
|
+
// src/hexagon/application/dtos.ts
|
|
1460
|
+
function catalogueDTO(catalogue) {
|
|
1461
|
+
return { type: "Catalogue", data: { entries: catalogue.entries.map((one) => ({ type: "CatalogueEntry", data: one })) } };
|
|
1989
1462
|
}
|
|
1990
1463
|
function doctorOutcomeDTO({ agents }, faultsByFile, planSummary, driftedVendors2, repo) {
|
|
1991
1464
|
const faults = Object.values(faultsByFile.files).flat();
|
|
@@ -2052,11 +1525,11 @@ function primitiveKindsDTO(kinds) {
|
|
|
2052
1525
|
return { type: "PrimitiveKinds", data: Object.fromEntries(kinds.map((one) => [one.kind, one.activatesWhen])) };
|
|
2053
1526
|
}
|
|
2054
1527
|
function primitiveDTO(primitive) {
|
|
2055
|
-
const {
|
|
1528
|
+
const { layerName, file, body, hash } = primitive;
|
|
2056
1529
|
return {
|
|
2057
1530
|
type: "Primitive",
|
|
2058
1531
|
data: {
|
|
2059
|
-
|
|
1532
|
+
id: primitive.headers.id,
|
|
2060
1533
|
kind: primitive.kind,
|
|
2061
1534
|
description: primitive.headers.description,
|
|
2062
1535
|
file,
|
|
@@ -2077,7 +1550,7 @@ function primitivesDTO(primitives) {
|
|
|
2077
1550
|
return {
|
|
2078
1551
|
type: "Primitives",
|
|
2079
1552
|
data: {
|
|
2080
|
-
primitives: [...primitives].sort((one, another) => one.
|
|
1553
|
+
primitives: [...primitives].sort((one, another) => one.headers.id < another.headers.id ? -1 : one.headers.id > another.headers.id ? 1 : 0).map(primitiveDTO)
|
|
2081
1554
|
}
|
|
2082
1555
|
};
|
|
2083
1556
|
}
|
|
@@ -2104,9 +1577,9 @@ function testSuitesDTO({ suitesByFile, faultsByFiles }) {
|
|
|
2104
1577
|
name: testSuiteNameOf(path),
|
|
2105
1578
|
...suite === void 0 ? {} : { text: suite.body },
|
|
2106
1579
|
...suite?.description === void 0 ? {} : { description: suite.description },
|
|
2107
|
-
cases: (suite?.cases ?? []).map(({ situation, expectation,
|
|
1580
|
+
cases: (suite?.cases ?? []).map(({ situation, expectation, activatedId }) => ({
|
|
2108
1581
|
type: "TestCase",
|
|
2109
|
-
data: { situation, expectation, ...
|
|
1582
|
+
data: { situation, expectation, ...activatedId === void 0 ? {} : { id: activatedId } }
|
|
2110
1583
|
})),
|
|
2111
1584
|
...fault === void 0 ? {} : { fault: faultDTO(fault) }
|
|
2112
1585
|
}
|
|
@@ -2143,10 +1616,10 @@ function api(charterAuthoringApp2, charterVendoringApp2, testAuthoringApp2) {
|
|
|
2143
1616
|
createRoute({
|
|
2144
1617
|
method: "get",
|
|
2145
1618
|
path: "/charter/root/primitives",
|
|
2146
|
-
request: { query:
|
|
1619
|
+
request: { query: z8.object({ matching: z8.string().optional() }) },
|
|
2147
1620
|
responses: {
|
|
2148
1621
|
200: json(DataDTOs.Primitives, "Every primitive of every layer, or those mentioning the word"),
|
|
2149
|
-
422: json(
|
|
1622
|
+
422: json(z8.union([DataDTOs.FaultsByFile, DataDTOs.Fault]), "The charter does not hold")
|
|
2150
1623
|
}
|
|
2151
1624
|
}),
|
|
2152
1625
|
async (c) => {
|
|
@@ -2156,16 +1629,16 @@ function api(charterAuthoringApp2, charterVendoringApp2, testAuthoringApp2) {
|
|
|
2156
1629
|
).openapi(
|
|
2157
1630
|
createRoute({
|
|
2158
1631
|
method: "get",
|
|
2159
|
-
path: "/charter/root/primitives/{
|
|
2160
|
-
request: { params:
|
|
1632
|
+
path: "/charter/root/primitives/{id}/explanation",
|
|
1633
|
+
request: { params: z8.object({ id: z8.string() }) },
|
|
2161
1634
|
responses: {
|
|
2162
1635
|
200: json(OutcomeDTOs.ExplanationOutcome, "What the engine says of one primitive"),
|
|
2163
|
-
422: json(
|
|
1636
|
+
422: json(z8.union([DataDTOs.FaultsByFile, DataDTOs.Fault]), "The charter does not hold, or holds nothing of it")
|
|
2164
1637
|
}
|
|
2165
1638
|
}),
|
|
2166
1639
|
async (c) => {
|
|
2167
|
-
const {
|
|
2168
|
-
const explanationOutcomeDTO2 = await charterAuthoringApp2.explain(
|
|
1640
|
+
const { id } = c.req.valid("param");
|
|
1641
|
+
const explanationOutcomeDTO2 = await charterAuthoringApp2.explain(id);
|
|
2169
1642
|
return explanationOutcomeDTO2.type === "FaultsByFile" ? c.json(explanationOutcomeDTO2, 422) : c.json(explanationOutcomeDTO2, 200);
|
|
2170
1643
|
}
|
|
2171
1644
|
).openapi(
|
|
@@ -2174,11 +1647,11 @@ function api(charterAuthoringApp2, charterVendoringApp2, testAuthoringApp2) {
|
|
|
2174
1647
|
path: "/charter/root/primitives",
|
|
2175
1648
|
request: {
|
|
2176
1649
|
body: json(
|
|
2177
|
-
|
|
2178
|
-
kind:
|
|
2179
|
-
id:
|
|
2180
|
-
headers:
|
|
2181
|
-
body:
|
|
1650
|
+
z8.object({
|
|
1651
|
+
kind: z8.string(),
|
|
1652
|
+
id: z8.string(),
|
|
1653
|
+
headers: z8.record(z8.string(), z8.union([z8.string(), z8.array(z8.string())])),
|
|
1654
|
+
body: z8.string()
|
|
2182
1655
|
}),
|
|
2183
1656
|
"A new primitive: its kind and id, a line or a list under each header, and its body"
|
|
2184
1657
|
)
|
|
@@ -2186,46 +1659,46 @@ function api(charterAuthoringApp2, charterVendoringApp2, testAuthoringApp2) {
|
|
|
2186
1659
|
responses: {
|
|
2187
1660
|
201: {
|
|
2188
1661
|
...json(DataDTOs.Primitive, "The primitive written"),
|
|
2189
|
-
headers:
|
|
1662
|
+
headers: z8.object({ Location: z8.string() })
|
|
2190
1663
|
},
|
|
2191
|
-
422: json(
|
|
1664
|
+
422: json(z8.union([DataDTOs.Faults, DataDTOs.Fault]), "The answers the kind refused, or an id already claimed")
|
|
2192
1665
|
}
|
|
2193
1666
|
}),
|
|
2194
1667
|
async (c) => {
|
|
2195
1668
|
const { kind, id, headers, body } = c.req.valid("json");
|
|
2196
1669
|
const primitiveDTO2 = await charterAuthoringApp2.add(kind, id, headers, body);
|
|
2197
1670
|
if (primitiveDTO2.type === "Faults") return c.json(primitiveDTO2, 422);
|
|
2198
|
-
return c.json(primitiveDTO2, 201, { Location: `/api/charter/root/primitives/${encodeURIComponent(primitiveDTO2.data.
|
|
1671
|
+
return c.json(primitiveDTO2, 201, { Location: `/api/charter/root/primitives/${encodeURIComponent(primitiveDTO2.data.id)}` });
|
|
2199
1672
|
}
|
|
2200
1673
|
).openapi(
|
|
2201
1674
|
createRoute({
|
|
2202
1675
|
method: "get",
|
|
2203
|
-
path: "/charter/root/primitives/{
|
|
2204
|
-
request: { params:
|
|
1676
|
+
path: "/charter/root/primitives/{id}",
|
|
1677
|
+
request: { params: z8.object({ id: z8.string() }) },
|
|
2205
1678
|
responses: {
|
|
2206
1679
|
200: {
|
|
2207
1680
|
...json(DataDTOs.Primitive, "One primitive as the charter read it"),
|
|
2208
|
-
headers:
|
|
1681
|
+
headers: z8.object({ ETag: z8.string() })
|
|
2209
1682
|
},
|
|
2210
1683
|
422: json(DataDTOs.Fault, "The charter holds nothing of it")
|
|
2211
1684
|
}
|
|
2212
1685
|
}),
|
|
2213
1686
|
async (c) => {
|
|
2214
|
-
const {
|
|
2215
|
-
const primitiveDTO2 = await charterAuthoringApp2.open(
|
|
1687
|
+
const { id } = c.req.valid("param");
|
|
1688
|
+
const primitiveDTO2 = await charterAuthoringApp2.open(id);
|
|
2216
1689
|
return c.json(primitiveDTO2, 200, { ETag: `"${primitiveDTO2.data.hash}"` });
|
|
2217
1690
|
}
|
|
2218
1691
|
).openapi(
|
|
2219
1692
|
createRoute({
|
|
2220
1693
|
method: "put",
|
|
2221
|
-
path: "/charter/root/primitives/{
|
|
1694
|
+
path: "/charter/root/primitives/{id}",
|
|
2222
1695
|
request: {
|
|
2223
|
-
params:
|
|
2224
|
-
headers:
|
|
1696
|
+
params: z8.object({ id: z8.string() }),
|
|
1697
|
+
headers: z8.object({ "if-match": z8.string().optional() }),
|
|
2225
1698
|
body: json(
|
|
2226
|
-
|
|
2227
|
-
headers:
|
|
2228
|
-
body:
|
|
1699
|
+
z8.object({
|
|
1700
|
+
headers: z8.record(z8.string(), z8.union([z8.string(), z8.array(z8.string())])),
|
|
1701
|
+
body: z8.string()
|
|
2229
1702
|
}),
|
|
2230
1703
|
"A line or a list under each header, and the body, written over the primitive's own"
|
|
2231
1704
|
)
|
|
@@ -2233,12 +1706,12 @@ function api(charterAuthoringApp2, charterVendoringApp2, testAuthoringApp2) {
|
|
|
2233
1706
|
responses: {
|
|
2234
1707
|
200: json(DataDTOs.Primitive, "The primitive written"),
|
|
2235
1708
|
412: json(DataDTOs.Fault, "The file changed on disk after it was opened"),
|
|
2236
|
-
422: json(
|
|
1709
|
+
422: json(z8.union([DataDTOs.Faults, DataDTOs.Fault]), "The answers the kind refused, or a primitive this repository did not author")
|
|
2237
1710
|
}
|
|
2238
1711
|
}),
|
|
2239
1712
|
async (c) => {
|
|
2240
|
-
const {
|
|
2241
|
-
const primitive = (await charterAuthoringApp2.open(
|
|
1713
|
+
const { id } = c.req.valid("param");
|
|
1714
|
+
const primitive = (await charterAuthoringApp2.open(id)).data;
|
|
2242
1715
|
if (c.req.valid("header")["if-match"] !== `"${primitive.hash}"`)
|
|
2243
1716
|
return c.json(
|
|
2244
1717
|
faultDTO(
|
|
@@ -2250,22 +1723,22 @@ function api(charterAuthoringApp2, charterVendoringApp2, testAuthoringApp2) {
|
|
|
2250
1723
|
412
|
|
2251
1724
|
);
|
|
2252
1725
|
const { headers, body } = c.req.valid("json");
|
|
2253
|
-
const primitiveDTO2 = await charterAuthoringApp2.rewrite(
|
|
1726
|
+
const primitiveDTO2 = await charterAuthoringApp2.rewrite(id, headers, body, primitive.hash);
|
|
2254
1727
|
return primitiveDTO2.type === "Faults" ? c.json(primitiveDTO2, 422) : c.json(primitiveDTO2, 200);
|
|
2255
1728
|
}
|
|
2256
1729
|
).openapi(
|
|
2257
1730
|
createRoute({
|
|
2258
1731
|
method: "delete",
|
|
2259
|
-
path: "/charter/root/primitives/{
|
|
2260
|
-
request: { params:
|
|
1732
|
+
path: "/charter/root/primitives/{id}",
|
|
1733
|
+
request: { params: z8.object({ id: z8.string() }) },
|
|
2261
1734
|
responses: {
|
|
2262
1735
|
204: { description: "The file is gone" },
|
|
2263
1736
|
422: json(DataDTOs.Fault, "A primitive this repository did not author, or none at all")
|
|
2264
1737
|
}
|
|
2265
1738
|
}),
|
|
2266
1739
|
async (c) => {
|
|
2267
|
-
const {
|
|
2268
|
-
await charterAuthoringApp2.remove(
|
|
1740
|
+
const { id } = c.req.valid("param");
|
|
1741
|
+
await charterAuthoringApp2.remove(id);
|
|
2269
1742
|
return c.body(null, 204);
|
|
2270
1743
|
}
|
|
2271
1744
|
).openapi(
|
|
@@ -2279,7 +1752,7 @@ function api(charterAuthoringApp2, charterVendoringApp2, testAuthoringApp2) {
|
|
|
2279
1752
|
createRoute({
|
|
2280
1753
|
method: "get",
|
|
2281
1754
|
path: "/definitions/kinds/{kind}/requirements",
|
|
2282
|
-
request: { params:
|
|
1755
|
+
request: { params: z8.object({ kind: z8.string() }) },
|
|
2283
1756
|
responses: {
|
|
2284
1757
|
200: json(DataDTOs.PrimitiveRequirements, "Every header the kind takes, and its sample"),
|
|
2285
1758
|
422: json(DataDTOs.Fault, "A word that is no kind")
|
|
@@ -2332,8 +1805,8 @@ function api(charterAuthoringApp2, charterVendoringApp2, testAuthoringApp2) {
|
|
|
2332
1805
|
path: "/test-suites",
|
|
2333
1806
|
responses: {
|
|
2334
1807
|
201: {
|
|
2335
|
-
...json(
|
|
2336
|
-
headers:
|
|
1808
|
+
...json(z8.string(), "The name of the test file written"),
|
|
1809
|
+
headers: z8.object({ Location: z8.string() })
|
|
2337
1810
|
}
|
|
2338
1811
|
}
|
|
2339
1812
|
}),
|
|
@@ -2346,11 +1819,11 @@ function api(charterAuthoringApp2, charterVendoringApp2, testAuthoringApp2) {
|
|
|
2346
1819
|
method: "put",
|
|
2347
1820
|
path: "/test-suites/{name}",
|
|
2348
1821
|
request: {
|
|
2349
|
-
params:
|
|
2350
|
-
body: json(
|
|
1822
|
+
params: z8.object({ name: z8.string() }),
|
|
1823
|
+
body: json(z8.object({ text: z8.string() }), "The whole text the test file is written over with")
|
|
2351
1824
|
},
|
|
2352
1825
|
responses: {
|
|
2353
|
-
200: json(
|
|
1826
|
+
200: json(z8.string(), "The name of the test file written"),
|
|
2354
1827
|
422: json(DataDTOs.Fault, "Text that is not a suite, with its sample, or a name that is no test file")
|
|
2355
1828
|
}
|
|
2356
1829
|
}),
|
|
@@ -2359,7 +1832,7 @@ function api(charterAuthoringApp2, charterVendoringApp2, testAuthoringApp2) {
|
|
|
2359
1832
|
createRoute({
|
|
2360
1833
|
method: "delete",
|
|
2361
1834
|
path: "/test-suites/{name}",
|
|
2362
|
-
request: { params:
|
|
1835
|
+
request: { params: z8.object({ name: z8.string() }) },
|
|
2363
1836
|
responses: {
|
|
2364
1837
|
204: { description: "The test file is gone" },
|
|
2365
1838
|
422: json(DataDTOs.Fault, "A name that is no test file")
|
|
@@ -2386,7 +1859,7 @@ function api(charterAuthoringApp2, charterVendoringApp2, testAuthoringApp2) {
|
|
|
2386
1859
|
createRoute({
|
|
2387
1860
|
method: "get",
|
|
2388
1861
|
path: "/vendors",
|
|
2389
|
-
responses: { 200: json(
|
|
1862
|
+
responses: { 200: json(z8.array(z8.string()).readonly(), "Every folder a vendor source was installed as") }
|
|
2390
1863
|
}),
|
|
2391
1864
|
async (c) => c.json(await charterVendoringApp2.installed(), 200)
|
|
2392
1865
|
).openapi(
|
|
@@ -2395,7 +1868,7 @@ function api(charterAuthoringApp2, charterVendoringApp2, testAuthoringApp2) {
|
|
|
2395
1868
|
path: "/vendors",
|
|
2396
1869
|
request: {
|
|
2397
1870
|
body: json(
|
|
2398
|
-
|
|
1871
|
+
z8.object({ source: z8.string(), version: z8.string().optional() }),
|
|
2399
1872
|
"A source as git fetches it, and the version to pin it to where one is named"
|
|
2400
1873
|
)
|
|
2401
1874
|
},
|
|
@@ -2413,7 +1886,7 @@ function api(charterAuthoringApp2, charterVendoringApp2, testAuthoringApp2) {
|
|
|
2413
1886
|
createRoute({
|
|
2414
1887
|
method: "delete",
|
|
2415
1888
|
path: "/vendors/{name}",
|
|
2416
|
-
request: { params:
|
|
1889
|
+
request: { params: z8.object({ name: z8.string() }) },
|
|
2417
1890
|
responses: {
|
|
2418
1891
|
204: { description: "The folder is gone, and that it is gone is committed" },
|
|
2419
1892
|
422: json(DataDTOs.Fault, "No repository, work in hand, or what git refused")
|
|
@@ -2494,9 +1967,9 @@ Press Ctrl+C to stop it.
|
|
|
2494
1967
|
// src/driver/cli/commands/RemoveCommand.ts
|
|
2495
1968
|
import prompts4 from "prompts";
|
|
2496
1969
|
var OPTIONS8 = {
|
|
2497
|
-
|
|
1970
|
+
id: {
|
|
2498
1971
|
type: "string",
|
|
2499
|
-
describe: 'What the primitive is named by across the whole charter: "
|
|
1972
|
+
describe: 'What the primitive is named by across the whole charter, its id: "no-any"'
|
|
2500
1973
|
},
|
|
2501
1974
|
yes: {
|
|
2502
1975
|
type: "boolean",
|
|
@@ -2506,16 +1979,16 @@ var OPTIONS8 = {
|
|
|
2506
1979
|
}
|
|
2507
1980
|
};
|
|
2508
1981
|
var RemoveCommand = class {
|
|
2509
|
-
name = "remove <
|
|
1982
|
+
name = "remove <id>";
|
|
2510
1983
|
summary = "Delete the file of one primitive this repository authored";
|
|
2511
1984
|
options = OPTIONS8;
|
|
2512
|
-
async run({ charterAuthoringApp: charterAuthoringApp2 }, {
|
|
2513
|
-
const primitive = (await charterAuthoringApp2.open(
|
|
1985
|
+
async run({ charterAuthoringApp: charterAuthoringApp2 }, { id, yes }) {
|
|
1986
|
+
const primitive = (await charterAuthoringApp2.open(id ?? "")).data;
|
|
2514
1987
|
if (!yes && primitive.layerName === REPO_LAYER) {
|
|
2515
1988
|
if (!process.stdin.isTTY)
|
|
2516
1989
|
throw new DomainFault(
|
|
2517
1990
|
`Removing ${primitive.file} is asked first, and nobody is at the terminal to answer.`,
|
|
2518
|
-
`Run "cw remove ${primitive.
|
|
1991
|
+
`Run "cw remove ${primitive.id} --yes" to remove it without asking.`
|
|
2519
1992
|
);
|
|
2520
1993
|
const { confirmed } = await prompts4({
|
|
2521
1994
|
type: "confirm",
|
|
@@ -2523,288 +1996,807 @@ var RemoveCommand = class {
|
|
|
2523
1996
|
message: `Remove ${primitive.file}?`,
|
|
2524
1997
|
initial: false
|
|
2525
1998
|
});
|
|
2526
|
-
if (confirmed !== true) return { code: EXIT_OK, result: "Nothing was removed.\n" };
|
|
1999
|
+
if (confirmed !== true) return { code: EXIT_OK, result: "Nothing was removed.\n" };
|
|
2000
|
+
}
|
|
2001
|
+
const primitiveDTO2 = await charterAuthoringApp2.remove(primitive.id);
|
|
2002
|
+
return {
|
|
2003
|
+
code: EXIT_OK,
|
|
2004
|
+
result: [
|
|
2005
|
+
`Removed ${primitiveDTO2.data.file}.`,
|
|
2006
|
+
'Run "cw doctor" to see what still names it, then "cw build".',
|
|
2007
|
+
""
|
|
2008
|
+
].join("\n")
|
|
2009
|
+
};
|
|
2010
|
+
}
|
|
2011
|
+
};
|
|
2012
|
+
|
|
2013
|
+
// src/driver/cli/commands/SuiteAddCommand.ts
|
|
2014
|
+
var SuiteAddCommand = class {
|
|
2015
|
+
name = "suite add";
|
|
2016
|
+
summary = `Write a new test file under ${TEST_DIRECTORY}/, holding one sample case`;
|
|
2017
|
+
async run({ testAuthoringApp: testAuthoringApp2 }) {
|
|
2018
|
+
const name = await testAuthoringApp2.addSuite();
|
|
2019
|
+
return {
|
|
2020
|
+
code: EXIT_OK,
|
|
2021
|
+
result: [`Wrote ${TEST_DIRECTORY}/${name}.`, `Run "cw suite edit ${name}" to write its cases, then "cw test".`, ""].join("\n")
|
|
2022
|
+
};
|
|
2023
|
+
}
|
|
2024
|
+
};
|
|
2025
|
+
|
|
2026
|
+
// src/driver/cli/commands/SuiteEditCommand.ts
|
|
2027
|
+
var OPTIONS9 = {
|
|
2028
|
+
name: {
|
|
2029
|
+
type: "string",
|
|
2030
|
+
describe: `The test file's name under ${TEST_DIRECTORY}/: "untitled-1.json"`
|
|
2031
|
+
}
|
|
2032
|
+
};
|
|
2033
|
+
var SuiteEditCommand = class {
|
|
2034
|
+
name = "suite edit <name>";
|
|
2035
|
+
summary = "Open one test file in your editor";
|
|
2036
|
+
options = OPTIONS9;
|
|
2037
|
+
async run({ cwd, testAuthoringApp: testAuthoringApp2 }, { name }) {
|
|
2038
|
+
const { testSuites } = (await testAuthoringApp2.suites()).data;
|
|
2039
|
+
if (!testSuites.some((one) => one.data.name === name))
|
|
2040
|
+
throw new DomainFault(`${TEST_DIRECTORY}/ holds no test file called "${name ?? ""}".`, 'Run "cw suite add" to write a new one.');
|
|
2041
|
+
const file = `${TEST_DIRECTORY}/${name}`;
|
|
2042
|
+
const problem = await editInEditor(cwd, file);
|
|
2043
|
+
if (problem !== void 0) return { code: EXIT_FAILURE, problem };
|
|
2044
|
+
return { code: EXIT_OK, result: [`Closed ${file}.`, 'Run "cw test" to see how its cases come out.', ""].join("\n") };
|
|
2045
|
+
}
|
|
2046
|
+
};
|
|
2047
|
+
|
|
2048
|
+
// src/driver/cli/commands/SuiteRemoveCommand.ts
|
|
2049
|
+
var OPTIONS10 = {
|
|
2050
|
+
name: {
|
|
2051
|
+
type: "string",
|
|
2052
|
+
describe: `The test file's name under ${TEST_DIRECTORY}/: "untitled-1.json"`
|
|
2053
|
+
}
|
|
2054
|
+
};
|
|
2055
|
+
var SuiteRemoveCommand = class {
|
|
2056
|
+
name = "suite remove <name>";
|
|
2057
|
+
summary = "Delete one test file";
|
|
2058
|
+
options = OPTIONS10;
|
|
2059
|
+
async run({ testAuthoringApp: testAuthoringApp2 }, { name }) {
|
|
2060
|
+
const removedName = await testAuthoringApp2.removeSuite(name ?? "");
|
|
2061
|
+
return { code: EXIT_OK, result: `Removed ${TEST_DIRECTORY}/${removedName}.
|
|
2062
|
+
` };
|
|
2063
|
+
}
|
|
2064
|
+
};
|
|
2065
|
+
|
|
2066
|
+
// src/driver/cli/commands/TestCommand.ts
|
|
2067
|
+
var TestCommand = class {
|
|
2068
|
+
name = "test";
|
|
2069
|
+
summary = `Resolve every situation in ${TEST_DIRECTORY}/ against the charter`;
|
|
2070
|
+
async run({ charterAuthoringApp: charterAuthoringApp2 }, _options) {
|
|
2071
|
+
const testRunReportDTO2 = await charterAuthoringApp2.test();
|
|
2072
|
+
if (testRunReportDTO2.type === "FaultsByFile")
|
|
2073
|
+
return { code: EXIT_FAILURE, problem: `Nothing was resolved.
|
|
2074
|
+
|
|
2075
|
+
${toText(testRunReportDTO2)}` };
|
|
2076
|
+
const cases = testRunReportDTO2.data.testCaseReports;
|
|
2077
|
+
if (cases.length === 0) {
|
|
2078
|
+
return {
|
|
2079
|
+
code: EXIT_OK,
|
|
2080
|
+
result: `No tests: ${TEST_DIRECTORY}/ holds no case.
|
|
2081
|
+
Write one as ${TEST_DIRECTORY}/<name>.json to pin a situation down.
|
|
2082
|
+
`
|
|
2083
|
+
};
|
|
2084
|
+
}
|
|
2085
|
+
const failed = cases.filter((one) => !one.data.passed);
|
|
2086
|
+
return {
|
|
2087
|
+
code: failed.length === 0 ? EXIT_OK : EXIT_FAILURE,
|
|
2088
|
+
result: [
|
|
2089
|
+
...cases.flatMap(lines2),
|
|
2090
|
+
"",
|
|
2091
|
+
failed.length === 0 ? `${cases.length} case${cases.length === 1 ? "" : "s"} passed.` : `${failed.length} of ${cases.length} case${cases.length === 1 ? "" : "s"} failed.`,
|
|
2092
|
+
""
|
|
2093
|
+
].join("\n")
|
|
2094
|
+
};
|
|
2095
|
+
}
|
|
2096
|
+
};
|
|
2097
|
+
function lines2(one) {
|
|
2098
|
+
return [
|
|
2099
|
+
` ${one.data.passed ? "pass" : "FAIL"} ${one.data.situation}`,
|
|
2100
|
+
...one.data.unmet === void 0 ? [] : [` ${one.data.unmet.data.message}`, ` ${one.data.unmet.data.fix}`]
|
|
2101
|
+
];
|
|
2102
|
+
}
|
|
2103
|
+
|
|
2104
|
+
// src/driver/cli/commands/VendorAddCommand.ts
|
|
2105
|
+
var OPTIONS11 = {
|
|
2106
|
+
source: {
|
|
2107
|
+
type: "string",
|
|
2108
|
+
describe: "What git clones the charter from: an address over any transport, or a path on disk"
|
|
2109
|
+
},
|
|
2110
|
+
ref: {
|
|
2111
|
+
type: "string",
|
|
2112
|
+
describe: "The tag, branch or commit to pin to; the source's own default where none is named"
|
|
2113
|
+
}
|
|
2114
|
+
};
|
|
2115
|
+
var VendorAddCommand = class {
|
|
2116
|
+
name = "vendor add <source>";
|
|
2117
|
+
summary = "Install a charter from a git source, or bring an installed one up to date";
|
|
2118
|
+
options = OPTIONS11;
|
|
2119
|
+
async run({ charterVendoringApp: charterVendoringApp2 }, { source, ref }) {
|
|
2120
|
+
const folder = await charterVendoringApp2.add(source ?? "", ref);
|
|
2121
|
+
return {
|
|
2122
|
+
code: EXIT_OK,
|
|
2123
|
+
result: [
|
|
2124
|
+
`Installed ${source} under ${folder}${ref === void 0 ? "" : `, pinned to ${ref}`}.`,
|
|
2125
|
+
'Run "cw build" to compile what it adds to this charter.',
|
|
2126
|
+
""
|
|
2127
|
+
].join("\n")
|
|
2128
|
+
};
|
|
2129
|
+
}
|
|
2130
|
+
};
|
|
2131
|
+
|
|
2132
|
+
// src/driver/cli/commands/VendorListCommand.ts
|
|
2133
|
+
var VendorListCommand = class {
|
|
2134
|
+
name = "vendor list";
|
|
2135
|
+
summary = "List the folders this repository installed charters as";
|
|
2136
|
+
async run({ charterVendoringApp: charterVendoringApp2 }) {
|
|
2137
|
+
const folders = await charterVendoringApp2.installed();
|
|
2138
|
+
if (folders.length === 0)
|
|
2139
|
+
return { code: EXIT_OK, result: 'No vendor source is installed. Run "cw vendor add <source>" to install one.\n' };
|
|
2140
|
+
return { code: EXIT_OK, result: `${folders.join("\n")}
|
|
2141
|
+
` };
|
|
2142
|
+
}
|
|
2143
|
+
};
|
|
2144
|
+
|
|
2145
|
+
// src/driver/cli/commands/VendorRemoveCommand.ts
|
|
2146
|
+
var OPTIONS12 = {
|
|
2147
|
+
name: {
|
|
2148
|
+
type: "string",
|
|
2149
|
+
describe: "The folder this charter was installed as, as it reads under .cw/vendor/"
|
|
2150
|
+
}
|
|
2151
|
+
};
|
|
2152
|
+
var VendorRemoveCommand = class {
|
|
2153
|
+
name = "vendor remove <name>";
|
|
2154
|
+
summary = "Take an installed charter away";
|
|
2155
|
+
options = OPTIONS12;
|
|
2156
|
+
async run({ charterVendoringApp: charterVendoringApp2 }, { name }) {
|
|
2157
|
+
const folder = await charterVendoringApp2.remove(name ?? "");
|
|
2158
|
+
return {
|
|
2159
|
+
code: EXIT_OK,
|
|
2160
|
+
result: [
|
|
2161
|
+
`Removed ${folder}.`,
|
|
2162
|
+
'Run "cw build" to compile this charter without it.',
|
|
2163
|
+
""
|
|
2164
|
+
].join("\n")
|
|
2165
|
+
};
|
|
2166
|
+
}
|
|
2167
|
+
};
|
|
2168
|
+
|
|
2169
|
+
// src/driver/cli/commands/index.ts
|
|
2170
|
+
var COMMANDS = [
|
|
2171
|
+
new InitCommand(),
|
|
2172
|
+
new BuildCommand(),
|
|
2173
|
+
new ListCommand(),
|
|
2174
|
+
new KindsCommand(),
|
|
2175
|
+
new AddCommand(),
|
|
2176
|
+
new EditCommand(),
|
|
2177
|
+
new RemoveCommand(),
|
|
2178
|
+
new ExplainCommand(),
|
|
2179
|
+
new DoctorCommand(),
|
|
2180
|
+
new TestCommand(),
|
|
2181
|
+
new SuiteAddCommand(),
|
|
2182
|
+
new SuiteEditCommand(),
|
|
2183
|
+
new SuiteRemoveCommand(),
|
|
2184
|
+
new VendorAddCommand(),
|
|
2185
|
+
new VendorRemoveCommand(),
|
|
2186
|
+
new VendorListCommand(),
|
|
2187
|
+
new PortalCommand(),
|
|
2188
|
+
new McpAuthCommand(),
|
|
2189
|
+
new McpServeCommand()
|
|
2190
|
+
];
|
|
2191
|
+
|
|
2192
|
+
// src/driver/cli/Commander.ts
|
|
2193
|
+
var Commander = class {
|
|
2194
|
+
#context;
|
|
2195
|
+
#commands;
|
|
2196
|
+
constructor(context, commands = COMMANDS) {
|
|
2197
|
+
this.#context = context;
|
|
2198
|
+
this.#commands = commands;
|
|
2199
|
+
}
|
|
2200
|
+
async run(argv) {
|
|
2201
|
+
let code = EXIT_OK;
|
|
2202
|
+
let failed = false;
|
|
2203
|
+
await this.#parser((outcome) => {
|
|
2204
|
+
code = outcome.code;
|
|
2205
|
+
if (outcome.result) process.stdout.write(outcome.result);
|
|
2206
|
+
if (outcome.problem) process.stderr.write(outcome.problem);
|
|
2207
|
+
}).parseAsync([...argv], {}, (error, _parsed, output) => {
|
|
2208
|
+
failed = error !== null && error !== void 0;
|
|
2209
|
+
if (output) (failed ? process.stderr : process.stdout).write(`${output}
|
|
2210
|
+
`);
|
|
2211
|
+
});
|
|
2212
|
+
return failed ? EXIT_USAGE : code;
|
|
2213
|
+
}
|
|
2214
|
+
#parser(collect) {
|
|
2215
|
+
let cli2 = yargs().scriptName("cw").usage("Usage: $0 <command> [options]").demandCommand(1, "").strict().recommendCommands().help("help").alias("h", "help").version(this.#context.version).wrap(80).exitProcess(false);
|
|
2216
|
+
const groups = /* @__PURE__ */ new Map();
|
|
2217
|
+
for (const command of this.#commands) {
|
|
2218
|
+
const head = wordsOf(command.name)[0];
|
|
2219
|
+
groups.set(head, [...groups.get(head) ?? [], command]);
|
|
2220
|
+
}
|
|
2221
|
+
for (const [head, commands] of groups) {
|
|
2222
|
+
const [only] = commands;
|
|
2223
|
+
cli2 = commands.length === 1 && only !== void 0 && wordsOf(only.name).length === 1 ? this.#declaringCommand(cli2, only, only.name, collect) : cli2.command({
|
|
2224
|
+
command: head,
|
|
2225
|
+
// What the group is for is what its commands are: saying it any
|
|
2226
|
+
// other way would be a second description to keep in step.
|
|
2227
|
+
describe: commands.map((one) => wordsOf(one.name)[1]).join(", "),
|
|
2228
|
+
builder: (child) => commands.reduce((parser, command) => this.#declaringCommand(parser, command, command.name.slice(head.length + 1), collect), child).demandCommand(1, ""),
|
|
2229
|
+
handler: () => void 0
|
|
2230
|
+
});
|
|
2527
2231
|
}
|
|
2528
|
-
|
|
2529
|
-
|
|
2530
|
-
|
|
2531
|
-
|
|
2532
|
-
|
|
2533
|
-
|
|
2534
|
-
|
|
2535
|
-
|
|
2536
|
-
|
|
2232
|
+
return cli2;
|
|
2233
|
+
}
|
|
2234
|
+
/** One command, said to yargs under the name it is typed by — the whole of it
|
|
2235
|
+
* for a command that stands alone, what follows the group for one inside
|
|
2236
|
+
* it. */
|
|
2237
|
+
#declaringCommand(cli2, command, name, collect) {
|
|
2238
|
+
return cli2.command({
|
|
2239
|
+
command: name,
|
|
2240
|
+
describe: command.summary,
|
|
2241
|
+
builder: (child) => declaringArguments(child, command.options ?? {}, name),
|
|
2242
|
+
handler: async (options) => {
|
|
2243
|
+
try {
|
|
2244
|
+
collect(await command.run(this.#context, options));
|
|
2245
|
+
} catch (raised) {
|
|
2246
|
+
if (!(raised instanceof DomainFault)) throw raised;
|
|
2247
|
+
collect({ code: EXIT_FAILURE, problem: `${raised.message}
|
|
2248
|
+
${raised.fix}
|
|
2249
|
+
` });
|
|
2250
|
+
}
|
|
2251
|
+
}
|
|
2252
|
+
});
|
|
2537
2253
|
}
|
|
2538
2254
|
};
|
|
2539
|
-
|
|
2540
|
-
|
|
2541
|
-
|
|
2542
|
-
|
|
2543
|
-
|
|
2544
|
-
|
|
2545
|
-
|
|
2546
|
-
|
|
2547
|
-
|
|
2548
|
-
|
|
2255
|
+
function wordsOf(name) {
|
|
2256
|
+
return name.split(" ").filter((word) => !word.startsWith("<") && !word.startsWith("["));
|
|
2257
|
+
}
|
|
2258
|
+
function declaringArguments(cli2, options, name) {
|
|
2259
|
+
return Object.entries(options).reduce((parser, [argument, option]) => {
|
|
2260
|
+
const declared = {
|
|
2261
|
+
type: option.type,
|
|
2262
|
+
describe: option.describe,
|
|
2263
|
+
...option.alias === void 0 ? {} : { alias: option.alias },
|
|
2264
|
+
...option.default === void 0 ? {} : { default: option.default },
|
|
2265
|
+
// Typed once per value: yargs collects them, and a run that typed none
|
|
2266
|
+
// reads an empty list rather than nothing, so the command asks how many
|
|
2267
|
+
// were typed instead of whether any were.
|
|
2268
|
+
...option.isArray === void 0 ? {} : { array: true, default: [] }
|
|
2549
2269
|
};
|
|
2270
|
+
return name.includes(`<${argument}>`) || name.includes(`[${argument}]`) ? parser.positional(argument, declared) : parser.option(argument, declared);
|
|
2271
|
+
}, cli2);
|
|
2272
|
+
}
|
|
2273
|
+
|
|
2274
|
+
// src/hexagon/utils/globs.ts
|
|
2275
|
+
import picomatch from "picomatch";
|
|
2276
|
+
function covers(outer, inner) {
|
|
2277
|
+
return outer === inner || matches(outer, inner);
|
|
2278
|
+
}
|
|
2279
|
+
function matches(glob, path) {
|
|
2280
|
+
return picomatch(glob, { dot: true })(path);
|
|
2281
|
+
}
|
|
2282
|
+
|
|
2283
|
+
// src/hexagon/domain/models/charter/primitive/Primitive.ts
|
|
2284
|
+
import { z as z12 } from "zod";
|
|
2285
|
+
|
|
2286
|
+
// src/hexagon/domain/models/charter/primitive/AgentPrimitive.ts
|
|
2287
|
+
var AgentHeadersSchema = CommonHeadersSchema.extend({
|
|
2288
|
+
tools: GoodArraySchema
|
|
2289
|
+
});
|
|
2290
|
+
var AgentPrimitive = class _AgentPrimitive extends BasePrimitive {
|
|
2291
|
+
static kind = "agent";
|
|
2292
|
+
kind = _AgentPrimitive.kind;
|
|
2293
|
+
/** What this kind requires beyond the common headers, declared where its
|
|
2294
|
+
* contract is: it is read both where a file is checked and where one is
|
|
2295
|
+
* scaffolded (FR-004, FR-039). */
|
|
2296
|
+
static requires = { tools: "list" };
|
|
2297
|
+
/** The schema its headers are read by: what `headersOf` refuses a file
|
|
2298
|
+
* against, and what says the shape of every header this kind takes, required
|
|
2299
|
+
* or not (FR-004). */
|
|
2300
|
+
static schema = AgentHeadersSchema;
|
|
2301
|
+
/** When a reader of this charter is to open this kind at all, said
|
|
2302
|
+
* where the kind's contract is: the neutral surface lists one
|
|
2303
|
+
* line per kind and none of them is written down twice (FR-002). */
|
|
2304
|
+
static activatesWhen = "it is spawned by id, holding the `tools` it lists and nothing else \u2014 a place as `[[<id>]]`, or one of its tools as `[[<id>]]:<tool>`";
|
|
2305
|
+
/** An agent as an author writes one: what a refused header is fixed with.
|
|
2306
|
+
* It holds one tool of a place the way an author writes it (FR-156). */
|
|
2307
|
+
static sample = {
|
|
2308
|
+
id: "reviewer",
|
|
2309
|
+
description: "Review a diff and name what is wrong with it.",
|
|
2310
|
+
tools: ["Read", "Grep", "[[mfbs/billing]]:search_code"]
|
|
2311
|
+
};
|
|
2312
|
+
/** One agent, or every fault its headers have (FR-004). */
|
|
2313
|
+
static of(record, body, assetFiles = [], primitiveLayer) {
|
|
2314
|
+
return new _AgentPrimitive(headersOf(_AgentPrimitive, AgentHeadersSchema, record), body, assetFiles, primitiveLayer);
|
|
2550
2315
|
}
|
|
2551
2316
|
};
|
|
2552
2317
|
|
|
2553
|
-
// src/
|
|
2554
|
-
var
|
|
2555
|
-
|
|
2556
|
-
|
|
2557
|
-
|
|
2318
|
+
// src/hexagon/domain/models/charter/primitive/CorpusPrimitive.ts
|
|
2319
|
+
var CorpusHeadersSchema = CommonHeadersSchema.extend({});
|
|
2320
|
+
var CorpusPrimitive = class _CorpusPrimitive extends BasePrimitive {
|
|
2321
|
+
static kind = "corpus";
|
|
2322
|
+
kind = _CorpusPrimitive.kind;
|
|
2323
|
+
/** What this kind requires beyond the common headers: nothing. Declared all
|
|
2324
|
+
* the same, since what a file is scaffolded with is read off this (FR-039). */
|
|
2325
|
+
static requires = {};
|
|
2326
|
+
/** The schema its headers are read by: what `headersOf` refuses a file
|
|
2327
|
+
* against, and what says the shape of every header this kind takes, required
|
|
2328
|
+
* or not (FR-004). */
|
|
2329
|
+
static schema = CorpusHeadersSchema;
|
|
2330
|
+
/** When a reader of this charter is to open this kind at all, said
|
|
2331
|
+
* where the kind's contract is: the neutral surface lists one
|
|
2332
|
+
* line per kind and none of them is written down twice (FR-002). */
|
|
2333
|
+
static activatesWhen = "a primitive's `rationale` cites it \u2014 the reasoning, read when someone asks why";
|
|
2334
|
+
/** A corpus as an author writes one: what a refused header is fixed with. */
|
|
2335
|
+
static sample = {
|
|
2336
|
+
id: "type-safety",
|
|
2337
|
+
description: "Why the charter insists on types."
|
|
2338
|
+
};
|
|
2339
|
+
/** One corpus, or every fault its headers have (FR-004). */
|
|
2340
|
+
static of(record, body, assetFiles = [], primitiveLayer) {
|
|
2341
|
+
return new _CorpusPrimitive(headersOf(_CorpusPrimitive, CorpusHeadersSchema, record), body, assetFiles, primitiveLayer);
|
|
2558
2342
|
}
|
|
2559
2343
|
};
|
|
2560
|
-
|
|
2561
|
-
|
|
2562
|
-
|
|
2563
|
-
|
|
2564
|
-
|
|
2565
|
-
|
|
2566
|
-
|
|
2567
|
-
|
|
2568
|
-
|
|
2569
|
-
|
|
2570
|
-
|
|
2571
|
-
|
|
2344
|
+
|
|
2345
|
+
// src/hexagon/domain/models/charter/primitive/GuidePrimitive.ts
|
|
2346
|
+
var GuideHeadersSchema = CommonHeadersSchema.extend({});
|
|
2347
|
+
var GuidePrimitive = class _GuidePrimitive extends BasePrimitive {
|
|
2348
|
+
static kind = "guide";
|
|
2349
|
+
kind = _GuidePrimitive.kind;
|
|
2350
|
+
/** What this kind requires beyond the common headers: nothing. Declared all
|
|
2351
|
+
* the same, since what a file is scaffolded with is read off this (FR-039). */
|
|
2352
|
+
static requires = {};
|
|
2353
|
+
/** The schema its headers are read by: what `headersOf` refuses a file
|
|
2354
|
+
* against, and what says the shape of every header this kind takes, required
|
|
2355
|
+
* or not (FR-004). */
|
|
2356
|
+
static schema = GuideHeadersSchema;
|
|
2357
|
+
/** When a reader of this charter is to open this kind at all, said
|
|
2358
|
+
* where the kind's contract is: the neutral surface lists one
|
|
2359
|
+
* line per kind and none of them is written down twice (FR-002). */
|
|
2360
|
+
static activatesWhen = "a touched file matches one of its `globs`, or every turn where globs are not specified";
|
|
2361
|
+
/** A guide as an author writes one: what a refused header is fixed with. */
|
|
2362
|
+
static sample = {
|
|
2363
|
+
id: "no-any",
|
|
2364
|
+
description: "Reject the any type in application code.",
|
|
2365
|
+
globs: ["src/**/*.ts"],
|
|
2366
|
+
tags: ["typescript"],
|
|
2367
|
+
rationale: "type-safety"
|
|
2368
|
+
};
|
|
2369
|
+
/** One guide, or every fault its headers have (FR-004). */
|
|
2370
|
+
static of(record, body, assetFiles = [], primitiveLayer) {
|
|
2371
|
+
return new _GuidePrimitive(headersOf(_GuidePrimitive, GuideHeadersSchema, record), body, assetFiles, primitiveLayer);
|
|
2572
2372
|
}
|
|
2573
2373
|
};
|
|
2574
2374
|
|
|
2575
|
-
// src/
|
|
2576
|
-
|
|
2577
|
-
|
|
2578
|
-
|
|
2579
|
-
|
|
2375
|
+
// src/hexagon/domain/models/charter/primitive/McpPrimitive.ts
|
|
2376
|
+
import { z as z9 } from "zod";
|
|
2377
|
+
|
|
2378
|
+
// src/hexagon/domain/models/McpAuthMethod.ts
|
|
2379
|
+
var MCP_AUTH_METHODS = ["oauth", "token"];
|
|
2380
|
+
|
|
2381
|
+
// src/hexagon/domain/models/charter/primitive/McpPrimitive.ts
|
|
2382
|
+
function isSecureEndpoint(endpoint) {
|
|
2383
|
+
if (!URL.canParse(endpoint)) return false;
|
|
2384
|
+
const endpointURL = new URL(endpoint);
|
|
2385
|
+
return endpointURL.protocol === "https:" || endpointURL.protocol === "http:" && ["127.0.0.1", "localhost"].includes(endpointURL.hostname);
|
|
2386
|
+
}
|
|
2387
|
+
var McpHeadersSchema = CommonHeadersSchema.extend({
|
|
2388
|
+
/** Reached over HTTP. Plain `http` only to this machine, for a server under
|
|
2389
|
+
* test: anything else would send a developer's credential in the clear. */
|
|
2390
|
+
endpoint: GoodLineSchema.refine(isSecureEndpoint, {
|
|
2391
|
+
message: `"endpoint" is an https:// URL, or http:// to 127.0.0.1 or localhost for a server under test.`
|
|
2392
|
+
}).optional(),
|
|
2393
|
+
/** Started as a local process, spoken to over its standard input and output. */
|
|
2394
|
+
command: GoodLineSchema.optional(),
|
|
2395
|
+
args: z9.array(GoodLineSchema).readonly().optional(),
|
|
2396
|
+
auth: z9.array(z9.enum(MCP_AUTH_METHODS)).min(1).readonly().optional(),
|
|
2397
|
+
/** The environment variable a command reads its token from. */
|
|
2398
|
+
tokenEnv: z9.string().regex(/^[A-Za-z_][A-Za-z0-9_]*$/).optional(),
|
|
2399
|
+
/** The place inside that server: a repository, a database, a channel. */
|
|
2400
|
+
path: GoodLineSchema.optional(),
|
|
2401
|
+
/** The server's tools the agent may use there, by the server's own names. */
|
|
2402
|
+
tools: GoodArraySchema
|
|
2403
|
+
}).superRefine((headers, context) => {
|
|
2404
|
+
const addFault = (message) => context.addIssue({ code: "custom", message });
|
|
2405
|
+
const { endpoint, command, args, auth: auth2, tokenEnv } = headers;
|
|
2406
|
+
if (endpoint !== void 0 && command !== void 0)
|
|
2407
|
+
return addFault(`This declares both "endpoint" and "command"; an mcp is one server, reached one way.`);
|
|
2408
|
+
if (endpoint === void 0 && command === void 0)
|
|
2409
|
+
return addFault(`This declares neither "endpoint" nor "command", so there is no server to reach.`);
|
|
2410
|
+
if (endpoint !== void 0) {
|
|
2411
|
+
if (auth2 === void 0) addFault(`An endpoint says how a developer signs in to it: "auth" of oauth, token or both.`);
|
|
2412
|
+
if (args !== void 0) addFault(`"args" are a command's; an endpoint takes none.`);
|
|
2413
|
+
if (tokenEnv !== void 0) addFault(`"tokenEnv" is where a command reads its token; an endpoint takes none.`);
|
|
2414
|
+
return;
|
|
2415
|
+
}
|
|
2416
|
+
const takesToken = auth2 !== void 0;
|
|
2417
|
+
if (takesToken && (auth2.length !== 1 || auth2[0] !== "token"))
|
|
2418
|
+
addFault(`A command signs in by token alone: "auth: [token]", or no "auth" for one that takes none.`);
|
|
2419
|
+
if (takesToken && tokenEnv === void 0) addFault(`A command taking a token names where it reads it: "tokenEnv".`);
|
|
2420
|
+
if (!takesToken && tokenEnv !== void 0) addFault(`"tokenEnv" is read only by a command taking a token: add "auth: [token]", or drop it.`);
|
|
2421
|
+
});
|
|
2422
|
+
var McpPrimitive = class _McpPrimitive extends BasePrimitive {
|
|
2423
|
+
static kind = "mcp";
|
|
2424
|
+
kind = _McpPrimitive.kind;
|
|
2425
|
+
/** What this kind requires beyond the common headers. `endpoint` or
|
|
2426
|
+
* `command` is required too, but which depends on the shape, so the schema's
|
|
2427
|
+
* refinement says it rather than this (FR-004, FR-039). */
|
|
2428
|
+
static requires = { tools: "list" };
|
|
2429
|
+
/** The schema its headers are read by: what `headersOf` refuses a file
|
|
2430
|
+
* against, and what says the shape of every header this kind takes, required
|
|
2431
|
+
* or not (FR-004). */
|
|
2432
|
+
static schema = McpHeadersSchema;
|
|
2433
|
+
/** When a reader of this charter is to open this kind at all, said
|
|
2434
|
+
* where the kind's contract is: the neutral surface lists one
|
|
2435
|
+
* line per kind and none of them is written down twice (FR-002). */
|
|
2436
|
+
static activatesWhen = "a primitive's body names it as `[[<id>]]` \u2014 a place outside the repository, reached through `cw mcp serve`";
|
|
2437
|
+
/** An mcp reached at an endpoint, as an author writes one: what a refused
|
|
2438
|
+
* header is fixed with. */
|
|
2439
|
+
static sample = {
|
|
2440
|
+
id: "mfbs/billing",
|
|
2441
|
+
description: "Billing service code and pull requests.",
|
|
2442
|
+
endpoint: "https://api.githubcopilot.com/mcp/",
|
|
2443
|
+
path: "moneyforward/billing-service",
|
|
2444
|
+
auth: ["oauth", "token"],
|
|
2445
|
+
tools: ["get_file_contents", "search_code", "list_pull_requests"]
|
|
2446
|
+
};
|
|
2447
|
+
/** The same place started as a local process: what a refused file declaring
|
|
2448
|
+
* `command` is held up against, since that is the shape it was nearer. */
|
|
2449
|
+
static commandSample = {
|
|
2450
|
+
id: "mfbs/billing",
|
|
2451
|
+
description: "Billing service code and pull requests.",
|
|
2452
|
+
command: "npx",
|
|
2453
|
+
args: ["-y", "@modelcontextprotocol/server-github"],
|
|
2454
|
+
tokenEnv: "GITHUB_PERSONAL_ACCESS_TOKEN",
|
|
2455
|
+
auth: ["token"],
|
|
2456
|
+
path: "moneyforward/billing-service",
|
|
2457
|
+
tools: ["get_file_contents", "search_code", "list_pull_requests"]
|
|
2458
|
+
};
|
|
2459
|
+
/** What a developer signs in to, and what places are told apart by beside
|
|
2460
|
+
* their `path`: the endpoint, or the command followed by each of its
|
|
2461
|
+
* arguments. The refinement has already held that one of the two is here
|
|
2462
|
+
* (FR-142). */
|
|
2463
|
+
get address() {
|
|
2464
|
+
const { endpoint, command, args = [] } = this.headers;
|
|
2465
|
+
return endpoint ?? [command, ...args].join(" ");
|
|
2580
2466
|
}
|
|
2581
|
-
|
|
2582
|
-
|
|
2583
|
-
|
|
2584
|
-
|
|
2585
|
-
|
|
2586
|
-
async run({ testAuthoringApp: testAuthoringApp2 }, { name }) {
|
|
2587
|
-
const removedName = await testAuthoringApp2.removeSuite(name ?? "");
|
|
2588
|
-
return { code: EXIT_OK, result: `Removed ${TEST_DIRECTORY}/${removedName}.
|
|
2589
|
-
` };
|
|
2467
|
+
/** One mcp, or every fault its headers have, shown the sample of the shape
|
|
2468
|
+
* it declared (FR-004, FR-142). */
|
|
2469
|
+
static of(record, body, assetFiles = [], primitiveLayer) {
|
|
2470
|
+
const nearerSample = record.command === void 0 ? _McpPrimitive.sample : _McpPrimitive.commandSample;
|
|
2471
|
+
return new _McpPrimitive(headersOf({ kind: _McpPrimitive.kind, sample: nearerSample }, McpHeadersSchema, record), body, assetFiles, primitiveLayer);
|
|
2590
2472
|
}
|
|
2591
2473
|
};
|
|
2592
2474
|
|
|
2593
|
-
// src/
|
|
2594
|
-
|
|
2595
|
-
|
|
2596
|
-
|
|
2597
|
-
|
|
2598
|
-
|
|
2599
|
-
|
|
2600
|
-
|
|
2601
|
-
|
|
2602
|
-
|
|
2603
|
-
|
|
2604
|
-
|
|
2605
|
-
|
|
2606
|
-
|
|
2607
|
-
|
|
2608
|
-
|
|
2609
|
-
|
|
2610
|
-
|
|
2611
|
-
|
|
2612
|
-
|
|
2613
|
-
|
|
2614
|
-
|
|
2615
|
-
|
|
2616
|
-
|
|
2617
|
-
|
|
2618
|
-
|
|
2619
|
-
|
|
2620
|
-
|
|
2621
|
-
|
|
2475
|
+
// src/hexagon/domain/models/charter/primitive/MixinPrimitive.ts
|
|
2476
|
+
import { z as z10 } from "zod";
|
|
2477
|
+
var MixinHeadersSchema = CommonHeadersSchema.extend({
|
|
2478
|
+
/** Never: reading refuses a mixin that declares one. */
|
|
2479
|
+
mixins: z10.undefined().optional()
|
|
2480
|
+
});
|
|
2481
|
+
var MixinPrimitive = class _MixinPrimitive extends BasePrimitive {
|
|
2482
|
+
static kind = "mixin";
|
|
2483
|
+
kind = _MixinPrimitive.kind;
|
|
2484
|
+
/** What this kind requires beyond the common headers: nothing. Declared all
|
|
2485
|
+
* the same, since what a file is scaffolded with is read off this (FR-039). */
|
|
2486
|
+
static requires = {};
|
|
2487
|
+
/** The schema its headers are read by: what `headersOf` refuses a file
|
|
2488
|
+
* against, and what says the shape of every header this kind takes, required
|
|
2489
|
+
* or not (FR-004). */
|
|
2490
|
+
static schema = MixinHeadersSchema;
|
|
2491
|
+
/** When a reader of this charter is to open this kind at all, said
|
|
2492
|
+
* where the kind's contract is: the neutral surface lists one
|
|
2493
|
+
* line per kind and none of them is written down twice (FR-002). */
|
|
2494
|
+
static activatesWhen = "never on its own: its body is lent to the primitives that pull it in";
|
|
2495
|
+
/** A mixin as an author writes one: what a refused header is fixed with. */
|
|
2496
|
+
static sample = {
|
|
2497
|
+
id: "typescript-scope",
|
|
2498
|
+
description: "What counts as TypeScript application code.",
|
|
2499
|
+
globs: ["src/**/*.ts"]
|
|
2500
|
+
};
|
|
2501
|
+
/** One mixin, or every fault its headers have (FR-004, FR-006). */
|
|
2502
|
+
static of(record, body, assetFiles = [], primitiveLayer) {
|
|
2503
|
+
return new _MixinPrimitive(headersOf(_MixinPrimitive, MixinHeadersSchema, record), body, assetFiles, primitiveLayer);
|
|
2622
2504
|
}
|
|
2623
2505
|
};
|
|
2624
|
-
function lines2(one) {
|
|
2625
|
-
return [
|
|
2626
|
-
` ${one.data.passed ? "pass" : "FAIL"} ${one.data.situation}`,
|
|
2627
|
-
...one.data.unmet === void 0 ? [] : [` ${one.data.unmet.data.message}`, ` ${one.data.unmet.data.fix}`]
|
|
2628
|
-
];
|
|
2629
|
-
}
|
|
2630
2506
|
|
|
2631
|
-
// src/
|
|
2632
|
-
var
|
|
2633
|
-
|
|
2634
|
-
|
|
2635
|
-
|
|
2636
|
-
|
|
2637
|
-
|
|
2638
|
-
|
|
2639
|
-
|
|
2507
|
+
// src/hexagon/domain/models/charter/primitive/PlaybookPrimitive.ts
|
|
2508
|
+
var PlaybookHeadersSchema = CommonHeadersSchema.extend({
|
|
2509
|
+
triggers: GoodArraySchema
|
|
2510
|
+
});
|
|
2511
|
+
var PlaybookPrimitive = class _PlaybookPrimitive extends BasePrimitive {
|
|
2512
|
+
static kind = "playbook";
|
|
2513
|
+
kind = _PlaybookPrimitive.kind;
|
|
2514
|
+
/** What this kind requires beyond the common headers, declared where its
|
|
2515
|
+
* contract is: it is read both where a file is checked and where one is
|
|
2516
|
+
* scaffolded (FR-004, FR-039). */
|
|
2517
|
+
static requires = { triggers: "list" };
|
|
2518
|
+
/** The schema its headers are read by: what `headersOf` refuses a file
|
|
2519
|
+
* against, and what says the shape of every header this kind takes, required
|
|
2520
|
+
* or not (FR-004). */
|
|
2521
|
+
static schema = PlaybookHeadersSchema;
|
|
2522
|
+
/** When a reader of this charter is to open this kind at all, said
|
|
2523
|
+
* where the kind's contract is: the neutral surface lists one
|
|
2524
|
+
* line per kind and none of them is written down twice (FR-002). */
|
|
2525
|
+
static activatesWhen = "one of its `triggers` matches; the body is a sequence of skills";
|
|
2526
|
+
/** A playbook as an author writes one: what a refused header is fixed with. */
|
|
2527
|
+
static sample = {
|
|
2528
|
+
id: "ship-a-feature",
|
|
2529
|
+
description: "Take a finished branch from review to release.",
|
|
2530
|
+
triggers: ["ship this", "release this feature"]
|
|
2531
|
+
};
|
|
2532
|
+
/** What this is found by: what it is for, and the requests its author said
|
|
2533
|
+
* bring it up. */
|
|
2534
|
+
description() {
|
|
2535
|
+
return `${this.headers.description} Use when: ${this.headers.triggers.join("; ")}.`;
|
|
2640
2536
|
}
|
|
2641
|
-
|
|
2642
|
-
|
|
2643
|
-
|
|
2644
|
-
summary = "Install a charter from a git source, or bring an installed one up to date";
|
|
2645
|
-
options = OPTIONS11;
|
|
2646
|
-
async run({ charterVendoringApp: charterVendoringApp2 }, { source, ref }) {
|
|
2647
|
-
const folder = await charterVendoringApp2.add(source ?? "", ref);
|
|
2648
|
-
return {
|
|
2649
|
-
code: EXIT_OK,
|
|
2650
|
-
result: [
|
|
2651
|
-
`Installed ${source} under ${folder}${ref === void 0 ? "" : `, pinned to ${ref}`}.`,
|
|
2652
|
-
'Run "cw build" to compile what it adds to this charter.',
|
|
2653
|
-
""
|
|
2654
|
-
].join("\n")
|
|
2655
|
-
};
|
|
2537
|
+
/** One playbook, or every fault its headers have (FR-004). */
|
|
2538
|
+
static of(record, body, assetFiles = [], primitiveLayer) {
|
|
2539
|
+
return new _PlaybookPrimitive(headersOf(_PlaybookPrimitive, PlaybookHeadersSchema, record), body, assetFiles, primitiveLayer);
|
|
2656
2540
|
}
|
|
2657
2541
|
};
|
|
2658
2542
|
|
|
2659
|
-
// src/
|
|
2660
|
-
var
|
|
2661
|
-
|
|
2662
|
-
|
|
2663
|
-
|
|
2664
|
-
|
|
2665
|
-
|
|
2666
|
-
|
|
2667
|
-
|
|
2668
|
-
|
|
2543
|
+
// src/hexagon/domain/models/charter/primitive/PosturePrimitive.ts
|
|
2544
|
+
var PostureHeadersSchema = CommonHeadersSchema.extend({
|
|
2545
|
+
allow: GoodArraySchema,
|
|
2546
|
+
deny: GoodArraySchema
|
|
2547
|
+
});
|
|
2548
|
+
var PosturePrimitive = class _PosturePrimitive extends BasePrimitive {
|
|
2549
|
+
static kind = "posture";
|
|
2550
|
+
kind = _PosturePrimitive.kind;
|
|
2551
|
+
/** What this kind requires beyond the common headers, declared where its
|
|
2552
|
+
* contract is: it is read both where a file is checked and where one is
|
|
2553
|
+
* scaffolded (FR-004, FR-039). */
|
|
2554
|
+
static requires = { allow: "list", deny: "list" };
|
|
2555
|
+
/** The schema its headers are read by: what `headersOf` refuses a file
|
|
2556
|
+
* against, and what says the shape of every header this kind takes, required
|
|
2557
|
+
* or not (FR-004). */
|
|
2558
|
+
static schema = PostureHeadersSchema;
|
|
2559
|
+
/** When a reader of this charter is to open this kind at all, said
|
|
2560
|
+
* where the kind's contract is: the neutral surface lists one
|
|
2561
|
+
* line per kind and none of them is written down twice (FR-002). */
|
|
2562
|
+
static activatesWhen = "always, wherever the host can be told what to `allow` and what to `deny`";
|
|
2563
|
+
/** A posture as an author writes one: what a refused header is fixed with. */
|
|
2564
|
+
static sample = {
|
|
2565
|
+
id: "no-force-push",
|
|
2566
|
+
description: "Never rewrite history someone else may have pulled.",
|
|
2567
|
+
allow: ["Bash(git push:*)"],
|
|
2568
|
+
deny: ["Bash(git push --force:*)"]
|
|
2569
|
+
};
|
|
2570
|
+
/** One posture, or every fault its headers have (FR-004). */
|
|
2571
|
+
static of(record, body, assetFiles = [], primitiveLayer) {
|
|
2572
|
+
return new _PosturePrimitive(headersOf(_PosturePrimitive, PostureHeadersSchema, record), body, assetFiles, primitiveLayer);
|
|
2669
2573
|
}
|
|
2670
2574
|
};
|
|
2671
2575
|
|
|
2672
|
-
// src/
|
|
2673
|
-
var
|
|
2674
|
-
|
|
2675
|
-
|
|
2676
|
-
|
|
2576
|
+
// src/hexagon/domain/models/charter/primitive/SkillPrimitive.ts
|
|
2577
|
+
var SkillHeadersSchema = CommonHeadersSchema.extend({
|
|
2578
|
+
triggers: GoodArraySchema
|
|
2579
|
+
});
|
|
2580
|
+
var SkillPrimitive = class _SkillPrimitive extends BasePrimitive {
|
|
2581
|
+
static kind = "skill";
|
|
2582
|
+
kind = _SkillPrimitive.kind;
|
|
2583
|
+
/** What this kind requires beyond the common headers, declared where its
|
|
2584
|
+
* contract is: it is read both where a file is checked and where one is
|
|
2585
|
+
* scaffolded (FR-004, FR-039). */
|
|
2586
|
+
static requires = { triggers: "list" };
|
|
2587
|
+
/** The schema its headers are read by: what `headersOf` refuses a file
|
|
2588
|
+
* against, and what says the shape of every header this kind takes, required
|
|
2589
|
+
* or not (FR-004). */
|
|
2590
|
+
static schema = SkillHeadersSchema;
|
|
2591
|
+
/** When a reader of this charter is to open this kind at all, said
|
|
2592
|
+
* where the kind's contract is: the neutral surface lists one
|
|
2593
|
+
* line per kind and none of them is written down twice (FR-002). */
|
|
2594
|
+
static activatesWhen = "one of its `triggers` matches what is being asked";
|
|
2595
|
+
/** A skill as an author writes one: what a refused header is fixed with. */
|
|
2596
|
+
static sample = {
|
|
2597
|
+
id: "refactoring",
|
|
2598
|
+
description: "Change how code is structured without changing what it does.",
|
|
2599
|
+
triggers: ["refactor this", "clean this up"]
|
|
2600
|
+
};
|
|
2601
|
+
/** What this is found by: what it is for, and the requests its author said
|
|
2602
|
+
* bring it up. */
|
|
2603
|
+
description() {
|
|
2604
|
+
return `${this.headers.description} Use when: ${this.headers.triggers.join("; ")}.`;
|
|
2605
|
+
}
|
|
2606
|
+
/** One skill, or every fault its headers have (FR-004). */
|
|
2607
|
+
static of(record, body, assetFiles = [], primitiveLayer) {
|
|
2608
|
+
return new _SkillPrimitive(headersOf(_SkillPrimitive, SkillHeadersSchema, record), body, assetFiles, primitiveLayer);
|
|
2677
2609
|
}
|
|
2678
2610
|
};
|
|
2679
|
-
|
|
2680
|
-
|
|
2681
|
-
|
|
2682
|
-
|
|
2683
|
-
|
|
2684
|
-
|
|
2685
|
-
|
|
2686
|
-
|
|
2687
|
-
|
|
2688
|
-
|
|
2689
|
-
|
|
2690
|
-
|
|
2691
|
-
|
|
2692
|
-
|
|
2611
|
+
|
|
2612
|
+
// src/hexagon/domain/models/charter/primitive/ScriptPrimitive.ts
|
|
2613
|
+
import { z as z11 } from "zod";
|
|
2614
|
+
var ScriptHeadersSchema = CommonHeadersSchema.extend({
|
|
2615
|
+
/** The one asset that runs, from the script's `index.md`: `./` and a path
|
|
2616
|
+
* inside its folder, no segment of which climbs out again. Letters, digits,
|
|
2617
|
+
* `.`, `_` and `-` only: it is written into commands a shell reads
|
|
2618
|
+
* (FR-169). */
|
|
2619
|
+
executionPath: z11.string().regex(/^\.\/[A-Za-z0-9._-]+(\/[A-Za-z0-9._-]+)*$/).refine((executionPath) => !executionPath.split("/").slice(1).some((segment) => segment === "." || segment === ".."), {
|
|
2620
|
+
message: `"executionPath" stays inside the script's own folder: no segment of it is "." or "..".`
|
|
2621
|
+
})
|
|
2622
|
+
});
|
|
2623
|
+
var ScriptPrimitive = class _ScriptPrimitive extends BasePrimitive {
|
|
2624
|
+
static kind = "script";
|
|
2625
|
+
kind = _ScriptPrimitive.kind;
|
|
2626
|
+
/** What this kind requires beyond the common headers, declared where its
|
|
2627
|
+
* contract is (FR-004, FR-039). */
|
|
2628
|
+
static requires = { executionPath: "line" };
|
|
2629
|
+
/** The schema its headers are read by (FR-004). */
|
|
2630
|
+
static schema = ScriptHeadersSchema;
|
|
2631
|
+
/** When a reader of this charter is to open this kind at all (FR-002). */
|
|
2632
|
+
static activatesWhen = "a primitive names it as `[[<id>]]`, or a sensor runs it \u2014 its assets, copied to `.cw/out/script/<id>/`, of which its `executionPath` names the one to run";
|
|
2633
|
+
/** A script as an author writes one: what a refused header is fixed with. */
|
|
2634
|
+
static sample = {
|
|
2635
|
+
id: "check-changelog",
|
|
2636
|
+
description: "Fail when CHANGELOG.md holds no entry for the version being released.",
|
|
2637
|
+
executionPath: "./run.sh"
|
|
2638
|
+
};
|
|
2639
|
+
/** One script, or every fault its headers have (FR-004). */
|
|
2640
|
+
static of(record, body, assetFiles = [], primitiveLayer) {
|
|
2641
|
+
return new _ScriptPrimitive(headersOf(_ScriptPrimitive, ScriptHeadersSchema, record), body, assetFiles, primitiveLayer);
|
|
2642
|
+
}
|
|
2643
|
+
/** The asset that runs, as a path inside its folder: `run.sh` of
|
|
2644
|
+
* `./run.sh`. */
|
|
2645
|
+
get executionPathInFolder() {
|
|
2646
|
+
return this.headers.executionPath.slice("./".length);
|
|
2647
|
+
}
|
|
2648
|
+
/** Whether an asset of it is the one its `executionPath` names: the one
|
|
2649
|
+
* run by its path, and no other (FR-160). */
|
|
2650
|
+
isExecutionFile(file) {
|
|
2651
|
+
return file === this.executionPathInFolder;
|
|
2693
2652
|
}
|
|
2694
2653
|
};
|
|
2695
2654
|
|
|
2696
|
-
// src/
|
|
2697
|
-
var
|
|
2698
|
-
|
|
2699
|
-
|
|
2700
|
-
|
|
2701
|
-
|
|
2702
|
-
|
|
2703
|
-
|
|
2704
|
-
|
|
2705
|
-
|
|
2706
|
-
|
|
2707
|
-
|
|
2708
|
-
|
|
2709
|
-
|
|
2710
|
-
|
|
2711
|
-
|
|
2712
|
-
|
|
2713
|
-
|
|
2714
|
-
|
|
2715
|
-
|
|
2716
|
-
new McpServeCommand()
|
|
2717
|
-
];
|
|
2718
|
-
|
|
2719
|
-
// src/driver/cli/Commander.ts
|
|
2720
|
-
var Commander = class {
|
|
2721
|
-
#context;
|
|
2722
|
-
#commands;
|
|
2723
|
-
constructor(context, commands = COMMANDS) {
|
|
2724
|
-
this.#context = context;
|
|
2725
|
-
this.#commands = commands;
|
|
2726
|
-
}
|
|
2727
|
-
async run(argv) {
|
|
2728
|
-
let code = EXIT_OK;
|
|
2729
|
-
let failed = false;
|
|
2730
|
-
await this.#parser((outcome) => {
|
|
2731
|
-
code = outcome.code;
|
|
2732
|
-
if (outcome.result) process.stdout.write(outcome.result);
|
|
2733
|
-
if (outcome.problem) process.stderr.write(outcome.problem);
|
|
2734
|
-
}).parseAsync([...argv], {}, (error, _parsed, output) => {
|
|
2735
|
-
failed = error !== null && error !== void 0;
|
|
2736
|
-
if (output) (failed ? process.stderr : process.stdout).write(`${output}
|
|
2737
|
-
`);
|
|
2738
|
-
});
|
|
2739
|
-
return failed ? EXIT_USAGE : code;
|
|
2655
|
+
// src/hexagon/domain/models/charter/primitive/TemplatePrimitive.ts
|
|
2656
|
+
var TemplateHeadersSchema = CommonHeadersSchema.extend({});
|
|
2657
|
+
var TemplatePrimitive = class _TemplatePrimitive extends BasePrimitive {
|
|
2658
|
+
static kind = "template";
|
|
2659
|
+
kind = _TemplatePrimitive.kind;
|
|
2660
|
+
/** What this kind requires beyond the common headers: nothing. Declared all
|
|
2661
|
+
* the same, since what a file is scaffolded with is read off this (FR-039). */
|
|
2662
|
+
static requires = {};
|
|
2663
|
+
/** The schema its headers are read by (FR-004). */
|
|
2664
|
+
static schema = TemplateHeadersSchema;
|
|
2665
|
+
/** When a reader of this charter is to open this kind at all (FR-002). */
|
|
2666
|
+
static activatesWhen = "a primitive names it as `[[<id>]]` \u2014 a file the agent fills or copies, written as its body";
|
|
2667
|
+
/** A template as an author writes one: what a refused header is fixed with. */
|
|
2668
|
+
static sample = {
|
|
2669
|
+
id: "release-note",
|
|
2670
|
+
description: "The shape every release note follows."
|
|
2671
|
+
};
|
|
2672
|
+
/** One template, or every fault its headers have (FR-004). */
|
|
2673
|
+
static of(record, body, assetFiles = [], primitiveLayer) {
|
|
2674
|
+
return new _TemplatePrimitive(headersOf(_TemplatePrimitive, TemplateHeadersSchema, record), body, assetFiles, primitiveLayer);
|
|
2740
2675
|
}
|
|
2741
|
-
|
|
2742
|
-
|
|
2743
|
-
|
|
2744
|
-
|
|
2745
|
-
|
|
2746
|
-
|
|
2747
|
-
|
|
2748
|
-
|
|
2749
|
-
|
|
2750
|
-
|
|
2751
|
-
|
|
2752
|
-
|
|
2753
|
-
|
|
2754
|
-
|
|
2755
|
-
|
|
2756
|
-
|
|
2757
|
-
|
|
2758
|
-
|
|
2759
|
-
|
|
2676
|
+
};
|
|
2677
|
+
|
|
2678
|
+
// src/hexagon/domain/models/charter/primitive/Primitive.ts
|
|
2679
|
+
var PRIMITIVE_CLASSES = [
|
|
2680
|
+
GuidePrimitive,
|
|
2681
|
+
SensorPrimitive,
|
|
2682
|
+
SkillPrimitive,
|
|
2683
|
+
PlaybookPrimitive,
|
|
2684
|
+
AgentPrimitive,
|
|
2685
|
+
PosturePrimitive,
|
|
2686
|
+
CorpusPrimitive,
|
|
2687
|
+
MixinPrimitive,
|
|
2688
|
+
McpPrimitive,
|
|
2689
|
+
ScriptPrimitive,
|
|
2690
|
+
TemplatePrimitive
|
|
2691
|
+
];
|
|
2692
|
+
var KINDS = Object.freeze(PRIMITIVE_CLASSES.map((one) => one.kind));
|
|
2693
|
+
function primitiveIdOf(typed) {
|
|
2694
|
+
if (!CommonHeadersSchema.shape.id.safeParse(typed).success)
|
|
2695
|
+
throw new CharterPrimitiveFault(
|
|
2696
|
+
`"${typed}" is not an id: one is lowercase letters, digits and inner hyphens, in segments joined by "/".`,
|
|
2697
|
+
'Type it as the primitive declares it, as in "no-any".'
|
|
2698
|
+
);
|
|
2699
|
+
return typed;
|
|
2700
|
+
}
|
|
2701
|
+
function isKind(value) {
|
|
2702
|
+
return typeof value === "string" && KINDS.includes(value);
|
|
2703
|
+
}
|
|
2704
|
+
var CLASS_OF = new Map(
|
|
2705
|
+
PRIMITIVE_CLASSES.map((one) => [one.kind, one])
|
|
2706
|
+
);
|
|
2707
|
+
var PrimitiveHeader = class {
|
|
2708
|
+
constructor(field, shape, required, allowedValues) {
|
|
2709
|
+
this.field = field;
|
|
2710
|
+
this.shape = shape;
|
|
2711
|
+
this.required = required;
|
|
2712
|
+
this.allowedValues = allowedValues;
|
|
2760
2713
|
}
|
|
2761
|
-
|
|
2762
|
-
|
|
2763
|
-
|
|
2764
|
-
|
|
2765
|
-
|
|
2766
|
-
|
|
2767
|
-
|
|
2768
|
-
|
|
2769
|
-
|
|
2770
|
-
try {
|
|
2771
|
-
collect(await command.run(this.#context, options));
|
|
2772
|
-
} catch (raised) {
|
|
2773
|
-
if (!(raised instanceof DomainFault)) throw raised;
|
|
2774
|
-
collect({ code: EXIT_FAILURE, problem: `${raised.message}
|
|
2775
|
-
${raised.fix}
|
|
2776
|
-
` });
|
|
2777
|
-
}
|
|
2778
|
-
}
|
|
2779
|
-
});
|
|
2714
|
+
field;
|
|
2715
|
+
shape;
|
|
2716
|
+
required;
|
|
2717
|
+
allowedValues;
|
|
2718
|
+
};
|
|
2719
|
+
var PrimitiveRequirements = class {
|
|
2720
|
+
constructor(headers, sample) {
|
|
2721
|
+
this.headers = headers;
|
|
2722
|
+
this.sample = sample;
|
|
2780
2723
|
}
|
|
2724
|
+
headers;
|
|
2725
|
+
sample;
|
|
2781
2726
|
};
|
|
2782
|
-
function
|
|
2783
|
-
|
|
2784
|
-
}
|
|
2785
|
-
|
|
2786
|
-
|
|
2787
|
-
|
|
2788
|
-
|
|
2789
|
-
|
|
2790
|
-
|
|
2791
|
-
|
|
2792
|
-
|
|
2793
|
-
|
|
2794
|
-
|
|
2795
|
-
|
|
2796
|
-
};
|
|
2797
|
-
return name.includes(`<${argument}>`) || name.includes(`[${argument}]`) ? parser.positional(argument, declared) : parser.option(argument, declared);
|
|
2798
|
-
}, cli2);
|
|
2727
|
+
function primitiveHeadersOf(kind) {
|
|
2728
|
+
const kindClass = CLASS_OF.get(kind);
|
|
2729
|
+
const required = { ...BasePrimitive.requires, ...kindClass.requires };
|
|
2730
|
+
return Object.entries(kindClass.schema.shape).filter(([field]) => field !== "id").map(([field, type]) => {
|
|
2731
|
+
let heldType = type;
|
|
2732
|
+
while (!(heldType instanceof z12.ZodArray) && typeof heldType?.unwrap === "function")
|
|
2733
|
+
heldType = heldType.unwrap();
|
|
2734
|
+
return new PrimitiveHeader(
|
|
2735
|
+
field,
|
|
2736
|
+
heldType instanceof z12.ZodArray ? "list" : "line",
|
|
2737
|
+
field in required,
|
|
2738
|
+
heldType instanceof z12.ZodEnum ? heldType.options : void 0
|
|
2739
|
+
);
|
|
2740
|
+
});
|
|
2799
2741
|
}
|
|
2800
|
-
|
|
2801
|
-
|
|
2802
|
-
import picomatch from "picomatch";
|
|
2803
|
-
function covers(outer, inner) {
|
|
2804
|
-
return outer === inner || matches(outer, inner);
|
|
2742
|
+
function primitiveSampleOf(kind) {
|
|
2743
|
+
return Object.entries({ kind, ...CLASS_OF.get(kind).sample }).map(([field, value]) => `${field}: ${formatFrontmatterValue(value)}`).join("\n");
|
|
2805
2744
|
}
|
|
2806
|
-
function
|
|
2807
|
-
|
|
2745
|
+
function primitiveOf(input, parser, assetFiles = [], primitiveLayer) {
|
|
2746
|
+
let headers;
|
|
2747
|
+
let body;
|
|
2748
|
+
if (typeof input === "string") {
|
|
2749
|
+
const lines3 = input.replace(/^/, "").split(/\r?\n/);
|
|
2750
|
+
if (lines3[0]?.trim() !== DELIMITER)
|
|
2751
|
+
throwAggregateError([
|
|
2752
|
+
new CharterPrimitiveFault(
|
|
2753
|
+
`This file opens with no "${DELIMITER}" frontmatter block.`,
|
|
2754
|
+
`Add one at the top declaring at least "id" and "description".`
|
|
2755
|
+
)
|
|
2756
|
+
]);
|
|
2757
|
+
const end = lines3.findIndex((line, index) => index > 0 && line.trim() === DELIMITER);
|
|
2758
|
+
if (end === -1)
|
|
2759
|
+
throwAggregateError([
|
|
2760
|
+
new CharterPrimitiveFault(
|
|
2761
|
+
`The frontmatter block is never closed.`,
|
|
2762
|
+
`Add a "${DELIMITER}" line after the headers.`
|
|
2763
|
+
)
|
|
2764
|
+
]);
|
|
2765
|
+
headers = parser.parse(lines3.slice(1, end).join("\n"));
|
|
2766
|
+
body = lines3.slice(end + 1).join("\n").replace(/^(\s*\n)+/, "").trimEnd();
|
|
2767
|
+
} else {
|
|
2768
|
+
headers = input.headers;
|
|
2769
|
+
body = input.body;
|
|
2770
|
+
}
|
|
2771
|
+
if (!isKind(headers.kind))
|
|
2772
|
+
throwAggregateError([
|
|
2773
|
+
headers.kind === void 0 ? new CharterPrimitiveFault(
|
|
2774
|
+
`This file declares no "kind", so nothing knows what to read it as.`,
|
|
2775
|
+
`Add "kind: <one the charter knows>" to the frontmatter.`
|
|
2776
|
+
) : new CharterPrimitiveFault(
|
|
2777
|
+
`"${String(headers.kind)}" is not a kind the charter knows.`,
|
|
2778
|
+
`Declare a kind the charter knows: ${KINDS.join(", ")}.`
|
|
2779
|
+
)
|
|
2780
|
+
]);
|
|
2781
|
+
const kind = headers.kind;
|
|
2782
|
+
if (typeof input === "string") return CLASS_OF.get(kind).of(headers, body, assetFiles, primitiveLayer);
|
|
2783
|
+
const headerFields = primitiveHeadersOf(kind).map((header) => header.field);
|
|
2784
|
+
const unknownHeaderFaults = Object.keys(headers).filter((field) => field !== "kind" && field !== "id" && !headerFields.includes(field)).map(
|
|
2785
|
+
(field) => new CharterPrimitiveFault(
|
|
2786
|
+
`A ${kind} holds no "${field}".`,
|
|
2787
|
+
`Answer one of what a ${kind} holds: ${headerFields.join(", ")}.`
|
|
2788
|
+
)
|
|
2789
|
+
);
|
|
2790
|
+
let primitive;
|
|
2791
|
+
let schemaFaults = [];
|
|
2792
|
+
try {
|
|
2793
|
+
primitive = CLASS_OF.get(kind).of(headers, body);
|
|
2794
|
+
} catch (raised) {
|
|
2795
|
+
if (!(raised instanceof AggregateError)) throw raised;
|
|
2796
|
+
schemaFaults = raised.errors;
|
|
2797
|
+
}
|
|
2798
|
+
if (primitive === void 0 || unknownHeaderFaults.length > 0) throwAggregateError([...unknownHeaderFaults, ...schemaFaults]);
|
|
2799
|
+
return primitive;
|
|
2808
2800
|
}
|
|
2809
2801
|
|
|
2810
2802
|
// src/hexagon/domain/models/charter/CharterRoot.ts
|
|
@@ -2823,48 +2815,38 @@ var CharterRoot = class {
|
|
|
2823
2815
|
get allFaultsByFiles() {
|
|
2824
2816
|
return this.faultsByFiles.with(this.compositeFaultsByFiles);
|
|
2825
2817
|
}
|
|
2826
|
-
/** Every mixin, under the id a host names it by, wherever it was authored. */
|
|
2827
|
-
get mixins() {
|
|
2828
|
-
return new Map(
|
|
2829
|
-
this.primitives.filter((primitive) => primitive.kind === "mixin").map((one) => [one.headers.id, one])
|
|
2830
|
-
);
|
|
2831
|
-
}
|
|
2832
2818
|
/**
|
|
2833
|
-
* Every primitive under the
|
|
2819
|
+
* Every primitive under the id the whole charter names it by (FR-015,
|
|
2820
|
+
* FR-017).
|
|
2834
2821
|
*
|
|
2835
|
-
*
|
|
2836
|
-
*
|
|
2837
|
-
*
|
|
2838
|
-
*
|
|
2839
|
-
* The first claim on an identity is the one in here where two files claim it.
|
|
2840
|
-
* A collision is what `compositeFaultsByFiles` is for, and it names both files
|
|
2822
|
+
* One id space covers every layer and every kind, so which file declares a
|
|
2823
|
+
* name is a question about the whole charter and not about any one layer.
|
|
2824
|
+
* The first claim on an id is the one in here where two files claim it: a
|
|
2825
|
+
* collision is what `compositeFaultsByFiles` is for, and it names both files
|
|
2841
2826
|
* there; nothing is asked to explain a charter that has one (SC-006).
|
|
2842
2827
|
*/
|
|
2843
2828
|
get primitiveById() {
|
|
2844
|
-
const
|
|
2845
|
-
for (const one of this.primitives) if (!
|
|
2846
|
-
return
|
|
2847
|
-
}
|
|
2848
|
-
/** Every corpus, under the identity a primitive cites it by, wherever it was
|
|
2849
|
-
* authored: `corpus:<id>`. */
|
|
2850
|
-
get corpora() {
|
|
2851
|
-
return new Set(
|
|
2852
|
-
this.primitives.filter((primitive) => primitive.kind === "corpus").map((one) => one.identity)
|
|
2853
|
-
);
|
|
2829
|
+
const primitiveById = /* @__PURE__ */ new Map();
|
|
2830
|
+
for (const one of this.primitives) if (!primitiveById.has(one.headers.id)) primitiveById.set(one.headers.id, one);
|
|
2831
|
+
return primitiveById;
|
|
2854
2832
|
}
|
|
2855
2833
|
/** Every mixin one primitive uses, in the order it named them (FR-014). A
|
|
2856
|
-
* name nothing answers to is left out:
|
|
2857
|
-
* names that file as an error. */
|
|
2834
|
+
* name nothing answers to, or that answers with another kind, is left out:
|
|
2835
|
+
* `compositeFaultsByFiles` already names that file as an error. */
|
|
2858
2836
|
mixinsOf(one) {
|
|
2859
|
-
const
|
|
2860
|
-
return (one.headers.mixins ?? []).flatMap((id) =>
|
|
2837
|
+
const primitiveById = this.primitiveById;
|
|
2838
|
+
return (one.headers.mixins ?? []).flatMap((id) => {
|
|
2839
|
+
const mixin = primitiveById.get(id);
|
|
2840
|
+
return mixin?.kind === "mixin" ? [mixin] : [];
|
|
2841
|
+
});
|
|
2861
2842
|
}
|
|
2862
2843
|
/** The corpus one primitive cites as its rationale, or nothing where it cites
|
|
2863
|
-
* none or cites one this charter does not hold
|
|
2844
|
+
* none or cites one this charter does not hold as a corpus — which
|
|
2864
2845
|
* `compositeFaultsByFiles` already names (FR-005, FR-014). */
|
|
2865
2846
|
rationaleOf(one) {
|
|
2866
2847
|
const { rationale } = one.headers;
|
|
2867
|
-
|
|
2848
|
+
const corpusPrimitive = rationale === void 0 ? void 0 : this.primitiveById.get(rationale);
|
|
2849
|
+
return corpusPrimitive?.kind === "corpus" ? corpusPrimitive : void 0;
|
|
2868
2850
|
}
|
|
2869
2851
|
/** Every primitive using one mixin; nothing for any other kind (FR-014). */
|
|
2870
2852
|
hostsOf(mixin) {
|
|
@@ -2875,24 +2857,21 @@ var CharterRoot = class {
|
|
|
2875
2857
|
* kind (FR-014). */
|
|
2876
2858
|
citersOf(corpus) {
|
|
2877
2859
|
if (corpus.kind !== "corpus") return [];
|
|
2878
|
-
return this.primitives.filter((citer) => citer.headers.rationale === corpus.
|
|
2879
|
-
}
|
|
2880
|
-
/** Every primitive naming one
|
|
2881
|
-
|
|
2882
|
-
|
|
2883
|
-
|
|
2884
|
-
|
|
2885
|
-
|
|
2886
|
-
*
|
|
2887
|
-
* FR-161)
|
|
2888
|
-
|
|
2889
|
-
identitiesMentionedIn(one) {
|
|
2860
|
+
return this.primitives.filter((citer) => citer.headers.rationale === corpus.headers.id);
|
|
2861
|
+
}
|
|
2862
|
+
/** Every primitive naming one, as `[[<id>]]` (FR-144, FR-163, FR-172). */
|
|
2863
|
+
mentionersOf(referencedPrimitive) {
|
|
2864
|
+
return this.primitives.filter((mentioner) => this.idsMentionedIn(mentioner).includes(referencedPrimitive.headers.id));
|
|
2865
|
+
}
|
|
2866
|
+
/** The ids one primitive names as `[[<id>]]`, whether the charter holds them
|
|
2867
|
+
* or not: in its body (FR-143, FR-172); for an agent, each place it holds by
|
|
2868
|
+
* listing it among its tools (FR-156); and for a sensor, each script its
|
|
2869
|
+
* `run` names (FR-161). */
|
|
2870
|
+
idsMentionedIn(one) {
|
|
2890
2871
|
return [
|
|
2891
|
-
...[...one.body.matchAll(
|
|
2892
|
-
...
|
|
2893
|
-
...[...one.
|
|
2894
|
-
...one instanceof AgentPrimitive ? one.headers.tools.flatMap((tool) => tool.match(MCP_TOOL_REFERENCE)?.[1] ?? []) : [],
|
|
2895
|
-
...one instanceof SensorPrimitive ? [...one.headers.run.matchAll(SCRIPT_MENTION)].map(([mentionedIdentity]) => mentionedIdentity) : []
|
|
2872
|
+
...[...one.body.matchAll(REFERENCE)].map(([, id]) => id),
|
|
2873
|
+
...one instanceof AgentPrimitive ? one.headers.tools.flatMap((tool) => tool.match(TOOL_REFERENCE)?.[1] ?? []) : [],
|
|
2874
|
+
...one instanceof SensorPrimitive ? [...one.headers.run.matchAll(REFERENCE)].map(([, id]) => id) : []
|
|
2896
2875
|
];
|
|
2897
2876
|
}
|
|
2898
2877
|
/** Does what a mixin lends reach as far as the host pulling it in, or the host
|
|
@@ -2911,8 +2890,8 @@ var CharterRoot = class {
|
|
|
2911
2890
|
*
|
|
2912
2891
|
* Reading refused the files whose own headers do not hold, and said so where
|
|
2913
2892
|
* it read them; what is left are the questions no single file answers. Every
|
|
2914
|
-
* layer is read into one
|
|
2915
|
-
* per package: two files claim one
|
|
2893
|
+
* layer is read into one id space, the way one registry holds one name
|
|
2894
|
+
* per package: two files claim one id; one names a mixin nothing
|
|
2916
2895
|
* answers to; one pulls in a mixin that speaks about other files than it does
|
|
2917
2896
|
* — a mixin lends its body at projection time, so what makes the two belong
|
|
2918
2897
|
* together is the files they both apply to (FR-006, FR-014). One cites a
|
|
@@ -2928,52 +2907,46 @@ var CharterRoot = class {
|
|
|
2928
2907
|
*/
|
|
2929
2908
|
get compositeFaultsByFiles() {
|
|
2930
2909
|
const everyPrimitive = this.primitives;
|
|
2931
|
-
const
|
|
2932
|
-
const
|
|
2933
|
-
const
|
|
2934
|
-
|
|
2935
|
-
);
|
|
2936
|
-
const everyIdentity = new Set(everyPrimitive.map(({ identity }) => identity));
|
|
2937
|
-
const mentionedIdentities = new Set(everyPrimitive.flatMap((one) => this.identitiesMentionedIn(one)));
|
|
2938
|
-
const fileByIdentity = /* @__PURE__ */ new Map();
|
|
2939
|
-
const identityByNormalizedIdentity = /* @__PURE__ */ new Map();
|
|
2910
|
+
const primitiveById = this.primitiveById;
|
|
2911
|
+
const referencedIds = new Set(everyPrimitive.flatMap((one) => this.idsMentionedIn(one)));
|
|
2912
|
+
const fileById = /* @__PURE__ */ new Map();
|
|
2913
|
+
const idByNormId = /* @__PURE__ */ new Map();
|
|
2940
2914
|
const faultsByFiles = {};
|
|
2941
2915
|
const addFault = (file, found) => {
|
|
2942
2916
|
faultsByFiles[file] = [...faultsByFiles[file] ?? [], found];
|
|
2943
2917
|
};
|
|
2944
|
-
for (const
|
|
2945
|
-
const { file } =
|
|
2946
|
-
const
|
|
2947
|
-
const
|
|
2948
|
-
|
|
2949
|
-
const declaredIn = fileByIdentity.get(identity);
|
|
2950
|
-
if (declaredIn === void 0) fileByIdentity.set(identity, file);
|
|
2918
|
+
for (const primitive of everyPrimitive) {
|
|
2919
|
+
const { file } = primitive;
|
|
2920
|
+
const { id } = primitive.headers;
|
|
2921
|
+
const declaringFile = fileById.get(id);
|
|
2922
|
+
if (declaringFile === void 0) fileById.set(id, file);
|
|
2951
2923
|
else
|
|
2952
2924
|
addFault(
|
|
2953
2925
|
file,
|
|
2954
2926
|
new CharterRootFault(
|
|
2955
|
-
`"${
|
|
2927
|
+
`"${id}" is already declared by ${declaringFile}. One id names one primitive in a charter, whatever its kind and whichever layer it was authored in.`,
|
|
2956
2928
|
`Give this one an id of its own, or delete it if the other says the same thing. A vendor claiming an id you authored is one to raise with whoever publishes it. An id ${BUILTIN_PRIMITIVE_LAYER.charterFolder} claims is the engine's own and nobody can change it, so the one to rename is yours.`
|
|
2957
2929
|
)
|
|
2958
2930
|
);
|
|
2959
|
-
const
|
|
2960
|
-
|
|
2961
|
-
|
|
2931
|
+
const { normId } = primitive;
|
|
2932
|
+
const claimingId = idByNormId.get(normId);
|
|
2933
|
+
if (claimingId === void 0) idByNormId.set(normId, id);
|
|
2934
|
+
else if (claimingId !== id)
|
|
2962
2935
|
addFault(
|
|
2963
2936
|
file,
|
|
2964
2937
|
new CharterRootFault(
|
|
2965
|
-
`"${
|
|
2938
|
+
`"${id}" and "${claimingId}" are both named "${normId}" in an agent's files, where "/" is written as "-". Only one of them would be written there.`,
|
|
2966
2939
|
`Give this one an id that stays its own once "/" is written as "-".`
|
|
2967
2940
|
)
|
|
2968
2941
|
);
|
|
2969
|
-
for (const
|
|
2970
|
-
const mixin =
|
|
2971
|
-
if (mixin
|
|
2942
|
+
for (const mixinId of primitive.headers.mixins ?? []) {
|
|
2943
|
+
const mixin = primitiveById.get(mixinId);
|
|
2944
|
+
if (mixin?.kind !== "mixin") {
|
|
2972
2945
|
addFault(
|
|
2973
2946
|
file,
|
|
2974
2947
|
new CharterRootFault(
|
|
2975
|
-
`This pulls in the mixin "${
|
|
2976
|
-
`Author it, or drop "${
|
|
2948
|
+
`This pulls in the mixin "${mixinId}", and this charter holds no mixin of that id.`,
|
|
2949
|
+
`Author it, or drop "${mixinId}" from "mixins". A mixin is named by its id, whichever layer authored it.`
|
|
2977
2950
|
)
|
|
2978
2951
|
);
|
|
2979
2952
|
continue;
|
|
@@ -2982,30 +2955,29 @@ var CharterRoot = class {
|
|
|
2982
2955
|
addFault(
|
|
2983
2956
|
file,
|
|
2984
2957
|
new CharterRootFault(
|
|
2985
|
-
`This pulls in the mixin "${
|
|
2958
|
+
`This pulls in the mixin "${mixinId}", and neither one's "globs" covers the other's. The mixin's body would be written into a primitive that applies to different files.`,
|
|
2986
2959
|
`Widen one side's "globs" until it covers the other's, or move the shared text into a corpus and cite it with "rationale".`
|
|
2987
2960
|
)
|
|
2988
2961
|
);
|
|
2989
2962
|
}
|
|
2990
2963
|
}
|
|
2991
|
-
const
|
|
2992
|
-
if (
|
|
2964
|
+
const rationaleId = primitive.headers.rationale;
|
|
2965
|
+
if (rationaleId !== void 0 && primitiveById.get(rationaleId)?.kind !== "corpus")
|
|
2993
2966
|
addFault(
|
|
2994
2967
|
file,
|
|
2995
2968
|
new CharterRootFault(
|
|
2996
|
-
`This cites "${
|
|
2997
|
-
`Author that corpus, correct the reference, or drop "rationale". A corpus is cited
|
|
2969
|
+
`This cites "${rationaleId}" as its rationale, and this charter holds no corpus of that id.`,
|
|
2970
|
+
`Author that corpus, correct the reference, or drop "rationale". A corpus is cited by its id, whichever layer authored it.`,
|
|
2998
2971
|
"warn"
|
|
2999
2972
|
)
|
|
3000
2973
|
);
|
|
3001
|
-
|
|
3002
|
-
|
|
3003
|
-
if (!everyIdentity.has(mentionedIdentity))
|
|
2974
|
+
for (const referencedId of new Set(this.idsMentionedIn(primitive)))
|
|
2975
|
+
if (!primitiveById.has(referencedId))
|
|
3004
2976
|
addFault(
|
|
3005
2977
|
file,
|
|
3006
2978
|
new CharterRootFault(
|
|
3007
|
-
`This names "${
|
|
3008
|
-
`Author it, or correct the name. It is named as "<
|
|
2979
|
+
`This names "${referencedId}", and this charter holds no primitive of that id.`,
|
|
2980
|
+
`Author it, or correct the name. It is named as "[[<id>]]", whichever layer authored it.`
|
|
3009
2981
|
)
|
|
3010
2982
|
);
|
|
3011
2983
|
if (primitive instanceof ScriptPrimitive && !primitive.assets.some((assetFile) => primitive.isExecutionFile(assetFile.file)))
|
|
@@ -3017,55 +2989,55 @@ var CharterRoot = class {
|
|
|
3017
2989
|
)
|
|
3018
2990
|
);
|
|
3019
2991
|
if (primitive instanceof AgentPrimitive)
|
|
3020
|
-
for (const tool of primitive.headers.tools.filter((
|
|
3021
|
-
const [,
|
|
3022
|
-
const
|
|
3023
|
-
if (
|
|
2992
|
+
for (const tool of primitive.headers.tools.filter((one) => one.startsWith("[["))) {
|
|
2993
|
+
const [, mcpId, mcpTool] = tool.match(TOOL_REFERENCE) ?? [];
|
|
2994
|
+
const mcpPrimitive = mcpId === void 0 ? void 0 : primitiveById.get(mcpId);
|
|
2995
|
+
if (mcpId === void 0)
|
|
3024
2996
|
addFault(
|
|
3025
2997
|
file,
|
|
3026
2998
|
new CharterRootFault(
|
|
3027
2999
|
`"${tool}" under "tools" is not a place this role can hold.`,
|
|
3028
|
-
`Write a place as "
|
|
3000
|
+
`Write a place as "[[<id>]]" for every tool it declares, or "[[<id>]]:<tool>" for one.`
|
|
3029
3001
|
)
|
|
3030
3002
|
);
|
|
3031
|
-
else if (
|
|
3003
|
+
else if (mcpPrimitive instanceof McpPrimitive && mcpTool !== void 0 && !mcpPrimitive.headers.tools.includes(mcpTool))
|
|
3032
3004
|
addFault(
|
|
3033
3005
|
file,
|
|
3034
3006
|
new CharterRootFault(
|
|
3035
|
-
`"${tool}" under "tools" names a tool "${
|
|
3036
|
-
`Name one of those, or add "${mcpTool}" to the tools of "${
|
|
3007
|
+
`"${tool}" under "tools" names a tool "${mcpId}" does not declare; it declares ${mcpPrimitive.headers.tools.join(", ")}.`,
|
|
3008
|
+
`Name one of those, or add "${mcpTool}" to the tools of "${mcpId}".`
|
|
3037
3009
|
)
|
|
3038
3010
|
);
|
|
3039
3011
|
}
|
|
3040
|
-
if (primitive.kind === "corpus" && this.citersOf(
|
|
3012
|
+
if (primitive.kind === "corpus" && this.citersOf(primitive).length === 0)
|
|
3041
3013
|
addFault(
|
|
3042
3014
|
file,
|
|
3043
3015
|
new CharterRootFault(
|
|
3044
|
-
`No primitive cites "${
|
|
3045
|
-
`Cite it from the rules it explains with "rationale: ${
|
|
3016
|
+
`No primitive cites "${id}" as its rationale, so nothing an agent reads leads to it.`,
|
|
3017
|
+
`Cite it from the rules it explains with "rationale: ${id}", or delete it.`,
|
|
3046
3018
|
"warn"
|
|
3047
3019
|
)
|
|
3048
3020
|
);
|
|
3049
|
-
if (primitive.kind === "mixin" && this.hostsOf(
|
|
3021
|
+
if (primitive.kind === "mixin" && this.hostsOf(primitive).length === 0)
|
|
3050
3022
|
addFault(
|
|
3051
3023
|
file,
|
|
3052
3024
|
new CharterRootFault(
|
|
3053
|
-
`No primitive pulls in the mixin "${
|
|
3054
|
-
`Name "${
|
|
3025
|
+
`No primitive pulls in the mixin "${id}", so its body is never written anywhere.`,
|
|
3026
|
+
`Name "${id}" under "mixins" of the primitives it was written for, or delete it.`,
|
|
3055
3027
|
"warn"
|
|
3056
3028
|
)
|
|
3057
3029
|
);
|
|
3058
|
-
if ((primitive.kind === "mcp" || primitive.kind === "script" || primitive.kind === "template") && !
|
|
3030
|
+
if ((primitive.kind === "mcp" || primitive.kind === "script" || primitive.kind === "template") && !referencedIds.has(id))
|
|
3059
3031
|
addFault(
|
|
3060
3032
|
file,
|
|
3061
3033
|
new CharterRootFault(
|
|
3062
|
-
`No primitive names "${
|
|
3063
|
-
`Name it where it is used, as "${
|
|
3034
|
+
`No primitive names "${id}" in its body, so nothing an agent reads leads to it.`,
|
|
3035
|
+
`Name it where it is used, as "[[${id}]]", or delete it.`,
|
|
3064
3036
|
"warn"
|
|
3065
3037
|
)
|
|
3066
3038
|
);
|
|
3067
3039
|
}
|
|
3068
|
-
const mcpPrimitives = everyPrimitive.flatMap((primitive) => primitive instanceof McpPrimitive ? [primitive] : []).sort((one, another) => one.
|
|
3040
|
+
const mcpPrimitives = everyPrimitive.flatMap((primitive) => primitive instanceof McpPrimitive ? [primitive] : []).sort((one, another) => one.headers.id < another.headers.id ? -1 : 1);
|
|
3069
3041
|
for (const primitive of mcpPrimitives) {
|
|
3070
3042
|
const { file } = primitive;
|
|
3071
3043
|
const { tokenEnv } = primitive.headers;
|
|
@@ -3076,7 +3048,7 @@ var CharterRoot = class {
|
|
|
3076
3048
|
addFault(
|
|
3077
3049
|
file,
|
|
3078
3050
|
new CharterRootFault(
|
|
3079
|
-
`This hands "${primitive.address}" its token in "${tokenEnv}", and "${conflictingMcp.
|
|
3051
|
+
`This hands "${primitive.address}" its token in "${tokenEnv}", and "${conflictingMcp.headers.id}" in "${conflictingMcp.headers.tokenEnv}". One process reads its token from one variable.`,
|
|
3080
3052
|
`Name the variable that command reads in both, or drop "tokenEnv" and "auth" from one of them.`
|
|
3081
3053
|
)
|
|
3082
3054
|
);
|
|
@@ -3107,7 +3079,7 @@ function charterRootOf({
|
|
|
3107
3079
|
if (one.pathInLayer !== `${primitive.primitiveFolder}/${primitive.index}`) {
|
|
3108
3080
|
faultsByFiles[one.path] = [
|
|
3109
3081
|
new CharterRootFault(
|
|
3110
|
-
`This declares "${primitive.
|
|
3082
|
+
`This declares "${primitive.kind}" "${primitive.headers.id}", and it is kept at "${one.pathInLayer}"; a primitive is kept at "<kind>/<id>/${BasePrimitive.index}".`,
|
|
3111
3083
|
`Move its folder to "${charterFolder}/${primitive.primitiveFolder}/", or declare the kind and id its folder says.`
|
|
3112
3084
|
)
|
|
3113
3085
|
];
|
|
@@ -3144,7 +3116,7 @@ var CwAuthorSkill = class extends SkillPrimitive {
|
|
|
3144
3116
|
|
|
3145
3117
|
This skill is cw's own. It comes with the engine, is not authored in this
|
|
3146
3118
|
repository, and cannot be changed here. A repository that wants another way of
|
|
3147
|
-
authoring writes a primitive of its own, under an
|
|
3119
|
+
authoring writes a primitive of its own, under an id of its own.
|
|
3148
3120
|
|
|
3149
3121
|
It covers writing a new primitive. Changing or removing one that already exists
|
|
3150
3122
|
is not what it covers.
|
|
@@ -3166,7 +3138,10 @@ refused against.
|
|
|
3166
3138
|
cw refuses, it says why and shows the kind's sample: answer again with that
|
|
3167
3139
|
in hand. Nothing is written until the headers hold.
|
|
3168
3140
|
4. Write the body into the file cw named. The headers decide when the primitive
|
|
3169
|
-
comes up; the body is what an agent reads once it has.
|
|
3141
|
+
comes up; the body is what an agent reads once it has. Name another
|
|
3142
|
+
primitive by its id alone: bare in a header, and as \`[[<id>]]\` in the
|
|
3143
|
+
body, which the build turns into a link to it. An id names one primitive
|
|
3144
|
+
whatever its kind.
|
|
3170
3145
|
5. Run \`cw build\`. If it refuses, \`cw doctor\` names each file with a problem
|
|
3171
3146
|
and how to fix it.`,
|
|
3172
3147
|
[],
|
|
@@ -3228,7 +3203,7 @@ async function writeCharter(repo, primitive, fileReader2, fileWriter2) {
|
|
|
3228
3203
|
if (await fileReader2.readIfThere(file) !== void 0)
|
|
3229
3204
|
throw new DomainFault(
|
|
3230
3205
|
`${primitive.file} is already there, and writing this one would write over what it holds.`,
|
|
3231
|
-
`Open it, or run this again with an
|
|
3206
|
+
`Open it, or run this again with an id this charter has not got.`
|
|
3232
3207
|
);
|
|
3233
3208
|
const folder = new URL(".", file).href;
|
|
3234
3209
|
const primitiveInTheWay = (await fileReader2.readFilesRecursively(new URL(`${primitive.kind}/`, charterFolderIn(repo)))).map((readFile2) => readFile2.file.href).find((href) => href.endsWith(`/${BasePrimitive.index}`) && (folder.startsWith(new URL(".", href).href) || href.startsWith(folder)));
|
|
@@ -3392,15 +3367,16 @@ function runSuite(charter, suiteName, suite) {
|
|
|
3392
3367
|
return suite.cases.map((one) => runCase(charter, suiteName, one));
|
|
3393
3368
|
}
|
|
3394
3369
|
function findUntestedPrimitives(charter, testSuites) {
|
|
3395
|
-
const
|
|
3370
|
+
const testedIds = new Set(testSuites.flatMap((testSuite) => testSuite.cases.map((one) => one.activatedId)));
|
|
3396
3371
|
const faultsByFiles = {};
|
|
3397
3372
|
for (const primitive of charter.primitives) {
|
|
3398
|
-
const {
|
|
3399
|
-
|
|
3373
|
+
const { file } = primitive;
|
|
3374
|
+
const { id } = primitive.headers;
|
|
3375
|
+
if (primitive.kind !== "guide" && primitive.kind !== "sensor" || testedIds.has(id)) continue;
|
|
3400
3376
|
faultsByFiles[file] = [
|
|
3401
3377
|
new TestCaseFault(
|
|
3402
|
-
`No test case names "${
|
|
3403
|
-
primitive.kind === "guide" ? `Add a case to a file under .cw/test/: { "do": { "touchFile": "<a file it speaks about>" }, "expect": { "activate": "${
|
|
3378
|
+
`No test case names "${id}", so nothing notices when it stops coming up where it should.`,
|
|
3379
|
+
primitive.kind === "guide" ? `Add a case to a file under .cw/test/: { "do": { "touchFile": "<a file it speaks about>" }, "expect": { "activate": "${id}" } }.` : `Add a case to a file under .cw/test/: { "when": "${primitive.headers.signal}", "expect": { "run": "${id}" } }.`,
|
|
3404
3380
|
"warn"
|
|
3405
3381
|
)
|
|
3406
3382
|
];
|
|
@@ -3423,22 +3399,22 @@ function runCase(charter, suiteName, one) {
|
|
|
3423
3399
|
}
|
|
3424
3400
|
throw new Error(`Never happen`);
|
|
3425
3401
|
}
|
|
3426
|
-
function assertPrimitiveActivated(charter, file,
|
|
3427
|
-
const declared = charter.primitiveById.get(
|
|
3428
|
-
if (declared === void 0) return primitiveNotFound(
|
|
3402
|
+
function assertPrimitiveActivated(charter, file, id) {
|
|
3403
|
+
const declared = charter.primitiveById.get(id);
|
|
3404
|
+
if (declared === void 0) return primitiveNotFound(id);
|
|
3429
3405
|
if (declared.kind !== "guide")
|
|
3430
3406
|
return new TestCaseFault(
|
|
3431
|
-
`Coming up when a file is touched is a guide's rule, and "${
|
|
3407
|
+
`Coming up when a file is touched is a guide's rule, and "${id}" is a ${declared.kind}.`,
|
|
3432
3408
|
`Expect a guide here, or put the situation that kind answers.`
|
|
3433
3409
|
);
|
|
3434
3410
|
const { globs = [] } = declared.headers;
|
|
3435
3411
|
if (globs.length === 0)
|
|
3436
3412
|
return new TestCaseFault(
|
|
3437
|
-
`"${
|
|
3413
|
+
`"${id}" names no files, so touching one never brings it up.`,
|
|
3438
3414
|
`Give that guide "globs", or expect nothing of it here.`
|
|
3439
3415
|
);
|
|
3440
3416
|
return globs.some((glob) => matches(glob, file)) ? void 0 : new TestCaseFault(
|
|
3441
|
-
`This file matches none of the globs "${
|
|
3417
|
+
`This file matches none of the globs "${id}" speaks about: ${globs.join(", ")}.`,
|
|
3442
3418
|
`Widen that guide's "globs", or touch a file it already speaks about.`
|
|
3443
3419
|
);
|
|
3444
3420
|
}
|
|
@@ -3448,7 +3424,7 @@ function assertAllowed(charter, file, expected) {
|
|
|
3448
3424
|
);
|
|
3449
3425
|
if (expected)
|
|
3450
3426
|
return denying.length === 0 ? void 0 : new TestCaseFault(
|
|
3451
|
-
`${denying.map((one) => `"${one.
|
|
3427
|
+
`${denying.map((one) => `"${one.headers.id}"`).join(", ")} denies this file.`,
|
|
3452
3428
|
`Drop it from that posture's "deny", or expect "allow": false.`
|
|
3453
3429
|
);
|
|
3454
3430
|
const postures = charter.primitives.filter((primitive) => primitive.kind === "posture");
|
|
@@ -3462,24 +3438,24 @@ function assertAllowed(charter, file, expected) {
|
|
|
3462
3438
|
`Add it to a posture's "deny", or expect "allow": true.`
|
|
3463
3439
|
);
|
|
3464
3440
|
}
|
|
3465
|
-
function assertScriptRun(charter, event,
|
|
3466
|
-
const declared = charter.primitiveById.get(
|
|
3467
|
-
if (declared === void 0) return primitiveNotFound(
|
|
3441
|
+
function assertScriptRun(charter, event, id) {
|
|
3442
|
+
const declared = charter.primitiveById.get(id);
|
|
3443
|
+
if (declared === void 0) return primitiveNotFound(id);
|
|
3468
3444
|
if (declared.kind !== "sensor")
|
|
3469
3445
|
return new TestCaseFault(
|
|
3470
|
-
`Running when an event is raised is a sensor's rule, and "${
|
|
3446
|
+
`Running when an event is raised is a sensor's rule, and "${id}" is a ${declared.kind}.`,
|
|
3471
3447
|
`Expect a sensor here, or put the situation that kind answers.`
|
|
3472
3448
|
);
|
|
3473
3449
|
const { signal } = declared.headers;
|
|
3474
3450
|
return signal === event ? void 0 : new TestCaseFault(
|
|
3475
|
-
`"${
|
|
3451
|
+
`"${id}" fires on ${signal}, not on ${event}.`,
|
|
3476
3452
|
`Raise the event it names, or declare the "signal" this case raises.`
|
|
3477
3453
|
);
|
|
3478
3454
|
}
|
|
3479
|
-
function primitiveNotFound(
|
|
3455
|
+
function primitiveNotFound(id) {
|
|
3480
3456
|
return new TestCaseFault(
|
|
3481
|
-
`This charter holds no primitive called "${
|
|
3482
|
-
`Author it, or correct the name. "cw list --min" says every
|
|
3457
|
+
`This charter holds no primitive called "${id}".`,
|
|
3458
|
+
`Author it, or correct the name. "cw list --min" says every id this charter does hold.`
|
|
3483
3459
|
);
|
|
3484
3460
|
}
|
|
3485
3461
|
|
|
@@ -3492,7 +3468,7 @@ var RepoLayerPrimitive = class {
|
|
|
3492
3468
|
|
|
3493
3469
|
// src/hexagon/domain/models/output/common/Catalogue.ts
|
|
3494
3470
|
var Catalogue = class _Catalogue {
|
|
3495
|
-
/** Entries in the order they are to be listed: by
|
|
3471
|
+
/** Entries in the order they are to be listed: by id (SC-007). */
|
|
3496
3472
|
constructor(entries) {
|
|
3497
3473
|
this.entries = entries;
|
|
3498
3474
|
}
|
|
@@ -3518,7 +3494,7 @@ var Catalogue = class _Catalogue {
|
|
|
3518
3494
|
*
|
|
3519
3495
|
* A narrowed listing is a listing: what comes back answers `entries` the way
|
|
3520
3496
|
* this does, so whoever reads it never has to ask whether it was narrowed. Nothing is re-sorted — the entries keep the order they were
|
|
3521
|
-
* put in, which is the order by
|
|
3497
|
+
* put in, which is the order by id they were made in.
|
|
3522
3498
|
*/
|
|
3523
3499
|
filterByKind(kind) {
|
|
3524
3500
|
return new _Catalogue(this.entries.filter((one) => one.kind === kind));
|
|
@@ -3528,13 +3504,13 @@ var Catalogue = class _Catalogue {
|
|
|
3528
3504
|
// src/hexagon/domain/services/compile/catalogueFactory.ts
|
|
3529
3505
|
function catalogueOf(charter, fileOf) {
|
|
3530
3506
|
return new Catalogue(
|
|
3531
|
-
// Ordered by
|
|
3507
|
+
// Ordered by id rather than by the order the files happened to be
|
|
3532
3508
|
// read, so the same charter catalogues byte for byte the same (SC-007).
|
|
3533
|
-
[...charter.primitives].sort((one, another) => one.
|
|
3509
|
+
[...charter.primitives].sort((one, another) => one.headers.id < another.headers.id ? -1 : one.headers.id > another.headers.id ? 1 : 0).map((one) => {
|
|
3534
3510
|
const { description, globs } = one.headers;
|
|
3535
3511
|
return {
|
|
3536
|
-
identity: one.identity,
|
|
3537
3512
|
kind: one.kind,
|
|
3513
|
+
id: one.headers.id,
|
|
3538
3514
|
description,
|
|
3539
3515
|
file: fileOf(one),
|
|
3540
3516
|
...globs === void 0 ? {} : { globs }
|
|
@@ -3622,11 +3598,11 @@ function charterMdOf(kinds) {
|
|
|
3622
3598
|
"# Charter",
|
|
3623
3599
|
`This repository is governed by a charter \u2014 the standards it authored under \`${CHARTER_DIRECTORY}/\`, which constrain whatever coding agent runs here. Every surface an agent reads, this file included, is generated from that charter: do not edit them, edit the primitive behind them.`,
|
|
3624
3600
|
"## Read first",
|
|
3625
|
-
`[catalog.json](./catalog.json) \u2014 the kind,
|
|
3601
|
+
`[catalog.json](./catalog.json) \u2014 the kind, id, description and file of every primitive this charter holds, with its globs. Survey it to find what you need, and open a file only once its activation condition below is met.`,
|
|
3626
3602
|
"## When each kind applies",
|
|
3627
3603
|
kinds.map((one) => `- **${one.kind}** \u2014 ${one.activatesWhen}.`).join("\n"),
|
|
3628
|
-
"## One
|
|
3629
|
-
`An
|
|
3604
|
+
"## One id, one primitive",
|
|
3605
|
+
`An id names one primitive in the whole charter, whatever its kind: it is all a primitive is named by \u2014 bare in a header, a test or a command, and as \`[[<id>]]\` in a body, a sensor's \`run\` or an agent's \`tools\` \u2014 whether this repository authored it, installed it from a vendor, or the engine brought it. Nothing overrides anything \u2014 two files claiming one id is an error the charter refuses to build with.`
|
|
3630
3606
|
].join("\n\n")}
|
|
3631
3607
|
`));
|
|
3632
3608
|
}
|
|
@@ -3662,13 +3638,11 @@ var PRIMITIVES_MENTION = new RegExp(
|
|
|
3662
3638
|
/^ {0,3}(`{3,}|~{3,})[\s\S]*?(?:^ {0,3}\1[^\n]*$|(?![\s\S]))/.source,
|
|
3663
3639
|
/\[[^\]\n]*\]\([^)\n]*\)/.source,
|
|
3664
3640
|
/`[^`\n]*`/.source,
|
|
3665
|
-
|
|
3666
|
-
SCRIPT_MENTION.source,
|
|
3667
|
-
TEMPLATE_MENTION.source
|
|
3641
|
+
REFERENCE.source
|
|
3668
3642
|
].join("|"),
|
|
3669
3643
|
"gm"
|
|
3670
3644
|
);
|
|
3671
|
-
var PRIMITIVES_EXACT_MENTION = new RegExp(
|
|
3645
|
+
var PRIMITIVES_EXACT_MENTION = new RegExp(`^\`${REFERENCE.source}\`$`);
|
|
3672
3646
|
function compiledPrimitiveOf(charter, primitive) {
|
|
3673
3647
|
const markdown = primitive.toMarkdown({
|
|
3674
3648
|
// A mixin nothing answers to is left out: the charter already names that
|
|
@@ -3676,9 +3650,11 @@ function compiledPrimitiveOf(charter, primitive) {
|
|
|
3676
3650
|
mixins: charter.mixinsOf(primitive).flatMap((mixin) => mixin instanceof MixinPrimitive ? [mixin] : []),
|
|
3677
3651
|
idReplacer: (body) => body.replace(PRIMITIVES_MENTION, (found) => {
|
|
3678
3652
|
const exactFound = PRIMITIVES_EXACT_MENTION.test(found);
|
|
3679
|
-
|
|
3680
|
-
|
|
3681
|
-
|
|
3653
|
+
const isReference = /^\[\[[^\]]*\]\]$/.test(found);
|
|
3654
|
+
if (!exactFound && !isReference) return found;
|
|
3655
|
+
const referencedId = (exactFound ? found.slice(1, -1) : found).slice(2, -2);
|
|
3656
|
+
const referencedPrimitive = charter.primitiveById.get(referencedId);
|
|
3657
|
+
return referencedPrimitive === void 0 ? found : `[${exactFound ? `\`${referencedId}\`` : referencedId}](${"../".repeat(primitive.primitiveFolder.split("/").length)}${referencedPrimitive.primitiveFolder}/${referencedPrimitive.index})`;
|
|
3682
3658
|
})
|
|
3683
3659
|
});
|
|
3684
3660
|
return new CompiledPrimitive(
|
|
@@ -3835,18 +3811,18 @@ var ClaudeSettings = class _ClaudeSettings extends SettingBasedComponent {
|
|
|
3835
3811
|
};
|
|
3836
3812
|
|
|
3837
3813
|
// src/hexagon/domain/services/compile/claudeComponentFactory.ts
|
|
3838
|
-
function claudeComponentsOf(charter, compiledPrimitiveByPrimitive,
|
|
3814
|
+
function claudeComponentsOf(charter, compiledPrimitiveByPrimitive, shortMcpIds) {
|
|
3839
3815
|
const isSettingComponent = (one) => one.kind === PosturePrimitive.kind || one.kind === SensorPrimitive.kind;
|
|
3840
3816
|
const asSettings = charter.primitives.filter(isSettingComponent);
|
|
3841
3817
|
const asDocuments = charter.primitives.filter((one) => !isSettingComponent(one));
|
|
3842
|
-
const
|
|
3843
|
-
charter.primitives.flatMap((primitive) => primitive instanceof McpPrimitive ? [[primitive.
|
|
3818
|
+
const toolsByMcpId = new Map(
|
|
3819
|
+
charter.primitives.flatMap((primitive) => primitive instanceof McpPrimitive ? [[primitive.headers.id, primitive.headers.tools]] : [])
|
|
3844
3820
|
);
|
|
3845
3821
|
const agentToolsOf = (agent) => agent.headers.tools.flatMap((tool) => {
|
|
3846
|
-
const [,
|
|
3847
|
-
if (
|
|
3848
|
-
return (mcpTool === void 0 ?
|
|
3849
|
-
(oneTool) => `mcp__${ClaudeMcpConfig.cwMcpName}__${
|
|
3822
|
+
const [, mcpId, mcpTool] = tool.match(TOOL_REFERENCE) ?? [];
|
|
3823
|
+
if (mcpId === void 0) return [tool];
|
|
3824
|
+
return (mcpTool === void 0 ? toolsByMcpId.get(mcpId) ?? [] : [mcpTool]).map(
|
|
3825
|
+
(oneTool) => `mcp__${ClaudeMcpConfig.cwMcpName}__${shortMcpIds[mcpId]}__${oneTool}`
|
|
3850
3826
|
);
|
|
3851
3827
|
});
|
|
3852
3828
|
return [
|
|
@@ -3877,10 +3853,10 @@ function claudeSettingsOf(charter, compiledPrimitiveByPrimitive, ones) {
|
|
|
3877
3853
|
}
|
|
3878
3854
|
if (one.kind === SensorPrimitive.kind) {
|
|
3879
3855
|
const { signal, run: run2 } = one.headers;
|
|
3880
|
-
const command = run2.replace(
|
|
3881
|
-
const
|
|
3882
|
-
const executionFile =
|
|
3883
|
-
return executionFile ??
|
|
3856
|
+
const command = run2.replace(REFERENCE, (reference, referencedId) => {
|
|
3857
|
+
const scriptPrimitive = charter.primitiveById.get(referencedId);
|
|
3858
|
+
const executionFile = scriptPrimitive && compiledPrimitiveByPrimitive.get(scriptPrimitive)?.projections.find((projection) => projection.executable)?.file;
|
|
3859
|
+
return executionFile ?? reference;
|
|
3884
3860
|
});
|
|
3885
3861
|
if (isClaudeHookEvent(signal))
|
|
3886
3862
|
hooks[signal] = [...hooks[signal] ?? [], { hooks: [{ type: "command", command }] }];
|
|
@@ -3892,14 +3868,14 @@ function claudeSettingsOf(charter, compiledPrimitiveByPrimitive, ones) {
|
|
|
3892
3868
|
};
|
|
3893
3869
|
}
|
|
3894
3870
|
function claudeDocumentComponentOf(sc, compiledFile, agentTools) {
|
|
3895
|
-
const name = sc.
|
|
3871
|
+
const name = sc.normId;
|
|
3896
3872
|
const compiledFromClaudeFolder = `../../${compiledFile}`;
|
|
3897
3873
|
const pointerTo = (path) => `Read and follow @${path}.
|
|
3898
3874
|
`;
|
|
3899
3875
|
switch (sc.kind) {
|
|
3900
3876
|
// A guide is a rule this host loads into context whole: when a file it names
|
|
3901
3877
|
// is touched, which is what `paths` on a rule does, and at the start of every
|
|
3902
|
-
// session where it names none (FR-013). Under the
|
|
3878
|
+
// session where it names none (FR-013). Under the id it is changed by,
|
|
3903
3879
|
// so a reader who wants it changed is sent to the primitive rather than
|
|
3904
3880
|
// editing what the next build overwrites (FR-017, FR-020).
|
|
3905
3881
|
case GuidePrimitive.kind: {
|
|
@@ -3924,9 +3900,9 @@ function claudeDocumentComponentOf(sc, compiledFile, agentTools) {
|
|
|
3924
3900
|
}
|
|
3925
3901
|
|
|
3926
3902
|
// src/hexagon/domain/services/compile/mcpOriginsFactory.ts
|
|
3927
|
-
function mcpOriginsOf(charter,
|
|
3903
|
+
function mcpOriginsOf(charter, shortMcpIds) {
|
|
3928
3904
|
const mcpsByKey = /* @__PURE__ */ new Map();
|
|
3929
|
-
for (const primitive of [...charter.primitives].sort((one, another) => one.
|
|
3905
|
+
for (const primitive of [...charter.primitives].sort((one, another) => one.headers.id < another.headers.id ? -1 : 1)) {
|
|
3930
3906
|
if (!(primitive instanceof McpPrimitive)) continue;
|
|
3931
3907
|
const key = `${primitive.address}
|
|
3932
3908
|
${primitive.headers.path ?? ""}`;
|
|
@@ -3938,8 +3914,8 @@ ${primitive.headers.path ?? ""}`;
|
|
|
3938
3914
|
const tokenEnv = mcpsAtOrigin.find((one) => one.headers.tokenEnv !== void 0)?.headers.tokenEnv;
|
|
3939
3915
|
const auths = new Set(mcpsAtOrigin.flatMap((one) => one.headers.auth ?? []));
|
|
3940
3916
|
return {
|
|
3941
|
-
|
|
3942
|
-
names: Object.fromEntries(mcpsAtOrigin.map((one) => [one.
|
|
3917
|
+
ids: mcpsAtOrigin.map((one) => one.headers.id),
|
|
3918
|
+
names: Object.fromEntries(mcpsAtOrigin.map((one) => [one.headers.id, shortMcpIds[one.headers.id]])),
|
|
3943
3919
|
address: firstMcp.address,
|
|
3944
3920
|
...endpoint === void 0 ? {} : { endpoint },
|
|
3945
3921
|
...command === void 0 ? {} : { command: { command, args, ...tokenEnv === void 0 ? {} : { tokenEnv } } },
|
|
@@ -3951,11 +3927,11 @@ ${primitive.headers.path ?? ""}`;
|
|
|
3951
3927
|
|
|
3952
3928
|
// src/hexagon/domain/services/compile/compileService.ts
|
|
3953
3929
|
function compile(charter, agents) {
|
|
3954
|
-
const
|
|
3955
|
-
charter.primitives.filter((primitive) => primitive.kind === McpPrimitive.kind).map((one) => one.
|
|
3930
|
+
const shortMcpIds = shortenStringsOf(
|
|
3931
|
+
charter.primitives.filter((primitive) => primitive.kind === McpPrimitive.kind).map((one) => one.headers.id)
|
|
3956
3932
|
);
|
|
3957
3933
|
const compiledPrimitiveByPrimitive = new Map(
|
|
3958
|
-
[...charter.primitives].sort((one, another) => one.
|
|
3934
|
+
[...charter.primitives].sort((one, another) => one.headers.id < another.headers.id ? -1 : one.headers.id > another.headers.id ? 1 : 0).map((primitive) => [primitive, compiledPrimitiveOf(charter, primitive)])
|
|
3959
3935
|
);
|
|
3960
3936
|
return new CharterOutput(
|
|
3961
3937
|
// What an agent opens from the catalogue is the primitive as it compiled,
|
|
@@ -3965,14 +3941,14 @@ function compile(charter, agents) {
|
|
|
3965
3941
|
catalogueOf(charter, (one) => compiledPrimitiveByPrimitive.get(one).projections[0].file),
|
|
3966
3942
|
charterMdOf(PRIMITIVE_CLASSES),
|
|
3967
3943
|
[...compiledPrimitiveByPrimitive.values()],
|
|
3968
|
-
new McpOrigins(mcpOriginsOf(charter,
|
|
3969
|
-
agents.flatMap((agent) => providerComponentsOf(agent, charter, compiledPrimitiveByPrimitive,
|
|
3944
|
+
new McpOrigins(mcpOriginsOf(charter, shortMcpIds)),
|
|
3945
|
+
agents.flatMap((agent) => providerComponentsOf(agent, charter, compiledPrimitiveByPrimitive, shortMcpIds))
|
|
3970
3946
|
);
|
|
3971
3947
|
}
|
|
3972
|
-
function providerComponentsOf(agent, charter, compiledPrimitiveByPrimitive,
|
|
3948
|
+
function providerComponentsOf(agent, charter, compiledPrimitiveByPrimitive, shortMcpIds) {
|
|
3973
3949
|
switch (agent) {
|
|
3974
3950
|
case "claude":
|
|
3975
|
-
return claudeComponentsOf(charter, compiledPrimitiveByPrimitive,
|
|
3951
|
+
return claudeComponentsOf(charter, compiledPrimitiveByPrimitive, shortMcpIds);
|
|
3976
3952
|
}
|
|
3977
3953
|
}
|
|
3978
3954
|
|
|
@@ -4175,37 +4151,37 @@ var CharterAuthoring = class {
|
|
|
4175
4151
|
);
|
|
4176
4152
|
}
|
|
4177
4153
|
/**
|
|
4178
|
-
* Which file declares one
|
|
4154
|
+
* Which file declares one id, which layer it arrived in, when it comes
|
|
4179
4155
|
* up, the mixins it uses and the corpus it cites, what uses, cites or names
|
|
4180
4156
|
* it, and the situations this repository wrote down about it (FR-014, FR-017,
|
|
4181
4157
|
* FR-163).
|
|
4182
4158
|
*
|
|
4183
|
-
* The charter is asked, not searched here: one
|
|
4159
|
+
* The charter is asked, not searched here: one id space covers every
|
|
4184
4160
|
* layer, and which file answers to a name, and which names one primitive
|
|
4185
4161
|
* shares with another, are the charter's own questions (FR-014). What is here
|
|
4186
4162
|
* is the order — read, refuse a charter that does not hold, then ask.
|
|
4187
4163
|
*
|
|
4188
|
-
* An
|
|
4164
|
+
* An id nothing answers to is raised rather than reported, the way a
|
|
4189
4165
|
* word that is no kind is: there is no file it is wrong with, and the next
|
|
4190
4166
|
* move is to go and look at what the charter does hold.
|
|
4191
4167
|
*
|
|
4192
4168
|
* Reads and says: this holds a writing port and never reaches for it (FR-041).
|
|
4193
4169
|
*/
|
|
4194
|
-
async explain(
|
|
4195
|
-
const
|
|
4170
|
+
async explain(id) {
|
|
4171
|
+
const primitiveId = primitiveIdOf(id);
|
|
4196
4172
|
const [charter, , faultsByFiles] = await this.#read();
|
|
4197
4173
|
if (charter === void 0) return faultsByFileDTO(faultsByFiles.errors(), this.#repoPath);
|
|
4198
|
-
const declared = charter.primitiveById.get(
|
|
4174
|
+
const declared = charter.primitiveById.get(primitiveId);
|
|
4199
4175
|
if (declared === void 0)
|
|
4200
4176
|
throw new DomainFault(
|
|
4201
|
-
`This charter holds no "${
|
|
4202
|
-
'Run "cw list --min" to see every
|
|
4177
|
+
`This charter holds no "${id}".`,
|
|
4178
|
+
'Run "cw list --min" to see every id it does hold.'
|
|
4203
4179
|
);
|
|
4204
4180
|
const testRoot = await loadTestRoot(this.#repoPath, this.#fileReader);
|
|
4205
4181
|
const testCasesByFile = Object.fromEntries(
|
|
4206
4182
|
Object.entries(testRoot.suitesByFile).map(([file, suite]) => [
|
|
4207
4183
|
file,
|
|
4208
|
-
suite.cases.filter((each) => each.
|
|
4184
|
+
suite.cases.filter((each) => each.activatedId === primitiveId).map((each) => each.describe())
|
|
4209
4185
|
]).filter(([, situations]) => situations.length > 0)
|
|
4210
4186
|
);
|
|
4211
4187
|
return explanationOutcomeDTO(
|
|
@@ -4313,15 +4289,15 @@ var CharterAuthoring = class {
|
|
|
4313
4289
|
* in an editor afterwards (FR-117).
|
|
4314
4290
|
*
|
|
4315
4291
|
* Nothing is validated and nothing is asked of the charter but which
|
|
4316
|
-
*
|
|
4292
|
+
* ids it already holds. What arrives is what an author answered, and
|
|
4317
4293
|
* reading it as the primitive it claims to be is what refuses an answer its
|
|
4318
4294
|
* kind will not take — the same reading a file of it would get, so there is no
|
|
4319
4295
|
* second contract here to keep in step with the kind's (FR-004). Where it goes
|
|
4320
4296
|
* is the convention: a directory per kind, since nothing reads the directory
|
|
4321
4297
|
* (FR-002).
|
|
4322
4298
|
*
|
|
4323
|
-
* An
|
|
4324
|
-
* file holding it: one
|
|
4299
|
+
* An id the charter already holds, in any layer, is refused naming the
|
|
4300
|
+
* file holding it: one id names one primitive in the whole charter, and
|
|
4325
4301
|
* a second file claiming it is a collision the next read would report
|
|
4326
4302
|
* (FR-014). The files are read without being validated, so a charter wrong
|
|
4327
4303
|
* somewhere else can still be added to.
|
|
@@ -4336,10 +4312,10 @@ var CharterAuthoring = class {
|
|
|
4336
4312
|
return faultsDTO(new Faults(raised.errors));
|
|
4337
4313
|
}
|
|
4338
4314
|
const charter = await loadCharterRoot(this.#repoPath, this.#fileReader, this.#yamlParser);
|
|
4339
|
-
const claimingPrimitive = charter.primitiveById.get(
|
|
4315
|
+
const claimingPrimitive = charter.primitiveById.get(id);
|
|
4340
4316
|
if (claimingPrimitive !== void 0)
|
|
4341
4317
|
throw new DomainFault(
|
|
4342
|
-
`"${
|
|
4318
|
+
`"${id}" is already there, declared by ${claimingPrimitive.file}, and one id names one primitive in the whole charter.`,
|
|
4343
4319
|
`Open ${claimingPrimitive.file}, or run this again with an id this charter has not got.`
|
|
4344
4320
|
);
|
|
4345
4321
|
return primitiveDTO(await writeCharter(this.#repoPath, primitive, this.#fileReader, this.#fileWriter));
|
|
@@ -4353,8 +4329,8 @@ var CharterAuthoring = class {
|
|
|
4353
4329
|
* needs to open it. A save made over its hash is checked against the
|
|
4354
4330
|
* primitive read again then.
|
|
4355
4331
|
*/
|
|
4356
|
-
async open(
|
|
4357
|
-
return primitiveDTO(await this.#
|
|
4332
|
+
async open(id) {
|
|
4333
|
+
return primitiveDTO(await this.#primitiveById(id));
|
|
4358
4334
|
}
|
|
4359
4335
|
/**
|
|
4360
4336
|
* One repository primitive's file written over with new headers and a new
|
|
@@ -4366,16 +4342,16 @@ var CharterAuthoring = class {
|
|
|
4366
4342
|
* this one (FR-078). Answers the kind will not take come back as the faults
|
|
4367
4343
|
* `add` gives for them, since both read answers the one way.
|
|
4368
4344
|
*
|
|
4369
|
-
* Kind and id are the
|
|
4345
|
+
* Kind and id are the id's, never the headers': an edit cannot move a
|
|
4370
4346
|
* file, and a rename is a new primitive and a deletion. Nothing is compiled
|
|
4371
4347
|
* and nothing committed (FR-079).
|
|
4372
4348
|
*/
|
|
4373
|
-
async rewrite(
|
|
4374
|
-
const authoredPrimitive = await this.#
|
|
4349
|
+
async rewrite(id, headers, body, openedHash) {
|
|
4350
|
+
const authoredPrimitive = await this.#primitiveById(id);
|
|
4375
4351
|
if (!RepoLayerPrimitive.isSatisfiedBy(authoredPrimitive))
|
|
4376
4352
|
throw new DomainFault(
|
|
4377
|
-
`${
|
|
4378
|
-
`To differ from it, author a primitive of your own under an
|
|
4353
|
+
`${id} was not authored in this repository, and ${authoredPrimitive.file} is read-only here.`,
|
|
4354
|
+
`To differ from it, author a primitive of your own under an id of its own.`
|
|
4379
4355
|
);
|
|
4380
4356
|
if (authoredPrimitive.hash !== openedHash)
|
|
4381
4357
|
throw new DomainFault(
|
|
@@ -4404,29 +4380,29 @@ var CharterAuthoring = class {
|
|
|
4404
4380
|
* each as dangling, and what to do about them is its author's call. Nothing is
|
|
4405
4381
|
* compiled and nothing committed (FR-079).
|
|
4406
4382
|
*/
|
|
4407
|
-
async remove(
|
|
4408
|
-
const primitive = await this.#
|
|
4383
|
+
async remove(id) {
|
|
4384
|
+
const primitive = await this.#primitiveById(id);
|
|
4409
4385
|
if (!RepoLayerPrimitive.isSatisfiedBy(primitive))
|
|
4410
4386
|
throw new DomainFault(
|
|
4411
|
-
`${
|
|
4412
|
-
`To differ from it, author a primitive of your own under an
|
|
4387
|
+
`${id} was not authored in this repository, and ${primitive.file} is read-only here.`,
|
|
4388
|
+
`To differ from it, author a primitive of your own under an id of its own.`
|
|
4413
4389
|
);
|
|
4414
4390
|
const file = new URL(primitive.file, this.#repoPath);
|
|
4415
4391
|
await this.#fileWriter.delete(file);
|
|
4416
4392
|
for (const assetFile of primitive.assets) await this.#fileWriter.delete(new URL(assetFile.file, file));
|
|
4417
4393
|
return primitiveDTO(primitive);
|
|
4418
4394
|
}
|
|
4419
|
-
/** The primitive one
|
|
4395
|
+
/** The primitive one id names, off the files as read and not the
|
|
4420
4396
|
* validation. Raised when the charter holds nothing of it, the way `explain`
|
|
4421
4397
|
* raises it: there is no file it is wrong with. */
|
|
4422
|
-
async #
|
|
4423
|
-
const
|
|
4398
|
+
async #primitiveById(id) {
|
|
4399
|
+
const primitiveId = primitiveIdOf(id);
|
|
4424
4400
|
const charter = await loadCharterRoot(this.#repoPath, this.#fileReader, this.#yamlParser);
|
|
4425
|
-
const primitive = charter.primitiveById.get(
|
|
4401
|
+
const primitive = charter.primitiveById.get(primitiveId);
|
|
4426
4402
|
if (primitive === void 0)
|
|
4427
4403
|
throw new DomainFault(
|
|
4428
|
-
`This charter holds no "${
|
|
4429
|
-
'Run "cw list --min" to see every
|
|
4404
|
+
`This charter holds no "${id}".`,
|
|
4405
|
+
'Run "cw list --min" to see every id it does hold.'
|
|
4430
4406
|
);
|
|
4431
4407
|
return primitive;
|
|
4432
4408
|
}
|
|
@@ -4567,7 +4543,7 @@ async function writeCredential(secrets2, credential) {
|
|
|
4567
4543
|
async function credentialFor(secrets2, authorizing2, origin, { refused = false } = {}) {
|
|
4568
4544
|
if (origin.endpoint !== void 0 && !isSecureEndpoint(origin.endpoint))
|
|
4569
4545
|
throw new DomainFault(`${origin.address} is plain http to another machine, so no credential is sent there.`, 'Declare its endpoint as https://, then run "cw build".');
|
|
4570
|
-
const signInAgain = `Run "cw mcp auth ${origin.
|
|
4546
|
+
const signInAgain = `Run "cw mcp auth ${origin.ids[0] ?? ""}" at a terminal to sign in there.`;
|
|
4571
4547
|
const credential = await readCredential(secrets2, origin.address);
|
|
4572
4548
|
if (credential === void 0) throw new DomainFault(`You are not signed in to ${origin.address}.`, signInAgain);
|
|
4573
4549
|
const expired = refused || credential.expiresAt !== void 0 && Date.parse(credential.expiresAt) <= Date.now();
|
|
@@ -4602,18 +4578,18 @@ async function loadMcpTools(repo, fileReader2, yamlParser2) {
|
|
|
4602
4578
|
if (Array.isArray(catalogJson)) catalogEntries = catalogJson;
|
|
4603
4579
|
} catch {
|
|
4604
4580
|
}
|
|
4605
|
-
const
|
|
4606
|
-
for (const {
|
|
4607
|
-
if (kind !== McpPrimitive.kind || typeof
|
|
4581
|
+
const toolsById = /* @__PURE__ */ new Map();
|
|
4582
|
+
for (const { id, kind, file } of catalogEntries) {
|
|
4583
|
+
if (kind !== McpPrimitive.kind || typeof id !== "string" || typeof file !== "string") continue;
|
|
4608
4584
|
const compiledDocument = await fileReader2.readIfThere(new URL(file, repo));
|
|
4609
4585
|
if (compiledDocument === void 0) continue;
|
|
4610
4586
|
try {
|
|
4611
4587
|
const primitive = primitiveOf(compiledDocument, yamlParser2);
|
|
4612
|
-
if (primitive instanceof McpPrimitive)
|
|
4588
|
+
if (primitive instanceof McpPrimitive) toolsById.set(id, primitive.headers.tools);
|
|
4613
4589
|
} catch {
|
|
4614
4590
|
}
|
|
4615
4591
|
}
|
|
4616
|
-
return
|
|
4592
|
+
return toolsById;
|
|
4617
4593
|
}
|
|
4618
4594
|
|
|
4619
4595
|
// src/hexagon/port/zdriven/DrivenFault.ts
|
|
@@ -4648,11 +4624,11 @@ var McpConnecting = class {
|
|
|
4648
4624
|
}
|
|
4649
4625
|
async signInStatus() {
|
|
4650
4626
|
return Promise.all(
|
|
4651
|
-
(await this.#signInAddresses()).map(async ({ address,
|
|
4627
|
+
(await this.#signInAddresses()).map(async ({ address, ids, auth: auth2 }) => {
|
|
4652
4628
|
const credential = await readCredential(this.#secrets, address);
|
|
4653
4629
|
return {
|
|
4654
4630
|
type: "SignInStatus",
|
|
4655
|
-
data: { address,
|
|
4631
|
+
data: { address, ids, auth: auth2, signedIn: credential !== void 0, ...credential && { method: credential.method } }
|
|
4656
4632
|
};
|
|
4657
4633
|
})
|
|
4658
4634
|
);
|
|
@@ -4675,8 +4651,8 @@ var McpConnecting = class {
|
|
|
4675
4651
|
const toolRoute = toolRoutes.get(name);
|
|
4676
4652
|
if (toolRoute === void 0)
|
|
4677
4653
|
return errorAnswerOf(unreachableTools.get(name) ?? `"${name}" is not a tool this server serves; call one it listed.`);
|
|
4678
|
-
const { origin,
|
|
4679
|
-
const signInAgain = `${origin.address} refused your credential. Run "cw mcp auth ${
|
|
4654
|
+
const { origin, id, upstream, connection } = toolRoute;
|
|
4655
|
+
const signInAgain = `${origin.address} refused your credential. Run "cw mcp auth ${id}" at a terminal to sign in there again.`;
|
|
4680
4656
|
const accessTokenOf = (refused) => origin.endpoint === void 0 ? Promise.resolve(void 0) : credentialFor(this.#secrets, this.#authorizing, origin, { refused });
|
|
4681
4657
|
try {
|
|
4682
4658
|
try {
|
|
@@ -4700,7 +4676,7 @@ var McpConnecting = class {
|
|
|
4700
4676
|
}
|
|
4701
4677
|
/**
|
|
4702
4678
|
* Every place the last build listed, reached at once and once per run
|
|
4703
|
-
* (FR-152). What each lists is kept where one of its
|
|
4679
|
+
* (FR-152). What each lists is kept where one of its ids
|
|
4704
4680
|
* declares it, renamed `<prefix>__<tool>`; a place down or not signed in to,
|
|
4705
4681
|
* and a declared tool its place lacks, is said and left out, and the rest
|
|
4706
4682
|
* are served (FR-153, FR-154, SC-039).
|
|
@@ -4708,7 +4684,7 @@ var McpConnecting = class {
|
|
|
4708
4684
|
#serving() {
|
|
4709
4685
|
this.#mcpServing ??= (async () => {
|
|
4710
4686
|
const origins = await loadMcpOrigins(this.#repoPath, this.#fileReader);
|
|
4711
|
-
const
|
|
4687
|
+
const toolsById = await loadMcpTools(this.#repoPath, this.#fileReader, this.#yamlParser);
|
|
4712
4688
|
const problems = [];
|
|
4713
4689
|
const servedTools = [];
|
|
4714
4690
|
const toolRoutes = /* @__PURE__ */ new Map();
|
|
@@ -4727,30 +4703,30 @@ var McpConnecting = class {
|
|
|
4727
4703
|
);
|
|
4728
4704
|
for (const { origin, connection, reason } of reachedPlaces) {
|
|
4729
4705
|
if (connection !== void 0) connections.push(connection);
|
|
4730
|
-
else problems.push(`${origin.
|
|
4731
|
-
for (const
|
|
4732
|
-
const declaredTools =
|
|
4706
|
+
else problems.push(`${origin.ids.join(", ")}: ${reason}`);
|
|
4707
|
+
for (const id of origin.ids) {
|
|
4708
|
+
const declaredTools = toolsById.get(id);
|
|
4733
4709
|
if (declaredTools === void 0) {
|
|
4734
|
-
problems.push(`${
|
|
4710
|
+
problems.push(`${id} has no compiled document under .cw/out/, so none of its tools is served. Run "cw build".`);
|
|
4735
4711
|
continue;
|
|
4736
4712
|
}
|
|
4737
|
-
const
|
|
4738
|
-
if (
|
|
4739
|
-
problems.push(`${
|
|
4713
|
+
const servedPrefix = origin.names?.[id];
|
|
4714
|
+
if (servedPrefix === void 0) {
|
|
4715
|
+
problems.push(`${id} has no served name in .cw/out/mcp-origins.json, so none of its tools is served. Run "cw build" again.`);
|
|
4740
4716
|
continue;
|
|
4741
4717
|
}
|
|
4742
4718
|
for (const tool of declaredTools) {
|
|
4743
|
-
const servedName = `${
|
|
4719
|
+
const servedName = `${servedPrefix}__${tool}`;
|
|
4744
4720
|
if (connection === void 0) {
|
|
4745
4721
|
unreachableTools.set(servedName, reason ?? "");
|
|
4746
4722
|
continue;
|
|
4747
4723
|
}
|
|
4748
4724
|
const upstreamTool = connection.tools.find(({ name }) => name === tool);
|
|
4749
4725
|
if (upstreamTool === void 0) {
|
|
4750
|
-
problems.push(`${
|
|
4726
|
+
problems.push(`${id} declares "${tool}", which ${origin.address} does not have; it is not served.`);
|
|
4751
4727
|
continue;
|
|
4752
4728
|
}
|
|
4753
|
-
const placeName = origin.path === void 0 ?
|
|
4729
|
+
const placeName = origin.path === void 0 ? id : `${id} \u2014 ${origin.path}`;
|
|
4754
4730
|
servedTools.push({
|
|
4755
4731
|
type: "ServedTool",
|
|
4756
4732
|
data: {
|
|
@@ -4759,7 +4735,7 @@ var McpConnecting = class {
|
|
|
4759
4735
|
inputSchema: upstreamTool.inputSchema
|
|
4760
4736
|
}
|
|
4761
4737
|
});
|
|
4762
|
-
toolRoutes.set(servedName, { origin,
|
|
4738
|
+
toolRoutes.set(servedName, { origin, id, upstream: tool, connection });
|
|
4763
4739
|
}
|
|
4764
4740
|
}
|
|
4765
4741
|
}
|
|
@@ -4784,16 +4760,16 @@ var McpConnecting = class {
|
|
|
4784
4760
|
async #signInAddresses() {
|
|
4785
4761
|
const origins = await loadMcpOrigins(this.#repoPath, this.#fileReader);
|
|
4786
4762
|
const addresses = /* @__PURE__ */ new Map();
|
|
4787
|
-
for (const { address, endpoint,
|
|
4788
|
-
const signInAddress = addresses.get(address) ?? { ...endpoint !== void 0 && { endpoint },
|
|
4789
|
-
|
|
4763
|
+
for (const { address, endpoint, ids, auth: auth2 } of origins.filter((one) => one.auth.length > 0)) {
|
|
4764
|
+
const signInAddress = addresses.get(address) ?? { ...endpoint !== void 0 && { endpoint }, ids: /* @__PURE__ */ new Set(), auth: /* @__PURE__ */ new Set() };
|
|
4765
|
+
ids.forEach((id) => signInAddress.ids.add(id));
|
|
4790
4766
|
auth2.forEach((method) => signInAddress.auth.add(method));
|
|
4791
4767
|
addresses.set(address, signInAddress);
|
|
4792
4768
|
}
|
|
4793
|
-
return [...addresses].sort(([one], [other]) => one.localeCompare(other)).map(([address, { endpoint,
|
|
4769
|
+
return [...addresses].sort(([one], [other]) => one.localeCompare(other)).map(([address, { endpoint, ids, auth: auth2 }]) => ({
|
|
4794
4770
|
address,
|
|
4795
4771
|
...endpoint !== void 0 && { endpoint },
|
|
4796
|
-
|
|
4772
|
+
ids: [...ids].sort(),
|
|
4797
4773
|
auth: ["oauth", "token"].filter((method) => auth2.has(method))
|
|
4798
4774
|
}));
|
|
4799
4775
|
}
|