ccdesk 0.2.2 → 0.2.4
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 +46 -0
- package/README.md +7 -4
- package/dist/core/agent-edit.d.ts +6 -0
- package/dist/{server → core}/agent-edit.js +15 -21
- package/dist/core/backups.d.ts +19 -0
- package/dist/{server → core}/backups.js +64 -48
- package/dist/core/core.d.ts +96 -0
- package/dist/core/core.js +57 -0
- package/dist/core/index.d.ts +22 -0
- package/dist/core/index.js +38 -0
- package/dist/core/mcp-edit.d.ts +23 -0
- package/dist/{server → core}/mcp-edit.js +121 -100
- package/dist/core/mutations.d.ts +20 -0
- package/dist/{server → core}/mutations.js +8 -15
- package/dist/core/open.d.ts +28 -0
- package/dist/{server → core}/open.js +38 -13
- package/dist/core/parse-edit.d.ts +12 -0
- package/dist/core/parse-edit.js +64 -0
- package/dist/core/reference.d.ts +3 -0
- package/dist/core/reference.js +11 -0
- package/dist/core/scan/compare.d.ts +7 -0
- package/dist/{server → core}/scan/compare.js +2 -2
- package/dist/core/scan/context.d.ts +13 -0
- package/dist/{server → core}/scan/context.js +55 -32
- package/dist/core/scan/data.d.ts +14 -0
- package/dist/core/scan/data.js +26 -0
- package/dist/core/scan/env.d.ts +28 -0
- package/dist/{server → core}/scan/env.js +15 -4
- package/dist/core/scan/files.d.ts +68 -0
- package/dist/core/scan/files.js +270 -0
- package/dist/core/scan/git.d.ts +20 -0
- package/dist/core/scan/git.js +106 -0
- package/dist/core/scan/index.d.ts +5 -0
- package/dist/{server → core}/scan/index.js +119 -82
- package/dist/core/scan/instructions.d.ts +54 -0
- package/dist/{server → core}/scan/instructions.js +141 -73
- package/dist/core/scan/inventory.d.ts +38 -0
- package/dist/{server → core}/scan/inventory.js +134 -115
- package/dist/core/scan/mcp.d.ts +28 -0
- package/dist/{server → core}/scan/mcp.js +153 -95
- package/dist/core/scan/memory.d.ts +8 -0
- package/dist/{server → core}/scan/memory.js +8 -8
- package/dist/core/scan/path-test.d.ts +9 -0
- package/dist/core/scan/path-test.js +156 -0
- package/dist/core/scan/permissions.d.ts +31 -0
- package/dist/{server → core}/scan/permissions.js +5 -8
- package/dist/core/scan/plugins.d.ts +39 -0
- package/dist/{server → core}/scan/plugins.js +36 -27
- package/dist/core/scan/projects.d.ts +9 -0
- package/dist/{server → core}/scan/projects.js +7 -5
- package/dist/core/scan/sandbox.d.ts +14 -0
- package/dist/{server → core}/scan/sandbox.js +87 -59
- package/dist/core/scan/settings.d.ts +37 -0
- package/dist/{server → core}/scan/settings.js +98 -93
- package/dist/core/scan/tools.d.ts +10 -0
- package/dist/{server → core}/scan/tools.js +28 -20
- package/dist/core/scan/usage.d.ts +19 -0
- package/dist/{server → core}/scan/usage.js +122 -97
- package/dist/core/schema.d.ts +8 -0
- package/dist/core/schema.js +67 -0
- package/dist/core/secrets.d.ts +45 -0
- package/dist/core/secrets.js +207 -0
- package/dist/core/settings-edit.d.ts +9 -0
- package/dist/{server → core}/settings-edit.js +91 -112
- package/dist/core/watch.d.ts +45 -0
- package/dist/{server → core}/watch.js +54 -17
- package/dist/core/write.d.ts +117 -0
- package/dist/{server → core}/write.js +61 -8
- package/dist/server/args.d.ts +32 -0
- package/dist/server/args.js +106 -0
- package/dist/server/cli.d.ts +2 -0
- package/dist/server/cli.js +58 -83
- package/dist/server/commands.d.ts +8 -0
- package/dist/server/commands.js +39 -0
- package/dist/server/server.d.ts +12 -0
- package/dist/server/server.js +80 -195
- package/dist/server/start.d.ts +13 -0
- package/dist/server/start.js +21 -0
- package/dist/shared/compare.d.ts +25 -0
- package/dist/shared/memory.d.ts +6 -0
- package/dist/shared/memory.js +18 -2
- package/dist/shared/requests.d.ts +104 -0
- package/dist/shared/requests.js +73 -0
- package/dist/shared/scopes.d.ts +10 -0
- package/dist/shared/settings-catalog.json +2199 -0
- package/dist/shared/types.d.ts +535 -0
- package/dist/shared/types.js +11 -0
- package/dist/ui/assets/index-C9qqEI5Z.js +60 -0
- package/dist/ui/index.html +1 -1
- package/dist/view/diff.d.ts +16 -0
- package/dist/view/diff.js +85 -0
- package/dist/view/format.d.ts +17 -0
- package/dist/view/format.js +47 -0
- package/dist/view/index.d.ts +11 -0
- package/dist/view/index.js +13 -0
- package/dist/view/lines.d.ts +9 -0
- package/dist/view/lines.js +12 -0
- package/dist/view/path-test.d.ts +3 -0
- package/dist/view/path-test.js +7 -0
- package/dist/view/sections/catalog.d.ts +9 -0
- package/dist/view/sections/catalog.js +47 -0
- package/dist/view/sections/extensions.d.ts +1 -0
- package/dist/view/sections/extensions.js +48 -0
- package/dist/view/sections/hooks.d.ts +1 -0
- package/dist/view/sections/hooks.js +32 -0
- package/dist/view/sections/index.d.ts +6 -0
- package/dist/view/sections/index.js +27 -0
- package/dist/view/sections/instructions.d.ts +6 -0
- package/dist/view/sections/instructions.js +38 -0
- package/dist/view/sections/mcp.d.ts +3 -0
- package/dist/view/sections/mcp.js +38 -0
- package/dist/view/sections/memory.d.ts +1 -0
- package/dist/view/sections/memory.js +31 -0
- package/dist/view/sections/permissions.d.ts +17 -0
- package/dist/view/sections/permissions.js +87 -0
- package/dist/view/sections/settings.d.ts +1 -0
- package/dist/view/sections/settings.js +33 -0
- package/dist/view/sections/tools.d.ts +6 -0
- package/dist/view/sections/tools.js +57 -0
- package/dist/view/sections/types.d.ts +123 -0
- package/dist/view/sections/types.js +10 -0
- package/dist/view/settings.d.ts +12 -0
- package/dist/view/settings.js +31 -0
- package/dist/view/usage.d.ts +8 -0
- package/dist/view/usage.js +18 -0
- package/package.json +13 -2
- package/dist/server/scan/files.js +0 -480
- package/dist/server/scan/path-test.js +0 -136
- package/dist/server/schema.js +0 -15
- package/dist/server/secrets.js +0 -63
- package/dist/ui/assets/index-Dg9dxngR.js +0 -60
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
import { constants } from "node:fs";
|
|
2
|
-
import { access, stat } from "node:fs/promises";
|
|
3
1
|
import { dirname, isAbsolute, join, matchesGlob, parse, resolve } from "node:path";
|
|
4
2
|
import {} from "../../shared/types.js";
|
|
5
3
|
import {} from "./env.js";
|
|
6
|
-
import { isFile, isNetworkPath, isWithin, MAX_TEXT_BYTES,
|
|
4
|
+
import { isFile, isNetworkPath, isWithin, MAX_TEXT_BYTES, readTextFile, resolvedPath, toStringList, walkMarkdown, } from "./files.js";
|
|
5
|
+
import { parseFrontmatter } from "./data.js";
|
|
7
6
|
const MAX_IMPORT_HOPS = 4;
|
|
8
7
|
export const AGENTS_MD_MODES = [
|
|
9
8
|
"claude-md-or-agents-md",
|
|
@@ -11,6 +10,45 @@ export const AGENTS_MD_MODES = [
|
|
|
11
10
|
"claude-md",
|
|
12
11
|
"managed-only",
|
|
13
12
|
];
|
|
13
|
+
/** The CLAUDE.md and AGENTS.md files each folder can hold, in the order Claude Code reads them. */
|
|
14
|
+
export const CLAUDE_MD_NAMES = ["CLAUDE.md", ".claude/CLAUDE.md"];
|
|
15
|
+
export const AGENTS_MD_NAMES = ["AGENTS.md", ".claude/AGENTS.md"];
|
|
16
|
+
/**
|
|
17
|
+
* Whether AGENTS.md loads, given the instruction mode and whether a CLAUDE.md or CLAUDE.local.md
|
|
18
|
+
* is there (docs: memory#choose-which-instruction-files-load).
|
|
19
|
+
*/
|
|
20
|
+
export function agentsMdLoads(mode, hasClaudeMd) {
|
|
21
|
+
return mode === "claude-md-and-agents-md" || (mode === "claude-md-or-agents-md" && !hasClaudeMd);
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Whether a claudeMdExcludes glob matches a file by its own path or, for a link, its target's
|
|
25
|
+
* (docs: memory#exclude-specific-claude-md-files).
|
|
26
|
+
*/
|
|
27
|
+
export function isExcluded(excludes, path, realPath) {
|
|
28
|
+
return excludes.some((glob) => [path, realPath].some((p) => p !== undefined && safeMatch(p, glob)));
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Where a rule in `dir` really is when that is outside both the project and `dir`: a link's
|
|
32
|
+
* target, or the file a linked folder leads to. Undefined for a rule that stays inside.
|
|
33
|
+
*/
|
|
34
|
+
export async function ruleTarget(file, dir, realCwd) {
|
|
35
|
+
if (file.realPath)
|
|
36
|
+
return file.realPath;
|
|
37
|
+
const real = await resolvedPath(file.path);
|
|
38
|
+
return isWithin(realCwd, real) || isWithin(await resolvedPath(dir), real) ? undefined : real;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Why a project rule linked from outside the project does not load, or null when it does: it waits
|
|
42
|
+
* for your approval of outside imports, and once approved loads only without a paths field
|
|
43
|
+
* (docs: memory#share-rules-across-projects-with-symlinks).
|
|
44
|
+
*/
|
|
45
|
+
export function linkedRuleBlock(target, realCwd, approved, hasPaths) {
|
|
46
|
+
if (target === undefined || isWithin(realCwd, target))
|
|
47
|
+
return null;
|
|
48
|
+
if (!approved)
|
|
49
|
+
return "needs-approval";
|
|
50
|
+
return hasPaths ? "has-paths" : null;
|
|
51
|
+
}
|
|
14
52
|
/** Directories from the filesystem root down to `cwd`, the order Claude Code concatenates them in. */
|
|
15
53
|
export function ancestorsRootFirst(cwd) {
|
|
16
54
|
const dirs = [];
|
|
@@ -31,25 +69,31 @@ export function ancestorsRootFirst(cwd) {
|
|
|
31
69
|
*/
|
|
32
70
|
export async function resolveInstructions(options) {
|
|
33
71
|
const { env, cwd, mode, excludes, externalImports, managedClaudeMd, problems } = options;
|
|
34
|
-
/** A file that exists but cannot be read is reported, not taken for a missing one. */
|
|
35
|
-
const readInstruction = async (path) => {
|
|
36
|
-
const file = await readTextFile(env, path);
|
|
37
|
-
if (file || !(await stat(path).catch(() => null))?.isFile())
|
|
38
|
-
return file;
|
|
39
|
-
const denied = await access(path, constants.R_OK).then(() => null, (error) => error.code ?? String(error));
|
|
40
|
-
if (denied)
|
|
41
|
-
problems.push({ path, error: `cannot be read (${denied})` });
|
|
42
|
-
return null;
|
|
43
|
-
};
|
|
44
72
|
const ctx = {
|
|
45
73
|
realCwd: await resolvedPath(resolve(cwd)),
|
|
46
74
|
env,
|
|
47
75
|
approved: externalImports === "approved",
|
|
76
|
+
problems,
|
|
48
77
|
};
|
|
49
|
-
const
|
|
50
|
-
const
|
|
78
|
+
const out = await readOwnFiles(ctx, managedClaudeMd);
|
|
79
|
+
const { claudeFiles, agentsFiles } = await readDirectoryFiles(ctx, cwd);
|
|
80
|
+
explainClaudeFiles(claudeFiles, cwd);
|
|
81
|
+
explainAgentsFiles(agentsFiles, claudeFiles, mode);
|
|
82
|
+
for (const dir of ancestorsRootFirst(cwd))
|
|
83
|
+
out.push(...claudeFiles.filter((f) => f.dir === dir), ...agentsFiles.filter((f) => f.dir === dir));
|
|
84
|
+
applyLoadRules(out, ctx, mode, excludes, externalImports);
|
|
85
|
+
addChecks(out);
|
|
86
|
+
return out;
|
|
87
|
+
}
|
|
88
|
+
/** A file that exists but cannot be read is reported, not taken for a missing one. */
|
|
89
|
+
function readInstruction(ctx, path) {
|
|
90
|
+
return readTextFile(ctx.env, path, ctx.problems);
|
|
91
|
+
}
|
|
92
|
+
/** The managed and user files, which come first and do not depend on the folder. */
|
|
93
|
+
async function readOwnFiles(ctx, managedClaudeMd) {
|
|
94
|
+
const { env } = ctx;
|
|
51
95
|
const out = [];
|
|
52
|
-
const managed = await readInstruction(join(env.managedDir, "CLAUDE.md"));
|
|
96
|
+
const managed = await readInstruction(ctx, join(env.managedDir, "CLAUDE.md"));
|
|
53
97
|
if (managed)
|
|
54
98
|
out.push(await instruction(managed, "claude-md", "managed", env.managedDir, ctx, "Organisation policy, always loaded"));
|
|
55
99
|
if (managedClaudeMd) {
|
|
@@ -65,69 +109,94 @@ export async function resolveInstructions(options) {
|
|
|
65
109
|
entry.imports = [];
|
|
66
110
|
out.push(entry);
|
|
67
111
|
}
|
|
68
|
-
const user = await readInstruction(join(env.claudeDir, "CLAUDE.md"));
|
|
112
|
+
const user = await readInstruction(ctx, join(env.claudeDir, "CLAUDE.md"));
|
|
69
113
|
if (user)
|
|
70
114
|
out.push(await instruction(user, "claude-md", "user", env.claudeDir, ctx, "Your instructions for every project"));
|
|
71
115
|
for (const path of await walkMarkdown(join(env.claudeDir, "rules"))) {
|
|
72
|
-
const file = await readInstruction(path);
|
|
116
|
+
const file = await readInstruction(ctx, path);
|
|
73
117
|
if (file)
|
|
74
118
|
out.push(await rule(file, "user", env.claudeDir, ctx));
|
|
75
119
|
}
|
|
76
|
-
|
|
77
|
-
|
|
120
|
+
return out;
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* The project and local files of `cwd` and every folder above it. Per directory, root first:
|
|
124
|
+
* CLAUDE.md, .claude/CLAUDE.md, rules, then CLAUDE.local.md last ("your personal notes are the last
|
|
125
|
+
* thing Claude reads at that level"), then AGENTS.md.
|
|
126
|
+
*/
|
|
127
|
+
async function readDirectoryFiles(ctx, cwd) {
|
|
78
128
|
const claudeFiles = [];
|
|
79
129
|
const agentsFiles = [];
|
|
80
130
|
for (const dir of ancestorsRootFirst(cwd)) {
|
|
81
|
-
|
|
82
|
-
const
|
|
83
|
-
|
|
84
|
-
if (isConfigHome && name === ".claude/CLAUDE.md")
|
|
85
|
-
continue;
|
|
86
|
-
const file = await readInstruction(join(dir, name));
|
|
87
|
-
if (file)
|
|
88
|
-
claudeFiles.push(await instruction(file, "claude-md", "project", dir, ctx, ""));
|
|
89
|
-
}
|
|
90
|
-
if (!isConfigHome && dir !== env.claudeDir)
|
|
91
|
-
for (const path of await walkMarkdown(join(dir, ".claude", "rules"))) {
|
|
92
|
-
const file = await readInstruction(path);
|
|
93
|
-
if (file)
|
|
94
|
-
claudeFiles.push(await rule(file, "project", dir, ctx));
|
|
95
|
-
}
|
|
96
|
-
const local = await readInstruction(join(dir, "CLAUDE.local.md"));
|
|
97
|
-
if (local)
|
|
98
|
-
claudeFiles.push(await instruction(local, "claude-local-md", "local", dir, ctx, ""));
|
|
99
|
-
for (const name of ["AGENTS.md", ".claude/AGENTS.md"]) {
|
|
100
|
-
const file = await readInstruction(join(dir, name));
|
|
131
|
+
claudeFiles.push(...(await readClaudeFiles(ctx, dir)));
|
|
132
|
+
for (const name of AGENTS_MD_NAMES) {
|
|
133
|
+
const file = await readInstruction(ctx, join(dir, name));
|
|
101
134
|
if (file)
|
|
102
135
|
agentsFiles.push(await instruction(file, "agents-md", "project", dir, ctx, ""));
|
|
103
136
|
}
|
|
104
137
|
}
|
|
105
|
-
|
|
138
|
+
return { claudeFiles, agentsFiles };
|
|
139
|
+
}
|
|
140
|
+
/** One folder's CLAUDE.md files, rules and CLAUDE.local.md, in the order Claude Code reads them. */
|
|
141
|
+
async function readClaudeFiles(ctx, dir) {
|
|
142
|
+
const { env } = ctx;
|
|
143
|
+
const out = [];
|
|
144
|
+
// ~/.claude/CLAUDE.md and ~/.claude/rules are the user files above, not the home's project files.
|
|
145
|
+
const holdsClaudeDir = join(dir, ".claude") === env.claudeDir;
|
|
146
|
+
for (const name of CLAUDE_MD_NAMES) {
|
|
147
|
+
if (holdsClaudeDir && name === ".claude/CLAUDE.md")
|
|
148
|
+
continue;
|
|
149
|
+
const file = await readInstruction(ctx, join(dir, name));
|
|
150
|
+
if (file)
|
|
151
|
+
out.push(await instruction(file, "claude-md", "project", dir, ctx, ""));
|
|
152
|
+
}
|
|
153
|
+
if (!holdsClaudeDir && dir !== env.claudeDir)
|
|
154
|
+
for (const path of await walkMarkdown(join(dir, ".claude", "rules"))) {
|
|
155
|
+
const file = await readInstruction(ctx, path);
|
|
156
|
+
if (file)
|
|
157
|
+
out.push(await rule(file, "project", dir, ctx));
|
|
158
|
+
}
|
|
159
|
+
const local = await readInstruction(ctx, join(dir, "CLAUDE.local.md"));
|
|
160
|
+
if (local)
|
|
161
|
+
out.push(await instruction(local, "claude-local-md", "local", dir, ctx, ""));
|
|
162
|
+
return out;
|
|
163
|
+
}
|
|
164
|
+
/** Why each CLAUDE.md and CLAUDE.local.md loads; rules have their reason already. */
|
|
165
|
+
function explainClaudeFiles(claudeFiles, cwd) {
|
|
106
166
|
for (const file of claudeFiles) {
|
|
107
167
|
if (file.kind === "rule")
|
|
108
168
|
continue;
|
|
109
|
-
file.
|
|
110
|
-
file.
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
169
|
+
if (file.kind === "claude-local-md")
|
|
170
|
+
file.reason = "Your private notes for this directory";
|
|
171
|
+
else if (file.dir === resolve(cwd))
|
|
172
|
+
file.reason = "Instructions for this directory";
|
|
173
|
+
else
|
|
174
|
+
file.reason = "Inherited from a parent directory";
|
|
115
175
|
}
|
|
176
|
+
}
|
|
177
|
+
/** Whether each AGENTS.md loads next to the CLAUDE.md files found, and why. */
|
|
178
|
+
function explainAgentsFiles(agentsFiles, claudeFiles, mode) {
|
|
179
|
+
const hasClaudeMd = claudeFiles.some((f) => f.kind !== "rule");
|
|
116
180
|
const includedElsewhere = new Set(claudeFiles.flatMap((f) => [f.realPath ?? f.path, ...f.imports.map((i) => i.resolved)]));
|
|
117
181
|
for (const file of agentsFiles) {
|
|
118
|
-
if (mode
|
|
119
|
-
skip(file, skippedByMode);
|
|
182
|
+
if (!agentsMdLoads(mode, false))
|
|
183
|
+
skip(file, skippedByMode(mode));
|
|
120
184
|
else if (includedElsewhere.has(file.realPath ?? file.path))
|
|
121
185
|
skip(file, "Already included through a CLAUDE.md import or symlink");
|
|
186
|
+
else if (!agentsMdLoads(mode, hasClaudeMd))
|
|
187
|
+
skip(file, "Ignored: a CLAUDE.md or CLAUDE.local.md exists here or above, so Claude Code reads that instead");
|
|
122
188
|
else if (mode === "claude-md-and-agents-md")
|
|
123
189
|
file.reason = "Loaded alongside CLAUDE.md (instruction mode claude-md-and-agents-md)";
|
|
124
|
-
else if (hasClaudeMd)
|
|
125
|
-
skip(file, "Ignored: a CLAUDE.md or CLAUDE.local.md exists here or above, so Claude Code reads that instead");
|
|
126
190
|
else
|
|
127
191
|
file.reason = "Loaded because there is no CLAUDE.md here or above";
|
|
128
192
|
}
|
|
129
|
-
|
|
130
|
-
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* Skips what Claude Code leaves out of every file but managed ones: all of them in managed-only
|
|
196
|
+
* mode, ones matched by claudeMdExcludes, ones over 4 MiB, and project rules linked from outside
|
|
197
|
+
* the project that do not qualify.
|
|
198
|
+
*/
|
|
199
|
+
function applyLoadRules(out, ctx, mode, excludes, externalImports) {
|
|
131
200
|
const waiting = externalImports === "declined"
|
|
132
201
|
? "You declined imports from outside this project"
|
|
133
202
|
: "Waits for you to approve imports from outside this project";
|
|
@@ -135,28 +204,27 @@ export async function resolveInstructions(options) {
|
|
|
135
204
|
if (file.scope === "managed")
|
|
136
205
|
continue;
|
|
137
206
|
// Path-scoped rules still load on demand in managed-only mode (docs: memory#choose-which-instruction-files-load).
|
|
138
|
-
if (
|
|
139
|
-
skip(file, skippedByMode);
|
|
140
|
-
// Managed files cannot be excluded
|
|
141
|
-
|
|
142
|
-
const excluded = excludes.some((glob) => [file.path, file.realPath].some((p) => p !== undefined && safeMatch(p, glob)));
|
|
143
|
-
if (excluded)
|
|
207
|
+
if (mode === "managed-only" && file.status === "loaded")
|
|
208
|
+
skip(file, skippedByMode(mode));
|
|
209
|
+
// Managed files cannot be excluded.
|
|
210
|
+
if (isExcluded(excludes, file.path, file.realPath))
|
|
144
211
|
skip(file, "Skipped by claudeMdExcludes");
|
|
145
212
|
if (file.status !== "skipped" && file.bytes > MAX_TEXT_BYTES)
|
|
146
213
|
skip(file, "Skipped: Claude Code does not load an instruction file over 4 MiB");
|
|
147
214
|
// A project's links outside it need your approval, like its imports; your own files do not.
|
|
148
215
|
if (file.scope !== "project" && file.scope !== "local")
|
|
149
216
|
continue;
|
|
150
|
-
if (file.kind
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
217
|
+
if (file.kind !== "rule")
|
|
218
|
+
continue;
|
|
219
|
+
const block = linkedRuleBlock(file.realPath, ctx.realCwd, ctx.approved, file.paths !== undefined);
|
|
220
|
+
if (block === "needs-approval")
|
|
221
|
+
skip(file, `${waiting}; this rule links outside it`);
|
|
222
|
+
else if (block === "has-paths")
|
|
223
|
+
skip(file, "Links outside this project, and such a rule loads only without a paths field");
|
|
157
224
|
}
|
|
158
|
-
|
|
159
|
-
|
|
225
|
+
}
|
|
226
|
+
function skippedByMode(mode) {
|
|
227
|
+
return `Skipped: instruction mode is ${mode}`;
|
|
160
228
|
}
|
|
161
229
|
function skip(file, reason) {
|
|
162
230
|
if (file.status === "skipped")
|
|
@@ -165,7 +233,7 @@ function skip(file, reason) {
|
|
|
165
233
|
file.reason = reason;
|
|
166
234
|
}
|
|
167
235
|
/** An invalid pattern matches nothing. */
|
|
168
|
-
function safeMatch(path, glob) {
|
|
236
|
+
export function safeMatch(path, glob) {
|
|
169
237
|
try {
|
|
170
238
|
return matchesGlob(path, glob);
|
|
171
239
|
}
|
|
@@ -213,9 +281,9 @@ async function rule(file, scope, dir, ctx) {
|
|
|
213
281
|
entry.status = "on-match";
|
|
214
282
|
}
|
|
215
283
|
// A rule reached through a linked folder is not a link itself, but its target can be outside.
|
|
216
|
-
const
|
|
217
|
-
if (
|
|
218
|
-
entry.realPath =
|
|
284
|
+
const target = await ruleTarget(file, dir, ctx.realCwd);
|
|
285
|
+
if (target)
|
|
286
|
+
entry.realPath = target;
|
|
219
287
|
return entry;
|
|
220
288
|
}
|
|
221
289
|
async function instruction(file, kind, scope, dir, ctx, reason) {
|
|
@@ -248,7 +316,7 @@ async function collectImports(file, ctx, readExternal) {
|
|
|
248
316
|
continue;
|
|
249
317
|
seen.add(real);
|
|
250
318
|
const external = !isWithin(ctx.realCwd, real);
|
|
251
|
-
const imported = !external || readExternal ? await readTextFile(ctx.env, target) : null;
|
|
319
|
+
const imported = !external || readExternal ? await readTextFile(ctx.env, target, ctx.problems) : null;
|
|
252
320
|
const ref = {
|
|
253
321
|
raw,
|
|
254
322
|
resolved: real,
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { type EffectiveSetting, type Entry, type Inventory, type Problem, type Scope, type SessionProfile, type SettingsLayer, type Trust } from "../../shared/types.ts";
|
|
2
|
+
import { type ScanEnv } from "./env.ts";
|
|
3
|
+
type Source = Scope | "plugin";
|
|
4
|
+
interface ReadOptions {
|
|
5
|
+
plugin?: string | undefined;
|
|
6
|
+
/** Joins the folders of a file in a subfolder into its name; commands use ":". */
|
|
7
|
+
separator?: string;
|
|
8
|
+
/** Receives files that are there but cannot be read. */
|
|
9
|
+
problems?: Problem[] | undefined;
|
|
10
|
+
}
|
|
11
|
+
export declare function readSkill(env: ScanEnv, folder: string, scope: Source, { plugin, problems }?: ReadOptions): Promise<Entry | null>;
|
|
12
|
+
interface InventoryInput {
|
|
13
|
+
env: ScanEnv;
|
|
14
|
+
projectDir: string;
|
|
15
|
+
/** The repository (or worktree) root, where the search for project skills stops. */
|
|
16
|
+
gitRoot: string | null;
|
|
17
|
+
layers: SettingsLayer[];
|
|
18
|
+
effective: EffectiveSetting[];
|
|
19
|
+
/** Managed settings as written, nothing hidden: for checks that must see header values. */
|
|
20
|
+
managed: Record<string, unknown> | null;
|
|
21
|
+
claudeJson: Record<string, unknown> | null;
|
|
22
|
+
trust: Trust;
|
|
23
|
+
profile: SessionProfile;
|
|
24
|
+
/** Receives files that are there but cannot be read or parsed. */
|
|
25
|
+
problems: Problem[];
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* What Claude Code requires of a subagent file; it skips one that falls short, and treats one
|
|
29
|
+
* without a name as documentation (docs: sub-agents#subagent-files-claude-code-skips).
|
|
30
|
+
*/
|
|
31
|
+
export declare function agentProblems(text: string): string[];
|
|
32
|
+
/**
|
|
33
|
+
* Applies skillOverrides to your skills and command files; plugin skills are not affected
|
|
34
|
+
* (docs: skills#override-skill-visibility-from-settings).
|
|
35
|
+
*/
|
|
36
|
+
export declare function applySkillOverrides(entries: Entry[], overrides: unknown): void;
|
|
37
|
+
export declare function readInventory(input: InventoryInput): Promise<Inventory>;
|
|
38
|
+
export {};
|