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
|
@@ -93,7 +93,7 @@ export async function loadCompilerSources(startPath, options) {
|
|
|
93
93
|
knowledgeLimit: options.knowledgeLimit ?? 10,
|
|
94
94
|
deadendLimit: options.deadendLimit ?? 10,
|
|
95
95
|
noteLimit: options.noteLimit ?? 20,
|
|
96
|
-
});
|
|
96
|
+
}, null);
|
|
97
97
|
const projectRoot = orientation.projectRoot;
|
|
98
98
|
const git = await readGitWorktreeState(projectRoot);
|
|
99
99
|
let events = [];
|
|
@@ -110,21 +110,17 @@ export async function loadCompilerSources(startPath, options) {
|
|
|
110
110
|
return { orientation, anchor, events, workspace, fallbackReason };
|
|
111
111
|
}
|
|
112
112
|
export function latestHostIntent(events, anchor) {
|
|
113
|
-
|
|
114
|
-
if (
|
|
113
|
+
const event = [...events].reverse().find((candidate) => {
|
|
114
|
+
if (candidate.type !== 'host.prompt.submitted')
|
|
115
115
|
return false;
|
|
116
|
-
if (
|
|
116
|
+
if (candidate.git.worktreeId !== null && candidate.git.worktreeId !== anchor.worktreeId)
|
|
117
117
|
return false;
|
|
118
|
-
if (
|
|
118
|
+
if (candidate.git.ref !== null && candidate.git.ref !== anchor.ref)
|
|
119
119
|
return false;
|
|
120
|
-
const payload =
|
|
120
|
+
const payload = candidate.payload;
|
|
121
121
|
return typeof payload.intent === 'string' && payload.intent.trim().length > 0;
|
|
122
|
-
})
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
return null;
|
|
127
|
-
return { value: event.payload.intent, eventId: event.eventId };
|
|
128
|
-
})()
|
|
129
|
-
: null;
|
|
122
|
+
});
|
|
123
|
+
if (!event)
|
|
124
|
+
return null;
|
|
125
|
+
return { value: event.payload.intent, eventId: event.eventId };
|
|
130
126
|
}
|
|
@@ -2,6 +2,8 @@ import type { CognitionGitAnchor, WorkspacePathChange } from '../cognition/types
|
|
|
2
2
|
import type { ContextOrientResult } from '../agent/types.js';
|
|
3
3
|
import type { Deadend, Knowledge, Note, ResumeSummary, StructuredContext } from '../runtime/types.js';
|
|
4
4
|
export type ContextScenario = 'resume' | 'implementation' | 'debugging' | 'verification' | 'handoff' | 'exploration' | 'unknown';
|
|
5
|
+
export type ContextLevel = 'signal' | 'summary' | 'structured' | 'evidence';
|
|
6
|
+
export type ContextExpandLevel = 'structured' | 'evidence';
|
|
5
7
|
export type ContextSourceKind = 'current-context' | 'checkpoint' | 'note' | 'journal' | 'knowledge' | 'deadend';
|
|
6
8
|
export type ContextCandidateSection = 'summary' | 'pending' | 'decision' | 'risk' | 'note' | 'knowledge' | 'deadend' | 'activity';
|
|
7
9
|
export type ContextSignalOrigin = 'explicit' | 'deterministic';
|
|
@@ -41,6 +43,12 @@ export interface ContextBudget {
|
|
|
41
43
|
reservedForRenderer: number;
|
|
42
44
|
sections?: Partial<Record<ContextCandidateSection, number>>;
|
|
43
45
|
}
|
|
46
|
+
export interface ContextTokenEstimate {
|
|
47
|
+
input: number;
|
|
48
|
+
output: number;
|
|
49
|
+
unit: 'estimated-tokens';
|
|
50
|
+
method: 'chars-div-four';
|
|
51
|
+
}
|
|
44
52
|
export interface ContextSourceApplicability {
|
|
45
53
|
project: true;
|
|
46
54
|
branchMode: 'global' | 'exact' | 'unsupported';
|
|
@@ -100,6 +108,10 @@ export interface ContextCandidate<T = unknown> {
|
|
|
100
108
|
selectedChars: number;
|
|
101
109
|
selected: boolean;
|
|
102
110
|
truncated: boolean;
|
|
111
|
+
level: ContextLevel;
|
|
112
|
+
itemHash: string;
|
|
113
|
+
tokenEstimate: number;
|
|
114
|
+
expandable: boolean;
|
|
103
115
|
}
|
|
104
116
|
export type ContextExclusionReason = 'no_active_context' | 'context_mismatch' | 'status_not_verified' | 'not_open' | 'absorbed' | 'applicability_mismatch' | 'scope_mismatch' | 'path_mismatch' | 'query_no_match' | 'result_limit' | 'budget' | 'unsupported_applicability' | 'v2_unavailable';
|
|
105
117
|
export interface ContextExclusion {
|
|
@@ -138,6 +150,11 @@ export interface ContextManifestProvenance {
|
|
|
138
150
|
selectedEventIds: string[];
|
|
139
151
|
fallbackReason: string | null;
|
|
140
152
|
}
|
|
153
|
+
export interface ContextManifestIdentity {
|
|
154
|
+
manifestHash: string;
|
|
155
|
+
selectedItemHashes: string[];
|
|
156
|
+
tokenEstimate: ContextTokenEstimate;
|
|
157
|
+
}
|
|
141
158
|
export interface ContextCompileOptions {
|
|
142
159
|
contextId?: string;
|
|
143
160
|
query?: string;
|
|
@@ -154,9 +171,37 @@ export interface ContextCompileOptions {
|
|
|
154
171
|
sessionId?: string;
|
|
155
172
|
eventId?: string;
|
|
156
173
|
}
|
|
174
|
+
export interface ContextExpandInput {
|
|
175
|
+
manifestHash: string;
|
|
176
|
+
candidateId?: string;
|
|
177
|
+
itemHash?: string;
|
|
178
|
+
level: ContextExpandLevel;
|
|
179
|
+
maxChars?: number;
|
|
180
|
+
compile: ContextCompileOptions;
|
|
181
|
+
}
|
|
182
|
+
export interface ContextExpandResult {
|
|
183
|
+
status: 'complete' | 'partial';
|
|
184
|
+
projectRoot: string;
|
|
185
|
+
manifestHash: string;
|
|
186
|
+
candidateId: string;
|
|
187
|
+
itemHash: string;
|
|
188
|
+
previousLevel: ContextLevel;
|
|
189
|
+
requestedLevel: ContextExpandLevel;
|
|
190
|
+
resolvedLevel: ContextExpandLevel;
|
|
191
|
+
source: ContextSourceDescriptor;
|
|
192
|
+
content: unknown | null;
|
|
193
|
+
text: string;
|
|
194
|
+
budget: {
|
|
195
|
+
requestedChars: number;
|
|
196
|
+
usedChars: number;
|
|
197
|
+
remainingChars: number;
|
|
198
|
+
truncated: boolean;
|
|
199
|
+
};
|
|
200
|
+
provenance: ContextManifestProvenance;
|
|
201
|
+
}
|
|
157
202
|
export interface ContextManifest {
|
|
158
203
|
schemaVersion: 1;
|
|
159
|
-
compilerVersion: '0.7.
|
|
204
|
+
compilerVersion: '0.7.7';
|
|
160
205
|
rankingVersion: 'compiler-ranking-v2';
|
|
161
206
|
status: 'ready' | 'no_context';
|
|
162
207
|
projectRoot: string;
|
|
@@ -178,6 +223,7 @@ export interface ContextManifest {
|
|
|
178
223
|
};
|
|
179
224
|
selected: ContextCandidate[];
|
|
180
225
|
excluded: ContextExclusion[];
|
|
226
|
+
identity: ContextManifestIdentity;
|
|
181
227
|
budget: ContextBudgetReport;
|
|
182
228
|
truncation: ContextTruncation;
|
|
183
229
|
provenance: ContextManifestProvenance;
|
package/dist/src/mcp/server.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { McpServer } from '@modelcontextprotocol/server';
|
|
2
2
|
import { z } from 'zod';
|
|
3
|
-
import {
|
|
3
|
+
import { addNote, listNotes } from '../runtime/notes.js';
|
|
4
|
+
import { contextCompile, contextExpand, contextOrient, contextUsageReport, loadContext, saveContext } from '../agent/api.js';
|
|
4
5
|
import { ensureRuntimeForSession } from '../cognition/runtime.js';
|
|
5
6
|
import { VERSION } from '../version.js';
|
|
6
7
|
const orientInput = z.object({
|
|
@@ -13,29 +14,203 @@ const orientInput = z.object({
|
|
|
13
14
|
deadendLimit: z.int().nonnegative().optional(),
|
|
14
15
|
noteLimit: z.int().nonnegative().optional(),
|
|
15
16
|
});
|
|
17
|
+
const scenario = z.enum(['resume', 'implementation', 'debugging', 'verification', 'handoff', 'exploration', 'unknown']);
|
|
18
|
+
const trigger = z.enum(['session-start', 'prompt', 'manual', 'tool-call']);
|
|
19
|
+
const noteKind = z.enum(['problem', 'action', 'observation', 'decision']);
|
|
20
|
+
const noteActor = z.enum(['agent', 'human']);
|
|
21
|
+
const noteStatus = z.enum(['open', 'resolved']);
|
|
22
|
+
const compileOptions = z.object({
|
|
23
|
+
contextId: z.string().optional(),
|
|
24
|
+
query: z.string().optional(),
|
|
25
|
+
scenario: scenario.optional(),
|
|
26
|
+
scope: z.string().optional(),
|
|
27
|
+
paths: z.array(z.string()).optional(),
|
|
28
|
+
maxChars: z.int().nonnegative().optional(),
|
|
29
|
+
knowledgeLimit: z.int().nonnegative().optional(),
|
|
30
|
+
deadendLimit: z.int().nonnegative().optional(),
|
|
31
|
+
noteLimit: z.int().nonnegative().optional(),
|
|
32
|
+
activityLimit: z.int().nonnegative().optional(),
|
|
33
|
+
trigger: trigger.optional(),
|
|
34
|
+
adapter: z.string().optional(),
|
|
35
|
+
sessionId: z.string().optional(),
|
|
36
|
+
eventId: z.string().optional(),
|
|
37
|
+
});
|
|
38
|
+
const expandInput = z.object({
|
|
39
|
+
path: z.string().optional(),
|
|
40
|
+
manifestHash: z.string(),
|
|
41
|
+
candidateId: z.string().optional(),
|
|
42
|
+
itemHash: z.string().optional(),
|
|
43
|
+
level: z.enum(['structured', 'evidence']),
|
|
44
|
+
maxChars: z.int().nonnegative().optional(),
|
|
45
|
+
compile: compileOptions,
|
|
46
|
+
});
|
|
47
|
+
const compileInput = z.object({ path: z.string().optional(), ...compileOptions.shape });
|
|
48
|
+
const resumeInput = z.object({
|
|
49
|
+
path: z.string().optional(),
|
|
50
|
+
contextId: z.string().optional(),
|
|
51
|
+
maxChars: z.int().nonnegative().optional(),
|
|
52
|
+
includeDetails: z.boolean().optional(),
|
|
53
|
+
query: z.string().optional(),
|
|
54
|
+
scope: z.string().optional(),
|
|
55
|
+
});
|
|
56
|
+
const noteListInput = z.object({
|
|
57
|
+
path: z.string().optional(),
|
|
58
|
+
contextId: z.string().optional(),
|
|
59
|
+
openOnly: z.boolean().optional(),
|
|
60
|
+
limit: z.int().nonnegative().optional(),
|
|
61
|
+
maxChars: z.int().nonnegative().optional(),
|
|
62
|
+
});
|
|
63
|
+
const noteAddInput = z.object({
|
|
64
|
+
path: z.string().optional(),
|
|
65
|
+
message: z.string(),
|
|
66
|
+
kind: noteKind.optional(),
|
|
67
|
+
actor: noteActor.optional(),
|
|
68
|
+
contextId: z.string().optional(),
|
|
69
|
+
snapshotId: z.string().optional(),
|
|
70
|
+
relatedFiles: z.array(z.string()).optional(),
|
|
71
|
+
status: noteStatus.optional(),
|
|
72
|
+
});
|
|
73
|
+
const checkpointInput = z.object({
|
|
74
|
+
path: z.string().optional(),
|
|
75
|
+
contextId: z.string().optional(),
|
|
76
|
+
absorbNotes: z.boolean().optional(),
|
|
77
|
+
title: z.string().optional(),
|
|
78
|
+
progressSummary: z.string().optional(),
|
|
79
|
+
lastError: z.string().nullable().optional(),
|
|
80
|
+
completed: z.array(z.string()).optional(),
|
|
81
|
+
pendingTasks: z.array(z.string()).optional(),
|
|
82
|
+
decisions: z.array(z.string()).optional(),
|
|
83
|
+
risks: z.array(z.string()).optional(),
|
|
84
|
+
relatedFiles: z.array(z.string()).optional(),
|
|
85
|
+
});
|
|
86
|
+
const usageReportInput = z.object({
|
|
87
|
+
path: z.string().optional(),
|
|
88
|
+
since: z.string().optional(),
|
|
89
|
+
until: z.string().optional(),
|
|
90
|
+
days: z.int().nonnegative().optional(),
|
|
91
|
+
month: z.string().optional(),
|
|
92
|
+
granularity: z.enum(['day', 'week', 'month']).optional(),
|
|
93
|
+
baselineTokens: z.number().nonnegative().optional(),
|
|
94
|
+
baselineSource: z.enum(['manual', 'recorded-control']).optional(),
|
|
95
|
+
});
|
|
16
96
|
function errorMessage(error) {
|
|
17
97
|
return error instanceof Error ? error.message : String(error);
|
|
18
98
|
}
|
|
99
|
+
function successResponse(result) {
|
|
100
|
+
return {
|
|
101
|
+
content: [{ type: 'text', text: JSON.stringify(result) }],
|
|
102
|
+
structuredContent: result,
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
function failureResponse(tool, error) {
|
|
106
|
+
return {
|
|
107
|
+
isError: true,
|
|
108
|
+
content: [{ type: 'text', text: `${tool} failed: ${errorMessage(error)}` }],
|
|
109
|
+
};
|
|
110
|
+
}
|
|
19
111
|
export function createMcpServer() {
|
|
20
112
|
const server = new McpServer({ name: 'fluffy-context', version: VERSION });
|
|
21
113
|
server.registerTool('context_orient', {
|
|
22
114
|
title: 'Orient context',
|
|
23
|
-
description: 'Load bounded, read-only task context with verified consensus and open notes.',
|
|
115
|
+
description: 'Load bounded, read-only task context with verified consensus and open notes. Compiler manifests expose deterministic levels, hashes, and estimated tokens through the Agent API and CLI.',
|
|
24
116
|
inputSchema: orientInput,
|
|
25
117
|
}, async ({ path, ...options }) => {
|
|
26
118
|
try {
|
|
27
119
|
await ensureRuntimeForSession(path, { source: 'mcp', timeoutMs: 1_000 }).catch(() => undefined);
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
120
|
+
return successResponse(await contextOrient(path, options, 'mcp'));
|
|
121
|
+
}
|
|
122
|
+
catch (error) {
|
|
123
|
+
return failureResponse('context_orient', error);
|
|
124
|
+
}
|
|
125
|
+
});
|
|
126
|
+
server.registerTool('context_expand', {
|
|
127
|
+
title: 'Expand context candidate',
|
|
128
|
+
description: 'Read a selected compiler candidate at structured or evidence level without writing project state or reading file contents.',
|
|
129
|
+
inputSchema: expandInput,
|
|
130
|
+
}, async ({ path, ...input }) => {
|
|
131
|
+
try {
|
|
132
|
+
await ensureRuntimeForSession(path, { source: 'mcp', timeoutMs: 1_000 }).catch(() => undefined);
|
|
133
|
+
return successResponse(await contextExpand(path, input, 'mcp'));
|
|
134
|
+
}
|
|
135
|
+
catch (error) {
|
|
136
|
+
return failureResponse('context_expand', error);
|
|
137
|
+
}
|
|
138
|
+
});
|
|
139
|
+
server.registerTool('context_compile', {
|
|
140
|
+
title: 'Compile context manifest',
|
|
141
|
+
description: 'Compile a bounded, deterministic, read-only context manifest without writing project state or reading file contents.',
|
|
142
|
+
inputSchema: compileInput,
|
|
143
|
+
}, async ({ path, ...options }) => {
|
|
144
|
+
try {
|
|
145
|
+
await ensureRuntimeForSession(path, { source: 'mcp', timeoutMs: 1_000 }).catch(() => undefined);
|
|
146
|
+
return successResponse(await contextCompile(path, options, 'mcp'));
|
|
147
|
+
}
|
|
148
|
+
catch (error) {
|
|
149
|
+
return failureResponse('context_compile', error);
|
|
150
|
+
}
|
|
151
|
+
});
|
|
152
|
+
server.registerTool('context_resume', {
|
|
153
|
+
title: 'Resume context',
|
|
154
|
+
description: 'Resume the selected active context with a bounded summary and optional details. This may update the existing lastUsedAt field.',
|
|
155
|
+
inputSchema: resumeInput,
|
|
156
|
+
}, async ({ path, ...options }) => {
|
|
157
|
+
try {
|
|
158
|
+
await ensureRuntimeForSession(path, { source: 'mcp', timeoutMs: 1_000 }).catch(() => undefined);
|
|
159
|
+
return successResponse(await loadContext(path, options, 'mcp'));
|
|
160
|
+
}
|
|
161
|
+
catch (error) {
|
|
162
|
+
return failureResponse('context_resume', error);
|
|
163
|
+
}
|
|
164
|
+
});
|
|
165
|
+
server.registerTool('context_note_list', {
|
|
166
|
+
title: 'List context notes',
|
|
167
|
+
description: 'List bounded notes without modifying project state or absorbing or resolving notes.',
|
|
168
|
+
inputSchema: noteListInput,
|
|
169
|
+
}, async ({ path, ...query }) => {
|
|
170
|
+
try {
|
|
171
|
+
await ensureRuntimeForSession(path, { source: 'mcp', timeoutMs: 1_000 }).catch(() => undefined);
|
|
172
|
+
return successResponse({ notes: await listNotes(path, query) });
|
|
173
|
+
}
|
|
174
|
+
catch (error) {
|
|
175
|
+
return failureResponse('context_note_list', error);
|
|
176
|
+
}
|
|
177
|
+
});
|
|
178
|
+
server.registerTool('context_note_add', {
|
|
179
|
+
title: 'Add context note',
|
|
180
|
+
description: 'Append one explicit note to project state. This writes the local context store but does not create a Snapshot.',
|
|
181
|
+
inputSchema: noteAddInput,
|
|
182
|
+
}, async ({ path, message, ...input }) => {
|
|
183
|
+
try {
|
|
184
|
+
await ensureRuntimeForSession(path, { source: 'mcp', timeoutMs: 1_000 }).catch(() => undefined);
|
|
185
|
+
return successResponse(await addNote(path, message, input));
|
|
186
|
+
}
|
|
187
|
+
catch (error) {
|
|
188
|
+
return failureResponse('context_note_add', error);
|
|
189
|
+
}
|
|
190
|
+
});
|
|
191
|
+
server.registerTool('context_usage_report', {
|
|
192
|
+
title: 'Context usage report',
|
|
193
|
+
description: 'Build a read-only report of context assets, explicit reuse, estimated token investment, and operation metrics.',
|
|
194
|
+
inputSchema: usageReportInput,
|
|
195
|
+
}, async ({ path, ...options }) => {
|
|
196
|
+
try {
|
|
197
|
+
return successResponse(await contextUsageReport(path, options));
|
|
198
|
+
}
|
|
199
|
+
catch (error) {
|
|
200
|
+
return failureResponse('context_usage_report', error);
|
|
201
|
+
}
|
|
202
|
+
});
|
|
203
|
+
server.registerTool('context_checkpoint', {
|
|
204
|
+
title: 'Checkpoint context',
|
|
205
|
+
description: 'Explicitly save context progress and create a Snapshot when changes are present; runtime rate limits remain enforced.',
|
|
206
|
+
inputSchema: checkpointInput,
|
|
207
|
+
}, async ({ path, ...input }) => {
|
|
208
|
+
try {
|
|
209
|
+
await ensureRuntimeForSession(path, { source: 'mcp', timeoutMs: 1_000 }).catch(() => undefined);
|
|
210
|
+
return successResponse(await saveContext(path, input));
|
|
33
211
|
}
|
|
34
212
|
catch (error) {
|
|
35
|
-
return
|
|
36
|
-
isError: true,
|
|
37
|
-
content: [{ type: 'text', text: `context_orient failed: ${errorMessage(error)}` }],
|
|
38
|
-
};
|
|
213
|
+
return failureResponse('context_checkpoint', error);
|
|
39
214
|
}
|
|
40
215
|
});
|
|
41
216
|
return server;
|
|
@@ -253,6 +253,16 @@ export async function checkpoint(startPath, input, options = {}) {
|
|
|
253
253
|
}
|
|
254
254
|
const content = await contextFromInput(projectRoot, input, previous);
|
|
255
255
|
const patch = diffContext(previous, content);
|
|
256
|
+
const hasContent = content.progressSummary.length > 0
|
|
257
|
+
|| content.lastError !== null
|
|
258
|
+
|| content.completed.length > 0
|
|
259
|
+
|| content.pendingTasks.length > 0
|
|
260
|
+
|| content.decisions.length > 0
|
|
261
|
+
|| content.risks.length > 0
|
|
262
|
+
|| content.relatedFiles.length > 0;
|
|
263
|
+
if (!hasContent && !previousSnapshot) {
|
|
264
|
+
return { status: 'no_change', context, snapshot: null };
|
|
265
|
+
}
|
|
256
266
|
if (previousSnapshot && Object.keys(patch).length === 0)
|
|
257
267
|
return { status: 'no_change', context, snapshot: previousSnapshot };
|
|
258
268
|
const minSaveIntervalMs = options.minSaveIntervalMs ?? 10_000;
|
package/dist/src/version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const VERSION = "0.7.
|
|
1
|
+
export declare const VERSION = "0.7.7";
|
package/dist/src/version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const VERSION = '0.7.
|
|
1
|
+
export const VERSION = '0.7.7';
|
package/package.json
CHANGED
|
@@ -19,9 +19,16 @@ compatibility: 需要 Node.js >=20.19.0;Git 可选。CLI 通过 npm 全局安
|
|
|
19
19
|
- 仅需要完整详情时再运行 `ctx resume --max-chars <预算>`;它会保留既有的 `lastUsedAt` 更新语义。
|
|
20
20
|
- Agent 集成从 `fluffy-context` 或 `fluffy-context/agent` 导入 `contextOrient`、`compileContext`、`saveContext`、`loadContext` 和 `searchContext`,不应直接读写 `.context` 或导入内部 `dist/...` 路径。
|
|
21
21
|
- `ctx compile` 的 `activity` section 只表示 Runtime 观察到的近期文件 metadata 变化;它不代表文件被读取或理解,也不替代 checkpoint、Note 或已验证 Knowledge。
|
|
22
|
+
- 0.7.7 的 Compiler Manifest 提供稳定 `identity.manifestHash`、候选 `itemHash`、`tokenEstimate` 和 `level` 契约;默认输出 `summary` 层。
|
|
23
|
+
- 只对 selected candidate 使用 `ctx expand --manifest-hash ... --candidate-id ... --level structured|evidence`。必须带上原始 compile 参数和相同的 compile budget;Manifest 过期时应重新 compile,不要绕过 hash 校验。
|
|
24
|
+
- `expand` 是无状态、只读的渐进式展开:`complete` 才提供完整 `content`,`partial` 提供有界 `text` 且 `content` 为 null。它不写入 Context/journal、不记录 feedback、不读取项目文件正文、diff、命令输出或敏感数据。Agent API 使用 `contextExpand`,MCP 使用 `context_expand`。
|
|
22
25
|
- 新任务先发现已验证共识,再开始实现;候选项只在显式审查时使用。
|
|
23
26
|
- 普通查询只使用已验证 Knowledge 和 Deadend;需要审查候选项时显式使用 `--all`。
|
|
24
27
|
- CLI 业务命令的标准输出是 JSON;错误写入标准错误并返回非零退出码。解析输出时不要把 `--help` 的纯文本当作 JSON。
|
|
28
|
+
- 每周或每月复盘时使用 `ctx usage report`,不要为每条 prompt 运行;它只读 journal 和 v1 数据,不创建事件或修改项目状态。
|
|
29
|
+
- Report 中的 `chars / 4` 是明确标注的 estimated token,不是真实 Provider token;Provider token 和计费金额当前 unavailable。
|
|
30
|
+
- 只有显式 `record.used` 才算 Knowledge/Deadend reuse;compile 命中或 orient 返回不等于复用。
|
|
31
|
+
- 没有显式 baseline 时,report 不生成 savings、ROI 或 payback 数字;report 不能替代 `doctor`、`journal verify` 或人工治理。
|
|
25
32
|
|
|
26
33
|
## 快速工作流
|
|
27
34
|
|
|
@@ -43,6 +50,21 @@ ctx learn ... / ctx deadend ... → 显式 verify
|
|
|
43
50
|
|
|
44
51
|
`ctx orient` 不带查询时也有效,用于恢复当前任务和 open Note。带查询时只发现已验证的 Knowledge 与 Deadend;不要以候选项驱动普通实现。
|
|
45
52
|
|
|
53
|
+
### Usage report
|
|
54
|
+
|
|
55
|
+
在阶段性复盘、周报或月报时运行只读观测报告:
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
ctx usage report --days 30
|
|
59
|
+
ctx usage report --month 2026-08 --granularity week
|
|
60
|
+
ctx usage report --since 2026-08-01T00:00:00.000Z --until 2026-09-01T00:00:00.000Z --baseline-tokens 12000 --baseline-source manual
|
|
61
|
+
ctx usage report --format ascii
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
JSON 是默认输出;`--format ascii` 仅用于终端展示。报告分别统计 v1 文件资产和 v2 journal 事件,展示 Knowledge、Deadend、Note、Context、Snapshot、显式 reuse、操作趋势与 estimated token 投入。`record.used` 是唯一的 reuse 信号,候选项命中、compile 选择和普通检索都不算复用。
|
|
65
|
+
|
|
66
|
+
`--baseline-tokens` 必须是显式对照输入。未提供 baseline 时 savings、ROI 和 payback 为 `null` 或 `no_baseline`;字符换算不代表 Provider 真实 token,也不提供默认单价。报告失败时应使用 `ctx doctor` 与 `ctx journal verify` 进一步诊断,而不是把 report 当作数据修复工具。
|
|
67
|
+
|
|
46
68
|
### 初始化
|
|
47
69
|
|
|
48
70
|
```bash
|
|
@@ -100,7 +122,9 @@ ctx resume --path path/to/project --max-chars 2000
|
|
|
100
122
|
|
|
101
123
|
## MCP 与 Claude Code 集成
|
|
102
124
|
|
|
103
|
-
`ctx agent serve` 提供 MCP stdio server
|
|
125
|
+
`ctx agent serve` 提供 MCP stdio server,注册以下工具:`context_orient`、`context_expand`、`context_compile`、`context_resume`、`context_note_list`、`context_note_add`、`context_usage_report` 和 `context_checkpoint`。标准输入和输出都是 MCP 协议,不能输出提示、日志或交互文本;调用错误是单次工具错误,不应让 server 退出。
|
|
126
|
+
|
|
127
|
+
读取工作流通常是 `context_orient → context_compile → context_expand`。`context_compile` 与 `context_expand` 继续保持有界、确定性和不读取文件正文的边界;`context_resume` 复用 `loadContext`,可能更新 `lastUsedAt`。`context_note_list` 是只读的,`context_note_add` 和 `context_checkpoint` 是显式写入操作,分别新增 Note 或保存 Snapshot;它们不会自动记录 feedback、Knowledge、Deadend 或重复 checkpoint。Knowledge/Deadend 的完整记录和治理仍使用 CLI/Agent API。
|
|
104
128
|
|
|
105
129
|
```bash
|
|
106
130
|
ctx agent serve
|
|
@@ -336,7 +360,9 @@ ctx learn "订单取消后不能再次进入支付中状态"
|
|
|
336
360
|
[ ] .contextignored 排除敏感和越界路径
|
|
337
361
|
[ ] Knowledge candidate → verified
|
|
338
362
|
[ ] Deadend candidate → verified
|
|
339
|
-
[ ] MCP context_orient
|
|
363
|
+
[ ] MCP context_orient/context_expand/context_compile/context_resume/context_note_list/context_note_add/context_usage_report/context_checkpoint 可调用,且单次错误不会终止 server
|
|
364
|
+
[ ] ctx usage report 的 JSON/ASCII 输出、baseline 边界和只读性符合预期
|
|
365
|
+
[ ] MCP 只读工具不写入 Context;Note add 和 checkpoint 仅在显式调用时写入
|
|
340
366
|
[ ] Claude 集成预览不写文件,--apply 幂等且拒绝冲突
|
|
341
367
|
[ ] Hook 失败时 fail open,且不会自动 checkpoint
|
|
342
368
|
[ ] npm pack 的安装包可运行 ctx 并导入 contextOrient
|