@handsupmin/gc-tree 0.6.2 → 0.7.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.
package/README.es.md CHANGED
@@ -152,6 +152,7 @@ Probado con documentación interna real (4 exportaciones de Notion, consultas mi
152
152
  ## Funciona con Claude Code y Codex — ambos verificados
153
153
 
154
154
  ```bash
155
+ gctree init # configura ~/.gctree y la activación global para el provider elegido
155
156
  gctree scaffold --host claude-code # instala snippet en CLAUDE.md + /gc-onboard, /gc-update-global-context
156
157
  gctree scaffold --host codex # instala snippet en AGENTS.md + $gc-onboard, $gc-update-global-context
157
158
  gctree scaffold --host both # ambos a la vez
@@ -217,7 +218,7 @@ gctree set-repo-scope --branch client-b --exclude # excluir el repo actual
217
218
  ~/.gctree/
218
219
  branches/
219
220
  main/
220
- index.md ← índice compacto, ≤2000 chars, se carga primero
221
+ index.md ← índice compacto, se carga primero
221
222
  docs/
222
223
  auth.md ← doc completo, se lee solo cuando hace falta
223
224
  architecture.md
@@ -247,7 +248,8 @@ El contexto vive fuera de tus repositorios — sin reglas de `.gitignore`, sin c
247
248
  | Ver reglas de alcance por repo | `gctree repo-map` |
248
249
  | Incluir o excluir el repo actual para un gc-branch | `gctree set-repo-scope --branch <name> --include` / `--exclude` |
249
250
  | Resetear un gc-branch antes de re-onboard | `gctree reset-gc-branch --branch <name> --yes` |
250
- | Instalar scaffold en un nuevo entorno | `gctree scaffold --host codex --target /path/to/repo` |
251
+ | Instalar un override local en un repo | `gctree scaffold --host codex --target /path/to/repo` |
252
+ | Quitar la activación global y el contexto | `gctree uninstall --yes` |
251
253
 
252
254
  ---
253
255
 
package/README.ja.md CHANGED
@@ -152,6 +152,7 @@ AI ツールに渡るのは正しいコンテキスト——ナレッジベー
152
152
  ## Claude Code と Codex、両方で動作検証済み
153
153
 
154
154
  ```bash
155
+ gctree init # ~/.gctree と選んだプロバイダーのグローバル有効化を設定
155
156
  gctree scaffold --host claude-code # CLAUDE.md スニペット + /gc-onboard、/gc-update-global-context をインストール
156
157
  gctree scaffold --host codex # AGENTS.md スニペット + $gc-onboard、$gc-update-global-context をインストール
157
158
  gctree scaffold --host both # 両方同時に
@@ -217,7 +218,7 @@ gctree set-repo-scope --branch client-b --exclude # 現在のリポジトリ
217
218
  ~/.gctree/
218
219
  branches/
219
220
  main/
220
- index.md ← 圧縮インデックス、≤2000 文字、最初にロード
221
+ index.md ← 圧縮インデックス、最初にロード
221
222
  docs/
222
223
  auth.md ← 全文、必要なときだけ読む
223
224
  architecture.md
@@ -247,7 +248,8 @@ gctree set-repo-scope --branch client-b --exclude # 現在のリポジトリ
247
248
  | リポジトリ範囲ルールを確認する | `gctree repo-map` |
248
249
  | 現在のリポジトリを gc-branch に含める / 除外する | `gctree set-repo-scope --branch <name> --include` / `--exclude` |
249
250
  | 再オンボーディング前に gc-branch をリセットする | `gctree reset-gc-branch --branch <name> --yes` |
250
- | 別の環境に scaffold をインストールする | `gctree scaffold --host codex --target /path/to/repo` |
251
+ | 1つのリポジトリにローカル override を入れる | `gctree scaffold --host codex --target /path/to/repo` |
252
+ | グローバルな gc-tree 有効化とコンテキストを外す | `gctree uninstall --yes` |
251
253
 
252
254
  ---
253
255
 
package/README.ko.md CHANGED
@@ -152,6 +152,7 @@ AI 도구가 올바른 컨텍스트를 가져옵니다. 지식 베이스 전체
152
152
  ## Claude Code와 Codex 양쪽 모두 검증 완료
153
153
 
154
154
  ```bash
155
+ gctree init # ~/.gctree와 선택한 프로바이더의 전역 활성화 설정
155
156
  gctree scaffold --host claude-code # CLAUDE.md 스니펫 + /gc-onboard, /gc-update-global-context 설치
156
157
  gctree scaffold --host codex # AGENTS.md 스니펫 + $gc-onboard, $gc-update-global-context 설치
157
158
  gctree scaffold --host both # 둘 다 한번에
@@ -217,7 +218,7 @@ gctree set-repo-scope --branch client-b --exclude # 현재 레포 제외
217
218
  ~/.gctree/
218
219
  branches/
219
220
  main/
220
- index.md ← 압축 인덱스, ≤2000자, 먼저 로드됨
221
+ index.md ← 압축 인덱스, 먼저 로드됨
221
222
  docs/
222
223
  auth.md ← 전체 문서, 필요할 때만 읽음
223
224
  architecture.md
@@ -247,7 +248,8 @@ gctree set-repo-scope --branch client-b --exclude # 현재 레포 제외
247
248
  | 레포 범위 규칙 확인 | `gctree repo-map` |
248
249
  | 현재 레포 포함/제외 설정 | `gctree set-repo-scope --branch <name> --include` / `--exclude` |
249
250
  | 재온보딩 전 gc-branch 초기화 | `gctree reset-gc-branch --branch <name> --yes` |
250
- | 환경 스캐폴딩 | `gctree scaffold --host codex --target /path/to/repo` |
251
+ | 특정 레포에 로컬 override 설치 | `gctree scaffold --host codex --target /path/to/repo` |
252
+ | 전역 gc-tree 활성화와 컨텍스트 제거 | `gctree uninstall --yes` |
251
253
 
252
254
  ---
253
255
 
package/README.md CHANGED
@@ -152,6 +152,7 @@ Tested against real internal documentation (4 Notion exports, Korean + English m
152
152
  ## Works with Claude Code and Codex — both verified
153
153
 
154
154
  ```bash
155
+ gctree init # sets up ~/.gctree and global activation for your chosen provider(s)
155
156
  gctree scaffold --host claude-code # installs CLAUDE.md snippet + /gc-onboard, /gc-update-global-context
156
157
  gctree scaffold --host codex # installs AGENTS.md snippet + $gc-onboard, $gc-update-global-context
157
158
  gctree scaffold --host both # both at once
@@ -217,7 +218,7 @@ gctree set-repo-scope --branch client-b --exclude # exclude current repo
217
218
  ~/.gctree/
218
219
  branches/
219
220
  main/
220
- index.md ← compact index, ≤2000 chars, loaded first
221
+ index.md ← compact index, loaded first
221
222
  docs/
222
223
  auth.md ← full doc, read only when needed
223
224
  architecture.md
@@ -247,7 +248,8 @@ Context lives outside your repos — no `.gitignore` rules needed, no accidental
247
248
  | Show repo-scope rules | `gctree repo-map` |
248
249
  | Include/exclude current repo for a gc-branch | `gctree set-repo-scope --branch <name> --include` / `--exclude` |
249
250
  | Reset a gc-branch before re-onboarding | `gctree reset-gc-branch --branch <name> --yes` |
250
- | Scaffold a new environment | `gctree scaffold --host codex --target /path/to/repo` |
251
+ | Add a local override in one repo | `gctree scaffold --host codex --target /path/to/repo` |
252
+ | Remove global gc-tree activation and context | `gctree uninstall --yes` |
251
253
 
252
254
  ---
253
255
 
package/README.zh.md CHANGED
@@ -152,6 +152,7 @@ AI 工具拿到的是正确的上下文——不是整个知识库,而是恰
152
152
  ## Claude Code 和 Codex 均已验证可用
153
153
 
154
154
  ```bash
155
+ gctree init # 设置 ~/.gctree 和所选 provider 的全局激活
155
156
  gctree scaffold --host claude-code # 安装 CLAUDE.md 片段 + /gc-onboard、/gc-update-global-context
156
157
  gctree scaffold --host codex # 安装 AGENTS.md 片段 + $gc-onboard、$gc-update-global-context
157
158
  gctree scaffold --host both # 两者同时安装
@@ -217,7 +218,7 @@ gctree set-repo-scope --branch client-b --exclude # 排除当前仓库
217
218
  ~/.gctree/
218
219
  branches/
219
220
  main/
220
- index.md ← 压缩索引,≤2000 字符,优先加载
221
+ index.md ← 压缩索引,优先加载
221
222
  docs/
222
223
  auth.md ← 完整文档,按需读取
223
224
  architecture.md
@@ -247,7 +248,8 @@ gctree set-repo-scope --branch client-b --exclude # 排除当前仓库
247
248
  | 查看仓库范围规则 | `gctree repo-map` |
248
249
  | 为 gc-branch 包含或排除当前仓库 | `gctree set-repo-scope --branch <name> --include` / `--exclude` |
249
250
  | 重新 onboarding 前重置 gc-branch | `gctree reset-gc-branch --branch <name> --yes` |
250
- | 在新环境中安装 scaffold | `gctree scaffold --host codex --target /path/to/repo` |
251
+ | 在单个仓库中安装本地 override | `gctree scaffold --host codex --target /path/to/repo` |
252
+ | 移除全局 gc-tree 激活和上下文 | `gctree uninstall --yes` |
251
253
 
252
254
  ---
253
255
 
package/dist/src/cli.js CHANGED
@@ -7,7 +7,7 @@ import { dispatchGcTreeHook } from './hook.js';
7
7
  import { onboardBranch } from './onboard.js';
8
8
  import { buildProviderLaunchPlan, maybeLaunchProvider, promptLanguageSelection, promptLaunchProviderSelection, promptProviderSelection, } from './provider.js';
9
9
  import { DEFAULT_BRANCH, branchDir, resolveHome } from './paths.js';
10
- import { branchRepoMapPath, branchScopeStatus, detectCurrentRepoId, detectRepoRoot, promptResolveScopeDecision, readBranchRepoMap, resolveBranchForRepo, setRepoScopeForBranch, } from './repo-map.js';
10
+ import { branchRepoMapPath, branchScopeStatus, detectCurrentRepoId, promptResolveScopeDecision, readBranchRepoMap, resolveBranchForRepo, setRepoScopeForBranch, } from './repo-map.js';
11
11
  import { findRelatedDocs, getDocById, resolveContext } from './resolve.js';
12
12
  import { scaffoldHostIntegration } from './scaffold.js';
13
13
  import { requirePreferredProvider, writeSettings, readSettings } from './settings.js';
@@ -100,21 +100,16 @@ function normalizeProviderMode(value) {
100
100
  async function resolvePreferredProvider(home, explicitProvider) {
101
101
  return normalizeProvider(explicitProvider) || (await requirePreferredProvider(home));
102
102
  }
103
- async function ensureScaffold({ providerMode, targetDir, force = false, }) {
103
+ async function ensureScaffold({ providerMode, targetDir, force = false, scope = 'local', }) {
104
104
  const hosts = providerMode === 'both' ? ['claude-code', 'codex'] : [providerMode];
105
- const combined = { hosts: providerMode, target_dir: targetDir, written: [], skipped_existing: [] };
105
+ const combined = { hosts: providerMode, target_dir: scope === 'global' ? '(global)' : (targetDir || process.cwd()), written: [], skipped_existing: [] };
106
106
  for (const host of hosts) {
107
- const result = await scaffoldHostIntegration({ host, targetDir, force });
107
+ const result = await scaffoldHostIntegration({ host, targetDir, force, scope });
108
108
  combined.written.push(...result.written);
109
109
  combined.skipped_existing.push(...result.skipped_existing);
110
110
  }
111
111
  return combined;
112
112
  }
113
- async function resolveTargetDir(explicitTarget) {
114
- if (explicitTarget)
115
- return explicitTarget;
116
- return (await detectRepoRoot(process.cwd())) || process.cwd();
117
- }
118
113
  async function maybePromptProviderMode(explicitProvider) {
119
114
  const provider = normalizeProviderMode(explicitProvider);
120
115
  if (provider)
@@ -170,7 +165,8 @@ async function main() {
170
165
  const providerMode = await maybePromptProviderMode(readArg('--provider'));
171
166
  const provider = await resolveLaunchProvider(providerMode);
172
167
  const preferredLanguage = await maybePromptLanguage(readArg('--language'));
173
- const targetDir = readArg('--target') || process.cwd();
168
+ const explicitTargetDir = readArg('--target');
169
+ const launchTargetDir = explicitTargetDir || process.cwd();
174
170
  const result = await initHome(home);
175
171
  const settings = await writeSettings({
176
172
  home,
@@ -178,13 +174,16 @@ async function main() {
178
174
  preferredProvider: provider,
179
175
  preferredLanguage,
180
176
  });
181
- const scaffold = await ensureScaffold({ providerMode, targetDir, force: hasFlag('--force') });
177
+ const globalScaffold = await ensureScaffold({ providerMode, scope: 'global', force: hasFlag('--force') });
178
+ const scaffold = explicitTargetDir
179
+ ? await ensureScaffold({ providerMode, targetDir: explicitTargetDir, scope: 'local', force: hasFlag('--force') })
180
+ : null;
182
181
  const launch = (await isBranchContextEmpty(home, result.gc_branch))
183
182
  ? await launchGuidedFlow({
184
183
  provider,
185
184
  providerMode,
186
185
  preferredLanguage,
187
- targetDir,
186
+ targetDir: launchTargetDir,
188
187
  gcBranch: result.gc_branch,
189
188
  command: 'gc-onboard',
190
189
  noLaunch: hasFlag('--no-launch'),
@@ -195,6 +194,7 @@ async function main() {
195
194
  provider_mode: settings.provider_mode,
196
195
  preferred_provider: settings.preferred_provider,
197
196
  preferred_language: settings.preferred_language,
197
+ global_scaffold: globalScaffold,
198
198
  scaffold,
199
199
  launch,
200
200
  }, null, 2));
@@ -277,7 +277,9 @@ async function main() {
277
277
  const provider = await resolvePreferredProvider(home, readArg('--provider'));
278
278
  const settings = await readSettings(home);
279
279
  const targetDir = readArg('--target') || process.cwd();
280
- const scaffold = await ensureScaffold({ providerMode: settings?.provider_mode || provider, targetDir, force: hasFlag('--force') });
280
+ const scaffold = readArg('--target')
281
+ ? await ensureScaffold({ providerMode: settings?.provider_mode || provider, targetDir, scope: 'local', force: hasFlag('--force') })
282
+ : null;
281
283
  const launch = await launchGuidedFlow({
282
284
  provider,
283
285
  providerMode: settings?.provider_mode || provider,
@@ -323,10 +325,9 @@ async function main() {
323
325
  throw new Error('uninstall is destructive. Re-run with --yes to confirm.');
324
326
  }
325
327
  const host = normalizeProviderMode(readArg('--host')) || 'both';
326
- const targetDir = await resolveTargetDir(readArg('--target'));
327
328
  const result = await uninstallGcTree({
328
329
  home,
329
- targetDir,
330
+ targetDir: readArg('--target'),
330
331
  host,
331
332
  keepHome: hasFlag('--keep-home'),
332
333
  });
@@ -473,7 +474,9 @@ async function main() {
473
474
  const provider = await resolvePreferredProvider(home, readArg('--provider'));
474
475
  const settings = await readSettings(home);
475
476
  const targetDir = readArg('--target') || process.cwd();
476
- const scaffold = await ensureScaffold({ providerMode: settings?.provider_mode || provider, targetDir, force: hasFlag('--force') });
477
+ const scaffold = readArg('--target')
478
+ ? await ensureScaffold({ providerMode: settings?.provider_mode || provider, targetDir, scope: 'local', force: hasFlag('--force') })
479
+ : null;
477
480
  const launch = await launchGuidedFlow({
478
481
  provider,
479
482
  providerMode: settings?.provider_mode || provider,
@@ -1,5 +1,6 @@
1
1
  import { access, readFile, rm, writeFile } from 'node:fs/promises';
2
2
  import { constants } from 'node:fs';
3
+ import { homedir } from 'node:os';
3
4
  import { dirname, join } from 'node:path';
4
5
  import { mkdir } from 'node:fs/promises';
5
6
  const MARKERS = {
@@ -168,3 +169,13 @@ export function gctreeHookJsonTargets(targetDir) {
168
169
  claude: join(targetDir, '.claude', 'hooks', 'hooks.json'),
169
170
  };
170
171
  }
172
+ export function gctreeGlobalRoot(host) {
173
+ if (host === 'codex') {
174
+ return process.env.GCTREE_CODEX_GLOBAL_DIR || join(homedir(), '.codex');
175
+ }
176
+ return process.env.GCTREE_CLAUDE_GLOBAL_DIR || join(homedir(), '.claude');
177
+ }
178
+ export function gctreeGlobalHookJsonTarget(host) {
179
+ const root = gctreeGlobalRoot(host);
180
+ return host === 'codex' ? join(root, 'hooks.json') : join(root, 'hooks', 'hooks.json');
181
+ }
@@ -7,6 +7,29 @@ export function slugify(value) {
7
7
  .replace(/^-|-$/g, '');
8
8
  return normalized || 'doc';
9
9
  }
10
+ function parseDocReference(value) {
11
+ const trimmed = String(value || '').trim().replace(/^\.?\//, '');
12
+ if (!trimmed)
13
+ return null;
14
+ const normalized = trimmed.replace(/\\/g, '/');
15
+ const docsMatch = normalized.match(/^docs\/([^/]+)\/([^/]+?)(?:\.md)?$/i);
16
+ if (docsMatch) {
17
+ const category = normalizeCategory(docsMatch[1]);
18
+ const slug = slugify(docsMatch[2]);
19
+ if (slug === 'index')
20
+ return null;
21
+ return { category, slug, label: docsMatch[2].replace(/\.md$/i, '').trim() };
22
+ }
23
+ const directMatch = normalized.match(/^(role|repos|domain|workflows|conventions|infra|verification)\/([^/]+?)(?:\.md)?$/i);
24
+ if (directMatch) {
25
+ const category = normalizeCategory(directMatch[1]);
26
+ const slug = slugify(directMatch[2]);
27
+ if (slug === 'index')
28
+ return null;
29
+ return { category, slug, label: directMatch[2].replace(/\.md$/i, '').trim() };
30
+ }
31
+ return null;
32
+ }
10
33
  export function ensureSummary(summary) {
11
34
  const trimmed = String(summary || '').trim();
12
35
  if (!trimmed) {
@@ -158,6 +181,35 @@ export function extractIndexEntries(markdown) {
158
181
  .map((line) => line.slice(2).trim())
159
182
  .filter(Boolean);
160
183
  }
184
+ export function normalizeIndexEntry(value, fallback) {
185
+ const reference = parseDocReference(value);
186
+ if (reference) {
187
+ return {
188
+ category: reference.category,
189
+ label: reference.label,
190
+ };
191
+ }
192
+ const trimmed = String(value || '').trim();
193
+ if (!trimmed)
194
+ return null;
195
+ if (/^docs\/index\.md$/i.test(trimmed) || /^index\.md$/i.test(trimmed))
196
+ return null;
197
+ return {
198
+ category: fallback.category,
199
+ label: trimmed,
200
+ };
201
+ }
202
+ export function inferDocPlacement(input) {
203
+ for (const candidate of [input.slug, input.indexLabel]) {
204
+ const reference = parseDocReference(candidate || '');
205
+ if (reference) {
206
+ return { category: reference.category, slug: reference.slug };
207
+ }
208
+ }
209
+ const explicitCategory = input.category ? slugify(input.category) : null;
210
+ const slug = slugify(input.slug || input.indexLabel || input.title);
211
+ return { category: explicitCategory, slug };
212
+ }
161
213
  export function extractSummary(markdown) {
162
214
  const match = String(markdown || '').match(/## Summary\s+([\s\S]*?)(?:\n## |$)/);
163
215
  return match?.[1]?.trim() || '';
@@ -1,13 +1,13 @@
1
1
  import { mkdir, rm, writeFile } from 'node:fs/promises';
2
2
  import { dirname, join } from 'node:path';
3
- import { renderDocMarkdown, slugify } from './markdown.js';
3
+ import { inferDocPlacement, renderDocMarkdown } from './markdown.js';
4
4
  import { branchDocsDir, DEFAULT_BRANCH } from './paths.js';
5
5
  import { ensureBranchExists, updateBranchMeta, writeIndexFromDocs } from './store.js';
6
6
  function docRelativePath(doc) {
7
7
  if (doc.slug?.includes('/'))
8
8
  return `${doc.slug.replace(/\.md$/i, '')}.md`;
9
- const fileBase = slugify(doc.slug || doc.indexLabel || doc.title);
10
- const category = doc.category ? slugify(doc.category) : '';
9
+ const { category, slug } = inferDocPlacement(doc);
10
+ const fileBase = slug;
11
11
  return category ? `${category}/${fileBase}.md` : `${fileBase}.md`;
12
12
  }
13
13
  export async function onboardBranch({ home, input, branch, }) {
@@ -1,6 +1,6 @@
1
1
  import { access, mkdir, writeFile } from 'node:fs/promises';
2
2
  import { dirname, join } from 'node:path';
3
- import { gctreeHookJsonTargets, gctreeManagedMarkdownTargets, mergeGcTreeHooksJson, unmergeGcTreeHooksJson, upsertManagedMarkdownBlock, } from './integration-files.js';
3
+ import { gctreeGlobalHookJsonTarget, gctreeGlobalRoot, gctreeHookJsonTargets, gctreeManagedMarkdownTargets, mergeGcTreeHooksJson, unmergeGcTreeHooksJson, upsertManagedMarkdownBlock, } from './integration-files.js';
4
4
  import { onboardingCompletionLines, onboardingProtocolLines } from './onboarding-protocol.js';
5
5
  function renderCodexAgentsSnippet() {
6
6
  return [
@@ -205,8 +205,17 @@ function renderClaudeUpdateCommand() {
205
205
  '',
206
206
  ].join('\n');
207
207
  }
208
- export function scaffoldFiles(host) {
208
+ export function scaffoldFiles(host, scope = 'local') {
209
209
  if (host === 'codex') {
210
+ if (scope === 'global') {
211
+ return [
212
+ { path: 'hooks.json', content: renderCodexHooksJson() },
213
+ { path: 'prompts/gctree-bootstrap.md', content: renderCodexBootstrapPrompt() },
214
+ { path: 'skills/gc-resolve-context/SKILL.md', content: renderCodexResolveSkill() },
215
+ { path: 'skills/gc-onboard/SKILL.md', content: renderCodexOnboardSkill() },
216
+ { path: 'skills/gc-update-global-context/SKILL.md', content: renderCodexUpdateSkill() },
217
+ ];
218
+ }
210
219
  return [
211
220
  { path: 'AGENTS.md', content: renderCodexAgentsSnippet() },
212
221
  { path: '.codex/hooks.json', content: renderCodexHooksJson() },
@@ -216,6 +225,15 @@ export function scaffoldFiles(host) {
216
225
  { path: '.codex/skills/gc-update-global-context/SKILL.md', content: renderCodexUpdateSkill() },
217
226
  ];
218
227
  }
228
+ if (scope === 'global') {
229
+ return [
230
+ { path: 'hooks/hooks.json', content: renderClaudeHooksJson() },
231
+ { path: 'hooks/gctree-session-start.md', content: renderClaudeSessionStartHook() },
232
+ { path: 'commands/gc-resolve-context.md', content: renderClaudeResolveCommand() },
233
+ { path: 'commands/gc-onboard.md', content: renderClaudeOnboardCommand() },
234
+ { path: 'commands/gc-update-global-context.md', content: renderClaudeUpdateCommand() },
235
+ ];
236
+ }
219
237
  return [
220
238
  { path: 'CLAUDE.md', content: renderClaudeSnippet() },
221
239
  { path: '.claude/hooks/hooks.json', content: renderClaudeHooksJson() },
@@ -225,39 +243,48 @@ export function scaffoldFiles(host) {
225
243
  { path: '.claude/commands/gc-update-global-context.md', content: renderClaudeUpdateCommand() },
226
244
  ];
227
245
  }
228
- export async function scaffoldHostIntegration({ host, targetDir, force = false, }) {
229
- const files = scaffoldFiles(host);
246
+ export async function scaffoldHostIntegration({ host, targetDir, force = false, scope = 'local', }) {
247
+ const resolvedTargetDir = scope === 'global' ? gctreeGlobalRoot(host) : targetDir;
248
+ const files = scaffoldFiles(host, scope);
230
249
  const written = [];
231
250
  const skippedExisting = [];
232
- const markdownTargets = gctreeManagedMarkdownTargets(targetDir);
233
- const hookTargets = gctreeHookJsonTargets(targetDir);
234
251
  const isCodex = host === 'codex';
235
- const managedMarkdownPath = isCodex ? markdownTargets.codex : markdownTargets.claude;
236
- const managedMarkdownContent = isCodex ? renderCodexAgentsSnippet() : renderClaudeSnippet();
237
- await upsertManagedMarkdownBlock({
238
- filePath: managedMarkdownPath,
239
- content: managedMarkdownContent,
240
- marker: isCodex ? 'codex' : 'claude',
241
- });
242
- written.push(managedMarkdownPath);
243
- const hookPath = isCodex ? hookTargets.codex : hookTargets.claude;
252
+ let managedMarkdownPath = null;
253
+ let hookPath;
254
+ if (scope === 'local') {
255
+ const markdownTargets = gctreeManagedMarkdownTargets(resolvedTargetDir);
256
+ const hookTargets = gctreeHookJsonTargets(resolvedTargetDir);
257
+ managedMarkdownPath = isCodex ? markdownTargets.codex : markdownTargets.claude;
258
+ const managedMarkdownContent = isCodex ? renderCodexAgentsSnippet() : renderClaudeSnippet();
259
+ await upsertManagedMarkdownBlock({
260
+ filePath: managedMarkdownPath,
261
+ content: managedMarkdownContent,
262
+ marker: isCodex ? 'codex' : 'claude',
263
+ });
264
+ written.push(managedMarkdownPath);
265
+ hookPath = isCodex ? hookTargets.codex : hookTargets.claude;
266
+ if (!isCodex) {
267
+ const oldHooksPath = join(resolvedTargetDir, '.claude', 'settings.json');
268
+ await unmergeGcTreeHooksJson(oldHooksPath);
269
+ }
270
+ }
271
+ else {
272
+ hookPath = gctreeGlobalHookJsonTarget(host);
273
+ if (!isCodex) {
274
+ const oldHooksPath = join(resolvedTargetDir, 'settings.json');
275
+ await unmergeGcTreeHooksJson(oldHooksPath);
276
+ }
277
+ }
244
278
  await mergeGcTreeHooksJson({
245
279
  filePath: hookPath,
246
280
  target: isCodex ? 'codex' : 'claude-code',
247
281
  });
248
282
  written.push(hookPath);
249
- // Migrate: clean up gctree entries from old hooks.json location (claude-code only)
250
- if (!isCodex) {
251
- const oldHooksPath = join(targetDir, '.claude', 'settings.json');
252
- await unmergeGcTreeHooksJson(oldHooksPath);
253
- }
254
283
  const targets = files.map((file) => ({
255
284
  ...file,
256
- fullPath: join(targetDir, file.path),
285
+ fullPath: join(resolvedTargetDir, file.path),
257
286
  })).filter((target) => {
258
- if (isCodex)
259
- return target.path !== 'AGENTS.md' && target.path !== '.codex/hooks.json';
260
- return target.path !== 'CLAUDE.md' && target.path !== '.claude/hooks/hooks.json';
287
+ return target.fullPath !== hookPath && target.fullPath !== managedMarkdownPath;
261
288
  });
262
289
  for (const target of targets) {
263
290
  let exists = false;
@@ -279,7 +306,7 @@ export async function scaffoldHostIntegration({ host, targetDir, force = false,
279
306
  }
280
307
  return {
281
308
  host,
282
- target_dir: targetDir,
309
+ target_dir: resolvedTargetDir,
283
310
  written,
284
311
  skipped_existing: skippedExisting,
285
312
  };
package/dist/src/store.js CHANGED
@@ -2,7 +2,7 @@ import { cp, mkdir, readFile, readdir, rm, writeFile } from 'node:fs/promises';
2
2
  import { existsSync } from 'node:fs';
3
3
  import { join } from 'node:path';
4
4
  import { branchDir, branchDocsDir, branchIndexPath, branchMetaPath, branchesRoot, DEFAULT_BRANCH, headPath, } from './paths.js';
5
- import { extractIndexEntries, renderIndexMarkdown } from './markdown.js';
5
+ import { extractIndexEntries, inferDocPlacement, normalizeIndexEntry, renderIndexMarkdown } from './markdown.js';
6
6
  async function listDocRelativePaths(dir, prefix = '') {
7
7
  const entries = await readdir(dir, { withFileTypes: true }).catch(() => []);
8
8
  const files = [];
@@ -113,14 +113,19 @@ export async function writeIndexFromDocs(home, branch) {
113
113
  const raw = await readFile(join(docsDir, file), 'utf8');
114
114
  const title = raw.match(/^#\s+(.+)$/m)?.[1]?.trim() || file.replace(/\.md$/i, '').replace(/-/g, ' ');
115
115
  const parts = file.replace(/\.md$/i, '').split('/').filter(Boolean);
116
- const category = parts.length > 1 ? parts[0] : 'general';
117
- const label = parts.length > 1 ? parts[parts.length - 1] : title;
118
- const indexEntries = extractIndexEntries(raw);
119
- const entryLabels = indexEntries.length > 0 ? indexEntries : [label];
120
- return entryLabels.map((entryLabel) => ({
116
+ const fallbackCategory = parts.length > 1 ? parts[0] : 'general';
117
+ const fallbackLabel = parts.length > 1 ? parts[parts.length - 1] : title;
118
+ const inferred = inferDocPlacement({ title, indexLabel: title });
119
+ const docCategory = inferred.category || fallbackCategory;
120
+ const indexEntries = extractIndexEntries(raw)
121
+ .map((entry) => normalizeIndexEntry(entry, { category: docCategory, label: fallbackLabel }))
122
+ .filter((entry) => Boolean(entry));
123
+ const entryLabels = indexEntries.length > 0 ? indexEntries : [{ category: docCategory, label: fallbackLabel }];
124
+ const uniqueEntries = [...new Map(entryLabels.map((entry) => [`${entry.category}::${entry.label}`, entry])).values()];
125
+ return uniqueEntries.map((entry) => ({
121
126
  title,
122
- label: entryLabel,
123
- category,
127
+ label: entry.label,
128
+ category: entry.category,
124
129
  path: `docs/${file}`,
125
130
  }));
126
131
  }));
@@ -1,6 +1,6 @@
1
1
  import { readdir, rm } from 'node:fs/promises';
2
2
  import { dirname, join } from 'node:path';
3
- import { gctreeHookJsonTargets, gctreeManagedMarkdownTargets, pathExists, removeManagedMarkdownBlock, unmergeGcTreeHooksJson, } from './integration-files.js';
3
+ import { gctreeGlobalHookJsonTarget, gctreeGlobalRoot, gctreeHookJsonTargets, gctreeManagedMarkdownTargets, pathExists, removeManagedMarkdownBlock, unmergeGcTreeHooksJson, } from './integration-files.js';
4
4
  import { scaffoldFiles } from './scaffold.js';
5
5
  async function pathHasEntries(path) {
6
6
  const entries = await readdir(path).catch(() => []);
@@ -18,52 +18,56 @@ async function pruneEmptyParents(targetPath, stopDir) {
18
18
  function hostsFor(mode) {
19
19
  return mode === 'both' ? ['claude-code', 'codex'] : [mode];
20
20
  }
21
+ async function uninstallHostScaffold({ host, scope, targetDir, removed, }) {
22
+ const isCodex = host === 'codex';
23
+ const managedMarkdownPath = scope === 'local'
24
+ ? (isCodex ? gctreeManagedMarkdownTargets(targetDir).codex : gctreeManagedMarkdownTargets(targetDir).claude)
25
+ : null;
26
+ const hookPath = scope === 'local'
27
+ ? (isCodex ? gctreeHookJsonTargets(targetDir).codex : gctreeHookJsonTargets(targetDir).claude)
28
+ : gctreeGlobalHookJsonTarget(host);
29
+ if (managedMarkdownPath) {
30
+ const markdownStatus = await removeManagedMarkdownBlock({
31
+ filePath: managedMarkdownPath,
32
+ marker: isCodex ? 'codex' : 'claude',
33
+ });
34
+ if (markdownStatus !== 'missing') {
35
+ removed.push(managedMarkdownPath);
36
+ await pruneEmptyParents(managedMarkdownPath, targetDir);
37
+ }
38
+ }
39
+ const hookStatus = await unmergeGcTreeHooksJson(hookPath);
40
+ if (hookStatus !== 'missing') {
41
+ removed.push(hookPath);
42
+ await pruneEmptyParents(hookPath, targetDir);
43
+ }
44
+ const extraTargets = scaffoldFiles(host, scope)
45
+ .map((file) => join(targetDir, file.path))
46
+ .filter((path) => path !== hookPath && path !== managedMarkdownPath);
47
+ for (const target of [...new Set(extraTargets)]) {
48
+ if (!(await pathExists(target)))
49
+ continue;
50
+ await rm(target, { recursive: true, force: true });
51
+ removed.push(target);
52
+ await pruneEmptyParents(target, targetDir);
53
+ }
54
+ }
21
55
  export async function uninstallGcTree({ home, targetDir, host = 'both', keepHome = false, }) {
22
56
  const removed = [];
23
- const markdownTargets = gctreeManagedMarkdownTargets(targetDir);
24
- const hookTargets = gctreeHookJsonTargets(targetDir);
25
57
  for (const entry of hostsFor(host)) {
26
- if (entry === 'codex') {
27
- const markdownStatus = await removeManagedMarkdownBlock({
28
- filePath: markdownTargets.codex,
29
- marker: 'codex',
30
- });
31
- if (markdownStatus !== 'missing') {
32
- removed.push(markdownTargets.codex);
33
- await pruneEmptyParents(markdownTargets.codex, targetDir);
34
- }
35
- const hookStatus = await unmergeGcTreeHooksJson(hookTargets.codex);
36
- if (hookStatus !== 'missing') {
37
- removed.push(hookTargets.codex);
38
- await pruneEmptyParents(hookTargets.codex, targetDir);
39
- }
40
- }
41
- else {
42
- const markdownStatus = await removeManagedMarkdownBlock({
43
- filePath: markdownTargets.claude,
44
- marker: 'claude',
58
+ await uninstallHostScaffold({
59
+ host: entry,
60
+ scope: 'global',
61
+ targetDir: gctreeGlobalRoot(entry),
62
+ removed,
63
+ });
64
+ if (targetDir) {
65
+ await uninstallHostScaffold({
66
+ host: entry,
67
+ scope: 'local',
68
+ targetDir,
69
+ removed,
45
70
  });
46
- if (markdownStatus !== 'missing') {
47
- removed.push(markdownTargets.claude);
48
- await pruneEmptyParents(markdownTargets.claude, targetDir);
49
- }
50
- const hookStatus = await unmergeGcTreeHooksJson(hookTargets.claude);
51
- if (hookStatus !== 'missing') {
52
- removed.push(hookTargets.claude);
53
- await pruneEmptyParents(hookTargets.claude, targetDir);
54
- }
55
- }
56
- const extraTargets = scaffoldFiles(entry)
57
- .map((file) => join(targetDir, file.path))
58
- .filter((path) => entry === 'codex'
59
- ? path !== markdownTargets.codex && path !== hookTargets.codex
60
- : path !== markdownTargets.claude && path !== hookTargets.claude);
61
- for (const target of [...new Set(extraTargets)]) {
62
- if (!(await pathExists(target)))
63
- continue;
64
- await rm(target, { recursive: true, force: true });
65
- removed.push(target);
66
- await pruneEmptyParents(target, targetDir);
67
71
  }
68
72
  }
69
73
  let homeRemoved = false;
@@ -73,7 +77,7 @@ export async function uninstallGcTree({ home, targetDir, host = 'both', keepHome
73
77
  removed.push(home);
74
78
  }
75
79
  return {
76
- target_dir: targetDir,
80
+ target_dir: targetDir || '(global)',
77
81
  home,
78
82
  host,
79
83
  home_removed: homeRemoved,
@@ -36,7 +36,7 @@ Esto le da a las herramientas aguas abajo un camino rápido: leer primero la ver
36
36
  ## 5. Hacer el onboarding explícito y guiado
37
37
 
38
38
  Un usuario no debería tener que escribir a mano JSON de onboarding solo para crear un árbol de contexto útil.
39
- `gctree init` y `gctree onboard` deben guiar al usuario a través de su proveedor preferido y escribir el contexto resultante en la gc-branch activa.
39
+ `gctree init` y `gctree onboard` deben guiar al usuario a través de su proveedor preferido, habilitar ese proveedor globalmente y escribir el contexto resultante en la gc-branch activa.
40
40
 
41
41
  El onboarding es solo para gc-branches vacías.
42
42
  Si una gc-branch ya contiene contexto, el camino correcto es:
@@ -59,5 +59,5 @@ En la práctica, esto significa que se inyecta aproximadamente el 4 % del contex
59
59
  ## 8. Mantenerse agnóstico al proveedor
60
60
 
61
61
  `gctree` almacena el contexto en archivos markdown simples que cualquier herramienta puede leer.
62
- Claude Code y Codex hacen scaffold contra el mismo almacén subyacente mediante `gctree scaffold`.
62
+ Claude Code y Codex usan el mismo almacén subyacente. `gctree init` instala la superficie global de hooks orientada al proveedor, y `gctree scaffold` es la ruta de override local para un repositorio o workspace concreto.
63
63
  Agregar soporte para un nuevo proveedor significa escribir una nueva plantilla de scaffold — sin cambios en la lógica central de almacenamiento ni de resolve.
@@ -36,7 +36,7 @@ gc-branch がマシン上のすべてのリポジトリに黙って影響して
36
36
  ## 5. オンボーディングは明示的かつガイド付きにする
37
37
 
38
38
  使えるコンテキストツリーを作るために、ユーザーがオンボーディング用の JSON を手書きしなければならないのはよくありません。
39
- `gctree init` と `gctree onboard` は、ユーザーが選んだプロバイダーに沿って流れを案内し、その結果のコンテキストを現在アクティブな gc-branch に書き込むべきです。
39
+ `gctree init` と `gctree onboard` は、ユーザーが選んだプロバイダーに沿って流れを案内し、そのプロバイダーをグローバル有効化し、その結果のコンテキストを現在アクティブな gc-branch に書き込むべきです。
40
40
 
41
41
  オンボーディングは空の gc-branch に対してだけ行うものです。
42
42
  すでにコンテキストが入っている gc-branch の場合、正しい道筋は次のどちらかです。
@@ -59,5 +59,5 @@ gc-branch がマシン上のすべてのリポジトリに黙って影響して
59
59
  ## 8. プロバイダーに依存しない設計を保つ
60
60
 
61
61
  `gctree` はコンテキストをプレーンなマークダウンファイルとして保存するため、どのツールでも読み込めます。
62
- Claude Code と Codex はどちらも `gctree scaffold` を通じて同じ基盤のストアに対して scaffold します。
62
+ Claude Code と Codex はどちらも同じ基盤ストアを使います。`gctree init` はグローバルなプロバイダーフック面をインストールし、`gctree scaffold` は1つのリポジトリやワークスペース向けのローカル override 経路です。
63
63
  新しいプロバイダーのサポートを追加するには、新しい scaffold テンプレートを書くだけです。コアのストレージや resolve ロジックに変更は不要です。
@@ -36,7 +36,7 @@ gc-branch가 머신의 모든 저장소에 조용히 영향을 미쳐서는 안
36
36
  ## 5. 온보딩을 명시적이고 안내된 방식으로 만든다
37
37
 
38
38
  사용자가 유용한 컨텍스트 트리를 만들기 위해 온보딩 JSON을 직접 작성하도록 요구해서는 안 됩니다.
39
- `gctree init`과 `gctree onboard`는 사용자가 선호하는 프로바이더를 안내하고, 결과 컨텍스트를 활성 gc-branch에 기록해야 합니다.
39
+ `gctree init`과 `gctree onboard`는 사용자가 선호하는 프로바이더를 안내하고, 해당 프로바이더를 전역 활성화하며, 결과 컨텍스트를 활성 gc-branch에 기록해야 합니다.
40
40
 
41
41
  온보딩은 빈 gc-branch에만 적용됩니다.
42
42
  gc-branch에 이미 컨텍스트가 있다면, 올바른 경로는 다음 중 하나입니다:
@@ -59,5 +59,5 @@ gc-branch에 이미 컨텍스트가 있다면, 올바른 경로는 다음 중
59
59
  ## 8. 프로바이더에 종속되지 않는다
60
60
 
61
61
  `gctree`는 어떤 도구든 읽을 수 있는 일반 마크다운 파일에 컨텍스트를 저장합니다.
62
- Claude Code와 Codex 모두 `gctree scaffold`를 통해 동일한 기반 저장소에 대해 scaffold합니다.
62
+ Claude Code와 Codex 모두 동일한 기반 저장소를 사용합니다. `gctree init`은 전역 프로바이더 훅 표면을 설치하고, `gctree scaffold`는 저장소나 워크스페이스를 위한 로컬 override 경로입니다.
63
63
  새로운 프로바이더 지원 추가는 새 scaffold 템플릿을 작성하는 것을 의미합니다 — 핵심 저장소나 resolve 로직은 변경이 필요 없습니다.
@@ -36,7 +36,7 @@ That gives downstream tools a fast path: read the short version first, then expa
36
36
  ## 5. Make onboarding explicit and guided
37
37
 
38
38
  A user should not have to hand-author onboarding JSON just to create a useful context tree.
39
- `gctree init` and `gctree onboard` should guide the user through their preferred provider and write the resulting context into the active gc-branch.
39
+ `gctree init` and `gctree onboard` should guide the user through their preferred provider, enable that provider globally, and write the resulting context into the active gc-branch.
40
40
 
41
41
  Onboarding is only for empty gc-branches.
42
42
  If a gc-branch already contains context, the correct path is either:
@@ -59,5 +59,5 @@ In practice this means roughly 4% of total stored context is injected per query.
59
59
  ## 8. Stay provider-agnostic
60
60
 
61
61
  `gctree` stores context in plain markdown files that any tool can read.
62
- Claude Code and Codex both scaffold against the same underlying store via `gctree scaffold`.
62
+ Claude Code and Codex both use the same underlying store. `gctree init` installs the global provider-facing hook surface, and `gctree scaffold` is the local override path for one repository or workspace.
63
63
  Adding support for a new provider means writing a new scaffold template — no changes to the core storage or resolve logic.
@@ -36,7 +36,7 @@ gc-branch 不应该悄无声息地影响机器上的每一个代码仓库。
36
36
  ## 5. 让 onboarding 明确且有引导
37
37
 
38
38
  用户不应该仅仅为了创建一个有用的上下文树而手动编写 onboarding JSON。
39
- `gctree init` 和 `gctree onboard` 应该引导用户选择首选的提供商,并将生成的上下文写入活跃的 gc-branch。
39
+ `gctree init` 和 `gctree onboard` 应该引导用户选择首选的提供商,为该提供商启用全局激活,并将生成的上下文写入活跃的 gc-branch。
40
40
 
41
41
  Onboarding 仅适用于空的 gc-branch。
42
42
  如果一个 gc-branch 已经包含上下文,正确的做法是:
@@ -59,5 +59,5 @@ Onboarding 仅适用于空的 gc-branch。
59
59
  ## 8. 保持提供商无关性
60
60
 
61
61
  `gctree` 将上下文存储在任何工具都能读取的纯 Markdown 文件中。
62
- Claude Code 和 Codex 都通过 `gctree scaffold` 对接同一个底层存储。
62
+ Claude Code 和 Codex 使用同一个底层存储。`gctree init` 安装全局 provider hook 表面,`gctree scaffold` 则是针对单个仓库或工作区的本地 override 路径。
63
63
  添加对新提供商的支持,意味着编写一个新的 scaffold 模板——无需对核心存储或 resolve 逻辑做任何修改。
package/docs/usage.es.md CHANGED
@@ -25,7 +25,7 @@ Un flujo de trabajo estándar con `gctree` tiene este aspecto: inicializar gc-tr
25
25
 
26
26
  | Comando | Propósito |
27
27
  | --- | --- |
28
- | `gctree init` | Crear `~/.gctree`, crear la gc-branch `main` predeterminada, guardar el modo de proveedor, el proveedor de onboarding y el idioma preferido, hacer scaffold del entorno actual y comenzar el onboarding guiado cuando `main` está vacía. |
28
+ | `gctree init` | Crear `~/.gctree`, crear la gc-branch `main` predeterminada, guardar el modo de proveedor, el proveedor de onboarding y el idioma preferido, instalar hooks/comandos/skills globales del proveedor y comenzar el onboarding guiado cuando `main` está vacía. |
29
29
  | `gctree checkout <branch>` | Cambiar la gc-branch activa. |
30
30
  | `gctree checkout -b <branch>` | Crear y cambiar a una nueva gc-branch vacía. |
31
31
  | `gctree branches` | Listar las gc-branches disponibles y mostrar la activa. |
@@ -40,7 +40,8 @@ Un flujo de trabajo estándar con `gctree` tiene este aspecto: inicializar gc-tr
40
40
  | `gctree reset-gc-branch --branch <name> --yes` | Limpiar una gc-branch para poder hacer onboarding de nuevo. |
41
41
  | `gctree update-global-context` | Iniciar una actualización duradera guiada para la gc-branch activa. |
42
42
  | `gctree update-gc` / `gctree ugc` | Alias para `gctree update-global-context`. |
43
- | `gctree scaffold --host <codex\|claude-code>` | Instalar la interfaz de comandos orientada al proveedor en otro entorno. |
43
+ | `gctree scaffold --host <codex\|claude-code>` | Instalar un override local orientado al proveedor en un repositorio o workspace concreto. |
44
+ | `gctree uninstall --yes` | Eliminar `~/.gctree` y la activación global de gctree. |
44
45
 
45
46
  ## Qué devuelve resolve
46
47
 
@@ -121,7 +122,7 @@ gctree init
121
122
  Luego:
122
123
 
123
124
  1. elegir `codex` o `claude-code`
124
- 2. dejar que `gctree` haga scaffold del entorno actual
125
+ 2. dejar que `gctree` instale la activación global para ese proveedor
125
126
  3. completar el onboarding guiado para la gc-branch `main`
126
127
 
127
128
  ## Ejemplo de flujo con múltiples branches
@@ -154,7 +155,7 @@ Si un repositorio recién relevante también debería pasar a formar parte del c
154
155
 
155
156
  ### Codex CLI / Claude Code CLI
156
157
 
157
- `gctree scaffold` instala archivos de comandos orientados al proveedor en el directorio de destino.
158
+ `gctree init` instala la superficie global de hooks orientada al proveedor. `gctree scaffold` instala un override local en un directorio de destino cuando un repositorio concreto necesita sus propios snippets markdown o una superficie de comandos local.
158
159
 
159
160
  ```bash
160
161
  gctree scaffold --host codex --target /path/to/repo
@@ -162,7 +163,17 @@ gctree scaffold --host claude-code --target /path/to/repo
162
163
  gctree scaffold --host both --target /path/to/repo
163
164
  ```
164
165
 
165
- **Archivos escritos para `--host codex`:**
166
+ **Archivos globales de Codex (`gctree init`):**
167
+
168
+ ```
169
+ ~/.codex/hooks.json ← hooks de auto-resolve para SessionStart/UserPromptSubmit
170
+ ~/.codex/prompts/gctree-bootstrap.md ← contexto de arranque para sesiones de Codex
171
+ ~/.codex/skills/gc-resolve-context/SKILL.md ← skill de resolve
172
+ ~/.codex/skills/gc-onboard/SKILL.md ← skill de onboarding
173
+ ~/.codex/skills/gc-update-global-context/SKILL.md ← skill de actualización
174
+ ```
175
+
176
+ **Archivos de override local para `gctree scaffold --host codex`:**
166
177
 
167
178
  ```
168
179
  AGENTS.md ← fragmento de gctree añadido a las instrucciones del agente
@@ -173,7 +184,17 @@ AGENTS.md ← fragmento de gctree añadido a la
173
184
  .codex/skills/gc-update-global-context/SKILL.md ← skill de actualización
174
185
  ```
175
186
 
176
- **Archivos escritos para `--host claude-code`:**
187
+ **Archivos globales de Claude Code (`gctree init`):**
188
+
189
+ ```
190
+ ~/.claude/hooks/hooks.json ← hooks de auto-resolve para SessionStart/UserPromptSubmit
191
+ ~/.claude/hooks/gctree-session-start.md ← nota fallback de inicio de sesión
192
+ ~/.claude/commands/gc-resolve-context.md ← comando slash de resolve
193
+ ~/.claude/commands/gc-onboard.md ← comando slash de onboard
194
+ ~/.claude/commands/gc-update-global-context.md ← comando slash de actualización
195
+ ```
196
+
197
+ **Archivos de override local para `gctree scaffold --host claude-code`:**
177
198
 
178
199
  ```
179
200
  CLAUDE.md ← fragmento de gctree añadido
@@ -184,7 +205,7 @@ CLAUDE.md ← fragmento de gctree añadid
184
205
  .claude/commands/gc-update-global-context.md ← comando slash de actualización
185
206
  ```
186
207
 
187
- Los archivos existentes no se modifican a menos que pases `--force`.
208
+ Los archivos locales existentes no se modifican a menos que pases `--force`.
188
209
 
189
210
  ### Comportamiento en tiempo de ejecución
190
211
 
package/docs/usage.ja.md CHANGED
@@ -25,7 +25,7 @@
25
25
 
26
26
  | コマンド | 役割 |
27
27
  | --- | --- |
28
- | `gctree init` | `~/.gctree` を作成し、デフォルトの `main` gc-branch を作り、プロバイダーモード・オンボーディングプロバイダー・優先言語を保存し、現在の環境を scaffold し、`main` が空ならガイド付きオンボーディングまで開始します。 |
28
+ | `gctree init` | `~/.gctree` を作成し、デフォルトの `main` gc-branch を作り、プロバイダーモード・オンボーディングプロバイダー・優先言語を保存し、グローバルなプロバイダーフック/コマンド/スキルをインストールし、`main` が空ならガイド付きオンボーディングまで開始します。 |
29
29
  | `gctree checkout <branch>` | アクティブな gc-branch を切り替えます。 |
30
30
  | `gctree checkout -b <branch>` | 新しい空の gc-branch を作成し、そのまま切り替えます。 |
31
31
  | `gctree branches` | 利用可能な gc-branch 一覧と、現在アクティブなものを表示します。 |
@@ -40,7 +40,8 @@
40
40
  | `gctree reset-gc-branch --branch <name> --yes` | gc-branch を空にして再オンボード可能な状態にします。 |
41
41
  | `gctree update-global-context` | アクティブな gc-branch に対するガイド付き永続的更新を開始します。 |
42
42
  | `gctree update-gc` / `gctree ugc` | `gctree update-global-context` の別名です。 |
43
- | `gctree scaffold --host <codex\|claude-code>` | 別の環境にプロバイダー向けコマンド面をインストールします。 |
43
+ | `gctree scaffold --host <codex\|claude-code>` | 1つのリポジトリやワークスペースにローカルなプロバイダー override をインストールします。 |
44
+ | `gctree uninstall --yes` | `~/.gctree` とグローバルな gctree 有効化を削除します。 |
44
45
 
45
46
  ## resolve が返す内容
46
47
 
@@ -121,7 +122,7 @@ gctree init
121
122
  そのあと:
122
123
 
123
124
  1. `codex` か `claude-code` を選ぶ
124
- 2. `gctree` に現在の環境を scaffold させる
125
+ 2. `gctree` にそのプロバイダー向けのグローバル有効化をインストールさせる
125
126
  3. `main` gc-branch のガイド付きオンボーディングを完了する
126
127
 
127
128
  ## 複数ブランチを使う例
@@ -154,7 +155,7 @@ gctree ugc
154
155
 
155
156
  ### Codex CLI / Claude Code CLI
156
157
 
157
- `gctree scaffold` はプロバイダー向けのコマンドファイルをターゲットディレクトリにインストールします。
158
+ `gctree init` はグローバルなプロバイダーフック面をインストールします。`gctree scaffold` は特定のリポジトリに独自の markdown スニペットやローカルなコマンド面が必要なときに、ターゲットディレクトリへローカル override をインストールします。
158
159
 
159
160
  ```bash
160
161
  gctree scaffold --host codex --target /path/to/repo
@@ -162,7 +163,17 @@ gctree scaffold --host claude-code --target /path/to/repo
162
163
  gctree scaffold --host both --target /path/to/repo
163
164
  ```
164
165
 
165
- **`--host codex` で書き込まれるファイル:**
166
+ **Codex のグローバルファイル(`gctree init`):**
167
+
168
+ ```
169
+ ~/.codex/hooks.json ← SessionStart / UserPromptSubmit の自動 resolve フック
170
+ ~/.codex/prompts/gctree-bootstrap.md ← Codex セッション向けのブートストラップコンテキスト
171
+ ~/.codex/skills/gc-resolve-context/SKILL.md ← resolve スキル
172
+ ~/.codex/skills/gc-onboard/SKILL.md ← オンボーディングスキル
173
+ ~/.codex/skills/gc-update-global-context/SKILL.md ← 更新スキル
174
+ ```
175
+
176
+ **`gctree scaffold --host codex` のローカル override ファイル:**
166
177
 
167
178
  ```
168
179
  AGENTS.md ← gctree のスニペットがエージェント指示に追記される
@@ -173,7 +184,17 @@ AGENTS.md ← gctree のスニペットがエ
173
184
  .codex/skills/gc-update-global-context/SKILL.md ← 更新スキル
174
185
  ```
175
186
 
176
- **`--host claude-code` で書き込まれるファイル:**
187
+ **Claude Code のグローバルファイル(`gctree init`):**
188
+
189
+ ```
190
+ ~/.claude/hooks/hooks.json ← SessionStart / UserPromptSubmit の自動 resolve フック
191
+ ~/.claude/hooks/gctree-session-start.md ← セッション開始のフォールバックメモ
192
+ ~/.claude/commands/gc-resolve-context.md ← resolve スラッシュコマンド
193
+ ~/.claude/commands/gc-onboard.md ← onboard スラッシュコマンド
194
+ ~/.claude/commands/gc-update-global-context.md ← 更新スラッシュコマンド
195
+ ```
196
+
197
+ **`gctree scaffold --host claude-code` のローカル override ファイル:**
177
198
 
178
199
  ```
179
200
  CLAUDE.md ← gctree のスニペットが追記される
@@ -184,7 +205,7 @@ CLAUDE.md ← gctree のスニペット
184
205
  .claude/commands/gc-update-global-context.md ← 更新スラッシュコマンド
185
206
  ```
186
207
 
187
- `--force` を渡さない限り、既存のファイルは変更されません。
208
+ `--force` を渡さない限り、既存のローカルファイルは変更されません。
188
209
 
189
210
  ### ランタイムの動作
190
211
 
package/docs/usage.ko.md CHANGED
@@ -25,7 +25,7 @@
25
25
 
26
26
  | 명령어 | 설명 |
27
27
  | --- | --- |
28
- | `gctree init` | `~/.gctree` 생성, 기본 `main` gc-branch 생성, 프로바이더 모드·온보딩 프로바이더·선호 언어 저장, 현재 환경 scaffold, `main`이 비어 있으면 안내된 온보딩 시작. |
28
+ | `gctree init` | `~/.gctree` 생성, 기본 `main` gc-branch 생성, 프로바이더 모드·온보딩 프로바이더·선호 언어 저장, 전역 프로바이더 훅/명령/스킬 설치, `main`이 비어 있으면 안내된 온보딩 시작. |
29
29
  | `gctree checkout <branch>` | 활성 gc-branch 전환. |
30
30
  | `gctree checkout -b <branch>` | 새 빈 gc-branch를 생성하고 전환. |
31
31
  | `gctree branches` | 사용 가능한 gc-branch 목록 표시 및 현재 활성 브랜치 표시. |
@@ -40,7 +40,8 @@
40
40
  | `gctree reset-gc-branch --branch <name> --yes` | gc-branch를 초기화하여 다시 온보딩할 수 있게 함. |
41
41
  | `gctree update-global-context` | 활성 gc-branch의 안내된 지속 업데이트 실행. |
42
42
  | `gctree update-gc` / `gctree ugc` | `gctree update-global-context`의 별칭. |
43
- | `gctree scaffold --host <codex\|claude-code>` | 다른 환경에 프로바이더 대면 명령 표면 설치. |
43
+ | `gctree scaffold --host <codex\|claude-code>` | 특정 저장소나 워크스페이스에 로컬 프로바이더 override 설치. |
44
+ | `gctree uninstall --yes` | `~/.gctree`와 전역 gctree 활성화를 제거. |
44
45
 
45
46
  ## resolve가 반환하는 것
46
47
 
@@ -121,7 +122,7 @@ gctree init
121
122
  이후:
122
123
 
123
124
  1. `codex` 또는 `claude-code` 선택
124
- 2. `gctree`가 현재 환경을 scaffold하도록 허용
125
+ 2. `gctree`가 해당 프로바이더에 대해 전역 활성화를 설치하도록 허용
125
126
  3. `main` gc-branch의 안내된 온보딩 완료
126
127
 
127
128
  ## 다중 브랜치 예시 흐름
@@ -154,7 +155,7 @@ gctree ugc
154
155
 
155
156
  ### Codex CLI / Claude Code CLI
156
157
 
157
- `gctree scaffold`는 대상 디렉토리에 프로바이더 대면 명령 파일을 설치합니다.
158
+ `gctree init`은 전역 프로바이더 훅 표면을 설치합니다. `gctree scaffold`는 특정 저장소에 자체 markdown 스니펫이나 로컬 명령 표면이 필요할 때 대상 디렉토리에 로컬 override를 설치합니다.
158
159
 
159
160
  ```bash
160
161
  gctree scaffold --host codex --target /path/to/repo
@@ -162,7 +163,17 @@ gctree scaffold --host claude-code --target /path/to/repo
162
163
  gctree scaffold --host both --target /path/to/repo
163
164
  ```
164
165
 
165
- **`--host codex`로 작성되는 파일:**
166
+ **Codex 전역 파일 (`gctree init`):**
167
+
168
+ ```
169
+ ~/.codex/hooks.json ← SessionStart/UserPromptSubmit 자동 resolve 훅
170
+ ~/.codex/prompts/gctree-bootstrap.md ← Codex 세션 부트스트랩 컨텍스트
171
+ ~/.codex/skills/gc-resolve-context/SKILL.md ← resolve 스킬
172
+ ~/.codex/skills/gc-onboard/SKILL.md ← 온보딩 스킬
173
+ ~/.codex/skills/gc-update-global-context/SKILL.md ← 업데이트 스킬
174
+ ```
175
+
176
+ **`gctree scaffold --host codex` 로컬 override 파일:**
166
177
 
167
178
  ```
168
179
  AGENTS.md ← 에이전트 지시사항에 gctree 스니펫 추가
@@ -173,7 +184,17 @@ AGENTS.md ← 에이전트 지시사항에 gctr
173
184
  .codex/skills/gc-update-global-context/SKILL.md ← 업데이트 스킬
174
185
  ```
175
186
 
176
- **`--host claude-code`로 작성되는 파일:**
187
+ **Claude Code 전역 파일 (`gctree init`):**
188
+
189
+ ```
190
+ ~/.claude/hooks/hooks.json ← SessionStart/UserPromptSubmit 자동 resolve 훅
191
+ ~/.claude/hooks/gctree-session-start.md ← 세션 시작 fallback 메모
192
+ ~/.claude/commands/gc-resolve-context.md ← resolve 슬래시 명령
193
+ ~/.claude/commands/gc-onboard.md ← onboard 슬래시 명령
194
+ ~/.claude/commands/gc-update-global-context.md ← 업데이트 슬래시 명령
195
+ ```
196
+
197
+ **`gctree scaffold --host claude-code` 로컬 override 파일:**
177
198
 
178
199
  ```
179
200
  CLAUDE.md ← gctree 스니펫 추가
@@ -184,7 +205,7 @@ CLAUDE.md ← gctree 스니펫 추가
184
205
  .claude/commands/gc-update-global-context.md ← 업데이트 슬래시 명령
185
206
  ```
186
207
 
187
- `--force`를 전달하지 않으면 기존 파일은 그대로 유지됩니다.
208
+ `--force`를 전달하지 않으면 기존 로컬 파일은 그대로 유지됩니다.
188
209
 
189
210
  ### 런타임 동작
190
211
 
package/docs/usage.md CHANGED
@@ -25,7 +25,7 @@ A standard `gctree` workflow looks like this: initialize gc-tree, choose a provi
25
25
 
26
26
  | Command | Purpose |
27
27
  | --- | --- |
28
- | `gctree init` | Create `~/.gctree`, create the default `main` gc-branch, save the provider mode, onboarding provider, and preferred language, scaffold the current environment, and start guided onboarding when `main` is empty. |
28
+ | `gctree init` | Create `~/.gctree`, create the default `main` gc-branch, save the provider mode, onboarding provider, and preferred language, install global provider hooks/commands/skills, and start guided onboarding when `main` is empty. |
29
29
  | `gctree checkout <branch>` | Switch the active gc-branch. |
30
30
  | `gctree checkout -b <branch>` | Create and switch to a new empty gc-branch. |
31
31
  | `gctree branches` | List available gc-branches and show the active one. |
@@ -40,7 +40,8 @@ A standard `gctree` workflow looks like this: initialize gc-tree, choose a provi
40
40
  | `gctree reset-gc-branch --branch <name> --yes` | Clear a gc-branch so it can be onboarded again. |
41
41
  | `gctree update-global-context` | Launch a guided durable update for the active gc-branch. |
42
42
  | `gctree update-gc` / `gctree ugc` | Aliases for `gctree update-global-context`. |
43
- | `gctree scaffold --host <codex\|claude-code>` | Install the provider-facing command surface in another environment. |
43
+ | `gctree scaffold --host <codex\|claude-code>` | Install a local provider-facing override in one repository or workspace. |
44
+ | `gctree uninstall --yes` | Remove `~/.gctree` and the global gctree activation for the configured providers. |
44
45
 
45
46
  ## What resolve returns
46
47
 
@@ -121,7 +122,7 @@ gctree init
121
122
  Then:
122
123
 
123
124
  1. choose `codex` or `claude-code`
124
- 2. let `gctree` scaffold the current environment
125
+ 2. let `gctree` install global activation for that provider
125
126
  3. complete guided onboarding for the `main` gc-branch
126
127
 
127
128
  ## Example multi-branch flow
@@ -154,7 +155,7 @@ If a newly relevant repo should also become part of the durable context, the nat
154
155
 
155
156
  ### Codex CLI / Claude Code CLI
156
157
 
157
- `gctree scaffold` installs provider-facing command files into the target directory.
158
+ `gctree init` installs the provider-facing hook surface globally. `gctree scaffold` installs a local override into one target directory when a specific repository needs its own markdown snippets or local command surface.
158
159
 
159
160
  ```bash
160
161
  gctree scaffold --host codex --target /path/to/repo
@@ -162,7 +163,17 @@ gctree scaffold --host claude-code --target /path/to/repo
162
163
  gctree scaffold --host both --target /path/to/repo
163
164
  ```
164
165
 
165
- **Files written for `--host codex`:**
166
+ **Global files for Codex (`gctree init`):**
167
+
168
+ ```
169
+ ~/.codex/hooks.json ← SessionStart/UserPromptSubmit auto-resolve hooks
170
+ ~/.codex/prompts/gctree-bootstrap.md ← bootstrap context for Codex sessions
171
+ ~/.codex/skills/gc-resolve-context/SKILL.md ← resolve skill
172
+ ~/.codex/skills/gc-onboard/SKILL.md ← onboarding skill
173
+ ~/.codex/skills/gc-update-global-context/SKILL.md ← update skill
174
+ ```
175
+
176
+ **Local override files for `gctree scaffold --host codex`:**
166
177
 
167
178
  ```
168
179
  AGENTS.md ← gctree snippet appended to agent instructions
@@ -173,7 +184,17 @@ AGENTS.md ← gctree snippet appended to agent
173
184
  .codex/skills/gc-update-global-context/SKILL.md ← update skill
174
185
  ```
175
186
 
176
- **Files written for `--host claude-code`:**
187
+ **Global files for Claude Code (`gctree init`):**
188
+
189
+ ```
190
+ ~/.claude/hooks/hooks.json ← SessionStart/UserPromptSubmit auto-resolve hooks
191
+ ~/.claude/hooks/gctree-session-start.md ← session-start fallback note
192
+ ~/.claude/commands/gc-resolve-context.md ← resolve slash command
193
+ ~/.claude/commands/gc-onboard.md ← onboard slash command
194
+ ~/.claude/commands/gc-update-global-context.md ← update slash command
195
+ ```
196
+
197
+ **Local override files for `gctree scaffold --host claude-code`:**
177
198
 
178
199
  ```
179
200
  CLAUDE.md ← gctree snippet appended
@@ -184,7 +205,7 @@ CLAUDE.md ← gctree snippet appended
184
205
  .claude/commands/gc-update-global-context.md ← update slash command
185
206
  ```
186
207
 
187
- Existing files are left untouched unless you pass `--force`.
208
+ Existing local files are left untouched unless you pass `--force`.
188
209
 
189
210
  ### Runtime behavior
190
211
 
package/docs/usage.zh.md CHANGED
@@ -25,7 +25,7 @@
25
25
 
26
26
  | 命令 | 用途 |
27
27
  | --- | --- |
28
- | `gctree init` | 创建 `~/.gctree`,创建默认的 `main` gc-branch,保存提供商模式、onboarding 提供商及首选语言,scaffold 当前环境,并在 `main` 为空时启动有引导的 onboarding。 |
28
+ | `gctree init` | 创建 `~/.gctree`,创建默认的 `main` gc-branch,保存提供商模式、onboarding 提供商及首选语言,安装全局 provider hook/command/skill,并在 `main` 为空时启动有引导的 onboarding。 |
29
29
  | `gctree checkout <branch>` | 切换活跃的 gc-branch。 |
30
30
  | `gctree checkout -b <branch>` | 创建并切换到一个新的空 gc-branch。 |
31
31
  | `gctree branches` | 列出可用的 gc-branch 并显示当前活跃的那个。 |
@@ -40,7 +40,8 @@
40
40
  | `gctree reset-gc-branch --branch <name> --yes` | 清空一个 gc-branch,使其可以重新 onboard。 |
41
41
  | `gctree update-global-context` | 为活跃的 gc-branch 启动有引导的持久化更新。 |
42
42
  | `gctree update-gc` / `gctree ugc` | `gctree update-global-context` 的别名。 |
43
- | `gctree scaffold --host <codex\|claude-code>` | 在另一个环境中安装面向提供商的命令接口。 |
43
+ | `gctree scaffold --host <codex\|claude-code>` | 在单个仓库或工作区中安装本地 provider override。 |
44
+ | `gctree uninstall --yes` | 删除 `~/.gctree` 和全局 gctree 激活。 |
44
45
 
45
46
  ## resolve 返回的内容
46
47
 
@@ -121,7 +122,7 @@ gctree init
121
122
  然后:
122
123
 
123
124
  1. 选择 `codex` 或 `claude-code`
124
- 2. 让 `gctree` scaffold 当前环境
125
+ 2. 让 `gctree` 为该 provider 安装全局激活
125
126
  3. 对 `main` gc-branch 完成有引导的 onboarding
126
127
 
127
128
  ## 多分支示例流程
@@ -154,7 +155,7 @@ gctree ugc
154
155
 
155
156
  ### Codex CLI / Claude Code CLI
156
157
 
157
- `gctree scaffold` 将面向提供商的命令文件安装到目标目录中。
158
+ `gctree init` 会安装全局 provider hook 表面。`gctree scaffold` 则在某个仓库需要自己的 markdown 片段或本地命令表面时,将本地 override 安装到目标目录中。
158
159
 
159
160
  ```bash
160
161
  gctree scaffold --host codex --target /path/to/repo
@@ -162,7 +163,17 @@ gctree scaffold --host claude-code --target /path/to/repo
162
163
  gctree scaffold --host both --target /path/to/repo
163
164
  ```
164
165
 
165
- **`--host codex` 写入的文件:**
166
+ **Codex 全局文件(`gctree init`):**
167
+
168
+ ```
169
+ ~/.codex/hooks.json ← SessionStart / UserPromptSubmit 自动 resolve hook
170
+ ~/.codex/prompts/gctree-bootstrap.md ← Codex 会话引导上下文
171
+ ~/.codex/skills/gc-resolve-context/SKILL.md ← resolve 技能
172
+ ~/.codex/skills/gc-onboard/SKILL.md ← onboarding 技能
173
+ ~/.codex/skills/gc-update-global-context/SKILL.md ← 更新技能
174
+ ```
175
+
176
+ **`gctree scaffold --host codex` 的本地 override 文件:**
166
177
 
167
178
  ```
168
179
  AGENTS.md ← gctree 代码片段追加到 agent 指令中
@@ -173,7 +184,17 @@ AGENTS.md ← gctree 代码片段追加到 agen
173
184
  .codex/skills/gc-update-global-context/SKILL.md ← 更新技能
174
185
  ```
175
186
 
176
- **`--host claude-code` 写入的文件:**
187
+ **Claude Code 全局文件(`gctree init`):**
188
+
189
+ ```
190
+ ~/.claude/hooks/hooks.json ← SessionStart / UserPromptSubmit 自动 resolve hook
191
+ ~/.claude/hooks/gctree-session-start.md ← 会话启动 fallback 说明
192
+ ~/.claude/commands/gc-resolve-context.md ← resolve 斜杠命令
193
+ ~/.claude/commands/gc-onboard.md ← onboard 斜杠命令
194
+ ~/.claude/commands/gc-update-global-context.md ← 更新斜杠命令
195
+ ```
196
+
197
+ **`gctree scaffold --host claude-code` 的本地 override 文件:**
177
198
 
178
199
  ```
179
200
  CLAUDE.md ← gctree 代码片段追加
@@ -184,7 +205,7 @@ CLAUDE.md ← gctree 代码片段追加
184
205
  .claude/commands/gc-update-global-context.md ← 更新斜杠命令
185
206
  ```
186
207
 
187
- 除非传入 `--force`,否则已有文件不会被修改。
208
+ 除非传入 `--force`,否则已有本地文件不会被修改。
188
209
 
189
210
  ### 运行时行为
190
211
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@handsupmin/gc-tree",
3
- "version": "0.6.2",
3
+ "version": "0.7.1",
4
4
  "description": "Global Context Tree, a lightweight branch-aware global context orchestrator for AI coding tools",
5
5
  "type": "module",
6
6
  "private": false,