cherry-works 0.7.2 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/main.js CHANGED
@@ -9507,7 +9507,7 @@ var require_core = __commonJS({
9507
9507
  errorsText(errors = this.errors, { separator = ", ", dataVar = "data" } = {}) {
9508
9508
  if (!errors || errors.length === 0)
9509
9509
  return "No errors";
9510
- return errors.map((e) => `${dataVar}${e.instancePath} ${e.message}`).reduce((text, msg) => text + separator + msg);
9510
+ return errors.map((e) => `${dataVar}${e.instancePath} ${e.message}`).reduce((text2, msg) => text2 + separator + msg);
9511
9511
  }
9512
9512
  $dataMetaSchema(metaSchema, keywordsJsonPointers) {
9513
9513
  const rules = this.RULES.all;
@@ -14822,14 +14822,14 @@ var require_foldFlowLines = __commonJS({
14822
14822
  var FOLD_FLOW = "flow";
14823
14823
  var FOLD_BLOCK = "block";
14824
14824
  var FOLD_QUOTED = "quoted";
14825
- function foldFlowLines(text, indent, mode = "flow", { indentAtStart, lineWidth = 80, minContentWidth = 20, onFold, onOverflow } = {}) {
14825
+ function foldFlowLines(text2, indent, mode = "flow", { indentAtStart, lineWidth = 80, minContentWidth = 20, onFold, onOverflow } = {}) {
14826
14826
  if (!lineWidth || lineWidth < 0)
14827
- return text;
14827
+ return text2;
14828
14828
  if (lineWidth < minContentWidth)
14829
14829
  minContentWidth = 0;
14830
14830
  const endStep = Math.max(1 + minContentWidth, 1 + lineWidth - indent.length);
14831
- if (text.length <= endStep)
14832
- return text;
14831
+ if (text2.length <= endStep)
14832
+ return text2;
14833
14833
  const folds = [];
14834
14834
  const escapedFolds = {};
14835
14835
  let end = lineWidth - indent.length;
@@ -14846,14 +14846,14 @@ var require_foldFlowLines = __commonJS({
14846
14846
  let escStart = -1;
14847
14847
  let escEnd = -1;
14848
14848
  if (mode === FOLD_BLOCK) {
14849
- i = consumeMoreIndentedLines(text, i, indent.length);
14849
+ i = consumeMoreIndentedLines(text2, i, indent.length);
14850
14850
  if (i !== -1)
14851
14851
  end = i + endStep;
14852
14852
  }
14853
- for (let ch; ch = text[i += 1]; ) {
14853
+ for (let ch; ch = text2[i += 1]; ) {
14854
14854
  if (mode === FOLD_QUOTED && ch === "\\") {
14855
14855
  escStart = i;
14856
- switch (text[i + 1]) {
14856
+ switch (text2[i + 1]) {
14857
14857
  case "x":
14858
14858
  i += 3;
14859
14859
  break;
@@ -14870,12 +14870,12 @@ var require_foldFlowLines = __commonJS({
14870
14870
  }
14871
14871
  if (ch === "\n") {
14872
14872
  if (mode === FOLD_BLOCK)
14873
- i = consumeMoreIndentedLines(text, i, indent.length);
14873
+ i = consumeMoreIndentedLines(text2, i, indent.length);
14874
14874
  end = i + indent.length + endStep;
14875
14875
  split = void 0;
14876
14876
  } else {
14877
14877
  if (ch === " " && prev && prev !== " " && prev !== "\n" && prev !== " ") {
14878
- const next = text[i + 1];
14878
+ const next = text2[i + 1];
14879
14879
  if (next && next !== " " && next !== "\n" && next !== " ")
14880
14880
  split = i;
14881
14881
  }
@@ -14887,12 +14887,12 @@ var require_foldFlowLines = __commonJS({
14887
14887
  } else if (mode === FOLD_QUOTED) {
14888
14888
  while (prev === " " || prev === " ") {
14889
14889
  prev = ch;
14890
- ch = text[i += 1];
14890
+ ch = text2[i += 1];
14891
14891
  overflow = true;
14892
14892
  }
14893
14893
  const j = i > escEnd + 1 ? i - 2 : escStart - 1;
14894
14894
  if (escapedFolds[j])
14895
- return text;
14895
+ return text2;
14896
14896
  folds.push(j);
14897
14897
  escapedFolds[j] = true;
14898
14898
  end = j + endStep;
@@ -14907,39 +14907,39 @@ var require_foldFlowLines = __commonJS({
14907
14907
  if (overflow && onOverflow)
14908
14908
  onOverflow();
14909
14909
  if (folds.length === 0)
14910
- return text;
14910
+ return text2;
14911
14911
  if (onFold)
14912
14912
  onFold();
14913
- let res = text.slice(0, folds[0]);
14913
+ let res = text2.slice(0, folds[0]);
14914
14914
  for (let i2 = 0; i2 < folds.length; ++i2) {
14915
14915
  const fold = folds[i2];
14916
- const end2 = folds[i2 + 1] || text.length;
14916
+ const end2 = folds[i2 + 1] || text2.length;
14917
14917
  if (fold === 0)
14918
14918
  res = `
14919
- ${indent}${text.slice(0, end2)}`;
14919
+ ${indent}${text2.slice(0, end2)}`;
14920
14920
  else {
14921
14921
  if (mode === FOLD_QUOTED && escapedFolds[fold])
14922
- res += `${text[fold]}\\`;
14922
+ res += `${text2[fold]}\\`;
14923
14923
  res += `
14924
- ${indent}${text.slice(fold + 1, end2)}`;
14924
+ ${indent}${text2.slice(fold + 1, end2)}`;
14925
14925
  }
14926
14926
  }
14927
14927
  return res;
14928
14928
  }
14929
- function consumeMoreIndentedLines(text, i, indent) {
14929
+ function consumeMoreIndentedLines(text2, i, indent) {
14930
14930
  let end = i;
14931
14931
  let start = i + 1;
14932
- let ch = text[start];
14932
+ let ch = text2[start];
14933
14933
  while (ch === " " || ch === " ") {
14934
14934
  if (i < start + indent) {
14935
- ch = text[++i];
14935
+ ch = text2[++i];
14936
14936
  } else {
14937
14937
  do {
14938
- ch = text[++i];
14938
+ ch = text2[++i];
14939
14939
  } while (ch && ch !== "\n");
14940
14940
  end = i;
14941
14941
  start = i + 1;
14942
- ch = text[start];
14942
+ ch = text2[start];
14943
14943
  }
14944
14944
  }
14945
14945
  return end;
@@ -21665,7 +21665,7 @@ var require_cross_spawn = __commonJS({
21665
21665
  var cp = __require("child_process");
21666
21666
  var parse5 = require_parse2();
21667
21667
  var enoent = require_enoent();
21668
- function spawn5(command2, args, options) {
21668
+ function spawn6(command2, args, options) {
21669
21669
  const parsed = parse5(command2, args, options);
21670
21670
  const spawned = cp.spawn(parsed.command, parsed.args, parsed.options);
21671
21671
  enoent.hookChildProcess(spawned, parsed);
@@ -21677,8 +21677,8 @@ var require_cross_spawn = __commonJS({
21677
21677
  result.error = result.error || enoent.verifyENOENTSync(result.status, parsed);
21678
21678
  return result;
21679
21679
  }
21680
- module.exports = spawn5;
21681
- module.exports.spawn = spawn5;
21680
+ module.exports = spawn6;
21681
+ module.exports.spawn = spawn6;
21682
21682
  module.exports.sync = spawnSync;
21683
21683
  module.exports._parse = parse5;
21684
21684
  module.exports._enoent = enoent;
@@ -21898,7 +21898,7 @@ import { pathToFileURL as pathToFileURL2 } from "url";
21898
21898
  // package.json
21899
21899
  var package_default = {
21900
21900
  name: "cherry-works",
21901
- version: "0.7.2",
21901
+ version: "0.8.0",
21902
21902
  description: "The charter engine: resolves a charter and compiles it into what a coding agent reads.",
21903
21903
  keywords: [
21904
21904
  "claude-code",
@@ -22057,10 +22057,10 @@ var UI = class {
22057
22057
  });
22058
22058
  return this.rows[this.rows.length - 1];
22059
22059
  }
22060
- colFromString(text) {
22060
+ colFromString(text2) {
22061
22061
  return {
22062
- text,
22063
- padding: this.measurePadding(text)
22062
+ text: text2,
22063
+ padding: this.measurePadding(text2)
22064
22064
  };
22065
22065
  }
22066
22066
  measurePadding(str) {
@@ -25324,17 +25324,17 @@ function usage(yargs, shim3) {
25324
25324
  };
25325
25325
  return self;
25326
25326
  }
25327
- function isIndentedText(text) {
25328
- return typeof text === "object";
25327
+ function isIndentedText(text2) {
25328
+ return typeof text2 === "object";
25329
25329
  }
25330
- function addIndentation(text, indent) {
25331
- return isIndentedText(text) ? { text: text.text, indentation: text.indentation + indent } : { text, indentation: indent };
25330
+ function addIndentation(text2, indent) {
25331
+ return isIndentedText(text2) ? { text: text2.text, indentation: text2.indentation + indent } : { text: text2, indentation: indent };
25332
25332
  }
25333
- function getIndentation(text) {
25334
- return isIndentedText(text) ? text.indentation : 0;
25333
+ function getIndentation(text2) {
25334
+ return isIndentedText(text2) ? text2.indentation : 0;
25335
25335
  }
25336
- function getText(text) {
25337
- return isIndentedText(text) ? text.text : text;
25336
+ function getText(text2) {
25337
+ return isIndentedText(text2) ? text2.text : text2;
25338
25338
  }
25339
25339
 
25340
25340
  // node_modules/.pnpm/yargs@18.1.0/node_modules/yargs/build/lib/completion-templates.js
@@ -37577,8 +37577,8 @@ function ko_default() {
37577
37577
  }
37578
37578
 
37579
37579
  // node_modules/.pnpm/zod@4.6.1/node_modules/zod/v4/locales/lt.js
37580
- var capitalizeFirstCharacter = (text) => {
37581
- return text.charAt(0).toUpperCase() + text.slice(1);
37580
+ var capitalizeFirstCharacter = (text2) => {
37581
+ return text2.charAt(0).toUpperCase() + text2.slice(1);
37582
37582
  };
37583
37583
  function getUnitTypeFromNumber(number4) {
37584
37584
  const abs = Math.abs(number4);
@@ -51930,7 +51930,7 @@ var HonoRequest = class {
51930
51930
  * ```
51931
51931
  */
51932
51932
  json() {
51933
- return this.#cachedBody("text").then((text) => JSON.parse(text));
51933
+ return this.#cachedBody("text").then((text2) => JSON.parse(text2));
51934
51934
  }
51935
51935
  /**
51936
51936
  * `.text()` can parse Request body of type `text/plain`
@@ -52496,9 +52496,9 @@ var Context = class {
52496
52496
  * })
52497
52497
  * ```
52498
52498
  */
52499
- text = (text, arg, headers) => {
52500
- return !this.#preparedHeaders && !this.#status && !arg && !headers && !this.finalized ? new Response(text) : this.#newResponse(
52501
- text,
52499
+ text = (text2, arg, headers) => {
52500
+ return !this.#preparedHeaders && !this.#status && !arg && !headers && !this.finalized ? new Response(text2) : this.#newResponse(
52501
+ text2,
52502
52502
  arg,
52503
52503
  setDefaultContentType(TEXT_PLAIN, headers)
52504
52504
  );
@@ -57394,6 +57394,15 @@ var ServedToolsSchema = dto(
57394
57394
  })
57395
57395
  );
57396
57396
  var ToolAnswerSchema = dto("ToolAnswer", external_exports.record(external_exports.string(), external_exports.unknown()));
57397
+ var VendorPrimitiveSchema = dto(
57398
+ "VendorPrimitive",
57399
+ external_exports.object({
57400
+ kind: external_exports.string(),
57401
+ id: external_exports.string(),
57402
+ description: external_exports.string(),
57403
+ leftOutHeaders: StringsSchema
57404
+ })
57405
+ );
57397
57406
  var WorkspaceSettingsSchema = dto(
57398
57407
  "WorkspaceSettings",
57399
57408
  external_exports.object({
@@ -57474,6 +57483,7 @@ var DataDTOs = byType(
57474
57483
  TestSuiteSchema,
57475
57484
  TestSuitesSchema,
57476
57485
  ToolAnswerSchema,
57486
+ VendorPrimitiveSchema,
57477
57487
  WorkspaceSettingsSchema
57478
57488
  );
57479
57489
 
@@ -57515,7 +57525,28 @@ var SessionReviewOutcomeSchema = dto(
57515
57525
  dailySessionSummary: external_exports.record(external_exports.string(), DataDTOs.SessionSummary)
57516
57526
  })
57517
57527
  );
57518
- var OutcomeDTOs = byType(DoctorOutcomeSchema, ExplanationOutcomeSchema, SessionReviewOutcomeSchema);
57528
+ var SensorRunOutcomeSchema = dto(
57529
+ "SensorRunOutcome",
57530
+ external_exports.object({
57531
+ exitCode: external_exports.number(),
57532
+ answer: external_exports.string(),
57533
+ problem: external_exports.string()
57534
+ })
57535
+ );
57536
+ var VendorAddOutcomeSchema = dto(
57537
+ "VendorAddOutcome",
57538
+ external_exports.object({
57539
+ folder: external_exports.string(),
57540
+ primitives: external_exports.array(DataDTOs.VendorPrimitive).readonly()
57541
+ })
57542
+ );
57543
+ var OutcomeDTOs = byType(
57544
+ DoctorOutcomeSchema,
57545
+ ExplanationOutcomeSchema,
57546
+ SensorRunOutcomeSchema,
57547
+ SessionReviewOutcomeSchema,
57548
+ VendorAddOutcomeSchema
57549
+ );
57519
57550
 
57520
57551
  // src/hexagon/domain/models/helper.ts
57521
57552
  function formatFrontmatterValue(value) {
@@ -57837,10 +57868,10 @@ var SAMPLE_CASES = [
57837
57868
  var SAMPLE = JSON.stringify({ cases: SAMPLE_CASES }, void 0, 1).replace(/\n\s*/g, " ");
57838
57869
  var DEFAULT_TEST_SUITE_BODY = `${JSON.stringify({ description: "What these cases pin down.", cases: [SAMPLE_CASES[0]] }, null, 2)}
57839
57870
  `;
57840
- function testSuiteOf(text) {
57871
+ function testSuiteOf(text2) {
57841
57872
  let written;
57842
57873
  try {
57843
- written = JSON.parse(text);
57874
+ written = JSON.parse(text2);
57844
57875
  } catch {
57845
57876
  throw new TestSuiteFault(
57846
57877
  `This file is not JSON, so nothing can read the cases it was meant to hold.`,
@@ -57849,7 +57880,7 @@ function testSuiteOf(text) {
57849
57880
  }
57850
57881
  const read = TestSuiteSchema2.safeParse(written);
57851
57882
  if (!read.success) throw new TestSuiteFault(`This file is not written as a suite of cases.`, `Write it as ${SAMPLE}`);
57852
- return new TestSuite(read.data.description, read.data.cases, text);
57883
+ return new TestSuite(read.data.description, read.data.cases, text2);
57853
57884
  }
57854
57885
 
57855
57886
  // src/hexagon/domain/models/test/TestRoot.ts
@@ -58075,6 +58106,16 @@ function sessionReviewOutcomeDTO(sessionAnalysis, sessionAnalysisSummary, sessio
58075
58106
  }
58076
58107
  };
58077
58108
  }
58109
+ function sensorRunOutcomeDTO(signalAnswer) {
58110
+ return { type: "SensorRunOutcome", data: { exitCode: signalAnswer.exitCode, answer: signalAnswer.stdout, problem: signalAnswer.stderr } };
58111
+ }
58112
+ function vendorPrimitiveDTO(vendorPrimitive) {
58113
+ const { kind, id, description, leftOutHeaders } = vendorPrimitive;
58114
+ return { type: "VendorPrimitive", data: { kind, id, description, leftOutHeaders } };
58115
+ }
58116
+ function vendorAddOutcomeDTO(vendorInstall) {
58117
+ return { type: "VendorAddOutcome", data: { folder: vendorInstall.folder, primitives: vendorInstall.vendorPrimitives.map(vendorPrimitiveDTO) } };
58118
+ }
58078
58119
 
58079
58120
  // src/driver/portal/routes.ts
58080
58121
  var json3 = (schema, description) => ({
@@ -58377,8 +58418,8 @@ function api(charterAuthoringApp2, charterVendoringApp2, testAuthoringApp2) {
58377
58418
  )
58378
58419
  },
58379
58420
  responses: {
58380
- 204: { description: "The source is installed, and committed" },
58381
- 422: json3(DataDTOs.Fault, "No repository, work in hand, or what git refused")
58421
+ 204: { description: "The source is installed \u2014 a charter whole, or every skill of a skills repository \u2014 left unstaged" },
58422
+ 422: json3(DataDTOs.Fault, "No repository, a source holding neither a charter nor a skill, or what git refused")
58382
58423
  }
58383
58424
  }),
58384
58425
  async (c) => {
@@ -58514,8 +58555,27 @@ var RemoveCommand = class {
58514
58555
  }
58515
58556
  };
58516
58557
 
58517
- // src/driver/cli/commands/SessionsCommand.ts
58558
+ // src/driver/cli/commands/SensorRunCommand.ts
58559
+ import { text } from "stream/consumers";
58518
58560
  var OPTIONS12 = {
58561
+ signal: {
58562
+ type: "string",
58563
+ describe: "The signal the host fired, as a sensor names it: Stop, PreToolUse\u2026"
58564
+ }
58565
+ };
58566
+ var SensorRunCommand = class {
58567
+ name = "sensor run <signal>";
58568
+ summary = "Run every sensor of a signal, as the host's one hook for it";
58569
+ options = OPTIONS12;
58570
+ async run({ sensorRunningApp: sensorRunningApp2 }, { signal }) {
58571
+ const stdin = process.stdin.isTTY ? "" : await text(process.stdin);
58572
+ const { data: sensorRunOutcome } = await sensorRunningApp2.runSensors(signal ?? "", stdin);
58573
+ return { code: sensorRunOutcome.exitCode, result: sensorRunOutcome.answer, problem: sensorRunOutcome.problem };
58574
+ }
58575
+ };
58576
+
58577
+ // src/driver/cli/commands/SessionsCommand.ts
58578
+ var OPTIONS13 = {
58519
58579
  since: {
58520
58580
  type: "string",
58521
58581
  describe: "Count the sessions whose last stop is on or after this day, YYYY-MM-DD; 31 days from it unless --until says"
@@ -58533,7 +58593,7 @@ var OPTIONS12 = {
58533
58593
  var SessionsCommand = class {
58534
58594
  name = "sessions";
58535
58595
  summary = "Sum up the tokens of the sessions kept on this machine, by day and by session";
58536
- options = OPTIONS12;
58596
+ options = OPTIONS13;
58537
58597
  async run({ sessionReviewingApp: sessionReviewingApp2 }, { since, until, json: isJson }) {
58538
58598
  const sessionReviewOutcome = await sessionReviewingApp2.sessionSummary(since, until);
58539
58599
  if (isJson) return { code: EXIT_OK, result: `${JSON.stringify(sessionReviewOutcome, null, 2)}
@@ -58591,7 +58651,7 @@ var SuiteAddCommand = class {
58591
58651
  };
58592
58652
 
58593
58653
  // src/driver/cli/commands/SuiteEditCommand.ts
58594
- var OPTIONS13 = {
58654
+ var OPTIONS14 = {
58595
58655
  name: {
58596
58656
  type: "string",
58597
58657
  describe: `The test file's name under ${TEST_DIRECTORY}/: "untitled-1.json"`
@@ -58600,7 +58660,7 @@ var OPTIONS13 = {
58600
58660
  var SuiteEditCommand = class {
58601
58661
  name = "suite edit <name>";
58602
58662
  summary = "Open one test file in your editor";
58603
- options = OPTIONS13;
58663
+ options = OPTIONS14;
58604
58664
  async run({ cwd, testAuthoringApp: testAuthoringApp2 }, { name }) {
58605
58665
  const { testSuites } = (await testAuthoringApp2.suites()).data;
58606
58666
  if (!testSuites.some((one) => one.data.name === name))
@@ -58613,7 +58673,7 @@ var SuiteEditCommand = class {
58613
58673
  };
58614
58674
 
58615
58675
  // src/driver/cli/commands/SuiteRemoveCommand.ts
58616
- var OPTIONS14 = {
58676
+ var OPTIONS15 = {
58617
58677
  name: {
58618
58678
  type: "string",
58619
58679
  describe: `The test file's name under ${TEST_DIRECTORY}/: "untitled-1.json"`
@@ -58622,7 +58682,7 @@ var OPTIONS14 = {
58622
58682
  var SuiteRemoveCommand = class {
58623
58683
  name = "suite remove <name>";
58624
58684
  summary = "Delete one test file";
58625
- options = OPTIONS14;
58685
+ options = OPTIONS15;
58626
58686
  async run({ testAuthoringApp: testAuthoringApp2 }, { name }) {
58627
58687
  const removedName = await testAuthoringApp2.removeSuite(name ?? "");
58628
58688
  return { code: EXIT_OK, result: `Removed ${TEST_DIRECTORY}/${removedName}.
@@ -58669,26 +58729,34 @@ function lines2(one) {
58669
58729
  }
58670
58730
 
58671
58731
  // src/driver/cli/commands/VendorAddCommand.ts
58672
- var OPTIONS15 = {
58732
+ var OPTIONS16 = {
58673
58733
  source: {
58674
58734
  type: "string",
58675
- describe: "What git clones the charter from: an address over any transport, or a path on disk"
58735
+ describe: "What git fetches the charter or the skills from: an address over any transport, or a path on disk"
58676
58736
  },
58677
58737
  ref: {
58678
58738
  type: "string",
58679
58739
  describe: "The tag, branch or commit to pin to; the source's own default where none is named"
58740
+ },
58741
+ skill: {
58742
+ type: "string",
58743
+ isArray: true,
58744
+ describe: "A skill to take from a skills repository, by its name; typed once per skill, and every skill it holds where none is named. Refused for a charter"
58680
58745
  }
58681
58746
  };
58682
58747
  var VendorAddCommand = class {
58683
58748
  name = "vendor add <source>";
58684
- summary = "Install a charter from a git source, or bring an installed one up to date";
58685
- options = OPTIONS15;
58686
- async run({ charterVendoringApp: charterVendoringApp2 }, { source, ref }) {
58687
- const folder = await charterVendoringApp2.add(source ?? "", ref);
58749
+ summary = "Install a charter or skills from a git source, or bring an installed one up to date";
58750
+ options = OPTIONS16;
58751
+ async run({ charterVendoringApp: charterVendoringApp2 }, { source, ref, skill }) {
58752
+ const { folder, primitives } = (await charterVendoringApp2.add(source ?? "", ref, skill)).data;
58688
58753
  return {
58689
58754
  code: EXIT_OK,
58690
58755
  result: [
58691
58756
  `Installed ${source} under ${folder}${ref === void 0 ? "" : `, pinned to ${ref}`}.`,
58757
+ ...primitives.map(
58758
+ ({ data: { kind, id, leftOutHeaders } }) => ` ${kind} ${id}${leftOutHeaders.length === 0 ? "" : `, leaving out ${leftOutHeaders.join(", ")}: the charter has no field for them`}`
58759
+ ),
58692
58760
  'Run "cw build" to compile what it adds to this charter.',
58693
58761
  ""
58694
58762
  ].join("\n")
@@ -58710,7 +58778,7 @@ var VendorListCommand = class {
58710
58778
  };
58711
58779
 
58712
58780
  // src/driver/cli/commands/VendorRemoveCommand.ts
58713
- var OPTIONS16 = {
58781
+ var OPTIONS17 = {
58714
58782
  name: {
58715
58783
  type: "string",
58716
58784
  describe: "The folder this charter was installed as, as it reads under .cw/vendor/"
@@ -58719,7 +58787,7 @@ var OPTIONS16 = {
58719
58787
  var VendorRemoveCommand = class {
58720
58788
  name = "vendor remove <name>";
58721
58789
  summary = "Take an installed charter away";
58722
- options = OPTIONS16;
58790
+ options = OPTIONS17;
58723
58791
  async run({ charterVendoringApp: charterVendoringApp2 }, { name }) {
58724
58792
  const folder = await charterVendoringApp2.remove(name ?? "");
58725
58793
  return {
@@ -58756,7 +58824,8 @@ var COMMANDS = [
58756
58824
  new VendorListCommand(),
58757
58825
  new PortalCommand(),
58758
58826
  new McpAuthCommand(),
58759
- new McpServeCommand()
58827
+ new McpServeCommand(),
58828
+ new SensorRunCommand()
58760
58829
  ];
58761
58830
 
58762
58831
  // src/driver/cli/Commander.ts
@@ -59088,7 +59157,16 @@ var PlaybookHeadersSchema = CommonHeadersSchema.extend({
59088
59157
  triggers: GoodArraySchema,
59089
59158
  /** Whether it is kept out of the commands its host offers the user, being a
59090
59159
  * step of something else; the agent still opens it (CORE-FR-173). */
59091
- "disable-user-invocation": FlagSchema.optional()
59160
+ "disable-user-invocation": FlagSchema.optional(),
59161
+ /** Whether the agent is kept from opening it by itself, its description
59162
+ * matching what is asked; the user still runs it as a command (CORE-FR-181). */
59163
+ "disable-model-invocation": FlagSchema.optional()
59164
+ }).superRefine((headers, context) => {
59165
+ if (headers["disable-user-invocation"] === true && headers["disable-model-invocation"] === true)
59166
+ context.addIssue({
59167
+ code: "custom",
59168
+ message: `This playbook declares both "disable-user-invocation: true" and "disable-model-invocation: true", so neither the user nor the agent could open it; drop one.`
59169
+ });
59092
59170
  });
59093
59171
  var PlaybookPrimitive = class _PlaybookPrimitive extends BasePrimitive {
59094
59172
  static kind = "playbook";
@@ -59160,7 +59238,16 @@ var SkillHeadersSchema = CommonHeadersSchema.extend({
59160
59238
  triggers: GoodArraySchema,
59161
59239
  /** Whether it is kept out of the commands its host offers the user, being a
59162
59240
  * step of something else; the agent still opens it (CORE-FR-173). */
59163
- "disable-user-invocation": FlagSchema.optional()
59241
+ "disable-user-invocation": FlagSchema.optional(),
59242
+ /** Whether the agent is kept from opening it by itself, its description
59243
+ * matching what is asked; the user still runs it as a command (CORE-FR-181). */
59244
+ "disable-model-invocation": FlagSchema.optional()
59245
+ }).superRefine((headers, context) => {
59246
+ if (headers["disable-user-invocation"] === true && headers["disable-model-invocation"] === true)
59247
+ context.addIssue({
59248
+ code: "custom",
59249
+ message: `This skill declares both "disable-user-invocation: true" and "disable-model-invocation: true", so neither the user nor the agent could open it; drop one.`
59250
+ });
59164
59251
  });
59165
59252
  var SkillPrimitive = class _SkillPrimitive extends BasePrimitive {
59166
59253
  static kind = "skill";
@@ -59958,6 +60045,220 @@ agent stops as it would have.`,
59958
60045
  // src/hexagon/domain/models/charter/builtin/index.ts
59959
60046
  var BUILTIN_PRIMITIVES = [new CwAuthorSkill(), new CwSessionTokensCounterScript(), new CwSessionTokensSensor(), new CwSessionCostSkill()];
59960
60047
 
60048
+ // src/hexagon/domain/models/vendor/VendorPrimitive.ts
60049
+ var SKILL_FILE = "SKILL.md";
60050
+ var SKILLS_FOLDER = "skills";
60051
+ var SKILLS_FOLDER_DEPTH = 3;
60052
+ var DEPENDENCIES_FOLDER = "node_modules";
60053
+ var CONVERTED_HEADERS = ["name", "description", "disable-model-invocation", "user-invocable"];
60054
+ var VendorPrimitive = class {
60055
+ constructor(kind, id, description, sourceFolder, convertedSkill, leftOutHeaders = []) {
60056
+ this.kind = kind;
60057
+ this.id = id;
60058
+ this.description = description;
60059
+ this.sourceFolder = sourceFolder;
60060
+ this.convertedSkill = convertedSkill;
60061
+ this.leftOutHeaders = leftOutHeaders;
60062
+ }
60063
+ kind;
60064
+ id;
60065
+ description;
60066
+ sourceFolder;
60067
+ convertedSkill;
60068
+ leftOutHeaders;
60069
+ };
60070
+ function frontmatterOf(markdown, parser2) {
60071
+ const lines3 = markdown.replace(/^/, "").split(/\r?\n/);
60072
+ const frontmatterEnd = lines3[0]?.trim() === "---" ? lines3.findIndex((line, index) => index > 0 && line.trim() === "---") : -1;
60073
+ if (frontmatterEnd === -1) return void 0;
60074
+ return {
60075
+ headers: parser2.parse(lines3.slice(1, frontmatterEnd).join("\n")),
60076
+ body: lines3.slice(frontmatterEnd + 1).join("\n").replace(/^(\s*\n)+/, "").trimEnd()
60077
+ };
60078
+ }
60079
+ function skillFilesOf(sourcePaths) {
60080
+ const skillFolders = sourcePaths.filter((sourcePath) => sourcePath.startsWith(`${SKILLS_FOLDER}/`) && sourcePath.endsWith(`/${SKILL_FILE}`)).map((skillFile) => skillFile.slice(0, -`/${SKILL_FILE}`.length)).filter((skillFolder) => {
60081
+ const folderNames = skillFolder.split("/");
60082
+ return folderNames.length - 1 <= SKILLS_FOLDER_DEPTH && !folderNames.includes(DEPENDENCIES_FOLDER);
60083
+ });
60084
+ return skillFolders.filter((skillFolder) => !skillFolders.some((outerFolder) => skillFolder.startsWith(`${outerFolder}/`))).map((skillFolder) => `${skillFolder}/${SKILL_FILE}`);
60085
+ }
60086
+ function charterVendorPrimitiveOf(indexMd, indexPath, charterFolder, parser2) {
60087
+ const sourceFolder = indexPath.slice(0, indexPath.lastIndexOf("/"));
60088
+ const [kind = "", ...idFolders] = sourceFolder.slice(charterFolder.length + 1).split("/");
60089
+ let description = "";
60090
+ try {
60091
+ const declaredDescription = frontmatterOf(indexMd, parser2)?.headers.description;
60092
+ if (typeof declaredDescription === "string") description = declaredDescription;
60093
+ } catch {
60094
+ }
60095
+ return new VendorPrimitive(kind, idFolders.join("/"), description, sourceFolder);
60096
+ }
60097
+ function skillVendorPrimitiveOf(skillMd, skillFile, parser2) {
60098
+ const skillFrontmatter = frontmatterOf(skillMd, parser2);
60099
+ if (skillFrontmatter === void 0)
60100
+ throw new VendorFault(`${skillFile} opens with no "---" frontmatter block.`, `Take a skill whose ${SKILL_FILE} declares its name and description.`);
60101
+ const { headers: skillHeaders, body } = skillFrontmatter;
60102
+ try {
60103
+ const skillPrimitive = SkillPrimitive.of(
60104
+ {
60105
+ id: skillHeaders.name,
60106
+ description: skillHeaders.description,
60107
+ triggers: [skillHeaders.name],
60108
+ ...skillHeaders["disable-model-invocation"] === true ? { "disable-model-invocation": true } : {},
60109
+ ...skillHeaders["user-invocable"] === false ? { "disable-user-invocation": true } : {}
60110
+ },
60111
+ body
60112
+ );
60113
+ return new VendorPrimitive(
60114
+ SkillPrimitive.kind,
60115
+ skillPrimitive.headers.id,
60116
+ skillPrimitive.headers.description,
60117
+ skillFile.slice(0, -SKILL_FILE.length).replace(/\/$/, ""),
60118
+ skillPrimitive,
60119
+ Object.keys(skillHeaders).filter((header) => !CONVERTED_HEADERS.includes(header))
60120
+ );
60121
+ } catch (raised) {
60122
+ const faultMessages = raised instanceof AggregateError ? raised.errors.map((fault) => fault.message) : [`${raised}`];
60123
+ throw new VendorFault(
60124
+ `${skillFile} is no skill the charter can hold: ${faultMessages.join(" ")}`,
60125
+ `Take a skill whose ${SKILL_FILE} declares a "name" of lowercase words joined by "-", and a "description".`
60126
+ );
60127
+ }
60128
+ }
60129
+
60130
+ // src/hexagon/domain/models/vendor/VendorSource.ts
60131
+ var VendorSource = class {
60132
+ constructor(source, name) {
60133
+ this.source = source;
60134
+ this.name = name;
60135
+ }
60136
+ source;
60137
+ name;
60138
+ };
60139
+ function vendorSourceOf(source) {
60140
+ const addressPath = source.replace(/\.git$/, "").replace(/^[a-z][a-z0-9+.-]*:\/\/[^/]*/i, "").replace(/^[^/\\:@]+@[^/\\:]+:/, "");
60141
+ const folderNames = addressPath.split(/[/\\]/).filter(Boolean).slice(-2);
60142
+ if (folderNames.length < 2 || folderNames.some((folderName) => folderName === "." || folderName === ".."))
60143
+ throw new VendorFault(`Invalid path "${source}".`, "Use correct git path, naming its owner and its repository.");
60144
+ return new VendorSource(source, folderNames.join("/"));
60145
+ }
60146
+ function installedVendorFolderOf(name) {
60147
+ const folderNames = name.split(/[/\\]/).filter(Boolean);
60148
+ if (folderNames.length === 0 || folderNames.length > 2 || folderNames.some((folderName) => folderName === "." || folderName === ".."))
60149
+ throw new VendorFault(`Invalid path "${name}".`, "Name the folder as cw vendor list names it under .cw/vendor/.");
60150
+ return folderNames.join("/");
60151
+ }
60152
+
60153
+ // src/hexagon/service/vendorRepo.ts
60154
+ var VENDOR_README = "README.md";
60155
+ async function loadVendorFolders(repo, fileReaders) {
60156
+ const installedFolders = await fileReaders.listFolders(vendorFolderIn(repo));
60157
+ const vendorFolders = await Promise.all(
60158
+ installedFolders.map(async (folder) => {
60159
+ const ownerName = decodeURIComponent(folder.href.split("/").at(-2) ?? "");
60160
+ const childFolders = await fileReaders.listFolders(folder);
60161
+ const childFolderNames = childFolders.map((childFolder) => decodeURIComponent(childFolder.href.split("/").at(-2) ?? ""));
60162
+ const isSource = childFolderNames.some((childFolderName) => KINDS.includes(childFolderName)) || (await fileReaders.listFiles(folder)).some(({ file: file2 }) => file2.href.endsWith(`/${VENDOR_README}`));
60163
+ if (isSource) return [{ folder, vendorName: ownerName }];
60164
+ return childFolders.map((childFolder, index) => ({ folder: childFolder, vendorName: `${ownerName}/${childFolderNames[index]}` }));
60165
+ })
60166
+ );
60167
+ return vendorFolders.flat().sort((leftFolder, rightFolder) => leftFolder.vendorName.localeCompare(rightFolder.vendorName));
60168
+ }
60169
+ async function loadVendorNames(repo, fileReaders) {
60170
+ return (await loadVendorFolders(repo, fileReaders)).map(({ vendorName }) => `${VENDOR_DIRECTORY}/${vendorName}`);
60171
+ }
60172
+ async function ensureVCSReady(repo, vcs2, because) {
60173
+ if (!await vcs2.isInstalled(repo))
60174
+ throw new DomainFault(
60175
+ "This folder is not inside a git repository, and a vendor lives in one.",
60176
+ 'Run "git init" here, or run this again where your repository is.'
60177
+ );
60178
+ if (because !== void 0 && !await vcs2.isClean(repo))
60179
+ throw new DomainFault(
60180
+ `This repository has work in hand, and ${because}.`,
60181
+ "Commit or stash what you are holding, then run this again."
60182
+ );
60183
+ }
60184
+ async function loadFetchedVendor(repo, fileReaders, vcs2, yamlParser2, source, fetchedCommit) {
60185
+ const vendorName = vendorSourceOf(source).name;
60186
+ const folder = `${VENDOR_DIRECTORY}/${vendorName}`;
60187
+ const sourcePaths = await vcs2.listFilesAt(repo, fetchedCommit);
60188
+ const folderUrl = new URL(`${folder}/`, repo);
60189
+ const installedFolderNames = (await fileReaders.listFolders(folderUrl)).map((installedFolder) => decodeURIComponent(installedFolder.href.split("/").at(-2) ?? ""));
60190
+ const holdsCharter = installedFolderNames.some((installedFolderName) => installedFolderName !== SkillPrimitive.kind) || (await fileReaders.listFiles(folderUrl)).some(({ file: file2 }) => file2.href.endsWith(`/${VENDOR_README}`));
60191
+ const holdsVendorSkills = !holdsCharter && installedFolderNames.includes(SkillPrimitive.kind);
60192
+ const reinstallFix = `Run "cw vendor remove ${vendorName}", then install it again.`;
60193
+ if (sourcePaths.some((sourcePath) => sourcePath.startsWith(`${CHARTER_DIRECTORY}/`))) {
60194
+ if (sourcePaths.some((sourcePath) => sourcePath.startsWith(`${VENDOR_DIRECTORY}/`)))
60195
+ throw new VendorFault(`${source} holds "${VENDOR_DIRECTORY}", and vendoring is one level deep.`, `Remove "${VENDOR_DIRECTORY}" from the source, or install a version without it.`);
60196
+ if (holdsVendorSkills) throw new VendorFault(`${folder} holds skills taken from ${source}, which holds a charter now.`, reinstallFix);
60197
+ const charterIndexPaths = sourcePaths.filter((sourcePath) => sourcePath.startsWith(`${CHARTER_DIRECTORY}/`) && sourcePath.endsWith(`/${SkillPrimitive.index}`));
60198
+ const vendorPrimitives2 = await Promise.all(
60199
+ charterIndexPaths.map(async (indexPath) => charterVendorPrimitiveOf(await vcs2.readFileAt(repo, fetchedCommit, indexPath), indexPath, CHARTER_DIRECTORY, yamlParser2))
60200
+ );
60201
+ return { folder, isCharter: true, vendorPrimitives: vendorPrimitives2 };
60202
+ }
60203
+ const skillFiles = skillFilesOf(sourcePaths);
60204
+ if (skillFiles.length === 0)
60205
+ throw new VendorFault(
60206
+ `${source} has no "${CHARTER_DIRECTORY}" and no ${SKILL_FILE} under "${SKILLS_FOLDER}/".`,
60207
+ `Check that the source holds "${CHARTER_DIRECTORY}", or skills under "${SKILLS_FOLDER}/" each in a folder holding its ${SKILL_FILE}, at the version asked for.`
60208
+ );
60209
+ if (holdsCharter) throw new VendorFault(`${folder} holds a charter installed from ${source}, which holds skills alone now.`, reinstallFix);
60210
+ const vendorPrimitives = await Promise.all(
60211
+ skillFiles.map(async (skillFile) => skillVendorPrimitiveOf(await vcs2.readFileAt(repo, fetchedCommit, skillFile), skillFile, yamlParser2))
60212
+ );
60213
+ const sharedIdPrimitives = vendorPrimitives.filter((vendorPrimitive) => vendorPrimitives.some((otherPrimitive) => otherPrimitive !== vendorPrimitive && otherPrimitive.id === vendorPrimitive.id));
60214
+ if (sharedIdPrimitives.length > 0)
60215
+ throw new VendorFault(
60216
+ `${sharedIdPrimitives.map(({ sourceFolder }) => `${sourceFolder}/${SKILL_FILE}`).join(", ")} declare one name, and a name is one skill.`,
60217
+ "Take the skills from a version of the source whose names are its own."
60218
+ );
60219
+ return { folder, isCharter: false, vendorPrimitives: [...vendorPrimitives].sort((leftPrimitive, rightPrimitive) => leftPrimitive.id.localeCompare(rightPrimitive.id)) };
60220
+ }
60221
+ async function addVendor(repo, fileReaders, fileWriter2, vcs2, yamlParser2, source, version2, skillIds = []) {
60222
+ await ensureVCSReady(repo, vcs2);
60223
+ vendorSourceOf(source);
60224
+ const fetchedCommit = await vcs2.fetchCommit(source, repo, version2);
60225
+ const { folder, isCharter, vendorPrimitives } = await loadFetchedVendor(repo, fileReaders, vcs2, yamlParser2, source, fetchedCommit);
60226
+ if (isCharter) {
60227
+ if (skillIds.length > 0)
60228
+ throw new VendorFault(`${source} holds a charter, and --skill is for skills repositories (compatible with npx skills), not a charter: a charter is installed whole.`, "Run this again without --skill.");
60229
+ await vcs2.checkoutFolderAt(repo, fetchedCommit, CHARTER_DIRECTORY, folder);
60230
+ if ((await vcs2.listFilesAt(repo, fetchedCommit)).includes(VENDOR_README))
60231
+ await fileWriter2.write(new URL(`${folder}/${VENDOR_README}`, repo), await vcs2.readFileAt(repo, fetchedCommit, VENDOR_README));
60232
+ return { folder, vendorPrimitives };
60233
+ }
60234
+ const heldSkillIds = vendorPrimitives.map(({ id }) => id);
60235
+ const unheldSkillIds = skillIds.filter((skillId) => !heldSkillIds.includes(skillId));
60236
+ if (unheldSkillIds.length > 0) throw new VendorFault(`${source} holds no skill ${unheldSkillIds.join(", ")}.`, `Take one it holds: ${heldSkillIds.join(", ")}.`);
60237
+ const chosenPrimitives = skillIds.length === 0 ? vendorPrimitives : vendorPrimitives.filter(({ id }) => skillIds.includes(id));
60238
+ for (const { id, sourceFolder, convertedSkill } of chosenPrimitives) {
60239
+ const skillFolder = `${folder}/${SkillPrimitive.kind}/${id}`;
60240
+ await vcs2.checkoutFolderAt(repo, fetchedCommit, sourceFolder, skillFolder);
60241
+ await fileWriter2.write(new URL(`${skillFolder}/${SkillPrimitive.index}`, repo), convertedSkill.toMarkdown());
60242
+ await fileWriter2.delete(new URL(`${skillFolder}/${SKILL_FILE}`, repo));
60243
+ }
60244
+ return { folder, vendorPrimitives: chosenPrimitives };
60245
+ }
60246
+ async function removeVendor(repo, vcs2, name) {
60247
+ await ensureVCSReady(repo, vcs2, "removing a vendor commits that it is gone");
60248
+ const folder = `${VENDOR_DIRECTORY}/${installedVendorFolderOf(name)}`;
60249
+ await vcs2.removeSubFolder(repo, folder);
60250
+ return folder;
60251
+ }
60252
+ async function driftedVendors(repo, vcs2) {
60253
+ const changed = await vcs2.changedUnder(repo, VENDOR_DIRECTORY);
60254
+ const vendorNames = changed.map((path) => {
60255
+ const [ownerName = "", childName = ""] = path.slice(`${VENDOR_DIRECTORY}/`.length).split("/");
60256
+ const isSource = childName === VENDOR_README || KINDS.includes(childName);
60257
+ return isSource ? ownerName : `${ownerName}/${childName}`;
60258
+ });
60259
+ return [...new Set(vendorNames.filter((vendorName) => vendorName !== "" && !vendorName.endsWith("/")))].sort();
60260
+ }
60261
+
59961
60262
  // src/hexagon/service/charterRepo.ts
59962
60263
  async function loadCharterRoot(repo, fileReaders, yamlParser2) {
59963
60264
  const repoHref = repo.href.endsWith("/") ? repo.href : `${repo.href}/`;
@@ -59989,12 +60290,9 @@ async function loadCharterRoot(repo, fileReaders, yamlParser2) {
59989
60290
  ];
59990
60291
  };
59991
60292
  const authoredFiles = await read(charterFolderIn(repo));
59992
- const vendors = await fileReaders.listFolders(vendorFolderIn(repo));
60293
+ const vendorFolders = await loadVendorFolders(repo, fileReaders);
59993
60294
  const vendorFiles = await Promise.all(
59994
- vendors.map(async (folder) => {
59995
- const vendor = decodeURIComponent(folder.href.split("/").at(-2) ?? "");
59996
- return (await read(folder)).map((one) => ({ ...one, vendor }));
59997
- })
60295
+ vendorFolders.map(async ({ folder, vendorName }) => (await read(folder)).map((vendoredFile) => ({ ...vendoredFile, vendor: vendorName })))
59998
60296
  );
59999
60297
  const builtinFiles = BUILTIN_PRIMITIVES.map((primitive) => ({
60000
60298
  path: primitive.file,
@@ -60077,11 +60375,11 @@ var WorkspaceSettings = class {
60077
60375
 
60078
60376
  // src/hexagon/service/settingsRepo.ts
60079
60377
  async function loadSettings(repo, fileReaders) {
60080
- const text = await fileReaders.readIfThere(settingsFileIn(repo));
60081
- if (text === void 0) return new WorkspaceSettings([]);
60378
+ const text2 = await fileReaders.readIfThere(settingsFileIn(repo));
60379
+ if (text2 === void 0) return new WorkspaceSettings([]);
60082
60380
  let written;
60083
60381
  try {
60084
- written = JSON.parse(text);
60382
+ written = JSON.parse(text2);
60085
60383
  } catch {
60086
60384
  throw new SettingsFault(
60087
60385
  `These are this repository's own settings and this is not JSON.`,
@@ -60137,10 +60435,10 @@ async function addTestSuite(repo, fileReaders, fileWriter2) {
60137
60435
  await fileWriter2.write(new URL(name, testFolderIn(repo)), DEFAULT_TEST_SUITE_BODY);
60138
60436
  return name;
60139
60437
  }
60140
- async function writeTestSuite(repo, fileReaders, fileWriter2, name, text) {
60438
+ async function writeTestSuite(repo, fileReaders, fileWriter2, name, text2) {
60141
60439
  await ensureTestSuiteNamed(repo, fileReaders, name);
60142
- testSuiteOf(text);
60143
- await fileWriter2.write(new URL(name, testFolderIn(repo)), text);
60440
+ testSuiteOf(text2);
60441
+ await fileWriter2.write(new URL(name, testFolderIn(repo)), text2);
60144
60442
  return name;
60145
60443
  }
60146
60444
  async function removeTestSuite(repo, fileReaders, fileWriter2, name) {
@@ -60153,54 +60451,6 @@ async function ensureTestSuiteNamed(repo, fileReaders, name) {
60153
60451
  throw new TestSuiteFault(`${TEST_DIRECTORY}/ holds no test file called "${name}".`, 'Run "cw suite add" to write a new one.');
60154
60452
  }
60155
60453
 
60156
- // src/hexagon/domain/models/vendor/VendorSource.ts
60157
- var VendorSource = class {
60158
- constructor(source, name) {
60159
- this.source = source;
60160
- this.name = name;
60161
- }
60162
- source;
60163
- name;
60164
- };
60165
- function vendorSourceOf(source) {
60166
- const name = source.replace(/\.git$/, "").split(/[/:\\]/).filter(Boolean).at(-1) ?? "";
60167
- if (name === "." || name === ".." || name === "") throw new VendorFault(`Invalid path "${source}".`, "Use correct git path.");
60168
- return new VendorSource(source, name);
60169
- }
60170
-
60171
- // src/hexagon/service/vendorRepo.ts
60172
- async function loadVendorNames(repo, fileReaders) {
60173
- return (await fileReaders.listFolders(vendorFolderIn(repo))).map((folder) => `${VENDOR_DIRECTORY}/${decodeURIComponent(folder.href.split("/").at(-2) ?? "")}`).sort();
60174
- }
60175
- async function ensureVCSReady(repo, vcs2, because) {
60176
- if (!await vcs2.isInstalled(repo))
60177
- throw new DomainFault(
60178
- "This folder is not inside a git repository, and a vendor lives in one.",
60179
- 'Run "git init" here, or run this again where your repository is.'
60180
- );
60181
- if (because !== void 0 && !await vcs2.isClean(repo))
60182
- throw new DomainFault(
60183
- `This repository has work in hand, and ${because}.`,
60184
- "Commit or stash what you are holding, then run this again."
60185
- );
60186
- }
60187
- async function addVendor(repo, vcs2, source, version2) {
60188
- await ensureVCSReady(repo, vcs2);
60189
- const folder = `${VENDOR_DIRECTORY}/${vendorSourceOf(source).name}`;
60190
- return vcs2.subtreeAdd(source, [CHARTER_DIRECTORY, "README.md"], repo, folder, version2, VENDOR_DIRECTORY);
60191
- }
60192
- async function removeVendor(repo, vcs2, name) {
60193
- await ensureVCSReady(repo, vcs2, "removing a vendor commits that it is gone");
60194
- const folder = `${VENDOR_DIRECTORY}/${vendorSourceOf(name).name}`;
60195
- await vcs2.removeSubFolder(repo, folder);
60196
- return folder;
60197
- }
60198
- async function driftedVendors(repo, vcs2) {
60199
- const changed = await vcs2.changedUnder(repo, VENDOR_DIRECTORY);
60200
- const names = changed.map((path) => path.slice(`${VENDOR_DIRECTORY}/`.length).split("/")[0]).filter((name) => name !== void 0 && name !== "");
60201
- return [...new Set(names)].sort();
60202
- }
60203
-
60204
60454
  // src/hexagon/domain/services/testService.ts
60205
60455
  var TestCaseReport = class {
60206
60456
  constructor(suiteName, situation, unmet) {
@@ -60387,22 +60637,24 @@ function isStamped(contents) {
60387
60637
 
60388
60638
  // src/hexagon/domain/models/output/CharterOutput.ts
60389
60639
  var CharterOutput = class {
60390
- constructor(catalogue, charterMd, compiledPrimitives, mcpOrigins, providerComponents) {
60640
+ constructor(catalogue, charterMd, compiledPrimitives, mcpOrigins, builtSensors, providerComponents) {
60391
60641
  this.catalogue = catalogue;
60392
60642
  this.charterMd = charterMd;
60393
60643
  this.compiledPrimitives = compiledPrimitives;
60394
60644
  this.mcpOrigins = mcpOrigins;
60645
+ this.builtSensors = builtSensors;
60395
60646
  this.providerComponents = providerComponents;
60396
60647
  }
60397
60648
  catalogue;
60398
60649
  charterMd;
60399
60650
  compiledPrimitives;
60400
60651
  mcpOrigins;
60652
+ builtSensors;
60401
60653
  providerComponents;
60402
60654
  /** Every file this output is put down as: what each of its parts says it is
60403
60655
  * put down as, together (FR-020, FR-021). */
60404
60656
  get projections() {
60405
- return [this.catalogue, this.mcpOrigins, this.charterMd, ...this.compiledPrimitives, ...this.providerComponents].flatMap(
60657
+ return [this.catalogue, this.mcpOrigins, this.builtSensors, this.charterMd, ...this.compiledPrimitives, ...this.providerComponents].flatMap(
60406
60658
  (outputPart) => outputPart.projections
60407
60659
  );
60408
60660
  }
@@ -60528,6 +60780,36 @@ function compiledPrimitiveOf(charter, primitive) {
60528
60780
  );
60529
60781
  }
60530
60782
 
60783
+ // src/hexagon/domain/models/output/common/BuiltSensor.ts
60784
+ function sensorRunCommandOf(signal) {
60785
+ return `cw sensor run ${signal}`;
60786
+ }
60787
+ var BuiltSensors = class {
60788
+ constructor(sensors) {
60789
+ this.sensors = sensors;
60790
+ }
60791
+ sensors;
60792
+ /** The file it is put down as, in the output folder: generated, and the
60793
+ * charter's alone. Indented, since it is committed and read in diffs when a
60794
+ * sensor's command changes. */
60795
+ get projections() {
60796
+ return [
60797
+ {
60798
+ file: `${OUT_DIRECTORY}/sensors.json`,
60799
+ contents: `${JSON.stringify({ sensors: this.sensors }, void 0, 2)}
60800
+ `,
60801
+ projectionPolicy: "replace",
60802
+ executable: false
60803
+ }
60804
+ ];
60805
+ }
60806
+ /** Every signal at least one sensor names, each once, in the order the
60807
+ * sensors name them. */
60808
+ get signals() {
60809
+ return [...new Set(this.sensors.map((builtSensor) => builtSensor.signal))];
60810
+ }
60811
+ };
60812
+
60531
60813
  // src/hexagon/domain/models/output/provider-component/ProviderComponent.ts
60532
60814
  var ProviderComponent = class {
60533
60815
  constructor(path, document, projectionPolicy) {
@@ -60672,7 +60954,7 @@ var ClaudeSettings = class _ClaudeSettings extends SettingBasedComponent {
60672
60954
  };
60673
60955
 
60674
60956
  // src/hexagon/domain/services/compile/claudeComponentFactory.ts
60675
- function claudeComponentsOf(charter, compiledPrimitiveByPrimitive, shortMcpIds) {
60957
+ function claudeComponentsOf(charter, compiledPrimitiveByPrimitive, shortMcpIds, builtSensors) {
60676
60958
  const isSettingComponent = (one) => one.kind === PosturePrimitive.kind || one.kind === SensorPrimitive.kind;
60677
60959
  const asSettings = charter.primitives.filter(isSettingComponent);
60678
60960
  const asDocuments = charter.primitives.filter((one) => !isSettingComponent(one));
@@ -60690,7 +60972,7 @@ function claudeComponentsOf(charter, compiledPrimitiveByPrimitive, shortMcpIds)
60690
60972
  // The section of `CLAUDE.md` that sends this host to the charter at all
60691
60973
  // (FR-051).
60692
60974
  ClaudeEntryFile.of(),
60693
- ...asSettings.length === 0 && toolsByMcpId.size === 0 ? [] : [ClaudeSettings.of(claudeSettingsOf(charter, compiledPrimitiveByPrimitive, asSettings))],
60975
+ ...asSettings.length === 0 && toolsByMcpId.size === 0 ? [] : [ClaudeSettings.of(claudeSettingsOf(charter, builtSensors, asSettings))],
60694
60976
  ClaudeMcpConfig.of(),
60695
60977
  ...asDocuments.flatMap(
60696
60978
  (one) => claudeDocumentComponentOf(
@@ -60702,30 +60984,19 @@ function claudeComponentsOf(charter, compiledPrimitiveByPrimitive, shortMcpIds)
60702
60984
  )
60703
60985
  ];
60704
60986
  }
60705
- function claudeSettingsOf(charter, compiledPrimitiveByPrimitive, ones) {
60706
- const allow = /* @__PURE__ */ new Set();
60707
- const deny = /* @__PURE__ */ new Set();
60708
- const hooks = {};
60709
- for (const one of ones) {
60710
- if (one.kind === PosturePrimitive.kind) {
60711
- const headers = one.headers;
60712
- for (const permission of headers.allow) allow.add(permission);
60713
- for (const permission of headers.deny) deny.add(permission);
60714
- }
60715
- if (one.kind === SensorPrimitive.kind) {
60716
- const { signal, run: run3 } = one.headers;
60717
- const command2 = run3.replace(REFERENCE, (reference, referencedId) => {
60718
- const scriptPrimitive = charter.primitiveById.get(referencedId);
60719
- const executionFile = scriptPrimitive && compiledPrimitiveByPrimitive.get(scriptPrimitive)?.projections.find((projection) => projection.executable)?.file;
60720
- return executionFile ?? reference;
60721
- });
60722
- if (isClaudeHookEvent(signal))
60723
- hooks[signal] = [...hooks[signal] ?? [], { hooks: [{ type: "command", command: command2 }] }];
60987
+ function claudeSettingsOf(charter, builtSensors, settingPrimitives) {
60988
+ const allowedPermissions = /* @__PURE__ */ new Set();
60989
+ const deniedPermissions = /* @__PURE__ */ new Set();
60990
+ for (const settingPrimitive of settingPrimitives) {
60991
+ if (settingPrimitive.kind === PosturePrimitive.kind) {
60992
+ for (const permission of settingPrimitive.headers.allow) allowedPermissions.add(permission);
60993
+ for (const permission of settingPrimitive.headers.deny) deniedPermissions.add(permission);
60724
60994
  }
60725
60995
  }
60996
+ const hookEvents = builtSensors.signals.filter(isClaudeHookEvent).sort();
60726
60997
  return {
60727
- ...allow.size === 0 && deny.size === 0 ? {} : { permissions: { allow: [...allow], deny: [...deny] } },
60728
- ...Object.keys(hooks).length === 0 ? {} : { hooks },
60998
+ ...allowedPermissions.size === 0 && deniedPermissions.size === 0 ? {} : { permissions: { allow: [...allowedPermissions], deny: [...deniedPermissions] } },
60999
+ ...hookEvents.length === 0 ? {} : { hooks: Object.fromEntries(hookEvents.map((hookEvent) => [hookEvent, [{ hooks: [{ type: "command", command: sensorRunCommandOf(hookEvent) }] }]])) },
60729
61000
  // A charter reaching an mcp origin has its tools' schemas deferred by the host
60730
61001
  // itself, the one server listing every tool (EVAL-FR-016).
60731
61002
  ...charter.primitives.some((primitive) => primitive instanceof McpPrimitive) && { env: CLAUDE_TOOL_SEARCH_ENV }
@@ -60762,12 +61033,17 @@ function claudeDocumentComponentOf(sc, compiledFile, agentTools) {
60762
61033
  // for them: what decides that the body is worth opening is the description,
60763
61034
  // because that line is all this host reads before deciding (FR-013), and
60764
61035
  // composing it is the primitive's. One that is a step of another is kept
60765
- // out of the user's commands (CORE-FR-174).
61036
+ // out of the user's commands (CORE-FR-174), and one the user alone starts is
61037
+ // kept out of the agent's own reach (CORE-FR-182).
60766
61038
  case SkillPrimitive.kind:
60767
61039
  case PlaybookPrimitive.kind:
60768
61040
  return ClaudeSkill.of(
60769
61041
  name,
60770
- { description: sc.description(), ...sc.headers["disable-user-invocation"] === true ? { "user-invocable": false } : {} },
61042
+ {
61043
+ description: sc.description(),
61044
+ ...sc.headers["disable-user-invocation"] === true ? { "user-invocable": false } : {},
61045
+ ...sc.headers["disable-model-invocation"] === true ? { "disable-model-invocation": true } : {}
61046
+ },
60771
61047
  pointerTo(`../${compiledFromClaudeFolder}`)
60772
61048
  );
60773
61049
  default:
@@ -60805,6 +61081,19 @@ function mcpOriginsOf(charter, shortMcpIds, toolsByOriginKey = /* @__PURE__ */ n
60805
61081
  });
60806
61082
  }
60807
61083
 
61084
+ // src/hexagon/domain/services/compile/builtSensorsFactory.ts
61085
+ function builtSensorsOf(charter, compiledPrimitiveByPrimitive) {
61086
+ return [...charter.primitives].flatMap((primitive) => primitive.kind === SensorPrimitive.kind ? [primitive] : []).sort((one, another) => one.headers.id < another.headers.id ? -1 : 1).map(({ headers: { id, signal, run: run3 } }) => ({
61087
+ id,
61088
+ signal,
61089
+ command: run3.replace(REFERENCE, (reference, referencedId) => {
61090
+ const scriptPrimitive = charter.primitiveById.get(referencedId);
61091
+ const executionFile = scriptPrimitive && compiledPrimitiveByPrimitive.get(scriptPrimitive)?.projections.find((projection) => projection.executable)?.file;
61092
+ return executionFile ?? reference;
61093
+ })
61094
+ }));
61095
+ }
61096
+
60808
61097
  // src/hexagon/domain/services/compile/compileService.ts
60809
61098
  function compile2(charter, agents, toolsByOriginKey = /* @__PURE__ */ new Map()) {
60810
61099
  const shortMcpIds = shortenStringsOf(
@@ -60813,6 +61102,7 @@ function compile2(charter, agents, toolsByOriginKey = /* @__PURE__ */ new Map())
60813
61102
  const compiledPrimitiveByPrimitive = new Map(
60814
61103
  [...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)])
60815
61104
  );
61105
+ const builtSensors = new BuiltSensors(builtSensorsOf(charter, compiledPrimitiveByPrimitive));
60816
61106
  return new CharterOutput(
60817
61107
  // What an agent opens from the catalogue is the primitive as it compiled,
60818
61108
  // not the file its author wrote: the whole body in one place, under the
@@ -60822,13 +61112,14 @@ function compile2(charter, agents, toolsByOriginKey = /* @__PURE__ */ new Map())
60822
61112
  charterMdOf(PRIMITIVE_CLASSES),
60823
61113
  [...compiledPrimitiveByPrimitive.values()],
60824
61114
  new McpOrigins(mcpOriginsOf(charter, shortMcpIds, toolsByOriginKey)),
60825
- agents.flatMap((agent) => providerComponentsOf(agent, charter, compiledPrimitiveByPrimitive, shortMcpIds))
61115
+ builtSensors,
61116
+ agents.flatMap((agent) => providerComponentsOf(agent, charter, compiledPrimitiveByPrimitive, shortMcpIds, builtSensors))
60826
61117
  );
60827
61118
  }
60828
- function providerComponentsOf(agent, charter, compiledPrimitiveByPrimitive, shortMcpIds) {
61119
+ function providerComponentsOf(agent, charter, compiledPrimitiveByPrimitive, shortMcpIds, builtSensors) {
60829
61120
  switch (agent) {
60830
61121
  case "claude":
60831
- return claudeComponentsOf(charter, compiledPrimitiveByPrimitive, shortMcpIds);
61122
+ return claudeComponentsOf(charter, compiledPrimitiveByPrimitive, shortMcpIds, builtSensors);
60832
61123
  }
60833
61124
  }
60834
61125
 
@@ -61099,7 +61390,7 @@ function tokenFactorOf(agent) {
61099
61390
  function estimatedMainContextOf(charter, agent, tokenCounter2) {
61100
61391
  return new MainContext(
61101
61392
  agent,
61102
- mainContextTextsOf(charter, agent).map(({ text, ...mainContextText }) => ({ ...mainContextText, tokens: Math.round(tokenCounter2.countTokens(text) * tokenFactorOf(agent)) })),
61393
+ mainContextTextsOf(charter, agent).map(({ text: text2, ...mainContextText }) => ({ ...mainContextText, tokens: Math.round(tokenCounter2.countTokens(text2) * tokenFactorOf(agent)) })),
61103
61394
  false
61104
61395
  );
61105
61396
  }
@@ -61112,7 +61403,7 @@ async function exactMainContextOf(charter, agent, agentCli) {
61112
61403
  const countTokensFromAnswer = ({ usage: { inputTokens, cacheCreationInputTokens, cacheReadInputTokens } }) => inputTokens + cacheCreationInputTokens + cacheReadInputTokens;
61113
61404
  const ownTokens = countTokensFromAnswer(await agentCli.ask("."));
61114
61405
  const mainContextTexts = mainContextTextsOf(charter, agent);
61115
- const sentTokenCounts = await Promise.all(mainContextTexts.map(async ({ text }) => countTokensFromAnswer(await agentCli.ask(text))));
61406
+ const sentTokenCounts = await Promise.all(mainContextTexts.map(async ({ text: text2 }) => countTokensFromAnswer(await agentCli.ask(text2))));
61116
61407
  return new MainContext(
61117
61408
  agent,
61118
61409
  mainContextTexts.map(({ text: _text, ...mainContextText }, index) => ({ ...mainContextText, tokens: Math.max(0, sentTokenCounts[index] - ownTokens) })),
@@ -61596,13 +61887,17 @@ var CharterVendoring = class {
61596
61887
  #repoPath;
61597
61888
  #fileReader;
61598
61889
  #vcs;
61599
- constructor(repoPath2, fileReader2, vcs2) {
61890
+ #fileWriter;
61891
+ #yamlParser;
61892
+ constructor(repoPath2, fileReader2, vcs2, fileWriter2, yamlParser2) {
61600
61893
  this.#repoPath = repoPath2;
61601
61894
  this.#fileReader = fileReader2;
61602
61895
  this.#vcs = vcs2;
61896
+ this.#fileWriter = fileWriter2;
61897
+ this.#yamlParser = yamlParser2;
61603
61898
  }
61604
- async add(source, version2) {
61605
- return addVendor(this.#repoPath, this.#vcs, source, version2);
61899
+ async add(source, version2, skillIds = []) {
61900
+ return vendorAddOutcomeDTO(await addVendor(this.#repoPath, this.#fileReader, this.#fileWriter, this.#vcs, this.#yamlParser, source, version2, skillIds));
61606
61901
  }
61607
61902
  async remove(name) {
61608
61903
  return removeVendor(this.#repoPath, this.#vcs, name);
@@ -61630,8 +61925,8 @@ var TestAuthoring = class {
61630
61925
  async addSuite() {
61631
61926
  return addTestSuite(this.#repoPath, this.#fileReader, this.#fileWriter);
61632
61927
  }
61633
- async writeSuite(name, text) {
61634
- return writeTestSuite(this.#repoPath, this.#fileReader, this.#fileWriter, name, text);
61928
+ async writeSuite(name, text2) {
61929
+ return writeTestSuite(this.#repoPath, this.#fileReader, this.#fileWriter, name, text2);
61635
61930
  }
61636
61931
  async removeSuite(name) {
61637
61932
  return removeTestSuite(this.#repoPath, this.#fileReader, this.#fileWriter, name);
@@ -61648,9 +61943,9 @@ var UnauthorizedMcpFault = class extends AdapterFault {
61648
61943
 
61649
61944
  // src/hexagon/application/McpConnecting.ts
61650
61945
  var CONNECT_TIMEOUT_MS = 1e4;
61651
- var errorAnswerOf = (text) => ({
61946
+ var errorAnswerOf = (text2) => ({
61652
61947
  type: "ToolAnswer",
61653
- data: { content: [{ type: "text", text }], isError: true }
61948
+ data: { content: [{ type: "text", text: text2 }], isError: true }
61654
61949
  });
61655
61950
  var McpConnecting = class {
61656
61951
  #repoPath;
@@ -61947,10 +62242,10 @@ var EvalSuiteSchema = external_exports.strictObject({
61947
62242
  var SAMPLE2 = JSON.stringify({
61948
62243
  cases: [{ prompt: "plan this feature: export a report", expect: { invoke: "sdd-plan", follow: "plain-names", maxTokens: 2e5 } }]
61949
62244
  });
61950
- function evalSuiteOf(text) {
62245
+ function evalSuiteOf(text2) {
61951
62246
  let evalSuiteJson;
61952
62247
  try {
61953
- evalSuiteJson = JSON.parse(text);
62248
+ evalSuiteJson = JSON.parse(text2);
61954
62249
  } catch {
61955
62250
  throw new EvalSuiteFault(`This file is not JSON, so nothing can read the cases it was meant to hold.`, `Write it as ${SAMPLE2}`);
61956
62251
  }
@@ -62150,6 +62445,92 @@ var CharterEvaluating = class {
62150
62445
  }
62151
62446
  };
62152
62447
 
62448
+ // src/hexagon/domain/models/sensor/SensorAnswer.ts
62449
+ var CONTEXT_SIGNALS = ["UserPromptSubmit", "SessionStart"];
62450
+ var PERMISSION_DECISIONS = ["deny", "ask", "allow"];
62451
+ function signalAnswerOf(signal, sensorAnswers) {
62452
+ const blockingAnswers = sensorAnswers.filter((sensorAnswer) => sensorAnswer.exitCode === 2);
62453
+ const failedAnswers = sensorAnswers.filter((sensorAnswer) => sensorAnswer.exitCode !== 0);
62454
+ const exitCode = blockingAnswers.length > 0 ? 2 : failedAnswers[0]?.exitCode ?? 0;
62455
+ const stderr = (blockingAnswers.length > 0 ? blockingAnswers : failedAnswers).map((sensorAnswer) => sensorAnswer.stderr).join("");
62456
+ const answerTexts = sensorAnswers.map((sensorAnswer) => sensorAnswer.stdout.trim()).filter((answerText) => answerText !== "");
62457
+ const isPlainObject3 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
62458
+ const structuredAnswers = answerTexts.map((answerText) => {
62459
+ try {
62460
+ const answerJSON = JSON.parse(answerText);
62461
+ return isPlainObject3(answerJSON) ? answerJSON : void 0;
62462
+ } catch {
62463
+ return void 0;
62464
+ }
62465
+ });
62466
+ if (structuredAnswers.every((structuredAnswer) => structuredAnswer === void 0))
62467
+ return { exitCode, stdout: answerTexts.map((answerText) => `${answerText}
62468
+ `).join(""), stderr };
62469
+ const plainTexts = answerTexts.filter((_answerText, index) => structuredAnswers[index] === void 0);
62470
+ const answerJSONs = structuredAnswers.filter((structuredAnswer) => structuredAnswer !== void 0);
62471
+ const hookSpecificOutputs = answerJSONs.map((answerJSON) => answerJSON["hookSpecificOutput"]).filter(isPlainObject3);
62472
+ const joinedTextOf = (values) => values.filter((value) => typeof value === "string" && value !== "").join("\n");
62473
+ const combinedAnswer = Object.assign({}, ...[...answerJSONs].reverse());
62474
+ const combinedHookSpecificOutput = Object.assign({}, ...[...hookSpecificOutputs].reverse());
62475
+ const stoppingAnswers = answerJSONs.filter((answerJSON) => answerJSON["continue"] === false);
62476
+ if (stoppingAnswers.length > 0) {
62477
+ combinedAnswer["continue"] = false;
62478
+ combinedAnswer["stopReason"] = joinedTextOf(stoppingAnswers.map((answerJSON) => answerJSON["stopReason"]));
62479
+ }
62480
+ if ("suppressOutput" in combinedAnswer) combinedAnswer["suppressOutput"] = answerJSONs.every((answerJSON) => answerJSON["suppressOutput"] === true);
62481
+ const blockingJSONs = answerJSONs.filter((answerJSON) => answerJSON["decision"] === "block");
62482
+ if (blockingJSONs.length > 0) {
62483
+ combinedAnswer["decision"] = "block";
62484
+ combinedAnswer["reason"] = joinedTextOf(blockingJSONs.map((answerJSON) => answerJSON["reason"]));
62485
+ }
62486
+ const isContextSignal = CONTEXT_SIGNALS.includes(signal);
62487
+ const systemMessage = joinedTextOf([...answerJSONs.map((answerJSON) => answerJSON["systemMessage"]), ...isContextSignal ? [] : plainTexts]);
62488
+ if (systemMessage !== "") combinedAnswer["systemMessage"] = systemMessage;
62489
+ const permissionDecision = PERMISSION_DECISIONS.find((rankedDecision) => hookSpecificOutputs.some((hookSpecificOutput) => hookSpecificOutput["permissionDecision"] === rankedDecision));
62490
+ if (permissionDecision !== void 0) {
62491
+ combinedHookSpecificOutput["permissionDecision"] = permissionDecision;
62492
+ combinedHookSpecificOutput["permissionDecisionReason"] = joinedTextOf(
62493
+ hookSpecificOutputs.filter((hookSpecificOutput) => hookSpecificOutput["permissionDecision"] === permissionDecision).map((hookSpecificOutput) => hookSpecificOutput["permissionDecisionReason"])
62494
+ );
62495
+ }
62496
+ const additionalContext = joinedTextOf([...hookSpecificOutputs.map((hookSpecificOutput) => hookSpecificOutput["additionalContext"]), ...isContextSignal ? plainTexts : []]);
62497
+ if (additionalContext !== "") combinedHookSpecificOutput["additionalContext"] = additionalContext;
62498
+ if (Object.keys(combinedHookSpecificOutput).length > 0) combinedAnswer["hookSpecificOutput"] = { ...combinedHookSpecificOutput, hookEventName: signal };
62499
+ return { exitCode, stdout: `${JSON.stringify(combinedAnswer)}
62500
+ `, stderr };
62501
+ }
62502
+
62503
+ // src/hexagon/service/sensorsRepo.ts
62504
+ async function loadBuiltSensors(repo, fileReader2) {
62505
+ const contents = await fileReader2.readIfThere(new URL("sensors.json", outFolderIn(repo)));
62506
+ try {
62507
+ const sensorsJson = JSON.parse(contents ?? "{}");
62508
+ const sensors = sensorsJson.sensors;
62509
+ if (Array.isArray(sensors)) return new BuiltSensors(sensors);
62510
+ } catch {
62511
+ }
62512
+ return new BuiltSensors([]);
62513
+ }
62514
+
62515
+ // src/hexagon/application/SensorRunning.ts
62516
+ var SensorRunning = class {
62517
+ #repoPath;
62518
+ #fileReader;
62519
+ #shell;
62520
+ constructor(repoPath2, fileReader2, shell2) {
62521
+ this.#repoPath = repoPath2;
62522
+ this.#fileReader = fileReader2;
62523
+ this.#shell = shell2;
62524
+ }
62525
+ async runSensors(signal, stdin) {
62526
+ const builtSensors = await loadBuiltSensors(this.#repoPath, this.#fileReader);
62527
+ const sensorAnswers = await Promise.all(
62528
+ builtSensors.sensors.filter((builtSensor) => builtSensor.signal === signal).map((builtSensor) => this.#shell.runShellCommand(builtSensor.command, this.#repoPath, stdin))
62529
+ );
62530
+ return sensorRunOutcomeDTO(signalAnswerOf(signal, sensorAnswers));
62531
+ }
62532
+ };
62533
+
62153
62534
  // src/zdriven/FileReaders.ts
62154
62535
  import { readdir, readFile, stat } from "fs/promises";
62155
62536
  var FileReaders = class {
@@ -62237,7 +62618,7 @@ import { execFile } from "child_process";
62237
62618
  import { rm as rm2 } from "fs/promises";
62238
62619
  import { promisify } from "util";
62239
62620
  import { fileURLToPath as fileURLToPath3 } from "url";
62240
- import { basename as basename2, join as join3 } from "path";
62621
+ import { join as join3 } from "path";
62241
62622
  var run = promisify(execFile);
62242
62623
  var Git = class {
62243
62624
  async isInstalled(folder) {
@@ -62280,74 +62661,47 @@ var Git = class {
62280
62661
  return [];
62281
62662
  }
62282
62663
  }
62283
- /**
62284
- * `git fetch`, handed the source exactly as it was written, then each path
62285
- * asked for read into an index of its own and checked out into the folder
62286
- * given — a folder's files at that folder's root, a file under its own name:
62287
- * the repository's own index is never touched, so nothing is staged, nothing
62288
- * is committed, and whatever is in hand stays as it was. A folder already
62289
- * there is replaced, so installing a source and bringing it up to date are
62290
- * one call (FR-023, FR-030, FR-042).
62291
- *
62292
- * Which transport it is, whether the credentials are there, whether the
62293
- * version exists: git answers all three, and what it says is what the user
62294
- * reads (FR-031). `HEAD` where no version is named, which is what the source
62295
- * calls its own default.
62296
- *
62297
- * `git subtree` takes a source whole and cannot take one folder of it, so
62298
- * each path is read straight off what was fetched. Whether the first is
62299
- * there, and whether the refused one is, is asked of git before anything is
62300
- * touched. What lands has no history to walk (FR-028).
62301
- */
62302
- async subtreeAdd(source, includePaths, repo, intoSubFolder, version2, refusedIfSourceHolds) {
62664
+ /** `git fetch` of one version, and `FETCH_HEAD` read at once, before
62665
+ * anything else can move it. */
62666
+ async fetchCommit(source, repo, version2) {
62303
62667
  const cwd = fileURLToPath3(repo);
62304
- const ref = version2 ?? "HEAD";
62305
62668
  try {
62306
- await run("git", ["fetch", "--quiet", "--no-tags", "--", source, ref], { cwd });
62669
+ await run("git", ["fetch", "--quiet", "--no-tags", "--", source, version2 ?? "HEAD"], { cwd });
62307
62670
  } catch (raised) {
62308
62671
  throw new AdapterFault(
62309
- `${raised}`,
62672
+ `${source} could not be fetched: ${raised}`,
62310
62673
  version2 === void 0 ? "Check the address, and that you can reach it from here." : `Check the address, and that "${version2}" is a tag or branch it has.`
62311
62674
  );
62312
62675
  }
62313
- const fetchedCommit = (await run("git", ["rev-parse", "FETCH_HEAD"], { cwd })).stdout.trim();
62314
- const objectTypeAt = (path) => run("git", ["cat-file", "-t", `${fetchedCommit}:${path}`], { cwd }).then(
62315
- ({ stdout }) => stdout.trim(),
62316
- () => void 0
62317
- );
62318
- const atVersion = version2 === void 0 ? "" : ` at ${version2}`;
62319
- const [requiredPath = ""] = includePaths;
62320
- if (await objectTypeAt(requiredPath) === void 0)
62321
- throw new AdapterFault(`${source} has no "${requiredPath}"${atVersion}.`, `Check that the source holds "${requiredPath}" at the version asked for.`);
62322
- if (refusedIfSourceHolds !== void 0 && await objectTypeAt(refusedIfSourceHolds) === "tree")
62323
- throw new AdapterFault(
62324
- `${source} holds "${refusedIfSourceHolds}"${atVersion}, which this copy refuses.`,
62325
- `Remove "${refusedIfSourceHolds}" from the source, or copy a version without it.`
62326
- );
62327
- const vendorIndexPath = (await run("git", ["rev-parse", "--path-format=absolute", "--git-path", "cw-vendor-index"], { cwd })).stdout.trim();
62328
- const vendorIndexEnv = { ...process.env, GIT_INDEX_FILE: vendorIndexPath };
62676
+ return (await run("git", ["rev-parse", "FETCH_HEAD"], { cwd })).stdout.trim();
62677
+ }
62678
+ /** `git ls-tree -r`, the paths split on the NUL `-z` ends each with, so a
62679
+ * path holding a newline or a quote is read as it is. */
62680
+ async listFilesAt(repo, commit) {
62681
+ const { stdout } = await run("git", ["ls-tree", "-r", "-z", "--name-only", commit], { cwd: fileURLToPath3(repo), maxBuffer: 64 * 1024 * 1024 });
62682
+ return stdout.split("\0").filter((path) => path !== "");
62683
+ }
62684
+ /** `git show <commit>:<path>`. */
62685
+ async readFileAt(repo, commit, path) {
62686
+ return (await run("git", ["show", `${commit}:${path}`], { cwd: fileURLToPath3(repo), maxBuffer: 64 * 1024 * 1024 })).stdout;
62687
+ }
62688
+ /** `git read-tree --prefix` into an index of its own, then `checkout-index`
62689
+ * over the folder emptied first: the repository's own
62690
+ * index, and what is staged in it, is never read from or written to. */
62691
+ async checkoutFolderAt(repo, commit, fromFolder, intoSubFolder) {
62692
+ const cwd = fileURLToPath3(repo);
62693
+ const checkoutIndexPath = (await run("git", ["rev-parse", "--path-format=absolute", "--git-path", "cw-checkout-index"], { cwd })).stdout.trim();
62694
+ const checkoutIndexEnv = { ...process.env, GIT_INDEX_FILE: checkoutIndexPath };
62329
62695
  try {
62330
- await rm2(vendorIndexPath, { force: true });
62331
- for (const includePath of includePaths) {
62332
- const objectType = await objectTypeAt(includePath);
62333
- if (objectType === "tree")
62334
- await run("git", ["read-tree", `--prefix=${intoSubFolder}/`, `${fetchedCommit}:${includePath}`], { cwd, env: vendorIndexEnv });
62335
- if (objectType === "blob") {
62336
- const [fileMode = "100644", , fileHash = ""] = (await run("git", ["ls-tree", fetchedCommit, "--", includePath], { cwd })).stdout.split(/\s+/);
62337
- await run("git", ["update-index", "--add", "--cacheinfo", `${fileMode},${fileHash},${intoSubFolder}/${basename2(includePath)}`], {
62338
- cwd,
62339
- env: vendorIndexEnv
62340
- });
62341
- }
62342
- }
62696
+ await rm2(checkoutIndexPath, { force: true });
62697
+ await run("git", ["read-tree", `--prefix=${intoSubFolder}/`, fromFolder === "" ? commit : `${commit}:${fromFolder}`], { cwd, env: checkoutIndexEnv });
62343
62698
  await rm2(join3(cwd, intoSubFolder), { recursive: true, force: true });
62344
- await run("git", ["checkout-index", "--all", "--force"], { cwd, env: vendorIndexEnv });
62345
- return intoSubFolder;
62699
+ await run("git", ["checkout-index", "--all", "--force"], { cwd, env: checkoutIndexEnv });
62346
62700
  } catch (raised) {
62347
62701
  await run("git", ["restore", "--source=HEAD", "--worktree", "--", intoSubFolder], { cwd }).catch(() => void 0);
62348
62702
  throw new AdapterFault(`${raised}`, `Check that "${intoSubFolder}" can be written, and run this again.`);
62349
62703
  } finally {
62350
- await rm2(vendorIndexPath, { force: true });
62704
+ await rm2(checkoutIndexPath, { force: true });
62351
62705
  }
62352
62706
  }
62353
62707
  /** `git rm -r` and a commit of its own: the folder is gone from the working
@@ -64827,7 +65181,7 @@ var StreamableHTTPClientTransport = class {
64827
65181
  this._sessionId = sessionId;
64828
65182
  }
64829
65183
  if (!response.ok) {
64830
- const text = await response.text().catch(() => null);
65184
+ const text2 = await response.text().catch(() => null);
64831
65185
  if (response.status === 401 && this._authProvider) {
64832
65186
  if (this._hasCompletedAuthFlow) {
64833
65187
  throw new StreamableHTTPError(401, "Server returned 401 after successful authentication");
@@ -64873,7 +65227,7 @@ var StreamableHTTPClientTransport = class {
64873
65227
  return this.send(message);
64874
65228
  }
64875
65229
  }
64876
- throw new StreamableHTTPError(response.status, `Error POSTing to endpoint: ${text}`);
65230
+ throw new StreamableHTTPError(response.status, `Error POSTing to endpoint: ${text2}`);
64877
65231
  }
64878
65232
  this._hasCompletedAuthFlow = false;
64879
65233
  this._lastUpscopingHeader = void 0;
@@ -65096,12 +65450,12 @@ var _Tiktoken = class {
65096
65450
  this.textMap.set(rank, bytes);
65097
65451
  }
65098
65452
  this.specialTokens = { ...ranks.special_tokens, ...extendedSpecialTokens };
65099
- this.inverseSpecialTokens = Object.entries(this.specialTokens).reduce((memo2, [text, rank]) => {
65100
- memo2[rank] = this.textEncoder.encode(text);
65453
+ this.inverseSpecialTokens = Object.entries(this.specialTokens).reduce((memo2, [text2, rank]) => {
65454
+ memo2[rank] = this.textEncoder.encode(text2);
65101
65455
  return memo2;
65102
65456
  }, {});
65103
65457
  }
65104
- encode(text, allowedSpecial = [], disallowedSpecial = "all") {
65458
+ encode(text2, allowedSpecial = [], disallowedSpecial = "all") {
65105
65459
  const regexes = new RegExp(this.patStr, "ug");
65106
65460
  const specialRegex = _Tiktoken.specialTokenRegex(
65107
65461
  Object.keys(this.specialTokens)
@@ -65119,7 +65473,7 @@ var _Tiktoken = class {
65119
65473
  const disallowedSpecialRegex = _Tiktoken.specialTokenRegex([
65120
65474
  ...disallowedSpecialSet
65121
65475
  ]);
65122
- const specialMatch = text.match(disallowedSpecialRegex);
65476
+ const specialMatch = text2.match(disallowedSpecialRegex);
65123
65477
  if (specialMatch != null) {
65124
65478
  throw new Error(
65125
65479
  `The text contains a special token that is not allowed: ${specialMatch[0]}`
@@ -65132,13 +65486,13 @@ var _Tiktoken = class {
65132
65486
  let startFind = start;
65133
65487
  while (true) {
65134
65488
  specialRegex.lastIndex = startFind;
65135
- nextSpecial = specialRegex.exec(text);
65489
+ nextSpecial = specialRegex.exec(text2);
65136
65490
  if (nextSpecial == null || allowedSpecialSet.has(nextSpecial[0]))
65137
65491
  break;
65138
65492
  startFind = nextSpecial.index + 1;
65139
65493
  }
65140
- const end = nextSpecial?.index ?? text.length;
65141
- for (const match2 of text.substring(start, end).matchAll(regexes)) {
65494
+ const end = nextSpecial?.index ?? text2.length;
65495
+ for (const match2 of text2.substring(start, end).matchAll(regexes)) {
65142
65496
  const piece = this.textEncoder.encode(match2[0]);
65143
65497
  const token2 = this.rankMap.get(piece.join(","));
65144
65498
  if (token2 != null) {
@@ -65186,9 +65540,9 @@ var o200k_base_default = { "pat_str": "[^\\r\\n\\p{L}\\p{N}]?[\\p{Lu}\\p{Lt}\\p{
65186
65540
  // src/zdriven/Tiktoken.ts
65187
65541
  var Tiktoken2 = class {
65188
65542
  #encoding;
65189
- countTokens(text) {
65543
+ countTokens(text2) {
65190
65544
  this.#encoding ??= new Tiktoken(o200k_base_default);
65191
- return this.#encoding.encode(text).length;
65545
+ return this.#encoding.encode(text2).length;
65192
65546
  }
65193
65547
  };
65194
65548
 
@@ -65378,11 +65732,11 @@ var Promptfoo = class {
65378
65732
 
65379
65733
  // src/zdriven/ConsoleReporter.ts
65380
65734
  var ConsoleReporter = class {
65381
- report(text) {
65382
- process.stdout.write(text);
65735
+ report(text2) {
65736
+ process.stdout.write(text2);
65383
65737
  }
65384
- problem(text) {
65385
- process.stderr.write(text);
65738
+ problem(text2) {
65739
+ process.stderr.write(text2);
65386
65740
  }
65387
65741
  };
65388
65742
 
@@ -65393,6 +65747,29 @@ var SystemClock = class {
65393
65747
  }
65394
65748
  };
65395
65749
 
65750
+ // src/zdriven/Shell.ts
65751
+ import { spawn as spawn5 } from "child_process";
65752
+ import { fileURLToPath as fileURLToPath5 } from "url";
65753
+ var Shell = class {
65754
+ runShellCommand(command2, workingFolder, stdin) {
65755
+ return new Promise((resolve5) => {
65756
+ const childProcess = spawn5(command2, { shell: true, cwd: fileURLToPath5(workingFolder), stdio: "pipe" });
65757
+ const stdoutChunks = [];
65758
+ const stderrChunks = [];
65759
+ childProcess.stdout.on("data", (chunk) => stdoutChunks.push(chunk));
65760
+ childProcess.stderr.on("data", (chunk) => stderrChunks.push(chunk));
65761
+ childProcess.on("error", (raised) => resolve5({ exitCode: 1, stdout: "", stderr: `${raised.message}
65762
+ ` }));
65763
+ childProcess.on(
65764
+ "close",
65765
+ (exitCode) => resolve5({ exitCode: exitCode ?? 1, stdout: Buffer.concat(stdoutChunks).toString(), stderr: Buffer.concat(stderrChunks).toString() })
65766
+ );
65767
+ childProcess.stdin.on("error", () => void 0);
65768
+ childProcess.stdin.end(stdin);
65769
+ });
65770
+ }
65771
+ };
65772
+
65396
65773
  // main.ts
65397
65774
  var fileReader = new FileReaders();
65398
65775
  var yamlParser = new YamlParser();
@@ -65403,6 +65780,7 @@ var authorizing = new OAuth();
65403
65780
  var mcpServers = new McpClients();
65404
65781
  var tokenCounter = new Tiktoken2();
65405
65782
  var clock = new SystemClock();
65783
+ var shell = new Shell();
65406
65784
  var agentCliByProvider = { claude: new ClaudeCli() };
65407
65785
  var repoPath = pathToFileURL2(`${process.cwd()}/`);
65408
65786
  var charterAuthoringApp = new CharterAuthoring(
@@ -65417,7 +65795,7 @@ var charterAuthoringApp = new CharterAuthoring(
65417
65795
  secrets,
65418
65796
  authorizing
65419
65797
  );
65420
- var charterVendoringApp = new CharterVendoring(repoPath, fileReader, vcs);
65798
+ var charterVendoringApp = new CharterVendoring(repoPath, fileReader, vcs, fileWriter, yamlParser);
65421
65799
  var testAuthoringApp = new TestAuthoring(repoPath, fileReader, fileWriter);
65422
65800
  var mcpConnectingApp = new McpConnecting(repoPath, fileReader, secrets, authorizing, yamlParser, mcpServers);
65423
65801
  var homePath = pathToFileURL2(`${homedir()}/`);
@@ -65425,6 +65803,7 @@ var sessionReviewingApp = new SessionReviewing(repoPath, homePath, fileReader, c
65425
65803
  var promptfooRunner = new Promptfoo(join6(homedir(), ".cherry-works"));
65426
65804
  var progressReporter = new ConsoleReporter();
65427
65805
  var charterEvaluatingApp = new CharterEvaluating(repoPath, fileReader, yamlParser, fileWriter, vcs, agentCliByProvider, promptfooRunner, progressReporter);
65806
+ var sensorRunningApp = new SensorRunning(repoPath, fileReader, shell);
65428
65807
  var cli = new Commander({
65429
65808
  cwd: process.cwd(),
65430
65809
  version: package_default.version,
@@ -65433,7 +65812,8 @@ var cli = new Commander({
65433
65812
  testAuthoringApp,
65434
65813
  mcpConnectingApp,
65435
65814
  sessionReviewingApp,
65436
- charterEvaluatingApp
65815
+ charterEvaluatingApp,
65816
+ sensorRunningApp
65437
65817
  });
65438
65818
  process.exitCode = await cli.run(process.argv.slice(2));
65439
65819
  /*! Bundled license information: