@visulima/vis 1.0.0-alpha.2 → 1.0.0-alpha.20

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (147) hide show
  1. package/CHANGELOG.md +830 -14
  2. package/LICENSE.md +7667 -0
  3. package/README.md +322 -19
  4. package/dist/bin.js +1 -146
  5. package/dist/config/index.d.ts +2795 -0
  6. package/dist/config/index.js +1 -0
  7. package/dist/generate/index.d.ts +157 -0
  8. package/dist/generate/index.js +1 -0
  9. package/dist/packem_chunks/bin.js +1295 -0
  10. package/dist/packem_chunks/config.js +19 -0
  11. package/dist/packem_chunks/doctor-probe.js +2 -0
  12. package/dist/packem_chunks/fix.js +11 -0
  13. package/dist/packem_chunks/handler.js +1 -0
  14. package/dist/packem_chunks/handler10.js +1 -0
  15. package/dist/packem_chunks/handler11.js +5 -0
  16. package/dist/packem_chunks/handler12.js +1 -0
  17. package/dist/packem_chunks/handler13.js +27 -0
  18. package/dist/packem_chunks/handler14.js +5 -0
  19. package/dist/packem_chunks/handler15.js +1 -0
  20. package/dist/packem_chunks/handler16.js +1 -0
  21. package/dist/packem_chunks/handler17.js +1 -0
  22. package/dist/packem_chunks/handler18.js +1 -0
  23. package/dist/packem_chunks/handler19.js +1 -0
  24. package/dist/packem_chunks/handler2.js +2 -0
  25. package/dist/packem_chunks/handler20.js +5 -0
  26. package/dist/packem_chunks/handler21.js +2 -0
  27. package/dist/packem_chunks/handler22.js +2 -0
  28. package/dist/packem_chunks/handler23.js +18 -0
  29. package/dist/packem_chunks/handler24.js +1 -0
  30. package/dist/packem_chunks/handler25.js +1 -0
  31. package/dist/packem_chunks/handler26.js +5 -0
  32. package/dist/packem_chunks/handler27.js +1 -0
  33. package/dist/packem_chunks/handler28.js +3 -0
  34. package/dist/packem_chunks/handler29.js +1 -0
  35. package/dist/packem_chunks/handler3.js +4 -0
  36. package/dist/packem_chunks/handler30.js +7 -0
  37. package/dist/packem_chunks/handler31.js +33 -0
  38. package/dist/packem_chunks/handler32.js +3 -0
  39. package/dist/packem_chunks/handler33.js +1 -0
  40. package/dist/packem_chunks/handler34.js +26 -0
  41. package/dist/packem_chunks/handler35.js +3 -0
  42. package/dist/packem_chunks/handler36.js +7 -0
  43. package/dist/packem_chunks/handler37.js +22 -0
  44. package/dist/packem_chunks/handler38.js +428 -0
  45. package/dist/packem_chunks/handler39.js +6 -0
  46. package/dist/packem_chunks/handler4.js +8 -0
  47. package/dist/packem_chunks/handler40.js +24 -0
  48. package/dist/packem_chunks/handler41.js +10 -0
  49. package/dist/packem_chunks/handler42.js +153 -0
  50. package/dist/packem_chunks/handler43.js +25 -0
  51. package/dist/packem_chunks/handler44.js +24 -0
  52. package/dist/packem_chunks/handler45.js +213 -0
  53. package/dist/packem_chunks/handler46.js +3 -0
  54. package/dist/packem_chunks/handler47.js +27 -0
  55. package/dist/packem_chunks/handler48.js +167 -0
  56. package/dist/packem_chunks/handler49.js +34 -0
  57. package/dist/packem_chunks/handler5.js +1 -0
  58. package/dist/packem_chunks/handler6.js +1 -0
  59. package/dist/packem_chunks/handler7.js +1 -0
  60. package/dist/packem_chunks/handler8.js +1 -0
  61. package/dist/packem_chunks/handler9.js +2 -0
  62. package/dist/packem_chunks/heal-accept.js +10 -0
  63. package/dist/packem_chunks/heal.js +14 -0
  64. package/dist/packem_chunks/help-command.js +18 -0
  65. package/dist/packem_chunks/index.js +7 -0
  66. package/dist/packem_chunks/keys-refresh.js +4 -0
  67. package/dist/packem_chunks/list.js +3 -0
  68. package/dist/packem_chunks/loader.js +1 -0
  69. package/dist/packem_chunks/prune.js +3 -0
  70. package/dist/packem_chunks/run.js +1 -0
  71. package/dist/packem_chunks/status.js +2 -0
  72. package/dist/packem_chunks/sync.js +2 -0
  73. package/dist/packem_chunks/sync2.js +2 -0
  74. package/dist/packem_chunks/tar.js +3 -0
  75. package/dist/packem_chunks/tripwire.js +2 -0
  76. package/dist/packem_shared/advisories-DsynpacV.js +1 -0
  77. package/dist/packem_shared/ai-analysis-uYuTIIXi.js +68 -0
  78. package/dist/packem_shared/ai-cache-DuwHYx2O.js +1 -0
  79. package/dist/packem_shared/ai-fix-DzrA-dVz.js +43 -0
  80. package/dist/packem_shared/applyDefaults-BOVDw1jD.js +1 -0
  81. package/dist/packem_shared/build-scripts-DsWMSWDs.js +1 -0
  82. package/dist/packem_shared/cache-directory-DQak1Vjc.js +1 -0
  83. package/dist/packem_shared/cyclonedx-CiHXuG8M.js +4 -0
  84. package/dist/packem_shared/definePlugin-CWm4Dv_t.js +1 -0
  85. package/dist/packem_shared/dependency-scan-DC3nAFHS.js +1 -0
  86. package/dist/packem_shared/docker-B-CIN_nj.js +60 -0
  87. package/dist/packem_shared/failure-log-C3LEMmkq.js +2 -0
  88. package/dist/packem_shared/flakiness-Dq6K4ymq.js +1 -0
  89. package/dist/packem_shared/giget-CcEy_Elm.js +2 -0
  90. package/dist/packem_shared/glob-MHJQjR39-CQ2GC0b_.js +1 -0
  91. package/dist/packem_shared/index-DH-5hsrC.js +1 -0
  92. package/dist/packem_shared/lifecycle-Dv3nAtoD.js +2 -0
  93. package/dist/packem_shared/lockfile-C5DYMHVq.js +1 -0
  94. package/dist/packem_shared/manifests-B0fMp872.js +1 -0
  95. package/dist/packem_shared/min-release-age-BFozFonQ.js +34 -0
  96. package/dist/packem_shared/native-config-sync-Dvi1g2nQ.js +21 -0
  97. package/dist/packem_shared/otelPlugin-CJR2T_lk.js +1 -0
  98. package/dist/packem_shared/registry-keys-CewRFW0e.js +1 -0
  99. package/dist/packem_shared/resolve-explicit-CC4Kifk5.js +5 -0
  100. package/dist/packem_shared/run-summary-utils-BaBGP3bo.js +1 -0
  101. package/dist/packem_shared/runtime-check-BusAwPb2.js +1 -0
  102. package/dist/packem_shared/scan-progress-CMynp3eA.js +2 -0
  103. package/dist/packem_shared/selectors-B2ISH581.js +3 -0
  104. package/dist/packem_shared/signatures-5ZdjJ2Pu.js +2 -0
  105. package/dist/packem_shared/symbols-CQmER5MT.js +1 -0
  106. package/dist/packem_shared/toolchain-Cc3cwyLP.js +5 -0
  107. package/dist/packem_shared/typosquats-BCeR-sLf.js +1 -0
  108. package/dist/packem_shared/use-measured-height-DjYgUOKk.js +1 -0
  109. package/dist/packem_shared/utils-DrNg0XTR.js +1 -0
  110. package/dist/packem_shared/verify-07kUNTuP.js +1 -0
  111. package/dist/packem_shared/vis-update-app-CFrlJ3mW.js +1 -0
  112. package/dist/packem_shared/xxh3-DrAUNq4n.js +1 -0
  113. package/index.d.ts +358 -0
  114. package/index.js +609 -0
  115. package/package.json +57 -22
  116. package/schemas/project.schema.json +872 -0
  117. package/schemas/vis-config.schema.json +4306 -0
  118. package/skills/vis/SKILL.md +96 -0
  119. package/templates/buildkite-ci/.buildkite/pipeline.yml.tera +85 -0
  120. package/templates/buildkite-ci/template.yml +20 -0
  121. package/dist/ai-analysis.d.ts +0 -40
  122. package/dist/ai-cache.d.ts +0 -21
  123. package/dist/bin.d.ts +0 -1
  124. package/dist/catalog.d.ts +0 -110
  125. package/dist/commands/affected.d.ts +0 -3
  126. package/dist/commands/ai.d.ts +0 -3
  127. package/dist/commands/analyze.d.ts +0 -3
  128. package/dist/commands/check.d.ts +0 -3
  129. package/dist/commands/graph.d.ts +0 -3
  130. package/dist/commands/hook/constants.d.ts +0 -8
  131. package/dist/commands/hook/index.d.ts +0 -3
  132. package/dist/commands/hook/install.d.ts +0 -7
  133. package/dist/commands/hook/migrate.d.ts +0 -27
  134. package/dist/commands/hook/uninstall.d.ts +0 -3
  135. package/dist/commands/migrate/constants.d.ts +0 -12
  136. package/dist/commands/migrate/deps.d.ts +0 -32
  137. package/dist/commands/migrate/index.d.ts +0 -3
  138. package/dist/commands/migrate/json.d.ts +0 -20
  139. package/dist/commands/migrate/lint-staged.d.ts +0 -62
  140. package/dist/commands/migrate/types.d.ts +0 -20
  141. package/dist/commands/run.d.ts +0 -3
  142. package/dist/commands/staged.d.ts +0 -3
  143. package/dist/commands/update.d.ts +0 -3
  144. package/dist/config.d.ts +0 -40
  145. package/dist/config.js +0 -1
  146. package/dist/package-manager.d.ts +0 -23
  147. package/dist/workspace.d.ts +0 -58
@@ -0,0 +1,4306 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://visulima.com/schemas/vis-config.schema.json",
4
+ "title": "vis.config.ts",
5
+ "description": "Workspace configuration for @visulima/vis. Used by defineConfig() in vis.config.ts. This schema is for non-TypeScript editors; TypeScript users get autocomplete from defineConfig() types.",
6
+ "type": "object",
7
+ "properties": {
8
+ "ai": {
9
+ "type": "object",
10
+ "properties": {
11
+ "cacheTtl": {
12
+ "type": "number",
13
+ "description": "Cache TTL in milliseconds. Overrides default (1h / 30min for security)."
14
+ },
15
+ "priority": {
16
+ "type": "object",
17
+ "additionalProperties": {
18
+ "type": "number"
19
+ },
20
+ "description": "Override default provider priority. Higher number = preferred."
21
+ },
22
+ "provider": {
23
+ "type": "string",
24
+ "description": "Use a specific provider instead of auto-detecting (e.g., `\"claude\"`, `\"gemini\"`)."
25
+ }
26
+ },
27
+ "additionalProperties": false,
28
+ "description": "AI analysis configuration"
29
+ },
30
+ "branchScopedCache": {
31
+ "type": "boolean",
32
+ "description": "Scope the task-runner cache directory by the current git branch. When `true`, caches are stored under `<cacheDir>/branches/<slug>` so `main` and feature branches stop thrashing each other — generated artefacts (schemas, `.d.ts` snapshots) that legitimately differ across branches no longer oscillate the cache contents.\n\nFalls back to the unscoped path on detached HEAD, non-git workspaces, or when git isn't available.",
33
+ "default": false
34
+ },
35
+ "codeowners": {
36
+ "$ref": "#/$defs/CodeownersConfig",
37
+ "description": "Code ownership configuration. Controls how `vis sync codeowners` renders the generated CODEOWNERS file."
38
+ },
39
+ "constraints": {
40
+ "type": "object",
41
+ "properties": {
42
+ "dependencyKindRules": {
43
+ "type": "object",
44
+ "properties": {
45
+ "noDevDependencyOnProductionDep": {
46
+ "type": "boolean",
47
+ "description": "When true, a \"library\" project must not have workspace-internal devDependencies on other libraries that are also in its production dependencies. Prevents publishing libraries that silently rely on dev-only workspace packages.",
48
+ "default": false
49
+ },
50
+ "noProductionDependencyOnApplication": {
51
+ "type": "boolean",
52
+ "description": "When true, production `dependencies` must not point to deployment-target projects — `application`, `service`, or `tool`. devDependencies on those are allowed (e.g., for testing).",
53
+ "default": false
54
+ }
55
+ },
56
+ "additionalProperties": false,
57
+ "description": "Rules based on the dependency kind (static vs devDependency vs peerDependency)"
58
+ },
59
+ "enforceLayerRelationships": {
60
+ "type": "boolean",
61
+ "description": "When true, projects can only depend on projects at the same or lower layer in the hierarchy:\n\n configuration < library < scaffolding < tool < automation < application\n\nProjects without an explicit `layer` are unconstrained.",
62
+ "default": false
63
+ },
64
+ "tagRelationships": {
65
+ "type": "object",
66
+ "additionalProperties": {
67
+ "type": "array",
68
+ "items": {
69
+ "type": "string"
70
+ }
71
+ },
72
+ "description": "Tag-based dependency rules"
73
+ },
74
+ "typeBoundaries": {
75
+ "type": "object",
76
+ "properties": {
77
+ "allowedDependencyTypes": {
78
+ "type": "object",
79
+ "additionalProperties": {
80
+ "type": "array",
81
+ "items": {
82
+ "type": "string"
83
+ }
84
+ },
85
+ "description": "Custom rules mapping project types to allowed dependency types. Example: `{ \"application\": [\"library\"] }` means applications can only depend on libraries."
86
+ },
87
+ "enforceApplicationBoundary": {
88
+ "type": "boolean",
89
+ "description": "When true, no project may depend on a deployment target — projects of type `application`, `service`, or `tool`. Deployment targets are standalone artefacts, not reusable libraries.",
90
+ "default": true
91
+ }
92
+ },
93
+ "additionalProperties": false,
94
+ "description": "Project-type-based dependency rules"
95
+ }
96
+ },
97
+ "additionalProperties": false,
98
+ "description": "Project dependency constraints. Enforced after building the project graph, before running tasks."
99
+ },
100
+ "create": {
101
+ "type": "object",
102
+ "properties": {
103
+ "auth": {
104
+ "type": "string",
105
+ "description": "Authorization token for downloading private repository templates. Passed as Bearer token to the git host API. Can also be set via GIGET_AUTH, GITHUB_TOKEN, or GH_TOKEN environment variables."
106
+ },
107
+ "defaultEditor": {
108
+ "type": "string",
109
+ "const": "vscode",
110
+ "description": "Default editor to configure after scaffolding. When set, `vis create` automatically generates editor config files.",
111
+ "examples": [
112
+ "vscode"
113
+ ]
114
+ },
115
+ "defaultPm": {
116
+ "type": "string",
117
+ "enum": [
118
+ "bun",
119
+ "npm",
120
+ "pnpm",
121
+ "yarn"
122
+ ],
123
+ "description": "Default package manager for new standalone projects. When set, skips the PM selection prompt in interactive mode."
124
+ },
125
+ "defaultProvider": {
126
+ "type": "string",
127
+ "enum": [
128
+ "bitbucket",
129
+ "github",
130
+ "gitlab",
131
+ "sourcehut"
132
+ ],
133
+ "description": "Default giget provider for `owner/repo` shorthand inputs.",
134
+ "default": "github"
135
+ },
136
+ "gitInit": {
137
+ "type": "boolean",
138
+ "description": "Initialize a git repository after scaffolding standalone projects.",
139
+ "default": false
140
+ },
141
+ "install": {
142
+ "type": "boolean",
143
+ "description": "Install dependencies automatically after scaffolding.",
144
+ "default": true
145
+ },
146
+ "preferOffline": {
147
+ "type": "boolean",
148
+ "description": "Prefer locally cached templates over re-downloading. Useful for offline development or slow connections.",
149
+ "default": false
150
+ },
151
+ "registry": {
152
+ "anyOf": [
153
+ {
154
+ "type": "string"
155
+ },
156
+ {
157
+ "type": "boolean",
158
+ "const": false
159
+ }
160
+ ],
161
+ "description": "Custom template registry URL. When set, giget checks this registry for template metadata before falling back to direct provider resolution. Set to `false` to disable registry lookup entirely."
162
+ },
163
+ "templates": {
164
+ "type": "object",
165
+ "additionalProperties": {
166
+ "type": "string"
167
+ },
168
+ "description": "Named template aliases for quick access. Maps short names to full giget source strings."
169
+ }
170
+ },
171
+ "additionalProperties": false,
172
+ "description": "Configuration for the `vis create` scaffolding command. Controls template downloads (via giget), default options, and post-creation behavior."
173
+ },
174
+ "editorconfig": {
175
+ "type": "boolean",
176
+ "description": "Discover `.editorconfig` for indent / line-ending defaults during file transformations (sort-package-json, migrate, hook, pm overrides, workspace catalog rewrites). Per-command flags can still override.",
177
+ "default": true
178
+ },
179
+ "extends": {
180
+ "anyOf": [
181
+ {
182
+ "type": "string"
183
+ },
184
+ {
185
+ "type": "array",
186
+ "items": {
187
+ "type": "string"
188
+ }
189
+ }
190
+ ],
191
+ "description": "Inherit configuration from one or more parent configs. Entries are resolved left-to-right (later wins) and the consumer's own values always override anything pulled in from `extends`.\n\nEach entry is either:\n- a relative path (`./shared.config.ts`, `../shared.config.ts`) — resolved against the file declaring `extends`;\n- an npm package name (`@acme/vis-preset`) — resolved via Node.js module resolution from the consumer file.\n\nAbsolute paths are rejected — they break across machines and CI. Cycles raise `VisConfigCycleError` during load."
192
+ },
193
+ "fileGroups": {
194
+ "type": "object",
195
+ "additionalProperties": {
196
+ "type": "array",
197
+ "items": {
198
+ "type": "string"
199
+ }
200
+ },
201
+ "description": "Named file-group patterns, reusable from target `inputs` via the `@filegroup:&lt;name>` token. File groups are resolved relative to each project root at discovery time."
202
+ },
203
+ "generator": {
204
+ "type": "object",
205
+ "properties": {
206
+ "auth": {
207
+ "type": "string",
208
+ "description": "Authorization token forwarded to giget when fetching `git://`/`npm://` remote templates. Falls back to `GIGET_AUTH` / `GITHUB_TOKEN` / `GH_TOKEN` env vars."
209
+ },
210
+ "preferOffline": {
211
+ "type": "boolean",
212
+ "description": "Prefer locally cached remote templates over re-downloading. Overridable per invocation via `--prefer-offline`.",
213
+ "default": false
214
+ },
215
+ "templates": {
216
+ "type": "array",
217
+ "items": {
218
+ "type": "string"
219
+ },
220
+ "description": "Extra directories to scan for templates. Each directory is checked for both native templates (`&lt;name>.ts`) and moon-format directories (containing `template.yml`)."
221
+ }
222
+ },
223
+ "additionalProperties": false,
224
+ "description": "Configuration for the `vis generate` in-repo scaffolding command. Points at additional template directories beyond the defaults (`.vis/templates/` and `.moon/templates/`)."
225
+ },
226
+ "inferTargets": {
227
+ "anyOf": [
228
+ {
229
+ "type": "object",
230
+ "additionalProperties": {
231
+ "type": "boolean"
232
+ }
233
+ },
234
+ {
235
+ "type": "boolean"
236
+ }
237
+ ],
238
+ "description": "Auto-create targets from detected config files (Project Crystal-style). Inferred targets sit *below* explicit ones — anything in `package.json#scripts`, `project.json#targets`, or `vis.task.ts` wins per-key, so opting in never overrides existing setups.\n\nBuilt-in detectors and the targets they synthesize:\n\n- **App frameworks** — `nuxt` (build/dev/preview/generate), `next` (build/dev/start), `remix` (build/dev/start), `astro` (build/dev), `gatsby` (build/develop/serve), `docusaurus` (build/start/serve).\n- **Bundlers** — `vite` (build/dev/preview), `rolldown` (build), `tsdown` (build), `tsup` (build), `packem` (build), `rollup` (build), `webpack` (build).\n- **Docs sites** — `vitepress` (docs:build/docs:dev/docs:preview), `typedoc` (docs).\n- **Server frameworks** — `nest` (build/start/start:dev).\n- **Test runners** — `vitest` (test/test:watch), `jest` (test/test:watch), `bun` (test), `playwright` (test:e2e), `cypress` (test:e2e/cypress:open).\n- **Stories** — `storybook` (storybook/build-storybook).\n- **Type checking** — `typescript` (typecheck via `tsc --noEmit`).\n- **Lint / format** — `eslint` (lint), `prettier` (format / format:check), `biome` (lint, format), `oxlint` (lint), `oxfmt` (format / format:check), `stylelint` (lint:css), `knip` (knip).\n- **Runtimes** — `deno` (test/lint/fmt/check).\n- **Database tooling** — `prisma` (db:generate/db:migrate/ db:push/db:studio), `drizzle` (db:generate/db:migrate/ db:push/db:studio).\n- **Codegen / release** — `graphql-codegen` (codegen), `api-extractor` (api-extract), `changeset` (changeset:version / changeset:publish / changeset:status).\n\nTrigger: presence of any matching config file in the project root. Most detectors additionally match when their framework appears in `dependencies` / `devDependencies` / `peerDependencies` / `optionalDependencies` — covering convention-only setups (e.g. vitest with default config). Detectors that intentionally require a config file (because the package frequently appears transitively and a dep-only match would synthesize broken commands): `vite`, `rolldown`, `rollup`, `webpack`, `storybook`, `nest`, `remix`, `vitepress`, `bun`, `deno`, `changeset`.\n\nConflict resolution: detectors are evaluated in registration order (see `BUILT_IN_DETECTORS`) and the first to claim a target name wins. Per-name priorities: `build` → nuxt > next > remix > astro > gatsby > docusaurus > vite > nest > rolldown > tsdown > tsup > packem > rollup > webpack; `test` → vitest > jest > bun > deno; `test:e2e` → playwright > cypress; `lint` → eslint > biome > oxlint > deno; `format` → prettier > biome > oxfmt; `db:*` → prisma > drizzle.\n\nAlso accepts an object form (`{ vite: false, vitest: true }`) to opt individual detectors in or out by name. Detectors omitted from the object run at their default (enabled). Useful when one detector misfires for a given workspace without disabling the rest.",
239
+ "default": false
240
+ },
241
+ "install": {
242
+ "type": "object",
243
+ "properties": {
244
+ "backend": {
245
+ "type": "string",
246
+ "enum": [
247
+ "aube",
248
+ "auto",
249
+ "bun",
250
+ "npm",
251
+ "pnpm",
252
+ "yarn"
253
+ ],
254
+ "description": "Which package manager performs install/add/remove/etc.\n- `auto` (default): use `aube` when it is on PATH; otherwise fall back to the lockfile-detected PM.\n- explicit name: always use that PM. Errors when the named binary is missing rather than silently falling back.",
255
+ "default": "auto"
256
+ },
257
+ "corepack": {
258
+ "anyOf": [
259
+ {
260
+ "type": "string",
261
+ "const": "auto"
262
+ },
263
+ {
264
+ "type": "boolean"
265
+ }
266
+ ],
267
+ "description": "Whether to dispatch PM invocations through `corepack`.\n- `\"auto\"` (default): use corepack only when the workspace pins a PM via the `packageManager` field AND `corepack` is on PATH AND the PM is one corepack manages (pnpm/yarn/npm).\n- `true`: always prefix `corepack` when the binary is on PATH and the PM is corepack-managed (errors loudly otherwise).\n- `false`: never go through corepack — invoke the PM directly.\n\nMirrors nypm's `corepack: true` flag. Bun, deno, and aube are never wrapped — corepack does not manage them.",
268
+ "default": "auto"
269
+ }
270
+ },
271
+ "additionalProperties": false,
272
+ "description": "Installer backend selection for `vis install` / `vis add` / `vis remove` / `vis update` / `vis ci`.\n\nLets users opt into [aube](https://github.com/endevco/aube) — a Rust-native package manager that reads/writes pnpm/npm/yarn/bun lockfiles in place — as the default installer, while keeping a single switch to fall back to the conventional PM detected from the lockfile.\n\nResolution precedence (highest first): 1. CLI flag (`--installer &lt;name>` / `--no-aube`) 2. Env var `VIS_INSTALLER` 3. This config field 4. Auto-detect (the default)\n\nAube must be installed separately — `vis` does not bundle it. Install via npm (`@endevco/aube`), `mise use -g aube`, or `brew install endevco/tap/aube`."
273
+ },
274
+ "mcpPromote": {
275
+ "type": "object",
276
+ "properties": {
277
+ "enabled": {
278
+ "type": "boolean",
279
+ "description": "Show the vis-mcp promotion notice on successful command completion.",
280
+ "default": true
281
+ }
282
+ },
283
+ "additionalProperties": false,
284
+ "description": "`vis-mcp` promotion notice shown after successful commands when an AI CLI (Claude Code, Cursor, Windsurf, Continue, Zed, Cline) is installed but `@visulima/vis-mcp` is not wired into its config.\n\nShown at most once every 14 days; skipped in CI, non-TTY shells, during `--help`/`--version`/`ai`/`mcp` invocations, and when `VIS_NO_MCP_PROMOTE=1` is set. Set `enabled: false` to silence permanently for this workspace."
285
+ },
286
+ "namedInputs": {
287
+ "type": "object",
288
+ "additionalProperties": {
289
+ "type": "array",
290
+ "items": {
291
+ "anyOf": [
292
+ {
293
+ "type": "string"
294
+ },
295
+ {
296
+ "anyOf": [
297
+ {
298
+ "type": "object",
299
+ "properties": {
300
+ "fileset": {
301
+ "anyOf": [
302
+ {
303
+ "type": "object",
304
+ "properties": {
305
+ "base": {
306
+ "type": "string",
307
+ "enum": [
308
+ "package",
309
+ "workspace"
310
+ ],
311
+ "description": "Anchor for the pattern."
312
+ },
313
+ "pattern": {
314
+ "type": "string",
315
+ "description": "Glob pattern (may start with `!` for negation)."
316
+ }
317
+ },
318
+ "required": [
319
+ "base",
320
+ "pattern"
321
+ ],
322
+ "additionalProperties": false,
323
+ "description": "Object form of a fileset pattern, for anchoring to the workspace root."
324
+ },
325
+ {
326
+ "type": "string"
327
+ }
328
+ ]
329
+ }
330
+ },
331
+ "required": [
332
+ "fileset"
333
+ ],
334
+ "additionalProperties": false,
335
+ "description": "An input based on file patterns.\n\n`fileset` may be a bare glob string (package-root relative) or an object form `{ pattern, base }` to anchor explicitly to the workspace root. Negation (`!` prefix) works in both forms."
336
+ },
337
+ {
338
+ "type": "object",
339
+ "properties": {
340
+ "runtime": {
341
+ "type": "string"
342
+ }
343
+ },
344
+ "required": [
345
+ "runtime"
346
+ ],
347
+ "additionalProperties": false,
348
+ "description": "An input based on a runtime command."
349
+ },
350
+ {
351
+ "type": "object",
352
+ "properties": {
353
+ "env": {
354
+ "type": "string"
355
+ }
356
+ },
357
+ "required": [
358
+ "env"
359
+ ],
360
+ "additionalProperties": false,
361
+ "description": "An input based on environment variables."
362
+ },
363
+ {
364
+ "type": "object",
365
+ "properties": {
366
+ "externalDependencies": {
367
+ "type": "array",
368
+ "items": {
369
+ "type": "string"
370
+ }
371
+ }
372
+ },
373
+ "required": [
374
+ "externalDependencies"
375
+ ],
376
+ "additionalProperties": false,
377
+ "description": "An input based on external dependency versions."
378
+ }
379
+ ],
380
+ "description": "Defines an input for cache invalidation."
381
+ }
382
+ ]
383
+ }
384
+ },
385
+ "description": "Named input patterns inherited by every project target. Equivalent to task-runner's `namedInputs` but configurable from the vis config."
386
+ },
387
+ "overrides": {
388
+ "type": "object",
389
+ "additionalProperties": {
390
+ "type": "string"
391
+ },
392
+ "description": "Package override mappings applied during migration (e.g., `{ \"lodash\": \"lodash-es\" }`)"
393
+ },
394
+ "plugins": {
395
+ "type": "array",
396
+ "items": {
397
+ "$ref": "#/$defs/VisPlugin"
398
+ },
399
+ "description": "Plugins — each plugin registers typed hooks that fire at run / task / cache boundaries. See {@link VisPlugin } for the contract. Prefer plugins over per-target shell hooks when behaviour needs access to task metadata, results, or cache state."
400
+ },
401
+ "policy": {
402
+ "type": "object",
403
+ "properties": {
404
+ "bannedDeps": {
405
+ "type": "object",
406
+ "additionalProperties": {
407
+ "anyOf": [
408
+ {
409
+ "type": "string"
410
+ },
411
+ {
412
+ "type": "object",
413
+ "properties": {
414
+ "packages": {
415
+ "type": "array",
416
+ "items": {
417
+ "type": "string"
418
+ }
419
+ },
420
+ "paths": {
421
+ "type": "array",
422
+ "items": {
423
+ "type": "string"
424
+ }
425
+ },
426
+ "reason": {
427
+ "type": "string"
428
+ },
429
+ "replacement": {
430
+ "type": "string"
431
+ }
432
+ },
433
+ "required": [
434
+ "reason"
435
+ ],
436
+ "additionalProperties": false
437
+ }
438
+ ]
439
+ },
440
+ "description": "Map of dep names or globs → reason (or `{ reason, replacement, packages?, paths? }`). Internal/workspace deps are never flagged here; the workspace-protocol lint owns those.\n\nOptional `packages` (globs over the declaring package's `name`) and `paths` (globs over the workspace-relative `packageDir`) narrow where the rule applies. With both set, either match is enough. Omit both to ban anywhere — the default."
441
+ },
442
+ "customTypes": {
443
+ "type": "object",
444
+ "properties": {
445
+ "autofix": {
446
+ "anyOf": [
447
+ {
448
+ "type": "string",
449
+ "const": "prompt"
450
+ },
451
+ {
452
+ "type": "boolean"
453
+ }
454
+ ],
455
+ "description": "Three-state autofix opt-out. See `workspaceProtocol.autofix` for the contract — same semantics, applied to drift rewrites across engines / packageManager / volta / devEngines.\n\nNote: `--fix` strips any `+sha512.&lt;hash&gt;` suffix from `packageManager` on bump — content-integrity hashes are tied to a specific package, not a version, so users must regenerate via their PM (`pnpm install` re-pins; `corepack use pnpm@X` etc.).",
456
+ "default": true
457
+ },
458
+ "extraTypes": {
459
+ "type": "array",
460
+ "items": {
461
+ "$ref": "#/$defs/ExtraCustomType"
462
+ },
463
+ "description": "User-defined custom-type pin locations. Each entry tells the customTypes lint to read additional version pins from a non-standard JSON path inside every workspace package.json, cluster them by `(name × depName)` like the built-in types, and rewrite them with `--fix`.\n\nThe original built-ins (`engines`, `volta`, `packageManager`, `devEngines.runtime`, `devEngines.packageManager`) keep running unconditionally — these layer on top.\n\nStrategies:\n- `versionsByName`: the JSON at `path` is `{ [depName]: version }` (like `engines` or `pnpm.overrides`).\n- `name@version`: the JSON at `path` is a string of the form `name@version` (like `packageManager`). The leading `name@` is preserved; only the version segment is rewritten.\n- `string`: the JSON at `path` is a bare version string. The `depName` field is required and identifies the dep cluster.\n\n`name` must not collide with a built-in type name. `path` is a dot-separated walk into the package.json (e.g. `pnpm.overrides`)."
464
+ },
465
+ "ignore": {
466
+ "type": "array",
467
+ "items": {
468
+ "type": "string"
469
+ },
470
+ "description": "Dep names exempt from the drift check (exact match against the field name within the block — e.g. `node`, `pnpm`)."
471
+ },
472
+ "resolve": {
473
+ "type": "string",
474
+ "enum": [
475
+ "highest",
476
+ "lowest"
477
+ ],
478
+ "description": "Resolution strategy used when `--fix` runs.\n- `highest` (default): align every drifting instance to the highest declared version.\n- `lowest`: align to the lowest.",
479
+ "default": "highest"
480
+ }
481
+ },
482
+ "additionalProperties": false,
483
+ "description": "Tweak the custom-types lint that flags drift in `engines.{node,pnpm,...}`, `packageManager`, `volta.{node,pnpm,yarn}`, and the proposed `devEngines.{runtime,packageManager}` array form.\n\nEach (customType × name) cluster is tracked independently — `engines.node` and `volta.node` don't cross-couple here. Use a versionGroup once that lands if you need to enforce they agree."
484
+ },
485
+ "deadWorkspacePatterns": {
486
+ "type": "object",
487
+ "properties": {
488
+ "autofix": {
489
+ "anyOf": [
490
+ {
491
+ "type": "string",
492
+ "const": "prompt"
493
+ },
494
+ {
495
+ "type": "boolean"
496
+ }
497
+ ],
498
+ "description": "Three-state autofix opt-out. See `workspaceProtocol.autofix` for the contract — applied here to dropping unmatched patterns from the workspace config file.",
499
+ "default": true
500
+ }
501
+ },
502
+ "additionalProperties": false,
503
+ "description": "Tweak the dead-workspace-patterns lint that flags entries in `pnpm-workspace.yaml#packages` / `package.json#workspaces` which resolve to zero on-disk directories."
504
+ },
505
+ "emptyDeps": {
506
+ "type": "object",
507
+ "properties": {
508
+ "autofix": {
509
+ "anyOf": [
510
+ {
511
+ "type": "string",
512
+ "const": "prompt"
513
+ },
514
+ {
515
+ "type": "boolean"
516
+ }
517
+ ],
518
+ "description": "Three-state autofix opt-out. See `workspaceProtocol.autofix` for the contract — applied here to removing the empty key.",
519
+ "default": true
520
+ },
521
+ "ignoreBlocks": {
522
+ "type": "array",
523
+ "items": {
524
+ "type": "string",
525
+ "enum": [
526
+ "dependencies",
527
+ "devDependencies",
528
+ "optionalDependencies",
529
+ "peerDependencies"
530
+ ]
531
+ },
532
+ "description": "Block names exempt from the rule (e.g. `[\"peerDependencies\"]` to keep the key around as a marker even when empty)."
533
+ }
534
+ },
535
+ "additionalProperties": false,
536
+ "description": "Tweak the empty-deps lint that flags empty `dependencies` / `devDependencies` / `peerDependencies` / `optionalDependencies` blocks across the workspace."
537
+ },
538
+ "redefineRoot": {
539
+ "type": "object",
540
+ "properties": {
541
+ "ignore": {
542
+ "type": "array",
543
+ "items": {
544
+ "type": "string"
545
+ },
546
+ "description": "Dep names that are exempt from the redefine-root rule (exact match)."
547
+ }
548
+ },
549
+ "additionalProperties": false,
550
+ "description": "Tweak the redefine-root lint that flags non-root packages duplicating deps already pinned at the workspace root."
551
+ },
552
+ "rootDeps": {
553
+ "type": "object",
554
+ "properties": {
555
+ "autofix": {
556
+ "anyOf": [
557
+ {
558
+ "type": "string",
559
+ "const": "prompt"
560
+ },
561
+ {
562
+ "type": "boolean"
563
+ }
564
+ ],
565
+ "description": "Three-state autofix opt-out. See `workspaceProtocol.autofix` for the contract — applied here to moving entries from `dependencies` to `devDependencies` on the root package.json.",
566
+ "default": true
567
+ }
568
+ },
569
+ "additionalProperties": false,
570
+ "description": "Tweak the root-deps lint that flags runtime `dependencies` declared on the private workspace root (they should live in `devDependencies`)."
571
+ },
572
+ "rootPackageManager": {
573
+ "type": "object",
574
+ "properties": {
575
+ "autofix": {
576
+ "anyOf": [
577
+ {
578
+ "type": "string",
579
+ "const": "prompt"
580
+ },
581
+ {
582
+ "type": "boolean"
583
+ }
584
+ ],
585
+ "description": "Three-state autofix opt-out. See `workspaceProtocol.autofix` for the contract. `--fix` only writes when `suggested` is set — a missing `packageManager` field has no canonical default.",
586
+ "default": true
587
+ },
588
+ "suggested": {
589
+ "type": "string",
590
+ "description": "Canonical specifier (`name@version`) to write when `--fix` runs and the field is absent. Required to enable autofix — vis won't guess the workspace's preferred manager.",
591
+ "examples": [
592
+ "pnpm@10.32.1"
593
+ ]
594
+ }
595
+ },
596
+ "additionalProperties": false,
597
+ "description": "Tweak the root-package-manager lint that flags a missing or malformed `packageManager` field on the workspace root."
598
+ },
599
+ "rootPrivate": {
600
+ "type": "object",
601
+ "properties": {
602
+ "autofix": {
603
+ "anyOf": [
604
+ {
605
+ "type": "string",
606
+ "const": "prompt"
607
+ },
608
+ {
609
+ "type": "boolean"
610
+ }
611
+ ],
612
+ "description": "Three-state autofix opt-out. See `workspaceProtocol.autofix` for the contract — applied here to inserting `\"private\": true`.",
613
+ "default": true
614
+ }
615
+ },
616
+ "additionalProperties": false,
617
+ "description": "Tweak the root-private lint that flags a workspace root package.json missing `\"private\": true`. Only fires when the root looks like a workspace (npm/yarn/bun `workspaces` field or `pnpm-workspace.yaml`)."
618
+ },
619
+ "similarDeps": {
620
+ "type": "object",
621
+ "properties": {
622
+ "extraFamilies": {
623
+ "type": "array",
624
+ "items": {
625
+ "$ref": "#/$defs/SimilarDepFamily"
626
+ },
627
+ "description": "Additional families merged with the built-ins. Same `id` wins → user override fully replaces the built-in entry."
628
+ },
629
+ "ignoreFamilies": {
630
+ "type": "array",
631
+ "items": {
632
+ "type": "string"
633
+ },
634
+ "description": "Family ids to skip entirely (matches `SimilarDepFamily.id`)."
635
+ }
636
+ },
637
+ "additionalProperties": false,
638
+ "description": "Tweak the similar-deps lint that flags drift across related dep families (e.g. `react` and `react-dom`, all of `@babel/*`).\n\nThe lint is report-only — aligning a family requires picking a single canonical specifier across heterogeneous range syntaxes (`^`, `~`, exact), which is too lossy without user input."
639
+ },
640
+ "typesInDeps": {
641
+ "type": "object",
642
+ "properties": {
643
+ "autofix": {
644
+ "anyOf": [
645
+ {
646
+ "type": "string",
647
+ "const": "prompt"
648
+ },
649
+ {
650
+ "type": "boolean"
651
+ }
652
+ ],
653
+ "description": "Three-state autofix opt-out. See `workspaceProtocol.autofix` for the contract — applied here to moving the entry to `devDependencies`. Existing dev pins are preserved on conflict.",
654
+ "default": true
655
+ },
656
+ "ignore": {
657
+ "type": "array",
658
+ "items": {
659
+ "type": "string"
660
+ },
661
+ "description": "Dep names exempt from the rule (exact match, e.g. `@types/node`)."
662
+ }
663
+ },
664
+ "additionalProperties": false,
665
+ "description": "Tweak the types-in-deps lint that flags `@types/*` declared in `dependencies` on a private package (they belong in `devDependencies` since the package never ships)."
666
+ },
667
+ "workspaceProtocol": {
668
+ "type": "object",
669
+ "properties": {
670
+ "autofix": {
671
+ "anyOf": [
672
+ {
673
+ "type": "string",
674
+ "const": "prompt"
675
+ },
676
+ {
677
+ "type": "boolean"
678
+ }
679
+ ],
680
+ "description": "Three-state autofix opt-out. Some workspaces want detection without rewrite (e.g. dual-licensed packages where `workspace:*` is unsafe).\n- `true` (default): `--fix` rewrites the specifier.\n- `false`: never rewrite — report the violation only.\n- `\"prompt\"`: ask before each rewrite. Falls back to report-only when stdin isn't a TTY (CI). Reserved; not yet implemented.\n\nNote: when `false` (or `\"prompt\"`), `--fix` still **fails CI** on detected violations — the rule is \"report only\", not \"ignore\". Drop the rule from the lint selection if you want a clean exit.",
681
+ "default": true
682
+ }
683
+ },
684
+ "additionalProperties": false,
685
+ "description": "Tweak the workspace-protocol lint that flags internal deps not using the `workspace:` protocol."
686
+ },
687
+ "workspaceVersions": {
688
+ "type": "object",
689
+ "properties": {
690
+ "autofix": {
691
+ "anyOf": [
692
+ {
693
+ "type": "string",
694
+ "const": "prompt"
695
+ },
696
+ {
697
+ "type": "boolean"
698
+ }
699
+ ],
700
+ "description": "Three-state autofix opt-out. See `workspaceProtocol.autofix` for the contract — same semantics, applied to drift rewrites.\n\nAlso gates the `--propose-min` catalog suggestion writer: when `false` / `\"prompt\"`, `--fix --propose-min` reports the proposed catalog entries but does not write `pnpm-workspace.yaml`. Same \"report only, still fails CI\" note applies as on `workspaceProtocol.autofix`.",
701
+ "default": true
702
+ },
703
+ "ignore": {
704
+ "type": "array",
705
+ "items": {
706
+ "type": "string"
707
+ },
708
+ "description": "Dep names exempt from the version-drift check (exact match)."
709
+ },
710
+ "resolve": {
711
+ "type": "string",
712
+ "enum": [
713
+ "catalog",
714
+ "highest",
715
+ "lowest"
716
+ ],
717
+ "description": "Resolution strategy used when `--fix` runs.\n- `highest` (default): rewrite every drifting instance to the highest sibling specifier.\n- `lowest`: rewrite to the lowest.\n- `catalog`: rewrite any dep already pinned in a workspace catalog to `catalog:` / `catalog:&lt;name>`. Catalog must exist; this lint does not create the catalog (see `vis lint --resolve catalog --propose`).",
718
+ "default": "highest"
719
+ }
720
+ },
721
+ "additionalProperties": false,
722
+ "description": "Tweak the workspace-versions lint that flags external deps declared at inconsistent versions across the workspace."
723
+ }
724
+ },
725
+ "additionalProperties": false,
726
+ "description": "Workspace dep-policy lints exposed via `vis lint`. Each block opts in to a single rule; the command flags (`--workspace-protocol`, `--no-redefine-root`, `--banned-deps`) toggle them per-run."
727
+ },
728
+ "preflight": {
729
+ "type": "object",
730
+ "properties": {
731
+ "lockfile": {
732
+ "type": "boolean",
733
+ "description": "Detect \"lockfile changed but `node_modules` is stale\" before running tasks. Compares lockfile mtime against the package-manager-specific install marker (`node_modules/.modules.yaml` for pnpm, `.package-lock.json` for npm, etc.). Warns in TTY, hard-fails in CI.",
734
+ "default": true
735
+ }
736
+ },
737
+ "additionalProperties": false,
738
+ "description": "Pre-flight checks fired before `vis run` starts the orchestrator. Each check is opt-out (`false`) — defaults are sensible for the common monorepo case."
739
+ },
740
+ "run": {
741
+ "type": "object",
742
+ "properties": {
743
+ "ciGrouping": {
744
+ "type": "string",
745
+ "enum": [
746
+ "auto",
747
+ "azure",
748
+ "buildkite",
749
+ "github",
750
+ "gitlab",
751
+ "off"
752
+ ],
753
+ "description": "Wrap each task's CI log block in collapsible groups so users can fold/unfold per-task output in the host CI's web UI. Failed tasks always render expanded so the failure is visible without an extra click.\n\n- `auto` (default): pick the format from the detected runner — `GITHUB_ACTIONS=true` → `github` (`::group::`), `GITLAB_CI=true` → `gitlab` (`section_start:` ANSI sequences), `BUILDKITE=true` → `buildkite` (`---` collapsed headers), `TF_BUILD=True` → `azure` (`##[group]`), no grouping otherwise.\n- `off`: never group (raw separators only — useful when piping through tools that mangle the directives).\n- `azure` / `buildkite` / `github` / `gitlab`: force the format regardless of detected environment (useful for self-hosted runners that don't set the standard env vars).\n\nCircleCI is intentionally not auto-detected: its 2.0+ format has no inline grouping directive — steps auto-group in the web UI without any markup from the runner."
754
+ },
755
+ "services": {
756
+ "type": "string",
757
+ "enum": [
758
+ "auto",
759
+ "ephemeral",
760
+ "off",
761
+ "persistent"
762
+ ],
763
+ "description": "One knob controlling auto-start of missing service deps.\n- `auto` (default in TTY): pick by task — `dev` → ephemeral, others → persistent.\n- `ephemeral`: services die with the run (no registry entry).\n- `persistent`: services persist across runs in the registry.\n- `off` (default in CI / non-TTY): print diagnostics and abort."
764
+ }
765
+ },
766
+ "additionalProperties": false,
767
+ "description": "Behavior of `vis run` when invoked tasks declare service dependencies that aren't running in the workspace registry. CLI `--services=&lt;mode>` overrides this block."
768
+ },
769
+ "secrets": {
770
+ "type": "object",
771
+ "properties": {
772
+ "baseline": {
773
+ "type": "string",
774
+ "description": "Path to a baseline of previously-triaged findings (relative to workspace root)."
775
+ },
776
+ "config": {
777
+ "type": "object",
778
+ "properties": {
779
+ "extendBundled": {
780
+ "type": "boolean",
781
+ "description": "Layer the user's rules on top of the bundled ruleset. Default: `true`."
782
+ },
783
+ "inline": {
784
+ "type": "object",
785
+ "properties": {
786
+ "allowlist": {},
787
+ "allowlists": {
788
+ "type": "array",
789
+ "items": {}
790
+ },
791
+ "description": {
792
+ "type": "string"
793
+ },
794
+ "rules": {
795
+ "type": "array",
796
+ "items": {}
797
+ },
798
+ "title": {
799
+ "type": "string"
800
+ }
801
+ },
802
+ "additionalProperties": false,
803
+ "description": "Inline rule overrides. Wins over `path` when both are set."
804
+ },
805
+ "path": {
806
+ "type": "string",
807
+ "description": "Path to a JSON config (gitleaks-compatible)."
808
+ },
809
+ "presets": {
810
+ "type": "array",
811
+ "items": {
812
+ "type": "string"
813
+ },
814
+ "description": "Bundled presets layered on top of the default ruleset (e.g. `\"weak-passwords\"`)."
815
+ }
816
+ },
817
+ "additionalProperties": false,
818
+ "description": "Where the ruleset comes from. Omit for the bundled gitleaks default."
819
+ },
820
+ "redact": {
821
+ "type": "boolean",
822
+ "description": "Redact secret values in findings."
823
+ },
824
+ "rules": {
825
+ "type": "object",
826
+ "properties": {
827
+ "exclude": {
828
+ "type": "array",
829
+ "items": {
830
+ "type": "string"
831
+ },
832
+ "description": "Drop findings whose ruleId matches."
833
+ },
834
+ "include": {
835
+ "type": "array",
836
+ "items": {
837
+ "type": "string"
838
+ },
839
+ "description": "Only report findings whose ruleId matches."
840
+ }
841
+ },
842
+ "additionalProperties": false,
843
+ "description": "Rule-id filters applied after scanning."
844
+ },
845
+ "walk": {
846
+ "type": "object",
847
+ "properties": {
848
+ "excludeFromFiles": {
849
+ "type": "array",
850
+ "items": {
851
+ "type": "string"
852
+ },
853
+ "description": "Paths to additional `.gitignore`-syntax files (e.g. `.secretsignore`)."
854
+ },
855
+ "excludePatterns": {
856
+ "type": "array",
857
+ "items": {
858
+ "type": "string"
859
+ },
860
+ "description": "Gitignore-syntax patterns (supports negation, directory markers, leading `/`). Applied on top of `.gitignore`."
861
+ },
862
+ "gitignore": {
863
+ "type": "boolean",
864
+ "description": "Respect `.gitignore`. Default: `true`."
865
+ },
866
+ "includeHidden": {
867
+ "type": "boolean",
868
+ "description": "Include hidden (dotfile) entries. Default: `false`."
869
+ },
870
+ "maxFileSize": {
871
+ "type": "number",
872
+ "description": "Max file size in bytes. Default 10 MiB."
873
+ }
874
+ },
875
+ "additionalProperties": false,
876
+ "description": "Walker / filesystem traversal."
877
+ }
878
+ },
879
+ "additionalProperties": false,
880
+ "description": "Default options for `vis secrets`. CLI flags always take precedence; this block provides workspace-wide defaults so teams can commit config once and every invocation picks it up."
881
+ },
882
+ "security": {
883
+ "type": "object",
884
+ "properties": {
885
+ "acceptedRisks": {
886
+ "type": "object",
887
+ "additionalProperties": {
888
+ "type": "object",
889
+ "properties": {
890
+ "acceptedAt": {
891
+ "type": "string",
892
+ "description": "ISO 8601 timestamp when the risk was accepted."
893
+ },
894
+ "acceptedScore": {
895
+ "type": "number",
896
+ "description": "The overall Socket.dev score at the time of acceptance, in the range `[0, 1]` (mirrors `policies.score.minimum`). Only relevant for the `score` policy; ignored elsewhere."
897
+ },
898
+ "expiresAt": {
899
+ "type": "string",
900
+ "description": "ISO 8601 date (or datetime). After this point the acceptance stops applying and vis emits a warning. Leave undefined for non-expiring entries. Values that fail to parse as a Date are rejected by the loader rather than silently treated as \"always expired\"."
901
+ },
902
+ "policies": {
903
+ "type": "array",
904
+ "items": {
905
+ "$ref": "#/$defs/PolicyName"
906
+ },
907
+ "description": "Which policies this acceptance covers. When undefined the acceptance applies to every policy finding on this package."
908
+ },
909
+ "reason": {
910
+ "type": "string",
911
+ "description": "User-provided reason for accepting the risk."
912
+ }
913
+ },
914
+ "required": [
915
+ "acceptedAt",
916
+ "reason"
917
+ ],
918
+ "additionalProperties": false
919
+ },
920
+ "description": "Packages whose policy findings have been reviewed and explicitly accepted. Matched against every policy unless `policies` narrows the scope. Replaces the legacy `security.socket.acceptedRisks` map.\n\nKey format: package name (`\"lodash\"`), name@version (`\"lodash@4.17.21\"`), or glob (`\"@myorg/*\"`). Unversioned keys match all versions of that package."
921
+ },
922
+ "audit": {
923
+ "type": "object",
924
+ "properties": {
925
+ "advisories": {
926
+ "type": "object",
927
+ "properties": {
928
+ "allowedHosts": {
929
+ "type": "array",
930
+ "items": {
931
+ "type": "string"
932
+ },
933
+ "description": "Extra hosts permitted as `audit.advisories.source`. The built-in allowlist is enforced even if this field is omitted; entries here add to it.",
934
+ "examples": [
935
+ [
936
+ "mirror.corp.example.com"
937
+ ]
938
+ ]
939
+ },
940
+ "refreshIntervalHours": {
941
+ "type": "number",
942
+ "description": "Number of hours after `lastSyncIso` before `vis audit` prints a \"your advisory cache may be stale\" notice. `vis audit` never auto-syncs — the user runs `vis advisories sync` themselves.",
943
+ "default": 24
944
+ },
945
+ "source": {
946
+ "type": "string",
947
+ "description": "OSV mirror base URL (no trailing slash). Defaults to the public Google Cloud Storage bucket. Override to point at a corporate mirror; the hostname must appear in `allowedHosts` (or one of the built-in defaults) and the scheme must be `https://`.",
948
+ "default": "https://osv-vulnerabilities.storage.googleapis.com"
949
+ },
950
+ "verify": {
951
+ "type": "object",
952
+ "properties": {
953
+ "enabled": {
954
+ "type": "boolean",
955
+ "description": "Enable signature verification. The sync flow downloads `&lt;eco>/all.zip.sig` next to the zip and aborts if it cannot verify against `expectedIssuer` / `expectedSubject`.",
956
+ "default": false
957
+ },
958
+ "expectedIssuer": {
959
+ "type": "string",
960
+ "description": "OIDC issuer that signed the bundle."
961
+ },
962
+ "expectedSubject": {
963
+ "type": "string",
964
+ "description": "OIDC subject (workload identity) that signed the bundle."
965
+ }
966
+ },
967
+ "additionalProperties": false,
968
+ "description": "Sigstore signature verification for the OSV dump. Requires the native binding to be built with the `verify-signatures` Cargo feature (default in the release build). Off by default — the upstream OSV bucket does not ship signatures today."
969
+ }
970
+ },
971
+ "additionalProperties": false,
972
+ "description": "Offline advisory cache settings."
973
+ },
974
+ "apply": {
975
+ "type": "object",
976
+ "properties": {
977
+ "transitive": {
978
+ "type": "object",
979
+ "properties": {
980
+ "enabled": {
981
+ "type": "boolean",
982
+ "description": "When true, allows `--fix-transitive` to run in CI environments. Defaults to false because rewriting overrides is a higher blast radius than bumping a direct dep.",
983
+ "default": false
984
+ }
985
+ },
986
+ "additionalProperties": false,
987
+ "description": "Gates for `vis audit --fix-transitive`. Two-lock: the CLI requires `--yes` AND this flag set to `true` before it will rewrite override entries in CI."
988
+ }
989
+ },
990
+ "additionalProperties": false,
991
+ "description": "Gates for the auto-fix flow (`vis audit --fix` / `--fix-transitive`). The CLI prompts outside CI; inside CI the flags refuse to run unless `--yes` is set and, for transitives, `apply.transitive.enabled = true`."
992
+ },
993
+ "offlineByDefault": {
994
+ "type": "boolean",
995
+ "description": "When true, `vis audit` skips network calls and queries the offline cache. Equivalent to the CLI `--offline` flag.",
996
+ "default": false
997
+ }
998
+ },
999
+ "additionalProperties": false,
1000
+ "description": "Offline OSV advisory + `vis audit` configuration.\n\nControls `vis audit --offline` and `vis advisories sync` behavior:\n- `audit.advisories.source` is the OSV mirror to download from. It must be `https://` and resolve to a host in `allowedHosts` (or one of the built-in defaults).\n- `audit.offlineByDefault` flips the default of `--offline`.\n\nVulnerability severity gating and reachability filtering live under `policies.vulnerability` (see below)."
1001
+ },
1002
+ "allowBins": {
1003
+ "type": "object",
1004
+ "additionalProperties": {
1005
+ "type": "boolean"
1006
+ },
1007
+ "description": "Map of bin names (or `pkg#bin` qualifiers) blessed for shadowing. When two installed packages expose the same bin name, vis flags the collision in `vis security list` and the post-install drift report — set the bin (or `pkg#bin`) to `true` here to suppress the warning once you've reviewed the conflict.\n\nPort of LavaMoat allow-scripts' experimental `allowBins`. Bare names match any conflicting bin with that name; the `pkg#bin` form scopes the approval to a single package's bin."
1008
+ },
1009
+ "blockExoticSubdeps": {
1010
+ "type": "boolean",
1011
+ "description": "When true, prevents transitive dependencies from using exotic sources (git repositories, direct tarball URLs). Only direct dependencies may use such sources. Equivalent to pnpm's `blockExoticSubdeps`.",
1012
+ "default": false
1013
+ },
1014
+ "pinVersions": {
1015
+ "type": "boolean",
1016
+ "description": "When true, `security.policies.installScripts.allow` keys are matched as `name@version`. A version bump on an approved package drops it from the allowlist until the new version is explicitly re-approved (port of LavaMoat allow-scripts' version-aware policy matcher).\n\nAfter a version bump, run `vis approve-builds` or `vis security list` — both surface a \"Version drift\" block with the suggested new key (`old-key → new-key`) so you can update `vis.config.ts` by hand.",
1017
+ "default": false
1018
+ },
1019
+ "policies": {
1020
+ "type": "object",
1021
+ "properties": {
1022
+ "firstSeen": {
1023
+ "type": "object",
1024
+ "properties": {
1025
+ "exclude": {
1026
+ "type": "array",
1027
+ "items": {
1028
+ "type": "string"
1029
+ },
1030
+ "description": "Package names/patterns excluded from the firstSeen check. Equivalent to pnpm's `minimumReleaseAgeExclude`.",
1031
+ "examples": [
1032
+ [
1033
+ "webpack",
1034
+ "react",
1035
+ "@myorg/*"
1036
+ ]
1037
+ ]
1038
+ },
1039
+ "minutes": {
1040
+ "type": "number",
1041
+ "description": "Minutes after publish before install is allowed."
1042
+ }
1043
+ },
1044
+ "additionalProperties": false,
1045
+ "description": "Minimum number of minutes that must pass after a version is published before vis will allow installation. Migrated from the legacy `security.minimumReleaseAge` field. Equivalent to pnpm's `minimumReleaseAge`.",
1046
+ "examples": [
1047
+ {
1048
+ "minutes": 1440,
1049
+ "exclude": [
1050
+ "@myorg/*"
1051
+ ]
1052
+ }
1053
+ ],
1054
+ "default": 0
1055
+ },
1056
+ "installScripts": {
1057
+ "type": "object",
1058
+ "properties": {
1059
+ "allow": {
1060
+ "type": "object",
1061
+ "additionalProperties": {
1062
+ "type": "boolean"
1063
+ },
1064
+ "description": "Map of package names/patterns to allow (true) or deny (false) build scripts. Packages not listed are denied by default. Equivalent to pnpm's `allowBuilds`."
1065
+ },
1066
+ "strict": {
1067
+ "type": "boolean",
1068
+ "description": "When true, installation will fail (exit non-zero) if any dependencies have unreviewed build scripts. Equivalent to pnpm's `strictDepBuilds`.",
1069
+ "default": false
1070
+ }
1071
+ },
1072
+ "additionalProperties": false,
1073
+ "description": "Build-script (pre/install/postinstall/prepare) controls. Migrated from the legacy `security.allowBuilds` / `security.strictDepBuilds` fields.",
1074
+ "examples": [
1075
+ {
1076
+ "allow": {
1077
+ "esbuild": true
1078
+ },
1079
+ "strict": true
1080
+ }
1081
+ ]
1082
+ },
1083
+ "license": {
1084
+ "type": "object",
1085
+ "properties": {
1086
+ "allow": {
1087
+ "type": "array",
1088
+ "items": {
1089
+ "type": "string"
1090
+ },
1091
+ "description": "SPDX identifiers that are explicitly permitted. When set, any package whose declared license is not on this list is blocked."
1092
+ },
1093
+ "deny": {
1094
+ "type": "array",
1095
+ "items": {
1096
+ "type": "string"
1097
+ },
1098
+ "description": "SPDX identifiers that are explicitly forbidden. Always wins over `allow` when both reference the same identifier."
1099
+ }
1100
+ },
1101
+ "additionalProperties": false,
1102
+ "description": "SPDX license allow / deny lists. Deny wins on any sub-license match in SPDX expressions (`(MIT OR GPL-3.0)` against `deny: [\"GPL-3.0\"]` is blocked). Packages with no declared license are flagged when `allow` is set."
1103
+ },
1104
+ "malware": {
1105
+ "type": "object",
1106
+ "properties": {
1107
+ "mode": {
1108
+ "type": "string",
1109
+ "enum": [
1110
+ "block",
1111
+ "off",
1112
+ "warn"
1113
+ ],
1114
+ "description": "- `\"block\"` — emit a block decision.\n- `\"warn\"` — surface as a warning; do not gate exit code.\n- `\"off\"` — disable the policy entirely."
1115
+ }
1116
+ },
1117
+ "additionalProperties": false,
1118
+ "description": "Behavior when the Socket.dev feed flags a package as malicious (`alerts[].type === \"Malware\"`).\n\nThe default is cross-field: `{ mode: \"block\" }` whenever `security.socket.enabled !== false` (the engine cannot evaluate malware without Socket data), and `\"off\"` otherwise. Consumers resolve this default at evaluation time."
1119
+ },
1120
+ "publisherChange": {
1121
+ "type": "object",
1122
+ "properties": {
1123
+ "exclude": {
1124
+ "type": "array",
1125
+ "items": {
1126
+ "type": "string"
1127
+ },
1128
+ "description": "Package selectors excluded from the check. Equivalent to pnpm's `trustPolicyExclude`.",
1129
+ "examples": [
1130
+ [
1131
+ "chokidar@4.0.3"
1132
+ ]
1133
+ ]
1134
+ },
1135
+ "ignoreAfter": {
1136
+ "type": "number",
1137
+ "description": "Ignore packages published more than N minutes ago. Useful for older packages that pre-date provenance support. Equivalent to pnpm's `trustPolicyIgnoreAfter`."
1138
+ },
1139
+ "mode": {
1140
+ "type": "string",
1141
+ "enum": [
1142
+ "no-downgrade",
1143
+ "off"
1144
+ ],
1145
+ "description": "- `\"off\"` — no trust checking (default).\n- `\"no-downgrade\"` — block when a package's trust level has decreased compared to previous releases (e.g., was published by trusted publisher, now only has provenance)."
1146
+ }
1147
+ },
1148
+ "additionalProperties": false,
1149
+ "description": "Trust-level checking for package publishing. Migrated from the legacy `security.trustPolicy*` fields. Equivalent to pnpm's `trustPolicy`.",
1150
+ "examples": [
1151
+ {
1152
+ "mode": "no-downgrade",
1153
+ "ignoreAfter": 43200
1154
+ }
1155
+ ]
1156
+ },
1157
+ "score": {
1158
+ "type": "object",
1159
+ "properties": {
1160
+ "minimum": {
1161
+ "type": "number",
1162
+ "description": "Minimum overall Socket.dev score (0–1). Set to 0 to disable the gate while keeping Socket data fetched.\n\nConsulted by `vis add`, `audit`, `doctor`, `check`, and `update`; resolved once in `buildSocketOptions`, then threaded through every consumer. Falls back to `DEFAULT_LOW_SCORE_THRESHOLD` (`0.4`) when unset."
1163
+ }
1164
+ },
1165
+ "additionalProperties": false,
1166
+ "description": "Socket.dev overall-score threshold. Packages scoring below `minimum` trigger a block decision (or interactive prompt during `vis add`). Migrated from the legacy `security.socket.minimumScore` field.",
1167
+ "examples": [
1168
+ {
1169
+ "minimum": 0.4
1170
+ }
1171
+ ]
1172
+ },
1173
+ "unexpectedDeps": {
1174
+ "type": "object",
1175
+ "properties": {
1176
+ "allow": {
1177
+ "type": "array",
1178
+ "items": {
1179
+ "type": "string"
1180
+ },
1181
+ "description": "Allow-list of dependency names that may appear in the resolved package set. Glob patterns are supported.",
1182
+ "examples": [
1183
+ [
1184
+ "lodash",
1185
+ "axios",
1186
+ "@myorg/*"
1187
+ ]
1188
+ ]
1189
+ },
1190
+ "baselineLockfile": {
1191
+ "type": "string",
1192
+ "description": "Path (absolute or relative to the workspace root) to a baseline lockfile snapshot. The policy diffs the current lockfile against this baseline and flags any package that didn't exist before.",
1193
+ "examples": [
1194
+ "./security/lockfile.baseline.yaml"
1195
+ ]
1196
+ }
1197
+ },
1198
+ "additionalProperties": false,
1199
+ "description": "Net-new transitive dependency detection. Either provide a static allow-list, a baseline lockfile path (recommended), or both — the intersection is enforced.",
1200
+ "examples": [
1201
+ {
1202
+ "baselineLockfile": "./security/lockfile.baseline.yaml"
1203
+ }
1204
+ ]
1205
+ },
1206
+ "vulnerability": {
1207
+ "type": "object",
1208
+ "properties": {
1209
+ "failOn": {
1210
+ "type": "string",
1211
+ "enum": [
1212
+ "critical",
1213
+ "high",
1214
+ "low",
1215
+ "medium"
1216
+ ],
1217
+ "description": "Severity threshold that makes `vis audit` exit non-zero. Equivalent to the CLI `--fail-on` flag.",
1218
+ "examples": [
1219
+ "high"
1220
+ ]
1221
+ },
1222
+ "usage": {
1223
+ "type": "object",
1224
+ "properties": {
1225
+ "alwaysAssumeUsed": {
1226
+ "type": "array",
1227
+ "items": {
1228
+ "type": "string"
1229
+ },
1230
+ "description": "Packages to always treat as reachable even if no static import is found.",
1231
+ "examples": [
1232
+ [
1233
+ "esbuild",
1234
+ "webpack-cli"
1235
+ ]
1236
+ ]
1237
+ },
1238
+ "enabled": {
1239
+ "type": "boolean",
1240
+ "description": "Enable the reachability filter by default. Equivalent to `--usage` on the CLI; `--no-usage` disables.",
1241
+ "default": false
1242
+ }
1243
+ },
1244
+ "additionalProperties": false,
1245
+ "description": "Reachability filter — only report vulnerabilities in packages the workspace statically imports."
1246
+ }
1247
+ },
1248
+ "additionalProperties": false,
1249
+ "description": "OSV vulnerability gating. Migrated from the legacy `security.audit.failOn` + `security.audit.usage` fields."
1250
+ }
1251
+ },
1252
+ "additionalProperties": false,
1253
+ "description": "Supply-chain policy gates. Each sub-block enables one policy and configures its behavior. When a sub-block is omitted the policy is inactive. `acceptedRisks` (above) silences specific packages without disabling a policy globally.\n\nThe 8 policies are inspired by Socket.dev's classification:\n- `malware` — Socket-flagged malicious packages\n- `firstSeen` — packages published less than N minutes ago\n- `unexpectedDeps` — packages outside an allow-list / baseline\n- `publisherChange` — maintainer set changed between installs\n- `installScripts` — preinstall/install/postinstall scripts\n- `score` — Socket overall score below threshold\n- `vulnerability` — OSV vulnerability findings\n- `license` — SPDX allow / deny lists"
1254
+ },
1255
+ "socket": {
1256
+ "type": "object",
1257
+ "properties": {
1258
+ "apiToken": {
1259
+ "type": "string",
1260
+ "description": "Custom Socket.dev API token. Falls back to the public API token. Set via VIS_SOCKET_TOKEN environment variable or here."
1261
+ },
1262
+ "cacheTtlMs": {
1263
+ "type": "number",
1264
+ "description": "Cache TTL in milliseconds for Socket.dev reports.",
1265
+ "default": "3_600_000 (1 hour)"
1266
+ },
1267
+ "enabled": {
1268
+ "type": "boolean",
1269
+ "description": "Enable Socket.dev security scanning on install/update/check commands.",
1270
+ "default": false
1271
+ },
1272
+ "timeoutMs": {
1273
+ "type": "number",
1274
+ "description": "Request timeout in milliseconds for the Socket.dev API.",
1275
+ "default": "15_000 (15 seconds)"
1276
+ }
1277
+ },
1278
+ "additionalProperties": false,
1279
+ "description": "Socket.dev data-source configuration. Connection knobs only — score thresholds and accepted-risk overrides moved to `policies.score` and `security.acceptedRisks` respectively."
1280
+ },
1281
+ "marshalls": {
1282
+ "type": "object",
1283
+ "properties": {
1284
+ "archivedRepo": {
1285
+ "type": "object",
1286
+ "properties": {
1287
+ "allowlist": {
1288
+ "type": "array",
1289
+ "items": {
1290
+ "type": "string"
1291
+ },
1292
+ "description": "Package names to skip."
1293
+ },
1294
+ "enabled": {
1295
+ "type": "boolean",
1296
+ "description": "Default: marshall is on. Set false to disable."
1297
+ },
1298
+ "githubToken": {
1299
+ "type": "string",
1300
+ "description": "GitHub PAT for the API call (5k/hr vs 60/hr)."
1301
+ }
1302
+ },
1303
+ "additionalProperties": false,
1304
+ "description": "Archived-repo marshall (GitHub repository status)."
1305
+ },
1306
+ "author": {
1307
+ "type": "object",
1308
+ "properties": {
1309
+ "allowlist": {
1310
+ "type": "array",
1311
+ "items": {
1312
+ "type": "string"
1313
+ }
1314
+ },
1315
+ "dormantErrorDays": {
1316
+ "type": "number",
1317
+ "description": "Days since the publisher's last release before flagging as error."
1318
+ },
1319
+ "dormantWarnDays": {
1320
+ "type": "number",
1321
+ "description": "Days since the publisher's last release before flagging as warning."
1322
+ },
1323
+ "enabled": {
1324
+ "type": "boolean"
1325
+ },
1326
+ "newPublisherWindowDays": {
1327
+ "type": "number",
1328
+ "description": "Window for the \"new publisher on an established package\" check."
1329
+ },
1330
+ "recentVersionErrorDays": {
1331
+ "type": "number",
1332
+ "description": "Days since the resolved version was published — error threshold."
1333
+ },
1334
+ "recentVersionWarnDays": {
1335
+ "type": "number",
1336
+ "description": "Days since the resolved version was published — warning threshold."
1337
+ }
1338
+ },
1339
+ "additionalProperties": false,
1340
+ "description": "Author / publisher heuristics."
1341
+ },
1342
+ "downloads": {
1343
+ "type": "object",
1344
+ "properties": {
1345
+ "allowlist": {
1346
+ "type": "array",
1347
+ "items": {
1348
+ "type": "string"
1349
+ }
1350
+ },
1351
+ "enabled": {
1352
+ "type": "boolean"
1353
+ },
1354
+ "errorThreshold": {
1355
+ "type": "number",
1356
+ "description": "Below this monthly count → error (default: 20)."
1357
+ },
1358
+ "warnThreshold": {
1359
+ "type": "number",
1360
+ "description": "Below this monthly count → warning (default: 1000)."
1361
+ }
1362
+ },
1363
+ "additionalProperties": false,
1364
+ "description": "Monthly download-count floor."
1365
+ },
1366
+ "expiredDomains": {
1367
+ "type": "object",
1368
+ "properties": {
1369
+ "allowDomains": {
1370
+ "type": "array",
1371
+ "items": {
1372
+ "type": "string"
1373
+ },
1374
+ "description": "Domains exempted from the check (legacy / internal)."
1375
+ },
1376
+ "allowlist": {
1377
+ "type": "array",
1378
+ "items": {
1379
+ "type": "string"
1380
+ }
1381
+ },
1382
+ "dnsServers": {
1383
+ "type": "array",
1384
+ "items": {
1385
+ "type": "string"
1386
+ },
1387
+ "description": "DNS resolvers to query (default: system)."
1388
+ },
1389
+ "enabled": {
1390
+ "type": "boolean"
1391
+ },
1392
+ "timeoutMs": {
1393
+ "type": "number",
1394
+ "description": "Per-domain DNS timeout (default: 5000)."
1395
+ }
1396
+ },
1397
+ "additionalProperties": false,
1398
+ "description": "Maintainer-email-domain NS lookup."
1399
+ },
1400
+ "metadata": {
1401
+ "type": "object",
1402
+ "properties": {
1403
+ "allowlist": {
1404
+ "type": "array",
1405
+ "items": {
1406
+ "type": "string"
1407
+ }
1408
+ },
1409
+ "checks": {
1410
+ "type": "array",
1411
+ "items": {
1412
+ "type": "string",
1413
+ "enum": [
1414
+ "license",
1415
+ "readme",
1416
+ "repo"
1417
+ ]
1418
+ },
1419
+ "description": "Subset of checks to run. Default: all three."
1420
+ },
1421
+ "enabled": {
1422
+ "type": "boolean"
1423
+ }
1424
+ },
1425
+ "additionalProperties": false,
1426
+ "description": "README / license / repository presence checks."
1427
+ },
1428
+ "newBin": {
1429
+ "type": "object",
1430
+ "properties": {
1431
+ "allowlist": {
1432
+ "type": "array",
1433
+ "items": {
1434
+ "type": "string"
1435
+ }
1436
+ },
1437
+ "enabled": {
1438
+ "type": "boolean"
1439
+ }
1440
+ },
1441
+ "additionalProperties": false,
1442
+ "description": "New CLI-bin script introduced in this version."
1443
+ },
1444
+ "provenance": {
1445
+ "type": "object",
1446
+ "properties": {
1447
+ "allowlist": {
1448
+ "type": "array",
1449
+ "items": {
1450
+ "type": "string"
1451
+ }
1452
+ },
1453
+ "enabled": {
1454
+ "type": "boolean"
1455
+ }
1456
+ },
1457
+ "additionalProperties": false,
1458
+ "description": "Provenance regression check."
1459
+ },
1460
+ "signatures": {
1461
+ "type": "object",
1462
+ "properties": {
1463
+ "allowlist": {
1464
+ "type": "array",
1465
+ "items": {
1466
+ "type": "string"
1467
+ }
1468
+ },
1469
+ "enabled": {
1470
+ "type": "boolean",
1471
+ "description": "Default: marshall is *off*. Set true to enable."
1472
+ },
1473
+ "keysUrl": {
1474
+ "type": "string",
1475
+ "description": "Override the keys endpoint (default: npm registry)."
1476
+ },
1477
+ "treatExpiredAs": {
1478
+ "type": "string",
1479
+ "enum": [
1480
+ "error",
1481
+ "warning"
1482
+ ],
1483
+ "description": "How to treat an expired-but-known key. Default: \"warning\"."
1484
+ }
1485
+ },
1486
+ "additionalProperties": false,
1487
+ "description": "ECDSA P-256 verification against npm's signing keys. Disabled by default because npm coverage still has gaps that produce noisy warnings on legitimate packages."
1488
+ }
1489
+ },
1490
+ "additionalProperties": false,
1491
+ "description": "Pre-install marshall pipeline — packument-derived supply-chain gates (author, provenance, new-bin, metadata, downloads, expired-domains, signatures, archived-repo) that run before `vis add` / `vis install <pkg>` / `vis update <pkg>` hand off to the underlying package manager. Every entry is optional; omit a key and the marshall runs with defaults. Set `enabled: false` on a specific marshall to skip it without touching env vars."
1492
+ },
1493
+ "typosquatAllowlist": {
1494
+ "type": "array",
1495
+ "items": {
1496
+ "type": "string"
1497
+ },
1498
+ "description": "Package names to skip during typosquat detection. Use this for internal packages or known-safe names that happen to look similar to popular packages.",
1499
+ "examples": [
1500
+ [
1501
+ "my-internal-axois",
1502
+ "@myorg/recat"
1503
+ ]
1504
+ ]
1505
+ }
1506
+ },
1507
+ "additionalProperties": false,
1508
+ "description": "Supply chain security settings. These settings are inspired by pnpm's security features and are applied universally across all package managers (pnpm, npm, yarn, bun).\n\nFor pnpm users: these map directly to pnpm-workspace.yaml settings. For npm/yarn/bun users: vis enforces these at the vis layer since those package managers lack native support."
1509
+ },
1510
+ "sharedWorktreeCache": {
1511
+ "type": "boolean",
1512
+ "description": "Share the cache between sibling git worktrees. When the workspace is a linked worktree (created with `git worktree add`), the cache root is relocated from `&lt;linkedRoot>/.vis/cache` to the *main* worktree's `.vis/cache`. Multiple parallel agents working in sibling worktrees then share a single cache instead of rebuilding the same hash N times.\n\nSingle-checkout repos (where `.git` is a directory) are unaffected.\n\nSet to `false` to opt out — useful when worktrees deliberately need independent caches, e.g. for hermetic experiments.",
1513
+ "default": true
1514
+ },
1515
+ "sortPackageJson": {
1516
+ "type": "object",
1517
+ "properties": {
1518
+ "editorconfig": {
1519
+ "type": "boolean",
1520
+ "description": "Discover `.editorconfig` for indent / line-ending defaults (default: true)."
1521
+ },
1522
+ "formatBugs": {
1523
+ "type": "boolean",
1524
+ "description": "Collapse `bugs: { url }` to the bare string form when `url` is the only field (default: true)."
1525
+ },
1526
+ "formatRepository": {
1527
+ "type": "boolean",
1528
+ "description": "Collapse `repository: { type, url }` to the GitHub `owner/repo` shorthand (default: true)."
1529
+ },
1530
+ "sortExports": {
1531
+ "type": "boolean",
1532
+ "description": "Sort `exports` condition keys in canonical order (default: true)."
1533
+ },
1534
+ "sortScripts": {
1535
+ "type": "boolean",
1536
+ "description": "Alphabetize script commands (default: false)"
1537
+ }
1538
+ },
1539
+ "additionalProperties": false,
1540
+ "description": "sort-package-json command defaults"
1541
+ },
1542
+ "sponsor": {
1543
+ "type": "object",
1544
+ "properties": {
1545
+ "enabled": {
1546
+ "type": "boolean",
1547
+ "description": "Show the sponsor notice on successful command completion.",
1548
+ "default": true
1549
+ }
1550
+ },
1551
+ "additionalProperties": false,
1552
+ "description": "Sponsorship notice shown after successful commands.\n\nvis prints a one-line \"consider sponsoring visulima\" notice at most once every 14 days (skipped in CI, non-TTY, and when `VIS_NO_SPONSOR=1` is set). Set `enabled: false` to silence it permanently for this workspace."
1553
+ },
1554
+ "staged": {
1555
+ "$ref": "#/$defs/StagedConfig",
1556
+ "description": "Staged file patterns and commands (replaces lint-staged).\n\nAccepts all lint-staged config forms:\n- `string` or `string[]` commands\n- Sync/async functions returning `string | string[]`\n- `{ title, task }` objects for named side-effect tasks\n- Mixed arrays of strings and functions\n- A top-level generate-task function"
1557
+ },
1558
+ "strictEnv": {
1559
+ "type": "boolean",
1560
+ "description": "When `true`, every task command is scanned for `${VAR}` / `$VAR` references before spawn. If a referenced var is unset in the task's effective env (envFile + service env + per-task `env` + `process.env`), the task fails with an actionable error naming the missing variable, instead of letting the shell silently substitute an empty string.\n\nOverride per run with `--strict-env` / `--no-strict-env`. Override per target with `options.strictEnv`.",
1561
+ "default": false
1562
+ },
1563
+ "tasks": {
1564
+ "type": "object",
1565
+ "additionalProperties": {
1566
+ "type": "object",
1567
+ "properties": {
1568
+ "aliases": {
1569
+ "type": "array",
1570
+ "items": {
1571
+ "type": "string"
1572
+ },
1573
+ "description": "Alternate names that resolve to this target on the CLI. Useful for shortening long canonical names (`test` ↔ `t`) or for offering migration-friendly aliases when renaming targets. Aliases must be globally unique within the workspace."
1574
+ },
1575
+ "description": {
1576
+ "type": "string",
1577
+ "description": "One-line description surfaced by `vis list` and (in future) per-task `--help`. Kept short — longer docs belong in project READMEs or vis.config.ts comments."
1578
+ },
1579
+ "inferred": {
1580
+ "type": "boolean",
1581
+ "description": "True when the target was synthesized by a Project Crystal-style detector (see {@link ../inference } ) rather than declared by a package.json script, project.json, or vis.task.ts file. Surfaced by `vis list --inferred` and used by tooling to distinguish implicit defaults from explicit user intent."
1582
+ },
1583
+ "options": {
1584
+ "$ref": "#/$defs/VisTargetOptions",
1585
+ "description": "Vis-specific target options."
1586
+ },
1587
+ "preset": {
1588
+ "$ref": "#/$defs/TargetPreset",
1589
+ "description": "Preset applied before user-specified options."
1590
+ },
1591
+ "type": {
1592
+ "$ref": "#/$defs/TargetType",
1593
+ "description": "Semantic task type. Affects caching defaults and CI filtering.",
1594
+ "default": "test"
1595
+ },
1596
+ "always": {
1597
+ "type": "boolean",
1598
+ "description": "When `true`, this target runs after the main task graph completes — even if upstream tasks failed. Useful for cleanup, teardown, notifications, or anything that should fire regardless of build outcome. Always-tasks are not part of the normal dependency graph and never block other tasks."
1599
+ },
1600
+ "cache": {
1601
+ "type": "boolean",
1602
+ "description": "Whether this target is cacheable"
1603
+ },
1604
+ "cacheOnWarning": {
1605
+ "type": "boolean",
1606
+ "description": "When `false`, exit-0 runs whose terminal output matched any {@link TargetConfiguration.warningPattern } are not seeded into the cache. Defaults to `true` — warnings are surfaced on the result (`hadWarnings: true`) but caching still happens, matching the \"succeeded with warnings is incremental\" behaviour rush, lage and wireit users have repeatedly asked for."
1607
+ },
1608
+ "cacheRestore": {
1609
+ "type": "object",
1610
+ "properties": {
1611
+ "preserveMtime": {
1612
+ "type": "boolean",
1613
+ "description": "Restore each file's modification time from the captured tar header (second precision). When `false`, restored files take the current wall-clock time, matching the legacy behaviour."
1614
+ },
1615
+ "preservePerms": {
1616
+ "type": "boolean",
1617
+ "description": "Restore each file's POSIX mode bits (rwx triplets, low 12 bits). When `false`, restored files take the process umask. Only meaningful on POSIX hosts; Windows ignores tar mode."
1618
+ }
1619
+ },
1620
+ "additionalProperties": false,
1621
+ "description": "Fine-grained controls over how cached outputs are rehydrated. See {@link CacheRestoreOptions } . Both fields default to `true` (faithful restore); flip individually when downstream tooling needs the legacy \"now\"-stamped behaviour."
1622
+ },
1623
+ "command": {
1624
+ "type": "string",
1625
+ "description": "The command to run (alternative to executor)"
1626
+ },
1627
+ "configurations": {
1628
+ "type": "object",
1629
+ "additionalProperties": {
1630
+ "type": "object",
1631
+ "additionalProperties": {}
1632
+ },
1633
+ "description": "Named configurations (e.g., \"production\", \"development\")"
1634
+ },
1635
+ "concurrencyGroup": {
1636
+ "type": "string",
1637
+ "description": "Workspace-level concurrency group this target opts into. The scheduler caps the *combined* in-flight count of every task whose target carries the same group name to the value declared in {@link TaskRunnerOptions.concurrencyGroups } . Use this when several targets share an external resource (a single Postgres, a developer's Docker daemon, an account-wide deploy lock) so the cap follows the resource, not any single target name.\n\nTargets that name a group not declared in `concurrencyGroups` run uncapped — a typo shouldn't deadlock the graph. Caps don't apply to tasks marked `always: true`; those run in a separate finalisation phase outside the scheduler."
1638
+ },
1639
+ "dependsOn": {
1640
+ "type": "array",
1641
+ "items": {
1642
+ "anyOf": [
1643
+ {
1644
+ "type": "string"
1645
+ },
1646
+ {
1647
+ "type": "object",
1648
+ "properties": {
1649
+ "dependencies": {
1650
+ "type": "boolean",
1651
+ "description": "Whether this is a dependency on the same project"
1652
+ },
1653
+ "params": {
1654
+ "type": "string",
1655
+ "enum": [
1656
+ "forward",
1657
+ "ignore"
1658
+ ],
1659
+ "description": "Params to pass through"
1660
+ },
1661
+ "projects": {
1662
+ "anyOf": [
1663
+ {
1664
+ "type": "string"
1665
+ },
1666
+ {
1667
+ "type": "array",
1668
+ "items": {
1669
+ "type": "string"
1670
+ }
1671
+ }
1672
+ ],
1673
+ "description": "The project name (if different from the current project)"
1674
+ },
1675
+ "target": {
1676
+ "type": "string",
1677
+ "description": "The target name"
1678
+ }
1679
+ },
1680
+ "required": [
1681
+ "target"
1682
+ ],
1683
+ "additionalProperties": false,
1684
+ "description": "Defines a dependency on another target."
1685
+ }
1686
+ ]
1687
+ },
1688
+ "description": "Other targets this target depends on"
1689
+ },
1690
+ "executor": {
1691
+ "type": "string",
1692
+ "description": "The executor/command to run"
1693
+ },
1694
+ "inputs": {
1695
+ "type": "array",
1696
+ "items": {
1697
+ "anyOf": [
1698
+ {
1699
+ "type": "string"
1700
+ },
1701
+ {
1702
+ "anyOf": [
1703
+ {
1704
+ "type": "object",
1705
+ "properties": {
1706
+ "fileset": {
1707
+ "anyOf": [
1708
+ {
1709
+ "type": "object",
1710
+ "properties": {
1711
+ "base": {
1712
+ "type": "string",
1713
+ "enum": [
1714
+ "package",
1715
+ "workspace"
1716
+ ],
1717
+ "description": "Anchor for the pattern."
1718
+ },
1719
+ "pattern": {
1720
+ "type": "string",
1721
+ "description": "Glob pattern (may start with `!` for negation)."
1722
+ }
1723
+ },
1724
+ "required": [
1725
+ "base",
1726
+ "pattern"
1727
+ ],
1728
+ "additionalProperties": false,
1729
+ "description": "Object form of a fileset pattern, for anchoring to the workspace root."
1730
+ },
1731
+ {
1732
+ "type": "string"
1733
+ }
1734
+ ]
1735
+ }
1736
+ },
1737
+ "required": [
1738
+ "fileset"
1739
+ ],
1740
+ "additionalProperties": false,
1741
+ "description": "An input based on file patterns.\n\n`fileset` may be a bare glob string (package-root relative) or an object form `{ pattern, base }` to anchor explicitly to the workspace root. Negation (`!` prefix) works in both forms."
1742
+ },
1743
+ {
1744
+ "type": "object",
1745
+ "properties": {
1746
+ "runtime": {
1747
+ "type": "string"
1748
+ }
1749
+ },
1750
+ "required": [
1751
+ "runtime"
1752
+ ],
1753
+ "additionalProperties": false,
1754
+ "description": "An input based on a runtime command."
1755
+ },
1756
+ {
1757
+ "type": "object",
1758
+ "properties": {
1759
+ "env": {
1760
+ "type": "string"
1761
+ }
1762
+ },
1763
+ "required": [
1764
+ "env"
1765
+ ],
1766
+ "additionalProperties": false,
1767
+ "description": "An input based on environment variables."
1768
+ },
1769
+ {
1770
+ "type": "object",
1771
+ "properties": {
1772
+ "externalDependencies": {
1773
+ "type": "array",
1774
+ "items": {
1775
+ "type": "string"
1776
+ }
1777
+ }
1778
+ },
1779
+ "required": [
1780
+ "externalDependencies"
1781
+ ],
1782
+ "additionalProperties": false,
1783
+ "description": "An input based on external dependency versions."
1784
+ }
1785
+ ],
1786
+ "description": "Defines an input for cache invalidation."
1787
+ }
1788
+ ]
1789
+ },
1790
+ "description": "Input patterns for cache invalidation"
1791
+ },
1792
+ "maxConcurrent": {
1793
+ "type": "number",
1794
+ "description": "Maximum number of in-flight instances of this target across the whole graph. When set, the scheduler refuses to start an additional task whose `target.target` equals this target's name once the running count reaches `maxConcurrent`. Independent of `--parallel`: the global cap still bounds total in-flight tasks, and `maxConcurrent` further bounds the per-target subset.\n\nUse for tests/deploys that share an external resource (DB, port, mock server). Set to `1` to serialize. Values `<= 0` are ignored.\n\nIf multiple projects declare different values for the same target name, the runner uses the smallest declared cap. Caps don't apply to tasks marked `always: true`; those run in a separate finalisation phase outside the scheduler."
1795
+ },
1796
+ "outputs": {
1797
+ "type": "array",
1798
+ "items": {
1799
+ "type": "string"
1800
+ },
1801
+ "description": "Output patterns produced by this target"
1802
+ },
1803
+ "parallelism": {
1804
+ "type": "boolean",
1805
+ "description": "Whether this target supports parallel execution"
1806
+ },
1807
+ "warningPattern": {
1808
+ "anyOf": [
1809
+ {
1810
+ "type": "string"
1811
+ },
1812
+ {
1813
+ "type": "array",
1814
+ "items": {
1815
+ "type": "string"
1816
+ }
1817
+ }
1818
+ ],
1819
+ "description": "Regex source string(s) that mark a successful task as having emitted warnings. The orchestrator scans the task's combined terminal output after a 0-exit and, on first match, sets `hadWarnings` on the result. Combine with `cacheOnWarning: false` to skip caching for warning-tainted runs. Both bare strings and arrays are accepted; arrays are tested in order.\n\n ```ts warningPattern: [\"\\\\bwarning\\\\b\", \"TS\\\\d{4}\"] ```"
1820
+ },
1821
+ "when": {
1822
+ "type": "object",
1823
+ "properties": {
1824
+ "branch": {
1825
+ "anyOf": [
1826
+ {
1827
+ "type": "string"
1828
+ },
1829
+ {
1830
+ "type": "array",
1831
+ "items": {
1832
+ "type": "string"
1833
+ }
1834
+ }
1835
+ ],
1836
+ "description": "Match against the current git branch (HEAD)."
1837
+ },
1838
+ "ci": {
1839
+ "type": "boolean",
1840
+ "description": "Run only when invoked inside CI when `true`, only outside CI when `false`. Detects CI via the `CI` env var (the convention GitHub Actions, GitLab, CircleCI, Jenkins, etc. all share)."
1841
+ },
1842
+ "env": {
1843
+ "anyOf": [
1844
+ {
1845
+ "anyOf": [
1846
+ {
1847
+ "type": "string"
1848
+ },
1849
+ {
1850
+ "type": "object",
1851
+ "properties": {
1852
+ "equals": {
1853
+ "type": "string",
1854
+ "description": "Match this exact value. Mutually exclusive with `exists`."
1855
+ },
1856
+ "exists": {
1857
+ "type": "boolean",
1858
+ "description": "Assert the variable is set & non-empty (`true`) or unset/empty (`false`)."
1859
+ },
1860
+ "name": {
1861
+ "type": "string",
1862
+ "description": "Variable name."
1863
+ }
1864
+ },
1865
+ "required": [
1866
+ "name"
1867
+ ],
1868
+ "additionalProperties": false
1869
+ }
1870
+ ],
1871
+ "description": "An environment-variable match. The string form is shorthand for `{ name, exists: true }` (set + non-empty). The object form supports either presence assertions or exact-value matching."
1872
+ },
1873
+ {
1874
+ "type": "array",
1875
+ "items": {
1876
+ "anyOf": [
1877
+ {
1878
+ "type": "string"
1879
+ },
1880
+ {
1881
+ "type": "object",
1882
+ "properties": {
1883
+ "equals": {
1884
+ "type": "string",
1885
+ "description": "Match this exact value. Mutually exclusive with `exists`."
1886
+ },
1887
+ "exists": {
1888
+ "type": "boolean",
1889
+ "description": "Assert the variable is set & non-empty (`true`) or unset/empty (`false`)."
1890
+ },
1891
+ "name": {
1892
+ "type": "string",
1893
+ "description": "Variable name."
1894
+ }
1895
+ },
1896
+ "required": [
1897
+ "name"
1898
+ ],
1899
+ "additionalProperties": false
1900
+ }
1901
+ ],
1902
+ "description": "An environment-variable match. The string form is shorthand for `{ name, exists: true }` (set + non-empty). The object form supports either presence assertions or exact-value matching."
1903
+ }
1904
+ }
1905
+ ],
1906
+ "description": "Match against environment variables. A bare string asserts the variable is set and non-empty; the object form lets you match an exact value or just assert presence/absence."
1907
+ },
1908
+ "not": {
1909
+ "type": "object",
1910
+ "properties": {
1911
+ "branch": {
1912
+ "anyOf": [
1913
+ {
1914
+ "type": "string"
1915
+ },
1916
+ {
1917
+ "type": "array",
1918
+ "items": {
1919
+ "type": "string"
1920
+ }
1921
+ }
1922
+ ]
1923
+ },
1924
+ "ci": {
1925
+ "type": "boolean"
1926
+ },
1927
+ "env": {
1928
+ "anyOf": [
1929
+ {
1930
+ "anyOf": [
1931
+ {
1932
+ "type": "string"
1933
+ },
1934
+ {
1935
+ "type": "object",
1936
+ "properties": {
1937
+ "equals": {
1938
+ "type": "string",
1939
+ "description": "Match this exact value. Mutually exclusive with `exists`."
1940
+ },
1941
+ "exists": {
1942
+ "type": "boolean",
1943
+ "description": "Assert the variable is set & non-empty (`true`) or unset/empty (`false`)."
1944
+ },
1945
+ "name": {
1946
+ "type": "string",
1947
+ "description": "Variable name."
1948
+ }
1949
+ },
1950
+ "required": [
1951
+ "name"
1952
+ ],
1953
+ "additionalProperties": false
1954
+ }
1955
+ ],
1956
+ "description": "An environment-variable match. The string form is shorthand for `{ name, exists: true }` (set + non-empty). The object form supports either presence assertions or exact-value matching."
1957
+ },
1958
+ {
1959
+ "type": "array",
1960
+ "items": {
1961
+ "anyOf": [
1962
+ {
1963
+ "type": "string"
1964
+ },
1965
+ {
1966
+ "type": "object",
1967
+ "properties": {
1968
+ "equals": {
1969
+ "type": "string",
1970
+ "description": "Match this exact value. Mutually exclusive with `exists`."
1971
+ },
1972
+ "exists": {
1973
+ "type": "boolean",
1974
+ "description": "Assert the variable is set & non-empty (`true`) or unset/empty (`false`)."
1975
+ },
1976
+ "name": {
1977
+ "type": "string",
1978
+ "description": "Variable name."
1979
+ }
1980
+ },
1981
+ "required": [
1982
+ "name"
1983
+ ],
1984
+ "additionalProperties": false
1985
+ }
1986
+ ],
1987
+ "description": "An environment-variable match. The string form is shorthand for `{ name, exists: true }` (set + non-empty). The object form supports either presence assertions or exact-value matching."
1988
+ }
1989
+ }
1990
+ ]
1991
+ },
1992
+ "os": {
1993
+ "anyOf": [
1994
+ {
1995
+ "type": "string",
1996
+ "enum": [
1997
+ "aix",
1998
+ "darwin",
1999
+ "freebsd",
2000
+ "linux",
2001
+ "openbsd",
2002
+ "sunos",
2003
+ "windows",
2004
+ "win32"
2005
+ ],
2006
+ "description": "Aliased platform list — `\"windows\"` is sugar for Node's `\"win32\"`."
2007
+ },
2008
+ {
2009
+ "type": "array",
2010
+ "items": {
2011
+ "type": "string",
2012
+ "enum": [
2013
+ "aix",
2014
+ "darwin",
2015
+ "freebsd",
2016
+ "linux",
2017
+ "openbsd",
2018
+ "sunos",
2019
+ "windows",
2020
+ "win32"
2021
+ ],
2022
+ "description": "Aliased platform list — `\"windows\"` is sugar for Node's `\"win32\"`."
2023
+ }
2024
+ }
2025
+ ]
2026
+ }
2027
+ },
2028
+ "additionalProperties": false,
2029
+ "description": "Negative mirrors. A task runs only when *all* `not.*` clauses fail."
2030
+ },
2031
+ "os": {
2032
+ "anyOf": [
2033
+ {
2034
+ "type": "string",
2035
+ "enum": [
2036
+ "aix",
2037
+ "darwin",
2038
+ "freebsd",
2039
+ "linux",
2040
+ "openbsd",
2041
+ "sunos",
2042
+ "windows",
2043
+ "win32"
2044
+ ],
2045
+ "description": "Aliased platform list — `\"windows\"` is sugar for Node's `\"win32\"`."
2046
+ },
2047
+ {
2048
+ "type": "array",
2049
+ "items": {
2050
+ "type": "string",
2051
+ "enum": [
2052
+ "aix",
2053
+ "darwin",
2054
+ "freebsd",
2055
+ "linux",
2056
+ "openbsd",
2057
+ "sunos",
2058
+ "windows",
2059
+ "win32"
2060
+ ],
2061
+ "description": "Aliased platform list — `\"windows\"` is sugar for Node's `\"win32\"`."
2062
+ }
2063
+ }
2064
+ ],
2065
+ "description": "Match `process.platform` (`\"linux\" | \"darwin\" | \"win32\" | \"freebsd\" | \"openbsd\" | \"sunos\" | \"aix\"`). Pass `\"windows\"` as an alias for `\"win32\"` — easier to remember and matches what people type."
2066
+ }
2067
+ },
2068
+ "additionalProperties": false,
2069
+ "description": "Predicate that gates execution. When the condition evaluates to `false` for the current environment, the task is skipped (marked `\"skipped\"`, not failed). Combine with `os`, `env`, `branch`, and `ci` clauses for granular control:\n\n ```ts when: { os: \"linux\", ci: true, env: \"DEPLOY_TOKEN\" } ```"
2070
+ }
2071
+ },
2072
+ "additionalProperties": false
2073
+ },
2074
+ "description": "Workspace-wide task defaults keyed by target name. Applied universally to every project that exposes a matching target. Use `scopedTasks` when defaults should only apply to a subset of projects."
2075
+ },
2076
+ "scopedTasks": {
2077
+ "type": "array",
2078
+ "items": {
2079
+ "$ref": "#/$defs/ScopedTasksBlock"
2080
+ },
2081
+ "description": "Cascading scoped-task blocks. Each block may narrow its tasks to a subset of projects via `match`. Blocks are evaluated in order; later blocks override earlier ones when the same field is set.\n\nMatch predicates are additive — if `match` is omitted, the block applies to every project."
2082
+ },
2083
+ "taskGroups": {
2084
+ "type": "object",
2085
+ "additionalProperties": {
2086
+ "type": "array",
2087
+ "items": {
2088
+ "anyOf": [
2089
+ {
2090
+ "type": "string"
2091
+ },
2092
+ {
2093
+ "type": "object",
2094
+ "properties": {
2095
+ "dependencies": {
2096
+ "type": "boolean"
2097
+ },
2098
+ "projects": {
2099
+ "anyOf": [
2100
+ {
2101
+ "type": "string"
2102
+ },
2103
+ {
2104
+ "type": "array",
2105
+ "items": {
2106
+ "type": "string"
2107
+ }
2108
+ }
2109
+ ]
2110
+ },
2111
+ "target": {
2112
+ "type": "string"
2113
+ }
2114
+ },
2115
+ "required": [
2116
+ "target"
2117
+ ],
2118
+ "additionalProperties": false
2119
+ },
2120
+ {
2121
+ "type": "object",
2122
+ "properties": {
2123
+ "group": {
2124
+ "type": "string"
2125
+ }
2126
+ },
2127
+ "required": [
2128
+ "group"
2129
+ ],
2130
+ "additionalProperties": false
2131
+ }
2132
+ ]
2133
+ }
2134
+ },
2135
+ "description": "Named bundles of target dependencies, referenceable from any task's `dependsOn`. `dependsOn: [{ group: \"lint\" }]` expands to every entry in the named group; nested groups are resolved recursively and a cycle raises during discovery."
2136
+ },
2137
+ "taskRunner": {
2138
+ "type": "object",
2139
+ "properties": {
2140
+ "autoEnvVars": {
2141
+ "type": "boolean",
2142
+ "description": "Scan each task's resolved command text for `$VAR`/`${VAR}` references and automatically fingerprint those env vars. Catches tasks like `curl ${VERCEL_URL}/api` where the user forgot to declare the reference in `envVars`/`globalEnv`.",
2143
+ "default": false
2144
+ },
2145
+ "autoFingerprint": {
2146
+ "type": "boolean",
2147
+ "description": "Enable auto-fingerprinting mode (Vite Task-style). When enabled, the task runner automatically tracks which files a task accesses during execution and uses that for cache invalidation instead of requiring manual input configuration.\n\nFalls back to explicit inputs (Nx-style) when file tracking is not supported on the current platform.",
2148
+ "default": false
2149
+ },
2150
+ "cacheDiagnostics": {
2151
+ "type": "boolean",
2152
+ "description": "Whether to show cache miss diagnostics (why a cache miss occurred).",
2153
+ "default": false
2154
+ },
2155
+ "cacheDirectory": {
2156
+ "type": "string",
2157
+ "description": "Directory for storing cache"
2158
+ },
2159
+ "dataDirectory": {
2160
+ "type": "string",
2161
+ "description": "Directory used to persist run summaries (`runs/`), `last-summary.json`, and other run-scoped state. Defaults to `{workspaceRoot}/.task-runner` when omitted so standalone task-runner consumers keep their existing layout. Vis sets this to `{workspaceRoot}/.vis` so all per-workspace state lives under a single directory."
2162
+ },
2163
+ "dryRun": {
2164
+ "type": "boolean",
2165
+ "description": "Dry-run mode: compute hashes and check cache but don't execute tasks. Useful for debugging cache hits/misses.",
2166
+ "default": false
2167
+ },
2168
+ "envVars": {
2169
+ "type": "array",
2170
+ "items": {
2171
+ "type": "string"
2172
+ },
2173
+ "description": "Custom environment variables to include in hash"
2174
+ },
2175
+ "fingerprintEnvPatterns": {
2176
+ "type": "array",
2177
+ "items": {
2178
+ "type": "string"
2179
+ },
2180
+ "description": "Environment variable patterns to include in auto-fingerprint. Supports wildcard patterns like \"VITE_*\". Only used when autoFingerprint is enabled."
2181
+ },
2182
+ "frameworkInference": {
2183
+ "type": "boolean",
2184
+ "description": "Enable framework environment variable inference. When true, automatically detects common frontend frameworks and includes their public env var prefixes in the task hash:\n- Next.js: NEXT_PUBLIC_*\n- Vite: VITE_*\n- Create React App: REACT_APP_*\n- Gatsby: GATSBY_*\n- Nuxt: NUXT_PUBLIC_*\n- Expo: EXPO_PUBLIC_*\n\nMatches Turborepo's framework inference behavior.",
2185
+ "default": false
2186
+ },
2187
+ "globalEnv": {
2188
+ "type": "array",
2189
+ "items": {
2190
+ "type": "string"
2191
+ },
2192
+ "description": "Global environment variables that invalidate ALL task hashes. Matches Turborepo's `globalEnv`."
2193
+ },
2194
+ "globalInputs": {
2195
+ "type": "array",
2196
+ "items": {
2197
+ "type": "string"
2198
+ },
2199
+ "description": "Global input files that invalidate ALL task hashes when changed. These are workspace-root-relative paths.\n\nDefault: [\"package-lock.json\", \"pnpm-lock.yaml\", \"yarn.lock\", \"tsconfig.base.json\", \".env\"]\n\nWhen any global input changes, every task's hash changes, forcing a full rebuild. This matches Turborepo's `globalDependencies`."
2200
+ },
2201
+ "incrementalFileHashing": {
2202
+ "type": "boolean",
2203
+ "description": "When `true`, file hashes are cached in a persistent mtime+size indexed snapshot under `node_modules/.cache/task-runner/`. On subsequent runs, unchanged files skip content re-reads — cuts cold-cache fingerprint time dramatically on large workspaces.\n\nChanged or new files still get full content hashing. Safe to leave on by default; overhead when nothing matches is a single `stat` call per file.",
2204
+ "default": false
2205
+ },
2206
+ "concurrencyGroups": {
2207
+ "type": "object",
2208
+ "additionalProperties": {
2209
+ "type": "number"
2210
+ },
2211
+ "description": "Workspace-level concurrency group caps. Maps a group name to the maximum number of in-flight tasks across every target whose `concurrencyGroup` field equals the same name. Independent of `parallel`: the global cap still bounds total tasks, and group caps further bound the named subset.\n\nCommon shape — one cap per shared resource:\n\n ```ts concurrencyGroups: { \"db-bound\": 2, // any target hitting the local Postgres \"deploy-lock\": 1, // any deploy target } ```\n\nTargets opt in via {@link TargetConfiguration.concurrencyGroup } . Values `<= 0` are ignored."
2212
+ },
2213
+ "maxCacheAge": {
2214
+ "type": "number",
2215
+ "description": "Maximum age of cache entries in milliseconds"
2216
+ },
2217
+ "maxCacheSize": {
2218
+ "type": "string",
2219
+ "description": "Maximum cache size (e.g., \"1GB\")"
2220
+ },
2221
+ "namedInputs": {
2222
+ "type": "object",
2223
+ "additionalProperties": {
2224
+ "type": "array",
2225
+ "items": {
2226
+ "anyOf": [
2227
+ {
2228
+ "type": "string"
2229
+ },
2230
+ {
2231
+ "anyOf": [
2232
+ {
2233
+ "type": "object",
2234
+ "properties": {
2235
+ "fileset": {
2236
+ "anyOf": [
2237
+ {
2238
+ "type": "object",
2239
+ "properties": {
2240
+ "base": {
2241
+ "type": "string",
2242
+ "enum": [
2243
+ "package",
2244
+ "workspace"
2245
+ ],
2246
+ "description": "Anchor for the pattern."
2247
+ },
2248
+ "pattern": {
2249
+ "type": "string",
2250
+ "description": "Glob pattern (may start with `!` for negation)."
2251
+ }
2252
+ },
2253
+ "required": [
2254
+ "base",
2255
+ "pattern"
2256
+ ],
2257
+ "additionalProperties": false,
2258
+ "description": "Object form of a fileset pattern, for anchoring to the workspace root."
2259
+ },
2260
+ {
2261
+ "type": "string"
2262
+ }
2263
+ ]
2264
+ }
2265
+ },
2266
+ "required": [
2267
+ "fileset"
2268
+ ],
2269
+ "additionalProperties": false,
2270
+ "description": "An input based on file patterns.\n\n`fileset` may be a bare glob string (package-root relative) or an object form `{ pattern, base }` to anchor explicitly to the workspace root. Negation (`!` prefix) works in both forms."
2271
+ },
2272
+ {
2273
+ "type": "object",
2274
+ "properties": {
2275
+ "runtime": {
2276
+ "type": "string"
2277
+ }
2278
+ },
2279
+ "required": [
2280
+ "runtime"
2281
+ ],
2282
+ "additionalProperties": false,
2283
+ "description": "An input based on a runtime command."
2284
+ },
2285
+ {
2286
+ "type": "object",
2287
+ "properties": {
2288
+ "env": {
2289
+ "type": "string"
2290
+ }
2291
+ },
2292
+ "required": [
2293
+ "env"
2294
+ ],
2295
+ "additionalProperties": false,
2296
+ "description": "An input based on environment variables."
2297
+ },
2298
+ {
2299
+ "type": "object",
2300
+ "properties": {
2301
+ "externalDependencies": {
2302
+ "type": "array",
2303
+ "items": {
2304
+ "type": "string"
2305
+ }
2306
+ }
2307
+ },
2308
+ "required": [
2309
+ "externalDependencies"
2310
+ ],
2311
+ "additionalProperties": false,
2312
+ "description": "An input based on external dependency versions."
2313
+ }
2314
+ ],
2315
+ "description": "Defines an input for cache invalidation."
2316
+ }
2317
+ ]
2318
+ }
2319
+ },
2320
+ "description": "Named inputs for cache invalidation"
2321
+ },
2322
+ "namespaceByGlobalEnv": {
2323
+ "type": "boolean",
2324
+ "description": "When `true`, the cache directory is partitioned by a hash of the resolved `globalEnv` values. Changing a globalEnv var moves cache writes into a new namespace; rolling it back reuses the old namespace and its hits. Without this option, any globalEnv change silently busts every cached entry.\n\nKeep disabled when globalEnv is stable across runs — the extra path depth offers no value, and misconfigured namespaces can hide stale hits.",
2325
+ "default": false
2326
+ },
2327
+ "onFingerprint": {
2328
+ "not": {},
2329
+ "description": "Plugin extension point invoked during task fingerprinting. Fires once per task after the built-in inputs (filesets, runtime, env) have been gathered and before the hash is sealed. Contributions made through the supplied {@link FingerprintContributor } are mixed deterministically into the final hash.\n\nThrowing aborts fingerprinting for that task — the task fails before any cache lookup runs, so a buggy plugin can't silently corrupt cache state.\n\nWired by `vis` to bridge into the `task:fingerprint` hook; standalone task-runner consumers can pass a callback directly."
2330
+ },
2331
+ "parallel": {
2332
+ "type": [
2333
+ "number",
2334
+ "boolean"
2335
+ ],
2336
+ "description": "Maximum number of parallel tasks"
2337
+ },
2338
+ "remoteCache": {
2339
+ "type": "object",
2340
+ "properties": {
2341
+ "allowInsecureBearer": {
2342
+ "type": "boolean",
2343
+ "description": "Opt out of the REAPI safety check that refuses to send a bearer token over cleartext gRPC. Required only when the connection terminates inside a trusted boundary (loopback dev cache, mesh mTLS sidecar that strips/re-encrypts on the next hop). Default `false` — production callers should reach for `grpcs://` first.\n\nREAPI-only."
2344
+ },
2345
+ "backend": {
2346
+ "type": "string",
2347
+ "enum": [
2348
+ "http",
2349
+ "reapi"
2350
+ ],
2351
+ "description": "Wire-protocol selector. `\"http\"` is the Turborepo-compatible single-tarball cache; `\"reapi\"` switches to the Bazel Remote Execution API gRPC client, unlocking `bazel-remote`, BuildBuddy, BuildBarn, EngFlow as drop-in backends.",
2352
+ "default": "http"
2353
+ },
2354
+ "bearerToken": {
2355
+ "type": "string",
2356
+ "description": "Bearer token sent in REAPI's `authorization: Bearer {token}` gRPC metadata header. REAPI-only — HTTP backend uses `token`."
2357
+ },
2358
+ "compression": {
2359
+ "type": "string",
2360
+ "enum": [
2361
+ "brotli",
2362
+ "gzip"
2363
+ ],
2364
+ "description": "HTTP-only: tarball compression on the wire.",
2365
+ "default": "gzip"
2366
+ },
2367
+ "instanceName": {
2368
+ "type": "string",
2369
+ "description": "REAPI-only `instance_name` for multi-tenant servers (a single gRPC endpoint can host multiple logical caches keyed on the `instance_name` prefix)."
2370
+ },
2371
+ "localCasRoot": {
2372
+ "type": "string",
2373
+ "description": "Local CAS root used by the per-blob {@link RemoteCacheBackend } methods. The HTTP wire format is still single-tarball; on retrieve the bridge extracts blobs into this root so a follow-up `fetchBlob` is just a local read."
2374
+ },
2375
+ "mode": {
2376
+ "type": "string",
2377
+ "enum": [
2378
+ "read",
2379
+ "readwrite",
2380
+ "write"
2381
+ ],
2382
+ "description": "Canonical cache mode flag.\n\n- `\"read\"`: Pull cache hits, never push. Sensible default for local dev so a developer doesn't poison the shared cache while their workspace is dirty.\n- `\"write\"`: Push results, never read. Useful for refill / warm-up jobs that should always re-execute and re-upload.\n- `\"readwrite\"`: Both. Default for CI.",
2383
+ "default": "readwrite"
2384
+ },
2385
+ "signing": {
2386
+ "type": "object",
2387
+ "properties": {
2388
+ "secret": {
2389
+ "type": "string",
2390
+ "description": "Shared secret. Must be at least 16 characters."
2391
+ },
2392
+ "verifyOnDownload": {
2393
+ "type": "boolean",
2394
+ "description": "Reject downloads whose signature doesn't match or is missing. Set to `true` once every upload on your server is signed.",
2395
+ "default": false
2396
+ }
2397
+ },
2398
+ "required": [
2399
+ "secret"
2400
+ ],
2401
+ "additionalProperties": false,
2402
+ "description": "HTTP-only: HMAC-SHA256 signing for upload integrity."
2403
+ },
2404
+ "teamId": {
2405
+ "type": "string",
2406
+ "description": "Team / namespace for cache isolation."
2407
+ },
2408
+ "timeout": {
2409
+ "type": "number",
2410
+ "description": "Per-call request timeout in milliseconds.",
2411
+ "default": 30000
2412
+ },
2413
+ "token": {
2414
+ "type": "string",
2415
+ "description": "HTTP authentication token sent as `Authorization: Bearer …`. For REAPI, use `bearerToken` instead — same wire mechanic, but a separate field so REAPI's cleartext-bearer guard can distinguish \"user explicitly opted into a gRPC token\" from \"user set HTTP auth and accidentally selected the REAPI backend\"."
2416
+ },
2417
+ "url": {
2418
+ "type": "string",
2419
+ "description": "Cache server URL.\n\n- HTTP: `https://cache.example.com`\n- REAPI: `grpcs://host:port` (TLS, recommended) or `grpc://host:port` (cleartext — bearer tokens are refused unless `allowInsecureBearer`)."
2420
+ }
2421
+ },
2422
+ "required": [
2423
+ "url"
2424
+ ],
2425
+ "additionalProperties": false,
2426
+ "description": "Remote cache configuration. When configured, the task runner checks the remote cache after a local miss and uploads results after execution. See {@link RemoteCacheOptions } for the full HTTP and REAPI surface."
2427
+ },
2428
+ "skipNxCache": {
2429
+ "type": "boolean",
2430
+ "description": "Whether to skip cache reads"
2431
+ },
2432
+ "smartLockfileHashing": {
2433
+ "type": "boolean",
2434
+ "description": "Enable smart lockfile hashing. Instead of hashing the entire lockfile (which busts ALL caches), only hash the resolved versions of each package's actual dependencies. This matches Turborepo's smart lockfile hashing behavior.",
2435
+ "default": false
2436
+ },
2437
+ "summarize": {
2438
+ "type": "boolean",
2439
+ "description": "Generate a detailed JSON run summary after execution. When true, writes a summary file to `.task-runner/runs/` containing all task inputs, outputs, hashes, timings, and cache status.\n\nUseful for debugging cache misses and comparing runs. Matches Turborepo's `--summarize` flag.",
2440
+ "default": false
2441
+ },
2442
+ "targetDefaults": {
2443
+ "type": "object",
2444
+ "additionalProperties": {
2445
+ "type": "object",
2446
+ "properties": {
2447
+ "always": {
2448
+ "type": "boolean",
2449
+ "description": "When `true`, this target runs after the main task graph completes — even if upstream tasks failed. Useful for cleanup, teardown, notifications, or anything that should fire regardless of build outcome. Always-tasks are not part of the normal dependency graph and never block other tasks."
2450
+ },
2451
+ "cache": {
2452
+ "type": "boolean",
2453
+ "description": "Whether this target is cacheable"
2454
+ },
2455
+ "cacheOnWarning": {
2456
+ "type": "boolean",
2457
+ "description": "When `false`, exit-0 runs whose terminal output matched any {@link TargetConfiguration.warningPattern } are not seeded into the cache. Defaults to `true` — warnings are surfaced on the result (`hadWarnings: true`) but caching still happens, matching the \"succeeded with warnings is incremental\" behaviour rush, lage and wireit users have repeatedly asked for."
2458
+ },
2459
+ "cacheRestore": {
2460
+ "type": "object",
2461
+ "properties": {
2462
+ "preserveMtime": {
2463
+ "type": "boolean",
2464
+ "description": "Restore each file's modification time from the captured tar header (second precision). When `false`, restored files take the current wall-clock time, matching the legacy behaviour."
2465
+ },
2466
+ "preservePerms": {
2467
+ "type": "boolean",
2468
+ "description": "Restore each file's POSIX mode bits (rwx triplets, low 12 bits). When `false`, restored files take the process umask. Only meaningful on POSIX hosts; Windows ignores tar mode."
2469
+ }
2470
+ },
2471
+ "additionalProperties": false,
2472
+ "description": "Fine-grained controls over how cached outputs are rehydrated. See {@link CacheRestoreOptions } . Both fields default to `true` (faithful restore); flip individually when downstream tooling needs the legacy \"now\"-stamped behaviour."
2473
+ },
2474
+ "command": {
2475
+ "type": "string",
2476
+ "description": "The command to run (alternative to executor)"
2477
+ },
2478
+ "configurations": {
2479
+ "type": "object",
2480
+ "additionalProperties": {
2481
+ "type": "object",
2482
+ "additionalProperties": {}
2483
+ },
2484
+ "description": "Named configurations (e.g., \"production\", \"development\")"
2485
+ },
2486
+ "concurrencyGroup": {
2487
+ "type": "string",
2488
+ "description": "Workspace-level concurrency group this target opts into. The scheduler caps the *combined* in-flight count of every task whose target carries the same group name to the value declared in {@link TaskRunnerOptions.concurrencyGroups } . Use this when several targets share an external resource (a single Postgres, a developer's Docker daemon, an account-wide deploy lock) so the cap follows the resource, not any single target name.\n\nTargets that name a group not declared in `concurrencyGroups` run uncapped — a typo shouldn't deadlock the graph. Caps don't apply to tasks marked `always: true`; those run in a separate finalisation phase outside the scheduler."
2489
+ },
2490
+ "dependsOn": {
2491
+ "type": "array",
2492
+ "items": {
2493
+ "anyOf": [
2494
+ {
2495
+ "type": "string"
2496
+ },
2497
+ {
2498
+ "type": "object",
2499
+ "properties": {
2500
+ "dependencies": {
2501
+ "type": "boolean",
2502
+ "description": "Whether this is a dependency on the same project"
2503
+ },
2504
+ "params": {
2505
+ "type": "string",
2506
+ "enum": [
2507
+ "forward",
2508
+ "ignore"
2509
+ ],
2510
+ "description": "Params to pass through"
2511
+ },
2512
+ "projects": {
2513
+ "anyOf": [
2514
+ {
2515
+ "type": "string"
2516
+ },
2517
+ {
2518
+ "type": "array",
2519
+ "items": {
2520
+ "type": "string"
2521
+ }
2522
+ }
2523
+ ],
2524
+ "description": "The project name (if different from the current project)"
2525
+ },
2526
+ "target": {
2527
+ "type": "string",
2528
+ "description": "The target name"
2529
+ }
2530
+ },
2531
+ "required": [
2532
+ "target"
2533
+ ],
2534
+ "additionalProperties": false,
2535
+ "description": "Defines a dependency on another target."
2536
+ }
2537
+ ]
2538
+ },
2539
+ "description": "Other targets this target depends on"
2540
+ },
2541
+ "executor": {
2542
+ "type": "string",
2543
+ "description": "The executor/command to run"
2544
+ },
2545
+ "inputs": {
2546
+ "type": "array",
2547
+ "items": {
2548
+ "anyOf": [
2549
+ {
2550
+ "type": "string"
2551
+ },
2552
+ {
2553
+ "anyOf": [
2554
+ {
2555
+ "type": "object",
2556
+ "properties": {
2557
+ "fileset": {
2558
+ "anyOf": [
2559
+ {
2560
+ "type": "object",
2561
+ "properties": {
2562
+ "base": {
2563
+ "type": "string",
2564
+ "enum": [
2565
+ "package",
2566
+ "workspace"
2567
+ ],
2568
+ "description": "Anchor for the pattern."
2569
+ },
2570
+ "pattern": {
2571
+ "type": "string",
2572
+ "description": "Glob pattern (may start with `!` for negation)."
2573
+ }
2574
+ },
2575
+ "required": [
2576
+ "base",
2577
+ "pattern"
2578
+ ],
2579
+ "additionalProperties": false,
2580
+ "description": "Object form of a fileset pattern, for anchoring to the workspace root."
2581
+ },
2582
+ {
2583
+ "type": "string"
2584
+ }
2585
+ ]
2586
+ }
2587
+ },
2588
+ "required": [
2589
+ "fileset"
2590
+ ],
2591
+ "additionalProperties": false,
2592
+ "description": "An input based on file patterns.\n\n`fileset` may be a bare glob string (package-root relative) or an object form `{ pattern, base }` to anchor explicitly to the workspace root. Negation (`!` prefix) works in both forms."
2593
+ },
2594
+ {
2595
+ "type": "object",
2596
+ "properties": {
2597
+ "runtime": {
2598
+ "type": "string"
2599
+ }
2600
+ },
2601
+ "required": [
2602
+ "runtime"
2603
+ ],
2604
+ "additionalProperties": false,
2605
+ "description": "An input based on a runtime command."
2606
+ },
2607
+ {
2608
+ "type": "object",
2609
+ "properties": {
2610
+ "env": {
2611
+ "type": "string"
2612
+ }
2613
+ },
2614
+ "required": [
2615
+ "env"
2616
+ ],
2617
+ "additionalProperties": false,
2618
+ "description": "An input based on environment variables."
2619
+ },
2620
+ {
2621
+ "type": "object",
2622
+ "properties": {
2623
+ "externalDependencies": {
2624
+ "type": "array",
2625
+ "items": {
2626
+ "type": "string"
2627
+ }
2628
+ }
2629
+ },
2630
+ "required": [
2631
+ "externalDependencies"
2632
+ ],
2633
+ "additionalProperties": false,
2634
+ "description": "An input based on external dependency versions."
2635
+ }
2636
+ ],
2637
+ "description": "Defines an input for cache invalidation."
2638
+ }
2639
+ ]
2640
+ },
2641
+ "description": "Input patterns for cache invalidation"
2642
+ },
2643
+ "options": {
2644
+ "type": "object",
2645
+ "additionalProperties": {},
2646
+ "description": "Options passed to the executor"
2647
+ },
2648
+ "maxConcurrent": {
2649
+ "type": "number",
2650
+ "description": "Maximum number of in-flight instances of this target across the whole graph. When set, the scheduler refuses to start an additional task whose `target.target` equals this target's name once the running count reaches `maxConcurrent`. Independent of `--parallel`: the global cap still bounds total in-flight tasks, and `maxConcurrent` further bounds the per-target subset.\n\nUse for tests/deploys that share an external resource (DB, port, mock server). Set to `1` to serialize. Values `<= 0` are ignored.\n\nIf multiple projects declare different values for the same target name, the runner uses the smallest declared cap. Caps don't apply to tasks marked `always: true`; those run in a separate finalisation phase outside the scheduler."
2651
+ },
2652
+ "outputs": {
2653
+ "type": "array",
2654
+ "items": {
2655
+ "type": "string"
2656
+ },
2657
+ "description": "Output patterns produced by this target"
2658
+ },
2659
+ "parallelism": {
2660
+ "type": "boolean",
2661
+ "description": "Whether this target supports parallel execution"
2662
+ },
2663
+ "warningPattern": {
2664
+ "anyOf": [
2665
+ {
2666
+ "type": "string"
2667
+ },
2668
+ {
2669
+ "type": "array",
2670
+ "items": {
2671
+ "type": "string"
2672
+ }
2673
+ }
2674
+ ],
2675
+ "description": "Regex source string(s) that mark a successful task as having emitted warnings. The orchestrator scans the task's combined terminal output after a 0-exit and, on first match, sets `hadWarnings` on the result. Combine with `cacheOnWarning: false` to skip caching for warning-tainted runs. Both bare strings and arrays are accepted; arrays are tested in order.\n\n ```ts warningPattern: [\"\\\\bwarning\\\\b\", \"TS\\\\d{4}\"] ```"
2676
+ },
2677
+ "when": {
2678
+ "type": "object",
2679
+ "properties": {
2680
+ "branch": {
2681
+ "anyOf": [
2682
+ {
2683
+ "type": "string"
2684
+ },
2685
+ {
2686
+ "type": "array",
2687
+ "items": {
2688
+ "type": "string"
2689
+ }
2690
+ }
2691
+ ],
2692
+ "description": "Match against the current git branch (HEAD)."
2693
+ },
2694
+ "ci": {
2695
+ "type": "boolean",
2696
+ "description": "Run only when invoked inside CI when `true`, only outside CI when `false`. Detects CI via the `CI` env var (the convention GitHub Actions, GitLab, CircleCI, Jenkins, etc. all share)."
2697
+ },
2698
+ "env": {
2699
+ "anyOf": [
2700
+ {
2701
+ "anyOf": [
2702
+ {
2703
+ "type": "string"
2704
+ },
2705
+ {
2706
+ "type": "object",
2707
+ "properties": {
2708
+ "equals": {
2709
+ "type": "string",
2710
+ "description": "Match this exact value. Mutually exclusive with `exists`."
2711
+ },
2712
+ "exists": {
2713
+ "type": "boolean",
2714
+ "description": "Assert the variable is set & non-empty (`true`) or unset/empty (`false`)."
2715
+ },
2716
+ "name": {
2717
+ "type": "string",
2718
+ "description": "Variable name."
2719
+ }
2720
+ },
2721
+ "required": [
2722
+ "name"
2723
+ ],
2724
+ "additionalProperties": false
2725
+ }
2726
+ ],
2727
+ "description": "An environment-variable match. The string form is shorthand for `{ name, exists: true }` (set + non-empty). The object form supports either presence assertions or exact-value matching."
2728
+ },
2729
+ {
2730
+ "type": "array",
2731
+ "items": {
2732
+ "anyOf": [
2733
+ {
2734
+ "type": "string"
2735
+ },
2736
+ {
2737
+ "type": "object",
2738
+ "properties": {
2739
+ "equals": {
2740
+ "type": "string",
2741
+ "description": "Match this exact value. Mutually exclusive with `exists`."
2742
+ },
2743
+ "exists": {
2744
+ "type": "boolean",
2745
+ "description": "Assert the variable is set & non-empty (`true`) or unset/empty (`false`)."
2746
+ },
2747
+ "name": {
2748
+ "type": "string",
2749
+ "description": "Variable name."
2750
+ }
2751
+ },
2752
+ "required": [
2753
+ "name"
2754
+ ],
2755
+ "additionalProperties": false
2756
+ }
2757
+ ],
2758
+ "description": "An environment-variable match. The string form is shorthand for `{ name, exists: true }` (set + non-empty). The object form supports either presence assertions or exact-value matching."
2759
+ }
2760
+ }
2761
+ ],
2762
+ "description": "Match against environment variables. A bare string asserts the variable is set and non-empty; the object form lets you match an exact value or just assert presence/absence."
2763
+ },
2764
+ "not": {
2765
+ "type": "object",
2766
+ "properties": {
2767
+ "branch": {
2768
+ "anyOf": [
2769
+ {
2770
+ "type": "string"
2771
+ },
2772
+ {
2773
+ "type": "array",
2774
+ "items": {
2775
+ "type": "string"
2776
+ }
2777
+ }
2778
+ ]
2779
+ },
2780
+ "ci": {
2781
+ "type": "boolean"
2782
+ },
2783
+ "env": {
2784
+ "anyOf": [
2785
+ {
2786
+ "anyOf": [
2787
+ {
2788
+ "type": "string"
2789
+ },
2790
+ {
2791
+ "type": "object",
2792
+ "properties": {
2793
+ "equals": {
2794
+ "type": "string",
2795
+ "description": "Match this exact value. Mutually exclusive with `exists`."
2796
+ },
2797
+ "exists": {
2798
+ "type": "boolean",
2799
+ "description": "Assert the variable is set & non-empty (`true`) or unset/empty (`false`)."
2800
+ },
2801
+ "name": {
2802
+ "type": "string",
2803
+ "description": "Variable name."
2804
+ }
2805
+ },
2806
+ "required": [
2807
+ "name"
2808
+ ],
2809
+ "additionalProperties": false
2810
+ }
2811
+ ],
2812
+ "description": "An environment-variable match. The string form is shorthand for `{ name, exists: true }` (set + non-empty). The object form supports either presence assertions or exact-value matching."
2813
+ },
2814
+ {
2815
+ "type": "array",
2816
+ "items": {
2817
+ "anyOf": [
2818
+ {
2819
+ "type": "string"
2820
+ },
2821
+ {
2822
+ "type": "object",
2823
+ "properties": {
2824
+ "equals": {
2825
+ "type": "string",
2826
+ "description": "Match this exact value. Mutually exclusive with `exists`."
2827
+ },
2828
+ "exists": {
2829
+ "type": "boolean",
2830
+ "description": "Assert the variable is set & non-empty (`true`) or unset/empty (`false`)."
2831
+ },
2832
+ "name": {
2833
+ "type": "string",
2834
+ "description": "Variable name."
2835
+ }
2836
+ },
2837
+ "required": [
2838
+ "name"
2839
+ ],
2840
+ "additionalProperties": false
2841
+ }
2842
+ ],
2843
+ "description": "An environment-variable match. The string form is shorthand for `{ name, exists: true }` (set + non-empty). The object form supports either presence assertions or exact-value matching."
2844
+ }
2845
+ }
2846
+ ]
2847
+ },
2848
+ "os": {
2849
+ "anyOf": [
2850
+ {
2851
+ "type": "string",
2852
+ "enum": [
2853
+ "aix",
2854
+ "darwin",
2855
+ "freebsd",
2856
+ "linux",
2857
+ "openbsd",
2858
+ "sunos",
2859
+ "windows",
2860
+ "win32"
2861
+ ],
2862
+ "description": "Aliased platform list — `\"windows\"` is sugar for Node's `\"win32\"`."
2863
+ },
2864
+ {
2865
+ "type": "array",
2866
+ "items": {
2867
+ "type": "string",
2868
+ "enum": [
2869
+ "aix",
2870
+ "darwin",
2871
+ "freebsd",
2872
+ "linux",
2873
+ "openbsd",
2874
+ "sunos",
2875
+ "windows",
2876
+ "win32"
2877
+ ],
2878
+ "description": "Aliased platform list — `\"windows\"` is sugar for Node's `\"win32\"`."
2879
+ }
2880
+ }
2881
+ ]
2882
+ }
2883
+ },
2884
+ "additionalProperties": false,
2885
+ "description": "Negative mirrors. A task runs only when *all* `not.*` clauses fail."
2886
+ },
2887
+ "os": {
2888
+ "anyOf": [
2889
+ {
2890
+ "type": "string",
2891
+ "enum": [
2892
+ "aix",
2893
+ "darwin",
2894
+ "freebsd",
2895
+ "linux",
2896
+ "openbsd",
2897
+ "sunos",
2898
+ "windows",
2899
+ "win32"
2900
+ ],
2901
+ "description": "Aliased platform list — `\"windows\"` is sugar for Node's `\"win32\"`."
2902
+ },
2903
+ {
2904
+ "type": "array",
2905
+ "items": {
2906
+ "type": "string",
2907
+ "enum": [
2908
+ "aix",
2909
+ "darwin",
2910
+ "freebsd",
2911
+ "linux",
2912
+ "openbsd",
2913
+ "sunos",
2914
+ "windows",
2915
+ "win32"
2916
+ ],
2917
+ "description": "Aliased platform list — `\"windows\"` is sugar for Node's `\"win32\"`."
2918
+ }
2919
+ }
2920
+ ],
2921
+ "description": "Match `process.platform` (`\"linux\" | \"darwin\" | \"win32\" | \"freebsd\" | \"openbsd\" | \"sunos\" | \"aix\"`). Pass `\"windows\"` as an alias for `\"win32\"` — easier to remember and matches what people type."
2922
+ }
2923
+ },
2924
+ "additionalProperties": false,
2925
+ "description": "Predicate that gates execution. When the condition evaluates to `false` for the current environment, the task is skipped (marked `\"skipped\"`, not failed). Combine with `os`, `env`, `branch`, and `ci` clauses for granular control:\n\n ```ts when: { os: \"linux\", ci: true, env: \"DEPLOY_TOKEN\" } ```"
2926
+ }
2927
+ },
2928
+ "additionalProperties": false
2929
+ },
2930
+ "description": "Target-specific default configurations"
2931
+ },
2932
+ "untrackedEnvVars": {
2933
+ "type": "array",
2934
+ "items": {
2935
+ "type": "string"
2936
+ },
2937
+ "description": "Environment variables that should be passed to tasks but NOT included in the cache fingerprint. Useful for CI-specific vars. Only used when autoFingerprint is enabled."
2938
+ }
2939
+ },
2940
+ "additionalProperties": false,
2941
+ "description": "Task runner options forwarded verbatim to `defaultTaskRunner`.\n\nIncludes `remoteCache` (HTTP or REAPI gRPC backend), `cacheDirectory`, `parallel`, `globalEnv`, `globalInputs`, etc. See `TaskRunnerOptions` for the full surface."
2942
+ },
2943
+ "toolchain": {
2944
+ "type": "object",
2945
+ "additionalProperties": false,
2946
+ "properties": {
2947
+ "preferredManager": {
2948
+ "type": "string",
2949
+ "enum": [
2950
+ "asdf",
2951
+ "corepack",
2952
+ "fnm",
2953
+ "mise",
2954
+ "none",
2955
+ "nvm",
2956
+ "proto",
2957
+ "volta"
2958
+ ]
2959
+ },
2960
+ "autoInstall": {
2961
+ "type": "boolean",
2962
+ "description": "When a tool pin doesn't match the running version, try to fix it automatically before `vis run` / `vis ci` proceed. Defaults to `true` when {@link findInstalledManagers } reports at least one installed manager, `false` otherwise.\n\nSet to `false` to keep the doctor-style warning behaviour and make users run `vis toolchain install` themselves."
2963
+ },
2964
+ "tools": {
2965
+ "type": "object",
2966
+ "properties": {
2967
+ "bun": {
2968
+ "type": "string"
2969
+ },
2970
+ "deno": {
2971
+ "type": "string"
2972
+ },
2973
+ "go": {
2974
+ "type": "string"
2975
+ },
2976
+ "node": {
2977
+ "type": "string"
2978
+ },
2979
+ "npm": {
2980
+ "type": "string"
2981
+ },
2982
+ "pnpm": {
2983
+ "type": "string"
2984
+ },
2985
+ "python": {
2986
+ "type": "string"
2987
+ },
2988
+ "ruby": {
2989
+ "type": "string"
2990
+ },
2991
+ "rust": {
2992
+ "type": "string"
2993
+ },
2994
+ "yarn": {
2995
+ "type": "string"
2996
+ }
2997
+ },
2998
+ "additionalProperties": false,
2999
+ "description": "Overrides for engines/packageManager-derived pins."
3000
+ }
3001
+ },
3002
+ "description": "Toolchain (Node / pnpm / python / rust / ...) management. vis delegates to whichever version manager (proto, mise, fnm, volta, asdf, nvm, corepack) the developer already has — it does not ship its own.\n\nRe-exported from `./toolchain` so the public config type stays in lockstep with the resolver implementation. `self-activate` is narrowed out of `preferredManager` here — it's auto-resolved for pnpm/yarn `packageManager` pins and isn't meaningful as an override."
3003
+ },
3004
+ "tui": {
3005
+ "type": "object",
3006
+ "properties": {
3007
+ "autoExit": {
3008
+ "type": [
3009
+ "boolean",
3010
+ "number"
3011
+ ],
3012
+ "description": "Auto-exit the TUI after tasks complete.\n- `false`: Stay open until the user presses `q` (default)\n- `true`: Show quit dialog with 3-second countdown after completion\n- `number`: Show quit dialog with custom countdown in seconds"
3013
+ }
3014
+ },
3015
+ "additionalProperties": false,
3016
+ "description": "Terminal UI configuration"
3017
+ },
3018
+ "update": {
3019
+ "type": "object",
3020
+ "properties": {
3021
+ "depFields": {
3022
+ "type": "array",
3023
+ "items": {
3024
+ "type": "string"
3025
+ },
3026
+ "description": "Dependency fields to scan for outdated packages. Beyond the standard fields, supports:\n- `\"overrides\"` (npm)\n- `\"resolutions\"` (yarn)\n- `\"pnpm.overrides\"`",
3027
+ "default": [
3028
+ "dependencies",
3029
+ "devDependencies",
3030
+ "optionalDependencies",
3031
+ "peerDependencies"
3032
+ ]
3033
+ },
3034
+ "exclude": {
3035
+ "type": "array",
3036
+ "items": {
3037
+ "type": "string"
3038
+ }
3039
+ },
3040
+ "format": {
3041
+ "type": "string",
3042
+ "enum": [
3043
+ "json",
3044
+ "minimal",
3045
+ "table"
3046
+ ]
3047
+ },
3048
+ "ignore": {
3049
+ "type": "array",
3050
+ "items": {
3051
+ "type": "string"
3052
+ },
3053
+ "description": "Package names or glob patterns to permanently ignore during updates. Ignored packages are skipped and listed in the output so you know they were not checked.",
3054
+ "examples": [
3055
+ [
3056
+ "eslint",
3057
+ "@types/*"
3058
+ ]
3059
+ ]
3060
+ },
3061
+ "include": {
3062
+ "type": "array",
3063
+ "items": {
3064
+ "type": "string"
3065
+ }
3066
+ },
3067
+ "includeLocked": {
3068
+ "type": "boolean",
3069
+ "description": "Include packages with pinned/exact versions (no `^` or `~` prefix). By default, pinned versions are skipped during update checks.",
3070
+ "default": false
3071
+ },
3072
+ "install": {
3073
+ "type": "boolean"
3074
+ },
3075
+ "maxConcurrentRequests": {
3076
+ "type": "number",
3077
+ "description": "Maximum number of concurrent registry requests during outdated checks. Higher values speed up large workspaces but risk hitting registry rate limits or self-hosted Verdaccio caps.",
3078
+ "default": 8
3079
+ },
3080
+ "minimumReleaseAge": {
3081
+ "type": "number",
3082
+ "description": "Minimum number of minutes since a version was published before vis will consider it for updates. This mirrors pnpm's `minimumReleaseAge` — a single setting that applies to both install and update.\n\nNot set by default. If your package manager config (`pnpm-workspace.yaml`) has `minimumReleaseAge`, vis will read it from there as a fallback.",
3083
+ "examples": [
3084
+ 1440
3085
+ ]
3086
+ },
3087
+ "minimumReleaseAgeExclude": {
3088
+ "type": "array",
3089
+ "items": {
3090
+ "type": "string"
3091
+ },
3092
+ "description": "Package names/patterns excluded from the minimumReleaseAge check.",
3093
+ "examples": [
3094
+ [
3095
+ "webpack",
3096
+ "@myorg/*"
3097
+ ]
3098
+ ]
3099
+ },
3100
+ "packageMode": {
3101
+ "type": "object",
3102
+ "additionalProperties": {
3103
+ "type": "string",
3104
+ "enum": [
3105
+ "latest",
3106
+ "minor",
3107
+ "patch"
3108
+ ]
3109
+ },
3110
+ "description": "Per-package or per-pattern update target overrides. Keys are exact package names, glob patterns, or regex patterns wrapped in `/` (e.g., `/^@vue/`). Values are `\"latest\"`, `\"minor\"`, or `\"patch\"`.",
3111
+ "examples": [
3112
+ {
3113
+ "typescript": "minor",
3114
+ "/^@vue/": "patch"
3115
+ }
3116
+ ]
3117
+ },
3118
+ "prerelease": {
3119
+ "type": "boolean"
3120
+ },
3121
+ "releaseChannel": {
3122
+ "type": "string",
3123
+ "enum": [
3124
+ "any",
3125
+ "same",
3126
+ "stable"
3127
+ ],
3128
+ "description": "Which release channels to consider when picking the target version.\n- `\"stable\"` (default) — only ship stable releases (no prereleases).\n- `\"same\"` — match the prerelease channel of the *current* range: if you're on `react@19.0.0-rc.1`, only `rc.*` candidates qualify; if you're on a stable, only stable candidates. Prevents accidentally promoting a prerelease pin to a stable major bump.\n- `\"any\"` — equivalent to `--prerelease`. Any channel is fair game.\n\n`--release-channel` on the CLI overrides this. If `prerelease: true` is set without `releaseChannel`, vis treats it as `\"any\"`.",
3129
+ "default": "stable"
3130
+ },
3131
+ "security": {
3132
+ "type": "boolean"
3133
+ },
3134
+ "target": {
3135
+ "type": "string",
3136
+ "enum": [
3137
+ "latest",
3138
+ "minor",
3139
+ "patch"
3140
+ ]
3141
+ }
3142
+ },
3143
+ "additionalProperties": false,
3144
+ "description": "Update command defaults"
3145
+ },
3146
+ "versionConstraint": {
3147
+ "type": "string",
3148
+ "description": "Minimum vis CLI version required by this workspace. When the running vis binary is older than this constraint, vis exits with an actionable error before executing any command.\n\nAccepts a semver range string (e.g. `\">=1.0.0\"`, `\"^1.2.0\"`).",
3149
+ "examples": [
3150
+ ">=1.0.0"
3151
+ ]
3152
+ }
3153
+ },
3154
+ "additionalProperties": false,
3155
+ "$defs": {
3156
+ "CodeownersConfig": {
3157
+ "type": "object",
3158
+ "properties": {
3159
+ "globalPaths": {
3160
+ "type": "object",
3161
+ "additionalProperties": {
3162
+ "type": "array",
3163
+ "items": {
3164
+ "type": "string"
3165
+ }
3166
+ },
3167
+ "description": "Workspace-level paths that apply outside any project (e.g., `.github/**`)."
3168
+ },
3169
+ "orderBy": {
3170
+ "type": "string",
3171
+ "enum": [
3172
+ "file-source",
3173
+ "project-id"
3174
+ ],
3175
+ "description": "Sort order for generated entries — mirrors moon's `orderBy`."
3176
+ },
3177
+ "provider": {
3178
+ "type": "string",
3179
+ "enum": [
3180
+ "bitbucket",
3181
+ "github",
3182
+ "gitlab",
3183
+ "other"
3184
+ ],
3185
+ "description": "Provider determines whether `channel` is emitted (GitHub supports it via comment)."
3186
+ }
3187
+ },
3188
+ "additionalProperties": false
3189
+ },
3190
+ "VisPlugin": {
3191
+ "type": "object",
3192
+ "properties": {
3193
+ "hooks": {
3194
+ "type": "object",
3195
+ "properties": {
3196
+ "run:after": {
3197
+ "type": "array",
3198
+ "items": {
3199
+ "not": {},
3200
+ "description": "Fired after the entire task graph completes (including any failures). `results` maps task ID → {@link TaskResult } ."
3201
+ }
3202
+ },
3203
+ "run:before": {
3204
+ "type": "array",
3205
+ "items": {
3206
+ "not": {},
3207
+ "description": "Fired once before any task in the graph starts, after workspace discovery and graph construction. Throwing aborts the run."
3208
+ }
3209
+ },
3210
+ "service:attach": {
3211
+ "type": "array",
3212
+ "items": {
3213
+ "not": {},
3214
+ "description": "Fired after `vis run` auto-attaches to one or more registered services. `taskIds` lists the in-graph dependents that consumed the service's `env` block; an empty array means the service was registered but no kept task depended on it."
3215
+ }
3216
+ },
3217
+ "service:start": {
3218
+ "type": "array",
3219
+ "items": {
3220
+ "not": {},
3221
+ "description": "Fired after a service is registered and its readiness probe succeeds. Sourced from both `vis service start` (and `restart`'s post-start phase) and any future programmatic call sites."
3222
+ }
3223
+ },
3224
+ "service:stop": {
3225
+ "type": "array",
3226
+ "items": {
3227
+ "not": {},
3228
+ "description": "Fired after a registered service is stopped (SIGTERM/SIGKILL acknowledged, registry entry deleted). Not fired when stop is called against an unknown id — only when there was an alive entry to terminate."
3229
+ }
3230
+ },
3231
+ "task:after": {
3232
+ "type": "array",
3233
+ "items": {
3234
+ "not": {},
3235
+ "description": "Fired after a task completes (success, failure, or cache hit). Receives the final {@link TaskResult } ."
3236
+ }
3237
+ },
3238
+ "task:before": {
3239
+ "type": "array",
3240
+ "items": {
3241
+ "not": {},
3242
+ "description": "Fired before each task begins execution — after scheduling, before the executor runs the command. Throwing aborts that single task."
3243
+ }
3244
+ },
3245
+ "task:cacheHit": {
3246
+ "type": "array",
3247
+ "items": {
3248
+ "not": {},
3249
+ "description": "Fired when a task hit the local or remote cache."
3250
+ }
3251
+ },
3252
+ "task:cacheMiss": {
3253
+ "type": "array",
3254
+ "items": {
3255
+ "not": {},
3256
+ "description": "Fired when auto-fingerprint cache diagnostics reports a miss, carrying the human-readable reason string."
3257
+ }
3258
+ },
3259
+ "task:failure": {
3260
+ "type": "array",
3261
+ "items": {
3262
+ "not": {},
3263
+ "description": "Fired when a task exits non-zero."
3264
+ }
3265
+ },
3266
+ "task:fingerprint": {
3267
+ "type": "array",
3268
+ "items": {
3269
+ "not": {},
3270
+ "description": "Fired during fingerprint construction, after built-in inputs are gathered and before the hash is sealed. Plugins call `contributor.contribute(key, value)` to mix arbitrary strings into the task hash — the hasher namespaces and sorts contributions deterministically so call order doesn't change the result.\n\nThrowing aborts hashing for the offending task and surfaces as a task failure before any cache lookup runs. Use this to guarantee a buggy plugin can't quietly poison cache state."
3271
+ }
3272
+ },
3273
+ "task:retry": {
3274
+ "type": "array",
3275
+ "items": {
3276
+ "not": {},
3277
+ "description": "Fired right before a failed task is re-spawned by the retry controller. `attempt` is 1-indexed and counts the retry that's about to start (so the original failed run was attempt 0). `prevExitCode` is the failing exit status that triggered the retry (the full TaskResult isn't materialized at the retry boundary — only the per-attempt close event is available).\n\nThrowing aborts the retry; the previous failure becomes the final result."
3278
+ }
3279
+ },
3280
+ "task:stderr": {
3281
+ "type": "array",
3282
+ "items": {
3283
+ "not": {},
3284
+ "description": "Fired with a stderr chunk as a running task emits it. Plugins that ship logs live (Slack, Datadog) should prefer this over `task:after` so they don't wait for the full buffer."
3285
+ }
3286
+ },
3287
+ "task:stdout": {
3288
+ "type": "array",
3289
+ "items": {
3290
+ "not": {},
3291
+ "description": "Fired with a stdout chunk as a running task emits it. See `task:stderr` for semantics."
3292
+ }
3293
+ }
3294
+ },
3295
+ "additionalProperties": false,
3296
+ "description": "Declarative handlers — the common shape. One entry per hook name; pass a function or an array of functions (all run serially in order)."
3297
+ },
3298
+ "name": {
3299
+ "type": "string",
3300
+ "description": "Plugin name — surfaced in debug logs."
3301
+ }
3302
+ },
3303
+ "required": [
3304
+ "name"
3305
+ ],
3306
+ "additionalProperties": false,
3307
+ "description": "Public plugin contract. Implementations register handlers by returning a partial {@link VisHooks } map from `hooks`, or by mutating the Hookable instance directly via `setup(hooks)` for advanced cases (dynamic registration, removeHook, etc.).\n\nPlugins are loaded in the order they appear in `visConfig.plugins`. Handler execution order within a hook follows registration order, so earlier plugins see events first."
3308
+ },
3309
+ "ExtraCustomType": {
3310
+ "type": "object",
3311
+ "properties": {
3312
+ "depName": {
3313
+ "type": "string",
3314
+ "description": "Required when `strategy === \"string\"`. The dep-cluster key the bare version string at `path` should be associated with."
3315
+ },
3316
+ "name": {
3317
+ "type": "string",
3318
+ "description": "Display name for this customType. Used as the cluster key prefix in lint output and JSON. Must not collide with the built-in names."
3319
+ },
3320
+ "path": {
3321
+ "type": "string",
3322
+ "description": "Dot-separated walk into package.json (e.g. `pnpm.overrides`, `myTool.runtime`)."
3323
+ },
3324
+ "strategy": {
3325
+ "type": "string",
3326
+ "enum": [
3327
+ "name@version",
3328
+ "name~version",
3329
+ "string",
3330
+ "versionsByName"
3331
+ ],
3332
+ "description": "How to interpret the JSON found at `path`.\n- `name@version` — single string `pnpm@9.0.0` (with optional `+sha512.…` hash).\n- `name~version` — single string `node~20.0.0`, mirrors syncpack's tilde form.\n- `string` — bare version literal (requires `depName`).\n- `versionsByName` — `{ name: version }` object such as `engines`."
3333
+ }
3334
+ },
3335
+ "required": [
3336
+ "name",
3337
+ "path",
3338
+ "strategy"
3339
+ ],
3340
+ "additionalProperties": false,
3341
+ "description": "One user-declared customTypes entry. See `policy.customTypes.extraTypes` for the full contract — this is just the row shape."
3342
+ },
3343
+ "SimilarDepFamily": {
3344
+ "type": "object",
3345
+ "properties": {
3346
+ "id": {
3347
+ "type": "string",
3348
+ "description": "Stable id; used in report output and config overrides."
3349
+ },
3350
+ "label": {
3351
+ "type": "string",
3352
+ "description": "Pretty label for the report. Defaults to `id` when omitted."
3353
+ },
3354
+ "members": {
3355
+ "type": "array",
3356
+ "items": {
3357
+ "type": "string"
3358
+ },
3359
+ "description": "Dep names that belong to this family verbatim."
3360
+ },
3361
+ "prefixes": {
3362
+ "type": "array",
3363
+ "items": {
3364
+ "type": "string"
3365
+ },
3366
+ "description": "Dep-name prefixes (literal, no glob). Match if `depName.startsWith(prefix)`."
3367
+ }
3368
+ },
3369
+ "required": [
3370
+ "id"
3371
+ ],
3372
+ "additionalProperties": false,
3373
+ "description": "One family of upstream-coupled packages.\n\n`members` is an exact-match list. `prefixes` accept any dep whose name starts with the prefix — useful for monorepos that ship many subpackages under one scope (e.g. `@babel/`, `@storybook/`, `@nx/`). A family can use either or both; a dep matching either list belongs to the family."
3374
+ },
3375
+ "PolicyName": {
3376
+ "type": "string",
3377
+ "enum": [
3378
+ "firstSeen",
3379
+ "installScripts",
3380
+ "license",
3381
+ "malware",
3382
+ "publisherChange",
3383
+ "score",
3384
+ "unexpectedDeps",
3385
+ "vulnerability"
3386
+ ]
3387
+ },
3388
+ "StagedConfig": {
3389
+ "type": "object",
3390
+ "additionalProperties": {
3391
+ "$ref": "#/$defs/StagedTask"
3392
+ },
3393
+ "description": "Config object mapping glob patterns (basename or path-style) to tasks. A top-level function form lets the user generate the entire config from the staged file list."
3394
+ },
3395
+ "StagedTask": {
3396
+ "anyOf": [
3397
+ {
3398
+ "type": "array",
3399
+ "items": {
3400
+ "type": "string"
3401
+ }
3402
+ },
3403
+ {
3404
+ "type": "string"
3405
+ },
3406
+ {
3407
+ "type": "array",
3408
+ "items": {
3409
+ "type": "string"
3410
+ }
3411
+ }
3412
+ ],
3413
+ "description": "A task value as authored by the user. Command strings are split into argv and invoked with the matched file paths appended. Arrays run serially. Functions receive the matched paths and return further task values (possibly async). `{ title, task }` objects run `task` directly with no argv construction."
3414
+ },
3415
+ "VisTargetOptions": {
3416
+ "type": "object",
3417
+ "properties": {
3418
+ "affectedFiles": {
3419
+ "$ref": "#/$defs/AffectedFilesMode",
3420
+ "description": "How to forward affected files to the task process. Only used when invoked via `vis affected &lt;target>`.",
3421
+ "default": false
3422
+ },
3423
+ "envFile": {
3424
+ "anyOf": [
3425
+ {
3426
+ "type": "boolean"
3427
+ },
3428
+ {
3429
+ "type": "string"
3430
+ },
3431
+ {
3432
+ "type": "array",
3433
+ "items": {
3434
+ "type": "string"
3435
+ }
3436
+ }
3437
+ ],
3438
+ "description": "Load environment variables from dotenv file(s) before running.\n- `string`: a single file path (relative to project root).\n- `string[]`: multiple files — later entries override earlier ones, so put more-specific files last (e.g. `[\".env\", \".env.local\"]`).\n- `true`: auto-cascade in the Next/Vite order: `.env` → `.env.{NODE_ENV}` → `.env.local` → `.env.{NODE_ENV}.local`. Skips `.env.local` when NODE_ENV is `test`, matching Next.js."
3439
+ },
3440
+ "interactive": {
3441
+ "type": "boolean",
3442
+ "description": "When true, the task is serialized with respect to parallel execution and must be run on the main process (claims stdin). Used for commands that read from the terminal.",
3443
+ "default": false
3444
+ },
3445
+ "internal": {
3446
+ "type": "boolean",
3447
+ "description": "When true, the task is hidden from CLI listings and can only be invoked as a dependency of another task.",
3448
+ "default": false
3449
+ },
3450
+ "killGracePeriodMs": {
3451
+ "type": "number",
3452
+ "description": "Milliseconds the timeout watchdog waits between sending SIGTERM and SIGKILL when the `timeout` budget fires. Tasks that ignore SIGTERM (e.g. test runners holding open child processes) get force-killed after this grace window so a stuck task can't outlive its budget.\n\nSet to `0` to skip escalation and rely on SIGTERM only.",
3453
+ "default": 5000
3454
+ },
3455
+ "mutex": {
3456
+ "type": "string",
3457
+ "description": "Serializes all tasks that share the same mutex name. Useful for tasks that contend on a shared resource (e.g., a database migration)."
3458
+ },
3459
+ "outputStyle": {
3460
+ "type": "string",
3461
+ "enum": [
3462
+ "normal",
3463
+ "quiet"
3464
+ ],
3465
+ "description": "Per-target output verbosity. Overrides the global `--output-style` flag for this specific target.\n\n- `\"normal\"` (default): print every task's terminal output\n- `\"quiet\"`: only print output when the task fails. Successful and cached tasks contribute their status line and timing, but their captured stdout/stderr is suppressed.\n\nUseful when a routinely-noisy task (a linter or test runner with verbose progress output) should stay quiet during green builds but reveal everything when it fails."
3466
+ },
3467
+ "persistent": {
3468
+ "type": "boolean",
3469
+ "description": "When true, the task is a long-running / never-ending process. Persistent tasks are scheduled last, execute after all cacheable tasks complete, and are never cached.",
3470
+ "default": false
3471
+ },
3472
+ "preset": {
3473
+ "$ref": "#/$defs/TargetPreset",
3474
+ "description": "A preset that pre-fills a common bundle of options. User-provided fields always take precedence over the preset."
3475
+ },
3476
+ "pty": {
3477
+ "type": "boolean",
3478
+ "description": "Run the task through a pseudo-terminal so color-aware tools (vitest, eslint, biome, …) render as if attached to a real TTY instead of a pipe. Output is captured via task-runner's `TerminalBuffer` so ANSI escapes are normalized into the final rendered state before reaching the reporter.\n\nForces cache to off — PTY output can include timing-dependent frames (spinners) that aren't safe to replay from a cache.",
3479
+ "default": false
3480
+ },
3481
+ "retryCount": {
3482
+ "type": "number",
3483
+ "description": "Number of times to retry the task on failure. Uses an exponential backoff by default (1s, 2s, 4s, ...).",
3484
+ "default": 0
3485
+ },
3486
+ "retryDelay": {
3487
+ "anyOf": [
3488
+ {
3489
+ "type": "number"
3490
+ },
3491
+ {
3492
+ "type": "string",
3493
+ "const": "exponential"
3494
+ }
3495
+ ],
3496
+ "description": "Delay between retry attempts in milliseconds, or `\"exponential\"` for 2^attempt * 1000 ms.",
3497
+ "default": "exponential"
3498
+ },
3499
+ "runFromWorkspaceRoot": {
3500
+ "type": "boolean",
3501
+ "description": "When true, the command executes with the workspace root as CWD instead of the project root.",
3502
+ "default": false
3503
+ },
3504
+ "runInCI": {
3505
+ "$ref": "#/$defs/RunInCI",
3506
+ "description": "Controls whether the task runs in CI environments.",
3507
+ "default": true
3508
+ },
3509
+ "runnerTags": {
3510
+ "type": "array",
3511
+ "items": {
3512
+ "type": "string"
3513
+ },
3514
+ "description": "Capability tags that gate this task to runners advertising the same tag. The CLI's `--runner-tags=gpu,slow` flag (or `VIS_RUNNER_TAGS` env var) tells vis what the current runner supports; tasks whose `runnerTags` share at least one tag with the runner set are eligible. Untagged tasks (no `runnerTags` or an empty array) are general-purpose and always run.\n\nUse this for special-purpose CI lanes — e.g. a GPU runner that should only pick up visual-regression suites, or a nightly job that runs `slow` integration tests. When neither flag nor env is set, the filter is inactive and every task runs."
3515
+ },
3516
+ "service": {
3517
+ "$ref": "#/$defs/ServiceConfig",
3518
+ "description": "Marks this target as a long-lived service that can be started via `vis service start &lt;id>` and auto-attached when other tasks declare it in `dependsOn`. Implies persistent + non-cacheable behaviour (set `preset: \"server\"` to inherit the rest of the bundle).\n\nThe presence of this block — not `preset: \"server\"` alone — is what makes a target eligible for the cross-invocation registry. `preset: \"server\"` without `service` keeps today's in-run-only behaviour."
3519
+ },
3520
+ "shell": {
3521
+ "type": "string",
3522
+ "description": "Per-target shell override. When set, the command runs through this shell instead of the platform default."
3523
+ },
3524
+ "strictEnv": {
3525
+ "type": "boolean",
3526
+ "description": "Override the workspace `strictEnv` setting for this target. When truthy, the target fails if its command references an env var that resolves to neither the task's effective env nor `process.env`. When `false`, the target opts out of a workspace `strictEnv: true` (e.g. for a one-off command that legitimately tolerates an unset variable)."
3527
+ },
3528
+ "timeout": {
3529
+ "type": "number",
3530
+ "description": "Maximum wall-clock milliseconds a single task run is allowed to take before being killed. `0` / `undefined` means no timeout.\n\nWhen the timeout fires the task is sent SIGTERM and, if it has not exited within `killGracePeriodMs`, SIGKILL. The task exits with a failure status carrying the `[timeout]` marker in `terminalOutput`. Retries count per-attempt, not cumulatively.\n\nUse this to prevent runaway tasks from eating CI wall-clock time up to the job-level cutoff."
3531
+ },
3532
+ "unixShell": {
3533
+ "type": "string",
3534
+ "description": "Per-target unix shell override, used on Linux and macOS. Takes precedence over `shell` on unix-like systems."
3535
+ },
3536
+ "windowsShell": {
3537
+ "type": "string",
3538
+ "description": "Per-target windows shell override, used on Windows. Takes precedence over `shell` on Windows."
3539
+ }
3540
+ },
3541
+ "additionalProperties": false,
3542
+ "description": "Vis-specific target options that extend the task-runner's base `TargetConfiguration`. These live under `target.options` and are interpreted by vis before handing the task off to task-runner.\n\nConditional execution (`when:`) and finally tasks (`always:`) live at the target top level, not under `options` — they're handled by the task-runner orchestrator. See `@visulima/task-runner`'s `WhenCondition`."
3543
+ },
3544
+ "AffectedFilesMode": {
3545
+ "type": [
3546
+ "string",
3547
+ "boolean"
3548
+ ],
3549
+ "enum": [
3550
+ "args",
3551
+ "both",
3552
+ "env",
3553
+ false
3554
+ ],
3555
+ "description": "Controls how affected files are forwarded to a task.\n- `false` (default): Do not forward.\n- `\"args\"`: Append affected paths as additional command arguments.\n- `\"env\"`: Expose them via `VIS_AFFECTED_FILES` environment variable.\n- `\"both\"`: Both of the above."
3556
+ },
3557
+ "TargetPreset": {
3558
+ "type": "string",
3559
+ "enum": [
3560
+ "server",
3561
+ "utility"
3562
+ ],
3563
+ "description": "Preset bundles of target options.\n- `server`: Long-running local dev server — caching off, not in CI, interactive, persistent.\n- `utility`: Short-lived helper — caching off, not in CI."
3564
+ },
3565
+ "RunInCI": {
3566
+ "anyOf": [
3567
+ {
3568
+ "type": "string",
3569
+ "const": "affected"
3570
+ },
3571
+ {
3572
+ "type": "string",
3573
+ "const": "always"
3574
+ },
3575
+ {
3576
+ "type": "boolean"
3577
+ }
3578
+ ],
3579
+ "description": "Controls whether a target runs in CI.\n- `true` (default): Always run.\n- `false`: Never run in CI (local-only).\n- `\"affected\"`: Only when the project is affected by the current change set.\n- `\"always\"`: Always run, even if unaffected."
3580
+ },
3581
+ "ServiceConfig": {
3582
+ "type": "object",
3583
+ "properties": {
3584
+ "env": {
3585
+ "type": "object",
3586
+ "additionalProperties": {
3587
+ "type": "string"
3588
+ },
3589
+ "description": "Env vars to expose to dependent tasks when this service is registered. Merged into the dependent task's env after the task's own envFile and before the task's explicit `env` overrides — the dependent task wins on key collisions.\n\nNote: only this `env` map propagates to dependents. The service target's own `envFile` is loaded into the **service process** at start time but is *not* forwarded — dependents must declare any shared values they need either here or in their own envFile. This boundary is intentional: envFiles often contain operator-only secrets (deploy keys, admin tokens) that should not leak into downstream test commands."
3590
+ },
3591
+ "killGracePeriodMs": {
3592
+ "type": "number",
3593
+ "description": "Grace period in milliseconds between SIGTERM and SIGKILL when the service is stopped.",
3594
+ "default": 5000
3595
+ },
3596
+ "port": {
3597
+ "type": "number",
3598
+ "description": "Optional port the service listens on. Used as the default for `readiness.tcp.port` when no explicit probe is configured, and surfaced by `vis service list`."
3599
+ },
3600
+ "readiness": {
3601
+ "type": "object",
3602
+ "properties": {
3603
+ "tcp": {
3604
+ "type": "object",
3605
+ "properties": {
3606
+ "host": {
3607
+ "type": "string"
3608
+ },
3609
+ "port": {
3610
+ "type": "number"
3611
+ },
3612
+ "timeoutMs": {
3613
+ "type": "number"
3614
+ }
3615
+ },
3616
+ "required": [
3617
+ "port"
3618
+ ],
3619
+ "additionalProperties": false
3620
+ }
3621
+ },
3622
+ "required": [
3623
+ "tcp"
3624
+ ],
3625
+ "additionalProperties": false,
3626
+ "description": "Readiness probe configuration. v1 supports TCP only."
3627
+ }
3628
+ },
3629
+ "additionalProperties": false,
3630
+ "description": "Configuration block declared on a target to mark it as a long-lived \"service\" — eligible to be started/stopped via `vis service` and auto-attached when other tasks depend on it.\n\nTargets must also carry `preset: \"server\"` (or the equivalent `persistent: true`) for the service-mode lifecycle to apply."
3631
+ },
3632
+ "TargetType": {
3633
+ "type": "string",
3634
+ "enum": [
3635
+ "build",
3636
+ "run",
3637
+ "test"
3638
+ ],
3639
+ "description": "Semantic classification for a target.\n- `build`: Generates one or more artifacts; cached by default.\n- `test`: Validation task (lint, typecheck, unit test). Default type.\n- `run`: One-off or long-running process. Not cached by default."
3640
+ },
3641
+ "ScopedTasksBlock": {
3642
+ "type": "object",
3643
+ "properties": {
3644
+ "match": {
3645
+ "$ref": "#/$defs/ScopedTasksMatch",
3646
+ "description": "Optional match predicate; if omitted, the block applies universally."
3647
+ },
3648
+ "tasks": {
3649
+ "type": "object",
3650
+ "additionalProperties": {
3651
+ "type": "object",
3652
+ "properties": {
3653
+ "aliases": {
3654
+ "type": "array",
3655
+ "items": {
3656
+ "type": "string"
3657
+ },
3658
+ "description": "Alternate names that resolve to this target on the CLI. Useful for shortening long canonical names (`test` ↔ `t`) or for offering migration-friendly aliases when renaming targets. Aliases must be globally unique within the workspace."
3659
+ },
3660
+ "description": {
3661
+ "type": "string",
3662
+ "description": "One-line description surfaced by `vis list` and (in future) per-task `--help`. Kept short — longer docs belong in project READMEs or vis.config.ts comments."
3663
+ },
3664
+ "inferred": {
3665
+ "type": "boolean",
3666
+ "description": "True when the target was synthesized by a Project Crystal-style detector (see {@link ../inference } ) rather than declared by a package.json script, project.json, or vis.task.ts file. Surfaced by `vis list --inferred` and used by tooling to distinguish implicit defaults from explicit user intent."
3667
+ },
3668
+ "options": {
3669
+ "$ref": "#/$defs/VisTargetOptions",
3670
+ "description": "Vis-specific target options."
3671
+ },
3672
+ "preset": {
3673
+ "$ref": "#/$defs/TargetPreset",
3674
+ "description": "Preset applied before user-specified options."
3675
+ },
3676
+ "type": {
3677
+ "$ref": "#/$defs/TargetType",
3678
+ "description": "Semantic task type. Affects caching defaults and CI filtering.",
3679
+ "default": "test"
3680
+ },
3681
+ "always": {
3682
+ "type": "boolean",
3683
+ "description": "When `true`, this target runs after the main task graph completes — even if upstream tasks failed. Useful for cleanup, teardown, notifications, or anything that should fire regardless of build outcome. Always-tasks are not part of the normal dependency graph and never block other tasks."
3684
+ },
3685
+ "cache": {
3686
+ "type": "boolean",
3687
+ "description": "Whether this target is cacheable"
3688
+ },
3689
+ "cacheOnWarning": {
3690
+ "type": "boolean",
3691
+ "description": "When `false`, exit-0 runs whose terminal output matched any {@link TargetConfiguration.warningPattern } are not seeded into the cache. Defaults to `true` — warnings are surfaced on the result (`hadWarnings: true`) but caching still happens, matching the \"succeeded with warnings is incremental\" behaviour rush, lage and wireit users have repeatedly asked for."
3692
+ },
3693
+ "cacheRestore": {
3694
+ "type": "object",
3695
+ "properties": {
3696
+ "preserveMtime": {
3697
+ "type": "boolean",
3698
+ "description": "Restore each file's modification time from the captured tar header (second precision). When `false`, restored files take the current wall-clock time, matching the legacy behaviour."
3699
+ },
3700
+ "preservePerms": {
3701
+ "type": "boolean",
3702
+ "description": "Restore each file's POSIX mode bits (rwx triplets, low 12 bits). When `false`, restored files take the process umask. Only meaningful on POSIX hosts; Windows ignores tar mode."
3703
+ }
3704
+ },
3705
+ "additionalProperties": false,
3706
+ "description": "Fine-grained controls over how cached outputs are rehydrated. See {@link CacheRestoreOptions } . Both fields default to `true` (faithful restore); flip individually when downstream tooling needs the legacy \"now\"-stamped behaviour."
3707
+ },
3708
+ "command": {
3709
+ "type": "string",
3710
+ "description": "The command to run (alternative to executor)"
3711
+ },
3712
+ "configurations": {
3713
+ "type": "object",
3714
+ "additionalProperties": {
3715
+ "type": "object",
3716
+ "additionalProperties": {}
3717
+ },
3718
+ "description": "Named configurations (e.g., \"production\", \"development\")"
3719
+ },
3720
+ "concurrencyGroup": {
3721
+ "type": "string",
3722
+ "description": "Workspace-level concurrency group this target opts into. The scheduler caps the *combined* in-flight count of every task whose target carries the same group name to the value declared in {@link TaskRunnerOptions.concurrencyGroups } . Use this when several targets share an external resource (a single Postgres, a developer's Docker daemon, an account-wide deploy lock) so the cap follows the resource, not any single target name.\n\nTargets that name a group not declared in `concurrencyGroups` run uncapped — a typo shouldn't deadlock the graph. Caps don't apply to tasks marked `always: true`; those run in a separate finalisation phase outside the scheduler."
3723
+ },
3724
+ "dependsOn": {
3725
+ "type": "array",
3726
+ "items": {
3727
+ "anyOf": [
3728
+ {
3729
+ "type": "string"
3730
+ },
3731
+ {
3732
+ "type": "object",
3733
+ "properties": {
3734
+ "dependencies": {
3735
+ "type": "boolean",
3736
+ "description": "Whether this is a dependency on the same project"
3737
+ },
3738
+ "params": {
3739
+ "type": "string",
3740
+ "enum": [
3741
+ "forward",
3742
+ "ignore"
3743
+ ],
3744
+ "description": "Params to pass through"
3745
+ },
3746
+ "projects": {
3747
+ "anyOf": [
3748
+ {
3749
+ "type": "string"
3750
+ },
3751
+ {
3752
+ "type": "array",
3753
+ "items": {
3754
+ "type": "string"
3755
+ }
3756
+ }
3757
+ ],
3758
+ "description": "The project name (if different from the current project)"
3759
+ },
3760
+ "target": {
3761
+ "type": "string",
3762
+ "description": "The target name"
3763
+ }
3764
+ },
3765
+ "required": [
3766
+ "target"
3767
+ ],
3768
+ "additionalProperties": false,
3769
+ "description": "Defines a dependency on another target."
3770
+ }
3771
+ ]
3772
+ },
3773
+ "description": "Other targets this target depends on"
3774
+ },
3775
+ "executor": {
3776
+ "type": "string",
3777
+ "description": "The executor/command to run"
3778
+ },
3779
+ "inputs": {
3780
+ "type": "array",
3781
+ "items": {
3782
+ "anyOf": [
3783
+ {
3784
+ "type": "string"
3785
+ },
3786
+ {
3787
+ "anyOf": [
3788
+ {
3789
+ "type": "object",
3790
+ "properties": {
3791
+ "fileset": {
3792
+ "anyOf": [
3793
+ {
3794
+ "type": "object",
3795
+ "properties": {
3796
+ "base": {
3797
+ "type": "string",
3798
+ "enum": [
3799
+ "package",
3800
+ "workspace"
3801
+ ],
3802
+ "description": "Anchor for the pattern."
3803
+ },
3804
+ "pattern": {
3805
+ "type": "string",
3806
+ "description": "Glob pattern (may start with `!` for negation)."
3807
+ }
3808
+ },
3809
+ "required": [
3810
+ "base",
3811
+ "pattern"
3812
+ ],
3813
+ "additionalProperties": false,
3814
+ "description": "Object form of a fileset pattern, for anchoring to the workspace root."
3815
+ },
3816
+ {
3817
+ "type": "string"
3818
+ }
3819
+ ]
3820
+ }
3821
+ },
3822
+ "required": [
3823
+ "fileset"
3824
+ ],
3825
+ "additionalProperties": false,
3826
+ "description": "An input based on file patterns.\n\n`fileset` may be a bare glob string (package-root relative) or an object form `{ pattern, base }` to anchor explicitly to the workspace root. Negation (`!` prefix) works in both forms."
3827
+ },
3828
+ {
3829
+ "type": "object",
3830
+ "properties": {
3831
+ "runtime": {
3832
+ "type": "string"
3833
+ }
3834
+ },
3835
+ "required": [
3836
+ "runtime"
3837
+ ],
3838
+ "additionalProperties": false,
3839
+ "description": "An input based on a runtime command."
3840
+ },
3841
+ {
3842
+ "type": "object",
3843
+ "properties": {
3844
+ "env": {
3845
+ "type": "string"
3846
+ }
3847
+ },
3848
+ "required": [
3849
+ "env"
3850
+ ],
3851
+ "additionalProperties": false,
3852
+ "description": "An input based on environment variables."
3853
+ },
3854
+ {
3855
+ "type": "object",
3856
+ "properties": {
3857
+ "externalDependencies": {
3858
+ "type": "array",
3859
+ "items": {
3860
+ "type": "string"
3861
+ }
3862
+ }
3863
+ },
3864
+ "required": [
3865
+ "externalDependencies"
3866
+ ],
3867
+ "additionalProperties": false,
3868
+ "description": "An input based on external dependency versions."
3869
+ }
3870
+ ],
3871
+ "description": "Defines an input for cache invalidation."
3872
+ }
3873
+ ]
3874
+ },
3875
+ "description": "Input patterns for cache invalidation"
3876
+ },
3877
+ "maxConcurrent": {
3878
+ "type": "number",
3879
+ "description": "Maximum number of in-flight instances of this target across the whole graph. When set, the scheduler refuses to start an additional task whose `target.target` equals this target's name once the running count reaches `maxConcurrent`. Independent of `--parallel`: the global cap still bounds total in-flight tasks, and `maxConcurrent` further bounds the per-target subset.\n\nUse for tests/deploys that share an external resource (DB, port, mock server). Set to `1` to serialize. Values `<= 0` are ignored.\n\nIf multiple projects declare different values for the same target name, the runner uses the smallest declared cap. Caps don't apply to tasks marked `always: true`; those run in a separate finalisation phase outside the scheduler."
3880
+ },
3881
+ "outputs": {
3882
+ "type": "array",
3883
+ "items": {
3884
+ "type": "string"
3885
+ },
3886
+ "description": "Output patterns produced by this target"
3887
+ },
3888
+ "parallelism": {
3889
+ "type": "boolean",
3890
+ "description": "Whether this target supports parallel execution"
3891
+ },
3892
+ "warningPattern": {
3893
+ "anyOf": [
3894
+ {
3895
+ "type": "string"
3896
+ },
3897
+ {
3898
+ "type": "array",
3899
+ "items": {
3900
+ "type": "string"
3901
+ }
3902
+ }
3903
+ ],
3904
+ "description": "Regex source string(s) that mark a successful task as having emitted warnings. The orchestrator scans the task's combined terminal output after a 0-exit and, on first match, sets `hadWarnings` on the result. Combine with `cacheOnWarning: false` to skip caching for warning-tainted runs. Both bare strings and arrays are accepted; arrays are tested in order.\n\n ```ts warningPattern: [\"\\\\bwarning\\\\b\", \"TS\\\\d{4}\"] ```"
3905
+ },
3906
+ "when": {
3907
+ "type": "object",
3908
+ "properties": {
3909
+ "branch": {
3910
+ "anyOf": [
3911
+ {
3912
+ "type": "string"
3913
+ },
3914
+ {
3915
+ "type": "array",
3916
+ "items": {
3917
+ "type": "string"
3918
+ }
3919
+ }
3920
+ ],
3921
+ "description": "Match against the current git branch (HEAD)."
3922
+ },
3923
+ "ci": {
3924
+ "type": "boolean",
3925
+ "description": "Run only when invoked inside CI when `true`, only outside CI when `false`. Detects CI via the `CI` env var (the convention GitHub Actions, GitLab, CircleCI, Jenkins, etc. all share)."
3926
+ },
3927
+ "env": {
3928
+ "anyOf": [
3929
+ {
3930
+ "anyOf": [
3931
+ {
3932
+ "type": "string"
3933
+ },
3934
+ {
3935
+ "type": "object",
3936
+ "properties": {
3937
+ "equals": {
3938
+ "type": "string",
3939
+ "description": "Match this exact value. Mutually exclusive with `exists`."
3940
+ },
3941
+ "exists": {
3942
+ "type": "boolean",
3943
+ "description": "Assert the variable is set & non-empty (`true`) or unset/empty (`false`)."
3944
+ },
3945
+ "name": {
3946
+ "type": "string",
3947
+ "description": "Variable name."
3948
+ }
3949
+ },
3950
+ "required": [
3951
+ "name"
3952
+ ],
3953
+ "additionalProperties": false
3954
+ }
3955
+ ],
3956
+ "description": "An environment-variable match. The string form is shorthand for `{ name, exists: true }` (set + non-empty). The object form supports either presence assertions or exact-value matching."
3957
+ },
3958
+ {
3959
+ "type": "array",
3960
+ "items": {
3961
+ "anyOf": [
3962
+ {
3963
+ "type": "string"
3964
+ },
3965
+ {
3966
+ "type": "object",
3967
+ "properties": {
3968
+ "equals": {
3969
+ "type": "string",
3970
+ "description": "Match this exact value. Mutually exclusive with `exists`."
3971
+ },
3972
+ "exists": {
3973
+ "type": "boolean",
3974
+ "description": "Assert the variable is set & non-empty (`true`) or unset/empty (`false`)."
3975
+ },
3976
+ "name": {
3977
+ "type": "string",
3978
+ "description": "Variable name."
3979
+ }
3980
+ },
3981
+ "required": [
3982
+ "name"
3983
+ ],
3984
+ "additionalProperties": false
3985
+ }
3986
+ ],
3987
+ "description": "An environment-variable match. The string form is shorthand for `{ name, exists: true }` (set + non-empty). The object form supports either presence assertions or exact-value matching."
3988
+ }
3989
+ }
3990
+ ],
3991
+ "description": "Match against environment variables. A bare string asserts the variable is set and non-empty; the object form lets you match an exact value or just assert presence/absence."
3992
+ },
3993
+ "not": {
3994
+ "type": "object",
3995
+ "properties": {
3996
+ "branch": {
3997
+ "anyOf": [
3998
+ {
3999
+ "type": "string"
4000
+ },
4001
+ {
4002
+ "type": "array",
4003
+ "items": {
4004
+ "type": "string"
4005
+ }
4006
+ }
4007
+ ]
4008
+ },
4009
+ "ci": {
4010
+ "type": "boolean"
4011
+ },
4012
+ "env": {
4013
+ "anyOf": [
4014
+ {
4015
+ "anyOf": [
4016
+ {
4017
+ "type": "string"
4018
+ },
4019
+ {
4020
+ "type": "object",
4021
+ "properties": {
4022
+ "equals": {
4023
+ "type": "string",
4024
+ "description": "Match this exact value. Mutually exclusive with `exists`."
4025
+ },
4026
+ "exists": {
4027
+ "type": "boolean",
4028
+ "description": "Assert the variable is set & non-empty (`true`) or unset/empty (`false`)."
4029
+ },
4030
+ "name": {
4031
+ "type": "string",
4032
+ "description": "Variable name."
4033
+ }
4034
+ },
4035
+ "required": [
4036
+ "name"
4037
+ ],
4038
+ "additionalProperties": false
4039
+ }
4040
+ ],
4041
+ "description": "An environment-variable match. The string form is shorthand for `{ name, exists: true }` (set + non-empty). The object form supports either presence assertions or exact-value matching."
4042
+ },
4043
+ {
4044
+ "type": "array",
4045
+ "items": {
4046
+ "anyOf": [
4047
+ {
4048
+ "type": "string"
4049
+ },
4050
+ {
4051
+ "type": "object",
4052
+ "properties": {
4053
+ "equals": {
4054
+ "type": "string",
4055
+ "description": "Match this exact value. Mutually exclusive with `exists`."
4056
+ },
4057
+ "exists": {
4058
+ "type": "boolean",
4059
+ "description": "Assert the variable is set & non-empty (`true`) or unset/empty (`false`)."
4060
+ },
4061
+ "name": {
4062
+ "type": "string",
4063
+ "description": "Variable name."
4064
+ }
4065
+ },
4066
+ "required": [
4067
+ "name"
4068
+ ],
4069
+ "additionalProperties": false
4070
+ }
4071
+ ],
4072
+ "description": "An environment-variable match. The string form is shorthand for `{ name, exists: true }` (set + non-empty). The object form supports either presence assertions or exact-value matching."
4073
+ }
4074
+ }
4075
+ ]
4076
+ },
4077
+ "os": {
4078
+ "anyOf": [
4079
+ {
4080
+ "type": "string",
4081
+ "enum": [
4082
+ "aix",
4083
+ "darwin",
4084
+ "freebsd",
4085
+ "linux",
4086
+ "openbsd",
4087
+ "sunos",
4088
+ "windows",
4089
+ "win32"
4090
+ ],
4091
+ "description": "Aliased platform list — `\"windows\"` is sugar for Node's `\"win32\"`."
4092
+ },
4093
+ {
4094
+ "type": "array",
4095
+ "items": {
4096
+ "type": "string",
4097
+ "enum": [
4098
+ "aix",
4099
+ "darwin",
4100
+ "freebsd",
4101
+ "linux",
4102
+ "openbsd",
4103
+ "sunos",
4104
+ "windows",
4105
+ "win32"
4106
+ ],
4107
+ "description": "Aliased platform list — `\"windows\"` is sugar for Node's `\"win32\"`."
4108
+ }
4109
+ }
4110
+ ]
4111
+ }
4112
+ },
4113
+ "additionalProperties": false,
4114
+ "description": "Negative mirrors. A task runs only when *all* `not.*` clauses fail."
4115
+ },
4116
+ "os": {
4117
+ "anyOf": [
4118
+ {
4119
+ "type": "string",
4120
+ "enum": [
4121
+ "aix",
4122
+ "darwin",
4123
+ "freebsd",
4124
+ "linux",
4125
+ "openbsd",
4126
+ "sunos",
4127
+ "windows",
4128
+ "win32"
4129
+ ],
4130
+ "description": "Aliased platform list — `\"windows\"` is sugar for Node's `\"win32\"`."
4131
+ },
4132
+ {
4133
+ "type": "array",
4134
+ "items": {
4135
+ "type": "string",
4136
+ "enum": [
4137
+ "aix",
4138
+ "darwin",
4139
+ "freebsd",
4140
+ "linux",
4141
+ "openbsd",
4142
+ "sunos",
4143
+ "windows",
4144
+ "win32"
4145
+ ],
4146
+ "description": "Aliased platform list — `\"windows\"` is sugar for Node's `\"win32\"`."
4147
+ }
4148
+ }
4149
+ ],
4150
+ "description": "Match `process.platform` (`\"linux\" | \"darwin\" | \"win32\" | \"freebsd\" | \"openbsd\" | \"sunos\" | \"aix\"`). Pass `\"windows\"` as an alias for `\"win32\"` — easier to remember and matches what people type."
4151
+ }
4152
+ },
4153
+ "additionalProperties": false,
4154
+ "description": "Predicate that gates execution. When the condition evaluates to `false` for the current environment, the task is skipped (marked `\"skipped\"`, not failed). Combine with `os`, `env`, `branch`, and `ci` clauses for granular control:\n\n ```ts when: { os: \"linux\", ci: true, env: \"DEPLOY_TOKEN\" } ```"
4155
+ }
4156
+ },
4157
+ "additionalProperties": false
4158
+ },
4159
+ "description": "Task default configurations, keyed by target name."
4160
+ }
4161
+ },
4162
+ "required": [
4163
+ "tasks"
4164
+ ],
4165
+ "additionalProperties": false,
4166
+ "description": "A single scoped-tasks block — a set of task defaults gated by an optional match predicate."
4167
+ },
4168
+ "ScopedTasksMatch": {
4169
+ "type": "object",
4170
+ "properties": {
4171
+ "language": {
4172
+ "anyOf": [
4173
+ {
4174
+ "type": "string"
4175
+ },
4176
+ {
4177
+ "type": "array",
4178
+ "items": {
4179
+ "type": "string"
4180
+ }
4181
+ }
4182
+ ],
4183
+ "description": "Match on primary language."
4184
+ },
4185
+ "layer": {
4186
+ "anyOf": [
4187
+ {
4188
+ "type": "string",
4189
+ "enum": [
4190
+ "application",
4191
+ "automation",
4192
+ "configuration",
4193
+ "library",
4194
+ "scaffolding",
4195
+ "tool"
4196
+ ],
4197
+ "description": "Project layer, used for constraint inheritance and query filtering."
4198
+ },
4199
+ {
4200
+ "type": "array",
4201
+ "items": {
4202
+ "anyOf": [
4203
+ {
4204
+ "type": "string",
4205
+ "const": "application"
4206
+ },
4207
+ {
4208
+ "type": "string",
4209
+ "const": "automation"
4210
+ },
4211
+ {
4212
+ "type": "string",
4213
+ "const": "configuration"
4214
+ },
4215
+ {
4216
+ "type": "string",
4217
+ "const": "library"
4218
+ },
4219
+ {
4220
+ "type": "string",
4221
+ "const": "scaffolding"
4222
+ },
4223
+ {
4224
+ "type": "string",
4225
+ "const": "tool"
4226
+ },
4227
+ {
4228
+ "not": {}
4229
+ }
4230
+ ],
4231
+ "description": "Project layer, used for constraint inheritance and query filtering."
4232
+ }
4233
+ }
4234
+ ],
4235
+ "description": "Match on project layer."
4236
+ },
4237
+ "projectType": {
4238
+ "type": "string",
4239
+ "enum": [
4240
+ "application",
4241
+ "library",
4242
+ "service",
4243
+ "tool"
4244
+ ],
4245
+ "description": "Match on project type."
4246
+ },
4247
+ "stack": {
4248
+ "anyOf": [
4249
+ {
4250
+ "type": "string",
4251
+ "enum": [
4252
+ "backend",
4253
+ "data",
4254
+ "frontend",
4255
+ "infrastructure",
4256
+ "systems"
4257
+ ],
4258
+ "description": "Tech stack."
4259
+ },
4260
+ {
4261
+ "type": "array",
4262
+ "items": {
4263
+ "anyOf": [
4264
+ {
4265
+ "type": "string",
4266
+ "const": "backend"
4267
+ },
4268
+ {
4269
+ "type": "string",
4270
+ "const": "data"
4271
+ },
4272
+ {
4273
+ "type": "string",
4274
+ "const": "frontend"
4275
+ },
4276
+ {
4277
+ "type": "string",
4278
+ "const": "infrastructure"
4279
+ },
4280
+ {
4281
+ "type": "string",
4282
+ "const": "systems"
4283
+ },
4284
+ {
4285
+ "not": {}
4286
+ }
4287
+ ],
4288
+ "description": "Tech stack."
4289
+ }
4290
+ }
4291
+ ],
4292
+ "description": "Match on project stack."
4293
+ },
4294
+ "tags": {
4295
+ "type": "array",
4296
+ "items": {
4297
+ "type": "string"
4298
+ },
4299
+ "description": "Match projects tagged with any of these tags."
4300
+ }
4301
+ },
4302
+ "additionalProperties": false,
4303
+ "description": "A predicate used by {@link VisConfig.scopedTasks } . All listed constraints must match for the block to apply."
4304
+ }
4305
+ }
4306
+ }