@gajae-code/coding-agent 0.16.7 → 0.17.1

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 (321) hide show
  1. package/CHANGELOG.md +180 -1
  2. package/dist/types/capability/fs.d.ts +37 -4
  3. package/dist/types/capability/index.d.ts +19 -0
  4. package/dist/types/capability/types.d.ts +9 -0
  5. package/dist/types/cli/doctor/args.d.ts +36 -0
  6. package/dist/types/cli/doctor/checks.d.ts +18 -0
  7. package/dist/types/cli/doctor/config-repairs.d.ts +38 -0
  8. package/dist/types/cli/doctor/context.d.ts +89 -0
  9. package/dist/types/cli/doctor/files.d.ts +27 -0
  10. package/dist/types/cli/doctor/ids.d.ts +20 -0
  11. package/dist/types/cli/doctor/install-repairs.d.ts +94 -0
  12. package/dist/types/cli/doctor/journal.d.ts +43 -0
  13. package/dist/types/cli/doctor/managed-link.d.ts +39 -0
  14. package/dist/types/cli/doctor/native-probe.d.ts +2 -0
  15. package/dist/types/cli/doctor/permission-repairs.d.ts +25 -0
  16. package/dist/types/cli/doctor/plugin-quarantine.d.ts +77 -0
  17. package/dist/types/cli/doctor/plugin-restore.d.ts +40 -0
  18. package/dist/types/cli/doctor/plugin-targets.d.ts +11 -0
  19. package/dist/types/cli/doctor/probe-client.d.ts +9 -0
  20. package/dist/types/cli/doctor/probe-types.d.ts +18 -0
  21. package/dist/types/cli/doctor/probe.d.ts +3 -0
  22. package/dist/types/cli/doctor/projection-probe.d.ts +2 -0
  23. package/dist/types/cli/doctor/repairs.d.ts +28 -0
  24. package/dist/types/cli/doctor/report.d.ts +27 -0
  25. package/dist/types/cli/doctor/runner.d.ts +4 -0
  26. package/dist/types/cli/doctor/safety.d.ts +10 -0
  27. package/dist/types/cli/doctor/service-repairs.d.ts +8 -0
  28. package/dist/types/cli/doctor/service-targets.d.ts +41 -0
  29. package/dist/types/cli/doctor/types.d.ts +145 -0
  30. package/dist/types/cli/doctor-cli.d.ts +7 -0
  31. package/dist/types/cli/doctor-supervisor.d.ts +121 -0
  32. package/dist/types/cli/doctor-worker.d.ts +29 -0
  33. package/dist/types/cli/install-activation.d.ts +158 -0
  34. package/dist/types/cli/macos-community-app.d.ts +41 -0
  35. package/dist/types/cli/mcp-cli.d.ts +0 -11
  36. package/dist/types/cli/update-cli.d.ts +41 -4
  37. package/dist/types/cli-main.d.ts +174 -0
  38. package/dist/types/cli.d.ts +1 -171
  39. package/dist/types/commands/doctor.d.ts +7 -0
  40. package/dist/types/commands/sdk.d.ts +17 -8
  41. package/dist/types/config/autorouting-tier-map.d.ts +19 -0
  42. package/dist/types/config/file-lock.d.ts +27 -3
  43. package/dist/types/config/model-preset-registry.d.ts +1 -0
  44. package/dist/types/config/model-registry.d.ts +12 -5
  45. package/dist/types/config/model-resolver.d.ts +16 -3
  46. package/dist/types/config/models-config-schema.d.ts +2 -0
  47. package/dist/types/config/settings-schema.d.ts +1 -1
  48. package/dist/types/coordinator-mcp/question-gate-codec.d.ts +1 -1
  49. package/dist/types/coordinator-mcp/session-reaper.d.ts +15 -5
  50. package/dist/types/cursor.d.ts +9 -9
  51. package/dist/types/discovery/agents-md.d.ts +6 -1
  52. package/dist/types/discovery/helpers.d.ts +36 -7
  53. package/dist/types/discovery/index.d.ts +1 -1
  54. package/dist/types/discovery/mcp-json.d.ts +2 -0
  55. package/dist/types/extensibility/extensions/loader.d.ts +22 -1
  56. package/dist/types/extensibility/gjc-plugins/lifecycle.d.ts +14 -1
  57. package/dist/types/extensibility/gjc-plugins/paths.d.ts +2 -1
  58. package/dist/types/extensibility/gjc-plugins/types.d.ts +25 -0
  59. package/dist/types/extensibility/plugins/manager.d.ts +33 -4
  60. package/dist/types/extensibility/plugins/marketplace/cache.d.ts +6 -0
  61. package/dist/types/extensibility/plugins/marketplace/manager.d.ts +186 -0
  62. package/dist/types/extensibility/plugins/marketplace/registry.d.ts +27 -0
  63. package/dist/types/extensibility/plugins/marketplace/source-resolver.d.ts +16 -1
  64. package/dist/types/extensibility/runtime-skill-discovery.d.ts +16 -3
  65. package/dist/types/gjc-runtime/autoresearch-runtime.d.ts +6 -0
  66. package/dist/types/gjc-runtime/launch-worktree.d.ts +53 -0
  67. package/dist/types/gjc-runtime/session-layout.d.ts +8 -0
  68. package/dist/types/gjc-runtime/session-state-lock.d.ts +15 -0
  69. package/dist/types/gjc-runtime/session-state-sidecar.d.ts +13 -0
  70. package/dist/types/gjc-runtime/ultragoal-runtime.d.ts +6 -0
  71. package/dist/types/main.d.ts +19 -1
  72. package/dist/types/modes/acp/acp-agent.d.ts +40 -0
  73. package/dist/types/modes/components/agent-dashboard.d.ts +1 -0
  74. package/dist/types/modes/components/model-selector.d.ts +2 -0
  75. package/dist/types/modes/interactive-mode.d.ts +1 -1
  76. package/dist/types/runtime-mcp/config-writer.d.ts +47 -0
  77. package/dist/types/runtime-mcp/policy-json-edit.d.ts +60 -0
  78. package/dist/types/runtime-mcp/startup-policy.d.ts +4 -0
  79. package/dist/types/runtime-mcp/types.d.ts +2 -2
  80. package/dist/types/sdk/acp/adapter.d.ts +5 -1
  81. package/dist/types/sdk/broker/broker.d.ts +51 -0
  82. package/dist/types/sdk/broker/daemon-entry.d.ts +39 -0
  83. package/dist/types/sdk/broker/discovery.d.ts +38 -0
  84. package/dist/types/sdk/broker/doctor-restart.d.ts +79 -0
  85. package/dist/types/sdk/broker/endpoint-authority.d.ts +7 -0
  86. package/dist/types/sdk/broker/ensure.d.ts +14 -0
  87. package/dist/types/sdk/broker/lifecycle.d.ts +2 -0
  88. package/dist/types/sdk/broker/process-incarnation.d.ts +50 -0
  89. package/dist/types/sdk/broker/runtime.d.ts +26 -0
  90. package/dist/types/sdk/broker/session-index.d.ts +13 -4
  91. package/dist/types/sdk/broker/worktree-occupancy.d.ts +14 -0
  92. package/dist/types/sdk/bus/chat-daemon-cli.d.ts +16 -0
  93. package/dist/types/sdk/bus/chat-daemon-control.d.ts +101 -6
  94. package/dist/types/sdk/bus/chat-daemon-runtime.d.ts +16 -0
  95. package/dist/types/sdk/bus/daemon-startup-exclusion.d.ts +43 -0
  96. package/dist/types/sdk/bus/doctor-daemon-restart-client.d.ts +51 -0
  97. package/dist/types/sdk/bus/doctor-daemon-restart.d.ts +39 -0
  98. package/dist/types/sdk/bus/index.d.ts +2 -0
  99. package/dist/types/sdk/bus/notification-service.d.ts +3 -0
  100. package/dist/types/sdk/bus/telegram-daemon-cli.d.ts +11 -0
  101. package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +1 -1
  102. package/dist/types/sdk/bus/telegram-daemon-control.d.ts +5 -0
  103. package/dist/types/sdk/bus/telegram-daemon-owner-registry.d.ts +1 -0
  104. package/dist/types/sdk/bus/telegram-daemon.d.ts +36 -5
  105. package/dist/types/sdk/cli/session-cli.d.ts +1 -0
  106. package/dist/types/sdk/host/session-runtime.d.ts +4 -0
  107. package/dist/types/sdk/host/surface-policy.d.ts +3 -1
  108. package/dist/types/sdk/lifecycle/service.d.ts +44 -10
  109. package/dist/types/sdk/prompt-deadline-lease.d.ts +6 -3
  110. package/dist/types/sdk/prompt-deadline-manager.d.ts +2 -2
  111. package/dist/types/sdk/prompt-failure.d.ts +47 -0
  112. package/dist/types/sdk/prompt-status.d.ts +19 -0
  113. package/dist/types/sdk/router/session-router.d.ts +3 -4
  114. package/dist/types/sdk/service-artifact-paths.d.ts +16 -0
  115. package/dist/types/sdk/session-authority.d.ts +9 -0
  116. package/dist/types/sdk/session.d.ts +3 -1
  117. package/dist/types/session/agent-session.d.ts +1 -1
  118. package/dist/types/session/blob-store.d.ts +1 -1
  119. package/dist/types/session/fallback-chain-controller.d.ts +3 -6
  120. package/dist/types/session/internal/managed-session-scope.d.ts +44 -0
  121. package/dist/types/session/primary-control-surface.d.ts +4 -0
  122. package/dist/types/session/session-manager.d.ts +6 -0
  123. package/dist/types/system-prompt.d.ts +6 -0
  124. package/dist/types/tools/ask.d.ts +25 -0
  125. package/dist/types/tools/image-gen.d.ts +0 -4
  126. package/dist/types/tools/skill-discovery.d.ts +7 -5
  127. package/dist/types/tools/ultragoal-ask-guard.d.ts +7 -4
  128. package/dist/types/utils/changelog.d.ts +0 -9
  129. package/dist/types/utils/mupdf-wasm.d.ts +1 -0
  130. package/package.json +9 -8
  131. package/scripts/compile-args.ts +8 -4
  132. package/src/capability/fs.ts +395 -17
  133. package/src/capability/index.ts +273 -10
  134. package/src/capability/types.ts +9 -0
  135. package/src/cli/customize-doctor.ts +18 -12
  136. package/src/cli/doctor/args.ts +236 -0
  137. package/src/cli/doctor/checks.ts +431 -0
  138. package/src/cli/doctor/config-repairs.ts +517 -0
  139. package/src/cli/doctor/context.ts +149 -0
  140. package/src/cli/doctor/files.ts +149 -0
  141. package/src/cli/doctor/ids.ts +115 -0
  142. package/src/cli/doctor/install-repairs.ts +335 -0
  143. package/src/cli/doctor/journal.ts +423 -0
  144. package/src/cli/doctor/managed-link.ts +514 -0
  145. package/src/cli/doctor/native-probe.ts +13 -0
  146. package/src/cli/doctor/permission-repairs.ts +242 -0
  147. package/src/cli/doctor/plugin-quarantine.ts +377 -0
  148. package/src/cli/doctor/plugin-restore.ts +59 -0
  149. package/src/cli/doctor/plugin-targets.ts +373 -0
  150. package/src/cli/doctor/probe-client.ts +195 -0
  151. package/src/cli/doctor/probe-types.ts +21 -0
  152. package/src/cli/doctor/probe.ts +42 -0
  153. package/src/cli/doctor/projection-probe.ts +249 -0
  154. package/src/cli/doctor/repairs.ts +1157 -0
  155. package/src/cli/doctor/report.ts +176 -0
  156. package/src/cli/doctor/runner.ts +342 -0
  157. package/src/cli/doctor/safety.ts +40 -0
  158. package/src/cli/doctor/service-repairs.ts +452 -0
  159. package/src/cli/doctor/service-targets.ts +274 -0
  160. package/src/cli/doctor/types.ts +164 -0
  161. package/src/cli/doctor-cli.ts +62 -0
  162. package/src/cli/doctor-supervisor.ts +571 -0
  163. package/src/cli/doctor-worker.ts +181 -0
  164. package/src/cli/install-activation.ts +666 -0
  165. package/src/cli/macos-community-app.ts +1449 -0
  166. package/src/cli/mcp-cli.ts +11 -21
  167. package/src/cli/nofile-limit.ts +5 -1
  168. package/src/cli/plugin-cli.ts +6 -1
  169. package/src/cli/update-cli.ts +362 -264
  170. package/src/cli-main.ts +517 -0
  171. package/src/cli.ts +64 -433
  172. package/src/commands/autoresearch.ts +11 -5
  173. package/src/commands/completion.ts +1 -1
  174. package/src/commands/doctor.ts +13 -0
  175. package/src/commands/sdk.ts +58 -10
  176. package/src/commands/ultragoal.ts +11 -5
  177. package/src/config/autorouting-tier-map.ts +7 -0
  178. package/src/config/file-lock.ts +736 -71
  179. package/src/config/mcp-schema.json +3 -3
  180. package/src/config/model-preset-registry.ts +1 -0
  181. package/src/config/model-registry.ts +80 -27
  182. package/src/config/model-resolver.ts +33 -4
  183. package/src/config/models-config-schema.ts +2 -0
  184. package/src/config/settings-schema.ts +6 -2
  185. package/src/coordinator-mcp/question-gate-codec.ts +1 -0
  186. package/src/coordinator-mcp/server.ts +534 -51
  187. package/src/coordinator-mcp/session-reaper.ts +64 -8
  188. package/src/cursor.ts +223 -53
  189. package/src/discovery/agents-md.ts +74 -8
  190. package/src/discovery/agents.ts +32 -7
  191. package/src/discovery/builtin.ts +184 -75
  192. package/src/discovery/claude-plugins.ts +85 -17
  193. package/src/discovery/cline.ts +38 -10
  194. package/src/discovery/cursor.ts +19 -6
  195. package/src/discovery/gemini.ts +62 -22
  196. package/src/discovery/github.ts +13 -3
  197. package/src/discovery/helpers.ts +483 -89
  198. package/src/discovery/index.ts +1 -0
  199. package/src/discovery/mcp-json.ts +36 -10
  200. package/src/discovery/opencode.ts +36 -19
  201. package/src/discovery/ssh.ts +11 -8
  202. package/src/discovery/windsurf.ts +12 -3
  203. package/src/exec/bash-executor.ts +10 -3
  204. package/src/exec/isolated-shell.ts +15 -2
  205. package/src/extensibility/extensions/loader.ts +88 -26
  206. package/src/extensibility/gjc-plugins/lifecycle.ts +189 -0
  207. package/src/extensibility/gjc-plugins/paths.ts +4 -1
  208. package/src/extensibility/gjc-plugins/types.ts +27 -0
  209. package/src/extensibility/plugins/manager.ts +200 -22
  210. package/src/extensibility/plugins/marketplace/cache.ts +55 -2
  211. package/src/extensibility/plugins/marketplace/manager.ts +1262 -162
  212. package/src/extensibility/plugins/marketplace/registry.ts +153 -0
  213. package/src/extensibility/plugins/marketplace/source-resolver.ts +116 -10
  214. package/src/extensibility/runtime-skill-discovery.ts +61 -9
  215. package/src/extensibility/skill-management.ts +7 -1
  216. package/src/gjc-runtime/autoresearch-runtime.ts +10 -1
  217. package/src/gjc-runtime/launch-worktree.test.ts +248 -0
  218. package/src/gjc-runtime/launch-worktree.ts +275 -38
  219. package/src/gjc-runtime/repository-binding.ts +26 -1
  220. package/src/gjc-runtime/session-layout.ts +10 -0
  221. package/src/gjc-runtime/session-state-lock.ts +330 -23
  222. package/src/gjc-runtime/session-state-sidecar.ts +328 -80
  223. package/src/gjc-runtime/ultragoal-runtime.ts +10 -1
  224. package/src/harness-control-plane/session-lease.ts +2 -2
  225. package/src/internal-urls/docs-index.generated.ts +28 -26
  226. package/src/main.ts +150 -29
  227. package/src/memories/index.ts +24 -2
  228. package/src/modes/acp/acp-agent.ts +522 -68
  229. package/src/modes/components/agent-dashboard.ts +3 -1
  230. package/src/modes/components/model-selector.ts +28 -19
  231. package/src/modes/components/plugin-settings.ts +2 -1
  232. package/src/modes/controllers/selector-controller.ts +15 -9
  233. package/src/modes/interactive-mode.ts +21 -3
  234. package/src/prompts/system/system-prompt.md +5 -1
  235. package/src/prompts/tools/browser.md +1 -1
  236. package/src/prompts/tools/move-session.md +4 -0
  237. package/src/prompts/tools/read.md +2 -1
  238. package/src/prompts/tools/skill-discovery.md +6 -5
  239. package/src/prompts/tools/task.md +1 -0
  240. package/src/runtime-mcp/config-writer.ts +228 -0
  241. package/src/runtime-mcp/oauth-discovery.ts +114 -14
  242. package/src/runtime-mcp/policy-json-edit.ts +129 -0
  243. package/src/runtime-mcp/startup-policy.ts +14 -0
  244. package/src/runtime-mcp/types.ts +2 -2
  245. package/src/sdk/acp/adapter.ts +27 -12
  246. package/src/sdk/broker/broker.ts +273 -44
  247. package/src/sdk/broker/daemon-entry.ts +119 -0
  248. package/src/sdk/broker/discovery.ts +173 -20
  249. package/src/sdk/broker/doctor-restart.ts +206 -0
  250. package/src/sdk/broker/endpoint-authority.ts +36 -0
  251. package/src/sdk/broker/ensure.ts +49 -14
  252. package/src/sdk/broker/lifecycle.ts +174 -42
  253. package/src/sdk/broker/process-incarnation.ts +86 -2
  254. package/src/sdk/broker/runtime.ts +145 -21
  255. package/src/sdk/broker/session-index.ts +136 -22
  256. package/src/sdk/broker/spawn-substrate.ts +4 -1
  257. package/src/sdk/broker/transport.ts +5 -0
  258. package/src/sdk/broker/worktree-occupancy.ts +21 -1
  259. package/src/sdk/bus/chat-daemon-cli.ts +63 -0
  260. package/src/sdk/bus/chat-daemon-control.ts +260 -64
  261. package/src/sdk/bus/chat-daemon-runtime.ts +141 -25
  262. package/src/sdk/bus/daemon-startup-exclusion.ts +119 -0
  263. package/src/sdk/bus/doctor-daemon-restart-client.ts +291 -0
  264. package/src/sdk/bus/doctor-daemon-restart.ts +92 -0
  265. package/src/sdk/bus/index.ts +252 -51
  266. package/src/sdk/bus/kind-aware-reconciliation.ts +76 -41
  267. package/src/sdk/bus/notification-service.ts +21 -3
  268. package/src/sdk/bus/reconciliation-store.ts +17 -11
  269. package/src/sdk/bus/telegram-daemon-cli.ts +91 -5
  270. package/src/sdk/bus/telegram-daemon-contract.ts +1 -1
  271. package/src/sdk/bus/telegram-daemon-control.ts +47 -0
  272. package/src/sdk/bus/telegram-daemon-owner-registry.ts +1 -1
  273. package/src/sdk/bus/telegram-daemon.ts +178 -13
  274. package/src/sdk/cli/session-cli.ts +100 -12
  275. package/src/sdk/host/host.ts +2 -7
  276. package/src/sdk/host/session-runtime.test.ts +329 -8
  277. package/src/sdk/host/session-runtime.ts +202 -50
  278. package/src/sdk/host/surface-policy.ts +8 -1
  279. package/src/sdk/lifecycle/service.ts +199 -22
  280. package/src/sdk/mcp/server.ts +1 -1
  281. package/src/sdk/prompt-deadline-lease.ts +12 -4
  282. package/src/sdk/prompt-deadline-manager.ts +5 -7
  283. package/src/sdk/prompt-failure.ts +148 -0
  284. package/src/sdk/prompt-status.ts +30 -1
  285. package/src/sdk/prompt-watchdog.ts +11 -3
  286. package/src/sdk/protocol/operation-inventory.generated.json +18 -0
  287. package/src/sdk/protocol/operation-registry.ts +20 -8
  288. package/src/sdk/router/session-router.ts +8 -3
  289. package/src/sdk/service-artifact-paths.ts +26 -0
  290. package/src/sdk/session-authority.ts +10 -0
  291. package/src/sdk/session.ts +361 -194
  292. package/src/session/agent-session.ts +398 -93
  293. package/src/session/blob-store.ts +9 -9
  294. package/src/session/fallback-chain-controller.ts +3 -7
  295. package/src/session/internal/managed-session-scope.ts +445 -29
  296. package/src/session/internal/managed-session-storage.ts +38 -6
  297. package/src/session/primary-control-surface.ts +12 -0
  298. package/src/session/session-manager.ts +11 -3
  299. package/src/session/session-storage.ts +1 -1
  300. package/src/session-import/codex.ts +6 -1
  301. package/src/session-import/redact.ts +7 -0
  302. package/src/skill-state/workflow-mutation-guard.ts +645 -39
  303. package/src/slash-commands/builtin-registry.ts +7 -3
  304. package/src/system-prompt.ts +8 -0
  305. package/src/task/executor.ts +36 -3
  306. package/src/task/types.ts +27 -5
  307. package/src/tools/ask.ts +114 -34
  308. package/src/tools/browser/actions.ts +2 -2
  309. package/src/tools/browser/tab-worker.ts +11 -8
  310. package/src/tools/descriptors.ts +6 -1
  311. package/src/tools/fetch.ts +7 -2
  312. package/src/tools/image-gen.ts +33 -34
  313. package/src/tools/read.ts +78 -32
  314. package/src/tools/skill-discovery.ts +17 -7
  315. package/src/tools/tool-catalog.generated.ts +8 -8
  316. package/src/tools/ultragoal-ask-guard.ts +16 -26
  317. package/src/utils/changelog.ts +7 -1
  318. package/src/utils/git.ts +3 -1
  319. package/src/utils/markit.ts +24 -2
  320. package/src/utils/mupdf-wasm.ts +26 -0
  321. package/src/web/search/providers/openai-compatible.ts +191 -14
@@ -16,7 +16,9 @@ import type { LoadContext, LoadResult } from "../capability/types";
16
16
  import { rootContainsGjcManifest } from "../extensibility/gjc-plugins";
17
17
  import {
18
18
  type ClaudePluginRoot,
19
+ canonicalizePathWithinHome,
19
20
  createSourceMeta,
21
+ getReadOptionsForContainment,
20
22
  invalidateClaudePluginRoots,
21
23
  listClaudePluginRoots,
22
24
  loadFilesFromDir,
@@ -40,9 +42,20 @@ interface ResolvedPluginDir {
40
42
  warning?: string;
41
43
  }
42
44
 
43
- async function readPluginManifest(root: ClaudePluginRoot): Promise<ClaudePluginManifest | null> {
44
- const manifestPath = path.join(root.path, ".claude-plugin", "plugin.json");
45
- const raw = await readFile(manifestPath);
45
+ async function readPluginManifest(
46
+ root: ClaudePluginRoot,
47
+ ctx: Pick<LoadContext, "home" | "isolatedHome" | "userAgentDir" | "homeIdentity" | "userAgentIdentity">,
48
+ ): Promise<ClaudePluginManifest | null> {
49
+ const manifestPath = await canonicalizePathWithinHome(
50
+ ctx,
51
+ path.join(root.path, ".claude-plugin", "plugin.json"),
52
+ root.path,
53
+ root.scope,
54
+ );
55
+ if (!manifestPath) return null;
56
+ const readOptions = await getReadOptionsForContainment(ctx, root.scope, root.path);
57
+ if (ctx.isolatedHome && !readOptions) return null;
58
+ const raw = await readFile(manifestPath, readOptions);
46
59
  if (raw === null) return null;
47
60
 
48
61
  try {
@@ -60,11 +73,12 @@ function isWithinPluginRoot(rootPath: string, targetPath: string): boolean {
60
73
  }
61
74
 
62
75
  async function resolvePluginDir(
76
+ ctx: Pick<LoadContext, "home" | "isolatedHome" | "userAgentDir" | "homeIdentity" | "userAgentIdentity">,
63
77
  root: ClaudePluginRoot,
64
78
  manifestKeys: ReadonlyArray<keyof ClaudePluginManifest>,
65
79
  fallback: string,
66
80
  ): Promise<ResolvedPluginDir> {
67
- const manifest = await readPluginManifest(root);
81
+ const manifest = await readPluginManifest(root, ctx);
68
82
  const fallbackDir = path.join(root.path, fallback);
69
83
 
70
84
  let configured: string | undefined;
@@ -84,7 +98,8 @@ async function resolvePluginDir(
84
98
 
85
99
  const resolved = path.resolve(root.path, configured);
86
100
  if (isWithinPluginRoot(root.path, resolved)) {
87
- return { dir: resolved };
101
+ const canonical = await canonicalizePathWithinHome(ctx, resolved, root.path, root.scope);
102
+ if (canonical) return { dir: canonical };
88
103
  }
89
104
 
90
105
  return {
@@ -96,16 +111,27 @@ async function resolvePluginDir(
96
111
  async function listNonGjcPluginRoots(
97
112
  home: string,
98
113
  cwd: string,
114
+ isolatedHome: boolean,
115
+ homeIdentity?: LoadContext["homeIdentity"],
99
116
  allowedLevels: ReadonlySet<"user" | "project"> = new Set(["user", "project"]),
100
117
  ): Promise<{ roots: ClaudePluginRoot[]; warnings: string[] }> {
101
- await invalidateClaudePluginRoots(home, cwd);
102
- const { roots, warnings } = await listClaudePluginRoots(home, cwd);
118
+ await invalidateClaudePluginRoots(home, cwd, isolatedHome);
119
+ const { roots, warnings } = await listClaudePluginRoots(home, cwd, isolatedHome, homeIdentity);
103
120
  const filteredRoots: ClaudePluginRoot[] = [];
104
121
  const filteredWarnings = allowedLevels.size === 2 ? [...warnings] : [];
105
122
 
106
123
  for (const root of roots) {
107
124
  if (!allowedLevels.has(root.scope)) continue;
108
- if (await rootContainsGjcManifest(root.path)) {
125
+ const manifestReadOptions = isolatedHome
126
+ ? {
127
+ isolatedHome: true,
128
+ home,
129
+ homeIdentity,
130
+ scope: "project" as const,
131
+ bypassCache: true,
132
+ }
133
+ : undefined;
134
+ if (await rootContainsGjcManifest(root.path, manifestReadOptions)) {
109
135
  filteredWarnings.push(`[claude-plugins] Skipping gajae-code plugin root (binding-only): ${root.path}`);
110
136
  continue;
111
137
  }
@@ -126,17 +152,25 @@ export async function loadMarketplaceSkills(
126
152
  const items: Skill[] = [];
127
153
  const warnings: string[] = [];
128
154
 
129
- const { roots, warnings: rootWarnings } = await listNonGjcPluginRoots(ctx.home, ctx.cwd, allowedLevels);
155
+ const { roots, warnings: rootWarnings } = await listNonGjcPluginRoots(
156
+ ctx.home,
157
+ ctx.cwd,
158
+ ctx.isolatedHome ?? false,
159
+ ctx.homeIdentity,
160
+ allowedLevels,
161
+ );
130
162
  warnings.push(...rootWarnings);
131
163
 
132
164
  const results = await Promise.all(
133
165
  roots.map(async root => {
134
- const { dir: skillsDir, warning } = await resolvePluginDir(root, ["skills"], "skills");
166
+ const { dir: skillsDir, warning } = await resolvePluginDir(ctx, root, ["skills"], "skills");
135
167
  const result = await scanSkillsFromDir(ctx, {
136
168
  dir: skillsDir,
137
169
  providerId: PROVIDER_ID,
138
170
  level: root.scope,
139
171
  requireDescription: true,
172
+ containmentRoot: root.path,
173
+ scope: root.scope,
140
174
  });
141
175
  return { root, result, warning };
142
176
  }),
@@ -162,14 +196,26 @@ async function loadSlashCommands(ctx: LoadContext): Promise<LoadResult<SlashComm
162
196
  const items: SlashCommand[] = [];
163
197
  const warnings: string[] = [];
164
198
 
165
- const { roots, warnings: rootWarnings } = await listNonGjcPluginRoots(ctx.home, ctx.cwd);
199
+ const { roots, warnings: rootWarnings } = await listNonGjcPluginRoots(
200
+ ctx.home,
201
+ ctx.cwd,
202
+ ctx.isolatedHome ?? false,
203
+ ctx.homeIdentity,
204
+ );
166
205
  warnings.push(...rootWarnings);
167
206
 
168
207
  const results = await Promise.all(
169
208
  roots.map(async root => {
170
- const { dir: commandsDir, warning } = await resolvePluginDir(root, ["commands", "slash-commands"], "commands");
209
+ const { dir: commandsDir, warning } = await resolvePluginDir(
210
+ ctx,
211
+ root,
212
+ ["commands", "slash-commands"],
213
+ "commands",
214
+ );
171
215
  const result = await loadFilesFromDir<SlashCommand>(ctx, commandsDir, PROVIDER_ID, root.scope, {
172
216
  extensions: ["md"],
217
+ containmentRoot: root.path,
218
+ scope: root.scope,
173
219
  transform: (name, content, filePath, source) => {
174
220
  const cmdName = name.replace(/\.md$/, "");
175
221
  return {
@@ -202,7 +248,12 @@ async function loadHooks(ctx: LoadContext): Promise<LoadResult<Hook>> {
202
248
  const items: Hook[] = [];
203
249
  const warnings: string[] = [];
204
250
 
205
- const { roots, warnings: rootWarnings } = await listNonGjcPluginRoots(ctx.home, ctx.cwd);
251
+ const { roots, warnings: rootWarnings } = await listNonGjcPluginRoots(
252
+ ctx.home,
253
+ ctx.cwd,
254
+ ctx.isolatedHome ?? false,
255
+ ctx.homeIdentity,
256
+ );
206
257
  warnings.push(...rootWarnings);
207
258
 
208
259
  const hookTypes = ["pre", "post"] as const;
@@ -218,6 +269,8 @@ async function loadHooks(ctx: LoadContext): Promise<LoadResult<Hook>> {
218
269
  loadTasks.map(async ({ root, hookType }) => {
219
270
  const hooksDir = path.join(root.path, "hooks", hookType);
220
271
  return loadFilesFromDir<Hook>(ctx, hooksDir, PROVIDER_ID, root.scope, {
272
+ containmentRoot: root.path,
273
+ scope: root.scope,
221
274
  transform: (name, _content, filePath, source) => {
222
275
  const toolName = name.replace(/\.(sh|bash|zsh|fish)$/, "");
223
276
  return {
@@ -249,13 +302,20 @@ async function loadTools(ctx: LoadContext): Promise<LoadResult<CustomTool>> {
249
302
  const items: CustomTool[] = [];
250
303
  const warnings: string[] = [];
251
304
 
252
- const { roots, warnings: rootWarnings } = await listNonGjcPluginRoots(ctx.home, ctx.cwd);
305
+ const { roots, warnings: rootWarnings } = await listNonGjcPluginRoots(
306
+ ctx.home,
307
+ ctx.cwd,
308
+ ctx.isolatedHome ?? false,
309
+ ctx.homeIdentity,
310
+ );
253
311
  warnings.push(...rootWarnings);
254
312
 
255
313
  const results = await Promise.all(
256
314
  roots.map(async root => {
257
315
  const toolsDir = path.join(root.path, "tools");
258
316
  return loadFilesFromDir<CustomTool>(ctx, toolsDir, PROVIDER_ID, root.scope, {
317
+ containmentRoot: root.path,
318
+ scope: root.scope,
259
319
  transform: (name, _content, filePath, source) => {
260
320
  const toolName = name.replace(/\.(ts|js|sh|bash|py)$/, "");
261
321
  return {
@@ -286,12 +346,20 @@ async function loadMCPServers(ctx: LoadContext): Promise<LoadResult<MCPServer>>
286
346
  const items: MCPServer[] = [];
287
347
  const warnings: string[] = [];
288
348
 
289
- const { roots, warnings: rootWarnings } = await listNonGjcPluginRoots(ctx.home, ctx.cwd);
349
+ const { roots, warnings: rootWarnings } = await listNonGjcPluginRoots(
350
+ ctx.home,
351
+ ctx.cwd,
352
+ ctx.isolatedHome ?? false,
353
+ ctx.homeIdentity,
354
+ );
290
355
  warnings.push(...rootWarnings);
291
356
 
292
357
  for (const root of roots) {
293
- const mcpPath = path.join(root.path, ".mcp.json");
294
- const raw = await readFile(mcpPath);
358
+ const mcpPath = await canonicalizePathWithinHome(ctx, path.join(root.path, ".mcp.json"), root.path, root.scope);
359
+ if (!mcpPath) continue;
360
+ const readOptions = await getReadOptionsForContainment(ctx, root.scope, root.path);
361
+ if (ctx.isolatedHome && !readOptions) continue;
362
+ const raw = await readFile(mcpPath, readOptions);
295
363
  if (raw === null) continue; // file absent — skip silently
296
364
 
297
365
  let parsed: unknown;
@@ -6,21 +6,38 @@
6
6
  */
7
7
  import * as path from "node:path";
8
8
  import { registerProvider } from "../capability";
9
- import { readDirEntries, readFile } from "../capability/fs";
9
+ import { type ReadFileOptions, readDirEntries, readFile } from "../capability/fs";
10
10
  import type { Rule } from "../capability/rule";
11
11
  import { ruleCapability } from "../capability/rule";
12
12
  import type { LoadContext, LoadResult } from "../capability/types";
13
- import { buildRuleFromMarkdown, createSourceMeta, loadFilesFromDir } from "./helpers";
13
+ import {
14
+ buildRuleFromMarkdown,
15
+ canonicalizePathWithinHome,
16
+ createSourceMeta,
17
+ getReadOptions,
18
+ loadFilesFromDir,
19
+ } from "./helpers";
14
20
 
15
21
  const PROVIDER_ID = "cline";
16
22
  const DISPLAY_NAME = "Cline";
17
23
  const PRIORITY = 40;
18
24
 
19
- async function findClinerules(startDir: string): Promise<{ path: string; isDir: boolean } | null> {
25
+ async function findClinerules(
26
+ startDir: string,
27
+ stopAt: string,
28
+ readOptions?: ReadFileOptions,
29
+ ): Promise<{ path: string; isDir: boolean } | null> {
20
30
  let current = path.resolve(startDir);
31
+ const resolvedStop = path.resolve(stopAt);
32
+ const relativeStop = path.relative(resolvedStop, current);
33
+ const effectiveStop =
34
+ relativeStop === "" ||
35
+ (relativeStop !== ".." && !relativeStop.startsWith(`..${path.sep}`) && !path.isAbsolute(relativeStop))
36
+ ? resolvedStop
37
+ : current;
21
38
 
22
39
  while (true) {
23
- const entries = await readDirEntries(current);
40
+ const entries = await readDirEntries(current, readOptions);
24
41
  const entry = entries.find(e => e.name === ".clinerules");
25
42
  if (entry) {
26
43
  return {
@@ -29,7 +46,7 @@ async function findClinerules(startDir: string): Promise<{ path: string; isDir:
29
46
  };
30
47
  }
31
48
  const parent = path.dirname(current);
32
- if (parent === current) return null;
49
+ if (current === effectiveStop || parent === current) return null;
33
50
  current = parent;
34
51
  }
35
52
  }
@@ -42,7 +59,13 @@ async function loadRules(ctx: LoadContext): Promise<LoadResult<Rule>> {
42
59
  const warnings: string[] = [];
43
60
 
44
61
  // Project-level only (Cline uses root-level .clinerules)
45
- const found = await findClinerules(ctx.cwd);
62
+ const homeRelative = path.relative(ctx.home, ctx.cwd);
63
+ const cwdIsWithinHome =
64
+ homeRelative === "" ||
65
+ (homeRelative !== ".." && !homeRelative.startsWith(`..${path.sep}`) && !path.isAbsolute(homeRelative));
66
+ const stopDirectory = ctx.repoRoot ?? (ctx.isolatedHome || cwdIsWithinHome ? ctx.home : path.parse(ctx.cwd).root);
67
+ const readOptions = getReadOptions(ctx, "project");
68
+ const found = await findClinerules(ctx.cwd, stopDirectory, readOptions);
46
69
  if (!found) {
47
70
  return { items, warnings };
48
71
  }
@@ -60,14 +83,19 @@ async function loadRules(ctx: LoadContext): Promise<LoadResult<Rule>> {
60
83
  if (result.warnings) warnings.push(...result.warnings);
61
84
  } else {
62
85
  // Single file format
63
- const content = await readFile(found.path);
86
+ const filePath = await canonicalizePathWithinHome(ctx, found.path, undefined, "project");
87
+ if (!filePath) {
88
+ warnings.push(`Refusing to read .clinerules outside the supplied home at ${found.path}`);
89
+ return { items, warnings };
90
+ }
91
+ const content = await readFile(filePath, readOptions);
64
92
  if (content === null) {
65
- warnings.push(`Failed to read .clinerules at ${found.path}`);
93
+ warnings.push(`Failed to read .clinerules at ${filePath}`);
66
94
  return { items, warnings };
67
95
  }
68
96
 
69
- const source = createSourceMeta(PROVIDER_ID, found.path, "project");
70
- items.push(buildRuleFromMarkdown("clinerules.md", content, found.path, source, { ruleName: "clinerules" }));
97
+ const source = createSourceMeta(PROVIDER_ID, filePath, "project");
98
+ items.push(buildRuleFromMarkdown("clinerules.md", content, filePath, source, { ruleName: "clinerules" }));
71
99
  }
72
100
 
73
101
  return { items, warnings };
@@ -25,7 +25,15 @@ import { ruleCapability } from "../capability/rule";
25
25
  import type { Settings } from "../capability/settings";
26
26
  import { settingsCapability } from "../capability/settings";
27
27
  import type { LoadContext, LoadResult, SourceMeta } from "../capability/types";
28
- import { buildRuleFromMarkdown, createSourceMeta, getProjectPath, getUserPath, loadFilesFromDir } from "./helpers";
28
+ import {
29
+ buildRuleFromMarkdown,
30
+ canonicalizePathWithinHome,
31
+ createSourceMeta,
32
+ getProjectPath,
33
+ getReadOptions,
34
+ getUserPath,
35
+ loadFilesFromDir,
36
+ } from "./helpers";
29
37
 
30
38
  const PROVIDER_ID = "cursor";
31
39
  const DISPLAY_NAME = "Cursor";
@@ -79,15 +87,20 @@ async function loadSettings(ctx: LoadContext): Promise<LoadResult<Settings>> {
79
87
  const items: Settings[] = [];
80
88
  const warnings: string[] = [];
81
89
 
82
- const userPath = getUserPath(ctx, "cursor", "settings.json");
90
+ const rawUserPath = getUserPath(ctx, "cursor", "settings.json");
91
+ const rawProjectPath = getProjectPath(ctx, "cursor", "settings.json");
83
92
 
84
- const [userContent, projectPath] = await Promise.all([
85
- userPath ? readFile(userPath) : Promise.resolve(null),
86
- getProjectPath(ctx, "cursor", "settings.json"),
93
+ const [userPath, projectPath] = await Promise.all([
94
+ rawUserPath ? canonicalizePathWithinHome(ctx, rawUserPath, undefined, "user") : Promise.resolve(null),
95
+ rawProjectPath ? canonicalizePathWithinHome(ctx, rawProjectPath, undefined, "project") : Promise.resolve(null),
87
96
  ]);
88
97
 
89
- const projectContentPromise = projectPath ? readFile(projectPath) : Promise.resolve(null);
98
+ const userContentPromise = userPath ? readFile(userPath, getReadOptions(ctx, "user")) : Promise.resolve(null);
99
+ const projectContentPromise = projectPath
100
+ ? readFile(projectPath, getReadOptions(ctx, "project"))
101
+ : Promise.resolve(null);
90
102
 
103
+ const userContent = await userContentPromise;
91
104
  if (userContent && userPath) {
92
105
  const parsed = tryParseJson<Record<string, unknown>>(userContent);
93
106
  if (parsed) {
@@ -30,9 +30,11 @@ import type { LoadContext, LoadResult } from "../capability/types";
30
30
  import {
31
31
  buildExtensionModuleItems,
32
32
  calculateDepth,
33
+ canonicalizePathWithinHome,
33
34
  createSourceMeta,
34
35
  discoverExtensionModulePaths,
35
36
  getProjectPath,
37
+ getReadOptions,
36
38
  getUserPath,
37
39
  } from "./helpers";
38
40
 
@@ -49,9 +51,12 @@ async function loadContextFiles(ctx: LoadContext): Promise<LoadResult<ContextFil
49
51
  const warnings: string[] = [];
50
52
 
51
53
  // User-level: ~/.gemini/GEMINI.md
52
- const userGeminiMd = getUserPath(ctx, "gemini", "GEMINI.md");
54
+ const rawUserGeminiMd = getUserPath(ctx, "gemini", "GEMINI.md");
55
+ const userGeminiMd = rawUserGeminiMd
56
+ ? await canonicalizePathWithinHome(ctx, rawUserGeminiMd, undefined, "user")
57
+ : null;
53
58
  if (userGeminiMd) {
54
- const content = await readFile(userGeminiMd);
59
+ const content = await readFile(userGeminiMd, getReadOptions(ctx, "user"));
55
60
  if (content) {
56
61
  items.push({
57
62
  path: userGeminiMd,
@@ -63,9 +68,12 @@ async function loadContextFiles(ctx: LoadContext): Promise<LoadResult<ContextFil
63
68
  }
64
69
 
65
70
  // Project-level: .gemini/GEMINI.md
66
- const projectGeminiMd = getProjectPath(ctx, "gemini", "GEMINI.md");
71
+ const rawProjectGeminiMd = getProjectPath(ctx, "gemini", "GEMINI.md");
72
+ const projectGeminiMd = rawProjectGeminiMd
73
+ ? await canonicalizePathWithinHome(ctx, rawProjectGeminiMd, undefined, "project")
74
+ : null;
67
75
  if (projectGeminiMd) {
68
- const content = await readFile(projectGeminiMd);
76
+ const content = await readFile(projectGeminiMd, getReadOptions(ctx, "project"));
69
77
  if (content) {
70
78
  const projectBase = getProjectPath(ctx, "gemini", "");
71
79
  const depth = projectBase ? calculateDepth(ctx.cwd, projectBase, path.sep) : 0;
@@ -94,7 +102,7 @@ async function loadExtensions(ctx: LoadContext): Promise<LoadResult<Extension>>
94
102
  // User-level: ~/.gemini/extensions/*/gemini-extension.json
95
103
  const userExtPath = getUserPath(ctx, "gemini", "extensions");
96
104
  if (userExtPath) {
97
- const result = await loadExtensionsFromDir(userExtPath, "user");
105
+ const result = await loadExtensionsFromDir(ctx, userExtPath, "user");
98
106
  items.push(...result.items);
99
107
  if (result.warnings) warnings.push(...result.warnings);
100
108
  }
@@ -102,7 +110,7 @@ async function loadExtensions(ctx: LoadContext): Promise<LoadResult<Extension>>
102
110
  // Project-level: .gemini/extensions/*/gemini-extension.json
103
111
  const projectExtPath = getProjectPath(ctx, "gemini", "extensions");
104
112
  if (projectExtPath) {
105
- const result = await loadExtensionsFromDir(projectExtPath, "project");
113
+ const result = await loadExtensionsFromDir(ctx, projectExtPath, "project");
106
114
  items.push(...result.items);
107
115
  if (result.warnings) warnings.push(...result.warnings);
108
116
  }
@@ -110,15 +118,35 @@ async function loadExtensions(ctx: LoadContext): Promise<LoadResult<Extension>>
110
118
  return { items, warnings };
111
119
  }
112
120
 
113
- async function loadExtensionsFromDir(extensionsDir: string, level: "user" | "project"): Promise<LoadResult<Extension>> {
114
- const entries = await readDirEntries(extensionsDir);
121
+ async function loadExtensionsFromDir(
122
+ ctx: LoadContext,
123
+ extensionsDir: string,
124
+ level: "user" | "project",
125
+ ): Promise<LoadResult<Extension>> {
126
+ const scope = level === "user" ? "user" : "project";
127
+ const readOptions = getReadOptions(ctx, scope);
128
+ const canonicalExtensionsDir = await canonicalizePathWithinHome(ctx, extensionsDir, undefined, scope);
129
+ if (!canonicalExtensionsDir) return { items: [], warnings: [] };
130
+ const entries = await readDirEntries(canonicalExtensionsDir, readOptions);
115
131
  const dirEntries = entries.filter(entry => entry.isDirectory());
116
132
 
117
133
  const results = await Promise.all(
118
134
  dirEntries.map(async entry => {
119
- const extPath = path.join(extensionsDir, entry.name);
120
- const manifestPath = path.join(extPath, "gemini-extension.json");
121
- const content = await readFile(manifestPath);
135
+ const extPath = await canonicalizePathWithinHome(
136
+ ctx,
137
+ path.join(canonicalExtensionsDir, entry.name),
138
+ undefined,
139
+ scope,
140
+ );
141
+ if (!extPath) return { entry, extPath: null, manifestPath: null, content: null };
142
+ const manifestPath = await canonicalizePathWithinHome(
143
+ ctx,
144
+ path.join(extPath, "gemini-extension.json"),
145
+ undefined,
146
+ scope,
147
+ );
148
+ if (!manifestPath) return { entry, extPath, manifestPath: null, content: null };
149
+ const content = await readFile(manifestPath, readOptions);
122
150
  return { entry, extPath, manifestPath, content };
123
151
  }),
124
152
  );
@@ -127,7 +155,7 @@ async function loadExtensionsFromDir(extensionsDir: string, level: "user" | "pro
127
155
  const warnings: string[] = [];
128
156
 
129
157
  for (const { entry, extPath, manifestPath, content } of results) {
130
- if (!content) continue;
158
+ if (!content || !extPath || !manifestPath) continue;
131
159
 
132
160
  const manifest = tryParseJson<ExtensionManifest>(content);
133
161
  if (!manifest) {
@@ -156,8 +184,10 @@ async function loadExtensionModules(ctx: LoadContext): Promise<LoadResult<Extens
156
184
  const projectExtensionsDir = getProjectPath(ctx, "gemini", "extensions");
157
185
 
158
186
  const [userPaths, projectPaths] = await Promise.all([
159
- userExtensionsDir ? discoverExtensionModulePaths(ctx, userExtensionsDir) : Promise.resolve([]),
160
- projectExtensionsDir ? discoverExtensionModulePaths(ctx, projectExtensionsDir) : Promise.resolve([]),
187
+ userExtensionsDir ? discoverExtensionModulePaths(ctx, userExtensionsDir, { scope: "user" }) : Promise.resolve([]),
188
+ projectExtensionsDir
189
+ ? discoverExtensionModulePaths(ctx, projectExtensionsDir, { scope: "project" })
190
+ : Promise.resolve([]),
161
191
  ]);
162
192
 
163
193
  const items = buildExtensionModuleItems(PROVIDER_ID, userPaths, projectPaths);
@@ -174,9 +204,10 @@ async function loadSettings(ctx: LoadContext): Promise<LoadResult<Settings>> {
174
204
  const warnings: string[] = [];
175
205
 
176
206
  // User-level: ~/.gemini/settings.json
177
- const userPath = getUserPath(ctx, "gemini", "settings.json");
207
+ const rawUserPath = getUserPath(ctx, "gemini", "settings.json");
208
+ const userPath = rawUserPath ? await canonicalizePathWithinHome(ctx, rawUserPath, undefined, "user") : null;
178
209
  if (userPath) {
179
- const content = await readFile(userPath);
210
+ const content = await readFile(userPath, getReadOptions(ctx, "user"));
180
211
  if (content) {
181
212
  const parsed = tryParseJson<Record<string, unknown>>(content);
182
213
  if (parsed) {
@@ -193,9 +224,12 @@ async function loadSettings(ctx: LoadContext): Promise<LoadResult<Settings>> {
193
224
  }
194
225
 
195
226
  // Project-level: .gemini/settings.json
196
- const projectPath = getProjectPath(ctx, "gemini", "settings.json");
227
+ const rawProjectPath = getProjectPath(ctx, "gemini", "settings.json");
228
+ const projectPath = rawProjectPath
229
+ ? await canonicalizePathWithinHome(ctx, rawProjectPath, undefined, "project")
230
+ : null;
197
231
  if (projectPath) {
198
- const content = await readFile(projectPath);
232
+ const content = await readFile(projectPath, getReadOptions(ctx, "project"));
199
233
  if (content) {
200
234
  const parsed = tryParseJson<Record<string, unknown>>(content);
201
235
  if (parsed) {
@@ -234,9 +268,12 @@ async function loadSystemPrompt(ctx: LoadContext): Promise<LoadResult<SystemProm
234
268
  const items: SystemPrompt[] = [];
235
269
 
236
270
  // User-level: ~/.gemini/system.md
237
- const userSystemMd = getUserPath(ctx, "gemini", "system.md");
271
+ const rawUserSystemMd = getUserPath(ctx, "gemini", "system.md");
272
+ const userSystemMd = rawUserSystemMd
273
+ ? await canonicalizePathWithinHome(ctx, rawUserSystemMd, undefined, "user")
274
+ : null;
238
275
  if (userSystemMd) {
239
- const content = await readFile(userSystemMd);
276
+ const content = await readFile(userSystemMd, getReadOptions(ctx, "user"));
240
277
  if (content) {
241
278
  items.push({
242
279
  path: userSystemMd,
@@ -248,9 +285,12 @@ async function loadSystemPrompt(ctx: LoadContext): Promise<LoadResult<SystemProm
248
285
  }
249
286
 
250
287
  // Project-level: .gemini/system.md
251
- const projectSystemMd = getProjectPath(ctx, "gemini", "system.md");
288
+ const rawProjectSystemMd = getProjectPath(ctx, "gemini", "system.md");
289
+ const projectSystemMd = rawProjectSystemMd
290
+ ? await canonicalizePathWithinHome(ctx, rawProjectSystemMd, undefined, "project")
291
+ : null;
252
292
  if (projectSystemMd) {
253
- const content = await readFile(projectSystemMd);
293
+ const content = await readFile(projectSystemMd, getReadOptions(ctx, "project"));
254
294
  if (content) {
255
295
  items.push({
256
296
  path: projectSystemMd,
@@ -19,7 +19,14 @@ import { readFile } from "../capability/fs";
19
19
  import { type Instruction, instructionCapability } from "../capability/instruction";
20
20
  import type { LoadContext, LoadResult, SourceMeta } from "../capability/types";
21
21
 
22
- import { calculateDepth, createSourceMeta, getProjectPath, loadFilesFromDir } from "./helpers";
22
+ import {
23
+ calculateDepth,
24
+ canonicalizePathWithinHome,
25
+ createSourceMeta,
26
+ getProjectPath,
27
+ getReadOptions,
28
+ loadFilesFromDir,
29
+ } from "./helpers";
23
30
 
24
31
  const PROVIDER_ID = "github";
25
32
  const DISPLAY_NAME = "GitHub Copilot";
@@ -33,9 +40,12 @@ async function loadContextFiles(ctx: LoadContext): Promise<LoadResult<ContextFil
33
40
  const items: ContextFile[] = [];
34
41
  const warnings: string[] = [];
35
42
 
36
- const copilotInstructionsPath = getProjectPath(ctx, "github", "copilot-instructions.md");
43
+ const rawCopilotInstructionsPath = getProjectPath(ctx, "github", "copilot-instructions.md");
44
+ const copilotInstructionsPath = rawCopilotInstructionsPath
45
+ ? await canonicalizePathWithinHome(ctx, rawCopilotInstructionsPath, undefined, "project")
46
+ : null;
37
47
  if (copilotInstructionsPath) {
38
- const content = await readFile(copilotInstructionsPath);
48
+ const content = await readFile(copilotInstructionsPath, getReadOptions(ctx, "project"));
39
49
  if (content) {
40
50
  const fileDir = path.dirname(copilotInstructionsPath);
41
51
  const depth = calculateDepth(ctx.cwd, fileDir, path.sep);