dsh-cc-loader 0.1.0 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/index.js +1 -1
- package/src/load.js +15 -6
- package/src/settings.js +6 -2
- package/src/skills.js +46 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-cc-loader",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "Shared parse layer for the dsh-cc ecosystem: parses Claude Code .claude/ (project + global) into a standalone .dsh intermediate representation (IR), classifying every component DIRECT/ADAPTED/UNSUPPORTED/BLOCKED and filtering unsupported ones out.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/index.js",
|
package/src/index.js
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
export { loadClaude, loadPermissions } from './load.js'
|
|
13
13
|
export {
|
|
14
14
|
discoverSkills, discoverCommands, collectClaudeDir, discoverRules,
|
|
15
|
-
findProjectRoot, parseFrontmatter, isSkillName, pathExists, readTextSafe,
|
|
15
|
+
findProjectRoot, findClaudeProjectRoot, parseFrontmatter, isSkillName, pathExists, readTextSafe,
|
|
16
16
|
} from './skills.js'
|
|
17
17
|
export { discoverSettings, mergeSettings } from './settings.js'
|
|
18
18
|
export {
|
package/src/load.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import { homedir } from 'node:os'
|
|
4
4
|
import { join } from 'node:path'
|
|
5
|
-
import { findProjectRoot, collectClaudeDir, discoverRules } from './skills.js'
|
|
5
|
+
import { findProjectRoot, findClaudeProjectRoot, collectClaudeDir, discoverRules } from './skills.js'
|
|
6
6
|
import { discoverSettings, mergeSettings } from './settings.js'
|
|
7
7
|
import { parseRulesFor, removedToolNames, classifyComponents } from './classify.js'
|
|
8
8
|
import { STATUS } from './classify.js'
|
|
@@ -30,12 +30,16 @@ import { discoverPluginRoot, discoverMarketplace } from './plugin.js'
|
|
|
30
30
|
export async function loadClaude(opts = {}) {
|
|
31
31
|
const cwd = opts.cwd ?? process.cwd()
|
|
32
32
|
const homeDir = opts.homeDir ?? homedir()
|
|
33
|
-
const markers = opts.projectRootMarkers ?? ['.git']
|
|
34
33
|
const projectSkillRank = opts.projectSkillRank ?? 150
|
|
35
34
|
const globalSkillRank = opts.globalSkillRank ?? 160
|
|
36
35
|
const warnings = []
|
|
37
36
|
|
|
38
|
-
|
|
37
|
+
// Project root: explicit projectRootMarkers wins (caller control, e.g.
|
|
38
|
+
// cc-mcp's configurable markers); otherwise CC semantics — closest
|
|
39
|
+
// .claude/ directory, falling back to the .git root, home excluded.
|
|
40
|
+
const projectRoot = opts.projectRootMarkers !== undefined
|
|
41
|
+
? await findProjectRoot(cwd, opts.projectRootMarkers)
|
|
42
|
+
: await findClaudeProjectRoot(cwd, { homeDir })
|
|
39
43
|
const skills = []
|
|
40
44
|
const commands = []
|
|
41
45
|
const rules = []
|
|
@@ -66,7 +70,10 @@ export async function loadClaude(opts = {}) {
|
|
|
66
70
|
warnings.push(...catalog.warnings)
|
|
67
71
|
|
|
68
72
|
// Permissions: discover the three settings files and merge.
|
|
69
|
-
const discovered = await discoverSettings(cwd, {
|
|
73
|
+
const discovered = await discoverSettings(cwd, {
|
|
74
|
+
homeDir,
|
|
75
|
+
...(opts.projectRootMarkers !== undefined ? { projectRootMarkers: opts.projectRootMarkers } : {}),
|
|
76
|
+
})
|
|
70
77
|
const merged = mergeSettings(discovered)
|
|
71
78
|
const parsed = parseRulesFor(merged)
|
|
72
79
|
for (const invalid of parsed.invalid) {
|
|
@@ -147,9 +154,11 @@ export async function loadClaude(opts = {}) {
|
|
|
147
154
|
export async function loadPermissions(opts = {}) {
|
|
148
155
|
const cwd = opts.cwd ?? process.cwd()
|
|
149
156
|
const homeDir = opts.homeDir ?? homedir()
|
|
150
|
-
const markers = opts.projectRootMarkers ?? ['.git']
|
|
151
157
|
const warnings = []
|
|
152
|
-
const discovered = await discoverSettings(cwd, {
|
|
158
|
+
const discovered = await discoverSettings(cwd, {
|
|
159
|
+
homeDir,
|
|
160
|
+
...(opts.projectRootMarkers !== undefined ? { projectRootMarkers: opts.projectRootMarkers } : {}),
|
|
161
|
+
})
|
|
153
162
|
const merged = mergeSettings(discovered)
|
|
154
163
|
const parsed = parseRulesFor(merged)
|
|
155
164
|
for (const invalid of parsed.invalid) {
|
package/src/settings.js
CHANGED
|
@@ -9,18 +9,22 @@
|
|
|
9
9
|
|
|
10
10
|
import { join } from 'node:path'
|
|
11
11
|
import { homedir } from 'node:os'
|
|
12
|
-
import { findProjectRoot, pathExists, readTextSafe } from './skills.js'
|
|
12
|
+
import { findProjectRoot, findClaudeProjectRoot, pathExists, readTextSafe } from './skills.js'
|
|
13
13
|
|
|
14
14
|
/**
|
|
15
15
|
* Discover the three settings files for a cwd.
|
|
16
16
|
* @param {string} cwd
|
|
17
17
|
* @param {{ homeDir?: string, projectRootMarkers?: string[] }} [opts]
|
|
18
|
+
* projectRootMarkers: explicit markers use findProjectRoot; otherwise CC
|
|
19
|
+
* semantics (closest .claude/ directory, .git fallback, home excluded).
|
|
18
20
|
* @returns {Promise<{ projectRoot?: string, user?: Source, project?: Source, local?: Source }>}
|
|
19
21
|
* Source = { scope, path, data } or undefined when absent/unreadable.
|
|
20
22
|
*/
|
|
21
23
|
export async function discoverSettings(cwd, opts = {}) {
|
|
22
24
|
const homeDir = opts.homeDir ?? homedir()
|
|
23
|
-
const projectRoot =
|
|
25
|
+
const projectRoot = opts.projectRootMarkers !== undefined
|
|
26
|
+
? await findProjectRoot(cwd, opts.projectRootMarkers)
|
|
27
|
+
: await findClaudeProjectRoot(cwd, { homeDir })
|
|
24
28
|
const userPath = join(homeDir, '.claude', 'settings.json')
|
|
25
29
|
const out = {}
|
|
26
30
|
if (projectRoot !== undefined) {
|
package/src/skills.js
CHANGED
|
@@ -24,6 +24,52 @@ export async function findProjectRoot(cwd, markers = ['.git']) {
|
|
|
24
24
|
}
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
+
/**
|
|
28
|
+
* Claude Code project-root discovery: the closest directory containing a
|
|
29
|
+
* `.claude/` directory IS the project configuration root — Claude Code walks
|
|
30
|
+
* up from the cwd looking for a `.claude/` directory (confirmed by
|
|
31
|
+
* anthropics/claude-code#80791: "Claude Code discovers project-level settings
|
|
32
|
+
* by walking up from the current working directory looking for a .claude/
|
|
33
|
+
* directory"), NOT the git root.
|
|
34
|
+
*
|
|
35
|
+
* Fallback: when no `.claude/` exists on the walk, fall back to the closest
|
|
36
|
+
* `.git` root (compat: repositories without a `.claude/` still resolve).
|
|
37
|
+
*
|
|
38
|
+
* Home-directory guard: `~/.claude` (Claude Code's global config dir) and a
|
|
39
|
+
* git-backed home are NEVER treated as a project root — the walk stops at
|
|
40
|
+
* home. Without the guard, every session outside a project would resolve its
|
|
41
|
+
* root to the user's home directory.
|
|
42
|
+
*
|
|
43
|
+
* @param {string} cwd - session working directory.
|
|
44
|
+
* @param {object} [opts] - { homeDir } excluded from root resolution;
|
|
45
|
+
* { stopAt } optional upper walk boundary (directory at or above which the
|
|
46
|
+
* walk stops — primarily a test seam so walk-top markers on the real
|
|
47
|
+
* machine cannot interfere; callers normally leave it unset).
|
|
48
|
+
* @returns {Promise<string | undefined>} project root directory.
|
|
49
|
+
*/
|
|
50
|
+
export async function findClaudeProjectRoot(cwd, opts = {}) {
|
|
51
|
+
const homeDir = opts.homeDir
|
|
52
|
+
const stopAt = opts.stopAt
|
|
53
|
+
const isHome = (dir) => homeDir !== undefined && dir === homeDir
|
|
54
|
+
const atStop = (dir) => stopAt !== undefined && dir === stopAt
|
|
55
|
+
// 1. Closest .claude/ directory (CC project-scope semantics), home excluded.
|
|
56
|
+
let current = resolve(cwd)
|
|
57
|
+
while (true) {
|
|
58
|
+
if (!isHome(current) && !atStop(current) && await pathExists(join(current, '.claude'))) return current
|
|
59
|
+
const parent = dirname(current)
|
|
60
|
+
if (parent === current || atStop(current)) break
|
|
61
|
+
current = parent
|
|
62
|
+
}
|
|
63
|
+
// 2. Fallback: closest .git root, home excluded.
|
|
64
|
+
current = resolve(cwd)
|
|
65
|
+
while (true) {
|
|
66
|
+
if (!isHome(current) && !atStop(current) && await pathExists(join(current, '.git'))) return current
|
|
67
|
+
const parent = dirname(current)
|
|
68
|
+
if (parent === current || atStop(current)) return undefined
|
|
69
|
+
current = parent
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
27
73
|
export async function pathExists(path) {
|
|
28
74
|
try { await stat(path); return true } catch { return false }
|
|
29
75
|
}
|