@handsupmin/gc-tree 0.5.4 → 0.6.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/dist/src/cli.js CHANGED
@@ -1,12 +1,13 @@
1
1
  #!/usr/bin/env node
2
+ import { existsSync } from 'node:fs';
2
3
  import { readFile } from 'node:fs/promises';
3
4
  import { stdin, stderr, stdout } from 'node:process';
4
5
  import { readAsciiLogo, readAsciiTree } from './ascii.js';
5
6
  import { dispatchGcTreeHook } from './hook.js';
6
7
  import { onboardBranch } from './onboard.js';
7
8
  import { buildProviderLaunchPlan, maybeLaunchProvider, promptLanguageSelection, promptLaunchProviderSelection, promptProviderSelection, } from './provider.js';
8
- import { DEFAULT_BRANCH, resolveHome } from './paths.js';
9
- import { branchRepoMapPath, branchScopeStatus, detectCurrentRepoId, promptResolveScopeDecision, readBranchRepoMap, resolveBranchForRepo, setRepoScopeForBranch, } from './repo-map.js';
9
+ import { DEFAULT_BRANCH, branchDir, resolveHome } from './paths.js';
10
+ import { branchRepoMapPath, branchScopeStatus, detectCurrentRepoId, detectRepoRoot, promptResolveScopeDecision, readBranchRepoMap, resolveBranchForRepo, setRepoScopeForBranch, } from './repo-map.js';
10
11
  import { findRelatedDocs, getDocById, resolveContext } from './resolve.js';
11
12
  import { scaffoldHostIntegration } from './scaffold.js';
12
13
  import { requirePreferredProvider, writeSettings, readSettings } from './settings.js';
@@ -109,6 +110,11 @@ async function ensureScaffold({ providerMode, targetDir, force = false, }) {
109
110
  }
110
111
  return combined;
111
112
  }
113
+ async function resolveTargetDir(explicitTarget) {
114
+ if (explicitTarget)
115
+ return explicitTarget;
116
+ return (await detectRepoRoot(process.cwd())) || process.cwd();
117
+ }
112
118
  async function maybePromptProviderMode(explicitProvider) {
113
119
  const provider = normalizeProviderMode(explicitProvider);
114
120
  if (provider)
@@ -244,6 +250,9 @@ async function main() {
244
250
  }
245
251
  case 'status': {
246
252
  const gcBranch = (await readHead(home)) || DEFAULT_BRANCH;
253
+ if (!existsSync(branchDir(home, gcBranch))) {
254
+ throw new Error('gc-tree is not initialized. Run `gctree init` first.');
255
+ }
247
256
  const result = await statusForBranch(home, gcBranch);
248
257
  const settings = await readSettings(home);
249
258
  const mapping = await readBranchRepoMap(home);
@@ -314,7 +323,7 @@ async function main() {
314
323
  throw new Error('uninstall is destructive. Re-run with --yes to confirm.');
315
324
  }
316
325
  const host = normalizeProviderMode(readArg('--host')) || 'both';
317
- const targetDir = readArg('--target') || process.cwd();
326
+ const targetDir = await resolveTargetDir(readArg('--target'));
318
327
  const result = await uninstallGcTree({
319
328
  home,
320
329
  targetDir,
@@ -499,7 +508,7 @@ async function main() {
499
508
  payload,
500
509
  });
501
510
  if (result) {
502
- stdout.write(`${result.hookSpecificOutput.additionalContext}\n`);
511
+ stdout.write(`${JSON.stringify(result, null, 2)}\n`);
503
512
  }
504
513
  return;
505
514
  }
@@ -165,6 +165,6 @@ export function gctreeManagedMarkdownTargets(targetDir) {
165
165
  export function gctreeHookJsonTargets(targetDir) {
166
166
  return {
167
167
  codex: join(targetDir, '.codex', 'hooks.json'),
168
- claude: join(targetDir, '.claude', 'settings.json'),
168
+ claude: join(targetDir, '.claude', 'hooks', 'hooks.json'),
169
169
  };
170
170
  }
@@ -70,7 +70,8 @@ export function renderIndexMarkdown(input) {
70
70
  for (const category of categories) {
71
71
  lines.push(`## ${displayCategory(category)}`, '');
72
72
  for (const doc of grouped.get(category).sort((a, b) => (a.label || a.title).localeCompare(b.label || b.title))) {
73
- lines.push(`- ${doc.label || doc.title} -> ${doc.path}`);
73
+ lines.push(`- ${doc.label || doc.title}`);
74
+ lines.push(` - ${doc.path}`);
74
75
  }
75
76
  lines.push('');
76
77
  }
@@ -107,25 +108,44 @@ export function displayCategory(category) {
107
108
  }
108
109
  export function parseIndexEntries(indexContent) {
109
110
  let currentCategory = 'general';
110
- return String(indexContent || '')
111
- .split(/\r?\n/)
112
- .map((line) => line.trim())
113
- .flatMap((line) => {
114
- if (!line)
115
- return [];
116
- const heading = line.match(/^##\s+(.+)$/);
111
+ let pendingLabel = null;
112
+ const entries = [];
113
+ for (const rawLine of String(indexContent || '').split(/\r?\n/)) {
114
+ const line = rawLine.trimEnd();
115
+ const trimmed = line.trim();
116
+ if (!trimmed) {
117
+ pendingLabel = null;
118
+ continue;
119
+ }
120
+ const heading = trimmed.match(/^##\s+(.+)$/);
117
121
  if (heading) {
118
122
  currentCategory = normalizeCategory(heading[1] || 'general');
119
- return [];
123
+ pendingLabel = null;
124
+ continue;
120
125
  }
121
- if (!/^-\s+.+\s+->\s+.+$/.test(line) || line.startsWith('- gc-branch:') || line.startsWith('- summary:')) {
122
- return [];
126
+ if (trimmed.startsWith('- gc-branch:') || trimmed.startsWith('- summary:') || trimmed === '- No source docs yet.') {
127
+ pendingLabel = null;
128
+ continue;
123
129
  }
124
- const body = line.slice(2);
125
- const [label, path] = body.split('->').map((part) => part.trim());
126
- const category = deriveCategoryFromPath(path) || currentCategory;
127
- return [{ id: docIdFromPath(path), title: label, label, category, path }];
128
- });
130
+ const pathMatch = rawLine.match(/^\s{2,}-\s+(docs\/.+)$/);
131
+ if (pathMatch && pendingLabel) {
132
+ const path = pathMatch[1].trim();
133
+ const category = deriveCategoryFromPath(path) || currentCategory;
134
+ entries.push({
135
+ id: docIdFromPath(path),
136
+ title: pendingLabel,
137
+ label: pendingLabel,
138
+ category,
139
+ path,
140
+ });
141
+ continue;
142
+ }
143
+ const labelMatch = trimmed.match(/^-\s+(.+)$/);
144
+ if (labelMatch) {
145
+ pendingLabel = labelMatch[1].trim();
146
+ }
147
+ }
148
+ return entries;
129
149
  }
130
150
  export function extractIndexEntries(markdown) {
131
151
  const match = String(markdown || '').match(/## Index Entries\s+([\s\S]*?)(?:\n## |$)/);
@@ -18,7 +18,11 @@ export function onboardingProtocolLines() {
18
18
  'Ask next for one core recurring work type only when the provided docs or description still do not make the work types clear, then ask whether there are more work types to capture.',
19
19
  'For each work type, ask how that work shows up day to day.',
20
20
  'Only after the work types are clear should you ask which repositories are involved in each work type.',
21
- 'For each repository, ask what role it plays in the work, what directories or files matter most, what the actual workflow is, and what hidden conventions, glossary terms, or boundaries matter.',
21
+ 'Once the user names concrete repositories, do not ask them to explain those repositories from scratch when recoverable local evidence exists.',
22
+ 'For each repository that can be inspected locally, read the strongest available evidence first (README, docs, package metadata, top-level entrypoints, deployment/config files, and a few pointed paths), then present your current understanding back as a short hypothesis.',
23
+ 'After that repository-level hypothesis, ask the user to choose only one structured confirmation: 1. This is mostly correct. 2. Some parts are wrong. Please explain what differs. 3. Most of this is wrong. Please explain the right frame.',
24
+ 'When local evidence already covers the repository role, important paths, workflow, or conventions well enough, skip the open-ended questions and ask only for missing deltas.',
25
+ 'Only ask open-ended repository questions when the needed detail cannot be recovered responsibly from local evidence.',
22
26
  'After the user\'s first answer, proactively inspect relevant local repos, docs, paths, and workflows whenever the connection is strong enough to test your current frame.',
23
27
  'Ask whether there are additional repositories for the current work type before moving on.',
24
28
  'After repository coverage, ask for company/domain glossary terms and acronyms that should become durable context.',
package/dist/src/paths.js CHANGED
@@ -1,7 +1,6 @@
1
1
  import { homedir } from 'node:os';
2
2
  import { join } from 'node:path';
3
3
  export const DEFAULT_BRANCH = 'main';
4
- export const INDEX_WARNING_CHARS = 2000;
5
4
  export function resolveHome(explicitHome) {
6
5
  return explicitHome || process.env.GCTREE_HOME || join(homedir(), '.gctree');
7
6
  }
@@ -248,7 +248,7 @@ export async function scaffoldHostIntegration({ host, targetDir, force = false,
248
248
  written.push(hookPath);
249
249
  // Migrate: clean up gctree entries from old hooks.json location (claude-code only)
250
250
  if (!isCodex) {
251
- const oldHooksPath = join(targetDir, '.claude', 'hooks', 'hooks.json');
251
+ const oldHooksPath = join(targetDir, '.claude', 'settings.json');
252
252
  await unmergeGcTreeHooksJson(oldHooksPath);
253
253
  }
254
254
  const targets = files.map((file) => ({
package/dist/src/store.js CHANGED
@@ -1,7 +1,7 @@
1
1
  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
- import { branchDir, branchDocsDir, branchIndexPath, branchMetaPath, branchesRoot, DEFAULT_BRANCH, headPath, INDEX_WARNING_CHARS, } from './paths.js';
4
+ import { branchDir, branchDocsDir, branchIndexPath, branchMetaPath, branchesRoot, DEFAULT_BRANCH, headPath, } from './paths.js';
5
5
  import { extractIndexEntries, renderIndexMarkdown } from './markdown.js';
6
6
  async function listDocRelativePaths(dir, prefix = '') {
7
7
  const entries = await readdir(dir, { withFileTypes: true }).catch(() => []);
@@ -150,9 +150,6 @@ export async function statusForBranch(home, branch) {
150
150
  const indexRaw = await readFile(branchIndexPath(home, branch), 'utf8');
151
151
  const docs = await listDocRelativePaths(branchDocsDir(home, branch));
152
152
  const warnings = [];
153
- if (indexRaw.length > INDEX_WARNING_CHARS) {
154
- warnings.push(`index.md is ${indexRaw.length} chars; keep it closer to an index than a knowledge dump.`);
155
- }
156
153
  for (const doc of docs) {
157
154
  const raw = await readFile(join(branchDocsDir(home, branch), doc), 'utf8');
158
155
  if (!/^## Summary$/m.test(raw)) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@handsupmin/gc-tree",
3
- "version": "0.5.4",
3
+ "version": "0.6.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,
@@ -30,6 +30,10 @@ Use this when a user wants to create global context for a product, company, or w
30
30
  - do not start by asking what one repo does
31
31
  - ask who the person is and what work they usually own only after the provided docs or description still leave real gaps
32
32
  - ask for multiple work types if needed, then multiple repos inside each work type if needed
33
+ - once the user names concrete repositories, do not ask them to explain those repositories from scratch when recoverable local evidence exists
34
+ - for each repository that can be inspected locally, read the strongest available evidence first, summarize your current understanding back, and have the user confirm with 1/2/3
35
+ - when the inspected evidence already covers a repository well enough, ask only for missing deltas instead of re-asking role, paths, and workflow from scratch
36
+ - only ask open-ended repository questions when the needed detail cannot be recovered responsibly from local evidence
33
37
  - ask about glossary terms and default verification commands before you finish
34
38
  - write compact source docs with a required `## Summary` section near the top
35
39
  - prefer an encyclopedia-style context set with many small docs instead of a few broad docs
@@ -61,24 +65,27 @@ Use this when a user wants to create global context for a product, company, or w
61
65
  13. Ask for one core recurring work type only when the provided docs or description still do not make the work types clear, then ask whether there are more work types to capture.
62
66
  14. For each work type, ask how it shows up in day-to-day work.
63
67
  15. Only after that ask which repositories are involved in that work type, and ask whether there are more repositories for that same work type.
64
- 16. For each relevant repo, ask:
65
- - what role it plays
66
- - which paths matter most
67
- - what the actual workflow is
68
- - what hidden conventions, glossary terms, boundaries, or constraints matter
69
- 17. After the user's first answer, proactively inspect relevant local repos, docs, paths, and workflows whenever the connection is strong enough to test your current frame.
70
- 18. Ask for company/domain glossary terms and acronyms that should become durable context.
71
- 19. Ask which verification commands should be treated as defaults for this gc-branch.
72
- 20. Structure the durable docs as a small encyclopedia: split by category directory, keep one concept/repo/workflow/convention per file when possible, and keep a short `## Summary` at the top of each doc.
73
- 21. Render `index.md` as a category-grouped dictionary-style table of contents with `label -> path` entries.
74
- 22. Launch the guided onboarding flow with `gctree onboard [--branch <name>]`.
75
- 23. Before you claim onboarding is complete, run `gctree verify-onboarding --branch <current-gc-branch>` and inspect the real gc-tree files.
76
- 24. Do not claim onboarding is complete unless verification returns `status: "complete"`.
77
- 25. After the onboarding docs are written, explicitly list which durable docs were saved.
78
- 26. Summarize what you now understand from the saved docs instead of ending at the filenames alone.
79
- 27. Ask whether that final summary matches the user's reality, and capture any corrections before you wrap up.
80
- 28. Ask whether anything else should be saved while the context is still fresh.
81
- 29. After docs are confirmed correct, ask which repositories discussed during onboarding should be explicitly mapped to this gc-branch. For each confirmed repo, navigate to that directory and run `gctree set-repo-scope --branch <gc-branch> --include`. Skip this step only if the user explicitly says repo mapping is not needed.
82
- 30. Do not finish onboarding while material related repos, workflows, or domain terms remain uninspected when recoverable local evidence is still available.
83
- 31. Only after the related repos, workflows, glossary, default verification commands, and repo-scope mapping are either captured or explicitly skipped should you wrap up, then remind the user that future changes belong in `gctree update-global-context`.
84
- 32. Keep the current gc-branch explicit while gathering context.
68
+ 16. Once the user names concrete repositories, do not ask them to explain those repositories from scratch when recoverable local evidence exists.
69
+ 17. For each repository that can be inspected locally, read the strongest available evidence first, then present a short hypothesis covering repo role, important paths, workflow, and notable conventions.
70
+ 18. After each repository-level hypothesis, ask the user to choose only one:
71
+ - 1. This is mostly correct.
72
+ - 2. Some parts are wrong. Please explain what differs.
73
+ - 3. Most of this is wrong. Please explain the right frame.
74
+ 19. When the inspected evidence already covers the repository well enough, ask only for the missing deltas instead of re-asking role, paths, and workflow from scratch.
75
+ 20. Only ask open-ended repository questions when the needed detail cannot be recovered responsibly from local evidence.
76
+ 21. After the user's first answer, proactively inspect relevant local repos, docs, paths, and workflows whenever the connection is strong enough to test your current frame.
77
+ 22. Ask for company/domain glossary terms and acronyms that should become durable context.
78
+ 23. Ask which verification commands should be treated as defaults for this gc-branch.
79
+ 24. Structure the durable docs as a small encyclopedia: split by category directory, keep one concept/repo/workflow/convention per file when possible, and keep a short `## Summary` at the top of each doc.
80
+ 25. Render `index.md` as a category-grouped dictionary-style table of contents with `label -> path` entries.
81
+ 26. Launch the guided onboarding flow with `gctree onboard [--branch <name>]`.
82
+ 27. Before you claim onboarding is complete, run `gctree verify-onboarding --branch <current-gc-branch>` and inspect the real gc-tree files.
83
+ 28. Do not claim onboarding is complete unless verification returns `status: "complete"`.
84
+ 29. After the onboarding docs are written, explicitly list which durable docs were saved.
85
+ 30. Summarize what you now understand from the saved docs instead of ending at the filenames alone.
86
+ 31. Ask whether that final summary matches the user's reality, and capture any corrections before you wrap up.
87
+ 32. Ask whether anything else should be saved while the context is still fresh.
88
+ 33. After docs are confirmed correct, ask which repositories discussed during onboarding should be explicitly mapped to this gc-branch. For each confirmed repo, navigate to that directory and run `gctree set-repo-scope --branch <gc-branch> --include`. Skip this step only if the user explicitly says repo mapping is not needed.
89
+ 34. Do not finish onboarding while material related repos, workflows, or domain terms remain uninspected when recoverable local evidence is still available.
90
+ 35. Only after the related repos, workflows, glossary, default verification commands, and repo-scope mapping are either captured or explicitly skipped should you wrap up, then remind the user that future changes belong in `gctree update-global-context`.
91
+ 36. Keep the current gc-branch explicit while gathering context.