@stackbilt/cli 0.16.0 → 1.0.0
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/__tests__/adf-init.test.js +31 -0
- package/dist/__tests__/adf-init.test.js.map +1 -1
- package/dist/__tests__/bootstrap.test.js +107 -0
- package/dist/__tests__/bootstrap.test.js.map +1 -1
- package/dist/__tests__/context-refresh-repo-intel.test.d.ts +9 -0
- package/dist/__tests__/context-refresh-repo-intel.test.d.ts.map +1 -0
- package/dist/__tests__/context-refresh-repo-intel.test.js +228 -0
- package/dist/__tests__/context-refresh-repo-intel.test.js.map +1 -0
- package/dist/__tests__/context-refresh.test.d.ts +2 -0
- package/dist/__tests__/context-refresh.test.d.ts.map +1 -0
- package/dist/__tests__/context-refresh.test.js +198 -0
- package/dist/__tests__/context-refresh.test.js.map +1 -0
- package/dist/__tests__/context.test.js +62 -0
- package/dist/__tests__/context.test.js.map +1 -1
- package/dist/__tests__/hook.test.js +25 -0
- package/dist/__tests__/hook.test.js.map +1 -1
- package/dist/__tests__/score.test.js +58 -0
- package/dist/__tests__/score.test.js.map +1 -1
- package/dist/__tests__/serve-context.test.d.ts +2 -0
- package/dist/__tests__/serve-context.test.d.ts.map +1 -0
- package/dist/__tests__/serve-context.test.js +112 -0
- package/dist/__tests__/serve-context.test.js.map +1 -0
- package/dist/__tests__/setup.test.js +48 -0
- package/dist/__tests__/setup.test.js.map +1 -1
- package/dist/__tests__/why.test.d.ts +2 -0
- package/dist/__tests__/why.test.d.ts.map +1 -0
- package/dist/__tests__/why.test.js +145 -0
- package/dist/__tests__/why.test.js.map +1 -0
- package/dist/commands/adf.d.ts +2 -2
- package/dist/commands/adf.d.ts.map +1 -1
- package/dist/commands/adf.js +12 -0
- package/dist/commands/adf.js.map +1 -1
- package/dist/commands/bootstrap.d.ts.map +1 -1
- package/dist/commands/bootstrap.js +145 -34
- package/dist/commands/bootstrap.js.map +1 -1
- package/dist/commands/context-refresh.d.ts +16 -0
- package/dist/commands/context-refresh.d.ts.map +1 -0
- package/dist/commands/context-refresh.js +841 -0
- package/dist/commands/context-refresh.js.map +1 -0
- package/dist/commands/context.d.ts.map +1 -1
- package/dist/commands/context.js +104 -7
- package/dist/commands/context.js.map +1 -1
- package/dist/commands/hook.d.ts +1 -0
- package/dist/commands/hook.d.ts.map +1 -1
- package/dist/commands/hook.js +30 -1
- package/dist/commands/hook.js.map +1 -1
- package/dist/commands/score.js +76 -14
- package/dist/commands/score.js.map +1 -1
- package/dist/commands/serve.d.ts +22 -1
- package/dist/commands/serve.d.ts.map +1 -1
- package/dist/commands/serve.js +70 -3
- package/dist/commands/serve.js.map +1 -1
- package/dist/commands/setup.d.ts.map +1 -1
- package/dist/commands/setup.js +47 -1
- package/dist/commands/setup.js.map +1 -1
- package/dist/commands/why.d.ts.map +1 -1
- package/dist/commands/why.js +44 -0
- package/dist/commands/why.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +10 -28
- package/dist/index.js.map +1 -1
- package/package.json +27 -15
- package/dist/__tests__/auth-wiring.test.d.ts +0 -2
- package/dist/__tests__/auth-wiring.test.d.ts.map +0 -1
- package/dist/__tests__/auth-wiring.test.js +0 -158
- package/dist/__tests__/auth-wiring.test.js.map +0 -1
- package/dist/__tests__/credentials.test.d.ts +0 -2
- package/dist/__tests__/credentials.test.d.ts.map +0 -1
- package/dist/__tests__/credentials.test.js +0 -145
- package/dist/__tests__/credentials.test.js.map +0 -1
- package/dist/__tests__/deprecated-commands.test.d.ts +0 -2
- package/dist/__tests__/deprecated-commands.test.d.ts.map +0 -1
- package/dist/__tests__/deprecated-commands.test.js +0 -59
- package/dist/__tests__/deprecated-commands.test.js.map +0 -1
- package/dist/__tests__/login.test.d.ts +0 -2
- package/dist/__tests__/login.test.d.ts.map +0 -1
- package/dist/__tests__/login.test.js +0 -66
- package/dist/__tests__/login.test.js.map +0 -1
- package/dist/commands/architect.d.ts +0 -12
- package/dist/commands/architect.d.ts.map +0 -1
- package/dist/commands/architect.js +0 -167
- package/dist/commands/architect.js.map +0 -1
- package/dist/commands/deprecation-warning.d.ts +0 -4
- package/dist/commands/deprecation-warning.d.ts.map +0 -1
- package/dist/commands/deprecation-warning.js +0 -20
- package/dist/commands/deprecation-warning.js.map +0 -1
- package/dist/commands/login.d.ts +0 -13
- package/dist/commands/login.d.ts.map +0 -1
- package/dist/commands/login.js +0 -80
- package/dist/commands/login.js.map +0 -1
- package/dist/commands/run.d.ts +0 -18
- package/dist/commands/run.d.ts.map +0 -1
- package/dist/commands/run.js +0 -247
- package/dist/commands/run.js.map +0 -1
- package/dist/commands/scaffold.d.ts +0 -11
- package/dist/commands/scaffold.d.ts.map +0 -1
- package/dist/commands/scaffold.js +0 -112
- package/dist/commands/scaffold.js.map +0 -1
- package/dist/credentials.d.ts +0 -34
- package/dist/credentials.d.ts.map +0 -1
- package/dist/credentials.js +0 -108
- package/dist/credentials.js.map +0 -1
- package/dist/http-client.d.ts +0 -114
- package/dist/http-client.d.ts.map +0 -1
- package/dist/http-client.js +0 -69
- package/dist/http-client.js.map +0 -1
- package/dist/types/scaffold-contract-types.d.ts +0 -90
- package/dist/types/scaffold-contract-types.d.ts.map +0 -1
- package/dist/types/scaffold-contract-types.js +0 -22
- package/dist/types/scaffold-contract-types.js.map +0 -1
|
@@ -0,0 +1,841 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* charter context-refresh
|
|
4
|
+
*
|
|
5
|
+
* Generates a live session snapshot and writes:
|
|
6
|
+
* - .ai/context.adf
|
|
7
|
+
* - .ai/context.snapshot.json
|
|
8
|
+
* Optional:
|
|
9
|
+
* - markdown mirror via --output
|
|
10
|
+
*/
|
|
11
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
12
|
+
if (k2 === undefined) k2 = k;
|
|
13
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
14
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
15
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
16
|
+
}
|
|
17
|
+
Object.defineProperty(o, k2, desc);
|
|
18
|
+
}) : (function(o, m, k, k2) {
|
|
19
|
+
if (k2 === undefined) k2 = k;
|
|
20
|
+
o[k2] = m[k];
|
|
21
|
+
}));
|
|
22
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
23
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
24
|
+
}) : function(o, v) {
|
|
25
|
+
o["default"] = v;
|
|
26
|
+
});
|
|
27
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
28
|
+
var ownKeys = function(o) {
|
|
29
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
30
|
+
var ar = [];
|
|
31
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
32
|
+
return ar;
|
|
33
|
+
};
|
|
34
|
+
return ownKeys(o);
|
|
35
|
+
};
|
|
36
|
+
return function (mod) {
|
|
37
|
+
if (mod && mod.__esModule) return mod;
|
|
38
|
+
var result = {};
|
|
39
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
40
|
+
__setModuleDefault(result, mod);
|
|
41
|
+
return result;
|
|
42
|
+
};
|
|
43
|
+
})();
|
|
44
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
45
|
+
exports.contextRefreshCommand = contextRefreshCommand;
|
|
46
|
+
const fs = __importStar(require("node:fs"));
|
|
47
|
+
const path = __importStar(require("node:path"));
|
|
48
|
+
const node_child_process_1 = require("node:child_process");
|
|
49
|
+
const index_1 = require("../index");
|
|
50
|
+
const flags_1 = require("../flags");
|
|
51
|
+
const SOURCE_SET = new Set(['git', 'github', 'repo-intel']);
|
|
52
|
+
const DEFAULT_CONFIG = {
|
|
53
|
+
version: 1,
|
|
54
|
+
defaults: {
|
|
55
|
+
sources: ['git'],
|
|
56
|
+
ttlMinutes: 30,
|
|
57
|
+
maxItems: {
|
|
58
|
+
gitCommits: 10,
|
|
59
|
+
gitDirtyFiles: 25,
|
|
60
|
+
githubIssues: 20,
|
|
61
|
+
},
|
|
62
|
+
},
|
|
63
|
+
sources: {
|
|
64
|
+
git: {
|
|
65
|
+
enabled: true,
|
|
66
|
+
},
|
|
67
|
+
github: {
|
|
68
|
+
enabled: false,
|
|
69
|
+
repo: null,
|
|
70
|
+
labels: [],
|
|
71
|
+
includePullRequests: true,
|
|
72
|
+
includeChecks: true,
|
|
73
|
+
},
|
|
74
|
+
'repo-intel': {
|
|
75
|
+
enabled: true,
|
|
76
|
+
},
|
|
77
|
+
},
|
|
78
|
+
};
|
|
79
|
+
async function contextRefreshCommand(options, args, io) {
|
|
80
|
+
const log = io?.log ?? console.log;
|
|
81
|
+
const resolved = resolveOptions(options, args);
|
|
82
|
+
const snapshotPath = path.join(resolved.aiDirAbs, 'context.snapshot.json');
|
|
83
|
+
const contextAdfPath = path.join(resolved.aiDirAbs, 'context.adf');
|
|
84
|
+
if (resolved.once && !resolved.force) {
|
|
85
|
+
const skipReason = shouldSkipRefresh(snapshotPath, resolved.ttlMinutes);
|
|
86
|
+
if (skipReason.skip) {
|
|
87
|
+
const existing = skipReason.snapshot;
|
|
88
|
+
const files = {
|
|
89
|
+
contextAdf: path.relative(process.cwd(), contextAdfPath) || '.',
|
|
90
|
+
snapshotJson: path.relative(process.cwd(), snapshotPath) || '.',
|
|
91
|
+
outputMarkdown: resolved.outputPathAbs
|
|
92
|
+
? (path.relative(process.cwd(), resolved.outputPathAbs) || '.')
|
|
93
|
+
: null,
|
|
94
|
+
};
|
|
95
|
+
if (options.format === 'json') {
|
|
96
|
+
log(JSON.stringify({
|
|
97
|
+
status: 'skipped',
|
|
98
|
+
reason: 'fresh_snapshot',
|
|
99
|
+
generatedAt: existing?.generatedAt ?? null,
|
|
100
|
+
expiresAt: existing?.expiresAt ?? null,
|
|
101
|
+
sourcesRequested: resolved.sourcesRequested,
|
|
102
|
+
sourcesUsed: existing?.sourcesUsed ?? [],
|
|
103
|
+
files,
|
|
104
|
+
warnings: [],
|
|
105
|
+
errors: [],
|
|
106
|
+
}, null, 2));
|
|
107
|
+
}
|
|
108
|
+
else {
|
|
109
|
+
log('');
|
|
110
|
+
log(' charter context-refresh');
|
|
111
|
+
log(' Status: skipped (fresh snapshot)');
|
|
112
|
+
log(` Snapshot: ${files.snapshotJson}`);
|
|
113
|
+
log(` TTL (mins): ${resolved.ttlMinutes}`);
|
|
114
|
+
log('');
|
|
115
|
+
}
|
|
116
|
+
return index_1.EXIT_CODE.SUCCESS;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
const snapshot = await buildSnapshot(process.cwd(), resolved);
|
|
120
|
+
const adf = renderContextAdf(snapshot);
|
|
121
|
+
const markdown = renderContextMarkdown(snapshot);
|
|
122
|
+
fs.mkdirSync(resolved.aiDirAbs, { recursive: true });
|
|
123
|
+
fs.writeFileSync(contextAdfPath, adf, 'utf8');
|
|
124
|
+
fs.writeFileSync(snapshotPath, JSON.stringify(snapshot, null, 2), 'utf8');
|
|
125
|
+
if (resolved.outputPathAbs) {
|
|
126
|
+
fs.mkdirSync(path.dirname(resolved.outputPathAbs), { recursive: true });
|
|
127
|
+
fs.writeFileSync(resolved.outputPathAbs, markdown, 'utf8');
|
|
128
|
+
}
|
|
129
|
+
const status = snapshot.errors.length > 0 || snapshot.warnings.length > 0
|
|
130
|
+
? 'partial_source_failure'
|
|
131
|
+
: 'refreshed';
|
|
132
|
+
const files = {
|
|
133
|
+
contextAdf: path.relative(process.cwd(), contextAdfPath) || '.',
|
|
134
|
+
snapshotJson: path.relative(process.cwd(), snapshotPath) || '.',
|
|
135
|
+
outputMarkdown: resolved.outputPathAbs
|
|
136
|
+
? (path.relative(process.cwd(), resolved.outputPathAbs) || '.')
|
|
137
|
+
: null,
|
|
138
|
+
};
|
|
139
|
+
if (options.format === 'json') {
|
|
140
|
+
log(JSON.stringify({
|
|
141
|
+
status: 'ok',
|
|
142
|
+
reason: status,
|
|
143
|
+
generatedAt: snapshot.generatedAt,
|
|
144
|
+
expiresAt: snapshot.expiresAt,
|
|
145
|
+
sourcesRequested: snapshot.sourcesRequested,
|
|
146
|
+
sourcesUsed: snapshot.sourcesUsed,
|
|
147
|
+
files,
|
|
148
|
+
warnings: snapshot.warnings,
|
|
149
|
+
errors: snapshot.errors,
|
|
150
|
+
}, null, 2));
|
|
151
|
+
}
|
|
152
|
+
else {
|
|
153
|
+
log('');
|
|
154
|
+
log(' charter context-refresh');
|
|
155
|
+
log(` Status: ${status}`);
|
|
156
|
+
log(` Sources: ${snapshot.sourcesUsed.join(', ') || '(none)'}`);
|
|
157
|
+
log(` Wrote: ${files.contextAdf}`);
|
|
158
|
+
log(` Snapshot: ${files.snapshotJson}`);
|
|
159
|
+
if (files.outputMarkdown) {
|
|
160
|
+
log(` Mirrored MD: ${files.outputMarkdown}`);
|
|
161
|
+
}
|
|
162
|
+
if (snapshot.warnings.length > 0) {
|
|
163
|
+
log(` Warnings: ${snapshot.warnings.length}`);
|
|
164
|
+
}
|
|
165
|
+
if (snapshot.errors.length > 0) {
|
|
166
|
+
log(` Errors: ${snapshot.errors.length}`);
|
|
167
|
+
}
|
|
168
|
+
log('');
|
|
169
|
+
}
|
|
170
|
+
return index_1.EXIT_CODE.SUCCESS;
|
|
171
|
+
}
|
|
172
|
+
function resolveOptions(options, args) {
|
|
173
|
+
const aiDir = (0, flags_1.getFlag)(args, '--ai-dir') || '.ai';
|
|
174
|
+
const outputPath = (0, flags_1.getFlag)(args, '--output');
|
|
175
|
+
const sourcesFlag = (0, flags_1.getFlag)(args, '--sources');
|
|
176
|
+
const ttlFlag = (0, flags_1.getFlag)(args, '--ttl-minutes');
|
|
177
|
+
const once = args.includes('--once');
|
|
178
|
+
const force = args.includes('--force');
|
|
179
|
+
const config = loadContextConfig(options.configPath);
|
|
180
|
+
const sourcesRequested = parseRequestedSources(sourcesFlag, config.defaults.sources);
|
|
181
|
+
const ttlMinutes = resolveTtlMinutes(ttlFlag, config.defaults.ttlMinutes);
|
|
182
|
+
return {
|
|
183
|
+
aiDirAbs: path.resolve(aiDir),
|
|
184
|
+
outputPathAbs: outputPath ? path.resolve(outputPath) : null,
|
|
185
|
+
ttlMinutes,
|
|
186
|
+
once,
|
|
187
|
+
force,
|
|
188
|
+
sourcesRequested,
|
|
189
|
+
config,
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
function loadContextConfig(configPath) {
|
|
193
|
+
const cfgFile = path.resolve(configPath, 'context-sources.json');
|
|
194
|
+
if (!fs.existsSync(cfgFile)) {
|
|
195
|
+
return JSON.parse(JSON.stringify(DEFAULT_CONFIG));
|
|
196
|
+
}
|
|
197
|
+
let parsed;
|
|
198
|
+
try {
|
|
199
|
+
parsed = JSON.parse(fs.readFileSync(cfgFile, 'utf8'));
|
|
200
|
+
}
|
|
201
|
+
catch (error) {
|
|
202
|
+
throw new index_1.CLIError(`Invalid JSON in ${path.relative(process.cwd(), cfgFile)}: ${error.message}`);
|
|
203
|
+
}
|
|
204
|
+
if (!parsed || typeof parsed !== 'object') {
|
|
205
|
+
throw new index_1.CLIError(`Invalid config in ${path.relative(process.cwd(), cfgFile)}: expected an object`);
|
|
206
|
+
}
|
|
207
|
+
const cfg = JSON.parse(JSON.stringify(DEFAULT_CONFIG));
|
|
208
|
+
const root = parsed;
|
|
209
|
+
if (typeof root.version === 'number')
|
|
210
|
+
cfg.version = root.version;
|
|
211
|
+
if (root.defaults && typeof root.defaults === 'object') {
|
|
212
|
+
const defaults = root.defaults;
|
|
213
|
+
if (Array.isArray(defaults.sources)) {
|
|
214
|
+
const cleaned = defaults.sources
|
|
215
|
+
.map((entry) => String(entry).trim().toLowerCase())
|
|
216
|
+
.filter((entry) => entry.length > 0 && SOURCE_SET.has(entry));
|
|
217
|
+
if (cleaned.length > 0)
|
|
218
|
+
cfg.defaults.sources = [...new Set(cleaned)];
|
|
219
|
+
}
|
|
220
|
+
if (typeof defaults.ttlMinutes === 'number' && defaults.ttlMinutes > 0) {
|
|
221
|
+
cfg.defaults.ttlMinutes = Math.floor(defaults.ttlMinutes);
|
|
222
|
+
}
|
|
223
|
+
if (defaults.maxItems && typeof defaults.maxItems === 'object') {
|
|
224
|
+
const maxItems = defaults.maxItems;
|
|
225
|
+
if (typeof maxItems.gitCommits === 'number' && maxItems.gitCommits > 0) {
|
|
226
|
+
cfg.defaults.maxItems.gitCommits = Math.floor(maxItems.gitCommits);
|
|
227
|
+
}
|
|
228
|
+
if (typeof maxItems.gitDirtyFiles === 'number' && maxItems.gitDirtyFiles > 0) {
|
|
229
|
+
cfg.defaults.maxItems.gitDirtyFiles = Math.floor(maxItems.gitDirtyFiles);
|
|
230
|
+
}
|
|
231
|
+
if (typeof maxItems.githubIssues === 'number' && maxItems.githubIssues > 0) {
|
|
232
|
+
cfg.defaults.maxItems.githubIssues = Math.floor(maxItems.githubIssues);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
if (root.sources && typeof root.sources === 'object') {
|
|
237
|
+
const sources = root.sources;
|
|
238
|
+
if (sources.git && typeof sources.git === 'object') {
|
|
239
|
+
const git = sources.git;
|
|
240
|
+
if (typeof git.enabled === 'boolean')
|
|
241
|
+
cfg.sources.git.enabled = git.enabled;
|
|
242
|
+
}
|
|
243
|
+
if (sources.github && typeof sources.github === 'object') {
|
|
244
|
+
const github = sources.github;
|
|
245
|
+
if (typeof github.enabled === 'boolean')
|
|
246
|
+
cfg.sources.github.enabled = github.enabled;
|
|
247
|
+
if (typeof github.repo === 'string')
|
|
248
|
+
cfg.sources.github.repo = github.repo.trim();
|
|
249
|
+
if (Array.isArray(github.labels)) {
|
|
250
|
+
cfg.sources.github.labels = github.labels
|
|
251
|
+
.map((entry) => String(entry).trim())
|
|
252
|
+
.filter((entry) => entry.length > 0);
|
|
253
|
+
}
|
|
254
|
+
if (typeof github.includePullRequests === 'boolean') {
|
|
255
|
+
cfg.sources.github.includePullRequests = github.includePullRequests;
|
|
256
|
+
}
|
|
257
|
+
if (typeof github.includeChecks === 'boolean') {
|
|
258
|
+
cfg.sources.github.includeChecks = github.includeChecks;
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
const repoIntelCfg = sources['repo-intel'];
|
|
262
|
+
if (repoIntelCfg && typeof repoIntelCfg === 'object') {
|
|
263
|
+
const ri = repoIntelCfg;
|
|
264
|
+
if (typeof ri.enabled === 'boolean')
|
|
265
|
+
cfg.sources['repo-intel'].enabled = ri.enabled;
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
return cfg;
|
|
269
|
+
}
|
|
270
|
+
function parseRequestedSources(sourcesFlag, fallback) {
|
|
271
|
+
if (!sourcesFlag)
|
|
272
|
+
return [...fallback];
|
|
273
|
+
const requested = sourcesFlag
|
|
274
|
+
.split(',')
|
|
275
|
+
.map((entry) => entry.trim().toLowerCase())
|
|
276
|
+
.filter((entry) => entry.length > 0);
|
|
277
|
+
const invalid = requested.filter((entry) => !SOURCE_SET.has(entry));
|
|
278
|
+
if (invalid.length > 0) {
|
|
279
|
+
throw new index_1.CLIError(`Unsupported --sources value(s): ${invalid.join(', ')}. Supported: git, github, repo-intel.`);
|
|
280
|
+
}
|
|
281
|
+
return [...new Set(requested)];
|
|
282
|
+
}
|
|
283
|
+
function resolveTtlMinutes(ttlFlag, defaultTtl) {
|
|
284
|
+
if (!ttlFlag)
|
|
285
|
+
return defaultTtl;
|
|
286
|
+
const parsed = Number(ttlFlag);
|
|
287
|
+
if (!Number.isFinite(parsed) || parsed <= 0) {
|
|
288
|
+
throw new index_1.CLIError(`Invalid --ttl-minutes value: ${ttlFlag}. Must be a positive number.`);
|
|
289
|
+
}
|
|
290
|
+
return Math.floor(parsed);
|
|
291
|
+
}
|
|
292
|
+
function shouldSkipRefresh(snapshotPath, ttlMinutes) {
|
|
293
|
+
if (!fs.existsSync(snapshotPath))
|
|
294
|
+
return { skip: false };
|
|
295
|
+
try {
|
|
296
|
+
const snapshot = JSON.parse(fs.readFileSync(snapshotPath, 'utf8'));
|
|
297
|
+
if (!snapshot.generatedAt)
|
|
298
|
+
return { skip: false };
|
|
299
|
+
const generatedAtMs = Date.parse(snapshot.generatedAt);
|
|
300
|
+
if (!Number.isFinite(generatedAtMs))
|
|
301
|
+
return { skip: false };
|
|
302
|
+
const ageMs = Date.now() - generatedAtMs;
|
|
303
|
+
const ttlMs = ttlMinutes * 60 * 1000;
|
|
304
|
+
if (ageMs <= ttlMs) {
|
|
305
|
+
return { skip: true, snapshot };
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
catch {
|
|
309
|
+
return { skip: false };
|
|
310
|
+
}
|
|
311
|
+
return { skip: false };
|
|
312
|
+
}
|
|
313
|
+
async function buildSnapshot(cwd, resolved) {
|
|
314
|
+
const now = new Date();
|
|
315
|
+
const generatedAt = now.toISOString();
|
|
316
|
+
const expiresAt = new Date(now.getTime() + resolved.ttlMinutes * 60_000).toISOString();
|
|
317
|
+
const warnings = [];
|
|
318
|
+
const errors = [];
|
|
319
|
+
const sourcesUsed = [];
|
|
320
|
+
const git = resolved.sourcesRequested.includes('git') && resolved.config.sources.git.enabled
|
|
321
|
+
? collectGitSnapshot(cwd, resolved.config.defaults.maxItems.gitCommits, resolved.config.defaults.maxItems.gitDirtyFiles)
|
|
322
|
+
: { available: false, branch: null, dirty: false, dirtyFiles: [], recentCommits: [], error: 'disabled' };
|
|
323
|
+
if (git.available) {
|
|
324
|
+
sourcesUsed.push('git');
|
|
325
|
+
}
|
|
326
|
+
else if (resolved.sourcesRequested.includes('git') && git.error && git.error !== 'disabled') {
|
|
327
|
+
warnings.push(`git source unavailable: ${git.error}`);
|
|
328
|
+
}
|
|
329
|
+
const github = resolved.sourcesRequested.includes('github') && resolved.config.sources.github.enabled
|
|
330
|
+
? await collectGitHubSnapshot(resolved.config, resolved.config.defaults.maxItems.githubIssues)
|
|
331
|
+
: { available: false, repo: null, filterMode: 'strict', labels: [], issues: [], error: 'disabled' };
|
|
332
|
+
if (github.available) {
|
|
333
|
+
sourcesUsed.push('github');
|
|
334
|
+
}
|
|
335
|
+
else if (resolved.sourcesRequested.includes('github') && github.error && github.error !== 'disabled') {
|
|
336
|
+
const msg = `github source unavailable: ${github.error}`;
|
|
337
|
+
warnings.push(msg);
|
|
338
|
+
if (github.error.startsWith('request_failed:') || github.error.startsWith('api_error:') || github.error.startsWith('invalid_json:')) {
|
|
339
|
+
errors.push(msg);
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
const repoIntelEnabled = resolved.sourcesRequested.includes('repo-intel') && resolved.config.sources['repo-intel'].enabled;
|
|
343
|
+
const repoIntel = repoIntelEnabled
|
|
344
|
+
? collectRepoIntelSnapshot(cwd, generatedAt)
|
|
345
|
+
: { available: false, generatedAt, openIssues: [], closedIssues: [], pullRequests: [], releases: [], summary: { openIssueCount: 0, stalledIssues: 0, recurringLabels: [], mergeVelocity: 0, releaseCadence: null }, error: 'disabled' };
|
|
346
|
+
if (repoIntel.available) {
|
|
347
|
+
sourcesUsed.push('repo-intel');
|
|
348
|
+
// Persist full snapshot to .charter/repo-intel/snapshot.json
|
|
349
|
+
const repoIntelSnapshotPath = path.resolve(cwd, '.charter', 'repo-intel', 'snapshot.json');
|
|
350
|
+
fs.mkdirSync(path.dirname(repoIntelSnapshotPath), { recursive: true });
|
|
351
|
+
fs.writeFileSync(repoIntelSnapshotPath, JSON.stringify(repoIntel, null, 2), 'utf8');
|
|
352
|
+
}
|
|
353
|
+
else if (resolved.sourcesRequested.includes('repo-intel') && repoIntel.error && repoIntel.error !== 'disabled') {
|
|
354
|
+
warnings.push(`repo-intel source unavailable: ${repoIntel.error}`);
|
|
355
|
+
}
|
|
356
|
+
const derived = deriveAggregates(git, github, repoIntel);
|
|
357
|
+
return {
|
|
358
|
+
version: 1,
|
|
359
|
+
generatedAt,
|
|
360
|
+
expiresAt,
|
|
361
|
+
repo: {
|
|
362
|
+
root: cwd,
|
|
363
|
+
name: path.basename(cwd) || cwd,
|
|
364
|
+
},
|
|
365
|
+
sourcesRequested: resolved.sourcesRequested,
|
|
366
|
+
sourcesUsed,
|
|
367
|
+
sources: {
|
|
368
|
+
git,
|
|
369
|
+
github,
|
|
370
|
+
'repo-intel': repoIntel,
|
|
371
|
+
},
|
|
372
|
+
openWork: derived.openWork,
|
|
373
|
+
recentActivity: derived.recentActivity,
|
|
374
|
+
pendingDecisions: derived.pendingDecisions,
|
|
375
|
+
warnings,
|
|
376
|
+
errors,
|
|
377
|
+
};
|
|
378
|
+
}
|
|
379
|
+
function collectGitSnapshot(cwd, commitLimit, dirtyLimit) {
|
|
380
|
+
const inside = runGit(cwd, ['rev-parse', '--is-inside-work-tree']);
|
|
381
|
+
if (inside !== 'true') {
|
|
382
|
+
return {
|
|
383
|
+
available: false,
|
|
384
|
+
branch: null,
|
|
385
|
+
dirty: false,
|
|
386
|
+
dirtyFiles: [],
|
|
387
|
+
recentCommits: [],
|
|
388
|
+
error: 'not a git repository',
|
|
389
|
+
};
|
|
390
|
+
}
|
|
391
|
+
const branch = runGit(cwd, ['rev-parse', '--abbrev-ref', 'HEAD']) ?? 'unknown';
|
|
392
|
+
const status = runGit(cwd, ['status', '--short']) ?? '';
|
|
393
|
+
const dirtyLines = status
|
|
394
|
+
.split(/\r?\n/)
|
|
395
|
+
.map((line) => line.trim())
|
|
396
|
+
.filter((line) => line.length > 0);
|
|
397
|
+
const dirtyFiles = dirtyLines.slice(0, dirtyLimit);
|
|
398
|
+
const log = runGit(cwd, ['log', '-n', String(commitLimit), '--date=iso-strict', '--pretty=format:%h\t%ad\t%s']) ?? '';
|
|
399
|
+
const recentCommits = [];
|
|
400
|
+
for (const line of log.split(/\r?\n/)) {
|
|
401
|
+
if (!line.trim())
|
|
402
|
+
continue;
|
|
403
|
+
const [hash, date, ...subjectParts] = line.split('\t');
|
|
404
|
+
if (!hash || !date || subjectParts.length === 0)
|
|
405
|
+
continue;
|
|
406
|
+
recentCommits.push({
|
|
407
|
+
hash: hash.trim(),
|
|
408
|
+
date: date.trim(),
|
|
409
|
+
subject: subjectParts.join('\t').trim(),
|
|
410
|
+
});
|
|
411
|
+
}
|
|
412
|
+
return {
|
|
413
|
+
available: true,
|
|
414
|
+
branch,
|
|
415
|
+
dirty: dirtyLines.length > 0,
|
|
416
|
+
dirtyFiles,
|
|
417
|
+
recentCommits,
|
|
418
|
+
};
|
|
419
|
+
}
|
|
420
|
+
function runGit(cwd, args) {
|
|
421
|
+
try {
|
|
422
|
+
const output = (0, node_child_process_1.execFileSync)('git', args, {
|
|
423
|
+
cwd,
|
|
424
|
+
encoding: 'utf8',
|
|
425
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
426
|
+
});
|
|
427
|
+
return output.trim();
|
|
428
|
+
}
|
|
429
|
+
catch {
|
|
430
|
+
return null;
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
async function collectGitHubSnapshot(config, issueLimit) {
|
|
434
|
+
const repo = config.sources.github.repo;
|
|
435
|
+
if (!repo) {
|
|
436
|
+
return {
|
|
437
|
+
available: false,
|
|
438
|
+
repo: null,
|
|
439
|
+
filterMode: 'strict',
|
|
440
|
+
labels: config.sources.github.labels,
|
|
441
|
+
issues: [],
|
|
442
|
+
error: 'not_configured',
|
|
443
|
+
};
|
|
444
|
+
}
|
|
445
|
+
const token = process.env.GITHUB_TOKEN?.trim();
|
|
446
|
+
if (!token) {
|
|
447
|
+
return {
|
|
448
|
+
available: false,
|
|
449
|
+
repo,
|
|
450
|
+
filterMode: 'strict',
|
|
451
|
+
labels: config.sources.github.labels,
|
|
452
|
+
issues: [],
|
|
453
|
+
error: 'missing GITHUB_TOKEN',
|
|
454
|
+
};
|
|
455
|
+
}
|
|
456
|
+
const labels = config.sources.github.labels.filter((label) => label.length > 0);
|
|
457
|
+
const params = new URLSearchParams({
|
|
458
|
+
state: 'open',
|
|
459
|
+
per_page: String(issueLimit),
|
|
460
|
+
});
|
|
461
|
+
if (labels.length > 0) {
|
|
462
|
+
params.set('labels', labels.join(','));
|
|
463
|
+
}
|
|
464
|
+
const endpoint = `https://api.github.com/repos/${repo}/issues?${params.toString()}`;
|
|
465
|
+
let response;
|
|
466
|
+
try {
|
|
467
|
+
response = await fetch(endpoint, {
|
|
468
|
+
headers: {
|
|
469
|
+
Authorization: `Bearer ${token}`,
|
|
470
|
+
Accept: 'application/vnd.github+json',
|
|
471
|
+
'User-Agent': 'charter-cli',
|
|
472
|
+
},
|
|
473
|
+
});
|
|
474
|
+
}
|
|
475
|
+
catch (error) {
|
|
476
|
+
return {
|
|
477
|
+
available: false,
|
|
478
|
+
repo,
|
|
479
|
+
filterMode: 'strict',
|
|
480
|
+
labels,
|
|
481
|
+
issues: [],
|
|
482
|
+
error: `request_failed: ${error.message}`,
|
|
483
|
+
};
|
|
484
|
+
}
|
|
485
|
+
if (!response.ok) {
|
|
486
|
+
return {
|
|
487
|
+
available: false,
|
|
488
|
+
repo,
|
|
489
|
+
filterMode: 'strict',
|
|
490
|
+
labels,
|
|
491
|
+
issues: [],
|
|
492
|
+
error: `api_error: ${response.status} ${response.statusText}`,
|
|
493
|
+
};
|
|
494
|
+
}
|
|
495
|
+
let payload = [];
|
|
496
|
+
try {
|
|
497
|
+
payload = await response.json();
|
|
498
|
+
}
|
|
499
|
+
catch (error) {
|
|
500
|
+
return {
|
|
501
|
+
available: false,
|
|
502
|
+
repo,
|
|
503
|
+
filterMode: 'strict',
|
|
504
|
+
labels,
|
|
505
|
+
issues: [],
|
|
506
|
+
error: `invalid_json: ${error.message}`,
|
|
507
|
+
};
|
|
508
|
+
}
|
|
509
|
+
const issues = payload
|
|
510
|
+
.filter((item) => !item.pull_request)
|
|
511
|
+
.map((item) => ({
|
|
512
|
+
number: item.number,
|
|
513
|
+
title: item.title,
|
|
514
|
+
state: item.state,
|
|
515
|
+
updatedAt: item.updated_at,
|
|
516
|
+
labels: item.labels.map((entry) => typeof entry === 'string' ? entry : (entry.name ?? '')).filter((entry) => entry.length > 0),
|
|
517
|
+
url: item.html_url,
|
|
518
|
+
}));
|
|
519
|
+
return {
|
|
520
|
+
available: true,
|
|
521
|
+
repo,
|
|
522
|
+
filterMode: 'strict',
|
|
523
|
+
labels,
|
|
524
|
+
issues,
|
|
525
|
+
};
|
|
526
|
+
}
|
|
527
|
+
function runGhCommand(args, cwd) {
|
|
528
|
+
try {
|
|
529
|
+
const output = (0, node_child_process_1.execFileSync)('gh', args, {
|
|
530
|
+
cwd,
|
|
531
|
+
encoding: 'utf8',
|
|
532
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
533
|
+
});
|
|
534
|
+
return output.trim();
|
|
535
|
+
}
|
|
536
|
+
catch {
|
|
537
|
+
return null;
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
function collectRepoIntelSnapshot(cwd, generatedAt) {
|
|
541
|
+
const empty = {
|
|
542
|
+
available: false,
|
|
543
|
+
generatedAt,
|
|
544
|
+
openIssues: [],
|
|
545
|
+
closedIssues: [],
|
|
546
|
+
pullRequests: [],
|
|
547
|
+
releases: [],
|
|
548
|
+
summary: { openIssueCount: 0, stalledIssues: 0, recurringLabels: [], mergeVelocity: 0, releaseCadence: null },
|
|
549
|
+
};
|
|
550
|
+
// Check if gh CLI is available
|
|
551
|
+
const ghVersion = runGhCommand(['--version'], cwd);
|
|
552
|
+
if (!ghVersion) {
|
|
553
|
+
return { ...empty, error: 'gh CLI not available' };
|
|
554
|
+
}
|
|
555
|
+
// Open issues (last 50, sorted by updated)
|
|
556
|
+
const openIssuesRaw = runGhCommand([
|
|
557
|
+
'issue', 'list', '--limit', '50', '--state', 'open',
|
|
558
|
+
'--json', 'number,title,labels,assignees,createdAt,updatedAt,comments',
|
|
559
|
+
], cwd);
|
|
560
|
+
if (!openIssuesRaw) {
|
|
561
|
+
return { ...empty, error: 'no GitHub remote or gh auth required' };
|
|
562
|
+
}
|
|
563
|
+
let openIssues;
|
|
564
|
+
try {
|
|
565
|
+
openIssues = JSON.parse(openIssuesRaw);
|
|
566
|
+
}
|
|
567
|
+
catch {
|
|
568
|
+
return { ...empty, error: 'invalid_json: open issues response' };
|
|
569
|
+
}
|
|
570
|
+
// Recent closed issues (last 20)
|
|
571
|
+
const closedIssuesRaw = runGhCommand([
|
|
572
|
+
'issue', 'list', '--limit', '20', '--state', 'closed',
|
|
573
|
+
'--json', 'number,title,labels,closedAt',
|
|
574
|
+
], cwd);
|
|
575
|
+
let closedIssues = [];
|
|
576
|
+
if (closedIssuesRaw) {
|
|
577
|
+
try {
|
|
578
|
+
closedIssues = JSON.parse(closedIssuesRaw);
|
|
579
|
+
}
|
|
580
|
+
catch { /* ignore parse failures for supplemental data */ }
|
|
581
|
+
}
|
|
582
|
+
// Recent PRs (last 30, all states)
|
|
583
|
+
const prsRaw = runGhCommand([
|
|
584
|
+
'pr', 'list', '--limit', '30', '--state', 'all',
|
|
585
|
+
'--json', 'number,title,state,author,mergedAt,createdAt,reviewDecision,labels',
|
|
586
|
+
], cwd);
|
|
587
|
+
let pullRequests = [];
|
|
588
|
+
if (prsRaw) {
|
|
589
|
+
try {
|
|
590
|
+
pullRequests = JSON.parse(prsRaw);
|
|
591
|
+
}
|
|
592
|
+
catch { /* ignore */ }
|
|
593
|
+
}
|
|
594
|
+
// Release cadence (last 10 releases)
|
|
595
|
+
const releasesRaw = runGhCommand([
|
|
596
|
+
'release', 'list', '--limit', '10',
|
|
597
|
+
'--json', 'tagName,publishedAt,isLatest',
|
|
598
|
+
], cwd);
|
|
599
|
+
let releases = [];
|
|
600
|
+
if (releasesRaw) {
|
|
601
|
+
try {
|
|
602
|
+
releases = JSON.parse(releasesRaw);
|
|
603
|
+
}
|
|
604
|
+
catch { /* ignore */ }
|
|
605
|
+
}
|
|
606
|
+
// Compute summary
|
|
607
|
+
const now = Date.now();
|
|
608
|
+
const thirtyDaysMs = 30 * 24 * 60 * 60 * 1000;
|
|
609
|
+
const stalledIssues = openIssues.filter((issue) => {
|
|
610
|
+
const updatedMs = Date.parse(issue.updatedAt);
|
|
611
|
+
return Number.isFinite(updatedMs) && (now - updatedMs) > thirtyDaysMs;
|
|
612
|
+
}).length;
|
|
613
|
+
// Count label occurrences in closed issues
|
|
614
|
+
const labelCounts = new Map();
|
|
615
|
+
for (const issue of closedIssues) {
|
|
616
|
+
for (const label of issue.labels) {
|
|
617
|
+
const name = label.name;
|
|
618
|
+
labelCounts.set(name, (labelCounts.get(name) ?? 0) + 1);
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
const recurringLabels = [...labelCounts.entries()]
|
|
622
|
+
.filter(([, count]) => count >= 3)
|
|
623
|
+
.sort((a, b) => b[1] - a[1])
|
|
624
|
+
.map(([name]) => name);
|
|
625
|
+
const mergeVelocity = pullRequests.filter((pr) => {
|
|
626
|
+
if (!pr.mergedAt)
|
|
627
|
+
return false;
|
|
628
|
+
const mergedMs = Date.parse(pr.mergedAt);
|
|
629
|
+
return Number.isFinite(mergedMs) && (now - mergedMs) <= thirtyDaysMs;
|
|
630
|
+
}).length;
|
|
631
|
+
let releaseCadence = null;
|
|
632
|
+
const lastFiveReleases = releases
|
|
633
|
+
.slice(0, 5)
|
|
634
|
+
.map((r) => Date.parse(r.publishedAt))
|
|
635
|
+
.filter((ms) => Number.isFinite(ms))
|
|
636
|
+
.sort((a, b) => b - a);
|
|
637
|
+
if (lastFiveReleases.length >= 2) {
|
|
638
|
+
const gaps = [];
|
|
639
|
+
for (let i = 0; i < lastFiveReleases.length - 1; i++) {
|
|
640
|
+
gaps.push((lastFiveReleases[i] - lastFiveReleases[i + 1]) / (24 * 60 * 60 * 1000));
|
|
641
|
+
}
|
|
642
|
+
releaseCadence = Math.round(gaps.reduce((a, b) => a + b, 0) / gaps.length);
|
|
643
|
+
}
|
|
644
|
+
const summary = {
|
|
645
|
+
openIssueCount: openIssues.length,
|
|
646
|
+
stalledIssues,
|
|
647
|
+
recurringLabels,
|
|
648
|
+
mergeVelocity,
|
|
649
|
+
releaseCadence,
|
|
650
|
+
};
|
|
651
|
+
return {
|
|
652
|
+
available: true,
|
|
653
|
+
generatedAt,
|
|
654
|
+
openIssues,
|
|
655
|
+
closedIssues,
|
|
656
|
+
pullRequests,
|
|
657
|
+
releases,
|
|
658
|
+
summary,
|
|
659
|
+
};
|
|
660
|
+
}
|
|
661
|
+
function deriveAggregates(git, github, repoIntel) {
|
|
662
|
+
const openWork = [];
|
|
663
|
+
const recentActivity = [];
|
|
664
|
+
const pendingDecisions = [];
|
|
665
|
+
if (git.available) {
|
|
666
|
+
openWork.push({
|
|
667
|
+
source: 'git',
|
|
668
|
+
type: 'branch',
|
|
669
|
+
summary: `Branch ${git.branch ?? 'unknown'}`,
|
|
670
|
+
});
|
|
671
|
+
if (git.dirty) {
|
|
672
|
+
openWork.push({
|
|
673
|
+
source: 'git',
|
|
674
|
+
type: 'working-tree',
|
|
675
|
+
summary: `Working tree has ${git.dirtyFiles.length} pending change(s)`,
|
|
676
|
+
});
|
|
677
|
+
for (const dirty of git.dirtyFiles) {
|
|
678
|
+
openWork.push({
|
|
679
|
+
source: 'git',
|
|
680
|
+
type: 'dirty-file',
|
|
681
|
+
summary: dirty,
|
|
682
|
+
});
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
for (const commit of git.recentCommits) {
|
|
686
|
+
recentActivity.push({
|
|
687
|
+
source: 'git',
|
|
688
|
+
type: 'commit',
|
|
689
|
+
summary: `${commit.hash} ${commit.subject}`,
|
|
690
|
+
ref: commit.hash,
|
|
691
|
+
});
|
|
692
|
+
}
|
|
693
|
+
}
|
|
694
|
+
if (github.available) {
|
|
695
|
+
for (const issue of github.issues) {
|
|
696
|
+
openWork.push({
|
|
697
|
+
source: 'github',
|
|
698
|
+
type: 'issue',
|
|
699
|
+
summary: `#${issue.number} ${issue.title}`,
|
|
700
|
+
ref: issue.url,
|
|
701
|
+
});
|
|
702
|
+
pendingDecisions.push({
|
|
703
|
+
source: 'github',
|
|
704
|
+
type: 'issue',
|
|
705
|
+
summary: `Review issue #${issue.number}`,
|
|
706
|
+
ref: issue.url,
|
|
707
|
+
});
|
|
708
|
+
recentActivity.push({
|
|
709
|
+
source: 'github',
|
|
710
|
+
type: 'issue-update',
|
|
711
|
+
summary: `Issue #${issue.number} updated ${issue.updatedAt}`,
|
|
712
|
+
ref: issue.url,
|
|
713
|
+
});
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
if (repoIntel.available) {
|
|
717
|
+
const s = repoIntel.summary;
|
|
718
|
+
recentActivity.push({
|
|
719
|
+
source: 'repo-intel',
|
|
720
|
+
type: 'summary',
|
|
721
|
+
summary: `repo-intel: ${s.openIssueCount} open issues, ${s.mergeVelocity} PRs merged in last 30d, ${s.stalledIssues} stalled`,
|
|
722
|
+
});
|
|
723
|
+
if (s.stalledIssues > 0) {
|
|
724
|
+
openWork.push({
|
|
725
|
+
source: 'repo-intel',
|
|
726
|
+
type: 'stalled-issues',
|
|
727
|
+
summary: `${s.stalledIssues} open issue(s) with no activity in 30+ days`,
|
|
728
|
+
});
|
|
729
|
+
}
|
|
730
|
+
if (s.recurringLabels.length > 0) {
|
|
731
|
+
pendingDecisions.push({
|
|
732
|
+
source: 'repo-intel',
|
|
733
|
+
type: 'recurring-labels',
|
|
734
|
+
summary: `Recurring closed-issue labels (≥3 times): ${s.recurringLabels.slice(0, 5).join(', ')}`,
|
|
735
|
+
});
|
|
736
|
+
}
|
|
737
|
+
if (s.releaseCadence !== null) {
|
|
738
|
+
recentActivity.push({
|
|
739
|
+
source: 'repo-intel',
|
|
740
|
+
type: 'release-cadence',
|
|
741
|
+
summary: `Avg release cadence: ~${s.releaseCadence} day(s) between last 5 releases`,
|
|
742
|
+
});
|
|
743
|
+
}
|
|
744
|
+
}
|
|
745
|
+
return { openWork, recentActivity, pendingDecisions };
|
|
746
|
+
}
|
|
747
|
+
function renderContextAdf(snapshot) {
|
|
748
|
+
const lines = [];
|
|
749
|
+
lines.push('ADF: 0.1');
|
|
750
|
+
lines.push('ROLE: Live session snapshot for warm-start context');
|
|
751
|
+
lines.push('');
|
|
752
|
+
lines.push('STATE:');
|
|
753
|
+
lines.push(` GENERATED_AT: ${snapshot.generatedAt}`);
|
|
754
|
+
lines.push(` EXPIRES_AT: ${snapshot.expiresAt}`);
|
|
755
|
+
lines.push(` SOURCES_REQUESTED: ${snapshot.sourcesRequested.join(', ')}`);
|
|
756
|
+
lines.push(` SOURCES_USED: ${snapshot.sourcesUsed.join(', ') || '(none)'}`);
|
|
757
|
+
lines.push(` REPO_ROOT: ${snapshot.repo.name}`);
|
|
758
|
+
lines.push('');
|
|
759
|
+
lines.push('OPEN_WORK:');
|
|
760
|
+
if (snapshot.openWork.length > 0) {
|
|
761
|
+
for (const item of snapshot.openWork) {
|
|
762
|
+
lines.push(` - [${item.source}] ${item.summary}`);
|
|
763
|
+
}
|
|
764
|
+
}
|
|
765
|
+
else {
|
|
766
|
+
lines.push(' - none');
|
|
767
|
+
}
|
|
768
|
+
lines.push('');
|
|
769
|
+
lines.push('RECENT_ACTIVITY:');
|
|
770
|
+
if (snapshot.recentActivity.length > 0) {
|
|
771
|
+
for (const item of snapshot.recentActivity) {
|
|
772
|
+
lines.push(` - [${item.source}] ${item.summary}`);
|
|
773
|
+
}
|
|
774
|
+
}
|
|
775
|
+
else {
|
|
776
|
+
lines.push(' - none');
|
|
777
|
+
}
|
|
778
|
+
lines.push('');
|
|
779
|
+
lines.push('PENDING_DECISIONS:');
|
|
780
|
+
if (snapshot.pendingDecisions.length > 0) {
|
|
781
|
+
for (const item of snapshot.pendingDecisions) {
|
|
782
|
+
lines.push(` - [${item.source}] ${item.summary}`);
|
|
783
|
+
}
|
|
784
|
+
}
|
|
785
|
+
else {
|
|
786
|
+
lines.push(' - none');
|
|
787
|
+
}
|
|
788
|
+
if (snapshot.warnings.length > 0) {
|
|
789
|
+
lines.push('');
|
|
790
|
+
lines.push('WARNINGS:');
|
|
791
|
+
for (const warning of snapshot.warnings) {
|
|
792
|
+
lines.push(` - ${warning}`);
|
|
793
|
+
}
|
|
794
|
+
}
|
|
795
|
+
lines.push('');
|
|
796
|
+
return lines.join('\n');
|
|
797
|
+
}
|
|
798
|
+
function renderContextMarkdown(snapshot) {
|
|
799
|
+
const lines = [];
|
|
800
|
+
lines.push(`# Live Context — ${snapshot.generatedAt}`);
|
|
801
|
+
lines.push('');
|
|
802
|
+
lines.push('## Open Work');
|
|
803
|
+
if (snapshot.openWork.length > 0) {
|
|
804
|
+
for (const item of snapshot.openWork) {
|
|
805
|
+
lines.push(`- [${item.source}] ${item.summary}`);
|
|
806
|
+
}
|
|
807
|
+
}
|
|
808
|
+
else {
|
|
809
|
+
lines.push('- none');
|
|
810
|
+
}
|
|
811
|
+
lines.push('');
|
|
812
|
+
lines.push('## Recent Activity');
|
|
813
|
+
if (snapshot.recentActivity.length > 0) {
|
|
814
|
+
for (const item of snapshot.recentActivity) {
|
|
815
|
+
lines.push(`- [${item.source}] ${item.summary}`);
|
|
816
|
+
}
|
|
817
|
+
}
|
|
818
|
+
else {
|
|
819
|
+
lines.push('- none');
|
|
820
|
+
}
|
|
821
|
+
lines.push('');
|
|
822
|
+
lines.push('## Pending Decisions');
|
|
823
|
+
if (snapshot.pendingDecisions.length > 0) {
|
|
824
|
+
for (const item of snapshot.pendingDecisions) {
|
|
825
|
+
lines.push(`- [${item.source}] ${item.summary}`);
|
|
826
|
+
}
|
|
827
|
+
}
|
|
828
|
+
else {
|
|
829
|
+
lines.push('- none');
|
|
830
|
+
}
|
|
831
|
+
if (snapshot.warnings.length > 0) {
|
|
832
|
+
lines.push('');
|
|
833
|
+
lines.push('## Warnings');
|
|
834
|
+
for (const warning of snapshot.warnings) {
|
|
835
|
+
lines.push(`- ${warning}`);
|
|
836
|
+
}
|
|
837
|
+
}
|
|
838
|
+
lines.push('');
|
|
839
|
+
return lines.join('\n');
|
|
840
|
+
}
|
|
841
|
+
//# sourceMappingURL=context-refresh.js.map
|