cherry-works 0.3.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 +32 -18
- package/dist/cw.js +1 -3
- package/dist/main.js +1339 -1340
- 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")
|
|
@@ -2426,385 +1899,904 @@ function api(charterAuthoringApp2, charterVendoringApp2, testAuthoringApp2) {
|
|
|
2426
1899
|
);
|
|
2427
1900
|
}
|
|
2428
1901
|
|
|
2429
|
-
// src/driver/portal/server.ts
|
|
2430
|
-
var DEFAULT_PORT = 9927;
|
|
2431
|
-
async function startPortal(page, charterAuthoringApp2, charterVendoringApp2, testAuthoringApp2, port = DEFAULT_PORT) {
|
|
2432
|
-
await access(new URL("index.html", page)).catch(() => {
|
|
2433
|
-
throw new DomainFault("The portal's page is not built.", 'Run "pnpm build", then start the portal again.');
|
|
2434
|
-
});
|
|
2435
|
-
const token = randomBytes(32).toString("base64url");
|
|
2436
|
-
for (let next = port; ; next++) {
|
|
2437
|
-
try {
|
|
2438
|
-
const address = new URL(`http://127.0.0.1:${next}/?t=${token}`);
|
|
2439
|
-
return { address, server: await listen(portal(page, charterAuthoringApp2, charterVendoringApp2, testAuthoringApp2, token, next), next) };
|
|
2440
|
-
} catch (raised) {
|
|
2441
|
-
if (raised.code !== "EADDRINUSE" || next === 65535) throw raised;
|
|
1902
|
+
// src/driver/portal/server.ts
|
|
1903
|
+
var DEFAULT_PORT = 9927;
|
|
1904
|
+
async function startPortal(page, charterAuthoringApp2, charterVendoringApp2, testAuthoringApp2, port = DEFAULT_PORT) {
|
|
1905
|
+
await access(new URL("index.html", page)).catch(() => {
|
|
1906
|
+
throw new DomainFault("The portal's page is not built.", 'Run "pnpm build", then start the portal again.');
|
|
1907
|
+
});
|
|
1908
|
+
const token = randomBytes(32).toString("base64url");
|
|
1909
|
+
for (let next = port; ; next++) {
|
|
1910
|
+
try {
|
|
1911
|
+
const address = new URL(`http://127.0.0.1:${next}/?t=${token}`);
|
|
1912
|
+
return { address, server: await listen(portal(page, charterAuthoringApp2, charterVendoringApp2, testAuthoringApp2, token, next), next) };
|
|
1913
|
+
} catch (raised) {
|
|
1914
|
+
if (raised.code !== "EADDRINUSE" || next === 65535) throw raised;
|
|
1915
|
+
}
|
|
1916
|
+
}
|
|
1917
|
+
}
|
|
1918
|
+
function portal(page, charterAuthoringApp2, charterVendoringApp2, testAuthoringApp2, token, port) {
|
|
1919
|
+
const hosts = [`127.0.0.1:${port}`, `localhost:${port}`];
|
|
1920
|
+
return new Hono().use(async (c, next) => {
|
|
1921
|
+
if (!hosts.includes(c.req.header("host") ?? "")) {
|
|
1922
|
+
return c.text(`The portal answers only at ${hosts.join(" and ")}.`, 403);
|
|
1923
|
+
}
|
|
1924
|
+
if (c.req.method !== "GET" && c.req.header("content-type")?.split(";")[0]?.trim() !== "application/json") {
|
|
1925
|
+
return c.text("Every request but a GET is sent with Content-Type: application/json.", 415);
|
|
1926
|
+
}
|
|
1927
|
+
await next();
|
|
1928
|
+
}).use("/", async (c, next) => {
|
|
1929
|
+
if (c.req.query("t") !== token) return c.text('Open the address "cw portal" printed: this one has no token.', 401);
|
|
1930
|
+
await next();
|
|
1931
|
+
}).use("/api/*", bearerAuth({ token })).route("/api", api(charterAuthoringApp2, charterVendoringApp2, testAuthoringApp2)).use("/*", serveStatic({ root: fileURLToPath(page) }));
|
|
1932
|
+
}
|
|
1933
|
+
function listen(app, port) {
|
|
1934
|
+
return new Promise((resolve, reject) => {
|
|
1935
|
+
const server = serve({ fetch: app.fetch, hostname: "127.0.0.1", port }, () => resolve(server));
|
|
1936
|
+
server.once("error", reject);
|
|
1937
|
+
});
|
|
1938
|
+
}
|
|
1939
|
+
|
|
1940
|
+
// src/driver/cli/commands/PortalCommand.ts
|
|
1941
|
+
var OPTIONS7 = {
|
|
1942
|
+
port: {
|
|
1943
|
+
type: "number",
|
|
1944
|
+
describe: "Which port to serve on, the next free one when it is taken",
|
|
1945
|
+
default: DEFAULT_PORT
|
|
1946
|
+
}
|
|
1947
|
+
};
|
|
1948
|
+
var PortalCommand = class {
|
|
1949
|
+
name = "portal";
|
|
1950
|
+
summary = "Serve this repository's charter to a browser on this machine";
|
|
1951
|
+
options = OPTIONS7;
|
|
1952
|
+
async run({ charterAuthoringApp: charterAuthoringApp2, charterVendoringApp: charterVendoringApp2, testAuthoringApp: testAuthoringApp2 }, { port }) {
|
|
1953
|
+
await charterAuthoringApp2.ensureRepoReady();
|
|
1954
|
+
const { address } = await startPortal(
|
|
1955
|
+
new URL("./", import.meta.resolve("#portal/index.html")),
|
|
1956
|
+
charterAuthoringApp2,
|
|
1957
|
+
charterVendoringApp2,
|
|
1958
|
+
testAuthoringApp2,
|
|
1959
|
+
port
|
|
1960
|
+
);
|
|
1961
|
+
return { code: EXIT_OK, result: `The portal is at ${address}
|
|
1962
|
+
Press Ctrl+C to stop it.
|
|
1963
|
+
` };
|
|
1964
|
+
}
|
|
1965
|
+
};
|
|
1966
|
+
|
|
1967
|
+
// src/driver/cli/commands/RemoveCommand.ts
|
|
1968
|
+
import prompts4 from "prompts";
|
|
1969
|
+
var OPTIONS8 = {
|
|
1970
|
+
id: {
|
|
1971
|
+
type: "string",
|
|
1972
|
+
describe: 'What the primitive is named by across the whole charter, its id: "no-any"'
|
|
1973
|
+
},
|
|
1974
|
+
yes: {
|
|
1975
|
+
type: "boolean",
|
|
1976
|
+
alias: "y",
|
|
1977
|
+
default: false,
|
|
1978
|
+
describe: "Remove it without asking first, as a run with nobody at the terminal must"
|
|
1979
|
+
}
|
|
1980
|
+
};
|
|
1981
|
+
var RemoveCommand = class {
|
|
1982
|
+
name = "remove <id>";
|
|
1983
|
+
summary = "Delete the file of one primitive this repository authored";
|
|
1984
|
+
options = OPTIONS8;
|
|
1985
|
+
async run({ charterAuthoringApp: charterAuthoringApp2 }, { id, yes }) {
|
|
1986
|
+
const primitive = (await charterAuthoringApp2.open(id ?? "")).data;
|
|
1987
|
+
if (!yes && primitive.layerName === REPO_LAYER) {
|
|
1988
|
+
if (!process.stdin.isTTY)
|
|
1989
|
+
throw new DomainFault(
|
|
1990
|
+
`Removing ${primitive.file} is asked first, and nobody is at the terminal to answer.`,
|
|
1991
|
+
`Run "cw remove ${primitive.id} --yes" to remove it without asking.`
|
|
1992
|
+
);
|
|
1993
|
+
const { confirmed } = await prompts4({
|
|
1994
|
+
type: "confirm",
|
|
1995
|
+
name: "confirmed",
|
|
1996
|
+
message: `Remove ${primitive.file}?`,
|
|
1997
|
+
initial: false
|
|
1998
|
+
});
|
|
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
|
+
};
|
|
2442
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
|
+
};
|
|
2443
2095
|
}
|
|
2444
|
-
}
|
|
2445
|
-
function
|
|
2446
|
-
|
|
2447
|
-
|
|
2448
|
-
|
|
2449
|
-
|
|
2450
|
-
}
|
|
2451
|
-
if (c.req.method !== "GET" && c.req.header("content-type")?.split(";")[0]?.trim() !== "application/json") {
|
|
2452
|
-
return c.text("Every request but a GET is sent with Content-Type: application/json.", 415);
|
|
2453
|
-
}
|
|
2454
|
-
await next();
|
|
2455
|
-
}).use("/", async (c, next) => {
|
|
2456
|
-
if (c.req.query("t") !== token) return c.text('Open the address "cw portal" printed: this one has no token.', 401);
|
|
2457
|
-
await next();
|
|
2458
|
-
}).use("/api/*", bearerAuth({ token })).route("/api", api(charterAuthoringApp2, charterVendoringApp2, testAuthoringApp2)).use("/*", serveStatic({ root: fileURLToPath(page) }));
|
|
2459
|
-
}
|
|
2460
|
-
function listen(app, port) {
|
|
2461
|
-
return new Promise((resolve, reject) => {
|
|
2462
|
-
const server = serve({ fetch: app.fetch, hostname: "127.0.0.1", port }, () => resolve(server));
|
|
2463
|
-
server.once("error", reject);
|
|
2464
|
-
});
|
|
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
|
+
];
|
|
2465
2102
|
}
|
|
2466
2103
|
|
|
2467
|
-
// src/driver/cli/commands/
|
|
2468
|
-
var
|
|
2469
|
-
|
|
2470
|
-
type: "
|
|
2471
|
-
describe: "
|
|
2472
|
-
|
|
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"
|
|
2473
2113
|
}
|
|
2474
2114
|
};
|
|
2475
|
-
var
|
|
2476
|
-
name = "
|
|
2477
|
-
summary = "
|
|
2478
|
-
options =
|
|
2479
|
-
async run({
|
|
2480
|
-
await
|
|
2481
|
-
|
|
2482
|
-
|
|
2483
|
-
|
|
2484
|
-
|
|
2485
|
-
|
|
2486
|
-
|
|
2487
|
-
|
|
2488
|
-
|
|
2489
|
-
|
|
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")}
|
|
2490
2141
|
` };
|
|
2491
2142
|
}
|
|
2492
2143
|
};
|
|
2493
2144
|
|
|
2494
|
-
// src/driver/cli/commands/
|
|
2495
|
-
|
|
2496
|
-
|
|
2497
|
-
|
|
2498
|
-
|
|
2499
|
-
|
|
2500
|
-
|
|
2501
|
-
|
|
2502
|
-
|
|
2503
|
-
|
|
2504
|
-
|
|
2505
|
-
|
|
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
|
+
});
|
|
2231
|
+
}
|
|
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
|
+
});
|
|
2253
|
+
}
|
|
2254
|
+
};
|
|
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: [] }
|
|
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);
|
|
2506
2315
|
}
|
|
2507
2316
|
};
|
|
2508
|
-
|
|
2509
|
-
|
|
2510
|
-
|
|
2511
|
-
|
|
2512
|
-
|
|
2513
|
-
|
|
2514
|
-
|
|
2515
|
-
|
|
2516
|
-
|
|
2517
|
-
|
|
2518
|
-
|
|
2519
|
-
|
|
2520
|
-
|
|
2521
|
-
|
|
2522
|
-
|
|
2523
|
-
|
|
2524
|
-
|
|
2525
|
-
|
|
2526
|
-
|
|
2527
|
-
|
|
2528
|
-
|
|
2529
|
-
|
|
2530
|
-
|
|
2531
|
-
|
|
2532
|
-
|
|
2533
|
-
'Run "cw doctor" to see what still names it, then "cw build".',
|
|
2534
|
-
""
|
|
2535
|
-
].join("\n")
|
|
2536
|
-
};
|
|
2317
|
+
|
|
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);
|
|
2537
2342
|
}
|
|
2538
2343
|
};
|
|
2539
2344
|
|
|
2540
|
-
// src/
|
|
2541
|
-
var
|
|
2542
|
-
|
|
2543
|
-
|
|
2544
|
-
|
|
2545
|
-
|
|
2546
|
-
|
|
2547
|
-
|
|
2548
|
-
|
|
2549
|
-
|
|
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);
|
|
2550
2372
|
}
|
|
2551
2373
|
};
|
|
2552
2374
|
|
|
2553
|
-
// src/
|
|
2554
|
-
|
|
2555
|
-
|
|
2556
|
-
|
|
2557
|
-
|
|
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(" ");
|
|
2466
|
+
}
|
|
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);
|
|
2558
2472
|
}
|
|
2559
2473
|
};
|
|
2560
|
-
|
|
2561
|
-
|
|
2562
|
-
|
|
2563
|
-
|
|
2564
|
-
|
|
2565
|
-
|
|
2566
|
-
|
|
2567
|
-
|
|
2568
|
-
|
|
2569
|
-
|
|
2570
|
-
|
|
2571
|
-
|
|
2474
|
+
|
|
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);
|
|
2572
2504
|
}
|
|
2573
2505
|
};
|
|
2574
2506
|
|
|
2575
|
-
// src/
|
|
2576
|
-
var
|
|
2577
|
-
|
|
2578
|
-
|
|
2579
|
-
|
|
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("; ")}.`;
|
|
2580
2536
|
}
|
|
2581
|
-
|
|
2582
|
-
|
|
2583
|
-
|
|
2584
|
-
summary = "Delete one test file";
|
|
2585
|
-
options = OPTIONS10;
|
|
2586
|
-
async run({ testAuthoringApp: testAuthoringApp2 }, { name }) {
|
|
2587
|
-
const removedName = await testAuthoringApp2.removeSuite(name ?? "");
|
|
2588
|
-
return { code: EXIT_OK, result: `Removed ${TEST_DIRECTORY}/${removedName}.
|
|
2589
|
-
` };
|
|
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);
|
|
2590
2540
|
}
|
|
2591
2541
|
};
|
|
2592
2542
|
|
|
2593
|
-
// src/
|
|
2594
|
-
var
|
|
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
|
-
|
|
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);
|
|
2622
2573
|
}
|
|
2623
2574
|
};
|
|
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
2575
|
|
|
2631
|
-
// src/
|
|
2632
|
-
var
|
|
2633
|
-
|
|
2634
|
-
|
|
2635
|
-
|
|
2636
|
-
|
|
2637
|
-
|
|
2638
|
-
|
|
2639
|
-
|
|
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("; ")}.`;
|
|
2640
2605
|
}
|
|
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
|
-
};
|
|
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);
|
|
2656
2609
|
}
|
|
2657
2610
|
};
|
|
2658
2611
|
|
|
2659
|
-
// src/
|
|
2660
|
-
|
|
2661
|
-
|
|
2662
|
-
|
|
2663
|
-
|
|
2664
|
-
|
|
2665
|
-
|
|
2666
|
-
|
|
2667
|
-
|
|
2668
|
-
|
|
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);
|
|
2669
2642
|
}
|
|
2670
|
-
|
|
2671
|
-
|
|
2672
|
-
|
|
2673
|
-
|
|
2674
|
-
|
|
2675
|
-
|
|
2676
|
-
|
|
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;
|
|
2677
2652
|
}
|
|
2678
2653
|
};
|
|
2679
|
-
|
|
2680
|
-
|
|
2681
|
-
|
|
2682
|
-
|
|
2683
|
-
|
|
2684
|
-
|
|
2685
|
-
|
|
2686
|
-
|
|
2687
|
-
|
|
2688
|
-
|
|
2689
|
-
|
|
2690
|
-
|
|
2691
|
-
|
|
2692
|
-
|
|
2654
|
+
|
|
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);
|
|
2693
2675
|
}
|
|
2694
2676
|
};
|
|
2695
2677
|
|
|
2696
|
-
// src/
|
|
2697
|
-
var
|
|
2698
|
-
|
|
2699
|
-
|
|
2700
|
-
|
|
2701
|
-
|
|
2702
|
-
|
|
2703
|
-
|
|
2704
|
-
|
|
2705
|
-
|
|
2706
|
-
|
|
2707
|
-
|
|
2708
|
-
|
|
2709
|
-
new SuiteEditCommand(),
|
|
2710
|
-
new SuiteRemoveCommand(),
|
|
2711
|
-
new VendorAddCommand(),
|
|
2712
|
-
new VendorRemoveCommand(),
|
|
2713
|
-
new VendorListCommand(),
|
|
2714
|
-
new PortalCommand(),
|
|
2715
|
-
new McpAuthCommand(),
|
|
2716
|
-
new McpServeCommand()
|
|
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
|
|
2717
2691
|
];
|
|
2718
|
-
|
|
2719
|
-
|
|
2720
|
-
|
|
2721
|
-
|
|
2722
|
-
|
|
2723
|
-
|
|
2724
|
-
|
|
2725
|
-
|
|
2726
|
-
|
|
2727
|
-
|
|
2728
|
-
|
|
2729
|
-
|
|
2730
|
-
|
|
2731
|
-
|
|
2732
|
-
|
|
2733
|
-
|
|
2734
|
-
|
|
2735
|
-
|
|
2736
|
-
|
|
2737
|
-
|
|
2738
|
-
|
|
2739
|
-
return failed ? EXIT_USAGE : code;
|
|
2740
|
-
}
|
|
2741
|
-
#parser(collect) {
|
|
2742
|
-
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);
|
|
2743
|
-
const groups = /* @__PURE__ */ new Map();
|
|
2744
|
-
for (const command of this.#commands) {
|
|
2745
|
-
const head = wordsOf(command.name)[0];
|
|
2746
|
-
groups.set(head, [...groups.get(head) ?? [], command]);
|
|
2747
|
-
}
|
|
2748
|
-
for (const [head, commands] of groups) {
|
|
2749
|
-
const [only] = commands;
|
|
2750
|
-
cli2 = commands.length === 1 && only !== void 0 && wordsOf(only.name).length === 1 ? this.#declaringCommand(cli2, only, only.name, collect) : cli2.command({
|
|
2751
|
-
command: head,
|
|
2752
|
-
// What the group is for is what its commands are: saying it any
|
|
2753
|
-
// other way would be a second description to keep in step.
|
|
2754
|
-
describe: commands.map((one) => wordsOf(one.name)[1]).join(", "),
|
|
2755
|
-
builder: (child) => commands.reduce((parser, command) => this.#declaringCommand(parser, command, command.name.slice(head.length + 1), collect), child).demandCommand(1, ""),
|
|
2756
|
-
handler: () => void 0
|
|
2757
|
-
});
|
|
2758
|
-
}
|
|
2759
|
-
return cli2;
|
|
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)));
|
|
@@ -3354,8 +3329,7 @@ async function ensureVCSReady(repo, vcs2, because) {
|
|
|
3354
3329
|
async function addVendor(repo, vcs2, source, version) {
|
|
3355
3330
|
await ensureVCSReady(repo, vcs2, "installing a vendor commits what it installs");
|
|
3356
3331
|
const folder = `${VENDOR_DIRECTORY}/${vendorSourceOf(source).name}`;
|
|
3357
|
-
|
|
3358
|
-
return folder;
|
|
3332
|
+
return vcs2.subtreeAdd(source, CHARTER_DIRECTORY, repo, folder, version, VENDOR_DIRECTORY);
|
|
3359
3333
|
}
|
|
3360
3334
|
async function removeVendor(repo, vcs2, name) {
|
|
3361
3335
|
await ensureVCSReady(repo, vcs2, "removing a vendor commits that it is gone");
|
|
@@ -3393,15 +3367,16 @@ function runSuite(charter, suiteName, suite) {
|
|
|
3393
3367
|
return suite.cases.map((one) => runCase(charter, suiteName, one));
|
|
3394
3368
|
}
|
|
3395
3369
|
function findUntestedPrimitives(charter, testSuites) {
|
|
3396
|
-
const
|
|
3370
|
+
const testedIds = new Set(testSuites.flatMap((testSuite) => testSuite.cases.map((one) => one.activatedId)));
|
|
3397
3371
|
const faultsByFiles = {};
|
|
3398
3372
|
for (const primitive of charter.primitives) {
|
|
3399
|
-
const {
|
|
3400
|
-
|
|
3373
|
+
const { file } = primitive;
|
|
3374
|
+
const { id } = primitive.headers;
|
|
3375
|
+
if (primitive.kind !== "guide" && primitive.kind !== "sensor" || testedIds.has(id)) continue;
|
|
3401
3376
|
faultsByFiles[file] = [
|
|
3402
3377
|
new TestCaseFault(
|
|
3403
|
-
`No test case names "${
|
|
3404
|
-
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}" } }.`,
|
|
3405
3380
|
"warn"
|
|
3406
3381
|
)
|
|
3407
3382
|
];
|
|
@@ -3424,22 +3399,22 @@ function runCase(charter, suiteName, one) {
|
|
|
3424
3399
|
}
|
|
3425
3400
|
throw new Error(`Never happen`);
|
|
3426
3401
|
}
|
|
3427
|
-
function assertPrimitiveActivated(charter, file,
|
|
3428
|
-
const declared = charter.primitiveById.get(
|
|
3429
|
-
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);
|
|
3430
3405
|
if (declared.kind !== "guide")
|
|
3431
3406
|
return new TestCaseFault(
|
|
3432
|
-
`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}.`,
|
|
3433
3408
|
`Expect a guide here, or put the situation that kind answers.`
|
|
3434
3409
|
);
|
|
3435
3410
|
const { globs = [] } = declared.headers;
|
|
3436
3411
|
if (globs.length === 0)
|
|
3437
3412
|
return new TestCaseFault(
|
|
3438
|
-
`"${
|
|
3413
|
+
`"${id}" names no files, so touching one never brings it up.`,
|
|
3439
3414
|
`Give that guide "globs", or expect nothing of it here.`
|
|
3440
3415
|
);
|
|
3441
3416
|
return globs.some((glob) => matches(glob, file)) ? void 0 : new TestCaseFault(
|
|
3442
|
-
`This file matches none of the globs "${
|
|
3417
|
+
`This file matches none of the globs "${id}" speaks about: ${globs.join(", ")}.`,
|
|
3443
3418
|
`Widen that guide's "globs", or touch a file it already speaks about.`
|
|
3444
3419
|
);
|
|
3445
3420
|
}
|
|
@@ -3449,7 +3424,7 @@ function assertAllowed(charter, file, expected) {
|
|
|
3449
3424
|
);
|
|
3450
3425
|
if (expected)
|
|
3451
3426
|
return denying.length === 0 ? void 0 : new TestCaseFault(
|
|
3452
|
-
`${denying.map((one) => `"${one.
|
|
3427
|
+
`${denying.map((one) => `"${one.headers.id}"`).join(", ")} denies this file.`,
|
|
3453
3428
|
`Drop it from that posture's "deny", or expect "allow": false.`
|
|
3454
3429
|
);
|
|
3455
3430
|
const postures = charter.primitives.filter((primitive) => primitive.kind === "posture");
|
|
@@ -3463,24 +3438,24 @@ function assertAllowed(charter, file, expected) {
|
|
|
3463
3438
|
`Add it to a posture's "deny", or expect "allow": true.`
|
|
3464
3439
|
);
|
|
3465
3440
|
}
|
|
3466
|
-
function assertScriptRun(charter, event,
|
|
3467
|
-
const declared = charter.primitiveById.get(
|
|
3468
|
-
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);
|
|
3469
3444
|
if (declared.kind !== "sensor")
|
|
3470
3445
|
return new TestCaseFault(
|
|
3471
|
-
`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}.`,
|
|
3472
3447
|
`Expect a sensor here, or put the situation that kind answers.`
|
|
3473
3448
|
);
|
|
3474
3449
|
const { signal } = declared.headers;
|
|
3475
3450
|
return signal === event ? void 0 : new TestCaseFault(
|
|
3476
|
-
`"${
|
|
3451
|
+
`"${id}" fires on ${signal}, not on ${event}.`,
|
|
3477
3452
|
`Raise the event it names, or declare the "signal" this case raises.`
|
|
3478
3453
|
);
|
|
3479
3454
|
}
|
|
3480
|
-
function primitiveNotFound(
|
|
3455
|
+
function primitiveNotFound(id) {
|
|
3481
3456
|
return new TestCaseFault(
|
|
3482
|
-
`This charter holds no primitive called "${
|
|
3483
|
-
`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.`
|
|
3484
3459
|
);
|
|
3485
3460
|
}
|
|
3486
3461
|
|
|
@@ -3493,7 +3468,7 @@ var RepoLayerPrimitive = class {
|
|
|
3493
3468
|
|
|
3494
3469
|
// src/hexagon/domain/models/output/common/Catalogue.ts
|
|
3495
3470
|
var Catalogue = class _Catalogue {
|
|
3496
|
-
/** Entries in the order they are to be listed: by
|
|
3471
|
+
/** Entries in the order they are to be listed: by id (SC-007). */
|
|
3497
3472
|
constructor(entries) {
|
|
3498
3473
|
this.entries = entries;
|
|
3499
3474
|
}
|
|
@@ -3519,7 +3494,7 @@ var Catalogue = class _Catalogue {
|
|
|
3519
3494
|
*
|
|
3520
3495
|
* A narrowed listing is a listing: what comes back answers `entries` the way
|
|
3521
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
|
|
3522
|
-
* put in, which is the order by
|
|
3497
|
+
* put in, which is the order by id they were made in.
|
|
3523
3498
|
*/
|
|
3524
3499
|
filterByKind(kind) {
|
|
3525
3500
|
return new _Catalogue(this.entries.filter((one) => one.kind === kind));
|
|
@@ -3529,13 +3504,13 @@ var Catalogue = class _Catalogue {
|
|
|
3529
3504
|
// src/hexagon/domain/services/compile/catalogueFactory.ts
|
|
3530
3505
|
function catalogueOf(charter, fileOf) {
|
|
3531
3506
|
return new Catalogue(
|
|
3532
|
-
// Ordered by
|
|
3507
|
+
// Ordered by id rather than by the order the files happened to be
|
|
3533
3508
|
// read, so the same charter catalogues byte for byte the same (SC-007).
|
|
3534
|
-
[...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) => {
|
|
3535
3510
|
const { description, globs } = one.headers;
|
|
3536
3511
|
return {
|
|
3537
|
-
identity: one.identity,
|
|
3538
3512
|
kind: one.kind,
|
|
3513
|
+
id: one.headers.id,
|
|
3539
3514
|
description,
|
|
3540
3515
|
file: fileOf(one),
|
|
3541
3516
|
...globs === void 0 ? {} : { globs }
|
|
@@ -3623,11 +3598,11 @@ function charterMdOf(kinds) {
|
|
|
3623
3598
|
"# Charter",
|
|
3624
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.`,
|
|
3625
3600
|
"## Read first",
|
|
3626
|
-
`[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.`,
|
|
3627
3602
|
"## When each kind applies",
|
|
3628
3603
|
kinds.map((one) => `- **${one.kind}** \u2014 ${one.activatesWhen}.`).join("\n"),
|
|
3629
|
-
"## One
|
|
3630
|
-
`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.`
|
|
3631
3606
|
].join("\n\n")}
|
|
3632
3607
|
`));
|
|
3633
3608
|
}
|
|
@@ -3663,13 +3638,11 @@ var PRIMITIVES_MENTION = new RegExp(
|
|
|
3663
3638
|
/^ {0,3}(`{3,}|~{3,})[\s\S]*?(?:^ {0,3}\1[^\n]*$|(?![\s\S]))/.source,
|
|
3664
3639
|
/\[[^\]\n]*\]\([^)\n]*\)/.source,
|
|
3665
3640
|
/`[^`\n]*`/.source,
|
|
3666
|
-
|
|
3667
|
-
SCRIPT_MENTION.source,
|
|
3668
|
-
TEMPLATE_MENTION.source
|
|
3641
|
+
REFERENCE.source
|
|
3669
3642
|
].join("|"),
|
|
3670
3643
|
"gm"
|
|
3671
3644
|
);
|
|
3672
|
-
var PRIMITIVES_EXACT_MENTION = new RegExp(
|
|
3645
|
+
var PRIMITIVES_EXACT_MENTION = new RegExp(`^\`${REFERENCE.source}\`$`);
|
|
3673
3646
|
function compiledPrimitiveOf(charter, primitive) {
|
|
3674
3647
|
const markdown = primitive.toMarkdown({
|
|
3675
3648
|
// A mixin nothing answers to is left out: the charter already names that
|
|
@@ -3677,9 +3650,11 @@ function compiledPrimitiveOf(charter, primitive) {
|
|
|
3677
3650
|
mixins: charter.mixinsOf(primitive).flatMap((mixin) => mixin instanceof MixinPrimitive ? [mixin] : []),
|
|
3678
3651
|
idReplacer: (body) => body.replace(PRIMITIVES_MENTION, (found) => {
|
|
3679
3652
|
const exactFound = PRIMITIVES_EXACT_MENTION.test(found);
|
|
3680
|
-
|
|
3681
|
-
|
|
3682
|
-
|
|
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})`;
|
|
3683
3658
|
})
|
|
3684
3659
|
});
|
|
3685
3660
|
return new CompiledPrimitive(
|
|
@@ -3836,18 +3811,18 @@ var ClaudeSettings = class _ClaudeSettings extends SettingBasedComponent {
|
|
|
3836
3811
|
};
|
|
3837
3812
|
|
|
3838
3813
|
// src/hexagon/domain/services/compile/claudeComponentFactory.ts
|
|
3839
|
-
function claudeComponentsOf(charter, compiledPrimitiveByPrimitive,
|
|
3814
|
+
function claudeComponentsOf(charter, compiledPrimitiveByPrimitive, shortMcpIds) {
|
|
3840
3815
|
const isSettingComponent = (one) => one.kind === PosturePrimitive.kind || one.kind === SensorPrimitive.kind;
|
|
3841
3816
|
const asSettings = charter.primitives.filter(isSettingComponent);
|
|
3842
3817
|
const asDocuments = charter.primitives.filter((one) => !isSettingComponent(one));
|
|
3843
|
-
const
|
|
3844
|
-
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]] : [])
|
|
3845
3820
|
);
|
|
3846
3821
|
const agentToolsOf = (agent) => agent.headers.tools.flatMap((tool) => {
|
|
3847
|
-
const [,
|
|
3848
|
-
if (
|
|
3849
|
-
return (mcpTool === void 0 ?
|
|
3850
|
-
(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}`
|
|
3851
3826
|
);
|
|
3852
3827
|
});
|
|
3853
3828
|
return [
|
|
@@ -3878,10 +3853,10 @@ function claudeSettingsOf(charter, compiledPrimitiveByPrimitive, ones) {
|
|
|
3878
3853
|
}
|
|
3879
3854
|
if (one.kind === SensorPrimitive.kind) {
|
|
3880
3855
|
const { signal, run: run2 } = one.headers;
|
|
3881
|
-
const command = run2.replace(
|
|
3882
|
-
const
|
|
3883
|
-
const executionFile =
|
|
3884
|
-
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;
|
|
3885
3860
|
});
|
|
3886
3861
|
if (isClaudeHookEvent(signal))
|
|
3887
3862
|
hooks[signal] = [...hooks[signal] ?? [], { hooks: [{ type: "command", command }] }];
|
|
@@ -3893,14 +3868,14 @@ function claudeSettingsOf(charter, compiledPrimitiveByPrimitive, ones) {
|
|
|
3893
3868
|
};
|
|
3894
3869
|
}
|
|
3895
3870
|
function claudeDocumentComponentOf(sc, compiledFile, agentTools) {
|
|
3896
|
-
const name = sc.
|
|
3871
|
+
const name = sc.normId;
|
|
3897
3872
|
const compiledFromClaudeFolder = `../../${compiledFile}`;
|
|
3898
3873
|
const pointerTo = (path) => `Read and follow @${path}.
|
|
3899
3874
|
`;
|
|
3900
3875
|
switch (sc.kind) {
|
|
3901
3876
|
// A guide is a rule this host loads into context whole: when a file it names
|
|
3902
3877
|
// is touched, which is what `paths` on a rule does, and at the start of every
|
|
3903
|
-
// session where it names none (FR-013). Under the
|
|
3878
|
+
// session where it names none (FR-013). Under the id it is changed by,
|
|
3904
3879
|
// so a reader who wants it changed is sent to the primitive rather than
|
|
3905
3880
|
// editing what the next build overwrites (FR-017, FR-020).
|
|
3906
3881
|
case GuidePrimitive.kind: {
|
|
@@ -3925,9 +3900,9 @@ function claudeDocumentComponentOf(sc, compiledFile, agentTools) {
|
|
|
3925
3900
|
}
|
|
3926
3901
|
|
|
3927
3902
|
// src/hexagon/domain/services/compile/mcpOriginsFactory.ts
|
|
3928
|
-
function mcpOriginsOf(charter,
|
|
3903
|
+
function mcpOriginsOf(charter, shortMcpIds) {
|
|
3929
3904
|
const mcpsByKey = /* @__PURE__ */ new Map();
|
|
3930
|
-
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)) {
|
|
3931
3906
|
if (!(primitive instanceof McpPrimitive)) continue;
|
|
3932
3907
|
const key = `${primitive.address}
|
|
3933
3908
|
${primitive.headers.path ?? ""}`;
|
|
@@ -3939,8 +3914,8 @@ ${primitive.headers.path ?? ""}`;
|
|
|
3939
3914
|
const tokenEnv = mcpsAtOrigin.find((one) => one.headers.tokenEnv !== void 0)?.headers.tokenEnv;
|
|
3940
3915
|
const auths = new Set(mcpsAtOrigin.flatMap((one) => one.headers.auth ?? []));
|
|
3941
3916
|
return {
|
|
3942
|
-
|
|
3943
|
-
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]])),
|
|
3944
3919
|
address: firstMcp.address,
|
|
3945
3920
|
...endpoint === void 0 ? {} : { endpoint },
|
|
3946
3921
|
...command === void 0 ? {} : { command: { command, args, ...tokenEnv === void 0 ? {} : { tokenEnv } } },
|
|
@@ -3952,11 +3927,11 @@ ${primitive.headers.path ?? ""}`;
|
|
|
3952
3927
|
|
|
3953
3928
|
// src/hexagon/domain/services/compile/compileService.ts
|
|
3954
3929
|
function compile(charter, agents) {
|
|
3955
|
-
const
|
|
3956
|
-
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)
|
|
3957
3932
|
);
|
|
3958
3933
|
const compiledPrimitiveByPrimitive = new Map(
|
|
3959
|
-
[...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)])
|
|
3960
3935
|
);
|
|
3961
3936
|
return new CharterOutput(
|
|
3962
3937
|
// What an agent opens from the catalogue is the primitive as it compiled,
|
|
@@ -3966,14 +3941,14 @@ function compile(charter, agents) {
|
|
|
3966
3941
|
catalogueOf(charter, (one) => compiledPrimitiveByPrimitive.get(one).projections[0].file),
|
|
3967
3942
|
charterMdOf(PRIMITIVE_CLASSES),
|
|
3968
3943
|
[...compiledPrimitiveByPrimitive.values()],
|
|
3969
|
-
new McpOrigins(mcpOriginsOf(charter,
|
|
3970
|
-
agents.flatMap((agent) => providerComponentsOf(agent, charter, compiledPrimitiveByPrimitive,
|
|
3944
|
+
new McpOrigins(mcpOriginsOf(charter, shortMcpIds)),
|
|
3945
|
+
agents.flatMap((agent) => providerComponentsOf(agent, charter, compiledPrimitiveByPrimitive, shortMcpIds))
|
|
3971
3946
|
);
|
|
3972
3947
|
}
|
|
3973
|
-
function providerComponentsOf(agent, charter, compiledPrimitiveByPrimitive,
|
|
3948
|
+
function providerComponentsOf(agent, charter, compiledPrimitiveByPrimitive, shortMcpIds) {
|
|
3974
3949
|
switch (agent) {
|
|
3975
3950
|
case "claude":
|
|
3976
|
-
return claudeComponentsOf(charter, compiledPrimitiveByPrimitive,
|
|
3951
|
+
return claudeComponentsOf(charter, compiledPrimitiveByPrimitive, shortMcpIds);
|
|
3977
3952
|
}
|
|
3978
3953
|
}
|
|
3979
3954
|
|
|
@@ -4176,37 +4151,37 @@ var CharterAuthoring = class {
|
|
|
4176
4151
|
);
|
|
4177
4152
|
}
|
|
4178
4153
|
/**
|
|
4179
|
-
* Which file declares one
|
|
4154
|
+
* Which file declares one id, which layer it arrived in, when it comes
|
|
4180
4155
|
* up, the mixins it uses and the corpus it cites, what uses, cites or names
|
|
4181
4156
|
* it, and the situations this repository wrote down about it (FR-014, FR-017,
|
|
4182
4157
|
* FR-163).
|
|
4183
4158
|
*
|
|
4184
|
-
* The charter is asked, not searched here: one
|
|
4159
|
+
* The charter is asked, not searched here: one id space covers every
|
|
4185
4160
|
* layer, and which file answers to a name, and which names one primitive
|
|
4186
4161
|
* shares with another, are the charter's own questions (FR-014). What is here
|
|
4187
4162
|
* is the order — read, refuse a charter that does not hold, then ask.
|
|
4188
4163
|
*
|
|
4189
|
-
* An
|
|
4164
|
+
* An id nothing answers to is raised rather than reported, the way a
|
|
4190
4165
|
* word that is no kind is: there is no file it is wrong with, and the next
|
|
4191
4166
|
* move is to go and look at what the charter does hold.
|
|
4192
4167
|
*
|
|
4193
4168
|
* Reads and says: this holds a writing port and never reaches for it (FR-041).
|
|
4194
4169
|
*/
|
|
4195
|
-
async explain(
|
|
4196
|
-
const
|
|
4170
|
+
async explain(id) {
|
|
4171
|
+
const primitiveId = primitiveIdOf(id);
|
|
4197
4172
|
const [charter, , faultsByFiles] = await this.#read();
|
|
4198
4173
|
if (charter === void 0) return faultsByFileDTO(faultsByFiles.errors(), this.#repoPath);
|
|
4199
|
-
const declared = charter.primitiveById.get(
|
|
4174
|
+
const declared = charter.primitiveById.get(primitiveId);
|
|
4200
4175
|
if (declared === void 0)
|
|
4201
4176
|
throw new DomainFault(
|
|
4202
|
-
`This charter holds no "${
|
|
4203
|
-
'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.'
|
|
4204
4179
|
);
|
|
4205
4180
|
const testRoot = await loadTestRoot(this.#repoPath, this.#fileReader);
|
|
4206
4181
|
const testCasesByFile = Object.fromEntries(
|
|
4207
4182
|
Object.entries(testRoot.suitesByFile).map(([file, suite]) => [
|
|
4208
4183
|
file,
|
|
4209
|
-
suite.cases.filter((each) => each.
|
|
4184
|
+
suite.cases.filter((each) => each.activatedId === primitiveId).map((each) => each.describe())
|
|
4210
4185
|
]).filter(([, situations]) => situations.length > 0)
|
|
4211
4186
|
);
|
|
4212
4187
|
return explanationOutcomeDTO(
|
|
@@ -4314,15 +4289,15 @@ var CharterAuthoring = class {
|
|
|
4314
4289
|
* in an editor afterwards (FR-117).
|
|
4315
4290
|
*
|
|
4316
4291
|
* Nothing is validated and nothing is asked of the charter but which
|
|
4317
|
-
*
|
|
4292
|
+
* ids it already holds. What arrives is what an author answered, and
|
|
4318
4293
|
* reading it as the primitive it claims to be is what refuses an answer its
|
|
4319
4294
|
* kind will not take — the same reading a file of it would get, so there is no
|
|
4320
4295
|
* second contract here to keep in step with the kind's (FR-004). Where it goes
|
|
4321
4296
|
* is the convention: a directory per kind, since nothing reads the directory
|
|
4322
4297
|
* (FR-002).
|
|
4323
4298
|
*
|
|
4324
|
-
* An
|
|
4325
|
-
* 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
|
|
4326
4301
|
* a second file claiming it is a collision the next read would report
|
|
4327
4302
|
* (FR-014). The files are read without being validated, so a charter wrong
|
|
4328
4303
|
* somewhere else can still be added to.
|
|
@@ -4337,10 +4312,10 @@ var CharterAuthoring = class {
|
|
|
4337
4312
|
return faultsDTO(new Faults(raised.errors));
|
|
4338
4313
|
}
|
|
4339
4314
|
const charter = await loadCharterRoot(this.#repoPath, this.#fileReader, this.#yamlParser);
|
|
4340
|
-
const claimingPrimitive = charter.primitiveById.get(
|
|
4315
|
+
const claimingPrimitive = charter.primitiveById.get(id);
|
|
4341
4316
|
if (claimingPrimitive !== void 0)
|
|
4342
4317
|
throw new DomainFault(
|
|
4343
|
-
`"${
|
|
4318
|
+
`"${id}" is already there, declared by ${claimingPrimitive.file}, and one id names one primitive in the whole charter.`,
|
|
4344
4319
|
`Open ${claimingPrimitive.file}, or run this again with an id this charter has not got.`
|
|
4345
4320
|
);
|
|
4346
4321
|
return primitiveDTO(await writeCharter(this.#repoPath, primitive, this.#fileReader, this.#fileWriter));
|
|
@@ -4354,8 +4329,8 @@ var CharterAuthoring = class {
|
|
|
4354
4329
|
* needs to open it. A save made over its hash is checked against the
|
|
4355
4330
|
* primitive read again then.
|
|
4356
4331
|
*/
|
|
4357
|
-
async open(
|
|
4358
|
-
return primitiveDTO(await this.#
|
|
4332
|
+
async open(id) {
|
|
4333
|
+
return primitiveDTO(await this.#primitiveById(id));
|
|
4359
4334
|
}
|
|
4360
4335
|
/**
|
|
4361
4336
|
* One repository primitive's file written over with new headers and a new
|
|
@@ -4367,16 +4342,16 @@ var CharterAuthoring = class {
|
|
|
4367
4342
|
* this one (FR-078). Answers the kind will not take come back as the faults
|
|
4368
4343
|
* `add` gives for them, since both read answers the one way.
|
|
4369
4344
|
*
|
|
4370
|
-
* Kind and id are the
|
|
4345
|
+
* Kind and id are the id's, never the headers': an edit cannot move a
|
|
4371
4346
|
* file, and a rename is a new primitive and a deletion. Nothing is compiled
|
|
4372
4347
|
* and nothing committed (FR-079).
|
|
4373
4348
|
*/
|
|
4374
|
-
async rewrite(
|
|
4375
|
-
const authoredPrimitive = await this.#
|
|
4349
|
+
async rewrite(id, headers, body, openedHash) {
|
|
4350
|
+
const authoredPrimitive = await this.#primitiveById(id);
|
|
4376
4351
|
if (!RepoLayerPrimitive.isSatisfiedBy(authoredPrimitive))
|
|
4377
4352
|
throw new DomainFault(
|
|
4378
|
-
`${
|
|
4379
|
-
`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.`
|
|
4380
4355
|
);
|
|
4381
4356
|
if (authoredPrimitive.hash !== openedHash)
|
|
4382
4357
|
throw new DomainFault(
|
|
@@ -4405,29 +4380,29 @@ var CharterAuthoring = class {
|
|
|
4405
4380
|
* each as dangling, and what to do about them is its author's call. Nothing is
|
|
4406
4381
|
* compiled and nothing committed (FR-079).
|
|
4407
4382
|
*/
|
|
4408
|
-
async remove(
|
|
4409
|
-
const primitive = await this.#
|
|
4383
|
+
async remove(id) {
|
|
4384
|
+
const primitive = await this.#primitiveById(id);
|
|
4410
4385
|
if (!RepoLayerPrimitive.isSatisfiedBy(primitive))
|
|
4411
4386
|
throw new DomainFault(
|
|
4412
|
-
`${
|
|
4413
|
-
`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.`
|
|
4414
4389
|
);
|
|
4415
4390
|
const file = new URL(primitive.file, this.#repoPath);
|
|
4416
4391
|
await this.#fileWriter.delete(file);
|
|
4417
4392
|
for (const assetFile of primitive.assets) await this.#fileWriter.delete(new URL(assetFile.file, file));
|
|
4418
4393
|
return primitiveDTO(primitive);
|
|
4419
4394
|
}
|
|
4420
|
-
/** The primitive one
|
|
4395
|
+
/** The primitive one id names, off the files as read and not the
|
|
4421
4396
|
* validation. Raised when the charter holds nothing of it, the way `explain`
|
|
4422
4397
|
* raises it: there is no file it is wrong with. */
|
|
4423
|
-
async #
|
|
4424
|
-
const
|
|
4398
|
+
async #primitiveById(id) {
|
|
4399
|
+
const primitiveId = primitiveIdOf(id);
|
|
4425
4400
|
const charter = await loadCharterRoot(this.#repoPath, this.#fileReader, this.#yamlParser);
|
|
4426
|
-
const primitive = charter.primitiveById.get(
|
|
4401
|
+
const primitive = charter.primitiveById.get(primitiveId);
|
|
4427
4402
|
if (primitive === void 0)
|
|
4428
4403
|
throw new DomainFault(
|
|
4429
|
-
`This charter holds no "${
|
|
4430
|
-
'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.'
|
|
4431
4406
|
);
|
|
4432
4407
|
return primitive;
|
|
4433
4408
|
}
|
|
@@ -4568,7 +4543,7 @@ async function writeCredential(secrets2, credential) {
|
|
|
4568
4543
|
async function credentialFor(secrets2, authorizing2, origin, { refused = false } = {}) {
|
|
4569
4544
|
if (origin.endpoint !== void 0 && !isSecureEndpoint(origin.endpoint))
|
|
4570
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".');
|
|
4571
|
-
const signInAgain = `Run "cw mcp auth ${origin.
|
|
4546
|
+
const signInAgain = `Run "cw mcp auth ${origin.ids[0] ?? ""}" at a terminal to sign in there.`;
|
|
4572
4547
|
const credential = await readCredential(secrets2, origin.address);
|
|
4573
4548
|
if (credential === void 0) throw new DomainFault(`You are not signed in to ${origin.address}.`, signInAgain);
|
|
4574
4549
|
const expired = refused || credential.expiresAt !== void 0 && Date.parse(credential.expiresAt) <= Date.now();
|
|
@@ -4603,18 +4578,18 @@ async function loadMcpTools(repo, fileReader2, yamlParser2) {
|
|
|
4603
4578
|
if (Array.isArray(catalogJson)) catalogEntries = catalogJson;
|
|
4604
4579
|
} catch {
|
|
4605
4580
|
}
|
|
4606
|
-
const
|
|
4607
|
-
for (const {
|
|
4608
|
-
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;
|
|
4609
4584
|
const compiledDocument = await fileReader2.readIfThere(new URL(file, repo));
|
|
4610
4585
|
if (compiledDocument === void 0) continue;
|
|
4611
4586
|
try {
|
|
4612
4587
|
const primitive = primitiveOf(compiledDocument, yamlParser2);
|
|
4613
|
-
if (primitive instanceof McpPrimitive)
|
|
4588
|
+
if (primitive instanceof McpPrimitive) toolsById.set(id, primitive.headers.tools);
|
|
4614
4589
|
} catch {
|
|
4615
4590
|
}
|
|
4616
4591
|
}
|
|
4617
|
-
return
|
|
4592
|
+
return toolsById;
|
|
4618
4593
|
}
|
|
4619
4594
|
|
|
4620
4595
|
// src/hexagon/port/zdriven/DrivenFault.ts
|
|
@@ -4649,11 +4624,11 @@ var McpConnecting = class {
|
|
|
4649
4624
|
}
|
|
4650
4625
|
async signInStatus() {
|
|
4651
4626
|
return Promise.all(
|
|
4652
|
-
(await this.#signInAddresses()).map(async ({ address,
|
|
4627
|
+
(await this.#signInAddresses()).map(async ({ address, ids, auth: auth2 }) => {
|
|
4653
4628
|
const credential = await readCredential(this.#secrets, address);
|
|
4654
4629
|
return {
|
|
4655
4630
|
type: "SignInStatus",
|
|
4656
|
-
data: { address,
|
|
4631
|
+
data: { address, ids, auth: auth2, signedIn: credential !== void 0, ...credential && { method: credential.method } }
|
|
4657
4632
|
};
|
|
4658
4633
|
})
|
|
4659
4634
|
);
|
|
@@ -4676,8 +4651,8 @@ var McpConnecting = class {
|
|
|
4676
4651
|
const toolRoute = toolRoutes.get(name);
|
|
4677
4652
|
if (toolRoute === void 0)
|
|
4678
4653
|
return errorAnswerOf(unreachableTools.get(name) ?? `"${name}" is not a tool this server serves; call one it listed.`);
|
|
4679
|
-
const { origin,
|
|
4680
|
-
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.`;
|
|
4681
4656
|
const accessTokenOf = (refused) => origin.endpoint === void 0 ? Promise.resolve(void 0) : credentialFor(this.#secrets, this.#authorizing, origin, { refused });
|
|
4682
4657
|
try {
|
|
4683
4658
|
try {
|
|
@@ -4701,7 +4676,7 @@ var McpConnecting = class {
|
|
|
4701
4676
|
}
|
|
4702
4677
|
/**
|
|
4703
4678
|
* Every place the last build listed, reached at once and once per run
|
|
4704
|
-
* (FR-152). What each lists is kept where one of its
|
|
4679
|
+
* (FR-152). What each lists is kept where one of its ids
|
|
4705
4680
|
* declares it, renamed `<prefix>__<tool>`; a place down or not signed in to,
|
|
4706
4681
|
* and a declared tool its place lacks, is said and left out, and the rest
|
|
4707
4682
|
* are served (FR-153, FR-154, SC-039).
|
|
@@ -4709,7 +4684,7 @@ var McpConnecting = class {
|
|
|
4709
4684
|
#serving() {
|
|
4710
4685
|
this.#mcpServing ??= (async () => {
|
|
4711
4686
|
const origins = await loadMcpOrigins(this.#repoPath, this.#fileReader);
|
|
4712
|
-
const
|
|
4687
|
+
const toolsById = await loadMcpTools(this.#repoPath, this.#fileReader, this.#yamlParser);
|
|
4713
4688
|
const problems = [];
|
|
4714
4689
|
const servedTools = [];
|
|
4715
4690
|
const toolRoutes = /* @__PURE__ */ new Map();
|
|
@@ -4728,30 +4703,30 @@ var McpConnecting = class {
|
|
|
4728
4703
|
);
|
|
4729
4704
|
for (const { origin, connection, reason } of reachedPlaces) {
|
|
4730
4705
|
if (connection !== void 0) connections.push(connection);
|
|
4731
|
-
else problems.push(`${origin.
|
|
4732
|
-
for (const
|
|
4733
|
-
const declaredTools =
|
|
4706
|
+
else problems.push(`${origin.ids.join(", ")}: ${reason}`);
|
|
4707
|
+
for (const id of origin.ids) {
|
|
4708
|
+
const declaredTools = toolsById.get(id);
|
|
4734
4709
|
if (declaredTools === void 0) {
|
|
4735
|
-
problems.push(`${
|
|
4710
|
+
problems.push(`${id} has no compiled document under .cw/out/, so none of its tools is served. Run "cw build".`);
|
|
4736
4711
|
continue;
|
|
4737
4712
|
}
|
|
4738
|
-
const
|
|
4739
|
-
if (
|
|
4740
|
-
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.`);
|
|
4741
4716
|
continue;
|
|
4742
4717
|
}
|
|
4743
4718
|
for (const tool of declaredTools) {
|
|
4744
|
-
const servedName = `${
|
|
4719
|
+
const servedName = `${servedPrefix}__${tool}`;
|
|
4745
4720
|
if (connection === void 0) {
|
|
4746
4721
|
unreachableTools.set(servedName, reason ?? "");
|
|
4747
4722
|
continue;
|
|
4748
4723
|
}
|
|
4749
4724
|
const upstreamTool = connection.tools.find(({ name }) => name === tool);
|
|
4750
4725
|
if (upstreamTool === void 0) {
|
|
4751
|
-
problems.push(`${
|
|
4726
|
+
problems.push(`${id} declares "${tool}", which ${origin.address} does not have; it is not served.`);
|
|
4752
4727
|
continue;
|
|
4753
4728
|
}
|
|
4754
|
-
const placeName = origin.path === void 0 ?
|
|
4729
|
+
const placeName = origin.path === void 0 ? id : `${id} \u2014 ${origin.path}`;
|
|
4755
4730
|
servedTools.push({
|
|
4756
4731
|
type: "ServedTool",
|
|
4757
4732
|
data: {
|
|
@@ -4760,7 +4735,7 @@ var McpConnecting = class {
|
|
|
4760
4735
|
inputSchema: upstreamTool.inputSchema
|
|
4761
4736
|
}
|
|
4762
4737
|
});
|
|
4763
|
-
toolRoutes.set(servedName, { origin,
|
|
4738
|
+
toolRoutes.set(servedName, { origin, id, upstream: tool, connection });
|
|
4764
4739
|
}
|
|
4765
4740
|
}
|
|
4766
4741
|
}
|
|
@@ -4785,16 +4760,16 @@ var McpConnecting = class {
|
|
|
4785
4760
|
async #signInAddresses() {
|
|
4786
4761
|
const origins = await loadMcpOrigins(this.#repoPath, this.#fileReader);
|
|
4787
4762
|
const addresses = /* @__PURE__ */ new Map();
|
|
4788
|
-
for (const { address, endpoint,
|
|
4789
|
-
const signInAddress = addresses.get(address) ?? { ...endpoint !== void 0 && { endpoint },
|
|
4790
|
-
|
|
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));
|
|
4791
4766
|
auth2.forEach((method) => signInAddress.auth.add(method));
|
|
4792
4767
|
addresses.set(address, signInAddress);
|
|
4793
4768
|
}
|
|
4794
|
-
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 }]) => ({
|
|
4795
4770
|
address,
|
|
4796
4771
|
...endpoint !== void 0 && { endpoint },
|
|
4797
|
-
|
|
4772
|
+
ids: [...ids].sort(),
|
|
4798
4773
|
auth: ["oauth", "token"].filter((method) => auth2.has(method))
|
|
4799
4774
|
}));
|
|
4800
4775
|
}
|
|
@@ -4900,12 +4875,6 @@ import { promisify } from "util";
|
|
|
4900
4875
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
4901
4876
|
var run = promisify(execFile);
|
|
4902
4877
|
var Git = class {
|
|
4903
|
-
/** How it looks at a folder. Reading files is somebody else's port, and this
|
|
4904
|
-
* asks it rather than reaching for the filesystem itself. */
|
|
4905
|
-
constructor(fileReader2) {
|
|
4906
|
-
this.fileReader = fileReader2;
|
|
4907
|
-
}
|
|
4908
|
-
fileReader;
|
|
4909
4878
|
async isInstalled(folder) {
|
|
4910
4879
|
try {
|
|
4911
4880
|
await run("git", ["rev-parse", "--git-dir"], { cwd: fileURLToPath2(folder) });
|
|
@@ -4947,35 +4916,65 @@ var Git = class {
|
|
|
4947
4916
|
}
|
|
4948
4917
|
}
|
|
4949
4918
|
/**
|
|
4950
|
-
* `git
|
|
4951
|
-
*
|
|
4952
|
-
*
|
|
4919
|
+
* `git fetch`, handed the source exactly as it was written, then
|
|
4920
|
+
* `git read-tree` of the one folder asked for into the folder given, and a
|
|
4921
|
+
* commit: a folder already there is removed in the same commit, so
|
|
4922
|
+
* installing a source and bringing it up to date are one call (FR-023,
|
|
4923
|
+
* FR-030, FR-042).
|
|
4953
4924
|
*
|
|
4954
4925
|
* Which transport it is, whether the credentials are there, whether the
|
|
4955
4926
|
* version exists: git answers all three, and what it says is what the user
|
|
4956
|
-
* reads (FR-031).
|
|
4957
|
-
*
|
|
4958
|
-
* `--squash` because what is installed is content to read and not a history
|
|
4959
|
-
* to walk: one commit lands, holding the source's files under the folder
|
|
4960
|
-
* given (FR-028). `HEAD` where no version is named, which is what the source
|
|
4927
|
+
* reads (FR-031). `HEAD` where no version is named, which is what the source
|
|
4961
4928
|
* calls its own default.
|
|
4929
|
+
*
|
|
4930
|
+
* `git subtree` takes a source whole and cannot take one folder of it, so
|
|
4931
|
+
* the tree is read straight off what was fetched. Whether that folder is
|
|
4932
|
+
* there, and whether the refused one is, is asked of git before anything is
|
|
4933
|
+
* touched. One commit lands, holding the folder's files and no history to
|
|
4934
|
+
* walk (FR-028); none lands where the copy is what was already there.
|
|
4962
4935
|
*/
|
|
4963
|
-
async subtreeAdd(source, repo, intoSubFolder, version) {
|
|
4936
|
+
async subtreeAdd(source, sourceSubFolder, repo, intoSubFolder, version, refusedIfSourceHolds) {
|
|
4964
4937
|
const cwd = fileURLToPath2(repo);
|
|
4965
|
-
const
|
|
4966
|
-
const targetFolderExist = (await this.fileReader.readFilesRecursively(targetFolder)).length > 0;
|
|
4938
|
+
const ref = version ?? "HEAD";
|
|
4967
4939
|
try {
|
|
4968
|
-
await run(
|
|
4969
|
-
"git",
|
|
4970
|
-
["subtree", targetFolderExist ? "pull" : "add", `--prefix=${intoSubFolder}`, "--squash", "--", source, version ?? "HEAD"],
|
|
4971
|
-
{ cwd }
|
|
4972
|
-
);
|
|
4940
|
+
await run("git", ["fetch", "--quiet", "--no-tags", "--", source, ref], { cwd });
|
|
4973
4941
|
} catch (raised) {
|
|
4974
4942
|
throw new DrivenFault(
|
|
4975
4943
|
`${raised}`,
|
|
4976
4944
|
version === void 0 ? "Check the address, and that you can reach it from here." : `Check the address, and that "${version}" is a tag or branch it has.`
|
|
4977
4945
|
);
|
|
4978
4946
|
}
|
|
4947
|
+
const { stdout: fetchedCommit } = await run("git", ["rev-parse", "FETCH_HEAD"], { cwd });
|
|
4948
|
+
const isTreeAt = (folder) => run("git", ["cat-file", "-t", `${fetchedCommit.trim()}:${folder}`], { cwd }).then(
|
|
4949
|
+
({ stdout }) => stdout.trim() === "tree",
|
|
4950
|
+
() => false
|
|
4951
|
+
);
|
|
4952
|
+
const atVersion = version === void 0 ? "" : ` at ${version}`;
|
|
4953
|
+
if (!await isTreeAt(sourceSubFolder))
|
|
4954
|
+
throw new DrivenFault(
|
|
4955
|
+
`${source} has no folder "${sourceSubFolder}"${atVersion}.`,
|
|
4956
|
+
`Check that the source holds "${sourceSubFolder}" at the version asked for.`
|
|
4957
|
+
);
|
|
4958
|
+
if (refusedIfSourceHolds !== void 0 && await isTreeAt(refusedIfSourceHolds))
|
|
4959
|
+
throw new DrivenFault(
|
|
4960
|
+
`${source} holds "${refusedIfSourceHolds}"${atVersion}, which this copy refuses.`,
|
|
4961
|
+
`Remove "${refusedIfSourceHolds}" from the source, or copy a version without it.`
|
|
4962
|
+
);
|
|
4963
|
+
const sourceTree = `${fetchedCommit.trim()}:${sourceSubFolder}`;
|
|
4964
|
+
try {
|
|
4965
|
+
await run("git", ["rm", "-r", "--quiet", "--ignore-unmatch", "--", intoSubFolder], { cwd });
|
|
4966
|
+
await run("git", ["read-tree", `--prefix=${intoSubFolder}/`, "-u", sourceTree], { cwd });
|
|
4967
|
+
const unchanged = await run("git", ["diff", "--cached", "--quiet"], { cwd }).then(
|
|
4968
|
+
() => true,
|
|
4969
|
+
() => false
|
|
4970
|
+
);
|
|
4971
|
+
if (!unchanged)
|
|
4972
|
+
await run("git", ["commit", "-qm", `Install ${source}${atVersion} into ${intoSubFolder}`], { cwd });
|
|
4973
|
+
return intoSubFolder;
|
|
4974
|
+
} catch (raised) {
|
|
4975
|
+
await run("git", ["restore", "--source=HEAD", "--staged", "--worktree", "--", intoSubFolder], { cwd }).catch(() => void 0);
|
|
4976
|
+
throw new DrivenFault(`${raised}`, `Check that "${intoSubFolder}" holds nothing of your own in hand, and run this again.`);
|
|
4977
|
+
}
|
|
4979
4978
|
}
|
|
4980
4979
|
/** `git rm -r` and a commit of its own: the folder is gone from the working
|
|
4981
4980
|
* tree and from the history from here on, the way adding it put it into
|
|
@@ -5258,7 +5257,7 @@ function faultOf(raised, failed, fix) {
|
|
|
5258
5257
|
var fileReader = new FileReaders();
|
|
5259
5258
|
var yamlParser = new YamlParser();
|
|
5260
5259
|
var fileWriter = new FileOutput();
|
|
5261
|
-
var vcs = new Git(
|
|
5260
|
+
var vcs = new Git();
|
|
5262
5261
|
var secrets = new OsSecrets();
|
|
5263
5262
|
var authorizing = new OAuth();
|
|
5264
5263
|
var mcpServers = new McpClients();
|