dsh-agent-plugins-market 0.6.0 → 0.6.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +143 -169
- package/README.zh.md +136 -170
- package/client/client.js +8202 -684
- package/client/client.js.map +1 -1
- package/lib/application/catalog.js +236 -47
- package/lib/application/details.js +19 -10
- package/lib/application/panel-resources.js +95 -0
- package/lib/application/project-agent-roles.js +39 -0
- package/lib/catalog/codex-mcp.js +114 -0
- package/lib/catalog/component-files.js +151 -0
- package/lib/catalog/manifests.js +105 -44
- package/lib/catalog/native-project.js +44 -23
- package/lib/catalog/paths.js +6 -6
- package/lib/catalog/project-config.js +96 -0
- package/lib/catalog/project-hooks.js +108 -0
- package/lib/catalog/scan-pipeline.js +6 -1
- package/lib/catalog/scan-resolvers.js +70 -26
- package/lib/catalog/source-catalog.js +16 -3
- package/lib/catalog/suite-components.js +125 -0
- package/lib/catalog/surfaces.js +60 -46
- package/lib/catalog/validate.js +16 -11
- package/lib/contracts/market.js +16 -1
- package/lib/index.js +187 -35
- package/lib/model/layouts.js +49 -0
- package/lib/routes.js +106 -6
- package/lib/runtime/agent-role-router.js +226 -0
- package/lib/runtime/commands-mounts.js +27 -67
- package/lib/runtime/feedback-tool.js +181 -0
- package/lib/runtime/hooks-mounts.js +66 -13
- package/lib/runtime/host-locale.js +23 -7
- package/lib/runtime/legacy-root-migration.js +12 -0
- package/lib/runtime/lsp-direct-config.js +6 -3
- package/lib/runtime/lsp-mounts.js +38 -24
- package/lib/runtime/lsp-server-state.js +19 -0
- package/lib/runtime/lsp-status.js +3 -2
- package/lib/runtime/mcp-backend.js +6 -2
- package/lib/runtime/mcp-client/config.js +6 -0
- package/lib/runtime/mcp-client/connection.js +8 -2
- package/lib/runtime/mcp-client/tools.js +6 -3
- package/lib/runtime/mcp-config.js +26 -13
- package/lib/runtime/mcp-direct-config.js +56 -0
- package/lib/runtime/mcp-mounts.js +25 -9
- package/lib/runtime/mcp-overrides.js +11 -5
- package/lib/runtime/mcp-status.js +10 -7
- package/lib/runtime/model-catalog.js +48 -0
- package/lib/runtime/project-runtime.js +136 -0
- package/lib/runtime/reconciler.js +38 -28
- package/lib/runtime/server-config.js +75 -0
- package/lib/runtime/skills-provider.js +31 -77
- package/lib/runtime/storage-migration.js +169 -0
- package/lib/runtime/subagent-catalog.js +129 -0
- package/lib/runtime/user-commands.js +86 -0
- package/lib/runtime/user-panels.js +176 -0
- package/lib/runtime/user-store.js +142 -0
- package/lib/types/application/catalog.d.ts +39 -9
- package/lib/types/application/panel-resources.d.ts +13 -0
- package/lib/types/application/project-agent-roles.d.ts +3 -0
- package/lib/types/application/queries.d.ts +17 -1
- package/lib/types/catalog/codex-mcp.d.ts +4 -0
- package/lib/types/catalog/component-files.d.ts +17 -0
- package/lib/types/catalog/manifests.d.ts +14 -9
- package/lib/types/catalog/native-project.d.ts +2 -5
- package/lib/types/catalog/paths.d.ts +4 -4
- package/lib/types/catalog/project-config.d.ts +6 -0
- package/lib/types/catalog/project-hooks.d.ts +10 -0
- package/lib/types/catalog/scan-pipeline.d.ts +2 -0
- package/lib/types/catalog/scan-resolvers.d.ts +4 -3
- package/lib/types/catalog/source-catalog.d.ts +1 -1
- package/lib/types/catalog/suite-components.d.ts +5 -0
- package/lib/types/catalog/surfaces.d.ts +2 -2
- package/lib/types/catalog/validate.d.ts +2 -0
- package/lib/types/contracts/market.d.ts +60 -0
- package/lib/types/contracts/mcp-status.d.ts +5 -1
- package/lib/types/index.d.ts +2 -2
- package/lib/types/model/layouts.d.ts +102 -0
- package/lib/types/model/types.d.ts +51 -6
- package/lib/types/routes.d.ts +6 -1
- package/lib/types/runtime/agent-role-router.d.ts +95 -0
- package/lib/types/runtime/commands-mounts.d.ts +16 -7
- package/lib/types/runtime/feedback-tool.d.ts +49 -0
- package/lib/types/runtime/hooks-mounts.d.ts +4 -0
- package/lib/types/runtime/host-locale.d.ts +1 -1
- package/lib/types/runtime/legacy-root-migration.d.ts +2 -0
- package/lib/types/runtime/lsp-mounts.d.ts +4 -0
- package/lib/types/runtime/lsp-server-state.d.ts +3 -0
- package/lib/types/runtime/lsp-status.d.ts +1 -0
- package/lib/types/runtime/mcp-backend.d.ts +9 -1
- package/lib/types/runtime/mcp-client/config.d.ts +4 -3
- package/lib/types/runtime/mcp-client/tools.d.ts +3 -0
- package/lib/types/runtime/mcp-config.d.ts +11 -5
- package/lib/types/runtime/mcp-direct-config.d.ts +7 -0
- package/lib/types/runtime/mcp-mounts.d.ts +2 -1
- package/lib/types/runtime/mcp-overrides.d.ts +2 -0
- package/lib/types/runtime/mcp-status.d.ts +1 -1
- package/lib/types/runtime/model-catalog.d.ts +7 -0
- package/lib/types/runtime/project-runtime.d.ts +32 -0
- package/lib/types/runtime/reconciler.d.ts +2 -0
- package/lib/types/runtime/server-config.d.ts +10 -0
- package/lib/types/runtime/skills-provider.d.ts +1 -5
- package/lib/types/runtime/storage-migration.d.ts +15 -0
- package/lib/types/runtime/subagent-catalog.d.ts +52 -0
- package/lib/types/runtime/user-commands.d.ts +24 -0
- package/lib/types/runtime/user-panels.d.ts +91 -0
- package/lib/types/runtime/user-store.d.ts +51 -0
- package/package.json +10 -3
- package/schemas/README.md +69 -7
- package/schemas/agent-plugins/spec.md +48 -0
- package/schemas/claude-code/marketplace.schema.json +143 -0
- package/schemas/claude-code/plugin.schema.json +105 -0
- package/schemas/claude-code/spec.md +108 -0
- package/schemas/codex/marketplace.schema.json +78 -0
- package/schemas/codex/plugin.schema.json +77 -0
- package/schemas/codex/spec.md +91 -0
- package/schemas/cursor/marketplace.schema.json +82 -0
- package/schemas/cursor/plugin.schema.json +58 -0
- package/schemas/cursor/spec.md +92 -0
- package/schemas/github-copilot/marketplace.schema.json +107 -0
- package/schemas/github-copilot/plugin.schema.json +89 -0
- package/schemas/github-copilot/spec.md +105 -0
- package/schemas/kimi/marketplace.schema.json +38 -0
- package/schemas/kimi/plugin.schema.json +84 -0
- package/schemas/kimi/spec.md +94 -0
- package/schemas/qoder/marketplace.schema.json +115 -0
- package/schemas/qoder/plugin.schema.json +127 -0
- package/schemas/qoder/spec.md +111 -0
- package/schemas/skill-collection/spec.md +29 -0
- package/schemas/universal/marketplace.schema.json +98 -0
- package/schemas/universal/plugin.schema.json +49 -0
- package/schemas/universal/spec.md +53 -0
- package/schemas/zcode/marketplace.schema.json +161 -0
- package/schemas/zcode/plugin.schema.json +89 -0
- package/schemas/zcode/spec.md +102 -0
- package/lib/catalog/legacy-root-migration.js +0 -51
- package/lib/types/catalog/legacy-root-migration.d.ts +0 -14
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* User panel storage: the persistence layer behind the skills / commands /
|
|
3
|
+
* agent-personas panels. Each surface owns one directory of Markdown files
|
|
4
|
+
* under `<userRoot>/user/<kind>/`, so user-authored entries survive restarts,
|
|
5
|
+
* are trivially hand-editable, and stay outside suite checkouts.
|
|
6
|
+
*
|
|
7
|
+
* Skills entries follow the SKILL.md frontmatter grammar (`name`,
|
|
8
|
+
* `description`, optional `whenToUse`, invocation controls) plus a
|
|
9
|
+
* `disabled: true` panel control; commands follow Claude Code command
|
|
10
|
+
* frontmatter (`description`, optional `argument-hint`, `disabled`); agent
|
|
11
|
+
* personas follow Claude Code agents frontmatter (`name`, `description`,
|
|
12
|
+
* optional `whenToUse`, `tools`, `model`, `disabled`).
|
|
13
|
+
* @module runtime/user-panels
|
|
14
|
+
*/
|
|
15
|
+
import { join } from 'node:path';
|
|
16
|
+
import { deleteEntryFile, entryExists, listEntryFiles, readEntryFile, USER_ENTRY_NAME, userEntryDir, writeEntryFile } from './user-store.js';
|
|
17
|
+
/** The skill-source label user-panel entries carry into the skill registry. */
|
|
18
|
+
export const USER_PANEL_SKILL_SOURCE = 'user-panel';
|
|
19
|
+
/** Ranks sit behind every shipped root so real files always win a name clash. */
|
|
20
|
+
const USER_PANEL_RANK = 600;
|
|
21
|
+
/** Throwing CRUD over one panel directory, shared by the three panels. */
|
|
22
|
+
export class UserPanelStore {
|
|
23
|
+
dataRoot;
|
|
24
|
+
kind;
|
|
25
|
+
extraNameCheck;
|
|
26
|
+
constructor(dataRoot, kind,
|
|
27
|
+
/** Extra name grammar for this panel (runs after USER_ENTRY_NAME). */
|
|
28
|
+
extraNameCheck = () => true) {
|
|
29
|
+
this.dataRoot = dataRoot;
|
|
30
|
+
this.kind = kind;
|
|
31
|
+
this.extraNameCheck = extraNameCheck;
|
|
32
|
+
}
|
|
33
|
+
/** The panel's directory under the data root. */
|
|
34
|
+
dirPath() {
|
|
35
|
+
return userEntryDir(this.dataRoot, this.kind);
|
|
36
|
+
}
|
|
37
|
+
/** The plugin data root the panel lives under. */
|
|
38
|
+
root() {
|
|
39
|
+
return this.dataRoot;
|
|
40
|
+
}
|
|
41
|
+
get dir() {
|
|
42
|
+
return this.dirPath();
|
|
43
|
+
}
|
|
44
|
+
/** Every entry, sorted by name; strict snapshots propagate temporary I/O failures. */
|
|
45
|
+
async list(strict = false) {
|
|
46
|
+
const files = await listEntryFiles(this.dir, strict);
|
|
47
|
+
return files.map(file => this.serialize(file));
|
|
48
|
+
}
|
|
49
|
+
/** One entry's full record, including the raw file text. */
|
|
50
|
+
async get(name) {
|
|
51
|
+
if (!USER_ENTRY_NAME.test(name))
|
|
52
|
+
return undefined;
|
|
53
|
+
const file = await readEntryFile(join(this.dir, `${name}.md`), name);
|
|
54
|
+
if (file === undefined)
|
|
55
|
+
return undefined;
|
|
56
|
+
return this.serialize(file);
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Project one parsed file onto the wire shape. The description prefers the
|
|
60
|
+
* strict frontmatter parse (multi-line YAML like `description: |` survives
|
|
61
|
+
* intact there) and falls back to the shallow line record.
|
|
62
|
+
*/
|
|
63
|
+
serialize(file) {
|
|
64
|
+
const strictDescription = typeof file.description === 'string' && file.description !== '' ? file.description : undefined;
|
|
65
|
+
const shallowDescription = typeof file.meta['description'] === 'string' && file.meta['description'] !== '' ? file.meta['description'] : '';
|
|
66
|
+
return {
|
|
67
|
+
name: file.fallbackName,
|
|
68
|
+
description: strictDescription ?? shallowDescription,
|
|
69
|
+
disabled: file.meta['disabled'] === true,
|
|
70
|
+
metadata: file.meta,
|
|
71
|
+
path: file.file,
|
|
72
|
+
content: file.body,
|
|
73
|
+
rawText: file.text,
|
|
74
|
+
origin: 'user'
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
/** Create an entry; refuses an occupied name. */
|
|
78
|
+
async create(name, text) {
|
|
79
|
+
if (!USER_ENTRY_NAME.test(name) || !this.extraNameCheck(name))
|
|
80
|
+
throw new Error(`invalid name "${name}" — use lowercase letters, digits, and dashes, starting with a letter or digit`);
|
|
81
|
+
if (await entryExists(this.dir, name))
|
|
82
|
+
throw new Error(`an entry named "${name}" already exists`);
|
|
83
|
+
await writeEntryFile(this.dir, name, text);
|
|
84
|
+
const created = await this.get(name);
|
|
85
|
+
if (created === undefined)
|
|
86
|
+
throw new Error(`entry "${name}" vanished after write`);
|
|
87
|
+
return created;
|
|
88
|
+
}
|
|
89
|
+
/** Replace one entry's file content wholesale. */
|
|
90
|
+
async update(name, text) {
|
|
91
|
+
if (!USER_ENTRY_NAME.test(name) || !this.extraNameCheck(name))
|
|
92
|
+
throw new Error(`invalid entry name "${name}"`);
|
|
93
|
+
if (!(await entryExists(this.dir, name)))
|
|
94
|
+
throw new Error(`no entry named "${name}"`);
|
|
95
|
+
await writeEntryFile(this.dir, name, text);
|
|
96
|
+
}
|
|
97
|
+
/** Delete one entry; a missing file is a no-op (idempotent delete). */
|
|
98
|
+
async remove(name) {
|
|
99
|
+
if (!USER_ENTRY_NAME.test(name))
|
|
100
|
+
throw new Error(`invalid entry name "${name}"`);
|
|
101
|
+
await deleteEntryFile(this.dir, name);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
/** Locate the three stores; constructed once per plugin activation. */
|
|
105
|
+
export function createUserPanelStores(dataRoot) {
|
|
106
|
+
return {
|
|
107
|
+
skills: new UserPanelStore(dataRoot, 'skills', isUserSkillEntryName),
|
|
108
|
+
commands: new UserPanelStore(dataRoot, 'commands'),
|
|
109
|
+
agents: new UserPanelStore(dataRoot, 'agents', isUserSkillEntryName)
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* The skills/agents panels accept only harness skill-grammar names
|
|
114
|
+
* (`[a-z0-9-]`, no underscores): a candidate name reaching the skill
|
|
115
|
+
* registry's `get()` with an invalid name is dropped there, and several
|
|
116
|
+
* consumers validate strictly, so `_`-named entries would be dead weight.
|
|
117
|
+
* Commands keep the looser command grammar (`_` allowed).
|
|
118
|
+
*/
|
|
119
|
+
export function isUserSkillEntryName(name) {
|
|
120
|
+
return /^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(name);
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Skill provider over user skills only. Agent definitions belong to the
|
|
124
|
+
* subagent catalog and never become skill candidates or slash entries.
|
|
125
|
+
*/
|
|
126
|
+
export class UserPanelSkillProvider {
|
|
127
|
+
skills;
|
|
128
|
+
t;
|
|
129
|
+
name = 'user-panel';
|
|
130
|
+
constructor(skills, t) {
|
|
131
|
+
this.skills = skills;
|
|
132
|
+
this.t = t;
|
|
133
|
+
}
|
|
134
|
+
async list(_options) {
|
|
135
|
+
const candidates = [];
|
|
136
|
+
const skills = await this.skills.list();
|
|
137
|
+
for (const entry of skills) {
|
|
138
|
+
if (entry.disabled || !isUserSkillEntryName(entry.name))
|
|
139
|
+
continue;
|
|
140
|
+
candidates.push({
|
|
141
|
+
name: entry.name,
|
|
142
|
+
description: this.t('userSkillDescription', { description: entry.description === '' ? entry.name : entry.description }),
|
|
143
|
+
...(typeof entry.metadata['whenToUse'] === 'string' && entry.metadata['whenToUse'] !== '' ? { whenToUse: entry.metadata['whenToUse'] } : {}),
|
|
144
|
+
invocation: {
|
|
145
|
+
modelInvocable: entry.metadata['disable-model-invocation'] !== true,
|
|
146
|
+
userInvocable: entry.metadata['user-invocable'] !== false
|
|
147
|
+
},
|
|
148
|
+
source: USER_PANEL_SKILL_SOURCE,
|
|
149
|
+
provider: this.name,
|
|
150
|
+
rank: USER_PANEL_RANK,
|
|
151
|
+
locator: { name: entry.name },
|
|
152
|
+
path: entry.path,
|
|
153
|
+
resourceBase: { kind: 'directory', path: this.skills.dirPath() }
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
return candidates;
|
|
157
|
+
}
|
|
158
|
+
async get(candidate, _options) {
|
|
159
|
+
const locator = candidate.locator;
|
|
160
|
+
const store = this.skills;
|
|
161
|
+
const entry = await store.get(locator.name);
|
|
162
|
+
if (entry === undefined || entry.disabled)
|
|
163
|
+
return undefined;
|
|
164
|
+
return {
|
|
165
|
+
name: candidate.name,
|
|
166
|
+
description: candidate.description,
|
|
167
|
+
...(typeof entry.metadata['whenToUse'] === 'string' && entry.metadata['whenToUse'] !== '' ? { whenToUse: entry.metadata['whenToUse'] } : {}),
|
|
168
|
+
invocation: candidate.invocation,
|
|
169
|
+
source: candidate.source,
|
|
170
|
+
provider: this.name,
|
|
171
|
+
resourceBase: { kind: 'directory', path: store.dirPath() },
|
|
172
|
+
path: entry.path,
|
|
173
|
+
content: entry.content
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared persistence primitives for the market's user-authored panels:
|
|
3
|
+
* skills, commands, and agent personas. Each panel owns one directory of
|
|
4
|
+
* Markdown files under the plugin data root, parsed with the same frontmatter
|
|
5
|
+
* rules the catalog uses, so user entries look exactly like suite entries to
|
|
6
|
+
* the runtime mounts.
|
|
7
|
+
* @module runtime/user-store
|
|
8
|
+
*/
|
|
9
|
+
import { mkdir, readdir, readFile, rm, stat, writeFile } from 'node:fs/promises';
|
|
10
|
+
import { join } from 'node:path';
|
|
11
|
+
import { parseDocument, stringify } from 'yaml';
|
|
12
|
+
import { parseSkillFrontmatter, stripFrontmatter } from '../catalog/skills-parse.js';
|
|
13
|
+
/** Where user panel entries persist: `<userRoot>/user/<kind>/`. */
|
|
14
|
+
export function userEntryDir(dataRoot, kind) {
|
|
15
|
+
return join(dataRoot, 'user', kind);
|
|
16
|
+
}
|
|
17
|
+
/** `[a-z][a-z0-9_-]*` — the grammar every panel entry name must satisfy. */
|
|
18
|
+
export const USER_ENTRY_NAME = /^[a-z][a-z0-9_-]*$/;
|
|
19
|
+
/** The enablement frontmatter key shared by skills and commands. */
|
|
20
|
+
export const DISABLED_KEY = 'disabled';
|
|
21
|
+
/** Parse full YAML metadata without flattening arrays, mappings, or multiline strings. */
|
|
22
|
+
export function parseFrontmatterRecord(text) {
|
|
23
|
+
const match = /^---\r?\n([\s\S]*?)\r?\n---(?:\r?\n|$)/.exec(text);
|
|
24
|
+
if (match === null) {
|
|
25
|
+
if (/^---(?:\r?\n|$)/.test(text))
|
|
26
|
+
throw new Error('Invalid frontmatter: missing closing delimiter');
|
|
27
|
+
return {};
|
|
28
|
+
}
|
|
29
|
+
const doc = parseDocument(match[1]);
|
|
30
|
+
if (doc.errors.length > 0)
|
|
31
|
+
throw new Error(`Invalid frontmatter: ${doc.errors[0].message}`);
|
|
32
|
+
const value = doc.toJS({ maxAliasCount: 100 });
|
|
33
|
+
if (value === null)
|
|
34
|
+
return {};
|
|
35
|
+
if (typeof value !== 'object' || Array.isArray(value))
|
|
36
|
+
throw new Error('Frontmatter must be a mapping');
|
|
37
|
+
return value;
|
|
38
|
+
}
|
|
39
|
+
/** Read one entry; strict runtime snapshots throw on I/O errors other than a vanished file. */
|
|
40
|
+
export async function readEntryFile(file, fallbackName, strict = false) {
|
|
41
|
+
if (fallbackName !== '' && !USER_ENTRY_NAME.test(fallbackName))
|
|
42
|
+
return undefined;
|
|
43
|
+
let text;
|
|
44
|
+
try {
|
|
45
|
+
text = await readFile(file, 'utf8');
|
|
46
|
+
}
|
|
47
|
+
catch (error) {
|
|
48
|
+
if (strict && error.code !== 'ENOENT')
|
|
49
|
+
throw error;
|
|
50
|
+
return undefined;
|
|
51
|
+
}
|
|
52
|
+
let meta;
|
|
53
|
+
try {
|
|
54
|
+
meta = parseFrontmatterRecord(text);
|
|
55
|
+
}
|
|
56
|
+
catch (error) {
|
|
57
|
+
meta = { disabled: true, validationError: String(error) };
|
|
58
|
+
}
|
|
59
|
+
const parsed = parseSkillFrontmatter(text, fallbackName);
|
|
60
|
+
const name = typeof parsed === 'string' ? fallbackName : typeof parsed.name === 'string' && parsed.name !== '' ? parsed.name : fallbackName;
|
|
61
|
+
return {
|
|
62
|
+
fallbackName,
|
|
63
|
+
name,
|
|
64
|
+
file,
|
|
65
|
+
text,
|
|
66
|
+
body: stripFrontmatter(text),
|
|
67
|
+
meta,
|
|
68
|
+
...(typeof parsed === 'string' ? {} : parsed)
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
/** List `.md` entries; strict runtime snapshots propagate I/O errors instead of publishing partial inventories. */
|
|
72
|
+
export async function listEntryFiles(dir, strict = false) {
|
|
73
|
+
let entries;
|
|
74
|
+
try {
|
|
75
|
+
entries = await readdir(dir);
|
|
76
|
+
}
|
|
77
|
+
catch (error) {
|
|
78
|
+
if (strict && error.code !== 'ENOENT')
|
|
79
|
+
throw error;
|
|
80
|
+
return [];
|
|
81
|
+
}
|
|
82
|
+
const found = [];
|
|
83
|
+
for (const entry of entries.sort()) {
|
|
84
|
+
if (!entry.endsWith('.md'))
|
|
85
|
+
continue;
|
|
86
|
+
const file = join(dir, entry);
|
|
87
|
+
try {
|
|
88
|
+
const info = await stat(file);
|
|
89
|
+
if (!info.isFile())
|
|
90
|
+
continue;
|
|
91
|
+
}
|
|
92
|
+
catch (error) {
|
|
93
|
+
if (strict && error.code !== 'ENOENT')
|
|
94
|
+
throw error;
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
97
|
+
const parsed = await readEntryFile(file, entry.slice(0, -3), strict);
|
|
98
|
+
if (parsed !== undefined)
|
|
99
|
+
found.push(parsed);
|
|
100
|
+
}
|
|
101
|
+
return found;
|
|
102
|
+
}
|
|
103
|
+
/** Write one panel entry; the name becomes `<name>.md`. */
|
|
104
|
+
export async function writeEntryFile(dir, name, text) {
|
|
105
|
+
assertEntryName(name);
|
|
106
|
+
parseFrontmatterRecord(text);
|
|
107
|
+
await mkdir(dir, { recursive: true });
|
|
108
|
+
await writeFile(join(dir, `${name}.md`), text, 'utf8');
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Enforce the entry-name grammar at every filesystem choke point. The name
|
|
112
|
+
* is interpolated into `join(dir, name + '.md')`, so an unchecked name with
|
|
113
|
+
* separators or `..` could address any `.md` file (or directory) on disk —
|
|
114
|
+
* read, write, and delete all go through this guard.
|
|
115
|
+
*/
|
|
116
|
+
function assertEntryName(name) {
|
|
117
|
+
if (!USER_ENTRY_NAME.test(name))
|
|
118
|
+
throw new Error(`invalid entry name "${name}" — use lowercase letters, digits, dashes, or underscores, starting with a letter`);
|
|
119
|
+
}
|
|
120
|
+
/** Delete one panel entry file; missing files resolve silently. */
|
|
121
|
+
export async function deleteEntryFile(dir, name) {
|
|
122
|
+
assertEntryName(name);
|
|
123
|
+
await rm(join(dir, `${name}.md`), { force: true });
|
|
124
|
+
}
|
|
125
|
+
/** Whether an entry file with that name already exists. */
|
|
126
|
+
export async function entryExists(dir, name) {
|
|
127
|
+
if (!USER_ENTRY_NAME.test(name))
|
|
128
|
+
return false;
|
|
129
|
+
try {
|
|
130
|
+
await stat(join(dir, `${name}.md`));
|
|
131
|
+
return true;
|
|
132
|
+
}
|
|
133
|
+
catch {
|
|
134
|
+
return false;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
/** Serialize a frontmatter block from a shallow record (deterministic key order). */
|
|
138
|
+
export function serializeFrontmatter(meta) {
|
|
139
|
+
if (Object.keys(meta).length === 0)
|
|
140
|
+
return '';
|
|
141
|
+
return `---\n${stringify(meta, { sortMapEntries: true })}---\n`;
|
|
142
|
+
}
|
|
@@ -7,7 +7,7 @@ import { type HostMcpClientProbe, type McpBackend } from '../runtime/mcp-backend
|
|
|
7
7
|
import { type DownloadRegionSetting, type EffectiveRegion } from '../runtime/regions.js';
|
|
8
8
|
import type { McpStatusPayload } from '../contracts/mcp-status.js';
|
|
9
9
|
import type { LspStatusPayload } from '../contracts/lsp-status.js';
|
|
10
|
-
import type { OverviewPayload, SkillContent, SuiteDetail } from '../contracts/market.js';
|
|
10
|
+
import type { OverviewPayload, ServerConfigPayload, SkillContent, SuiteDetail } from '../contracts/market.js';
|
|
11
11
|
import { type SourceKind, type SourceRef, type Suite, type SuiteDimension, type SuiteSurfaceKey } from '../model/types.js';
|
|
12
12
|
/**
|
|
13
13
|
* Git network tuning, plumbed from the host config to every remote-touching
|
|
@@ -70,12 +70,13 @@ export declare class Catalog {
|
|
|
70
70
|
* discovery (cheap mapping) instead of re-walking every checkout — a full
|
|
71
71
|
* rescan of a 2,500-suite catalog costs ~1s and UI mutations happen in
|
|
72
72
|
* bursts. Content-changing mutations (add / update / remove / adopt /
|
|
73
|
-
* refresh / acquire)
|
|
74
|
-
*
|
|
73
|
+
* refresh / acquire) clear every cached discovery generation; the TTL
|
|
74
|
+
* bounds staleness for in-place working-tree
|
|
75
75
|
* edits of local sources.
|
|
76
76
|
*/
|
|
77
77
|
private readonly scanCache;
|
|
78
|
-
private
|
|
78
|
+
private scanGeneration;
|
|
79
|
+
private readonly scanPromises;
|
|
79
80
|
private static readonly SCAN_CACHE_TTL_MS;
|
|
80
81
|
private static readonly SCAN_CACHE_MAX_ENTRIES;
|
|
81
82
|
/**
|
|
@@ -107,6 +108,9 @@ export declare class Catalog {
|
|
|
107
108
|
private mcpBackendWriter;
|
|
108
109
|
/** Download-region source (the host settings namespace scope). */
|
|
109
110
|
private downloadRegionProvider;
|
|
111
|
+
private scanProjectLayouts;
|
|
112
|
+
/** Apply the host's project-layout switch and invalidate every project snapshot. */
|
|
113
|
+
setScanProjectLayouts(enabled: boolean): Promise<void>;
|
|
110
114
|
constructor(options: CatalogOptions);
|
|
111
115
|
/** Install the host tool snapshot provider used by the MCP status surface. */
|
|
112
116
|
setMcpToolSnapshotProvider(provider: () => readonly McpToolSnapshot[]): void;
|
|
@@ -137,12 +141,22 @@ export declare class Catalog {
|
|
|
137
141
|
subscribe(listener: () => void): () => void;
|
|
138
142
|
/** Build the flat MCP service inventory for the status surface. */
|
|
139
143
|
mcpStatus(): Promise<McpStatusPayload>;
|
|
144
|
+
/** Persist a user-owned MCP service and reconcile its bridge mount. */
|
|
145
|
+
addMcpServer(name: string, server: unknown): Promise<void>;
|
|
146
|
+
/** Read effective service configuration without exposing credential literals. */
|
|
147
|
+
serverConfig(kind: 'mcp' | 'lsp', id: string): Promise<ServerConfigPayload>;
|
|
148
|
+
private resolveServerConfig;
|
|
149
|
+
/** Validate a complete replacement before writing; plugin checkouts remain untouched. */
|
|
150
|
+
saveServerConfig(kind: 'mcp' | 'lsp', id: string, config: unknown): Promise<void>;
|
|
140
151
|
/** The LSP status surface: declared servers merged with mount diagnostics. */
|
|
141
152
|
lspStatus(): Promise<LspStatusPayload>;
|
|
142
153
|
/** The user's direct LSP server table (normalized specs). */
|
|
143
154
|
lspServers(): Promise<Record<string, import('../model/types.js').LspServerSpec>>;
|
|
155
|
+
/** Create one direct LSP declaration without replacing other user servers. */
|
|
156
|
+
addLspServer(name: string, config: unknown): Promise<void>;
|
|
144
157
|
/** Validate and persist the user's direct LSP server table. */
|
|
145
158
|
setLspServers(raw: unknown): Promise<Record<string, import('../model/types.js').LspServerSpec>>;
|
|
159
|
+
setLspServerEnabled(id: string, enabled: boolean): Promise<void>;
|
|
146
160
|
/** One suite's persisted MCP overrides, addressed by qualified suite id. */
|
|
147
161
|
mcpOverrides(sourceId: string, suiteId: string): Promise<McpSuiteOverrides>;
|
|
148
162
|
/**
|
|
@@ -183,8 +197,8 @@ export declare class Catalog {
|
|
|
183
197
|
setMcpBackendWriter(writer: (backend: McpBackend) => Promise<void>): void;
|
|
184
198
|
/** Install the download-region source (the host settings namespace scope). */
|
|
185
199
|
setDownloadRegionProvider(provider: () => Promise<DownloadRegionSetting>): void;
|
|
186
|
-
/**
|
|
187
|
-
allMcpOverrides(): Promise<Map<string, McpSuiteOverrides>>;
|
|
200
|
+
/** MCP overrides for a supplied runtime selection, or the full market catalog when omitted. */
|
|
201
|
+
allMcpOverrides(suites?: readonly Suite[]): Promise<Map<string, McpSuiteOverrides>>;
|
|
188
202
|
/** Load persisted user state once at plugin activation. */
|
|
189
203
|
load(): Promise<void>;
|
|
190
204
|
get sources(): SourceRef[];
|
|
@@ -201,7 +215,7 @@ export declare class Catalog {
|
|
|
201
215
|
skillContent(sourceId: string, suiteId: string, skillName: string): Promise<SkillContent>;
|
|
202
216
|
/** The full market overview from one user snapshot. */
|
|
203
217
|
overview(): Promise<OverviewPayload>;
|
|
204
|
-
/**
|
|
218
|
+
/** Runtime discovery scans only sources containing an enabled install. No acquisition or network access. */
|
|
205
219
|
enabledUserSuites(): Promise<Suite[]>;
|
|
206
220
|
/** Enabled user- and project-dimension suites for a workspace cwd. */
|
|
207
221
|
enabledSuitesForCwd(cwd: string): Promise<{
|
|
@@ -271,8 +285,16 @@ export declare class Catalog {
|
|
|
271
285
|
kind?: SourceKind;
|
|
272
286
|
sha256?: string;
|
|
273
287
|
}): Promise<void>;
|
|
274
|
-
/**
|
|
275
|
-
|
|
288
|
+
/**
|
|
289
|
+
* Remove a source and its install entries. `deleteCheckout` (the UI's
|
|
290
|
+
* "also delete the managed market directory" option) physically removes the
|
|
291
|
+
* `.sources/<id>` checkout — including an adopted one, since a directory
|
|
292
|
+
* under `.sources` is manager-owned storage. A `local` source whose URL
|
|
293
|
+
* points outside `.sources` is registration-only: its directory is never
|
|
294
|
+
* touched. Deleting the checkout is what keeps a removed source from
|
|
295
|
+
* reappearing as an unmanaged entry.
|
|
296
|
+
*/
|
|
297
|
+
removeSource(sourceId: string, deleteCheckout?: boolean): Promise<void>;
|
|
276
298
|
/** Refresh one source checkout, or every source when sourceId is omitted. */
|
|
277
299
|
refreshSource(sourceId?: string): Promise<void>;
|
|
278
300
|
/** Install a suite from a source and enable it. The client confirms the
|
|
@@ -287,6 +309,13 @@ export declare class Catalog {
|
|
|
287
309
|
setSurface(sourceId: string, suiteId: string, surface: SuiteSurfaceKey, enabled: boolean): Promise<void>;
|
|
288
310
|
/** Append config-seeded sources missing from user state and persist them. */
|
|
289
311
|
mergeSources(sources: SourceRef[]): Promise<void>;
|
|
312
|
+
/**
|
|
313
|
+
* Panel changed hook: the HTTP layer notifies after a user-panel mutation
|
|
314
|
+
* (skills / commands / agent personas). Catalog state is untouched — this
|
|
315
|
+
* only runs the change pipeline so the runtime remounts commands and the
|
|
316
|
+
* skill providers re-read their catalogs.
|
|
317
|
+
*/
|
|
318
|
+
notifyPanelsChanged(): Promise<void>;
|
|
290
319
|
private setInstalled;
|
|
291
320
|
/**
|
|
292
321
|
* Acquire one source's content by kind: clone (git), download+extract
|
|
@@ -299,6 +328,7 @@ export declare class Catalog {
|
|
|
299
328
|
private sourceCheckoutPath;
|
|
300
329
|
private buildSnapshot;
|
|
301
330
|
private invalidateSnapshot;
|
|
331
|
+
private invalidateScans;
|
|
302
332
|
/**
|
|
303
333
|
* Invalidate snapshots and run the change pipeline. `keepScanCache` marks
|
|
304
334
|
* state-only mutations (install, enable, surface toggles, MCP overrides)
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { UserPanelEntryWire, UserPanelKind } from '../contracts/market.js';
|
|
2
|
+
import type { UserPanelStore } from '../runtime/user-panels.js';
|
|
3
|
+
import type { Catalog } from './catalog.js';
|
|
4
|
+
/** The routes consume this structural surface, also implemented by user-only stores in tests. */
|
|
5
|
+
export interface PanelResourceStore {
|
|
6
|
+
/** Strict runtime reads propagate I/O failures so catalogs cannot publish partial replacements. */
|
|
7
|
+
list(strict?: boolean): Promise<UserPanelEntryWire[]>;
|
|
8
|
+
get(id: string): Promise<UserPanelEntryWire | undefined>;
|
|
9
|
+
create(name: string, text: string): Promise<UserPanelEntryWire>;
|
|
10
|
+
update(id: string, text: string): Promise<void>;
|
|
11
|
+
remove(id: string): Promise<void>;
|
|
12
|
+
}
|
|
13
|
+
export declare function createPanelResources(catalog: Catalog, users: Record<UserPanelKind, UserPanelStore>): Record<UserPanelKind, PanelResourceStore>;
|
|
@@ -5,8 +5,10 @@ import type { OverviewPayload, SkillContent, SourceProgress, SuiteDetail } from
|
|
|
5
5
|
import type { SourceRef, SuiteSurfaceKey } from '../model/types.js';
|
|
6
6
|
import type { McpServerOverride, McpSuiteOverrides } from '../runtime/mcp-overrides.js';
|
|
7
7
|
import type { HostMcpClientProbe, McpBackend } from '../runtime/mcp-backend.js';
|
|
8
|
+
import type { ServerConfigPayload } from '../contracts/market.js';
|
|
8
9
|
/** Read-only market operations required by HTTP routes. */
|
|
9
10
|
export interface MarketQueries {
|
|
11
|
+
serverConfig(kind: 'mcp' | 'lsp', id: string): Promise<ServerConfigPayload>;
|
|
10
12
|
readonly sources: SourceRef[];
|
|
11
13
|
overview(): Promise<OverviewPayload>;
|
|
12
14
|
mcpStatus(): Promise<McpStatusPayload>;
|
|
@@ -19,6 +21,9 @@ export interface MarketQueries {
|
|
|
19
21
|
}
|
|
20
22
|
/** Mutating market operations required by HTTP routes. */
|
|
21
23
|
export interface MarketMutations {
|
|
24
|
+
saveServerConfig(kind: 'mcp' | 'lsp', id: string, config: unknown): Promise<void>;
|
|
25
|
+
addMcpServer(name: string, server: unknown): Promise<void>;
|
|
26
|
+
addLspServer(name: string, config: unknown): Promise<void>;
|
|
22
27
|
addSource(input: {
|
|
23
28
|
url: string;
|
|
24
29
|
branch?: string;
|
|
@@ -33,7 +38,11 @@ export interface MarketMutations {
|
|
|
33
38
|
kind?: 'git' | 'local' | 'archive';
|
|
34
39
|
sha256?: string;
|
|
35
40
|
}): Promise<void>;
|
|
36
|
-
|
|
41
|
+
/**
|
|
42
|
+
* Remove a source registration; `deleteCheckout` also physically deletes
|
|
43
|
+
* its managed `.sources/<id>` checkout. External local paths are never deleted.
|
|
44
|
+
*/
|
|
45
|
+
removeSource(sourceId: string, deleteCheckout?: boolean): Promise<void>;
|
|
37
46
|
/** Register an unmanaged `.sources/` checkout in place (manual-clone repair). */
|
|
38
47
|
adoptSource(id: string): Promise<SourceRef>;
|
|
39
48
|
refreshSource(sourceId?: string): Promise<void>;
|
|
@@ -44,6 +53,7 @@ export interface MarketMutations {
|
|
|
44
53
|
setMcpOverride(sourceId: string, suiteId: string, serverKey: string, override: McpServerOverride | null): Promise<void>;
|
|
45
54
|
/** Validate and persist the user's direct LSP server table. */
|
|
46
55
|
setLspServers(raw: unknown): Promise<Record<string, import('../model/types.js').LspServerSpec>>;
|
|
56
|
+
setLspServerEnabled(id: string, enabled: boolean): Promise<void>;
|
|
47
57
|
/** Re-run the MCP reconcile pass: retries failed mounts and clears residual tools. */
|
|
48
58
|
retryMounts(): Promise<void>;
|
|
49
59
|
reauthorizeMcpServer(serverName: string): Promise<void>;
|
|
@@ -59,6 +69,12 @@ export interface MarketMutations {
|
|
|
59
69
|
}>;
|
|
60
70
|
/** Switch the MCP mount backend and remount every suite server through it. */
|
|
61
71
|
setMcpBackend(backend: McpBackend): Promise<void>;
|
|
72
|
+
/**
|
|
73
|
+
* Panel change hook: the HTTP layer notifies after a user-panel mutation
|
|
74
|
+
* (skills / commands / agent personas) so the runtime remounts commands
|
|
75
|
+
* and the skill providers re-read their catalogs.
|
|
76
|
+
*/
|
|
77
|
+
notifyPanelsChanged(): Promise<void>;
|
|
62
78
|
}
|
|
63
79
|
/** Complete application surface required by the HTTP routes. */
|
|
64
80
|
export type MarketService = MarketQueries & MarketMutations;
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
/** Codex TOML MCP tables normalize into the existing transport and credential contracts. */
|
|
2
|
+
import type { McpServer } from '../model/types.js';
|
|
3
|
+
/** Unknown or malformed server options reject that server; other servers stay independently usable. */
|
|
4
|
+
export declare function normalizeCodexMcp(raw: Record<string, unknown>, errors: string[]): Record<string, McpServer>;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { SuiteMarkdownResource } from '../model/types.js';
|
|
2
|
+
export declare function isRecord(value: unknown): value is Record<string, unknown>;
|
|
3
|
+
/** Reject both lexical and symlink escapes. Optional defaults may be absent without a diagnostic. */
|
|
4
|
+
export declare function componentPath(root: string, value: string, errors: string[], required?: boolean): Promise<string | undefined>;
|
|
5
|
+
/** Commands and agents may declare one file, several files, or recursive directories. */
|
|
6
|
+
export declare function discoverMarkdownResources(root: string, component: 'commands' | 'agents', declaration: unknown, manifestPath: string, errors: string[], extensions?: readonly string[]): Promise<SuiteMarkdownResource[]>;
|
|
7
|
+
export interface ComponentDocument {
|
|
8
|
+
path?: string;
|
|
9
|
+
value: Record<string, unknown>;
|
|
10
|
+
}
|
|
11
|
+
/** A malformed declared file invalidates that configuration surface instead of falling back. */
|
|
12
|
+
export declare function componentDocuments(root: string, declaration: unknown, errors: string[], allowDirectory?: boolean): Promise<ComponentDocument[] | undefined>;
|
|
13
|
+
export declare function firstComponentFile(root: string, candidates: readonly string[]): Promise<string | undefined>;
|
|
14
|
+
/** Legacy/native suites without catalog resource lists keep their conventional directory behavior. */
|
|
15
|
+
export declare function defaultMarkdownResources(root: string, component: 'agents' | 'commands'): Promise<SuiteMarkdownResource[]>;
|
|
16
|
+
export declare function resourceText(resource: SuiteMarkdownResource): Promise<string>;
|
|
17
|
+
export declare function resourceCommandName(name: string): string;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import type { SuiteManifest } from '../model/types.js';
|
|
2
|
-
|
|
1
|
+
import type { SuiteManifest, SuiteComponents } from '../model/types.js';
|
|
2
|
+
import { type ManifestKind } from '../model/layouts.js';
|
|
3
|
+
export type { ManifestKind } from '../model/layouts.js';
|
|
3
4
|
/** One manifest candidate: its file path and the dialect it selects. */
|
|
4
5
|
export interface ManifestCandidate {
|
|
5
6
|
kind: ManifestKind;
|
|
@@ -11,17 +12,20 @@ export declare function detectManifest(dir: string): Promise<ManifestCandidate |
|
|
|
11
12
|
export declare function hasSuiteManifest(dir: string): Promise<boolean>;
|
|
12
13
|
/** Fallback suite identity for manifest-less skill collections. */
|
|
13
14
|
export declare function syntheticManifestName(root: string): string;
|
|
15
|
+
export declare function componentDeclarations(record: object): SuiteComponents;
|
|
14
16
|
/**
|
|
15
17
|
* Parse one manifest document into a normalized SuiteManifest. The v1 dialect
|
|
16
18
|
* is schema-validated (fail-closed); the others are structurally read with
|
|
17
19
|
* light tolerance, and `hint` (a marketplace plugin entry) fills in gaps.
|
|
18
20
|
*/
|
|
19
|
-
export declare function readManifest(root: string, errors: string[], hint: {
|
|
21
|
+
export declare function readManifest(root: string, errors: string[], hint: ({
|
|
20
22
|
name?: string;
|
|
21
23
|
version?: string;
|
|
22
24
|
description?: string;
|
|
23
|
-
} | undefined): Promise<SuiteManifest | undefined>;
|
|
24
|
-
export interface MarketplaceEntry {
|
|
25
|
+
} & SuiteComponents) | undefined): Promise<SuiteManifest | undefined>;
|
|
26
|
+
export interface MarketplaceEntry extends SuiteComponents {
|
|
27
|
+
layout?: ManifestKind;
|
|
28
|
+
strict?: boolean;
|
|
25
29
|
name?: string;
|
|
26
30
|
version?: string;
|
|
27
31
|
description?: string;
|
|
@@ -36,6 +40,7 @@ export interface MarketplaceEntry {
|
|
|
36
40
|
};
|
|
37
41
|
}
|
|
38
42
|
export interface Marketplace {
|
|
43
|
+
pluginRoot?: string;
|
|
39
44
|
name?: string;
|
|
40
45
|
entries: MarketplaceEntry[];
|
|
41
46
|
}
|
|
@@ -45,11 +50,11 @@ export interface ReadMarketplaceResult extends Marketplace {
|
|
|
45
50
|
}
|
|
46
51
|
/**
|
|
47
52
|
* Read every marketplace manifest the checkout carries, in dialect
|
|
48
|
-
* precedence order
|
|
49
|
-
*
|
|
50
|
-
*
|
|
53
|
+
* precedence order shared with suite manifests, then the shared root catalog.
|
|
54
|
+
* The scan strategy selects the first catalog that produces suites; an empty
|
|
55
|
+
* or invalid catalog allows the next candidate to be tried.
|
|
51
56
|
*/
|
|
52
|
-
export declare function readMarketplaces(checkoutDir: string): Promise<ReadMarketplaceResult[]>;
|
|
57
|
+
export declare function readMarketplaces(checkoutDir: string, errors?: string[]): Promise<ReadMarketplaceResult[]>;
|
|
53
58
|
/** Read the highest-precedence marketplace manifest, or undefined when absent. */
|
|
54
59
|
export declare function readMarketplace(checkoutDir: string): Promise<Marketplace | undefined>;
|
|
55
60
|
/** Resolve one marketplace entry to a local checkout-relative directory, or
|
|
@@ -1,14 +1,11 @@
|
|
|
1
1
|
import type { Suite, SuiteDimension } from '../model/types.js';
|
|
2
|
+
import { type ProjectLayout } from '../model/layouts.js';
|
|
2
3
|
/**
|
|
3
4
|
* Project-local directory conventions, in precedence order. Each entry maps
|
|
4
5
|
* one agent convention onto the content subdirectories it owns.
|
|
5
6
|
*/
|
|
6
7
|
export declare const NATIVE_PROJECT_DIRS: readonly NativeProjectDir[];
|
|
7
|
-
export
|
|
8
|
-
dirName: string;
|
|
9
|
-
label: string;
|
|
10
|
-
subdirs: readonly string[];
|
|
11
|
-
}
|
|
8
|
+
export type NativeProjectDir = ProjectLayout;
|
|
12
9
|
/**
|
|
13
10
|
* Discover native project-layout suites under a project root. Each
|
|
14
11
|
* convention directory that carries content becomes one synthetic suite;
|
|
@@ -7,14 +7,14 @@ export declare const STATE_FILE_NAME = "state.json";
|
|
|
7
7
|
export declare function expandHome(path: string): string;
|
|
8
8
|
/** Resolve the harness home (`$DSH_HOME` or `~/.dsh`). */
|
|
9
9
|
export declare function resolveDshHome(): string;
|
|
10
|
-
/** Resolve the user-dimension
|
|
11
|
-
export declare function resolveUserRoot(
|
|
10
|
+
/** Resolve the canonical user-dimension root. Legacy overrides are migration inputs only. */
|
|
11
|
+
export declare function resolveUserRoot(_configUserRoot?: string): string;
|
|
12
12
|
/**
|
|
13
13
|
* Resolve the suite data root hosting `${PLUGIN_DATA}` directories and the
|
|
14
14
|
* MCP overrides. Defaults under the user root so the whole plugin persists
|
|
15
|
-
* into one directory
|
|
15
|
+
* into one directory. Legacy overrides are migration inputs only.
|
|
16
16
|
*/
|
|
17
|
-
export declare function resolveDataRoot(
|
|
17
|
+
export declare function resolveDataRoot(_configDataRoot?: string, _configUserRoot?: string): string;
|
|
18
18
|
/** Resolve a project root from a workspace cwd: nearest ancestor with `.git`. */
|
|
19
19
|
export declare function findProjectRoot(cwd: string): Promise<string>;
|
|
20
20
|
/** Resolve the project-dimension suite root for a workspace cwd. */
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { McpSuiteConfig } from '../model/types.js';
|
|
2
|
+
import type { ProjectMcpFormat } from '../model/layouts.js';
|
|
3
|
+
/** Later files override earlier keys; a malformed layer invalidates the whole layout's table. */
|
|
4
|
+
export declare function discoverProjectMcp(projectRoot: string, files: readonly string[], errors: string[], format?: ProjectMcpFormat): Promise<McpSuiteConfig | undefined>;
|
|
5
|
+
/** Missing is undefined; malformed/unreadable is null and carries a diagnostic. */
|
|
6
|
+
export declare function readProjectDocument(projectRoot: string, file: string, errors: string[], format?: 'JSON' | 'TOML'): Promise<Record<string, unknown> | undefined | null>;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/** Normalize native Claude-shaped command hooks for the existing host execution bridge. */
|
|
2
|
+
import type { ProjectHooks } from '../model/types.js';
|
|
3
|
+
import type { ProjectHookFormat } from '../model/layouts.js';
|
|
4
|
+
/** Merge settings layers additively, deduplicating identical event/matcher/command triples. */
|
|
5
|
+
export declare function discoverProjectHooks(projectRoot: string, files: readonly string[], errors: string[], format?: ProjectHookFormat): Promise<ProjectHooks | undefined>;
|
|
6
|
+
/** Pure validation shared by native settings and plugin file/inline declarations. */
|
|
7
|
+
export declare function normalizeHookDocuments(projectRoot: string, documents: Array<{
|
|
8
|
+
file: string;
|
|
9
|
+
settings: Record<string, unknown>;
|
|
10
|
+
}>, errors: string[], format?: ProjectHookFormat): ProjectHooks | undefined;
|