@handsupmin/gc-tree 0.7.1 → 0.7.3
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 +9 -4
- package/dist/src/markdown.js +67 -4
- package/dist/src/onboard.js +3 -0
- package/dist/src/onboarding-protocol.js +3 -1
- package/dist/src/store.js +5 -4
- package/package.json +1 -1
- package/skills/onboard/SKILL.md +12 -10
package/dist/src/cli.js
CHANGED
|
@@ -139,6 +139,11 @@ async function launchGuidedFlow({ provider, providerMode, preferredLanguage, tar
|
|
|
139
139
|
return plan;
|
|
140
140
|
return maybeLaunchProvider(plan);
|
|
141
141
|
}
|
|
142
|
+
function printJsonUnlessLaunched(payload, launch) {
|
|
143
|
+
if (launch?.launched)
|
|
144
|
+
return;
|
|
145
|
+
console.log(JSON.stringify(payload, null, 2));
|
|
146
|
+
}
|
|
142
147
|
async function main() {
|
|
143
148
|
if (hasFlag('--version') || hasFlag('-v')) {
|
|
144
149
|
stdout.write(`${await readPackageVersion()}\n`);
|
|
@@ -189,7 +194,7 @@ async function main() {
|
|
|
189
194
|
noLaunch: hasFlag('--no-launch'),
|
|
190
195
|
})
|
|
191
196
|
: null;
|
|
192
|
-
|
|
197
|
+
printJsonUnlessLaunched({
|
|
193
198
|
...result,
|
|
194
199
|
provider_mode: settings.provider_mode,
|
|
195
200
|
preferred_provider: settings.preferred_provider,
|
|
@@ -197,7 +202,7 @@ async function main() {
|
|
|
197
202
|
global_scaffold: globalScaffold,
|
|
198
203
|
scaffold,
|
|
199
204
|
launch,
|
|
200
|
-
},
|
|
205
|
+
}, launch);
|
|
201
206
|
return;
|
|
202
207
|
}
|
|
203
208
|
case 'checkout': {
|
|
@@ -289,7 +294,7 @@ async function main() {
|
|
|
289
294
|
command: 'gc-onboard',
|
|
290
295
|
noLaunch: hasFlag('--no-launch'),
|
|
291
296
|
});
|
|
292
|
-
|
|
297
|
+
printJsonUnlessLaunched({ mode: 'guided_onboarding', gc_branch: gcBranch, preferred_provider: provider, scaffold, launch }, launch);
|
|
293
298
|
return;
|
|
294
299
|
}
|
|
295
300
|
case 'verify-onboarding': {
|
|
@@ -486,7 +491,7 @@ async function main() {
|
|
|
486
491
|
command: 'gc-update-global-context',
|
|
487
492
|
noLaunch: hasFlag('--no-launch'),
|
|
488
493
|
});
|
|
489
|
-
|
|
494
|
+
printJsonUnlessLaunched({ mode: 'guided_update', gc_branch: gcBranch, preferred_provider: provider, scaffold, launch }, launch);
|
|
490
495
|
return;
|
|
491
496
|
}
|
|
492
497
|
case '__apply-update': {
|
package/dist/src/markdown.js
CHANGED
|
@@ -30,6 +30,40 @@ function parseDocReference(value) {
|
|
|
30
30
|
}
|
|
31
31
|
return null;
|
|
32
32
|
}
|
|
33
|
+
function parseTitlePlacement(value) {
|
|
34
|
+
const trimmed = String(value || '').trim();
|
|
35
|
+
if (!trimmed)
|
|
36
|
+
return null;
|
|
37
|
+
if (/^index$/i.test(trimmed))
|
|
38
|
+
return { category: 'index', slug: 'index', label: 'Index' };
|
|
39
|
+
const match = trimmed.match(/^(role|repo|repos|repository|repositories|domain|workflow|workflows|convention|conventions|infra|verification)\s*:\s*(.+)$/i);
|
|
40
|
+
if (!match)
|
|
41
|
+
return null;
|
|
42
|
+
const rawCategory = match[1].toLowerCase();
|
|
43
|
+
const categoryMap = {
|
|
44
|
+
role: 'role',
|
|
45
|
+
repo: 'repos',
|
|
46
|
+
repos: 'repos',
|
|
47
|
+
repository: 'repos',
|
|
48
|
+
repositories: 'repos',
|
|
49
|
+
domain: 'domain',
|
|
50
|
+
workflow: 'workflows',
|
|
51
|
+
workflows: 'workflows',
|
|
52
|
+
convention: 'conventions',
|
|
53
|
+
conventions: 'conventions',
|
|
54
|
+
infra: 'infra',
|
|
55
|
+
verification: 'verification',
|
|
56
|
+
};
|
|
57
|
+
const label = match[2].trim();
|
|
58
|
+
const category = categoryMap[rawCategory];
|
|
59
|
+
if (!category || !label)
|
|
60
|
+
return null;
|
|
61
|
+
return {
|
|
62
|
+
category,
|
|
63
|
+
slug: slugify(label),
|
|
64
|
+
label,
|
|
65
|
+
};
|
|
66
|
+
}
|
|
33
67
|
export function ensureSummary(summary) {
|
|
34
68
|
const trimmed = String(summary || '').trim();
|
|
35
69
|
if (!trimmed) {
|
|
@@ -40,6 +74,10 @@ export function ensureSummary(summary) {
|
|
|
40
74
|
export function renderDocMarkdown(doc) {
|
|
41
75
|
const summary = ensureSummary(doc.summary);
|
|
42
76
|
const body = String(doc.body || '').trim();
|
|
77
|
+
const normalizedIndexEntries = [...new Set([
|
|
78
|
+
...(doc.indexLabel?.trim() ? [doc.indexLabel.trim()] : []),
|
|
79
|
+
...(doc.indexEntries || []).map((entry) => String(entry || '').trim()).filter(Boolean),
|
|
80
|
+
])];
|
|
43
81
|
return [
|
|
44
82
|
`# ${doc.title.trim()}`,
|
|
45
83
|
'',
|
|
@@ -50,8 +88,8 @@ export function renderDocMarkdown(doc) {
|
|
|
50
88
|
...(doc.tags && doc.tags.length > 0
|
|
51
89
|
? ['## Tags', '', ...doc.tags.map((tag) => `- ${tag}`), '']
|
|
52
90
|
: []),
|
|
53
|
-
...(
|
|
54
|
-
? ['## Index Entries', '', ...
|
|
91
|
+
...(normalizedIndexEntries.length > 0
|
|
92
|
+
? ['## Index Entries', '', ...normalizedIndexEntries.map((entry) => `- ${entry}`), '']
|
|
55
93
|
: []),
|
|
56
94
|
'## Details',
|
|
57
95
|
'',
|
|
@@ -194,6 +232,15 @@ export function normalizeIndexEntry(value, fallback) {
|
|
|
194
232
|
return null;
|
|
195
233
|
if (/^docs\/index\.md$/i.test(trimmed) || /^index\.md$/i.test(trimmed))
|
|
196
234
|
return null;
|
|
235
|
+
const titlePlacement = parseTitlePlacement(trimmed);
|
|
236
|
+
if (titlePlacement) {
|
|
237
|
+
if (titlePlacement.category === 'index')
|
|
238
|
+
return null;
|
|
239
|
+
return {
|
|
240
|
+
category: titlePlacement.category,
|
|
241
|
+
label: titlePlacement.label,
|
|
242
|
+
};
|
|
243
|
+
}
|
|
197
244
|
return {
|
|
198
245
|
category: fallback.category,
|
|
199
246
|
label: trimmed,
|
|
@@ -203,12 +250,28 @@ export function inferDocPlacement(input) {
|
|
|
203
250
|
for (const candidate of [input.slug, input.indexLabel]) {
|
|
204
251
|
const reference = parseDocReference(candidate || '');
|
|
205
252
|
if (reference) {
|
|
206
|
-
return { category: reference.category, slug: reference.slug };
|
|
253
|
+
return { category: reference.category, slug: reference.slug, label: reference.label, isIndexDoc: false };
|
|
207
254
|
}
|
|
208
255
|
}
|
|
256
|
+
const titlePlacement = parseTitlePlacement(input.title);
|
|
257
|
+
const explicitLabel = String(input.indexLabel || '').trim();
|
|
258
|
+
const preferredLabel = explicitLabel && !parseDocReference(explicitLabel) ? explicitLabel : input.title.trim();
|
|
259
|
+
if (titlePlacement) {
|
|
260
|
+
return {
|
|
261
|
+
category: titlePlacement.category === 'index' ? null : titlePlacement.category,
|
|
262
|
+
slug: titlePlacement.slug,
|
|
263
|
+
label: explicitLabel && !parseDocReference(explicitLabel) ? explicitLabel : titlePlacement.label,
|
|
264
|
+
isIndexDoc: titlePlacement.category === 'index',
|
|
265
|
+
};
|
|
266
|
+
}
|
|
209
267
|
const explicitCategory = input.category ? slugify(input.category) : null;
|
|
210
268
|
const slug = slugify(input.slug || input.indexLabel || input.title);
|
|
211
|
-
return {
|
|
269
|
+
return {
|
|
270
|
+
category: explicitCategory,
|
|
271
|
+
slug,
|
|
272
|
+
label: preferredLabel,
|
|
273
|
+
isIndexDoc: slug === 'index' && explicitCategory === null,
|
|
274
|
+
};
|
|
212
275
|
}
|
|
213
276
|
export function extractSummary(markdown) {
|
|
214
277
|
const match = String(markdown || '').match(/## Summary\s+([\s\S]*?)(?:\n## |$)/);
|
package/dist/src/onboard.js
CHANGED
|
@@ -17,6 +17,9 @@ export async function onboardBranch({ home, input, branch, }) {
|
|
|
17
17
|
await mkdir(branchDocsDir(home, targetBranch), { recursive: true });
|
|
18
18
|
const written = [];
|
|
19
19
|
for (const doc of input.docs) {
|
|
20
|
+
const inferred = inferDocPlacement(doc);
|
|
21
|
+
if (inferred.isIndexDoc)
|
|
22
|
+
continue;
|
|
20
23
|
const fullPath = join(branchDocsDir(home, targetBranch), docRelativePath(doc));
|
|
21
24
|
await mkdir(dirname(fullPath), { recursive: true });
|
|
22
25
|
await writeFile(fullPath, renderDocMarkdown(doc), 'utf8');
|
|
@@ -41,6 +41,7 @@ export function onboardingCompletionLines() {
|
|
|
41
41
|
return [
|
|
42
42
|
'Before you claim onboarding is complete, run `gctree verify-onboarding --branch <current-gc-branch>` and inspect the real gc-tree files.',
|
|
43
43
|
'Do not claim onboarding is complete unless verification returns `status: "complete"`.',
|
|
44
|
+
'If verification returns anything other than `status: "complete"`, do not tell the user onboarding is done; inspect the reported failures, heal what can be healed automatically, rerun verification, and repeat until it passes or a real blocker remains.',
|
|
44
45
|
'After applying the onboarding docs, explicitly list which durable docs were saved.',
|
|
45
46
|
'Then summarize what you now understand from the saved docs instead of stopping at the filenames alone.',
|
|
46
47
|
'For that final summary, do not ask an open-ended validation question first; present the summary and ask the user to choose only one structured confirmation: 1. This matches well enough. 2. Some parts are wrong. I will give the delta. 3. The frame is wrong. I will restate it.',
|
|
@@ -49,6 +50,7 @@ export function onboardingCompletionLines() {
|
|
|
49
50
|
'After docs are confirmed correct, do not ask the user to recall repo-scope mappings from scratch; propose the concrete repository candidates that appear materially tied to this gc-branch, then ask the user to choose only one structured confirmation: 1. Map these candidates. 2. Map these, but with corrections. 3. Skip repo mapping for now.',
|
|
50
51
|
'If the user picks 2 for repo mapping, ask only for the repo delta to add or remove. If the user picks 1 or gives corrected candidates, navigate to each confirmed repository and run `gctree set-repo-scope --branch <current-gc-branch> --include`. Skip mapping only if the user picks 3 or explicitly says mapping is not needed.',
|
|
51
52
|
'Do not finish onboarding while material related repos, workflows, or domain terms remain uninspected when recoverable local evidence is still available.',
|
|
52
|
-
'Only after the related repos, workflows, glossary, default verification commands,
|
|
53
|
+
'Only after the related repos, workflows, glossary, default verification commands, repo-scope mapping, and verification gate are all complete should you wrap up.',
|
|
54
|
+
'When you do wrap up, explicitly tell the user three things in plain language: onboarding is finished; future durable changes can be made with `gctree update-global-context`, or directly through the provider command surface as Codex `$gc-update-global-context {prompt}` and Claude Code `/gc-update-global-context {prompt}`; and they can close this session and start fresh in a new one.',
|
|
53
55
|
];
|
|
54
56
|
}
|
package/dist/src/store.js
CHANGED
|
@@ -109,18 +109,19 @@ export async function writeIndexFromDocs(home, branch) {
|
|
|
109
109
|
const docsDir = branchDocsDir(home, branch);
|
|
110
110
|
await mkdir(docsDir, { recursive: true });
|
|
111
111
|
const files = await listDocRelativePaths(docsDir);
|
|
112
|
-
const docs = await Promise.all(files.map(async (file) => {
|
|
112
|
+
const docs = await Promise.all(files.filter((file) => !/(^|\/)index\.md$/i.test(file)).map(async (file) => {
|
|
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
116
|
const fallbackCategory = parts.length > 1 ? parts[0] : 'general';
|
|
117
117
|
const fallbackLabel = parts.length > 1 ? parts[parts.length - 1] : title;
|
|
118
|
-
const inferred = inferDocPlacement({ title
|
|
118
|
+
const inferred = inferDocPlacement({ title });
|
|
119
119
|
const docCategory = inferred.category || fallbackCategory;
|
|
120
|
+
const docLabel = inferred.label || fallbackLabel;
|
|
120
121
|
const indexEntries = extractIndexEntries(raw)
|
|
121
|
-
.map((entry) => normalizeIndexEntry(entry, { category: docCategory, label:
|
|
122
|
+
.map((entry) => normalizeIndexEntry(entry, { category: docCategory, label: docLabel }))
|
|
122
123
|
.filter((entry) => Boolean(entry));
|
|
123
|
-
const entryLabels = indexEntries.length > 0 ? indexEntries : [{ category: docCategory, label:
|
|
124
|
+
const entryLabels = indexEntries.length > 0 ? indexEntries : [{ category: docCategory, label: docLabel }];
|
|
124
125
|
const uniqueEntries = [...new Map(entryLabels.map((entry) => [`${entry.category}::${entry.label}`, entry])).values()];
|
|
125
126
|
return uniqueEntries.map((entry) => ({
|
|
126
127
|
title,
|
package/package.json
CHANGED
package/skills/onboard/SKILL.md
CHANGED
|
@@ -81,19 +81,21 @@ Use this when a user wants to create global context for a product, company, or w
|
|
|
81
81
|
26. Launch the guided onboarding flow with `gctree onboard [--branch <name>]`.
|
|
82
82
|
27. Before you claim onboarding is complete, run `gctree verify-onboarding --branch <current-gc-branch>` and inspect the real gc-tree files.
|
|
83
83
|
28. Do not claim onboarding is complete unless verification returns `status: "complete"`.
|
|
84
|
-
29.
|
|
85
|
-
30.
|
|
86
|
-
31.
|
|
84
|
+
29. If verification returns anything other than `status: "complete"`, do not tell the user onboarding is done. Inspect the reported failures, heal what can be healed automatically, rerun verification, and repeat until it passes or a real blocker remains.
|
|
85
|
+
30. After the onboarding docs are written, explicitly list which durable docs were saved.
|
|
86
|
+
31. Summarize what you now understand from the saved docs instead of ending at the filenames alone.
|
|
87
|
+
32. For that final summary, do not ask an open-ended validation question first. Present the summary and ask the user to choose only one:
|
|
87
88
|
- 1. This matches well enough.
|
|
88
89
|
- 2. Some parts are wrong. I will give the delta.
|
|
89
90
|
- 3. The frame is wrong. I will restate it.
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
91
|
+
33. If the user picks 2 or 3 for the final summary, ask only for the correction delta or replacement frame, then update the saved understanding instead of restarting the interview.
|
|
92
|
+
34. Ask whether anything else should be saved while the context is still fresh.
|
|
93
|
+
35. After docs are confirmed correct, do not ask the user to recall repo-scope mappings from scratch. Propose the concrete repository candidates that appear materially tied to this gc-branch, then ask the user to choose only one:
|
|
93
94
|
- 1. Map these candidates.
|
|
94
95
|
- 2. Map these, but with corrections.
|
|
95
96
|
- 3. Skip repo mapping for now.
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
97
|
+
36. If the user picks 2 for repo mapping, ask only for the repo delta to add or remove. If the user picks 1 or gives corrected candidates, navigate to each confirmed repo and run `gctree set-repo-scope --branch <gc-branch> --include`. Skip mapping only if the user picks 3 or explicitly says mapping is not needed.
|
|
98
|
+
37. Do not finish onboarding while material related repos, workflows, or domain terms remain uninspected when recoverable local evidence is still available.
|
|
99
|
+
38. Only after the related repos, workflows, glossary, default verification commands, repo-scope mapping, and verification gate are all complete should you wrap up.
|
|
100
|
+
39. When you wrap up, explicitly tell the user three things in plain language: onboarding is finished; future durable changes can be made with `gctree update-global-context`, or directly through the provider command surface as Codex `$gc-update-global-context {prompt}` and Claude Code `/gc-update-global-context {prompt}`; and they can close this session and start fresh in a new one.
|
|
101
|
+
40. Keep the current gc-branch explicit while gathering context.
|