Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 8 additions & 6 deletions docs/customization.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,10 @@ GJC has two canonical persistence scopes:

- **Project:** `<project>/.gjc/` (the repository's project root, or the opened
project directory when there is no repository root).
- **User:** `~/.gjc/agent/` (the canonical user agent directory; the configured
- **User:** the agent directory printed by `gjc config dir` (`~/.gjc/agent/` by
default; `--agent-dir` / `GJC_CODING_AGENT_DIR` move it). The configured
home-relative GJC config root and its legacy skill roots are described in
[Skills](./skills.md)).
[Skills](./skills.md).

These `.gjc` scopes are the long-term GJC authority. A normal standalone session
loads native project/user configuration from them, applies the native
Expand Down Expand Up @@ -43,9 +44,9 @@ wizard (or use an explicit non-interactive command).

| Surface | Native GJC project | Native GJC user | Claude Code project source | Claude Code user source | Codex project source | Codex user source | GJC treatment |
| --- | --- | --- | --- | --- | --- | --- | --- |
| **MCP** | `<project>/.gjc/mcp.json` | `~/.gjc/agent/mcp.json` | `<project>/.mcp.json` for the #4492 import transaction; the doctor also reports `.claude/mcp.json` and `.claude/.mcp.json` convention candidates | `~/.claude.json` for the import transaction | `<project>/.codex/config.toml`, `[mcp_servers.<name>]` | `~/.codex/config.toml`, `[mcp_servers.<name>]` | Only native `.gjc` MCPs autoload in ordinary standalone sessions. Import adapters normalize bounded JSON/TOML entries, validate them, and write native `mcp.json`. |
| **Skill** | `<project>/.gjc/skills/<name>/SKILL.md` | `~/.gjc/agent/skills/<name>/SKILL.md` | `<project>/.claude/skills/<name>/SKILL.md` | `~/.claude/skills/<name>/SKILL.md` | `<project>/.codex/skills/<name>/SKILL.md` | `~/.codex/skills/<name>/SKILL.md` | Native `.gjc` skills are loaded by GJC. Claude/Codex skills are import candidates; they are not loaded directly into a GJC session. |
| **Hook** | `<project>/.gjc/hooks/pre|post/<file>` | `~/.gjc/agent/hooks/pre|post/<file>` | `<project>/.claude/hooks/pre|post/<file>` | `~/.claude/hooks/pre|post/<file>` when explicitly selected for import | `<project>/.codex/hooks/pre-<tool>.ts` / `post-<tool>.ts` | `~/.codex/hooks/pre-<tool>.ts` / `post-<tool>.ts` when explicitly selected for import | Ordinary sessions execute only canonical native `.gjc` directory hooks. Claude/Codex layouts are explicit import and diagnostic sources; accepted imports are normalized to canonical `pre`/`post` phases. Codex-managed `hooks.json` remains Codex-owned. |
| **MCP** | `<project>/.gjc/mcp.json` | `<agentDir>/mcp.json` (`~/.gjc/agent/mcp.json` by default) | `<project>/.mcp.json` for the #4492 import transaction; the doctor also reports `.claude/mcp.json` and `.claude/.mcp.json` convention candidates | `~/.claude.json` for the import transaction | `<project>/.codex/config.toml`, `[mcp_servers.<name>]` | `~/.codex/config.toml`, `[mcp_servers.<name>]` | Only native `.gjc` MCPs autoload in ordinary standalone sessions. Import adapters normalize bounded JSON/TOML entries, validate them, and write native `mcp.json`. |
| **Skill** | `<project>/.gjc/skills/<name>/SKILL.md` | `<agentDir>/skills/<name>/SKILL.md` (see `gjc config dir`) | `<project>/.claude/skills/<name>/SKILL.md` | `~/.claude/skills/<name>/SKILL.md` | `<project>/.codex/skills/<name>/SKILL.md` | `~/.codex/skills/<name>/SKILL.md` | Native `.gjc` skills are loaded by GJC. Claude/Codex skills are import candidates; they are not loaded directly into a GJC session. |
| **Hook** | `<project>/.gjc/hooks/pre|post/<file>` | `<agentDir>/hooks/pre|post/<file>` (`~/.gjc/agent/hooks/pre|post/<file>` by default) | `<project>/.claude/hooks/pre|post/<file>` | `~/.claude/hooks/pre|post/<file>` when explicitly selected for import | `<project>/.codex/hooks/pre-<tool>.ts` / `post-<tool>.ts` | `~/.codex/hooks/pre-<tool>.ts` / `post-<tool>.ts` when explicitly selected for import | Ordinary sessions execute only canonical native `.gjc` directory hooks. Claude/Codex layouts are explicit import and diagnostic sources; accepted imports are normalized to canonical `pre`/`post` phases. Codex-managed `hooks.json` remains Codex-owned. |

The Claude MCP paths above are intentionally explicit: the `/extensions`
import implementation reads the project `.mcp.json` and user `~/.claude.json`
Expand All @@ -67,7 +68,8 @@ boundary and the host-specific compatibility notes.
5. Choose a collision policy, review the normalized preview, and confirm.

A project import writes beneath `<project>/.gjc/`; a user import writes beneath
`~/.gjc/agent/`. A project source does not become user configuration, and a
the agent directory (`gjc config dir`; `~/.gjc/agent/` by default). A project
source does not become user configuration, and a
user source does not write into the project unless the destination was selected
as project. The source is read only during preview/apply and is never mutated.

Expand Down
32 changes: 23 additions & 9 deletions docs/skills.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,25 @@ Project scope (trusted from the repository you open):
|---|---|
| `<project>/.gjc/skills/<name>/SKILL.md` | Native GJC location; discovered from every ancestor of `cwd` up to the repo root (closest first) |

User scope (installed once, available in every project):
User scope (installed once, available in every project). The canonical root is
the **agent directory** printed by `gjc config dir` (`~/.gjc/agent` by default;
`--agent-dir` / `GJC_CODING_AGENT_DIR` move it) — the same directory
`gjc migrate` writes user skills into:

| Location | Scope notes |
|---|---|
| `~/.gjc/agent/skills/<name>/SKILL.md` | Canonical GJC user location |
| `<agentDir>/skills/<name>/SKILL.md` | Canonical GJC user location (the agent directory from `gjc config dir`) |
| `~/.gjc/agent/skills/<name>/SKILL.md` | Location of the canonical root in the default profile |
| `<config>/skills/<name>/SKILL.md` | Configured legacy root (`<config>` is the home-relative directory from `GJC_CONFIG_DIR`, then `PI_CONFIG_DIR`, then `.gjc`) |
| `~/.gjc/skills/<name>/SKILL.md` | Historical legacy user location (still honored) |

An agent-directory profile is a **separate user scope** (the same contract as
MCP user config): under `--agent-dir` / `GJC_CODING_AGENT_DIR`, only
`<agentDir>/skills` is scanned — the default profile's home-relative roots are
not read into a profile, and a profile's skills never leak into the default
profile. The legacy roots above apply in the default profile, exactly as
before.

## Claude Code / Codex layouts (explicit import sources)

GJC recognizes the Claude Code and Codex skill layouts but never loads them
Expand Down Expand Up @@ -53,8 +64,8 @@ mkdir -p .gjc/skills/my-skill
cp .claude/skills/my-skill/SKILL.md .gjc/skills/my-skill/SKILL.md

# import one Codex user skill into your user-wide GJC skills
mkdir -p ~/.gjc/agent/skills/my-skill
cp ~/.codex/skills/my-skill/SKILL.md ~/.gjc/agent/skills/my-skill/SKILL.md
mkdir -p "$(gjc config dir)/skills/my-skill"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Replace gjc config dir with gjc config path

In both user-scope installation snippets, gjc config dir is an invalid command: parseConfigArgs rejects every action outside its allowlist, while the CLI help identifies gjc config path as the command that prints the config directory (packages/coding-agent/src/cli/config-cli.ts:127-146,640-645). Consequently the command substitution produces no path, so these examples fail or attempt to write under /skills/my-skill; use $(gjc config path) in the snippets and related guidance.

Useful? React with 👍 / 👎.

cp ~/.codex/skills/my-skill/SKILL.md "$(gjc config dir)/skills/my-skill/SKILL.md"
```

## Installing a skill
Expand All @@ -68,8 +79,8 @@ mkdir -p .gjc/skills/my-skill
cp my-skill/SKILL.md .gjc/skills/my-skill/SKILL.md

# user-wide, available in every project
mkdir -p ~/.gjc/agent/skills/my-skill
cp my-skill/SKILL.md ~/.gjc/agent/skills/my-skill/SKILL.md
mkdir -p "$(gjc config dir)/skills/my-skill"
cp my-skill/SKILL.md "$(gjc config dir)/skills/my-skill/SKILL.md"
```

Start a new session and invoke the skill with `/skill:my-skill`, or let the
Expand All @@ -83,7 +94,7 @@ Skill discovery is controlled by three settings, all on by default:
|---|---|
| `skills.enabled` | Master switch for all filesystem skill discovery |
| `skills.trustProjectSkills` | Load project-scoped `.gjc/skills` and surface project `.claude`/`.codex` import candidates |
| `skills.trustUserSkills` | Load user-scoped skills (`~/.gjc/agent/skills` and legacy roots) and surface user-home import candidates |
| `skills.trustUserSkills` | Load user-scoped skills (the agent directory's `skills` root and legacy home-relative roots) and surface user-home import candidates |

```sh
gjc config set skills.trustProjectSkills false # ignore repo-controlled skills only
Expand All @@ -106,8 +117,11 @@ Duplicate names resolve deterministically, first location wins:
1. project scope beats user scope;
2. within project scope, the `.gjc/skills` directory nearest to `cwd` wins
(ancestors are walked from `cwd` up to the repo root, closest first);
3. within user scope: `<config>/agent/skills` > legacy `<config>/skills` >
legacy `~/.gjc/skills`.
3. within user scope: the agent directory's `skills` root (`gjc config dir`);
in the default profile its home-relative legacy roots follow at lower
precedence: `<config>/agent/skills` > legacy `<config>/skills` >
legacy `~/.gjc/skills` (under an agent-directory profile only the agent
directory is scanned).

Shadowed duplicates are diagnosed rather than silent. Bundled workflow skill
names are reserved: a project skill named `autoresearch`, `deep-interview`, `ralplan`,
Expand Down
35 changes: 31 additions & 4 deletions packages/coding-agent/src/capability/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
* - Loading items for a capability across all providers
*/
import * as path from "node:path";
import { getAgentDir, getProjectDir, getTrustedHomeDir, logger } from "@gajae-code/utils";
import { getAgentDir, getConfigDirName, getProjectDir, getTrustedHomeDir, logger } from "@gajae-code/utils";

import type { Settings } from "../config/settings";
import { clearCache as clearFsCache, findRepoRoot, cacheStats as fsCacheStats, invalidate as invalidateFs } from "./fs";
Expand Down Expand Up @@ -229,22 +229,49 @@ function filterProviders<T>(capability: Capability<T>, options: LoadOptions): Pr
/**
* Load a capability by ID.
*/
export async function loadCapability<T>(capabilityId: string, options: LoadOptions = {}): Promise<CapabilityResult<T>> {
async function loadCapabilityWithContext<T>(
capabilityId: string,
options: LoadOptions,
home: string,
): Promise<CapabilityResult<T>> {
const capability = capabilities.get(capabilityId) as Capability<T> | undefined;
if (!capability) {
throw new Error(`Unknown capability: "${capabilityId}"`);
}

const cwd = options.cwd ?? getProjectDir();
const home = getTrustedHomeDir();
const userAgentDir = options.agentDir ? path.resolve(options.agentDir) : getAgentDir();
const userAgentDir = options.agentDir
? path.resolve(options.agentDir)
: path.join(home, getConfigDirName(), "agent");
const repoRoot = await findRepoRoot(cwd);
const ctx: LoadContext = { cwd, home, userAgentDir, repoRoot };
const providers = filterProviders(capability, options);

return await loadImpl(capability, providers, ctx, options);
}

export async function loadCapability<T>(capabilityId: string, options: LoadOptions = {}): Promise<CapabilityResult<T>> {
return await loadCapabilityWithContext(
capabilityId,
{ ...options, agentDir: options.agentDir || getAgentDir() },
getTrustedHomeDir(),
);
}

/**
* Load a capability against an explicitly supplied home for SDK compatibility
* seams that already expose a home-scoped discovery option. This stays internal
* to the coding-agent package; the public loadCapability API remains bound to
* the trusted process home.
*/
export async function loadCapabilityForHome<T>(
capabilityId: string,
home: string,
options: LoadOptions = {},
): Promise<CapabilityResult<T>> {
return await loadCapabilityWithContext(capabilityId, options, path.resolve(home));
}

// =============================================================================
// Provider Enable/Disable API
// =============================================================================
Expand Down
5 changes: 3 additions & 2 deletions packages/coding-agent/src/capability/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,9 @@ export interface LoadContext {
* A native surface whose write path targets the agent directory resolves its
* user scope from here, or discovery reads a different file than the one the
* writer produced: `gjc mcp add` (user scope) writes `getMCPConfigPath("user")`
* under this directory. Surfaces whose writers are home-relative (skills) keep
* resolving from `home`.
* and `gjc migrate` / `gjc skill` write `<agentDir>/skills` under this
* directory. Home-relative legacy roots and the foreign Claude/Codex scans
* keep resolving from `home`.
*/
userAgentDir?: string;
/** Git repository root (directory containing .git), or null if not in a repo */
Expand Down
4 changes: 3 additions & 1 deletion packages/coding-agent/src/cli/customize-doctor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1013,7 +1013,9 @@ async function collectCommands(cwd: string, activeSettings: SettingsInstance): P
const disabledExts = disabledExtensionIds(activeSettings);
const disabledProviders = new Set(activeSettings.get("disabledProviders"));
// Exact session-startup consumer (interactive/print modes).
const loadedNames = new Set((await loadSlashCommands({ cwd })).map(cmd => cmd.name));
const loadedNames = new Set(
(await loadSlashCommands({ cwd, agentDir: activeSettings.getAgentDir() })).map(cmd => cmd.name),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Pass the profile into the doctor capability scan

When runCustomizeDoctor receives session-equivalent settings whose getAgentDir() differs from process-wide getAgentDir(), this loads loadedNames from the injected profile, but discoverCapability immediately above still calls loadCapability without agentDir. The command inventory therefore comes from the process profile while startup comparison comes from the injected profile, causing profile commands to be omitted and unrelated commands to be reported as managed or loaded incorrectly; pass activeSettings.getAgentDir() into the inventory scan as well.

Useful? React with 👍 / 👎.

);

const items: CustomizeDoctorItem[] = entries.map(entry => {
const base = baseItem("command", entry);
Expand Down
1 change: 1 addition & 0 deletions packages/coding-agent/src/cli/skills-cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ export async function runSkillsCommand(cmd: SkillsCommandArgs): Promise<void> {
const settings = await Settings.loadForScope({ cwd: process.cwd() });
const result = await discoverRuntimeSkills({
cwd: process.cwd(),
agentDir: settings.getAgentDir(),
source,
policy: {
...settings.getGroup("skills"),
Expand Down
2 changes: 1 addition & 1 deletion packages/coding-agent/src/config/settings-schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3695,7 +3695,7 @@ export const SETTINGS_SCHEMA = {
tab: "customization",
label: "Trust User Skills",
description:
"Load skills from ~/.gjc/agent/skills (and legacy ~/.gjc/skills / <config>/skills). Set to false to ignore user-installed skills while keeping project skills.",
"Load skills from the agent directory's skills root (`gjc config dir`; ~/.gjc/agent/skills by default, with legacy ~/.gjc/skills / <config>/skills roots in the default profile). Set to false to ignore user-installed skills while keeping project skills.",
},
},

Expand Down
50 changes: 25 additions & 25 deletions packages/coding-agent/src/discovery/builtin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import {
discoverExtensionModulePaths,
expandEnvVarsDeep,
getExtensionNameFromPath,
getUserSkillScanDirs,
loadFilesFromDir,
SOURCE_PATHS,
scanSkillsFromDir,
Expand All @@ -41,8 +42,8 @@ const PRIORITY = 100;

const PATHS = SOURCE_PATHS.native;

function getUserAgentDirs(): string[] {
return [PATHS.userAgent];
function getUserAgentDirs(ctx: LoadContext): string[] {
return [resolveUserAgentDir(ctx)];
}

/**
Expand All @@ -56,7 +57,7 @@ function getUserAgentDirs(): string[] {
* profile's servers into it.
*/
function resolveUserAgentDir(ctx: LoadContext): string {
return ctx.userAgentDir ?? getAgentDir();
return path.resolve(ctx.userAgentDir ?? getAgentDir());

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Thread the session agent directory into startup discovery

When createAgentSession({ agentDir }) selects a profile different from the process-wide getAgentDir(), this fallback still selects the process-wide profile because sdk/session.ts invokes loadSkills, rule discovery, and context-file discovery with only cwd, while the system-prompt loaders also expose no agentDir option. The session can therefore omit the requested profile's skills/instructions and inject SYSTEM.md, RULES.md, AGENTS.md, or skills from another profile; pass the resolved session agentDir through all capability-based startup readers rather than relying on this fallback.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Thread the session profile into hook discovery

When createAgentSession({ agentDir }) uses a profile different from process-wide getAgentDir() and extension discovery is enabled, sdk/session.ts calls discoverAndLoadHookExtensions(..., cwd), and extensibility/hooks/loader.ts:275 calls loadCapability without agentDir; this fallback therefore imports executable hooks from the process profile while omitting the requested profile's hooks. Fresh evidence beyond the earlier startup-discovery report is this still-unthreaded hook loader at sdk/session.ts:2532 and extensibility/hooks/loader.ts:275; pass the session agent directory through both APIs.

Useful? React with 👍 / 👎.

}

function getProjectConfigDirs(): string[] {
Expand Down Expand Up @@ -84,8 +85,8 @@ async function getConfigDirs(ctx: LoadContext): Promise<Array<{ dir: string; lev
result.push({ dir: projectDir, level: "project" });
}
}
for (const userAgentDir of getUserAgentDirs()) {
const userDir = await ifNonEmptyDir(ctx.home, userAgentDir);
for (const userAgentDir of getUserAgentDirs(ctx)) {
const userDir = await ifNonEmptyDir(userAgentDir);
Comment on lines +88 to +89

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Pass the session profile into slash-command discovery

When createAgentSession({ agentDir }) selects a profile different from process-wide getAgentDir(), both interactive-mode.ts:1108-1112 and print-mode.ts:220-222 reload slash commands with only the session cwd. loadSlashCommands likewise exposes no agentDir option, so this new fallback scans the process profile rather than the session profile, omitting that profile's commands and potentially making commands from another profile executable. Thread session.getSessionAgentDir() through the slash-command loader and capability call.

Useful? React with 👍 / 👎.

if (userDir) {
result.push({ dir: userDir, level: "user" });
}
Expand Down Expand Up @@ -282,17 +283,18 @@ registerProvider<MCPServer>(mcpCapability.id, {
async function loadSystemPrompt(ctx: LoadContext): Promise<LoadResult<SystemPrompt>> {
const items: SystemPrompt[] = [];

for (const userAgentDir of getUserAgentDirs()) {
const userPath = path.join(ctx.home, userAgentDir, "SYSTEM.md");
const userContent = await readFile(userPath);
if (userContent) {
items.push({
path: userPath,
content: userContent,
level: "user",
_source: createSourceMeta(PROVIDER_ID, userPath, "user"),
});
}
// User scope is the agent directory — the directory `gjc config dir` prints
// and the only user-scope seam. A profile is a separate scope; the default
// profile's home-relative SYSTEM.md is not read under one.
const userPath = path.join(resolveUserAgentDir(ctx), "SYSTEM.md");
const userContent = await readFile(userPath);
if (userContent) {
items.push({
path: userPath,
content: userContent,
level: "user",
_source: createSourceMeta(PROVIDER_ID, userPath, "user"),
});
}

const nearestProjectConfigDir = await findNearestProjectConfigDir(ctx.cwd, ctx.repoRoot);
Expand Down Expand Up @@ -336,9 +338,9 @@ async function loadSkills(ctx: LoadContext): Promise<LoadResult<Skill>> {
);

// User-level scan from ~/.gjc/agent/skills/
const userScans = getUserAgentDirs().map(userAgentDir =>
const userScans = getUserSkillScanDirs(ctx.home, resolveUserAgentDir(ctx)).map(dir =>
scanSkillsFromDir(ctx, {
dir: path.join(ctx.home, userAgentDir, "skills"),
dir,
providerId: PROVIDER_ID,
level: "user",
requireDescription: true,
Expand Down Expand Up @@ -412,13 +414,11 @@ async function loadRules(ctx: LoadContext): Promise<LoadResult<Rule>> {
// Top-level RULES.md is a sticky always-apply rule. The context-file
// discovery contract treats it as the file "re-injected near the current
// turn so they keep hold across long conversations".
// User scope: ~/.gjc/agent/RULES.md
// User scope: <agentDir>/RULES.md (a profile is a separate user scope;
// the default profile's home-relative copy is not read)
// Project scope: nearest .gjc/RULES.md walking up from cwd to repoRoot
for (const userAgentDir of getUserAgentDirs()) {
const userRulesFile = path.join(ctx.home, userAgentDir, "RULES.md");
const userRule = await loadStickyRulesFile(userRulesFile, "user");
if (userRule) items.push(userRule);
}
const userRule = await loadStickyRulesFile(path.join(resolveUserAgentDir(ctx), "RULES.md"), "user");
if (userRule) items.push(userRule);

const nearestProjectConfigDir = await findNearestProjectConfigDir(ctx.cwd, ctx.repoRoot);
if (nearestProjectConfigDir) {
Expand Down Expand Up @@ -933,7 +933,7 @@ async function loadContextFiles(ctx: LoadContext): Promise<LoadResult<ContextFil
const items: ContextFile[] = [];
const warnings: string[] = [];

const userPath = path.join(ctx.home, PATHS.userAgent, "AGENTS.md");
const userPath = path.join(resolveUserAgentDir(ctx), "AGENTS.md");
const userContent = await readFile(userPath);
if (userContent) {
items.push({
Expand Down
30 changes: 30 additions & 0 deletions packages/coding-agent/src/discovery/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,36 @@ export function getUserPath(ctx: LoadContext, source: SourceId, subpath: string)
return path.join(ctx.home, paths.userAgent, subpath);
}

/**
* Deterministic user-scope skill scan directories, highest precedence first.
*
* The canonical root is the agent directory (`getAgentDir()`, moved by
* `--agent-dir` / `GJC_CODING_AGENT_DIR` / `setAgentDir()`) — the target of
* every user-scope skill writer (`gjc migrate`, `gjc skill`). An agent-directory
* profile is a *separate* user scope, the same contract as MCP user config
* (#4768): its legacy home-relative roots are not scanned, so a profile cannot
* pick up the default profile's skills (and vice versa). In the default
* profile the agent directory is `<home>/<configDir>/agent` and the configured
* legacy roots below it are still honored, exactly as before.
*/
export function resolveUserAgentDir(home: string, userAgentDir?: string): string {
return path.resolve(userAgentDir ?? path.join(home, SOURCE_PATHS.native.userAgent));
}

export function getUserSkillScanDirs(home: string, userAgentDir?: string): string[] {
const resolvedAgentDir = resolveUserAgentDir(home, userAgentDir);
if (resolvedAgentDir !== path.resolve(path.join(home, SOURCE_PATHS.native.userAgent))) {
return [path.join(resolvedAgentDir, "skills")];
Comment on lines +129 to +132

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Add the profile-discovery fix to the changelog

This user-visible change redirects skills and other native user-scope surfaces to the selected agent directory, but the diff does not add a corresponding entry to packages/coding-agent/CHANGELOG.md; the existing Unreleased profile entry covers MCP autoload only, so release notes would omit this broader behavior and isolation fix. Add a Fixed entry under ## [Unreleased] as required by the repository contract.

AGENTS.md reference: AGENTS.md:L188-L188

Useful? React with 👍 / 👎.

}
return [
...new Set([
path.join(home, SOURCE_PATHS.native.userAgent, "skills"),
path.join(home, SOURCE_PATHS.native.userBase, "skills"),
path.join(home, ".gjc", "skills"),
]),
];
}

/**
* Get project-level path for a source (cwd only).
*/
Expand Down
Loading
Loading