agents-inc 0.161.1 → 0.162.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.
Files changed (91) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/dist/{chunk-C2K7Q7HO.js → chunk-2IMHZEIK.js} +2 -2
  3. package/dist/{chunk-RLSZWDTB.js → chunk-33ZKIYJM.js} +2 -2
  4. package/dist/{chunk-Q4KCUR2T.js → chunk-4KVJJJG2.js} +4 -4
  5. package/dist/{chunk-DOQUAWL4.js → chunk-53XPSAJ4.js} +10 -10
  6. package/dist/{chunk-ZYXQUU2P.js → chunk-57YWAMNT.js} +4 -4
  7. package/dist/{chunk-SBL4AYSB.js → chunk-5J6DO4VW.js} +2 -2
  8. package/dist/{chunk-Z56AUTER.js → chunk-AQSMNUKJ.js} +2 -2
  9. package/dist/{chunk-ZPFM2VHZ.js → chunk-BR3TNSO3.js} +4 -4
  10. package/dist/{chunk-U5O4BWKI.js → chunk-C53RJ7AA.js} +7 -7
  11. package/dist/{chunk-QTLYSVYE.js → chunk-ES7WGE32.js} +3 -3
  12. package/dist/{chunk-IAN7QDBR.js → chunk-GFO55TMY.js} +8 -3
  13. package/dist/chunk-GFO55TMY.js.map +1 -0
  14. package/dist/{chunk-F5JTW3HZ.js → chunk-H2MDPOLG.js} +3 -3
  15. package/dist/{chunk-YC4CLTQE.js → chunk-HRVPZMKK.js} +4 -4
  16. package/dist/{chunk-SXYFATQN.js → chunk-IMTWWA3N.js} +10 -10
  17. package/dist/{chunk-JEJJHRZA.js → chunk-J2YG7P7L.js} +30 -4
  18. package/dist/{chunk-JEJJHRZA.js.map → chunk-J2YG7P7L.js.map} +1 -1
  19. package/dist/{chunk-UE3MLDL2.js → chunk-K26SBQLO.js} +2 -2
  20. package/dist/{chunk-GUFRQJAE.js → chunk-L65XGQKR.js} +4 -4
  21. package/dist/{chunk-SWYV7QXX.js → chunk-RMMEYCEA.js} +2 -2
  22. package/dist/{chunk-WDN4HAN7.js → chunk-S6GX7IJ2.js} +3 -3
  23. package/dist/{chunk-ESI3JYOQ.js → chunk-T2RPUNYP.js} +2 -2
  24. package/dist/{chunk-7WKNPDN2.js → chunk-UNASGGW2.js} +44 -25
  25. package/dist/chunk-UNASGGW2.js.map +1 -0
  26. package/dist/{chunk-QG4NRKZD.js → chunk-VEBEGZUV.js} +2 -2
  27. package/dist/{chunk-TPRRWOHZ.js → chunk-VEONZFTL.js} +2 -2
  28. package/dist/{chunk-ADSIYZGS.js → chunk-WIJM6BX6.js} +2 -2
  29. package/dist/{chunk-2P3MKX5Q.js → chunk-XSM3IBMG.js} +2 -2
  30. package/dist/{chunk-7ZFEZN4Y.js → chunk-ZFPQM63K.js} +69 -7
  31. package/dist/{chunk-7ZFEZN4Y.js.map → chunk-ZFPQM63K.js.map} +1 -1
  32. package/dist/commands/build/marketplace.js +4 -4
  33. package/dist/commands/build/plugins.js +4 -4
  34. package/dist/commands/compile.js +7 -7
  35. package/dist/commands/doctor.js +7 -7
  36. package/dist/commands/edit.js +24 -24
  37. package/dist/commands/eject.js +7 -7
  38. package/dist/commands/init.js +24 -24
  39. package/dist/commands/list.js +5 -5
  40. package/dist/commands/new/marketplace.js +4 -4
  41. package/dist/commands/search.js +12 -11
  42. package/dist/commands/search.js.map +1 -1
  43. package/dist/commands/share.js +19 -13
  44. package/dist/commands/share.js.map +1 -1
  45. package/dist/commands/uninstall.js +7 -7
  46. package/dist/commands/update.js +3 -3
  47. package/dist/components/wizard/domain-selection.js +4 -4
  48. package/dist/components/wizard/run-wizard-session.js +15 -15
  49. package/dist/components/wizard/skill-agent-summary.js +3 -3
  50. package/dist/components/wizard/stack-selection.js +3 -3
  51. package/dist/components/wizard/step-agents.js +4 -4
  52. package/dist/components/wizard/step-build.js +3 -3
  53. package/dist/components/wizard/step-confirm.js +6 -6
  54. package/dist/components/wizard/step-sources.js +3 -3
  55. package/dist/components/wizard/step-stack.js +4 -4
  56. package/dist/components/wizard/summary-panel.js +5 -5
  57. package/dist/components/wizard/wizard-layout.js +6 -6
  58. package/dist/components/wizard/wizard.js +14 -14
  59. package/dist/hooks/init.js +24 -24
  60. package/dist/{load-agent-defs-TWDFYM5S.js → load-agent-defs-57YKPVQW.js} +4 -4
  61. package/dist/{recompile-project-agents-YIGIVMCQ.js → recompile-project-agents-IQD3HRRS.js} +6 -6
  62. package/dist/{source-loader-7T754EZC.js → source-loader-KAWB76OH.js} +2 -2
  63. package/dist/stores/wizard-store.js +2 -2
  64. package/package.json +2 -2
  65. package/dist/chunk-7WKNPDN2.js.map +0 -1
  66. package/dist/chunk-IAN7QDBR.js.map +0 -1
  67. /package/dist/{chunk-C2K7Q7HO.js.map → chunk-2IMHZEIK.js.map} +0 -0
  68. /package/dist/{chunk-RLSZWDTB.js.map → chunk-33ZKIYJM.js.map} +0 -0
  69. /package/dist/{chunk-Q4KCUR2T.js.map → chunk-4KVJJJG2.js.map} +0 -0
  70. /package/dist/{chunk-DOQUAWL4.js.map → chunk-53XPSAJ4.js.map} +0 -0
  71. /package/dist/{chunk-ZYXQUU2P.js.map → chunk-57YWAMNT.js.map} +0 -0
  72. /package/dist/{chunk-SBL4AYSB.js.map → chunk-5J6DO4VW.js.map} +0 -0
  73. /package/dist/{chunk-Z56AUTER.js.map → chunk-AQSMNUKJ.js.map} +0 -0
  74. /package/dist/{chunk-ZPFM2VHZ.js.map → chunk-BR3TNSO3.js.map} +0 -0
  75. /package/dist/{chunk-U5O4BWKI.js.map → chunk-C53RJ7AA.js.map} +0 -0
  76. /package/dist/{chunk-QTLYSVYE.js.map → chunk-ES7WGE32.js.map} +0 -0
  77. /package/dist/{chunk-F5JTW3HZ.js.map → chunk-H2MDPOLG.js.map} +0 -0
  78. /package/dist/{chunk-YC4CLTQE.js.map → chunk-HRVPZMKK.js.map} +0 -0
  79. /package/dist/{chunk-SXYFATQN.js.map → chunk-IMTWWA3N.js.map} +0 -0
  80. /package/dist/{chunk-UE3MLDL2.js.map → chunk-K26SBQLO.js.map} +0 -0
  81. /package/dist/{chunk-GUFRQJAE.js.map → chunk-L65XGQKR.js.map} +0 -0
  82. /package/dist/{chunk-SWYV7QXX.js.map → chunk-RMMEYCEA.js.map} +0 -0
  83. /package/dist/{chunk-WDN4HAN7.js.map → chunk-S6GX7IJ2.js.map} +0 -0
  84. /package/dist/{chunk-ESI3JYOQ.js.map → chunk-T2RPUNYP.js.map} +0 -0
  85. /package/dist/{chunk-QG4NRKZD.js.map → chunk-VEBEGZUV.js.map} +0 -0
  86. /package/dist/{chunk-TPRRWOHZ.js.map → chunk-VEONZFTL.js.map} +0 -0
  87. /package/dist/{chunk-ADSIYZGS.js.map → chunk-WIJM6BX6.js.map} +0 -0
  88. /package/dist/{chunk-2P3MKX5Q.js.map → chunk-XSM3IBMG.js.map} +0 -0
  89. /package/dist/{load-agent-defs-TWDFYM5S.js.map → load-agent-defs-57YKPVQW.js.map} +0 -0
  90. /package/dist/{recompile-project-agents-YIGIVMCQ.js.map → recompile-project-agents-IQD3HRRS.js.map} +0 -0
  91. /package/dist/{source-loader-7T754EZC.js.map → source-loader-KAWB76OH.js.map} +0 -0
@@ -8,10 +8,11 @@ import {
8
8
  readLocalSkillMetadata,
9
9
  readSkillMetadata,
10
10
  resolveInstallPaths,
11
+ truncateText,
11
12
  validateSkillPath,
12
13
  writeFile,
13
14
  writeMetadataYaml
14
- } from "./chunk-7WKNPDN2.js";
15
+ } from "./chunk-UNASGGW2.js";
15
16
  import {
16
17
  getCategoryDomain,
17
18
  typedEntries
@@ -551,6 +552,9 @@ var store = createApiClient({
551
552
  headers: { accept: "application/json", "user-agent": SEED_USER_AGENT },
552
553
  credentials: "omit"
553
554
  });
555
+ var EXPLANATION_BUDGET = 120;
556
+ var QUOTABLE_TYPE = "text/plain";
557
+ var NO_SUCH_CONFIG = 404;
554
558
  async function fetchSeedConfig(id) {
555
559
  let response;
556
560
  try {
@@ -558,11 +562,11 @@ async function fetchSeedConfig(id) {
558
562
  } catch {
559
563
  return { ok: false, error: `Could not reach ${SEED_API_URL} \u2014 check your connection.` };
560
564
  }
561
- if (response.status === 404) {
565
+ if (response.status === NO_SUCH_CONFIG) {
562
566
  return { ok: false, error: `No configuration found for id '${id}'.` };
563
567
  }
564
568
  if (!response.ok) {
565
- return { ok: false, error: `Fetching configuration failed (HTTP ${response.status}).` };
569
+ return { ok: false, error: await refusalMessage(response) };
566
570
  }
567
571
  let body;
568
572
  try {
@@ -579,6 +583,28 @@ async function fetchSeedConfig(id) {
579
583
  }
580
584
  return { ok: true, payload: parsed.data };
581
585
  }
586
+ async function refusalMessage(response) {
587
+ const refused = `Fetching configuration failed (HTTP ${response.status}).`;
588
+ const explanation = explanationOf(await refusalBody(response));
589
+ return explanation === void 0 ? refused : `${refused} The store said: ${explanation}`;
590
+ }
591
+ async function refusalBody(response) {
592
+ if (!arrivedAsText(response)) return void 0;
593
+ try {
594
+ return await response.text();
595
+ } catch {
596
+ return void 0;
597
+ }
598
+ }
599
+ function arrivedAsText(response) {
600
+ return (response.headers.get("content-type") ?? "").startsWith(QUOTABLE_TYPE);
601
+ }
602
+ function explanationOf(body) {
603
+ if (body === void 0) return void 0;
604
+ const said = body.trim();
605
+ if (said === "") return void 0;
606
+ return truncateText(said, EXPLANATION_BUDGET);
607
+ }
582
608
 
583
609
  export {
584
610
  registerExternalSkills,
@@ -588,4 +614,4 @@ export {
588
614
  SEED_USER_AGENT,
589
615
  fetchSeedConfig
590
616
  };
591
- //# sourceMappingURL=chunk-JEJJHRZA.js.map
617
+ //# sourceMappingURL=chunk-J2YG7P7L.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/cli/lib/seed/external-skills.ts","../../../node_modules/hono/dist/utils/cookie.js","../../../node_modules/hono/dist/client/utils.js","../../../node_modules/hono/dist/client/client.js","../../api/src/client.ts","../src/cli/lib/seed/fetch-seed.ts"],"sourcesContent":["import path from \"path\";\n\nimport { seedExternalSkillSchema } from \"@workspace/matrix/seed\";\n\nimport { GITHUB_SOURCE, LOCAL_SKILLS_PATH, STANDARD_FILES } from \"../../consts.js\";\nimport { resolveInstallPaths } from \"../installation/install-base-dir.js\";\nimport { readSkillMetadata } from \"../loading/loader.js\";\nimport { getCategoryDomain } from \"../matrix/matrix-provider.js\";\nimport { claimSlug } from \"../matrix/skill-resolution.js\";\nimport { validateSkillPath } from \"../skills/skill-copier.js\";\nimport {\n injectForkedFromMetadata,\n readLocalSkillMetadata,\n writeMetadataYaml,\n} from \"../skills/skill-metadata.js\";\nimport { computeFileHash } from \"../versioning.js\";\nimport { glob, readFile, writeFile } from \"../../utils/fs.js\";\nimport { typedEntries } from \"../../utils/typed-object.js\";\n\nimport type { SkillScope } from \"../../types/config.js\";\nimport type { Domain, MergedSkillsMatrix, ResolvedSkill } from \"../../types/matrix.js\";\nimport type { CategoryPath, SkillId, SkillSlug } from \"../../types/skills.js\";\nimport type { ForkedFromMetadata, LocalSkillMetadata } from \"../skills/skill-metadata.js\";\nimport type {\n SeedExternalSkill,\n SeedPayload,\n SeedSkill,\n SeedSkillTree,\n} from \"@workspace/matrix/seed\";\n\n/**\n * One skill the payload carries rather than names, seated and ready to be written.\n *\n * `skillDir` and `domain` are resolved once, where the checks that make them safe already are:\n * the directory has to be the one the catalogue entry points at, because that is what tells the\n * copy step this skill is already where it belongs, and the domain is what the category guard has\n * just proved this catalogue declares.\n */\nexport type ExternalSkillInstall = {\n id: SkillId;\n scope: SkillScope;\n skill: SeedExternalSkill;\n domain: Domain;\n skillDir: string;\n};\n\n/** One external entry with the skill row that selected it — what the payload says about it. */\ntype SelectedExternal = { id: SkillId; skill: SeedExternalSkill; entry: SeedSkill };\n\n/**\n * Names every carried skill a payload asks to install as a plugin, not just the first.\n *\n * There is nothing to install them FROM: the payload is the source, so no marketplace serves\n * these ids. Coercing them to eject would install something the payload did not ask for, under a\n * mode the sharer's own screen still shows as plugin — so the ids are named and the run stops.\n */\nfunction pluginInstallError(ids: SkillId[]): string {\n return [\n \"This configuration cannot be installed: these skills travel inside it, so no marketplace \" +\n \"serves them and they cannot be installed as plugins:\",\n ...ids.map((id) => ` ${id}`),\n \"Re-share it with each skill above set to eject, which is the only way a carried skill installs.\",\n ].join(\"\\n\");\n}\n\n/**\n * The external entries this selection actually names.\n *\n * Presence in `skills` is selection and `external` is only where the bytes are, so content nobody\n * picked installs nothing — seating it would put a skill in the catalogue no part of the\n * configuration asked for.\n */\nfunction selectedExternals(payload: SeedPayload): SelectedExternal[] {\n return typedEntries<SkillId, SeedExternalSkill>(payload.external ?? {}).flatMap(([id, skill]) => {\n const entry = payload.skills[id];\n return entry ? [{ id, skill, entry }] : [];\n });\n}\n\nfunction askedForAsPlugin(externals: SelectedExternal[]): SkillId[] {\n return externals.filter(({ entry }) => entry.install !== \"eject\").map(({ id }) => id);\n}\n\n/**\n * Boundary cast: a carried skill's id is minted at intake and belongs to no generated union,\n * which is exactly what makes it external. It is its own slug too — a skill outside every\n * catalogue has no shorter name anything else would recognise, and the sending side says the same.\n */\nfunction externalSlug(id: SkillId): SkillSlug {\n return id as SkillSlug;\n}\n\n/** The repository's owner as a handle, which is all the authorship a carried skill records. */\nfunction skillAuthor(repo: string): string {\n const [owner = repo] = repo.split(\"/\");\n return `@${owner}`;\n}\n\n/** The repository ref a carried skill's provenance records, in the form every other ref wears. */\nfunction repoRef(repo: string): string {\n return `${GITHUB_SOURCE.GITHUB_PREFIX}${repo}`;\n}\n\n/** Where a carried skill's directory goes: under the skills directory of the scope it names. */\nfunction externalSkillDir(id: SkillId, scope: SkillScope, projectDir: string): string {\n return path.join(resolveInstallPaths(projectDir, scope).skillsDir, id);\n}\n\n/**\n * The catalogue entry a carried skill becomes on arrival.\n *\n * It declares no relationships and is named by none, which is honest: nothing outside the\n * catalogue can say what this skill conflicts with, and nothing inside it knows the skill exists.\n */\nfunction externalCatalogueEntry(install: ExternalSkillInstall): ResolvedSkill {\n return {\n id: install.id,\n slug: externalSlug(install.id),\n displayName: install.skill.displayName,\n description: install.skill.description,\n // Boundary cast held by the guard above it: the category was confirmed against the catalogue\n // the payload names, and this one has just been shown to declare it too.\n category: install.skill.categoryId as CategoryPath,\n author: skillAuthor(install.skill.repo),\n path: `${LOCAL_SKILLS_PATH}/${install.id}/`,\n conflictsWith: [],\n requires: [],\n alternatives: [],\n discourages: [],\n // Local and custom for the same reason: the bytes are on this machine and nowhere else, and\n // the skill was authored outside the vocabulary this catalogue ships.\n local: true,\n custom: true,\n localPath: install.skillDir,\n };\n}\n\n/**\n * Seats one carried skill in the catalogue, or leaves it unseated.\n *\n * A skill is PLACED in the taxonomy and never extends it, so a category this catalogue has no\n * definition for belongs to no domain, renders in no tab and reaches no sub-agent. Left unseated,\n * the id falls to the decode's own skip, which reports it by name.\n */\nfunction seatExternalSkill(\n { id, skill, entry }: SelectedExternal,\n matrix: MergedSkillsMatrix,\n projectDir: string,\n): ExternalSkillInstall[] {\n const domain = getCategoryDomain(skill.categoryId);\n if (domain === undefined) return [];\n\n const install: ExternalSkillInstall = {\n id,\n skill,\n domain,\n scope: entry.scope,\n skillDir: externalSkillDir(id, entry.scope, projectDir),\n };\n\n matrix.skills[id] = externalCatalogueEntry(install);\n // Completes the map over the matrix: an entry the slug map does not carry is invisible to\n // anything that addresses a skill by slug.\n claimSlug(matrix.slugMap, externalSlug(id), id);\n\n return [install];\n}\n\n/**\n * Seats the catalogue entries a payload carries, and says where each one's bytes go.\n *\n * Every other id in a payload is resolved against a catalogue the receiver already has, so one it\n * does not know is skipped and reported. A carried skill answers to no catalogue at all — which is\n * why its whole directory travels — so it has to BECOME a catalogue entry before the decode reads\n * the selection, or it is skipped like any other unknown id and the configuration installs\n * quietly smaller than it was shared.\n *\n * The entries are written into `matrix` in place, exactly as the local-skill merge writes the\n * skills it discovers on disk. That matrix is the object `initializeMatrix` seated, which is what\n * {@link getCategoryDomain} reads — every caller passes the loaded source's own.\n *\n * @throws {Error} If any carried skill asks to be installed as a plugin — see\n * {@link pluginInstallError}.\n */\nexport function registerExternalSkills(\n payload: SeedPayload,\n matrix: MergedSkillsMatrix,\n projectDir: string,\n): ExternalSkillInstall[] {\n const externals = selectedExternals(payload);\n const asPlugin = askedForAsPlugin(externals);\n\n if (asPlugin.length > 0) throw new Error(pluginInstallError(asPlugin));\n\n return externals.flatMap((external) => seatExternalSkill(external, matrix, projectDir));\n}\n\n/**\n * Writes each carried skill's directory where its own entry said, and registers it on disk.\n *\n * Three things have to be true afterwards, and each is a separate act: the files are there, the\n * skill answers to the id the configuration recorded, and a metadata.yaml describes it well\n * enough for the next `edit`, `compile` or `list` to find it again. The seat above lasts one run;\n * this is what makes the install survive.\n */\nexport async function writeExternalSkills(installs: ExternalSkillInstall[]): Promise<void> {\n for (const install of installs) {\n await writeSkillTree(install);\n await writeSkillManifest(install);\n await registerSkillOnDisk(install);\n }\n}\n\n/**\n * The directory as the payload holds it, keys and all.\n *\n * The keys came off the wire, so a payload may ask for any path it likes — including one that\n * climbs out of the skill's own directory. They answer to the same guard a marketplace's own\n * paths do.\n */\nasync function writeSkillTree(install: ExternalSkillInstall): Promise<void> {\n for (const [relativePath, contents] of Object.entries(install.skill.files)) {\n const filePath = path.join(install.skillDir, relativePath);\n validateSkillPath(filePath, install.skillDir, relativePath);\n await writeFile(filePath, contents);\n }\n}\n\n/** The frontmatter block at the head of a SKILL.md, and the `name` field inside it. */\nconst FRONTMATTER_BLOCK = /^---\\r?\\n([\\s\\S]*?)\\r?\\n---/;\nconst NAME_FIELD = /^name:.*$/m;\n\n/**\n * Names the skill by the id it installs under.\n *\n * Every loader reads a skill's id off this one field, and a compiled sub-agent references the id\n * the configuration recorded — the one minted at intake, which is also this directory's name. The\n * repository the skill came from knew about neither, so left as it was written the two never\n * meet: the sub-agent names a skill Claude Code knows as something else, and the next load\n * registers an id no configuration carries.\n *\n * Only the name. Everything else in the block is the author's and travels untouched.\n */\nfunction withInstalledName(manifest: string, install: ExternalSkillInstall): string {\n const named = `name: ${install.id}`;\n const block = FRONTMATTER_BLOCK.exec(manifest);\n\n // A manifest with no frontmatter at all describes no skill to Claude Code either. The payload\n // carries both fields one needs, so it is given one rather than installed unreadable.\n if (!block) {\n return `---\\n${named}\\ndescription: ${install.skill.description}\\n---\\n\\n${manifest}`;\n }\n\n const [whole, fields = \"\"] = block;\n const renamed = NAME_FIELD.test(fields)\n ? fields.replace(NAME_FIELD, named)\n : `${named}\\n${fields}`;\n\n // The block is anchored at the head of the file, so what follows it is the rest verbatim.\n return `---\\n${renamed}\\n---${manifest.slice(whole.length)}`;\n}\n\nasync function writeSkillManifest(install: ExternalSkillInstall): Promise<void> {\n const manifestPath = path.join(install.skillDir, STANDARD_FILES.SKILL_MD);\n await writeFile(manifestPath, withInstalledName(await readFile(manifestPath), install));\n}\n\n/**\n * What the payload confirmed, over what the repository shipped, over what neither says.\n *\n * The order is the whole of it. The taxonomy is the placement the user chose against the\n * catalogue this configuration names, and the repository's own idea of where its skill belongs\n * answers to a taxonomy nobody here shares — so that is written over the file. Everything else\n * the repository wrote is kept, because its author knows more about their skill than a default\n * does.\n *\n * The defaults underneath exist because `doctor` validates every installed metadata.yaml, and a\n * file this command writes that that command reports as an error is the CLI disagreeing with\n * itself — in a file the user cannot fix, since the skill is somebody else's repository. Neither\n * is invented: the authorship is the repository's owner, and the usage line is the same words the\n * stack loader already writes for every skill reference it has nothing more specific for.\n */\nfunction externalSkillMetadata(\n install: ExternalSkillInstall,\n shipped: LocalSkillMetadata | null,\n): LocalSkillMetadata {\n return {\n author: skillAuthor(install.skill.repo),\n usageGuidance: `Use when working with ${install.skill.displayName}`,\n ...shipped,\n displayName: install.skill.displayName,\n slug: install.id,\n category: install.skill.categoryId,\n domain: install.domain,\n cliDescription: install.skill.description,\n custom: true,\n };\n}\n\n/**\n * Registers the skill on disk, where the next command will look for it.\n *\n * `forkedFrom` is stamped through the one writer that stamps it, because it is the package's\n * single answer to \"did the CLI put this directory here?\" — `uninstall` reads it to decide what it\n * may delete, and the producer reads it to decide what the round trip owns. A carried skill IS the\n * round trip's; a skill the user wrote by hand is not, and carries nothing.\n *\n * The repository AND the directory inside it, because this is the only record of where these bytes\n * came from. Sharing this installation has to rebuild the entry the payload carried, and a\n * repository alone cannot say which of its directories travelled — see {@link readCarriedSkills}.\n */\nasync function registerSkillOnDisk(install: ExternalSkillInstall): Promise<void> {\n const metadataPath = path.join(install.skillDir, STANDARD_FILES.METADATA_YAML);\n const shipped = await readLocalSkillMetadata(install.skillDir);\n\n await writeMetadataYaml(metadataPath, externalSkillMetadata(install, shipped));\n await injectForkedFromMetadata(\n install.skillDir,\n install.id,\n await computeFileHash(path.join(install.skillDir, STANDARD_FILES.SKILL_MD)),\n { source: repoRef(install.skill.repo), path: install.skill.path },\n );\n}\n\n/**\n * One installed skill directory the round trip owns, and what its install recorded about it.\n *\n * Read by the caller rather than here: whether a directory is the CLI's copy or the user's own\n * work is a question about ownership, which `installation-payload.ts` already asks of every entry\n * before anything else. This module answers only the next one — whether the bytes have to travel.\n */\nexport type OwnedSkillDir = { id: string; skillDir: string; provenance: ForkedFromMetadata };\n\n/**\n * What one installation carries rather than names.\n *\n * The same shape the mapper's other readings wear: what can travel, and the lines naming what\n * cannot. Both halves matter — a payload quietly missing content it could not read still mints an\n * id, and that id installs a configuration nobody chose.\n */\nexport type ContentReading = {\n /** Skill id -> the entry a receiver seats it from. Keyed exactly as the wire keys `external`. */\n external: Record<string, SeedExternalSkill>;\n /** One line per skill whose content this installation cannot carry, for the refusal. */\n uncarryable: string[];\n};\n\n/** What one owned directory turns out to carry: content, a reason it cannot, or nothing. */\ntype CarriedRead =\n | { carries: \"content\"; id: string; skill: SeedExternalSkill }\n | { carries: \"nothing\" }\n | { carries: \"unshareable\"; line: string };\n\nfunction unnameableRepoMessage(id: string): string {\n return `${id} travels inside a shared configuration, and this installation does not record the repository its content came from`;\n}\n\nfunction uncarryableContentMessage(id: string, reason: string): string {\n return `${id} travels inside a shared configuration, and ${reason}`;\n}\n\n/**\n * The inverse of {@link repoRef}: the repository a ref names, or nothing when it names none this\n * contract can state.\n *\n * Provenance is written as a ref because that is the form every other source in a configuration\n * wears; the wire wants GitHub's own `owner/name`. Any other ref is a repository this contract has\n * no word for, which is a refusal rather than something to guess at.\n */\nfunction repoFromRef(ref: string | undefined): string | undefined {\n if (ref === undefined || !ref.startsWith(GITHUB_SOURCE.GITHUB_PREFIX)) return undefined;\n return ref.slice(GITHUB_SOURCE.GITHUB_PREFIX.length);\n}\n\n/** Every file under the skill, keyed by its path relative to it — dotfiles and nesting included. */\nasync function readSkillTree(skillDir: string): Promise<SeedSkillTree> {\n const files = await glob(\"**/*\", skillDir, { dot: true });\n const entries = await Promise.all(\n files.map(async (file) => [file, await readFile(path.join(skillDir, file))] as const),\n );\n return Object.fromEntries(entries);\n}\n\n/**\n * Rebuilds the entry a carried skill arrived as, from the directory the install wrote.\n *\n * The bytes as they stand rather than as they arrived: the manifest was renamed to the id this\n * install recorded, and a user may have edited the skill since. Both are what is installed here,\n * and what is installed here is what a share carries.\n *\n * Validated against the contract's own schema rather than by rules restated here — that is what\n * makes the weight limit, the manifest requirement and every field's shape one definition rather\n * than two that can drift.\n */\nasync function readCarriedSkill({ id, skillDir, provenance }: OwnedSkillDir): Promise<CarriedRead> {\n // The directory is what marks a skill as one no catalogue can resolve. Without it there is a\n // catalogue behind this id, so the receiver installs it from there and the bytes stay home.\n if (provenance.path === undefined) return { carries: \"nothing\" };\n\n const repo = repoFromRef(provenance.source);\n if (repo === undefined) return { carries: \"unshareable\", line: unnameableRepoMessage(id) };\n\n const described = await readSkillMetadata(path.join(skillDir, STANDARD_FILES.METADATA_YAML));\n if (!described.usable) {\n return { carries: \"unshareable\", line: uncarryableContentMessage(id, described.reason) };\n }\n\n const parsed = seedExternalSkillSchema.safeParse({\n displayName: described.metadata.displayName,\n description: described.metadata.cliDescription,\n categoryId: described.metadata.category,\n repo,\n path: provenance.path,\n files: await readSkillTree(skillDir),\n });\n if (!parsed.success) {\n const reason = parsed.error.issues.map((issue) => issue.message).join(\"; \");\n return { carries: \"unshareable\", line: uncarryableContentMessage(id, reason) };\n }\n\n return { carries: \"content\", id, skill: parsed.data };\n}\n\n/**\n * What a set of owned skill directories carries, and what it cannot.\n *\n * The mirror of {@link registerExternalSkills}: that seats an entry a payload carried and writes\n * its bytes, this reads those bytes back into the entry a payload carries. Every other id in a\n * configuration is resolved by the receiver against a catalogue it already has, so its content has\n * nothing to add; an added skill answers to no catalogue in either direction, and a share that\n * named it without carrying it would mint an id that installs a configuration missing it.\n */\nexport async function readCarriedSkills(owned: OwnedSkillDir[]): Promise<ContentReading> {\n const reads = await Promise.all(owned.map(readCarriedSkill));\n\n return {\n external: Object.fromEntries(\n reads.flatMap((read) => (read.carries === \"content\" ? [[read.id, read.skill]] : [])),\n ),\n uncarryable: reads.flatMap((read) => (read.carries === \"unshareable\" ? [read.line] : [])),\n };\n}\n","// src/utils/cookie.ts\nimport { decodeURIComponent_, tryDecode } from \"./url.js\";\nvar algorithm = { name: \"HMAC\", hash: \"SHA-256\" };\nvar getCryptoKey = async (secret) => {\n const secretBuf = typeof secret === \"string\" ? new TextEncoder().encode(secret) : secret;\n return await crypto.subtle.importKey(\"raw\", secretBuf, algorithm, false, [\"sign\", \"verify\"]);\n};\nvar makeSignature = async (value, secret) => {\n const key = await getCryptoKey(secret);\n const signature = await crypto.subtle.sign(algorithm.name, key, new TextEncoder().encode(value));\n return btoa(String.fromCharCode(...new Uint8Array(signature)));\n};\nvar verifySignature = async (base64Signature, value, secret) => {\n try {\n const signatureBinStr = atob(base64Signature);\n const signature = new Uint8Array(signatureBinStr.length);\n for (let i = 0, len = signatureBinStr.length; i < len; i++) {\n signature[i] = signatureBinStr.charCodeAt(i);\n }\n return await crypto.subtle.verify(algorithm, secret, signature, new TextEncoder().encode(value));\n } catch {\n return false;\n }\n};\nvar validCookieNameRegEx = /^[\\w!#$%&'*.^`|~+-]+$/;\nvar relaxedCookieNameRegEx = /^[!#-:<>-[\\]-~]+$/;\nvar validCookieValueRegEx = /^[ !#-:<-[\\]-~]*$/;\nvar trimCookieWhitespace = (value) => {\n let start = 0;\n let end = value.length;\n while (start < end) {\n const charCode = value.charCodeAt(start);\n if (charCode !== 32 && charCode !== 9) {\n break;\n }\n start++;\n }\n while (end > start) {\n const charCode = value.charCodeAt(end - 1);\n if (charCode !== 32 && charCode !== 9) {\n break;\n }\n end--;\n }\n return start === 0 && end === value.length ? value : value.slice(start, end);\n};\nvar parse = (cookie, name) => {\n if (name && cookie.indexOf(name) === -1) {\n return {};\n }\n const pairs = cookie.split(\";\");\n const parsedCookie = /* @__PURE__ */ Object.create(null);\n for (const pairStr of pairs) {\n const valueStartPos = pairStr.indexOf(\"=\");\n if (valueStartPos === -1) {\n continue;\n }\n const cookieName = trimCookieWhitespace(pairStr.substring(0, valueStartPos));\n if (name && name !== cookieName || !relaxedCookieNameRegEx.test(cookieName) || cookieName in parsedCookie) {\n continue;\n }\n let cookieValue = trimCookieWhitespace(pairStr.substring(valueStartPos + 1));\n if (cookieValue.startsWith('\"') && cookieValue.endsWith('\"')) {\n cookieValue = cookieValue.slice(1, -1);\n }\n if (validCookieValueRegEx.test(cookieValue)) {\n parsedCookie[cookieName] = cookieValue.indexOf(\"%\") !== -1 ? tryDecode(cookieValue, decodeURIComponent_) : cookieValue;\n if (name) {\n break;\n }\n }\n }\n return parsedCookie;\n};\nvar parseSigned = async (cookie, secret, name) => {\n const parsedCookie = /* @__PURE__ */ Object.create(null);\n const secretKey = await getCryptoKey(secret);\n for (const [key, value] of Object.entries(parse(cookie, name))) {\n const signatureStartPos = value.lastIndexOf(\".\");\n if (signatureStartPos < 1) {\n continue;\n }\n const signedValue = value.substring(0, signatureStartPos);\n const signature = value.substring(signatureStartPos + 1);\n if (signature.length !== 44 || !signature.endsWith(\"=\")) {\n continue;\n }\n const isVerified = await verifySignature(signature, signedValue, secretKey);\n parsedCookie[key] = isVerified ? signedValue : false;\n }\n return parsedCookie;\n};\nvar _serialize = (name, value, opt = {}) => {\n if (!validCookieNameRegEx.test(name)) {\n throw new Error(\"Invalid cookie name\");\n }\n let cookie = `${name}=${value}`;\n if (name.startsWith(\"__Secure-\") && !opt.secure) {\n throw new Error(\"__Secure- Cookie must have Secure attributes\");\n }\n if (name.startsWith(\"__Host-\")) {\n if (!opt.secure) {\n throw new Error(\"__Host- Cookie must have Secure attributes\");\n }\n if (opt.path !== \"/\") {\n throw new Error('__Host- Cookie must have Path attributes with \"/\"');\n }\n if (opt.domain) {\n throw new Error(\"__Host- Cookie must not have Domain attributes\");\n }\n }\n for (const key of [\"domain\", \"path\", \"sameSite\", \"priority\"]) {\n if (opt[key] && /[;\\r\\n]/.test(opt[key])) {\n throw new Error(`${key} must not contain \";\", \"\\\\r\", or \"\\\\n\"`);\n }\n }\n if (opt && typeof opt.maxAge === \"number\" && opt.maxAge >= 0) {\n if (opt.maxAge > 3456e4) {\n throw new Error(\n \"Cookies Max-Age SHOULD NOT be greater than 400 days (34560000 seconds) in duration.\"\n );\n }\n cookie += `; Max-Age=${opt.maxAge | 0}`;\n }\n if (opt.domain && opt.prefix !== \"host\") {\n cookie += `; Domain=${opt.domain}`;\n }\n if (opt.path) {\n cookie += `; Path=${opt.path}`;\n }\n if (opt.expires) {\n if (opt.expires.getTime() - Date.now() > 3456e7) {\n throw new Error(\n \"Cookies Expires SHOULD NOT be greater than 400 days (34560000 seconds) in the future.\"\n );\n }\n cookie += `; Expires=${opt.expires.toUTCString()}`;\n }\n if (opt.httpOnly) {\n cookie += \"; HttpOnly\";\n }\n if (opt.secure) {\n cookie += \"; Secure\";\n }\n if (opt.sameSite) {\n cookie += `; SameSite=${opt.sameSite.charAt(0).toUpperCase() + opt.sameSite.slice(1)}`;\n }\n if (opt.priority) {\n cookie += `; Priority=${opt.priority.charAt(0).toUpperCase() + opt.priority.slice(1)}`;\n }\n if (opt.partitioned) {\n if (!opt.secure) {\n throw new Error(\"Partitioned Cookie must have Secure attributes\");\n }\n cookie += \"; Partitioned\";\n }\n return cookie;\n};\nvar serialize = (name, value, opt) => {\n value = encodeURIComponent(value);\n return _serialize(name, value, opt);\n};\nvar serializeSigned = async (name, value, secret, opt = {}) => {\n const signature = await makeSignature(value, secret);\n value = `${value}.${signature}`;\n value = encodeURIComponent(value);\n return _serialize(name, value, opt);\n};\nexport {\n parse,\n parseSigned,\n serialize,\n serializeSigned\n};\n","// src/client/utils.ts\nimport { fetchRP, DetailedError } from \"./fetch-result-please.js\";\nvar mergePath = (base, path) => {\n base = base.replace(/\\/+$/, \"\");\n base = base + \"/\";\n path = path.replace(/^\\/+/, \"\");\n return base + path;\n};\nvar replaceUrlParam = (urlString, params) => {\n for (const [k, v] of Object.entries(params)) {\n const reg = new RegExp(\"/:\" + k + \"(?:{[^/]+})?\\\\??(?=/|$)\");\n urlString = urlString.replace(reg, v ? `/${v}` : \"\");\n }\n return urlString;\n};\nvar buildSearchParams = (query) => {\n const searchParams = new URLSearchParams();\n for (const [k, v] of Object.entries(query)) {\n if (v === void 0) {\n continue;\n }\n if (Array.isArray(v)) {\n for (const v2 of v) {\n searchParams.append(k, v2);\n }\n } else {\n searchParams.set(k, v);\n }\n }\n return searchParams;\n};\nvar replaceUrlProtocol = (urlString, protocol) => {\n switch (protocol) {\n case \"ws\":\n return urlString.replace(/^http/, \"ws\");\n case \"http\":\n return urlString.replace(/^ws/, \"http\");\n }\n};\nvar removeIndexString = (urlString) => {\n if (/^https?:\\/\\/[^\\/]+?\\/index(?=\\?|$)/.test(urlString)) {\n return urlString.replace(/\\/index(?=\\?|$)/, \"/\");\n }\n return urlString.replace(/\\/index(?=\\?|$)/, \"\");\n};\nfunction isObject(item) {\n return typeof item === \"object\" && item !== null && !Array.isArray(item);\n}\nfunction deepMerge(target, source) {\n if (!isObject(target) && !isObject(source)) {\n return source;\n }\n const merged = { ...target };\n for (const key in source) {\n const value = source[key];\n if (isObject(merged[key]) && isObject(value)) {\n merged[key] = deepMerge(merged[key], value);\n } else {\n merged[key] = value;\n }\n }\n return merged;\n}\nasync function parseResponse(fetchRes) {\n return fetchRP(fetchRes);\n}\nexport {\n DetailedError,\n buildSearchParams,\n deepMerge,\n mergePath,\n parseResponse,\n removeIndexString,\n replaceUrlParam,\n replaceUrlProtocol\n};\n","// src/client/client.ts\nimport { serialize } from \"../utils/cookie.js\";\nimport {\n buildSearchParams,\n deepMerge,\n mergePath,\n removeIndexString,\n replaceUrlParam,\n replaceUrlProtocol\n} from \"./utils.js\";\nvar createProxy = (callback, path) => {\n const proxy = new Proxy(() => {\n }, {\n get(_obj, key) {\n if (typeof key !== \"string\" || key === \"then\") {\n return void 0;\n }\n return createProxy(callback, [...path, key]);\n },\n apply(_1, _2, args) {\n return callback({\n path,\n args\n });\n }\n });\n return proxy;\n};\nvar ClientRequestImpl = class {\n url;\n method;\n buildSearchParams;\n queryParams = void 0;\n pathParams = {};\n rBody;\n cType = void 0;\n constructor(url, method, options) {\n this.url = url;\n this.method = method;\n this.buildSearchParams = options.buildSearchParams;\n }\n fetch = async (args, opt) => {\n if (args) {\n if (args.query) {\n this.queryParams = this.buildSearchParams(args.query);\n }\n if (args.form) {\n const form = new FormData();\n for (const [k, v] of Object.entries(args.form)) {\n if (v === void 0) {\n continue;\n }\n if (Array.isArray(v)) {\n for (const v2 of v) {\n form.append(k, v2);\n }\n } else {\n form.append(k, v);\n }\n }\n this.rBody = form;\n }\n if (args.json) {\n this.rBody = JSON.stringify(args.json);\n this.cType = \"application/json\";\n }\n if (args.param) {\n this.pathParams = args.param;\n }\n }\n let methodUpperCase = this.method.toUpperCase();\n const headerValues = {\n ...args?.header,\n ...typeof opt?.headers === \"function\" ? await opt.headers() : opt?.headers\n };\n if (args?.cookie) {\n const cookies = [];\n for (const [key, value] of Object.entries(args.cookie)) {\n cookies.push(serialize(key, value, { path: \"/\" }));\n }\n headerValues[\"Cookie\"] = cookies.join(\",\");\n }\n if (this.cType) {\n headerValues[\"Content-Type\"] = this.cType;\n }\n const headers = new Headers(headerValues ?? void 0);\n let url = this.url;\n url = removeIndexString(url);\n url = replaceUrlParam(url, this.pathParams);\n if (this.queryParams) {\n url = url + \"?\" + this.queryParams.toString();\n }\n methodUpperCase = this.method.toUpperCase();\n const setBody = !(methodUpperCase === \"GET\" || methodUpperCase === \"HEAD\");\n return (opt?.fetch || fetch)(url, {\n body: setBody ? this.rBody : void 0,\n method: methodUpperCase,\n headers,\n ...opt?.init\n });\n };\n};\nvar hc = (baseUrl, options) => createProxy(function proxyCallback(opts) {\n const buildSearchParamsOption = options?.buildSearchParams ?? buildSearchParams;\n const parts = [...opts.path];\n const lastParts = parts.slice(-3).reverse();\n if (lastParts[0] === \"toString\") {\n if (lastParts[1] === \"name\") {\n return lastParts[2] || \"\";\n }\n return proxyCallback.toString();\n }\n if (lastParts[0] === \"valueOf\") {\n if (lastParts[1] === \"name\") {\n return lastParts[2] || \"\";\n }\n return proxyCallback;\n }\n let method = \"\";\n if (/^\\$/.test(lastParts[0])) {\n const last = parts.pop();\n if (last) {\n method = last.replace(/^\\$/, \"\");\n }\n }\n const path = parts.join(\"/\");\n const url = mergePath(baseUrl, path);\n if (method === \"url\" || method === \"path\") {\n let result = url;\n if (opts.args[0]) {\n if (opts.args[0].param) {\n result = replaceUrlParam(url, opts.args[0].param);\n }\n if (opts.args[0].query) {\n result = result + \"?\" + buildSearchParamsOption(opts.args[0].query).toString();\n }\n }\n result = removeIndexString(result);\n if (method === \"url\") {\n return new URL(result);\n }\n return result.slice(baseUrl.replace(/\\/+$/, \"\").length).replace(/^\\/?/, \"/\");\n }\n if (method === \"ws\") {\n const webSocketUrl = replaceUrlProtocol(\n opts.args[0] && opts.args[0].param ? replaceUrlParam(url, opts.args[0].param) : url,\n \"ws\"\n );\n const targetUrl = new URL(webSocketUrl);\n const queryParams = opts.args[0]?.query;\n if (queryParams) {\n Object.entries(queryParams).forEach(([key, value]) => {\n if (Array.isArray(value)) {\n value.forEach((item) => targetUrl.searchParams.append(key, item));\n } else {\n targetUrl.searchParams.set(key, value);\n }\n });\n }\n const establishWebSocket = (...args) => {\n if (options?.webSocket !== void 0 && typeof options.webSocket === \"function\") {\n return options.webSocket(...args);\n }\n return new WebSocket(...args);\n };\n return establishWebSocket(targetUrl.toString());\n }\n const req = new ClientRequestImpl(url, method, {\n buildSearchParams: buildSearchParamsOption\n });\n if (method) {\n options ??= {};\n const reqOptions = { ...opts.args[1] };\n const baseHeaders = options.headers;\n const reqHeaders = reqOptions.headers;\n if (baseHeaders && reqHeaders) {\n reqOptions.headers = async () => ({\n ...typeof baseHeaders === \"function\" ? await baseHeaders() : baseHeaders,\n ...typeof reqHeaders === \"function\" ? await reqHeaders() : reqHeaders\n });\n }\n const args = deepMerge(options, reqOptions);\n return req.fetch(opts.args[0], args);\n }\n return req;\n}, []);\nexport {\n hc\n};\n","import { hc } from \"hono/client\"\n\nimport type { AppType } from \"server\"\n\n/**\n * The typed client both front doors reach the worker through.\n *\n * The editor and the CLI shared nothing before this package existed: the\n * editor configured `hc<AppType>` and the CLI hand-wrote a `fetch` at a URL it\n * built itself, so the two agreed about the worker's routes only by\n * coincidence. What they genuinely differ in is small and finite — where the\n * worker is, whether a session cookie rides along, and what identifies the\n * caller — so those are the parameters and everything else is shared.\n *\n * What this package deliberately does NOT own is either consumer's refusal\n * copy. The editor turns a failure into a union member a component renders;\n * the CLI turns one into a sentence a person reads in a terminal. Those are\n * different artefacts for different readers, and a shared \"message\" would be\n * wrong for both.\n *\n * `AppType` is a type and nothing else. `import type` erases before any\n * bundler sees it, so no worker code is reachable from the editor's bundle\n * (which fails its build on a first-paint budget) or from the CLI's.\n */\n\n/**\n * Whether the browser's session cookie rides along.\n *\n * Only the two values either consumer has a use for, rather than the full\n * `RequestCredentials`: `same-origin` is exactly the setting that breaks the\n * editor, because the cookie is set on `.agentsinc.sh` by a different origin\n * than the one the app is served from, and naming it here would make it\n * reachable by a typo.\n */\nexport type ApiCredentials = \"include\" | \"omit\"\n\n/**\n * What every request carries, however it is sent.\n *\n * `credentials` defaults to `\"include\"` and that direction is the point. The\n * session cookie is set by a different origin on the same registrable domain,\n * and without it sign-in appears to work and the very next request is\n * anonymous — with nothing logged on either side. A policy that has to be\n * opted INTO is one a new call site can forget silently; this one has to be\n * opted out of by name, which is a thing a reader can see.\n */\nexport type ApiRequestPolicy = {\n headers?: Record<string, string>\n credentials?: ApiCredentials\n}\n\nexport type ApiClientOptions = ApiRequestPolicy & {\n /** Where the worker is. No default: a wrong guess is a silent wrong API. */\n baseUrl: string\n /**\n * The seam tests reach through. hc calls\n * `(opt?.fetch || fetch)(url, { body, method, headers, ...opt?.init })`, so\n * injecting one is the only way to observe what this package produces without\n * a network — and the exact shape matters, because `init` is spread LAST.\n * That is why `headers` goes through hc's own option rather than through\n * `init`: in `init` it would replace the whole `Headers` object, including\n * the content type hc sets for a JSON body.\n */\n fetch?: typeof globalThis.fetch\n}\n\ntype ResolvedPolicy = {\n headers: Record<string, string>\n credentials: ApiCredentials\n}\n\n/**\n * The one place the defaults are applied, so the typed client and any\n * hand-written fetch cannot drift about what a request carries.\n */\nconst resolvePolicy = ({\n headers = {},\n credentials = \"include\",\n}: ApiRequestPolicy): ResolvedPolicy => ({ headers, credentials })\n\n/**\n * The policy as a `RequestInit`, for the paths `AppType` does not describe.\n *\n * Better Auth mounts its own surface under `/api/auth/*` with a single\n * handler rather than a described route, so there is nothing there for `hc` to\n * type — and a hand-written fetch at those paths is how the editor signs a\n * person in. This is what keeps that call on the same session policy as the\n * typed ones instead of on a second copy of it.\n *\n * Spread it LAST over a caller's own init: a caller may add a method, a body\n * or headers of its own, and may not drop the cookie.\n */\nexport const apiRequestInit = (policy: ApiRequestPolicy): RequestInit =>\n resolvePolicy(policy)\n\n/**\n * Builds the client for one consumer's base URL, headers and session policy.\n *\n * The two configured values go to different places in hc's options, and which\n * goes where is load-bearing. hc spreads `init` LAST over the request it\n * built, so anything named there replaces what hc put in that field — a\n * `headers` in `init` would replace the whole `Headers` object, including the\n * `Content-Type: application/json` hc sets for a `$post({ json })`. Headers go\n * through hc's own `headers` option, which is merged into that object instead,\n * and `init` names `credentials` and nothing else.\n */\nexport const createApiClient = ({\n baseUrl,\n fetch,\n ...policy\n}: ApiClientOptions) => {\n const { headers, credentials } = resolvePolicy(policy)\n\n return hc<AppType>(baseUrl, {\n headers,\n init: { credentials },\n // Spread rather than named. Under `exactOptionalPropertyTypes` hc's\n // optional `fetch` does not accept an explicit `undefined`, so writing\n // `fetch,` here is a TS2379 whenever a caller omits one.\n ...(fetch && { fetch }),\n })\n}\n\n/** The configured client, for consumers that pass one around. */\nexport type ApiClient = ReturnType<typeof createApiClient>\n","import { createApiClient } from \"@workspace/api\";\nimport { seedPayloadSchema, type SeedPayload } from \"@workspace/matrix/seed\";\n\n/** The config store behind agentsinc.sh. Overridable so tests never touch the network. */\nexport const SEED_API_URL = process.env.AGENTS_INC_API_URL ?? \"https://api.agentsinc.sh\";\n\n/**\n * Identifies this fetch as the CLI rather than a browser.\n *\n * `GET /configs/:id` is the only place either side can observe a config being *installed* rather\n * than merely built, and the worker can only separate that from someone opening a share link if\n * we say so. Without this the conversion signal does not exist.\n */\nexport const SEED_USER_AGENT = \"agents-inc-cli\";\n\n/**\n * The worker's routes, typed from the worker itself, configured for a terminal.\n *\n * `credentials: \"omit\"` is named rather than left to the shared default. The default is `include`\n * because the editor's session is a cookie the browser holds; a terminal holds no cookie jar at\n * all, so inheriting that default would claim a session that cannot exist.\n *\n * Built once at module scope, like `SEED_API_URL` above and for the same reason — the base URL is\n * read from the environment once at import, so there is nothing per-call for a client to pick up.\n */\nconst store = createApiClient({\n baseUrl: SEED_API_URL,\n headers: { accept: \"application/json\", \"user-agent\": SEED_USER_AGENT },\n credentials: \"omit\",\n});\n\nexport type FetchSeedResult = { ok: true; payload: SeedPayload } | { ok: false; error: string };\n\n/**\n * Fetches and validates a shared configuration.\n *\n * Every failure is a message rather than a throw: this runs before anything has been written, so\n * there is nothing to roll back and the caller's job is to explain rather than recover.\n */\nexport async function fetchSeedConfig(id: string): Promise<FetchSeedResult> {\n let response: Response;\n try {\n // Escaped here rather than by the client: hono's `replaceUrlParam` substitutes a path\n // parameter verbatim, so an id carrying a `/` or a `?` would silently address another route.\n response = await store.configs[\":id\"].$get({ param: { id: encodeURIComponent(id) } });\n } catch {\n return { ok: false, error: `Could not reach ${SEED_API_URL} — check your connection.` };\n }\n\n if (response.status === 404) {\n return { ok: false, error: `No configuration found for id '${id}'.` };\n }\n if (!response.ok) {\n return { ok: false, error: `Fetching configuration failed (HTTP ${response.status}).` };\n }\n\n let body: unknown;\n try {\n body = await response.json();\n } catch {\n return { ok: false, error: \"The configuration store returned something that is not JSON.\" };\n }\n\n const parsed = seedPayloadSchema.safeParse(body);\n if (!parsed.success) {\n // Names the remedy rather than a cause it cannot observe. The payload was validated by the\n // store on the way IN, so a stored payload that no longer parses means the contract moved\n // underneath it — and every way that happens lands in this one `safeParse`: an id minted\n // before a version bump, an id minted after one, and a body that is simply broken. The CLI\n // cannot tell them apart, and the one thing true of all three is that re-sharing fixes it,\n // because the store content-addresses an id and a fresh share mints one under this version.\n return {\n ok: false,\n error: `Configuration '${id}' is not in a format this version of the CLI can install. Shared ids are never migrated — re-share the configuration to mint a current one, or update the CLI if that id came from a newer version.`,\n };\n }\n\n return { ok: true, payload: parsed.data };\n}\n"],"mappings":"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","names":["path","path","fetch"]}
1
+ {"version":3,"sources":["../src/cli/lib/seed/external-skills.ts","../../../node_modules/hono/dist/utils/cookie.js","../../../node_modules/hono/dist/client/utils.js","../../../node_modules/hono/dist/client/client.js","../../api/src/client.ts","../src/cli/lib/seed/fetch-seed.ts"],"sourcesContent":["import path from \"path\";\n\nimport { seedExternalSkillSchema } from \"@workspace/matrix/seed\";\n\nimport { GITHUB_SOURCE, LOCAL_SKILLS_PATH, STANDARD_FILES } from \"../../consts.js\";\nimport { resolveInstallPaths } from \"../installation/install-base-dir.js\";\nimport { readSkillMetadata } from \"../loading/loader.js\";\nimport { getCategoryDomain } from \"../matrix/matrix-provider.js\";\nimport { claimSlug } from \"../matrix/skill-resolution.js\";\nimport { validateSkillPath } from \"../skills/skill-copier.js\";\nimport {\n injectForkedFromMetadata,\n readLocalSkillMetadata,\n writeMetadataYaml,\n} from \"../skills/skill-metadata.js\";\nimport { computeFileHash } from \"../versioning.js\";\nimport { glob, readFile, writeFile } from \"../../utils/fs.js\";\nimport { typedEntries } from \"../../utils/typed-object.js\";\n\nimport type { SkillScope } from \"../../types/config.js\";\nimport type { Domain, MergedSkillsMatrix, ResolvedSkill } from \"../../types/matrix.js\";\nimport type { CategoryPath, SkillId, SkillSlug } from \"../../types/skills.js\";\nimport type { ForkedFromMetadata, LocalSkillMetadata } from \"../skills/skill-metadata.js\";\nimport type {\n SeedExternalSkill,\n SeedPayload,\n SeedSkill,\n SeedSkillTree,\n} from \"@workspace/matrix/seed\";\n\n/**\n * One skill the payload carries rather than names, seated and ready to be written.\n *\n * `skillDir` and `domain` are resolved once, where the checks that make them safe already are:\n * the directory has to be the one the catalogue entry points at, because that is what tells the\n * copy step this skill is already where it belongs, and the domain is what the category guard has\n * just proved this catalogue declares.\n */\nexport type ExternalSkillInstall = {\n id: SkillId;\n scope: SkillScope;\n skill: SeedExternalSkill;\n domain: Domain;\n skillDir: string;\n};\n\n/** One external entry with the skill row that selected it — what the payload says about it. */\ntype SelectedExternal = { id: SkillId; skill: SeedExternalSkill; entry: SeedSkill };\n\n/**\n * Names every carried skill a payload asks to install as a plugin, not just the first.\n *\n * There is nothing to install them FROM: the payload is the source, so no marketplace serves\n * these ids. Coercing them to eject would install something the payload did not ask for, under a\n * mode the sharer's own screen still shows as plugin — so the ids are named and the run stops.\n */\nfunction pluginInstallError(ids: SkillId[]): string {\n return [\n \"This configuration cannot be installed: these skills travel inside it, so no marketplace \" +\n \"serves them and they cannot be installed as plugins:\",\n ...ids.map((id) => ` ${id}`),\n \"Re-share it with each skill above set to eject, which is the only way a carried skill installs.\",\n ].join(\"\\n\");\n}\n\n/**\n * The external entries this selection actually names.\n *\n * Presence in `skills` is selection and `external` is only where the bytes are, so content nobody\n * picked installs nothing — seating it would put a skill in the catalogue no part of the\n * configuration asked for.\n */\nfunction selectedExternals(payload: SeedPayload): SelectedExternal[] {\n return typedEntries<SkillId, SeedExternalSkill>(payload.external ?? {}).flatMap(([id, skill]) => {\n const entry = payload.skills[id];\n return entry ? [{ id, skill, entry }] : [];\n });\n}\n\nfunction askedForAsPlugin(externals: SelectedExternal[]): SkillId[] {\n return externals.filter(({ entry }) => entry.install !== \"eject\").map(({ id }) => id);\n}\n\n/**\n * Boundary cast: a carried skill's id is minted at intake and belongs to no generated union,\n * which is exactly what makes it external. It is its own slug too — a skill outside every\n * catalogue has no shorter name anything else would recognise, and the sending side says the same.\n */\nfunction externalSlug(id: SkillId): SkillSlug {\n return id as SkillSlug;\n}\n\n/** The repository's owner as a handle, which is all the authorship a carried skill records. */\nfunction skillAuthor(repo: string): string {\n const [owner = repo] = repo.split(\"/\");\n return `@${owner}`;\n}\n\n/** The repository ref a carried skill's provenance records, in the form every other ref wears. */\nfunction repoRef(repo: string): string {\n return `${GITHUB_SOURCE.GITHUB_PREFIX}${repo}`;\n}\n\n/** Where a carried skill's directory goes: under the skills directory of the scope it names. */\nfunction externalSkillDir(id: SkillId, scope: SkillScope, projectDir: string): string {\n return path.join(resolveInstallPaths(projectDir, scope).skillsDir, id);\n}\n\n/**\n * The catalogue entry a carried skill becomes on arrival.\n *\n * It declares no relationships and is named by none, which is honest: nothing outside the\n * catalogue can say what this skill conflicts with, and nothing inside it knows the skill exists.\n */\nfunction externalCatalogueEntry(install: ExternalSkillInstall): ResolvedSkill {\n return {\n id: install.id,\n slug: externalSlug(install.id),\n displayName: install.skill.displayName,\n description: install.skill.description,\n // Boundary cast held by the guard above it: the category was confirmed against the catalogue\n // the payload names, and this one has just been shown to declare it too.\n category: install.skill.categoryId as CategoryPath,\n author: skillAuthor(install.skill.repo),\n path: `${LOCAL_SKILLS_PATH}/${install.id}/`,\n conflictsWith: [],\n requires: [],\n alternatives: [],\n discourages: [],\n // Local and custom for the same reason: the bytes are on this machine and nowhere else, and\n // the skill was authored outside the vocabulary this catalogue ships.\n local: true,\n custom: true,\n localPath: install.skillDir,\n };\n}\n\n/**\n * Seats one carried skill in the catalogue, or leaves it unseated.\n *\n * A skill is PLACED in the taxonomy and never extends it, so a category this catalogue has no\n * definition for belongs to no domain, renders in no tab and reaches no sub-agent. Left unseated,\n * the id falls to the decode's own skip, which reports it by name.\n */\nfunction seatExternalSkill(\n { id, skill, entry }: SelectedExternal,\n matrix: MergedSkillsMatrix,\n projectDir: string,\n): ExternalSkillInstall[] {\n const domain = getCategoryDomain(skill.categoryId);\n if (domain === undefined) return [];\n\n const install: ExternalSkillInstall = {\n id,\n skill,\n domain,\n scope: entry.scope,\n skillDir: externalSkillDir(id, entry.scope, projectDir),\n };\n\n matrix.skills[id] = externalCatalogueEntry(install);\n // Completes the map over the matrix: an entry the slug map does not carry is invisible to\n // anything that addresses a skill by slug.\n claimSlug(matrix.slugMap, externalSlug(id), id);\n\n return [install];\n}\n\n/**\n * Seats the catalogue entries a payload carries, and says where each one's bytes go.\n *\n * Every other id in a payload is resolved against a catalogue the receiver already has, so one it\n * does not know is skipped and reported. A carried skill answers to no catalogue at all — which is\n * why its whole directory travels — so it has to BECOME a catalogue entry before the decode reads\n * the selection, or it is skipped like any other unknown id and the configuration installs\n * quietly smaller than it was shared.\n *\n * The entries are written into `matrix` in place, exactly as the local-skill merge writes the\n * skills it discovers on disk. That matrix is the object `initializeMatrix` seated, which is what\n * {@link getCategoryDomain} reads — every caller passes the loaded source's own.\n *\n * @throws {Error} If any carried skill asks to be installed as a plugin — see\n * {@link pluginInstallError}.\n */\nexport function registerExternalSkills(\n payload: SeedPayload,\n matrix: MergedSkillsMatrix,\n projectDir: string,\n): ExternalSkillInstall[] {\n const externals = selectedExternals(payload);\n const asPlugin = askedForAsPlugin(externals);\n\n if (asPlugin.length > 0) throw new Error(pluginInstallError(asPlugin));\n\n return externals.flatMap((external) => seatExternalSkill(external, matrix, projectDir));\n}\n\n/**\n * Writes each carried skill's directory where its own entry said, and registers it on disk.\n *\n * Three things have to be true afterwards, and each is a separate act: the files are there, the\n * skill answers to the id the configuration recorded, and a metadata.yaml describes it well\n * enough for the next `edit`, `compile` or `list` to find it again. The seat above lasts one run;\n * this is what makes the install survive.\n */\nexport async function writeExternalSkills(installs: ExternalSkillInstall[]): Promise<void> {\n for (const install of installs) {\n await writeSkillTree(install);\n await writeSkillManifest(install);\n await registerSkillOnDisk(install);\n }\n}\n\n/**\n * The directory as the payload holds it, keys and all.\n *\n * The keys came off the wire, so a payload may ask for any path it likes — including one that\n * climbs out of the skill's own directory. They answer to the same guard a marketplace's own\n * paths do.\n */\nasync function writeSkillTree(install: ExternalSkillInstall): Promise<void> {\n for (const [relativePath, contents] of Object.entries(install.skill.files)) {\n const filePath = path.join(install.skillDir, relativePath);\n validateSkillPath(filePath, install.skillDir, relativePath);\n await writeFile(filePath, contents);\n }\n}\n\n/** The frontmatter block at the head of a SKILL.md, and the `name` field inside it. */\nconst FRONTMATTER_BLOCK = /^---\\r?\\n([\\s\\S]*?)\\r?\\n---/;\nconst NAME_FIELD = /^name:.*$/m;\n\n/**\n * Names the skill by the id it installs under.\n *\n * Every loader reads a skill's id off this one field, and a compiled sub-agent references the id\n * the configuration recorded — the one minted at intake, which is also this directory's name. The\n * repository the skill came from knew about neither, so left as it was written the two never\n * meet: the sub-agent names a skill Claude Code knows as something else, and the next load\n * registers an id no configuration carries.\n *\n * Only the name. Everything else in the block is the author's and travels untouched.\n */\nfunction withInstalledName(manifest: string, install: ExternalSkillInstall): string {\n const named = `name: ${install.id}`;\n const block = FRONTMATTER_BLOCK.exec(manifest);\n\n // A manifest with no frontmatter at all describes no skill to Claude Code either. The payload\n // carries both fields one needs, so it is given one rather than installed unreadable.\n if (!block) {\n return `---\\n${named}\\ndescription: ${install.skill.description}\\n---\\n\\n${manifest}`;\n }\n\n const [whole, fields = \"\"] = block;\n const renamed = NAME_FIELD.test(fields)\n ? fields.replace(NAME_FIELD, named)\n : `${named}\\n${fields}`;\n\n // The block is anchored at the head of the file, so what follows it is the rest verbatim.\n return `---\\n${renamed}\\n---${manifest.slice(whole.length)}`;\n}\n\nasync function writeSkillManifest(install: ExternalSkillInstall): Promise<void> {\n const manifestPath = path.join(install.skillDir, STANDARD_FILES.SKILL_MD);\n await writeFile(manifestPath, withInstalledName(await readFile(manifestPath), install));\n}\n\n/**\n * What the payload confirmed, over what the repository shipped, over what neither says.\n *\n * The order is the whole of it. The taxonomy is the placement the user chose against the\n * catalogue this configuration names, and the repository's own idea of where its skill belongs\n * answers to a taxonomy nobody here shares — so that is written over the file. Everything else\n * the repository wrote is kept, because its author knows more about their skill than a default\n * does.\n *\n * The defaults underneath exist because `doctor` validates every installed metadata.yaml, and a\n * file this command writes that that command reports as an error is the CLI disagreeing with\n * itself — in a file the user cannot fix, since the skill is somebody else's repository. Neither\n * is invented: the authorship is the repository's owner, and the usage line is the same words the\n * stack loader already writes for every skill reference it has nothing more specific for.\n */\nfunction externalSkillMetadata(\n install: ExternalSkillInstall,\n shipped: LocalSkillMetadata | null,\n): LocalSkillMetadata {\n return {\n author: skillAuthor(install.skill.repo),\n usageGuidance: `Use when working with ${install.skill.displayName}`,\n ...shipped,\n displayName: install.skill.displayName,\n slug: install.id,\n category: install.skill.categoryId,\n domain: install.domain,\n cliDescription: install.skill.description,\n custom: true,\n };\n}\n\n/**\n * Registers the skill on disk, where the next command will look for it.\n *\n * `forkedFrom` is stamped through the one writer that stamps it, because it is the package's\n * single answer to \"did the CLI put this directory here?\" — `uninstall` reads it to decide what it\n * may delete, and the producer reads it to decide what the round trip owns. A carried skill IS the\n * round trip's; a skill the user wrote by hand is not, and carries nothing.\n *\n * The repository AND the directory inside it, because this is the only record of where these bytes\n * came from. Sharing this installation has to rebuild the entry the payload carried, and a\n * repository alone cannot say which of its directories travelled — see {@link readCarriedSkills}.\n */\nasync function registerSkillOnDisk(install: ExternalSkillInstall): Promise<void> {\n const metadataPath = path.join(install.skillDir, STANDARD_FILES.METADATA_YAML);\n const shipped = await readLocalSkillMetadata(install.skillDir);\n\n await writeMetadataYaml(metadataPath, externalSkillMetadata(install, shipped));\n await injectForkedFromMetadata(\n install.skillDir,\n install.id,\n await computeFileHash(path.join(install.skillDir, STANDARD_FILES.SKILL_MD)),\n { source: repoRef(install.skill.repo), path: install.skill.path },\n );\n}\n\n/**\n * One installed skill directory the round trip owns, and what its install recorded about it.\n *\n * Read by the caller rather than here: whether a directory is the CLI's copy or the user's own\n * work is a question about ownership, which `installation-payload.ts` already asks of every entry\n * before anything else. This module answers only the next one — whether the bytes have to travel.\n */\nexport type OwnedSkillDir = { id: string; skillDir: string; provenance: ForkedFromMetadata };\n\n/**\n * What one installation carries rather than names.\n *\n * The same shape the mapper's other readings wear: what can travel, and the lines naming what\n * cannot. Both halves matter — a payload quietly missing content it could not read still mints an\n * id, and that id installs a configuration nobody chose.\n */\nexport type ContentReading = {\n /** Skill id -> the entry a receiver seats it from. Keyed exactly as the wire keys `external`. */\n external: Record<string, SeedExternalSkill>;\n /** One line per skill whose content this installation cannot carry, for the refusal. */\n uncarryable: string[];\n};\n\n/** What one owned directory turns out to carry: content, a reason it cannot, or nothing. */\ntype CarriedRead =\n | { carries: \"content\"; id: string; skill: SeedExternalSkill }\n | { carries: \"nothing\" }\n | { carries: \"unshareable\"; line: string };\n\nfunction unnameableRepoMessage(id: string): string {\n return `${id} travels inside a shared configuration, and this installation does not record the repository its content came from`;\n}\n\nfunction uncarryableContentMessage(id: string, reason: string): string {\n return `${id} travels inside a shared configuration, and ${reason}`;\n}\n\n/**\n * The inverse of {@link repoRef}: the repository a ref names, or nothing when it names none this\n * contract can state.\n *\n * Provenance is written as a ref because that is the form every other source in a configuration\n * wears; the wire wants GitHub's own `owner/name`. Any other ref is a repository this contract has\n * no word for, which is a refusal rather than something to guess at.\n */\nfunction repoFromRef(ref: string | undefined): string | undefined {\n if (ref === undefined || !ref.startsWith(GITHUB_SOURCE.GITHUB_PREFIX)) return undefined;\n return ref.slice(GITHUB_SOURCE.GITHUB_PREFIX.length);\n}\n\n/** Every file under the skill, keyed by its path relative to it — dotfiles and nesting included. */\nasync function readSkillTree(skillDir: string): Promise<SeedSkillTree> {\n const files = await glob(\"**/*\", skillDir, { dot: true });\n const entries = await Promise.all(\n files.map(async (file) => [file, await readFile(path.join(skillDir, file))] as const),\n );\n return Object.fromEntries(entries);\n}\n\n/**\n * Rebuilds the entry a carried skill arrived as, from the directory the install wrote.\n *\n * The bytes as they stand rather than as they arrived: the manifest was renamed to the id this\n * install recorded, and a user may have edited the skill since. Both are what is installed here,\n * and what is installed here is what a share carries.\n *\n * Validated against the contract's own schema rather than by rules restated here — that is what\n * makes the weight limit, the manifest requirement and every field's shape one definition rather\n * than two that can drift.\n */\nasync function readCarriedSkill({ id, skillDir, provenance }: OwnedSkillDir): Promise<CarriedRead> {\n // The directory is what marks a skill as one no catalogue can resolve. Without it there is a\n // catalogue behind this id, so the receiver installs it from there and the bytes stay home.\n if (provenance.path === undefined) return { carries: \"nothing\" };\n\n const repo = repoFromRef(provenance.source);\n if (repo === undefined) return { carries: \"unshareable\", line: unnameableRepoMessage(id) };\n\n const described = await readSkillMetadata(path.join(skillDir, STANDARD_FILES.METADATA_YAML));\n if (!described.usable) {\n return { carries: \"unshareable\", line: uncarryableContentMessage(id, described.reason) };\n }\n\n const parsed = seedExternalSkillSchema.safeParse({\n displayName: described.metadata.displayName,\n description: described.metadata.cliDescription,\n categoryId: described.metadata.category,\n repo,\n path: provenance.path,\n files: await readSkillTree(skillDir),\n });\n if (!parsed.success) {\n const reason = parsed.error.issues.map((issue) => issue.message).join(\"; \");\n return { carries: \"unshareable\", line: uncarryableContentMessage(id, reason) };\n }\n\n return { carries: \"content\", id, skill: parsed.data };\n}\n\n/**\n * What a set of owned skill directories carries, and what it cannot.\n *\n * The mirror of {@link registerExternalSkills}: that seats an entry a payload carried and writes\n * its bytes, this reads those bytes back into the entry a payload carries. Every other id in a\n * configuration is resolved by the receiver against a catalogue it already has, so its content has\n * nothing to add; an added skill answers to no catalogue in either direction, and a share that\n * named it without carrying it would mint an id that installs a configuration missing it.\n */\nexport async function readCarriedSkills(owned: OwnedSkillDir[]): Promise<ContentReading> {\n const reads = await Promise.all(owned.map(readCarriedSkill));\n\n return {\n external: Object.fromEntries(\n reads.flatMap((read) => (read.carries === \"content\" ? [[read.id, read.skill]] : [])),\n ),\n uncarryable: reads.flatMap((read) => (read.carries === \"unshareable\" ? [read.line] : [])),\n };\n}\n","// src/utils/cookie.ts\nimport { decodeURIComponent_, tryDecode } from \"./url.js\";\nvar algorithm = { name: \"HMAC\", hash: \"SHA-256\" };\nvar getCryptoKey = async (secret) => {\n const secretBuf = typeof secret === \"string\" ? new TextEncoder().encode(secret) : secret;\n return await crypto.subtle.importKey(\"raw\", secretBuf, algorithm, false, [\"sign\", \"verify\"]);\n};\nvar makeSignature = async (value, secret) => {\n const key = await getCryptoKey(secret);\n const signature = await crypto.subtle.sign(algorithm.name, key, new TextEncoder().encode(value));\n return btoa(String.fromCharCode(...new Uint8Array(signature)));\n};\nvar verifySignature = async (base64Signature, value, secret) => {\n try {\n const signatureBinStr = atob(base64Signature);\n const signature = new Uint8Array(signatureBinStr.length);\n for (let i = 0, len = signatureBinStr.length; i < len; i++) {\n signature[i] = signatureBinStr.charCodeAt(i);\n }\n return await crypto.subtle.verify(algorithm, secret, signature, new TextEncoder().encode(value));\n } catch {\n return false;\n }\n};\nvar validCookieNameRegEx = /^[\\w!#$%&'*.^`|~+-]+$/;\nvar relaxedCookieNameRegEx = /^[!#-:<>-[\\]-~]+$/;\nvar validCookieValueRegEx = /^[ !#-:<-[\\]-~]*$/;\nvar trimCookieWhitespace = (value) => {\n let start = 0;\n let end = value.length;\n while (start < end) {\n const charCode = value.charCodeAt(start);\n if (charCode !== 32 && charCode !== 9) {\n break;\n }\n start++;\n }\n while (end > start) {\n const charCode = value.charCodeAt(end - 1);\n if (charCode !== 32 && charCode !== 9) {\n break;\n }\n end--;\n }\n return start === 0 && end === value.length ? value : value.slice(start, end);\n};\nvar parse = (cookie, name) => {\n if (name && cookie.indexOf(name) === -1) {\n return {};\n }\n const pairs = cookie.split(\";\");\n const parsedCookie = /* @__PURE__ */ Object.create(null);\n for (const pairStr of pairs) {\n const valueStartPos = pairStr.indexOf(\"=\");\n if (valueStartPos === -1) {\n continue;\n }\n const cookieName = trimCookieWhitespace(pairStr.substring(0, valueStartPos));\n if (name && name !== cookieName || !relaxedCookieNameRegEx.test(cookieName) || cookieName in parsedCookie) {\n continue;\n }\n let cookieValue = trimCookieWhitespace(pairStr.substring(valueStartPos + 1));\n if (cookieValue.startsWith('\"') && cookieValue.endsWith('\"')) {\n cookieValue = cookieValue.slice(1, -1);\n }\n if (validCookieValueRegEx.test(cookieValue)) {\n parsedCookie[cookieName] = cookieValue.indexOf(\"%\") !== -1 ? tryDecode(cookieValue, decodeURIComponent_) : cookieValue;\n if (name) {\n break;\n }\n }\n }\n return parsedCookie;\n};\nvar parseSigned = async (cookie, secret, name) => {\n const parsedCookie = /* @__PURE__ */ Object.create(null);\n const secretKey = await getCryptoKey(secret);\n for (const [key, value] of Object.entries(parse(cookie, name))) {\n const signatureStartPos = value.lastIndexOf(\".\");\n if (signatureStartPos < 1) {\n continue;\n }\n const signedValue = value.substring(0, signatureStartPos);\n const signature = value.substring(signatureStartPos + 1);\n if (signature.length !== 44 || !signature.endsWith(\"=\")) {\n continue;\n }\n const isVerified = await verifySignature(signature, signedValue, secretKey);\n parsedCookie[key] = isVerified ? signedValue : false;\n }\n return parsedCookie;\n};\nvar _serialize = (name, value, opt = {}) => {\n if (!validCookieNameRegEx.test(name)) {\n throw new Error(\"Invalid cookie name\");\n }\n let cookie = `${name}=${value}`;\n if (name.startsWith(\"__Secure-\") && !opt.secure) {\n throw new Error(\"__Secure- Cookie must have Secure attributes\");\n }\n if (name.startsWith(\"__Host-\")) {\n if (!opt.secure) {\n throw new Error(\"__Host- Cookie must have Secure attributes\");\n }\n if (opt.path !== \"/\") {\n throw new Error('__Host- Cookie must have Path attributes with \"/\"');\n }\n if (opt.domain) {\n throw new Error(\"__Host- Cookie must not have Domain attributes\");\n }\n }\n for (const key of [\"domain\", \"path\", \"sameSite\", \"priority\"]) {\n if (opt[key] && /[;\\r\\n]/.test(opt[key])) {\n throw new Error(`${key} must not contain \";\", \"\\\\r\", or \"\\\\n\"`);\n }\n }\n if (opt && typeof opt.maxAge === \"number\" && opt.maxAge >= 0) {\n if (opt.maxAge > 3456e4) {\n throw new Error(\n \"Cookies Max-Age SHOULD NOT be greater than 400 days (34560000 seconds) in duration.\"\n );\n }\n cookie += `; Max-Age=${opt.maxAge | 0}`;\n }\n if (opt.domain && opt.prefix !== \"host\") {\n cookie += `; Domain=${opt.domain}`;\n }\n if (opt.path) {\n cookie += `; Path=${opt.path}`;\n }\n if (opt.expires) {\n if (opt.expires.getTime() - Date.now() > 3456e7) {\n throw new Error(\n \"Cookies Expires SHOULD NOT be greater than 400 days (34560000 seconds) in the future.\"\n );\n }\n cookie += `; Expires=${opt.expires.toUTCString()}`;\n }\n if (opt.httpOnly) {\n cookie += \"; HttpOnly\";\n }\n if (opt.secure) {\n cookie += \"; Secure\";\n }\n if (opt.sameSite) {\n cookie += `; SameSite=${opt.sameSite.charAt(0).toUpperCase() + opt.sameSite.slice(1)}`;\n }\n if (opt.priority) {\n cookie += `; Priority=${opt.priority.charAt(0).toUpperCase() + opt.priority.slice(1)}`;\n }\n if (opt.partitioned) {\n if (!opt.secure) {\n throw new Error(\"Partitioned Cookie must have Secure attributes\");\n }\n cookie += \"; Partitioned\";\n }\n return cookie;\n};\nvar serialize = (name, value, opt) => {\n value = encodeURIComponent(value);\n return _serialize(name, value, opt);\n};\nvar serializeSigned = async (name, value, secret, opt = {}) => {\n const signature = await makeSignature(value, secret);\n value = `${value}.${signature}`;\n value = encodeURIComponent(value);\n return _serialize(name, value, opt);\n};\nexport {\n parse,\n parseSigned,\n serialize,\n serializeSigned\n};\n","// src/client/utils.ts\nimport { fetchRP, DetailedError } from \"./fetch-result-please.js\";\nvar mergePath = (base, path) => {\n base = base.replace(/\\/+$/, \"\");\n base = base + \"/\";\n path = path.replace(/^\\/+/, \"\");\n return base + path;\n};\nvar replaceUrlParam = (urlString, params) => {\n for (const [k, v] of Object.entries(params)) {\n const reg = new RegExp(\"/:\" + k + \"(?:{[^/]+})?\\\\??(?=/|$)\");\n urlString = urlString.replace(reg, v ? `/${v}` : \"\");\n }\n return urlString;\n};\nvar buildSearchParams = (query) => {\n const searchParams = new URLSearchParams();\n for (const [k, v] of Object.entries(query)) {\n if (v === void 0) {\n continue;\n }\n if (Array.isArray(v)) {\n for (const v2 of v) {\n searchParams.append(k, v2);\n }\n } else {\n searchParams.set(k, v);\n }\n }\n return searchParams;\n};\nvar replaceUrlProtocol = (urlString, protocol) => {\n switch (protocol) {\n case \"ws\":\n return urlString.replace(/^http/, \"ws\");\n case \"http\":\n return urlString.replace(/^ws/, \"http\");\n }\n};\nvar removeIndexString = (urlString) => {\n if (/^https?:\\/\\/[^\\/]+?\\/index(?=\\?|$)/.test(urlString)) {\n return urlString.replace(/\\/index(?=\\?|$)/, \"/\");\n }\n return urlString.replace(/\\/index(?=\\?|$)/, \"\");\n};\nfunction isObject(item) {\n return typeof item === \"object\" && item !== null && !Array.isArray(item);\n}\nfunction deepMerge(target, source) {\n if (!isObject(target) && !isObject(source)) {\n return source;\n }\n const merged = { ...target };\n for (const key in source) {\n const value = source[key];\n if (isObject(merged[key]) && isObject(value)) {\n merged[key] = deepMerge(merged[key], value);\n } else {\n merged[key] = value;\n }\n }\n return merged;\n}\nasync function parseResponse(fetchRes) {\n return fetchRP(fetchRes);\n}\nexport {\n DetailedError,\n buildSearchParams,\n deepMerge,\n mergePath,\n parseResponse,\n removeIndexString,\n replaceUrlParam,\n replaceUrlProtocol\n};\n","// src/client/client.ts\nimport { serialize } from \"../utils/cookie.js\";\nimport {\n buildSearchParams,\n deepMerge,\n mergePath,\n removeIndexString,\n replaceUrlParam,\n replaceUrlProtocol\n} from \"./utils.js\";\nvar createProxy = (callback, path) => {\n const proxy = new Proxy(() => {\n }, {\n get(_obj, key) {\n if (typeof key !== \"string\" || key === \"then\") {\n return void 0;\n }\n return createProxy(callback, [...path, key]);\n },\n apply(_1, _2, args) {\n return callback({\n path,\n args\n });\n }\n });\n return proxy;\n};\nvar ClientRequestImpl = class {\n url;\n method;\n buildSearchParams;\n queryParams = void 0;\n pathParams = {};\n rBody;\n cType = void 0;\n constructor(url, method, options) {\n this.url = url;\n this.method = method;\n this.buildSearchParams = options.buildSearchParams;\n }\n fetch = async (args, opt) => {\n if (args) {\n if (args.query) {\n this.queryParams = this.buildSearchParams(args.query);\n }\n if (args.form) {\n const form = new FormData();\n for (const [k, v] of Object.entries(args.form)) {\n if (v === void 0) {\n continue;\n }\n if (Array.isArray(v)) {\n for (const v2 of v) {\n form.append(k, v2);\n }\n } else {\n form.append(k, v);\n }\n }\n this.rBody = form;\n }\n if (args.json) {\n this.rBody = JSON.stringify(args.json);\n this.cType = \"application/json\";\n }\n if (args.param) {\n this.pathParams = args.param;\n }\n }\n let methodUpperCase = this.method.toUpperCase();\n const headerValues = {\n ...args?.header,\n ...typeof opt?.headers === \"function\" ? await opt.headers() : opt?.headers\n };\n if (args?.cookie) {\n const cookies = [];\n for (const [key, value] of Object.entries(args.cookie)) {\n cookies.push(serialize(key, value, { path: \"/\" }));\n }\n headerValues[\"Cookie\"] = cookies.join(\",\");\n }\n if (this.cType) {\n headerValues[\"Content-Type\"] = this.cType;\n }\n const headers = new Headers(headerValues ?? void 0);\n let url = this.url;\n url = removeIndexString(url);\n url = replaceUrlParam(url, this.pathParams);\n if (this.queryParams) {\n url = url + \"?\" + this.queryParams.toString();\n }\n methodUpperCase = this.method.toUpperCase();\n const setBody = !(methodUpperCase === \"GET\" || methodUpperCase === \"HEAD\");\n return (opt?.fetch || fetch)(url, {\n body: setBody ? this.rBody : void 0,\n method: methodUpperCase,\n headers,\n ...opt?.init\n });\n };\n};\nvar hc = (baseUrl, options) => createProxy(function proxyCallback(opts) {\n const buildSearchParamsOption = options?.buildSearchParams ?? buildSearchParams;\n const parts = [...opts.path];\n const lastParts = parts.slice(-3).reverse();\n if (lastParts[0] === \"toString\") {\n if (lastParts[1] === \"name\") {\n return lastParts[2] || \"\";\n }\n return proxyCallback.toString();\n }\n if (lastParts[0] === \"valueOf\") {\n if (lastParts[1] === \"name\") {\n return lastParts[2] || \"\";\n }\n return proxyCallback;\n }\n let method = \"\";\n if (/^\\$/.test(lastParts[0])) {\n const last = parts.pop();\n if (last) {\n method = last.replace(/^\\$/, \"\");\n }\n }\n const path = parts.join(\"/\");\n const url = mergePath(baseUrl, path);\n if (method === \"url\" || method === \"path\") {\n let result = url;\n if (opts.args[0]) {\n if (opts.args[0].param) {\n result = replaceUrlParam(url, opts.args[0].param);\n }\n if (opts.args[0].query) {\n result = result + \"?\" + buildSearchParamsOption(opts.args[0].query).toString();\n }\n }\n result = removeIndexString(result);\n if (method === \"url\") {\n return new URL(result);\n }\n return result.slice(baseUrl.replace(/\\/+$/, \"\").length).replace(/^\\/?/, \"/\");\n }\n if (method === \"ws\") {\n const webSocketUrl = replaceUrlProtocol(\n opts.args[0] && opts.args[0].param ? replaceUrlParam(url, opts.args[0].param) : url,\n \"ws\"\n );\n const targetUrl = new URL(webSocketUrl);\n const queryParams = opts.args[0]?.query;\n if (queryParams) {\n Object.entries(queryParams).forEach(([key, value]) => {\n if (Array.isArray(value)) {\n value.forEach((item) => targetUrl.searchParams.append(key, item));\n } else {\n targetUrl.searchParams.set(key, value);\n }\n });\n }\n const establishWebSocket = (...args) => {\n if (options?.webSocket !== void 0 && typeof options.webSocket === \"function\") {\n return options.webSocket(...args);\n }\n return new WebSocket(...args);\n };\n return establishWebSocket(targetUrl.toString());\n }\n const req = new ClientRequestImpl(url, method, {\n buildSearchParams: buildSearchParamsOption\n });\n if (method) {\n options ??= {};\n const reqOptions = { ...opts.args[1] };\n const baseHeaders = options.headers;\n const reqHeaders = reqOptions.headers;\n if (baseHeaders && reqHeaders) {\n reqOptions.headers = async () => ({\n ...typeof baseHeaders === \"function\" ? await baseHeaders() : baseHeaders,\n ...typeof reqHeaders === \"function\" ? await reqHeaders() : reqHeaders\n });\n }\n const args = deepMerge(options, reqOptions);\n return req.fetch(opts.args[0], args);\n }\n return req;\n}, []);\nexport {\n hc\n};\n","import { hc } from \"hono/client\"\n\nimport type { AppType } from \"server\"\n\n/**\n * The typed client both front doors reach the worker through.\n *\n * The editor and the CLI shared nothing before this package existed: the\n * editor configured `hc<AppType>` and the CLI hand-wrote a `fetch` at a URL it\n * built itself, so the two agreed about the worker's routes only by\n * coincidence. What they genuinely differ in is small and finite — where the\n * worker is, whether a session cookie rides along, and what identifies the\n * caller — so those are the parameters and everything else is shared.\n *\n * What this package deliberately does NOT own is either consumer's refusal\n * copy. The editor turns a failure into a union member a component renders;\n * the CLI turns one into a sentence a person reads in a terminal. Those are\n * different artefacts for different readers, and a shared \"message\" would be\n * wrong for both.\n *\n * `AppType` is a type and nothing else. `import type` erases before any\n * bundler sees it, so no worker code is reachable from the editor's bundle\n * (which fails its build on a first-paint budget) or from the CLI's.\n */\n\n/**\n * Whether the browser's session cookie rides along.\n *\n * Only the two values either consumer has a use for, rather than the full\n * `RequestCredentials`: `same-origin` is exactly the setting that breaks the\n * editor, because the cookie is set on `.agentsinc.sh` by a different origin\n * than the one the app is served from, and naming it here would make it\n * reachable by a typo.\n */\nexport type ApiCredentials = \"include\" | \"omit\"\n\n/**\n * What every request carries, however it is sent.\n *\n * `credentials` defaults to `\"include\"` and that direction is the point. The\n * session cookie is set by a different origin on the same registrable domain,\n * and without it sign-in appears to work and the very next request is\n * anonymous — with nothing logged on either side. A policy that has to be\n * opted INTO is one a new call site can forget silently; this one has to be\n * opted out of by name, which is a thing a reader can see.\n */\nexport type ApiRequestPolicy = {\n headers?: Record<string, string>\n credentials?: ApiCredentials\n}\n\nexport type ApiClientOptions = ApiRequestPolicy & {\n /** Where the worker is. No default: a wrong guess is a silent wrong API. */\n baseUrl: string\n /**\n * The seam tests reach through. hc calls\n * `(opt?.fetch || fetch)(url, { body, method, headers, ...opt?.init })`, so\n * injecting one is the only way to observe what this package produces without\n * a network — and the exact shape matters, because `init` is spread LAST.\n * That is why `headers` goes through hc's own option rather than through\n * `init`: in `init` it would replace the whole `Headers` object, including\n * the content type hc sets for a JSON body.\n */\n fetch?: typeof globalThis.fetch\n}\n\ntype ResolvedPolicy = {\n headers: Record<string, string>\n credentials: ApiCredentials\n}\n\n/**\n * The one place the defaults are applied, so the typed client and any\n * hand-written fetch cannot drift about what a request carries.\n */\nconst resolvePolicy = ({\n headers = {},\n credentials = \"include\",\n}: ApiRequestPolicy): ResolvedPolicy => ({ headers, credentials })\n\n/**\n * The policy as a `RequestInit`, for the paths `AppType` does not describe.\n *\n * Better Auth mounts its own surface under `/api/auth/*` with a single\n * handler rather than a described route, so there is nothing there for `hc` to\n * type — and a hand-written fetch at those paths is how the editor signs a\n * person in. This is what keeps that call on the same session policy as the\n * typed ones instead of on a second copy of it.\n *\n * Spread it LAST over a caller's own init: a caller may add a method, a body\n * or headers of its own, and may not drop the cookie.\n */\nexport const apiRequestInit = (policy: ApiRequestPolicy): RequestInit =>\n resolvePolicy(policy)\n\n/**\n * Builds the client for one consumer's base URL, headers and session policy.\n *\n * The two configured values go to different places in hc's options, and which\n * goes where is load-bearing. hc spreads `init` LAST over the request it\n * built, so anything named there replaces what hc put in that field — a\n * `headers` in `init` would replace the whole `Headers` object, including the\n * `Content-Type: application/json` hc sets for a `$post({ json })`. Headers go\n * through hc's own `headers` option, which is merged into that object instead,\n * and `init` names `credentials` and nothing else.\n */\nexport const createApiClient = ({\n baseUrl,\n fetch,\n ...policy\n}: ApiClientOptions) => {\n const { headers, credentials } = resolvePolicy(policy)\n\n return hc<AppType>(baseUrl, {\n headers,\n init: { credentials },\n // Spread rather than named. Under `exactOptionalPropertyTypes` hc's\n // optional `fetch` does not accept an explicit `undefined`, so writing\n // `fetch,` here is a TS2379 whenever a caller omits one.\n ...(fetch && { fetch }),\n })\n}\n\n/** The configured client, for consumers that pass one around. */\nexport type ApiClient = ReturnType<typeof createApiClient>\n","import { createApiClient } from \"@workspace/api\";\nimport { seedPayloadSchema, type SeedPayload } from \"@workspace/matrix/seed\";\n\nimport { truncateText } from \"../../utils/string.js\";\n\n/** The config store behind agentsinc.sh. Overridable so tests never touch the network. */\nexport const SEED_API_URL = process.env.AGENTS_INC_API_URL ?? \"https://api.agentsinc.sh\";\n\n/**\n * Identifies this fetch as the CLI rather than a browser.\n *\n * `GET /configs/:id` is the only place either side can observe a config being *installed* rather\n * than merely built, and the worker can only separate that from someone opening a share link if\n * we say so. Without this the conversion signal does not exist.\n */\nexport const SEED_USER_AGENT = \"agents-inc-cli\";\n\n/**\n * The worker's routes, typed from the worker itself, configured for a terminal.\n *\n * `credentials: \"omit\"` is named rather than left to the shared default. The default is `include`\n * because the editor's session is a cookie the browser holds; a terminal holds no cookie jar at\n * all, so inheriting that default would claim a session that cannot exist.\n *\n * Built once at module scope, like `SEED_API_URL` above and for the same reason — the base URL is\n * read from the environment once at import, so there is nothing per-call for a client to pick up.\n */\nconst store = createApiClient({\n baseUrl: SEED_API_URL,\n headers: { accept: \"application/json\", \"user-agent\": SEED_USER_AGENT },\n credentials: \"omit\",\n});\n\n/**\n * How much of the store's account of a refused read to quote back.\n *\n * Sized on what this route really says, which is one short line per refusal: `Stored config is\n * unreadable` and `Could not read this config` are both under thirty characters. This holds the\n * longer of them four times over — room for that sentence to grow into a real one, and none for\n * anything that has stopped being a sentence.\n *\n * Far short of the 300-character `EXPLANATION_BUDGET` in `publish-seed.ts`, and for the reason\n * that one gives for its own size rather than in spite of it: a budget is sized on what the store\n * really writes there, and what it writes there is a list of schema issues naming a skill and a\n * sub-agent apiece. Neither number is a policy about terminals; each is a measurement of one\n * route.\n */\nconst EXPLANATION_BUDGET = 120;\n\n/** The content type the store's own refusals arrive as, and the only one worth reading. */\nconst QUOTABLE_TYPE = \"text/plain\";\n\n/** The status the store spends on an id it has never held, which is the one it is asked for. */\nconst NO_SUCH_CONFIG = 404;\n\nexport type FetchSeedResult = { ok: true; payload: SeedPayload } | { ok: false; error: string };\n\n/**\n * Fetches and validates a shared configuration.\n *\n * Every failure is a message rather than a throw: this runs before anything has been written, so\n * there is nothing to roll back and the caller's job is to explain rather than recover.\n */\nexport async function fetchSeedConfig(id: string): Promise<FetchSeedResult> {\n let response: Response;\n try {\n // Escaped here rather than by the client: hono's `replaceUrlParam` substitutes a path\n // parameter verbatim, so an id carrying a `/` or a `?` would silently address another route.\n response = await store.configs[\":id\"].$get({ param: { id: encodeURIComponent(id) } });\n } catch {\n return { ok: false, error: `Could not reach ${SEED_API_URL} — check your connection.` };\n }\n\n // Ahead of the quoting arm below, and the only status held out of it. The store answers this\n // one `No config under this id`, which says strictly less than the sentence here does — it does\n // not name the id the caller typed, and that is the whole of what a person needs to see.\n if (response.status === NO_SUCH_CONFIG) {\n return { ok: false, error: `No configuration found for id '${id}'.` };\n }\n if (!response.ok) {\n return { ok: false, error: await refusalMessage(response) };\n }\n\n let body: unknown;\n try {\n body = await response.json();\n } catch {\n return { ok: false, error: \"The configuration store returned something that is not JSON.\" };\n }\n\n const parsed = seedPayloadSchema.safeParse(body);\n if (!parsed.success) {\n // Names the remedy rather than a cause it cannot observe. The payload was validated by the\n // store on the way IN, so a stored payload that no longer parses means the contract moved\n // underneath it — and every way that happens lands in this one `safeParse`: an id minted\n // before a version bump, an id minted after one, and a body that is simply broken. The CLI\n // cannot tell them apart, and the one thing true of all three is that re-sharing fixes it,\n // because the store content-addresses an id and a fresh share mints one under this version.\n return {\n ok: false,\n error: `Configuration '${id}' is not in a format this version of the CLI can install. Shared ids are never migrated — re-share the configuration to mint a current one, or update the CLI if that id came from a newer version.`,\n };\n }\n\n return { ok: true, payload: parsed.data };\n}\n\n/**\n * A refusal, carrying the store's own account of it wherever there is one to have.\n *\n * The same posture `refusalMessage` in `publish-seed.ts` takes on the write side, and deliberately\n * NOT the same mechanism. That route's 400 arrives as a `@hono/zod-validator` envelope with the\n * refused issues rendered inside it; `getConfig` writes every one of its refusals with `c.text`,\n * so there is no envelope here to parse and a copy of that parser would find nothing in any body\n * this route can produce.\n *\n * The status is never given up. It is what separates a store that is present and holding bytes it\n * can no longer read (500) from one that would not answer at all (503) — and what the body then\n * adds is the store saying which of its own failures this was, in words the status has no room\n * for.\n */\nasync function refusalMessage(response: Response): Promise<string> {\n const refused = `Fetching configuration failed (HTTP ${response.status}).`;\n const explanation = explanationOf(await refusalBody(response));\n\n return explanation === undefined ? refused : `${refused} The store said: ${explanation}`;\n}\n\n/**\n * Whatever the store sent, as far as it can be read — and only where it was the store that sent it.\n *\n * Nothing below this line is worth failing over: the message being assembled is already a failure,\n * and a body that cannot be read is one more thing the status will have to cover on its own.\n * Draining the stream can throw in its own right — a connection dropped mid-body — which is why\n * the `try` is around the read.\n */\nasync function refusalBody(response: Response): Promise<string | undefined> {\n if (!arrivedAsText(response)) return undefined;\n\n try {\n return await response.text();\n } catch {\n return undefined;\n }\n}\n\n/**\n * Whether what arrived is the store speaking, as far as the wire says it is.\n *\n * `getConfig` writes every refusal with `c.text(...)`, so `text/plain` is the shape the store's\n * own words take on this route. What arrives as anything else did not come from it: a proxy, a\n * WAF, a captive portal or a gateway answering in its place — and their answer is markup rather\n * than prose, which quoted into a terminal is a screenful of tags that explains nothing and buries\n * the status that does.\n *\n * This is the plain-text half of what `refusalSchema` does for `publish-seed.ts`. That side has a\n * SHAPE a foreign body fails to be; a sentence has no shape, so the wire's own statement of what\n * it sent is the only discriminator there is. It is not proof — a hostile answer may claim any\n * type it likes — which is what {@link EXPLANATION_BUDGET} is for, and why what survives both is\n * attributed to the store rather than spoken in the CLI's own voice.\n */\nfunction arrivedAsText(response: Response): boolean {\n return (response.headers.get(\"content-type\") ?? \"\").startsWith(QUOTABLE_TYPE);\n}\n\n/** The store's account of a refusal, where what came back really is one. */\nfunction explanationOf(body: string | undefined): string | undefined {\n if (body === undefined) return undefined;\n\n const said = body.trim();\n if (said === \"\") return undefined;\n\n return truncateText(said, EXPLANATION_BUDGET);\n}\n"],"mappings":"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","names":["path","path","fetch"]}
@@ -9,7 +9,7 @@ import {
9
9
  getUnmetRequiredBy,
10
10
  isLocalOnlySkill,
11
11
  warn
12
- } from "./chunk-7WKNPDN2.js";
12
+ } from "./chunk-UNASGGW2.js";
13
13
  import {
14
14
  getCategoryDomain,
15
15
  getSkillById,
@@ -1159,4 +1159,4 @@ export {
1159
1159
  showWarningsAsToast,
1160
1160
  hydrateWizardStore
1161
1161
  };
1162
- //# sourceMappingURL=chunk-UE3MLDL2.js.map
1162
+ //# sourceMappingURL=chunk-K26SBQLO.js.map
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  SummaryPanel
4
- } from "./chunk-Q4KCUR2T.js";
4
+ } from "./chunk-4KVJJJG2.js";
5
5
  import {
6
6
  Toast
7
7
  } from "./chunk-MSUTHS2V.js";
@@ -12,7 +12,7 @@ import {
12
12
  import {
13
13
  getDomainDisplayName,
14
14
  getStackName
15
- } from "./chunk-SBL4AYSB.js";
15
+ } from "./chunk-5J6DO4VW.js";
16
16
  import {
17
17
  HOTKEY_INFO,
18
18
  HOTKEY_SCOPE,
@@ -25,7 +25,7 @@ import {
25
25
  import {
26
26
  getActiveStepFlow,
27
27
  useWizardStore
28
- } from "./chunk-UE3MLDL2.js";
28
+ } from "./chunk-K26SBQLO.js";
29
29
  import {
30
30
  formatTerminalTooSmallMessage,
31
31
  isTerminalLargeEnough
@@ -234,4 +234,4 @@ export {
234
234
  useTerminalDimensions,
235
235
  WizardLayout
236
236
  };
237
- //# sourceMappingURL=chunk-GUFRQJAE.js.map
237
+ //# sourceMappingURL=chunk-L65XGQKR.js.map
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  StackSelection
4
- } from "./chunk-QG4NRKZD.js";
4
+ } from "./chunk-VEBEGZUV.js";
5
5
 
6
6
  // src/cli/components/wizard/step-stack.tsx
7
7
  import { Box } from "ink";
@@ -14,4 +14,4 @@ var StepStack = ({ onCancel }) => {
14
14
  export {
15
15
  StepStack
16
16
  };
17
- //# sourceMappingURL=chunk-SWYV7QXX.js.map
17
+ //# sourceMappingURL=chunk-RMMEYCEA.js.map
@@ -1,13 +1,13 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  getDomainDisplayName
4
- } from "./chunk-SBL4AYSB.js";
4
+ } from "./chunk-5J6DO4VW.js";
5
5
  import {
6
6
  CheckboxGrid
7
7
  } from "./chunk-VFZF7LVY.js";
8
8
  import {
9
9
  useWizardStore
10
- } from "./chunk-UE3MLDL2.js";
10
+ } from "./chunk-K26SBQLO.js";
11
11
  import {
12
12
  matrix,
13
13
  typedEntries
@@ -56,4 +56,4 @@ var DomainSelection = () => {
56
56
  export {
57
57
  DomainSelection
58
58
  };
59
- //# sourceMappingURL=chunk-WDN4HAN7.js.map
59
+ //# sourceMappingURL=chunk-S6GX7IJ2.js.map
@@ -8,7 +8,7 @@ import {
8
8
  import {
9
9
  buildCategoriesForDomain,
10
10
  useWizardStore
11
- } from "./chunk-UE3MLDL2.js";
11
+ } from "./chunk-K26SBQLO.js";
12
12
 
13
13
  // src/cli/components/wizard/step-build.tsx
14
14
  import { Box, useInput } from "ink";
@@ -89,4 +89,4 @@ var StepBuild = ({
89
89
  export {
90
90
  StepBuild
91
91
  };
92
- //# sourceMappingURL=chunk-ESI3JYOQ.js.map
92
+ //# sourceMappingURL=chunk-T2RPUNYP.js.map
@@ -119,9 +119,31 @@ var LOCAL_DEFAULTS = {
119
119
  var SKILL_CONTENT_FILES = [STANDARD_FILES.SKILL_MD, STANDARD_FILES.REFERENCE_MD];
120
120
  var SKILL_CONTENT_DIRS = [STANDARD_DIRS.EXAMPLES, STANDARD_DIRS.SCRIPTS];
121
121
 
122
+ // src/cli/utils/string.ts
123
+ var OSC = /\u001B\][\s\S]*?(?:\u0007|\u001B\\|$)/;
124
+ var CSI = /\u001B\[[0-?]*[ -/]*[@-~]?/;
125
+ var OTHER_ESCAPE = /\u001B.?/;
126
+ var C1 = /[\u0080-\u009F]/;
127
+ var C0 = /[\u0000-\u0008\u000B-\u001F\u007F]/;
128
+ function anyOf(patterns) {
129
+ return new RegExp(patterns.map((pattern) => pattern.source).join("|"), "g");
130
+ }
131
+ var TERMINAL_CONTROLS = anyOf([OSC, CSI, OTHER_ESCAPE, C1, C0]);
132
+ function stripTerminalControls(text) {
133
+ return text.replace(TERMINAL_CONTROLS, "");
134
+ }
135
+ function truncateText(text, maxLength) {
136
+ const inert = stripTerminalControls(text);
137
+ if (inert.length <= maxLength) return inert;
138
+ return inert.slice(0, maxLength - 1) + "\u2026";
139
+ }
140
+ function toTitleCase(kebabCase) {
141
+ return kebabCase.split("-").map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
142
+ }
143
+
122
144
  // src/cli/utils/errors.ts
123
145
  function getErrorMessage(error) {
124
- return error instanceof Error ? error.message : String(error);
146
+ return stripTerminalControls(error instanceof Error ? error.message : String(error));
125
147
  }
126
148
 
127
149
  // src/cli/utils/fs.ts
@@ -1245,11 +1267,11 @@ import { partition } from "remeda";
1245
1267
  // src/cli/lib/schema-validator.ts
1246
1268
  import "zod";
1247
1269
  function formatZodIssue(issue) {
1248
- const path25 = issue.path.join(".");
1270
+ const path25 = stripTerminalControls(issue.path.join("."));
1249
1271
  if (issue.code === "unrecognized_keys") {
1250
- return `Unrecognized key: "${issue.keys.join('", "')}"`;
1272
+ return `Unrecognized key: "${stripTerminalControls(issue.keys.join('", "'))}"`;
1251
1273
  }
1252
- return path25 ? `${path25}: ${issue.message}` : issue.message;
1274
+ return path25 ? `${path25}: ${stripTerminalControls(issue.message)}` : stripTerminalControls(issue.message);
1253
1275
  }
1254
1276
  function formatZodErrors(error) {
1255
1277
  return error.issues.map(formatZodIssue);
@@ -6076,17 +6098,6 @@ async function extractAllSkills(skillsDir) {
6076
6098
 
6077
6099
  // src/cli/lib/matrix/skill-resolution.ts
6078
6100
  import { unique as unique5, uniqueBy as uniqueBy3 } from "remeda";
6079
-
6080
- // src/cli/utils/string.ts
6081
- function truncateText(text, maxLength) {
6082
- if (text.length <= maxLength) return text;
6083
- return text.slice(0, maxLength - 1) + "\u2026";
6084
- }
6085
- function toTitleCase(kebabCase) {
6086
- return kebabCase.split("-").map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
6087
- }
6088
-
6089
- // src/cli/lib/matrix/skill-resolution.ts
6090
6101
  var AUTO_SYNTH_ORDER = 999;
6091
6102
  function warnOnDomainDisagreement(skill, decidedBy) {
6092
6103
  if (skill.domain === decidedBy.domain) return;
@@ -6290,7 +6301,7 @@ function buildResolvedSkill(skill, _categories, relationships, slugMap) {
6290
6301
  // src/cli/lib/matrix/matrix-resolver.ts
6291
6302
  import { groupBy as groupBy4 } from "remeda";
6292
6303
  function getLabel(skill) {
6293
- return skill.displayName;
6304
+ return stripTerminalControls(skill.displayName);
6294
6305
  }
6295
6306
  function joinWithConjunction(items, conjunction) {
6296
6307
  if (items.length <= 1) return items[0] ?? "";
@@ -6310,7 +6321,11 @@ function toSelectionFacts(source) {
6310
6321
  conflictsWith: skill.conflictsWith.map((relation) => relation.skillId),
6311
6322
  discourages: skill.discourages.map((relation) => ({
6312
6323
  skillId: relation.skillId,
6313
- reason: relation.reason
6324
+ // The one author string that leaves this module without being re-rendered: the shared
6325
+ // semantics carry it through and `getCellState` hands the verdict back whole, so a
6326
+ // discouraged cell prints exactly what was put in here. Made inert on the way in, which
6327
+ // is the only point on that route this package owns.
6328
+ reason: stripTerminalControls(relation.reason)
6314
6329
  })),
6315
6330
  requires: skill.requires.map((requirement) => ({
6316
6331
  skillIds: requirement.skillIds,
@@ -6366,10 +6381,10 @@ function getUnmetRequiredBy(skillId, currentSelections) {
6366
6381
  if (!req.skillIds.includes(skillId)) continue;
6367
6382
  if (req.needsAny) {
6368
6383
  if (!req.skillIds.some((id) => selectedSet.has(id))) {
6369
- return selectedSkill.displayName;
6384
+ return getLabel(selectedSkill);
6370
6385
  }
6371
6386
  } else {
6372
- return selectedSkill.displayName;
6387
+ return getLabel(selectedSkill);
6373
6388
  }
6374
6389
  }
6375
6390
  }
@@ -6392,7 +6407,7 @@ function getUnmetRequirementsReason(skillId, currentSelections) {
6392
6407
  if (missing.length === 0) continue;
6393
6408
  const names = missing.map((id) => {
6394
6409
  const s = matrix.skills[id];
6395
- return s ? getLabel(s) : id;
6410
+ return s ? getLabel(s) : stripTerminalControls(id);
6396
6411
  });
6397
6412
  return withAuthorsReason(namesTheMissing(names, requirement.needsAny), requirement.reason);
6398
6413
  }
@@ -6422,7 +6437,10 @@ function validateConflicts(resolvedSelections) {
6422
6437
  if (conflict) {
6423
6438
  errors.push({
6424
6439
  type: "conflict",
6425
- message: `${getLabel(skillA)} conflicts with ${getLabel(getSkillById(skillBId))}: ${conflict.reason}`,
6440
+ // The author's own sentence, made inert but not shortened: this message is reported on
6441
+ // its own line rather than into a fixed-height cell, so it needs no budget the way
6442
+ // `withAuthorsReason` does — only the guarantee that a terminal will print it.
6443
+ message: `${getLabel(skillA)} conflicts with ${getLabel(getSkillById(skillBId))}: ${stripTerminalControls(conflict.reason)}`,
6426
6444
  skills: [skillA.id, skillBId]
6427
6445
  });
6428
6446
  }
@@ -6471,7 +6489,7 @@ function validateExclusivity(resolvedSelections) {
6471
6489
  if (category?.exclusive) {
6472
6490
  errors.push({
6473
6491
  type: "categoryExclusive",
6474
- message: `Category "${category.displayName}" only allows one selection, but multiple selected: ${skillIds.map((id) => getLabel(getSkillById(id))).join(", ")}`,
6492
+ message: `Category "${stripTerminalControls(category.displayName)}" only allows one selection, but multiple selected: ${skillIds.map((id) => getLabel(getSkillById(id))).join(", ")}`,
6475
6493
  skills: skillIds
6476
6494
  });
6477
6495
  }
@@ -8519,6 +8537,9 @@ function mergeLocalSkillsIntoMatrix(matrix2, localResult) {
8519
8537
  }
8520
8538
 
8521
8539
  export {
8540
+ stripTerminalControls,
8541
+ truncateText,
8542
+ toTitleCase,
8522
8543
  getErrorMessage,
8523
8544
  setVerbose,
8524
8545
  verbose,
@@ -8699,8 +8720,6 @@ export {
8699
8720
  unresolvedSkillRemovalReasons,
8700
8721
  deleteLocalSkill,
8701
8722
  migrateLocalSkillScope,
8702
- truncateText,
8703
- toTitleCase,
8704
8723
  claimSlug,
8705
8724
  getUnmetRequiredBy,
8706
8725
  getCellState,
@@ -8731,4 +8750,4 @@ export {
8731
8750
  detectMigrations,
8732
8751
  executeMigration
8733
8752
  };
8734
- //# sourceMappingURL=chunk-7WKNPDN2.js.map
8753
+ //# sourceMappingURL=chunk-UNASGGW2.js.map