fluffy-context 0.7.2 → 0.7.7
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/README.md +481 -175
- package/dist/src/agent/api.d.ts +8 -2
- package/dist/src/agent/api.js +148 -128
- package/dist/src/agent/index.d.ts +3 -1
- package/dist/src/agent/index.js +2 -0
- package/dist/src/cli/main.js +112 -9
- package/dist/src/cognition/journal.js +30 -1
- package/dist/src/cognition/projections.js +0 -0
- package/dist/src/cognition/types.d.ts +16 -2
- package/dist/src/cognition/usage-report.d.ts +40 -0
- package/dist/src/cognition/usage-report.js +232 -0
- package/dist/src/cognition/usage.d.ts +5 -0
- package/dist/src/cognition/usage.js +48 -0
- package/dist/src/compiler/compile.js +26 -3
- package/dist/src/compiler/expand.d.ts +2 -0
- package/dist/src/compiler/expand.js +187 -0
- package/dist/src/compiler/identity.d.ts +6 -0
- package/dist/src/compiler/identity.js +36 -0
- package/dist/src/compiler/sources.js +10 -14
- package/dist/src/compiler/types.d.ts +47 -1
- package/dist/src/mcp/server.js +186 -11
- package/dist/src/runtime/runtime.js +10 -0
- package/dist/src/runtime/types.d.ts +1 -1
- package/dist/src/version.d.ts +1 -1
- package/dist/src/version.js +1 -1
- package/package.json +1 -1
- package/skills/fluffy-context/SKILL.md +28 -2
package/dist/src/agent/api.d.ts
CHANGED
|
@@ -1,10 +1,16 @@
|
|
|
1
|
+
import { type UsageInterface } from '../cognition/usage.js';
|
|
2
|
+
import { type UsageReport, type UsageReportOptions } from '../cognition/usage-report.js';
|
|
1
3
|
import type { AgentLoadOptions, AgentLoadResult, AgentSaveResult, CheckpointInput, ContextSearchOptions, ContextSearchResult } from '../runtime/types.js';
|
|
2
4
|
import type { ContextOrientOptions, ContextOrientResult, ContextUseInput } from './types.js';
|
|
5
|
+
import type { ContextExpandInput, ContextExpandResult, ContextCompileOptions, ContextManifest } from '../compiler/types.js';
|
|
3
6
|
export declare function saveContext(startPath: string | undefined, input: CheckpointInput, options?: {
|
|
4
7
|
minSaveIntervalMs?: number;
|
|
5
8
|
}): Promise<AgentSaveResult>;
|
|
6
|
-
export declare function
|
|
7
|
-
export declare function
|
|
9
|
+
export declare function contextExpand(startPath: string | undefined, input: ContextExpandInput, interfaceName?: UsageInterface): Promise<ContextExpandResult>;
|
|
10
|
+
export declare function contextCompile(startPath: string | undefined, options?: ContextCompileOptions, interfaceName?: UsageInterface): Promise<ContextManifest>;
|
|
11
|
+
export declare function loadContext(startPath: string | undefined, options?: AgentLoadOptions, interfaceName?: UsageInterface): Promise<AgentLoadResult>;
|
|
12
|
+
export declare function contextOrient(startPath: string | undefined, options?: ContextOrientOptions, interfaceName?: UsageInterface | null): Promise<ContextOrientResult>;
|
|
13
|
+
export declare function contextUsageReport(startPath: string | undefined, options?: UsageReportOptions): Promise<UsageReport>;
|
|
8
14
|
export declare function recordContextUse(startPath: string | undefined, input: ContextUseInput): Promise<{
|
|
9
15
|
recorded: boolean;
|
|
10
16
|
eventId: string;
|
package/dist/src/agent/api.js
CHANGED
|
@@ -8,6 +8,10 @@ import { selectV2Orientation } from '../cognition/orient.js';
|
|
|
8
8
|
import { discoverV2Deadends, discoverV2Knowledge } from '../cognition/retrieval.js';
|
|
9
9
|
import { discoverDeadends, discoverKnowledge, listDeadends } from '../runtime/knowledge.js';
|
|
10
10
|
import { listNotes } from '../runtime/notes.js';
|
|
11
|
+
import { expandContext } from '../compiler/expand.js';
|
|
12
|
+
import { compileContext } from '../compiler/compile.js';
|
|
13
|
+
import { withUsageTelemetry } from '../cognition/usage.js';
|
|
14
|
+
import { buildUsageReport } from '../cognition/usage-report.js';
|
|
11
15
|
function normalized(value) {
|
|
12
16
|
return value.trim().toLocaleLowerCase();
|
|
13
17
|
}
|
|
@@ -138,147 +142,163 @@ export async function saveContext(startPath, input, options = {}) {
|
|
|
138
142
|
const projectRoot = await resolveProjectRoot(startPath);
|
|
139
143
|
return { projectRoot, result: await checkpoint(projectRoot, input, options) };
|
|
140
144
|
}
|
|
141
|
-
export async function
|
|
142
|
-
|
|
143
|
-
const result = await resume(projectRoot, options.contextId, options.maxChars ?? 4000);
|
|
144
|
-
const knowledge = options.query === undefined ? null : await discoverKnowledge(projectRoot, options.query, { scope: options.scope, maxChars: options.maxChars ?? 4000 });
|
|
145
|
-
const verifiedDeadends = await listDeadends(projectRoot);
|
|
146
|
-
return {
|
|
147
|
-
projectRoot,
|
|
148
|
-
context: result.context,
|
|
149
|
-
snapshot: result.snapshot,
|
|
150
|
-
resumeSummary: result.resumeSummary,
|
|
151
|
-
...(options.includeDetails ? { details: result.details } : {}),
|
|
152
|
-
knowledge,
|
|
153
|
-
verifiedDeadendIds: verifiedDeadends.map((item) => item.deadendId),
|
|
154
|
-
};
|
|
145
|
+
export async function contextExpand(startPath, input, interfaceName = 'agent') {
|
|
146
|
+
return withUsageTelemetry(startPath, 'expand', 'agent', input, () => resolveProjectRoot(startPath).then((projectRoot) => expandContext(projectRoot, input)), interfaceName);
|
|
155
147
|
}
|
|
156
|
-
export async function
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
const
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
148
|
+
export async function contextCompile(startPath, options = {}, interfaceName = 'agent') {
|
|
149
|
+
return withUsageTelemetry(startPath, 'compile', 'agent', options, () => compileContext(startPath, options), interfaceName);
|
|
150
|
+
}
|
|
151
|
+
export async function loadContext(startPath, options = {}, interfaceName = 'agent') {
|
|
152
|
+
return withUsageTelemetry(startPath, 'resume', 'agent', options, async () => {
|
|
153
|
+
const projectRoot = await resolveProjectRoot(startPath);
|
|
154
|
+
const result = await resume(projectRoot, options.contextId, options.maxChars ?? 4000);
|
|
155
|
+
const knowledge = options.query === undefined ? null : await discoverKnowledge(projectRoot, options.query, { scope: options.scope, maxChars: options.maxChars ?? 4000 });
|
|
156
|
+
const verifiedDeadends = await listDeadends(projectRoot);
|
|
157
|
+
return {
|
|
158
|
+
projectRoot,
|
|
159
|
+
context: result.context,
|
|
160
|
+
snapshot: result.snapshot,
|
|
161
|
+
resumeSummary: result.resumeSummary,
|
|
162
|
+
...(options.includeDetails ? { details: result.details } : {}),
|
|
163
|
+
knowledge,
|
|
164
|
+
verifiedDeadendIds: verifiedDeadends.map((item) => item.deadendId),
|
|
165
|
+
};
|
|
166
|
+
}, interfaceName);
|
|
167
|
+
}
|
|
168
|
+
export async function contextOrient(startPath, options = {}, interfaceName = 'agent') {
|
|
169
|
+
const action = async () => {
|
|
170
|
+
const projectRoot = await resolveProjectRoot(startPath);
|
|
171
|
+
const query = options.query === undefined ? null : options.query.trim();
|
|
172
|
+
if (query === '')
|
|
173
|
+
throw new Error('context orient query must not be empty');
|
|
174
|
+
let loaded;
|
|
175
|
+
try {
|
|
176
|
+
const v2 = await selectV2Orientation(projectRoot, options.contextId);
|
|
177
|
+
loaded = v2
|
|
178
|
+
? await resumeSnapshot(projectRoot, v2.contextId, v2.snapshotId, options.maxChars ?? 4000)
|
|
179
|
+
: await resume(projectRoot, options.contextId, options.maxChars ?? 4000, { touchLastUsedAt: false });
|
|
188
180
|
}
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
181
|
+
catch (error) {
|
|
182
|
+
if (error instanceof Error && error.message === 'no active context found') {
|
|
183
|
+
return {
|
|
184
|
+
status: 'no_context',
|
|
185
|
+
projectRoot,
|
|
186
|
+
query,
|
|
187
|
+
context: null,
|
|
188
|
+
snapshot: null,
|
|
189
|
+
resumeSummary: null,
|
|
190
|
+
knowledge: emptyKnowledge(query ?? ''),
|
|
191
|
+
deadends: emptyDeadends(query ?? ''),
|
|
192
|
+
notes: [],
|
|
193
|
+
truncated: false,
|
|
194
|
+
explanation: {
|
|
195
|
+
mode: 'v1-fallback',
|
|
196
|
+
selection: 'v1',
|
|
197
|
+
retrieval: { knowledge: {}, deadends: {} },
|
|
198
|
+
truncation: { summary: false, knowledge: false, deadends: false, notes: false },
|
|
199
|
+
},
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
throw error;
|
|
203
|
+
}
|
|
204
|
+
const v2 = await selectV2Orientation(projectRoot, options.contextId);
|
|
205
|
+
const notes = (await listNotes(projectRoot, {
|
|
206
|
+
contextId: loaded.context.id,
|
|
207
|
+
openOnly: true,
|
|
208
|
+
limit: v2 ? Number.MAX_SAFE_INTEGER : options.noteLimit ?? 20,
|
|
209
|
+
maxChars: options.maxChars ?? 4000,
|
|
210
|
+
})).filter((note) => !v2 || v2.noteIds.has(note.noteId)).slice(0, options.noteLimit ?? 20);
|
|
211
|
+
const v2Knowledge = query === null || !v2 ? null : discoverV2Knowledge(v2.retrieval, query, v2.currentGit, {
|
|
211
212
|
scope: options.scope,
|
|
212
213
|
limit: options.knowledgeLimit ?? 10,
|
|
213
214
|
maxChars: options.maxChars ?? 4000,
|
|
214
215
|
});
|
|
215
|
-
|
|
216
|
-
? emptyDeadends('')
|
|
217
|
-
: v2Deadends?.result ?? await discoverDeadends(projectRoot, query, {
|
|
216
|
+
const v2Deadends = query === null || !v2 ? null : discoverV2Deadends(v2.retrieval, query, v2.currentGit, {
|
|
218
217
|
scope: options.scope,
|
|
219
218
|
limit: options.deadendLimit ?? 10,
|
|
220
219
|
maxChars: options.maxChars ?? 4000,
|
|
221
220
|
});
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
221
|
+
const applicableKnowledge = query === null
|
|
222
|
+
? emptyKnowledge('')
|
|
223
|
+
: v2Knowledge?.result ?? await discoverKnowledge(projectRoot, query, {
|
|
224
|
+
scope: options.scope,
|
|
225
|
+
limit: options.knowledgeLimit ?? 10,
|
|
226
|
+
maxChars: options.maxChars ?? 4000,
|
|
227
|
+
});
|
|
228
|
+
const applicableDeadends = query === null
|
|
229
|
+
? emptyDeadends('')
|
|
230
|
+
: v2Deadends?.result ?? await discoverDeadends(projectRoot, query, {
|
|
231
|
+
scope: options.scope,
|
|
232
|
+
limit: options.deadendLimit ?? 10,
|
|
233
|
+
maxChars: options.maxChars ?? 4000,
|
|
234
|
+
});
|
|
235
|
+
const budget = { remaining: Math.max(0, options.maxChars ?? 4000) };
|
|
236
|
+
const resumeSummary = {
|
|
237
|
+
progressSummary: limited(loaded.resumeSummary.progressSummary, budget),
|
|
238
|
+
lastError: loaded.resumeSummary.lastError === null ? null : limited(loaded.resumeSummary.lastError, budget),
|
|
239
|
+
completed: limitedList(loaded.resumeSummary.completed, budget),
|
|
240
|
+
pendingTasks: limitedList(loaded.resumeSummary.pendingTasks, budget),
|
|
241
|
+
decisions: limitedList(loaded.resumeSummary.decisions, budget),
|
|
242
|
+
risks: limitedList(loaded.resumeSummary.risks, budget),
|
|
243
|
+
relatedFiles: limitedList(loaded.resumeSummary.relatedFiles, budget),
|
|
244
|
+
branchOrCommitDrift: loaded.resumeSummary.branchOrCommitDrift,
|
|
245
|
+
};
|
|
246
|
+
const resumeTruncated = textLength(resumeSummary.progressSummary) < textLength(loaded.resumeSummary.progressSummary)
|
|
247
|
+
|| textLength(resumeSummary.lastError) < textLength(loaded.resumeSummary.lastError)
|
|
248
|
+
|| textLength(resumeSummary.completed) < textLength(loaded.resumeSummary.completed)
|
|
249
|
+
|| textLength(resumeSummary.pendingTasks) < textLength(loaded.resumeSummary.pendingTasks)
|
|
250
|
+
|| textLength(resumeSummary.decisions) < textLength(loaded.resumeSummary.decisions)
|
|
251
|
+
|| textLength(resumeSummary.risks) < textLength(loaded.resumeSummary.risks)
|
|
252
|
+
|| textLength(resumeSummary.relatedFiles) < textLength(loaded.resumeSummary.relatedFiles);
|
|
253
|
+
const limitedKnowledgeResult = limitKnowledge(applicableKnowledge, budget);
|
|
254
|
+
const limitedDeadendResult = limitDeadends(applicableDeadends, budget);
|
|
255
|
+
const limitedNotesResult = limitNotes(notes, budget);
|
|
256
|
+
return {
|
|
257
|
+
status: 'ready',
|
|
258
|
+
projectRoot,
|
|
259
|
+
query,
|
|
260
|
+
context: {
|
|
261
|
+
id: loaded.context.id,
|
|
262
|
+
title: loaded.context.title,
|
|
263
|
+
status: loaded.context.status,
|
|
264
|
+
branch: loaded.context.branch,
|
|
265
|
+
commit: loaded.context.commit,
|
|
266
|
+
updatedAt: loaded.context.updatedAt,
|
|
267
|
+
},
|
|
268
|
+
snapshot: {
|
|
269
|
+
snapshotId: loaded.snapshot.snapshotId,
|
|
270
|
+
mode: loaded.snapshot.mode,
|
|
271
|
+
createdAt: loaded.snapshot.createdAt,
|
|
272
|
+
branch: loaded.snapshot.branch,
|
|
273
|
+
commit: loaded.snapshot.commit,
|
|
273
274
|
},
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
275
|
+
resumeSummary,
|
|
276
|
+
knowledge: limitedKnowledgeResult.result,
|
|
277
|
+
deadends: limitedDeadendResult.result,
|
|
278
|
+
notes: limitedNotesResult.notes,
|
|
279
|
+
truncated: resumeTruncated || limitedKnowledgeResult.truncated || limitedDeadendResult.truncated || limitedNotesResult.truncated,
|
|
280
|
+
explanation: {
|
|
281
|
+
mode: v2 ? 'v2' : 'v1-fallback',
|
|
282
|
+
selection: v2?.selection ?? 'v1',
|
|
283
|
+
retrieval: {
|
|
284
|
+
knowledge: v2Knowledge?.exclusions ?? {},
|
|
285
|
+
deadends: v2Deadends?.exclusions ?? {},
|
|
286
|
+
},
|
|
287
|
+
truncation: {
|
|
288
|
+
summary: resumeTruncated,
|
|
289
|
+
knowledge: limitedKnowledgeResult.truncated,
|
|
290
|
+
deadends: limitedDeadendResult.truncated,
|
|
291
|
+
notes: limitedNotesResult.truncated,
|
|
292
|
+
},
|
|
279
293
|
},
|
|
280
|
-
}
|
|
294
|
+
};
|
|
281
295
|
};
|
|
296
|
+
return interfaceName === null
|
|
297
|
+
? action()
|
|
298
|
+
: withUsageTelemetry(startPath, 'orient', 'agent', options, action, interfaceName);
|
|
299
|
+
}
|
|
300
|
+
export async function contextUsageReport(startPath, options = {}) {
|
|
301
|
+
return buildUsageReport(startPath, options);
|
|
282
302
|
}
|
|
283
303
|
export async function recordContextUse(startPath, input) {
|
|
284
304
|
return recordUse(startPath, input);
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
export * from './api.js';
|
|
2
2
|
export type { ContextOrientContext, ContextUseInput, ContextOrientExplanation, ContextOrientNoContextResult, ContextOrientOptions, ContextOrientReadyResult, ContextOrientResult, ContextOrientSnapshot, } from './types.js';
|
|
3
3
|
export type { AgentLoadOptions, AgentLoadResult, AgentSaveResult, ContextSearchHit, ContextSearchOptions, ContextSearchResult, } from '../runtime/types.js';
|
|
4
|
+
export { contextCompile } from './api.js';
|
|
4
5
|
export { compileContext } from '../compiler/compile.js';
|
|
5
|
-
export
|
|
6
|
+
export { canonicalJson, contentHash, estimateTokens, rebuildManifestHash, validateManifestIdentity } from '../compiler/identity.js';
|
|
7
|
+
export type { ContextActivityRecord, ContextAnchor, ContextBudget, ContextBudgetReport, ContextCandidate, ContextCandidateSection, ContextCompileOptions, ContextExpandInput, ContextExpandLevel, ContextExpandResult, ContextExclusion, ContextExclusionReason, ContextIntent, ContextIntentSignal, ContextLevel, ContextManifest, ContextProvenance, ContextScenario, ContextSourceDescriptor, ContextTruncation, } from '../compiler/types.js';
|
package/dist/src/agent/index.js
CHANGED
package/dist/src/cli/main.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { checkpoint
|
|
3
|
-
import { contextOrient } from '../agent/api.js';
|
|
4
|
-
import {
|
|
2
|
+
import { checkpoint } from '../runtime/runtime.js';
|
|
3
|
+
import { contextCompile, contextExpand, contextOrient, contextUsageReport, loadContext } from '../agent/api.js';
|
|
4
|
+
import { formatUsageReport } from '../cognition/usage-report.js';
|
|
5
5
|
import { importV1, verifyV1Import } from '../cognition/migration-v1.js';
|
|
6
6
|
import { recordUse } from '../cognition/feedback.js';
|
|
7
7
|
import { verifyJournal } from '../cognition/journal.js';
|
|
@@ -81,6 +81,34 @@ Initialize the local .context runtime layout. Re-running init is safe.
|
|
|
81
81
|
|
|
82
82
|
Options:
|
|
83
83
|
--path <path> Project path`,
|
|
84
|
+
usage: `usage: ctx usage report [options]
|
|
85
|
+
|
|
86
|
+
Show read-only context asset, reuse, token investment, and operation metrics.
|
|
87
|
+
|
|
88
|
+
Options:
|
|
89
|
+
--path <path> Project path
|
|
90
|
+
--since <ISO> Window start
|
|
91
|
+
--until <ISO> Window end
|
|
92
|
+
--days <number> Rolling window length (default: 30)
|
|
93
|
+
--month <YYYY-MM> Calendar month window
|
|
94
|
+
--granularity <value> day|week|month
|
|
95
|
+
--format <value> json|ascii (default: json)
|
|
96
|
+
--baseline-tokens <number> Explicit comparison token count
|
|
97
|
+
--baseline-source <value> manual|recorded-control`,
|
|
98
|
+
'usage report': `usage: ctx usage report [options]
|
|
99
|
+
|
|
100
|
+
Show read-only context asset, reuse, token investment, and operation metrics.
|
|
101
|
+
|
|
102
|
+
Options:
|
|
103
|
+
--path <path> Project path
|
|
104
|
+
--since <ISO> Window start
|
|
105
|
+
--until <ISO> Window end
|
|
106
|
+
--days <number> Rolling window length (default: 30)
|
|
107
|
+
--month <YYYY-MM> Calendar month window
|
|
108
|
+
--granularity <value> day|week|month
|
|
109
|
+
--format <value> json|ascii (default: json)
|
|
110
|
+
--baseline-tokens <number> Explicit comparison token count
|
|
111
|
+
--baseline-source <value> manual|recorded-control`,
|
|
84
112
|
checkpoint: `usage: ctx checkpoint [options]
|
|
85
113
|
|
|
86
114
|
Save structured work state as a baseline or incremental snapshot.
|
|
@@ -131,6 +159,27 @@ Options:
|
|
|
131
159
|
--knowledge-limit <number> Maximum knowledge matches (default: 10)
|
|
132
160
|
--deadend-limit <number> Maximum deadend matches (default: 10)
|
|
133
161
|
--note-limit <number> Maximum open notes (default: 20)
|
|
162
|
+
--activity-limit <number> Maximum recent activity groups (default: 20)`,
|
|
163
|
+
expand: `usage: ctx expand [options]
|
|
164
|
+
|
|
165
|
+
Expand one selected compiler candidate without writing project state.
|
|
166
|
+
|
|
167
|
+
Options:
|
|
168
|
+
--path <path> Project path
|
|
169
|
+
--manifest-hash <hash> Manifest identity hash from ctx compile
|
|
170
|
+
--candidate-id <id> Selected candidate ID
|
|
171
|
+
--item-hash <hash> Selected item hash
|
|
172
|
+
--level <level> structured|evidence
|
|
173
|
+
--max-chars <number> Maximum expanded characters
|
|
174
|
+
--compile-max-chars <number> Original compile budget (default: 4000)
|
|
175
|
+
--context <id> Context ID
|
|
176
|
+
--query <text> Original compile query
|
|
177
|
+
--scenario <scenario> resume|implementation|debugging|verification|handoff|exploration|unknown
|
|
178
|
+
--scope <scope> Exact consensus scope
|
|
179
|
+
--paths <paths> Comma-separated project-relative paths
|
|
180
|
+
--knowledge-limit <number> Maximum knowledge matches (default: 10)
|
|
181
|
+
--deadend-limit <number> Maximum deadend matches (default: 10)
|
|
182
|
+
--note-limit <number> Maximum open notes (default: 20)
|
|
134
183
|
--activity-limit <number> Maximum recent activity groups (default: 20)`,
|
|
135
184
|
agent: `usage: ctx agent serve
|
|
136
185
|
|
|
@@ -139,7 +188,7 @@ Start an MCP stdio server for Agent integrations.
|
|
|
139
188
|
Run "ctx agent serve --help" for details.`,
|
|
140
189
|
'agent serve': `usage: ctx agent serve
|
|
141
190
|
|
|
142
|
-
Start an MCP stdio server exposing
|
|
191
|
+
Start an MCP stdio server exposing context_orient, context_expand, context_compile, context_resume, context_note_list, context_note_add, context_usage_report, and context_checkpoint tools.
|
|
143
192
|
|
|
144
193
|
The server owns standard input/output; do not use it interactively.`,
|
|
145
194
|
hook: `usage: ctx hook claude-code session-start|user-prompt
|
|
@@ -341,7 +390,7 @@ Stop the local runtime through authenticated loopback IPC.`,
|
|
|
341
390
|
Internal runtime daemon entry point.`,
|
|
342
391
|
};
|
|
343
392
|
function usage() {
|
|
344
|
-
return `usage: ctx init|checkpoint|resume|orient|compile|agent|hook|integrate|status|doctor|learn|knowledge|deadend|deadends|note|activity|migrate|runtime|filesystem|feedback|journal [options]
|
|
393
|
+
return `usage: ctx init|checkpoint|resume|orient|compile|expand|usage|agent|hook|integrate|status|doctor|learn|knowledge|deadend|deadends|note|activity|migrate|runtime|filesystem|feedback|journal [options]
|
|
345
394
|
|
|
346
395
|
Run \"ctx <command> --help\" for command details.`;
|
|
347
396
|
}
|
|
@@ -370,7 +419,7 @@ function printHelp(args) {
|
|
|
370
419
|
|| (command === 'note' && ['add', 'list'].includes(args[1]))
|
|
371
420
|
|| (command === 'migrate' && ['v1', 'verify'].includes(args[1]))
|
|
372
421
|
|| (command === 'runtime' && ['enable', 'disable', 'start', 'stop', 'status', 'serve'].includes(args[1]))
|
|
373
|
-
|| (command === '
|
|
422
|
+
|| (command === 'usage' && args[1] === 'report');
|
|
374
423
|
const key = nested ? `${command} ${args[1]}` : command;
|
|
375
424
|
const nestedKey = key === 'integrate claude' && (args[2] === 'inspect' || args[2] === 'install') ? `${key} ${args[2]}` : key;
|
|
376
425
|
const help = HELP[nestedKey];
|
|
@@ -428,7 +477,7 @@ async function run(args) {
|
|
|
428
477
|
case 'resume':
|
|
429
478
|
validateOptions(args.slice(1), ['--path', '--context', '--max-chars'], ['--path', '--context', '--max-chars']);
|
|
430
479
|
validatePositionals(positionals(args.slice(1), ['--path', '--context', '--max-chars']), 0, HELP.resume);
|
|
431
|
-
print(await
|
|
480
|
+
print(await loadContext(target, { contextId: option(args, '--context'), maxChars: numericOption(args, '--max-chars', 4000) }, 'cli'));
|
|
432
481
|
return;
|
|
433
482
|
case 'orient': {
|
|
434
483
|
const valueOptions = ['--path', '--context', '--scope', '--max-chars', '--knowledge-limit', '--deadend-limit', '--note-limit'];
|
|
@@ -444,7 +493,7 @@ async function run(args) {
|
|
|
444
493
|
knowledgeLimit: numericOption(args, '--knowledge-limit', 10),
|
|
445
494
|
deadendLimit: numericOption(args, '--deadend-limit', 10),
|
|
446
495
|
noteLimit: numericOption(args, '--note-limit', 20),
|
|
447
|
-
}));
|
|
496
|
+
}, 'cli'));
|
|
448
497
|
return;
|
|
449
498
|
}
|
|
450
499
|
case 'compile': {
|
|
@@ -452,7 +501,7 @@ async function run(args) {
|
|
|
452
501
|
validateOptions(args.slice(1), valueOptions, valueOptions);
|
|
453
502
|
const query = positionals(args.slice(1), valueOptions);
|
|
454
503
|
validatePositionals(query, 1, HELP.compile);
|
|
455
|
-
print(await
|
|
504
|
+
print(await contextCompile(target, {
|
|
456
505
|
contextId: option(args, '--context'),
|
|
457
506
|
query: query[0],
|
|
458
507
|
scenario: enumOption(args, '--scenario', ['resume', 'implementation', 'debugging', 'verification', 'handoff', 'exploration', 'unknown']),
|
|
@@ -463,9 +512,63 @@ async function run(args) {
|
|
|
463
512
|
deadendLimit: numericOption(args, '--deadend-limit', 10),
|
|
464
513
|
noteLimit: numericOption(args, '--note-limit', 20),
|
|
465
514
|
activityLimit: numericOption(args, '--activity-limit', 20),
|
|
515
|
+
}, 'cli'));
|
|
516
|
+
return;
|
|
517
|
+
}
|
|
518
|
+
case 'expand': {
|
|
519
|
+
const valueOptions = ['--path', '--manifest-hash', '--candidate-id', '--item-hash', '--level', '--max-chars', '--compile-max-chars', '--context', '--query', '--scenario', '--scope', '--paths', '--knowledge-limit', '--deadend-limit', '--note-limit', '--activity-limit'];
|
|
520
|
+
validateOptions(args.slice(1), valueOptions, valueOptions);
|
|
521
|
+
validatePositionals(positionals(args.slice(1), valueOptions), 0, HELP.expand);
|
|
522
|
+
const level = enumOption(args, '--level', ['structured', 'evidence']);
|
|
523
|
+
if (!level)
|
|
524
|
+
throw new Error('--level is required');
|
|
525
|
+
const manifestHash = option(args, '--manifest-hash');
|
|
526
|
+
if (!manifestHash)
|
|
527
|
+
throw new Error('--manifest-hash is required');
|
|
528
|
+
const compileMaxChars = numericOption(args, '--compile-max-chars', 4000);
|
|
529
|
+
await ensureRuntimeForSession(target, { source: 'cli', timeoutMs: 1_000 }).catch(() => undefined);
|
|
530
|
+
print(await contextExpand(target, {
|
|
531
|
+
manifestHash,
|
|
532
|
+
candidateId: option(args, '--candidate-id'),
|
|
533
|
+
itemHash: option(args, '--item-hash'),
|
|
534
|
+
level: level,
|
|
535
|
+
maxChars: option(args, '--max-chars') === undefined ? undefined : numericOption(args, '--max-chars', 0),
|
|
536
|
+
compile: {
|
|
537
|
+
contextId: option(args, '--context'),
|
|
538
|
+
query: option(args, '--query'),
|
|
539
|
+
scenario: enumOption(args, '--scenario', ['resume', 'implementation', 'debugging', 'verification', 'handoff', 'exploration', 'unknown']),
|
|
540
|
+
scope: option(args, '--scope'),
|
|
541
|
+
paths: listOption(args, '--paths'),
|
|
542
|
+
maxChars: compileMaxChars,
|
|
543
|
+
knowledgeLimit: numericOption(args, '--knowledge-limit', 10),
|
|
544
|
+
deadendLimit: numericOption(args, '--deadend-limit', 10),
|
|
545
|
+
noteLimit: numericOption(args, '--note-limit', 20),
|
|
546
|
+
activityLimit: numericOption(args, '--activity-limit', 20),
|
|
547
|
+
},
|
|
466
548
|
}));
|
|
467
549
|
return;
|
|
468
550
|
}
|
|
551
|
+
case 'usage': {
|
|
552
|
+
if (args[1] !== 'report')
|
|
553
|
+
throw new Error(HELP.usage);
|
|
554
|
+
const valueOptions = ['--path', '--since', '--until', '--days', '--month', '--granularity', '--format', '--baseline-tokens', '--baseline-source'];
|
|
555
|
+
validateOptions(args.slice(2), valueOptions, valueOptions);
|
|
556
|
+
validatePositionals(positionals(args.slice(2), valueOptions), 0, HELP['usage report']);
|
|
557
|
+
const report = await contextUsageReport(target, {
|
|
558
|
+
since: option(args, '--since'),
|
|
559
|
+
until: option(args, '--until'),
|
|
560
|
+
days: option(args, '--days') === undefined ? undefined : numericOption(args, '--days', 30),
|
|
561
|
+
month: option(args, '--month'),
|
|
562
|
+
granularity: enumOption(args, '--granularity', ['day', 'week', 'month']),
|
|
563
|
+
baselineTokens: option(args, '--baseline-tokens') === undefined ? undefined : numericOption(args, '--baseline-tokens', 0),
|
|
564
|
+
baselineSource: enumOption(args, '--baseline-source', ['manual', 'recorded-control']),
|
|
565
|
+
});
|
|
566
|
+
if (enumOption(args, '--format', ['json', 'ascii']) === 'ascii')
|
|
567
|
+
process.stdout.write(formatUsageReport(report));
|
|
568
|
+
else
|
|
569
|
+
print(report);
|
|
570
|
+
return;
|
|
571
|
+
}
|
|
469
572
|
case 'agent':
|
|
470
573
|
if (args[1] !== 'serve')
|
|
471
574
|
throw new Error(HELP.agent);
|
|
@@ -11,9 +11,10 @@ const eventTypes = new Set([
|
|
|
11
11
|
'context.checkpoint.imported', 'context.state.recorded', 'note.recorded', 'note.resolved', 'note.absorbed',
|
|
12
12
|
'knowledge.proposed', 'knowledge.verified', 'knowledge.deprecated', 'knowledge.rejected',
|
|
13
13
|
'deadend.proposed', 'deadend.verified', 'deadend.obsoleted', 'deadend.rejected', 'record.used',
|
|
14
|
+
'usage.operation.completed',
|
|
14
15
|
'projection.advanced', 'projection.failed',
|
|
15
16
|
]);
|
|
16
|
-
const sourceKinds = new Set(['cli', 'claude-hook', 'mcp', 'git-observer', 'fs-observer', 'migration']);
|
|
17
|
+
const sourceKinds = new Set(['cli', 'claude-hook', 'mcp', 'agent', 'git-observer', 'fs-observer', 'migration']);
|
|
17
18
|
const segmentName = '00000001.ndjson';
|
|
18
19
|
function canonical(value) {
|
|
19
20
|
if (value === null || typeof value !== 'object')
|
|
@@ -55,6 +56,33 @@ function validGitAnchor(value) {
|
|
|
55
56
|
function validIsoDate(value) {
|
|
56
57
|
return typeof value === 'string' && Number.isFinite(Date.parse(value));
|
|
57
58
|
}
|
|
59
|
+
function validatePayloadBoundary(input) {
|
|
60
|
+
const payload = input.payload;
|
|
61
|
+
if (input.type === 'host.prompt.submitted' && (typeof payload.intent !== 'string' || payload.intent.trim().length === 0)) {
|
|
62
|
+
throw new Error('host prompt intent must not be empty');
|
|
63
|
+
}
|
|
64
|
+
if (['note.recorded', 'note.resolved', 'note.absorbed'].includes(input.type)) {
|
|
65
|
+
const record = payload.record;
|
|
66
|
+
if (!isRecord(record) || typeof record.message !== 'string' || record.message.trim().length === 0)
|
|
67
|
+
throw new Error('note message must not be empty');
|
|
68
|
+
}
|
|
69
|
+
if (input.type === 'usage.operation.completed') {
|
|
70
|
+
const requiredNumbers = ['durationMs', 'inputChars', 'outputChars', 'inputBytes', 'outputBytes', 'estimatedInputTokens', 'estimatedOutputTokens'];
|
|
71
|
+
if (payload.operation !== 'orient' && payload.operation !== 'compile' && payload.operation !== 'expand' && payload.operation !== 'resume')
|
|
72
|
+
throw new Error('invalid usage operation');
|
|
73
|
+
if (payload.interface !== 'cli' && payload.interface !== 'mcp' && payload.interface !== 'agent' && payload.interface !== 'hook')
|
|
74
|
+
throw new Error('invalid usage interface');
|
|
75
|
+
if (typeof payload.success !== 'boolean' || payload.estimatedMethod !== 'chars-div-four' || requiredNumbers.some((key) => typeof payload[key] !== 'number' || !Number.isFinite(payload[key]) || payload[key] < 0))
|
|
76
|
+
throw new Error('invalid usage telemetry payload');
|
|
77
|
+
if (!payload.success && typeof payload.errorClass !== 'string')
|
|
78
|
+
throw new Error('usage errors require an error class');
|
|
79
|
+
}
|
|
80
|
+
if (input.type === 'workspace.paths.changed') {
|
|
81
|
+
const changes = payload.changes;
|
|
82
|
+
if (!Array.isArray(changes) || changes.length === 0)
|
|
83
|
+
throw new Error('workspace activity must contain changes');
|
|
84
|
+
}
|
|
85
|
+
}
|
|
58
86
|
function validateEventShape(event, projectId, file, line) {
|
|
59
87
|
if (!isRecord(event)
|
|
60
88
|
|| event.schemaVersion !== 2
|
|
@@ -140,6 +168,7 @@ export async function verifyJournal(startPath) {
|
|
|
140
168
|
return readJournal(startPath, { allowPartialTail: false });
|
|
141
169
|
}
|
|
142
170
|
export async function appendEvent(startPath, input) {
|
|
171
|
+
validatePayloadBoundary(input);
|
|
143
172
|
const projectRoot = await resolveProjectRoot(startPath);
|
|
144
173
|
return withLock(`${locksDirectory(projectRoot)}/cognition-journal.lock`, async () => {
|
|
145
174
|
await recoverPartialTail(projectRoot);
|
|
Binary file
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export declare const COGNITION_LAYOUT_VERSION: "event-journal-v2";
|
|
2
|
-
export type CognitionEventSource = 'cli' | 'claude-hook' | 'mcp' | 'git-observer' | 'fs-observer' | 'migration';
|
|
3
|
-
export type CognitionEventType = 'runtime.enabled' | 'runtime.started' | 'runtime.stopped' | 'host.session.started' | 'host.prompt.submitted' | 'git.state.observed' | 'git.branch.changed' | 'git.head.changed' | 'workspace.paths.changed' | 'context.checkpoint.imported' | 'context.state.recorded' | 'note.recorded' | 'note.resolved' | 'note.absorbed' | 'knowledge.proposed' | 'knowledge.verified' | 'knowledge.deprecated' | 'knowledge.rejected' | 'deadend.proposed' | 'deadend.verified' | 'deadend.obsoleted' | 'deadend.rejected' | 'record.used' | 'projection.advanced' | 'projection.failed';
|
|
2
|
+
export type CognitionEventSource = 'cli' | 'claude-hook' | 'mcp' | 'agent' | 'git-observer' | 'fs-observer' | 'migration';
|
|
3
|
+
export type CognitionEventType = 'runtime.enabled' | 'runtime.started' | 'runtime.stopped' | 'host.session.started' | 'host.prompt.submitted' | 'git.state.observed' | 'git.branch.changed' | 'git.head.changed' | 'workspace.paths.changed' | 'context.checkpoint.imported' | 'context.state.recorded' | 'note.recorded' | 'note.resolved' | 'note.absorbed' | 'knowledge.proposed' | 'knowledge.verified' | 'knowledge.deprecated' | 'knowledge.rejected' | 'deadend.proposed' | 'deadend.verified' | 'deadend.obsoleted' | 'deadend.rejected' | 'record.used' | 'usage.operation.completed' | 'projection.advanced' | 'projection.failed';
|
|
4
4
|
export interface CognitionGitAnchor {
|
|
5
5
|
worktreeId: string | null;
|
|
6
6
|
branch: string | null;
|
|
@@ -67,6 +67,20 @@ export interface CognitionUseFeedback {
|
|
|
67
67
|
entityId: string;
|
|
68
68
|
outcome: 'used';
|
|
69
69
|
}
|
|
70
|
+
export interface UsageOperationCompletedPayload {
|
|
71
|
+
operation: 'orient' | 'compile' | 'expand' | 'resume';
|
|
72
|
+
interface: 'cli' | 'mcp' | 'agent' | 'hook';
|
|
73
|
+
success: boolean;
|
|
74
|
+
durationMs: number;
|
|
75
|
+
inputChars: number;
|
|
76
|
+
outputChars: number;
|
|
77
|
+
inputBytes: number;
|
|
78
|
+
outputBytes: number;
|
|
79
|
+
estimatedInputTokens: number;
|
|
80
|
+
estimatedOutputTokens: number;
|
|
81
|
+
estimatedMethod: 'chars-div-four';
|
|
82
|
+
errorClass?: string;
|
|
83
|
+
}
|
|
70
84
|
export interface CognitionRetrievalEntry {
|
|
71
85
|
entity: 'knowledge' | 'deadend';
|
|
72
86
|
entityId: string;
|