@danieljvdm/dev-kit 0.4.0 → 0.5.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/README.md CHANGED
@@ -20,7 +20,8 @@ Install the published Dev Kit package:
20
20
  bun add -d @danieljvdm/dev-kit
21
21
  ```
22
22
 
23
- Initialize the project, browse the approved catalog, and add skills:
23
+ Initialize the project, browse available built-in, approved Git, and installed
24
+ package skills, then add the ones you want:
24
25
 
25
26
  ```bash
26
27
  bun x dev-kit init
@@ -107,7 +108,7 @@ the root package itself.
107
108
  | `dev-kit remove <skill...>` | Deselect and uninstall skills safely. |
108
109
  | `dev-kit list [--all]` | List selected skills or browse the catalog. |
109
110
  | `dev-kit search <words...>` | Search names and descriptions. |
110
- | `dev-kit info <skill>` | Show description, source, and approved commit. |
111
+ | `dev-kit info <skill>` | Show description and Git or installed-package provenance. |
111
112
  | `dev-kit status` | Check whether the project matches its selection. |
112
113
  | `dev-kit sync` | Apply the current manifest. |
113
114
  | `dev-kit plan` | Preview project changes without writing files. |
@@ -149,7 +150,8 @@ tool versions. A project-local process lock also prevents concurrent applies.
149
150
 
150
151
  ## Manifest
151
152
 
152
- `include` accepts individual skill names and skill families:
153
+ `include` accepts static skill names, skill families, and explicit
154
+ `<package>#<skill>` selectors:
153
155
 
154
156
  ```jsonc
155
157
  {
@@ -159,9 +161,13 @@ tool versions. A project-local process lock also prevents concurrent applies.
159
161
  "effect",
160
162
  "workers-best-practices",
161
163
  "wrangler",
162
- "serve-sim"
164
+ "serve-sim",
165
+ "@tanstack/ai#ai-core"
163
166
  ],
164
167
  "exclude": ["animation-vocabulary"],
168
+ "setup": {
169
+ "claudeInstructions": { "enabled": true }
170
+ },
165
171
  "targets": {
166
172
  "agents": { "enabled": true, "mode": "copy" },
167
173
  "claude": { "enabled": true, "mode": "symlink" },
@@ -175,6 +181,8 @@ tool versions. A project-local process lock also prevents concurrent applies.
175
181
  - Prefer individual external skills such as `workers-best-practices` and
176
182
  `wrangler`, selected after scanning the project for relevant technologies.
177
183
  - `serve-sim` selects the approved Evan Bacon simulator skill directly.
184
+ - `@tanstack/ai#ai-core` explicitly selects a skill discovered in that direct
185
+ project dependency; discovery alone never selects it.
178
186
  - An approved source ID is broad shorthand that selects every skill from that
179
187
  source. Use it only when the scan confirms that every member applies.
180
188
 
@@ -183,6 +191,25 @@ Dev Kit reserves `.repos/<source-id>` for project-local source checkouts. Run
183
191
  The patch is idempotent, preserves existing lines, and refuses symlinked
184
192
  `.gitignore` files.
185
193
 
194
+ ## Claude instructions
195
+
196
+ Enable a portable Claude Code instruction bridge in the manifest:
197
+
198
+ ```jsonc
199
+ {
200
+ "include": [],
201
+ "setup": {
202
+ "claudeInstructions": { "enabled": true }
203
+ }
204
+ }
205
+ ```
206
+
207
+ `dev-kit apply` requires a project-root `AGENTS.md`, then manages
208
+ `CLAUDE.md` as the relative symlink `CLAUDE.md → AGENTS.md`. The link is
209
+ recorded in the lockfile and local ownership state. Dev Kit refuses to replace
210
+ an unowned `CLAUDE.md` and removes the link when the task is disabled only if
211
+ the owned link is unchanged.
212
+
186
213
  ## Effect source checkout
187
214
 
188
215
  Enable a local checkout of the exact installed Effect release in the manifest:
@@ -248,10 +275,65 @@ troubleshooting the task directly.
248
275
  Package and tsconfig edits remain explicit until Dev Kit can safely own parts
249
276
  of shared JSONC files.
250
277
 
251
- ## Approved external skills
278
+ ## Installed package skills
279
+
280
+ Dev Kit generically discovers agent skills bundled by the project's installed
281
+ JavaScript packages. It reads the project's direct dependencies, checks the
282
+ package's Intent v1 discovery metadata (or Intent's repository-metadata
283
+ fallback), then looks for the layout
284
+ `node_modules/<package>/skills/<skill>/SKILL.md`.
285
+ TanStack is one publisher of this layout; no TanStack package names or skill
286
+ paths are hard-coded into Dev Kit.
287
+
288
+ Discovery is browse-only. These commands show an installed package skill but do
289
+ not select, copy, symlink, lock, or otherwise install it:
290
+
291
+ ```bash
292
+ bun x dev-kit list --all
293
+ bun x dev-kit search tanstack
294
+ bun x dev-kit info @tanstack/ai#ai-core
295
+ ```
296
+
297
+ Selection is explicit and package-qualified:
298
+
299
+ ```bash
300
+ bun x dev-kit add @tanstack/ai#ai-core
301
+ ```
302
+
303
+ That writes `@tanstack/ai#ai-core` to `dev-kit.jsonc` and, unless
304
+ `--no-apply` is passed, installs it through the normal ownership-safe sync
305
+ path. The qualifier prevents ambiguity when two dependencies publish the same
306
+ skill name. Two selected skills that would both write the same destination are
307
+ rejected before any output is changed.
252
308
 
253
- This repository is an opinionated catalog, not a mirror of every upstream skill
254
- tree. `skill-sources.jsonc` declares sources Dan has approved:
309
+ The initial compatibility boundary is intentionally small and deterministic:
310
+
311
+ - only packages named in the root project's `dependencies`,
312
+ `devDependencies`, `optionalDependencies`, or `peerDependencies` are
313
+ scanned;
314
+ - package code is never imported or executed;
315
+ - npm-style and pnpm/workspace symlinks under `node_modules` are supported;
316
+ - Yarn Plug'n'Play and transitive dependency traversal are not scanned; and
317
+ - immediate `skills/<name>/SKILL.md` roots are listed. Nested topic skills and
318
+ references remain part of that root and are copied with it.
319
+
320
+ The last rule adapts Intent's routed, nested skill trees to the immediate folder
321
+ and frontmatter-name invariants expected by Agent Skills targets. Dev Kit does
322
+ not rewrite nested names or ask Intent to manage agent configuration.
323
+
324
+ The project `dev-kit.lock.json` records the selected package name, installed
325
+ version, skill name, and content digest. `apply --locked` therefore rejects
326
+ package-version or skill-content drift. Dev Kit never downloads a missing
327
+ package or substitutes a registry version.
328
+
329
+ See TanStack's
330
+ [Agent Skills documentation](https://tanstack.com/ai/latest/docs/getting-started/agent-skills)
331
+ for a real package suite that uses this convention.
332
+
333
+ ## Approved external Git skills
334
+
335
+ This repository remains an opinionated catalog for Git-hosted skills.
336
+ `skill-sources.jsonc` contains only reviewed Git sources:
255
337
 
256
338
  ```jsonc
257
339
  {
@@ -276,38 +358,25 @@ bun run catalog:refresh
276
358
  bun run catalog:check
277
359
  ```
278
360
 
279
- Adding a source does not require editing JSONC:
361
+ Adding a Git source does not require editing JSONC:
280
362
 
281
363
  ```bash
282
- # Opens a skill picker in a terminal
283
364
  dev-kit catalog add https://github.com/owner/repository
284
-
285
- # Explicit and automation-friendly
286
365
  dev-kit catalog add https://github.com/owner/repository \
287
366
  --skill one --skill two
288
367
  dev-kit catalog add https://github.com/owner/repository --all
289
368
  ```
290
369
 
291
- GitHub tree URLs are accepted, so a URL such as
292
- `https://github.com/owner/repository/tree/main/skills` supplies the repository,
293
- ref, and skills path together. `--all` expands to the skills discovered at that
370
+ GitHub tree URLs are accepted. `--all` expands to the skills found at that
294
371
  exact snapshot; it never writes a wildcard that could silently approve a future
295
- upstream addition.
296
-
297
- The current catalog includes approved snapshots from Emil Kowalski,
298
- Cloudflare, and Evan Bacon. Inspect them with `dev-kit catalog list` and
299
- `dev-kit catalog info <source>`.
300
-
301
- The refresh resolves refs to exact commits, validates names and paths, rejects
302
- symlinks and collisions, extracts short descriptions, and updates
303
- `skill-sources.lock.json`. It does not copy upstream skill trees into this
304
- repository.
305
-
306
- When a consuming project selects an external skill, Dev Kit fetches that exact
307
- approved commit into the ignored `.dev-kit/cache`, applies declared compatibility
308
- transforms, and installs the result through the ownership-safe sync path. Normal
309
- installs never float to a newer upstream commit; only a reviewed catalog refresh
310
- changes what is approved.
372
+ upstream addition. Catalog refresh resolves refs to exact commits, validates
373
+ names and paths, rejects symlinks and collisions, extracts descriptions, and
374
+ updates `skill-sources.lock.json`.
375
+
376
+ When a project selects one of these Git-backed skills, Dev Kit fetches the
377
+ approved commit into the ignored `.dev-kit/cache` and installs it through the
378
+ same ownership-safe sync path. Only a reviewed catalog refresh changes the
379
+ approved Git content.
311
380
 
312
381
  ## Oxlint and Oxfmt configurations
313
382
 
@@ -2,6 +2,7 @@
2
2
  "$schema": "./schema/dev-kit.schema.json",
3
3
  "include": ["dev-kit", "effect"],
4
4
  "setup": {
5
+ "claudeInstructions": { "enabled": true },
5
6
  "effectSource": { "enabled": true },
6
7
  "effectTsgo": { "enabled": true }
7
8
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@danieljvdm/dev-kit",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "description": "Declarative project development toolkit with portable agent skills.",
@@ -10,21 +10,21 @@
10
10
  "type": "string"
11
11
  },
12
12
  "include": {
13
- "description": "Skill names or family names to sync. Prefer individually relevant external skills; an external source id selects every approved skill from that source.",
13
+ "description": "Static skill names, family names, or exact <package>#<skill> selectors to sync. Installed package skills are discovered for browsing but selected only when explicitly included.",
14
14
  "type": "array",
15
15
  "items": {
16
16
  "type": "string",
17
- "pattern": "^[a-z0-9]+(-[a-z0-9]+)*$"
17
+ "pattern": "^(?:[a-z0-9]+(?:-[a-z0-9]+)*|(?:[a-z0-9][a-z0-9._-]*|@[a-z0-9][a-z0-9._-]*/[a-z0-9][a-z0-9._-]*)#[a-z0-9]+(?:-[a-z0-9]+)*)$"
18
18
  },
19
19
  "uniqueItems": true,
20
20
  "minItems": 0
21
21
  },
22
22
  "exclude": {
23
- "description": "Skill names or family names to remove from the selected set after expanding includes.",
23
+ "description": "Static skill names, family names, or exact <package>#<skill> selectors to remove after expanding includes.",
24
24
  "type": "array",
25
25
  "items": {
26
26
  "type": "string",
27
- "pattern": "^[a-z0-9]+(-[a-z0-9]+)*$"
27
+ "pattern": "^(?:[a-z0-9]+(?:-[a-z0-9]+)*|(?:[a-z0-9][a-z0-9._-]*|@[a-z0-9][a-z0-9._-]*/[a-z0-9][a-z0-9._-]*)#[a-z0-9]+(?:-[a-z0-9]+)*)$"
28
28
  },
29
29
  "uniqueItems": true,
30
30
  "default": []
@@ -34,6 +34,9 @@
34
34
  "type": "object",
35
35
  "additionalProperties": false,
36
36
  "properties": {
37
+ "claudeInstructions": {
38
+ "$ref": "#/$defs/claudeInstructionsSetup"
39
+ },
37
40
  "effectSource": {
38
41
  "$ref": "#/$defs/effectSourceSetup"
39
42
  },
@@ -58,6 +61,17 @@
58
61
  },
59
62
  "required": ["include"],
60
63
  "$defs": {
64
+ "claudeInstructionsSetup": {
65
+ "description": "Manage CLAUDE.md as a relative symlink to the project-root AGENTS.md file.",
66
+ "type": "object",
67
+ "additionalProperties": false,
68
+ "properties": {
69
+ "enabled": {
70
+ "type": "boolean",
71
+ "default": false
72
+ }
73
+ }
74
+ },
61
75
  "effectSourceSetup": {
62
76
  "type": "object",
63
77
  "additionalProperties": false,
@@ -67,6 +67,9 @@ skill as `dev-kit` when project agents should carry the toolkit procedure.
67
67
  "$schema": "./node_modules/@danieljvdm/dev-kit/schema/dev-kit.schema.json",
68
68
  "include": ["dev-kit", "effect"],
69
69
  "exclude": [],
70
+ "setup": {
71
+ "claudeInstructions": { "enabled": true }
72
+ },
70
73
  "targets": {
71
74
  "agents": { "enabled": true, "mode": "copy" },
72
75
  "claude": { "enabled": true, "mode": "symlink" },
@@ -80,6 +83,12 @@ use symlinks for additional harness discovery paths. Keep every target path
80
83
  project-relative and separate from the manifest, lock, state, and process-lock
81
84
  paths.
82
85
 
86
+ Enable `setup.claudeInstructions` when Claude Code should consume the same
87
+ project-root instructions as Codex. It manages `CLAUDE.md` as a relative
88
+ symlink to an existing `AGENTS.md`. Preserve any conflicting `CLAUDE.md`; when
89
+ disabled, dev-kit removes only an unchanged link recorded in local ownership
90
+ state.
91
+
83
92
  ## Ownership and conflicts
84
93
 
85
94
  Dev-kit adopts an existing destination only when its digest exactly matches a
@@ -195,8 +204,9 @@ in the consuming project.
195
204
 
196
205
  ## Current boundary
197
206
 
198
- Manage skill outputs, the `setup.effectSource` checkout, and the explicit
199
- `setup.effectTsgo` task. Edit shared `package.json` and `tsconfig.json`
207
+ Manage skill outputs, the `setup.claudeInstructions` link, the
208
+ `setup.effectSource` checkout, and the explicit `setup.effectTsgo` task. Edit
209
+ shared `package.json` and `tsconfig.json`
200
210
  contributions deliberately. The Oxlint and Oxfmt configurations are composable
201
211
  package exports, not manifest-managed outputs. Treat broader setup tasks as
202
212
  future manifest capabilities until the installed CLI exposes them.
@@ -57,7 +57,7 @@ const addCommand = CliCommand.make(
57
57
  skills.length === 0
58
58
  ? chooseSkillsToAdd({ apply: !noApply, manifestPath: manifest, projectDir })
59
59
  : addSkills(skills, { apply: !noApply, manifestPath: manifest, projectDir }),
60
- ).pipe(CliCommand.withDescription("Select and install one or more approved skills."));
60
+ ).pipe(CliCommand.withDescription("Select and install one or more available skills."));
61
61
 
62
62
  const removeCommand = CliCommand.make(
63
63
  "remove",
@@ -89,13 +89,13 @@ const searchCommand = CliCommand.make(
89
89
  { query: Argument.string("query").pipe(Argument.variadic({ min: 1 })), ...projectFlags },
90
90
  ({ query, manifest, projectDir }) =>
91
91
  listSkills({ all: true, query: query.join(" "), manifestPath: manifest, projectDir }),
92
- ).pipe(CliCommand.withDescription("Search approved skill names and descriptions."));
92
+ ).pipe(CliCommand.withDescription("Search available skill names and descriptions."));
93
93
 
94
94
  const infoCommand = CliCommand.make(
95
95
  "info",
96
- { skill: Argument.string("skill") },
97
- ({ skill }) => showSkill(skill),
98
- ).pipe(CliCommand.withDescription("Show provenance and details for an approved skill."));
96
+ { skill: Argument.string("skill"), ...projectFlags },
97
+ ({ skill, manifest, projectDir }) => showSkill(skill, { manifestPath: manifest, projectDir }),
98
+ ).pipe(CliCommand.withDescription("Show provenance and details for an available skill."));
99
99
 
100
100
  const planCommand = CliCommand.make(
101
101
  "plan",
package/src/catalog.ts CHANGED
@@ -2,7 +2,11 @@ import { parse as parseJsonc, type ParseError } from "jsonc-parser";
2
2
  import { Effect, FileSystem, Path, Schema, Stream } from "effect";
3
3
  import { ChildProcess } from "effect/unstable/process";
4
4
 
5
- import { observePath } from "./path-digest.ts";
5
+ import { observePath, type Digest } from "./path-digest.ts";
6
+ import {
7
+ discoverPackageSkills,
8
+ resolvePackageSkillSelector,
9
+ } from "./package-skill-source.ts";
6
10
  import {
7
11
  SkillSourcesLockSchema,
8
12
  type LockedSkillSource,
@@ -11,9 +15,14 @@ import {
11
15
 
12
16
  export type CatalogSkill = {
13
17
  readonly name: string;
18
+ readonly selector: string;
14
19
  readonly description: string;
15
20
  readonly source: string;
16
21
  readonly bundled: boolean;
22
+ readonly package?: {
23
+ readonly name: string;
24
+ readonly version: string;
25
+ };
17
26
  };
18
27
 
19
28
  export type SkillCatalog = {
@@ -24,11 +33,19 @@ export type SkillCatalog = {
24
33
 
25
34
  export type ResolvedSkillSource = {
26
35
  readonly path: string;
27
- readonly catalog?: {
28
- readonly source: string;
29
- readonly repository: string;
30
- readonly resolved: string;
31
- };
36
+ readonly linkPath?: string;
37
+ readonly catalog?:
38
+ | {
39
+ readonly source: string;
40
+ readonly repository: string;
41
+ readonly resolved: string;
42
+ }
43
+ | {
44
+ readonly package: string;
45
+ readonly version: string;
46
+ readonly skill: string;
47
+ readonly digest: Digest;
48
+ };
32
49
  };
33
50
 
34
51
  class CatalogError extends Schema.TaggedErrorClass<CatalogError>()("CatalogError", {
@@ -87,6 +104,7 @@ const readDescription = Effect.fn("readSkillDescription")(function* (skillPath:
87
104
 
88
105
  export const loadSkillCatalog = Effect.fn("loadSkillCatalog")(function* (
89
106
  packageRoot: string,
107
+ projectDir: string,
90
108
  ) {
91
109
  const fs = yield* FileSystem.FileSystem;
92
110
  const path = yield* Path.Path;
@@ -98,6 +116,7 @@ export const loadSkillCatalog = Effect.fn("loadSkillCatalog")(function* (
98
116
  if ((yield* fs.exists(path.join(skillPath, "SKILL.md")))) {
99
117
  skills.push({
100
118
  name,
119
+ selector: name,
101
120
  description: yield* readDescription(skillPath),
102
121
  source: "built-in",
103
122
  bundled: true,
@@ -110,28 +129,49 @@ export const loadSkillCatalog = Effect.fn("loadSkillCatalog")(function* (
110
129
  for (const name of source.skills) {
111
130
  skills.push({
112
131
  name,
132
+ selector: name,
113
133
  description: source.descriptions?.[name] ?? "",
114
134
  source: source.id,
115
135
  bundled: false,
116
136
  });
117
137
  }
118
138
  }
139
+ const discovery = yield* discoverPackageSkills(projectDir);
140
+ for (const candidate of discovery.candidates) {
141
+ skills.push({
142
+ name: candidate.name,
143
+ selector: candidate.selector,
144
+ description: candidate.description,
145
+ source: candidate.package,
146
+ bundled: false,
147
+ package: { name: candidate.package, version: candidate.version },
148
+ });
149
+ }
119
150
  const duplicates = skills.filter(
120
- (skill, index) => skills.findIndex((candidate) => candidate.name === skill.name) !== index,
151
+ (skill, index) => skills.findIndex((candidate) => candidate.selector === skill.selector) !== index,
121
152
  );
122
153
  if (duplicates.length > 0) {
123
154
  return yield* new CatalogError({
124
- message: `duplicate catalog skill: ${duplicates[0]?.name ?? "unknown"}`,
155
+ message: `duplicate catalog skill selector: ${duplicates[0]?.selector ?? "unknown"}`,
156
+ });
157
+ }
158
+ const externalFamilies = (lock?.sources ?? []).map((source) =>
159
+ [source.id, source.skills] as const
160
+ );
161
+ const duplicateFamily = externalFamilies.find(
162
+ ([id], index) => externalFamilies.findIndex(([candidate]) => candidate === id) !== index,
163
+ );
164
+ if (duplicateFamily !== undefined) {
165
+ return yield* new CatalogError({
166
+ message: `duplicate catalog family: ${duplicateFamily[0]}`,
125
167
  });
126
168
  }
127
169
  const families: Readonly<Record<string, ReadonlyArray<string>>> = {
128
170
  effect: ["effect-ts"],
129
- ...Object.fromEntries(
130
- (lock?.sources ?? []).map((source) => [source.id, source.skills]),
131
- ),
171
+ ...Object.fromEntries(externalFamilies),
132
172
  };
133
173
  return {
134
- skills: skills.sort((left, right) => left.name.localeCompare(right.name)),
174
+ skills: skills.sort((left, right) => left.selector.localeCompare(right.selector)),
135
175
  families,
136
176
  ...(lock ? { lock } : {}),
137
177
  } satisfies SkillCatalog;
@@ -224,15 +264,15 @@ const materializeSource = Effect.fn("materializeCatalogSource")(function* (
224
264
  export const resolveSkillSources = Effect.fn("resolveSkillSources")(function* (
225
265
  packageRoot: string,
226
266
  projectDir: string,
267
+ catalog: SkillCatalog,
227
268
  selected: ReadonlyArray<string>,
228
269
  cache = true,
229
270
  ) {
230
271
  const path = yield* Path.Path;
231
- const catalog = yield* loadSkillCatalog(packageRoot);
232
272
  const sources = new Map<string, ResolvedSkillSource>();
233
273
  for (const skill of catalog.skills.filter((skill) => skill.bundled)) {
234
- if (selected.includes(skill.name)) {
235
- sources.set(skill.name, { path: path.join(packageRoot, "skills", skill.name) });
274
+ if (selected.includes(skill.selector)) {
275
+ sources.set(skill.selector, { path: path.join(packageRoot, "skills", skill.name) });
236
276
  }
237
277
  }
238
278
  for (const source of catalog.lock?.sources ?? []) {
@@ -242,5 +282,22 @@ export const resolveSkillSources = Effect.fn("resolveSkillSources")(function* (
242
282
  sources.set(name, sourcePath);
243
283
  }
244
284
  }
285
+ for (const selector of selected.filter((value) => value.includes("#"))) {
286
+ const resolved = yield* resolvePackageSkillSelector(projectDir, selector);
287
+ const observation = yield* observePath(resolved.path);
288
+ if (observation.kind !== "directory") {
289
+ return yield* new CatalogError({ message: `package skill is missing: ${selector}` });
290
+ }
291
+ sources.set(selector, {
292
+ path: resolved.path,
293
+ linkPath: resolved.linkPath,
294
+ catalog: {
295
+ package: resolved.package,
296
+ version: resolved.version,
297
+ skill: resolved.name,
298
+ digest: observation.digest,
299
+ },
300
+ });
301
+ }
245
302
  return sources;
246
303
  });
package/src/index.ts CHANGED
@@ -1,4 +1,6 @@
1
1
  export {
2
+ type ClaudeInstructionsSetup,
3
+ ClaudeInstructionsSetupSchema,
2
4
  type DevKitManifest,
3
5
  DevKitManifestSchema,
4
6
  type EffectSourceSetup,
@@ -58,15 +60,21 @@ export {
58
60
  } from "./sync.ts";
59
61
  export {
60
62
  AppliedStateSchema,
63
+ CatalogProvenanceSchema,
61
64
  DevKitLockSchema,
62
65
  EffectSourceLockSchema,
63
66
  EffectTsgoLockSchema,
67
+ ManagedInstructionOutputSchema,
68
+ ManagedOutputSchema,
64
69
  ManagedSkillOutputSchema,
65
70
  OwnershipReceiptSchema,
66
71
  type AppliedState,
72
+ type CatalogProvenance,
67
73
  type DevKitLock,
68
74
  type EffectSourceLock,
69
75
  type EffectTsgoLock,
76
+ type ManagedInstructionOutput,
77
+ type ManagedOutput,
70
78
  type ManagedSkillOutput,
71
79
  type OwnershipReceipt,
72
80
  } from "./project-state.ts";
package/src/manifest.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import { Schema } from "effect";
2
2
 
3
+ import { SKILL_SELECTOR_PATTERN } from "./skill-selector.ts";
3
4
  import { TYPESCRIPT_PACKAGE_NAME_PATTERN } from "./typescript-package-name.ts";
4
5
 
5
6
  export type HarnessTarget = "agents" | "claude" | "opencode";
@@ -36,12 +37,19 @@ export const EffectSourceSetupSchema = Schema.Struct({
36
37
 
37
38
  export type EffectSourceSetup = typeof EffectSourceSetupSchema.Type;
38
39
 
40
+ export const ClaudeInstructionsSetupSchema = Schema.Struct({
41
+ enabled: Schema.optional(Schema.Boolean),
42
+ });
43
+
44
+ export type ClaudeInstructionsSetup = typeof ClaudeInstructionsSetupSchema.Type;
45
+
39
46
  export const DevKitManifestSchema = Schema.Struct({
40
47
  $schema: Schema.optional(Schema.String),
41
- include: Schema.Array(Schema.String),
42
- exclude: Schema.optional(Schema.Array(Schema.String)),
48
+ include: Schema.Array(Schema.String.check(Schema.isPattern(SKILL_SELECTOR_PATTERN))),
49
+ exclude: Schema.optional(Schema.Array(Schema.String.check(Schema.isPattern(SKILL_SELECTOR_PATTERN)))),
43
50
  setup: Schema.optional(
44
51
  Schema.Struct({
52
+ claudeInstructions: Schema.optional(ClaudeInstructionsSetupSchema),
45
53
  effectSource: Schema.optional(EffectSourceSetupSchema),
46
54
  effectTsgo: Schema.optional(EffectTsgoSetupSchema),
47
55
  }),
@@ -67,6 +75,9 @@ export type NormalizedManifest = {
67
75
  readonly include: ReadonlyArray<string>;
68
76
  readonly exclude: ReadonlyArray<string>;
69
77
  readonly setup: {
78
+ readonly claudeInstructions: {
79
+ readonly enabled: boolean;
80
+ };
70
81
  readonly effectSource: {
71
82
  readonly enabled: boolean;
72
83
  readonly packageName: string;
@@ -114,6 +125,9 @@ export const normalizeManifest = (manifest: DevKitManifest): NormalizedManifest
114
125
  exclude: manifest.exclude ?? [],
115
126
  include: manifest.include,
116
127
  setup: {
128
+ claudeInstructions: {
129
+ enabled: manifest.setup?.claudeInstructions?.enabled ?? false,
130
+ },
117
131
  effectSource: {
118
132
  enabled: manifest.setup?.effectSource?.enabled ?? false,
119
133
  packageName: manifest.setup?.effectSource?.packageName ?? "effect",