@handsupmin/gc-tree 0.7.17 → 0.7.19

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/dist/src/cli.js CHANGED
@@ -12,9 +12,9 @@ import { DEFAULT_BRANCH, branchDir, resolveHome } from './paths.js';
12
12
  import { branchRepoMapPath, branchScopeStatus, detectCurrentRepoId, promptResolveScopeDecision, readBranchRepoMap, resolveBranchForRepo, setRepoScopeForBranch, } from './repo-map.js';
13
13
  import { findRelatedDocs, getDocById, resolveContext } from './resolve.js';
14
14
  import { scaffoldHostIntegration } from './scaffold.js';
15
- import { requirePreferredProvider, writeSettings, readSettings } from './settings.js';
15
+ import { appendScaffoldedHost, requirePreferredProvider, writeSettings, readSettings } from './settings.js';
16
16
  import { checkoutBranch, initHome, listBranches, readHead, resetBranchContext, statusForBranch, ensureBranchExists, isBranchContextEmpty } from './store.js';
17
- import { updateBranchContext } from './update.js';
17
+ import { selfUpdate, updateBranchContext } from './update.js';
18
18
  import { uninstallGcTree } from './uninstall.js';
19
19
  import { verifyOnboarding } from './verify-onboarding.js';
20
20
  function readArg(flag) {
@@ -59,6 +59,7 @@ function usage() {
59
59
  gctree update-gc [--home DIR] [--branch NAME] [--provider <codex|claude-code>] [--target DIR] [--no-launch]
60
60
  gctree ugc [--home DIR] [--branch NAME] [--provider <codex|claude-code>] [--target DIR] [--no-launch]
61
61
  gctree scaffold --host <codex|claude-code|both> [--target DIR] [--force]
62
+ gctree update
62
63
 
63
64
  Internal commands:
64
65
  gctree __apply-onboarding --input FILE [--home DIR] [--branch NAME]
@@ -102,13 +103,16 @@ function normalizeProviderMode(value) {
102
103
  async function resolvePreferredProvider(home, explicitProvider) {
103
104
  return normalizeProvider(explicitProvider) || (await requirePreferredProvider(home));
104
105
  }
105
- async function ensureScaffold({ providerMode, targetDir, force = false, scope = 'local', }) {
106
+ async function ensureScaffold({ providerMode, targetDir, force = false, scope = 'local', home: scaffoldHome, }) {
106
107
  const hosts = providerMode === 'both' ? ['claude-code', 'codex'] : [providerMode];
107
108
  const combined = { hosts: providerMode, target_dir: scope === 'global' ? '(global)' : (targetDir || process.cwd()), written: [], skipped_existing: [] };
108
109
  for (const host of hosts) {
109
110
  const result = await scaffoldHostIntegration({ host, targetDir, force, scope });
110
111
  combined.written.push(...result.written);
111
112
  combined.skipped_existing.push(...result.skipped_existing);
113
+ if (scaffoldHome && result.written.length > 0) {
114
+ await appendScaffoldedHost(scaffoldHome, { host, scope, target_dir: scope === 'local' ? (targetDir || process.cwd()) : undefined });
115
+ }
112
116
  }
113
117
  return combined;
114
118
  }
@@ -181,9 +185,9 @@ async function main() {
181
185
  preferredProvider: provider,
182
186
  preferredLanguage,
183
187
  });
184
- const globalScaffold = await ensureScaffold({ providerMode, scope: 'global', force: hasFlag('--force') });
188
+ const globalScaffold = await ensureScaffold({ providerMode, scope: 'global', force: hasFlag('--force'), home });
185
189
  const scaffold = explicitTargetDir
186
- ? await ensureScaffold({ providerMode, targetDir: explicitTargetDir, scope: 'local', force: hasFlag('--force') })
190
+ ? await ensureScaffold({ providerMode, targetDir: explicitTargetDir, scope: 'local', force: hasFlag('--force'), home })
187
191
  : null;
188
192
  const launch = (await isBranchContextEmpty(home, result.gc_branch))
189
193
  ? await launchGuidedFlow({
@@ -285,7 +289,7 @@ async function main() {
285
289
  const settings = await readSettings(home);
286
290
  const targetDir = readArg('--target') || process.cwd();
287
291
  const scaffold = readArg('--target')
288
- ? await ensureScaffold({ providerMode: settings?.provider_mode || provider, targetDir, scope: 'local', force: hasFlag('--force') })
292
+ ? await ensureScaffold({ providerMode: settings?.provider_mode || provider, targetDir, scope: 'local', force: hasFlag('--force'), home })
289
293
  : null;
290
294
  const launch = await launchGuidedFlow({
291
295
  provider,
@@ -482,7 +486,7 @@ async function main() {
482
486
  const settings = await readSettings(home);
483
487
  const targetDir = readArg('--target') || process.cwd();
484
488
  const scaffold = readArg('--target')
485
- ? await ensureScaffold({ providerMode: settings?.provider_mode || provider, targetDir, scope: 'local', force: hasFlag('--force') })
489
+ ? await ensureScaffold({ providerMode: settings?.provider_mode || provider, targetDir, scope: 'local', force: hasFlag('--force'), home })
486
490
  : null;
487
491
  const launch = await launchGuidedFlow({
488
492
  provider,
@@ -532,10 +536,15 @@ async function main() {
532
536
  providerMode: host,
533
537
  ...(isGlobal ? { scope: 'global' } : { targetDir: explicitTarget, scope: 'local' }),
534
538
  force: hasFlag('--force'),
539
+ home,
535
540
  });
536
541
  console.log(JSON.stringify(result, null, 2));
537
542
  return;
538
543
  }
544
+ case 'update': {
545
+ await selfUpdate(home);
546
+ return;
547
+ }
539
548
  default:
540
549
  usage();
541
550
  }
@@ -8,7 +8,11 @@ function renderCodexAgentsSnippet() {
8
8
  '',
9
9
  '- Treat the active gctree branch as a **gc-branch** when you describe it to users.',
10
10
  '- gctree init installs SessionStart and UserPromptSubmit hooks that auto-check gc-tree before work.',
11
- '- When you encounter an unfamiliar term, acronym, concept, or domain word, run `gctree resolve --query "<term>"` **before** grepping or reading code files. The gc-tree index is the first lookup, not the last resort.',
11
+ '- When you encounter an unfamiliar term, acronym, concept, or domain word, follow this protocol **before** grepping or reading code files:',
12
+ ' 1. Run `gctree resolve --query "<term>"` — if matched, use the result directly.',
13
+ ' 2. If no match, try broader related queries to check if the concept lives in any doc.',
14
+ ' 3. If the concept exists in a doc but was not indexed → propose adding it as an Index Entry to that doc (so future resolves find it).',
15
+ ' 4. If it does not exist anywhere → decide whether a new doc is needed and propose it to the user.',
12
16
  '- When a UserPromptSubmit hook provides `[gc-tree] PRE-TASK CONTEXT`, treat it as mandatory pre-reading before any tool use.',
13
17
  '- If the injected summaries contain enough information to answer the question or complete the task, respond directly from them **without calling any tools**. Do not grep, explore, or read code files to confirm what the summaries already cover.',
14
18
  '- Only use tools if the summaries are clearly insufficient — meaning the answer cannot be derived from them at all.',
@@ -114,7 +118,11 @@ function renderClaudeSnippet() {
114
118
  '',
115
119
  '- Treat the active gctree branch as a **gc-branch** in user-facing language.',
116
120
  '- gctree init installs SessionStart and UserPromptSubmit hooks that auto-inject gc-tree context before every prompt.',
117
- '- When you encounter an unfamiliar term, acronym, concept, or domain word, run `gctree resolve --query "<term>"` **before** grepping or reading code files. The gc-tree index is the first lookup, not the last resort.',
121
+ '- When you encounter an unfamiliar term, acronym, concept, or domain word, follow this protocol **before** grepping or reading code files:',
122
+ ' 1. Run `gctree resolve --query "<term>"` — if matched, use the result directly.',
123
+ ' 2. If no match, try broader related queries to check if the concept lives in any doc.',
124
+ ' 3. If the concept exists in a doc but was not indexed → propose adding it as an Index Entry to that doc (so future resolves find it).',
125
+ ' 4. If it does not exist anywhere → decide whether a new doc is needed and propose it to the user.',
118
126
  '- When a UserPromptSubmit hook provides `[gc-tree] PRE-TASK CONTEXT`, treat it as mandatory pre-reading before any tool use.',
119
127
  '- If the injected summaries contain enough information to answer the question or complete the task, respond directly from them **without calling any tools**. Do not grep, explore, or read code files to confirm what the summaries already cover.',
120
128
  '- Only use tools if the summaries are clearly insufficient — meaning the answer cannot be derived from them at all.',
@@ -21,6 +21,17 @@ export async function writeSettings({ home, providerMode, preferredProvider, pre
21
21
  await writeFile(settingsPath(home), `${JSON.stringify(settings, null, 2)}\n`, 'utf8');
22
22
  return settings;
23
23
  }
24
+ export async function appendScaffoldedHost(home, record) {
25
+ const settings = await readSettings(home);
26
+ if (!settings)
27
+ return;
28
+ const existing = settings.scaffolded_hosts || [];
29
+ const filtered = existing.filter((h) => !(h.host === record.host && h.scope === record.scope && h.target_dir === record.target_dir));
30
+ filtered.push({ ...record, scaffolded_at: new Date().toISOString() });
31
+ const updated = { ...settings, scaffolded_hosts: filtered, updated_at: new Date().toISOString() };
32
+ await mkdir(home, { recursive: true });
33
+ await writeFile(settingsPath(home), `${JSON.stringify(updated, null, 2)}\n`, 'utf8');
34
+ }
24
35
  export async function requirePreferredProvider(home) {
25
36
  const settings = await readSettings(home);
26
37
  if (!settings?.preferred_provider) {
@@ -1,7 +1,8 @@
1
- import { mkdir, writeFile } from 'node:fs/promises';
1
+ import { execSync } from 'node:child_process';
2
+ import { mkdir, readFile, writeFile } from 'node:fs/promises';
2
3
  import { dirname, join } from 'node:path';
3
4
  import { renderDocMarkdown, slugify } from './markdown.js';
4
- import { branchDocsDir, DEFAULT_BRANCH } from './paths.js';
5
+ import { branchDocsDir, DEFAULT_BRANCH, settingsPath } from './paths.js';
5
6
  import { ensureBranchExists, updateBranchMeta, writeIndexFromDocs } from './store.js';
6
7
  function docRelativePath(doc) {
7
8
  if (doc.slug?.includes('/'))
@@ -31,3 +32,35 @@ export async function updateBranchContext({ home, input, branch, }) {
31
32
  index_path: index.index_path,
32
33
  };
33
34
  }
35
+ async function readScaffoldedHosts(home) {
36
+ try {
37
+ const raw = await readFile(settingsPath(home), 'utf8');
38
+ const settings = JSON.parse(raw);
39
+ return settings.scaffolded_hosts || [];
40
+ }
41
+ catch {
42
+ return [];
43
+ }
44
+ }
45
+ export async function selfUpdate(home) {
46
+ const hosts = await readScaffoldedHosts(home);
47
+ if (hosts.length === 0) {
48
+ process.stderr.write('No scaffolded providers found in settings. Run `gctree scaffold --host <host>` first.\n');
49
+ }
50
+ else {
51
+ process.stderr.write(`Found ${hosts.length} scaffolded provider(s): ${hosts.map((h) => `${h.host}(${h.scope})`).join(', ')}\n`);
52
+ }
53
+ process.stderr.write('\nUpdating gctree to latest...\n');
54
+ execSync('npm install -g @handsupmin/gc-tree@latest', { stdio: 'inherit' });
55
+ if (hosts.length > 0) {
56
+ process.stderr.write('\nRe-scaffolding providers with new version...\n');
57
+ for (const record of hosts) {
58
+ const args = ['gctree', 'scaffold', '--host', record.host, '--force'];
59
+ if (record.scope === 'local' && record.target_dir)
60
+ args.push('--target', record.target_dir);
61
+ process.stderr.write(` ${args.join(' ')}\n`);
62
+ execSync(args.join(' '), { stdio: 'inherit' });
63
+ }
64
+ }
65
+ process.stderr.write('\ngctree update complete.\n');
66
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@handsupmin/gc-tree",
3
- "version": "0.7.17",
3
+ "version": "0.7.19",
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,