@clipboard-health/groundcrew 4.7.2 → 4.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -9,7 +9,7 @@
9
9
  */
10
10
  import { existsSync, readdirSync, rmSync } from "node:fs";
11
11
  import { userInfo } from "node:os";
12
- import { isAbsolute, relative, resolve } from "node:path";
12
+ import path from "node:path";
13
13
  import { runCommandAsync } from "./commandRunner.js";
14
14
  import { resolveDefaultBranch } from "./defaultBranch.js";
15
15
  import { debug, errorMessage, isVerbose } from "./util.js";
@@ -42,7 +42,7 @@ function repoDirFor(config, repository) {
42
42
  if (!config.workspace.knownRepositories.includes(repository)) {
43
43
  throw new Error(`Repository "${repository}" is not in workspace.knownRepositories: ${config.workspace.knownRepositories.join(", ")}`);
44
44
  }
45
- const repoDir = resolve(config.workspace.projectDir, repository);
45
+ const repoDir = path.resolve(config.workspace.projectDir, repository);
46
46
  if (!existsSync(repoDir)) {
47
47
  throw new Error(`Repository not found: ${repoDir}`);
48
48
  }
@@ -51,14 +51,14 @@ function repoDirFor(config, repository) {
51
51
  function basePaths(config, repository, ticket) {
52
52
  // Tickets must match the same shape the worktree discovery regexes use,
53
53
  // so create()/list()/findByTicket() agree on what's a valid worktree.
54
- // This also rejects traversal tokens before they reach resolve().
54
+ // This also rejects traversal tokens before they reach path.resolve().
55
55
  if (!TICKET_RE.test(ticket)) {
56
56
  throw new Error(`Invalid ticket "${ticket}": must be a plain ticket id`);
57
57
  }
58
- const projectDir = resolve(config.workspace.projectDir);
58
+ const projectDir = path.resolve(config.workspace.projectDir);
59
59
  const repoDir = repoDirFor(config, repository);
60
60
  const hostWorktreeName = `${repository}-${ticket}`;
61
- const hostWorktreeDir = resolve(projectDir, hostWorktreeName);
61
+ const hostWorktreeDir = path.resolve(projectDir, hostWorktreeName);
62
62
  return {
63
63
  projectDir,
64
64
  repoDir,
@@ -122,17 +122,17 @@ async function createWorktree(config, spec, signal) {
122
122
  };
123
123
  }
124
124
  function listWorktrees(config) {
125
- const projectDir = resolve(config.workspace.projectDir);
125
+ const projectDir = path.resolve(config.workspace.projectDir);
126
126
  const entries = [];
127
127
  // Worktrees live at `projectDir/<repository>-<ticket>`. When `repository`
128
- // contains a slash (e.g. "owner/repo"), `resolve()` nests one level
128
+ // contains a slash (e.g. "owner/repo"), `path.resolve()` nests one level
129
129
  // deeper, so the worktree path is `projectDir/owner/repo-<ticket>`.
130
130
  // Scan each known repository's parent directory rather than the project
131
131
  // root, so nested worktrees are discovered alongside bare ones.
132
132
  const reposByParent = new Map();
133
133
  for (const repository of config.workspace.knownRepositories) {
134
134
  const lastSlash = repository.lastIndexOf("/");
135
- const parentDir = lastSlash === -1 ? projectDir : resolve(projectDir, repository.slice(0, lastSlash));
135
+ const parentDir = lastSlash === -1 ? projectDir : path.resolve(projectDir, repository.slice(0, lastSlash));
136
136
  const basename = lastSlash === -1 ? repository : repository.slice(lastSlash + 1);
137
137
  let repoByBasename = reposByParent.get(parentDir);
138
138
  if (repoByBasename === undefined) {
@@ -170,7 +170,7 @@ function listWorktrees(config) {
170
170
  repository,
171
171
  ticket,
172
172
  branchName: branchNameForTicket(ticket),
173
- dir: resolve(parentDir, entry.name),
173
+ dir: path.resolve(parentDir, entry.name),
174
174
  kind: "host",
175
175
  });
176
176
  }
@@ -178,8 +178,8 @@ function listWorktrees(config) {
178
178
  return entries;
179
179
  }
180
180
  async function removeWorktree(config, entry, options) {
181
- const projectDir = resolve(config.workspace.projectDir);
182
- const repoDir = resolve(projectDir, entry.repository);
181
+ const projectDir = path.resolve(config.workspace.projectDir);
182
+ const repoDir = path.resolve(projectDir, entry.repository);
183
183
  if (existsSync(entry.dir)) {
184
184
  debug(`Removing worktree ${entry.dir}${options.force ? " (--force)" : ""}...`);
185
185
  const removeArguments = ["-C", repoDir, "worktree", "remove"];
@@ -297,12 +297,12 @@ async function probeWorktreeRegistration(arguments_) {
297
297
  catch {
298
298
  return "unknown";
299
299
  }
300
- const resolvedWorktreeDir = resolve(arguments_.worktreeDir);
300
+ const resolvedWorktreeDir = path.resolve(arguments_.worktreeDir);
301
301
  for (const line of output.split("\n")) {
302
302
  if (!line.startsWith(WORKTREE_LIST_PREFIX)) {
303
303
  continue;
304
304
  }
305
- if (resolve(line.slice(WORKTREE_LIST_PREFIX.length)) === resolvedWorktreeDir) {
305
+ if (path.resolve(line.slice(WORKTREE_LIST_PREFIX.length)) === resolvedWorktreeDir) {
306
306
  return "registered";
307
307
  }
308
308
  }
@@ -313,18 +313,18 @@ function describeOrphanWorktree(arguments_) {
313
313
  return `directory exists but is not a registered git worktree. Run \`crew cleanup --force ${ticket}\` to remove ${dir}, or inspect it first if it may contain valuable files.`;
314
314
  }
315
315
  function expectedHostWorktreeDir(config, entry) {
316
- return resolve(config.workspace.projectDir, `${entry.repository}-${entry.ticket}`);
316
+ return path.resolve(config.workspace.projectDir, `${entry.repository}-${entry.ticket}`);
317
317
  }
318
318
  function isInsideDirectory(parentDir, childDir) {
319
- const childRelativePath = relative(parentDir, childDir);
319
+ const childRelativePath = path.relative(parentDir, childDir);
320
320
  return (childRelativePath.length > 0 &&
321
321
  !childRelativePath.startsWith("..") &&
322
- !isAbsolute(childRelativePath));
322
+ !path.isAbsolute(childRelativePath));
323
323
  }
324
324
  function removeOrphanWorktreeDirectory(config, entry) {
325
- const projectDir = resolve(config.workspace.projectDir);
325
+ const projectDir = path.resolve(config.workspace.projectDir);
326
326
  const expectedDir = expectedHostWorktreeDir(config, entry);
327
- const targetDir = resolve(entry.dir);
327
+ const targetDir = path.resolve(entry.dir);
328
328
  if (targetDir !== expectedDir || !isInsideDirectory(projectDir, targetDir)) {
329
329
  throw new Error(`Refusing to force-delete ${entry.dir}: expected groundcrew worktree path ${expectedDir}.`);
330
330
  }
package/dist/lib/xdg.js CHANGED
@@ -1,19 +1,19 @@
1
1
  import { homedir } from "node:os";
2
- import { isAbsolute, resolve } from "node:path";
2
+ import path from "node:path";
3
3
  import { readEnvironmentVariable } from "./util.js";
4
4
  // Per the XDG Base Directory spec, relative override paths are invalid and
5
5
  // must be ignored — without this guard, a relative override would anchor to
6
6
  // the cwd via `resolve` instead of falling back to $HOME.
7
7
  function xdgBase(envName, fallbackSegments) {
8
8
  const override = readEnvironmentVariable(envName);
9
- if (override !== undefined && override.length > 0 && isAbsolute(override)) {
9
+ if (override !== undefined && override.length > 0 && path.isAbsolute(override)) {
10
10
  return override;
11
11
  }
12
- return resolve(homedir(), ...fallbackSegments);
12
+ return path.resolve(homedir(), ...fallbackSegments);
13
13
  }
14
14
  export function xdgConfigPath(...segments) {
15
- return resolve(xdgBase("XDG_CONFIG_HOME", [".config"]), ...segments);
15
+ return path.resolve(xdgBase("XDG_CONFIG_HOME", [".config"]), ...segments);
16
16
  }
17
17
  export function xdgStatePath(...segments) {
18
- return resolve(xdgBase("XDG_STATE_HOME", [".local", "state"]), ...segments);
18
+ return path.resolve(xdgBase("XDG_STATE_HOME", [".local", "state"]), ...segments);
19
19
  }
@@ -1,11 +1,12 @@
1
1
  # Configuration
2
2
 
3
- Two keys are required; everything else has a default.
3
+ Workspace settings and at least one enabled model are required; everything else has a default.
4
4
 
5
5
  | Key | What |
6
6
  | ----------------------------- | ---------------------------------------------------------------------- |
7
7
  | `workspace.projectDir` | Parent dir for cloned repos and sibling ticket worktrees. |
8
8
  | `workspace.knownRepositories` | Repos searched for in ticket descriptions to infer where work belongs. |
9
+ | `models.definitions` | Enabled model set. Built-in presets can be enabled with `{}`. |
9
10
 
10
11
  The branch prefix (`<prefix>-<TICKET>`) is derived from `os.userInfo().username` and is not configurable. There is no `linear` config block. Groundcrew picks up every issue assigned to your API key's viewer that carries an `agent-*` label across every visible team and project, governed by a single `orchestrator.maximumInProgress` budget.
11
12
 
@@ -40,41 +41,51 @@ The "Loaded config from ..." line at startup tells you which config won.
40
41
 
41
42
  ## Agent Label Routing
42
43
 
43
- - `agent-claude`, `agent-codex`, `agent-<name>` routes to that model.
44
+ - `agent-claude`, `agent-codex`, `agent-<name>` routes to that enabled model.
44
45
  - `agent-any` routes to the model with the most available session capacity.
45
- - Unknown `agent-<name>` falls back to `models.default` with a warning.
46
+ - Unknown `agent-<name>` falls back to `models.default`.
47
+ - A built-in `agent-<name>` label whose model is not enabled falls back to `models.default` with a warning.
46
48
  - No `agent-*` label is ignored by `crew run`. Dispatch on demand with `crew start <TICKET>`, which falls back to `models.default`.
47
49
  - Todo tickets blocked by non-terminal blockers are skipped until their blockers reach a terminal status.
48
50
 
49
51
  Status classification uses Linear's workflow `state.type` (`unstarted`, `started`, `completed`, `canceled`, `duplicate`), so renamed status columns work without configuration. Parent issues with children are ignored; sub-issues are the work items.
50
52
 
51
- ## Disabling A Shipped Default Model
53
+ ## Enabling Model Presets
52
54
 
53
- Groundcrew ships `claude` and `codex` as default model definitions, additively merged into every resolved config. To stop probing one:
55
+ Groundcrew ships built-in presets for `claude` and `codex`, but models are not enabled by default. List the models you want in `models.definitions`:
54
56
 
55
57
  ```ts
56
58
  export default {
57
59
  models: {
58
60
  default: "claude",
59
61
  definitions: {
60
- codex: { disabled: true },
62
+ claude: {},
61
63
  },
62
64
  },
63
65
  };
64
66
  ```
65
67
 
66
- Effects:
68
+ To keep both shipped presets enabled:
67
69
 
68
- - `crew doctor` does not probe the disabled model's CLI.
69
- - `agent-any` only resolves to enabled models.
70
- - An `agent-<disabled>` label on a ticket falls back to `models.default` with a warning in the log.
70
+ ```ts
71
+ export default {
72
+ models: {
73
+ default: "claude",
74
+ definitions: {
75
+ claude: {},
76
+ codex: {},
77
+ },
78
+ },
79
+ };
80
+ ```
71
81
 
72
82
  Rules:
73
83
 
74
- - `disabled` only accepts shipped-default keys (`claude`, `codex`). A typo fails loudly at config load.
75
- - `disabled` must be exactly the boolean `true`.
76
- - It cannot be combined with `cmd`, `color`, or `usage` in the same entry.
84
+ - `models.definitions` is the enabled model set; `crew doctor` only probes listed models.
85
+ - Built-in entries can be `{}` or partial overrides such as `{ cmd: "..." }`.
86
+ - Custom model names must provide `cmd` and `color`.
77
87
  - `models.default` must point at an enabled model.
88
+ - Legacy model entries like `codex: { disabled: true }` are rejected with migration guidance; remove unwanted entries instead.
78
89
 
79
90
  ## Prompt Customization
80
91
 
@@ -96,26 +107,25 @@ This keeps package defaults portable while letting your private config reference
96
107
 
97
108
  ## Full Reference
98
109
 
99
- | Key | Default | What it does |
100
- | ---------------------------------------- | ------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
101
- | `sources` | `[]` | Additional pluggable ticket sources, dispatched alongside the built-in Linear adapter. Built-in kinds: `shell`, `linear`. |
102
- | `git.remote` | `"origin"` | Remote used for `fetch` and as the worktree base ref. |
103
- | `git.defaultBranch` | `"main"` | Branch fetched from `git.remote` and used as the worktree base. |
104
- | `workspace.projectDir` | **required** | Parent dir for cloned repos and sibling ticket worktrees. |
105
- | `workspace.knownRepositories` | **required** | Repos searched for in ticket descriptions to infer where work belongs. A ticket labeled for groundcrew (`agent-*`) fails fast when no known repo appears; unlabeled tickets are ignored. |
106
- | `orchestrator.maximumInProgress` | `4` | Cap on in-progress tickets at once for this `crew` instance. |
107
- | `orchestrator.pollIntervalMilliseconds` | `120_000` | Poll interval in `--watch` mode. |
108
- | `orchestrator.sessionLimitPercentage` | `85` | Number in `(0, 100]`. A model whose codexbar session window exceeds this percentage is skipped that tick. |
109
- | `models.default` | `"claude"` | Tiebreak for `agent-any` resolution and fallback for explicit but unknown `agent-*` labels. Also used by `crew start <TICKET>` for unlabeled tickets. `crew run` ignores unlabeled tickets and does not apply this default. Must exist in `models.definitions`. |
110
- | `models.definitions` | `{ claude, codex }` | Agent definitions. Additive merge with shipped defaults. |
111
- | `models.definitions.<name>.cmd` | | Shell command launched for the model. Runs in the worktree through the resolved `local.runner`. `{{worktree}}` is replaced before launch; `{{sandbox}}` expands to the sbx sandbox name under the sdx runner and an empty string otherwise. |
112
- | `models.definitions.<name>.color` | | Color for the workspace status pill (cmux only; tmux silently drops it). |
113
- | `models.definitions.<name>.usage` | optional | If set, codexbar usage is fetched for this model and gated by `sessionLimitPercentage`. Falls back to default when unset, with gating enabled for known models. When `usage.codexbar.source` is omitted, groundcrew uses `oauth` for Codex/Claude on macOS, `auto` for other macOS providers, and `cli` elsewhere. Set to `{ disabled: true }` to disable usage gating. |
114
- | `models.definitions.<name>.sandbox` | optional | Docker Sandboxes binding for the model. Required at launch when `local.runner` resolves to `sdx`. Fields: `agent` (required sbx agent name) and `setupCommand` (override for the inside-sandbox setup script). Groundcrew assumes the `groundcrew-<agent>` sandbox already exists. |
115
- | `models.definitions.<name>.preLaunch` | optional | Host-only shell snippet run before the agent exec and outside Safehouse/sdx. Exports survive into the launch shell; under the default `safehouse` runner they are only forwarded to the agent when listed via `preLaunchEnv` or when `cmd` includes its own `safehouse --env-pass=NAMES`. `{{worktree}}` is substituted. A non-zero exit aborts launch. Not supported when `local.runner` resolves to `sdx` in v1. |
116
- | `models.definitions.<name>.preLaunchEnv` | optional | Companion to `preLaunch`: list of env var names to append to groundcrew's `safehouse-clearance` `--env-pass=` flag, so `preLaunch` exports reach the agent without overriding `cmd` and losing the project's egress allowlist. Each entry must match `[A-Za-z_][A-Za-z0-9_]*`. Under `runner: "none"` exports already inherit and `preLaunchEnv` is a no-op. An empty array is a uniform no-op in every runner; a non-empty list is rejected when `cmd` already starts with `safehouse` or when `runner` resolves to `sdx`. |
117
- | `models.definitions.<name>.disabled` | optional | When set to exactly `true`, drops the named shipped default (`claude` or `codex`). Doctor skips probing it; `agent-<name>` labels fall back to `models.default` with a warning. |
118
- | `prompts.initial` | unattended template | First message sent to the agent. Placeholders: `{{ticket}}`, `{{worktree}}`, `{{title}}`, `{{description}}`. Override this from `crew.config.ts` for team-specific statuses, tools, plugins, or review loops. |
119
- | `workspaceKind` | `"auto"` | Terminal session manager. `"auto"` picks `cmux` when on PATH, else `tmux`. Set to `"cmux"` or `"tmux"` to fail loudly when the chosen backend is missing. |
120
- | `local.runner` | `"auto"` | Local isolation backend. `"auto"` uses `safehouse` on macOS and `sdx` on Linux/WSL. Explicit: `"safehouse"`, `"sdx"`, `"none"`. `"none"` is never picked implicitly. |
121
- | `logging.file` | XDG state path | Append-mode log file. `log()` / `logEvent()` tee here in addition to stdout. Defaults to `${XDG_STATE_HOME:-$HOME/.local/state}/groundcrew/groundcrew.log`. |
110
+ | Key | Default | What it does |
111
+ | ---------------------------------------- | -------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
112
+ | `sources` | `[]` | Additional pluggable ticket sources, dispatched alongside the built-in Linear adapter. Built-in kinds: `shell`, `linear`. |
113
+ | `git.remote` | `"origin"` | Remote used for `fetch` and as the worktree base ref. |
114
+ | `git.defaultBranch` | `"main"` | Branch fetched from `git.remote` and used as the worktree base. |
115
+ | `workspace.projectDir` | **required** | Parent dir for cloned repos and sibling ticket worktrees. |
116
+ | `workspace.knownRepositories` | **required** | Repos searched for in ticket descriptions to infer where work belongs. A ticket labeled for groundcrew (`agent-*`) fails fast when no known repo appears; unlabeled tickets are ignored. |
117
+ | `orchestrator.maximumInProgress` | `4` | Cap on in-progress tickets at once for this `crew` instance. |
118
+ | `orchestrator.pollIntervalMilliseconds` | `120_000` | Poll interval in `--watch` mode. |
119
+ | `orchestrator.sessionLimitPercentage` | `85` | Number in `(0, 100]`. A model whose codexbar session window exceeds this percentage is skipped that tick. |
120
+ | `models.default` | `"claude"` | Tiebreak for `agent-any` resolution and fallback for explicit but unknown `agent-*` labels. Also used by `crew start <TICKET>` for unlabeled tickets. `crew run` ignores unlabeled tickets and does not apply this default. Must exist in `models.definitions`. If you enable only `codex`, set `default: "codex"`. |
121
+ | `models.definitions` | **required** | Enabled model set. Built-in keys (`claude`, `codex`) can use `{}` to opt into the shipped preset. Custom model names must provide `cmd` and `color`. |
122
+ | `models.definitions.<name>.cmd` | preset for built-ins | Shell command launched for the model. Required for custom models. Runs in the worktree through the resolved `local.runner`. `{{worktree}}` is replaced before launch; `{{sandbox}}` expands to the sbx sandbox name under the sdx runner and an empty string otherwise. |
123
+ | `models.definitions.<name>.color` | preset for built-ins | Color for the workspace status pill (cmux only; tmux silently drops it). Required for custom models. |
124
+ | `models.definitions.<name>.usage` | preset for built-ins | If set, codexbar usage is fetched for this model and gated by `sessionLimitPercentage`. When `usage.codexbar.source` is omitted, groundcrew uses `oauth` for Codex/Claude on macOS, `auto` for other macOS providers, and `cli` elsewhere. Set to `{ disabled: true }` to disable usage gating while keeping the model enabled. |
125
+ | `models.definitions.<name>.sandbox` | optional | Docker Sandboxes binding for the model. Required at launch when `local.runner` resolves to `sdx`. Fields: `agent` (required sbx agent name) and `setupCommand` (override for the inside-sandbox setup script). Groundcrew assumes the `groundcrew-<agent>` sandbox already exists. |
126
+ | `models.definitions.<name>.preLaunch` | optional | Host-only shell snippet run before the agent exec and outside Safehouse/sdx. Exports survive into the launch shell; under the default `safehouse` runner they are only forwarded to the agent when listed via `preLaunchEnv` or when `cmd` includes its own `safehouse --env-pass=NAMES`. `{{worktree}}` is substituted. A non-zero exit aborts launch. Not supported when `local.runner` resolves to `sdx` in v1. |
127
+ | `models.definitions.<name>.preLaunchEnv` | optional | Companion to `preLaunch`: list of env var names to append to groundcrew's `safehouse-clearance` `--env-pass=` flag, so `preLaunch` exports reach the agent without overriding `cmd` and losing the project's egress allowlist. Each entry must match `[A-Za-z_][A-Za-z0-9_]*`. Under `runner: "none"` exports already inherit and `preLaunchEnv` is a no-op. An empty array is a uniform no-op in every runner; a non-empty list is rejected when `cmd` already starts with `safehouse` or when `runner` resolves to `sdx`. |
128
+ | `prompts.initial` | unattended template | First message sent to the agent. Placeholders: `{{ticket}}`, `{{worktree}}`, `{{title}}`, `{{description}}`. Override this from `crew.config.ts` for team-specific statuses, tools, plugins, or review loops. |
129
+ | `workspaceKind` | `"auto"` | Terminal session manager. `"auto"` picks `cmux` when on PATH, else `tmux`. Set to `"cmux"` or `"tmux"` to fail loudly when the chosen backend is missing. |
130
+ | `local.runner` | `"auto"` | Local isolation backend. `"auto"` uses `safehouse` on macOS and `sdx` on Linux/WSL. Explicit: `"safehouse"`, `"sdx"`, `"none"`. `"none"` is never picked implicitly. |
131
+ | `logging.file` | XDG state path | Append-mode log file. `log()` / `logEvent()` tee here in addition to stdout. Defaults to `${XDG_STATE_HOME:-$HOME/.local/state}/groundcrew/groundcrew.log`. |
@@ -4,18 +4,18 @@ First stop for "what exists locally right now": `crew status <ticket>` shows the
4
4
 
5
5
  ## Missing Model CLI
6
6
 
7
- `models.definitions` includes both shipped defaults (`claude`, `codex`) by default via additive merge. If you only intend to label tickets `agent-claude` and do not have `codex` installed, initialize with `crew init --model claude` or set:
7
+ `crew doctor` probes every model listed in `models.definitions`. If you do not have `codex` installed, initialize with `crew init --model claude` or leave `codex` out of the enabled model set:
8
8
 
9
9
  ```ts
10
10
  models: {
11
11
  default: "claude",
12
12
  definitions: {
13
- codex: { disabled: true },
13
+ claude: {},
14
14
  },
15
15
  },
16
16
  ```
17
17
 
18
- Without that, doctor exits non-zero on a missing `codex` binary even though `crew run` would never route to it.
18
+ If `codex: {}` is listed, doctor expects the `codex` CLI to be installed because tickets can route to `agent-codex` and `agent-any` can select it.
19
19
 
20
20
  ## Safehouse-Wrapped Commands Are Not Re-Wrapped
21
21
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@clipboard-health/groundcrew",
3
- "version": "4.7.2",
3
+ "version": "4.8.0",
4
4
  "description": "Linear-driven orchestrator that launches AI coding agents in git worktrees, with workspace lifecycle and usage tracking.",
5
5
  "keywords": [
6
6
  "agent",
@@ -68,7 +68,7 @@
68
68
  "verify": "node scripts/verifyAll.mts"
69
69
  },
70
70
  "dependencies": {
71
- "@clipboard-health/clearance": "1.0.8",
71
+ "@clipboard-health/clearance": "1.1.7",
72
72
  "@linear/sdk": "86.0.0",
73
73
  "cosmiconfig": "9.0.1",
74
74
  "tslib": "2.8.1",
@@ -77,10 +77,10 @@
77
77
  "devDependencies": {
78
78
  "@clipboard-health/ai-rules": "2.21.0",
79
79
  "@clipboard-health/oxlint-config": "1.9.4",
80
- "@nx/js": "22.7.2",
80
+ "@nx/js": "22.7.3",
81
81
  "@tsconfig/node24": "24.0.4",
82
82
  "@tsconfig/strictest": "2.0.8",
83
- "@types/node": "24.12.4",
83
+ "@types/node": "25.9.1",
84
84
  "@typescript/native-preview": "7.0.0-dev.20260527.2",
85
85
  "@vitest/coverage-v8": "4.1.7",
86
86
  "cspell": "10.0.0",
@@ -90,11 +90,11 @@
90
90
  "knip": "6.14.2",
91
91
  "lint-staged": "17.0.5",
92
92
  "markdownlint-cli2": "0.22.1",
93
- "nx": "22.7.2",
93
+ "nx": "22.7.3",
94
94
  "oxfmt": "0.52.0",
95
- "oxlint": "1.65.0",
95
+ "oxlint": "1.66.0",
96
96
  "oxlint-tsgolint": "0.23.0",
97
- "syncpack": "15.2.0",
97
+ "syncpack": "15.3.1",
98
98
  "vite": "8.0.14",
99
99
  "vitest": "4.1.7"
100
100
  },