@danieljvdm/dev-kit 0.11.3 → 0.12.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 +70 -76
- package/dev-kit.example.jsonc +0 -4
- package/package.json +10 -6
- package/schema/dev-kit.schema.json +1 -46
- package/skills/build-effect-apis/SKILL.md +77 -0
- package/skills/build-effect-apis/agents/openai.yaml +4 -0
- package/skills/build-effect-apis/references/cloudflare-workers.md +71 -0
- package/skills/build-effect-apis/references/effect-atom-client.md +161 -0
- package/skills/build-effect-apis/references/effect-atom-lifecycle.md +78 -0
- package/skills/build-effect-apis/references/effect-atom-testing.md +74 -0
- package/skills/build-effect-apis/references/runtime-assembly.md +56 -0
- package/skills/build-effect-apis/references/server-and-middleware.md +174 -0
- package/skills/build-effect-apis/references/shared-contracts.md +108 -0
- package/skills/build-effect-apis/references/tanstack-start.md +86 -0
- package/skills/build-effect-apis/references/verification.md +50 -0
- package/skills/dev-kit/SKILL.md +58 -46
- package/skills/effect-architecture-audit/SKILL.md +26 -0
- package/skills/effect-architecture-audit/agents/openai.yaml +4 -0
- package/skills/effect-architecture-audit/references/service-and-boundary-audit.md +150 -0
- package/skills/effect-ts/SKILL.md +21 -256
- package/skills/effect-ts/agents/openai.yaml +3 -3
- package/skills/testing/SKILL.md +5 -0
- package/src/catalog-manager.ts +16 -17
- package/src/catalog.ts +71 -16
- package/src/effect-source.ts +46 -24
- package/src/effect-tsgo.ts +49 -24
- package/src/gitignore.ts +5 -5
- package/src/index.ts +0 -6
- package/src/manifest.ts +0 -34
- package/src/node-symbolic-link.ts +2 -2
- package/src/oxfmt.js +5 -0
- package/src/oxfmt.ts +5 -0
- package/src/oxlint.js +5 -0
- package/src/oxlint.ts +5 -0
- package/src/package-skill-source.ts +51 -59
- package/src/path-digest.ts +7 -7
- package/src/project-package.ts +8 -7
- package/src/project-process-lock.ts +17 -12
- package/src/project-state.ts +1 -1
- package/src/skill-manager.ts +16 -14
- package/src/skill-selector.ts +12 -0
- package/src/sync.ts +170 -120
- package/src/tool-ignore-patterns.js +9 -0
- package/src/tool-ignore-patterns.ts +15 -0
- package/src/vendor.ts +67 -61
- package/src/vite-plus-dependency.ts +10 -11
- package/src/vite-plus-hooks.ts +24 -14
- package/src/vite-plus-quality.ts +21 -172
- package/src/vite-plus.js +81 -0
- package/src/vite-plus.ts +102 -0
- package/templates/AGENTS.md +1 -1
- package/skills/effect-ts/UPSTREAM.md +0 -28
- package/skills/effect-ts/references/atom-cache-lifecycle.md +0 -78
- package/skills/effect-ts/references/atom-http-and-invalidation.md +0 -97
- package/skills/effect-ts/references/atom-tanstack-start.md +0 -69
- package/skills/effect-ts/references/atom-testing.md +0 -67
- package/skills/effect-ts/references/audit-services.md +0 -144
- package/skills/effect-ts/references/features.md +0 -525
- package/skills/effect-ts/references/guide-atom-data-fetching.md +0 -44
- package/skills/effect-ts/references/guide-cli.md +0 -107
- package/skills/effect-ts/references/guide-datetime.md +0 -72
- package/skills/effect-ts/references/guide-effect.md +0 -440
- package/skills/effect-ts/references/guide-error-handling.md +0 -565
- package/skills/effect-ts/references/guide-http-boundaries.md +0 -55
- package/skills/effect-ts/references/guide-layers.md +0 -989
- package/skills/effect-ts/references/guide-observability.md +0 -746
- package/skills/effect-ts/references/guide-retries.md +0 -434
- package/skills/effect-ts/references/guide-schedule.md +0 -343
- package/skills/effect-ts/references/guide-schema.md +0 -664
- package/skills/effect-ts/references/guide-sql.md +0 -536
- package/skills/effect-ts/references/guide-testing.md +0 -532
- package/skills/effect-ts/references/guide-type-safety-and-boundaries.md +0 -131
- package/skills/effect-ts/references/version-and-source.md +0 -86
- package/templates/vite-plus/vite.config.ts +0 -22
package/src/path-digest.ts
CHANGED
|
@@ -9,7 +9,7 @@ export type ObservedPath =
|
|
|
9
9
|
| { readonly kind: "missing" }
|
|
10
10
|
| { readonly kind: "file" | "directory" | "symlink"; readonly digest: Digest };
|
|
11
11
|
|
|
12
|
-
export class PathInspectionError extends Schema.
|
|
12
|
+
export class PathInspectionError extends Schema.TaggedError<PathInspectionError>()(
|
|
13
13
|
"PathInspectionError",
|
|
14
14
|
{
|
|
15
15
|
path: Schema.String,
|
|
@@ -124,7 +124,7 @@ const digestFileSystemPath = Effect.fn("digestFileSystemPath")(function* (
|
|
|
124
124
|
const child = yield* digestFileSystemPath(childPath, fileMode);
|
|
125
125
|
|
|
126
126
|
if (child.kind === "missing") {
|
|
127
|
-
return yield*
|
|
127
|
+
return yield* PathInspectionError.make({
|
|
128
128
|
path: childPath,
|
|
129
129
|
operation: "inspect a stable directory tree",
|
|
130
130
|
cause: "path disappeared during inspection",
|
|
@@ -136,7 +136,7 @@ const digestFileSystemPath = Effect.fn("digestFileSystemPath")(function* (
|
|
|
136
136
|
return { kind: "directory", digest: yield* digestFrames(frames) };
|
|
137
137
|
}
|
|
138
138
|
|
|
139
|
-
return yield*
|
|
139
|
+
return yield* PathInspectionError.make({
|
|
140
140
|
path: absolutePath,
|
|
141
141
|
operation: "inspect unsupported filesystem entry",
|
|
142
142
|
cause: info.type,
|
|
@@ -146,9 +146,9 @@ const digestFileSystemPath = Effect.fn("digestFileSystemPath")(function* (
|
|
|
146
146
|
export const observePath = Effect.fn("observeManagedPath")(function* (absolutePath: string) {
|
|
147
147
|
return yield* digestFileSystemPath(absolutePath, canonicalFileMode).pipe(
|
|
148
148
|
Effect.mapError((cause) =>
|
|
149
|
-
cause
|
|
149
|
+
Schema.is(PathInspectionError)(cause)
|
|
150
150
|
? cause
|
|
151
|
-
:
|
|
151
|
+
: PathInspectionError.make({ path: absolutePath, operation: "inspect", cause }),
|
|
152
152
|
),
|
|
153
153
|
);
|
|
154
154
|
});
|
|
@@ -158,9 +158,9 @@ export const observePathWithRawModes = Effect.fn("observeManagedPathWithRawModes
|
|
|
158
158
|
) {
|
|
159
159
|
return yield* digestFileSystemPath(absolutePath, rawFileMode).pipe(
|
|
160
160
|
Effect.mapError((cause) =>
|
|
161
|
-
cause
|
|
161
|
+
Schema.is(PathInspectionError)(cause)
|
|
162
162
|
? cause
|
|
163
|
-
:
|
|
163
|
+
: PathInspectionError.make({ path: absolutePath, operation: "inspect", cause }),
|
|
164
164
|
),
|
|
165
165
|
);
|
|
166
166
|
});
|
package/src/project-package.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Effect, FileSystem, Path, Schema } from "effect";
|
|
2
2
|
|
|
3
|
-
export class ProjectPackageError extends Schema.
|
|
3
|
+
export class ProjectPackageError extends Schema.TaggedError<ProjectPackageError>()(
|
|
4
4
|
"ProjectPackageError",
|
|
5
5
|
{ message: Schema.String },
|
|
6
6
|
) {}
|
|
@@ -24,15 +24,16 @@ export const readProjectPackage = Effect.fn("readProjectPackage")(function* (pro
|
|
|
24
24
|
const manifestPath = path.join(projectDir, "package.json");
|
|
25
25
|
|
|
26
26
|
if (!(yield* fs.exists(manifestPath))) {
|
|
27
|
-
return yield*
|
|
27
|
+
return yield* ProjectPackageError.make({
|
|
28
|
+
message: `package.json not found: ${manifestPath}`,
|
|
29
|
+
});
|
|
28
30
|
}
|
|
29
31
|
const manifest = yield* fs.readFileString(manifestPath).pipe(
|
|
30
32
|
Effect.flatMap(Schema.decodeUnknownEffect(ProjectPackageSchema)),
|
|
31
|
-
Effect.mapError(
|
|
32
|
-
(
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
}),
|
|
33
|
+
Effect.mapError(() =>
|
|
34
|
+
ProjectPackageError.make({
|
|
35
|
+
message: `invalid project package.json: ${manifestPath}`,
|
|
36
|
+
}),
|
|
36
37
|
),
|
|
37
38
|
);
|
|
38
39
|
|
|
@@ -4,7 +4,7 @@ import { DEV_KIT_VERSION } from "./tool-metadata.ts";
|
|
|
4
4
|
|
|
5
5
|
export const PROJECT_PROCESS_LOCK_PATH = ".dev-kit/apply.lock";
|
|
6
6
|
|
|
7
|
-
export class ProjectAlreadyLockedError extends Schema.
|
|
7
|
+
export class ProjectAlreadyLockedError extends Schema.TaggedError<ProjectAlreadyLockedError>()(
|
|
8
8
|
"ProjectAlreadyLockedError",
|
|
9
9
|
{ path: Schema.String },
|
|
10
10
|
) {
|
|
@@ -13,6 +13,15 @@ export class ProjectAlreadyLockedError extends Schema.TaggedErrorClass<ProjectAl
|
|
|
13
13
|
}
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
+
const ProjectProcessLockOwnerSchema = Schema.fromJsonString(
|
|
17
|
+
Schema.Struct({
|
|
18
|
+
version: Schema.Literal(1),
|
|
19
|
+
toolVersion: Schema.String,
|
|
20
|
+
token: Schema.String,
|
|
21
|
+
startedAt: Schema.String,
|
|
22
|
+
}),
|
|
23
|
+
);
|
|
24
|
+
|
|
16
25
|
export const acquireProjectProcessLock = Effect.fn("acquireProjectProcessLock")(function* (
|
|
17
26
|
projectDir: string,
|
|
18
27
|
) {
|
|
@@ -24,16 +33,12 @@ export const acquireProjectProcessLock = Effect.fn("acquireProjectProcessLock")(
|
|
|
24
33
|
const ownerPath = path.join(lockDir, "owner.json");
|
|
25
34
|
const token = yield* crypto.randomUUIDv7;
|
|
26
35
|
const startedAt = DateTime.formatIso(yield* DateTime.now);
|
|
27
|
-
const ownerContents = `${
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
},
|
|
34
|
-
null,
|
|
35
|
-
2,
|
|
36
|
-
)}\n`;
|
|
36
|
+
const ownerContents = `${yield* Schema.encodeEffect(ProjectProcessLockOwnerSchema)({
|
|
37
|
+
version: 1,
|
|
38
|
+
toolVersion: DEV_KIT_VERSION,
|
|
39
|
+
token,
|
|
40
|
+
startedAt,
|
|
41
|
+
}).pipe(Effect.orDie)}\n`;
|
|
37
42
|
|
|
38
43
|
return yield* Effect.acquireRelease(
|
|
39
44
|
Effect.gen(function* () {
|
|
@@ -46,7 +51,7 @@ export const acquireProjectProcessLock = Effect.fn("acquireProjectProcessLock")(
|
|
|
46
51
|
.pipe(
|
|
47
52
|
Effect.mapError((error) =>
|
|
48
53
|
error.reason._tag === "AlreadyExists"
|
|
49
|
-
?
|
|
54
|
+
? ProjectAlreadyLockedError.make({ path: lockDir })
|
|
50
55
|
: error,
|
|
51
56
|
),
|
|
52
57
|
);
|
package/src/project-state.ts
CHANGED
|
@@ -50,7 +50,7 @@ export const ManagedClaudeInstructionsOutputSchema = Schema.Struct({
|
|
|
50
50
|
export type ManagedClaudeInstructionsOutput = typeof ManagedClaudeInstructionsOutputSchema.Type;
|
|
51
51
|
|
|
52
52
|
export const ManagedGeneratedFileOutputSchema = Schema.Struct({
|
|
53
|
-
resourceId: Schema.
|
|
53
|
+
resourceId: Schema.Literal("setup:vite-plus-github-actions"),
|
|
54
54
|
path: Schema.String,
|
|
55
55
|
sourcePath: Schema.String,
|
|
56
56
|
mode: Schema.Literal("copy"),
|
package/src/skill-manager.ts
CHANGED
|
@@ -9,7 +9,7 @@ import { DevKitManifestSchema } from "./manifest.ts";
|
|
|
9
9
|
import { observeSymbolicLink } from "./node-symbolic-link.ts";
|
|
10
10
|
import { runProjectSkillPlan } from "./sync.ts";
|
|
11
11
|
|
|
12
|
-
class SkillManagerError extends Schema.
|
|
12
|
+
class SkillManagerError extends Schema.TaggedError<SkillManagerError>()("SkillManagerError", {
|
|
13
13
|
message: Schema.String,
|
|
14
14
|
}) {}
|
|
15
15
|
|
|
@@ -32,7 +32,7 @@ const resolvePaths = Effect.fn("resolveSkillManagerPaths")(function* (options: M
|
|
|
32
32
|
const candidate = options.manifestPath ?? "dev-kit.jsonc";
|
|
33
33
|
|
|
34
34
|
if (candidate.length === 0 || path.isAbsolute(candidate)) {
|
|
35
|
-
return yield*
|
|
35
|
+
return yield* SkillManagerError.make({
|
|
36
36
|
message: "--manifest must be a non-empty project-relative path",
|
|
37
37
|
});
|
|
38
38
|
}
|
|
@@ -40,7 +40,7 @@ const resolvePaths = Effect.fn("resolveSkillManagerPaths")(function* (options: M
|
|
|
40
40
|
const relative = path.relative(projectDir, manifestPath);
|
|
41
41
|
|
|
42
42
|
if (relative === ".." || relative.startsWith(`..${path.sep}`) || path.isAbsolute(relative)) {
|
|
43
|
-
return yield*
|
|
43
|
+
return yield* SkillManagerError.make({
|
|
44
44
|
message: "--manifest must resolve inside the project",
|
|
45
45
|
});
|
|
46
46
|
}
|
|
@@ -49,7 +49,7 @@ const resolvePaths = Effect.fn("resolveSkillManagerPaths")(function* (options: M
|
|
|
49
49
|
for (const segment of relative.split(path.sep).slice(0, -1)) {
|
|
50
50
|
ancestor = path.join(ancestor, segment);
|
|
51
51
|
if ((yield* observeSymbolicLink(ancestor)).kind === "symlink") {
|
|
52
|
-
return yield*
|
|
52
|
+
return yield* SkillManagerError.make({
|
|
53
53
|
message: `manifest ancestor is a symlink: ${path.relative(projectDir, ancestor)}`,
|
|
54
54
|
});
|
|
55
55
|
}
|
|
@@ -57,10 +57,12 @@ const resolvePaths = Effect.fn("resolveSkillManagerPaths")(function* (options: M
|
|
|
57
57
|
const destination = yield* observeSymbolicLink(manifestPath);
|
|
58
58
|
|
|
59
59
|
if (destination.kind === "symlink") {
|
|
60
|
-
return yield*
|
|
60
|
+
return yield* SkillManagerError.make({ message: `manifest is a symlink: ${relative}` });
|
|
61
61
|
}
|
|
62
62
|
if (destination.kind === "not-symlink" && (yield* fs.stat(manifestPath)).type !== "File") {
|
|
63
|
-
return yield*
|
|
63
|
+
return yield* SkillManagerError.make({
|
|
64
|
+
message: `manifest is not a regular file: ${relative}`,
|
|
65
|
+
});
|
|
64
66
|
}
|
|
65
67
|
|
|
66
68
|
return {
|
|
@@ -128,7 +130,7 @@ const readManifest = Effect.fn("readManagedSkillManifest")(function* (
|
|
|
128
130
|
|
|
129
131
|
if (!(yield* fs.exists(paths.manifestPath))) {
|
|
130
132
|
if (!create) {
|
|
131
|
-
return yield*
|
|
133
|
+
return yield* SkillManagerError.make({
|
|
132
134
|
message: "dev-kit.jsonc not found. Run `dev-kit init` first.",
|
|
133
135
|
});
|
|
134
136
|
}
|
|
@@ -139,10 +141,10 @@ const readManifest = Effect.fn("readManagedSkillManifest")(function* (
|
|
|
139
141
|
const parsed = parseJsonc(raw, errors, { allowTrailingComma: true });
|
|
140
142
|
|
|
141
143
|
if (errors.length > 0) {
|
|
142
|
-
return yield*
|
|
144
|
+
return yield* SkillManagerError.make({ message: `could not parse ${paths.manifestPath}` });
|
|
143
145
|
}
|
|
144
146
|
const manifest = yield* Schema.decodeUnknownEffect(DevKitManifestSchema)(parsed).pipe(
|
|
145
|
-
Effect.mapError((error) =>
|
|
147
|
+
Effect.mapError((error) => SkillManagerError.make({ message: error.message })),
|
|
146
148
|
);
|
|
147
149
|
|
|
148
150
|
return { ...paths, manifest, raw };
|
|
@@ -272,7 +274,7 @@ export const addSkills = Effect.fn("addManagedSkills")(function* (
|
|
|
272
274
|
const unknown = names.filter((name) => !known.has(name));
|
|
273
275
|
|
|
274
276
|
if (unknown.length > 0) {
|
|
275
|
-
return yield*
|
|
277
|
+
return yield* SkillManagerError.make({
|
|
276
278
|
message: `unknown skill${unknown.length === 1 ? "" : "s"}: ${unknown.join(", ")}. Try \`dev-kit search ${unknown[0]}\`.`,
|
|
277
279
|
});
|
|
278
280
|
}
|
|
@@ -319,7 +321,7 @@ export const removeSkills = Effect.fn("removeManagedSkills")(function* (
|
|
|
319
321
|
);
|
|
320
322
|
|
|
321
323
|
if (absent.length > 0) {
|
|
322
|
-
return yield*
|
|
324
|
+
return yield* SkillManagerError.make({ message: `not selected: ${absent.join(", ")}` });
|
|
323
325
|
}
|
|
324
326
|
const include = current.manifest.include.filter((name) => !names.includes(name));
|
|
325
327
|
const excluded = new Set(current.manifest.exclude ?? []);
|
|
@@ -399,7 +401,7 @@ export const showSkill = Effect.fn("showCatalogSkill")(function* (
|
|
|
399
401
|
const catalog = yield* loadSkillCatalog(yield* packageRoot(), paths.projectDir);
|
|
400
402
|
const skill = catalog.skills.find((candidate) => candidate.selector === name);
|
|
401
403
|
|
|
402
|
-
if (!skill) return yield*
|
|
404
|
+
if (!skill) return yield* SkillManagerError.make({ message: `unknown skill: ${name}` });
|
|
403
405
|
yield* printLine(skill.selector);
|
|
404
406
|
if (skill.description) yield* printLine(displayValue(skill.description));
|
|
405
407
|
if (skill.package) {
|
|
@@ -436,7 +438,7 @@ export const chooseSkillsToAdd = Effect.fn("chooseSkillsToAdd")(function* (
|
|
|
436
438
|
options: ManagerOptions,
|
|
437
439
|
) {
|
|
438
440
|
if (!(yield* isInteractiveTerminal)) {
|
|
439
|
-
return yield*
|
|
441
|
+
return yield* SkillManagerError.make({
|
|
440
442
|
message: "pass one or more skill names, or run this command in a terminal",
|
|
441
443
|
});
|
|
442
444
|
}
|
|
@@ -471,7 +473,7 @@ export const chooseSkillsToRemove = Effect.fn("chooseSkillsToRemove")(function*
|
|
|
471
473
|
options: ManagerOptions,
|
|
472
474
|
) {
|
|
473
475
|
if (!(yield* isInteractiveTerminal)) {
|
|
474
|
-
return yield*
|
|
476
|
+
return yield* SkillManagerError.make({
|
|
475
477
|
message: "pass one or more skill names, or run this command in a terminal",
|
|
476
478
|
});
|
|
477
479
|
}
|
package/src/skill-selector.ts
CHANGED
|
@@ -28,6 +28,18 @@ export const SKILL_SELECTOR_PATTERN =
|
|
|
28
28
|
|
|
29
29
|
export const isSkillName = (value: string): boolean => SKILL_NAME_PATTERN.test(value);
|
|
30
30
|
|
|
31
|
+
/**
|
|
32
|
+
* The package-qualified directory name for an installed package skill. The npm
|
|
33
|
+
* package name keeps its identity in the flattened output: `@` is dropped,
|
|
34
|
+
* every other non-alphanumeric run becomes one dash, and the bare skill name
|
|
35
|
+
* is appended. `@tanstack/table-core#core` installs as
|
|
36
|
+
* `tanstack-table-core-core`.
|
|
37
|
+
*/
|
|
38
|
+
export const packageSkillInstallName = (packageName: string, skillName: string): string =>
|
|
39
|
+
`${packageName.replaceAll("@", "")}-${skillName}`
|
|
40
|
+
.replace(/[^a-z0-9]+/g, "-")
|
|
41
|
+
.replace(/^-+|-+$/g, "");
|
|
42
|
+
|
|
31
43
|
/** Parse an exact, canonical manifest skill selector. */
|
|
32
44
|
export const parseSkillSelector = (value: string): SkillSelector | undefined => {
|
|
33
45
|
if (isSkillName(value)) return { type: "static", name: value };
|