@danieljvdm/dev-kit 0.17.0 → 1.0.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.
Files changed (68) hide show
  1. package/README.md +148 -648
  2. package/package.json +19 -62
  3. package/skills/dev-kit/SKILL.md +42 -213
  4. package/skills/dev-kit/agents/openai.yaml +2 -2
  5. package/skills/dev-kit/references/cloudflare-worker-api.md +37 -0
  6. package/skills/dev-kit/references/default-typescript-repository.md +43 -0
  7. package/skills/dev-kit/references/legacy-eject.md +46 -0
  8. package/skills/dev-kit/references/repository-setup.md +53 -0
  9. package/skills/dev-kit/references/skills.md +35 -0
  10. package/src/bin/dev-kit.ts +153 -134
  11. package/src/catalog-manager.ts +23 -18
  12. package/src/catalog.ts +8 -5
  13. package/src/cli-ui.ts +2 -2
  14. package/src/effect-tsgo.ts +10 -6
  15. package/src/eject.ts +715 -0
  16. package/src/gitignore.ts +6 -3
  17. package/src/legacy-project.ts +67 -0
  18. package/src/oxfmt.ts +1 -4
  19. package/src/oxlint-plugin-anti-slop/LICENSE +21 -0
  20. package/src/oxlint-plugin-anti-slop/index.ts +43 -0
  21. package/src/oxlint-plugin-anti-slop/rules/no-chained-type-assertions.ts +79 -0
  22. package/src/oxlint-plugin-anti-slop/rules/no-conditional-empty-object-spread.ts +51 -0
  23. package/src/oxlint-plugin-anti-slop/rules/no-known-value-widening.ts +267 -0
  24. package/src/oxlint-plugin-anti-slop/rules/no-module-mocking.ts +105 -0
  25. package/src/oxlint-plugin-anti-slop/rules/no-object-parameters.ts +141 -0
  26. package/src/oxlint-plugin-anti-slop/rules/no-reflect-apply.ts +28 -0
  27. package/src/oxlint-plugin-anti-slop/rules/no-reflect-get.ts +28 -0
  28. package/src/oxlint-plugin-anti-slop/rules/no-runtime-typeof.ts +25 -0
  29. package/src/oxlint-plugin-anti-slop/rules/no-shape-in-symbol-names.ts +38 -0
  30. package/src/oxlint-plugin-anti-slop/rules/no-unknown-parameters.ts +86 -0
  31. package/src/oxlint-plugin-anti-slop/rules/no-unknown-returns.ts +125 -0
  32. package/src/oxlint-plugin-anti-slop/rules/no-unknown-type-aliases.ts +73 -0
  33. package/src/oxlint-plugin-anti-slop/rules/no-unsafe-dictionary-type.ts +139 -0
  34. package/src/oxlint-plugin-anti-slop/rules/no-widen-then-assert.ts +396 -0
  35. package/src/oxlint-plugin-anti-slop/rules/require-safety-comment-for-type-assertion.ts +61 -0
  36. package/src/oxlint-plugin-anti-slop/runtime.js +1209 -0
  37. package/src/oxlint-plugin-anti-slop/shared/dictionary-types.ts +555 -0
  38. package/src/oxlint-plugin-anti-slop/shared/reflect-method.ts +40 -0
  39. package/src/oxlint.ts +26 -14
  40. package/src/package-skill-source.ts +17 -25
  41. package/src/path-digest.ts +62 -1
  42. package/src/project-package.ts +14 -12
  43. package/src/project-skills.ts +722 -0
  44. package/src/tool-metadata.ts +0 -2
  45. package/src/vendor.ts +35 -21
  46. package/src/vite-plus.ts +1 -3
  47. package/dev-kit.example.jsonc +0 -22
  48. package/schema/dev-kit.schema.json +0 -218
  49. package/schema/skill-sources.schema.json +0 -83
  50. package/src/index.ts +0 -125
  51. package/src/manifest.ts +0 -216
  52. package/src/oxfmt.js +0 -23
  53. package/src/oxlint-plugin-effect.d.ts +0 -17
  54. package/src/oxlint-plugin-style.d.ts +0 -8
  55. package/src/oxlint.js +0 -94
  56. package/src/project-state.ts +0 -122
  57. package/src/scaffold.ts +0 -79
  58. package/src/skill-manager.ts +0 -515
  59. package/src/sync.ts +0 -1919
  60. package/src/tool-ignore-patterns.js +0 -9
  61. package/src/vite-plus-dependency.ts +0 -69
  62. package/src/vite-plus-hooks.ts +0 -175
  63. package/src/vite-plus-workflow.ts +0 -82
  64. package/src/vite-plus.js +0 -88
  65. package/src/worktrunk-config.ts +0 -88
  66. package/templates/AGENTS.md +0 -11
  67. package/templates/vite-plus/github-actions-check.yml +0 -51
  68. package/templates/worktrunk/wt.toml +0 -27
@@ -1,4 +1,4 @@
1
- import { Effect, FileSystem, Path, Result, Schema } from "effect";
1
+ import { Effect, FileSystem, Option, Path, Result, Schema } from "effect";
2
2
 
3
3
  import { observeSymbolicLink } from "./node-symbolic-link.ts";
4
4
  import { readDirectDependencyNames } from "./project-package.ts";
@@ -34,33 +34,25 @@ const PackageMetadataSchema = Schema.fromJsonString(
34
34
  }),
35
35
  );
36
36
 
37
- const nonEmptyString = (value: unknown): value is string =>
38
- typeof value === "string" && value.trim().length > 0;
37
+ const DiscoveryLocationSchema = Schema.String.check(Schema.isPattern(/\S/));
38
+ const IntentDiscoveryMetadataSchema = Schema.Struct({
39
+ version: Schema.Literal(1),
40
+ repo: DiscoveryLocationSchema,
41
+ docs: DiscoveryLocationSchema,
42
+ });
43
+ const RepositoryDiscoveryMetadataSchema = Schema.Union([
44
+ DiscoveryLocationSchema,
45
+ Schema.Struct({ url: DiscoveryLocationSchema }),
46
+ ]);
47
+ const decodeIntentDiscoveryMetadata = Schema.decodeUnknownOption(IntentDiscoveryMetadataSchema);
48
+ const decodeRepositoryDiscoveryMetadata = Schema.decodeUnknownOption(
49
+ RepositoryDiscoveryMetadataSchema,
50
+ );
39
51
 
40
52
  const hasIntentDiscoveryMetadata = (metadata: typeof PackageMetadataSchema.Type): boolean => {
41
- const intent = metadata.intent;
53
+ if (Option.isSome(decodeIntentDiscoveryMetadata(metadata.intent))) return true;
42
54
 
43
- if (
44
- typeof intent === "object" &&
45
- intent !== null &&
46
- "version" in intent &&
47
- intent.version === 1 &&
48
- "repo" in intent &&
49
- nonEmptyString(intent.repo) &&
50
- "docs" in intent &&
51
- nonEmptyString(intent.docs)
52
- ) {
53
- return true;
54
- }
55
- const repository = metadata.repository;
56
-
57
- return (
58
- nonEmptyString(repository) ||
59
- (typeof repository === "object" &&
60
- repository !== null &&
61
- "url" in repository &&
62
- nonEmptyString(repository.url))
63
- );
55
+ return Option.isSome(decodeRepositoryDiscoveryMetadata(metadata.repository));
64
56
  };
65
57
 
66
58
  const isSafePackageVersion = (value: string): boolean =>
@@ -23,13 +23,14 @@ export class PathInspectionError extends Schema.TaggedError<PathInspectionError>
23
23
  }
24
24
 
25
25
  const textEncoder = new TextEncoder();
26
+ const isString = Schema.is(Schema.String);
26
27
 
27
28
  // Git preserves only the executable distinction for regular files. Canonicalizing
28
29
  // the remaining bits keeps digests stable across checkout and copy umasks.
29
30
  const canonicalFileMode = (mode: number): number => ((mode & 0o111) === 0 ? 0o644 : 0o755);
30
31
 
31
32
  const frame = (value: string | Uint8Array): Uint8Array => {
32
- const bytes = typeof value === "string" ? textEncoder.encode(value) : value;
33
+ const bytes = isString(value) ? textEncoder.encode(value) : value;
33
34
  const framed = new Uint8Array(4 + bytes.length);
34
35
 
35
36
  new DataView(framed.buffer).setUint32(0, bytes.length);
@@ -152,6 +153,66 @@ export const observePath = Effect.fn("observeManagedPath")(function* (absolutePa
152
153
  );
153
154
  });
154
155
 
156
+ export const observeDirectoryWithoutEntry = Effect.fn("observeDirectoryWithoutEntry")(function* (
157
+ absolutePath: string,
158
+ excludedEntry: string,
159
+ ) {
160
+ const fs = yield* FileSystem.FileSystem;
161
+ const path = yield* Path.Path;
162
+
163
+ if (
164
+ excludedEntry.length === 0 ||
165
+ excludedEntry === "." ||
166
+ excludedEntry === ".." ||
167
+ excludedEntry.includes("/") ||
168
+ excludedEntry.includes("\\")
169
+ ) {
170
+ return yield* PathInspectionError.make({
171
+ path: absolutePath,
172
+ operation: "exclude a direct directory entry",
173
+ cause: excludedEntry,
174
+ });
175
+ }
176
+ const observed = yield* observeSymbolicLink(absolutePath);
177
+
178
+ if (observed.kind === "missing") return { kind: "missing" } as const;
179
+ if (observed.kind === "symlink") {
180
+ return yield* PathInspectionError.make({
181
+ path: absolutePath,
182
+ operation: "inspect a regular directory",
183
+ cause: "path is a symlink",
184
+ });
185
+ }
186
+ const info = yield* fs.stat(absolutePath);
187
+
188
+ if (info.type !== "Directory") {
189
+ return yield* PathInspectionError.make({
190
+ path: absolutePath,
191
+ operation: "inspect a regular directory",
192
+ cause: info.type,
193
+ });
194
+ }
195
+ const entries = (yield* fs.readDirectory(absolutePath))
196
+ .filter((entry) => entry !== excludedEntry)
197
+ .sort(compareUtf8);
198
+ const frames: Array<string | Uint8Array> = ["directory-v1"];
199
+
200
+ for (const entry of entries) {
201
+ const child = yield* digestFileSystemPath(path.join(absolutePath, entry), canonicalFileMode);
202
+
203
+ if (child.kind === "missing") {
204
+ return yield* PathInspectionError.make({
205
+ path: path.join(absolutePath, entry),
206
+ operation: "inspect a stable directory tree",
207
+ cause: "path disappeared during inspection",
208
+ });
209
+ }
210
+ frames.push(entry, child.kind, child.digest);
211
+ }
212
+
213
+ return { kind: "directory", digest: yield* digestFrames(frames) } as const;
214
+ });
215
+
155
216
  export const digestText = Effect.fn("digestText")(function* (value: string) {
156
217
  return yield* digestFrames(["text-v1", value]);
157
218
  });
@@ -47,14 +47,16 @@ export const PACKAGE_MANAGER_COMMANDS = {
47
47
  yarn: { install: "yarn install", label: "Yarn" },
48
48
  } as const;
49
49
 
50
- export type PackageManagerName = keyof typeof PACKAGE_MANAGER_COMMANDS;
50
+ const PackageManagerNameSchema = Schema.Literals(["bun", "npm", "pnpm", "yarn"]);
51
+
52
+ export type PackageManagerName = typeof PackageManagerNameSchema.Type;
53
+
54
+ const decodePackageManagerName = Schema.decodeUnknownOption(PackageManagerNameSchema);
51
55
 
52
56
  const packageManagerName = (declaration: string | undefined): PackageManagerName | undefined => {
53
57
  const name = declaration?.split("@", 1)[0];
54
58
 
55
- return name !== undefined && name in PACKAGE_MANAGER_COMMANDS
56
- ? (name as PackageManagerName)
57
- : undefined;
59
+ return name === undefined ? undefined : Option.getOrUndefined(decodePackageManagerName(name));
58
60
  };
59
61
 
60
62
  export const detectPackageManager = Effect.fn("detectPackageManager")(function* (
@@ -129,13 +131,9 @@ const WorkspacePatternsSchema = Schema.Union([
129
131
  // this is a genuinely untyped boundary.
130
132
  const decodeWorkspacePatterns = Schema.decodeUnknownOption(WorkspacePatternsSchema);
131
133
 
132
- const workspacePatterns = (workspaces: unknown): ReadonlyArray<string> => {
133
- const decoded = decodeWorkspacePatterns(workspaces);
134
-
135
- if (Option.isNone(decoded)) return [];
136
-
137
- return "packages" in decoded.value ? decoded.value.packages : decoded.value;
138
- };
134
+ const workspacePatterns = (
135
+ workspaces: typeof WorkspacePatternsSchema.Type,
136
+ ): ReadonlyArray<string> => ("packages" in workspaces ? workspaces.packages : workspaces);
139
137
 
140
138
  /**
141
139
  * Direct dependency names of the project package plus every workspace member
@@ -149,8 +147,12 @@ export const readWorkspaceDependencyNames = Effect.fn("readWorkspaceDependencyNa
149
147
  const path = yield* Path.Path;
150
148
  const manifest = yield* readOptionalProjectPackage(projectDir);
151
149
  const names = new Set(manifestDependencyNames(manifest));
150
+ const decodedWorkspaces = decodeWorkspacePatterns(manifest?.workspaces);
151
+ const patterns = Option.isSome(decodedWorkspaces)
152
+ ? workspacePatterns(decodedWorkspaces.value)
153
+ : [];
152
154
 
153
- for (const pattern of workspacePatterns(manifest?.workspaces)) {
155
+ for (const pattern of patterns) {
154
156
  if (pattern.startsWith("!")) continue;
155
157
  const star = pattern.indexOf("*");
156
158
  let memberDirs: ReadonlyArray<string> = [];