@handsupmin/gc-tree 0.8.17 → 0.8.18

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.
@@ -123,14 +123,21 @@ export function renderIndexMarkdown(input) {
123
123
  }
124
124
  else {
125
125
  const categoryOrder = ['role', 'repos', 'domain', 'workflows', 'conventions', 'infra', 'verification', 'general'];
126
- const grouped = new Map();
126
+ // Group by category, then by path within category
127
+ const byCategory = new Map();
127
128
  for (const doc of input.docs) {
128
129
  const category = normalizeCategory(doc.category || deriveCategoryFromPath(doc.path));
129
- if (!grouped.has(category))
130
- grouped.set(category, []);
131
- grouped.get(category).push(doc);
130
+ if (!byCategory.has(category))
131
+ byCategory.set(category, new Map());
132
+ const byPath = byCategory.get(category);
133
+ if (!byPath.has(doc.path))
134
+ byPath.set(doc.path, []);
135
+ const label = (doc.label || doc.title).trim();
136
+ if (label && !byPath.get(doc.path).includes(label)) {
137
+ byPath.get(doc.path).push(label);
138
+ }
132
139
  }
133
- const categories = [...grouped.keys()].sort((a, b) => {
140
+ const categories = [...byCategory.keys()].sort((a, b) => {
134
141
  const ai = categoryOrder.indexOf(a);
135
142
  const bi = categoryOrder.indexOf(b);
136
143
  if (ai === -1 && bi === -1)
@@ -143,9 +150,12 @@ export function renderIndexMarkdown(input) {
143
150
  });
144
151
  for (const category of categories) {
145
152
  lines.push(`## ${displayCategory(category)}`, '');
146
- for (const doc of grouped.get(category).sort((a, b) => (a.label || a.title).localeCompare(b.label || b.title))) {
147
- lines.push(`- ${doc.label || doc.title}`);
148
- lines.push(` - ${doc.path}`);
153
+ const byPath = byCategory.get(category);
154
+ for (const [path, labels] of [...byPath.entries()].sort(([a], [b]) => a.localeCompare(b))) {
155
+ lines.push(`- ${path}`);
156
+ for (const label of labels) {
157
+ lines.push(` - ${label}`);
158
+ }
149
159
  }
150
160
  lines.push('');
151
161
  }
@@ -182,41 +192,55 @@ export function displayCategory(category) {
182
192
  }
183
193
  export function parseIndexEntries(indexContent) {
184
194
  let currentCategory = 'general';
185
- let pendingLabel = null;
195
+ let pendingLabel = null; // legacy: label before indented path
196
+ let currentPath = null; // new: path before indented labels
186
197
  const entries = [];
187
198
  for (const rawLine of String(indexContent || '').split(/\r?\n/)) {
188
199
  const line = rawLine.trimEnd();
189
200
  const trimmed = line.trim();
190
201
  if (!trimmed) {
191
202
  pendingLabel = null;
203
+ currentPath = null;
192
204
  continue;
193
205
  }
194
206
  const heading = trimmed.match(/^##\s+(.+)$/);
195
207
  if (heading) {
196
208
  currentCategory = normalizeCategory(heading[1] || 'general');
197
209
  pendingLabel = null;
210
+ currentPath = null;
198
211
  continue;
199
212
  }
200
213
  if (trimmed.startsWith('- gc-branch:') || trimmed.startsWith('- summary:') || trimmed === '- No source docs yet.') {
201
214
  pendingLabel = null;
215
+ currentPath = null;
202
216
  continue;
203
217
  }
204
- const pathMatch = rawLine.match(/^\s{2,}-\s+(docs\/.+)$/);
205
- if (pathMatch && pendingLabel) {
206
- const path = pathMatch[1].trim();
207
- const category = deriveCategoryFromPath(path) || currentCategory;
208
- entries.push({
209
- id: docIdFromPath(path),
210
- title: pendingLabel,
211
- label: pendingLabel,
212
- category,
213
- path,
214
- });
218
+ const isIndented = /^\s{2,}/.test(rawLine);
219
+ const itemText = trimmed.replace(/^-\s*/, '');
220
+ if (!isIndented) {
221
+ // Top-level list item
222
+ if (itemText.startsWith('docs/')) {
223
+ // New format: path is top-level
224
+ currentPath = itemText;
225
+ pendingLabel = null;
226
+ }
227
+ else {
228
+ // Legacy format: label is top-level
229
+ pendingLabel = itemText;
230
+ currentPath = null;
231
+ }
215
232
  continue;
216
233
  }
217
- const labelMatch = trimmed.match(/^-\s+(.+)$/);
218
- if (labelMatch) {
219
- pendingLabel = labelMatch[1].trim();
234
+ // Indented item
235
+ if (currentPath && !itemText.startsWith('docs/')) {
236
+ // New format: keyword under path
237
+ const category = deriveCategoryFromPath(currentPath) || currentCategory;
238
+ entries.push({ id: docIdFromPath(currentPath), title: itemText, label: itemText, category, path: currentPath });
239
+ }
240
+ else if (pendingLabel && itemText.startsWith('docs/')) {
241
+ // Legacy format: path under label
242
+ const category = deriveCategoryFromPath(itemText) || currentCategory;
243
+ entries.push({ id: docIdFromPath(itemText), title: pendingLabel, label: pendingLabel, category, path: itemText });
220
244
  }
221
245
  }
222
246
  return entries;
@@ -33,11 +33,10 @@ export function onboardingProtocolLines() {
33
33
  'Synthesize the interview into an encyclopedia-style context set with many small docs instead of a few broad docs.',
34
34
  'Prefer category directories such as `docs/role/`, `docs/repos/`, `docs/domain/`, `docs/workflows/`, `docs/conventions/`, and `docs/infra/` whenever that split fits the material.',
35
35
  'Prefer one concept, one repository, one workflow, or one convention per file when possible.',
36
- 'Treat `index.md` as concept-first: show the keywords a user or AI would search for, not just broad document titles.',
37
- 'Generate index entries automatically from primary concept names, aliases, repository nicknames, and workflow labels when those are clear.',
36
+ 'Index entries are the search surface — maximize keyword density. For each doc, generate index entries from: primary concept names, aliases, repo nicknames, workflow names, field names, command names, acronyms, related terms a developer might search for, and any term that would make someone want to open this doc. Do NOT be conservative — more keywords means more chances of being found.',
37
+ 'The index format groups keywords under their path: top-level `- docs/path.md` with indented ` - keyword` lines under it. This keeps the index compact while allowing many keywords per doc.',
38
38
  'Split glossary docs when a concept is likely to be searched directly, needs more than a short definition, or carries workflow/constraint details; keep only low-value leftover terms in a shared glossary.',
39
39
  'The `## Summary` section of every doc must be actionable, not descriptive — write the actual patterns, commands, or constraints a developer needs, not a sentence about what the doc covers. Bad: "이 문서는 updateCollection 패턴을 설명합니다." Good: "updateCollection: { ...dto } spread 필수. return plainToInstance(Res, result satisfies Res). 새 필드 추가 = DTO → 서비스 → 컨트롤러 순서." The summary is injected into the AI context before every task — if it reads like a table of contents entry, it is useless.',
40
- 'Treat `index.md` as a human-readable dictionary-style table of contents grouped by category headings and `label -> path` entries.',
41
40
  ];
42
41
  }
43
42
  export function onboardingCompletionLines() {
@@ -45,9 +44,12 @@ export function onboardingCompletionLines() {
45
44
  'Before you claim onboarding is complete, run `gctree verify-onboarding --branch <current-gc-branch>` and inspect the real gc-tree files.',
46
45
  'Do not claim onboarding is complete unless verification returns `status: "complete"` **and** `quality_issues` is an empty array.',
47
46
  'If `quality_issues` is non-empty, do **not** tell the user onboarding is done. Self-heal immediately without asking the user: (a) identify which docs have `category: "general"`, (b) assign each a correct category from `role | repos | domain | workflows | conventions | infra | verification` based on content, (c) rebuild the full onboarding JSON with every doc having an explicit `category` field set to one of those values, (d) run `gctree __apply-onboarding --input <temp-file>` again, (e) rerun `gctree verify-onboarding` and repeat until `quality_issues` is empty. Never use `"general"` as a category in the JSON — it is a fallback for missing data, not a valid category.',
48
- 'After quality_issues passes, perform an index coverage check: for every written doc, verify that its key concepts (repo names, workflow names, domain terms, convention patterns) appear as searchable index labels in the verify output. If any important concept is missing from the index, add it as an index entry and re-apply. Do not skip this step a doc that exists but cannot be found by search is useless.',
47
+ 'After quality_issues passes, perform a summary quality check: read the actual `## Summary` section of each written doc file. If any summary reads like a table of contents entry ("이 문서는 X를 설명합니다", "X에 대한 패턴 모음") rather than actionable content (real patterns, commands, constraints), rewrite it in-place before continuing.',
48
+ 'After summary check, perform an index coverage check: for every written doc, read its `## Index Entries` section and verify that the entries cover a wide range of terms a developer would search for — not just the exact document title. If a doc has fewer than 5 index entries, it almost certainly needs more. Add aliases, related terms, command names, field names, and workflow names. Re-apply if entries were added.',
49
49
  'If verification returns `status: "incomplete"` for reasons other than quality_issues, 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.',
50
50
  'After applying the onboarding docs, explicitly list which durable docs were saved.',
51
+ 'Before the final summary, run a coverage checklist and ask the user to confirm each area: (a) workflows — are the key cross-repo action sequences documented? (b) conventions — are the code patterns for each repo documented? (c) repos — is each relevant repo covered with role, paths, and cross-repo deps? (d) domain terms / glossary — are important acronyms and terms indexed? Present this as a numbered checklist and ask the user: "이 중 빠진 것이 있으면 말씀해주세요. 없으면 완료하겠습니다." Do not skip this step.',
52
+ 'If the user identifies missing areas, gather the information and add the docs before continuing.',
51
53
  'Then summarize what you now understand from the saved docs instead of stopping at the filenames alone.',
52
54
  '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.',
53
55
  '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.',
@@ -116,14 +116,15 @@ function renderUpdateProtocol() {
116
116
  ' - `docs/infra/<topic>.md` — infra, deployment, environment config',
117
117
  ' - `docs/role/<name>.md` — team member roles, responsibilities',
118
118
  '4. Write a `## Summary` that is actionable: actual patterns/commands/constraints a developer needs — not a sentence about what the doc covers.',
119
- '5. Create a temporary JSON file with the updated `docs[]` and run `gctree __apply-update --input <temp-file>`.',
120
- '6. Run `gctree verify-onboarding --branch <gc-branch>` and inspect the output:',
119
+ '5. Include a `## Index Entries` section in each doc\'s content with many keywords: aliases, related terms, command names, field names, workflow names, acronyms. More entries = more chances of being found by `gctree resolve`. Fewer than 5 entries almost always means more are needed.',
120
+ '6. Create a temporary JSON file with the updated `docs[]` and run `gctree __apply-update --input <temp-file>`.',
121
+ '7. Run `gctree verify-onboarding --branch <gc-branch>` and inspect the output:',
121
122
  ' - Check that every updated doc appears in the verified doc list.',
122
123
  ' - Check that index entries for the new content exist and are searchable (not just generic titles).',
123
124
  ' - Check that no doc has `category: "general"` — reassign to correct category if so.',
124
125
  ' - Check that important concepts from the update are present as index labels — if missing, re-apply with added index entries.',
125
126
  ' - Self-heal any issues without asking the user: fix category, add index entries, re-run `gctree __apply-update`, re-verify.',
126
- '7. Show the user which docs were updated and confirm the index now covers the new content.',
127
+ '8. Show the user which docs were updated and confirm the index now covers the new content.',
127
128
  ];
128
129
  }
129
130
  function renderCodexUpdateSkill() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@handsupmin/gc-tree",
3
- "version": "0.8.17",
3
+ "version": "0.8.18",
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,
@@ -43,10 +43,10 @@ Use this when a user wants to create global context for a product, company, or w
43
43
  - prefer an encyclopedia-style context set with many small docs instead of a few broad docs
44
44
  - prefer category directories like `docs/role/`, `docs/repos/`, `docs/domain/`, `docs/workflows/`, `docs/conventions/`, and `docs/infra/`
45
45
  - prefer one concept, one repository, one workflow, or one convention per file when possible
46
- - treat `index.md` as concept-first: surface the keywords a user or AI would search for, not just broad document titles
46
+ - treat `index.md` as the search surface maximize keyword density; for each doc generate entries from: primary names, aliases, nicknames, field names, command names, acronyms, related terms a developer might search for; do NOT be conservative — more keywords = more chances of being found; fewer than 5 index entries per doc almost certainly means more are needed
47
47
  - generate index entries automatically from primary concept names, aliases, repository nicknames, and workflow labels when those are clear
48
48
  - split glossary docs when a concept is likely to be searched directly, needs more than a short definition, or carries workflow/constraint details; keep only low-value leftover terms in a shared glossary
49
- - keep `index.md` as a human-readable dictionary-style TOC grouped by category headings and `label -> path` entries
49
+ - index format: top-level `- docs/path.md` with indented ` - keyword` lines under it groups all keywords per doc, eliminates path duplication
50
50
  - every doc in the JSON **must** have an explicit `category` field set to one of: `role`, `repos`, `domain`, `workflows`, `conventions`, `infra`, `verification` — never use `"general"` as a category; it is a fallback for missing data, not a valid category choice
51
51
  - use onboarding only for an empty gc-branch
52
52
 
@@ -88,12 +88,14 @@ Use this when a user wants to create global context for a product, company, or w
88
88
  22. Ask for company/domain glossary terms and acronyms that should become durable context.
89
89
  23. Ask which verification commands should be treated as defaults for this gc-branch.
90
90
  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.
91
- 25. Render `index.md` as a category-grouped dictionary-style table of contents with `label -> path` entries.
91
+ 25. Render `index.md` grouped by category; within each category list each doc path as a top-level item with all its keywords as indented children. Maximize keywords per doc — aliases, related terms, command names, field names, workflow names.
92
92
  26. Launch the guided onboarding flow with `gctree onboard [--branch <name>]`.
93
93
  27. Before you claim onboarding is complete, run `gctree verify-onboarding --branch <current-gc-branch>` and inspect the real gc-tree files.
94
94
  28. Do not claim onboarding is complete unless verification returns `status: "complete"` **and** `quality_issues` is an empty array.
95
95
  29. If `quality_issues` is non-empty, do **not** tell the user onboarding is done. Self-heal immediately without prompting the user: (a) identify which docs have `category: "general"`, (b) assign each a correct category from `role | repos | domain | workflows | conventions | infra | verification` based on content, (c) rebuild the full onboarding JSON with every doc having an explicit `category` field — never use `"general"` as a category, (d) rerun `gctree __apply-onboarding --input <temp-file>`, (e) rerun `gctree verify-onboarding`, repeat until `quality_issues` is empty. If `status` is `"incomplete"` for other reasons, inspect the failures, heal them autonomously, and repeat.
96
96
  30. After the onboarding docs are written, explicitly list which durable docs were saved.
97
+ 30a. Run a coverage checklist before the final summary. Ask the user to confirm each area: (a) workflows — are key cross-repo action sequences documented? (b) conventions — are code patterns for each repo documented with a `## Patterns` section? (c) repos — is each relevant repo covered with role, paths, and cross-repo deps? (d) domain/glossary — are important acronyms and terms indexed with enough keywords? Present as a numbered checklist: "이 중 빠진 것이 있으면 말씀해주세요. 없으면 완료하겠습니다." If the user identifies gaps, gather and add before continuing. Do not skip this step.
98
+ 30b. Check index keyword density: read the `## Index Entries` section of each written doc. If any doc has fewer than 5 entries, add more — aliases, related terms, command names, field names, workflow names. Re-apply if entries were added.
97
99
  31. Summarize what you now understand from the saved docs instead of ending at the filenames alone.
98
100
  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:
99
101
  - 1. This matches well enough.