fluffy-context 0.6.0 → 0.7.2
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 +99 -8
- package/dist/src/agent/index.d.ts +2 -0
- package/dist/src/agent/index.js +1 -0
- package/dist/src/cli/main.js +69 -5
- package/dist/src/cognition/filesystem-observer.d.ts +41 -0
- package/dist/src/cognition/filesystem-observer.js +164 -0
- package/dist/src/cognition/git-observer.d.ts +1 -0
- package/dist/src/cognition/git-observer.js +3 -2
- package/dist/src/cognition/journal.d.ts +4 -1
- package/dist/src/cognition/journal.js +83 -4
- package/dist/src/cognition/migration-v1.js +1 -1
- package/dist/src/cognition/orient.js +4 -3
- package/dist/src/cognition/policy.d.ts +4 -0
- package/dist/src/cognition/policy.js +49 -0
- package/dist/src/cognition/projections.js +0 -0
- package/dist/src/cognition/runtime.d.ts +20 -1
- package/dist/src/cognition/runtime.js +229 -53
- package/dist/src/cognition/types.d.ts +16 -0
- package/dist/src/cognition/v1-bridge.js +3 -2
- package/dist/src/compiler/activity.d.ts +3 -0
- package/dist/src/compiler/activity.js +79 -0
- package/dist/src/compiler/compile.d.ts +2 -0
- package/dist/src/compiler/compile.js +330 -0
- package/dist/src/compiler/sources.d.ts +19 -0
- package/dist/src/compiler/sources.js +130 -0
- package/dist/src/compiler/types.d.ts +187 -0
- package/dist/src/compiler/types.js +1 -0
- package/dist/src/hooks/claude-code.js +7 -15
- package/dist/src/mcp/main.js +2 -0
- package/dist/src/mcp/server.js +2 -0
- package/dist/src/storage/layout.d.ts +1 -0
- package/dist/src/storage/layout.js +3 -0
- 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 +2 -1
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
import type { CognitionGitAnchor, WorkspacePathChange } from '../cognition/types.js';
|
|
2
|
+
import type { ContextOrientResult } from '../agent/types.js';
|
|
3
|
+
import type { Deadend, Knowledge, Note, ResumeSummary, StructuredContext } from '../runtime/types.js';
|
|
4
|
+
export type ContextScenario = 'resume' | 'implementation' | 'debugging' | 'verification' | 'handoff' | 'exploration' | 'unknown';
|
|
5
|
+
export type ContextSourceKind = 'current-context' | 'checkpoint' | 'note' | 'journal' | 'knowledge' | 'deadend';
|
|
6
|
+
export type ContextCandidateSection = 'summary' | 'pending' | 'decision' | 'risk' | 'note' | 'knowledge' | 'deadend' | 'activity';
|
|
7
|
+
export type ContextSignalOrigin = 'explicit' | 'deterministic';
|
|
8
|
+
export interface ContextAnchor {
|
|
9
|
+
projectRoot: string;
|
|
10
|
+
worktreeId: string | null;
|
|
11
|
+
branch: string | null;
|
|
12
|
+
ref: string | null;
|
|
13
|
+
head: string | null;
|
|
14
|
+
}
|
|
15
|
+
export interface ContextIntentSignal {
|
|
16
|
+
source: 'explicit-query' | 'explicit-scenario' | 'context-state' | 'host-prompt' | 'path' | 'git';
|
|
17
|
+
value: string;
|
|
18
|
+
origin: ContextSignalOrigin;
|
|
19
|
+
confidence: 'high' | 'medium' | 'low';
|
|
20
|
+
}
|
|
21
|
+
export interface ContextIntent {
|
|
22
|
+
schemaVersion: 1;
|
|
23
|
+
trigger: 'session-start' | 'prompt' | 'manual' | 'tool-call';
|
|
24
|
+
query: string | null;
|
|
25
|
+
normalizedTerms: string[];
|
|
26
|
+
scenario: ContextScenario;
|
|
27
|
+
scope: string | null;
|
|
28
|
+
paths: string[];
|
|
29
|
+
contextId: string | null;
|
|
30
|
+
anchor: ContextAnchor;
|
|
31
|
+
signals: ContextIntentSignal[];
|
|
32
|
+
requestedSections: ContextCandidateSection[];
|
|
33
|
+
budget: ContextBudget;
|
|
34
|
+
adapter: string;
|
|
35
|
+
sessionId: string | null;
|
|
36
|
+
eventId: string | null;
|
|
37
|
+
}
|
|
38
|
+
export interface ContextBudget {
|
|
39
|
+
unit: 'chars';
|
|
40
|
+
total: number;
|
|
41
|
+
reservedForRenderer: number;
|
|
42
|
+
sections?: Partial<Record<ContextCandidateSection, number>>;
|
|
43
|
+
}
|
|
44
|
+
export interface ContextSourceApplicability {
|
|
45
|
+
project: true;
|
|
46
|
+
branchMode: 'global' | 'exact' | 'unsupported';
|
|
47
|
+
branches: string[];
|
|
48
|
+
paths: string[];
|
|
49
|
+
worktreeId: string | null;
|
|
50
|
+
ref: string | null;
|
|
51
|
+
}
|
|
52
|
+
export interface ContextProvenance {
|
|
53
|
+
origin: 'v1-file' | 'v2-journal' | 'mixed' | 'live';
|
|
54
|
+
eventId: string | null;
|
|
55
|
+
eventType: string | null;
|
|
56
|
+
occurredAt: string | null;
|
|
57
|
+
sourcePath: string | null;
|
|
58
|
+
sourceHash: string | null;
|
|
59
|
+
anchorProvenance: 'snapshot' | 'live' | 'global' | null;
|
|
60
|
+
}
|
|
61
|
+
export interface ContextSourceDescriptor {
|
|
62
|
+
sourceId: string;
|
|
63
|
+
kind: ContextSourceKind;
|
|
64
|
+
entityId: string;
|
|
65
|
+
contextId: string | null;
|
|
66
|
+
snapshotId: string | null;
|
|
67
|
+
status: string | null;
|
|
68
|
+
scope: string | null;
|
|
69
|
+
paths: string[];
|
|
70
|
+
anchor: ContextAnchor;
|
|
71
|
+
applicability: ContextSourceApplicability;
|
|
72
|
+
privacy: {
|
|
73
|
+
classification: 'metadata' | 'redacted-text' | 'unknown';
|
|
74
|
+
redactionCount: number;
|
|
75
|
+
};
|
|
76
|
+
provenance: ContextProvenance;
|
|
77
|
+
}
|
|
78
|
+
export interface ContextActivityRecord {
|
|
79
|
+
eventId: string;
|
|
80
|
+
occurredAt: string;
|
|
81
|
+
anchor: ContextAnchor;
|
|
82
|
+
paths: string[];
|
|
83
|
+
changes: WorkspacePathChange[];
|
|
84
|
+
pathCount: number;
|
|
85
|
+
coalesced: boolean;
|
|
86
|
+
truncated: boolean;
|
|
87
|
+
focusMatch: boolean;
|
|
88
|
+
}
|
|
89
|
+
export interface ContextCandidate<T = unknown> {
|
|
90
|
+
candidateId: string;
|
|
91
|
+
section: ContextCandidateSection;
|
|
92
|
+
source: ContextSourceDescriptor;
|
|
93
|
+
content: T;
|
|
94
|
+
text: string;
|
|
95
|
+
priority: number;
|
|
96
|
+
score: number;
|
|
97
|
+
matchedTerms: string[];
|
|
98
|
+
matchedFields: string[];
|
|
99
|
+
originalChars: number;
|
|
100
|
+
selectedChars: number;
|
|
101
|
+
selected: boolean;
|
|
102
|
+
truncated: boolean;
|
|
103
|
+
}
|
|
104
|
+
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
|
+
export interface ContextExclusion {
|
|
106
|
+
candidateId: string;
|
|
107
|
+
source: ContextSourceDescriptor | null;
|
|
108
|
+
reason: ContextExclusionReason;
|
|
109
|
+
detail?: string;
|
|
110
|
+
}
|
|
111
|
+
export interface ContextTruncation {
|
|
112
|
+
truncated: boolean;
|
|
113
|
+
summary: boolean;
|
|
114
|
+
pending: boolean;
|
|
115
|
+
decisions: boolean;
|
|
116
|
+
risks: boolean;
|
|
117
|
+
notes: boolean;
|
|
118
|
+
knowledge: boolean;
|
|
119
|
+
deadends: boolean;
|
|
120
|
+
activity: boolean;
|
|
121
|
+
candidatesOmittedByBudget: number;
|
|
122
|
+
candidatesPartiallyTruncated: number;
|
|
123
|
+
}
|
|
124
|
+
export interface ContextBudgetReport {
|
|
125
|
+
requested: ContextBudget;
|
|
126
|
+
usedChars: number;
|
|
127
|
+
remainingChars: number;
|
|
128
|
+
sections: Partial<Record<ContextCandidateSection, {
|
|
129
|
+
allocated: number;
|
|
130
|
+
consumed: number;
|
|
131
|
+
truncated: boolean;
|
|
132
|
+
}>>;
|
|
133
|
+
}
|
|
134
|
+
export interface ContextManifestProvenance {
|
|
135
|
+
journalRead: boolean;
|
|
136
|
+
journalEventCount: number;
|
|
137
|
+
selectedSourceIds: string[];
|
|
138
|
+
selectedEventIds: string[];
|
|
139
|
+
fallbackReason: string | null;
|
|
140
|
+
}
|
|
141
|
+
export interface ContextCompileOptions {
|
|
142
|
+
contextId?: string;
|
|
143
|
+
query?: string;
|
|
144
|
+
scenario?: ContextScenario;
|
|
145
|
+
scope?: string;
|
|
146
|
+
paths?: string[];
|
|
147
|
+
maxChars?: number;
|
|
148
|
+
knowledgeLimit?: number;
|
|
149
|
+
deadendLimit?: number;
|
|
150
|
+
noteLimit?: number;
|
|
151
|
+
activityLimit?: number;
|
|
152
|
+
trigger?: ContextIntent['trigger'];
|
|
153
|
+
adapter?: string;
|
|
154
|
+
sessionId?: string;
|
|
155
|
+
eventId?: string;
|
|
156
|
+
}
|
|
157
|
+
export interface ContextManifest {
|
|
158
|
+
schemaVersion: 1;
|
|
159
|
+
compilerVersion: '0.7.2';
|
|
160
|
+
rankingVersion: 'compiler-ranking-v2';
|
|
161
|
+
status: 'ready' | 'no_context';
|
|
162
|
+
projectRoot: string;
|
|
163
|
+
mode: 'v2' | 'v1-fallback';
|
|
164
|
+
anchor: ContextAnchor;
|
|
165
|
+
intent: ContextIntent;
|
|
166
|
+
context: ContextOrientResult['context'];
|
|
167
|
+
snapshot: ContextOrientResult['snapshot'];
|
|
168
|
+
resumeSummary: ResumeSummary | null;
|
|
169
|
+
sections: {
|
|
170
|
+
summary: ContextCandidate[];
|
|
171
|
+
pending: ContextCandidate[];
|
|
172
|
+
decision: ContextCandidate[];
|
|
173
|
+
risk: ContextCandidate[];
|
|
174
|
+
notes: ContextCandidate[];
|
|
175
|
+
knowledge: ContextCandidate[];
|
|
176
|
+
deadends: ContextCandidate[];
|
|
177
|
+
activity: ContextCandidate<ContextActivityRecord>[];
|
|
178
|
+
};
|
|
179
|
+
selected: ContextCandidate[];
|
|
180
|
+
excluded: ContextExclusion[];
|
|
181
|
+
budget: ContextBudgetReport;
|
|
182
|
+
truncation: ContextTruncation;
|
|
183
|
+
provenance: ContextManifestProvenance;
|
|
184
|
+
text: string;
|
|
185
|
+
}
|
|
186
|
+
export type CompilerRecord = StructuredContext | Note | Knowledge | Deadend;
|
|
187
|
+
export type CompilerGitAnchor = CognitionGitAnchor;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
import { contextOrient } from '../agent/api.js';
|
|
2
2
|
import { appendEvent } from '../cognition/journal.js';
|
|
3
3
|
import { v2Enabled } from '../cognition/orient.js';
|
|
4
|
-
import {
|
|
4
|
+
import { ensureRuntimeForSession, touchRuntime } from '../cognition/runtime.js';
|
|
5
|
+
import { loadRuntimePolicy } from '../cognition/policy.js';
|
|
5
6
|
import { readGitWorktreeState } from '../git/git-adapter.js';
|
|
6
7
|
import { resolveProjectRoot } from '../project/project-resolver.js';
|
|
7
|
-
import { cognitionPolicyPath } from '../storage/layout.js';
|
|
8
|
-
import { readJson } from '../storage/json-store.js';
|
|
9
8
|
const HOOK_RESPONSE = '{}\n';
|
|
10
9
|
function text(value) {
|
|
11
10
|
return typeof value === 'string' ? value : undefined;
|
|
@@ -68,15 +67,12 @@ function redactIntent(value) {
|
|
|
68
67
|
.replace(/\b(?:sk|ghp|gho|github_pat)_[A-Za-z0-9_-]+\b/g, () => { redactionCount += 1; return '[REDACTED]'; });
|
|
69
68
|
return { intent, redactionCount };
|
|
70
69
|
}
|
|
71
|
-
async function within(task, milliseconds) {
|
|
72
|
-
return Promise.race([task, new Promise((_, reject) => setTimeout(() => reject(new Error('hook deadline exceeded')), milliseconds))]);
|
|
73
|
-
}
|
|
74
70
|
async function capture(event, input) {
|
|
75
71
|
const cwd = text(input.cwd);
|
|
76
72
|
const projectRoot = await resolveProjectRoot(cwd);
|
|
77
73
|
if (!await v2Enabled(projectRoot))
|
|
78
74
|
return;
|
|
79
|
-
const policy = await
|
|
75
|
+
const policy = await loadRuntimePolicy(projectRoot);
|
|
80
76
|
const sessionId = stableId(input, 'session');
|
|
81
77
|
const eventId = stableId(input, 'event');
|
|
82
78
|
if (!sessionId || !eventId)
|
|
@@ -94,12 +90,7 @@ async function capture(event, input) {
|
|
|
94
90
|
});
|
|
95
91
|
}
|
|
96
92
|
async function maybeStartRuntime(input) {
|
|
97
|
-
|
|
98
|
-
if (!await v2Enabled(projectRoot))
|
|
99
|
-
return;
|
|
100
|
-
const policy = await readJson(cognitionPolicyPath(projectRoot));
|
|
101
|
-
if (policy.allowHookStartup)
|
|
102
|
-
await within(startRuntime(projectRoot), 1_000);
|
|
93
|
+
await ensureRuntimeForSession(text(input.cwd), { source: 'claude-hook', timeoutMs: 1_000 });
|
|
103
94
|
}
|
|
104
95
|
async function parseInput(raw) {
|
|
105
96
|
try {
|
|
@@ -115,9 +106,10 @@ export async function runClaudeCodeHook(event, raw) {
|
|
|
115
106
|
if (!input)
|
|
116
107
|
return HOOK_RESPONSE;
|
|
117
108
|
try {
|
|
118
|
-
|
|
119
|
-
|
|
109
|
+
await maybeStartRuntime(input).catch(() => undefined);
|
|
110
|
+
await touchRuntime(text(input.cwd)).catch(() => false);
|
|
120
111
|
await capture(event, input).catch(() => undefined);
|
|
112
|
+
await touchRuntime(text(input.cwd)).catch(() => false);
|
|
121
113
|
const cwd = text(input.cwd);
|
|
122
114
|
const result = event === 'session-start'
|
|
123
115
|
? await contextOrient(cwd, { maxChars: 1200, noteLimit: 8 })
|
package/dist/src/mcp/main.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { serveStdio } from '@modelcontextprotocol/server/stdio';
|
|
2
|
+
import { ensureRuntimeForSession } from '../cognition/runtime.js';
|
|
2
3
|
import { createMcpServer } from './server.js';
|
|
4
|
+
await ensureRuntimeForSession(process.cwd(), { source: 'mcp', timeoutMs: 1_000 }).catch(() => undefined);
|
|
3
5
|
serveStdio(createMcpServer, {
|
|
4
6
|
onerror: (error) => process.stderr.write(`MCP server error: ${error.message}\n`),
|
|
5
7
|
});
|
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
3
|
import { contextOrient } from '../agent/api.js';
|
|
4
|
+
import { ensureRuntimeForSession } from '../cognition/runtime.js';
|
|
4
5
|
import { VERSION } from '../version.js';
|
|
5
6
|
const orientInput = z.object({
|
|
6
7
|
path: z.string().optional(),
|
|
@@ -23,6 +24,7 @@ export function createMcpServer() {
|
|
|
23
24
|
inputSchema: orientInput,
|
|
24
25
|
}, async ({ path, ...options }) => {
|
|
25
26
|
try {
|
|
27
|
+
await ensureRuntimeForSession(path, { source: 'mcp', timeoutMs: 1_000 }).catch(() => undefined);
|
|
26
28
|
const result = await contextOrient(path, options);
|
|
27
29
|
return {
|
|
28
30
|
content: [{ type: 'text', text: JSON.stringify(result) }],
|
|
@@ -28,5 +28,6 @@ export declare function cognitionV1ImportPath(projectRoot: string): string;
|
|
|
28
28
|
export declare function cognitionRuntimeDirectory(projectRoot: string): string;
|
|
29
29
|
export declare function cognitionRuntimeLeasePath(projectRoot: string): string;
|
|
30
30
|
export declare function cognitionRuntimeSecretPath(projectRoot: string): string;
|
|
31
|
+
export declare function cognitionRuntimeStartupPath(projectRoot: string): string;
|
|
31
32
|
export declare function cognitionV1SyncPath(projectRoot: string): string;
|
|
32
33
|
export declare function cognitionQuarantineDirectory(projectRoot: string): string;
|
|
@@ -89,6 +89,9 @@ export function cognitionRuntimeLeasePath(projectRoot) {
|
|
|
89
89
|
export function cognitionRuntimeSecretPath(projectRoot) {
|
|
90
90
|
return path.join(cognitionRuntimeDirectory(projectRoot), 'secret');
|
|
91
91
|
}
|
|
92
|
+
export function cognitionRuntimeStartupPath(projectRoot) {
|
|
93
|
+
return path.join(cognitionRuntimeDirectory(projectRoot), 'startup.json');
|
|
94
|
+
}
|
|
92
95
|
export function cognitionV1SyncPath(projectRoot) {
|
|
93
96
|
return path.join(cognitionRuntimeDirectory(projectRoot), 'v1-sync.json');
|
|
94
97
|
}
|
package/dist/src/version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const VERSION = "0.
|
|
1
|
+
export declare const VERSION = "0.7.2";
|
package/dist/src/version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const VERSION = '0.
|
|
1
|
+
export const VERSION = '0.7.2';
|
package/package.json
CHANGED
|
@@ -17,7 +17,8 @@ compatibility: 需要 Node.js >=20.19.0;Git 可选。CLI 通过 npm 全局安
|
|
|
17
17
|
- 人类需要了解 Agent 发生了什么时使用 `ctx activity`;它是只读时间线,不要求人类参与 Agent 的高频记录操作。
|
|
18
18
|
- 新会话与新任务优先运行 `ctx orient [query] --max-chars <预算>`;它只读地返回有界摘要、匹配的已验证共识和当前 Context 的 open Note,不会创建 Snapshot 或更新 `lastUsedAt`。
|
|
19
19
|
- 仅需要完整详情时再运行 `ctx resume --max-chars <预算>`;它会保留既有的 `lastUsedAt` 更新语义。
|
|
20
|
-
- Agent 集成从 `fluffy-context` 或 `fluffy-context/agent` 导入 `contextOrient`、`saveContext`、`loadContext` 和 `searchContext`,不应直接读写 `.context` 或导入内部 `dist/...` 路径。
|
|
20
|
+
- Agent 集成从 `fluffy-context` 或 `fluffy-context/agent` 导入 `contextOrient`、`compileContext`、`saveContext`、`loadContext` 和 `searchContext`,不应直接读写 `.context` 或导入内部 `dist/...` 路径。
|
|
21
|
+
- `ctx compile` 的 `activity` section 只表示 Runtime 观察到的近期文件 metadata 变化;它不代表文件被读取或理解,也不替代 checkpoint、Note 或已验证 Knowledge。
|
|
21
22
|
- 新任务先发现已验证共识,再开始实现;候选项只在显式审查时使用。
|
|
22
23
|
- 普通查询只使用已验证 Knowledge 和 Deadend;需要审查候选项时显式使用 `--all`。
|
|
23
24
|
- CLI 业务命令的标准输出是 JSON;错误写入标准错误并返回非零退出码。解析输出时不要把 `--help` 的纯文本当作 JSON。
|