ccdesk 0.1.0 → 0.2.1
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/CHANGELOG.md +86 -0
- package/README.md +84 -67
- package/dist/server/agent-edit.js +77 -0
- package/dist/server/backups.js +129 -52
- package/dist/server/cli.js +21 -8
- package/dist/server/mcp-edit.js +263 -0
- package/dist/server/mutations.js +8 -3
- package/dist/server/scan/compare.js +34 -0
- package/dist/server/scan/context.js +6 -5
- package/dist/server/scan/env.js +8 -3
- package/dist/server/scan/files.js +118 -30
- package/dist/server/scan/index.js +29 -15
- package/dist/server/scan/instructions.js +11 -7
- package/dist/server/scan/inventory.js +134 -42
- package/dist/server/scan/mcp.js +88 -30
- package/dist/server/scan/memory.js +8 -9
- package/dist/server/scan/path-test.js +32 -10
- package/dist/server/scan/permissions.js +22 -6
- package/dist/server/scan/plugins.js +18 -11
- package/dist/server/scan/projects.js +7 -6
- package/dist/server/scan/sandbox.js +161 -0
- package/dist/server/scan/settings.js +33 -20
- package/dist/server/scan/tools.js +7 -3
- package/dist/server/scan/usage.js +119 -46
- package/dist/server/schema.js +15 -0
- package/dist/server/secrets.js +63 -0
- package/dist/server/server.js +222 -128
- package/dist/server/settings-edit.js +90 -21
- package/dist/server/watch.js +52 -10
- package/dist/server/write.js +58 -20
- package/dist/shared/compare.js +1 -0
- package/dist/shared/hook-events.json +35 -0
- package/dist/shared/mcp-server.schema.json +25 -0
- package/dist/shared/memory.js +4 -0
- package/dist/shared/scopes.js +3 -1
- package/dist/shared/types.js +1 -1
- package/dist/ui/assets/hanken-grotesk-cyrillic-ext-wght-normal-D_UHSL_T.woff2 +0 -0
- package/dist/ui/assets/index-D5XqP5c_.css +1 -0
- package/dist/ui/assets/index-XqJwI3k9.js +60 -0
- package/dist/ui/assets/jetbrains-mono-cyrillic-ext-wght-normal-EocZY2iu.woff2 +0 -0
- package/dist/ui/index.html +2 -2
- package/package.json +10 -4
- package/dist/ui/assets/index-BM4K7hSO.css +0 -1
- package/dist/ui/assets/index-CN3vkVjv.js +0 -45
package/dist/server/watch.js
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { watch } from "node:fs";
|
|
2
|
+
import { stat } from "node:fs/promises";
|
|
2
3
|
import { basename, dirname, join } from "node:path";
|
|
4
|
+
import { storeDirName } from "../shared/memory.js";
|
|
3
5
|
import {} from "../shared/types.js";
|
|
4
6
|
import {} from "./scan/env.js";
|
|
5
7
|
import { isNetworkPath, mainWorktreeRoot } from "./scan/files.js";
|
|
6
|
-
import {
|
|
8
|
+
import { scanDirectory } from "./scan/index.js";
|
|
7
9
|
/** How long one request waits for a change before answering "nothing changed". */
|
|
8
10
|
const WAIT_MS = 25_000;
|
|
9
11
|
/** Editors write a file in several steps; one change covers them all. */
|
|
@@ -71,18 +73,53 @@ export function dependencies(view) {
|
|
|
71
73
|
/** One set of file watchers per directory a page is looking at, shared by its requests. */
|
|
72
74
|
export class Watcher {
|
|
73
75
|
env;
|
|
76
|
+
scanOf;
|
|
74
77
|
subscriptions = new Map();
|
|
75
|
-
|
|
78
|
+
/** What the latest scan of each directory read, kept for a watch that starts after the scan. */
|
|
79
|
+
scanned = new Map();
|
|
80
|
+
latest = new Map();
|
|
81
|
+
constructor(env, scanOf = scanDirectory) {
|
|
76
82
|
this.env = env;
|
|
83
|
+
this.scanOf = scanOf;
|
|
77
84
|
}
|
|
78
|
-
/**
|
|
79
|
-
|
|
80
|
-
const
|
|
85
|
+
/** Scans `dir` and remembers what the scan read, so those files are watched too. */
|
|
86
|
+
async scan(dir, profile = "interactive") {
|
|
87
|
+
const startedAt = Date.now();
|
|
88
|
+
const stateFile = await this.stateFile();
|
|
89
|
+
const view = await this.scanOf(this.env, dir, profile);
|
|
90
|
+
const subscription = this.subscriptions.get(view.path);
|
|
91
|
+
// Only a watched directory's view can be known to be current later.
|
|
92
|
+
if (subscription)
|
|
93
|
+
this.latest.set(view.path, { view, startedAt, stateFile });
|
|
81
94
|
const extra = dependencies(view);
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
95
|
+
this.scanned.set(view.path, extra);
|
|
96
|
+
if (subscription && JSON.stringify(extra) !== JSON.stringify(subscription.extra)) {
|
|
97
|
+
subscription.extra = extra;
|
|
98
|
+
void this.rewatch(view.path, subscription);
|
|
99
|
+
}
|
|
100
|
+
return view;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* The view of `dir`: the latest scan while it is known to be current, else a new scan. It is
|
|
104
|
+
* current only while watchers that started before it have seen no change since it started, and
|
|
105
|
+
* ~/.claude.json, which is not watched, is as it was. Only an interactive scan is reused, as the
|
|
106
|
+
* path tester describes an interactive session.
|
|
107
|
+
*/
|
|
108
|
+
async view(dir) {
|
|
109
|
+
const latest = this.latest.get(dir);
|
|
110
|
+
const subscription = this.subscriptions.get(dir);
|
|
111
|
+
if (latest?.view.profile === "interactive" &&
|
|
112
|
+
subscription &&
|
|
113
|
+
subscription.watchingSince < latest.startedAt &&
|
|
114
|
+
subscription.touchedAt < latest.startedAt &&
|
|
115
|
+
latest.stateFile === (await this.stateFile()))
|
|
116
|
+
return latest.view;
|
|
117
|
+
return this.scan(dir);
|
|
118
|
+
}
|
|
119
|
+
/** ~/.claude.json's size and time; Claude Code rewrites it too often to watch. */
|
|
120
|
+
async stateFile() {
|
|
121
|
+
const info = await stat(this.env.claudeJson).catch(() => null);
|
|
122
|
+
return info ? `${info.size}:${info.mtimeMs}` : "missing";
|
|
86
123
|
}
|
|
87
124
|
/**
|
|
88
125
|
* Answers once something changed after `since` (ms since the epoch), or after `timeoutMs` with
|
|
@@ -122,8 +159,10 @@ export class Watcher {
|
|
|
122
159
|
const subscription = {
|
|
123
160
|
watchers: [],
|
|
124
161
|
changedAt: 0,
|
|
162
|
+
touchedAt: 0,
|
|
163
|
+
watchingSince: Number.POSITIVE_INFINITY,
|
|
125
164
|
waiters: new Set(),
|
|
126
|
-
extra: [],
|
|
165
|
+
extra: this.scanned.get(dir) ?? [],
|
|
127
166
|
};
|
|
128
167
|
this.subscriptions.set(dir, subscription);
|
|
129
168
|
await this.rewatch(dir, subscription);
|
|
@@ -138,6 +177,7 @@ export class Watcher {
|
|
|
138
177
|
for (const w of subscription.watchers)
|
|
139
178
|
w.close();
|
|
140
179
|
this.subscriptions.delete(dir);
|
|
180
|
+
this.latest.delete(dir);
|
|
141
181
|
}
|
|
142
182
|
/** (Re)creates the watchers, so a folder created since, such as a new .claude, is watched. */
|
|
143
183
|
async rewatch(dir, subscription) {
|
|
@@ -153,6 +193,7 @@ export class Watcher {
|
|
|
153
193
|
const changed = name === null ? "" : String(name);
|
|
154
194
|
if (target.names && !target.names.includes(basename(changed)))
|
|
155
195
|
return;
|
|
196
|
+
subscription.touchedAt = Date.now();
|
|
156
197
|
clearTimeout(subscription.settle);
|
|
157
198
|
subscription.settle = setTimeout(() => {
|
|
158
199
|
subscription.changedAt = Date.now();
|
|
@@ -168,5 +209,6 @@ export class Watcher {
|
|
|
168
209
|
// A folder that does not exist yet is caught by watching its parent.
|
|
169
210
|
}
|
|
170
211
|
}
|
|
212
|
+
subscription.watchingSince = Date.now();
|
|
171
213
|
}
|
|
172
214
|
}
|
package/dist/server/write.js
CHANGED
|
@@ -3,7 +3,7 @@ import { chmod, link, lstat, mkdir, open, readFile, realpath, rename, rm, writeF
|
|
|
3
3
|
import { basename, dirname, join } from "node:path";
|
|
4
4
|
import {} from "../shared/types.js";
|
|
5
5
|
import {} from "./scan/env.js";
|
|
6
|
-
import {
|
|
6
|
+
import { isCredentials, isSecretPath, revisionOf } from "./scan/files.js";
|
|
7
7
|
/** A change ccdesk refuses on purpose; the server answers 409 with the message. */
|
|
8
8
|
export class RefusedError extends Error {
|
|
9
9
|
}
|
|
@@ -29,16 +29,6 @@ export function backupRoot(env) {
|
|
|
29
29
|
function realOrNull(path) {
|
|
30
30
|
return realpath(path).catch(() => null);
|
|
31
31
|
}
|
|
32
|
-
/**
|
|
33
|
-
* Whether a path is Claude Code's credentials file: by name, through any link on the way, or by
|
|
34
|
-
* ending at the same file as `<config>/.credentials.json`, so one kept under another name counts.
|
|
35
|
-
*/
|
|
36
|
-
export async function isCredentials(env, path) {
|
|
37
|
-
if (await leadsToSecret(path))
|
|
38
|
-
return true;
|
|
39
|
-
const real = await realOrNull(path);
|
|
40
|
-
return real !== null && real === (await realOrNull(join(env.claudeDir, ".credentials.json")));
|
|
41
|
-
}
|
|
42
32
|
/** The file a path leads to after links; a missing file keeps its own path. */
|
|
43
33
|
async function target(env, path) {
|
|
44
34
|
if (await isCredentials(env, path))
|
|
@@ -75,7 +65,8 @@ export async function readIfExists(env, path) {
|
|
|
75
65
|
* written to a temporary file first and the revision is checked again just before the rename, so
|
|
76
66
|
* an edit made elsewhere while ccdesk was saving is kept and the save refused. A symlink is written
|
|
77
67
|
* through, so links into a dotfiles repository survive; the temporary file is new, unguessable and
|
|
78
|
-
* created exclusively, so nothing planted beside the target can redirect the write.
|
|
68
|
+
* created exclusively, so nothing planted beside the target can redirect the write. A file with
|
|
69
|
+
* other hard links is refused, since the rename would leave them on the old text.
|
|
79
70
|
*/
|
|
80
71
|
export async function writeAtomic(env, path, content, expected) {
|
|
81
72
|
const file = await target(env, path);
|
|
@@ -83,6 +74,8 @@ export async function writeAtomic(env, path, content, expected) {
|
|
|
83
74
|
throw new RefusedError(STATE_FILE);
|
|
84
75
|
await mkdir(dirname(file), { recursive: true });
|
|
85
76
|
const existing = await lstat(file).catch(() => null);
|
|
77
|
+
if (existing && existing.nlink > 1)
|
|
78
|
+
throw new RefusedError(`${path} has other hard links, and saving it would split it from its other names; edit it in your editor.`);
|
|
86
79
|
const temp = join(dirname(file), `.${basename(file)}.ccdesk-${randomBytes(6).toString("hex")}`);
|
|
87
80
|
const handle = await open(temp, "wx", 0o600);
|
|
88
81
|
try {
|
|
@@ -111,7 +104,15 @@ export async function writeAtomic(env, path, content, expected) {
|
|
|
111
104
|
export async function removeExactly(path, expected) {
|
|
112
105
|
const aside = join(dirname(path), `.${basename(path)}.ccdesk-${randomBytes(6).toString("hex")}`);
|
|
113
106
|
await rename(path, aside);
|
|
114
|
-
|
|
107
|
+
let text;
|
|
108
|
+
try {
|
|
109
|
+
text = await readFile(aside, "utf8");
|
|
110
|
+
}
|
|
111
|
+
catch (error) {
|
|
112
|
+
await rename(aside, path);
|
|
113
|
+
throw error;
|
|
114
|
+
}
|
|
115
|
+
if (revisionOf(text) === expected) {
|
|
115
116
|
await rm(aside);
|
|
116
117
|
return true;
|
|
117
118
|
}
|
|
@@ -132,8 +133,50 @@ export async function removeExactly(path, expected) {
|
|
|
132
133
|
return false;
|
|
133
134
|
}
|
|
134
135
|
export const MANIFEST = "ccdesk-backup.json";
|
|
136
|
+
/** Takes a change out of Recent changes, for one whose effect was undone again right away. */
|
|
137
|
+
export function forgetChange(env, id) {
|
|
138
|
+
return rm(join(backupRoot(env), id), { recursive: true, force: true });
|
|
139
|
+
}
|
|
135
140
|
/** The copy of the target inside a backup folder. */
|
|
136
141
|
export const SNAPSHOT = "before";
|
|
142
|
+
/** For a change made through `claude mcp`: the affected servers after it, values hidden. */
|
|
143
|
+
export const SHOWN_AFTER = "after";
|
|
144
|
+
/** A new, empty backup folder that only you can read, and its id. */
|
|
145
|
+
async function newBackup(env, kind) {
|
|
146
|
+
const createdAt = new Date().toISOString();
|
|
147
|
+
const id = `${createdAt.replace(/[:.]/g, "-")}-${randomBytes(3).toString("hex")}-${kind}`;
|
|
148
|
+
const root = backupRoot(env);
|
|
149
|
+
const dir = join(root, id);
|
|
150
|
+
await mkdir(dir, { recursive: true, mode: 0o700 });
|
|
151
|
+
await chmod(root, 0o700);
|
|
152
|
+
return { createdAt, id, dir };
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Lists a change Claude Code made through `claude mcp` in Recent changes. ccdesk cannot undo it,
|
|
156
|
+
* so it keeps only the affected servers before and after as the page shows them, secrets hidden,
|
|
157
|
+
* and the commands that would put them back; never a value the page does not show.
|
|
158
|
+
*/
|
|
159
|
+
export async function recordClaudeMcpChange(env, record) {
|
|
160
|
+
const { createdAt, dir } = await newBackup(env, "mcp-claude");
|
|
161
|
+
const manifest = {
|
|
162
|
+
kind: "mcp-claude",
|
|
163
|
+
target: record.target,
|
|
164
|
+
createdAt,
|
|
165
|
+
before: revisionOf(record.shownBefore),
|
|
166
|
+
after: revisionOf(record.shownAfter),
|
|
167
|
+
undoCommands: record.undoCommands,
|
|
168
|
+
};
|
|
169
|
+
try {
|
|
170
|
+
await writeFile(join(dir, SNAPSHOT), record.shownBefore, { mode: 0o600 });
|
|
171
|
+
await writeFile(join(dir, SHOWN_AFTER), record.shownAfter, { mode: 0o600 });
|
|
172
|
+
// Written last, so a folder without it is never listed.
|
|
173
|
+
await writeFile(join(dir, MANIFEST), `${JSON.stringify(manifest, null, 2)}\n`, { mode: 0o600 });
|
|
174
|
+
}
|
|
175
|
+
catch (error) {
|
|
176
|
+
await rm(dir, { recursive: true, force: true });
|
|
177
|
+
throw error;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
137
180
|
/**
|
|
138
181
|
* Makes a change and records it in Recent changes. The file's previous text and a manifest are put
|
|
139
182
|
* in a new backup folder first, so a change interrupted halfway can still be restored; the manifest
|
|
@@ -142,12 +185,7 @@ export const SNAPSHOT = "before";
|
|
|
142
185
|
* otherwise it stays, marked partial, so it can be put back. Backups can hold settings with secrets, so only the user can read them.
|
|
143
186
|
*/
|
|
144
187
|
export async function recordChange(env, record, change) {
|
|
145
|
-
const createdAt =
|
|
146
|
-
const id = `${createdAt.replace(/[:.]/g, "-")}-${randomBytes(3).toString("hex")}-${record.kind}`;
|
|
147
|
-
const root = backupRoot(env);
|
|
148
|
-
const dir = join(root, id);
|
|
149
|
-
await mkdir(dir, { recursive: true, mode: 0o700 });
|
|
150
|
-
await chmod(root, 0o700);
|
|
188
|
+
const { createdAt, id, dir } = await newBackup(env, record.kind);
|
|
151
189
|
const manifest = {
|
|
152
190
|
kind: record.kind,
|
|
153
191
|
target: record.target,
|
|
@@ -191,5 +229,5 @@ export async function recordChange(env, record, change) {
|
|
|
191
229
|
}
|
|
192
230
|
manifest.after = result.revision;
|
|
193
231
|
await writeManifest();
|
|
194
|
-
return { ...result, id };
|
|
232
|
+
return { ...result, id, created: record.before === null };
|
|
195
233
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import {} from "./types.js";
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
[
|
|
2
|
+
"PreToolUse",
|
|
3
|
+
"PostToolUse",
|
|
4
|
+
"PostToolUseFailure",
|
|
5
|
+
"PostToolBatch",
|
|
6
|
+
"Notification",
|
|
7
|
+
"UserPromptSubmit",
|
|
8
|
+
"UserPromptExpansion",
|
|
9
|
+
"SessionStart",
|
|
10
|
+
"SessionEnd",
|
|
11
|
+
"Stop",
|
|
12
|
+
"StopFailure",
|
|
13
|
+
"SubagentStart",
|
|
14
|
+
"SubagentStop",
|
|
15
|
+
"PreCompact",
|
|
16
|
+
"PostCompact",
|
|
17
|
+
"PreModelSwitch",
|
|
18
|
+
"PostModelSwitch",
|
|
19
|
+
"PermissionRequest",
|
|
20
|
+
"PermissionDenied",
|
|
21
|
+
"Setup",
|
|
22
|
+
"TeammateIdle",
|
|
23
|
+
"TaskCreated",
|
|
24
|
+
"TaskCompleted",
|
|
25
|
+
"Elicitation",
|
|
26
|
+
"ElicitationResult",
|
|
27
|
+
"ConfigChange",
|
|
28
|
+
"WorktreeCreate",
|
|
29
|
+
"WorktreeRemove",
|
|
30
|
+
"InstructionsLoaded",
|
|
31
|
+
"CwdChanged",
|
|
32
|
+
"FileChanged",
|
|
33
|
+
"DirectoryAdded",
|
|
34
|
+
"MessageDisplay"
|
|
35
|
+
]
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
+
"$comment": "One server entry of .mcp.json, ~/.claude.json or claude mcp add-json, with the fields https://code.claude.com/docs/en/mcp defines. Fields it does not name are left alone.",
|
|
4
|
+
"type": "object",
|
|
5
|
+
"properties": {
|
|
6
|
+
"type": { "enum": ["stdio", "http", "streamable-http", "sse", "ws"] },
|
|
7
|
+
"command": { "type": "string" },
|
|
8
|
+
"args": { "type": "array", "items": { "type": "string" } },
|
|
9
|
+
"env": { "type": "object", "additionalProperties": { "type": "string" } },
|
|
10
|
+
"url": { "type": "string" },
|
|
11
|
+
"headers": { "type": "object", "additionalProperties": { "type": "string" } },
|
|
12
|
+
"headersHelper": { "type": "string" },
|
|
13
|
+
"oauth": {
|
|
14
|
+
"type": "object",
|
|
15
|
+
"properties": {
|
|
16
|
+
"clientId": { "type": "string" },
|
|
17
|
+
"callbackPort": { "type": "integer" },
|
|
18
|
+
"authServerMetadataUrl": { "type": "string" },
|
|
19
|
+
"scopes": { "type": "string" }
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
"timeout": { "type": "number" },
|
|
23
|
+
"alwaysLoad": { "type": "boolean" }
|
|
24
|
+
}
|
|
25
|
+
}
|
package/dist/shared/scopes.js
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import scopeTable from "./settings-scopes.json" with { type: "json" };
|
|
2
2
|
import {} from "./types.js";
|
|
3
|
-
const EVERY_FILE = ["user", "project", "local", "managed"];
|
|
3
|
+
export const EVERY_FILE = ["user", "project", "local", "managed"];
|
|
4
4
|
/** Settings files by precedence: managed beats local beats project beats user. */
|
|
5
5
|
export const HIGHEST_FIRST = ["managed", "local", "project", "user"];
|
|
6
6
|
/** The settings files ccdesk writes to; managed settings belong to your organisation. */
|
|
7
7
|
export const EDITABLE_SCOPES = ["user", "project", "local"];
|
|
8
|
+
/** The permission lists, in the order ccdesk shows them. */
|
|
9
|
+
export const PERMISSION_LISTS = ["allow", "ask", "deny"];
|
|
8
10
|
/** Keys Claude Code reads only from some files, from the settings index (`pnpm scopes`). */
|
|
9
11
|
const SCOPE_LIMITED_KEYS = scopeTable.limited;
|
|
10
12
|
export function allowedScopes(key) {
|
package/dist/shared/types.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
// Shapes the server sends to the UI. Paths are absolute; the UI shortens them for display.
|
|
2
|
-
export
|
|
2
|
+
export const SESSION_PROFILES = ["interactive", "print"];
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
@font-face{font-family:Hanken Grotesk Variable;font-style:normal;font-display:swap;font-weight:100 900;src:url(/assets/hanken-grotesk-cyrillic-ext-wght-normal-D_UHSL_T.woff2)format("woff2-variations");unicode-range:U+460-52F,U+1C80-1C8A,U+20B4,U+2DE0-2DFF,U+A640-A69F,U+FE2E-FE2F}@font-face{font-family:Hanken Grotesk Variable;font-style:normal;font-display:swap;font-weight:100 900;src:url(/assets/hanken-grotesk-vietnamese-wght-normal-CHiFlh_0.woff2)format("woff2-variations");unicode-range:U+102-103,U+110-111,U+128-129,U+168-169,U+1A0-1A1,U+1AF-1B0,U+300-301,U+303-304,U+308-309,U+323,U+329,U+1EA0-1EF9,U+20AB}@font-face{font-family:Hanken Grotesk Variable;font-style:normal;font-display:swap;font-weight:100 900;src:url(/assets/hanken-grotesk-latin-ext-wght-normal-Dg-wlmqe.woff2)format("woff2-variations");unicode-range:U+100-2BA,U+2BD-2C5,U+2C7-2CC,U+2CE-2D7,U+2DD-2FF,U+304,U+308,U+329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:Hanken Grotesk Variable;font-style:normal;font-display:swap;font-weight:100 900;src:url(/assets/hanken-grotesk-latin-wght-normal-CaVRRdDk.woff2)format("woff2-variations");unicode-range:U+??,U+131,U+152-153,U+2BB-2BC,U+2C6,U+2DA,U+2DC,U+304,U+308,U+329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:JetBrains Mono Variable;font-style:normal;font-display:swap;font-weight:100 800;src:url(/assets/jetbrains-mono-cyrillic-ext-wght-normal-EocZY2iu.woff2)format("woff2-variations");unicode-range:U+460-52F,U+1C80-1C8A,U+20B4,U+2DE0-2DFF,U+A640-A69F,U+FE2E-FE2F}@font-face{font-family:JetBrains Mono Variable;font-style:normal;font-display:swap;font-weight:100 800;src:url(/assets/jetbrains-mono-cyrillic-wght-normal-D73BlboJ.woff2)format("woff2-variations");unicode-range:U+301,U+400-45F,U+490-491,U+4B0-4B1,U+2116}@font-face{font-family:JetBrains Mono Variable;font-style:normal;font-display:swap;font-weight:100 800;src:url(/assets/jetbrains-mono-greek-wght-normal-Bw9x6K1M.woff2)format("woff2-variations");unicode-range:U+370-377,U+37A-37F,U+384-38A,U+38C,U+38E-3A1,U+3A3-3FF}@font-face{font-family:JetBrains Mono Variable;font-style:normal;font-display:swap;font-weight:100 800;src:url(/assets/jetbrains-mono-vietnamese-wght-normal-Bt-aOZkq.woff2)format("woff2-variations");unicode-range:U+102-103,U+110-111,U+128-129,U+168-169,U+1A0-1A1,U+1AF-1B0,U+300-301,U+303-304,U+308-309,U+323,U+329,U+1EA0-1EF9,U+20AB}@font-face{font-family:JetBrains Mono Variable;font-style:normal;font-display:swap;font-weight:100 800;src:url(/assets/jetbrains-mono-latin-ext-wght-normal-DBQx-q_a.woff2)format("woff2-variations");unicode-range:U+100-2BA,U+2BD-2C5,U+2C7-2CC,U+2CE-2D7,U+2DD-2FF,U+304,U+308,U+329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:JetBrains Mono Variable;font-style:normal;font-display:swap;font-weight:100 800;src:url(/assets/jetbrains-mono-latin-wght-normal-B9CIFXIH.woff2)format("woff2-variations");unicode-range:U+??,U+131,U+152-153,U+2BB-2BC,U+2C6,U+2DA,U+2DC,U+304,U+308,U+329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}:root{--paper:#fbfbf7;--panel:#fff;--sunk:#f1f0ea;--ink:#141414;--ink-2:#4a4a45;--muted:#6b6a63;--rule:#dcdad1;--rule-strong:#141414;--sel:#fff3c4;--sel-ink:#141414;--hover:#f4f3ec;--focus:#0072b2;--ok:#1d6b47;--warn:#8a5a00;--warn-bg:#fff1d6;--bad:#a3361c;--bad-bg:#fde7e0;--on-bad:#fff;--line-managed:#b84f00;--line-user:#0072b2;--line-project:#007a5a;--line-local:#e69f00;--line-memory:#cc79a7;--line-plugin:#5f5f5a;--on-managed:#fff;--on-user:#fff;--on-project:#fff;--on-local:#141414;--on-memory:#141414;--on-plugin:#fff;--sans:"Hanken Grotesk Variable", "Helvetica Neue", Helvetica, "Nimbus Sans", "Liberation Sans", Arial, sans-serif;--mono:"JetBrains Mono Variable", ui-monospace, "JetBrains Mono", "SF Mono", Menlo, Consolas, "Liberation Mono", monospace;--row:32px;--head:92px;--rail:248px;--inspector:clamp(440px, 30vw, 600px);--lightningcss-light:initial;--lightningcss-dark: ;color-scheme:light}@media (prefers-color-scheme:dark){:root:not([data-theme=light]){--paper:#131312;--panel:#1a1a18;--sunk:#222220;--ink:#ece8de;--ink-2:#c7c2b6;--muted:#938f84;--rule:#2c2b28;--rule-strong:#ece8de;--sel:#33302a;--sel-ink:#fff6e0;--hover:#1d1d1b;--focus:#f2c14e;--ok:#6fcf97;--warn:#f2c14e;--warn-bg:#2b2512;--bad:#ff8a73;--bad-bg:#371b15;--on-bad:#131312;--line-managed:#ff7a3d;--line-user:#4aa8e8;--line-project:#2fbf8f;--line-local:#f2b233;--line-memory:#e391bd;--line-plugin:#8d95a5;--on-managed:#131312;--on-user:#131312;--on-project:#131312;--on-plugin:#131312;--lightningcss-light: ;--lightningcss-dark:initial;color-scheme:dark}}:root[data-theme=dark]{--paper:#131312;--panel:#1a1a18;--sunk:#222220;--ink:#ece8de;--ink-2:#c7c2b6;--muted:#938f84;--rule:#2c2b28;--rule-strong:#ece8de;--sel:#33302a;--sel-ink:#fff6e0;--hover:#1d1d1b;--focus:#f2c14e;--ok:#6fcf97;--warn:#f2c14e;--warn-bg:#2b2512;--bad:#ff8a73;--bad-bg:#371b15;--on-bad:#131312;--line-managed:#ff7a3d;--line-user:#4aa8e8;--line-project:#2fbf8f;--line-local:#f2b233;--line-memory:#e391bd;--line-plugin:#8d95a5;--on-managed:#131312;--on-user:#131312;--on-project:#131312;--on-plugin:#131312;--lightningcss-light: ;--lightningcss-dark:initial;color-scheme:dark}*{box-sizing:border-box}html,body,#root{height:100%}body{background:var(--paper);color:var(--ink);font:15px / 1.45 var(--sans);font-variant-numeric:tabular-nums;-webkit-font-smoothing:antialiased;margin:0}::selection{background:var(--sel);color:var(--sel-ink)}:focus-visible{outline:2px solid var(--focus);outline-offset:2px}*{scrollbar-width:thin;scrollbar-color:var(--rule) transparent}button,input{font:inherit;color:inherit}button{cursor:pointer}.mono,code,pre,kbd{font-family:var(--mono);font-size:13.5px}kbd{border:1px solid var(--rule);color:var(--muted);background:var(--panel);border-bottom-width:2px;border-radius:4px;padding:0 5px;font-size:12px}.muted{color:var(--muted)}.small{font-size:13.5px}.icon{flex-shrink:0;display:block}.app{grid-template-columns:var(--rail) minmax(0, 1fr);height:100%;display:grid;overflow:hidden}.main{flex-direction:column;min-width:0;display:flex;overflow:hidden}.workspace{grid-template-columns:minmax(0, 1fr) var(--inspector);height:100%;min-height:0;display:grid}.workspace.no-details{grid-template-columns:minmax(0,1fr)}.work-main{min-width:0;padding:0 28px 64px;overflow-y:auto;container-type:inline-size}.rail{background:var(--panel);border-right:1px solid var(--rule);flex-direction:column;gap:2px;padding:14px 10px 32px;display:flex;overflow-y:auto}.brand{letter-spacing:-.01em;align-items:center;gap:8px;padding:2px 6px 14px;font-size:16px;font-weight:700;display:flex}.brand-link{color:inherit;align-items:center;gap:8px;text-decoration:none;display:inline-flex}.brand-link:hover span{text-underline-offset:3px;text-decoration:underline}.brand .text-size{margin-left:auto}.brand .text-size button{justify-content:center;width:24px;padding:0}.brand .text-size button.smaller{font-size:11px}.brand .text-size button.larger{font-size:15px}html[data-text-size=smaller] #root{zoom:.9}html[data-text-size=larger] #root{zoom:1.15}.rail-group{color:var(--muted);justify-content:space-between;align-items:center;margin:14px 6px 4px;font-size:13px;font-weight:600;display:flex}.rail-group.toggle{text-align:left;background:0 0;border:0;padding:0}.rail-group.toggle:hover{color:var(--ink)}.rail-list{margin:0;padding:0;list-style:none}.rail-item{text-align:left;background:0 0;border:0;border-radius:6px;align-items:center;gap:8px;width:100%;height:30px;padding:0 8px;font-size:14.5px;display:flex}.rail-item:hover{background:var(--hover)}.rail-item.current{background:var(--sel);color:var(--sel-ink);font-weight:600}.rail-item.gone .rail-label{text-decoration:line-through;-webkit-text-decoration-color:var(--muted);text-decoration-color:var(--muted)}.rail-label{text-overflow:ellipsis;white-space:nowrap;flex:1;overflow:hidden}.rail-count{font-family:var(--mono);color:var(--muted);font-size:12.5px}.rail-item.current .rail-count{color:inherit;opacity:.75}.rail-filter,.filter,.palette-input{border:1px solid var(--rule);background:var(--paper);height:30px;color:var(--muted);border-radius:6px;align-items:center;gap:6px;padding:0 8px;display:flex}.rail-filter{margin:12px 0 0}.rail-filter input,.filter input,.palette-input input{min-width:0;color:var(--ink);background:0 0;border:0;outline:0;flex:1}.rail-filter:focus-within,.filter:focus-within{border-color:var(--focus)}input::placeholder{color:var(--muted)}.dir-head{z-index:3;background:var(--paper);height:var(--head);grid-template-rows:32px 28px;grid-template-columns:minmax(0,1fr) auto;align-items:center;gap:8px 16px;padding:16px 0 8px;display:grid;position:sticky;top:0}.dir-title{align-items:center;gap:10px;min-width:0;display:flex}.dir-title h1{letter-spacing:-.01em;white-space:nowrap;text-overflow:ellipsis;min-width:0;margin:0;font-size:19px;font-weight:700;overflow:hidden}.dir-title .flag,.session-line{color:var(--muted);align-items:center;gap:8px;margin:4px 0 12px;font-size:14px;display:flex}.section-index{scrollbar-width:none;grid-column:1/-1;gap:2px;display:flex;overflow-x:auto}.section-index a{height:26px;color:var(--ink-2);white-space:nowrap;border-radius:6px;align-items:center;gap:6px;padding:0 6px;font-size:14px;text-decoration:none;display:inline-flex}.section-index a:hover{background:var(--hover);color:var(--ink)}.section-index a.empty{color:var(--muted)}.section-index kbd{padding:0 4px}@container (width<=1100px){.section-index kbd{display:none}}@container (width<=840px){.section-index .seg-count{display:none}}@container (width<=680px){.section-index a{padding:0 3px;font-size:12.5px}}.seg-count{font-family:var(--mono);color:var(--muted);font-size:12px}[aria-pressed=true] .seg-count{color:inherit;opacity:.75}.tt-section{scroll-margin-top:var(--head)}.via-agents{vertical-align:middle;font-family:var(--mono);margin-left:8px}.vis-personal{color:var(--muted)}.toolbar{align-items:center;gap:8px;display:flex}.filter{width:240px}@container (width<=960px){.filter{width:140px}.palette-label{display:none}}.palette-button{border:1px solid var(--rule);background:var(--panel);border-radius:6px;align-items:center;gap:8px;height:30px;padding:0 10px;font-size:14px;display:flex}.palette-button:hover{border-color:var(--ink-2)}.route{border-top:2px solid var(--rule-strong);border-bottom:1px solid var(--rule);margin:4px 0 8px}.route-stops{margin:0;padding:0;list-style:none;display:flex;position:relative}.stop{flex:2 1 0;min-width:0}.stop.idle{flex:1 1 0}.route-stops:before{content:"";background:var(--ink);height:3px;position:absolute;top:25px;left:22px;right:22px}.stop button{text-align:left;background:0 0;border:0;border-radius:6px;grid-template-rows:22px auto auto;justify-items:start;gap:4px;width:100%;padding:14px 8px 12px;display:grid;position:relative}.stop button:hover{background:var(--hover)}.stop button.on{background:var(--sunk)}.stop-node{background:var(--paper);border-radius:999px;padding:0 6px 0 0;display:flex}.stop button:hover .stop-node,.stop button.on .stop-node{background:0 0}.stop-name{font-size:14.5px;font-weight:700}.stop-facts{font-family:var(--mono);color:var(--muted);font-size:12.5px}.stop.idle .stop-name{color:var(--muted);font-weight:500}.stop.lit .bullet{box-shadow:0 0 0 2px var(--paper), 0 0 0 4px var(--ink)}.stop button.on .stop-name{text-underline-offset:3px;text-decoration:underline;text-decoration-thickness:2px}.bullet{width:22px;height:22px;font:700 11.5px / 1 var(--sans);background:var(--bullet);color:var(--bullet-ink);border:2px solid var(--bullet);border-radius:999px;flex-shrink:0;place-items:center;transition:box-shadow .16s cubic-bezier(.16,1,.3,1);display:inline-grid}.bullet.hollow{background:var(--paper);color:var(--bullet)}.bullet.hollow.bullet-local,.bullet.hollow.bullet-memory{color:var(--ink)}.bullet-managed{--bullet:var(--line-managed);--bullet-ink:var(--on-managed)}.bullet-user{--bullet:var(--line-user);--bullet-ink:var(--on-user)}.bullet-project{--bullet:var(--line-project);--bullet-ink:var(--on-project)}.bullet-local{--bullet:var(--line-local);--bullet-ink:var(--on-local)}.bullet-memory{--bullet:var(--line-memory);--bullet-ink:var(--on-memory)}.bullet-plugin{--bullet:var(--line-plugin);--bullet-ink:var(--on-plugin)}.tt .bullet{vertical-align:middle}.tt .bullet,.palette .bullet,.layers .bullet{width:20px;height:20px;font-size:10.5px}.tt-section{margin-top:26px}.tt-head{border-top:2px solid var(--rule-strong);flex-wrap:wrap;align-items:center;gap:10px;padding:8px 0 6px;display:flex}.tt-head h2{letter-spacing:-.005em;margin:0;font-size:15px;font-weight:700}.tt-count{font-family:var(--mono);color:var(--muted);font-size:13px}.tt-empty{color:var(--muted);margin:4px 0 0;padding:6px 0}.tt{border-collapse:collapse;table-layout:fixed;width:100%}.tt th{top:var(--head);z-index:1;background:var(--paper);text-align:left;color:var(--muted);border-bottom:1px solid var(--rule-strong);white-space:nowrap;height:26px;box-shadow:0 -16px 0 var(--paper);padding:0 8px;font-size:13px;font-weight:600;position:sticky}.tt td{height:var(--row);border-bottom:1px solid var(--rule);white-space:nowrap;text-overflow:ellipsis;vertical-align:middle;padding:0 8px;overflow:hidden}.tt tbody tr{cursor:pointer}.tt tbody tr:hover td{background:var(--hover)}.tt tbody tr.selected td{background:var(--sel);color:var(--sel-ink)}.tt tbody tr.selected .row-name{font-weight:700}.tt tbody tr.dim td{color:var(--muted)}.tt td.num{text-align:right;font-family:var(--mono);color:var(--muted);font-size:13px}.tt td.num.used{color:var(--ink)}.tt th:first-child,.tt td:first-child{padding-left:2px}.row-name{font:inherit;text-overflow:ellipsis;white-space:nowrap;text-align:left;vertical-align:middle;background:0 0;border:0;max-width:100%;padding:0;overflow:hidden}.via{vertical-align:middle;color:var(--muted);margin-left:6px;display:inline-flex}.value,.why,.desc{color:var(--ink-2)}.tt-instructions th:first-child,.tt-instructions th:nth-child(2){width:40px}.tt-instructions th:nth-child(3){width:32%}.tt-instructions th:nth-child(4){width:86px}.tt-instructions th:nth-child(6){text-align:right;width:58px}.tt-instructions th:nth-child(7){width:108px}.tt-settings th:first-child{width:76px}.tt-settings th:nth-child(2),.tt-settings th:nth-child(4){width:30%}.tt-memory th:first-child{width:40px}.tt-memory th:nth-child(2){width:24%}.tt-memory th:nth-child(3){width:98px}.tt-memory th:nth-child(5){width:200px}.tt-memory th:nth-child(6){text-align:right;width:82px}.tt-hooks th:first-child,.tt-permissions th:first-child,.tt-mcp th:first-child,.tt-extensions th:first-child{width:40px}.tt-hooks th:nth-child(2){width:140px}.tt-hooks th:nth-child(3){width:26%}.tt-permissions th:nth-child(2){width:120px}.tt-permissions th:nth-child(4){width:34%}.tt-mcp th:nth-child(2){width:26%}.tt-mcp th:nth-child(3){width:92px}.tt-extensions th:nth-child(2){width:104px}.tt-extensions th:nth-child(3){width:30%}.tt-tools th:first-child{width:40px}.tt-tools th:nth-child(2){width:190px}.tt-tools th:nth-child(3){width:150px}.tt-tools th:nth-child(4){width:190px}.tt-tools th:nth-child(5){text-align:right;width:64px}.tt-dirs th:nth-child(2),.tt-dirs th:nth-child(3){text-align:right;width:96px}.tt-dirs th:nth-child(4){text-align:right;width:110px}.tt-shared th:first-child{width:40px}.tt-shared th:nth-child(2){width:30%}.tt-shared th:nth-child(3){width:130px}.bullets>*+*{margin-left:3px}.beat{color:var(--muted)}.beaten{font-family:var(--mono);align-items:center;gap:4px;margin-right:10px;font-size:13px;display:inline-flex}.beaten s{text-decoration-thickness:1.5px}.status{font-size:13.5px;font-weight:600}.status-loaded{color:var(--ok)}.status-skipped{color:var(--muted)}.status-on-match{color:var(--ink-2)}.vis{font-size:13.5px}.vis-tracked{color:var(--ok);font-weight:600}.vis-untracked{color:var(--warn)}.vis-ignored,.vis-no-git{color:var(--muted)}.type{align-items:center;gap:6px;font-size:14px;display:inline-flex}.type-dot{background:var(--ink-2);flex-shrink:0;width:8px;height:8px}.type-user{border-radius:999px}.type-feedback{clip-path:polygon(50% 0,100% 100%,0 100%)}.type-reference{transform:rotate(45deg)scale(.85)}.type-unknown{box-shadow:inset 0 0 0 1.5px var(--muted);background:0 0}.checks>*+*{margin-left:4px}.flag{background:var(--sunk);color:var(--ink-2);white-space:nowrap;border-radius:4px;align-items:center;gap:4px;padding:0 6px;font-size:12.5px;line-height:18px;display:inline-flex}.flag .icon{width:13px;height:13px}.flag.warn{background:var(--warn-bg);color:var(--warn)}.flag.bad{background:var(--bad-bg);color:var(--bad)}.segmented{border:1px solid var(--rule);scrollbar-width:none;border-radius:6px;min-width:0;max-width:100%;margin:0 0 0 6px;padding:0;display:inline-flex;overflow-x:auto}.segmented button{white-space:nowrap;border:0;border-right:1px solid var(--rule);background:var(--panel);flex:none;align-items:center;gap:5px;height:24px;padding:0 9px;font-size:13.5px;display:inline-flex}.segmented button:last-child{border-right:0}.segmented button[aria-pressed=true]{background:var(--ink);color:var(--paper)}.segmented.small button{height:22px;font-size:13px}.inspector{border-left:1px solid var(--rule);background:var(--panel);min-width:0;overflow-y:auto}.insp-head{z-index:2;background:var(--panel);border-bottom:2px solid var(--rule-strong);align-items:center;gap:10px;padding:16px 18px 12px;display:flex;position:sticky;top:0}.insp-head h2{overflow-wrap:anywhere;flex:1;min-width:0;margin:0;font-size:16px;font-weight:700}.insp-body{padding:14px 18px 40px}.verdict{color:var(--ink-2);margin:0 0 14px}.verdict.ok{color:var(--ink)}.verdict.off{color:var(--muted)}.fields{grid-template-columns:104px minmax(0,1fr);gap:6px 12px;margin:0 0 16px;font-size:14px;display:grid}.fields dt{color:var(--muted)}.fields dd{min-width:0;margin:0}.path{flex-wrap:wrap;align-items:center;gap:2px 6px;max-width:100%;display:inline-flex}.path .mono{overflow-wrap:anywhere}.copied{color:var(--ok);flex-basis:100%;font-size:13px}.wrap{overflow-wrap:anywhere;white-space:normal}.icon-button{width:28px;height:28px;color:var(--muted);background:0 0;border:0;border-radius:6px;flex-shrink:0;place-items:center;display:inline-grid}.icon-button:hover{background:var(--hover);color:var(--ink)}.icon-button.small{width:22px;height:22px}.sub{margin:0 0 16px}.sub h3,.sub-title{margin:0 0 6px;font-size:14px;font-weight:700}.plain{gap:4px;margin:0;padding:0;list-style:none;display:grid}.plain li{flex-wrap:wrap;align-items:center;gap:8px;display:flex}.layers{border-collapse:collapse;width:100%;margin-bottom:14px}.layers th{text-align:left;width:40px;padding:4px 0}.layers td{border-bottom:1px solid var(--rule);overflow-wrap:anywhere;padding:4px 8px}.layers tr.wins td{font-weight:600}.layers tr.lost td{color:var(--muted);text-decoration:line-through}.layers tr.unset td{color:var(--muted)}.contents{border-top:1px solid var(--rule-strong);margin-top:8px}.contents-bar{justify-content:space-between;align-items:center;padding:8px 0;font-size:14px;font-weight:700;display:flex}pre.raw{background:var(--sunk);white-space:pre-wrap;overflow-wrap:anywhere;border-radius:6px;margin:0 0 12px;padding:12px;line-height:1.5;overflow-x:auto}.prose{overflow-wrap:anywhere;max-width:68ch;font-size:14.5px;line-height:1.6}.prose h1,.prose h2,.prose h3{margin:18px 0 6px;font-size:14px}.prose p,.prose ul,.prose ol{margin:8px 0}.prose ul,.prose ol{padding-left:20px}.prose code{background:var(--sunk);border-radius:3px;padding:1px 4px}.prose pre{background:var(--sunk);border-radius:6px;padding:10px;overflow-x:auto}.prose pre code{background:0 0;padding:0}.prose a,.link{color:var(--focus);text-underline-offset:2px}.prose table{border-collapse:collapse;display:block;overflow-x:auto}.prose td,.prose th{border:1px solid var(--rule);padding:3px 6px}.link{text-align:left;background:0 0;border:0;padding:0;text-decoration:underline}.actions{margin:4px 0 16px}.quiet-danger{border:1px solid var(--rule);background:var(--panel);height:30px;color:var(--bad);border-radius:6px;align-items:center;gap:6px;padding:0 10px;font-size:14px;display:inline-flex}.quiet-danger:hover{border-color:var(--bad)}.confirm{border:1px solid var(--bad);background:var(--bad-bg);border-radius:8px;padding:12px}.confirm p{margin:0 0 10px;font-size:14px}.confirm-buttons{gap:8px;display:flex}.confirm-buttons button{border:1px solid var(--rule);background:var(--panel);border-radius:6px;height:30px;padding:0 12px;font-size:14px}.confirm-buttons button.danger{background:var(--bad);border-color:var(--bad);color:var(--on-bad);font-weight:600}button:disabled{opacity:.6;cursor:progress}.page{height:100%;padding:22px 28px 64px;overflow-y:auto}.page-head h1{letter-spacing:-.015em;margin:0 0 4px;font-size:22px}.lede{color:var(--ink-2);max-width:72ch;margin:0}.page .tt th{top:-22px}.stores>*+*{margin-left:12px}.palette-backdrop{z-index:20;background:color-mix(in srgb, var(--paper) 55%, transparent);place-items:start center;padding-top:12vh;display:grid;position:fixed;inset:0}.palette{background:var(--panel);border:1px solid var(--rule-strong);border-radius:10px;width:min(640px,100vw - 32px);animation:.18s cubic-bezier(.16,1,.3,1) rise;overflow:hidden;box-shadow:0 18px 48px -12px #00000059}.palette-input{border:0;border-bottom:1px solid var(--rule);background:var(--panel);border-radius:0;height:46px;padding:0 14px}.palette-results{max-height:50vh;margin:0;padding:6px;list-style:none;overflow-y:auto}.palette-results [role=option]{height:var(--row);cursor:pointer;white-space:nowrap;border-radius:6px;align-items:center;gap:10px;padding:0 10px;display:flex;overflow:hidden}.palette-results [role=option].active{background:var(--sel);color:var(--sel-ink)}.palette-empty{color:var(--muted);padding:8px 10px}.toast-region{z-index:30;position:fixed}.toast{z-index:30;background:var(--ink);color:var(--paper);border-radius:8px;align-items:center;gap:14px;padding:8px 8px 8px 14px;font-size:14.5px;animation:.22s cubic-bezier(.16,1,.3,1) rise;display:flex;position:fixed;bottom:22px;left:50%;transform:translate(-50%);box-shadow:0 12px 32px -10px #0006}.toast button{border:1px solid color-mix(in srgb, var(--paper) 40%, transparent);color:inherit;background:0 0;border-radius:6px;align-items:center;gap:6px;height:28px;padding:0 10px;font-weight:600;display:inline-flex}@keyframes rise{0%{opacity:0;translate:0 6px}}.loading{color:var(--muted);flex-direction:column;gap:12px;padding:48px 28px;display:flex}.loading-line{background:linear-gradient(90deg, var(--ink) 0 30%, var(--rule) 30% 100%);background-size:200% 100%;width:220px;height:3px;animation:1.2s cubic-bezier(.65,0,.35,1) infinite travel;display:block}@keyframes travel{0%{background-position:100% 0}to{background-position:-40% 0}}.problem{max-width:64ch;padding:48px 28px}.problem h1{margin:0 0 8px;font-size:18px}.tt-note{color:var(--muted);font-size:13.5px}.perm{font-size:13.5px;font-weight:600}.perm-allow{color:var(--ok)}.perm-ask{color:var(--warn)}.perm-deny{color:var(--bad)}.fix{flex-wrap:wrap;align-items:center;gap:6px;display:inline-flex}.fix .head-action{height:22px;margin-left:0;padding:0 8px}.tt-head .fix{margin-left:4px}.view-switch{flex-shrink:0;margin-left:0}.view-switch button{height:28px}.filter.hidden{display:none}.section-index.hidden{visibility:hidden}.context{border-top:2px solid var(--rule-strong);margin-top:4px;padding-top:12px}.context-summary{margin:0 0 4px;font-size:15px}.context>.muted{max-width:80ch;margin:0 0 12px}.context-large{white-space:normal;margin:0 0 12px;padding:6px 10px;line-height:1.5}.context-bar{gap:2px;height:8px;margin:0 0 8px;display:flex}.context-bar span{border-radius:2px;min-width:3px}.bar-managed{background:var(--line-managed)}.bar-user{background:var(--line-user)}.bar-project{background:var(--line-project)}.bar-local{background:var(--line-local)}.bar-memory{background:var(--line-memory)}.context-list{margin:0;padding:0;list-style:none}.context-list li{border-bottom:1px solid var(--rule)}.context-list summary{min-height:var(--row);cursor:pointer;grid-template-columns:28px 26px minmax(0,1fr) 84px 64px 64px;align-items:center;gap:8px;padding:6px 4px;list-style:none;display:grid}.context-list summary::-webkit-details-marker{display:none}.context-list summary:hover{background:var(--hover)}.context-list summary .num{font-family:var(--mono);text-align:right;color:var(--ink-2);font-size:13px}.context-list summary .num:first-child{text-align:left;color:var(--muted)}.context-name{flex-direction:column;min-width:0;display:flex}.context-name .mono{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.context-list details[open] summary{background:var(--sunk)}.context-list pre.raw{max-height:60vh;margin:8px 0 12px;overflow-y:auto}.context-hidden{margin:8px 4px 12px}.sharing{flex-wrap:wrap;gap:6px 24px;margin:0 0 4px;font-size:13.5px;display:flex}.sharing-group{align-items:baseline;gap:8px;min-width:0;display:flex}.sharing dt{white-space:nowrap}.sharing dd{color:var(--ink-2);flex-wrap:wrap;gap:2px 14px;margin:0;display:flex}.sharing dd span{white-space:nowrap}.problems{background:var(--warn-bg);color:var(--warn);border-radius:6px;align-items:flex-start;gap:8px;margin:0 0 12px;padding:8px 12px;font-size:13.5px;display:flex}.problems ul{overflow-wrap:anywhere;margin:0}@media (prefers-reduced-motion:reduce){*,:before{transition:none!important;animation:none!important}}@media (width>=1181px) and (width<=1400px){:root{--rail:220px;--inspector:360px}.tt-instructions :is(th,td):nth-child(6){display:none}}@media (width<=1180px){.workspace{grid-template-columns:minmax(0,1fr)}.inspector{width:min(var(--inspector), 100vw);z-index:10;position:fixed;top:0;bottom:0;right:0;box-shadow:-16px 0 40px -16px #00000059}.rail-keys{display:none}}@media (width<=760px){.app{grid-template-rows:auto minmax(0,1fr);grid-template-columns:minmax(0,1fr)}.rail{border-right:0;border-bottom:1px solid var(--rule);max-height:34vh}.work-main,.page{padding-left:16px;padding-right:16px}.filter{flex:160px;width:auto}.toolbar{flex-wrap:wrap;width:100%}.section-index{flex-wrap:wrap}.sharing-group{flex-direction:column;gap:2px}.dir-head{grid-template-rows:auto;grid-template-columns:minmax(0,1fr);height:auto;padding-top:18px;position:static}.dir-title{flex-wrap:wrap}.tt-section{scroll-margin-top:0}.palette-button{display:none}.context-list summary{grid-template-columns:22px 26px minmax(0,1fr) 56px}.context-list summary .num:nth-child(4),.context-list summary .num:last-child{display:none}.route-stops{flex-direction:column}.route-stops:before{width:3px;height:auto;inset:22px auto 22px 19px}.stop button{grid-template-rows:auto;grid-template-columns:30px auto 1fr;align-items:center;padding:6px 8px}.tt th:nth-child(n){width:auto}.tt th:first-child:nth-child(n),.tt-instructions th:nth-child(2):nth-child(n){width:40px}.tt-settings th:first-child:nth-child(n){width:64px}.tt-compare,.tt-compare :is(thead,tbody){display:block}.tt-compare tr{grid-template-columns:repeat(2,minmax(0,1fr));display:grid}.tt-compare th:first-child{display:none}.tt-compare td:first-child{border-bottom:0;grid-column:1/-1;padding-bottom:0}.tt-instructions th:first-child,.tt-instructions td:first-child,.tt-instructions :is(th,td):is(:nth-child(5),:nth-child(6)),.tt-settings :is(th,td):nth-child(4),.tt-memory :is(th,td):is(:nth-child(3),:nth-child(4),:nth-child(6)),.tt-permissions :is(th,td):nth-child(4),.tt-tools :is(th,td):is(:nth-child(4),:nth-child(6)),.tt-hooks :is(th,td):nth-child(4),.tt-mcp :is(th,td):nth-child(4),.tt-extensions :is(th,td):nth-child(4),.tt-shared :is(th,td):nth-child(n+4){display:none}.tt-instructions th:nth-child(4):nth-child(n){width:72px}.tt-instructions th:nth-child(7):nth-child(n){width:84px}.tt th{box-shadow:none;position:static}}.head-action{border:1px solid var(--rule);background:var(--panel);border-radius:6px;align-items:center;gap:6px;height:26px;margin-left:auto;padding:0 10px;font-size:13.5px;display:inline-flex}.head-action:hover{border-color:var(--ink-2)}.editor{gap:10px;margin:0 0 18px;display:grid}.editor>p{margin:0}.hint{color:var(--ink-2);font-size:14px}.editor .segmented{justify-self:start;margin-left:0}.field{color:var(--muted);gap:4px;font-size:13.5px;display:grid}.field input:not([type=checkbox]),.field select,.field textarea,.path-form input{border:1px solid var(--rule);background:var(--paper);width:100%;color:var(--ink);border-radius:6px;padding:6px 8px;font-size:14px}.field input:not([type=checkbox]),.field select,.path-form input{height:32px}.field textarea{resize:vertical;line-height:1.5}.field input:focus,.field select:focus,.field textarea:focus,.path-form input:focus{outline:2px solid var(--focus);outline-offset:-1px}.check{color:var(--ink);align-items:center;gap:8px;font-size:14px;display:inline-flex}.check input{width:16px;height:16px;accent-color:var(--ink);margin:0}.editor-buttons{flex-wrap:wrap;gap:8px;display:flex}.editor-buttons button,.path-form button{border:1px solid var(--rule);background:var(--panel);border-radius:6px;height:30px;padding:0 12px;font-size:14px}.editor-buttons button:hover,.path-form button:hover{border-color:var(--ink-2)}.editor-buttons button.primary{background:var(--ink);border-color:var(--ink);color:var(--paper);font-weight:600}.key-picker{position:relative}.key-results{border:1px solid var(--rule);background:var(--panel);border-radius:6px;max-height:280px;margin-top:4px;padding:4px;overflow-y:auto}.key-results [role=option]{cursor:pointer;border-radius:4px;gap:1px;padding:6px 8px;font-size:13.5px;display:grid}.key-results [role=option] .muted{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.key-results [role=option].active{background:var(--sel);color:var(--sel-ink)}.review{border-top:1px solid var(--rule-strong);gap:8px;padding-top:10px;display:grid}.suggestions{border:0;flex-wrap:wrap;align-items:center;gap:4px;margin:0;padding:0;display:flex}.suggestions .muted{margin-right:4px}.suggestions button{border:1px solid var(--rule);background:var(--panel);border-radius:4px;height:24px;padding:0 8px;font-size:13px}.suggestions button[aria-pressed=true]{background:var(--ink);border-color:var(--ink);color:var(--paper)}.docs-link{align-items:center;gap:4px;display:inline-flex}.facet-select{border:1px solid var(--rule);background:var(--panel);max-width:260px;height:26px;color:var(--ink);border-radius:6px;padding:0 6px;font-size:13.5px}.catalog-files{white-space:nowrap}.catalog-files .bullet+.bullet{margin-left:2px}.docs-text>:first-child{margin-top:0}.docs-text>:last-child{margin-bottom:0}.tool-switch-rule{flex-wrap:wrap;align-items:center;gap:6px;padding:4px 0;display:flex}.tool-switch-rule .head-action{margin-left:auto}.tt-head .memory-review{flex-basis:100%}.memory-review-file{gap:4px;display:grid}.editor-buttons button.danger{background:var(--bad);border-color:var(--bad);color:var(--on-bad);font-weight:600}.review .sub-title{overflow-wrap:anywhere;margin:0}pre.diff{background:var(--sunk);border-radius:6px;max-height:360px;margin:0;padding:8px 0;line-height:1.5;overflow-x:auto}pre.diff span{white-space:pre;padding:0 10px;display:block}.diff-add{background:color-mix(in srgb, var(--ok) 16%, transparent);color:var(--ink)}.diff-remove{background:color-mix(in srgb, var(--bad) 16%, transparent);color:var(--ink);text-decoration:line-through;-webkit-text-decoration-color:color-mix(in srgb, var(--bad) 60%, transparent);text-decoration-color:color-mix(in srgb, var(--bad) 60%, transparent)}.diff-same{color:var(--muted)}.rail-empty{color:var(--muted);margin:2px 8px;font-size:14px}.rail-keys{color:var(--muted);gap:6px;margin:auto 8px 0;padding:24px 0 0;font-size:13px;list-style:none;display:grid}.visually-hidden{clip-path:inset(50%);white-space:nowrap;width:1px;height:1px;position:absolute;overflow:hidden}.tt th .sort{height:100%;font:inherit;color:inherit;background:0 0;border:0;align-items:center;gap:4px;padding:0;display:inline-flex}.tt th .sort:hover,.tt th[aria-sort] .sort{color:var(--ink)}.sort-mark{font-family:var(--mono);opacity:0;font-size:12px;transition:opacity .12s}.tt th .sort:hover .sort-mark,.tt th .sort:focus-visible .sort-mark,.sort-mark.on{opacity:1}.show-more{border:0;border-bottom:1px solid var(--rule);text-align:left;background:0 0;width:100%;margin:0;padding:7px 8px;font-size:13.5px;font-weight:600;display:block}.show-more:hover{background:var(--hover)}.tt-head .filter{height:28px;margin-left:auto}.tool-outcome{font-size:13.5px}.tool-denied{color:var(--bad);font-weight:600}.tool-asks{color:var(--warn)}.tool-allowed{color:var(--ok);font-weight:600}.tool-free{color:var(--muted)}.diff-skip{color:var(--muted);font-style:italic}.tt-settings.no-overrides :is(th,td):nth-child(4){display:none}.head-action+.head-action{margin-left:0}.tt-compare th:first-child{width:28%}.tt-compare tbody tr{cursor:default}.tt-compare td{vertical-align:top;height:auto;padding-top:5px;padding-bottom:5px}.cmp-value,.cmp-from{text-overflow:ellipsis;display:block;overflow:hidden}.cmp-from{color:var(--muted);font-size:13px}.cmp-from>*+*{margin-left:8px}.change-day{max-width:980px;margin-top:22px}.change-day h2{border-top:2px solid var(--rule-strong);margin:0 0 4px;padding-top:8px;font-size:14px;font-weight:700}.changes{grid-template-columns:minmax(0,1fr);max-width:980px;display:grid}.changes .change{border-top:1px solid var(--rule);grid-template-columns:minmax(0,1fr);gap:10px;padding:10px 0;display:grid}.change-row{grid-template-columns:11rem minmax(0,1fr) 6rem 9rem;grid-template-areas:"what file when action";align-items:baseline;gap:6px 12px;font-size:14px;display:grid}.change-what{grid-area:what;font-weight:600}.change-file{grid-area:file;gap:2px;min-width:0;display:grid}.change-file .muted{font-size:12.5px}.change-row .num{grid-area:when}.change-row .head-action{grid-area:action;justify-self:end;margin-left:0}@media (width<=720px){.change-row{grid-template-columns:minmax(0,1fr) auto;grid-template-areas:"what when""file file""action action"}.change-row .head-action{justify-self:start}}.path-tester{border-top:1px solid var(--rule);gap:8px;max-width:760px;margin-top:28px;padding-top:14px;display:grid}.path-tester .sub-title{margin:0}.path-form{gap:8px;display:flex}.path-form input{flex:1;min-width:0}.path-matches{gap:6px;font-size:14px;display:grid}.path-matches li{flex-wrap:wrap;align-items:baseline;gap:4px 10px;display:flex}
|