@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
@@ -17,10 +17,27 @@ import type { Run, SDKUserMessage, SendOptions } from "./run.js";
17
17
  export interface SystemPromptSkillRef {
18
18
  name: string;
19
19
  description: string;
20
+ /**
21
+ * Where this skill came from: the absolute path to its `SKILL.md` for a skill read from disk,
22
+ * or the synthetic `inline://<name>` marker `createSkill` stamps on a code-defined one (see
23
+ * `create-skill.ts`) — there is no third case, so this is populated for every skill `list()`
24
+ * returns.
25
+ *
26
+ * It is the ORIGIN and NOT the body: `list()` stays lean by contract, and a skill's instructions
27
+ * are reachable only through {@link SDKAgentSkills.get}. What this adds is the answer to
28
+ * usetheokit/theokit-sdk#524's visibility question — which root a skill came from, so a listing
29
+ * can show that one arrived from `.claude/skills/` rather than the project's own directory, or
30
+ * that it was never on disk at all.
31
+ *
32
+ * Typed optional rather than required because {@link SystemPromptSkillRef} is the shape any
33
+ * future skill producer returns, and this SDK should not assert every one of them can name an
34
+ * origin.
35
+ */
36
+ source?: string;
20
37
  }
21
38
  /**
22
39
  * A skill resolved WITH its body, returned by {@link SDKAgentSkills.get}. Unlike
23
- * {@link SystemPromptSkillRef} (name + description only), this carries the full
40
+ * {@link SystemPromptSkillRef}, which carries metadata only, this carries the full
24
41
  * `instructions` — read from the SKILL.md for filesystem skills or the inline
25
42
  * `createSkill` body. @public
26
43
  */
@@ -64,6 +81,16 @@ export interface SDKAgentSkills {
64
81
  export interface SDKPluginMetadata {
65
82
  name: string;
66
83
  description?: string;
84
+ /**
85
+ * Absolute path the plugin manifest was read from.
86
+ *
87
+ * The runtime has ALWAYS returned this — `agent.plugins.list()` hands back the internal
88
+ * `PluginMetadata`, whose own docblock says it carries provenance "so callers can audit where the
89
+ * plugin came from". This type simply did not declare it, so the caller received the field and
90
+ * the compiler denied it existed. Declaring it is what makes the audit usetheokit/theokit-sdk#524
91
+ * asks for reachable in typed code.
92
+ */
93
+ source?: string;
67
94
  }
68
95
  /**
69
96
  * Public plugin listing handle exposed as `agent.plugins`. Populated when
@@ -1,5 +1,5 @@
1
1
  import { ZodType, z } from 'zod';
2
- import { S as SDKAgent } from './sdk-agent-Dxjb0IfM.cjs';
2
+ import { S as SDKAgent } from './sdk-agent-BOiKqOgL.cjs';
3
3
  import { t as MessageOrigin, C as CustomTool } from './run-CTAdRU3U.cjs';
4
4
  import { T as TheokitAgentError } from './errors-base-B5g01neU.cjs';
5
5
 
@@ -1,5 +1,5 @@
1
1
  import { ZodType, z } from 'zod';
2
- import { S as SDKAgent } from './sdk-agent-ldzpyAaF.js';
2
+ import { S as SDKAgent } from './sdk-agent-D4a_BR_6.js';
3
3
  import { t as MessageOrigin, C as CustomTool } from './run-CTAdRU3U.js';
4
4
  import { T as TheokitAgentError } from './errors-base-B5g01neU.js';
5
5
 
@@ -40,18 +40,18 @@ Branch on `code`, never on the message: messages carry context (an id, a path, a
40
40
  | `cloud_custom_tools_rejected` | domain | ConfigurationError | `packages/sdk/src/internal/cloud-agent/cloud-agent.ts:150` +1 |
41
41
  | `cloud_incompatible_function_resolver` | domain | ConfigurationError | `packages/sdk/src/internal/cloud-agent/cloud-tool-parity.ts:43` +2 |
42
42
  | `cloud_incompatible_mcp_stdio_local` | domain | ConfigurationError | `packages/sdk/src/internal/cloud-agent/cloud-tool-parity.ts:74` |
43
- | `cloud_plugin_path_rejected` | domain | ConfigurationError | `packages/sdk/src/internal/runtime/plugin-loader/plugins-manager.ts:149` +1 |
43
+ | `cloud_plugin_path_rejected` | domain | ConfigurationError | `packages/sdk/src/internal/runtime/plugin-loader/plugins-manager.ts:159` +1 |
44
44
  | `cloud_run_http_error` | domain | NetworkError | `packages/sdk/src/internal/cloud-agent/real-cloud-run.ts:155` |
45
45
  | `cloud_run_unknown_status` | domain | NetworkError | `packages/sdk/src/internal/cloud-agent/real-cloud-run.ts:284` +1 |
46
46
  | `cloud_runtime_pre_release` | domain | ConfigurationError | `packages/sdk/src/agent.ts:417` +2 |
47
47
  | `cloud_stdio_cwd_rejected` | domain | ConfigurationError | `packages/sdk/src/internal/runtime/validation/validate-agent-options.ts:121` |
48
48
  | `compression_failed` | domain | — | `packages/sdk/src/internal/runtime/compression/compression-summarizer.ts:41` |
49
49
  | `compression_model_unresolved` | domain | — | `packages/sdk/src/internal/runtime/compression/compression-model-registry.ts:102` +1 |
50
- | `context_config_shape` | domain | ConfigurationError | `packages/sdk/src/internal/runtime/context/context-manager.ts:255` |
50
+ | `context_config_shape` | domain | ConfigurationError | `packages/sdk/src/internal/runtime/context/context-manager.ts:256` |
51
51
  | `context_frontmatter_invalid` | domain | ConfigurationError | `packages/sdk/src/internal/runtime/context/context-frontmatter.ts:38` |
52
- | `context_json_invalid` | domain | ConfigurationError | `packages/sdk/src/internal/runtime/context/context-manager.ts:249` |
53
- | `context_read_error` | domain | ConfigurationError | `packages/sdk/src/internal/runtime/context/context-manager.ts:236` |
54
- | `context_sources_shape` | domain | ConfigurationError | `packages/sdk/src/internal/runtime/context/context-manager.ts:276` |
52
+ | `context_json_invalid` | domain | ConfigurationError | `packages/sdk/src/internal/runtime/context/context-manager.ts:250` |
53
+ | `context_read_error` | domain | ConfigurationError | `packages/sdk/src/internal/runtime/context/context-manager.ts:237` |
54
+ | `context_sources_shape` | domain | ConfigurationError | `packages/sdk/src/internal/runtime/context/context-manager.ts:277` |
55
55
  | `credential_pool_ambiguous` | domain | ConfigurationError | `packages/sdk/src/internal/runtime/validation/validate-agent-options.ts:68` +1 |
56
56
  | `credential_pool_empty` | domain | ConfigurationError | `packages/sdk/src/internal/llm/credential-pool.ts:91` |
57
57
  | `cron_ambiguous_target` | domain | ConfigurationError | `packages/sdk/src/cron.ts:215` |
@@ -77,11 +77,11 @@ Branch on `code`, never on the message: messages carry context (an id, a path, a
77
77
  | `handoff_target_invalid` | domain | ConfigurationError | `packages/sdk-handoff/src/handoff.ts:116` |
78
78
  | `handoff_target_required` | domain | ConfigurationError | `packages/sdk-handoff/src/handoff.ts:111` |
79
79
  | `hitl_timeout` | domain | HitlTimeoutError | `packages/sdk/src/internal/runtime/tools/hitl-middleware.ts:42` +1 |
80
- | `hook_denied` | domain | ConfigurationError | `packages/sdk/src/internal/local-agent/local-agent.ts:453` |
81
- | `hooks_invalid_command` | domain | ConfigurationError | `packages/sdk/src/internal/runtime/hooks/hooks-source.ts:268` |
82
- | `hooks_json_invalid` | domain | ConfigurationError | `packages/sdk/src/internal/runtime/hooks/hooks-source.ts:186` +2 |
83
- | `hooks_read_error` | domain | ConfigurationError | `packages/sdk/src/internal/runtime/hooks/hooks-source.ts:177` |
84
- | `hooks_unsupported_type` | domain | ConfigurationError | `packages/sdk/src/internal/runtime/hooks/hooks-source.ts:262` |
80
+ | `hook_denied` | domain | ConfigurationError | `packages/sdk/src/internal/local-agent/local-agent.ts:454` |
81
+ | `hooks_invalid_command` | domain | ConfigurationError | `packages/sdk/src/internal/runtime/hooks/hooks-source.ts:272` |
82
+ | `hooks_json_invalid` | domain | ConfigurationError | `packages/sdk/src/internal/runtime/hooks/hooks-source.ts:190` +2 |
83
+ | `hooks_read_error` | domain | ConfigurationError | `packages/sdk/src/internal/runtime/hooks/hooks-source.ts:181` |
84
+ | `hooks_unsupported_type` | domain | ConfigurationError | `packages/sdk/src/internal/runtime/hooks/hooks-source.ts:266` |
85
85
  | `interactive_unavailable` | domain | InteractiveUnavailableError | `packages/sdk/src/interactive/types.ts:28` +1 |
86
86
  | `INTERNAL_SERVER_ERROR` | domain | — | `packages/sdk/src/server/errors-envelope.ts:100` |
87
87
  | `invalid_argument` | domain | TheokitAgentError | `packages/sdk/src/compaction.ts:79` |
@@ -160,16 +160,16 @@ Branch on `code`, never on the message: messages carry context (an id, a path, a
160
160
  | `parse_failed` | domain | — | `packages/sdk/src/errors.ts:653` |
161
161
  | `path_traversal` | domain | — | `packages/sdk/src/internal/security/path-guard.ts:37` |
162
162
  | `permission_enforcement_unavailable` | domain | ConfigurationError | `packages/acp/src/permission-plugin.ts:138` +1 |
163
- | `personality_empty_body` | domain | ConfigurationError | `packages/sdk/src/internal/personality/registry.ts:115` |
163
+ | `personality_empty_body` | domain | ConfigurationError | `packages/sdk/src/internal/personality/registry.ts:117` |
164
164
  | `personality_not_found` | domain | ConfigurationError | `packages/sdk/src/internal/personality/switch.ts:60` |
165
- | `personality_reserved_name` | domain | ConfigurationError | `packages/sdk/src/internal/personality/registry.ts:108` |
165
+ | `personality_reserved_name` | domain | ConfigurationError | `packages/sdk/src/internal/personality/registry.ts:110` |
166
166
  | `pipeline_duplicate_provider` | domain | ConfigurationError | `packages/sdk/src/internal/runtime/system-prompt/pipeline.ts:33` |
167
- | `plugin_entry_missing` | domain | ConfigurationError | `packages/sdk/src/internal/runtime/plugin-loader/plugins-manager.ts:124` |
167
+ | `plugin_entry_missing` | domain | ConfigurationError | `packages/sdk/src/internal/runtime/plugin-loader/plugins-manager.ts:134` |
168
168
  | `plugin_frontmatter_invalid` | domain | ConfigurationError | `packages/sdk/src/internal/runtime/plugin-loader/plugin-frontmatter.ts:39` |
169
169
  | `plugin_late_register_kind` | domain | ConfigurationError | `packages/sdk/src/internal/plugins/manager.ts:103` |
170
- | `plugin_manifest_invalid` | domain | ConfigurationError | `packages/sdk/src/internal/runtime/plugin-loader/plugins-manager.ts:174` |
171
- | `plugin_manifest_shape` | domain | ConfigurationError | `packages/sdk/src/internal/runtime/plugin-loader/plugins-manager.ts:180` |
172
- | `plugin_missing_manifest` | domain | ConfigurationError | `packages/sdk/src/internal/runtime/plugin-loader/plugins-manager.ts:165` +2 |
170
+ | `plugin_manifest_invalid` | domain | ConfigurationError | `packages/sdk/src/internal/runtime/plugin-loader/plugins-manager.ts:185` |
171
+ | `plugin_manifest_shape` | domain | ConfigurationError | `packages/sdk/src/internal/runtime/plugin-loader/plugins-manager.ts:191` |
172
+ | `plugin_missing_manifest` | domain | ConfigurationError | `packages/sdk/src/internal/runtime/plugin-loader/plugins-manager.ts:176` +2 |
173
173
  | `programmatic_hooks_rejected` | domain | ConfigurationError | `packages/sdk/src/internal/runtime/validation/validate-agent-options.ts:97` |
174
174
  | `provider_unresolved` | domain | ConfigurationError | `packages/sdk/src/internal/llm/router.ts:99` +1 |
175
175
  | `rate_limit` | domain | — | `packages/sdk/src/internal/error-mappers/vertex.ts:47` |
@@ -195,13 +195,13 @@ Branch on `code`, never on the message: messages carry context (an id, a path, a
195
195
  | `ssrf_blocked` | domain | — | `packages/sdk-tools/src/internal/network-guard.ts:23` |
196
196
  | `stream_idle_timeout` | domain | NetworkError | `packages/sdk/src/internal/llm/sse.ts:95` |
197
197
  | `stream_truncated` | domain | NetworkError | `packages/sdk/src/internal/llm/anthropic.ts:184` +1 |
198
- | `subagent_mcp_unsupported_local` | domain | ConfigurationError | `packages/sdk/src/internal/runtime/skills/subagents-loader.ts:170` |
198
+ | `subagent_mcp_unsupported_local` | domain | ConfigurationError | `packages/sdk/src/internal/runtime/skills/subagents-loader.ts:173` |
199
199
  | `subagent_missing_description` | domain | ConfigurationError | `packages/sdk/src/internal/runtime/validation/validate-agent-options.ts:142` |
200
- | `subagent_missing_frontmatter` | domain | ConfigurationError | `packages/sdk/src/internal/runtime/skills/subagents-loader.ts:247` |
200
+ | `subagent_missing_frontmatter` | domain | ConfigurationError | `packages/sdk/src/internal/runtime/skills/subagents-loader.ts:250` |
201
201
  | `subagent_missing_prompt` | domain | ConfigurationError | `packages/sdk/src/internal/runtime/validation/validate-agent-options.ts:147` |
202
- | `subagent_reasoning_effort_without_model` | domain | ConfigurationError | `packages/sdk/src/internal/runtime/skills/subagents-loader.ts:190` |
203
- | `subagent_sandbox_not_boolean` | domain | ConfigurationError | `packages/sdk/src/internal/runtime/skills/subagents-loader.ts:210` |
204
- | `subagent_unknown_field` | domain | ConfigurationError | `packages/sdk/src/internal/runtime/skills/subagents-loader.ts:155` |
202
+ | `subagent_reasoning_effort_without_model` | domain | ConfigurationError | `packages/sdk/src/internal/runtime/skills/subagents-loader.ts:193` |
203
+ | `subagent_sandbox_not_boolean` | domain | ConfigurationError | `packages/sdk/src/internal/runtime/skills/subagents-loader.ts:213` |
204
+ | `subagent_unknown_field` | domain | ConfigurationError | `packages/sdk/src/internal/runtime/skills/subagents-loader.ts:158` |
205
205
  | `subagent_unknown_setting_source` | domain | ConfigurationError | `packages/sdk/src/subagents-loader.ts:63` |
206
206
  | `subscribe_baseUrl_missing` | domain | SubscriptionError | `packages/sdk/src/subscription/theokit-subscribe.ts:77` |
207
207
  | `subscribe_name_invalid` | domain | SubscriptionError | `packages/sdk/src/subscription/theokit-subscribe.ts:72` |
@@ -4,7 +4,7 @@ Every public symbol the TheoKit workspace publishes, and the exact specifier to
4
4
 
5
5
  A symbol listed under two specifiers is reachable from both, but that does NOT make the two interchangeable: a class emitted separately into a subpath entry is a distinct nominal type from the one in the root bundle, so passing one where the other is expected fails on a private field. When a symbol appears twice, import it and everything it is passed to from the SAME specifier.
6
6
 
7
- 1187 export(s) across 46 entry point(s).
7
+ 1189 export(s) across 46 entry point(s).
8
8
 
9
9
  ## `@theokit/acp`
10
10
 
@@ -116,6 +116,8 @@ A symbol listed under two specifiers is reachable from both, but that does NOT m
116
116
  | `CloudOptions` | interface | Cloud agent configuration. |
117
117
  | `CloudRepo` | interface | Repo to clone into a cloud agent's VM. |
118
118
  | `CompatSource` | type | A foreign configuration dialect this SDK can read. |
119
+ | `CompatSourceAdapter` | interface | A foreign source admitted to named surfaces only. |
120
+ | `CompatSurface` | type | A surface a foreign configuration source may be admitted to. |
119
121
  | `CompletionCheck` | interface | SE34 — the per-send completion criterion (see {@link SendOptions.completionCheck } ). |
120
122
  | `CompletionCheckResult` | interface | SE34 — the resolved per-send completion verdict (see {@link RunResult.completionCheck } ). |
121
123
  | `computeCost` | function | Returns CostBreakdown with `status="estimated"`, `"unknown"`, or `"included"`. |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@theokit/sdk",
3
- "version": "5.0.0-next.1",
3
+ "version": "5.0.0-next.2",
4
4
  "description": "TypeScript SDK for the Theo agent harness — same surface, local or cloud.",
5
5
  "license": "Apache-2.0",
6
6
  "homepage": "https://github.com/usetheokit/theokit-sdk#readme",
@@ -1 +0,0 @@
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-CEILSTWB.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 * 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(cwd: string, declared: readonly string[]): void {\n const declaredKinds = new Set(adaptersFor(declared).map((a) => a.kind));\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 { adaptersFor, THEOKIT_DIR_LITERAL } 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 * Every directory a project's configuration may be read from, in precedence order.\n *\n * `.theokit` first, then `.claude`. The order is the whole contract: a project that declares a\n * skill, agent or rule in both means the explicit namespace to win, and a caller merging these\n * 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 * Deliberately NOT affected by `THEOKIT_HOME`, and this is the one thing to remember about it.\n * That variable relocates cwd-anchored SDK *state* — sessions, the credential store. A project's\n * *configuration* is a property of the repository, not of where this SDK keeps its state, and the\n * loaders that read these directories have always anchored on `cwd` directly. Honouring the\n * override here would silently move where a project's agents and skills come from, which is a\n * behaviour change wearing the costume of a refactor.\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(cwd: string, sources: readonly string[] = []): string[] {\n return [\n join(cwd, THEOKIT_DIR_LITERAL),\n ...adaptersFor(sources).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(cwd: string, sources: readonly string[] = []): string[] {\n return projectConfigRoots(cwd, sources).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 +0,0 @@
1
- {"version":3,"sources":["../src/internal/runtime/plugin-loader/plugin-bundles.ts","../src/internal/runtime/skills/subagents-loader.ts"],"names":["pluginBundleRoots","readdir","join","projectConfigRoots","readWorkspaceDir","path","readFile","diag","ConfigurationError","parseSimpleYaml"],"mappings":"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","file":"chunk-G4ZGYKJG.cjs","sourcesContent":["/**\n * Locating the plugin bundles a project carries.\n *\n * Shared by the skills and subagents loaders, which both need the same answer to \"which folders in\n * this project are plugins\" and would otherwise each grow their own copy of the directory walk.\n *\n * @internal\n */\n\nimport type { Dirent } from \"node:fs\";\nimport { readdir } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { pluginBundleRoots } from \"../../persistence/paths.js\";\n\n/**\n * Every plugin folder under the project's plugin roots.\n *\n * Returns the FOLDERS, not their contents — what a bundle contributes (`skills/`, `agents/`) is the\n * caller's business, and a loader that also knew the layout would have to change whenever the other\n * one did.\n *\n * A missing root is not an error: most projects carry no plugins, and treating their absence as a\n * failure would make \"none installed\" indistinguishable from \"the directory could not be read\".\n */\nexport async function pluginBundleDirs(\n cwd: string,\n /** Declared foreign dialects (#524). Empty reads `.theokit/plugins` only. */\n compatSources: readonly string[] = [],\n): Promise<string[]> {\n const dirs: string[] = [];\n for (const root of pluginBundleRoots(cwd, compatSources)) {\n let entries: Dirent[];\n try {\n entries = await readdir(root, { withFileTypes: true });\n } catch {\n continue;\n }\n for (const entry of entries) {\n if (entry.isDirectory()) dirs.push(join(root, entry.name));\n }\n }\n return dirs;\n}\n","import { readFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\n\nimport { ConfigurationError } from \"../../../errors.js\";\nimport type { AgentDefinition } from \"../../../types/agent.js\";\nimport type { ModelSelection } from \"../../../types/agent-prims.js\";\nimport { diag } from \"../../diagnostics.js\";\nimport { projectConfigRoots } from \"../../persistence/paths.js\";\nimport { readWorkspaceDir } from \"../config/workspace-dir.js\";\nimport { type FrontmatterValue, parseSimpleYaml } from \"../context/yaml-frontmatter.js\";\nimport { pluginBundleDirs } from \"../plugin-loader/plugin-bundles.js\";\n\n/**\n * Load file-based subagents from `.theokit/agents/*.md` and merge with\n * inline definitions. Inline overrides file-based on name conflict.\n *\n * Each markdown file has YAML frontmatter (description + optional model)\n * and a body that becomes the subagent prompt.\n *\n * @internal\n */\nexport async function loadSubagents(\n cwd: string,\n settingSourcesIncludeProject: boolean,\n inline: Record<string, AgentDefinition> | undefined,\n /** Declared foreign dialects (#524). Empty reads `.theokit/` only. */\n compatSources: readonly string[] = [],\n): Promise<Record<string, AgentDefinition>> {\n const result: Record<string, AgentDefinition> = {};\n if (settingSourcesIncludeProject) {\n const projectAgents = await readProjectSubagents(cwd, compatSources);\n for (const [name, definition] of Object.entries(projectAgents)) {\n result[name] = definition;\n }\n }\n if (inline !== undefined) {\n for (const [name, definition] of Object.entries(inline)) {\n result[name] = definition;\n }\n }\n return result;\n}\n\n/**\n * Read agent declarations from every project config root (`.theokit`, then `.claude`).\n *\n * FIRST occurrence of a name wins, which is what makes `projectConfigRoots`' order a contract rather\n * than a detail: a project declaring the same agent in both means the explicit namespace.\n */\nasync function readProjectSubagents(\n cwd: string,\n compatSources: readonly string[],\n): Promise<Record<string, AgentDefinition>> {\n const subagents: Record<string, AgentDefinition> = {};\n for (const configRoot of projectConfigRoots(cwd, compatSources)) {\n await readSubagentsFrom(join(configRoot, \"agents\"), subagents);\n }\n // A Claude Code plugin is a BUNDLE, and its `agents/` is what it exists to contribute. Read after\n // the project's own, so a project can shadow an agent a plugin ships without editing the plugin.\n for (const bundle of await pluginBundleDirs(cwd, compatSources)) {\n await readSubagentsFrom(join(bundle, \"agents\"), subagents);\n }\n return subagents;\n}\n\nasync function readSubagentsFrom(\n root: string,\n subagents: Record<string, AgentDefinition>,\n): Promise<void> {\n const entries = await readWorkspaceDir(root, \"subagents_read_error\", \"subagents directory\");\n for (const entry of entries) {\n if (!entry.isFile() || !entry.name.endsWith(\".md\")) continue;\n const path = join(root, entry.name);\n const raw = await readFile(path, \"utf8\");\n // A markdown file with NO frontmatter is not an agent declaration — a directory of agents\n // written for the Claude Code CLI conventionally carries documentation beside them, and\n // `.claude/agents/README.md` exists in this repository. Throwing on it made ONE such file stop\n // every agent in the directory from loading.\n //\n // Skipped with a warn rather than in silence, and ONLY for the no-frontmatter case: a file that\n // HAS frontmatter and gets it wrong is a broken agent and still fails loudly, which is what\n // keeps a typo'd `sandbox` from returning as a silent gate through this door.\n if (!hasFrontmatter(raw)) {\n diag(`[theokit-sdk] ${entry.name} has no frontmatter — not an agent declaration, skipping`);\n continue;\n }\n const definition = parseSubagentMarkdown(raw, entry.name);\n if (subagents[definition.name] === undefined) {\n subagents[definition.name] = definition.definition;\n }\n }\n}\n\n// The frontmatter keys a disk subagent may declare. Any other key is a typed load\n// error rather than a silent drop — a dropped `sandbox` an operator wrote believing\n// it confines the child is exactly the silent-gate failure class this guards against.\nconst ACCEPTED_FIELDS = new Set([\n \"name\",\n \"description\",\n \"model\",\n \"tools\",\n \"reasoning_effort\",\n \"mcp\",\n \"sandbox\",\n]);\n\n// Fields the Claude Code CLI writes that carry NO behaviour for this runtime. Accepted and ignored,\n// so an agent authored for the CLI loads here unchanged — measured 2026-08-26 across the 59 agent\n// files on one machine, where `color` appeared in 38 of them and made every one of those a\n// `subagent_unknown_field` load error.\n//\n// Named explicitly instead of loosening the check above, because that check's reason is sound: a\n// dropped `sandbox` an operator wrote believing it confines the child is a silent gate. A field that\n// COULD change behaviour must still fail loudly. This set is the difference between \"we know this\n// one and it does nothing\" and \"we have never heard of this\" — two facts a bare allow-everything\n// would collapse into one.\n//\n// Anything added here needs the same justification: inert for THIS runtime, not merely unfamiliar.\nconst INERT_CLAUDE_CODE_FIELDS = new Set([\n /** The CLI's label colour for the agent. Presentation only. */\n \"color\",\n]);\n\nfunction parseSubagentMarkdown(\n raw: string,\n filename: string,\n): { name: string; definition: AgentDefinition } {\n const { frontmatter, body } = splitFrontmatter(raw, filename);\n const fields = parseFrontmatterFields(frontmatter);\n rejectUnknownFields(fields, filename);\n rejectMcp(fields, filename);\n\n const definition: AgentDefinition = {\n description: asString(fields.description) ?? \"\",\n prompt: body,\n };\n const model = resolveModel(fields, filename);\n if (model !== undefined) definition.model = model;\n const tools = toStringList(fields.tools);\n if (tools.length > 0) definition.tools = tools;\n const sandbox = resolveSandbox(fields, filename);\n if (sandbox !== undefined) definition.sandbox = sandbox;\n\n const name = asString(fields.name) ?? filename.replace(/\\.md$/, \"\");\n return { name, definition };\n}\n\nfunction rejectUnknownFields(\n fields: Record<string, FrontmatterValue | undefined>,\n filename: string,\n): void {\n for (const key of Object.keys(fields)) {\n if (INERT_CLAUDE_CODE_FIELDS.has(key)) continue;\n if (!ACCEPTED_FIELDS.has(key)) {\n throw new ConfigurationError(\n `Subagent ${filename}: unknown frontmatter field \"${key}\" (accepted: ${[...ACCEPTED_FIELDS].join(\", \")})`,\n { code: \"subagent_unknown_field\" },\n );\n }\n }\n}\n\n// mcp: a known field, but not yet honored on the LOCAL delegation path. The frontmatter YAML can only\n// express server NAMES (parseSimpleYaml has no nested-object support), while a child's `Agent.create`\n// needs `mcpServers` as a Record<name, config>; resolving names→config per-subagent in local delegation\n// is its own follow-up. Rather than silently drop it (the M26/M32 silent-gate class), it is a typed load\n// error that names the field and points at the alternative.\nfunction rejectMcp(fields: Record<string, FrontmatterValue | undefined>, filename: string): void {\n if (fields.mcp !== undefined) {\n throw new ConfigurationError(\n `Subagent ${filename}: per-subagent \"mcp\" is not yet supported on the local delegation path; declare MCP servers in .theokit/mcp.json (or the parent) instead`,\n { code: \"subagent_mcp_unsupported_local\" },\n );\n }\n}\n\n// model + reasoning_effort — effort rides inside `model.params[thinking]`, so it requires a concrete\n// model id to attach to (a child inheriting the parent's model cannot carry the parent's provider-\n// specific effort param safely).\nfunction resolveModel(\n fields: Record<string, FrontmatterValue | undefined>,\n filename: string,\n): ModelSelection | \"inherit\" | undefined {\n const modelId = asString(fields.model);\n const effort = asString(fields.reasoning_effort);\n // reasoning_effort rides in model.params[thinking], so it needs a CONCRETE model id to attach to.\n // Neither an absent model NOR `model: inherit` can carry it (the inherited id is unknown at load), so\n // both are typed errors rather than a silently-dropped effort — the silent-gate class this guards.\n if (effort !== undefined && (modelId === undefined || modelId === \"inherit\")) {\n throw new ConfigurationError(\n `Subagent ${filename}: reasoning_effort requires a concrete model (effort is a model parameter; an absent model or \"inherit\" cannot carry it)`,\n { code: \"subagent_reasoning_effort_without_model\" },\n );\n }\n if (modelId === undefined) return undefined;\n if (modelId === \"inherit\") return \"inherit\";\n return effort !== undefined\n ? { id: modelId, params: [{ id: \"thinking\", value: effort }] }\n : { id: modelId };\n}\n\n// sandbox: boolean only. A granular mode string (read-only/…) is unsupported by the SDK runtime and is\n// a typed error rather than a silent coercion to a boolean.\nfunction resolveSandbox(\n fields: Record<string, FrontmatterValue | undefined>,\n filename: string,\n): boolean | undefined {\n if (fields.sandbox === undefined) return undefined;\n if (typeof fields.sandbox !== \"boolean\") {\n throw new ConfigurationError(\n `Subagent ${filename}: sandbox must be a boolean (got \"${String(fields.sandbox)}\"); granular sandbox modes are not supported by the runtime`,\n { code: \"subagent_sandbox_not_boolean\" },\n );\n }\n return fields.sandbox;\n}\n\nfunction asString(v: FrontmatterValue | undefined): string | undefined {\n if (typeof v !== \"string\") return undefined;\n // parseSimpleYaml does not strip quotes (documented), and `model`/`reasoning_effort` are fields users\n // habitually quote (`model: \"openai/gpt-4o\"`). Strip a single matching surrounding quote pair so a\n // quoted id/effort does not slip past validation and fail only at the provider.\n const m = /^([\"'])(.*)\\1$/.exec(v);\n return m ? m[2] : v;\n}\n\n/** Accept a YAML list (`string[]`) or a comma/space-separated scalar; trim + drop empties. */\nfunction toStringList(v: FrontmatterValue | undefined): string[] {\n if (Array.isArray(v)) return v.map((t) => t.trim()).filter((t) => t.length > 0);\n if (typeof v === \"string\") {\n return v\n .split(/[\\s,]+/)\n .map((t) => t.trim())\n .filter((t) => t.length > 0);\n }\n return [];\n}\n\n/** Does this file open with a frontmatter block at all? Its ABSENCE means \"not an agent\". */\nfunction hasFrontmatter(raw: string): boolean {\n return /^---\\s*\\n/.test(raw);\n}\n\nfunction splitFrontmatter(raw: string, filename: string): { frontmatter: string; body: string } {\n const match = /^---\\s*\\n([\\s\\S]*?)\\n---\\s*\\n([\\s\\S]*)$/.exec(raw);\n if (match === null) {\n throw new ConfigurationError(`Subagent ${filename} is missing frontmatter`, {\n code: \"subagent_missing_frontmatter\",\n });\n }\n return { frontmatter: match[1] ?? \"\", body: (match[2] ?? \"\").trim() };\n}\n\nfunction parseFrontmatterFields(frontmatter: string): Record<string, FrontmatterValue | undefined> {\n // Preserve the rich YAML value types (boolean/number/string[]): `sandbox: true` and\n // `mcp: [a, b]` are meaningful here, so narrowing everything to string (as the\n // pre-M33 loader did) would drop them. Per-field validation happens in parseSubagentMarkdown.\n return parseSimpleYaml(frontmatter);\n}\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/internal/runtime/hooks/hooks-source.ts","../src/internal/personality/context.ts"],"names":["diag","existsSync","join","projectConfigRoots","readFile","ConfigurationError","AsyncLocalStorage"],"mappings":"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","file":"chunk-HWSALKOR.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 } from \"../../persistence/paths.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 string[] = [],\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(cwd, \".theokit\", \"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(cwd: string, compatSources: readonly string[]): string[] {\n const roots = projectConfigRoots(cwd, compatSources);\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 +0,0 @@
1
- {"version":3,"sources":["../src/internal/runtime/hooks/hooks-source.ts","../src/internal/personality/context.ts"],"names":[],"mappings":"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","file":"chunk-IJM7UGS5.js","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 } from \"../../persistence/paths.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 string[] = [],\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(cwd, \".theokit\", \"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(cwd: string, compatSources: readonly string[]): string[] {\n const roots = projectConfigRoots(cwd, compatSources);\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 +0,0 @@
1
- {"version":3,"sources":["../src/internal/runtime/compat/foreign-config-sources.ts","../src/internal/persistence/paths.ts"],"names":["join"],"mappings":"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","file":"chunk-JTZ74HFD.js","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 * 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(cwd: string, declared: readonly string[]): void {\n const declaredKinds = new Set(adaptersFor(declared).map((a) => a.kind));\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 { adaptersFor, THEOKIT_DIR_LITERAL } 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 * Every directory a project's configuration may be read from, in precedence order.\n *\n * `.theokit` first, then `.claude`. The order is the whole contract: a project that declares a\n * skill, agent or rule in both means the explicit namespace to win, and a caller merging these\n * 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 * Deliberately NOT affected by `THEOKIT_HOME`, and this is the one thing to remember about it.\n * That variable relocates cwd-anchored SDK *state* — sessions, the credential store. A project's\n * *configuration* is a property of the repository, not of where this SDK keeps its state, and the\n * loaders that read these directories have always anchored on `cwd` directly. Honouring the\n * override here would silently move where a project's agents and skills come from, which is a\n * behaviour change wearing the costume of a refactor.\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(cwd: string, sources: readonly string[] = []): string[] {\n return [\n join(cwd, THEOKIT_DIR_LITERAL),\n ...adaptersFor(sources).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(cwd: string, sources: readonly string[] = []): string[] {\n return projectConfigRoots(cwd, sources).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"]}