@fenglimg/fabric-server 1.3.1 → 1.5.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/{chunk-GU7AMRM3.js → chunk-E3RZ276F.js} +306 -99
- package/dist/{http-6V75VA23.js → http-SK2HEFK4.js} +27 -69
- package/dist/index.d.ts +26 -2
- package/dist/index.js +20 -126
- package/dist/static/assets/index-B5hhHHl2.css +1 -0
- package/dist/static/assets/index-Mf8rv3Zc.js +14 -0
- package/dist/static/index.html +2 -2
- package/package.json +3 -3
- package/dist/static/assets/index-D45wW11O.css +0 -1
- package/dist/static/assets/index-DvqI1Lwz.js +0 -10
package/dist/index.js
CHANGED
|
@@ -2,21 +2,26 @@ import {
|
|
|
2
2
|
AGENTS_MD_RESOURCE_URI,
|
|
3
3
|
FABRIC_DIR,
|
|
4
4
|
appendEditIntentAuditEvents,
|
|
5
|
-
appendGetRulesAuditEvent,
|
|
6
5
|
appendLedgerEntry,
|
|
6
|
+
approveHumanLock,
|
|
7
7
|
atomicWriteText,
|
|
8
8
|
contextCache,
|
|
9
|
+
getRules,
|
|
10
|
+
loadGetRulesContext,
|
|
11
|
+
normalizeRulesPath,
|
|
9
12
|
readAgentsMeta,
|
|
10
13
|
readHumanLock,
|
|
14
|
+
readHumanLockEntry,
|
|
11
15
|
resolveProjectRoot,
|
|
16
|
+
resolveRulesForPath,
|
|
12
17
|
runDoctorAuditReport,
|
|
13
18
|
runDoctorReport,
|
|
14
19
|
sha256
|
|
15
|
-
} from "./chunk-
|
|
20
|
+
} from "./chunk-E3RZ276F.js";
|
|
16
21
|
|
|
17
22
|
// src/index.ts
|
|
18
|
-
import { readFile
|
|
19
|
-
import { join as
|
|
23
|
+
import { readFile } from "fs/promises";
|
|
24
|
+
import { join as join2, resolve } from "path";
|
|
20
25
|
import { fileURLToPath } from "url";
|
|
21
26
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
22
27
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
@@ -98,128 +103,13 @@ function registerAppendIntent(server) {
|
|
|
98
103
|
|
|
99
104
|
// src/tools/get-rules.ts
|
|
100
105
|
import { z as z2 } from "zod";
|
|
101
|
-
|
|
102
|
-
// src/services/get-rules.ts
|
|
103
|
-
import { readFile } from "fs/promises";
|
|
104
|
-
import { join } from "path";
|
|
105
|
-
import { minimatch } from "minimatch";
|
|
106
|
-
var PRIORITY_ORDER = {
|
|
107
|
-
high: 0,
|
|
108
|
-
medium: 1,
|
|
109
|
-
low: 2
|
|
110
|
-
};
|
|
111
|
-
async function getRules(projectRoot, input) {
|
|
112
|
-
const context = await loadGetRulesContext(projectRoot);
|
|
113
|
-
const stale = input.client_hash !== void 0 && input.client_hash !== context.meta.revision;
|
|
114
|
-
const rules = await resolveRulesForPath(projectRoot, context, input.path);
|
|
115
|
-
const result = {
|
|
116
|
-
revision_hash: context.meta.revision,
|
|
117
|
-
stale,
|
|
118
|
-
rules
|
|
119
|
-
};
|
|
120
|
-
try {
|
|
121
|
-
await appendGetRulesAuditEvent(projectRoot, {
|
|
122
|
-
path: input.path,
|
|
123
|
-
client_hash: input.client_hash
|
|
124
|
-
});
|
|
125
|
-
} catch {
|
|
126
|
-
}
|
|
127
|
-
return result;
|
|
128
|
-
}
|
|
129
|
-
async function loadGetRulesContext(projectRoot) {
|
|
130
|
-
const cached = contextCache.get("context", projectRoot);
|
|
131
|
-
if (cached !== void 0) {
|
|
132
|
-
return cached;
|
|
133
|
-
}
|
|
134
|
-
const meta = await readAgentsMeta(projectRoot);
|
|
135
|
-
const l0Content = await readFile(join(projectRoot, ".fabric", "bootstrap", "README.md"), "utf8");
|
|
136
|
-
const humanLockedNearby = (await readHumanLock(projectRoot)).map((entry) => ({
|
|
137
|
-
file: entry.file,
|
|
138
|
-
excerpt: JSON.stringify(entry)
|
|
139
|
-
}));
|
|
140
|
-
const context = {
|
|
141
|
-
meta,
|
|
142
|
-
l0Content,
|
|
143
|
-
humanLockedNearby
|
|
144
|
-
};
|
|
145
|
-
contextCache.set("context", projectRoot, context);
|
|
146
|
-
return context;
|
|
147
|
-
}
|
|
148
|
-
async function resolveRulesForPath(projectRoot, context, path, options = {}) {
|
|
149
|
-
const loadedRules = await loadRulesForPath(projectRoot, context.meta, path);
|
|
150
|
-
const { L1, L2 } = partitionRulesByLevel(loadedRules, options.dedupeByPath ?? false);
|
|
151
|
-
return {
|
|
152
|
-
L0: context.l0Content,
|
|
153
|
-
L1,
|
|
154
|
-
L2,
|
|
155
|
-
human_locked_nearby: context.humanLockedNearby
|
|
156
|
-
};
|
|
157
|
-
}
|
|
158
|
-
function normalizeRulesPath(value) {
|
|
159
|
-
return value.replaceAll("\\", "/");
|
|
160
|
-
}
|
|
161
|
-
function classifyNode(nodeId) {
|
|
162
|
-
if (nodeId.startsWith("L1/")) {
|
|
163
|
-
return "L1";
|
|
164
|
-
}
|
|
165
|
-
if (nodeId.startsWith("L2/")) {
|
|
166
|
-
return "L2";
|
|
167
|
-
}
|
|
168
|
-
return null;
|
|
169
|
-
}
|
|
170
|
-
async function loadRulesForPath(projectRoot, meta, path) {
|
|
171
|
-
const requestedPath = normalizeRulesPath(path);
|
|
172
|
-
const matchedNodes = Object.entries(meta.nodes).filter(([, node]) => minimatch(requestedPath, normalizeRulesPath(node.scope_glob), { dot: true })).sort((left, right) => {
|
|
173
|
-
const [leftId, leftNode] = left;
|
|
174
|
-
const [rightId, rightNode] = right;
|
|
175
|
-
const priorityDelta = PRIORITY_ORDER[leftNode.priority] - PRIORITY_ORDER[rightNode.priority];
|
|
176
|
-
return priorityDelta !== 0 ? priorityDelta : leftId.localeCompare(rightId);
|
|
177
|
-
});
|
|
178
|
-
return await Promise.all(
|
|
179
|
-
matchedNodes.map(async ([nodeId, node]) => ({
|
|
180
|
-
level: classifyNode(nodeId),
|
|
181
|
-
entry: {
|
|
182
|
-
path: node.file,
|
|
183
|
-
content: await readFile(join(projectRoot, node.file), "utf8")
|
|
184
|
-
}
|
|
185
|
-
}))
|
|
186
|
-
);
|
|
187
|
-
}
|
|
188
|
-
function partitionRulesByLevel(loadedRules, dedupeByPath) {
|
|
189
|
-
const l1 = [];
|
|
190
|
-
const l2 = [];
|
|
191
|
-
for (const rule of loadedRules) {
|
|
192
|
-
if (rule.level === "L1") {
|
|
193
|
-
l1.push(rule.entry);
|
|
194
|
-
continue;
|
|
195
|
-
}
|
|
196
|
-
if (rule.level === "L2") {
|
|
197
|
-
l2.push(rule.entry);
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
return {
|
|
201
|
-
L1: dedupeByPath ? dedupeEntriesByPath(l1) : l1,
|
|
202
|
-
L2: dedupeByPath ? dedupeEntriesByPath(l2) : l2
|
|
203
|
-
};
|
|
204
|
-
}
|
|
205
|
-
function dedupeEntriesByPath(entries) {
|
|
206
|
-
const seenPaths = /* @__PURE__ */ new Set();
|
|
207
|
-
return entries.filter((entry) => {
|
|
208
|
-
if (seenPaths.has(entry.path)) {
|
|
209
|
-
return false;
|
|
210
|
-
}
|
|
211
|
-
seenPaths.add(entry.path);
|
|
212
|
-
return true;
|
|
213
|
-
});
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
// src/tools/get-rules.ts
|
|
217
106
|
var inputSchema2 = {
|
|
218
107
|
path: z2.string().describe("Target file path to query rules for"),
|
|
219
108
|
client_hash: z2.string().optional().describe("Revision hash from prior fab_get_rules response; enables stale detection")
|
|
220
109
|
};
|
|
221
110
|
var rulesEntrySchema = z2.object({ path: z2.string(), content: z2.string() });
|
|
222
111
|
var humanLockedSchema = z2.object({ file: z2.string(), excerpt: z2.string() });
|
|
112
|
+
var descriptionStubSchema = z2.object({ path: z2.string(), description: z2.string() });
|
|
223
113
|
var outputSchema2 = z2.object({
|
|
224
114
|
revision_hash: z2.string(),
|
|
225
115
|
stale: z2.boolean(),
|
|
@@ -227,7 +117,8 @@ var outputSchema2 = z2.object({
|
|
|
227
117
|
L0: z2.string(),
|
|
228
118
|
L1: z2.array(rulesEntrySchema),
|
|
229
119
|
L2: z2.array(rulesEntrySchema),
|
|
230
|
-
human_locked_nearby: z2.array(humanLockedSchema)
|
|
120
|
+
human_locked_nearby: z2.array(humanLockedSchema),
|
|
121
|
+
description_stubs: z2.array(descriptionStubSchema).optional()
|
|
231
122
|
})
|
|
232
123
|
});
|
|
233
124
|
function registerGetRules(server) {
|
|
@@ -331,9 +222,9 @@ import { z as z4 } from "zod";
|
|
|
331
222
|
|
|
332
223
|
// src/services/update-registry.ts
|
|
333
224
|
import { agentsMetaNodeSchema } from "@fenglimg/fabric-shared";
|
|
334
|
-
import { join
|
|
225
|
+
import { join } from "path";
|
|
335
226
|
async function updateRegistry(projectRoot, input) {
|
|
336
|
-
const metaPath =
|
|
227
|
+
const metaPath = join(projectRoot, FABRIC_DIR, "agents.meta.json");
|
|
337
228
|
const currentMeta = await readAgentsMeta(projectRoot);
|
|
338
229
|
const nextMeta = applyRegistryOperation(currentMeta, input.op, input.node_id, input.data);
|
|
339
230
|
const newRevision = computeRevision(nextMeta);
|
|
@@ -448,7 +339,7 @@ function formatError(error) {
|
|
|
448
339
|
function createFabricServer() {
|
|
449
340
|
const server = new McpServer({
|
|
450
341
|
name: "fabric-context-server",
|
|
451
|
-
version: "1.
|
|
342
|
+
version: "1.5.0"
|
|
452
343
|
});
|
|
453
344
|
registerGetRules(server);
|
|
454
345
|
registerPlanContext(server);
|
|
@@ -463,7 +354,7 @@ function createFabricServer() {
|
|
|
463
354
|
},
|
|
464
355
|
async (_uri) => {
|
|
465
356
|
const projectRoot = process.env.FABRIC_PROJECT_ROOT ?? process.cwd();
|
|
466
|
-
const content = await
|
|
357
|
+
const content = await readFile(join2(projectRoot, ".fabric", "bootstrap", "README.md"), "utf8");
|
|
467
358
|
return {
|
|
468
359
|
contents: [
|
|
469
360
|
{
|
|
@@ -483,7 +374,7 @@ async function startStdioServer() {
|
|
|
483
374
|
await server.connect(transport);
|
|
484
375
|
}
|
|
485
376
|
async function startHttpServer(options) {
|
|
486
|
-
const { createFabricHttpApp } = await import("./http-
|
|
377
|
+
const { createFabricHttpApp } = await import("./http-SK2HEFK4.js");
|
|
487
378
|
const { port, projectRoot, host = "127.0.0.1", authToken, dashboardDistPath, dev } = options;
|
|
488
379
|
const app = createFabricHttpApp({ projectRoot, host, authToken, dashboardDistPath, dev });
|
|
489
380
|
return await new Promise((resolveServer, rejectServer) => {
|
|
@@ -510,7 +401,10 @@ if (isMainModule) {
|
|
|
510
401
|
}
|
|
511
402
|
export {
|
|
512
403
|
AGENTS_MD_RESOURCE_URI,
|
|
404
|
+
approveHumanLock,
|
|
513
405
|
createFabricServer,
|
|
406
|
+
readHumanLock,
|
|
407
|
+
readHumanLockEntry,
|
|
514
408
|
runDoctorAuditReport,
|
|
515
409
|
runDoctorReport,
|
|
516
410
|
startHttpServer,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
:root{--color-surface-canvas: #0b1016;--color-surface-panel: #0f172a;--color-surface-raised: #1e293b;--color-surface-overlay: #334155;--color-surface-code: #020617;--color-border-subtle: #1e293b;--color-border-default: #334155;--color-border-strong: #475569;--color-text-primary: #f8fafc;--color-text-secondary: #cbd5e1;--color-text-muted: #94a3b8;--color-text-dim: #64748b;--color-text-mono: #e2e8f0;--color-state-locked-bg: rgba(245, 158, 11, .08);--color-state-locked-border: #b45309;--color-state-locked-text: #fbbf24;--color-state-locked-accent: #f59e0b;--color-state-stale-bg: rgba(220, 38, 38, .1);--color-state-stale-border: #991b1b;--color-state-stale-text: #f87171;--color-state-stale-accent: #ef4444;--color-state-drift-bg: rgba(234, 88, 12, .1);--color-state-drift-border: #c2410c;--color-state-drift-text: #fb923c;--color-state-drift-accent: #ea580c;--color-state-approved-bg: rgba(34, 197, 94, .1);--color-state-approved-border: #166534;--color-state-approved-text: #4ade80;--color-state-approved-accent: #22c55e;--color-state-pending-bg: rgba(100, 116, 139, .1);--color-state-pending-border: #475569;--color-state-pending-text: #94a3b8;--color-state-pending-accent: #64748b;--color-source-ai-bg: rgba(99, 102, 241, .12);--color-source-ai-border: #4338ca;--color-source-ai-text: #a5b4fc;--color-source-ai-accent: #6366f1;--color-source-human-bg: rgba(20, 184, 166, .12);--color-source-human-border: #0f766e;--color-source-human-text: #5eead4;--color-source-human-accent: #14b8a6;--color-action-primary: #22c55e;--color-action-primary-hover: #16a34a;--color-action-primary-text: #052e16;--color-action-neutral: #334155;--color-action-neutral-hover: #475569;--color-action-danger: #dc2626;--font-family-mono: "Space Mono", "JetBrains Mono", "SF Mono", "Monaco", "Consolas", ui-monospace, monospace;--font-family-sans: "Inter", -apple-system, "Segoe UI", system-ui, sans-serif;--font-size-xs: 11px;--font-size-sm: 12px;--font-size-base: 13px;--font-size-md: 14px;--font-size-lg: 16px;--font-size-xl: 20px;--font-size-2xl: 24px;--font-weight-regular: 400;--font-weight-medium: 500;--font-weight-bold: 700;--line-height-tight: 1.25;--line-height-base: 1.5;--line-height-loose: 1.65;--letter-spacing-mono: 0;--letter-spacing-sans: -.01em;--letter-spacing-chip: .04em;--space-0: 0;--space-0-5: 2px;--space-1: 4px;--space-2: 8px;--space-3: 12px;--space-4: 16px;--space-5: 20px;--space-6: 24px;--space-8: 32px;--space-10: 40px;--space-12: 48px;--space-16: 64px;--radius-none: 0;--radius-sm: 3px;--radius-md: 6px;--radius-lg: 8px;--radius-pill: 999px;--shadow-card: 0 1px 2px rgba(0, 0, 0, .3), 0 1px 1px rgba(0, 0, 0, .15);--shadow-raised: 0 4px 12px rgba(0, 0, 0, .4);--shadow-focus-ring: 0 0 0 2px #0f172a, 0 0 0 4px #a5b4fc;--motion-duration-fast: .12s;--motion-duration-base: .18s;--motion-duration-slow: .28s;--motion-easing-standard: cubic-bezier(.4, 0, .2, 1);--motion-easing-decel: cubic-bezier(0, 0, .2, 1);--z-base: 0;--z-sticky: 10;--z-dropdown: 20;--z-popover: 30;--z-modal: 50;--z-toast: 100;--layout-sidebar-width: 240px;--layout-sidebar-width-collapsed: 64px;--layout-header-height: 48px;--layout-container-max-width: 1440px;--layout-container-padding: 24px}*{box-sizing:border-box}html,body{height:100%;margin:0}body{background:radial-gradient(circle at top left,rgba(99,102,241,.12),transparent 32rem),linear-gradient(135deg,rgba(15,23,42,.9),var(--color-surface-canvas) 52%);color:var(--color-text-secondary);font-family:var(--font-family-sans);font-size:var(--font-size-base);line-height:var(--line-height-base);-webkit-font-smoothing:antialiased}button,input{font:inherit}button:focus-visible,a:focus-visible,input:focus-visible,[tabindex]:focus-visible{box-shadow:var(--shadow-focus-ring);outline:0}.app-shell{display:grid;grid-template-columns:var(--layout-sidebar-width) minmax(0,1fr);min-height:100vh}.sidebar{background:color-mix(in srgb,var(--color-surface-panel) 92%,black);border-right:1px solid var(--color-border-subtle);padding:var(--space-4)}.brand{align-items:center;color:var(--color-text-primary);display:flex;font-family:var(--font-family-mono);font-size:var(--font-size-md);font-weight:var(--font-weight-bold);gap:var(--space-2);margin-bottom:var(--space-4);padding:var(--space-2)}.brand-logo{align-items:center;border:1px solid var(--color-text-mono);border-radius:var(--radius-sm);color:var(--color-text-mono);display:inline-flex;font-size:10px;height:18px;justify-content:center;width:18px}.brand-version{color:var(--color-text-dim);font-size:10px;font-weight:var(--font-weight-regular);margin-left:auto}.nav-item{align-items:center;border-radius:var(--radius-md);color:var(--color-text-muted);display:grid;font-size:var(--font-size-sm);gap:var(--space-2);grid-template-columns:8px 1fr;margin-bottom:2px;min-height:44px;padding:var(--space-2) var(--space-3);text-decoration:none;transition:background var(--motion-duration-base),color var(--motion-duration-base)}.nav-item:hover,.nav-item.active{background:var(--color-surface-raised);color:var(--color-text-primary)}.nav-item small{color:var(--color-text-dim);font-family:var(--font-family-mono);font-size:10px;grid-column:2;margin-top:-6px}.nav-item .dot{background:var(--color-text-dim);border-radius:50%;height:6px;width:6px}.nav-item.active .dot{background:var(--color-action-primary)}.muted-nav{opacity:.7}.nav-section{color:var(--color-text-dim);font-size:10px;letter-spacing:.08em;padding:var(--space-4) var(--space-3) var(--space-2);text-transform:uppercase}.main{display:flex;flex-direction:column;min-width:0}.header{align-items:center;background:#0f172adb;border-bottom:1px solid var(--color-border-subtle);display:flex;height:var(--layout-header-height);justify-content:space-between;padding:0 var(--space-6)}.breadcrumb,.port-label{color:var(--color-text-muted);font-family:var(--font-family-mono);font-size:var(--font-size-sm)}.breadcrumb .sep{color:var(--color-text-dim);margin:0 var(--space-1)}.breadcrumb strong{color:var(--color-text-primary);font-weight:var(--font-weight-regular)}.header-actions{align-items:center;display:flex;gap:var(--space-3)}.badge-live{align-items:center;border:1px solid rgba(34,197,94,.3);border-radius:var(--radius-pill);color:var(--color-state-approved-text);display:inline-flex;font-family:var(--font-family-mono);font-size:10px;gap:var(--space-1);padding:2px var(--space-2)}.badge-live.disconnected{border-color:var(--color-state-pending-border);color:var(--color-state-pending-text)}.pulse{background:currentColor;border-radius:50%;height:6px;width:6px}.view{flex:1;overflow:auto;padding:var(--space-6)}.view-header{align-items:flex-end;display:flex;justify-content:space-between;margin-bottom:var(--space-5);position:sticky;top:0;z-index:var(--z-sticky)}.view-title{color:var(--color-text-primary);font-size:var(--font-size-xl);margin:0}.view-subtitle,.muted{color:var(--color-text-muted);font-size:var(--font-size-sm);margin:var(--space-1) 0 0}.view-split{align-items:start;display:grid;gap:var(--space-5);grid-template-columns:minmax(0,2fr) minmax(280px,1fr)}.tree-panel,.detail-panel,.empty-card,.filter-bar{background:var(--color-surface-panel);border:1px solid var(--color-border-subtle);border-radius:var(--radius-lg);box-shadow:var(--shadow-card)}.tree-filter{border-bottom:1px solid var(--color-border-subtle);display:flex;gap:var(--space-3);padding:var(--space-4)}.tree-filter input,.annotate-input{background:var(--color-surface-code);border:1px solid var(--color-border-default);border-radius:var(--radius-md);color:var(--color-text-primary);font-family:var(--font-family-mono);min-height:44px;padding:var(--space-2) var(--space-3);width:100%}.status-line{color:var(--color-text-dim);display:flex;font-family:var(--font-family-mono);font-size:var(--font-size-xs);justify-content:space-between;padding:var(--space-3) var(--space-4)}.tree{font-family:var(--font-family-mono);padding:var(--space-2)}.tree-node{align-items:center;border-radius:var(--radius-md);color:var(--color-text-secondary);cursor:pointer;display:flex;gap:var(--space-2);min-height:40px;padding:var(--space-2) var(--space-3);position:relative;transition:background var(--motion-duration-fast),transform var(--motion-duration-fast)}.tree-node.is-readonly{cursor:default}.tree-node:hover:not(.is-readonly),.tree-node.is-selected{background:var(--color-surface-raised)}.tree-node.is-selected{box-shadow:inset 2px 0 0 var(--color-source-ai-accent)}.tree-node.is-locked{background:var(--color-state-locked-bg);box-shadow:inset 3px 0 0 var(--color-state-locked-border)}.tree-node.is-stale:before{background:var(--color-state-stale-text);border-radius:50%;content:"";height:6px;left:-2px;position:absolute;top:50%;transform:translateY(-50%);width:6px}.tree-caret{color:var(--color-text-dim);display:inline-flex;justify-content:center;transition:transform var(--motion-duration-base);width:14px}.tree-node.is-expanded .tree-caret{transform:rotate(90deg)}.tree-label{color:var(--color-text-primary);min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.tree-level-1 .tree-label{color:var(--color-text-mono)}.tree-level-2 .tree-label{color:var(--color-text-secondary);font-weight:var(--font-weight-regular)}.tree-meta{align-items:center;display:flex;gap:var(--space-2);margin-left:auto}.tree-hash{color:var(--color-text-dim);font-size:var(--font-size-xs)}.tree-children{border-left:1px dashed var(--color-border-subtle);margin-left:18px;padding-left:var(--space-2)}.badge,.drift-pill,.source-badge,.filter-chip{align-items:center;border:1px solid transparent;border-radius:var(--radius-pill);display:inline-flex;font-family:var(--font-family-mono);font-size:10px;gap:var(--space-1);letter-spacing:var(--letter-spacing-chip);line-height:1;padding:3px 7px;text-transform:uppercase}.badge-level{background:var(--color-surface-overlay);color:var(--color-text-muted)}.badge-locked{background:var(--color-state-locked-bg);border-color:var(--color-state-locked-border);color:var(--color-state-locked-text)}.drift-dot{border-radius:50%;display:inline-block;height:6px;width:6px}.drift-pill,.drift-banner{background:var(--color-state-pending-bg);border-color:var(--color-state-pending-border);color:var(--color-state-pending-text)}.drift-drift{background:var(--color-state-drift-bg);border-color:var(--color-state-drift-border);color:var(--color-state-drift-text)}.drift-stale{background:var(--color-state-stale-bg);border-color:var(--color-state-stale-border);color:var(--color-state-stale-text)}.drift-locked{background:var(--color-state-locked-bg);border-color:var(--color-state-locked-border);color:var(--color-state-locked-text)}.drift-ok{background:var(--color-state-approved-bg);border-color:var(--color-state-approved-border);color:var(--color-state-approved-text)}.drift-banner{border-radius:var(--radius-md);display:flex;gap:var(--space-2);margin-bottom:var(--space-4);padding:var(--space-3)}.detail-panel{padding:var(--space-5);position:sticky;top:var(--space-6)}.detail-panel h3{color:var(--color-text-muted);font-size:var(--font-size-sm);letter-spacing:.06em;margin:0 0 var(--space-3);text-transform:uppercase}.kv{font-family:var(--font-family-mono);font-size:var(--font-size-sm)}.kv-row{border-bottom:1px dashed var(--color-border-subtle);display:flex;gap:var(--space-3);justify-content:space-between;padding:var(--space-1) 0}.kv-key{color:var(--color-text-muted)}.kv-value{color:var(--color-text-primary);overflow-wrap:anywhere;text-align:right}.code,.preview-body{background:var(--color-surface-code);color:var(--color-text-mono);font-family:var(--font-family-mono)}.code{border:1px solid var(--color-border-subtle);border-radius:var(--radius-md);margin-top:var(--space-3);overflow:auto;padding:var(--space-3)}.filter-bar{align-items:center;display:flex;gap:var(--space-2);margin-bottom:var(--space-4);padding:var(--space-3)}.filter-chip,.ghost-button{background:var(--color-surface-raised);border-color:var(--color-border-subtle);color:var(--color-text-muted);cursor:pointer;min-height:40px}.filter-chip.active,.filter-chip:hover,.ghost-button:hover{border-color:var(--color-border-default);color:var(--color-text-primary)}.filter-chip .count{background:var(--color-surface-code);border-radius:var(--radius-pill);padding:1px 5px}.filter-date{color:var(--color-text-muted);font-family:var(--font-family-mono);font-size:var(--font-size-sm);margin-left:auto}.topology-toolbar,.topology-status{background:var(--color-surface-panel);border:1px solid var(--color-border-subtle);border-radius:var(--radius-lg);margin-bottom:var(--space-4)}.topology-split{grid-template-columns:minmax(0,1.2fr) minmax(320px,1fr)}.topology-card{background:var(--color-surface-panel);border:1px solid var(--color-border-subtle);border-radius:var(--radius-lg);box-shadow:var(--shadow-card);min-height:280px;overflow:hidden}.topology-card-head{align-items:start;border-bottom:1px solid var(--color-border-subtle);display:flex;gap:var(--space-3);justify-content:space-between;padding:var(--space-4)}.topology-card-head h3{color:var(--color-text-primary);margin:0}.coverage-grid,.reason-list{display:grid;gap:var(--space-3);padding:var(--space-4)}.coverage-row,.reason-card,.module-placeholder{background:color-mix(in srgb,var(--color-surface-raised) 88%,transparent);border:1px solid var(--color-border-subtle);border-radius:var(--radius-md)}.coverage-row{padding:var(--space-3)}.coverage-row-main,.reason-card-head,.reason-card-meta{align-items:center;display:flex;gap:var(--space-2);justify-content:space-between}.coverage-row-meta,.reason-card-meta,.reason-description{color:var(--color-text-muted);font-family:var(--font-family-mono);font-size:var(--font-size-xs)}.coverage-row-meta,.reason-card-meta{margin-top:var(--space-2)}.coverage-path,.reason-card strong{color:var(--color-text-primary);font-family:var(--font-family-mono);overflow-wrap:anywhere}.coverage-chip-full,.reason-tier-always{background:#22c55e1f;border-color:#22c55e4d;color:#86efac}.coverage-chip-partial,.reason-tier-path{background:#facc151f;border-color:#facc154d;color:#fde68a}.coverage-chip-none,.reason-tier-description{background:#94a3b81f;border-color:#94a3b84d;color:#cbd5e1}.coverage-full{box-shadow:inset 2px 0 #22c55e73}.coverage-partial{box-shadow:inset 2px 0 #facc1573}.coverage-none{box-shadow:inset 2px 0 #94a3b840}.reason-card{padding:var(--space-3)}.reason-description{line-height:1.6;margin:var(--space-2) 0 0}.module-placeholder{padding:var(--space-6)}.module-placeholder strong{color:var(--color-text-primary);display:block;font-family:var(--font-family-mono);margin-bottom:var(--space-2)}.module-placeholder p{color:var(--color-text-muted);margin:0}.filter-label{color:var(--color-text-dim);font-size:var(--font-size-xs);letter-spacing:.08em;text-transform:uppercase}.history-toolbar{margin-bottom:var(--space-5)}.history-slider{flex:1}.history-layout{grid-template-columns:minmax(340px,1.05fr) minmax(0,1.4fr)}.history-timeline-panel{min-width:0}.history-timeline-list{display:grid;gap:var(--space-3);max-height:calc(100vh - 280px);overflow:auto;padding:var(--space-4)}.history-timeline-item{background:transparent;border:0;cursor:pointer;padding:0;text-align:left}.history-timeline-item .timeline-entry{margin-bottom:0}.history-timeline-item.selected .timeline-entry{box-shadow:inset 0 0 0 1px var(--color-source-ai-accent)}.history-state-head{align-items:start}.history-state-title{color:var(--color-text-primary);font-family:var(--font-family-mono)}.ghost-button{border:1px solid var(--color-border-subtle);border-radius:var(--radius-md);padding:var(--space-2) var(--space-3)}.empty-card{color:var(--color-text-muted);padding:var(--space-6);text-align:center}.lock-grid{display:grid;gap:var(--space-4);grid-template-columns:repeat(auto-fill,minmax(360px,1fr))}.lock-card,.timeline-entry{background:var(--color-surface-panel);border:1px solid var(--color-border-subtle);border-radius:var(--radius-lg);box-shadow:var(--shadow-card);overflow:hidden}.lock-card{display:flex;flex-direction:column}.lock-drift{border-left:3px solid var(--color-state-drift-border)}.lock-ok{border-left:3px solid var(--color-state-approved-border)}.lock-head,.lock-foot{align-items:center;display:flex;gap:var(--space-3);padding:var(--space-4)}.lock-head{border-bottom:1px solid var(--color-border-subtle)}.lock-foot{border-top:1px solid var(--color-border-subtle);justify-content:space-between}.lock-icon{align-items:center;border-radius:var(--radius-md);display:inline-flex;font-family:var(--font-family-mono);height:28px;justify-content:center;width:28px}.lock-drift .lock-icon{background:var(--color-state-drift-bg);color:var(--color-state-drift-text)}.lock-ok .lock-icon{background:var(--color-state-approved-bg);color:var(--color-state-approved-text)}.lock-title{display:flex;flex:1;flex-direction:column;min-width:0}.lock-title strong{color:var(--color-text-primary);font-family:var(--font-family-mono);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.lock-title span,.meta-line{color:var(--color-text-dim);font-family:var(--font-family-mono);font-size:var(--font-size-xs)}.lock-body{display:flex;flex:1;flex-direction:column;gap:var(--space-3);padding:var(--space-4)}.hash-block{font-family:var(--font-family-mono);font-size:var(--font-size-sm)}.hash-row{display:grid;gap:var(--space-2);grid-template-columns:110px minmax(0,1fr);padding:var(--space-1) 0}.hash-key{color:var(--color-text-muted);font-size:10px;letter-spacing:.06em;text-transform:uppercase}.hash-value{color:var(--color-text-primary);overflow-wrap:anywhere}.hash-value.is-stale,.hash-value.is-accent{color:var(--color-state-drift-text)}.hash-value.is-stale{text-decoration:line-through}.preview{border:1px solid var(--color-border-subtle);border-radius:var(--radius-md);overflow:hidden}.preview-head{align-items:center;background:var(--color-surface-panel);border-bottom:1px solid var(--color-border-subtle);color:var(--color-text-dim);display:flex;font-family:var(--font-family-mono);font-size:10px;justify-content:space-between;letter-spacing:.06em;padding:var(--space-1) var(--space-3);text-transform:uppercase}.preview-body{display:block;line-height:1.55;margin:0;min-height:92px;overflow:auto;padding:var(--space-3);white-space:pre-wrap}.line-add,.line-del,.line-ctx{display:block}.line-add{background:#22c55e14;color:#86efac}.line-del{background:#dc262614;color:#fca5a5;text-decoration:line-through}.line-num{color:var(--color-text-dim);display:inline-block;padding-right:var(--space-2);text-align:right;-webkit-user-select:none;user-select:none;width:28px}.action-button{align-items:center;border:1px solid var(--color-border-default);border-radius:var(--radius-md);cursor:pointer;display:inline-flex;font-weight:var(--font-weight-medium);gap:var(--space-2);min-height:40px;padding:var(--space-2) var(--space-4);transition:transform var(--motion-duration-fast),background var(--motion-duration-base)}.action-button:active{transform:scale(.98)}.action-approve{background:var(--color-action-primary);border-color:transparent;color:var(--color-action-primary-text)}.action-annotate{background:var(--color-source-human-bg);border-color:var(--color-source-human-border);color:var(--color-source-human-text)}.action-success{background:var(--color-state-approved-bg);border-color:var(--color-state-approved-border);color:var(--color-state-approved-text);cursor:default}.action-busy{pointer-events:none}.action-error{border-color:var(--color-action-danger)}.spinner{animation:spin .7s linear infinite;border:2px solid currentColor;border-right-color:transparent;border-radius:50%;height:14px;width:14px}.source-badge{background:var(--color-surface-raised);color:var(--color-text-muted);min-height:28px}button.source-badge{cursor:pointer}.source-badge-ai{background:var(--color-source-ai-bg);border-color:var(--color-source-ai-border);color:var(--color-source-ai-text)}.source-badge-human{background:var(--color-source-human-bg);border-color:var(--color-source-human-border);color:var(--color-source-human-text)}.source-badge-outline{background:transparent}.source-badge.is-selected{box-shadow:inset 0 0 0 1px currentColor}.source-badge-dot{background:currentColor;border-radius:50%;height:6px;width:6px}.col-headers{display:grid;gap:var(--space-4);grid-template-columns:1fr 1fr;margin-bottom:var(--space-3)}.col-head{align-items:center;border-radius:var(--radius-md);display:flex;font-family:var(--font-family-mono);justify-content:space-between;padding:var(--space-3)}.col-head.ai{background:var(--color-source-ai-bg);color:var(--color-source-ai-text)}.col-head.human{background:var(--color-source-human-bg);color:var(--color-source-human-text)}.timeline-grid{display:grid;gap:0;grid-template-columns:1fr 40px 1fr;position:relative}.axis{align-items:center;display:flex;grid-column:2;grid-row:1 / span 999;justify-content:center;pointer-events:none}.axis-line{align-self:stretch;background:linear-gradient(var(--color-border-subtle),var(--color-border-default),var(--color-border-subtle));width:2px}.timeline-entry{margin-bottom:var(--space-3);padding:var(--space-3) var(--space-4);position:relative}.timeline-ai{border-left:3px solid var(--color-source-ai-border);grid-column:1}.timeline-human{border-right:3px solid var(--color-source-human-border);grid-column:3}.dot-axis{border:2px solid var(--color-surface-canvas);border-radius:50%;height:10px;position:absolute;top:16px;width:10px}.dot-axis.ai{background:var(--color-source-ai-accent);right:-27px}.dot-axis.human{background:var(--color-source-human-accent);left:-27px}.timeline-head,.entry-meta,.entry-foot{align-items:center;display:flex;flex-wrap:wrap;gap:var(--space-2)}.entry-time{color:var(--color-text-muted);font-family:var(--font-family-mono);font-size:var(--font-size-xs);margin-left:auto}.entry-title{color:var(--color-text-primary);font-size:var(--font-size-md);font-weight:var(--font-weight-medium);margin:var(--space-2) 0 0}.entry-meta{color:var(--color-text-muted);font-family:var(--font-family-mono);font-size:var(--font-size-xs);margin-top:var(--space-2)}.meta-key{color:var(--color-text-dim)}.entry-body{color:var(--color-text-secondary);line-height:var(--line-height-loose);margin-top:var(--space-3)}.diff-badge,.commit-hash{color:var(--color-text-muted);font-family:var(--font-family-mono);font-size:var(--font-size-xs)}.diff-badge{background:var(--color-surface-code);border:1px solid var(--color-border-subtle);border-radius:var(--radius-pill);padding:2px 6px}.annotate-form{display:grid;gap:var(--space-2);margin-top:var(--space-3)}.annotate-form label{color:var(--color-text-muted);font-family:var(--font-family-mono);font-size:var(--font-size-xs);text-transform:uppercase}.timeline-empty{grid-column:1 / -1}.doctor-toolbar{margin-bottom:var(--space-5)}.doctor-layout{display:grid;gap:var(--space-5)}.doctor-summary-grid,.doctor-panels{display:grid;gap:var(--space-4);grid-template-columns:repeat(auto-fit,minmax(220px,1fr))}.doctor-card,.doctor-summary-card{background:var(--color-surface-panel);border:1px solid var(--color-border-subtle);border-radius:var(--radius-lg);box-shadow:var(--shadow-card)}.doctor-summary-card{display:flex;flex-direction:column;gap:var(--space-2);padding:var(--space-4)}.doctor-summary-label,.doctor-summary-detail,.doctor-card-head span{color:var(--color-text-muted);font-family:var(--font-family-mono);font-size:var(--font-size-xs)}.doctor-summary-label{letter-spacing:.08em;text-transform:uppercase}.doctor-summary-value{color:var(--color-text-primary);font-size:var(--font-size-lg)}.doctor-card{overflow:hidden}.doctor-card-head{align-items:center;border-bottom:1px solid var(--color-border-subtle);display:flex;justify-content:space-between;padding:var(--space-4)}.doctor-card-head h3{color:var(--color-text-primary);font-size:var(--font-size-md);margin:0}.doctor-entry-list,.doctor-check-list{display:grid}.doctor-entry,.doctor-check{display:grid;gap:var(--space-2);padding:var(--space-4)}.doctor-entry+.doctor-entry,.doctor-check+.doctor-check{border-top:1px solid var(--color-border-subtle)}.doctor-entry strong,.doctor-check strong{color:var(--color-text-primary);font-family:var(--font-family-mono);font-size:var(--font-size-sm)}.doctor-entry span,.doctor-check p{color:var(--color-text-muted);margin:0}.doctor-check-head{align-items:center;display:flex;gap:var(--space-3);justify-content:space-between}.doctor-check-warn{background:linear-gradient(180deg,var(--color-state-locked-bg),transparent 90%)}.doctor-check-error{background:linear-gradient(180deg,var(--color-state-stale-bg),transparent 90%)}.doctor-check-ok{background:linear-gradient(180deg,var(--color-state-approved-bg),transparent 90%)}.doctor-empty{border:0;border-radius:0;box-shadow:none;margin:0}.live-region{clip:rect(0 0 0 0);height:1px;overflow:hidden;position:absolute;white-space:nowrap;width:1px}@keyframes spin{to{transform:rotate(360deg)}}@keyframes pulse{50%{opacity:.45}}@media(prefers-reduced-motion:no-preference){.pulse,.drift-dot.drift-drift,.drift-dot.drift-stale{animation:pulse 2.4s infinite}}@media(prefers-reduced-motion:reduce){*,*:before,*:after{animation-duration:0ms!important;scroll-behavior:auto!important;transition-duration:0ms!important}}@media(max-width:960px){.app-shell{grid-template-columns:var(--layout-sidebar-width-collapsed) minmax(0,1fr)}.brand span:not(.brand-logo),.nav-item span:not(.dot),.nav-item small,.nav-section,.muted-nav{display:none}.sidebar{padding:var(--space-3) var(--space-2)}.nav-item{grid-template-columns:1fr;justify-items:center}}@media(max-width:720px){.app-shell,.view-split,.topology-split,.timeline-grid,.col-headers,.doctor-panels,.doctor-summary-grid{grid-template-columns:1fr}.sidebar{border-bottom:1px solid var(--color-border-subtle);border-right:0;display:flex;overflow-x:auto}.main{min-height:0}.header{align-items:flex-start;flex-direction:column;height:auto;padding:var(--space-3)}.view{padding:var(--space-4)}.detail-panel{position:static}.axis,.dot-axis{display:none}.timeline-ai,.timeline-human{grid-column:1}}
|