@danieljvdm/dev-kit 0.15.0 → 0.17.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 +73 -7
- package/package.json +1 -1
- package/skill-sources.jsonc +17 -0
- package/skill-sources.lock.json +48 -0
- package/skills/build-effect-apis/SKILL.md +13 -31
- package/skills/build-effect-apis/references/verification.md +3 -3
- package/skills/effect-atom-state/SKILL.md +97 -0
- package/skills/effect-atom-state/agents/openai.yaml +4 -0
- package/skills/effect-atom-state/references/effect-atom-workflows.md +180 -0
- package/src/bin/dev-kit.ts +21 -0
- package/src/catalog.ts +39 -15
- package/src/effect-source.ts +70 -4
- package/src/global-cache.ts +304 -0
- package/src/oxlint.js +23 -0
- package/src/oxlint.ts +37 -1
- package/src/project-package.ts +77 -13
- package/src/sync.ts +36 -2
- package/src/vite-plus.js +8 -1
- package/src/vite-plus.ts +15 -1
- /package/skills/{build-effect-apis → effect-atom-state}/references/effect-atom-client.md +0 -0
- /package/skills/{build-effect-apis → effect-atom-state}/references/effect-atom-lifecycle.md +0 -0
- /package/skills/{build-effect-apis → effect-atom-state}/references/effect-atom-testing.md +0 -0
- /package/skills/{build-effect-apis → effect-atom-state}/references/tanstack-start.md +0 -0
package/src/project-package.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Effect, FileSystem, Path, Schema } from "effect";
|
|
1
|
+
import { Effect, FileSystem, Option, Path, Schema } from "effect";
|
|
2
2
|
|
|
3
3
|
export class ProjectPackageError extends Schema.TaggedError<ProjectPackageError>()(
|
|
4
4
|
"ProjectPackageError",
|
|
@@ -91,23 +91,87 @@ export const detectPackageManager = Effect.fn("detectPackageManager")(function*
|
|
|
91
91
|
return detected.length === 1 ? detected[0] : undefined;
|
|
92
92
|
});
|
|
93
93
|
|
|
94
|
-
|
|
95
|
-
|
|
94
|
+
const readOptionalProjectPackage = Effect.fn("readOptionalProjectPackage")(function* (
|
|
95
|
+
packageDir: string,
|
|
96
96
|
) {
|
|
97
|
-
|
|
97
|
+
return yield* readProjectPackage(packageDir).pipe(
|
|
98
98
|
Effect.catchTag("ProjectPackageError", (error) =>
|
|
99
99
|
error.message.startsWith("package.json not found:") ? Effect.void : Effect.fail(error),
|
|
100
100
|
),
|
|
101
101
|
);
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
const manifestDependencyNames = (
|
|
105
|
+
manifest: (typeof ProjectPackageSchema)["Type"] | undefined | void,
|
|
106
|
+
): ReadonlyArray<string> =>
|
|
107
|
+
manifest === undefined
|
|
108
|
+
? []
|
|
109
|
+
: [
|
|
110
|
+
...Object.keys(manifest.dependencies ?? {}),
|
|
111
|
+
...Object.keys(manifest.devDependencies ?? {}),
|
|
112
|
+
...Object.keys(manifest.optionalDependencies ?? {}),
|
|
113
|
+
...Object.keys(manifest.peerDependencies ?? {}),
|
|
114
|
+
];
|
|
115
|
+
|
|
116
|
+
export const readDirectDependencyNames = Effect.fn("readDirectDependencyNames")(function* (
|
|
117
|
+
projectDir: string,
|
|
118
|
+
) {
|
|
119
|
+
const manifest = yield* readOptionalProjectPackage(projectDir);
|
|
120
|
+
|
|
121
|
+
return [...new Set(manifestDependencyNames(manifest))].sort();
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
const WorkspacePatternsSchema = Schema.Union([
|
|
125
|
+
Schema.Array(Schema.String),
|
|
126
|
+
Schema.Struct({ packages: Schema.Array(Schema.String) }),
|
|
127
|
+
]);
|
|
128
|
+
// `workspaces` is declared `Schema.Unknown` in the project manifest schema, so
|
|
129
|
+
// this is a genuinely untyped boundary.
|
|
130
|
+
const decodeWorkspacePatterns = Schema.decodeUnknownOption(WorkspacePatternsSchema);
|
|
131
|
+
|
|
132
|
+
const workspacePatterns = (workspaces: unknown): ReadonlyArray<string> => {
|
|
133
|
+
const decoded = decodeWorkspacePatterns(workspaces);
|
|
102
134
|
|
|
103
|
-
if (
|
|
135
|
+
if (Option.isNone(decoded)) return [];
|
|
136
|
+
|
|
137
|
+
return "packages" in decoded.value ? decoded.value.packages : decoded.value;
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Direct dependency names of the project package plus every workspace member
|
|
142
|
+
* package. Only literal workspace paths and single trailing-star globs
|
|
143
|
+
* (`apps/*`) are expanded; other patterns are skipped.
|
|
144
|
+
*/
|
|
145
|
+
export const readWorkspaceDependencyNames = Effect.fn("readWorkspaceDependencyNames")(function* (
|
|
146
|
+
projectDir: string,
|
|
147
|
+
) {
|
|
148
|
+
const fs = yield* FileSystem.FileSystem;
|
|
149
|
+
const path = yield* Path.Path;
|
|
150
|
+
const manifest = yield* readOptionalProjectPackage(projectDir);
|
|
151
|
+
const names = new Set(manifestDependencyNames(manifest));
|
|
152
|
+
|
|
153
|
+
for (const pattern of workspacePatterns(manifest?.workspaces)) {
|
|
154
|
+
if (pattern.startsWith("!")) continue;
|
|
155
|
+
const star = pattern.indexOf("*");
|
|
156
|
+
let memberDirs: ReadonlyArray<string> = [];
|
|
157
|
+
|
|
158
|
+
if (star === -1) {
|
|
159
|
+
memberDirs = [pattern];
|
|
160
|
+
} else if (pattern.endsWith("/*") && star === pattern.length - 1) {
|
|
161
|
+
const parent = path.join(projectDir, pattern.slice(0, -2));
|
|
162
|
+
|
|
163
|
+
if (yield* fs.exists(parent)) {
|
|
164
|
+
memberDirs = (yield* fs.readDirectory(parent)).map((name) =>
|
|
165
|
+
path.join(pattern.slice(0, -2), name),
|
|
166
|
+
);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
for (const memberDir of memberDirs) {
|
|
170
|
+
const member = yield* readOptionalProjectPackage(path.join(projectDir, memberDir));
|
|
171
|
+
|
|
172
|
+
for (const name of manifestDependencyNames(member)) names.add(name);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
104
175
|
|
|
105
|
-
return [
|
|
106
|
-
...new Set([
|
|
107
|
-
...Object.keys(manifest.dependencies ?? {}),
|
|
108
|
-
...Object.keys(manifest.devDependencies ?? {}),
|
|
109
|
-
...Object.keys(manifest.optionalDependencies ?? {}),
|
|
110
|
-
...Object.keys(manifest.peerDependencies ?? {}),
|
|
111
|
-
]),
|
|
112
|
-
].sort();
|
|
176
|
+
return [...names].sort();
|
|
113
177
|
});
|
package/src/sync.ts
CHANGED
|
@@ -15,6 +15,7 @@ import {
|
|
|
15
15
|
planEffectTsgoPatch,
|
|
16
16
|
type EffectTsgoPatchPlan,
|
|
17
17
|
} from "./effect-tsgo.ts";
|
|
18
|
+
import { maybePruneGlobalCache } from "./global-cache.ts";
|
|
18
19
|
import { DevKitManifestSchema, normalizeManifest } from "./manifest.ts";
|
|
19
20
|
import { observeSymbolicLink } from "./node-symbolic-link.ts";
|
|
20
21
|
import { resolvePackageSkillSelector } from "./package-skill-source.ts";
|
|
@@ -29,6 +30,7 @@ import {
|
|
|
29
30
|
detectPackageManager,
|
|
30
31
|
PACKAGE_MANAGER_COMMANDS,
|
|
31
32
|
readDirectDependencyNames,
|
|
33
|
+
readWorkspaceDependencyNames,
|
|
32
34
|
readProjectPackage,
|
|
33
35
|
type PackageManagerName,
|
|
34
36
|
} from "./project-package.ts";
|
|
@@ -266,7 +268,13 @@ const encodePlanSnapshotJson = Schema.encodeSync(Schema.fromJsonString(Schema.Un
|
|
|
266
268
|
const encodeAppliedStatePrettyJson = Schema.encodeSync(fromJsonString(AppliedStateSchema, 2));
|
|
267
269
|
|
|
268
270
|
const SKILL_FAMILIES: SkillCatalog = {
|
|
269
|
-
effect: [
|
|
271
|
+
effect: [
|
|
272
|
+
"effect-ts",
|
|
273
|
+
"effect-architecture-audit",
|
|
274
|
+
"build-effect-apis",
|
|
275
|
+
"effect-atom-state",
|
|
276
|
+
"build-effect-clis",
|
|
277
|
+
],
|
|
270
278
|
};
|
|
271
279
|
|
|
272
280
|
export const DEFAULT_MANIFEST = "dev-kit.jsonc";
|
|
@@ -805,7 +813,7 @@ const renderAgentInstructions = Effect.fn("renderAgentInstructions")(function* (
|
|
|
805
813
|
);
|
|
806
814
|
const directDependencyNames = yield* readDirectDependencyNames(projectDir);
|
|
807
815
|
const usesVitePlus = directDependencyNames.includes("vite-plus");
|
|
808
|
-
const
|
|
816
|
+
const effectGuideInstructions =
|
|
809
817
|
directDependencyNames.includes("effect") &&
|
|
810
818
|
(yield* observePath(path.join(projectDir, "node_modules", "effect", "AGENTS.md"))).kind ===
|
|
811
819
|
"file"
|
|
@@ -821,6 +829,20 @@ guide doesn't cover, search through the source code in \`node_modules/effect/src
|
|
|
821
829
|
|
|
822
830
|
`
|
|
823
831
|
: "";
|
|
832
|
+
const atomBoundaryInstructions = (yield* readWorkspaceDependencyNames(projectDir)).includes(
|
|
833
|
+
"@effect/atom-react",
|
|
834
|
+
)
|
|
835
|
+
? `# Effect Atom client boundary
|
|
836
|
+
|
|
837
|
+
This repository consumes APIs through Effect Atom clients (\`@effect/atom-react\`).
|
|
838
|
+
Keep business logic in Effect: compose multi-step client workflows as atoms,
|
|
839
|
+
declare cross-query invalidation as reactivity keys on mutations, and keep
|
|
840
|
+
promise-mode dispatches at the React boundary logic-free — no \`.then\` chains
|
|
841
|
+
in components or routes.
|
|
842
|
+
|
|
843
|
+
`
|
|
844
|
+
: "";
|
|
845
|
+
const effectInstructions = `${effectGuideInstructions}${atomBoundaryInstructions}`;
|
|
824
846
|
const projectPackage = yield* readProjectPackage(projectDir).pipe(
|
|
825
847
|
Effect.catchTag("ProjectPackageError", (error) =>
|
|
826
848
|
error.message.startsWith("package.json not found:") ? Effect.void : Effect.fail(error),
|
|
@@ -1877,6 +1899,18 @@ export const runProjectSkillPlan = Effect.fn("runProjectSkillPlan")(function* (
|
|
|
1877
1899
|
yield* applyPlannedSkillChanges(replanned);
|
|
1878
1900
|
}),
|
|
1879
1901
|
);
|
|
1902
|
+
const fs = yield* FileSystem.FileSystem;
|
|
1903
|
+
const path = yield* Path.Path;
|
|
1904
|
+
|
|
1905
|
+
// Catalog checkouts moved to the machine-global cache; drop the regenerable
|
|
1906
|
+
// project-local copies left behind by earlier dev-kit versions.
|
|
1907
|
+
yield* fs
|
|
1908
|
+
.remove(path.join(plan.projectDir, ".dev-kit", "cache", "catalog"), {
|
|
1909
|
+
force: true,
|
|
1910
|
+
recursive: true,
|
|
1911
|
+
})
|
|
1912
|
+
.pipe(Effect.ignore);
|
|
1913
|
+
yield* maybePruneGlobalCache().pipe(Effect.ignore);
|
|
1880
1914
|
yield* printStatus(
|
|
1881
1915
|
"success",
|
|
1882
1916
|
changes === 0 && !replanned.metadataChanged ? "Dev kit up to date" : "Dev kit ready",
|
package/src/vite-plus.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { recommendedOxfmtConfig } from "./oxfmt.js";
|
|
2
|
-
import { recommendedOxlintConfig } from "./oxlint.js";
|
|
2
|
+
import { createAbsoluteImportsOxlintOverride, recommendedOxlintConfig } from "./oxlint.js";
|
|
3
3
|
import { devKitToolIgnorePatterns } from "./tool-ignore-patterns.js";
|
|
4
4
|
|
|
5
5
|
export { devKitToolIgnorePatterns } from "./tool-ignore-patterns.js";
|
|
@@ -58,6 +58,12 @@ const createTypecheckTask = (options) => {
|
|
|
58
58
|
/** Build composable quality defaults for a project-owned Vite+ config. */
|
|
59
59
|
export const createRecommendedVitePlusConfig = (options = {}) => {
|
|
60
60
|
const ignorePatterns = [...devKitToolIgnorePatterns, ...(options.ignorePatterns ?? [])];
|
|
61
|
+
const lintOverrides = options.absoluteImports
|
|
62
|
+
? [
|
|
63
|
+
...recommendedOxlintConfig.overrides,
|
|
64
|
+
createAbsoluteImportsOxlintOverride(options.absoluteImports),
|
|
65
|
+
]
|
|
66
|
+
: recommendedOxlintConfig.overrides;
|
|
61
67
|
|
|
62
68
|
return {
|
|
63
69
|
staged: {
|
|
@@ -70,6 +76,7 @@ export const createRecommendedVitePlusConfig = (options = {}) => {
|
|
|
70
76
|
lint: {
|
|
71
77
|
...recommendedOxlintConfig,
|
|
72
78
|
ignorePatterns,
|
|
79
|
+
overrides: lintOverrides,
|
|
73
80
|
},
|
|
74
81
|
run: {
|
|
75
82
|
tasks: {
|
package/src/vite-plus.ts
CHANGED
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
import { recommendedOxfmtConfig } from "./oxfmt.ts";
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
type AbsoluteImportsOptions,
|
|
4
|
+
createAbsoluteImportsOxlintOverride,
|
|
5
|
+
recommendedOxlintConfig,
|
|
6
|
+
} from "./oxlint.ts";
|
|
3
7
|
import { devKitToolIgnorePatterns } from "./tool-ignore-patterns.ts";
|
|
4
8
|
|
|
9
|
+
export type { AbsoluteImportsOptions } from "./oxlint.ts";
|
|
5
10
|
export { devKitToolIgnorePatterns } from "./tool-ignore-patterns.ts";
|
|
6
11
|
|
|
7
12
|
export type VitePlusTypecheckOptions =
|
|
@@ -16,6 +21,8 @@ export type VitePlusTypecheckOptions =
|
|
|
16
21
|
};
|
|
17
22
|
|
|
18
23
|
export type RecommendedVitePlusConfigOptions = {
|
|
24
|
+
/** Enforce path-alias imports (no `../`) inside the given globs. */
|
|
25
|
+
readonly absoluteImports?: AbsoluteImportsOptions;
|
|
19
26
|
/** Additional project-owned generated or vendored paths. */
|
|
20
27
|
readonly ignorePatterns?: ReadonlyArray<string>;
|
|
21
28
|
readonly typecheck?: VitePlusTypecheckOptions;
|
|
@@ -79,6 +86,12 @@ const createTypecheckTask = (options: VitePlusTypecheckOptions | undefined) => {
|
|
|
79
86
|
*/
|
|
80
87
|
export const createRecommendedVitePlusConfig = (options: RecommendedVitePlusConfigOptions = {}) => {
|
|
81
88
|
const ignorePatterns = [...devKitToolIgnorePatterns, ...(options.ignorePatterns ?? [])];
|
|
89
|
+
const lintOverrides = options.absoluteImports
|
|
90
|
+
? [
|
|
91
|
+
...recommendedOxlintConfig.overrides,
|
|
92
|
+
createAbsoluteImportsOxlintOverride(options.absoluteImports),
|
|
93
|
+
]
|
|
94
|
+
: recommendedOxlintConfig.overrides;
|
|
82
95
|
|
|
83
96
|
return {
|
|
84
97
|
staged: {
|
|
@@ -91,6 +104,7 @@ export const createRecommendedVitePlusConfig = (options: RecommendedVitePlusConf
|
|
|
91
104
|
lint: {
|
|
92
105
|
...recommendedOxlintConfig,
|
|
93
106
|
ignorePatterns,
|
|
107
|
+
overrides: lintOverrides,
|
|
94
108
|
},
|
|
95
109
|
run: {
|
|
96
110
|
tasks: {
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|