@theokit/sdk 5.0.0-next.1 → 5.0.0-next.2

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 (79) hide show
  1. package/CHANGELOG.md +162 -0
  2. package/dist/{agent-CgNzshyV.d.cts → agent-C3KO5LKm.d.cts} +49 -3
  3. package/dist/{agent-CdYJfKr6.d.ts → agent-CvmTR9uf.d.ts} +49 -3
  4. package/dist/{agent-2E7DI47Y.cjs → agent-GPV2JWZG.cjs} +7 -7
  5. package/dist/{agent-2E7DI47Y.cjs.map → agent-GPV2JWZG.cjs.map} +1 -1
  6. package/dist/{agent-XMKRP27S.js → agent-Y24I5JBM.js} +6 -6
  7. package/dist/{agent-XMKRP27S.js.map → agent-Y24I5JBM.js.map} +1 -1
  8. package/dist/{chunk-XCCGNU3D.js → chunk-4S5TWFIZ.js} +5 -5
  9. package/dist/chunk-4S5TWFIZ.js.map +1 -0
  10. package/dist/{chunk-JTZ74HFD.js → chunk-67ADYSTW.js} +29 -9
  11. package/dist/chunk-67ADYSTW.js.map +1 -0
  12. package/dist/{chunk-CEILSTWB.cjs → chunk-GGZ5GPE3.cjs} +29 -8
  13. package/dist/chunk-GGZ5GPE3.cjs.map +1 -0
  14. package/dist/{chunk-HWSALKOR.cjs → chunk-H6YM3PV6.cjs} +5 -5
  15. package/dist/chunk-H6YM3PV6.cjs.map +1 -0
  16. package/dist/{chunk-NCGWJ3HS.js → chunk-KDR53EO7.js} +71 -32
  17. package/dist/chunk-KDR53EO7.js.map +1 -0
  18. package/dist/{chunk-WMH2MSOP.cjs → chunk-QUAEOKDW.cjs} +95 -56
  19. package/dist/chunk-QUAEOKDW.cjs.map +1 -0
  20. package/dist/{chunk-G4ZGYKJG.cjs → chunk-SYVWE64W.cjs} +6 -6
  21. package/dist/chunk-SYVWE64W.cjs.map +1 -0
  22. package/dist/{chunk-7ZK4DJD7.cjs → chunk-VXDEKILY.cjs} +4 -4
  23. package/dist/{chunk-7ZK4DJD7.cjs.map → chunk-VXDEKILY.cjs.map} +1 -1
  24. package/dist/{chunk-IJM7UGS5.js → chunk-XJZEBGXD.js} +5 -5
  25. package/dist/chunk-XJZEBGXD.js.map +1 -0
  26. package/dist/{chunk-6HIACV34.js → chunk-ZH26RNQA.js} +3 -3
  27. package/dist/{chunk-6HIACV34.js.map → chunk-ZH26RNQA.js.map} +1 -1
  28. package/dist/{context-74SBFD27.cjs → context-NI33FS2R.cjs} +7 -7
  29. package/dist/{context-74SBFD27.cjs.map → context-NI33FS2R.cjs.map} +1 -1
  30. package/dist/context-Y7B7SI5X.js +6 -0
  31. package/dist/{context-NZ2AC6S7.js.map → context-Y7B7SI5X.js.map} +1 -1
  32. package/dist/{cron-Bw6mSgD4.d.cts → cron-C3PhZED0.d.cts} +1 -1
  33. package/dist/{cron-DJv9oXSZ.d.ts → cron-crfMMdAw.d.ts} +1 -1
  34. package/dist/cron.cjs +6 -6
  35. package/dist/cron.d.cts +3 -3
  36. package/dist/cron.d.ts +3 -3
  37. package/dist/cron.js +5 -5
  38. package/dist/eval.cjs +5 -5
  39. package/dist/eval.js +4 -4
  40. package/dist/index.cjs +20 -20
  41. package/dist/index.d.cts +5 -5
  42. package/dist/index.d.ts +5 -5
  43. package/dist/index.js +7 -7
  44. package/dist/internal/persistence/index.cjs +4 -4
  45. package/dist/internal/persistence/index.js +1 -1
  46. package/dist/internal/persistence/paths.d.cts +25 -12
  47. package/dist/internal/persistence/paths.d.ts +25 -12
  48. package/dist/internal/runtime/compat/compat-config-file.d.ts +31 -0
  49. package/dist/internal/runtime/compat/foreign-config-sources.d.ts +31 -1
  50. package/dist/internal/runtime/hooks/hooks-executor.d.ts +2 -1
  51. package/dist/internal/runtime/plugin-loader/plugin-bundles.d.ts +2 -1
  52. package/dist/{sdk-agent-Dxjb0IfM.d.cts → sdk-agent-BOiKqOgL.d.cts} +28 -1
  53. package/dist/{sdk-agent-ldzpyAaF.d.ts → sdk-agent-D4a_BR_6.d.ts} +28 -1
  54. package/dist/subagents-loader-TPZLTXYZ.cjs +16 -0
  55. package/dist/{subagents-loader-YWFE7KYE.cjs.map → subagents-loader-TPZLTXYZ.cjs.map} +1 -1
  56. package/dist/subagents-loader-Y6JFYXAY.js +7 -0
  57. package/dist/{subagents-loader-LSKM2UIK.js.map → subagents-loader-Y6JFYXAY.js.map} +1 -1
  58. package/dist/subagents-loader.cjs +3 -3
  59. package/dist/subagents-loader.d.cts +2 -2
  60. package/dist/subagents-loader.d.ts +2 -2
  61. package/dist/subagents-loader.js +2 -2
  62. package/dist/types/agent.d.ts +47 -1
  63. package/dist/types/sdk-agent.d.ts +28 -1
  64. package/dist/workflow.d.cts +1 -1
  65. package/dist/workflow.d.ts +1 -1
  66. package/docs/error-codes.md +21 -21
  67. package/docs/harness-capability-map.md +3 -1
  68. package/package.json +1 -1
  69. package/dist/chunk-CEILSTWB.cjs.map +0 -1
  70. package/dist/chunk-G4ZGYKJG.cjs.map +0 -1
  71. package/dist/chunk-HWSALKOR.cjs.map +0 -1
  72. package/dist/chunk-IJM7UGS5.js.map +0 -1
  73. package/dist/chunk-JTZ74HFD.js.map +0 -1
  74. package/dist/chunk-NCGWJ3HS.js.map +0 -1
  75. package/dist/chunk-WMH2MSOP.cjs.map +0 -1
  76. package/dist/chunk-XCCGNU3D.js.map +0 -1
  77. package/dist/context-NZ2AC6S7.js +0 -6
  78. package/dist/subagents-loader-LSKM2UIK.js +0 -7
  79. package/dist/subagents-loader-YWFE7KYE.cjs +0 -16
@@ -30,6 +30,21 @@ function adaptersFor(kinds) {
30
30
  }
31
31
  return out;
32
32
  }
33
+ var COMPAT_SURFACES = ["hooks", "plugins", "skills", "subagents"];
34
+ function adaptersForSurface(sources, surface) {
35
+ const admitted = [];
36
+ for (const source of sources) {
37
+ if (typeof source === "string") {
38
+ admitted.push(source);
39
+ continue;
40
+ }
41
+ const wanted = source.import ?? [];
42
+ if (wanted.some((s) => s === surface && COMPAT_SURFACES.includes(s))) {
43
+ admitted.push(source.kind);
44
+ }
45
+ }
46
+ return adaptersFor(admitted);
47
+ }
33
48
  function adapterForConfigPath(path) {
34
49
  for (const segment of path.split(/[\\/]/)) {
35
50
  const adapter = BY_DIR_NAME.get(segment);
@@ -53,7 +68,9 @@ function undefinedVariablesIn(command, supplied, env = process.env) {
53
68
  }
54
69
  var reported = /* @__PURE__ */ new Set();
55
70
  function reportUndeclaredSources(cwd, declared) {
56
- const declaredKinds = new Set(adaptersFor(declared).map((a) => a.kind));
71
+ const declaredKinds = new Set(
72
+ adaptersFor(declared.map((d) => typeof d === "string" ? d : d.kind)).map((a) => a.kind)
73
+ );
57
74
  for (const adapter of FOREIGN_SOURCES) {
58
75
  if (declaredKinds.has(adapter.kind)) continue;
59
76
  const dir = path.join(cwd, adapter.dirName);
@@ -75,14 +92,17 @@ function getTheokitHome(cwd) {
75
92
  }
76
93
  return path.join(cwd, THEOKIT_DIR_LITERAL);
77
94
  }
78
- function projectConfigRoots(cwd, sources = []) {
95
+ function theokitConfigRoot(cwd) {
96
+ return path.join(cwd, THEOKIT_DIR_LITERAL);
97
+ }
98
+ function projectConfigRoots(cwd, sources, surface) {
79
99
  return [
80
- path.join(cwd, THEOKIT_DIR_LITERAL),
81
- ...adaptersFor(sources).map((adapter) => path.join(cwd, adapter.dirName))
100
+ theokitConfigRoot(cwd),
101
+ ...adaptersForSurface(sources, surface).map((adapter) => path.join(cwd, adapter.dirName))
82
102
  ];
83
103
  }
84
- function pluginBundleRoots(cwd, sources = []) {
85
- return projectConfigRoots(cwd, sources).map((root) => path.join(root, "plugins"));
104
+ function pluginBundleRoots(cwd, sources) {
105
+ return projectConfigRoots(cwd, sources, "plugins").map((root) => path.join(root, "plugins"));
86
106
  }
87
107
  function getProfilesRoot() {
88
108
  return path.join(os.homedir(), THEOKIT_DIR_LITERAL, "profiles");
@@ -104,6 +124,7 @@ exports.getTheokitHome = getTheokitHome;
104
124
  exports.pluginBundleRoots = pluginBundleRoots;
105
125
  exports.projectConfigRoots = projectConfigRoots;
106
126
  exports.reportUndeclaredSources = reportUndeclaredSources;
127
+ exports.theokitConfigRoot = theokitConfigRoot;
107
128
  exports.undefinedVariablesIn = undefinedVariablesIn;
108
- //# sourceMappingURL=chunk-CEILSTWB.cjs.map
109
- //# sourceMappingURL=chunk-CEILSTWB.cjs.map
129
+ //# sourceMappingURL=chunk-GGZ5GPE3.cjs.map
130
+ //# sourceMappingURL=chunk-GGZ5GPE3.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/internal/runtime/compat/foreign-config-sources.ts","../src/internal/persistence/paths.ts"],"names":["join","existsSync","diag","homedir"],"mappings":"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","file":"chunk-GGZ5GPE3.cjs","sourcesContent":["import { existsSync } from \"node:fs\";\nimport { join } from \"node:path\";\n\nimport { diag } from \"../../diagnostics.js\";\n\n/*\n * The foreign configuration dialects this SDK can read, and what each one PRESUMES.\n *\n * ## Why a registry and not a list of directory names\n *\n * `projectConfigRoots` returned `[\".theokit\", \".claude\"]` — two paths — and that shape is what\n * usetheokit/theokit-sdk#522 fell through. A path says WHERE a file lives. It does not say how the\n * file is parsed, and it does not say what runtime the commands inside it were written against.\n *\n * Claude Code defines `$CLAUDE_PROJECT_DIR` for the hook commands in its `settings.json`, and its\n * documentation tells authors to reach project files through it — an absolute path would break for\n * every other person on the team, so the shape that failed here is the shape upstream recommends.\n * This SDK read the file and ran the command without the variable. `sh` expands an unset variable to\n * the empty string, so\n *\n * bash \"$CLAUDE_PROJECT_DIR/.claude/hooks/guard.sh\" became bash \"/.claude/hooks/guard.sh\"\n *\n * which does not exist, which a hook runner correctly reads as a refusal. Every turn denied, in any\n * repository that also had Claude Code set up, with a message naming a file that was present and\n * executable all along.\n *\n * Importing a format means accepting the contract that format presumes. An adapter is where that\n * contract is written down, so the next dialect (`.codex/` is the obvious one) declares its own\n * instead of inheriting a hole.\n *\n * ## What an adapter deliberately does NOT do\n *\n * It does not make the foreign source trusted, and it does not make its hooks permissive: a script\n * that exits non-zero is still a refusal. It supplies the variables the format's authors were\n * entitled to assume, and nothing else — `env` here is merged over the scrubbed inherit policy by\n * `spawnAndCollect`, so it adds names rather than widening what a child can see.\n *\n * @internal\n */\n\n/**\n * The project config directory literal.\n *\n * Renamed from `THEOKIT_DIR_NAME` in #410. Sharing a name with the (now removed) sovereign env var\n * was the MECHANISM of that defect, not scenery: every grep for the variable landed on that const\n * and looked answered, so \"is it read?\" returned five hits and nobody checked what they were.\n *\n * Lives here rather than in `persistence/paths.ts` because a directory name is one third of what a\n * dialect is — the other two being how it parses and what it presumes — and splitting the three\n * across two modules is what let the third go unwritten.\n */\nexport const THEOKIT_DIR_LITERAL = \".theokit\";\n\n/** The Claude Code CLI's project configuration directory. */\nexport const CLAUDE_DIR_NAME = \".claude\";\n\n/** A configuration dialect this SDK understands. `theokit` is native; the rest are foreign. */\nexport interface ConfigSourceAdapter {\n /** Stable identifier, and what a consumer names to opt in. */\n readonly kind: string;\n /** The project-relative directory the dialect keeps its configuration in. */\n readonly dirName: string;\n /**\n * Variables the dialect's own runtime defines for commands it executes.\n *\n * Empty for the native source: a `.theokit/` hook is written against THIS runtime and inherits it\n * already. Non-empty is what makes a foreign command runnable rather than silently broken.\n */\n runtimeEnv(cwd: string): Record<string, string>;\n}\n\n/** The native source. Always read, never opted into, always first for precedence. */\nexport const NATIVE_SOURCE: ConfigSourceAdapter = {\n kind: \"theokit\",\n dirName: THEOKIT_DIR_LITERAL,\n runtimeEnv: () => ({}),\n};\n\n/**\n * Claude Code.\n *\n * `CLAUDE_PROJECT_DIR` is the documented way for a hook command in `settings.json` to reach a file\n * in the project. Only that one variable is supplied: `$CLAUDE_PLUGIN_ROOT` and the rest of that\n * runtime's surface are NOT defined here, because supplying a name whose value this SDK would have\n * to invent is worse than leaving it unset — an invented root sends a script somewhere real and\n * wrong, where an unset one fails loudly.\n */\nexport const CLAUDE_CODE_SOURCE: ConfigSourceAdapter = {\n kind: \"claude-code\",\n dirName: CLAUDE_DIR_NAME,\n runtimeEnv: (cwd) => ({ CLAUDE_PROJECT_DIR: cwd }),\n};\n\nconst FOREIGN_SOURCES: readonly ConfigSourceAdapter[] = [CLAUDE_CODE_SOURCE];\n\nconst BY_DIR_NAME: ReadonlyMap<string, ConfigSourceAdapter> = new Map(\n [NATIVE_SOURCE, ...FOREIGN_SOURCES].map((a) => [a.dirName, a]),\n);\n\n/**\n * The adapters a caller declared, in declaration order, skipping any name that names no adapter.\n *\n * An unknown name is DROPPED rather than turned into `<cwd>/<name>`: a typo must fail closed. Making\n * a directory out of an unrecognised string would import a dialect nothing knows how to parse — and\n * the whole reason this exists is that a directory name was never enough to describe a dialect.\n */\nexport function adaptersFor(kinds: readonly string[]): ConfigSourceAdapter[] {\n const byKind = new Map(FOREIGN_SOURCES.map((a) => [a.kind, a]));\n const out: ConfigSourceAdapter[] = [];\n for (const kind of kinds) {\n const adapter = byKind.get(kind);\n if (adapter !== undefined && !out.includes(adapter)) out.push(adapter);\n }\n return out;\n}\n\n/**\n * A surface a foreign source may be admitted to. The four the SDK reads a project directory for.\n *\n * They are listed separately because they carry very different risk, which is the whole reason\n * #524 asks for per-surface control: a skill is text that enters the system prompt, a hook is\n * command execution, a plugin is code loading. A consumer who wants their skills back has no\n * reason to be handed the other two along with them.\n */\nexport type CompatSurface = \"hooks\" | \"plugins\" | \"skills\" | \"subagents\";\n\nconst COMPAT_SURFACES: readonly CompatSurface[] = [\"hooks\", \"plugins\", \"skills\", \"subagents\"];\n\n/**\n * A declared foreign source: a bare kind, or a kind with the surfaces it may be read for.\n */\nexport type CompatSourceDeclaration =\n | string\n | { readonly kind: string; readonly import?: readonly string[] };\n\n/**\n * The adapters admitted to ONE surface.\n *\n * Three rules, and each one fails closed:\n *\n * - A bare string admits every surface. It is what `5.0.0-next.1` published, so narrowing it\n * silently would turn a working opt-in into a no-op — the exact defect #524 is about, one level\n * up.\n * - An object with no `import` admits nothing. The issue's own rule, and safe to apply strictly\n * because the object form is new and nobody can be depending on it.\n * - An unrecognised surface name is dropped rather than matched loosely, for the same reason an\n * unrecognised KIND is dropped in {@link adaptersFor}: a typo must not silently widen access.\n */\nexport function adaptersForSurface(\n sources: readonly CompatSourceDeclaration[],\n surface: CompatSurface,\n): ConfigSourceAdapter[] {\n const admitted: string[] = [];\n for (const source of sources) {\n if (typeof source === \"string\") {\n admitted.push(source);\n continue;\n }\n const wanted = source.import ?? [];\n if (wanted.some((s) => s === surface && COMPAT_SURFACES.includes(s as CompatSurface))) {\n admitted.push(source.kind);\n }\n }\n return adaptersFor(admitted);\n}\n\n/**\n * The adapter whose directory an absolute config path sits under, or `undefined` for a path that\n * belongs to no registered dialect.\n *\n * Matched on the path SEGMENT rather than with `includes`, so a workspace that happens to live under\n * `/home/me/.claude-backups/repo` does not read as a Claude Code source.\n */\nexport function adapterForConfigPath(path: string): ConfigSourceAdapter | undefined {\n for (const segment of path.split(/[\\\\/]/)) {\n const adapter = BY_DIR_NAME.get(segment);\n if (adapter !== undefined) return adapter;\n }\n return undefined;\n}\n\n/**\n * Variable references in a shell command that nothing will define.\n *\n * The second half of #522, and the half that cost the debugging session. `sh` expands an unset\n * variable to the empty string and says nothing, so the failure surfaces ten characters later as a\n * path: `bash: /.claude/hooks/guard.sh: No such file or directory` — which reads as \"your script is\n * missing\" while the script is present and executable. Nothing in that message contains the name of\n * the variable that was actually missing, so the reader looks in the wrong place.\n *\n * Checked against BOTH the process environment and the variables the dialect supplies, because\n * either is a legitimate source: a hook may reasonably use `$HOME`.\n *\n * ## What it deliberately does not try to be\n *\n * This is not a shell parser. It finds `$NAME` and `${NAME}` outside single quotes, which is the\n * shape a config file's hook commands take. It does NOT understand `${NAME:-default}` (a default\n * makes the variable optional, so it is not reported), assignments earlier in the same command, or\n * variables a sourced script exports. A false NEGATIVE there costs the old behaviour — the confusing\n * path error — and a false positive would deny a hook that would have worked, so the parse errs\n * toward silence and the check only ever ADDS a name to a failure that already happened.\n */\nexport function undefinedVariablesIn(\n command: string,\n supplied: Readonly<Record<string, string>>,\n env: Readonly<Record<string, string | undefined>> = process.env,\n): string[] {\n // Single-quoted spans are literal in `sh`: `echo '$FOO'` prints the dollar sign.\n const unquoted = command.replace(/'[^']*'/g, \" \");\n const names = new Set<string>();\n for (const match of unquoted.matchAll(\n /\\$\\{([A-Za-z_][A-Za-z0-9_]*)\\}|\\$([A-Za-z_][A-Za-z0-9_]*)/g,\n )) {\n const name = match[1] ?? match[2];\n if (name === undefined) continue;\n if (name in supplied) continue;\n if (env[name] !== undefined) continue;\n names.add(name);\n }\n return [...names];\n}\n\n/**\n * Workspaces already reported, so repeated agent construction in one process says it once.\n *\n * Keyed by the resolved directory rather than by dialect kind, so a long-lived host that drives\n * several workspaces still reports each of them.\n */\nconst reported = new Set<string>();\n\n/**\n * Reports a foreign configuration directory that exists in the workspace and was not declared.\n *\n * ## Why the flip needs a voice\n *\n * Before #524 a `.claude/` was read with no opt-in; after it, the same directory is ignored. From\n * inside the repository the two states are indistinguishable — the hook file is there, it is\n * executable, and it does not run. The only remaining way to learn why is a CHANGELOG entry for a\n * version the reader may not know they crossed.\n *\n * ## Why `diag` rather than `diagFailure`\n *\n * `diagFailure` falls back to stderr, and this is not a failure: ignoring an undeclared foreign\n * directory is precisely what #524 asked for. Every repository that has Claude Code set up and does\n * NOT want it imported would pay a stderr line at every agent start — on a TUI host's render\n * surface — for behaving as instructed. That is the corruption `diagnostics.ts` exists to prevent.\n *\n * So it goes on the interceptable channel, for the reader holding the question it answers.\n */\nexport function reportUndeclaredSources(\n cwd: string,\n declared: readonly CompatSourceDeclaration[],\n): void {\n // A kind named with a NARROW import list has still been declared: the consumer knows the\n // directory is there and chose which surfaces to admit. Warning them anyway would be the noise\n // that gets a warning ignored, and this one has exactly one job — telling somebody who does NOT\n // know the directory is being skipped.\n const declaredKinds = new Set(\n adaptersFor(declared.map((d) => (typeof d === \"string\" ? d : d.kind))).map((a) => a.kind),\n );\n for (const adapter of FOREIGN_SOURCES) {\n if (declaredKinds.has(adapter.kind)) continue;\n const dir = join(cwd, adapter.dirName);\n if (!existsSync(dir)) continue;\n if (reported.has(dir)) continue;\n reported.add(dir);\n diag(\n `[theokit] ${adapter.dirName}/ is present but not declared, so its hooks, skills, subagents ` +\n `and plugins are ignored. To read it, pass ` +\n `local: { compatSources: [\"${adapter.kind}\"] } (usetheokit/theokit-sdk#524).\\n`,\n );\n }\n}\n","/**\n * Path resolution for SDK state files (ADR D60).\n *\n * Theokit anchors state at `<cwd>/.theokit/` by default (per-cwd). An\n * optional `THEOKIT_HOME` environment variable overrides this, enabling\n * test isolation, profile switching, and multi-tenant deployments.\n *\n * Rules:\n * - `getTheokitHome(cwd)` is the canonical resolver **for cwd-anchored state**. Never hardcode\n * `path.join(cwd, \".theokit\")` in callers — use this function so tests\n * and overrides stay consistent.\n *\n * M94 — this comment said \"the ONLY canonical resolver\", and stopped being true: the\n * transcript gained `transcriptRoot()`, which is **home-anchored** (`~/.theokit`) with the same\n * `THEOKIT_HOME` override. The two defaults differ on purpose — unifying would move the\n * transcript of everyone who does NOT set the variable, which is a data migration and not a\n * re-export.\n *\n * A consequence worth writing down: **without `THEOKIT_HOME` the state stays split in two**\n * — registry in `<cwd>/.theokit`, transcript in `~/.theokit`. M94 unifies only for those who set\n * the variable. Unifying both defaults is another milestone's work.\n * - `getProfilesRoot()` is intentionally home-anchored (not affected by\n * `THEOKIT_HOME`) so `theokit profile list` discovers all profiles\n * regardless of which is active.\n * - `displayTheokitHome(cwd)` returns a human-readable path for logs.\n *\n * @internal\n */\n\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\n\nimport {\n adaptersForSurface,\n type CompatSourceDeclaration,\n type CompatSurface,\n THEOKIT_DIR_LITERAL,\n} from \"../runtime/compat/foreign-config-sources.js\";\n\n// The directory names live with the dialect registry that owns them — a name is one third of what a\n// configuration dialect is, and keeping the three together is what stops the next one shipping\n// without its runtime contract (#522).\n\n/**\n * Resolve the directory cwd-anchored SDK state lives in.\n *\n * `THEOKIT_HOME` wins when it is set and not blank after trimming; the trimmed value is used, and\n * it is used VERBATIM — it is not resolved against `cwd`, so a relative value stays relative and\n * `.theokit` is not appended to it. Otherwise the answer is `<cwd>/.theokit`.\n *\n * The environment is read on every call, so a change to the variable takes effect immediately\n * rather than being frozen at import.\n *\n * This creates nothing and checks nothing: the returned path may not exist, and the caller owns\n * the `mkdir`. Call it instead of writing `join(cwd, \".theokit\")` by hand, or the override stops\n * working for that one call site and tests silently touch the real home.\n *\n * Not the whole story about where state lives — the transcript is home-anchored via\n * `transcriptRoot()`, honoring the same variable but defaulting to `~/.theokit`. With\n * `THEOKIT_HOME` unset, state is genuinely split between two roots.\n *\n * Semver-exempt: reachable via the `@theokit/sdk/internal/persistence` sub-path, which the package\n * declares in `exports` but does NOT cover with its semver contract.\n */\nexport function getTheokitHome(cwd: string): string {\n const override = process.env.THEOKIT_HOME?.trim();\n if (override !== undefined && override.length > 0) {\n return override;\n }\n return join(cwd, THEOKIT_DIR_LITERAL);\n}\n\n/**\n * The project's own configuration root: `<cwd>/.theokit`, always — never `THEOKIT_HOME`.\n *\n * `THEOKIT_HOME` relocates cwd-anchored SDK STATE (sessions, credentials). A project's\n * CONFIGURATION belongs to the repository: hooks, MCP servers, context sources, subagents, the\n * personality a project declares, all committed to git and shared by a team. Following the\n * override for any of them would move where a project's declared capabilities come from — a\n * behaviour change wearing the costume of a refactor, which is exactly what this function exists\n * to make impossible to do by accident: every config-class reader calls this instead of writing\n * `join(cwd, \".theokit\")` by hand.\n *\n * NOT for the `.claude/`-style foreign roots {@link adaptersForSurface} adds — those are additive,\n * opt-in, and each has its own directory name.\n *\n * Semver-exempt: reachable via the `@theokit/sdk/internal/persistence` sub-path, which the package\n * declares in `exports` but does NOT cover with its semver contract.\n */\nexport function theokitConfigRoot(cwd: string): string {\n return join(cwd, THEOKIT_DIR_LITERAL);\n}\n\n/**\n * Every directory a project's configuration may be read from, in precedence order.\n *\n * `.theokit` first — via {@link theokitConfigRoot}, so it is NEVER affected by `THEOKIT_HOME` for\n * the reason documented there — then `.claude`. The order is the whole contract: a project that\n * declares a skill, agent or rule in both means the explicit namespace to win, and a caller merging\n * these roots must therefore keep the FIRST occurrence of a name rather than the last.\n *\n * `.claude` is read because the formats already agree and only the location did not. Measured\n * 2026-08-26: the SKILL.md frontmatter this SDK requires (`name` + `description`) is exactly what\n * the CLI writes, its hook config is the same JSON shape, and 59 of the CLI's agent declarations\n * parse here unchanged. A repository set up for the CLI was failing on the directory name alone.\n *\n * NOT a rename of `.theokit`, and not a migration. Both are read, so nothing that works today stops\n * working — which is why this returns a LIST and not a single resolved answer.\n *\n * Creates nothing and checks nothing; either path may not exist, and the caller owns that.\n *\n * Semver-exempt: reachable via the `@theokit/sdk/internal/persistence` sub-path, which the package\n * declares in `exports` but does NOT cover with its semver contract.\n */\nexport function projectConfigRoots(\n cwd: string,\n sources: readonly CompatSourceDeclaration[],\n surface: CompatSurface,\n): string[] {\n return [\n theokitConfigRoot(cwd),\n ...adaptersForSurface(sources, surface).map((adapter) => join(cwd, adapter.dirName)),\n ];\n}\n\n/**\n * Every directory that may hold a plugin BUNDLE contributed by the Claude Code CLI.\n *\n * A CLI plugin is not a JS entry point — it is a folder whose `skills/` and `agents/` are what it\n * exists to provide. Measured 2026-08-26 on an installed one: seven agents and three skills beside\n * a manifest in `.claude-plugin/plugin.json`. Parsing that manifest and stopping there produced a\n * plugin that loaded and did nothing.\n *\n * Project-scoped deliberately. The CLI also keeps plugins under `~/.claude/plugins/cache`, behind\n * its own installer and enable/disable state — reproducing that is an installation system, not\n * reading a project's configuration, and guessing at someone's enablement would run code they\n * turned off.\n */\nexport function pluginBundleRoots(\n cwd: string,\n sources: readonly CompatSourceDeclaration[],\n): string[] {\n // Always the `plugins` surface, including when the caller wants the SKILLS a bundle carries.\n // A bundle is code, and its skills arrive attached to it: admitting `skills` alone must not\n // reach inside a foreign plugin directory, or the narrower permission would silently grant the\n // wider one. `skills-manager` and `subagents-loader` both read bundle contents and both go\n // through here, so the rule holds in one place rather than three.\n return projectConfigRoots(cwd, sources, \"plugins\").map((root) => join(root, \"plugins\"));\n}\n\n/**\n * The directory holding every profile: always `~/.theokit/profiles`, from `os.homedir()`.\n *\n * Deliberately NOT affected by `THEOKIT_HOME`, which is the one thing to remember about it. If it\n * followed the override, a session pointed at one profile would only be able to see that profile,\n * and `theokit profile list` could never enumerate the rest. Profiles are the thing the override\n * switches between, so their index cannot live behind it.\n *\n * Takes no `cwd` for the same reason. Creates nothing; the path may not exist.\n *\n * Semver-exempt: reachable via the `@theokit/sdk/internal/persistence` sub-path, which the package\n * declares in `exports` but does NOT cover with its semver contract.\n */\nexport function getProfilesRoot(): string {\n return join(homedir(), THEOKIT_DIR_LITERAL, \"profiles\");\n}\n\n/**\n * The same path `getTheokitHome(cwd)` returns, shortened for display: the home directory prefix\n * collapses to `~`, so `/home/ada/.theokit` prints as `~/.theokit`.\n *\n * For humans only — log lines, CLI output, error messages. The result is NOT a usable path: `~`\n * is a shell convention that `fs` does not expand, so passing this to a filesystem call resolves\n * a literal directory named `~` relative to the process cwd. Use `getTheokitHome` for anything\n * that touches disk.\n *\n * Collapsing is a prefix match on the home directory followed by a literal `/`, so a sibling like\n * `/home/adalovelace` is left alone even though `/home/ada` is a string prefix of it. A path\n * outside the home directory comes back unchanged — and so does a Windows path, where the\n * separator is a backslash and the prefix test therefore never matches.\n *\n * Semver-exempt: reachable via the `@theokit/sdk/internal/persistence` sub-path, which the package\n * declares in `exports` but does NOT cover with its semver contract.\n */\nexport function displayTheokitHome(cwd: string): string {\n const resolved = getTheokitHome(cwd);\n const home = homedir();\n if (resolved === home) return \"~\";\n if (resolved.startsWith(`${home}/`)) {\n return `~${resolved.slice(home.length)}`;\n }\n return resolved;\n}\n"]}
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var chunkCEILSTWB_cjs = require('./chunk-CEILSTWB.cjs');
3
+ var chunkGGZ5GPE3_cjs = require('./chunk-GGZ5GPE3.cjs');
4
4
  var chunkJ7J7J2GN_cjs = require('./chunk-J7J7J2GN.cjs');
5
5
  var chunk6LHQPOMI_cjs = require('./chunk-6LHQPOMI.cjs');
6
6
  var async_hooks = require('async_hooks');
@@ -29,7 +29,7 @@ async function loadHookConfig(cwd, compatSources = []) {
29
29
  sawAny = true;
30
30
  mergeInto(merged, stampSource(await readHookFile(path), path));
31
31
  }
32
- if (!sawAny && fs.existsSync(path.join(cwd, ".theokit", "hooks"))) {
32
+ if (!sawAny && fs.existsSync(path.join(chunkGGZ5GPE3_cjs.theokitConfigRoot(cwd), "hooks"))) {
33
33
  warnOnce(
34
34
  "hooks-md-unsupported",
35
35
  "[theokit-sdk] .theokit/hooks/*.md hooks are no longer supported (ADR 0016) \u2014 migrate to a Claude-Code-shaped .theokit/hooks.json"
@@ -38,7 +38,7 @@ async function loadHookConfig(cwd, compatSources = []) {
38
38
  return merged;
39
39
  }
40
40
  function hookConfigCandidates(cwd, compatSources) {
41
- const roots = chunkCEILSTWB_cjs.projectConfigRoots(cwd, compatSources);
41
+ const roots = chunkGGZ5GPE3_cjs.projectConfigRoots(cwd, compatSources, "hooks");
42
42
  return [
43
43
  ...roots.map((root) => path.join(root, "hooks.json")),
44
44
  ...roots.map((root) => path.join(root, "settings.json")),
@@ -168,5 +168,5 @@ exports.loadHookConfig = loadHookConfig;
168
168
  exports.warnOnce = warnOnce;
169
169
  exports.warnPersonalitySwitchInsideFork = warnPersonalitySwitchInsideFork;
170
170
  exports.withPersonalityContext = withPersonalityContext;
171
- //# sourceMappingURL=chunk-HWSALKOR.cjs.map
172
- //# sourceMappingURL=chunk-HWSALKOR.cjs.map
171
+ //# sourceMappingURL=chunk-H6YM3PV6.cjs.map
172
+ //# sourceMappingURL=chunk-H6YM3PV6.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/internal/runtime/hooks/hooks-source.ts","../src/internal/personality/context.ts"],"names":["diag","existsSync","join","theokitConfigRoot","projectConfigRoots","readFile","ConfigurationError","AsyncLocalStorage"],"mappings":"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","file":"chunk-H6YM3PV6.cjs","sourcesContent":["/**\n * Single source of truth for loading the hooks config (ADR 0016 — reverses\n * D74/D77 for hooks: JSON is canonical again, in the Claude Code shape).\n *\n * `.theokit/hooks.json` (Claude-Code-shaped JSON) is the only supported form.\n * A stray legacy `.theokit/hooks/*.md` dir (no hooks.json) is NOT loaded — it\n * warns to migrate and yields no hooks. Absent both → empty config.\n *\n * Consumed by `hooks-executor.ts` (runtime dispatch).\n *\n * Config shape (identical to Claude Code's `settings.json` hooks):\n * { \"hooks\": { \"PreToolUse\": [ { \"matcher\": \"shell\",\n * \"hooks\": [ { \"type\": \"command\", \"command\": \"…\", \"timeout\": 30 } ] } ] } }\n *\n * @internal\n */\n\nimport { existsSync } from \"node:fs\";\nimport { readFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { ConfigurationError } from \"../../../errors.js\";\nimport { diag } from \"../../diagnostics.js\";\nimport { projectConfigRoots, theokitConfigRoot } from \"../../persistence/paths.js\";\nimport type { CompatSourceDeclaration } from \"../compat/foreign-config-sources.js\";\n\n/** The five lifecycle events the SDK runtime actually fires. */\nexport type HookEvent = \"preRun\" | \"postRun\" | \"preToolUse\" | \"postToolUse\" | \"stop\";\n\n/**\n * Claude Code event name → the SDK firing event. Only events the runtime\n * genuinely emits are mapped; a Claude Code event with no SDK firing point\n * (SessionStart / SubagentStop / PreCompact / Notification / SessionEnd) is\n * skipped with a warn rather than silently accepted (it would never run).\n */\nconst CLAUDE_CODE_EVENT_MAP: Readonly<Record<string, HookEvent>> = {\n PreToolUse: \"preToolUse\",\n PostToolUse: \"postToolUse\",\n UserPromptSubmit: \"preRun\",\n Stop: \"stop\",\n};\n\nexport interface HookCommand {\n command: string;\n matcher?: string;\n timeoutMs?: number;\n /**\n * The config file this command was declared in.\n *\n * Carried so the executor can supply the runtime contract the declaring DIALECT presumes — a\n * command from `.claude/settings.json` is written against Claude Code's runtime and expects\n * `$CLAUDE_PROJECT_DIR` to exist (#522). Absent for a command built in memory, which is native by\n * construction.\n */\n sourcePath?: string;\n}\n\nexport interface HookConfig {\n hooks?: Partial<Record<HookEvent, HookCommand[]>>;\n}\n\nconst warned = new Set<string>();\n\n/**\n * Emit a stderr warn once per process per unique key. Helps surface the\n * deprecation path without spamming when the loader is called many times\n * during a session (cron + send + skills all hit this).\n *\n * Note: spawned workers (cron, subagent) start fresh processes — warn\n * re-emits there, by design (1 per process boot, not per call).\n *\n * @internal\n */\nexport function warnOnce(key: string, message: string): void {\n if (warned.has(key)) return;\n warned.add(key);\n diag(`${message}\\n`);\n}\n\n/** Reset for tests; not exported via barrel. @internal */\nexport function _resetWarnOnceForTests(): void {\n warned.clear();\n}\n\n/**\n * Load hooks from `.theokit/hooks.json` (Claude-Code-shaped — the only supported\n * form). A stray legacy `.theokit/hooks/*.md` markdown dir (no `hooks.json`) is\n * NOT loaded — it emits a one-time migration warn and yields no hooks.\n *\n * @internal\n */\nexport async function loadHookConfig(\n cwd: string,\n compatSources: readonly CompatSourceDeclaration[] = [],\n): Promise<HookConfig> {\n const merged: HookConfig = {};\n let sawAny = false;\n for (const path of hookConfigCandidates(cwd, compatSources)) {\n if (!existsSync(path)) continue;\n sawAny = true;\n // Stamped at merge, where the file is still known. One line later the commands are pooled per\n // event and every trace of which dialect declared them is gone — which is how a Claude Code\n // command came to be run without Claude Code's runtime (#522).\n mergeInto(merged, stampSource(await readHookFile(path), path));\n }\n if (!sawAny && existsSync(join(theokitConfigRoot(cwd), \"hooks\"))) {\n warnOnce(\n \"hooks-md-unsupported\",\n \"[theokit-sdk] .theokit/hooks/*.md hooks are no longer supported (ADR 0016) — migrate to a Claude-Code-shaped .theokit/hooks.json\",\n );\n }\n return merged;\n}\n\n/**\n * Every file that may declare hooks, in precedence order.\n *\n * `hooks.json` under each project config root, then the Claude Code CLI's own settings files — which\n * is where the CLI actually keeps hooks, so a repository set up for it presents its hooks here\n * without being converted. `settings.local.json` is the CLI's personal-override file and sits beside\n * the shared one rather than replacing it.\n *\n * The shape never needed translating: `parseClaudeCodeConfig` reads the `hooks` key off whatever\n * object it is given, and a settings file is that same object with other keys alongside.\n */\nfunction hookConfigCandidates(\n cwd: string,\n compatSources: readonly CompatSourceDeclaration[],\n): string[] {\n const roots = projectConfigRoots(cwd, compatSources, \"hooks\");\n return [\n ...roots.map((root) => join(root, \"hooks.json\")),\n ...roots.map((root) => join(root, \"settings.json\")),\n ...roots.map((root) => join(root, \"settings.local.json\")),\n ];\n}\n\n/**\n * Record which file each command came from.\n *\n * A command already carrying a `sourcePath` keeps it: nothing produces that today, and a nested\n * config that declared its own origin would be describing something this function cannot see.\n */\nfunction stampSource(config: HookConfig, sourcePath: string): HookConfig {\n if (config.hooks === undefined) return config;\n const hooks: NonNullable<HookConfig[\"hooks\"]> = {};\n for (const [event, commands] of Object.entries(config.hooks) as [\n HookEvent,\n HookCommand[] | undefined,\n ][]) {\n if (commands === undefined) continue;\n hooks[event] = commands.map((c) => ({ sourcePath, ...c }));\n }\n return { hooks };\n}\n\n/**\n * Append one source's commands onto the accumulator, per event.\n *\n * MERGED, not first-wins, and the distinction is deliberate. An agent or a skill is a NAMED\n * declaration: two files claiming one name collide, and the explicit namespace should win. Hooks are\n * unnamed lists — two files declaring `PreToolUse` are two sets of commands an operator wrote, and\n * keeping only one drops the other in silence, which is the failure class this package guards\n * against everywhere else.\n */\nfunction mergeInto(target: HookConfig, source: HookConfig): void {\n for (const [event, commands] of Object.entries(source.hooks ?? {}) as [\n HookEvent,\n HookCommand[] | undefined,\n ][]) {\n if (commands === undefined || commands.length === 0) continue;\n target.hooks ??= {};\n target.hooks[event] = [...(target.hooks[event] ?? []), ...commands];\n }\n}\n\nasync function readHookFile(jsonPath: string): Promise<HookConfig> {\n let raw: string;\n try {\n raw = await readFile(jsonPath, \"utf8\");\n } catch (cause) {\n throw new ConfigurationError(`Failed to read hooks config: ${jsonPath}`, {\n code: \"hooks_read_error\",\n cause,\n });\n }\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch (cause) {\n throw new ConfigurationError(`Invalid JSON in hooks config: ${jsonPath}`, {\n code: \"hooks_json_invalid\",\n cause,\n });\n }\n return parseClaudeCodeConfig(parsed, jsonPath);\n}\n\n/** Narrow an unknown to a record, or throw a typed config error. */\nfunction asRecord(value: unknown, path: string, where: string): Record<string, unknown> {\n if (typeof value !== \"object\" || value === null || Array.isArray(value)) {\n throw new ConfigurationError(`hooks: expected an object at ${where} in ${path}`, {\n code: \"hooks_json_invalid\",\n });\n }\n return value as Record<string, unknown>;\n}\n\n/** Narrow an unknown to an array, or throw a typed config error. */\nfunction asArray(value: unknown, path: string, where: string): unknown[] {\n if (!Array.isArray(value)) {\n throw new ConfigurationError(`hooks: expected an array at ${where} in ${path}`, {\n code: \"hooks_json_invalid\",\n });\n }\n return value;\n}\n\n/**\n * Parse Claude Code's nested hooks config into the SDK's flat internal shape:\n * `{ hooks: { PreToolUse: [{ matcher?, hooks: [{ type:\"command\", command, timeout? }] }] } }`\n * → `{ hooks: { preToolUse: [{ command, matcher?, timeoutMs? }] } }`. Each group's\n * `matcher` applies to every command it wraps; `timeout` (seconds) → `timeoutMs`.\n */\nfunction parseClaudeCodeConfig(raw: unknown, path: string): HookConfig {\n const root = asRecord(raw, path, \"the root\");\n if (root.hooks === undefined) return {};\n const hooksRec = asRecord(root.hooks, path, `\"hooks\"`);\n const grouped: Partial<Record<HookEvent, HookCommand[]>> = {};\n\n for (const [ccEvent, groups] of Object.entries(hooksRec)) {\n const event = CLAUDE_CODE_EVENT_MAP[ccEvent];\n if (event === undefined) {\n warnOnce(\n `hooks-event-${ccEvent}`,\n `[theokit-sdk] hooks: event \"${ccEvent}\" is not fired by the SDK runtime (supported: ${Object.keys(CLAUDE_CODE_EVENT_MAP).join(\", \")}) — skipping`,\n );\n continue;\n }\n grouped[event] = [...(grouped[event] ?? []), ...flattenEventGroups(groups, path, ccEvent)];\n }\n return { hooks: grouped };\n}\n\n/** Flatten one Claude Code event's matcher-groups into internal HookCommands. */\nfunction flattenEventGroups(groups: unknown, path: string, ccEvent: string): HookCommand[] {\n const commands: HookCommand[] = [];\n for (const rawGroup of asArray(groups, path, `hooks.${ccEvent}`)) {\n const group = asRecord(rawGroup, path, `hooks.${ccEvent}[]`);\n const matcher = group.matcher === undefined ? undefined : String(group.matcher);\n for (const rawCmd of asArray(group.hooks, path, `hooks.${ccEvent}[].hooks`)) {\n commands.push(parseClaudeCodeCommand(rawCmd, matcher, path, ccEvent));\n }\n }\n return commands;\n}\n\n/** One `{ type:\"command\", command, timeout? }` entry → an internal HookCommand. */\nfunction parseClaudeCodeCommand(\n raw: unknown,\n matcher: string | undefined,\n path: string,\n ccEvent: string,\n): HookCommand {\n const cmd = asRecord(raw, path, `hooks.${ccEvent}[].hooks[]`);\n if (cmd.type !== \"command\") {\n throw new ConfigurationError(\n `hooks: only { \"type\": \"command\" } is supported (got ${JSON.stringify(cmd.type)}) in ${path}`,\n { code: \"hooks_unsupported_type\" },\n );\n }\n if (typeof cmd.command !== \"string\" || cmd.command.length === 0) {\n throw new ConfigurationError(`hooks: \"command\" must be a non-empty string in ${path}`, {\n code: \"hooks_invalid_command\",\n });\n }\n const hc: HookCommand = { command: cmd.command };\n if (matcher !== undefined) hc.matcher = matcher;\n if (typeof cmd.timeout === \"number\" && cmd.timeout > 0) {\n hc.timeoutMs = Math.round(cmd.timeout * 1000);\n }\n return hc;\n}\n","/**\n * Personality fork-context (ADR D168 + EC-A snapshot semantic).\n *\n * Uses Node's `AsyncLocalStorage` so a fork's execution chain can know\n * that it is running inside a fork AND can see the slug that was active\n * on the parent **at fork-construction time**.\n *\n * **EC-A:** The slug stored here is captured ONCE at the wrap site\n * (`localAgentFork`) — passing `parentStore.active(parentAgentId)`\n * returns a primitive `string | undefined`, which is then frozen\n * inside the ALS context object. Subsequent `usePersonality` calls on\n * the parent do NOT mutate the fork's view, because the fork reads from\n * its own ALS frame, not from the parent's store.\n *\n * @internal\n */\n\nimport { AsyncLocalStorage } from \"node:async_hooks\";\n\nimport { warnOnce } from \"../runtime/hooks/hooks-source.js\";\n\n/**\n * Snapshot data carried into a fork's async context.\n *\n * @internal\n */\nexport interface PersonalityForkContext {\n /** Parent's active personality slug at fork-construction time. */\n readonly slug: string | undefined;\n /** Always `true` inside this scope (used by guards). */\n readonly isFork: true;\n}\n\nconst storage = new AsyncLocalStorage<PersonalityForkContext>();\n\n/**\n * Run `fn` with `ctx` bound as the active fork context. Nested calls\n * shadow the outer context (EC-22).\n *\n * @internal\n */\nexport function withPersonalityContext<T>(\n ctx: PersonalityForkContext,\n fn: () => Promise<T>,\n): Promise<T> {\n return storage.run(ctx, fn);\n}\n\n/**\n * Return the active fork context, or `undefined` when called outside a\n * fork scope.\n *\n * @internal\n */\nexport function currentPersonalityContext(): PersonalityForkContext | undefined {\n return storage.getStore();\n}\n\n/**\n * Emit one warning per agentId stating that personality switches inside\n * a fork are no-ops. The fork inherits the parent snapshot — runtime\n * mutation is intentionally rejected to keep fork voice deterministic.\n *\n * @internal\n */\nexport function warnPersonalitySwitchInsideFork(agentId: string): void {\n warnOnce(\n `personality-switch-in-fork-${agentId}`,\n `[theokit-sdk] usePersonality is a no-op inside a fork (D168). Subagents inherit the parent's active personality at fork-construction time.`,\n );\n}\n"]}
@@ -1,7 +1,7 @@
1
1
  import { submit, emitRunEvent } from './chunk-K2VMFZQ5.js';
2
2
  import { enqueueSessionWrite, hydrateSession, persistTurnToTranscript, flushSessionWrites, appendSessionMessage, getSessionMessages, discardSession, clearSession } from './chunk-AWINL3ZC.js';
3
3
  import { IndexManager } from './chunk-2ZLVT7H3.js';
4
- import { loadHookConfig, warnOnce, currentPersonalityContext, warnPersonalitySwitchInsideFork } from './chunk-IJM7UGS5.js';
4
+ import { loadHookConfig, warnOnce, currentPersonalityContext, warnPersonalitySwitchInsideFork } from './chunk-XJZEBGXD.js';
5
5
  import { extractCodePlugins } from './chunk-2SFBB54R.js';
6
6
  import { createOpenAiCompatibleRuntime } from './chunk-55GB6JYQ.js';
7
7
  import { persistActiveMemoryTranscript } from './chunk-OR6XIWLB.js';
@@ -24,8 +24,8 @@ import { expandTilde, defaultBaseDir } from './chunk-JOFVLOFY.js';
24
24
  import { withFileLock } from './chunk-TA3K7SBK.js';
25
25
  import { withCwdMutex } from './chunk-Q5EWJPRY.js';
26
26
  import { sweepStaleAtomicTemps, atomicWriteJson } from './chunk-3JHIFQ4I.js';
27
- import { loadSubagents, pluginBundleDirs } from './chunk-XCCGNU3D.js';
28
- import { reportUndeclaredSources, undefinedVariablesIn, getTheokitHome, adapterForConfigPath, projectConfigRoots, pluginBundleRoots } from './chunk-JTZ74HFD.js';
27
+ import { loadSubagents, pluginBundleDirs } from './chunk-4S5TWFIZ.js';
28
+ import { reportUndeclaredSources, theokitConfigRoot, undefinedVariablesIn, getTheokitHome, adapterForConfigPath, projectConfigRoots, pluginBundleRoots } from './chunk-67ADYSTW.js';
29
29
  import { readWorkspaceDir, parseSimpleYaml } from './chunk-JNAA4G4H.js';
30
30
  import { resolveEffectiveContextWindow, CONTEXT_WINDOW_FLOOR, CONTEXT_WINDOW_MARGIN } from './chunk-6M2OIS4Y.js';
31
31
  import { parseModelId } from './chunk-R7WIIPUR.js';
@@ -36,11 +36,11 @@ import { AgentRunError, coerceToKnownAgentRunErrorCode, UnknownAgentError, Confi
36
36
  import { diag, resolveApiKey, API_KEY_ENV_VAR, redactSecrets, diagFailure } from './chunk-CZJ6Q7CW.js';
37
37
  import { stat, mkdir, readFile, readdir, access, unlink } from 'fs/promises';
38
38
  import { randomUUID, createHash } from 'crypto';
39
- import { join, dirname, resolve, isAbsolute } from 'path';
39
+ import { join, dirname, resolve, relative, isAbsolute } from 'path';
40
+ import { existsSync, readFileSync } from 'fs';
40
41
  import { spawn } from 'child_process';
41
42
  import { z } from 'zod';
42
43
  import { createRequire } from 'module';
43
- import { existsSync } from 'fs';
44
44
  import { homedir } from 'os';
45
45
 
46
46
  // src/agent-builder.ts
@@ -269,7 +269,7 @@ function enabledPluginNames(plugins) {
269
269
  // src/internal/runtime/registry/agent-registry-store.ts
270
270
  var SCHEMA_VERSION = 1;
271
271
  var LEGACY_SCHEMA_VERSION_STRING = "1.0";
272
- var REGISTRY_RELATIVE_PATH = join(".theokit", "agents", "registry.json");
272
+ var REGISTRY_RELATIVE_SUFFIX = join("agents", "registry.json");
273
273
  function stripSecretsFromOptions(options) {
274
274
  return assignDefined({
275
275
  name: options.name,
@@ -379,7 +379,7 @@ function serializeAgents(agents2) {
379
379
  return out;
380
380
  }
381
381
  function registryPath(cwd) {
382
- return join(cwd, REGISTRY_RELATIVE_PATH);
382
+ return join(theokitConfigRoot(cwd), REGISTRY_RELATIVE_SUFFIX);
383
383
  }
384
384
  async function loadRegistry(cwd) {
385
385
  void sweepStaleAtomicTemps(registryPath(cwd)).catch(() => void 0);
@@ -2518,6 +2518,37 @@ function removeAll(arr, toRemove) {
2518
2518
  if (idx !== -1) arr.splice(idx, 1);
2519
2519
  }
2520
2520
  }
2521
+ function readCompatConfigFile(cwd) {
2522
+ const path = join(theokitConfigRoot(cwd), "config.json");
2523
+ if (!existsSync(path)) return [];
2524
+ let raw;
2525
+ try {
2526
+ raw = readFileSync(path, "utf8");
2527
+ } catch {
2528
+ return [];
2529
+ }
2530
+ let parsed;
2531
+ try {
2532
+ parsed = JSON.parse(raw);
2533
+ } catch (cause) {
2534
+ diag(
2535
+ `[theokit] ${path} is not valid JSON \u2014 ignoring its "compat.adapters" (${cause.message}).
2536
+ `
2537
+ );
2538
+ return [];
2539
+ }
2540
+ const adapters = parsed.compat?.adapters;
2541
+ if (adapters === void 0) return [];
2542
+ if (!Array.isArray(adapters)) {
2543
+ diag(`[theokit] ${path}: "compat.adapters" must be an array \u2014 ignoring it.
2544
+ `);
2545
+ return [];
2546
+ }
2547
+ return adapters;
2548
+ }
2549
+ function resolveCompatSources(options, cwd) {
2550
+ return options.local?.compatSources ?? readCompatConfigFile(cwd);
2551
+ }
2521
2552
  function spawnAndCollect(options) {
2522
2553
  return new Promise((resolve) => {
2523
2554
  const timeoutMs = options.timeoutMs ?? 3e4;
@@ -2818,7 +2849,9 @@ var SkillsManager = class {
2818
2849
  }
2819
2850
  async refresh() {
2820
2851
  const roots = this.skillsDir === void 0 ? [
2821
- ...projectConfigRoots(this.cwd, this.compatSources).map((root) => join(root, "skills")),
2852
+ ...projectConfigRoots(this.cwd, this.compatSources, "skills").map(
2853
+ (root) => join(root, "skills")
2854
+ ),
2822
2855
  ...(await pluginBundleDirs(this.cwd, this.compatSources)).map((b) => join(b, "skills"))
2823
2856
  ] : [this.skillsDir];
2824
2857
  const discovered = [];
@@ -2963,7 +2996,7 @@ async function resolveSendSkills(inputs, userText, memoryFacts) {
2963
2996
  inputs.settingSourcesIncludeProject,
2964
2997
  settings.skillsDir,
2965
2998
  settings.inline,
2966
- inputs.options.local?.compatSources ?? []
2999
+ resolveCompatSources(inputs.options, inputs.workspaceCwd)
2967
3000
  );
2968
3001
  await manager.initialize();
2969
3002
  return { manager, autoInject: settings.autoInject ?? true };
@@ -4100,8 +4133,8 @@ var FileContextManager = class {
4100
4133
  }
4101
4134
  };
4102
4135
  async function loadContextConfig(cwd) {
4103
- const mdDir = join(cwd, ".theokit", "context");
4104
- const jsonPath = join(cwd, ".theokit", "context.json");
4136
+ const mdDir = join(theokitConfigRoot(cwd), "context");
4137
+ const jsonPath = join(theokitConfigRoot(cwd), "context.json");
4105
4138
  const mdEntities = await loadMarkdownEntities({
4106
4139
  dir: mdDir,
4107
4140
  schema: ContextSourceFrontmatterSchema,
@@ -4273,7 +4306,7 @@ var PluginsManager = class {
4273
4306
  const folderName = entry.name;
4274
4307
  const cliPath = join(pluginsRoot, folderName, ".claude-plugin", "plugin.json");
4275
4308
  if (existsSync(cliPath)) {
4276
- const cliMetadata = await loadPluginManifestFromJson(cliPath, folderName);
4309
+ const cliMetadata = await loadPluginManifestFromJson(this.cwd, cliPath, folderName);
4277
4310
  if (this.enabled === void 0 || this.enabled.includes(cliMetadata.name)) {
4278
4311
  this.plugins.push(cliMetadata);
4279
4312
  }
@@ -4281,7 +4314,7 @@ var PluginsManager = class {
4281
4314
  }
4282
4315
  const mdPath = join(pluginsRoot, folderName, "PLUGIN.md");
4283
4316
  const jsonPath = join(pluginsRoot, folderName, "plugin.json");
4284
- const metadata = existsSync(mdPath) ? await loadPluginManifestFromMarkdown(pluginsRoot, folderName) : await loadPluginManifestFromJson(jsonPath, folderName);
4317
+ const metadata = existsSync(mdPath) ? await loadPluginManifestFromMarkdown(this.cwd, pluginsRoot, folderName) : await loadPluginManifestFromJson(this.cwd, jsonPath, folderName);
4285
4318
  if (existsSync(mdPath) && existsSync(jsonPath)) {
4286
4319
  warnOnce(
4287
4320
  `plugin-${folderName}-both`,
@@ -4296,7 +4329,7 @@ var PluginsManager = class {
4296
4329
  );
4297
4330
  }
4298
4331
  if (this.enabled === void 0 || this.enabled.includes(metadata.name)) {
4299
- await this.assertEntryFileExists(metadata, folderName);
4332
+ await this.assertEntryFileExists(metadata, folderName, pluginsRoot);
4300
4333
  this.plugins.push(metadata);
4301
4334
  }
4302
4335
  }
@@ -4304,10 +4337,10 @@ var PluginsManager = class {
4304
4337
  list() {
4305
4338
  return Promise.resolve(this.plugins);
4306
4339
  }
4307
- async assertEntryFileExists(metadata, folderName) {
4340
+ async assertEntryFileExists(metadata, folderName, pluginsRoot) {
4308
4341
  const entry = metadata.entry;
4309
4342
  if (entry === void 0) return;
4310
- const pluginRoot = join(this.cwd, ".theokit", "plugins", folderName);
4343
+ const pluginRoot = join(pluginsRoot, folderName);
4311
4344
  const entryPath = safePathJoin(pluginRoot, entry);
4312
4345
  try {
4313
4346
  await readFile(entryPath, "utf8");
@@ -4343,7 +4376,7 @@ var PluginsManager = class {
4343
4376
  }
4344
4377
  }
4345
4378
  };
4346
- async function loadPluginManifestFromJson(manifestPath, folderName) {
4379
+ async function loadPluginManifestFromJson(cwd, manifestPath, folderName) {
4347
4380
  let raw;
4348
4381
  try {
4349
4382
  raw = await readFile(manifestPath, "utf8");
@@ -4371,12 +4404,12 @@ async function loadPluginManifestFromJson(manifestPath, folderName) {
4371
4404
  const name = typeof record.name === "string" ? record.name : folderName;
4372
4405
  const version = typeof record.version === "string" ? record.version : "0.0.0";
4373
4406
  const capabilities = Array.isArray(record.capabilities) ? record.capabilities.filter((cap) => typeof cap === "string") : [];
4374
- const source = manifestPath.slice(manifestPath.indexOf(".theokit/"));
4407
+ const source = relative(cwd, manifestPath);
4375
4408
  const metadata = { name, version, capabilities, source };
4376
4409
  if (typeof record.entry === "string") metadata.entry = record.entry;
4377
4410
  return metadata;
4378
4411
  }
4379
- async function loadPluginManifestFromMarkdown(pluginsRoot, folderName) {
4412
+ async function loadPluginManifestFromMarkdown(cwd, pluginsRoot, folderName) {
4380
4413
  const mdPath = join(pluginsRoot, folderName, "PLUGIN.md");
4381
4414
  let raw;
4382
4415
  try {
@@ -4402,7 +4435,7 @@ async function loadPluginManifestFromMarkdown(pluginsRoot, folderName) {
4402
4435
  });
4403
4436
  }
4404
4437
  const fm = entity.frontmatter;
4405
- const source = mdPath.slice(mdPath.indexOf(".theokit/"));
4438
+ const source = relative(cwd, mdPath);
4406
4439
  const metadata = {
4407
4440
  name: fm.name ?? folderName,
4408
4441
  version: fm.version ?? "0.0.0",
@@ -4458,14 +4491,20 @@ function buildSkills(args, out) {
4458
4491
  staticSkills?.inline,
4459
4492
  // #524 — the same declaration the per-send manager reads. Omitting it here would make
4460
4493
  // `agent.skills` disagree with the system prompt about which skills exist.
4461
- args.options.local?.compatSources ?? []
4494
+ resolveCompatSources(args.options, args.workspaceCwd)
4462
4495
  );
4463
4496
  const localSkills = out.skillsManager;
4464
4497
  out.skills = {
4465
4498
  // Project to the public shape (name + description only). Inline skills carry
4466
4499
  // their body + references on the object; `list()` must never leak them —
4467
4500
  // the body is reachable exclusively through `get()`.
4468
- list: async () => (await localSkills.list()).map((s) => ({ name: s.name, description: s.description })),
4501
+ list: async () => (await localSkills.list()).map((s) => ({
4502
+ name: s.name,
4503
+ description: s.description,
4504
+ // The PATH, never the body. Dropping it with the body answered a question nobody asked and
4505
+ // silenced the one #524 is about: which root did this skill come from.
4506
+ ...s.source === void 0 ? {} : { source: s.source }
4507
+ })),
4469
4508
  get: (name) => localSkills.get(name)
4470
4509
  };
4471
4510
  }
@@ -4479,7 +4518,7 @@ function buildPlugins(args, out) {
4479
4518
  args.settingSourcesIncludePlugins,
4480
4519
  false,
4481
4520
  void 0,
4482
- args.options.local?.compatSources ?? []
4521
+ resolveCompatSources(args.options, args.workspaceCwd)
4483
4522
  );
4484
4523
  const localPlugins = out.pluginsManager;
4485
4524
  out.plugins = { list: () => localPlugins.list() };
@@ -7610,7 +7649,7 @@ async function dispatchLocalRun(args) {
7610
7649
  });
7611
7650
  }
7612
7651
  async function readProjectMcpServers(cwd) {
7613
- const path = join(cwd, ".theokit", "mcp.json");
7652
+ const path = join(theokitConfigRoot(cwd), "mcp.json");
7614
7653
  try {
7615
7654
  const raw = await readFile(path, "utf8");
7616
7655
  const parsed = JSON.parse(raw);
@@ -8871,7 +8910,7 @@ var PersonalityFrontmatterSchema = z.object({
8871
8910
  var RESERVED_CLEAR_SLUGS = ["none", "default", "neutral"];
8872
8911
 
8873
8912
  // src/internal/personality/registry.ts
8874
- var PROJECT_SUBDIR = ".theokit/personalities";
8913
+ var PERSONALITIES_SUFFIX = "personalities";
8875
8914
  var USER_SUBDIR = ".theokit/personalities";
8876
8915
  var PersonalityRegistry = class _PersonalityRegistry {
8877
8916
  #byName;
@@ -8886,7 +8925,7 @@ var PersonalityRegistry = class _PersonalityRegistry {
8886
8925
  * @internal
8887
8926
  */
8888
8927
  static async load(cwd) {
8889
- const projectDir = join(cwd, PROJECT_SUBDIR);
8928
+ const projectDir = join(theokitConfigRoot(cwd), PERSONALITIES_SUFFIX);
8890
8929
  const userDir = join(homedir(), USER_SUBDIR);
8891
8930
  const [userEntities, projectEntities] = await Promise.all([
8892
8931
  loadMarkdownEntities({
@@ -9070,7 +9109,7 @@ async function* localAgentStreamToCompletion(agent, message, options) {
9070
9109
  async function localAgentFork(parent, options) {
9071
9110
  const { forkAgentImpl } = await import('./fork-agent-S2EPIML7.js');
9072
9111
  const { getAgentFacade } = await import('./agent-factory-registry-ZVTABLEY.js');
9073
- const { withPersonalityContext } = await import('./context-NZ2AC6S7.js');
9112
+ const { withPersonalityContext } = await import('./context-Y7B7SI5X.js');
9074
9113
  const create = getAgentFacade().create;
9075
9114
  return withPersonalityContext(
9076
9115
  { slug: parent.personalitySlugSnapshot, isFork: true },
@@ -9861,7 +9900,7 @@ var LocalAgent = class {
9861
9900
  this.pluginsManager = sub.pluginsManager;
9862
9901
  if (sub.plugins !== void 0) this.plugins = sub.plugins;
9863
9902
  reportUnknownLocalOptions(options.local);
9864
- const compatSources = options.local?.compatSources ?? [];
9903
+ const compatSources = resolveCompatSources(options, this.workspaceCwd);
9865
9904
  reportUndeclaredSources(this.workspaceCwd, compatSources);
9866
9905
  this.hooksExecutor = new HooksExecutor(this.workspaceCwd, compatSources);
9867
9906
  this.defaultMemoryProviderForLoop = createLocalAgentMemoryProvider({
@@ -9894,7 +9933,7 @@ var LocalAgent = class {
9894
9933
  this.workspaceCwd,
9895
9934
  this.settingSourcesIncludeProject,
9896
9935
  this.options.agents,
9897
- this.options.local?.compatSources ?? []
9936
+ resolveCompatSources(this.options, this.workspaceCwd)
9898
9937
  );
9899
9938
  await acquireLeaseIfPossible(this.sessionStore, this.agentId);
9900
9939
  try {
@@ -10843,7 +10882,7 @@ var Agent = class _Agent {
10843
10882
  */
10844
10883
  static async describe(agentId) {
10845
10884
  const agent = await getRegisteredAgentOrThrow(agentId);
10846
- const { loadSubagents: loadSubagents2 } = await import('./subagents-loader-LSKM2UIK.js');
10885
+ const { loadSubagents: loadSubagents2 } = await import('./subagents-loader-Y6JFYXAY.js');
10847
10886
  const settingSources = agent.options.local?.settingSources;
10848
10887
  const subagents = await loadSubagents2(
10849
10888
  agent.cwd ?? process.cwd(),
@@ -10931,5 +10970,5 @@ async function openLocalStore(reg) {
10931
10970
  }
10932
10971
 
10933
10972
  export { Agent, AgentBuilder, DEFAULT_AGENTIC_MODEL_ID, MEMORY_EMBEDDING_ADAPTERS, Tool, ToolError, UsageAccumulator, computeCost, generateCronId, getPricingEntry, openRouterMemoryEmbeddingProviderAdapter };
10934
- //# sourceMappingURL=chunk-NCGWJ3HS.js.map
10935
- //# sourceMappingURL=chunk-NCGWJ3HS.js.map
10973
+ //# sourceMappingURL=chunk-KDR53EO7.js.map
10974
+ //# sourceMappingURL=chunk-KDR53EO7.js.map