@deftai/directive-core 0.92.0 → 0.93.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/doctor/index.d.ts +1 -0
- package/dist/doctor/index.js +1 -0
- package/dist/doctor/main.js +12 -0
- package/dist/doctor/openclaw-l2-adapter.d.ts +26 -0
- package/dist/doctor/openclaw-l2-adapter.js +199 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/init-deposit/hygiene.js +22 -0
- package/dist/init-deposit/index.d.ts +3 -0
- package/dist/init-deposit/index.js +3 -0
- package/dist/init-deposit/init-deposit.js +10 -0
- package/dist/init-deposit/refresh.js +11 -0
- package/dist/init-deposit/scaffold.js +3 -133
- package/dist/init-deposit/skill-discovery-deposit.d.ts +65 -0
- package/dist/init-deposit/skill-discovery-deposit.js +287 -0
- package/dist/init-deposit/skill-discovery-hosts.d.ts +94 -0
- package/dist/init-deposit/skill-discovery-hosts.js +217 -0
- package/dist/init-deposit/slash-deposit.d.ts +46 -0
- package/dist/init-deposit/slash-deposit.js +174 -0
- package/dist/policy/host-slash-commands.d.ts +28 -0
- package/dist/policy/host-slash-commands.js +103 -0
- package/dist/policy/index.d.ts +1 -0
- package/dist/policy/index.js +47 -7
- package/dist/slash/emitters.d.ts +102 -0
- package/dist/slash/emitters.js +148 -0
- package/dist/slash/generator.d.ts +98 -0
- package/dist/slash/generator.js +145 -0
- package/dist/slash/index.d.ts +16 -0
- package/dist/slash/index.js +16 -0
- package/dist/slash/openclaw-adapter.d.ts +64 -0
- package/dist/slash/openclaw-adapter.js +198 -0
- package/dist/slash/openclaw-deposit.d.ts +73 -0
- package/dist/slash/openclaw-deposit.js +279 -0
- package/dist/slash/openclaw-slugs.d.ts +52 -0
- package/dist/slash/openclaw-slugs.js +126 -0
- package/dist/slash/product-set.d.ts +50 -0
- package/dist/slash/product-set.js +142 -0
- package/dist/vbrief-validate/plan-hooks.d.ts +4 -0
- package/dist/vbrief-validate/plan-hooks.js +50 -0
- package/dist/xbrief/create.d.ts +36 -0
- package/dist/xbrief/create.js +285 -0
- package/dist/xbrief/index.d.ts +14 -0
- package/dist/xbrief/index.js +42 -0
- package/dist/xbrief/paths.d.ts +37 -0
- package/dist/xbrief/paths.js +123 -0
- package/dist/xbrief/styles.d.ts +36 -0
- package/dist/xbrief/styles.js +235 -0
- package/dist/xbrief/types.d.ts +50 -0
- package/dist/xbrief/types.js +17 -0
- package/dist/xbrief/verify.d.ts +30 -0
- package/dist/xbrief/verify.js +251 -0
- package/package.json +11 -3
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Multi-host thin skill discovery deposit (#75 residual).
|
|
3
|
+
*
|
|
4
|
+
* Mirrors the consumer skill inventory already written to `.agents/skills/`
|
|
5
|
+
* into additional host paths (`.claude/skills/`, `.codex/skills/`,
|
|
6
|
+
* `.github/skills/`, `.cursor/skills/`). Thin pointers only — no full skill
|
|
7
|
+
* body copies. Windows-safe file writes (no elevated symlink requirement).
|
|
8
|
+
*
|
|
9
|
+
* Distinct from epic #55 slash-command deposit (#3054).
|
|
10
|
+
*/
|
|
11
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
12
|
+
import { join, resolve } from "node:path";
|
|
13
|
+
import { containedWrite } from "../fs/contained-write.js";
|
|
14
|
+
import { assertDestinationNotSymlink } from "../fs/projection-containment.js";
|
|
15
|
+
import { hostSkillRelativePath, isHostSkillDiscoveryEnabled, listSkillDiscoveryHosts, loadHostSkillDiscoveryPolicyFromProject, loadHostSkillDiscoveryRawFromProject, resolveHostSkillDiscoveryPolicyDetailed, SKILL_DISCOVERY_HOSTS, validateHostSkillDiscovery, } from "./skill-discovery-hosts.js";
|
|
16
|
+
/**
|
|
17
|
+
* Consumer skill discovery inventory shared by `.agents/skills/` and multi-host
|
|
18
|
+
* deposit. Each entry is a thin SKILL.md pointer into `.deft/core/…` — never a
|
|
19
|
+
* full skill body. Keep this list the single SoT for install-time discovery.
|
|
20
|
+
*/
|
|
21
|
+
export const CONSUMER_SKILL_DISCOVERY_INVENTORY = [
|
|
22
|
+
{
|
|
23
|
+
dir: "deft",
|
|
24
|
+
content: `---
|
|
25
|
+
name: deft
|
|
26
|
+
description: Apply deft framework standards for AI-assisted development. Use when starting projects, writing code, running tests, making commits, or when the user references deft, project standards, or coding guidelines.
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
Read and follow: .deft/core/SKILL.md
|
|
30
|
+
`,
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
dir: "deft-directive-setup",
|
|
34
|
+
content: `---
|
|
35
|
+
name: deft-directive-setup
|
|
36
|
+
description: >-
|
|
37
|
+
Set up a new project with Deft framework standards. Use when the user wants
|
|
38
|
+
to bootstrap user preferences, configure a project, or generate a project
|
|
39
|
+
specification. Walks through setup conversationally — no separate CLI needed.
|
|
40
|
+
---
|
|
41
|
+
|
|
42
|
+
Read and follow: .deft/core/skills/deft-directive-setup/SKILL.md
|
|
43
|
+
`,
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
dir: "deft-directive-build",
|
|
47
|
+
content: `---
|
|
48
|
+
name: deft-directive-build
|
|
49
|
+
description: >-
|
|
50
|
+
Build a project from scope vBRIEFs following Deft framework standards.
|
|
51
|
+
Use after deft-directive-setup has generated the project definition, or when
|
|
52
|
+
the user has scope vBRIEFs ready to implement. Handles scaffolding,
|
|
53
|
+
implementation, testing, and quality checks phase by phase.
|
|
54
|
+
---
|
|
55
|
+
|
|
56
|
+
Read and follow: .deft/core/skills/deft-directive-build/SKILL.md
|
|
57
|
+
`,
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
dir: "deft-directive-review-cycle",
|
|
61
|
+
content: `---
|
|
62
|
+
name: deft-directive-review-cycle
|
|
63
|
+
description: >-
|
|
64
|
+
Greptile bot reviewer response workflow. Use when running a review cycle
|
|
65
|
+
on a PR — to audit process prerequisites, fetch bot findings, fix all
|
|
66
|
+
issues in a single batch commit, and exit cleanly when no P0/P1 issues
|
|
67
|
+
remain. Enables cloud agents to run autonomous PR review cycles.
|
|
68
|
+
---
|
|
69
|
+
|
|
70
|
+
Read and follow: .deft/core/skills/deft-directive-review-cycle/SKILL.md
|
|
71
|
+
`,
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
dir: "deft-directive-refinement",
|
|
75
|
+
content: `---
|
|
76
|
+
name: deft-directive-refinement
|
|
77
|
+
description: >-
|
|
78
|
+
Structured refinement workflow. Compares open GitHub issues against
|
|
79
|
+
the roadmap, triages new issues one-at-a-time with human review, and updates
|
|
80
|
+
the roadmap with phase placement, analysis comments, and index entries.
|
|
81
|
+
---
|
|
82
|
+
|
|
83
|
+
Read and follow: .deft/core/skills/deft-directive-refinement/SKILL.md
|
|
84
|
+
`,
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
dir: "deft-directive-swarm",
|
|
88
|
+
content: `---
|
|
89
|
+
name: deft-directive-swarm
|
|
90
|
+
description: >-
|
|
91
|
+
Parallel local agent orchestration. Use when running multiple agents
|
|
92
|
+
on roadmap items simultaneously — to select non-overlapping tasks, set up
|
|
93
|
+
isolated worktrees, launch agents with proven prompts, monitor progress,
|
|
94
|
+
handle stalled review cycles, and close out PRs cleanly.
|
|
95
|
+
---
|
|
96
|
+
|
|
97
|
+
Read and follow: .deft/core/skills/deft-directive-swarm/SKILL.md
|
|
98
|
+
`,
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
dir: "deft-directive-interview",
|
|
102
|
+
content: `---
|
|
103
|
+
name: deft-directive-interview
|
|
104
|
+
description: >-
|
|
105
|
+
Deterministic structured Q&A interview skill. Use when a skill or workflow
|
|
106
|
+
needs to collect structured answers from the user — one question per turn,
|
|
107
|
+
numbered options, default acceptance, and a confirmation gate.
|
|
108
|
+
---
|
|
109
|
+
|
|
110
|
+
Read and follow: .deft/core/skills/deft-directive-interview/SKILL.md
|
|
111
|
+
`,
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
dir: "deft-directive-pre-pr",
|
|
115
|
+
content: `---
|
|
116
|
+
name: deft-directive-pre-pr
|
|
117
|
+
description: >-
|
|
118
|
+
Iterative pre-PR quality loop (Read-Write-Lint-Diff-Loop). Use before
|
|
119
|
+
pushing a branch for PR creation — structured self-review that agents run
|
|
120
|
+
to catch issues before they reach the bot reviewer.
|
|
121
|
+
---
|
|
122
|
+
|
|
123
|
+
Read and follow: .deft/core/skills/deft-directive-pre-pr/SKILL.md
|
|
124
|
+
`,
|
|
125
|
+
},
|
|
126
|
+
{
|
|
127
|
+
dir: "deft-directive-sync",
|
|
128
|
+
content: `---
|
|
129
|
+
name: deft-directive-sync
|
|
130
|
+
description: >-
|
|
131
|
+
Session-start framework sync skill. Use at the beginning of a session to
|
|
132
|
+
pull latest framework updates, validate project files, and confirm alignment
|
|
133
|
+
before starting work.
|
|
134
|
+
---
|
|
135
|
+
|
|
136
|
+
Read and follow: .deft/core/skills/deft-directive-sync/SKILL.md
|
|
137
|
+
`,
|
|
138
|
+
},
|
|
139
|
+
];
|
|
140
|
+
/** Max bytes for a thin pointer body — fat full-skill copies fail this guard. */
|
|
141
|
+
export const MAX_THIN_SKILL_POINTER_BYTES = 1200;
|
|
142
|
+
/**
|
|
143
|
+
* True when content is a thin discovery pointer (not an inlined skill body).
|
|
144
|
+
* Managed deposit must stay pointer-shaped so updates do not rot full copies.
|
|
145
|
+
*/
|
|
146
|
+
export function isThinSkillPointer(content) {
|
|
147
|
+
if (content.length > MAX_THIN_SKILL_POINTER_BYTES)
|
|
148
|
+
return false;
|
|
149
|
+
if (!content.includes("Read and follow:"))
|
|
150
|
+
return false;
|
|
151
|
+
// Canonical consumer target lives under the deposited framework root.
|
|
152
|
+
if (!content.includes(".deft/core/"))
|
|
153
|
+
return false;
|
|
154
|
+
// Reject bodies that look like full skill process docs.
|
|
155
|
+
if (/\n##\s+Phase\b/i.test(content))
|
|
156
|
+
return false;
|
|
157
|
+
if (/\n##\s+Anti-?[Pp]atterns\b/i.test(content))
|
|
158
|
+
return false;
|
|
159
|
+
return true;
|
|
160
|
+
}
|
|
161
|
+
function projectionTarget(projectDir, ...relSegments) {
|
|
162
|
+
const target = join(projectDir, ...relSegments);
|
|
163
|
+
assertDestinationNotSymlink(projectDir, target);
|
|
164
|
+
return target;
|
|
165
|
+
}
|
|
166
|
+
function containedProjectWrite(projectDir, target, data) {
|
|
167
|
+
containedWrite({
|
|
168
|
+
root: resolve(projectDir),
|
|
169
|
+
target,
|
|
170
|
+
data,
|
|
171
|
+
mode: "replace",
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Deposit thin skill discovery pointers into enabled multi-host paths.
|
|
176
|
+
* Idempotent: skips write when on-disk content already matches. Never creates
|
|
177
|
+
* symlinks (Windows-safe without elevation).
|
|
178
|
+
*/
|
|
179
|
+
export function writeMultiHostSkillDiscovery(projectDir, io, options = {}) {
|
|
180
|
+
const rawPolicy = options.policy !== undefined
|
|
181
|
+
? options.policy
|
|
182
|
+
: loadHostSkillDiscoveryRawFromProject(projectDir);
|
|
183
|
+
// Production validation surface (#75 SLizard P1): report malformed opt-outs
|
|
184
|
+
// before resolving the deposit enablement map.
|
|
185
|
+
for (const err of validateHostSkillDiscovery(rawPolicy)) {
|
|
186
|
+
io.printf(`WARNING: ${err}\n`);
|
|
187
|
+
}
|
|
188
|
+
const resolved = resolveHostSkillDiscoveryPolicyDetailed(rawPolicy);
|
|
189
|
+
for (const warning of resolved.warnings) {
|
|
190
|
+
io.printf(`WARNING: ${warning}\n`);
|
|
191
|
+
}
|
|
192
|
+
if (resolved.refuseAll) {
|
|
193
|
+
return {
|
|
194
|
+
changed: false,
|
|
195
|
+
changedPaths: [],
|
|
196
|
+
hostsTouched: [],
|
|
197
|
+
hostsSkipped: [...listSkillDiscoveryHosts()],
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
// Prefer the public project-root loader when reading disk so policy inspection
|
|
201
|
+
// and deposit share one production call path (not test-only exports).
|
|
202
|
+
const policy = options.policy !== undefined
|
|
203
|
+
? resolved.policy
|
|
204
|
+
: loadHostSkillDiscoveryPolicyFromProject(projectDir);
|
|
205
|
+
const inventory = options.inventory ?? CONSUMER_SKILL_DISCOVERY_INVENTORY;
|
|
206
|
+
for (const skill of inventory) {
|
|
207
|
+
if (!isThinSkillPointer(skill.content)) {
|
|
208
|
+
throw new Error(`skill discovery inventory entry "${skill.dir}" is not a thin pointer; ` +
|
|
209
|
+
`refusing multi-host deposit (#75 non-inlining).`);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
const changedPaths = [];
|
|
213
|
+
const hostsTouched = new Set();
|
|
214
|
+
const hostsSkipped = [];
|
|
215
|
+
for (const hostId of listSkillDiscoveryHosts()) {
|
|
216
|
+
if (!isHostSkillDiscoveryEnabled(hostId, policy)) {
|
|
217
|
+
hostsSkipped.push(hostId);
|
|
218
|
+
continue;
|
|
219
|
+
}
|
|
220
|
+
for (const skill of inventory) {
|
|
221
|
+
const rel = hostSkillRelativePath(hostId, skill.dir);
|
|
222
|
+
const segments = rel.split("/");
|
|
223
|
+
const abs = projectionTarget(projectDir, ...segments);
|
|
224
|
+
let existing = null;
|
|
225
|
+
if (existsSync(abs)) {
|
|
226
|
+
try {
|
|
227
|
+
existing = readFileSync(abs, "utf8");
|
|
228
|
+
}
|
|
229
|
+
catch {
|
|
230
|
+
existing = null;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
if (existing === skill.content) {
|
|
234
|
+
continue;
|
|
235
|
+
}
|
|
236
|
+
// Never clobber consumer-authored host skills. Only create missing
|
|
237
|
+
// files or rewrite prior managed thin pointers (#75 Greptile P1).
|
|
238
|
+
if (existing !== null && !isThinSkillPointer(existing)) {
|
|
239
|
+
io.printf(`Skill discovery (#75): preserving consumer skill at ${rel} (not a managed thin pointer).\n`);
|
|
240
|
+
continue;
|
|
241
|
+
}
|
|
242
|
+
containedProjectWrite(projectDir, abs, skill.content);
|
|
243
|
+
changedPaths.push(rel.replace(/\\/g, "/"));
|
|
244
|
+
hostsTouched.add(hostId);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
if (hostsSkipped.length > 0) {
|
|
248
|
+
io.printf(`Skill discovery multi-host opt-out (plan.policy.hostSkillDiscovery): skipped ${hostsSkipped.join(", ")}\n`);
|
|
249
|
+
}
|
|
250
|
+
if (changedPaths.length > 0) {
|
|
251
|
+
const hostList = [...hostsTouched].join(", ");
|
|
252
|
+
io.printf(`Multi-host skill discovery deposit (#75): wrote ${changedPaths.length} thin pointer(s) for host(s): ${hostList}\n`);
|
|
253
|
+
}
|
|
254
|
+
else if (hostsSkipped.length < SKILL_DISCOVERY_HOSTS.length) {
|
|
255
|
+
io.printf("Multi-host skill discovery deposit (#75): already current — skipping.\n");
|
|
256
|
+
}
|
|
257
|
+
return {
|
|
258
|
+
changed: changedPaths.length > 0,
|
|
259
|
+
changedPaths,
|
|
260
|
+
hostsTouched: [...hostsTouched],
|
|
261
|
+
hostsSkipped,
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
/**
|
|
265
|
+
* Shared inventory helper for `.agents/skills/` (canonical) deposit.
|
|
266
|
+
* Returns true when any pointer was created (matching writeAgentsSkills).
|
|
267
|
+
*/
|
|
268
|
+
export function writeAgentsSkillsFromInventory(projectDir, io, inventory = CONSUMER_SKILL_DISCOVERY_INVENTORY) {
|
|
269
|
+
projectionTarget(projectDir, ".agents");
|
|
270
|
+
const allExist = inventory.every((skill) => existsSync(join(projectDir, ".agents", "skills", skill.dir, "SKILL.md")));
|
|
271
|
+
if (allExist) {
|
|
272
|
+
io.printf(".agents/skills/ already present — skipping.\n");
|
|
273
|
+
return false;
|
|
274
|
+
}
|
|
275
|
+
for (const skill of inventory) {
|
|
276
|
+
if (!isThinSkillPointer(skill.content)) {
|
|
277
|
+
throw new Error(`skill discovery inventory entry "${skill.dir}" is not a thin pointer (#75).`);
|
|
278
|
+
}
|
|
279
|
+
const path = projectionTarget(projectDir, ".agents", "skills", skill.dir, "SKILL.md");
|
|
280
|
+
if (existsSync(path))
|
|
281
|
+
continue;
|
|
282
|
+
containedProjectWrite(projectDir, path, skill.content);
|
|
283
|
+
}
|
|
284
|
+
io.printf(".agents/skills/ created — deft skills will be auto-discovered.\n");
|
|
285
|
+
return true;
|
|
286
|
+
}
|
|
287
|
+
//# sourceMappingURL=skill-discovery-deposit.js.map
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Multi-host skill discovery layouts + per-host opt-out (#75 residual).
|
|
3
|
+
*
|
|
4
|
+
* Deposits **skill** discovery dirs (thin SKILL.md pointers). Distinct from
|
|
5
|
+
* epic #55 slash/command paths (`.claude/commands/`, `.codex/prompts/`, …).
|
|
6
|
+
*
|
|
7
|
+
* | Host id | Relative skills directory |
|
|
8
|
+
* |---------|---------------------------|
|
|
9
|
+
* | claude | `.claude/skills` |
|
|
10
|
+
* | cursor | `.cursor/skills` |
|
|
11
|
+
* | codex | `.codex/skills` |
|
|
12
|
+
* | github | `.github/skills` |
|
|
13
|
+
*
|
|
14
|
+
* Policy: `plan.policy.hostSkillDiscovery` — per-host boolean, default all true.
|
|
15
|
+
* Opt-out skips deposit for that host only.
|
|
16
|
+
*/
|
|
17
|
+
/** Hosts with a known residual skill-discovery path matrix (#75). */
|
|
18
|
+
export declare const SKILL_DISCOVERY_HOSTS: readonly ["claude", "cursor", "codex", "github"];
|
|
19
|
+
export type SkillDiscoveryHostId = (typeof SKILL_DISCOVERY_HOSTS)[number];
|
|
20
|
+
/** Repo-relative layout for one host’s skill discovery tree. */
|
|
21
|
+
export interface HostSkillDiscoveryLayout {
|
|
22
|
+
readonly hostId: SkillDiscoveryHostId;
|
|
23
|
+
/**
|
|
24
|
+
* Repo-relative directory (posix, no trailing slash).
|
|
25
|
+
* Example: `.claude/skills`
|
|
26
|
+
*/
|
|
27
|
+
readonly relativeDir: string;
|
|
28
|
+
/** Typical agent product that scans this path. */
|
|
29
|
+
readonly typicalHost: string;
|
|
30
|
+
/**
|
|
31
|
+
* Filename under `{relativeDir}/{skillDir}/` (always `SKILL.md` for v1 —
|
|
32
|
+
* same thin-pointer format as `.agents/skills/`).
|
|
33
|
+
*/
|
|
34
|
+
readonly skillFilename: string;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Documented host id → skills directory mapping (#75 residual matrix).
|
|
38
|
+
* Frozen; additive registration only.
|
|
39
|
+
*/
|
|
40
|
+
export declare const HOST_SKILL_DISCOVERY_LAYOUTS: Readonly<Record<SkillDiscoveryHostId, HostSkillDiscoveryLayout>>;
|
|
41
|
+
export declare const FIELD_HOST_SKILL_DISCOVERY = "plan.policy.hostSkillDiscovery";
|
|
42
|
+
export declare const FIELD_HOST_SKILL_DISCOVERY_CLI_ALIAS = "hostSkillDiscovery";
|
|
43
|
+
/** Per-host skill discovery deposit toggles (#75). Default: all enabled. */
|
|
44
|
+
export type HostSkillDiscoveryPolicy = Record<SkillDiscoveryHostId, boolean>;
|
|
45
|
+
export declare const DEFAULT_HOST_SKILL_DISCOVERY_POLICY: HostSkillDiscoveryPolicy;
|
|
46
|
+
export interface HostSkillDiscoveryPolicyField {
|
|
47
|
+
readonly name: string;
|
|
48
|
+
readonly current: HostSkillDiscoveryPolicy;
|
|
49
|
+
readonly default: HostSkillDiscoveryPolicy;
|
|
50
|
+
readonly source: string;
|
|
51
|
+
}
|
|
52
|
+
/** Type guard for {@link SkillDiscoveryHostId}. */
|
|
53
|
+
export declare function isSkillDiscoveryHostId(value: string): value is SkillDiscoveryHostId;
|
|
54
|
+
/** Stable list of residual skill-discovery hosts. */
|
|
55
|
+
export declare function listSkillDiscoveryHosts(): readonly SkillDiscoveryHostId[];
|
|
56
|
+
/** Look up the documented layout for a host, or throw. */
|
|
57
|
+
export declare function getHostSkillDiscoveryLayout(hostId: SkillDiscoveryHostId): HostSkillDiscoveryLayout;
|
|
58
|
+
export interface ResolvedHostSkillDiscoveryPolicy {
|
|
59
|
+
readonly policy: HostSkillDiscoveryPolicy;
|
|
60
|
+
/** Non-empty when raw policy was malformed; callers SHOULD surface these. */
|
|
61
|
+
readonly warnings: readonly string[];
|
|
62
|
+
/**
|
|
63
|
+
* True when the entire raw value is unusable (not an object). Deposit SHOULD
|
|
64
|
+
* skip all hosts rather than silently apply defaults after a bad opt-out.
|
|
65
|
+
*/
|
|
66
|
+
readonly refuseAll: boolean;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Resolve typed host skill discovery policy from raw PROJECT-DEFINITION value.
|
|
70
|
+
* Fail-closed on malformed opt-outs (#75 Greptile P1): bad host values disable
|
|
71
|
+
* that host; wholly non-object values refuse all multi-host deposit.
|
|
72
|
+
*/
|
|
73
|
+
export declare function resolveHostSkillDiscoveryPolicyDetailed(raw: unknown): ResolvedHostSkillDiscoveryPolicy;
|
|
74
|
+
/** Resolve typed host skill discovery policy from raw PROJECT-DEFINITION value. */
|
|
75
|
+
export declare function resolveHostSkillDiscoveryPolicy(raw: unknown): HostSkillDiscoveryPolicy;
|
|
76
|
+
export declare function validateHostSkillDiscovery(value: unknown): string[];
|
|
77
|
+
export declare function isHostSkillDiscoveryEnabled(host: SkillDiscoveryHostId, policy?: HostSkillDiscoveryPolicy): boolean;
|
|
78
|
+
/** Inspector row for `policy:show --field=hostSkillDiscovery`. */
|
|
79
|
+
export declare function inspectHostSkillDiscovery(data: Record<string, unknown> | null): HostSkillDiscoveryPolicyField;
|
|
80
|
+
/** Load raw hostSkillDiscovery value from PROJECT-DEFINITION, if present. */
|
|
81
|
+
export declare function loadHostSkillDiscoveryRawFromProject(projectRoot: string): unknown;
|
|
82
|
+
/** Resolve host skill discovery policy from PROJECT-DEFINITION on disk. */
|
|
83
|
+
export declare function loadHostSkillDiscoveryPolicyFromProject(projectRoot: string): HostSkillDiscoveryPolicy;
|
|
84
|
+
/**
|
|
85
|
+
* Resolve policy + validation warnings from PROJECT-DEFINITION for deposit.
|
|
86
|
+
* Surfaces malformed opt-outs so init/update can report them (#75 Greptile P1).
|
|
87
|
+
*/
|
|
88
|
+
export declare function loadHostSkillDiscoveryPolicyDetailedFromProject(projectRoot: string): ResolvedHostSkillDiscoveryPolicy;
|
|
89
|
+
/**
|
|
90
|
+
* Repo-relative posix path for one skill under a host layout.
|
|
91
|
+
* Example: `.claude/skills/deft-directive-build/SKILL.md`
|
|
92
|
+
*/
|
|
93
|
+
export declare function hostSkillRelativePath(hostId: SkillDiscoveryHostId, skillDir: string): string;
|
|
94
|
+
//# sourceMappingURL=skill-discovery-hosts.d.ts.map
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Multi-host skill discovery layouts + per-host opt-out (#75 residual).
|
|
3
|
+
*
|
|
4
|
+
* Deposits **skill** discovery dirs (thin SKILL.md pointers). Distinct from
|
|
5
|
+
* epic #55 slash/command paths (`.claude/commands/`, `.codex/prompts/`, …).
|
|
6
|
+
*
|
|
7
|
+
* | Host id | Relative skills directory |
|
|
8
|
+
* |---------|---------------------------|
|
|
9
|
+
* | claude | `.claude/skills` |
|
|
10
|
+
* | cursor | `.cursor/skills` |
|
|
11
|
+
* | codex | `.codex/skills` |
|
|
12
|
+
* | github | `.github/skills` |
|
|
13
|
+
*
|
|
14
|
+
* Policy: `plan.policy.hostSkillDiscovery` — per-host boolean, default all true.
|
|
15
|
+
* Opt-out skips deposit for that host only.
|
|
16
|
+
*/
|
|
17
|
+
import { readPlanPolicy } from "../policy/plan-extensions.js";
|
|
18
|
+
import { loadProjectDefinition } from "../policy/resolve.js";
|
|
19
|
+
/** Hosts with a known residual skill-discovery path matrix (#75). */
|
|
20
|
+
export const SKILL_DISCOVERY_HOSTS = ["claude", "cursor", "codex", "github"];
|
|
21
|
+
/**
|
|
22
|
+
* Documented host id → skills directory mapping (#75 residual matrix).
|
|
23
|
+
* Frozen; additive registration only.
|
|
24
|
+
*/
|
|
25
|
+
export const HOST_SKILL_DISCOVERY_LAYOUTS = Object.freeze({
|
|
26
|
+
claude: Object.freeze({
|
|
27
|
+
hostId: "claude",
|
|
28
|
+
relativeDir: ".claude/skills",
|
|
29
|
+
typicalHost: "Claude Code",
|
|
30
|
+
skillFilename: "SKILL.md",
|
|
31
|
+
}),
|
|
32
|
+
cursor: Object.freeze({
|
|
33
|
+
hostId: "cursor",
|
|
34
|
+
relativeDir: ".cursor/skills",
|
|
35
|
+
typicalHost: "Cursor",
|
|
36
|
+
skillFilename: "SKILL.md",
|
|
37
|
+
}),
|
|
38
|
+
codex: Object.freeze({
|
|
39
|
+
hostId: "codex",
|
|
40
|
+
relativeDir: ".codex/skills",
|
|
41
|
+
typicalHost: "OpenAI Codex",
|
|
42
|
+
skillFilename: "SKILL.md",
|
|
43
|
+
}),
|
|
44
|
+
github: Object.freeze({
|
|
45
|
+
hostId: "github",
|
|
46
|
+
relativeDir: ".github/skills",
|
|
47
|
+
typicalHost: "GitHub Copilot",
|
|
48
|
+
skillFilename: "SKILL.md",
|
|
49
|
+
}),
|
|
50
|
+
});
|
|
51
|
+
export const FIELD_HOST_SKILL_DISCOVERY = "plan.policy.hostSkillDiscovery";
|
|
52
|
+
export const FIELD_HOST_SKILL_DISCOVERY_CLI_ALIAS = "hostSkillDiscovery";
|
|
53
|
+
export const DEFAULT_HOST_SKILL_DISCOVERY_POLICY = {
|
|
54
|
+
claude: true,
|
|
55
|
+
cursor: true,
|
|
56
|
+
codex: true,
|
|
57
|
+
github: true,
|
|
58
|
+
};
|
|
59
|
+
/** Type guard for {@link SkillDiscoveryHostId}. */
|
|
60
|
+
export function isSkillDiscoveryHostId(value) {
|
|
61
|
+
return SKILL_DISCOVERY_HOSTS.includes(value);
|
|
62
|
+
}
|
|
63
|
+
/** Stable list of residual skill-discovery hosts. */
|
|
64
|
+
export function listSkillDiscoveryHosts() {
|
|
65
|
+
return SKILL_DISCOVERY_HOSTS;
|
|
66
|
+
}
|
|
67
|
+
/** Look up the documented layout for a host, or throw. */
|
|
68
|
+
export function getHostSkillDiscoveryLayout(hostId) {
|
|
69
|
+
const layout = HOST_SKILL_DISCOVERY_LAYOUTS[hostId];
|
|
70
|
+
if (layout === undefined) {
|
|
71
|
+
throw new Error(`No skill discovery layout for host: ${hostId}`);
|
|
72
|
+
}
|
|
73
|
+
return layout;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Resolve one host toggle. Malformed present values fail closed to **disabled**
|
|
77
|
+
* (do not silently enable deposit when the operator attempted an opt-out shape).
|
|
78
|
+
*/
|
|
79
|
+
function readHostBoolean(rec, host, fallback, warnings) {
|
|
80
|
+
if (!(host in rec)) {
|
|
81
|
+
return fallback;
|
|
82
|
+
}
|
|
83
|
+
const value = rec[host];
|
|
84
|
+
if (typeof value === "boolean") {
|
|
85
|
+
return value;
|
|
86
|
+
}
|
|
87
|
+
warnings.push(`${FIELD_HOST_SKILL_DISCOVERY}.${host} must be a boolean (got ${typeof value}); treating as disabled`);
|
|
88
|
+
return false;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Resolve typed host skill discovery policy from raw PROJECT-DEFINITION value.
|
|
92
|
+
* Fail-closed on malformed opt-outs (#75 Greptile P1): bad host values disable
|
|
93
|
+
* that host; wholly non-object values refuse all multi-host deposit.
|
|
94
|
+
*/
|
|
95
|
+
export function resolveHostSkillDiscoveryPolicyDetailed(raw) {
|
|
96
|
+
if (raw === null || raw === undefined) {
|
|
97
|
+
return {
|
|
98
|
+
policy: { ...DEFAULT_HOST_SKILL_DISCOVERY_POLICY },
|
|
99
|
+
warnings: [],
|
|
100
|
+
refuseAll: false,
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
if (typeof raw !== "object" || Array.isArray(raw)) {
|
|
104
|
+
return {
|
|
105
|
+
policy: {
|
|
106
|
+
claude: false,
|
|
107
|
+
cursor: false,
|
|
108
|
+
codex: false,
|
|
109
|
+
github: false,
|
|
110
|
+
},
|
|
111
|
+
warnings: [
|
|
112
|
+
`${FIELD_HOST_SKILL_DISCOVERY} must be an object; got ${typeof raw} — multi-host skill discovery deposit skipped`,
|
|
113
|
+
],
|
|
114
|
+
refuseAll: true,
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
const rec = raw;
|
|
118
|
+
const warnings = [];
|
|
119
|
+
for (const key of Object.keys(rec)) {
|
|
120
|
+
if (!isSkillDiscoveryHostId(key)) {
|
|
121
|
+
warnings.push(`${FIELD_HOST_SKILL_DISCOVERY}.${key} is not a skill-discovery host (${SKILL_DISCOVERY_HOSTS.join(", ")})`);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
const policy = {
|
|
125
|
+
claude: readHostBoolean(rec, "claude", DEFAULT_HOST_SKILL_DISCOVERY_POLICY.claude, warnings),
|
|
126
|
+
cursor: readHostBoolean(rec, "cursor", DEFAULT_HOST_SKILL_DISCOVERY_POLICY.cursor, warnings),
|
|
127
|
+
codex: readHostBoolean(rec, "codex", DEFAULT_HOST_SKILL_DISCOVERY_POLICY.codex, warnings),
|
|
128
|
+
github: readHostBoolean(rec, "github", DEFAULT_HOST_SKILL_DISCOVERY_POLICY.github, warnings),
|
|
129
|
+
};
|
|
130
|
+
return { policy, warnings, refuseAll: false };
|
|
131
|
+
}
|
|
132
|
+
/** Resolve typed host skill discovery policy from raw PROJECT-DEFINITION value. */
|
|
133
|
+
export function resolveHostSkillDiscoveryPolicy(raw) {
|
|
134
|
+
return resolveHostSkillDiscoveryPolicyDetailed(raw).policy;
|
|
135
|
+
}
|
|
136
|
+
export function validateHostSkillDiscovery(value) {
|
|
137
|
+
if (value === null || value === undefined) {
|
|
138
|
+
return [];
|
|
139
|
+
}
|
|
140
|
+
if (typeof value !== "object" || Array.isArray(value)) {
|
|
141
|
+
return [`${FIELD_HOST_SKILL_DISCOVERY} must be an object; got ${typeof value}`];
|
|
142
|
+
}
|
|
143
|
+
const rec = value;
|
|
144
|
+
const errors = [];
|
|
145
|
+
for (const host of SKILL_DISCOVERY_HOSTS) {
|
|
146
|
+
if (host in rec && typeof rec[host] !== "boolean") {
|
|
147
|
+
errors.push(`${FIELD_HOST_SKILL_DISCOVERY}.${host} must be a boolean`);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
for (const key of Object.keys(rec)) {
|
|
151
|
+
if (!isSkillDiscoveryHostId(key)) {
|
|
152
|
+
errors.push(`${FIELD_HOST_SKILL_DISCOVERY}.${key} is not a skill-discovery host (${SKILL_DISCOVERY_HOSTS.join(", ")})`);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
return errors;
|
|
156
|
+
}
|
|
157
|
+
export function isHostSkillDiscoveryEnabled(host, policy = DEFAULT_HOST_SKILL_DISCOVERY_POLICY) {
|
|
158
|
+
return policy[host];
|
|
159
|
+
}
|
|
160
|
+
function fieldFromResolved(resolved, source) {
|
|
161
|
+
return {
|
|
162
|
+
name: FIELD_HOST_SKILL_DISCOVERY,
|
|
163
|
+
current: resolved,
|
|
164
|
+
default: DEFAULT_HOST_SKILL_DISCOVERY_POLICY,
|
|
165
|
+
source,
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
/** Inspector row for `policy:show --field=hostSkillDiscovery`. */
|
|
169
|
+
export function inspectHostSkillDiscovery(data) {
|
|
170
|
+
if (data === null) {
|
|
171
|
+
return fieldFromResolved(DEFAULT_HOST_SKILL_DISCOVERY_POLICY, "default");
|
|
172
|
+
}
|
|
173
|
+
const policyBlock = readPlanPolicy(data.plan);
|
|
174
|
+
if (typeof policyBlock !== "object" ||
|
|
175
|
+
policyBlock === null ||
|
|
176
|
+
Array.isArray(policyBlock) ||
|
|
177
|
+
!("hostSkillDiscovery" in policyBlock)) {
|
|
178
|
+
return fieldFromResolved(DEFAULT_HOST_SKILL_DISCOVERY_POLICY, "default");
|
|
179
|
+
}
|
|
180
|
+
const resolved = resolveHostSkillDiscoveryPolicy(policyBlock.hostSkillDiscovery);
|
|
181
|
+
return fieldFromResolved(resolved, "typed");
|
|
182
|
+
}
|
|
183
|
+
/** Load raw hostSkillDiscovery value from PROJECT-DEFINITION, if present. */
|
|
184
|
+
export function loadHostSkillDiscoveryRawFromProject(projectRoot) {
|
|
185
|
+
const [data] = loadProjectDefinition(projectRoot);
|
|
186
|
+
if (data === null) {
|
|
187
|
+
return undefined;
|
|
188
|
+
}
|
|
189
|
+
const policyBlock = readPlanPolicy(data.plan);
|
|
190
|
+
if (typeof policyBlock !== "object" ||
|
|
191
|
+
policyBlock === null ||
|
|
192
|
+
Array.isArray(policyBlock) ||
|
|
193
|
+
!("hostSkillDiscovery" in policyBlock)) {
|
|
194
|
+
return undefined;
|
|
195
|
+
}
|
|
196
|
+
return policyBlock.hostSkillDiscovery;
|
|
197
|
+
}
|
|
198
|
+
/** Resolve host skill discovery policy from PROJECT-DEFINITION on disk. */
|
|
199
|
+
export function loadHostSkillDiscoveryPolicyFromProject(projectRoot) {
|
|
200
|
+
return resolveHostSkillDiscoveryPolicy(loadHostSkillDiscoveryRawFromProject(projectRoot));
|
|
201
|
+
}
|
|
202
|
+
/**
|
|
203
|
+
* Resolve policy + validation warnings from PROJECT-DEFINITION for deposit.
|
|
204
|
+
* Surfaces malformed opt-outs so init/update can report them (#75 Greptile P1).
|
|
205
|
+
*/
|
|
206
|
+
export function loadHostSkillDiscoveryPolicyDetailedFromProject(projectRoot) {
|
|
207
|
+
return resolveHostSkillDiscoveryPolicyDetailed(loadHostSkillDiscoveryRawFromProject(projectRoot));
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* Repo-relative posix path for one skill under a host layout.
|
|
211
|
+
* Example: `.claude/skills/deft-directive-build/SKILL.md`
|
|
212
|
+
*/
|
|
213
|
+
export function hostSkillRelativePath(hostId, skillDir) {
|
|
214
|
+
const layout = getHostSkillDiscoveryLayout(hostId);
|
|
215
|
+
return `${layout.relativeDir}/${skillDir}/${layout.skillFilename}`;
|
|
216
|
+
}
|
|
217
|
+
//# sourceMappingURL=skill-discovery-hosts.js.map
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Multi-host init/update deposit for native slash command files (#3054 / epic #55).
|
|
3
|
+
*
|
|
4
|
+
* Wires #3053 emitters into the init/update deposit path:
|
|
5
|
+
* - Default enabled set = hosts with real emitters (L6)
|
|
6
|
+
* - Per-host opt-out via `plan.policy.hostSlashCommands`
|
|
7
|
+
* - Idempotent managed rewrite (no duplicate pile-up)
|
|
8
|
+
* - Prefer commit of **managed product paths only** (L8) — exact allowlist, not whole dirs
|
|
9
|
+
*
|
|
10
|
+
* Parallel to agent-hooks deposit; does not touch hook JSON configs.
|
|
11
|
+
*
|
|
12
|
+
* Ownership: only create/update/remove files that are missing or still look like
|
|
13
|
+
* Directive thin wrappers (`isThinWrapperMarkdown`). Consumer-customized content
|
|
14
|
+
* at a product filename is left untouched.
|
|
15
|
+
*/
|
|
16
|
+
import { type HostSlashCommandsPolicy } from "../policy/host-slash-commands.js";
|
|
17
|
+
import { type SlashEmitterHostId } from "../slash/index.js";
|
|
18
|
+
import type { InitDepositIo } from "./constants.js";
|
|
19
|
+
export interface SlashCommandDepositResult {
|
|
20
|
+
readonly changed: boolean;
|
|
21
|
+
/** Repo-relative paths written or updated. */
|
|
22
|
+
readonly writtenPaths: string[];
|
|
23
|
+
/** Repo-relative paths removed on opt-out (managed product files only). */
|
|
24
|
+
readonly removedPaths: string[];
|
|
25
|
+
/** Repo-relative paths skipped because consumer content is not managed. */
|
|
26
|
+
readonly preservedCustomPaths: string[];
|
|
27
|
+
/** Hosts that received a deposit pass (policy enabled). */
|
|
28
|
+
readonly depositedHosts: SlashEmitterHostId[];
|
|
29
|
+
/** Hosts skipped by policy opt-out. */
|
|
30
|
+
readonly skippedHosts: SlashEmitterHostId[];
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Write thin-wrapper command/prompt files for every policy-enabled host.
|
|
34
|
+
*
|
|
35
|
+
* Idempotent: skips files whose on-disk bytes already match emission.
|
|
36
|
+
* Ownership-safe: never overwrites non-thin consumer customizations at product paths.
|
|
37
|
+
* Opt-out: removes managed thin wrappers only (leaves user customizations alone).
|
|
38
|
+
*/
|
|
39
|
+
export declare function writeSlashCommandDeposit(projectRoot: string, io?: InitDepositIo, policy?: HostSlashCommandsPolicy): SlashCommandDepositResult;
|
|
40
|
+
/**
|
|
41
|
+
* Exact repo-relative product command paths (all emitter hosts).
|
|
42
|
+
* Used by installerManagedMatchers for L8 prefer-commit staging without
|
|
43
|
+
* claiming whole host command directories (consumer custom files stay app-owned).
|
|
44
|
+
*/
|
|
45
|
+
export declare function slashCommandManagedExactPaths(): readonly string[];
|
|
46
|
+
//# sourceMappingURL=slash-deposit.d.ts.map
|