@gajae-code/coding-agent 0.16.3 → 0.16.6

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 (277) hide show
  1. package/CHANGELOG.md +113 -0
  2. package/dist/types/capability/types.d.ts +4 -0
  3. package/dist/types/cli/args.d.ts +2 -2
  4. package/dist/types/cli/customize-doctor.d.ts +22 -0
  5. package/dist/types/cli/root-flags.d.ts +4 -2
  6. package/dist/types/cli.d.ts +1 -1
  7. package/dist/types/commands/launch.d.ts +1 -1
  8. package/dist/types/config/autorouting-tier-map.d.ts +3 -0
  9. package/dist/types/config/file-lock.d.ts +31 -3
  10. package/dist/types/config/model-registry.d.ts +4 -0
  11. package/dist/types/config/model-resolver.d.ts +9 -0
  12. package/dist/types/config/settings-schema.d.ts +29 -1
  13. package/dist/types/coordinator-mcp/question-state.d.ts +7 -0
  14. package/dist/types/coordinator-mcp/server.d.ts +7 -0
  15. package/dist/types/discovery/claude-plugins.d.ts +3 -1
  16. package/dist/types/discovery/helpers.d.ts +35 -0
  17. package/dist/types/exec/bash-executor.d.ts +17 -1
  18. package/dist/types/exec/bash-shell-guardian.d.ts +15 -0
  19. package/dist/types/exec/bash-shell-runtime-entry.d.ts +2 -0
  20. package/dist/types/exec/bash-shell-signals.d.ts +2 -0
  21. package/dist/types/exec/bash-shell-supervisor-entry.d.ts +1 -0
  22. package/dist/types/exec/bash-shell-supervisor.d.ts +10 -0
  23. package/dist/types/exec/bash-shell-worker-entry.d.ts +2 -0
  24. package/dist/types/exec/bash-shell-worker-protocol.d.ts +63 -0
  25. package/dist/types/exec/bash-shell-worker.d.ts +1 -0
  26. package/dist/types/exec/isolated-shell.d.ts +19 -0
  27. package/dist/types/extensibility/extensions/function-hooks-internal.d.ts +9 -0
  28. package/dist/types/extensibility/extensions/function-hooks.d.ts +169 -0
  29. package/dist/types/extensibility/extensions/index.d.ts +1 -0
  30. package/dist/types/extensibility/extensions/runner.d.ts +46 -6
  31. package/dist/types/extensibility/extensions/types.d.ts +16 -0
  32. package/dist/types/extensibility/extensions/wrapper.d.ts +3 -1
  33. package/dist/types/extensibility/gjc-plugins/constrained-hooks.d.ts +35 -2
  34. package/dist/types/extensibility/gjc-plugins/mcp-policy.d.ts +16 -0
  35. package/dist/types/extensibility/gjc-plugins/paths.d.ts +2 -0
  36. package/dist/types/extensibility/gjc-plugins/types.d.ts +25 -0
  37. package/dist/types/extensibility/hooks/types.d.ts +4 -0
  38. package/dist/types/extensibility/runtime-skill-discovery.d.ts +5 -2
  39. package/dist/types/extensibility/shared-events.d.ts +1 -1
  40. package/dist/types/extensibility/skill-management.d.ts +9 -4
  41. package/dist/types/extensibility/skills.d.ts +2 -0
  42. package/dist/types/gjc-runtime/empty-delete-gc.d.ts +4 -1
  43. package/dist/types/gjc-runtime/linux-proc.d.ts +2 -0
  44. package/dist/types/gjc-runtime/session-state-lock.d.ts +14 -0
  45. package/dist/types/gjc-runtime/ultragoal-runtime.d.ts +41 -3
  46. package/dist/types/gjc-runtime/ultragoal-succession.d.ts +251 -0
  47. package/dist/types/modes/components/assistant-message.d.ts +1 -2
  48. package/dist/types/modes/components/bash-execution.d.ts +1 -0
  49. package/dist/types/modes/components/execution-shared.d.ts +1 -0
  50. package/dist/types/modes/components/settings-selector.d.ts +6 -1
  51. package/dist/types/modes/components/status-line/command.d.ts +35 -0
  52. package/dist/types/modes/components/status-line/index.d.ts +1 -0
  53. package/dist/types/modes/components/status-line/types.d.ts +5 -0
  54. package/dist/types/modes/components/tool-status-header.d.ts +14 -0
  55. package/dist/types/modes/controllers/event-controller.d.ts +6 -0
  56. package/dist/types/modes/interactive-mode.d.ts +3 -0
  57. package/dist/types/modes/types.d.ts +7 -0
  58. package/dist/types/runtime-mcp/client.d.ts +9 -1
  59. package/dist/types/runtime-mcp/manager.d.ts +7 -1
  60. package/dist/types/runtime-mcp/pool.d.ts +22 -3
  61. package/dist/types/runtime-mcp/transports/stdio.d.ts +0 -4
  62. package/dist/types/runtime-mcp/types.d.ts +22 -0
  63. package/dist/types/sdk/broker/ensure.d.ts +32 -0
  64. package/dist/types/sdk/broker/lifecycle-ledger.d.ts +0 -1
  65. package/dist/types/sdk/broker/lifecycle.d.ts +6 -0
  66. package/dist/types/sdk/broker/startup-failure.d.ts +4 -1
  67. package/dist/types/sdk/bus/chat-daemon-control.d.ts +2 -0
  68. package/dist/types/sdk/bus/chat-daemon-runtime.d.ts +12 -0
  69. package/dist/types/sdk/bus/chat-effect-journal.d.ts +17 -2
  70. package/dist/types/sdk/bus/conversation-store.d.ts +11 -0
  71. package/dist/types/sdk/bus/lifecycle-commands.d.ts +8 -8
  72. package/dist/types/sdk/bus/slack-daemon.d.ts +1 -1
  73. package/dist/types/sdk/bus/slack-live-provider.d.ts +6 -0
  74. package/dist/types/sdk/bus/slack-provider.d.ts +12 -0
  75. package/dist/types/sdk/bus/topic-registry.d.ts +8 -0
  76. package/dist/types/sdk/client/client.d.ts +6 -0
  77. package/dist/types/sdk/host/host.d.ts +3 -1
  78. package/dist/types/sdk/host/session-runtime.d.ts +26 -1
  79. package/dist/types/sdk/lifecycle/service.d.ts +1 -0
  80. package/dist/types/sdk/router/session-router.d.ts +10 -2
  81. package/dist/types/sdk/session-api-key.d.ts +26 -0
  82. package/dist/types/sdk/session.d.ts +5 -1
  83. package/dist/types/sdk/transport/serve-cli.d.ts +1 -1
  84. package/dist/types/session/agent-session.d.ts +11 -0
  85. package/dist/types/session/fallback-chain-controller.d.ts +15 -0
  86. package/dist/types/session/internal/managed-session-storage.d.ts +7 -0
  87. package/dist/types/session/messages.d.ts +1 -0
  88. package/dist/types/session/session-manager.d.ts +3 -1
  89. package/dist/types/setup/paseo/announce.d.ts +112 -0
  90. package/dist/types/setup/paseo/setup-deps.d.ts +2 -1
  91. package/dist/types/skill-state/canonical-skills.d.ts +4 -0
  92. package/dist/types/system-prompt.d.ts +10 -0
  93. package/dist/types/task/executor.d.ts +5 -1
  94. package/dist/types/tools/bash.d.ts +1 -0
  95. package/dist/types/tools/fetch.d.ts +7 -0
  96. package/dist/types/tools/github-cache.d.ts +2 -1
  97. package/dist/types/tools/index.d.ts +10 -0
  98. package/dist/types/tools/path-utils.d.ts +2 -0
  99. package/dist/types/tools/search.d.ts +6 -0
  100. package/package.json +9 -7
  101. package/scripts/capture-status-line-custom-editor-showcase.ts +151 -0
  102. package/scripts/generate-sdk-operation-inventory.ts +11 -0
  103. package/scripts/verify-status-line-custom-editor-showcase.ts +309 -0
  104. package/src/capability/index.ts +14 -2
  105. package/src/capability/types.ts +4 -0
  106. package/src/cli/args.ts +20 -9
  107. package/src/cli/customize-doctor.ts +155 -12
  108. package/src/cli/root-flags.ts +5 -3
  109. package/src/cli.ts +22 -0
  110. package/src/commands/sdk.ts +74 -31
  111. package/src/config/autorouting-tier-map.ts +1 -0
  112. package/src/config/file-lock-gc.ts +40 -2
  113. package/src/config/file-lock.ts +379 -31
  114. package/src/config/model-preset-registry.ts +120 -19
  115. package/src/config/model-profiles.ts +40 -0
  116. package/src/config/model-registry.ts +73 -23
  117. package/src/config/model-resolver.ts +48 -0
  118. package/src/config/settings-schema.ts +33 -1
  119. package/src/config/settings.ts +1 -0
  120. package/src/coordinator-mcp/question-state.ts +33 -2
  121. package/src/coordinator-mcp/server.ts +477 -101
  122. package/src/crash/sanitize.ts +1 -1
  123. package/src/defaults/gjc/extensions/grok-cli-vendor/src/models/catalog.ts +11 -11
  124. package/src/defaults/gjc/extensions/grok-cli-vendor/src/provider/register.ts +13 -2
  125. package/src/defaults/gjc/skills/ultragoal/SKILL.md +22 -0
  126. package/src/discovery/builtin.ts +52 -55
  127. package/src/discovery/claude-plugins.ts +12 -4
  128. package/src/discovery/helpers.ts +307 -34
  129. package/src/exec/bash-executor.ts +330 -50
  130. package/src/exec/bash-shell-guardian.ts +388 -0
  131. package/src/exec/bash-shell-runtime-entry.ts +5 -0
  132. package/src/exec/bash-shell-signals.ts +29 -0
  133. package/src/exec/bash-shell-supervisor-entry.ts +3 -0
  134. package/src/exec/bash-shell-supervisor.ts +359 -0
  135. package/src/exec/bash-shell-worker-entry.ts +5 -0
  136. package/src/exec/bash-shell-worker-protocol.ts +36 -0
  137. package/src/exec/bash-shell-worker.ts +116 -0
  138. package/src/exec/isolated-shell.ts +393 -0
  139. package/src/extensibility/extensions/function-hooks-internal.ts +73 -0
  140. package/src/extensibility/extensions/function-hooks.ts +959 -0
  141. package/src/extensibility/extensions/index.ts +22 -0
  142. package/src/extensibility/extensions/loader.ts +53 -2
  143. package/src/extensibility/extensions/runner.ts +895 -264
  144. package/src/extensibility/extensions/types.ts +42 -0
  145. package/src/extensibility/extensions/wrapper.ts +56 -15
  146. package/src/extensibility/gjc-plugins/compiler.ts +228 -60
  147. package/src/extensibility/gjc-plugins/constrained-hooks.ts +321 -23
  148. package/src/extensibility/gjc-plugins/installer.ts +13 -12
  149. package/src/extensibility/gjc-plugins/mcp-policy.ts +81 -52
  150. package/src/extensibility/gjc-plugins/paths.ts +13 -0
  151. package/src/extensibility/gjc-plugins/registry.ts +113 -1
  152. package/src/extensibility/gjc-plugins/runtime-adapters.ts +758 -16
  153. package/src/extensibility/gjc-plugins/runtime-quarantine.ts +20 -2
  154. package/src/extensibility/gjc-plugins/schema.ts +121 -4
  155. package/src/extensibility/gjc-plugins/types.ts +25 -0
  156. package/src/extensibility/hooks/loader.ts +133 -16
  157. package/src/extensibility/hooks/types.ts +28 -0
  158. package/src/extensibility/runtime-skill-discovery.ts +149 -190
  159. package/src/extensibility/shared-events.ts +1 -1
  160. package/src/extensibility/skill-management.ts +200 -52
  161. package/src/extensibility/skills.ts +73 -25
  162. package/src/gjc-runtime/empty-delete-gc.ts +94 -4
  163. package/src/gjc-runtime/gc-runtime.ts +29 -0
  164. package/src/gjc-runtime/linux-proc.ts +14 -0
  165. package/src/gjc-runtime/repository-binding.ts +1 -1
  166. package/src/gjc-runtime/session-state-lock.ts +143 -10
  167. package/src/gjc-runtime/session-state-sidecar.ts +27 -11
  168. package/src/gjc-runtime/state-writer.ts +30 -29
  169. package/src/gjc-runtime/ultragoal-runtime.ts +189 -47
  170. package/src/gjc-runtime/ultragoal-succession.ts +1667 -0
  171. package/src/gjc-runtime/workflow-manifest.generated.json +52 -1
  172. package/src/gjc-runtime/workflow-manifest.ts +16 -0
  173. package/src/harness-control-plane/session-lease.ts +2 -2
  174. package/src/internal-urls/docs-index.generated.ts +12 -12
  175. package/src/lsp/config.ts +10 -3
  176. package/src/lsp/index.ts +1 -1
  177. package/src/lsp/path-trust.ts +69 -14
  178. package/src/main.ts +78 -35
  179. package/src/modes/DESIGN.md +54 -0
  180. package/src/modes/acp/acp-agent.ts +16 -3
  181. package/src/modes/components/assistant-message.ts +22 -9
  182. package/src/modes/components/bash-execution.ts +4 -1
  183. package/src/modes/components/execution-shared.ts +4 -1
  184. package/src/modes/components/model-selector.ts +166 -33
  185. package/src/modes/components/session-observer-overlay.ts +1 -1
  186. package/src/modes/components/settings-selector.ts +1075 -235
  187. package/src/modes/components/status-line/command.ts +190 -0
  188. package/src/modes/components/status-line/index.ts +1 -0
  189. package/src/modes/components/status-line/segments.ts +32 -5
  190. package/src/modes/components/status-line/types.ts +5 -0
  191. package/src/modes/components/tool-status-header.ts +396 -22
  192. package/src/modes/controllers/command-controller.ts +3 -0
  193. package/src/modes/controllers/event-controller.ts +151 -7
  194. package/src/modes/controllers/extension-ui-controller.ts +1 -0
  195. package/src/modes/controllers/input-controller.ts +6 -3
  196. package/src/modes/controllers/selector-controller.ts +21 -0
  197. package/src/modes/interactive-mode.ts +53 -6
  198. package/src/modes/print-mode.ts +5 -1
  199. package/src/modes/prompt-action-autocomplete.ts +54 -27
  200. package/src/modes/runtime-init.ts +2 -0
  201. package/src/modes/types.ts +17 -3
  202. package/src/modes/utils/ui-helpers.ts +2 -0
  203. package/src/prompts/system/system-prompt.md +8 -0
  204. package/src/prompts/tools/search.md +2 -0
  205. package/src/prompts/tools/subagent.md +1 -0
  206. package/src/runtime-mcp/client.ts +60 -17
  207. package/src/runtime-mcp/manager.ts +189 -14
  208. package/src/runtime-mcp/pool.test.ts +218 -0
  209. package/src/runtime-mcp/pool.ts +224 -23
  210. package/src/runtime-mcp/transports/stdio.ts +158 -24
  211. package/src/runtime-mcp/types.ts +24 -0
  212. package/src/sdk/broker/broker.ts +124 -32
  213. package/src/sdk/broker/ensure.ts +248 -41
  214. package/src/sdk/broker/lifecycle-ledger.ts +7 -17
  215. package/src/sdk/broker/lifecycle.ts +56 -6
  216. package/src/sdk/broker/startup-failure.ts +15 -6
  217. package/src/sdk/bus/chat-daemon-control.ts +3 -1
  218. package/src/sdk/bus/chat-daemon-runtime.ts +72 -6
  219. package/src/sdk/bus/chat-effect-journal.ts +132 -7
  220. package/src/sdk/bus/conversation-store.ts +29 -0
  221. package/src/sdk/bus/lifecycle-commands.ts +17 -11
  222. package/src/sdk/bus/slack-conversation.ts +1 -0
  223. package/src/sdk/bus/slack-daemon.ts +478 -49
  224. package/src/sdk/bus/slack-live-provider.ts +12 -0
  225. package/src/sdk/bus/slack-provider.ts +5 -0
  226. package/src/sdk/bus/telegram-daemon.ts +16 -2
  227. package/src/sdk/bus/topic-registry.ts +19 -0
  228. package/src/sdk/cli/master-cli.ts +1 -1
  229. package/src/sdk/cli/session-cli.ts +11 -8
  230. package/src/sdk/client/client.ts +66 -17
  231. package/src/sdk/host/host.ts +29 -9
  232. package/src/sdk/host/query/revision-store.ts +19 -13
  233. package/src/sdk/host/reverse-leases.ts +7 -4
  234. package/src/sdk/host/session-runtime.test.ts +52 -10
  235. package/src/sdk/host/session-runtime.ts +383 -44
  236. package/src/sdk/lifecycle/service.ts +4 -0
  237. package/src/sdk/protocol/operation-inventory.generated.json +77 -0
  238. package/src/sdk/router/session-router.ts +89 -38
  239. package/src/sdk/session-api-key.ts +94 -0
  240. package/src/sdk/session.ts +228 -55
  241. package/src/sdk/transport/relay.ts +35 -13
  242. package/src/sdk/transport/serve-cli.ts +3 -3
  243. package/src/session/agent-session.ts +240 -90
  244. package/src/session/contribution-prep.ts +214 -21
  245. package/src/session/fallback-chain-controller.ts +38 -0
  246. package/src/session/internal/managed-session-storage.ts +19 -5
  247. package/src/session/messages.ts +5 -0
  248. package/src/session/session-manager.ts +29 -5
  249. package/src/session-import/redact.ts +5 -1
  250. package/src/setup/paseo/announce.ts +456 -0
  251. package/src/setup/paseo/setup-deps.ts +15 -3
  252. package/src/skill-state/canonical-skills.ts +12 -0
  253. package/src/skills/skills.test.ts +41 -21
  254. package/src/slash-commands/builtin-registry.ts +1 -0
  255. package/src/system-prompt.ts +29 -3
  256. package/src/task/executor.ts +7 -1
  257. package/src/task/index.ts +21 -4
  258. package/src/tools/bash.ts +63 -19
  259. package/src/tools/cron.ts +1 -1
  260. package/src/tools/fetch.ts +29 -1
  261. package/src/tools/gh.ts +26 -3
  262. package/src/tools/github-cache.ts +68 -16
  263. package/src/tools/index.ts +10 -0
  264. package/src/tools/job.ts +21 -4
  265. package/src/tools/monitor.ts +8 -2
  266. package/src/tools/path-utils.ts +7 -0
  267. package/src/tools/read.ts +14 -2
  268. package/src/tools/render-utils.ts +1 -3
  269. package/src/tools/resource-gc.ts +28 -8
  270. package/src/tools/search.ts +116 -58
  271. package/src/tools/skill-discovery.ts +5 -0
  272. package/src/tools/skill.ts +37 -2
  273. package/src/tools/subagent.ts +6 -3
  274. package/src/tools/tool-catalog.generated.ts +10 -3
  275. package/src/tools/ultragoal-ask-guard.ts +2 -2
  276. package/src/web/insane/bridge.ts +113 -23
  277. package/src/web/search/providers/perplexity.ts +102 -41
@@ -1,16 +1,31 @@
1
1
  import * as fs from "node:fs/promises";
2
2
  import * as path from "node:path";
3
- import { getPluginsDir, getTrustedHomeDir } from "@gajae-code/utils";
4
- import { findRepoRoot, invalidate as invalidateFsCache } from "../capability/fs";
3
+ import {
4
+ getAgentDir,
5
+ getAgentProfileAuthority,
6
+ getTrustedHomeDir,
7
+ normalizePathForComparison,
8
+ } from "@gajae-code/utils";
9
+ import { findRepoRoot } from "../capability/fs";
5
10
  import type { Skill as CapabilitySkill } from "../capability/skill";
6
11
  import type { SkillsSettings } from "../config/settings-schema";
7
12
  import { resolveSkillScopeTrust } from "../config/skill-settings-defaults";
8
13
  import { scanClaudeProjectSkills, scanClaudeUserSkills } from "../discovery/claude";
14
+ import { loadMarketplaceSkills } from "../discovery/claude-plugins";
9
15
  import { scanCodexProjectSkills, scanCodexUserSkills } from "../discovery/codex";
10
- import { compareSkillOrder, listClaudePluginRoots, SOURCE_PATHS, scanSkillsFromDir } from "../discovery/helpers";
11
- import { CANONICAL_GJC_WORKFLOW_SKILLS } from "../skill-state/canonical-skills";
16
+ import {
17
+ compareSkillOrder,
18
+ getAncestorDirs,
19
+ getUserSkillScanDirs,
20
+ SOURCE_PATHS,
21
+ scanSkillsFromDir,
22
+ } from "../discovery/helpers";
23
+ import {
24
+ getSkillFilesystemIdentity,
25
+ isCanonicalGjcWorkflowSkillFilesystemAlias,
26
+ } from "../skill-state/canonical-skills";
12
27
  import { expandTilde } from "../tools/path-utils";
13
- import type { Skill } from "./skills";
28
+ import { loadSkills, type Skill } from "./skills";
14
29
 
15
30
  export type RuntimeSkillDiscoverySource = "project" | "user";
16
31
 
@@ -40,6 +55,9 @@ export interface RuntimeSkillDiscoveryResult {
40
55
  export interface DiscoverRuntimeSkillsOptions {
41
56
  cwd: string;
42
57
  home?: string;
58
+ agentDir?: string;
59
+ /** Resolver-owned profile classification; unlike path comparison, this survives HOME refreshes. */
60
+ profileAuthority?: "default" | "custom";
43
61
  query?: string;
44
62
  limit?: number;
45
63
  source?: RuntimeSkillDiscoverySource | "all";
@@ -53,8 +71,6 @@ function getRuntimeHome(): string {
53
71
  const DEFAULT_LIMIT = 20;
54
72
  const MAX_LIMIT = 50;
55
73
  const MAX_DIAGNOSTICS = 10;
56
- const BUILT_IN_SKILL_NAMES = new Set<string>(CANONICAL_GJC_WORKFLOW_SKILLS);
57
-
58
74
  function normalizeLimit(limit: number | undefined): number {
59
75
  if (limit === undefined || !Number.isFinite(limit)) return DEFAULT_LIMIT;
60
76
  return Math.max(1, Math.min(MAX_LIMIT, Math.trunc(limit)));
@@ -81,41 +97,19 @@ async function getProjectSkillDirs(
81
97
  ): Promise<{ scans: ProjectScanDir[]; repoRoot: string | null }> {
82
98
  const scans: ProjectScanDir[] = [];
83
99
  const repoRoot = await findRepoRoot(cwd);
84
- const walkDirs = ancestorDirs(cwd, path.resolve(repoRoot ?? cwd), home);
85
- for (const dir of walkDirs) {
100
+ const walkDirs = getAncestorDirs(cwd, path.resolve(repoRoot ?? cwd), home);
101
+ for (const { dir } of walkDirs) {
86
102
  scans.push({ dir: path.join(dir, ".gjc", "skills"), label: "project .gjc/skills" });
87
103
  }
88
104
  return { scans, repoRoot };
89
105
  }
90
106
 
91
- /** Ancestor directories from `cwd` (inclusive) up to `stop` (inclusive), excluding `home`. */
92
- function ancestorDirs(cwd: string, stop: string, home: string): string[] {
93
- const dirs: string[] = [];
94
- let current = path.resolve(cwd);
95
- const resolvedStop = path.resolve(stop);
96
- const resolvedHome = path.resolve(home);
97
- while (true) {
98
- if (current !== resolvedHome) {
99
- dirs.push(current);
100
- }
101
- if (current === resolvedStop) break;
102
- const parent = path.dirname(current);
103
- if (parent === current) break;
104
- current = parent;
105
- }
106
- return dirs;
107
+ function getUserSkillDirs(home: string, agentDir = getAgentDir(), profileAuthority?: "default" | "custom"): string[] {
108
+ return getUserSkillScanDirs(home, agentDir, profileAuthority);
107
109
  }
108
110
 
109
- function getUserSkillDirs(home: string): string[] {
110
- const canonicalUserDir = SOURCE_PATHS.native.userAgent;
111
- const configuredLegacyDir = SOURCE_PATHS.native.userBase;
112
- return [
113
- ...new Set([
114
- path.join(home, canonicalUserDir, "skills"),
115
- path.join(home, configuredLegacyDir, "skills"),
116
- path.join(home, ".gjc", "skills"),
117
- ]),
118
- ];
111
+ function resolveRuntimeAgentDir(home: string, agentDir: string | undefined, homeWasInjected: boolean): string {
112
+ return agentDir ?? (homeWasInjected ? path.join(home, SOURCE_PATHS.native.userAgent) : getAgentDir());
119
113
  }
120
114
 
121
115
  /**
@@ -152,18 +146,6 @@ function getUseWhen(skill: CapabilitySkill): string[] | undefined {
152
146
  return values.length > 0 ? values : undefined;
153
147
  }
154
148
 
155
- function toRuntimeSkill(skill: CapabilitySkill, source: RuntimeSkillDiscoverySource): Skill {
156
- return {
157
- name: skill.name,
158
- description: typeof skill.frontmatter?.description === "string" ? skill.frontmatter.description : "",
159
- filePath: skill.path,
160
- baseDir: skill.path.replace(/[\\/]SKILL\.md$/, ""),
161
- source: `runtime:${source}`,
162
- hide: skill.frontmatter?.hide === true,
163
- _source: { ...skill._source, providerName: "Runtime skill discovery" },
164
- };
165
- }
166
-
167
149
  function sourceEnabled(source: RuntimeSkillDiscoverySource, policy: SkillsSettings | undefined): boolean {
168
150
  if (policy?.enabled !== true) return false;
169
151
  return resolveSkillScopeTrust(policy, source);
@@ -202,13 +184,13 @@ async function realPathOrSelf(filePath: string): Promise<string> {
202
184
  }
203
185
 
204
186
  interface ScanJobResult {
205
- items: Array<{ skill: CapabilitySkill; source: RuntimeSkillDiscoverySource }>;
187
+ items: Array<{ skill: CapabilitySkill; source: RuntimeSkillDiscoverySource; providerPriority: number }>;
206
188
  warnings: string[];
207
189
  label: string;
208
190
  }
209
191
 
210
192
  function isAllowedByPolicy(skill: CapabilitySkill, policy: SkillsSettings | undefined, diagnostics: string[]): boolean {
211
- if (BUILT_IN_SKILL_NAMES.has(skill.name)) {
193
+ if (isCanonicalGjcWorkflowSkillFilesystemAlias(skill.name)) {
212
194
  pushDiagnostic(
213
195
  diagnostics,
214
196
  `skill "${skill.name}" is a bundled GJC workflow skill and always resolves to the bundled definition; the filesystem copy at ${skill.path} is shadowed`,
@@ -234,23 +216,16 @@ function pushDiagnostic(diagnostics: string[], message: string): void {
234
216
  if (diagnostics.length < MAX_DIAGNOSTICS) diagnostics.push(message);
235
217
  }
236
218
 
237
- async function collectPluginSkillDirs(
219
+ async function collectPluginSkills(
238
220
  home: string,
239
221
  cwd: string,
240
- ): Promise<Array<{ dir: string; pluginName: string; level: "user" | "project" }>> {
241
- // Ensure the underlying installed_plugins.json fs cache is current — install/upgrade
242
- // writers already invalidate via clearPluginRootsAndCaches; this is a defensive
243
- // fallback so a stale in-memory cache cannot mask a disabled/removed plugin.
244
- invalidateFsCache(`${getPluginsDir(home)}/installed_plugins.json`);
222
+ allowedLevels: ReadonlySet<"user" | "project">,
223
+ ): Promise<{ items: CapabilitySkill[]; warnings: string[] }> {
245
224
  try {
246
- const { roots } = await listClaudePluginRoots(home, cwd);
247
- return roots.map(root => ({
248
- dir: `${root.path}/skills`,
249
- pluginName: root.plugin,
250
- level: root.scope,
251
- }));
252
- } catch {
253
- return [];
225
+ const result = await loadMarketplaceSkills({ cwd, home, repoRoot: await findRepoRoot(cwd) }, allowedLevels);
226
+ return { items: result.items, warnings: result.warnings ?? [] };
227
+ } catch (error) {
228
+ return { items: [], warnings: [`marketplace skill scan failed: ${String(error)}`] };
254
229
  }
255
230
  }
256
231
 
@@ -287,15 +262,18 @@ async function scanProjectOrUserDir(
287
262
  level: "project" | "user",
288
263
  label: string,
289
264
  source: RuntimeSkillDiscoverySource,
265
+ providerPriority: number,
266
+ authorityRoot?: string,
290
267
  ): Promise<ScanJobResult> {
291
268
  const result = await scanSkillsFromDir(ctx, {
292
269
  dir,
270
+ authorityRoot,
293
271
  providerId: "runtime",
294
272
  level,
295
273
  requireDescription: true,
296
274
  });
297
275
  return {
298
- items: result.items.map(skill => ({ skill, source })),
276
+ items: result.items.map(skill => ({ skill, source, providerPriority })),
299
277
  warnings: result.warnings ?? [],
300
278
  label,
301
279
  };
@@ -304,6 +282,7 @@ async function scanProjectOrUserDir(
304
282
  interface ConventionImportScan {
305
283
  host: "Claude Code" | "Codex";
306
284
  dir: string;
285
+ scope: RuntimeSkillDiscoverySource;
307
286
  skills: CapabilitySkill[];
308
287
  }
309
288
 
@@ -326,20 +305,20 @@ async function collectConventionImportCandidates(
326
305
  if ((source === "all" || source === "project") && sourceEnabled("project", policy)) {
327
306
  jobs.push(
328
307
  scanClaudeProjectSkills(ctx).then(result => {
329
- scans.push({ host: "Claude Code", dir: ".claude/skills", skills: result.items });
308
+ scans.push({ host: "Claude Code", dir: ".claude/skills", scope: "project", skills: result.items });
330
309
  }),
331
310
  scanCodexProjectSkills(ctx).then(result => {
332
- scans.push({ host: "Codex", dir: ".codex/skills", skills: result.items });
311
+ scans.push({ host: "Codex", dir: ".codex/skills", scope: "project", skills: result.items });
333
312
  }),
334
313
  );
335
314
  }
336
315
  if ((source === "all" || source === "user") && sourceEnabled("user", policy)) {
337
316
  jobs.push(
338
317
  scanClaudeUserSkills(ctx).then(result => {
339
- scans.push({ host: "Claude Code", dir: "~/.claude/skills", skills: result.items });
318
+ scans.push({ host: "Claude Code", dir: "~/.claude/skills", scope: "user", skills: result.items });
340
319
  }),
341
320
  scanCodexUserSkills(ctx).then(result => {
342
- scans.push({ host: "Codex", dir: "~/.codex/skills", skills: result.items });
321
+ scans.push({ host: "Codex", dir: "~/.codex/skills", scope: "user", skills: result.items });
343
322
  }),
344
323
  );
345
324
  }
@@ -353,10 +332,17 @@ function reportConventionImportCandidates(
353
332
  seenNames: Set<string>,
354
333
  diagnostics: string[],
355
334
  ): void {
356
- for (const scan of scans.sort((a, b) => a.host.localeCompare(b.host) || a.dir.localeCompare(b.dir))) {
335
+ // Project scope is reported first: the bounded diagnostic budget must not be
336
+ // spent on a populated user-home convention directory (hundreds of skills is
337
+ // ordinary) before a project convention skill is ever named.
338
+ const scopeRank = (scan: ConventionImportScan) => (scan.scope === "project" ? 0 : 1);
339
+ for (const scan of scans.sort(
340
+ (a, b) => scopeRank(a) - scopeRank(b) || a.host.localeCompare(b.host) || a.dir.localeCompare(b.dir),
341
+ )) {
357
342
  for (const skill of scan.skills) {
358
- if (seenNames.has(skill.name)) continue;
359
- seenNames.add(skill.name);
343
+ const normalizedName = getSkillFilesystemIdentity(skill.name);
344
+ if (seenNames.has(normalizedName)) continue;
345
+ seenNames.add(normalizedName);
360
346
  pushDiagnostic(
361
347
  diagnostics,
362
348
  `skill "${skill.name}" found at ${skill.path} (${scan.host} convention): import sources are not loaded directly; copy it into a trusted .gjc/skills directory to enable it, e.g. mkdir -p .gjc/skills/${skill.name} && cp ${skill.path} .gjc/skills/${skill.name}/SKILL.md`,
@@ -396,64 +382,73 @@ export function describeDisabledSkillScopes(
396
382
  export async function discoverRuntimeSkills(
397
383
  options: DiscoverRuntimeSkillsOptions,
398
384
  ): Promise<RuntimeSkillDiscoveryResult> {
385
+ const hasExplicitHome = options.home !== undefined;
399
386
  const home = options.home ?? getRuntimeHome();
400
387
  const source = options.source ?? "all";
401
388
  const policy = options.policy;
402
389
  const diagnostics: string[] = [];
390
+ const agentDir = resolveRuntimeAgentDir(home, options.agentDir, hasExplicitHome);
391
+ const profileAuthority =
392
+ options.profileAuthority ??
393
+ (!hasExplicitHome
394
+ ? options.agentDir !== undefined &&
395
+ normalizePathForComparison(options.agentDir) !== normalizePathForComparison(getAgentDir())
396
+ ? "custom"
397
+ : getAgentProfileAuthority()
398
+ : undefined);
403
399
  const scanJobs: Array<Promise<ScanJobResult>> = [];
404
400
  const projectDirs = await getProjectSkillDirs(options.cwd, home);
405
401
  const projectContext = { cwd: options.cwd, home, repoRoot: projectDirs.repoRoot };
406
402
  if ((source === "all" || source === "project") && sourceEnabled("project", policy)) {
407
403
  for (const { dir, label } of projectDirs.scans) {
408
- scanJobs.push(scanProjectOrUserDir(projectContext, dir, "project", label, "project"));
404
+ scanJobs.push(scanProjectOrUserDir(projectContext, dir, "project", label, "project", 100));
409
405
  }
410
406
  }
411
407
  if ((source === "all" || source === "user") && sourceEnabled("user", policy)) {
412
- for (const dir of getUserSkillDirs(home)) {
408
+ for (const dir of getUserSkillDirs(home, agentDir, profileAuthority)) {
413
409
  scanJobs.push(
414
- scanProjectOrUserDir({ cwd: options.cwd, home, repoRoot: home }, dir, "user", `user ${dir}`, "user"),
410
+ scanProjectOrUserDir(
411
+ { cwd: options.cwd, home, repoRoot: home },
412
+ dir,
413
+ "user",
414
+ `user ${dir}`,
415
+ "user",
416
+ 100,
417
+ path.resolve(dir) === path.resolve(agentDir, "skills") ? agentDir : undefined,
418
+ ),
415
419
  );
416
420
  }
417
421
  }
418
422
  if ((source === "all" || source === "user") && policy?.enabled === true) {
419
423
  for (const dir of getCustomSkillDirs(policy, home)) {
420
424
  scanJobs.push(
421
- scanProjectOrUserDir({ cwd: options.cwd, home, repoRoot: home }, dir, "user", `custom ${dir}`, "user"),
425
+ scanProjectOrUserDir({ cwd: options.cwd, home, repoRoot: home }, dir, "user", `custom ${dir}`, "user", 0),
422
426
  );
423
427
  }
424
428
  }
425
429
 
426
- // Marketplace plugin skills: version-stable, enabled-only.
427
- // Uses the same trust gate as native user/project scopes and the same
428
- // discovery root (<installPath>/skills) the capability provider uses,
429
- // namespaced as `plugin:skillName`.
430
+ // Marketplace plugin skills come from the same provider used to populate
431
+ // session.skills, so discovery, slash commands, SDK state, and exact-name
432
+ // invocation cannot drift on manifest paths, namespacing, or enabled roots.
430
433
  if (policy?.enabled === true) {
431
- const pluginDirs = await collectPluginSkillDirs(home, options.cwd);
432
- for (const entry of pluginDirs) {
433
- if (entry.level === "project" && !(source === "all" || source === "project")) continue;
434
- if (entry.level === "user" && !(source === "all" || source === "user")) continue;
435
- if (!sourceEnabled(entry.level, policy)) continue;
436
- const label = `plugin ${entry.pluginName} skills`;
437
- scanJobs.push(
438
- (async () => {
439
- const pluginCtx = { cwd: options.cwd, home, repoRoot: home };
440
- const result = await scanSkillsFromDir(pluginCtx, {
441
- dir: entry.dir,
442
- providerId: "plugin",
443
- level: entry.level,
444
- requireDescription: true,
445
- });
446
- for (const skill of result.items) {
447
- skill.name = `${entry.pluginName}:${skill.name}`;
448
- }
449
- return {
450
- items: result.items.map(skill => ({ skill, source: entry.level as RuntimeSkillDiscoverySource })),
451
- warnings: result.warnings ?? [],
452
- label,
453
- };
454
- })(),
455
- );
434
+ const allowedLevels = new Set<"user" | "project">();
435
+ if ((source === "all" || source === "project") && sourceEnabled("project", policy)) {
436
+ allowedLevels.add("project");
437
+ }
438
+ if ((source === "all" || source === "user") && sourceEnabled("user", policy)) {
439
+ allowedLevels.add("user");
456
440
  }
441
+ scanJobs.push(
442
+ collectPluginSkills(home, options.cwd, allowedLevels).then(result => ({
443
+ items: result.items.map(skill => ({
444
+ skill,
445
+ source: skill.level as RuntimeSkillDiscoverySource,
446
+ providerPriority: 70,
447
+ })),
448
+ warnings: result.warnings,
449
+ label: "marketplace plugin skills",
450
+ })),
451
+ );
457
452
  }
458
453
  if (policy?.enabled === true && policy.customDirectories && policy.customDirectories.length > 0) {
459
454
  for (const dir of getCustomSkillDirs(policy, home)) {
@@ -466,6 +461,7 @@ export async function discoverRuntimeSkills(
466
461
  const seenNames = new Set<string>();
467
462
  const seenPaths = new Set<string>();
468
463
  const candidates: RuntimeSkillDiscoveryCandidate[] = [];
464
+ const orderedItems: ScanJobResult["items"] = [];
469
465
  for (const entry of settled) {
470
466
  if ("error" in entry) {
471
467
  pushDiagnostic(diagnostics, `skill scan failed: ${entry.error}`);
@@ -476,29 +472,34 @@ export async function discoverRuntimeSkills(
476
472
  pushDiagnostic(diagnostics, `${entry.label}: ${warning}`);
477
473
  }
478
474
  }
479
- for (const item of entry.items) {
480
- if (!isAllowedByPolicy(item.skill, policy, diagnostics)) continue;
481
- const realPath = await realPathOrSelf(item.skill.path);
482
- if (seenPaths.has(realPath) || seenNames.has(item.skill.name)) {
483
- pushDiagnostic(
484
- diagnostics,
485
- `skill "${item.skill.name}" already resolved from a higher-precedence location; ignoring ${item.skill.path}`,
486
- );
487
- continue;
488
- }
489
- seenPaths.add(realPath);
490
- seenNames.add(item.skill.name);
491
-
492
- const candidate: RuntimeSkillDiscoveryCandidate = {
493
- name: item.skill.name,
494
- description:
495
- typeof item.skill.frontmatter?.description === "string" ? item.skill.frontmatter.description : "",
496
- source: item.source,
497
- path: item.skill.path,
498
- useWhen: getUseWhen(item.skill),
499
- };
500
- if (matchesQuery(candidate, options.query ?? "")) candidates.push(candidate);
475
+ orderedItems.push(...entry.items);
476
+ }
477
+ orderedItems.sort((a, b) => {
478
+ const levelOrder = { project: 0, user: 1 } as const;
479
+ return levelOrder[a.source] - levelOrder[b.source] || b.providerPriority - a.providerPriority;
480
+ });
481
+ for (const item of orderedItems) {
482
+ if (!isAllowedByPolicy(item.skill, policy, diagnostics)) continue;
483
+ const realPath = await realPathOrSelf(item.skill.path);
484
+ const normalizedName = getSkillFilesystemIdentity(item.skill.name);
485
+ if (seenPaths.has(realPath) || seenNames.has(normalizedName)) {
486
+ pushDiagnostic(
487
+ diagnostics,
488
+ `skill "${item.skill.name}" already resolved from a higher-precedence location; ignoring ${item.skill.path}`,
489
+ );
490
+ continue;
501
491
  }
492
+ seenPaths.add(realPath);
493
+ seenNames.add(normalizedName);
494
+
495
+ const candidate: RuntimeSkillDiscoveryCandidate = {
496
+ name: item.skill.name,
497
+ description: typeof item.skill.frontmatter?.description === "string" ? item.skill.frontmatter.description : "",
498
+ source: item.source,
499
+ path: item.skill.path,
500
+ useWhen: getUseWhen(item.skill),
501
+ };
502
+ if (matchesQuery(candidate, options.query ?? "")) candidates.push(candidate);
502
503
  }
503
504
  candidates.sort((a, b) => compareSkillOrder(a.name, a.path, b.name, b.path));
504
505
  reportConventionImportCandidates(
@@ -516,71 +517,29 @@ export async function findRuntimeSkillByName(
516
517
  cwd: string,
517
518
  name: string,
518
519
  policy?: SkillsSettings,
519
- home = getRuntimeHome(),
520
+ home?: string,
521
+ agentDir?: string,
522
+ profileAuthority?: "default" | "custom",
520
523
  ): Promise<Skill | undefined> {
521
524
  const normalized = name.trim();
522
525
  if (!normalized) return undefined;
523
- const scanJobs: Array<Promise<{ skill: CapabilitySkill; source: RuntimeSkillDiscoverySource }[]>> = [];
524
- const projectDirs = await getProjectSkillDirs(cwd, home);
525
- const projectContext = { cwd, home, repoRoot: projectDirs.repoRoot };
526
- if (sourceEnabled("project", policy)) {
527
- scanJobs.push(
528
- ...projectDirs.scans.map(scan =>
529
- scanSkillsFromDir(projectContext, {
530
- dir: scan.dir,
531
- providerId: "runtime",
532
- level: "project",
533
- requireDescription: true,
534
- }).then(result => result.items.map(skill => ({ skill, source: "project" as const }))),
535
- ),
536
- );
537
- }
538
- if (sourceEnabled("user", policy)) {
539
- for (const dir of getUserSkillDirs(home)) {
540
- scanJobs.push(
541
- scanSkillsFromDir(
542
- { cwd, home, repoRoot: home },
543
- { dir, providerId: "runtime", level: "user", requireDescription: true },
544
- ).then(result => result.items.map(skill => ({ skill, source: "user" as const }))),
545
- );
546
- }
547
- }
548
- if (policy?.enabled === true) {
549
- for (const dir of getCustomSkillDirs(policy, home)) {
550
- scanJobs.push(
551
- scanSkillsFromDir(
552
- { cwd, home, repoRoot: home },
553
- { dir, providerId: "runtime", level: "user", requireDescription: true },
554
- ).then(result => result.items.map(skill => ({ skill, source: "user" as const }))),
555
- );
556
- }
557
- }
558
- if (policy?.enabled === true) {
559
- try {
560
- const pluginDirs = await collectPluginSkillDirs(home, cwd);
561
- for (const entry of pluginDirs) {
562
- if (!sourceEnabled(entry.level, policy)) continue;
563
- scanJobs.push(
564
- scanSkillsFromDir(
565
- { cwd, home, repoRoot: home },
566
- {
567
- dir: entry.dir,
568
- providerId: "plugin",
569
- level: entry.level,
570
- requireDescription: true,
571
- },
572
- ).then(result => {
573
- for (const skill of result.items) skill.name = `${entry.pluginName}:${skill.name}`;
574
- return result.items.map(skill => ({ skill, source: entry.level as RuntimeSkillDiscoverySource }));
575
- }),
576
- );
577
- }
578
- } catch {}
579
- }
580
- for (const entry of (await Promise.all(scanJobs)).flat()) {
581
- if (entry.skill.name === normalized && isAllowedByPolicy(entry.skill, policy, [])) {
582
- return toRuntimeSkill(entry.skill, entry.source);
583
- }
584
- }
585
- return undefined;
526
+ if (isCanonicalGjcWorkflowSkillFilesystemAlias(normalized)) return undefined;
527
+ const hasExplicitHome = home !== undefined;
528
+ const resolvedHome = home ?? getRuntimeHome();
529
+ const resolvedAgentDir = resolveRuntimeAgentDir(resolvedHome, agentDir, hasExplicitHome);
530
+ const resolvedProfileAuthority =
531
+ profileAuthority ??
532
+ (!hasExplicitHome
533
+ ? agentDir !== undefined && normalizePathForComparison(agentDir) !== normalizePathForComparison(getAgentDir())
534
+ ? "custom"
535
+ : getAgentProfileAuthority()
536
+ : undefined);
537
+ const loaded = await loadSkills({
538
+ ...policy,
539
+ cwd,
540
+ home: resolvedHome,
541
+ agentDir: resolvedAgentDir,
542
+ profileAuthority: resolvedProfileAuthority,
543
+ });
544
+ return loaded.skills.find(skill => skill.name === normalized);
586
545
  }
@@ -188,7 +188,7 @@ export interface AgentEndEvent {
188
188
  sdkRunToken?: string;
189
189
  /** Indicates whether the loop ended normally, suspended, cancelled, or entered maintenance. */
190
190
  stopReason?: "completed" | "paused" | "cancelled" | "maintenance";
191
- /** Present for maintenance checkpoints; non-aborted checkpoints are not terminal. */
191
+ /** Present for maintenance checkpoints; only committed maintenance outcomes are non-terminal. */
192
192
  maintenanceOutcome?: MidRunMaintenanceOutcome;
193
193
  }
194
194