@handsupmin/gc-tree 0.4.5 → 0.5.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/hook.js CHANGED
@@ -56,7 +56,7 @@ function buildMatchContext({ gcBranch, currentRepo, repoScopeStatus, query, matc
56
56
  `Query: "${query}".`,
57
57
  `Use these matching gc-tree summaries before planning or implementation:`,
58
58
  formatMatches(matches),
59
- `Read full docs only if the summaries are insufficient.`,
59
+ `Read full docs only if the summaries are insufficient. Each doc has a ## Summary section at the top — read that before the full body.`,
60
60
  ].join('\n');
61
61
  }
62
62
  async function readHookCache(home, sessionId) {
@@ -27,6 +27,9 @@ export function renderDocMarkdown(doc) {
27
27
  ...(doc.tags && doc.tags.length > 0
28
28
  ? ['## Tags', '', ...doc.tags.map((tag) => `- ${tag}`), '']
29
29
  : []),
30
+ ...(doc.indexEntries && doc.indexEntries.length > 0
31
+ ? ['## Index Entries', '', ...doc.indexEntries.map((entry) => `- ${entry}`), '']
32
+ : []),
30
33
  '## Details',
31
34
  '',
32
35
  body || '(no details yet)',
@@ -124,6 +127,17 @@ export function parseIndexEntries(indexContent) {
124
127
  return [{ id: docIdFromPath(path), title: label, label, category, path }];
125
128
  });
126
129
  }
130
+ export function extractIndexEntries(markdown) {
131
+ const match = String(markdown || '').match(/## Index Entries\s+([\s\S]*?)(?:\n## |$)/);
132
+ if (!match?.[1])
133
+ return [];
134
+ return match[1]
135
+ .split(/\r?\n/)
136
+ .map((line) => line.trim())
137
+ .filter((line) => line.startsWith('- '))
138
+ .map((line) => line.slice(2).trim())
139
+ .filter(Boolean);
140
+ }
127
141
  export function extractSummary(markdown) {
128
142
  const match = String(markdown || '').match(/## Summary\s+([\s\S]*?)(?:\n## |$)/);
129
143
  return match?.[1]?.trim() || '';
@@ -26,6 +26,9 @@ export function onboardingProtocolLines() {
26
26
  'Synthesize the interview into an encyclopedia-style context set with many small docs instead of a few broad docs.',
27
27
  'Prefer category directories such as `docs/role/`, `docs/repos/`, `docs/domain/`, `docs/workflows/`, `docs/conventions/`, and `docs/infra/` whenever that split fits the material.',
28
28
  'Prefer one concept, one repository, one workflow, or one convention per file when possible.',
29
+ 'Treat `index.md` as concept-first: show the keywords a user or AI would search for, not just broad document titles.',
30
+ 'Generate index entries automatically from primary concept names, aliases, repository nicknames, and workflow labels when those are clear.',
31
+ '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.',
29
32
  'Keep each doc summary-first so the top section gives the gist before deeper details.',
30
33
  'Treat `index.md` as a human-readable dictionary-style table of contents grouped by category headings and `label -> path` entries.',
31
34
  ];
@@ -44,7 +44,8 @@ function scoreText(text, query) {
44
44
  async function readBranchDocs(home, branch) {
45
45
  const indexRaw = await readFile(branchIndexPath(home, branch), 'utf8');
46
46
  const entries = parseIndexEntries(indexRaw);
47
- return Promise.all(entries.map(async (entry) => {
47
+ const uniqueEntries = [...new Map(entries.map((entry) => [entry.path, entry])).values()];
48
+ return Promise.all(uniqueEntries.map(async (entry) => {
48
49
  const fullPath = join(branchDir(home, branch), entry.path);
49
50
  const raw = await readFile(fullPath, 'utf8');
50
51
  return {
package/dist/src/store.js CHANGED
@@ -2,7 +2,7 @@ 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
4
  import { branchDir, branchDocsDir, branchIndexPath, branchMetaPath, branchesRoot, DEFAULT_BRANCH, headPath, INDEX_WARNING_CHARS, } from './paths.js';
5
- import { renderIndexMarkdown } from './markdown.js';
5
+ import { extractIndexEntries, renderIndexMarkdown } from './markdown.js';
6
6
  async function listDocRelativePaths(dir, prefix = '') {
7
7
  const entries = await readdir(dir, { withFileTypes: true }).catch(() => []);
8
8
  const files = [];
@@ -115,10 +115,18 @@ export async function writeIndexFromDocs(home, branch) {
115
115
  const parts = file.replace(/\.md$/i, '').split('/').filter(Boolean);
116
116
  const category = parts.length > 1 ? parts[0] : 'general';
117
117
  const label = parts.length > 1 ? parts[parts.length - 1] : title;
118
- return { title, label, category, path: `docs/${file}` };
118
+ const indexEntries = extractIndexEntries(raw);
119
+ const entryLabels = indexEntries.length > 0 ? indexEntries : [label];
120
+ return entryLabels.map((entryLabel) => ({
121
+ title,
122
+ label: entryLabel,
123
+ category,
124
+ path: `docs/${file}`,
125
+ }));
119
126
  }));
127
+ const flatDocs = docs.flat().sort((a, b) => a.label.localeCompare(b.label));
120
128
  const meta = await readBranchMeta(home, branch);
121
- const index = renderIndexMarkdown({ branch, branchSummary: meta.summary, docs });
129
+ const index = renderIndexMarkdown({ branch, branchSummary: meta.summary, docs: flatDocs });
122
130
  await writeFile(branchIndexPath(home, branch), index, 'utf8');
123
131
  return { index_path: branchIndexPath(home, branch), doc_count: docs.length };
124
132
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@handsupmin/gc-tree",
3
- "version": "0.4.5",
3
+ "version": "0.5.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,
@@ -35,6 +35,9 @@ Use this when a user wants to create global context for a product, company, or w
35
35
  - prefer an encyclopedia-style context set with many small docs instead of a few broad docs
36
36
  - prefer category directories like `docs/role/`, `docs/repos/`, `docs/domain/`, `docs/workflows/`, `docs/conventions/`, and `docs/infra/`
37
37
  - prefer one concept, one repository, one workflow, or one convention per file when possible
38
+ - treat `index.md` as concept-first: surface the keywords a user or AI would search for, not just broad document titles
39
+ - generate index entries automatically from primary concept names, aliases, repository nicknames, and workflow labels when those are clear
40
+ - 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
38
41
  - keep `index.md` as a human-readable dictionary-style TOC grouped by category headings and `label -> path` entries
39
42
  - use onboarding only for an empty gc-branch
40
43