@danieljvdm/dev-kit 0.2.3 → 0.3.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 +22 -0
- package/package.json +17 -3
- package/skills/dev-kit/SKILL.md +27 -3
- package/src/catalog-manager.ts +4 -1
- package/src/gitignore.ts +1 -1
- package/src/node-symbolic-link.ts +1 -1
- package/src/oxlint.ts +35 -0
- package/src/path-digest.ts +1 -1
- package/src/skill-manager.ts +2 -1
- package/src/sync.ts +0 -1
- package/src/vendor.ts +20 -5
package/README.md
CHANGED
|
@@ -309,6 +309,28 @@ transforms, and installs the result through the ownership-safe sync path. Normal
|
|
|
309
309
|
installs never float to a newer upstream commit; only a reviewed catalog refresh
|
|
310
310
|
changes what is approved.
|
|
311
311
|
|
|
312
|
+
## Oxlint preset for Vite+
|
|
313
|
+
|
|
314
|
+
Dev Kit exports a typed, composable set of high-signal Oxlint rules for Vite+
|
|
315
|
+
projects:
|
|
316
|
+
|
|
317
|
+
```ts
|
|
318
|
+
import { recommendedOxlintConfig } from "@danieljvdm/dev-kit/oxlint";
|
|
319
|
+
import { defineConfig } from "vite-plus";
|
|
320
|
+
|
|
321
|
+
export default defineConfig({
|
|
322
|
+
lint: {
|
|
323
|
+
extends: [recommendedOxlintConfig],
|
|
324
|
+
rules: {
|
|
325
|
+
// Add repository-specific rules here.
|
|
326
|
+
},
|
|
327
|
+
},
|
|
328
|
+
});
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
Use `lint.extends` rather than a shallow object spread so Vite+ composes the
|
|
332
|
+
nested plugin and rule configuration correctly.
|
|
333
|
+
|
|
312
334
|
## Development
|
|
313
335
|
|
|
314
336
|
```bash
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@danieljvdm/dev-kit",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"description": "Declarative project development toolkit with portable agent skills.",
|
|
@@ -19,7 +19,11 @@
|
|
|
19
19
|
},
|
|
20
20
|
"./schema": "./schema/dev-kit.schema.json",
|
|
21
21
|
"./schema/dev-kit.schema.json": "./schema/dev-kit.schema.json",
|
|
22
|
-
"./schema/skill-sources.schema.json": "./schema/skill-sources.schema.json"
|
|
22
|
+
"./schema/skill-sources.schema.json": "./schema/skill-sources.schema.json",
|
|
23
|
+
"./oxlint": {
|
|
24
|
+
"types": "./src/oxlint.ts",
|
|
25
|
+
"default": "./src/oxlint.ts"
|
|
26
|
+
}
|
|
23
27
|
},
|
|
24
28
|
"files": [
|
|
25
29
|
"dev-kit.example.jsonc",
|
|
@@ -36,7 +40,8 @@
|
|
|
36
40
|
"changeset": "changeset",
|
|
37
41
|
"version-packages": "changeset version && ./bin/dev-kit.mjs apply",
|
|
38
42
|
"release": "changeset publish",
|
|
39
|
-
"check": "tsc --noEmit",
|
|
43
|
+
"check": "tsc --noEmit && vp lint",
|
|
44
|
+
"lint": "vp lint",
|
|
40
45
|
"plan": "./bin/dev-kit.mjs plan",
|
|
41
46
|
"apply": "./bin/dev-kit.mjs apply",
|
|
42
47
|
"gitignore": "./bin/dev-kit.mjs gitignore",
|
|
@@ -60,8 +65,17 @@
|
|
|
60
65
|
"@effect/vitest": "4.0.0-beta.102",
|
|
61
66
|
"@types/node": "25.9.1",
|
|
62
67
|
"typescript": "7.0.2",
|
|
68
|
+
"vite-plus": "0.2.6",
|
|
63
69
|
"vitest": "4.1.10"
|
|
64
70
|
},
|
|
71
|
+
"peerDependencies": {
|
|
72
|
+
"vite-plus": ">=0.2.6 <0.3.0"
|
|
73
|
+
},
|
|
74
|
+
"peerDependenciesMeta": {
|
|
75
|
+
"vite-plus": {
|
|
76
|
+
"optional": true
|
|
77
|
+
}
|
|
78
|
+
},
|
|
65
79
|
"engines": {
|
|
66
80
|
"node": ">=22.12.0"
|
|
67
81
|
},
|
package/skills/dev-kit/SKILL.md
CHANGED
|
@@ -156,10 +156,34 @@ dependencies; `dev-kit apply` patches once and then converges.
|
|
|
156
156
|
Use `dev-kit tsgo patch --dry-run` for focused diagnosis. Use `--force` only
|
|
157
157
|
after the user accepts a potentially commit-incompatible TypeScript binary.
|
|
158
158
|
|
|
159
|
+
## Oxlint preset for Vite+
|
|
160
|
+
|
|
161
|
+
For Vite+ projects, compose the package's typed recommended Oxlint preset with
|
|
162
|
+
project-specific lint configuration:
|
|
163
|
+
|
|
164
|
+
```ts
|
|
165
|
+
import { recommendedOxlintConfig } from "@danieljvdm/dev-kit/oxlint";
|
|
166
|
+
import { defineConfig } from "vite-plus";
|
|
167
|
+
|
|
168
|
+
export default defineConfig({
|
|
169
|
+
lint: {
|
|
170
|
+
extends: [recommendedOxlintConfig],
|
|
171
|
+
rules: {
|
|
172
|
+
// Project-specific rules apply after the shared preset.
|
|
173
|
+
},
|
|
174
|
+
},
|
|
175
|
+
});
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
Use `lint.extends` instead of spreading the object so Vite+ composes nested
|
|
179
|
+
rule maps correctly. Keep repository-specific path conventions and
|
|
180
|
+
platform-specific accessibility rules in the consuming project.
|
|
181
|
+
|
|
159
182
|
## Current boundary
|
|
160
183
|
|
|
161
184
|
Manage skill outputs, the `setup.effectSource` checkout, and the explicit
|
|
162
185
|
`setup.effectTsgo` task. Edit shared `package.json` and `tsconfig.json`
|
|
163
|
-
contributions deliberately.
|
|
164
|
-
|
|
165
|
-
exposes
|
|
186
|
+
contributions deliberately. The Oxlint preset is a composable package export,
|
|
187
|
+
not a manifest-managed output. Treat named bundles, Oxfmt presets, and broader
|
|
188
|
+
setup tasks as future manifest capabilities until the installed CLI exposes
|
|
189
|
+
them.
|
package/src/catalog-manager.ts
CHANGED
|
@@ -216,7 +216,10 @@ export const addCatalogSource = Effect.fn("addCatalogSource")(function* (
|
|
|
216
216
|
const existingIndex = byId >= 0 ? byId : byRepository;
|
|
217
217
|
let next = state.sources.raw;
|
|
218
218
|
if (existingIndex >= 0) {
|
|
219
|
-
const existing = sources[existingIndex]
|
|
219
|
+
const existing = sources[existingIndex];
|
|
220
|
+
if (existing === undefined) {
|
|
221
|
+
return yield* new CatalogManagerError({ message: "catalog source index is out of bounds" });
|
|
222
|
+
}
|
|
220
223
|
const approved = state.lock?.value.sources.find((source) => source.id === existing.id)?.skills ?? [];
|
|
221
224
|
const currentInclude = existing.include.includes("*") ? approved : existing.include;
|
|
222
225
|
const include = [...new Set([...currentInclude, ...selection.include])].sort();
|
package/src/gitignore.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Cause, Effect, FileSystem, Path, PlatformError, Schema } from "effect";
|
|
1
|
+
import { Cause, Effect, FileSystem, Path, type PlatformError, Schema } from "effect";
|
|
2
2
|
|
|
3
3
|
import { printStatus } from "./cli-ui.ts";
|
|
4
4
|
import { observeSymbolicLink } from "./node-symbolic-link.ts";
|
package/src/oxlint.ts
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { OxlintConfig } from "vite-plus/lint";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* High-signal Oxlint defaults for TypeScript projects using Vite+.
|
|
5
|
+
*
|
|
6
|
+
* Extend this object from `lint.extends` so project-local plugins, rules, and
|
|
7
|
+
* overrides compose without losing nested configuration.
|
|
8
|
+
*/
|
|
9
|
+
export const recommendedOxlintConfig = {
|
|
10
|
+
options: {
|
|
11
|
+
typeAware: true,
|
|
12
|
+
},
|
|
13
|
+
plugins: ["import", "react", "vitest"],
|
|
14
|
+
rules: {
|
|
15
|
+
eqeqeq: "error",
|
|
16
|
+
"import/default": "off",
|
|
17
|
+
"import/namespace": "off",
|
|
18
|
+
"import/no-cycle": "error",
|
|
19
|
+
"import/no-duplicates": "error",
|
|
20
|
+
"import/no-self-import": "error",
|
|
21
|
+
"react/exhaustive-deps": "error",
|
|
22
|
+
"react/rules-of-hooks": "error",
|
|
23
|
+
"typescript/consistent-type-imports": "error",
|
|
24
|
+
"typescript/no-explicit-any": "error",
|
|
25
|
+
"typescript/no-non-null-assertion": "error",
|
|
26
|
+
"typescript/switch-exhaustiveness-check": "error",
|
|
27
|
+
"unicorn/prefer-node-protocol": "error",
|
|
28
|
+
"vitest/no-focused-tests": "error",
|
|
29
|
+
"vitest/no-identical-title": "error",
|
|
30
|
+
"vitest/no-standalone-expect": "off",
|
|
31
|
+
"vitest/valid-expect": "error",
|
|
32
|
+
},
|
|
33
|
+
} satisfies OxlintConfig;
|
|
34
|
+
|
|
35
|
+
export type RecommendedOxlintConfig = typeof recommendedOxlintConfig;
|
package/src/path-digest.ts
CHANGED
package/src/skill-manager.ts
CHANGED
|
@@ -154,7 +154,8 @@ const writeArray = Effect.fn("writeManifestArray")(function* (
|
|
|
154
154
|
let next = raw;
|
|
155
155
|
const retained = [...current];
|
|
156
156
|
for (let index = current.length - 1; index >= 0; index -= 1) {
|
|
157
|
-
|
|
157
|
+
const currentValue = current[index];
|
|
158
|
+
if (currentValue !== undefined && !values.includes(currentValue)) {
|
|
158
159
|
next = applyEdits(next, modify(next, [property, index], undefined, {
|
|
159
160
|
formattingOptions: { insertSpaces: true, tabSize: 2 },
|
|
160
161
|
}));
|
package/src/sync.ts
CHANGED
|
@@ -284,7 +284,6 @@ const resolveManagedPath = Effect.fn("resolveManagedPath")(function* (
|
|
|
284
284
|
projectDir: string,
|
|
285
285
|
candidate: string,
|
|
286
286
|
) {
|
|
287
|
-
const fs = yield* FileSystem.FileSystem;
|
|
288
287
|
const path = yield* Path.Path;
|
|
289
288
|
if (candidate.length === 0 || path.isAbsolute(candidate)) {
|
|
290
289
|
return yield* new UnsafeManagedPathError({ path: candidate, reason: "must be a non-empty project-relative path" });
|
package/src/vendor.ts
CHANGED
|
@@ -109,6 +109,14 @@ const inferSourceId = (repository: string): string => {
|
|
|
109
109
|
return tail.replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
110
110
|
};
|
|
111
111
|
|
|
112
|
+
const containsControlCharacter = (value: string): boolean => {
|
|
113
|
+
for (const character of value) {
|
|
114
|
+
const code = character.charCodeAt(0);
|
|
115
|
+
if (code <= 0x1f || code === 0x7f) return true;
|
|
116
|
+
}
|
|
117
|
+
return false;
|
|
118
|
+
};
|
|
119
|
+
|
|
112
120
|
const normalizeRepositoryLocator = (
|
|
113
121
|
repository: string,
|
|
114
122
|
): Effect.Effect<{
|
|
@@ -116,7 +124,7 @@ const normalizeRepositoryLocator = (
|
|
|
116
124
|
readonly ref?: string;
|
|
117
125
|
readonly skillsPath?: string;
|
|
118
126
|
}, InvalidSourceError> => {
|
|
119
|
-
if (
|
|
127
|
+
if (containsControlCharacter(repository)) {
|
|
120
128
|
return Effect.fail(new InvalidSourceError({ source: repository, reason: "repository contains control characters" }));
|
|
121
129
|
}
|
|
122
130
|
try {
|
|
@@ -126,9 +134,9 @@ const normalizeRepositoryLocator = (
|
|
|
126
134
|
}
|
|
127
135
|
if (url.hostname.toLowerCase() !== "github.com") return Effect.succeed({ repository });
|
|
128
136
|
const segments = url.pathname.split("/").filter(Boolean).map(decodeURIComponent);
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
const name =
|
|
137
|
+
const [owner, rawName] = segments;
|
|
138
|
+
if (owner === undefined || rawName === undefined) return Effect.succeed({ repository });
|
|
139
|
+
const name = rawName.replace(/\.git$/i, "");
|
|
132
140
|
const normalized = `https://github.com/${owner}/${name}.git`;
|
|
133
141
|
if (segments[2] === "tree" && segments[3]) {
|
|
134
142
|
return Effect.succeed({
|
|
@@ -389,13 +397,20 @@ const prepareSource = Effect.fn("prepareSkillSource")(function* (
|
|
|
389
397
|
yield* fs.makeDirectory(checkoutDir, { recursive: true });
|
|
390
398
|
yield* runCommand(checkoutDir, "git", ["init", "--quiet"]);
|
|
391
399
|
yield* runCommand(checkoutDir, "git", ["remote", "add", "origin", source.repository]);
|
|
400
|
+
const fetchRef = useLock ? lockedSource?.resolved : source.ref;
|
|
401
|
+
if (fetchRef === undefined) {
|
|
402
|
+
return yield* new InvalidSourceError({
|
|
403
|
+
source: source.id,
|
|
404
|
+
reason: "no matching lockfile entry; run catalog refresh without --locked first",
|
|
405
|
+
});
|
|
406
|
+
}
|
|
392
407
|
yield* runCommand(checkoutDir, "git", [
|
|
393
408
|
"fetch",
|
|
394
409
|
"--quiet",
|
|
395
410
|
"--depth",
|
|
396
411
|
"1",
|
|
397
412
|
"origin",
|
|
398
|
-
|
|
413
|
+
fetchRef,
|
|
399
414
|
]);
|
|
400
415
|
yield* runCommand(checkoutDir, "git", ["checkout", "--quiet", "--detach", "FETCH_HEAD"]);
|
|
401
416
|
const resolved = yield* runCommand(checkoutDir, "git", ["rev-parse", "HEAD"]);
|