@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 +13 -4
- package/dist/src/integration-files.js +1 -1
- package/dist/src/markdown.js +36 -16
- package/dist/src/onboarding-protocol.js +5 -1
- package/dist/src/paths.js +0 -1
- package/dist/src/scaffold.js +1 -1
- package/dist/src/store.js +1 -4
- package/package.json +1 -1
- package/skills/onboard/SKILL.md +28 -21
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')
|
|
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
|
|
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', '
|
|
168
|
+
claude: join(targetDir, '.claude', 'hooks', 'hooks.json'),
|
|
169
169
|
};
|
|
170
170
|
}
|
package/dist/src/markdown.js
CHANGED
|
@@ -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}
|
|
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
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
.
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
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
|
-
|
|
123
|
+
pendingLabel = null;
|
|
124
|
+
continue;
|
|
120
125
|
}
|
|
121
|
-
if (
|
|
122
|
-
|
|
126
|
+
if (trimmed.startsWith('- gc-branch:') || trimmed.startsWith('- summary:') || trimmed === '- No source docs yet.') {
|
|
127
|
+
pendingLabel = null;
|
|
128
|
+
continue;
|
|
123
129
|
}
|
|
124
|
-
const
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
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
|
-
'
|
|
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
|
}
|
package/dist/src/scaffold.js
CHANGED
|
@@ -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', '
|
|
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,
|
|
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
package/skills/onboard/SKILL.md
CHANGED
|
@@ -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.
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
-
|
|
68
|
-
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
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.
|