@danieljvdm/dev-kit 0.16.0 → 0.18.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 +18 -7
- package/package.json +20 -9
- package/scripts/sync-anti-slop-runtime.mjs +19 -0
- package/skill-sources.jsonc +17 -0
- package/skill-sources.lock.json +48 -0
- package/skills/dev-kit/SKILL.md +4 -5
- package/src/bin/dev-kit.ts +11 -7
- package/src/catalog-manager.ts +23 -18
- package/src/catalog.ts +8 -5
- package/src/cli-ui.ts +2 -2
- package/src/effect-tsgo.ts +10 -6
- package/src/gitignore.ts +6 -3
- package/src/manifest.ts +10 -2
- package/src/oxlint-plugin-anti-slop/LICENSE +21 -0
- package/src/oxlint-plugin-anti-slop/index.ts +43 -0
- package/src/oxlint-plugin-anti-slop/rules/no-chained-type-assertions.ts +79 -0
- package/src/oxlint-plugin-anti-slop/rules/no-conditional-empty-object-spread.ts +51 -0
- package/src/oxlint-plugin-anti-slop/rules/no-known-value-widening.ts +267 -0
- package/src/oxlint-plugin-anti-slop/rules/no-module-mocking.ts +105 -0
- package/src/oxlint-plugin-anti-slop/rules/no-object-parameters.ts +141 -0
- package/src/oxlint-plugin-anti-slop/rules/no-reflect-apply.ts +28 -0
- package/src/oxlint-plugin-anti-slop/rules/no-reflect-get.ts +28 -0
- package/src/oxlint-plugin-anti-slop/rules/no-runtime-typeof.ts +25 -0
- package/src/oxlint-plugin-anti-slop/rules/no-shape-in-symbol-names.ts +38 -0
- package/src/oxlint-plugin-anti-slop/rules/no-unknown-parameters.ts +86 -0
- package/src/oxlint-plugin-anti-slop/rules/no-unknown-returns.ts +125 -0
- package/src/oxlint-plugin-anti-slop/rules/no-unknown-type-aliases.ts +73 -0
- package/src/oxlint-plugin-anti-slop/rules/no-unsafe-dictionary-type.ts +139 -0
- package/src/oxlint-plugin-anti-slop/rules/no-widen-then-assert.ts +396 -0
- package/src/oxlint-plugin-anti-slop/rules/require-safety-comment-for-type-assertion.ts +61 -0
- package/src/oxlint-plugin-anti-slop/runtime.d.ts +22 -0
- package/src/oxlint-plugin-anti-slop/runtime.js +1209 -0
- package/src/oxlint-plugin-anti-slop/shared/dictionary-types.ts +555 -0
- package/src/oxlint-plugin-anti-slop/shared/reflect-method.ts +40 -0
- package/src/oxlint-plugin-effect.d.ts +4 -2
- package/src/oxlint.js +19 -0
- package/src/oxlint.ts +22 -5
- package/src/package-skill-source.ts +17 -25
- package/src/path-digest.ts +2 -1
- package/src/project-package.ts +14 -12
- package/src/skill-manager.ts +25 -13
- package/src/sync.ts +65 -49
- package/src/vendor.ts +35 -16
package/README.md
CHANGED
|
@@ -493,7 +493,7 @@ Pin the compatible packages in the consuming project:
|
|
|
493
493
|
{
|
|
494
494
|
"devDependencies": {
|
|
495
495
|
"@danieljvdm/dev-kit": "^0.2.0",
|
|
496
|
-
"@effect/tsgo": "0.
|
|
496
|
+
"@effect/tsgo": "0.36.4",
|
|
497
497
|
"typescript": "7.0.2",
|
|
498
498
|
},
|
|
499
499
|
}
|
|
@@ -701,14 +701,25 @@ export default defineConfig({
|
|
|
701
701
|
});
|
|
702
702
|
```
|
|
703
703
|
|
|
704
|
-
The Oxlint preset enables
|
|
704
|
+
The Oxlint preset vendors and enables every rule from
|
|
705
|
+
[dmmulroy/anti-slop](https://github.com/dmmulroy/anti-slop) at upstream commit
|
|
706
|
+
`9b80d9a5c317d3af94d88a577bdbde4d9a45f7be`. These opinionated rules reject
|
|
707
|
+
low-evidence patterns such as broad `unknown` contracts, runtime `typeof`
|
|
708
|
+
narrowing, module mocking, chained assertions, and assertions without a
|
|
709
|
+
nearby `SAFETY:` justification. The package also exports the plugin from
|
|
710
|
+
`@danieljvdm/dev-kit/oxlint-plugin-anti-slop`. Its vendored MIT license is
|
|
711
|
+
retained beside the source.
|
|
712
|
+
|
|
713
|
+
The TypeScript files under `src/oxlint-plugin-anti-slop/` are canonical. After
|
|
714
|
+
updating them, run `vp run anti-slop:bundle` to regenerate the package-loadable
|
|
715
|
+
JavaScript entry point without cleaning canonical source.
|
|
716
|
+
|
|
717
|
+
The preset also enables `stylistic/padding-line-between-statements`: adjacent
|
|
705
718
|
variable declarations remain grouped, while the next logical statement and
|
|
706
719
|
all `return` statements require a separating blank line. The rule is fixable,
|
|
707
|
-
so `vp lint --fix` repairs missing spacing automatically.
|
|
708
|
-
the preset's JavaScript-plugin
|
|
709
|
-
|
|
710
|
-
must be enforced, until a supported Vite+ release executes configured JS
|
|
711
|
-
plugins.
|
|
720
|
+
so `vp lint --fix` repairs missing spacing automatically. With the matching
|
|
721
|
+
Oxlint 1.78 peer installed, Vite+ 0.2.6 executes the preset's JavaScript-plugin
|
|
722
|
+
rules through the normal `vp lint` command.
|
|
712
723
|
|
|
713
724
|
The preset also registers the shared `effect` JavaScript plugin. Effect
|
|
714
725
|
projects opt into its rules in path-specific overrides, for example
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@danieljvdm/dev-kit",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.18.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Declarative project development toolkit with portable agent skills.",
|
|
6
6
|
"license": "MIT",
|
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
"skill-sources.jsonc",
|
|
20
20
|
"skill-sources.lock.json",
|
|
21
21
|
"src/",
|
|
22
|
+
"scripts/",
|
|
22
23
|
"templates/"
|
|
23
24
|
],
|
|
24
25
|
"type": "module",
|
|
@@ -46,6 +47,10 @@
|
|
|
46
47
|
"types": "./src/oxlint-plugin-effect.d.ts",
|
|
47
48
|
"default": "./src/oxlint-plugin-effect.js"
|
|
48
49
|
},
|
|
50
|
+
"./oxlint-plugin-anti-slop": {
|
|
51
|
+
"types": "./src/oxlint-plugin-anti-slop/runtime.d.ts",
|
|
52
|
+
"default": "./src/oxlint-plugin-anti-slop/runtime.js"
|
|
53
|
+
},
|
|
49
54
|
"./oxlint-plugin-style": {
|
|
50
55
|
"types": "./src/oxlint-plugin-style.d.ts",
|
|
51
56
|
"default": "./src/oxlint-plugin-style.js"
|
|
@@ -66,31 +71,34 @@
|
|
|
66
71
|
"test:watch": "vitest --config vitest.config.ts",
|
|
67
72
|
"tsgo:patch": "./bin/dev-kit.mjs tsgo patch",
|
|
68
73
|
"catalog:refresh": "./bin/dev-kit.mjs catalog refresh",
|
|
69
|
-
"catalog:check": "./bin/dev-kit.mjs catalog verify"
|
|
74
|
+
"catalog:check": "./bin/dev-kit.mjs catalog verify",
|
|
75
|
+
"anti-slop:bundle": "vp pack src/oxlint-plugin-anti-slop/index.ts --out-dir .dev-kit/anti-slop-build --format esm --no-dts && node scripts/sync-anti-slop-runtime.mjs",
|
|
76
|
+
"anti-slop:check": "vp pack src/oxlint-plugin-anti-slop/index.ts --out-dir .dev-kit/anti-slop-build --format esm --no-dts && node scripts/sync-anti-slop-runtime.mjs --check"
|
|
70
77
|
},
|
|
71
78
|
"dependencies": {
|
|
72
|
-
"@effect/platform-bun": "4.0.0-beta.
|
|
79
|
+
"@effect/platform-bun": "4.0.0-beta.107",
|
|
80
|
+
"@oxlint/plugins": "1.78.0",
|
|
73
81
|
"@stylistic/eslint-plugin": "5.10.0",
|
|
74
|
-
"effect": "4.0.0-beta.
|
|
82
|
+
"effect": "4.0.0-beta.107",
|
|
75
83
|
"jsonc-parser": "3.3.1",
|
|
76
84
|
"semver": "7.8.5"
|
|
77
85
|
},
|
|
78
86
|
"devDependencies": {
|
|
79
87
|
"@changesets/cli": "3.0.0-next.10",
|
|
80
|
-
"@effect/platform-node": "4.0.0-beta.
|
|
81
|
-
"@effect/tsgo": "0.
|
|
82
|
-
"@effect/vitest": "4.0.0-beta.
|
|
88
|
+
"@effect/platform-node": "4.0.0-beta.107",
|
|
89
|
+
"@effect/tsgo": "0.36.4",
|
|
90
|
+
"@effect/vitest": "4.0.0-beta.107",
|
|
83
91
|
"@types/node": "25.9.1",
|
|
84
92
|
"@types/semver": "7.8.0",
|
|
85
93
|
"oxfmt": "0.60.0",
|
|
86
|
-
"oxlint": "1.
|
|
94
|
+
"oxlint": "1.78.0",
|
|
87
95
|
"typescript": "7.0.2",
|
|
88
96
|
"vite-plus": "0.2.6",
|
|
89
97
|
"vitest": "4.1.10"
|
|
90
98
|
},
|
|
91
99
|
"peerDependencies": {
|
|
92
100
|
"oxfmt": ">=0.60.0 <0.61.0",
|
|
93
|
-
"oxlint": ">=1.
|
|
101
|
+
"oxlint": ">=1.78.0 <2.0.0",
|
|
94
102
|
"vite-plus": ">=0.2.6 <0.3.0"
|
|
95
103
|
},
|
|
96
104
|
"peerDependenciesMeta": {
|
|
@@ -104,6 +112,9 @@
|
|
|
104
112
|
"optional": true
|
|
105
113
|
}
|
|
106
114
|
},
|
|
115
|
+
"overrides": {
|
|
116
|
+
"@effect/platform-node-shared": "4.0.0-beta.107"
|
|
117
|
+
},
|
|
107
118
|
"engines": {
|
|
108
119
|
"bun": ">=1.3.0"
|
|
109
120
|
},
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { readFile, writeFile } from "node:fs/promises";
|
|
2
|
+
|
|
3
|
+
const generatedUrl = new URL("../.dev-kit/anti-slop-build/index.mjs", import.meta.url);
|
|
4
|
+
const runtimeUrl = new URL("../src/oxlint-plugin-anti-slop/runtime.js", import.meta.url);
|
|
5
|
+
const generated = await readFile(generatedUrl, "utf8");
|
|
6
|
+
const header =
|
|
7
|
+
"// Bundled from the vendored anti-slop source at commit 9b80d9a5c317d3af94d88a577bdbde4d9a45f7be.\n";
|
|
8
|
+
const runtime = generated.replace(/^\/\/[#](?:end)?region.*\n/gmu, "");
|
|
9
|
+
const expected = `${header}${runtime}`;
|
|
10
|
+
|
|
11
|
+
if (process.argv.includes("--check")) {
|
|
12
|
+
const current = await readFile(runtimeUrl, "utf8");
|
|
13
|
+
|
|
14
|
+
if (current !== expected) {
|
|
15
|
+
throw new Error("anti-slop runtime is stale; run `vp run anti-slop:bundle`");
|
|
16
|
+
}
|
|
17
|
+
} else {
|
|
18
|
+
await writeFile(runtimeUrl, expected);
|
|
19
|
+
}
|
package/skill-sources.jsonc
CHANGED
|
@@ -85,5 +85,22 @@
|
|
|
85
85
|
],
|
|
86
86
|
"licensePath": "LICENSE",
|
|
87
87
|
},
|
|
88
|
+
{
|
|
89
|
+
"id": "getsentry-agent-plugin",
|
|
90
|
+
"repository": "https://github.com/getsentry/agent-plugin.git",
|
|
91
|
+
"ref": "main",
|
|
92
|
+
"skillsPath": "skills",
|
|
93
|
+
"include": [
|
|
94
|
+
"sentry-create-alert",
|
|
95
|
+
"sentry-debug-issue",
|
|
96
|
+
"sentry-fix-stack-traces",
|
|
97
|
+
"sentry-get-started",
|
|
98
|
+
"sentry-instrument",
|
|
99
|
+
"sentry-otel-exporter-setup",
|
|
100
|
+
"sentry-setup-releases",
|
|
101
|
+
"sentry-snapshots-cocoa",
|
|
102
|
+
],
|
|
103
|
+
"licensePath": "LICENSE",
|
|
104
|
+
},
|
|
88
105
|
],
|
|
89
106
|
}
|
package/skill-sources.lock.json
CHANGED
|
@@ -243,6 +243,54 @@
|
|
|
243
243
|
"expo-web-to-native": "sha256:360359742d72d0a8ed9926d53f19bae7603fb788f6e897a14208bbb35c2ed618"
|
|
244
244
|
},
|
|
245
245
|
"licensePath": "LICENSE"
|
|
246
|
+
},
|
|
247
|
+
{
|
|
248
|
+
"id": "getsentry-agent-plugin",
|
|
249
|
+
"repository": "https://github.com/getsentry/agent-plugin.git",
|
|
250
|
+
"ref": "main",
|
|
251
|
+
"resolved": "9f9156966be43f1bf4797bd9785a0644bf181d01",
|
|
252
|
+
"skillsPath": "skills",
|
|
253
|
+
"include": [
|
|
254
|
+
"sentry-create-alert",
|
|
255
|
+
"sentry-debug-issue",
|
|
256
|
+
"sentry-fix-stack-traces",
|
|
257
|
+
"sentry-get-started",
|
|
258
|
+
"sentry-instrument",
|
|
259
|
+
"sentry-otel-exporter-setup",
|
|
260
|
+
"sentry-setup-releases",
|
|
261
|
+
"sentry-snapshots-cocoa"
|
|
262
|
+
],
|
|
263
|
+
"skills": [
|
|
264
|
+
"sentry-create-alert",
|
|
265
|
+
"sentry-debug-issue",
|
|
266
|
+
"sentry-fix-stack-traces",
|
|
267
|
+
"sentry-get-started",
|
|
268
|
+
"sentry-instrument",
|
|
269
|
+
"sentry-otel-exporter-setup",
|
|
270
|
+
"sentry-setup-releases",
|
|
271
|
+
"sentry-snapshots-cocoa"
|
|
272
|
+
],
|
|
273
|
+
"descriptions": {
|
|
274
|
+
"sentry-create-alert": "Create Sentry alerts using the workflow engine API. Use when asked to create alerts, set up notifications, configure issue priority alerts, or build workflow automations. Supports email, Slack, PagerDuty, Discord, and other notification actions.",
|
|
275
|
+
"sentry-debug-issue": "Debug and fix a Sentry issue — find it (by link, ID, or search), pull full context (stack trace, breadcrumbs, trace, logs), optionally run Seer root-cause / autofix, apply the code fix, and resolve it via a `Fixes PROJECT-NAME-12A` commit/PR. Use when working a known error or hunting one down to fix.",
|
|
276
|
+
"sentry-fix-stack-traces": "Make Sentry stack traces readable — upload source maps for JavaScript/TypeScript, or debug files for native and mobile (dSYM, ProGuard/R8, NDK symbols, Dart obfuscation maps, .NET PDBs). Use when frames in Sentry show minified names, bundled paths, hex addresses, \"unknown\", or method names with no file/line, instead of your original source.",
|
|
277
|
+
"sentry-get-started": "Guided entry point for using Sentry through your agent. Orients you to your current setup and, for a new project, sets up Sentry end to end with sane defaults — provision a project, install the SDK (errors, tracing, and whatever it enables by default), and confirm real telemetry reaches Sentry. Routes other intents (adding more signals, fixing issues) to the right skill.",
|
|
278
|
+
"sentry-instrument": "Instrument an application with Sentry — detect the platform, install and initialize the SDK if needed, and wire up any signal — error monitoring, tracing/performance, logging, metrics, profiling, session replay, user feedback, cron check-ins, and AI/LLM monitoring (agent runs, token cost, and conversations for OpenAI, Anthropic, Vercel AI, LangChain, Google GenAI, Pydantic AI, and Laravel AI). Use to add Sentry to a project or to capture more than errors.",
|
|
279
|
+
"sentry-otel-exporter-setup": "Configure the OpenTelemetry Collector with Sentry Exporter for multi-project routing and automatic project creation. Use when setting up OTel with Sentry, configuring collector pipelines for traces and logs, or routing telemetry from multiple services to Sentry projects.",
|
|
280
|
+
"sentry-setup-releases": "Set up Sentry releases and deploy tracking — tag events with a version and environment, create the release in CI with its commits, and wire up suspect commits and code mappings, so Sentry can show which release introduced an issue, which commit is responsible, and release health. Use when asked to set up releases, track deploys, see what changed, or when issues show an unknown release or no suspect commit.",
|
|
281
|
+
"sentry-snapshots-cocoa": "Full Sentry Snapshots setup for Apple/Cocoa projects. Use when asked to \"setup SnapshotPreviews\", \"setup Apple snapshot testing\", \"upload Apple snapshots to Sentry\", \"setup Apple snapshot GitHub Actions\", or \"setup Apple selective snapshot testing\"."
|
|
282
|
+
},
|
|
283
|
+
"digests": {
|
|
284
|
+
"sentry-create-alert": "sha256:a2d4011d28593abd8148c95d3f3188bc68db02d8d9c8ae444723d5548f4e9f1f",
|
|
285
|
+
"sentry-debug-issue": "sha256:ee3b1bc93dddab007e54332bc39521e49221528f5350e7af5e0f0e962393c9f1",
|
|
286
|
+
"sentry-fix-stack-traces": "sha256:f66199cfe616abf2ff39b9e953cb30d1e5634f83fcffa8cce1da54b66ae4ad67",
|
|
287
|
+
"sentry-get-started": "sha256:68ae5780ab6713d2fbbcb553df6dbfa67db28589ee467f4e14524ab8c51046df",
|
|
288
|
+
"sentry-instrument": "sha256:2c97fe5dc8aae274ed0657de7bc8dd1c59bd454b44ed00b6bf98f1d010e1e885",
|
|
289
|
+
"sentry-otel-exporter-setup": "sha256:9c8ba54f7ec1b52beb24b0225b5836edcd61971619474d7eee3cd48d27a8d1a5",
|
|
290
|
+
"sentry-setup-releases": "sha256:3a13401662f32806d56b63765be846f6c4305190b2a4f184a1146b71eb373aa4",
|
|
291
|
+
"sentry-snapshots-cocoa": "sha256:9eeee7bc649475f2d8c00cb2725edcf07d9a6ce2784b4fbaf24ba0bd6de8f44b"
|
|
292
|
+
},
|
|
293
|
+
"licensePath": "LICENSE"
|
|
246
294
|
}
|
|
247
295
|
]
|
|
248
296
|
}
|
package/skills/dev-kit/SKILL.md
CHANGED
|
@@ -113,11 +113,10 @@ separate `check` and pure `typecheck` tasks; standalone Oxc projects import
|
|
|
113
113
|
`recommendedOxlintConfig`/`recommendedOxfmtConfig` directly.
|
|
114
114
|
|
|
115
115
|
Run the Effect-patched compiler separately with `vp run typecheck`; neither
|
|
116
|
-
Oxlint's bundled `tsgolint` nor Vite+'s native lint path uses the Effect patch
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
plugin's scope-sensitive rules stay consumer-scoped: enable them per path in
|
|
116
|
+
Oxlint's bundled `tsgolint` nor Vite+'s native lint path uses the Effect patch.
|
|
117
|
+
Keep Oxlint and `@oxlint/plugins` on the matching version expected by the
|
|
118
|
+
preset so Vite+ can execute its JavaScript plugins. The `effect` plugin's
|
|
119
|
+
scope-sensitive rules stay consumer-scoped: enable them per path in
|
|
121
120
|
Effect-owned code, with exceptions for tests and host boundaries.
|
|
122
121
|
|
|
123
122
|
`setup.vitePlus.hooks` converges the Git-ignored `.vite-hooks/_` dispatcher by
|
package/src/bin/dev-kit.ts
CHANGED
|
@@ -283,8 +283,8 @@ const catalogAddCommand = CliCommand.make(
|
|
|
283
283
|
lockfile,
|
|
284
284
|
repoDir,
|
|
285
285
|
sources,
|
|
286
|
-
}) =>
|
|
287
|
-
|
|
286
|
+
}) => {
|
|
287
|
+
const options = {
|
|
288
288
|
repository,
|
|
289
289
|
all,
|
|
290
290
|
dryRun,
|
|
@@ -293,11 +293,15 @@ const catalogAddCommand = CliCommand.make(
|
|
|
293
293
|
lockfilePath: lockfile,
|
|
294
294
|
repoDir,
|
|
295
295
|
sourcesPath: sources,
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
})
|
|
296
|
+
};
|
|
297
|
+
|
|
298
|
+
if (id) Object.assign(options, { id });
|
|
299
|
+
if (license) Object.assign(options, { licensePath: license });
|
|
300
|
+
if (ref) Object.assign(options, { ref });
|
|
301
|
+
if (skillsPath) Object.assign(options, { skillsPath });
|
|
302
|
+
|
|
303
|
+
return addCatalogSource(options);
|
|
304
|
+
},
|
|
301
305
|
).pipe(CliCommand.withDescription("Inspect a repository and approve selected skills."));
|
|
302
306
|
|
|
303
307
|
const catalogListCommand = CliCommand.make(
|
package/src/catalog-manager.ts
CHANGED
|
@@ -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
|
-
|
|
128
|
-
|
|
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
|
|
203
|
+
const inspectOptions = {
|
|
200
204
|
repository: options.repository,
|
|
201
205
|
repoDir: state.repoDir,
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
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
|
|
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
|
-
|
|
199
|
+
const catalog: SkillCatalog = {
|
|
200
200
|
skills: skills.sort((left, right) => left.selector.localeCompare(right.selector)),
|
|
201
201
|
families,
|
|
202
|
-
|
|
203
|
-
|
|
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
|
|
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;
|
package/src/effect-tsgo.ts
CHANGED
|
@@ -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.
|
|
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
|
-
|
|
265
|
+
const plan = {
|
|
266
266
|
alreadyPatched: patchState.alreadyPatched,
|
|
267
|
-
|
|
268
|
-
|
|
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* (
|
package/src/gitignore.ts
CHANGED
|
@@ -50,15 +50,18 @@ type PlannedGitignorePatch = GitignorePatch & {
|
|
|
50
50
|
readonly previousContents: string;
|
|
51
51
|
};
|
|
52
52
|
|
|
53
|
+
type GitignoreContentsPatch = {
|
|
54
|
+
readonly contents: string;
|
|
55
|
+
readonly added: ReadonlyArray<string>;
|
|
56
|
+
};
|
|
57
|
+
|
|
53
58
|
const publicPatch = (patch: PlannedGitignorePatch): GitignorePatch => ({
|
|
54
59
|
path: patch.path,
|
|
55
60
|
changed: patch.changed,
|
|
56
61
|
added: patch.added,
|
|
57
62
|
});
|
|
58
63
|
|
|
59
|
-
export const patchGitignoreContents = (
|
|
60
|
-
current: string,
|
|
61
|
-
): { readonly contents: string; readonly added: ReadonlyArray<string> } => {
|
|
64
|
+
export const patchGitignoreContents = (current: string): GitignoreContentsPatch => {
|
|
62
65
|
const lines = current.split(/\r?\n/);
|
|
63
66
|
const added = DEV_KIT_GITIGNORE_ENTRIES.filter((entry) => !lines.includes(entry));
|
|
64
67
|
|
package/src/manifest.ts
CHANGED
|
@@ -146,13 +146,21 @@ export type NormalizedManifest = {
|
|
|
146
146
|
readonly targets: Readonly<Record<HarnessTarget, NormalizedTargetConfig>>;
|
|
147
147
|
};
|
|
148
148
|
|
|
149
|
-
|
|
149
|
+
type DefaultTargetPaths = {
|
|
150
|
+
readonly [Target in HarnessTarget]: string;
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
type DefaultTargets = {
|
|
154
|
+
readonly [Target in HarnessTarget]: NormalizedTargetConfig;
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
const DEFAULT_TARGET_PATHS: DefaultTargetPaths = {
|
|
150
158
|
agents: ".agents/skills",
|
|
151
159
|
claude: ".claude/skills",
|
|
152
160
|
opencode: ".opencode/skills",
|
|
153
161
|
};
|
|
154
162
|
|
|
155
|
-
const DEFAULT_TARGETS:
|
|
163
|
+
const DEFAULT_TARGETS: DefaultTargets = {
|
|
156
164
|
agents: { enabled: true, mode: "copy", path: DEFAULT_TARGET_PATHS.agents },
|
|
157
165
|
claude: { enabled: false, mode: "symlink", path: DEFAULT_TARGET_PATHS.claude },
|
|
158
166
|
opencode: { enabled: false, mode: "symlink", path: DEFAULT_TARGET_PATHS.opencode },
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Dillon Mulroy
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
// Vendored from dmmulroy/anti-slop commit 9b80d9a5c317d3af94d88a577bdbde4d9a45f7be.
|
|
2
|
+
// The upstream MIT notice is retained in LICENSE.
|
|
3
|
+
import { definePlugin } from "@oxlint/plugins";
|
|
4
|
+
|
|
5
|
+
import { noChainedTypeAssertionsRule } from "./rules/no-chained-type-assertions.ts";
|
|
6
|
+
import { noConditionalEmptyObjectSpreadRule } from "./rules/no-conditional-empty-object-spread.ts";
|
|
7
|
+
import { noKnownValueWideningRule } from "./rules/no-known-value-widening.ts";
|
|
8
|
+
import { noModuleMockingRule } from "./rules/no-module-mocking.ts";
|
|
9
|
+
import { noObjectParametersRule } from "./rules/no-object-parameters.ts";
|
|
10
|
+
import { noReflectApplyRule } from "./rules/no-reflect-apply.ts";
|
|
11
|
+
import { noReflectGetRule } from "./rules/no-reflect-get.ts";
|
|
12
|
+
import { noRuntimeTypeofRule } from "./rules/no-runtime-typeof.ts";
|
|
13
|
+
import { noForbiddenTermInSymbolNamesRule } from "./rules/no-shape-in-symbol-names.ts";
|
|
14
|
+
import { noUnknownParametersRule } from "./rules/no-unknown-parameters.ts";
|
|
15
|
+
import { noUnknownReturnsRule } from "./rules/no-unknown-returns.ts";
|
|
16
|
+
import { noUnknownTypeAliasesRule } from "./rules/no-unknown-type-aliases.ts";
|
|
17
|
+
import { noUnsafeDictionaryTypeRule } from "./rules/no-unsafe-dictionary-type.ts";
|
|
18
|
+
import { noWidenThenAssertRule } from "./rules/no-widen-then-assert.ts";
|
|
19
|
+
import { requireSafetyCommentForTypeAssertionRule } from "./rules/require-safety-comment-for-type-assertion.ts";
|
|
20
|
+
|
|
21
|
+
/** Generic Oxlint rules that reject low-evidence and low-signal implementation patterns. */
|
|
22
|
+
const antiSlopPlugin = definePlugin({
|
|
23
|
+
meta: { name: "anti-slop" },
|
|
24
|
+
rules: {
|
|
25
|
+
"no-chained-type-assertions": noChainedTypeAssertionsRule,
|
|
26
|
+
"no-conditional-empty-object-spread": noConditionalEmptyObjectSpreadRule,
|
|
27
|
+
"no-known-value-widening": noKnownValueWideningRule,
|
|
28
|
+
"no-module-mocking": noModuleMockingRule,
|
|
29
|
+
"no-object-parameters": noObjectParametersRule,
|
|
30
|
+
"no-reflect-apply": noReflectApplyRule,
|
|
31
|
+
"no-reflect-get": noReflectGetRule,
|
|
32
|
+
"no-runtime-typeof": noRuntimeTypeofRule,
|
|
33
|
+
"no-unsafe-dictionary-type": noUnsafeDictionaryTypeRule,
|
|
34
|
+
"no-shape-in-symbol-names": noForbiddenTermInSymbolNamesRule,
|
|
35
|
+
"no-unknown-parameters": noUnknownParametersRule,
|
|
36
|
+
"no-unknown-returns": noUnknownReturnsRule,
|
|
37
|
+
"no-unknown-type-aliases": noUnknownTypeAliasesRule,
|
|
38
|
+
"no-widen-then-assert": noWidenThenAssertRule,
|
|
39
|
+
"require-safety-comment-for-type-assertion": requireSafetyCommentForTypeAssertionRule,
|
|
40
|
+
},
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
export default antiSlopPlugin;
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { defineRule, type ESTree } from "@oxlint/plugins";
|
|
2
|
+
|
|
3
|
+
type TypeAssertionExpression = ESTree.TSAsExpression | ESTree.TSTypeAssertion;
|
|
4
|
+
|
|
5
|
+
function isTypeAssertionExpression(node: ESTree.Node): node is TypeAssertionExpression {
|
|
6
|
+
return node.type === "TSAsExpression" || node.type === "TSTypeAssertion";
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
function unwrapParenthesizedExpression(expression: ESTree.Expression): ESTree.Expression {
|
|
10
|
+
let current = expression;
|
|
11
|
+
|
|
12
|
+
while (current.type === "ParenthesizedExpression") {
|
|
13
|
+
current = current.expression;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
return current;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function isConstAssertion(node: TypeAssertionExpression): boolean {
|
|
20
|
+
const { typeAnnotation } = node;
|
|
21
|
+
|
|
22
|
+
return (
|
|
23
|
+
typeAnnotation.type === "TSTypeReference" &&
|
|
24
|
+
typeAnnotation.typeName.type === "Identifier" &&
|
|
25
|
+
typeAnnotation.typeName.name === "const"
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function isOutermostAssertionInChain(node: TypeAssertionExpression): boolean {
|
|
30
|
+
let current: ESTree.Expression = node;
|
|
31
|
+
let parent = node.parent;
|
|
32
|
+
|
|
33
|
+
while (parent.type === "ParenthesizedExpression" && parent.expression === current) {
|
|
34
|
+
current = parent;
|
|
35
|
+
parent = parent.parent;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return !isTypeAssertionExpression(parent) || parent.expression !== current;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function isForbiddenAssertionChain(node: TypeAssertionExpression): boolean {
|
|
42
|
+
let assertionCount = 0;
|
|
43
|
+
let hasNonConstAssertion = false;
|
|
44
|
+
let current: ESTree.Expression = node;
|
|
45
|
+
|
|
46
|
+
while (isTypeAssertionExpression(current)) {
|
|
47
|
+
assertionCount += 1;
|
|
48
|
+
hasNonConstAssertion ||= !isConstAssertion(current);
|
|
49
|
+
current = unwrapParenthesizedExpression(current.expression);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return assertionCount > 1 && hasNonConstAssertion;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/** Disallow nested TypeScript type assertions, while permitting chains made only of const assertions. */
|
|
56
|
+
export const noChainedTypeAssertionsRule = defineRule({
|
|
57
|
+
meta: {
|
|
58
|
+
type: "problem",
|
|
59
|
+
docs: {
|
|
60
|
+
description:
|
|
61
|
+
"Disallow chained TypeScript as and angle-bracket assertions, including parenthesized chains.",
|
|
62
|
+
},
|
|
63
|
+
messages: {
|
|
64
|
+
chained:
|
|
65
|
+
"This assertion chain discards type evidence. Keep the original precise type, or parse untrusted input at its boundary before narrowing it.",
|
|
66
|
+
},
|
|
67
|
+
},
|
|
68
|
+
create(context) {
|
|
69
|
+
const checkTypeAssertion = (node: TypeAssertionExpression) => {
|
|
70
|
+
if (!isOutermostAssertionInChain(node) || !isForbiddenAssertionChain(node)) return;
|
|
71
|
+
context.report({ node, messageId: "chained" });
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
return {
|
|
75
|
+
TSAsExpression: checkTypeAssertion,
|
|
76
|
+
TSTypeAssertion: checkTypeAssertion,
|
|
77
|
+
};
|
|
78
|
+
},
|
|
79
|
+
});
|