@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
@@ -11,153 +11,179 @@ import {
11
11
  import { printError } from "../cli-ui.ts";
12
12
  import { syncEffectSource } from "../effect-source.ts";
13
13
  import { patchEffectTsgo } from "../effect-tsgo.ts";
14
+ import { runEject } from "../eject.ts";
14
15
  import { patchProjectGitignore } from "../gitignore.ts";
15
16
  import { CACHE_PRUNE_AGE_DAYS, runCachePrune } from "../global-cache.ts";
16
17
  import {
17
- addSkills,
18
- chooseSkillsToAdd,
19
- chooseSkillsToRemove,
20
- initProject,
21
- listSkills,
22
- removeSkills,
23
- showDashboard,
24
- showSkill,
25
- } from "../skill-manager.ts";
26
- import { DEFAULT_MANIFEST, runProjectSkillPlan } from "../sync.ts";
18
+ addProjectSkills,
19
+ detachProjectSkills,
20
+ diffProjectSkill,
21
+ listProjectSkills,
22
+ setupProject,
23
+ showProjectSkill,
24
+ showProjectSkillsDashboard,
25
+ statusProjectSkills,
26
+ updateProjectSkills,
27
+ } from "../project-skills.ts";
27
28
  import { DEV_KIT_VERSION } from "../tool-metadata.ts";
28
29
  import { refreshSkillCatalog } from "../vendor.ts";
29
30
 
30
- const projectFlags = {
31
- manifest: Flag.string("manifest").pipe(
32
- Flag.withDefault(DEFAULT_MANIFEST),
33
- Flag.withDescription("Project-relative manifest path."),
34
- ),
31
+ const repositorySkillFlags = {
35
32
  projectDir: Flag.string("project-dir").pipe(
36
33
  Flag.withDefault("."),
37
34
  Flag.withDescription("Project directory (defaults to the current directory)."),
38
35
  ),
36
+ target: Flag.string("target").pipe(
37
+ Flag.withDefault(".agents/skills"),
38
+ Flag.withDescription("Project-relative skills directory."),
39
+ ),
39
40
  };
40
41
 
41
- const initCommand = CliCommand.make("init", projectFlags, ({ manifest, projectDir }) =>
42
- initProject({ manifestPath: manifest, projectDir }),
43
- ).pipe(CliCommand.withDescription("Initialize skill management in this project."));
42
+ const setupCommand = CliCommand.make(
43
+ "setup",
44
+ {
45
+ dryRun: Flag.boolean("dry-run").pipe(Flag.withDescription("Preview without writing.")),
46
+ ...repositorySkillFlags,
47
+ },
48
+ ({ dryRun, projectDir, target }) => setupProject({ dryRun, projectDir, target }),
49
+ ).pipe(
50
+ CliCommand.withDescription(
51
+ "Copy the agent-led Dev Kit setup skill into a repository without adding a dependency.",
52
+ ),
53
+ );
44
54
 
45
- const addCommand = CliCommand.make(
46
- "add",
55
+ const ejectCommand = CliCommand.make(
56
+ "eject",
47
57
  {
48
- skills: Argument.string("skills").pipe(Argument.variadic()),
49
- noApply: Flag.boolean("no-apply").pipe(
50
- Flag.withDescription("Update the manifest without installing yet."),
58
+ dryRun: Flag.boolean("dry-run").pipe(Flag.withDescription("Preview without writing.")),
59
+ lockfile: Flag.string("lockfile").pipe(
60
+ Flag.withDefault("dev-kit.lock.json"),
61
+ Flag.withDescription("Project-relative legacy lock path."),
62
+ ),
63
+ manifest: Flag.string("manifest").pipe(
64
+ Flag.withDefault("dev-kit.jsonc"),
65
+ Flag.withDescription("Project-relative legacy manifest path."),
66
+ ),
67
+ projectDir: Flag.string("project-dir").pipe(
68
+ Flag.withDefault("."),
69
+ Flag.withDescription("Legacy project directory."),
70
+ ),
71
+ state: Flag.string("state").pipe(
72
+ Flag.withDefault(".dev-kit/state.json"),
73
+ Flag.withDescription("Project-relative legacy ownership receipt."),
74
+ ),
75
+ target: Flag.string("target").pipe(
76
+ Flag.withDefault(".agents/skills"),
77
+ Flag.withDescription("Project-relative destination for ejected skills."),
51
78
  ),
52
- ...projectFlags,
53
79
  },
54
- ({ skills, noApply, manifest, projectDir }) =>
55
- skills.length === 0
56
- ? chooseSkillsToAdd({ apply: !noApply, manifestPath: manifest, projectDir })
57
- : addSkills(skills, { apply: !noApply, manifestPath: manifest, projectDir }),
58
- ).pipe(CliCommand.withDescription("Select and install one or more available skills."));
80
+ ({ dryRun, lockfile, manifest, projectDir, state, target }) =>
81
+ runEject({
82
+ dryRun,
83
+ lockfilePath: lockfile,
84
+ manifestPath: manifest,
85
+ projectDir,
86
+ statePath: state,
87
+ target,
88
+ }),
89
+ ).pipe(
90
+ CliCommand.withDescription(
91
+ "Release a legacy managed project into repo-owned files after persistent helpers are materialized.",
92
+ ),
93
+ );
59
94
 
60
- const removeCommand = CliCommand.make(
61
- "remove",
95
+ const projectSkillsAddCommand = CliCommand.make(
96
+ "add",
62
97
  {
63
- skills: Argument.string("skills").pipe(Argument.variadic()),
64
- noApply: Flag.boolean("no-apply").pipe(
65
- Flag.withDescription("Update the manifest without uninstalling yet."),
66
- ),
67
- ...projectFlags,
98
+ skills: Argument.string("skills").pipe(Argument.variadic({ min: 1 })),
99
+ dryRun: Flag.boolean("dry-run").pipe(Flag.withDescription("Preview without writing.")),
100
+ ...repositorySkillFlags,
68
101
  },
69
- ({ skills, noApply, manifest, projectDir }) =>
70
- skills.length === 0
71
- ? chooseSkillsToRemove({ apply: !noApply, manifestPath: manifest, projectDir })
72
- : removeSkills(skills, { apply: !noApply, manifestPath: manifest, projectDir }),
73
- ).pipe(CliCommand.withDescription("Deselect and uninstall one or more skills."));
102
+ ({ skills, dryRun, projectDir, target }) =>
103
+ addProjectSkills(skills, { dryRun, projectDir, target }),
104
+ ).pipe(CliCommand.withDescription("Copy repo-owned skills from the approved catalog."));
74
105
 
75
- const listCommand = CliCommand.make(
106
+ const projectSkillsListCommand = CliCommand.make(
76
107
  "list",
77
108
  {
78
- all: Flag.boolean("all").pipe(Flag.withDescription("Include unselected skills.")),
79
- ...projectFlags,
109
+ all: Flag.boolean("all").pipe(Flag.withDescription("Include uninstalled skills.")),
110
+ ...repositorySkillFlags,
80
111
  },
81
- ({ all, manifest, projectDir }) => listSkills({ all, manifestPath: manifest, projectDir }),
82
- ).pipe(CliCommand.withDescription("List selected skills; use --all to browse the catalog."));
112
+ ({ all, projectDir, target }) => listProjectSkills({ all, projectDir, target }),
113
+ ).pipe(CliCommand.withDescription("List installed skills or browse the catalog."));
83
114
 
84
- const searchCommand = CliCommand.make(
115
+ const projectSkillsSearchCommand = CliCommand.make(
85
116
  "search",
86
- { query: Argument.string("query").pipe(Argument.variadic({ min: 1 })), ...projectFlags },
87
- ({ query, manifest, projectDir }) =>
88
- listSkills({ all: true, query: query.join(" "), manifestPath: manifest, projectDir }),
89
- ).pipe(CliCommand.withDescription("Search available skill names and descriptions."));
117
+ {
118
+ query: Argument.string("query").pipe(Argument.variadic({ min: 1 })),
119
+ ...repositorySkillFlags,
120
+ },
121
+ ({ query, projectDir, target }) =>
122
+ listProjectSkills({ all: true, projectDir, query: query.join(" "), target }),
123
+ ).pipe(CliCommand.withDescription("Search the approved skill catalog."));
90
124
 
91
- const infoCommand = CliCommand.make(
125
+ const projectSkillsInfoCommand = CliCommand.make(
92
126
  "info",
93
- { skill: Argument.string("skill"), ...projectFlags },
94
- ({ skill, manifest, projectDir }) => showSkill(skill, { manifestPath: manifest, projectDir }),
95
- ).pipe(CliCommand.withDescription("Show provenance and details for an available skill."));
127
+ { skill: Argument.string("skill"), ...repositorySkillFlags },
128
+ ({ skill, projectDir, target }) => showProjectSkill(skill, { projectDir, target }),
129
+ ).pipe(CliCommand.withDescription("Show one skill's description and provenance."));
96
130
 
97
- const planCommand = CliCommand.make(
98
- "plan",
99
- {
100
- locked: Flag.boolean("locked"),
101
- lockfile: Flag.string("lockfile").pipe(Flag.withDefault("dev-kit.lock.json")),
102
- manifest: Flag.string("manifest").pipe(Flag.withDefault(DEFAULT_MANIFEST)),
103
- projectDir: Flag.string("project-dir").pipe(Flag.withDefault(".")),
104
- },
105
- ({ locked, lockfile, manifest, projectDir }) =>
106
- runProjectSkillPlan({
107
- dryRun: true,
108
- locked,
109
- lockfilePath: lockfile,
110
- manifestPath: manifest,
111
- projectDir,
112
- }),
113
- ).pipe(
114
- CliCommand.withDescription("Plan ownership-safe project skill changes without writing files."),
115
- );
131
+ const projectSkillsStatusCommand = CliCommand.make(
132
+ "status",
133
+ repositorySkillFlags,
134
+ ({ projectDir, target }) => statusProjectSkills({ projectDir, target }),
135
+ ).pipe(CliCommand.withDescription("Compare tracked skills with their approved upstream copies."));
116
136
 
117
- const applyCommand = CliCommand.make(
118
- "apply",
137
+ const projectSkillsUpdateCommand = CliCommand.make(
138
+ "update",
119
139
  {
120
- locked: Flag.boolean("locked"),
121
- lockfile: Flag.string("lockfile").pipe(Flag.withDefault("dev-kit.lock.json")),
122
- manifest: Flag.string("manifest").pipe(Flag.withDefault(DEFAULT_MANIFEST)),
123
- projectDir: Flag.string("project-dir").pipe(Flag.withDefault(".")),
140
+ skills: Argument.string("skills").pipe(Argument.variadic()),
141
+ acceptLocal: Flag.boolean("accept-local").pipe(
142
+ Flag.withDescription(
143
+ "After an agent merge, keep local content and accept the latest upstream base.",
144
+ ),
145
+ ),
146
+ dryRun: Flag.boolean("dry-run").pipe(Flag.withDescription("Preview without writing.")),
147
+ ...repositorySkillFlags,
124
148
  },
125
- ({ locked, lockfile, manifest, projectDir }) =>
126
- runProjectSkillPlan({
127
- locked,
128
- lockfilePath: lockfile,
129
- manifestPath: manifest,
130
- projectDir,
131
- }),
149
+ ({ skills, acceptLocal, dryRun, projectDir, target }) =>
150
+ updateProjectSkills(skills, { acceptLocal, dryRun, projectDir, target }),
132
151
  ).pipe(
133
- CliCommand.withDescription("Apply ownership-safe project skill changes and update the lock."),
152
+ CliCommand.withDescription(
153
+ "Fast-forward unmodified tracked skills; preserve locally edited copies for an agent merge.",
154
+ ),
134
155
  );
135
156
 
136
- const syncCommand = CliCommand.make(
137
- "sync",
138
- {
139
- locked: Flag.boolean("locked"),
140
- lockfile: Flag.string("lockfile").pipe(Flag.withDefault("dev-kit.lock.json")),
141
- ...projectFlags,
142
- },
143
- ({ locked, lockfile, manifest, projectDir }) =>
144
- runProjectSkillPlan({ locked, lockfilePath: lockfile, manifestPath: manifest, projectDir }),
145
- ).pipe(CliCommand.withDescription("Install the skills selected in dev-kit.jsonc."));
157
+ const projectSkillsDiffCommand = CliCommand.make(
158
+ "diff",
159
+ { skill: Argument.string("skill"), ...repositorySkillFlags },
160
+ ({ skill, projectDir, target }) => diffProjectSkill(skill, { projectDir, target }),
161
+ ).pipe(CliCommand.withDescription("Diff a tracked skill against its approved upstream copy."));
146
162
 
147
- const statusCommand = CliCommand.make(
148
- "status",
163
+ const projectSkillsDetachCommand = CliCommand.make(
164
+ "detach",
149
165
  {
150
- lockfile: Flag.string("lockfile").pipe(Flag.withDefault("dev-kit.lock.json")),
151
- ...projectFlags,
166
+ skills: Argument.string("skills").pipe(Argument.variadic({ min: 1 })),
167
+ dryRun: Flag.boolean("dry-run").pipe(Flag.withDescription("Preview without writing.")),
168
+ ...repositorySkillFlags,
152
169
  },
153
- ({ lockfile, manifest, projectDir }) =>
154
- runProjectSkillPlan({
155
- dryRun: true,
156
- lockfilePath: lockfile,
157
- manifestPath: manifest,
158
- projectDir,
159
- }),
160
- ).pipe(CliCommand.withDescription("Check whether selected skills match the project."));
170
+ ({ skills, dryRun, projectDir, target }) =>
171
+ detachProjectSkills(skills, { dryRun, projectDir, target }),
172
+ ).pipe(CliCommand.withDescription("Remove upstream receipts while leaving skill content intact."));
173
+
174
+ const projectSkillsCommand = CliCommand.make("skills").pipe(
175
+ CliCommand.withDescription("Copy and optionally refresh repo-owned agent skills."),
176
+ CliCommand.withSubcommands([
177
+ projectSkillsAddCommand,
178
+ projectSkillsListCommand,
179
+ projectSkillsSearchCommand,
180
+ projectSkillsInfoCommand,
181
+ projectSkillsStatusCommand,
182
+ projectSkillsUpdateCommand,
183
+ projectSkillsDiffCommand,
184
+ projectSkillsDetachCommand,
185
+ ] as const),
186
+ );
161
187
 
162
188
  const gitignoreCommand = CliCommand.make(
163
189
  "gitignore",
@@ -283,8 +309,8 @@ const catalogAddCommand = CliCommand.make(
283
309
  lockfile,
284
310
  repoDir,
285
311
  sources,
286
- }) =>
287
- addCatalogSource({
312
+ }) => {
313
+ const options = {
288
314
  repository,
289
315
  all,
290
316
  dryRun,
@@ -293,11 +319,15 @@ const catalogAddCommand = CliCommand.make(
293
319
  lockfilePath: lockfile,
294
320
  repoDir,
295
321
  sourcesPath: sources,
296
- ...(id ? { id } : {}),
297
- ...(license ? { licensePath: license } : {}),
298
- ...(ref ? { ref } : {}),
299
- ...(skillsPath ? { skillsPath } : {}),
300
- }),
322
+ };
323
+
324
+ if (id) Object.assign(options, { id });
325
+ if (license) Object.assign(options, { licensePath: license });
326
+ if (ref) Object.assign(options, { ref });
327
+ if (skillsPath) Object.assign(options, { skillsPath });
328
+
329
+ return addCatalogSource(options);
330
+ },
301
331
  ).pipe(CliCommand.withDescription("Inspect a repository and approve selected skills."));
302
332
 
303
333
  const catalogListCommand = CliCommand.make(
@@ -383,27 +413,16 @@ const catalogCommand = CliCommand.make("catalog").pipe(
383
413
  ] as const),
384
414
  );
385
415
 
386
- const command = CliCommand.make("dev-kit", projectFlags, ({ manifest, projectDir }) =>
387
- showDashboard({ manifestPath: manifest, projectDir }),
388
- ).pipe(
389
- CliCommand.withDescription("Your approved skill catalog for coding agents."),
416
+ const command = CliCommand.make("dev-kit", {}, () => showProjectSkillsDashboard()).pipe(
417
+ CliCommand.withDescription("Agent-led repository setup and an approved skill catalog."),
390
418
  CliCommand.withSubcommands([
391
419
  {
392
- group: "Skills",
393
- commands: [initCommand, addCommand, removeCommand, listCommand, searchCommand, infoCommand],
420
+ group: "Repository",
421
+ commands: [setupCommand, projectSkillsCommand, ejectCommand],
394
422
  },
395
- { group: "Project", commands: [statusCommand, syncCommand] },
396
423
  {
397
- group: "Advanced",
398
- commands: [
399
- planCommand,
400
- applyCommand,
401
- gitignoreCommand,
402
- effectCommand,
403
- tsgoCommand,
404
- catalogCommand,
405
- cacheCommand,
406
- ],
424
+ group: "Toolbox",
425
+ commands: [gitignoreCommand, effectCommand, tsgoCommand, catalogCommand, cacheCommand],
407
426
  },
408
427
  ] as const),
409
428
  );
@@ -122,11 +122,15 @@ const selectSkills = Effect.fn("selectCatalogSkills")(function* (
122
122
  }
123
123
  const selected = yield* Prompt.multiSelect({
124
124
  message: `Approve skills from ${inspection.id}`,
125
- choices: inspection.skills.map((skill) => ({
126
- title: skill.name,
127
- value: skill.name,
128
- ...(skill.description ? { description: compactDescription(skill.description) } : {}),
129
- })),
125
+ choices: inspection.skills.map((skill) => {
126
+ const choice = { title: skill.name, value: skill.name };
127
+
128
+ if (skill.description) {
129
+ Object.assign(choice, { description: compactDescription(skill.description) });
130
+ }
131
+
132
+ return choice;
133
+ }),
130
134
  min: 1,
131
135
  });
132
136
 
@@ -196,13 +200,16 @@ export const addCatalogSource = Effect.fn("addCatalogSource")(function* (
196
200
  return yield* CatalogManagerError.make({ message: "use either --all or --skill, not both" });
197
201
  }
198
202
  const state = yield* readState(options);
199
- const inspection = yield* inspectCatalogRepository({
203
+ const inspectOptions = {
200
204
  repository: options.repository,
201
205
  repoDir: state.repoDir,
202
- ...(options.id ? { id: options.id } : {}),
203
- ...(options.ref ? { ref: options.ref } : {}),
204
- ...(options.skillsPath ? { skillsPath: options.skillsPath } : {}),
205
- });
206
+ };
207
+
208
+ if (options.id) Object.assign(inspectOptions, { id: options.id });
209
+ if (options.ref) Object.assign(inspectOptions, { ref: options.ref });
210
+ if (options.skillsPath) Object.assign(inspectOptions, { skillsPath: options.skillsPath });
211
+
212
+ const inspection = yield* inspectCatalogRepository(inspectOptions);
206
213
  const selection = yield* selectSkills(inspection, options.skills ?? [], options.all ?? false);
207
214
  const sources = state.sources.value.sources;
208
215
  const byId = sources.findIndex((source) => source.id === inspection.id);
@@ -256,15 +263,13 @@ export const addCatalogSource = Effect.fn("addCatalogSource")(function* (
256
263
  ref: inspection.ref,
257
264
  skillsPath: inspection.skillsPath,
258
265
  include: selection.include,
259
- ...(options.licensePath
260
- ? { licensePath: options.licensePath }
261
- : inspection.licensePath
262
- ? { licensePath: inspection.licensePath }
263
- : {}),
264
- ...(options.stripFrontmatter?.length
265
- ? { stripFrontmatter: [...new Set(options.stripFrontmatter)] }
266
- : {}),
267
266
  };
267
+ const licensePath = options.licensePath || inspection.licensePath;
268
+
269
+ if (licensePath) Object.assign(source, { licensePath });
270
+ if (options.stripFrontmatter?.length) {
271
+ Object.assign(source, { stripFrontmatter: [...new Set(options.stripFrontmatter)] });
272
+ }
268
273
 
269
274
  next = applyEdits(
270
275
  next,
package/src/catalog.ts CHANGED
@@ -185,7 +185,7 @@ export const loadSkillCatalog = Effect.fn("loadSkillCatalog")(function* (
185
185
  message: `duplicate catalog family: ${duplicateFamily[0]}`,
186
186
  });
187
187
  }
188
- const families: Readonly<Record<string, ReadonlyArray<string>>> = {
188
+ const families = {
189
189
  effect: [
190
190
  "effect-ts",
191
191
  "effect-architecture-audit",
@@ -194,13 +194,16 @@ export const loadSkillCatalog = Effect.fn("loadSkillCatalog")(function* (
194
194
  "build-effect-clis",
195
195
  ],
196
196
  ...Object.fromEntries(externalFamilies),
197
- };
197
+ } satisfies Readonly<Record<string, ReadonlyArray<string>>>;
198
198
 
199
- return {
199
+ const catalog: SkillCatalog = {
200
200
  skills: skills.sort((left, right) => left.selector.localeCompare(right.selector)),
201
201
  families,
202
- ...(lock ? { lock } : {}),
203
- } satisfies SkillCatalog;
202
+ };
203
+
204
+ if (lock) Object.assign(catalog, { lock });
205
+
206
+ return catalog;
204
207
  });
205
208
 
206
209
  const stripFrontmatterKeys = (text: string, keys: ReadonlyArray<string>): string => {
package/src/cli-ui.ts CHANGED
@@ -12,12 +12,12 @@ const ANSI = {
12
12
 
13
13
  type StatusKind = "success" | "info" | "plan" | "error";
14
14
 
15
- const statusAppearance: Readonly<Record<StatusKind, readonly [string, string]>> = {
15
+ const statusAppearance = {
16
16
  success: ["✓", ANSI.green],
17
17
  info: ["•", ANSI.cyan],
18
18
  plan: ["→", ANSI.yellow],
19
19
  error: ["✗", ANSI.red],
20
- };
20
+ } satisfies Readonly<Record<StatusKind, readonly [string, string]>>;
21
21
 
22
22
  const terminalCapabilities = Effect.fn("terminalCapabilities")(function* () {
23
23
  const terminal = yield* Terminal.Terminal;
@@ -5,7 +5,7 @@ import { printStatus, withSpinner } from "./cli-ui.ts";
5
5
  import { acquireProjectProcessLock } from "./project-process-lock.ts";
6
6
  import { isTypeScriptPackageName } from "./typescript-package-name.ts";
7
7
 
8
- export const EFFECT_TSGO_VERSION = "0.33.0";
8
+ export const EFFECT_TSGO_VERSION = "0.36.4";
9
9
  export const EFFECT_TSGO_TYPESCRIPT_VERSION = "7.0.2";
10
10
  export const EFFECT_TSGO_PLUGIN_NAME = "@effect/language-service";
11
11
 
@@ -262,11 +262,15 @@ export const planEffectTsgoPatch = Effect.fn("planEffectTsgoPatch")(function* (
262
262
  const typescriptPackageArgs =
263
263
  typescriptPackage === "typescript" ? [] : ["--typescript-package", typescriptPackage];
264
264
 
265
- return {
265
+ const plan = {
266
266
  alreadyPatched: patchState.alreadyPatched,
267
- ...(patchState.hasBackup && !patchState.alreadyPatched
268
- ? { unpatchArgs: ["unpatch", "--typescript", ...typescriptPackageArgs] }
269
- : {}),
267
+ };
268
+
269
+ if (patchState.hasBackup && !patchState.alreadyPatched) {
270
+ Object.assign(plan, { unpatchArgs: ["unpatch", "--typescript", ...typescriptPackageArgs] });
271
+ }
272
+
273
+ return Object.assign(plan, {
270
274
  projectDir,
271
275
  executable,
272
276
  args: [
@@ -278,7 +282,7 @@ export const planEffectTsgoPatch = Effect.fn("planEffectTsgoPatch")(function* (
278
282
  effectTsgoVersion,
279
283
  typescriptPackage,
280
284
  typescriptVersion,
281
- } satisfies EffectTsgoPatchPlan;
285
+ }) satisfies EffectTsgoPatchPlan;
282
286
  });
283
287
 
284
288
  const runEffectTsgoCommand = Effect.fn("runEffectTsgoCommand")(function* (