@handsupmin/gc-tree 0.7.12 → 0.7.14
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 +41 -71
- package/dist/src/markdown.js +14 -1
- package/package.json +1 -1
package/dist/src/hook.js
CHANGED
|
@@ -6,6 +6,7 @@ import { hookCachePath, } from './paths.js';
|
|
|
6
6
|
import { branchScopeStatus, readBranchRepoMap, resolveBranchForRepo } from './repo-map.js';
|
|
7
7
|
import { resolveContext } from './resolve.js';
|
|
8
8
|
import { readHead, statusForBranch } from './store.js';
|
|
9
|
+
const SELF_REVIEW_INTERVAL = 5;
|
|
9
10
|
function normalizeText(value) {
|
|
10
11
|
return value.trim().replace(/\s+/g, ' ');
|
|
11
12
|
}
|
|
@@ -59,6 +60,15 @@ function buildMatchContext({ gcBranch, currentRepo, repoScopeStatus, query, matc
|
|
|
59
60
|
`Read full docs (gctree resolve --id <id>) only if summaries are insufficient. Each doc has a ## Summary section — read that first.`,
|
|
60
61
|
].join('\n');
|
|
61
62
|
}
|
|
63
|
+
function buildSelfReviewAppend(promptCount) {
|
|
64
|
+
return [
|
|
65
|
+
`\n[gc-tree] CONTEXT SELF-REVIEW (every ${SELF_REVIEW_INTERVAL} prompts — this is prompt #${promptCount}):`,
|
|
66
|
+
`At the END of your response to this task, review the current gc-tree global context and compare it against what you have observed or changed in this session.`,
|
|
67
|
+
`If any docs need updating (new conventions discovered, corrected info, new repos or workflows, outdated content), propose the specific changes to the user and ask whether to apply them.`,
|
|
68
|
+
`Use \`/gc-update-global-context\` (Claude Code) or \`gctree update-global-context\` (CLI) to apply.`,
|
|
69
|
+
`If nothing meaningful changed, skip silently — do not mention this review to the user.`,
|
|
70
|
+
].join('\n');
|
|
71
|
+
}
|
|
62
72
|
async function readHookCache(home, sessionId) {
|
|
63
73
|
try {
|
|
64
74
|
const raw = await readFile(hookCachePath(home, sessionId), 'utf8');
|
|
@@ -87,6 +97,7 @@ function nextCacheState(previous, { sessionId, gcBranch, currentRepo, repoScopeS
|
|
|
87
97
|
branch_empty: changed ? false : previous.branch_empty,
|
|
88
98
|
branch_excluded: changed ? false : previous.branch_excluded,
|
|
89
99
|
no_match_signatures: changed ? [] : previous.no_match_signatures,
|
|
100
|
+
prompt_count: (previous?.prompt_count ?? 0) + 1,
|
|
90
101
|
updated_at: new Date().toISOString(),
|
|
91
102
|
};
|
|
92
103
|
}
|
|
@@ -126,84 +137,43 @@ export async function dispatchGcTreeHook({ event, home, payload, }) {
|
|
|
126
137
|
currentRepo,
|
|
127
138
|
repoScopeStatus,
|
|
128
139
|
});
|
|
140
|
+
let additionalContext;
|
|
129
141
|
if (repoScopeStatus === 'excluded') {
|
|
130
142
|
cache.branch_excluded = true;
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
additionalContext: buildExcludedContext({
|
|
137
|
-
gcBranch,
|
|
138
|
-
currentRepo,
|
|
139
|
-
cached: previousCache?.branch_excluded === true,
|
|
140
|
-
}),
|
|
141
|
-
},
|
|
142
|
-
};
|
|
143
|
+
additionalContext = buildExcludedContext({
|
|
144
|
+
gcBranch,
|
|
145
|
+
currentRepo,
|
|
146
|
+
cached: previousCache?.branch_excluded === true,
|
|
147
|
+
});
|
|
143
148
|
}
|
|
144
|
-
if (branchStatus.doc_count === 0) {
|
|
149
|
+
else if (branchStatus.doc_count === 0) {
|
|
145
150
|
const wasCached = cache.branch_empty;
|
|
146
151
|
cache.branch_empty = true;
|
|
147
|
-
|
|
148
|
-
await writeHookCache(home, cache);
|
|
149
|
-
return {
|
|
150
|
-
hookSpecificOutput: {
|
|
151
|
-
hookEventName: event,
|
|
152
|
-
additionalContext: buildEmptyBranchContext({
|
|
153
|
-
gcBranch,
|
|
154
|
-
currentRepo,
|
|
155
|
-
cached: wasCached,
|
|
156
|
-
}),
|
|
157
|
-
},
|
|
158
|
-
};
|
|
152
|
+
additionalContext = buildEmptyBranchContext({ gcBranch, currentRepo, cached: wasCached });
|
|
159
153
|
}
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
154
|
+
else {
|
|
155
|
+
const query = currentRepo ? `${currentRepo} ${prompt}` : prompt;
|
|
156
|
+
const signature = hashQuery(query);
|
|
157
|
+
if (cache.no_match_signatures.includes(signature)) {
|
|
158
|
+
additionalContext = buildNoMatchContext({ gcBranch, currentRepo, query, cached: true });
|
|
159
|
+
}
|
|
160
|
+
else {
|
|
161
|
+
const result = await resolveContext({ home, branch: gcBranch, query });
|
|
162
|
+
if (result.matches.length === 0) {
|
|
163
|
+
cache.no_match_signatures = [...new Set([...cache.no_match_signatures, signature])];
|
|
164
|
+
additionalContext = buildNoMatchContext({ gcBranch, currentRepo, query, cached: false });
|
|
165
|
+
}
|
|
166
|
+
else {
|
|
167
|
+
cache.no_match_signatures = cache.no_match_signatures.filter((entry) => entry !== signature);
|
|
168
|
+
additionalContext = buildMatchContext({ gcBranch, currentRepo, repoScopeStatus, query, matches: result.matches });
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
if (cache.prompt_count > 0 && cache.prompt_count % SELF_REVIEW_INTERVAL === 0) {
|
|
172
|
+
additionalContext += buildSelfReviewAppend(cache.prompt_count);
|
|
173
|
+
cache.prompt_count = 0;
|
|
174
|
+
}
|
|
176
175
|
}
|
|
177
|
-
const result = await resolveContext({ home, branch: gcBranch, query });
|
|
178
|
-
if (result.matches.length === 0) {
|
|
179
|
-
cache.no_match_signatures = [...new Set([...cache.no_match_signatures, signature])];
|
|
180
|
-
cache.updated_at = new Date().toISOString();
|
|
181
|
-
await writeHookCache(home, cache);
|
|
182
|
-
return {
|
|
183
|
-
hookSpecificOutput: {
|
|
184
|
-
hookEventName: event,
|
|
185
|
-
additionalContext: buildNoMatchContext({
|
|
186
|
-
gcBranch,
|
|
187
|
-
currentRepo,
|
|
188
|
-
query,
|
|
189
|
-
cached: false,
|
|
190
|
-
}),
|
|
191
|
-
},
|
|
192
|
-
};
|
|
193
|
-
}
|
|
194
|
-
cache.no_match_signatures = cache.no_match_signatures.filter((entry) => entry !== signature);
|
|
195
176
|
cache.updated_at = new Date().toISOString();
|
|
196
177
|
await writeHookCache(home, cache);
|
|
197
|
-
return {
|
|
198
|
-
hookSpecificOutput: {
|
|
199
|
-
hookEventName: event,
|
|
200
|
-
additionalContext: buildMatchContext({
|
|
201
|
-
gcBranch,
|
|
202
|
-
currentRepo,
|
|
203
|
-
repoScopeStatus,
|
|
204
|
-
query,
|
|
205
|
-
matches: result.matches,
|
|
206
|
-
}),
|
|
207
|
-
},
|
|
208
|
-
};
|
|
178
|
+
return { hookSpecificOutput: { hookEventName: event, additionalContext } };
|
|
209
179
|
}
|
package/dist/src/markdown.js
CHANGED
|
@@ -71,9 +71,22 @@ export function ensureSummary(summary) {
|
|
|
71
71
|
}
|
|
72
72
|
return trimmed;
|
|
73
73
|
}
|
|
74
|
+
function normalizeBody(raw, title) {
|
|
75
|
+
let s = raw.trim();
|
|
76
|
+
// Strip leading "# <title>" line (already rendered at top level)
|
|
77
|
+
const titleLine = `# ${title.trim()}`;
|
|
78
|
+
if (s.startsWith(titleLine))
|
|
79
|
+
s = s.slice(titleLine.length).trimStart();
|
|
80
|
+
// Strip leading "## Summary" block (already rendered at top level)
|
|
81
|
+
if (s.startsWith('## Summary')) {
|
|
82
|
+
const next = s.match(/\n(?=## )/);
|
|
83
|
+
s = next ? s.slice(next.index).trimStart() : '';
|
|
84
|
+
}
|
|
85
|
+
return s;
|
|
86
|
+
}
|
|
74
87
|
export function renderDocMarkdown(doc) {
|
|
75
88
|
const summary = ensureSummary(doc.summary);
|
|
76
|
-
const body = String(doc.body || '').
|
|
89
|
+
const body = normalizeBody(String(doc.body || ''), doc.title);
|
|
77
90
|
const normalizedIndexEntries = [...new Set([
|
|
78
91
|
...(doc.indexLabel?.trim() ? [doc.indexLabel.trim()] : []),
|
|
79
92
|
...(doc.indexEntries || []).map((entry) => String(entry || '').trim()).filter(Boolean),
|