@danieljvdm/dev-kit 0.9.0 → 0.11.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 +40 -15
- package/bin/dev-kit.mjs +16 -2
- package/package.json +5 -5
- package/schema/dev-kit.schema.json +1 -1
- package/skill-sources.jsonc +30 -0
- package/skill-sources.lock.json +100 -0
- package/skills/dev-kit/SKILL.md +36 -11
- package/src/bin/dev-kit.ts +3 -3
- package/src/project-package.ts +1 -0
- package/src/sync.ts +473 -41
- package/src/vite-plus-quality.ts +2 -1
- package/templates/AGENTS.md +2 -0
- package/templates/vite-plus/github-actions-check.yml +5 -1
package/README.md
CHANGED
|
@@ -20,6 +20,9 @@ Install the published Dev Kit package:
|
|
|
20
20
|
bun add -d @danieljvdm/dev-kit
|
|
21
21
|
```
|
|
22
22
|
|
|
23
|
+
Dev Kit requires Bun 1.3 or newer. Its published executable runs TypeScript
|
|
24
|
+
natively with Bun and rejects direct Node.js execution.
|
|
25
|
+
|
|
23
26
|
Initialize the project, browse available built-in, approved Git, and installed
|
|
24
27
|
package skills, then add the ones you want:
|
|
25
28
|
|
|
@@ -94,12 +97,14 @@ before running locked mode:
|
|
|
94
97
|
|
|
95
98
|
```bash
|
|
96
99
|
bun install --ignore-scripts
|
|
97
|
-
bun
|
|
100
|
+
bun ./node_modules/@danieljvdm/dev-kit/bin/dev-kit.mjs apply --locked
|
|
98
101
|
```
|
|
99
102
|
|
|
100
103
|
Or allow the normal postinstall and fail CI when it leaves tracked changes.
|
|
101
104
|
Do not run an unlocked apply before a locked verification because that would
|
|
102
105
|
regenerate the drift being checked.
|
|
106
|
+
The package-qualified path cannot be shadowed by a consumer script named
|
|
107
|
+
`dev-kit`.
|
|
103
108
|
|
|
104
109
|
This repository dogfoods the same flow with its committed `dev-kit.jsonc` and
|
|
105
110
|
`dev-kit.lock.json`. From this source checkout, invoke the local CLI with:
|
|
@@ -211,7 +216,7 @@ The patch is idempotent, preserves existing lines, and refuses symlinked
|
|
|
211
216
|
|
|
212
217
|
## Agent instructions
|
|
213
218
|
|
|
214
|
-
Enable
|
|
219
|
+
Enable managed project-root instruction sections and a portable Claude Code
|
|
215
220
|
bridge in the manifest:
|
|
216
221
|
|
|
217
222
|
```jsonc
|
|
@@ -224,18 +229,37 @@ bridge in the manifest:
|
|
|
224
229
|
}
|
|
225
230
|
```
|
|
226
231
|
|
|
227
|
-
`setup.agentInstructions` manages
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
+
`setup.agentInstructions` manages marked sections in the project-root
|
|
233
|
+
`AGENTS.md`, preserving handwritten project guidance around them. The Dev Kit
|
|
234
|
+
section contains a short description and a pointer to the installed `dev-kit`
|
|
235
|
+
skill. When the root `package.json` declares `vite-plus` directly, Dev Kit
|
|
236
|
+
renders its own repository-specific Vite+ guidance, including the unified
|
|
237
|
+
toolchain overview, help and documentation entry points, and `vp env doctor`
|
|
238
|
+
troubleshooting. It does not import Vite+'s generic `AGENTS.md`, which can
|
|
239
|
+
conflict with the repository's exact commands; transitive installations do not
|
|
240
|
+
opt a project in. Previously managed Vite+ sections are removed during a safe
|
|
241
|
+
owned update. Ambiguous or malformed managed markers fail closed.
|
|
242
|
+
|
|
243
|
+
The Dev Kit section also renders an opinionated project command policy. A
|
|
244
|
+
direct Vite+ dependency makes `vp` the only supported front door: built-in
|
|
245
|
+
format, lint, and test commands use `vp`, while repository tasks and package
|
|
246
|
+
scripts use `vp run`. When Dev Kit manages the quality config, the canonical
|
|
247
|
+
full validation and typecheck commands are `vp run check` and
|
|
248
|
+
`vp run typecheck`; `vp check` alone is only the Vite+ static-check command.
|
|
249
|
+
Without Vite+, Bun is the required package-script runner and Dev Kit lists only
|
|
250
|
+
quality scripts the root package actually declares. The package manager named
|
|
251
|
+
by `package.json#packageManager`, or inferred from a single recognized root
|
|
252
|
+
lockfile, is used only for dependency-install guidance. The policy forbids
|
|
253
|
+
switching script runners or bypassing project entry points with raw `tsc`,
|
|
254
|
+
test-runner, linter, or formatter commands.
|
|
232
255
|
|
|
233
256
|
`setup.claudeInstructions` manages `CLAUDE.md` as the relative symlink
|
|
234
|
-
`CLAUDE.md → AGENTS.md`. It can link to the
|
|
257
|
+
`CLAUDE.md → AGENTS.md`. It can link to the section-managed file in the same apply,
|
|
235
258
|
or retain the older behavior of linking to an existing regular `AGENTS.md` when
|
|
236
|
-
the
|
|
237
|
-
lockfile and local ownership state.
|
|
238
|
-
and
|
|
259
|
+
the section task is disabled. Both outputs are recorded independently in the
|
|
260
|
+
lockfile and local ownership state. Disabling agent instructions removes only
|
|
261
|
+
unchanged managed sections and deletes `AGENTS.md` only when no handwritten
|
|
262
|
+
content remains.
|
|
239
263
|
|
|
240
264
|
## Vite+ Git hooks
|
|
241
265
|
|
|
@@ -351,10 +375,11 @@ scripts, or TypeScript topology. It performs one frozen, script-suppressed
|
|
|
351
375
|
install, runs `dev-kit apply --locked`, and only then runs custom preparation,
|
|
352
376
|
formatting, linting, tests, and typechecking. Its default typecheck command is
|
|
353
377
|
`vp run typecheck`; `workflow.typecheck` replaces it. Vite+ maps install flags
|
|
354
|
-
to the detected package manager. The template
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
378
|
+
to the detected package manager. The template installs the consumer's declared
|
|
379
|
+
Bun version and pins exact `setup-bun` and `setup-vp` releases; the latter's
|
|
380
|
+
`v1` tag is frozen. Keep both current with Renovate or Dependabot. Existing
|
|
381
|
+
workflows remain user-owned until their rendered content matches exactly—Dev
|
|
382
|
+
Kit never merges YAML. See the primary
|
|
358
383
|
[`setup-vp` versioning guidance](https://github.com/voidzero-dev/setup-vp#versioning),
|
|
359
384
|
[Vite+ install guide](https://viteplus.dev/guide/install), and
|
|
360
385
|
[Vite Task run guide](https://viteplus.dev/guide/run) when maintaining the
|
package/bin/dev-kit.mjs
CHANGED
|
@@ -1,3 +1,17 @@
|
|
|
1
|
-
#!/usr/bin/env
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
2
|
|
|
3
|
-
import "
|
|
3
|
+
import semver from "semver";
|
|
4
|
+
|
|
5
|
+
const MINIMUM_BUN_VERSION = "1.3.0";
|
|
6
|
+
const bunVersion = globalThis.Bun?.version;
|
|
7
|
+
|
|
8
|
+
if (bunVersion === undefined) {
|
|
9
|
+
throw new Error(
|
|
10
|
+
`Dev Kit requires the Bun runtime, version ${MINIMUM_BUN_VERSION} or newer. Install Bun and run \`dev-kit\` again.`,
|
|
11
|
+
);
|
|
12
|
+
}
|
|
13
|
+
if (!semver.satisfies(bunVersion, `>=${MINIMUM_BUN_VERSION}`)) {
|
|
14
|
+
throw new Error(`Dev Kit requires Bun ${MINIMUM_BUN_VERSION} or newer; found ${bunVersion}.`);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
await import("../src/bin/dev-kit.ts");
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@danieljvdm/dev-kit",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.11.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Declarative project development toolkit with portable agent skills.",
|
|
6
6
|
"license": "MIT",
|
|
@@ -65,15 +65,15 @@
|
|
|
65
65
|
"catalog:check": "./bin/dev-kit.mjs catalog verify"
|
|
66
66
|
},
|
|
67
67
|
"dependencies": {
|
|
68
|
-
"@effect/platform-
|
|
68
|
+
"@effect/platform-bun": "4.0.0-beta.102",
|
|
69
69
|
"@stylistic/eslint-plugin": "5.10.0",
|
|
70
70
|
"effect": "4.0.0-beta.102",
|
|
71
71
|
"jsonc-parser": "3.3.1",
|
|
72
|
-
"semver": "7.8.5"
|
|
73
|
-
"tsx": "4.22.4"
|
|
72
|
+
"semver": "7.8.5"
|
|
74
73
|
},
|
|
75
74
|
"devDependencies": {
|
|
76
75
|
"@changesets/cli": "3.0.0-next.10",
|
|
76
|
+
"@effect/platform-node": "4.0.0-beta.102",
|
|
77
77
|
"@effect/tsgo": "0.24.3",
|
|
78
78
|
"@effect/vitest": "4.0.0-beta.102",
|
|
79
79
|
"@types/node": "25.9.1",
|
|
@@ -101,7 +101,7 @@
|
|
|
101
101
|
}
|
|
102
102
|
},
|
|
103
103
|
"engines": {
|
|
104
|
-
"
|
|
104
|
+
"bun": ">=1.3.0"
|
|
105
105
|
},
|
|
106
106
|
"packageManager": "bun@1.3.14"
|
|
107
107
|
}
|
|
@@ -68,7 +68,7 @@
|
|
|
68
68
|
"required": ["include"],
|
|
69
69
|
"$defs": {
|
|
70
70
|
"agentInstructionsSetup": {
|
|
71
|
-
"description": "Manage
|
|
71
|
+
"description": "Manage marked sections in project-root AGENTS.md with dev-kit guidance and conditional tool instructions while preserving handwritten content.",
|
|
72
72
|
"type": "object",
|
|
73
73
|
"additionalProperties": false,
|
|
74
74
|
"properties": {
|
package/skill-sources.jsonc
CHANGED
|
@@ -55,5 +55,35 @@
|
|
|
55
55
|
"include": ["tdd", "improve-codebase-architecture"],
|
|
56
56
|
"licensePath": "LICENSE",
|
|
57
57
|
},
|
|
58
|
+
{
|
|
59
|
+
"id": "expo-skills",
|
|
60
|
+
"repository": "https://github.com/expo/skills.git",
|
|
61
|
+
"ref": "main",
|
|
62
|
+
"skillsPath": "plugins/expo/skills",
|
|
63
|
+
"include": [
|
|
64
|
+
"eas-app-stores",
|
|
65
|
+
"eas-hosting",
|
|
66
|
+
"eas-observe",
|
|
67
|
+
"eas-simulator",
|
|
68
|
+
"eas-update-insights",
|
|
69
|
+
"eas-workflows",
|
|
70
|
+
"expo-app-clip",
|
|
71
|
+
"expo-brownfield",
|
|
72
|
+
"expo-data-fetching",
|
|
73
|
+
"expo-dev-client",
|
|
74
|
+
"expo-dom",
|
|
75
|
+
"expo-examples",
|
|
76
|
+
"expo-module",
|
|
77
|
+
"expo-native-ui",
|
|
78
|
+
"expo-project-structure",
|
|
79
|
+
"expo-router",
|
|
80
|
+
"expo-skill-feedback",
|
|
81
|
+
"expo-tailwind-setup",
|
|
82
|
+
"expo-ui",
|
|
83
|
+
"expo-upgrade",
|
|
84
|
+
"expo-web-to-native",
|
|
85
|
+
],
|
|
86
|
+
"licensePath": "LICENSE",
|
|
87
|
+
},
|
|
58
88
|
],
|
|
59
89
|
}
|
package/skill-sources.lock.json
CHANGED
|
@@ -143,6 +143,106 @@
|
|
|
143
143
|
"tdd": "sha256:ea2d26eda005b10a2de4bdb7de44d33072f2a8e82dbf670952ce30a9c2d7c475"
|
|
144
144
|
},
|
|
145
145
|
"licensePath": "LICENSE"
|
|
146
|
+
},
|
|
147
|
+
{
|
|
148
|
+
"id": "expo-skills",
|
|
149
|
+
"repository": "https://github.com/expo/skills.git",
|
|
150
|
+
"ref": "main",
|
|
151
|
+
"resolved": "994342f0e343f05b9b8cdd38a0e91356ceef00f6",
|
|
152
|
+
"skillsPath": "plugins/expo/skills",
|
|
153
|
+
"include": [
|
|
154
|
+
"eas-app-stores",
|
|
155
|
+
"eas-hosting",
|
|
156
|
+
"eas-observe",
|
|
157
|
+
"eas-simulator",
|
|
158
|
+
"eas-update-insights",
|
|
159
|
+
"eas-workflows",
|
|
160
|
+
"expo-app-clip",
|
|
161
|
+
"expo-brownfield",
|
|
162
|
+
"expo-data-fetching",
|
|
163
|
+
"expo-dev-client",
|
|
164
|
+
"expo-dom",
|
|
165
|
+
"expo-examples",
|
|
166
|
+
"expo-module",
|
|
167
|
+
"expo-native-ui",
|
|
168
|
+
"expo-project-structure",
|
|
169
|
+
"expo-router",
|
|
170
|
+
"expo-skill-feedback",
|
|
171
|
+
"expo-tailwind-setup",
|
|
172
|
+
"expo-ui",
|
|
173
|
+
"expo-upgrade",
|
|
174
|
+
"expo-web-to-native"
|
|
175
|
+
],
|
|
176
|
+
"skills": [
|
|
177
|
+
"eas-app-stores",
|
|
178
|
+
"eas-hosting",
|
|
179
|
+
"eas-observe",
|
|
180
|
+
"eas-simulator",
|
|
181
|
+
"eas-update-insights",
|
|
182
|
+
"eas-workflows",
|
|
183
|
+
"expo-app-clip",
|
|
184
|
+
"expo-brownfield",
|
|
185
|
+
"expo-data-fetching",
|
|
186
|
+
"expo-dev-client",
|
|
187
|
+
"expo-dom",
|
|
188
|
+
"expo-examples",
|
|
189
|
+
"expo-module",
|
|
190
|
+
"expo-native-ui",
|
|
191
|
+
"expo-project-structure",
|
|
192
|
+
"expo-router",
|
|
193
|
+
"expo-skill-feedback",
|
|
194
|
+
"expo-tailwind-setup",
|
|
195
|
+
"expo-ui",
|
|
196
|
+
"expo-upgrade",
|
|
197
|
+
"expo-web-to-native"
|
|
198
|
+
],
|
|
199
|
+
"descriptions": {
|
|
200
|
+
"eas-app-stores": "EAS service (paid). Deploy Expo apps to the app stores with EAS - build and submit to the iOS App Store, Google Play Store, and TestFlight, configure eas.json build and submit profiles, manage app versions and build numbers, and publish App Store metadata and ASO. Use whenever the user wants to deploy, release, or ship an app to production or the app stores, is preparing a production build, running eas build or eas submit, shipping to TestFlight, bumping version or build numbers, or setting up store listing metadata. For deploying an Expo website or API routes, use the eas-hosting skill.",
|
|
201
|
+
"eas-hosting": "EAS service (paid). Deploy Expo websites and Expo Router API routes to EAS Hosting - export the web bundle, run eas deploy for production and PR preview URLs, manage environment secrets and custom domains, and work within the Cloudflare Workers runtime. Also covers authoring API routes (+api.ts handlers, HTTP methods, request handling, CORS). Use when deploying an Expo web app or API routes, setting up EAS Hosting, or configuring hosting environments and domains. Not for native builds or store releases - use the eas-app-stores skill for those.",
|
|
202
|
+
"eas-observe": "EAS service (paid). Use for anything related to EAS Observe - adding `expo-observe` to an Expo project (AppMetricsRoot/ObserveRoot HOC, markInteractive, the useObserve hook, the Expo Router / React Navigation integrations for per-route metrics, and user-defined events via `Observe.logEvent`), querying via the EAS CLI (`eas observe:metrics-summary`, `observe:metrics`, `observe:routes`, `observe:events`, `observe:versions`), or interpreting the resulting metrics (cold/warm launch, TTR, TTI, navigation cold/warm TTR, update download, and the TTI frameRate params for triaging slow startups).",
|
|
203
|
+
"eas-simulator": "EAS service (paid). Run and control a user's app on a remote iOS/Android simulator hosted on EAS cloud. Read before running any `eas simulator:*` commands - it has the current syntax for this experimental API. Use whenever the user needs a simulator they can't run locally - 'run my app on a cloud simulator', 'use eas simulator to run/install/screenshot my app', 'I'm on Linux/Cursor and need an iOS device', 'no sim on this box / headless CI', 'let an agent click through my app and screenshot it', 'test my dev build on a remote sim with live reload', 'stream a sim to my browser' - even when they don't say 'EAS Simulator' or 'cloud'. On a host WITHOUT a local simulator (Linux, CI, cloud sandbox) it's the default; on macOS, do NOT auto-trigger for a plain 'run on the simulator' - use it only for a cloud/remote/shareable sim, an iOS version they lack, or an agent-driven session. NOT for local sims (expo run:ios, Xcode, Android Studio), EAS Build/Update, web preview, or physical devices.",
|
|
204
|
+
"eas-update-insights": "EAS service (paid). Check the health of published EAS Update: crash rates, install/launch counts, unique users, payload size, and the split between embedded and OTA users per channel. Use when the user asks how an update is performing, whether a rollout is healthy, how many users are on the embedded build vs OTA, or wants to gate CI on update health.",
|
|
205
|
+
"eas-workflows": "EAS service (paid). Helps understand and write EAS workflow YAML files for Expo projects. Use this skill when the user asks about CI/CD or workflows in an Expo or EAS context, mentions .eas/workflows/, or wants help with EAS build pipelines or deployment automation.",
|
|
206
|
+
"expo-app-clip": "Framework (OSS). Add an iOS App Clip target to an Expo app. Use when the user mentions App Clip, AASA, apple-app-site-association, appclips, smart app banner, or wants to ship a lightweight iOS Clip invoked from a URL alongside their parent app.",
|
|
207
|
+
"expo-brownfield": "Framework (OSS). Integrate Expo and React Native into an existing native iOS or Android app. Use when the user mentions brownfield, embedding React Native in a native app, AAR/XCFramework, or adding Expo to an existing Kotlin/Swift project. Covers both the isolated approach and the integrated approach.",
|
|
208
|
+
"expo-data-fetching": "Framework (OSS). Use when implementing or debugging ANY network request, API call, or data fetching. Covers fetch API, React Query, SWR, error handling, caching, offline support, and Expo Router data loaders (`useLoaderData`).",
|
|
209
|
+
"expo-dev-client": "Framework (OSS). Build and distribute Expo development clients locally or via TestFlight for internal testing. For production TestFlight releases and store submission, use the eas-app-stores skill.",
|
|
210
|
+
"expo-dom": "Framework (OSS). Use Expo DOM components to run web code in a webview on native and as-is on web. Migrate web code to native incrementally. For the end-to-end migration of a whole web app, use the expo-web-to-native skill.",
|
|
211
|
+
"expo-examples": "Framework (OSS). Expo's official example projects - the expo/examples repo of ~70 `with-*` integrations (Stripe, Clerk, Supabase, OpenAI, maps, Reanimated, SQLite, Skia, NativeWind, and more). Use when integrating a third-party library or service into an existing Expo app and you want the canonical, version-matched pattern to adapt, or when scaffolding a new project from one with `npx create-expo --example`.",
|
|
212
|
+
"expo-module": "Framework (OSS). Guide for creating and writing Expo native modules and views using the Expo Modules API (Swift, Kotlin, TypeScript). Covers module definition DSL, native views, shared objects, config plugins, lifecycle hooks, autolinking, and type system. Use when building or modifying native modules for Expo. Not for migrating an existing Swift module from the definition DSL to the Expo Modules API 2.0 macros; use expo-migrate-module (from the expo-experiments plugin) for that.",
|
|
213
|
+
"expo-native-ui": "Framework (OSS). Build beautiful, native-feeling Expo screens. Covers Apple HIG styling, semantic colors, native controls, SF Symbols, media, animations, visual effects, gradients, storage, and responsive layout. For routing and navigation, use the expo-router skill.",
|
|
214
|
+
"expo-project-structure": "Framework (OSS). Folder structure for a new Expo app. Use when scaffolding or laying out a new Expo project with Expo Router, or deciding where a file should live in one. For new projects only — never restructure an existing app to match.",
|
|
215
|
+
"expo-router": "Framework (OSS). Navigation and routing for Expo Router. Covers file-based routes, groups and dynamic routes, folder organization, Link with previews and context menus, native Stack, page titles, modals and form sheets, NativeTabs, headers and toolbars, and header search bars.",
|
|
216
|
+
"expo-skill-feedback": "Submit feedback on an Expo skill—or Expo itself—and control bundled anonymous usage telemetry (off by default / opt-in). Submit feedback with: npx --yes submit-expo-feedback@latest \"ACTIONABLE_FEEDBACK\". Optionally add either or both: --category \"CATEGORY\" and --subject \"SUBJECT\". Replace the uppercase placeholders before running. Use when a skill was useful, confusing, broken, missing context, or worth improving; when Expo, Expo CLI, EAS CLI, docs, or MCP worked well or fell short; when an AI agent repeatedly failed, got stuck, or needed the user to take over an Expo task (report it as an eval candidate); or when the user explicitly asks to enable or disable telemetry (tracking), check its status, or understand what it collects.",
|
|
217
|
+
"expo-tailwind-setup": "Framework (OSS). Set up Tailwind CSS v4 in Expo with react-native-css and NativeWind v5 for universal styling",
|
|
218
|
+
"expo-ui": "Framework (OSS). Build native UI with the @expo/ui package: real SwiftUI on iOS and Jetpack Compose on Android rendered from React in an Expo or React Native app. Covers universal cross-platform components (Host, Column, Row, Button, Text, List, and more imported from @expo/ui), drop-in replacements for popular React Native community libraries (BottomSheet, DateTimePicker, Slider, Menu, etc.), and platform-specific SwiftUI (@expo/ui/swift-ui, iOS only) and Jetpack Compose (@expo/ui/jetpack-compose, Android only) trees and modifiers. Use when adding or reviewing @expo/ui Host/RNHostView trees, building native-feeling UI where standard React Native components fall short (grouped settings forms with toggles, sections, menus, sheets, pickers, sliders), choosing between universal and platform-specific components, or replacing an RN community UI library with a native @expo/ui equivalent. Not for custom native modules, Expo Router navigation, Reanimated, or data fetching.",
|
|
219
|
+
"expo-upgrade": "Framework (OSS). Guidelines for upgrading Expo SDK versions and fixing dependency issues",
|
|
220
|
+
"expo-web-to-native": "Framework (OSS). Migrate an existing web React app to a native iOS/Android app with Expo. Use when the user wants to turn a website into a mobile app, port a Next.js/Vite/CRA React codebase to React Native, reuse web code on native incrementally, or asks how web idioms (the DOM, CSS, React Router, localStorage, window) map to native. This is the end-to-end migration guide; use the `expo-dom` skill for the DOM-component mechanism itself."
|
|
221
|
+
},
|
|
222
|
+
"digests": {
|
|
223
|
+
"eas-app-stores": "sha256:830332227adfe1421667abe0e3e90c554c85661c5646de4e05a976b276bd2c74",
|
|
224
|
+
"eas-hosting": "sha256:9d2df9eb3045016b45c69d8578b4d1bdfea5d520b22136145a4593a2ebc57586",
|
|
225
|
+
"eas-observe": "sha256:da1785de84561773ab17d36a80d82a0a2e764ed2585a6234a491b6296fb58e86",
|
|
226
|
+
"eas-simulator": "sha256:461021e81e3a6abf8ed4cd119208e62e420d3779a41edec94f84b5d7ce8cd0f6",
|
|
227
|
+
"eas-update-insights": "sha256:5a98762401a49571a68e8d7824c510bf6b54bd3d2e4d097c846964ee4ff25687",
|
|
228
|
+
"eas-workflows": "sha256:27bee5b70ef49ff91cc3fc3473257444af07733248926acd0115752223045eee",
|
|
229
|
+
"expo-app-clip": "sha256:9129826172144ec4e625ae768e79f2acaebb5cf53a8af51fbc32fb45d82c260a",
|
|
230
|
+
"expo-brownfield": "sha256:0fd07f633c868759a062fd0732b3702826bc3ca3f3a6c094cb7ea7a213ad1dfd",
|
|
231
|
+
"expo-data-fetching": "sha256:a556bea72824a147744fc5b784128207cbeb3425082fa741070ce47799631a27",
|
|
232
|
+
"expo-dev-client": "sha256:dd1a5364386a171d367108519a78cf743ecbac6f9f66f030910e0048e5650788",
|
|
233
|
+
"expo-dom": "sha256:46af40ce11aaa7e94b82fc2c3c38198241b87dbc31c925152da53bbd2a063d4e",
|
|
234
|
+
"expo-examples": "sha256:608f12b9e6c52c6968ae6d655f51d83384194f6a221ea02a0bf812ed23438a2f",
|
|
235
|
+
"expo-module": "sha256:8133a3f27a7b99d60961dc31dd27b9f0ea39b0bbac753be8a17ba8f554f7ef98",
|
|
236
|
+
"expo-native-ui": "sha256:87516edae70305f9e7231599533b2ae27c12a55bb5f0b7205b0d64ababa0c967",
|
|
237
|
+
"expo-project-structure": "sha256:28fafa0754bcd9686ead5dcbc6a8abf8e00325ee756b794989bfb8e11f6ae941",
|
|
238
|
+
"expo-router": "sha256:3d07d7c13c906665725fc235b6fa2cb28bbd8ad7184ec9eba3d2e2ae87baa3c2",
|
|
239
|
+
"expo-skill-feedback": "sha256:6c636f8bcec43c2ad1ed1bf11c90fc32d75b6b15dfe79f23d44387fa45e20307",
|
|
240
|
+
"expo-tailwind-setup": "sha256:de3cc17cc9e11f1989659a61a8fddb98a62b356c58d77c7ec5318b03789e896a",
|
|
241
|
+
"expo-ui": "sha256:b6773a56b0f9bf4cc3dc9cb1858ab50bdfcc3dc9b2d4e500990ab5d24e28d1ca",
|
|
242
|
+
"expo-upgrade": "sha256:6c65f94c60f8127a36ca2d9402b610756314388de42200a2a120bbbea77f131b",
|
|
243
|
+
"expo-web-to-native": "sha256:360359742d72d0a8ed9926d53f19bae7603fb788f6e897a14208bbb35c2ed618"
|
|
244
|
+
},
|
|
245
|
+
"licensePath": "LICENSE"
|
|
146
246
|
}
|
|
147
247
|
]
|
|
148
248
|
}
|
package/skills/dev-kit/SKILL.md
CHANGED
|
@@ -95,14 +95,30 @@ use symlinks for additional harness discovery paths. Keep every target path
|
|
|
95
95
|
project-relative and separate from the manifest, lock, state, and process-lock
|
|
96
96
|
paths.
|
|
97
97
|
|
|
98
|
-
Enable `setup.agentInstructions` to manage a project-root
|
|
99
|
-
|
|
100
|
-
|
|
98
|
+
Enable `setup.agentInstructions` to manage marked sections in a project-root
|
|
99
|
+
`AGENTS.md` while preserving handwritten project guidance. The Dev Kit section
|
|
100
|
+
points agents back to this skill. When `vite-plus` is a declared direct
|
|
101
|
+
dependency, dev-kit synthesizes repository-specific Vite+ guidance inside its
|
|
102
|
+
own section instead of importing Vite+'s generic `AGENTS.md`. Preserve the
|
|
103
|
+
useful unified-toolchain overview, help and documentation entry points, and
|
|
104
|
+
`vp env doctor` troubleshooting without duplicating generic commands that can
|
|
105
|
+
contradict the repository policy. Treat duplicate, overlapping, reversed, or
|
|
106
|
+
unmatched managed markers as a conflict rather than guessing which content Dev
|
|
107
|
+
Kit owns; remove a legacy owned Vite+ section during migration.
|
|
108
|
+
The managed section also publishes the repository's command authority. Direct
|
|
109
|
+
Vite+ projects must use `vp` built-ins and `vp run <task>`; managed quality
|
|
110
|
+
projects use `vp run check` for the complete format/lint/test/typecheck suite
|
|
111
|
+
and `vp run typecheck` for the Effect-patched compiler. Non-Vite+ projects run
|
|
112
|
+
existing root quality scripts through `bun run`; package-manager metadata and
|
|
113
|
+
lockfiles affect dependency-install guidance only. Never substitute another
|
|
114
|
+
script runner or call raw `tsc`, test, lint, or format binaries when a project
|
|
115
|
+
command exists.
|
|
101
116
|
Enable `setup.claudeInstructions` when Claude Code should consume the same
|
|
102
117
|
project-root instructions; it manages `CLAUDE.md` as a relative symlink to the
|
|
103
|
-
|
|
104
|
-
when disabled, dev-kit removes only unchanged
|
|
105
|
-
ownership state
|
|
118
|
+
section-managed file or to an existing regular `AGENTS.md`. Preserve
|
|
119
|
+
conflicting paths; when disabled, dev-kit removes only unchanged marked
|
|
120
|
+
sections recorded in local ownership state and leaves handwritten content in
|
|
121
|
+
place.
|
|
106
122
|
|
|
107
123
|
Enable `setup.vitePlus.hooks` when an installed direct `vite-plus` dependency
|
|
108
124
|
should manage Git hooks. Each apply checks the local `.vite-hooks/_` dispatcher,
|
|
@@ -129,9 +145,11 @@ builds custom. Workflow-only consumers may configure `workflow.beforeChecks`
|
|
|
129
145
|
and `workflow.typecheck`; treat these commands as trusted manifest input.
|
|
130
146
|
|
|
131
147
|
The workflow must use one frozen, script-suppressed install, then locked Dev Kit
|
|
132
|
-
convergence before preparation or checks.
|
|
133
|
-
|
|
134
|
-
|
|
148
|
+
convergence before preparation or checks. Set up Bun from the consumer's
|
|
149
|
+
`packageManager` or `engines.bun` declaration before Vite+ setup. Keep both
|
|
150
|
+
`setup-bun` and `setup-vp` pinned to reviewed release commits—the `setup-vp`
|
|
151
|
+
`v1` tag is frozen—and let Vite+ resolve the consumer's compatible locked
|
|
152
|
+
version.
|
|
135
153
|
|
|
136
154
|
## Ownership and conflicts
|
|
137
155
|
|
|
@@ -157,6 +175,10 @@ Run `dev-kit gitignore` to add `.repos/` and `.dev-kit/` additively. Preview wit
|
|
|
157
175
|
`dev-kit gitignore --dry-run`. Treat `.repos/<source-id>` as the reserved source
|
|
158
176
|
checkout root.
|
|
159
177
|
|
|
178
|
+
Require Bun 1.3 or newer wherever Dev Kit runs. The published `dev-kit`
|
|
179
|
+
executable uses Bun directly and rejects Node execution; keep Bun available to
|
|
180
|
+
package lifecycle scripts and locked CI verification.
|
|
181
|
+
|
|
160
182
|
For one lifecycle entry point, configure:
|
|
161
183
|
|
|
162
184
|
```jsonc
|
|
@@ -171,7 +193,10 @@ This intentionally refreshes the committed lock and owned outputs when the
|
|
|
171
193
|
package manager installs a new Dev Kit or selected package-skill version.
|
|
172
194
|
Review and commit those changes with the dependency update. Keep
|
|
173
195
|
`dev-kit apply --locked` as a verification command, not the normal local
|
|
174
|
-
lifecycle; in CI, run it only before any unlocked apply.
|
|
196
|
+
lifecycle; in CI, run it only before any unlocked apply. Invoke locked consumer
|
|
197
|
+
verification as
|
|
198
|
+
`bun ./node_modules/@danieljvdm/dev-kit/bin/dev-kit.mjs apply --locked` so a
|
|
199
|
+
package script named `dev-kit` cannot shadow the executable.
|
|
175
200
|
|
|
176
201
|
## Effect source checkout
|
|
177
202
|
|
|
@@ -263,7 +288,7 @@ in the consuming project.
|
|
|
263
288
|
|
|
264
289
|
## Current boundary
|
|
265
290
|
|
|
266
|
-
Manage skill outputs, the `setup.agentInstructions`
|
|
291
|
+
Manage skill outputs, the `setup.agentInstructions` marked sections, the
|
|
267
292
|
`setup.claudeInstructions` link, the `setup.vitePlus.hooks` dispatcher, the
|
|
268
293
|
independently opt-in `setup.vitePlus.quality` config and GitHub workflow, the
|
|
269
294
|
`setup.effectSource` checkout, and the explicit `setup.effectTsgo` task.
|
package/src/bin/dev-kit.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { BunRuntime, BunServices } from "@effect/platform-bun";
|
|
2
2
|
import { Effect, Result } from "effect";
|
|
3
3
|
import { Argument, CliError, Command as CliCommand, Flag } from "effect/unstable/cli";
|
|
4
4
|
|
|
@@ -399,7 +399,7 @@ const program = CliCommand.run(command, { version: DEV_KIT_VERSION }).pipe(
|
|
|
399
399
|
),
|
|
400
400
|
),
|
|
401
401
|
Effect.scoped,
|
|
402
|
-
Effect.provide(
|
|
402
|
+
Effect.provide(BunServices.layer),
|
|
403
403
|
);
|
|
404
404
|
|
|
405
|
-
|
|
405
|
+
BunRuntime.runMain(program, { disableErrorReporting: true });
|
package/src/project-package.ts
CHANGED
|
@@ -8,6 +8,7 @@ export class ProjectPackageError extends Schema.TaggedErrorClass<ProjectPackageE
|
|
|
8
8
|
const ProjectPackageSchema = Schema.fromJsonString(
|
|
9
9
|
Schema.Struct({
|
|
10
10
|
name: Schema.optional(Schema.String),
|
|
11
|
+
packageManager: Schema.optional(Schema.String),
|
|
11
12
|
scripts: Schema.optional(Schema.Record(Schema.String, Schema.String)),
|
|
12
13
|
dependencies: Schema.optional(Schema.Record(Schema.String, Schema.String)),
|
|
13
14
|
devDependencies: Schema.optional(Schema.Record(Schema.String, Schema.String)),
|
package/src/sync.ts
CHANGED
|
@@ -26,7 +26,7 @@ import {
|
|
|
26
26
|
observePathWithRawModes,
|
|
27
27
|
type ObservedPath,
|
|
28
28
|
} from "./path-digest.ts";
|
|
29
|
-
import { readDirectDependencyNames } from "./project-package.ts";
|
|
29
|
+
import { readDirectDependencyNames, readProjectPackage } from "./project-package.ts";
|
|
30
30
|
import { acquireProjectProcessLock, PROJECT_PROCESS_LOCK_PATH } from "./project-process-lock.ts";
|
|
31
31
|
import {
|
|
32
32
|
AppliedStateSchema,
|
|
@@ -115,12 +115,14 @@ type SkillPlanAction =
|
|
|
115
115
|
readonly action: "create" | "update";
|
|
116
116
|
readonly desired: DesiredOutput;
|
|
117
117
|
readonly observed: ObservedPath;
|
|
118
|
+
readonly stagedContent?: string;
|
|
118
119
|
}
|
|
119
120
|
| {
|
|
120
121
|
readonly action: "remove";
|
|
121
122
|
readonly previous: OwnershipReceipt;
|
|
122
123
|
readonly destination: string;
|
|
123
124
|
readonly observed: ObservedPath;
|
|
125
|
+
readonly stagedContent?: string;
|
|
124
126
|
}
|
|
125
127
|
| {
|
|
126
128
|
readonly action: "unchanged";
|
|
@@ -234,6 +236,255 @@ const DEFAULT_LOCKFILE = "dev-kit.lock.json";
|
|
|
234
236
|
const DEFAULT_STATE = ".dev-kit/state.json";
|
|
235
237
|
const AGENT_INSTRUCTIONS_TEMPLATE = "templates/AGENTS.md";
|
|
236
238
|
const DEV_KIT_SKILL_PATH_PLACEHOLDER = "{{DEV_KIT_SKILL_PATH}}";
|
|
239
|
+
const PROJECT_COMMAND_POLICY_PLACEHOLDER = "{{PROJECT_COMMAND_POLICY}}";
|
|
240
|
+
const AGENT_INSTRUCTION_MARKERS = [
|
|
241
|
+
{ start: "<!-- DEV KIT START -->", end: "<!-- DEV KIT END -->" },
|
|
242
|
+
// Legacy Dev Kit releases copied this upstream section into AGENTS.md. Keep
|
|
243
|
+
// recognizing it so an owned section can be removed during migration.
|
|
244
|
+
{ start: "<!--VITE PLUS START-->", end: "<!--VITE PLUS END-->" },
|
|
245
|
+
] as const;
|
|
246
|
+
|
|
247
|
+
type ManagedInstructionRange = {
|
|
248
|
+
readonly start: number;
|
|
249
|
+
readonly end: number;
|
|
250
|
+
readonly content: string;
|
|
251
|
+
};
|
|
252
|
+
|
|
253
|
+
type ManagedInstructionInspection =
|
|
254
|
+
| {
|
|
255
|
+
readonly kind: "valid";
|
|
256
|
+
readonly ranges: ReadonlyArray<ManagedInstructionRange>;
|
|
257
|
+
readonly content?: string;
|
|
258
|
+
}
|
|
259
|
+
| { readonly kind: "invalid"; readonly reason: string };
|
|
260
|
+
|
|
261
|
+
const findOccurrences = (content: string, marker: string): ReadonlyArray<number> => {
|
|
262
|
+
const positions: Array<number> = [];
|
|
263
|
+
let offset = 0;
|
|
264
|
+
|
|
265
|
+
while (offset < content.length) {
|
|
266
|
+
const position = content.indexOf(marker, offset);
|
|
267
|
+
|
|
268
|
+
if (position === -1) break;
|
|
269
|
+
positions.push(position);
|
|
270
|
+
offset = position + marker.length;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
return positions;
|
|
274
|
+
};
|
|
275
|
+
|
|
276
|
+
const inspectManagedInstructionSections = (content: string): ManagedInstructionInspection => {
|
|
277
|
+
const ranges: Array<ManagedInstructionRange> = [];
|
|
278
|
+
|
|
279
|
+
for (const markers of AGENT_INSTRUCTION_MARKERS) {
|
|
280
|
+
const starts = findOccurrences(content, markers.start);
|
|
281
|
+
const ends = findOccurrences(content, markers.end);
|
|
282
|
+
|
|
283
|
+
if (starts.length === 0 && ends.length === 0) continue;
|
|
284
|
+
if (
|
|
285
|
+
starts.length !== 1 ||
|
|
286
|
+
ends.length !== 1 ||
|
|
287
|
+
starts[0] === undefined ||
|
|
288
|
+
ends[0] === undefined
|
|
289
|
+
) {
|
|
290
|
+
return {
|
|
291
|
+
kind: "invalid",
|
|
292
|
+
reason: `expected exactly one ${markers.start}/${markers.end} marker pair`,
|
|
293
|
+
};
|
|
294
|
+
}
|
|
295
|
+
if (starts[0] >= ends[0]) {
|
|
296
|
+
return { kind: "invalid", reason: `${markers.end} appears before ${markers.start}` };
|
|
297
|
+
}
|
|
298
|
+
const end = ends[0] + markers.end.length;
|
|
299
|
+
|
|
300
|
+
ranges.push({ start: starts[0], end, content: content.slice(starts[0], end) });
|
|
301
|
+
}
|
|
302
|
+
ranges.sort((left, right) => left.start - right.start);
|
|
303
|
+
for (let index = 1; index < ranges.length; index += 1) {
|
|
304
|
+
const previous = ranges[index - 1];
|
|
305
|
+
const current = ranges[index];
|
|
306
|
+
|
|
307
|
+
if (previous !== undefined && current !== undefined && current.start < previous.end) {
|
|
308
|
+
return { kind: "invalid", reason: "managed instruction marker pairs overlap" };
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
return {
|
|
313
|
+
kind: "valid",
|
|
314
|
+
ranges,
|
|
315
|
+
...(ranges.length === 0
|
|
316
|
+
? {}
|
|
317
|
+
: { content: `${ranges.map((range) => range.content.trim()).join("\n\n")}\n` }),
|
|
318
|
+
};
|
|
319
|
+
};
|
|
320
|
+
|
|
321
|
+
const removeManagedInstructionSections = (
|
|
322
|
+
content: string,
|
|
323
|
+
ranges: ReadonlyArray<ManagedInstructionRange>,
|
|
324
|
+
): string => {
|
|
325
|
+
const first = ranges[0];
|
|
326
|
+
const last = ranges.at(-1);
|
|
327
|
+
const hasOnlyManagedSeparators = ranges.every((range, index) => {
|
|
328
|
+
const next = ranges[index + 1];
|
|
329
|
+
|
|
330
|
+
return next === undefined || /^\s*$/.test(content.slice(range.end, next.start));
|
|
331
|
+
});
|
|
332
|
+
|
|
333
|
+
if (first?.start === 0 && last !== undefined && hasOnlyManagedSeparators) {
|
|
334
|
+
let end = last.end;
|
|
335
|
+
|
|
336
|
+
if (content.startsWith("\r\n", end)) end += 2;
|
|
337
|
+
else if (content.startsWith("\n", end)) end += 1;
|
|
338
|
+
|
|
339
|
+
return content.slice(end);
|
|
340
|
+
}
|
|
341
|
+
let result = content;
|
|
342
|
+
|
|
343
|
+
for (const range of [...ranges].reverse()) {
|
|
344
|
+
result = result.slice(0, range.start) + result.slice(range.end);
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
return result;
|
|
348
|
+
};
|
|
349
|
+
|
|
350
|
+
const prependManagedInstructionSections = (content: string, managed: string): string => {
|
|
351
|
+
if (content.trim().length === 0) return managed;
|
|
352
|
+
|
|
353
|
+
return `${managed}${content}`;
|
|
354
|
+
};
|
|
355
|
+
|
|
356
|
+
const reconcileManagedInstructionSections = (
|
|
357
|
+
content: string,
|
|
358
|
+
inspection: Extract<ManagedInstructionInspection, { readonly kind: "valid" }>,
|
|
359
|
+
managed: string,
|
|
360
|
+
): string =>
|
|
361
|
+
prependManagedInstructionSections(
|
|
362
|
+
removeManagedInstructionSections(content, inspection.ranges),
|
|
363
|
+
managed,
|
|
364
|
+
);
|
|
365
|
+
|
|
366
|
+
const renderVitePlusCommandPolicy = (
|
|
367
|
+
scripts: Readonly<Record<string, string>>,
|
|
368
|
+
managesQualityConfig: boolean,
|
|
369
|
+
): string => {
|
|
370
|
+
const hasCheck = managesQualityConfig || scripts.check !== undefined;
|
|
371
|
+
const hasTypecheck = managesQualityConfig || scripts.typecheck !== undefined;
|
|
372
|
+
|
|
373
|
+
return [
|
|
374
|
+
"## Project command policy",
|
|
375
|
+
"",
|
|
376
|
+
"Vite+ is the unified toolchain and command authority for this repository. It wraps Vite, Rolldown, Vitest, tsdown, Oxlint, Oxfmt, and Vite Task behind the `vp` CLI; Vite+ is distinct from Vite.",
|
|
377
|
+
"",
|
|
378
|
+
"Run `vp help` for available commands and `vp <command> --help` for command-specific options. Documentation is available locally in `node_modules/vite-plus/docs` and online at https://viteplus.dev/guide/.",
|
|
379
|
+
"",
|
|
380
|
+
"Use these repository commands:",
|
|
381
|
+
"",
|
|
382
|
+
"- Install dependencies: `vp install`.",
|
|
383
|
+
...(hasCheck ? ["- Full validation: `vp run check`."] : []),
|
|
384
|
+
"- Static checks: `vp check`.",
|
|
385
|
+
"- Format check: `vp fmt --check`; format fixes: `vp fmt`.",
|
|
386
|
+
"- Lint only: `vp lint`; lint fixes: `vp lint --fix`.",
|
|
387
|
+
"- Tests only: `vp test`.",
|
|
388
|
+
...(hasTypecheck ? ["- Typecheck only: `vp run typecheck`."] : []),
|
|
389
|
+
"- Other repository tasks and package scripts: `vp run <task>`.",
|
|
390
|
+
"- Toolchain or runtime troubleshooting: run `vp env doctor` and include its output when asking for help.",
|
|
391
|
+
"",
|
|
392
|
+
"Do not use `bun run`, `npm run`, `pnpm run`, or `yarn run` in this repository. Do not invoke underlying tools such as `tsc`, `vitest`, `oxlint`, or `oxfmt` directly; use the Vite+ entry points above.",
|
|
393
|
+
].join("\n");
|
|
394
|
+
};
|
|
395
|
+
|
|
396
|
+
const PACKAGE_MANAGER_COMMANDS = {
|
|
397
|
+
bun: { install: "bun install", label: "Bun" },
|
|
398
|
+
npm: { install: "npm install", label: "npm" },
|
|
399
|
+
pnpm: { install: "pnpm install", label: "pnpm" },
|
|
400
|
+
yarn: { install: "yarn install", label: "Yarn" },
|
|
401
|
+
} as const;
|
|
402
|
+
|
|
403
|
+
type PackageManagerName = keyof typeof PACKAGE_MANAGER_COMMANDS;
|
|
404
|
+
|
|
405
|
+
const packageManagerName = (declaration: string | undefined): PackageManagerName | undefined => {
|
|
406
|
+
const name = declaration?.split("@", 1)[0];
|
|
407
|
+
|
|
408
|
+
return name !== undefined && name in PACKAGE_MANAGER_COMMANDS
|
|
409
|
+
? (name as PackageManagerName)
|
|
410
|
+
: undefined;
|
|
411
|
+
};
|
|
412
|
+
|
|
413
|
+
const detectPackageManager = Effect.fn("detectPackageManager")(function* (
|
|
414
|
+
projectDir: string,
|
|
415
|
+
declaration: string | undefined,
|
|
416
|
+
) {
|
|
417
|
+
const declared = packageManagerName(declaration);
|
|
418
|
+
|
|
419
|
+
if (declared !== undefined || declaration !== undefined) return declared;
|
|
420
|
+
const fs = yield* FileSystem.FileSystem;
|
|
421
|
+
const path = yield* Path.Path;
|
|
422
|
+
const lockfiles: ReadonlyArray<readonly [PackageManagerName, ReadonlyArray<string>]> = [
|
|
423
|
+
["bun", ["bun.lock", "bun.lockb"]],
|
|
424
|
+
["npm", ["package-lock.json", "npm-shrinkwrap.json"]],
|
|
425
|
+
["pnpm", ["pnpm-lock.yaml"]],
|
|
426
|
+
["yarn", ["yarn.lock"]],
|
|
427
|
+
];
|
|
428
|
+
const detected: Array<PackageManagerName> = [];
|
|
429
|
+
|
|
430
|
+
for (const [manager, files] of lockfiles) {
|
|
431
|
+
let found = false;
|
|
432
|
+
|
|
433
|
+
for (const file of files) {
|
|
434
|
+
if (yield* fs.exists(path.join(projectDir, file))) {
|
|
435
|
+
found = true;
|
|
436
|
+
break;
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
if (found) {
|
|
440
|
+
detected.push(manager);
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
return detected.length === 1 ? detected[0] : undefined;
|
|
445
|
+
});
|
|
446
|
+
|
|
447
|
+
const renderPackageScriptCommandPolicy = (
|
|
448
|
+
manager: PackageManagerName | undefined,
|
|
449
|
+
scripts: Readonly<Record<string, string>>,
|
|
450
|
+
): string => {
|
|
451
|
+
const installer = manager === undefined ? undefined : PACKAGE_MANAGER_COMMANDS[manager];
|
|
452
|
+
const entries = [
|
|
453
|
+
["check", "Full validation"],
|
|
454
|
+
["format:check", "Format check"],
|
|
455
|
+
["format", "Format"],
|
|
456
|
+
["lint", "Lint"],
|
|
457
|
+
["test", "Tests"],
|
|
458
|
+
["typecheck", "Typecheck"],
|
|
459
|
+
] as const;
|
|
460
|
+
const commands = entries.flatMap(([script, label]) =>
|
|
461
|
+
scripts[script] === undefined ? [] : [`- ${label}: \`bun run ${script}\`.`],
|
|
462
|
+
);
|
|
463
|
+
const knownScripts = new Set(entries.map(([script]) => script));
|
|
464
|
+
const additionalCommands = Object.keys(scripts)
|
|
465
|
+
.filter(
|
|
466
|
+
(script) =>
|
|
467
|
+
!knownScripts.has(script as (typeof entries)[number][0]) &&
|
|
468
|
+
/^(?:check|validate|fmt|format|lint|test|type-?check)(?::|$)/.test(script),
|
|
469
|
+
)
|
|
470
|
+
.sort()
|
|
471
|
+
.map((script) => `- Script \`${script}\`: \`bun run ${script}\`.`);
|
|
472
|
+
const qualityCommands = [...commands, ...additionalCommands];
|
|
473
|
+
|
|
474
|
+
return [
|
|
475
|
+
"## Project command policy",
|
|
476
|
+
"",
|
|
477
|
+
"Bun is the package-script runner for this repository:",
|
|
478
|
+
"",
|
|
479
|
+
...(installer === undefined
|
|
480
|
+
? []
|
|
481
|
+
: [`- Install dependencies with ${installer.label}: \`${installer.install}\`.`]),
|
|
482
|
+
...qualityCommands,
|
|
483
|
+
...(qualityCommands.length === 0 ? ["- No root quality scripts are currently declared."] : []),
|
|
484
|
+
"",
|
|
485
|
+
"Run only declared scripts through `bun run <script>`. Do not use `npm run`, `pnpm run`, or `yarn run`, invent missing scripts, or invoke underlying tools such as `tsc`, `vitest`, `eslint`, or `prettier` directly. The Bun script-runner requirement does not choose the package manager used to install dependencies.",
|
|
486
|
+
].join("\n");
|
|
487
|
+
};
|
|
237
488
|
|
|
238
489
|
const resolvePackageRoot = Effect.fn("resolvePackageRoot")(function* () {
|
|
239
490
|
const path = yield* Path.Path;
|
|
@@ -521,6 +772,7 @@ const renderAgentInstructions = Effect.fn("renderAgentInstructions")(function* (
|
|
|
521
772
|
packageRoot: string,
|
|
522
773
|
projectDir: string,
|
|
523
774
|
sourceBySkill: ReadonlyMap<string, ResolvedSkillSource>,
|
|
775
|
+
managesVitePlusQuality: boolean,
|
|
524
776
|
) {
|
|
525
777
|
const fs = yield* FileSystem.FileSystem;
|
|
526
778
|
const path = yield* Path.Path;
|
|
@@ -538,6 +790,11 @@ const renderAgentInstructions = Effect.fn("renderAgentInstructions")(function* (
|
|
|
538
790
|
message: `dev-kit agent instructions template is missing ${DEV_KIT_SKILL_PATH_PLACEHOLDER}`,
|
|
539
791
|
});
|
|
540
792
|
}
|
|
793
|
+
if (!template.includes(PROJECT_COMMAND_POLICY_PLACEHOLDER)) {
|
|
794
|
+
return yield* new InvalidProjectStateError({
|
|
795
|
+
message: `dev-kit agent instructions template is missing ${PROJECT_COMMAND_POLICY_PLACEHOLDER}`,
|
|
796
|
+
});
|
|
797
|
+
}
|
|
541
798
|
|
|
542
799
|
const devKitSkill = sourceBySkill.get("dev-kit");
|
|
543
800
|
const devKitSkillPath =
|
|
@@ -550,21 +807,22 @@ const renderAgentInstructions = Effect.fn("renderAgentInstructions")(function* (
|
|
|
550
807
|
path.join(devKitSkill.linkPath ?? devKitSkill.path, "SKILL.md"),
|
|
551
808
|
),
|
|
552
809
|
);
|
|
553
|
-
const
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
810
|
+
const usesVitePlus = (yield* readDirectDependencyNames(projectDir)).includes("vite-plus");
|
|
811
|
+
const projectPackage = yield* readProjectPackage(projectDir).pipe(
|
|
812
|
+
Effect.catchTag("ProjectPackageError", (error) =>
|
|
813
|
+
error.message.startsWith("package.json not found:") ? Effect.void : Effect.fail(error),
|
|
814
|
+
),
|
|
815
|
+
);
|
|
816
|
+
const manager = yield* detectPackageManager(projectDir, projectPackage?.packageManager);
|
|
817
|
+
const commandPolicy = usesVitePlus
|
|
818
|
+
? renderVitePlusCommandPolicy(projectPackage?.scripts ?? {}, managesVitePlusQuality)
|
|
819
|
+
: renderPackageScriptCommandPolicy(manager, projectPackage?.scripts ?? {});
|
|
820
|
+
const devKitInstructions = template
|
|
821
|
+
.replaceAll(DEV_KIT_SKILL_PATH_PLACEHOLDER, devKitSkillPath)
|
|
822
|
+
.replaceAll(PROJECT_COMMAND_POLICY_PLACEHOLDER, commandPolicy)
|
|
823
|
+
.trimEnd();
|
|
824
|
+
|
|
825
|
+
return `${devKitInstructions}\n`;
|
|
568
826
|
});
|
|
569
827
|
|
|
570
828
|
const readGeneratedFileTemplate = Effect.fn("readGeneratedFileTemplate")(function* (
|
|
@@ -597,7 +855,12 @@ const buildDesiredOutputs = Effect.fn("buildDesiredSkillOutputs")(function* (
|
|
|
597
855
|
|
|
598
856
|
if (setup.agentInstructions.enabled) {
|
|
599
857
|
const managed = yield* resolveManagedPath(projectDir, "AGENTS.md");
|
|
600
|
-
const content = yield* renderAgentInstructions(
|
|
858
|
+
const content = yield* renderAgentInstructions(
|
|
859
|
+
packageRoot,
|
|
860
|
+
projectDir,
|
|
861
|
+
sourceBySkill,
|
|
862
|
+
setup.vitePlus.quality.config.enabled,
|
|
863
|
+
);
|
|
601
864
|
|
|
602
865
|
outputs.push({
|
|
603
866
|
resourceId: "setup:agent-instructions",
|
|
@@ -662,7 +925,7 @@ const buildDesiredOutputs = Effect.fn("buildDesiredSkillOutputs")(function* (
|
|
|
662
925
|
devKitCommand:
|
|
663
926
|
projectDir === packageRoot
|
|
664
927
|
? "./bin/dev-kit.mjs apply --locked"
|
|
665
|
-
: "
|
|
928
|
+
: "bun ./node_modules/@danieljvdm/dev-kit/bin/dev-kit.mjs apply --locked",
|
|
666
929
|
workflow: setup.vitePlus.quality.workflow,
|
|
667
930
|
});
|
|
668
931
|
|
|
@@ -803,6 +1066,89 @@ const planDesiredOutputs = Effect.fn("planDesiredSkillOutputs")(function* (
|
|
|
803
1066
|
(rawModeObservation?.kind === matchingLockedOutput.kind &&
|
|
804
1067
|
rawModeObservation.digest === matchingLockedOutput.digest));
|
|
805
1068
|
|
|
1069
|
+
if (output.resourceId === "setup:agent-instructions" && "content" in output) {
|
|
1070
|
+
if (observed.kind === "missing") {
|
|
1071
|
+
actions.push({
|
|
1072
|
+
action: "create",
|
|
1073
|
+
desired: output,
|
|
1074
|
+
observed,
|
|
1075
|
+
stagedContent: output.content,
|
|
1076
|
+
});
|
|
1077
|
+
continue;
|
|
1078
|
+
}
|
|
1079
|
+
if (observed.kind !== "file") {
|
|
1080
|
+
actions.push({
|
|
1081
|
+
action: "conflict",
|
|
1082
|
+
path: output.path,
|
|
1083
|
+
reason: "destination is not a regular file",
|
|
1084
|
+
});
|
|
1085
|
+
continue;
|
|
1086
|
+
}
|
|
1087
|
+
const existingContent = yield* FileSystem.FileSystem.pipe(
|
|
1088
|
+
Effect.flatMap((fs) => fs.readFileString(output.destination)),
|
|
1089
|
+
);
|
|
1090
|
+
const inspection = inspectManagedInstructionSections(existingContent);
|
|
1091
|
+
|
|
1092
|
+
if (inspection.kind === "invalid") {
|
|
1093
|
+
actions.push({ action: "conflict", path: output.path, reason: inspection.reason });
|
|
1094
|
+
continue;
|
|
1095
|
+
}
|
|
1096
|
+
const managedDigest =
|
|
1097
|
+
inspection.content === undefined ? undefined : yield* digestFileContent(inspection.content);
|
|
1098
|
+
const receiptOwnsManaged =
|
|
1099
|
+
sameReceipt !== undefined &&
|
|
1100
|
+
(managedDigest === sameReceipt.digest || observed.digest === sameReceipt.digest);
|
|
1101
|
+
const lockOwnsManaged =
|
|
1102
|
+
matchingLockedOutput !== undefined &&
|
|
1103
|
+
(managedDigest === matchingLockedOutput.digest ||
|
|
1104
|
+
observed.digest === matchingLockedOutput.digest);
|
|
1105
|
+
const legacyOwnsWholeFile =
|
|
1106
|
+
(sameReceipt !== undefined &&
|
|
1107
|
+
observed.digest === sameReceipt.digest &&
|
|
1108
|
+
managedDigest !== sameReceipt.digest) ||
|
|
1109
|
+
(matchingLockedOutput !== undefined &&
|
|
1110
|
+
lockedOwnsObserved &&
|
|
1111
|
+
managedDigest !== matchingLockedOutput.digest);
|
|
1112
|
+
|
|
1113
|
+
if (managedDigest === output.digest && !legacyOwnsWholeFile) {
|
|
1114
|
+
if (sameReceipt !== undefined || lockOwnsManaged || lockedOwnsObserved) {
|
|
1115
|
+
actions.push({
|
|
1116
|
+
action: "unchanged",
|
|
1117
|
+
desired: output,
|
|
1118
|
+
observed,
|
|
1119
|
+
adopted: sameReceipt === undefined,
|
|
1120
|
+
});
|
|
1121
|
+
} else {
|
|
1122
|
+
actions.push({
|
|
1123
|
+
action: "conflict",
|
|
1124
|
+
path: output.path,
|
|
1125
|
+
reason: "managed instruction sections exist but are not owned",
|
|
1126
|
+
});
|
|
1127
|
+
}
|
|
1128
|
+
} else if (
|
|
1129
|
+
managedDigest === undefined ||
|
|
1130
|
+
receiptOwnsManaged ||
|
|
1131
|
+
lockOwnsManaged ||
|
|
1132
|
+
lockedOwnsObserved
|
|
1133
|
+
) {
|
|
1134
|
+
actions.push({
|
|
1135
|
+
action: "update",
|
|
1136
|
+
desired: output,
|
|
1137
|
+
observed,
|
|
1138
|
+
stagedContent: legacyOwnsWholeFile
|
|
1139
|
+
? output.content
|
|
1140
|
+
: reconcileManagedInstructionSections(existingContent, inspection, output.content),
|
|
1141
|
+
});
|
|
1142
|
+
} else {
|
|
1143
|
+
actions.push({
|
|
1144
|
+
action: "conflict",
|
|
1145
|
+
path: output.path,
|
|
1146
|
+
reason: "managed instruction sections exist but are not owned",
|
|
1147
|
+
});
|
|
1148
|
+
}
|
|
1149
|
+
continue;
|
|
1150
|
+
}
|
|
1151
|
+
|
|
806
1152
|
if (observed.kind === "missing") {
|
|
807
1153
|
actions.push({ action: "create", desired: output, observed });
|
|
808
1154
|
} else if (observed.kind === output.kind && observed.digest === output.digest) {
|
|
@@ -839,6 +1185,46 @@ const planDesiredOutputs = Effect.fn("planDesiredSkillOutputs")(function* (
|
|
|
839
1185
|
const observed = yield* observePath(managed.absolute);
|
|
840
1186
|
|
|
841
1187
|
if (observed.kind === "missing") continue;
|
|
1188
|
+
if (receipt.resourceId === "setup:agent-instructions") {
|
|
1189
|
+
if (observed.kind !== "file") {
|
|
1190
|
+
actions.push({
|
|
1191
|
+
action: "conflict",
|
|
1192
|
+
path: receipt.path,
|
|
1193
|
+
reason: "stale owned destination is not a regular file",
|
|
1194
|
+
});
|
|
1195
|
+
continue;
|
|
1196
|
+
}
|
|
1197
|
+
const existingContent = yield* FileSystem.FileSystem.pipe(
|
|
1198
|
+
Effect.flatMap((fs) => fs.readFileString(managed.absolute)),
|
|
1199
|
+
);
|
|
1200
|
+
const inspection = inspectManagedInstructionSections(existingContent);
|
|
1201
|
+
|
|
1202
|
+
if (inspection.kind === "invalid") {
|
|
1203
|
+
actions.push({ action: "conflict", path: receipt.path, reason: inspection.reason });
|
|
1204
|
+
continue;
|
|
1205
|
+
}
|
|
1206
|
+
if (inspection.content === undefined) continue;
|
|
1207
|
+
const managedDigest = yield* digestFileContent(inspection.content);
|
|
1208
|
+
|
|
1209
|
+
if (managedDigest === receipt.digest || observed.digest === receipt.digest) {
|
|
1210
|
+
const remaining = removeManagedInstructionSections(existingContent, inspection.ranges);
|
|
1211
|
+
|
|
1212
|
+
actions.push({
|
|
1213
|
+
action: "remove",
|
|
1214
|
+
previous: receipt,
|
|
1215
|
+
destination: managed.absolute,
|
|
1216
|
+
observed,
|
|
1217
|
+
...(remaining.trim().length === 0 ? {} : { stagedContent: remaining }),
|
|
1218
|
+
});
|
|
1219
|
+
} else {
|
|
1220
|
+
actions.push({
|
|
1221
|
+
action: "conflict",
|
|
1222
|
+
path: receipt.path,
|
|
1223
|
+
reason: "stale owned managed instruction sections were modified",
|
|
1224
|
+
});
|
|
1225
|
+
}
|
|
1226
|
+
continue;
|
|
1227
|
+
}
|
|
842
1228
|
if (observed.kind === receipt.kind && observed.digest === receipt.digest) {
|
|
843
1229
|
actions.push({
|
|
844
1230
|
action: "remove",
|
|
@@ -1110,16 +1496,6 @@ export const planProjectSkills = Effect.fn("planProjectSkills")(function* (optio
|
|
|
1110
1496
|
const currentLock = yield* readOptionalStructuredFile(lockManaged.absolute, DevKitLockSchema);
|
|
1111
1497
|
const currentState = yield* readOptionalStructuredFile(stateManaged.absolute, AppliedStateSchema);
|
|
1112
1498
|
|
|
1113
|
-
if (
|
|
1114
|
-
manifest.setup.claudeInstructions.enabled &&
|
|
1115
|
-
!manifest.setup.agentInstructions.enabled &&
|
|
1116
|
-
currentState?.outputs.some((output) => output.resourceId === "setup:agent-instructions")
|
|
1117
|
-
) {
|
|
1118
|
-
return yield* new InvalidProjectStateError({
|
|
1119
|
-
message:
|
|
1120
|
-
"cannot disable agentInstructions while claudeInstructions still links to its AGENTS.md wrapper",
|
|
1121
|
-
});
|
|
1122
|
-
}
|
|
1123
1499
|
yield* validateReservedPaths(projectDir, reservedPaths, [
|
|
1124
1500
|
...(currentLock?.outputs ?? []),
|
|
1125
1501
|
...(currentState?.outputs ?? []),
|
|
@@ -1143,6 +1519,19 @@ export const planProjectSkills = Effect.fn("planProjectSkills")(function* (optio
|
|
|
1143
1519
|
currentState,
|
|
1144
1520
|
nextLock,
|
|
1145
1521
|
);
|
|
1522
|
+
const removesClaudeInstructionsSource = planned.actions.some(
|
|
1523
|
+
(action) =>
|
|
1524
|
+
action.action === "remove" &&
|
|
1525
|
+
action.previous.resourceId === "setup:agent-instructions" &&
|
|
1526
|
+
action.stagedContent === undefined,
|
|
1527
|
+
);
|
|
1528
|
+
|
|
1529
|
+
if (manifest.setup.claudeInstructions.enabled && removesClaudeInstructionsSource) {
|
|
1530
|
+
return yield* new InvalidProjectStateError({
|
|
1531
|
+
message:
|
|
1532
|
+
"cannot disable agentInstructions while claudeInstructions still links to an AGENTS.md that would be removed",
|
|
1533
|
+
});
|
|
1534
|
+
}
|
|
1146
1535
|
|
|
1147
1536
|
return {
|
|
1148
1537
|
projectDir,
|
|
@@ -1310,13 +1699,28 @@ const applyPlannedSkillChanges = Effect.fn("applyPlannedSkillChanges")(function*
|
|
|
1310
1699
|
let stageIndex = 0;
|
|
1311
1700
|
|
|
1312
1701
|
for (const action of mutating) {
|
|
1313
|
-
if (action.action === "remove") continue;
|
|
1702
|
+
if (action.action === "remove" && action.stagedContent === undefined) continue;
|
|
1314
1703
|
const staged = path.join(stageDir, String(stageIndex++));
|
|
1315
1704
|
|
|
1316
1705
|
yield* fs.makeDirectory(path.dirname(staged), { recursive: true });
|
|
1317
|
-
if (action.
|
|
1706
|
+
if (action.stagedContent !== undefined && action.observed.kind === "file") {
|
|
1707
|
+
const destination =
|
|
1708
|
+
action.action === "remove" ? action.destination : action.desired.destination;
|
|
1709
|
+
|
|
1710
|
+
yield* fs.copy(destination, staged, { overwrite: true });
|
|
1711
|
+
yield* fs.writeFileString(staged, action.stagedContent);
|
|
1712
|
+
} else if (action.action === "remove") {
|
|
1713
|
+
if (action.stagedContent === undefined) {
|
|
1714
|
+
return yield* new InvalidProjectStateError({
|
|
1715
|
+
message: `missing staged content for ${action.previous.resourceId}`,
|
|
1716
|
+
});
|
|
1717
|
+
}
|
|
1718
|
+
yield* fs.writeFileString(staged, action.stagedContent, { mode: 0o644 });
|
|
1719
|
+
} else if (action.desired.mode === "copy") {
|
|
1318
1720
|
if (action.desired.kind === "file") {
|
|
1319
|
-
yield* fs.writeFileString(staged, action.desired.content, {
|
|
1721
|
+
yield* fs.writeFileString(staged, action.stagedContent ?? action.desired.content, {
|
|
1722
|
+
mode: 0o644,
|
|
1723
|
+
});
|
|
1320
1724
|
} else {
|
|
1321
1725
|
yield* fs.copy(action.desired.source, staged, { overwrite: true });
|
|
1322
1726
|
const symbolicLink = yield* findNestedSymbolicLink(staged);
|
|
@@ -1330,14 +1734,35 @@ const applyPlannedSkillChanges = Effect.fn("applyPlannedSkillChanges")(function*
|
|
|
1330
1734
|
} else {
|
|
1331
1735
|
yield* fs.symlink(action.desired.linkTarget, staged);
|
|
1332
1736
|
}
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1737
|
+
if (action.action !== "remove") {
|
|
1738
|
+
const observation = yield* observePath(staged);
|
|
1739
|
+
|
|
1740
|
+
if (action.desired.resourceId === "setup:agent-instructions") {
|
|
1741
|
+
const content = yield* fs.readFileString(staged);
|
|
1742
|
+
const inspection = inspectManagedInstructionSections(content);
|
|
1743
|
+
const digest =
|
|
1744
|
+
inspection.kind === "valid" && inspection.content !== undefined
|
|
1745
|
+
? yield* digestFileContent(inspection.content)
|
|
1746
|
+
: undefined;
|
|
1747
|
+
|
|
1748
|
+
if (observation.kind !== "file" || digest !== action.desired.digest) {
|
|
1749
|
+
return yield* new InvalidProjectStateError({
|
|
1750
|
+
message: `staged output digest mismatch for ${action.desired.path}`,
|
|
1751
|
+
});
|
|
1752
|
+
}
|
|
1753
|
+
} else if (
|
|
1754
|
+
observation.kind !== action.desired.kind ||
|
|
1755
|
+
observation.digest !== action.desired.digest
|
|
1756
|
+
) {
|
|
1757
|
+
return yield* new InvalidProjectStateError({
|
|
1758
|
+
message: `staged output digest mismatch for ${action.desired.path}`,
|
|
1759
|
+
});
|
|
1760
|
+
}
|
|
1339
1761
|
}
|
|
1340
|
-
stagedByResource.set(
|
|
1762
|
+
stagedByResource.set(
|
|
1763
|
+
action.action === "remove" ? action.previous.resourceId : action.desired.resourceId,
|
|
1764
|
+
staged,
|
|
1765
|
+
);
|
|
1341
1766
|
}
|
|
1342
1767
|
|
|
1343
1768
|
yield* verifyPackageSkillSources(plan);
|
|
@@ -1360,11 +1785,18 @@ const applyPlannedSkillChanges = Effect.fn("applyPlannedSkillChanges")(function*
|
|
|
1360
1785
|
|
|
1361
1786
|
for (const action of mutating) {
|
|
1362
1787
|
const staged =
|
|
1363
|
-
action.action === "remove"
|
|
1788
|
+
action.action === "remove"
|
|
1789
|
+
? stagedByResource.get(action.previous.resourceId)
|
|
1790
|
+
: stagedByResource.get(action.desired.resourceId);
|
|
1364
1791
|
|
|
1365
|
-
if (
|
|
1792
|
+
if (
|
|
1793
|
+
(action.action !== "remove" || action.stagedContent !== undefined) &&
|
|
1794
|
+
staged === undefined
|
|
1795
|
+
) {
|
|
1366
1796
|
return yield* new InvalidProjectStateError({
|
|
1367
|
-
message: `missing staged output for ${
|
|
1797
|
+
message: `missing staged output for ${
|
|
1798
|
+
action.action === "remove" ? action.previous.resourceId : action.desired.resourceId
|
|
1799
|
+
}`,
|
|
1368
1800
|
});
|
|
1369
1801
|
}
|
|
1370
1802
|
replacements.push({
|
package/src/vite-plus-quality.ts
CHANGED
|
@@ -32,7 +32,8 @@ export type VitePlusQualitySelection = {
|
|
|
32
32
|
};
|
|
33
33
|
|
|
34
34
|
const SINGLE_PROJECT_TYPECHECK_TASK = ' typecheck: "tsc --noEmit",';
|
|
35
|
-
const LOCKED_DEV_KIT_COMMAND =
|
|
35
|
+
const LOCKED_DEV_KIT_COMMAND =
|
|
36
|
+
"bun ./node_modules/@danieljvdm/dev-kit/bin/dev-kit.mjs apply --locked";
|
|
36
37
|
const BEFORE_CHECKS_MARKER =
|
|
37
38
|
" # Dev Kit inserts configured quality.workflow.beforeChecks steps here.\n\n";
|
|
38
39
|
const DEFAULT_WORKFLOW_TYPECHECK = ` - name: Type check with Effect TypeScript-Go
|
package/templates/AGENTS.md
CHANGED
|
@@ -22,6 +22,10 @@ jobs:
|
|
|
22
22
|
with:
|
|
23
23
|
persist-credentials: false
|
|
24
24
|
|
|
25
|
+
# setup-bun resolves the consumer's packageManager or engines.bun version.
|
|
26
|
+
- name: Set up Bun
|
|
27
|
+
uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # setup-bun action v2.2.0
|
|
28
|
+
|
|
25
29
|
# setup-vp's v1 tag is frozen. Keep this v1.16.1 commit current with
|
|
26
30
|
# Renovate or Dependabot so fixes arrive through reviewed pull requests.
|
|
27
31
|
- name: Set up Vite+ and install dependencies
|
|
@@ -34,7 +38,7 @@ jobs:
|
|
|
34
38
|
args: ["--frozen-lockfile", "--ignore-scripts"]
|
|
35
39
|
|
|
36
40
|
- name: Verify locked Dev Kit setup
|
|
37
|
-
run:
|
|
41
|
+
run: bun ./node_modules/@danieljvdm/dev-kit/bin/dev-kit.mjs apply --locked
|
|
38
42
|
|
|
39
43
|
# Dev Kit inserts configured quality.workflow.beforeChecks steps here.
|
|
40
44
|
|