mirror of
https://github.com/Fission-AI/OpenSpec.git
synced 2026-10-15 05:30:21 +08:00
Compare commits
15
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7949e21f20 | ||
|
|
7701149db3 | ||
|
|
9cb5ba8cec | ||
|
|
40db61856a | ||
|
|
1bce87f858 | ||
|
|
d9c703ab38 | ||
|
|
352fd3774a | ||
|
|
f8f8d4c5a5 | ||
|
|
8153fc5efc | ||
|
|
37597d88ce | ||
|
|
76d49a1d81 | ||
|
|
eb315c9d75 | ||
|
|
90c95c2a23 | ||
|
|
43820cae29 | ||
|
|
9111a7654d |
@@ -4,7 +4,7 @@ This directory contains the VS Code dev container configuration for OpenSpec dev
|
||||
|
||||
## What's Included
|
||||
|
||||
- **Node.js 20 LTS** (>=20.19.0) - TypeScript/JavaScript runtime
|
||||
- **Node.js 22 LTS** - TypeScript/JavaScript runtime (OpenSpec itself supports Node 20.19.0 and newer)
|
||||
- **pnpm** - Fast, disk space efficient package manager
|
||||
- **Git + GitHub CLI** - Version control tools
|
||||
- **VS Code Extensions**:
|
||||
|
||||
@@ -55,19 +55,34 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
# Every OS runs the oldest supported Node (package.json engines).
|
||||
# Linux also runs the current LTS lines users are likely to have.
|
||||
include:
|
||||
- os: ubuntu-latest
|
||||
shell: bash
|
||||
label: linux-bash
|
||||
node: '20.19.0'
|
||||
vitest_workers: 4
|
||||
- os: macos-latest
|
||||
shell: bash
|
||||
label: macos-bash
|
||||
node: '20.19.0'
|
||||
vitest_workers: 4
|
||||
- os: windows-latest
|
||||
shell: pwsh
|
||||
label: windows-pwsh
|
||||
node: '20.19.0'
|
||||
vitest_workers: 2
|
||||
- os: ubuntu-latest
|
||||
shell: bash
|
||||
label: linux-bash-node22
|
||||
node: '22'
|
||||
vitest_workers: 4
|
||||
- os: ubuntu-latest
|
||||
shell: bash
|
||||
label: linux-bash-node24
|
||||
node: '24'
|
||||
vitest_workers: 4
|
||||
|
||||
defaults:
|
||||
run:
|
||||
@@ -86,7 +101,7 @@ jobs:
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
||||
with:
|
||||
node-version: '20.19.0'
|
||||
node-version: ${{ matrix.node }}
|
||||
cache: 'pnpm'
|
||||
|
||||
- name: Print environment diagnostics
|
||||
@@ -105,7 +120,7 @@ jobs:
|
||||
run: pnpm test
|
||||
|
||||
- name: Upload test coverage
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
if: matrix.os == 'ubuntu-latest' && matrix.node == '20.19.0'
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||
with:
|
||||
name: coverage-report-${{ github.event_name }}
|
||||
|
||||
@@ -1,5 +1,21 @@
|
||||
# @fission-ai/openspec
|
||||
|
||||
## 1.14.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#2052](https://github.com/Fission-AI/OpenSpec/pull/2052) [`90c95c2`](https://github.com/Fission-AI/OpenSpec/commit/90c95c2a235e0d4088006a879c19cd1febff2a9e) Thanks [@drakeo338](https://github.com/drakeo338)! - ### Bug Fixes
|
||||
|
||||
- **Archive accepts `openspec/`, `openspec/changes` or `openspec/specs` as a symlink** — `openspec archive` no longer refuses a project-owned symlink that points to another directory, such as a shared docs repository. An `archive/` directory that resolves outside the changes directory is still refused.
|
||||
|
||||
- [#2086](https://github.com/Fission-AI/OpenSpec/pull/2086) [`9cb5ba8`](https://github.com/Fission-AI/OpenSpec/commit/9cb5ba8cec4bc47227dcac4974a74b4adf1819f6) Thanks [@clay-good](https://github.com/clay-good)! - ### Bug Fixes
|
||||
|
||||
- **Explore no longer offers to write default-schema files under a custom schema** — The explore workflow's "where to capture" table and example offers now say they describe the default spec-driven schema. With another schema, the agent captures each insight in an artifact that schema defines, names it in the offer, and never creates a file such as `design.md` that the schema does not have.
|
||||
|
||||
- [#2064](https://github.com/Fission-AI/OpenSpec/pull/2064) [`76d49a1`](https://github.com/Fission-AI/OpenSpec/commit/76d49a1d8176ad2bdb3696e29c4ffe222402f2da) Thanks [@ryandemelo](https://github.com/ryandemelo)! - Shell Tab completion is faster. `openspec __complete`, which the completion scripts run on every Tab press, now loads 48 modules instead of 344 when completing changes or specs, and no longer records usage, so a press never waits on the telemetry request and can't use up the first-run telemetry notice where nobody sees it. Completion output is unchanged.
|
||||
|
||||
- [#2054](https://github.com/Fission-AI/OpenSpec/pull/2054) [`1bce87f`](https://github.com/Fission-AI/OpenSpec/commit/1bce87f858c2591b2cb58108597ae6f0a9bb5d7c) Thanks [@runsonmypc](https://github.com/runsonmypc)! - `openspec validate` and `openspec archive` now warn when a delta spec holds text archive does not carry into the main spec: a `## ` section other than Purpose and the four delta sections, text above the first `## ` header, or prose before the first requirement in ADDED or MODIFIED. That text was dropped without notice before. Like other validation warnings, these fail `openspec validate --strict`. What archive carries is unchanged.
|
||||
|
||||
## 1.14.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -33,11 +33,6 @@ Our philosophy:
|
||||
→ scalable from personal projects to enterprises
|
||||
```
|
||||
|
||||
> [!TIP]
|
||||
> **New workflow now available!** We've rebuilt OpenSpec with a new artifact-guided workflow.
|
||||
>
|
||||
> Run `/opsx:propose "your idea"` to get started. → [Learn more here](docs/opsx.md)
|
||||
|
||||
<p align="center">
|
||||
Follow <a href="https://x.com/0xTab">@0xTab on X</a> for updates · Join the <a href="https://discord.gg/YctCnvvshC">OpenSpec Discord</a> for help and questions.
|
||||
</p>
|
||||
@@ -112,13 +107,13 @@ OpenSpec is built with OpenSpec — browse this repo's live [specs](openspec/spe
|
||||
|
||||
Solo, OpenSpec keeps you and your AI honest on a single repo. On a team, the hard part moves: a feature spans the API server, the web app, and a shared library; requirements are owned by one team and consumed by others; planning starts before any code exists.
|
||||
|
||||
**[Stores](docs/stores-beta/user-guide.md)** are the answer — planning in a repo of its own. The same `openspec/` shape you already know (specs and changes), shared by `git push` like anything else. One source of truth your whole team and every coding agent can read, across every repo.
|
||||
**[Stores](https://openspec.dev/docs/stores)** are the answer — planning in a repo of its own. The same `openspec/` shape you already know (specs and changes), shared by `git push` like anything else. One source of truth your whole team and every coding agent can read, across every repo.
|
||||
|
||||
- **Cross-repo features** — one change, one plan, even when the code lands in three repos.
|
||||
- **Shared requirements** — a platform team owns the specs; product teams reference them read-only, right where their coding agent can read them. No drifting wiki.
|
||||
- **Plan before code** — capture the plan in the store now; the code repos catch up later.
|
||||
|
||||
> Stores are in **beta**. Start with the [Stores User Guide](docs/stores-beta/user-guide.md).
|
||||
> Stores are in **beta**. Start with the [Stores guide](https://openspec.dev/docs/stores).
|
||||
|
||||
## Quick Start
|
||||
|
||||
@@ -143,44 +138,33 @@ cd your-project
|
||||
openspec init
|
||||
```
|
||||
|
||||
> **Want your AI to do it?** Paste the [setup prompt](docs-lab/start/installation.md#install-with-your-ai-assistant) into your coding assistant — it installs the CLI, runs `openspec init`, and verifies the result.
|
||||
> **Want your AI to do it?** Paste the [setup prompt](https://openspec.dev/docs/installation#install-with-your-ai-assistant) into your coding assistant — it installs the CLI, runs `openspec init`, and verifies the result.
|
||||
|
||||
Now talk to your AI:
|
||||
|
||||
- **Not sure what to build yet?** Start with `/opsx:explore`, a no-stakes thinking partner that reads your code, weighs options, and shapes a plan before any code gets written. ([Explore guide](docs/explore.md))
|
||||
- **Not sure what to build yet?** Start with `/opsx:explore`, a no-stakes thinking partner that reads your code, weighs options, and shapes a plan before any code gets written. ([Quickstart](https://openspec.dev/docs/quickstart))
|
||||
- **Already know what you want?** Go straight to `/opsx:propose <what-you-want-to-build>`.
|
||||
|
||||
Both are in the default profile. If you want the expanded workflow (`/opsx:new`, `/opsx:continue`, `/opsx:ff`, `/opsx:verify`, `/opsx:bulk-archive`, `/opsx:onboard`), select it with `openspec config profile` and apply with `openspec update`.
|
||||
|
||||
`/opsx:propose` is the canonical name; your tool may spell it `/opsx-propose` (Cursor, GitHub Copilot), `@opsx-propose` (Amazon Q) or `$openspec-propose` (Codex). `openspec init` prints the right form for the tools you picked — see [How To Invoke](docs/supported-tools.md#how-to-invoke).
|
||||
`/opsx:propose` is the canonical name; your tool may spell it `/opsx-propose` (Cursor, GitHub Copilot), `@opsx-propose` (Amazon Q) or `$openspec-propose` (Codex). `openspec init` prints the right form for the tools you picked — see [Supported Tools](https://openspec.dev/docs/supported-tools).
|
||||
|
||||
> [!NOTE]
|
||||
> Not sure if your tool is supported? [View the full list](docs/supported-tools.md) – we support 30+ tools and growing.
|
||||
> Not sure if your tool is supported? [View the full list](https://openspec.dev/docs/supported-tools) – we support 30+ tools and growing.
|
||||
>
|
||||
> Also works with Homebrew, pnpm, yarn, bun, and Nix. [See installation options](docs-lab/start/installation.md).
|
||||
> Also works with Homebrew, pnpm, yarn, bun, and Nix. [See installation options](https://openspec.dev/docs/installation).
|
||||
|
||||
## Docs
|
||||
|
||||
**Start here:** the **[Documentation Home](docs/README.md)** maps everything. New to OpenSpec? Read [Getting Started](docs/getting-started.md), then [How Commands Work](docs/how-commands-work.md) (where you actually type `/opsx:propose`).
|
||||
|
||||
→ **[Getting Started](docs/getting-started.md)**: first steps<br>
|
||||
→ **[Explore First](docs/explore.md)**: think it through with `/opsx:explore` before you commit<br>
|
||||
→ **[How Commands Work](docs/how-commands-work.md)**: where slash commands run vs the CLI<br>
|
||||
→ **[Core Concepts at a Glance](docs/overview.md)**: the whole mental model, one page<br>
|
||||
→ **[Examples & Recipes](docs/examples.md)**: real changes, start to finish<br>
|
||||
→ **[Workflows](docs/workflows.md)**: combos and patterns<br>
|
||||
→ **[Existing Projects](docs/existing-projects.md)**: adopt OpenSpec on a brownfield codebase<br>
|
||||
→ **[Editing a Change](docs/editing-changes.md)**: update artifacts, go back, reconcile manual edits<br>
|
||||
→ **[Commands](docs/commands.md)**: slash commands & skills<br>
|
||||
→ **[CLI](docs/cli.md)**: terminal reference<br>
|
||||
→ **[Stores](docs/stores-beta/user-guide.md)**: plan in a separate repo, shared across your team (beta)<br>
|
||||
→ **[Supported Tools](docs/supported-tools.md)**: tool integrations & install paths<br>
|
||||
→ **[Concepts](docs/concepts.md)**: how it all fits<br>
|
||||
→ **[Multi-Language](docs/multi-language.md)**: multi-language support<br>
|
||||
→ **[Customization](docs/customization.md)**: make it yours<br>
|
||||
→ **[Community Showcase](docs/community.md)**: projects and resources built with and for OpenSpec<br>
|
||||
→ **[FAQ](docs/faq.md)** · **[Troubleshooting](docs/troubleshooting.md)** · **[Glossary](docs/glossary.md)**: quick help
|
||||
Full documentation lives at **[openspec.dev/docs](https://openspec.dev/docs)**.
|
||||
|
||||
→ **[Quickstart](https://openspec.dev/docs/quickstart)**: your first change, start to finish<br>
|
||||
→ **[Set up your project](https://openspec.dev/docs/setup)**: what `openspec init` installs<br>
|
||||
→ **[CLI](https://openspec.dev/docs/cli)**: terminal reference<br>
|
||||
→ **[Supported Tools](https://openspec.dev/docs/supported-tools)**: tool integrations & install paths<br>
|
||||
→ **[Customize](https://openspec.dev/docs/customize)**: profiles, project config, and schemas<br>
|
||||
→ **[Stores](https://openspec.dev/docs/stores)**: plan in a separate repo, shared across your team (beta)<br>
|
||||
→ **[Community Showcase](docs/community.md)**: projects and resources built with and for OpenSpec
|
||||
|
||||
## Community schemas
|
||||
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
# OpenSpec Documentation
|
||||
|
||||
> [!WARNING]
|
||||
> These pages are legacy and some are out of date. The current docs are at **[openspec.dev/docs](https://openspec.dev/docs)** (source: [`docs-lab/`](../docs-lab/)).
|
||||
|
||||
Welcome. This is the home for everything OpenSpec.
|
||||
|
||||
OpenSpec helps you and your AI coding assistant **agree on what to build before any code is written.** You describe the change, the AI drafts a short spec and a task list, you both look at the same plan, and then the work happens. No more discovering halfway through that the AI built the wrong thing.
|
||||
|
||||
+1
-1
@@ -7,7 +7,7 @@ Listed projects are maintained independently. Inclusion does not imply official
|
||||
## Projects and resources
|
||||
|
||||
- **[OpenSpec Workbench](https://github.com/VeryComplexAndLongName/OpenSpec-UI)**: Running and supervising agents on OpenSpec changes.
|
||||
- **[MySpec](https://myspec.dev)**: Cloud beta (account required) that conducts guided spec interviews and exports four-file bundles or OpenSpec-compatible changes. The service sends submitted content to third-party AI providers and is not designed for sensitive data.
|
||||
- **[MySpec](https://myspec.dev)**: Cloud beta (account required) that conducts guided spec interviews and exports four-file bundles or OpenSpec-compatible changes. Its MCP server (`@myspec/mcp-server`) and [Claude Code plugins](https://github.com/myspecs/claude-plugins) let coding agents read those specs, write OpenSpec changes, and implement them task by task. The service sends submitted content to third-party AI providers and is not designed for sensitive data.
|
||||
- **[openspec-guard](https://github.com/guillaume-flambard/spec-guard)**: CLI and GitHub Action that reports which OpenSpec scenarios are covered by a Vitest or Jest test, without running the tests.
|
||||
|
||||
## Add your project
|
||||
|
||||
@@ -423,6 +423,7 @@ Community schemas are not vendored into OpenSpec core — they live in their own
|
||||
| `nanopm` | @nmrtn | [nmrtn/nanopm](https://github.com/nmrtn/nanopm/tree/main/openspec-schema) | PM-first workflow. Runs [nanopm](https://github.com/nmrtn/nanopm)'s planning pipeline (audit → strategy → roadmap → PRD) upstream of implementation. Bridges product planning to OpenSpec's spec-driven engineering workflow. Artifacts read from `.nanopm/` if present — proposal sources the audit, design sources the strategy, and tasks source the PRD breakdown. |
|
||||
| `e2e-runbooks` | @Lukk17 | [Lukk17/openspec-schemas](https://github.com/Lukk17/openspec-schemas/tree/master/openspec/schemas/e2e-runbooks) | Capability-level end-to-end test runbooks. Each capability gets an immutable spec, an immutable tasks-template, and one timestamped run record per execution. Assertions are observable behaviour only (HTTP status, response body, persisted state — never log substrings); each run records start/end UTC, duration, and best-estimate LLM token consumption. |
|
||||
| `anvil` | @jikkujoyce | [jikkujoyce/openspec-schemas](https://github.com/jikkujoyce/openspec-schemas/tree/main/schemas/anvil) | Spec-driven workflow with TDD discipline and an adversarial review step. Flow: `proposal` → `specs` → `design` → `review` → `test-plan` → `tasks` → `apply` → `verify`. `review` is written by a fresh-context, read-only reviewer (a second model when one is available) and emits a `VERDICT:` line telling the agent to gate `test-plan`, `tasks`, and `apply`; OpenSpec only checks that artifacts exist, so enforce the gate with your own CI or hook. `test-plan` maps every spec scenario to a named test and doubles as a red/green ledger that `verify` audits. |
|
||||
| `ml-research` | @tsenst | [idealo/openspec-schemas](https://github.com/idealo/openspec-schemas/tree/main/openspec/schemas/ml-research) | Machine-learning experiment workflow. Flow: `proposal` → `design` → `evaluation` → `tasks` → `apply` → `specs`. `evaluation` fixes metrics, success criteria, and the falsification condition before the run; `apply` writes `results.md`; `specs` records only evidence-backed findings (refuted hypotheses included) as `Finding:` requirements with linked `Evidence:` blocks. |
|
||||
|
||||
> Want to contribute a community schema? Open an issue with a link to your repository, or submit a PR adding a row to this table.
|
||||
|
||||
|
||||
@@ -53,7 +53,7 @@
|
||||
inherit (finalAttrs) pname version src;
|
||||
pnpm = final.pnpm_10;
|
||||
fetcherVersion = 3;
|
||||
hash = "sha256-NIMHf7t+FnBgsswDtB1k4E/zX1ks1r3708ggE2ps9E8=";
|
||||
hash = "sha256-hzhk/JEqix4m/k564GbcVuNtoi21E9TkJprPFnPV+Zk=";
|
||||
};
|
||||
|
||||
nativeBuildInputs = with final; [
|
||||
|
||||
@@ -224,12 +224,12 @@ The spec update confirmation SHALL provide clear visibility into changes before
|
||||
EXISTING specs to be updated:
|
||||
- cli-init (from changes/update-init-command/specs/cli-init/spec.md)
|
||||
|
||||
Update 2 specs and archive 'add-archive-command'? [y/N]:
|
||||
Proceed with spec updates? (Y/n)
|
||||
```
|
||||
#### Scenario: Handling confirmation response
|
||||
|
||||
- **WHEN** waiting for user confirmation
|
||||
- **THEN** default to "No" for safety (require explicit "y" or "yes")
|
||||
- **THEN** default to "Yes", so pressing Enter proceeds (declining requires an explicit "n" or "no")
|
||||
- **AND** skip confirmation when `--yes` or `-y` flag is provided
|
||||
|
||||
#### Scenario: User declines confirmation
|
||||
|
||||
@@ -92,14 +92,14 @@ The `openspec schemas` command SHALL display the source of each schema.
|
||||
|
||||
### Requirement: Use config schema as default for new changes
|
||||
|
||||
The system SHALL use the schema field from `openspec/config.yaml` as the default when creating new changes without explicit `--schema` flag and no planning-home default applies.
|
||||
The system SHALL use the schema field from `openspec/config.yaml` as the default when creating new changes without an explicit `--schema` flag, and SHALL fall back to the planning-home default when the config supplies no schema.
|
||||
|
||||
#### Scenario: Create change without --schema flag and config exists
|
||||
- **WHEN** user runs `openspec new change foo`, no planning-home default applies, and config contains `schema: "tdd"`
|
||||
- **THEN** system creates change with schema "tdd"
|
||||
- **WHEN** user runs `openspec new change foo` and config contains `schema: "tdd"`
|
||||
- **THEN** system creates change with schema "tdd", even when a planning-home default applies
|
||||
|
||||
#### Scenario: Create change without --schema flag and no config
|
||||
- **WHEN** user runs `openspec new change foo`, no planning-home default applies, and no config file exists
|
||||
- **WHEN** user runs `openspec new change foo`, no config file exists, and no planning-home default applies
|
||||
- **THEN** system creates change with default schema "spec-driven"
|
||||
|
||||
#### Scenario: Create change with explicit --schema flag
|
||||
@@ -108,7 +108,7 @@ The system SHALL use the schema field from `openspec/config.yaml` as the default
|
||||
|
||||
### Requirement: Resolve schema with updated precedence order
|
||||
|
||||
The system SHALL resolve the schema for a change using the following precedence order: CLI flag, change metadata, planning-home default, project config, hardcoded default.
|
||||
The system SHALL resolve the schema for a change using the following precedence order: CLI flag, change metadata, project config, planning-home default, hardcoded default.
|
||||
|
||||
#### Scenario: CLI flag is provided
|
||||
- **WHEN** user runs command with `--schema custom`
|
||||
@@ -119,11 +119,11 @@ The system SHALL resolve the schema for a change using the following precedence
|
||||
- **THEN** system uses "bound" from change metadata
|
||||
|
||||
#### Scenario: Only project config specifies schema
|
||||
- **WHEN** no CLI flag, change metadata, or planning-home default exists, but config has `schema: tdd`
|
||||
- **THEN** system uses "tdd" from project config
|
||||
- **WHEN** no CLI flag or change metadata exists, but config has `schema: tdd`
|
||||
- **THEN** system uses "tdd" from project config, even when a planning-home default applies
|
||||
|
||||
#### Scenario: No schema specified anywhere
|
||||
- **WHEN** no CLI flag, change metadata, planning-home default, or project config
|
||||
- **WHEN** no CLI flag, change metadata, project config, or planning-home default
|
||||
- **THEN** system uses hardcoded default "spec-driven"
|
||||
|
||||
### Requirement: Support project-local schema names in config
|
||||
|
||||
@@ -62,7 +62,7 @@ The system SHALL display a one-line telemetry disclosure notice on the first com
|
||||
#### Scenario: First command execution
|
||||
- **WHEN** a user runs their first openspec command
|
||||
- **AND** telemetry is enabled
|
||||
- **THEN** the system displays: "Note: OpenSpec collects anonymous usage stats. Opt out: OPENSPEC_TELEMETRY=0"
|
||||
- **THEN** the system displays: "Note: OpenSpec collects anonymous usage stats. Opt out: OPENSPEC_TELEMETRY=0 or openspec config set telemetry.enabled false"
|
||||
|
||||
#### Scenario: Subsequent command execution
|
||||
- **WHEN** a user has already seen the notice (noticeSeen: true in config)
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@fission-ai/openspec",
|
||||
"version": "1.14.1",
|
||||
"version": "1.14.2",
|
||||
"description": "AI-native system for spec-driven development",
|
||||
"keywords": [
|
||||
"openspec",
|
||||
|
||||
Generated
+228
-252
@@ -11,6 +11,7 @@ overrides:
|
||||
js-yaml@>=4.0.0 <4.3.1: '>=4.3.1 <5'
|
||||
nanoid@<3.3.17: '>=3.3.17 <4'
|
||||
fflate@<0.8.3: '>=0.8.3 <0.9'
|
||||
shell-quote@>=1.8.4 <1.11.0: '>=1.11.0 <2'
|
||||
|
||||
importers:
|
||||
|
||||
@@ -18,7 +19,7 @@ importers:
|
||||
dependencies:
|
||||
'@inquirer/core':
|
||||
specifier: ^12.0.0
|
||||
version: 12.0.3(@types/node@20.19.43)
|
||||
version: 12.0.4(@types/node@20.19.43)
|
||||
'@inquirer/prompts':
|
||||
specifier: ^8.5.2
|
||||
version: 8.5.2(@types/node@20.19.43)
|
||||
@@ -61,7 +62,7 @@ importers:
|
||||
version: 4.1.11(vitest@4.1.11)
|
||||
eslint:
|
||||
specifier: ^10.5.0
|
||||
version: 10.11.0
|
||||
version: 10.12.0
|
||||
smol-toml:
|
||||
specifier: ^1.7.1
|
||||
version: 1.9.0
|
||||
@@ -70,7 +71,7 @@ importers:
|
||||
version: 6.0.3
|
||||
typescript-eslint:
|
||||
specifier: ^8.65.0
|
||||
version: 8.70.1(eslint@10.11.0)(typescript@6.0.3)
|
||||
version: 8.71.0(eslint@10.12.0)(typescript@6.0.3)
|
||||
vitest:
|
||||
specifier: ^4.1.11
|
||||
version: 4.1.11(@types/node@20.19.43)(@vitest/ui@4.1.11)(vite@7.3.6(@types/node@20.19.43)(yaml@2.9.1))
|
||||
@@ -366,12 +367,8 @@ packages:
|
||||
resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==}
|
||||
engines: {node: '>=18.18'}
|
||||
|
||||
'@inquirer/ansi@2.0.7':
|
||||
resolution: {integrity: sha512-3eTuUO1vH2cZm2ZKHeQxnOqlTi9EfZDGgIe3BL3I4u+rJHocr9Fz86M4fjYABPvFnQG/gGK551HqDiIcETwU6Q==}
|
||||
engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'}
|
||||
|
||||
'@inquirer/ansi@2.0.8':
|
||||
resolution: {integrity: sha512-WpQM+Ti6Z40EFwwt+uL2p4UabT+W179zHp6HhLVOzfbwnVn05IPO/eXIZXGNqcT1jbQ15SujNLzQ39k4QPPxBQ==}
|
||||
'@inquirer/ansi@2.0.9':
|
||||
resolution: {integrity: sha512-0Ywt3S8tqM6TWUcsRlBFQ1x+X3/o8kktX0ZiKAnByYb9GFDknnOhse1PVtfiD2BXPI8ar+BWx9PV9R0lQIcygw==}
|
||||
engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'}
|
||||
|
||||
'@inquirer/checkbox@5.2.1':
|
||||
@@ -401,8 +398,8 @@ packages:
|
||||
'@types/node':
|
||||
optional: true
|
||||
|
||||
'@inquirer/core@12.0.3':
|
||||
resolution: {integrity: sha512-wsSy0sznmXwkty+2PzZwx00Cazc/E0r0B7mAzdGROz2Ct+DFZXaK7WDjGZvgjRldxH5ZhFVfF2lgkYrqgOw2KA==}
|
||||
'@inquirer/core@12.0.4':
|
||||
resolution: {integrity: sha512-M5fB67y7jPObI45vhD82RskWwCSm0scOvenV/I6K8tIi2gfr9DXqQqJ+iw+Wi6SgapikdOiJibYzz1gW47fsDg==}
|
||||
engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'}
|
||||
peerDependencies:
|
||||
'@types/node': '>=18'
|
||||
@@ -437,10 +434,6 @@ packages:
|
||||
'@types/node':
|
||||
optional: true
|
||||
|
||||
'@inquirer/figures@2.0.7':
|
||||
resolution: {integrity: sha512-aJ8TBPOGB6f/2qziPfElISTCEd5XOYTFckA2SGjhNmiKzfK/u4ot3v0DUzGVdUnKjN10EqnnEPck36BkyfLnJw==}
|
||||
engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'}
|
||||
|
||||
'@inquirer/figures@2.0.9':
|
||||
resolution: {integrity: sha512-EAWgUTGQ/Umgga51dE3B2PUHbufuXarDfg86uVgoSgNHNNQnyFKcOrQLWVqYMghuSyHh8+2HUH0Js9cTC1WAdg==}
|
||||
engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'}
|
||||
@@ -508,17 +501,8 @@ packages:
|
||||
'@types/node':
|
||||
optional: true
|
||||
|
||||
'@inquirer/type@4.0.7':
|
||||
resolution: {integrity: sha512-t28inv14nMQ1PhKpsJPY+kEs/c00qzeCOS2gTNRyTjG5d6qsVA2fItxW4hkvGZ5lvanGLdtCzVIx5dwdRpN1+g==}
|
||||
engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'}
|
||||
peerDependencies:
|
||||
'@types/node': '>=18'
|
||||
peerDependenciesMeta:
|
||||
'@types/node':
|
||||
optional: true
|
||||
|
||||
'@inquirer/type@4.1.1':
|
||||
resolution: {integrity: sha512-yJoHYrMnxIsJZCY+0Vb66Dy3he3kL3e2wOBKhoSwWWAzZAY82emlxwgprCtp6yRixvNRNq9ztfRWQYPNr3Go7A==}
|
||||
'@inquirer/type@4.2.0':
|
||||
resolution: {integrity: sha512-2rnHX8RPo4glJYqnFc/v3nQeIhDcjyRRyaT7MGcfL/8xv/8K03SXYoA2tX06WVSKywWOE/ncbG3dtuzLjR5TCQ==}
|
||||
engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'}
|
||||
peerDependencies:
|
||||
'@types/node': '>=18'
|
||||
@@ -576,141 +560,141 @@ packages:
|
||||
'@polka/url@1.0.0-next.29':
|
||||
resolution: {integrity: sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==}
|
||||
|
||||
'@rollup/rollup-android-arm-eabi@4.63.5':
|
||||
resolution: {integrity: sha512-J25QJU+B78T4FhhBsNpLJyVWOi31mwtpcMwywHmOKH65Q9IWGA81gPj+dnwlhU8wktVriYE+tFAaQgrnJRzAZg==}
|
||||
'@rollup/rollup-android-arm-eabi@4.64.3':
|
||||
resolution: {integrity: sha512-a31GFbL70GlGUGhn8goVjRLtdDG1urLCJFWLZ7JcxdHK9pPvCohLFtYLgJDdNbtrNZSrJl1u9bK05ISsSC2EpA==}
|
||||
cpu: [arm]
|
||||
os: [android]
|
||||
|
||||
'@rollup/rollup-android-arm64@4.63.5':
|
||||
resolution: {integrity: sha512-LDopB3zuZM5Ux9TT2luNEBJW/tYbGU2g1d+VpKk6I+gSKDb+/7sYE6M225gRQt4RbMX6MSwMsVR/phdjVUgRLg==}
|
||||
'@rollup/rollup-android-arm64@4.64.3':
|
||||
resolution: {integrity: sha512-IRv757RLiPGlNxAX5JXU0eiUlUPvKiwFRbkqLd+gywptLK4uSEg1qkl6en6r0cXMazSnWUWOBABv+QfuLV3n5Q==}
|
||||
cpu: [arm64]
|
||||
os: [android]
|
||||
|
||||
'@rollup/rollup-darwin-arm64@4.63.5':
|
||||
resolution: {integrity: sha512-wlJEERGfeuHeBavCL2qVnNacOK43NDoZM4sjkeRPymd04OAE9T1zBqDJgmZ+CIsPTYKwdzpUC8vmOw84dwY4Tg==}
|
||||
'@rollup/rollup-darwin-arm64@4.64.3':
|
||||
resolution: {integrity: sha512-UkzI35Fnz6+MJNATdnjnZ3TpdG/rXab8h7oDOD9R0zDVIFmuuYrvgUj8V8mh+A//+LR4TlEQo4ZW4RhyRtWv3A==}
|
||||
cpu: [arm64]
|
||||
os: [darwin]
|
||||
|
||||
'@rollup/rollup-darwin-x64@4.63.5':
|
||||
resolution: {integrity: sha512-4nJJGg5jbo2wwPP4JP+LfEBA3bvP8rU9CLuhp7jWvq9sxEyhjQFTFdrqi+/dHEin/pd8jpT0vcehIpnZtmEdcQ==}
|
||||
'@rollup/rollup-darwin-x64@4.64.3':
|
||||
resolution: {integrity: sha512-C3AyMtAnKQ/jSkZ0chIoWbPXRbFz8JnPLhl1ULecEhcaOGkT19xt7KmO8BulQxt9S/XideHRXvsVQUbGj4JCOg==}
|
||||
cpu: [x64]
|
||||
os: [darwin]
|
||||
|
||||
'@rollup/rollup-freebsd-arm64@4.63.5':
|
||||
resolution: {integrity: sha512-DrZbyCDF1hneuO6jRbvZ2D7+PIBM6yIwYnJpg2vIk58T+wuFpiaGZrfUr59lDWw45bg+IrpTGLPiNi/Fk4w3Cg==}
|
||||
'@rollup/rollup-freebsd-arm64@4.64.3':
|
||||
resolution: {integrity: sha512-M8yAdBMyHh14JLeXDNGvEkljzuHdcoLBQ399O4Fp4nvX51lueuEY/5U2G9Ci0D6/bQlBp/3JLiBQH55STwwbwg==}
|
||||
cpu: [arm64]
|
||||
os: [freebsd]
|
||||
|
||||
'@rollup/rollup-freebsd-x64@4.63.5':
|
||||
resolution: {integrity: sha512-gqfUVMJMB3mehqywxp6hTBFfgtMQykZY19+cfiaYP0toIJLb/1DZRJHVkQQGP13W4TAwfZDWeg1qBcheTRioXQ==}
|
||||
'@rollup/rollup-freebsd-x64@4.64.3':
|
||||
resolution: {integrity: sha512-WQjuOKGh2FN98ZK6i0uM6M6YqnPEds85p3oRkrxiEeTQo/p7RpuU8c9MwQ88LfgwVAzohumlzbQQSikz4Zw9rw==}
|
||||
cpu: [x64]
|
||||
os: [freebsd]
|
||||
|
||||
'@rollup/rollup-linux-arm-gnueabihf@4.63.5':
|
||||
resolution: {integrity: sha512-CFmhpvAwzSaWMlN3VN7UtmoTihlZNzoP0juQib5TQRnYUyDV8dXeWOp29sobWAT6gXl/hQgAClLlEiYozQG3OQ==}
|
||||
'@rollup/rollup-linux-arm-gnueabihf@4.64.3':
|
||||
resolution: {integrity: sha512-5tDLZG5s9WyVKoEHgwos6FpKrgpotl61EYeHSLSVkQzMzTsaClwEr8mok9rm3y734zjiGInG4dfw/56ebWVvEQ==}
|
||||
cpu: [arm]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@rollup/rollup-linux-arm-musleabihf@4.63.5':
|
||||
resolution: {integrity: sha512-Uc9H8eXCOayV6JLTH5bXKMId6qbhNHa818/BgYjm4jrlq3vZquC9cqyvHBw17xy5Mnj5f+I3gFK5JcEf3hSqrw==}
|
||||
'@rollup/rollup-linux-arm-musleabihf@4.64.3':
|
||||
resolution: {integrity: sha512-Mh5KbLeplM2HX6lQI18jsxSsqvPW8d+Hyk8HtY6Zl93woMitRSCZ92rBEzq6v8bfZGlpyMgj4P/sKW4KCjhH3A==}
|
||||
cpu: [arm]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
'@rollup/rollup-linux-arm64-gnu@4.63.5':
|
||||
resolution: {integrity: sha512-VcPr/szv/1BFw112Kt//fxulXt/JPqzzidU84iW68L2DdjnOO8QFUv2zTSYBEPHD6movBD4z+bbr5y60GYM7Jw==}
|
||||
'@rollup/rollup-linux-arm64-gnu@4.64.3':
|
||||
resolution: {integrity: sha512-QZrrwHnznTv6y4iiHtCqDN+ZsF4qNFQObMOLgPrbxhh9O5o4rgoUe5OnkqG/ceCObMrAjhQt+eQ6HSDzBFgVMw==}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@rollup/rollup-linux-arm64-musl@4.63.5':
|
||||
resolution: {integrity: sha512-BnxtJ5/91BrIHYIkGrmjz/lbMhqEHt1dPFqIxIFR+jPn0xVc/oUSCtIT089zfp5ufwGDlYz2UC+Fe1SRBpYFbQ==}
|
||||
'@rollup/rollup-linux-arm64-musl@4.64.3':
|
||||
resolution: {integrity: sha512-4vLehpXSwlkltnvk5ETB+Hz+d3xayZS3NamZ5Vq4bSo+IUoIALovEJF1YgWPMjHwN7R5o/R4BpP9h3q8EK1xAA==}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
'@rollup/rollup-linux-loong64-gnu@4.63.5':
|
||||
resolution: {integrity: sha512-LrYcHZwF+fAMNKHYTOQ5osWM4AZF7YF6D+XtsjDyEvljtt11twc+zHVXBLNEjxVSUnKYsOhvVz4Z213eW02COQ==}
|
||||
'@rollup/rollup-linux-loong64-gnu@4.64.3':
|
||||
resolution: {integrity: sha512-SEN7TTTOdXJKZmsWKOnQeVfgy3Dje/08ISXWwFhTggQ3SxjuIaChvmOB5ZDZqJNlBjaXrV6nAHraqEAvdLGqcw==}
|
||||
cpu: [loong64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@rollup/rollup-linux-loong64-musl@4.63.5':
|
||||
resolution: {integrity: sha512-nj7QKQePAAUpCpJHtg0pR0W/b92A9NO17JS3BAQmHDn/yhmkir2p8llrKY9TOhleKIaSzy1JhxS3T9FVld6coA==}
|
||||
'@rollup/rollup-linux-loong64-musl@4.64.3':
|
||||
resolution: {integrity: sha512-1LDsQMRMCEdGF9Dih5qyh6fF1LK3NnA+pAtE2+9AgZwUdXyd19TOBVC9crSB63iZBHR4vOG7h8gN/LcUH3J7xw==}
|
||||
cpu: [loong64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
'@rollup/rollup-linux-ppc64-gnu@4.63.5':
|
||||
resolution: {integrity: sha512-5ylkX6dWMeBKge9nTU+Rxfb+ZfaCIJ9lRqIFaK0eAMcWp7OJbYnLveLgXmm0VrvuLKb8qIK+mHyH0qu88RM+iA==}
|
||||
'@rollup/rollup-linux-ppc64-gnu@4.64.3':
|
||||
resolution: {integrity: sha512-v3D2tAfRriqGOi+5Ac5CiYbBo2NXtLVxJlludTvUZRj502lVMFeVIuvvWbz4qCrotn3e3Uvpvh7FcMpG2QxxXQ==}
|
||||
cpu: [ppc64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@rollup/rollup-linux-ppc64-musl@4.63.5':
|
||||
resolution: {integrity: sha512-oHK4ZHYFDKjZviK34I+NwgfbGxgI7ztrNxj2hPTSSNFgeq1a/lEd7dHV2fdGAuTH4Iym3RHJg+vAbWaWG4B7Zg==}
|
||||
'@rollup/rollup-linux-ppc64-musl@4.64.3':
|
||||
resolution: {integrity: sha512-db5q6dP189V2+JkMpsXBl8ym6bC0x8ydZ47fUJZ9YdM6T2OF5uqNzX9HaAZHRP/lqjA9bJeNwi0qw1/2/37dUA==}
|
||||
cpu: [ppc64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
'@rollup/rollup-linux-riscv64-gnu@4.63.5':
|
||||
resolution: {integrity: sha512-UcetmHZ6XOXuUByiKZyQmb55ZPr0LABr3Ec/HB9wKZn6CEAFWZkE+hsJErJ9hbPBC7nI0dKuELx7CoV6IM7TMg==}
|
||||
'@rollup/rollup-linux-riscv64-gnu@4.64.3':
|
||||
resolution: {integrity: sha512-nArAVDmrL5ix9UoipcKEsYJw4kMUQCIPYRWuYF2POOC7jd9rmN95d/48ewsYFC/OdtwEx6VYYWYxrp8FbMOU2Q==}
|
||||
cpu: [riscv64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@rollup/rollup-linux-riscv64-musl@4.63.5':
|
||||
resolution: {integrity: sha512-C5CmDPQBtvjVo8cgQsBs+w6WB0JLkiixhgi6hVLV11hERWdn/p0XcPU2OUcZzac9BPOFq7SbaHFa8r3SWEysCQ==}
|
||||
'@rollup/rollup-linux-riscv64-musl@4.64.3':
|
||||
resolution: {integrity: sha512-2dc7LElu7EVSvpQT8epNnBLa02wlqilNYqHN0FA/RaKqCr9U9ZFYQ0Eh4sfgSzLneLDuJsJRjcezR/l2UyYGXA==}
|
||||
cpu: [riscv64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
'@rollup/rollup-linux-s390x-gnu@4.63.5':
|
||||
resolution: {integrity: sha512-lHVQHJFKsuuxLMi3MQO9XVL8Tje3JR82CzB+QDKC5NWBcsIWuwsn9uIM5e3lBhI+fF1/s63qnyYqsg65+8rV/w==}
|
||||
'@rollup/rollup-linux-s390x-gnu@4.64.3':
|
||||
resolution: {integrity: sha512-9PYq2IEzSwXcvQLERcT9LKXRzw8J05GFTbJHA2anJR2WmlRjA1esgXVYhanp+bmjKsDVoZeU2L7Z2pRClLcZTw==}
|
||||
cpu: [s390x]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@rollup/rollup-linux-x64-gnu@4.63.5':
|
||||
resolution: {integrity: sha512-3W9bTFcQNJn71cSJVM9RKIiZOy8DO/XLDii8Uv/Pm6WKqDRj7JV3ZfuXIEfyuy5LXpIzAbB/1M4Ukp9GKNa7nA==}
|
||||
'@rollup/rollup-linux-x64-gnu@4.64.3':
|
||||
resolution: {integrity: sha512-vMh+mt0cw1BHldd8cqaU6A5s1W4EUKl6+8MRsv86mj954L4xyruKD7qPXMUI2icImadAZARK10h0onaGTLmCVg==}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@rollup/rollup-linux-x64-musl@4.63.5':
|
||||
resolution: {integrity: sha512-VDC7rRJlee/scpki96GZ27Omf6yU87s1YXwVTpjE5841faVlDYYT565rgfmoR1U0sqL7z5ivQSDjcsF6VRXyBA==}
|
||||
'@rollup/rollup-linux-x64-musl@4.64.3':
|
||||
resolution: {integrity: sha512-PpiKU+sWbqDGc0U0su8oXiyeyqm0UQjmaaPKHktX+DaLe6YKJwW2YVD0f2AXHdVvblZbRqpwRVClWP2F5HCqrQ==}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
'@rollup/rollup-openbsd-x64@4.63.5':
|
||||
resolution: {integrity: sha512-z86Ok2p4pTdv5xqCKZsTooO7yBEiaJR/HzU3Wx8RmWsPoLppnMKROhJusQob8B3IE1ghC343kUW9rC2r+Wf3ig==}
|
||||
'@rollup/rollup-openbsd-x64@4.64.3':
|
||||
resolution: {integrity: sha512-/3enY/AmaIt8IS0fWjX4ztYVPBqkj49cQTy/hCryhBFaEzDfJwwEolq9sxvyWAM2qmsmVuPdeRg5JXSrmeqvJg==}
|
||||
cpu: [x64]
|
||||
os: [openbsd]
|
||||
|
||||
'@rollup/rollup-openharmony-arm64@4.63.5':
|
||||
resolution: {integrity: sha512-IzQmj+xXwQFGhMAMKMQVXkMwMZN3TqkJgAE0nSsqvVwWWciP4AIPMmWRqOQ2GfX7TUDZr+xqGFcBS36CRPGw0g==}
|
||||
'@rollup/rollup-openharmony-arm64@4.64.3':
|
||||
resolution: {integrity: sha512-zsEP/QDuet52hhwvi5T1VPg0/Tkms0m3zkVv7CPsznvhdO4pL4oJTMz26EJsiCe18OeLKPgpTu3XVO3C1eq3ew==}
|
||||
cpu: [arm64]
|
||||
os: [openharmony]
|
||||
|
||||
'@rollup/rollup-win32-arm64-msvc@4.63.5':
|
||||
resolution: {integrity: sha512-F6qpTaPc9bwBH85kjy0/BLmLSW1uv7AoOXCoRIkg2arlgCYlWYcAbiMkvZuAcaWk9TpCRG//okznLAqLGshkMw==}
|
||||
'@rollup/rollup-win32-arm64-msvc@4.64.3':
|
||||
resolution: {integrity: sha512-4ESknW4Db+kqvazVOfsReYJaHxcIsOqcgwjKV2J1tG5Bn4RVf5ocTQKSGuq2HWBJcOLuyim8IWX8DuH6nKmELA==}
|
||||
cpu: [arm64]
|
||||
os: [win32]
|
||||
|
||||
'@rollup/rollup-win32-ia32-msvc@4.63.5':
|
||||
resolution: {integrity: sha512-igoDsTFhhwECBeGbUuLeIk7t8Y1apa+cs6mDWpx2EZ0ch7oEQgzHbFUXN9euoHekCAQzXdXApAGkV6jznS7tWw==}
|
||||
'@rollup/rollup-win32-ia32-msvc@4.64.3':
|
||||
resolution: {integrity: sha512-3B8VCQyIyv7FQxYYhb3HpR3bIpronR20txjMSffSoz/sm/DDs3/49CFpEueOsvq9qW9sWoI/6pi6mtciD9NXFg==}
|
||||
cpu: [ia32]
|
||||
os: [win32]
|
||||
|
||||
'@rollup/rollup-win32-x64-gnu@4.63.5':
|
||||
resolution: {integrity: sha512-U3teMeMbXFmaM5D+OTJpsOXd+wV/qftIeYF9kBKL4v73641qyJmoXFtA28DQLsnmlyayEsTe72xpLHrArq6vHw==}
|
||||
'@rollup/rollup-win32-x64-gnu@4.64.3':
|
||||
resolution: {integrity: sha512-YOoDkKh2oQxOxRB5D9AR8sj3qpMShpmyTkjojiBjMGigz3caV7UvCnXWWUQC0TBmrZ5zuHe2pWl8S+cMfvXzRg==}
|
||||
cpu: [x64]
|
||||
os: [win32]
|
||||
|
||||
'@rollup/rollup-win32-x64-msvc@4.63.5':
|
||||
resolution: {integrity: sha512-ypfC34F3RKXvCXBglGqGMsUSMKlgwd1HX9AOAlx9RoZZ6GaI42YHVeKpzg3JG+wpBUJYTG+NNZhqbDWL8tBZkw==}
|
||||
'@rollup/rollup-win32-x64-msvc@4.64.3':
|
||||
resolution: {integrity: sha512-qTv4bG+eKJKifzxNJevMUh8L6CjJrz9/Kn1IbP2F5bo0tBzNIR7HMg4vni6XmDzcKUl00lHHY+J5ZgjbGiujOQ==}
|
||||
cpu: [x64]
|
||||
os: [win32]
|
||||
|
||||
@@ -735,63 +719,63 @@ packages:
|
||||
'@types/node@20.19.43':
|
||||
resolution: {integrity: sha512-6oYBAi5ikg4Pl+kGsoYtawUMBT2zZMCvPNF7pVLnHZfd1zf38DRiWn/gT01RYCdUqkv7Fhr+C9ot4/tb+2sVvA==}
|
||||
|
||||
'@typescript-eslint/eslint-plugin@8.70.1':
|
||||
resolution: {integrity: sha512-nDNrUQ/4ruSNYbu749TRY7cfrzPtoLHEXSNBI8aaNY32LlZCajixqRf3FqcKC4p5Cam4VOHYx/t+i5+nKXvrqA==}
|
||||
'@typescript-eslint/eslint-plugin@8.71.0':
|
||||
resolution: {integrity: sha512-pqcS9c1HxZTHt7End4nXqd0s5lJrrFzrgCkKFJrsbUnaL6M3+6oBFZaslg6Gjsl3argl2DDRFROnXARaZ2e4Nw==}
|
||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||
peerDependencies:
|
||||
'@typescript-eslint/parser': ^8.70.1
|
||||
'@typescript-eslint/parser': ^8.71.0
|
||||
eslint: ^8.57.0 || ^9.0.0 || ^10.0.0
|
||||
typescript: '>=4.8.4 <6.1.0'
|
||||
|
||||
'@typescript-eslint/parser@8.70.1':
|
||||
resolution: {integrity: sha512-nO974WLllwhSFWQXnMLj6nDGa8f0khKEz1JzpPJ1u7Vm/4X1X6ZHajpoknU4bb41vJyMB0HHVyS2GqdhWfIXZw==}
|
||||
'@typescript-eslint/parser@8.71.0':
|
||||
resolution: {integrity: sha512-CG4nPk1f2zc8yw4pALqHsFYH2hdo+h1T9daSp21+Hnxi9LOE3GT9hAfTKJCBXVNM2GmYs1eMEP615wPoeOgk3A==}
|
||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||
peerDependencies:
|
||||
eslint: ^8.57.0 || ^9.0.0 || ^10.0.0
|
||||
typescript: '>=4.8.4 <6.1.0'
|
||||
|
||||
'@typescript-eslint/project-service@8.70.1':
|
||||
resolution: {integrity: sha512-62xOgboPfwc3/IgPSX/W6oQR3ZbF04194FPGUGH8HL8iLFHbt/456/8Ph1wLNUgVF+s94FlHoipBsz+v7+LMnA==}
|
||||
'@typescript-eslint/project-service@8.71.0':
|
||||
resolution: {integrity: sha512-aABjw5rjBacYONVPaPiWOCjJu0vEF4a25iQuodlmQYL1trtLZ0X/y+2Vzl3BKI1odM4LnwLE1oUDXYp1wzx1TQ==}
|
||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||
peerDependencies:
|
||||
typescript: '>=4.8.4 <6.1.0'
|
||||
|
||||
'@typescript-eslint/scope-manager@8.70.1':
|
||||
resolution: {integrity: sha512-Pa0EeSeAusQc1WbjQMac+YfenewYTBu0KjgYvkUKwhXaHUKbFog23Dm/rp0DX/6tyYOQ3Xl1a+3EcFNZynGHCw==}
|
||||
'@typescript-eslint/scope-manager@8.71.0':
|
||||
resolution: {integrity: sha512-gWF0BhUcnjZxSpLE8ngS/59n2SB0J3YqRxvX1+2aoRJk9hNtHSLOV+TcarFiOr5ipXm3yc1QrI4c9YZc8zyCxw==}
|
||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||
|
||||
'@typescript-eslint/tsconfig-utils@8.70.1':
|
||||
resolution: {integrity: sha512-jumze1fPI+sDOaM2TWGQdn39PDxTr7TZGeuyLkAbNyx2vtMT3uRnVKChN0hfht5V2TugphJzF6bYXvBcE09qqg==}
|
||||
'@typescript-eslint/tsconfig-utils@8.71.0':
|
||||
resolution: {integrity: sha512-Z1UlWHADEK2Mlb9NpWfDeSjqoZ5EyrOv4R3eQpbkzqn/EwaIdOpXXupEA1+0ZIOSJSZZDBHG0BrQyN8zUG6Pwg==}
|
||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||
peerDependencies:
|
||||
typescript: '>=4.8.4 <6.1.0'
|
||||
|
||||
'@typescript-eslint/type-utils@8.70.1':
|
||||
resolution: {integrity: sha512-7zKTnyvaVWqzLZHPFQtX1hVHqgkMC+WebPWakNCSyrQVbIP1AM0L0TlBZtACldIRb6PptI8Odk+jyZ5kP3B1VA==}
|
||||
'@typescript-eslint/type-utils@8.71.0':
|
||||
resolution: {integrity: sha512-i8uO1qbdxeKgRnS5sCRt6On3/nfo2d2DwQe3Yvjx543zLy7r8ySqRuPPiIIXAhS03U0v5NfAFx+rUgxFzKKwNw==}
|
||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||
peerDependencies:
|
||||
eslint: ^8.57.0 || ^9.0.0 || ^10.0.0
|
||||
typescript: '>=4.8.4 <6.1.0'
|
||||
|
||||
'@typescript-eslint/types@8.70.1':
|
||||
resolution: {integrity: sha512-Dm1ypdhhrGCTyyehxElhgJ6kgk8MVCv5qXdoOVqPr1uqk42jX8KjrZqhROvdShczA8qrDoYiOWn1ykWlx2k81Q==}
|
||||
'@typescript-eslint/types@8.71.0':
|
||||
resolution: {integrity: sha512-cJ4OoxPGWvFnBTnSZyaU+qJzGTqPTGJY+gDchj6cRyLRdmIdt4rcsE4twj+zPfrNiWuVi38wijHzShL++Z9atQ==}
|
||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||
|
||||
'@typescript-eslint/typescript-estree@8.70.1':
|
||||
resolution: {integrity: sha512-TU8PwyGN0PQJUcE96mw8eCQ44SmxGdQlJmlWakHaHQ15eIuuvye5yNtmh/i6oS88jzXVQB71xdNkbkB/fMwL0g==}
|
||||
'@typescript-eslint/typescript-estree@8.71.0':
|
||||
resolution: {integrity: sha512-PEEF4G5sLLWAS5BpPrUvms4ySZkiBQQZM4z+3ReI46axK5Vqr/vXBQatJQIZZOYdGyPUAKTtsrWzpqKuU+3DEw==}
|
||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||
peerDependencies:
|
||||
typescript: '>=4.8.4 <6.1.0'
|
||||
|
||||
'@typescript-eslint/utils@8.70.1':
|
||||
resolution: {integrity: sha512-Esgul8MsnKnRLdYU2Eb2cRV9bS5HJYtKj1ByJnOzzG2M58DGdSUQ1jUuILxipqcpB2h9WLrbD5GijIWUjX/Tqw==}
|
||||
'@typescript-eslint/utils@8.71.0':
|
||||
resolution: {integrity: sha512-pKR/tEMVrXZG23UFKUn5BQf3zfmfk7KQceI2cGzywZ5nxM5Eu3hEJU1utjWzydtzBbcJAQhHN8iPCxobHpPcZQ==}
|
||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||
peerDependencies:
|
||||
eslint: ^8.57.0 || ^9.0.0 || ^10.0.0
|
||||
typescript: '>=4.8.4 <6.1.0'
|
||||
|
||||
'@typescript-eslint/visitor-keys@8.70.1':
|
||||
resolution: {integrity: sha512-Vwj9lUIW5Xq3wQ9w6gv3R86g1hMK8f2zNOdGTAgeXUMMXFK78G9ruCjjqutHMNJc0+CH7LYRnHeUB9IT8wFmcw==}
|
||||
'@typescript-eslint/visitor-keys@8.71.0':
|
||||
resolution: {integrity: sha512-8eQ9R218XORK+KLosnf4bu/QsUXvUyVwTbArg7/0NMB1Pu87OJKvj4nhFblkYE8gQV73mW1dx1ptlPCkwRGa7A==}
|
||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||
|
||||
'@vitest/expect@4.1.11':
|
||||
@@ -941,8 +925,8 @@ packages:
|
||||
resolution: {integrity: sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==}
|
||||
engines: {node: ^20.19.0 || ^22.13.0 || >=24}
|
||||
|
||||
eslint@10.11.0:
|
||||
resolution: {integrity: sha512-P7a6UEEqb9G95MYAtqkmsTbVXIYyzIfl6NGOIJk162PaahFxFyeGcrlXYFSiagECg4sEm8IseJdZBKR3rx6MsQ==}
|
||||
eslint@10.12.0:
|
||||
resolution: {integrity: sha512-npHjrHb2o4ehH3mE+YuxABR+0gyb0aVWCIolgJDArwCiHEIsonBm3ZKjel5b0U5+a+MbEvIyRCGCzD3eKKK2NA==}
|
||||
engines: {node: ^20.19.0 || ^22.13.0 || >=24}
|
||||
hasBin: true
|
||||
peerDependencies:
|
||||
@@ -1165,8 +1149,8 @@ packages:
|
||||
resolution: {integrity: sha512-dkEJPVvun4FryqBmZ5KhDo0K9iDXAwn08tMLDinNdRBNPcYEDiWYysLcc6k3mjTMlbP9KyylvRpd4wFtwrT9rw==}
|
||||
engines: {node: ^20.17.0 || >=22.9.0}
|
||||
|
||||
nanoid@3.3.19:
|
||||
resolution: {integrity: sha512-Y2tUNy4ouw6tq5oDSKeQYGOyhkUBhNOcGV/02KC+6kd9eDGqdZd++mjMiIDilrBYvjEnCYvVtsuHCuP+okSfug==}
|
||||
nanoid@3.3.20:
|
||||
resolution: {integrity: sha512-uKdg2G3GNCKQn9byYOpxbGqrT2fGO5KRt5J/8b3pok8rT6qxGWF6hxMyJiEYtAf+FVyYuD9hRaDqX5uPFYJ4ZQ==}
|
||||
engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
|
||||
hasBin: true
|
||||
|
||||
@@ -1226,8 +1210,8 @@ packages:
|
||||
resolution: {integrity: sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
postcss@8.5.28:
|
||||
resolution: {integrity: sha512-RRuzqDtt5Y9h3quz5hWhK+TPnsmVs6WwSU6LkJMeY4HstUEDuYTG8UJSdawMRzmzAtV+KEoG8N3Qg2qLy5vM/A==}
|
||||
postcss@8.5.29:
|
||||
resolution: {integrity: sha512-49cGhUbXj8Qenv0iTMxA1cFBzxXoctpC9Ujd77t1WcbJIr6nF/eI7g/8MgxrYldFRuAXvja7xQRwavoW7kgrxQ==}
|
||||
engines: {node: ^10 || ^12 || >=14}
|
||||
|
||||
prelude-ls@1.2.1:
|
||||
@@ -1253,8 +1237,8 @@ packages:
|
||||
resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==}
|
||||
engines: {iojs: '>=1.0.0', node: '>=0.10.0'}
|
||||
|
||||
rollup@4.63.5:
|
||||
resolution: {integrity: sha512-KRWwmNLlPw5M7HcdYfm15oBv9n9LPtjzpzCIxS/phwqvPyxHSoKX6Y2YU3pxSPfy0CLquVgsx/j/hBi6OvH1Nw==}
|
||||
rollup@4.64.3:
|
||||
resolution: {integrity: sha512-iIMWbptLYsZdGTJMH8ngApXdsCatE6bCMv2eqGIfGE1++kwh7TCWBdWKK0FKC0yx++196vGK5VdagzRXSLMa8A==}
|
||||
engines: {node: '>=18.0.0', npm: '>=8.0.0'}
|
||||
hasBin: true
|
||||
|
||||
@@ -1277,8 +1261,8 @@ packages:
|
||||
resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
shell-quote@1.10.0:
|
||||
resolution: {integrity: sha512-w1aiOKwKuRgtwAReIIj89puqg+I7GvX4IbLrvmhXbzQsj1+Zwi4VO3+fa6ZF91TWSjIxoEkKnMeHcLEODK5ZXA==}
|
||||
shell-quote@1.12.0:
|
||||
resolution: {integrity: sha512-PcByqNyT/38F2kDNi006HAMRJaULuBzq/FOsw3qdZvX/GA9W/jamDaRskgHjubHiftXK5sIFxLNkvrXUwcof6Q==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
siginfo@2.0.0:
|
||||
@@ -1299,8 +1283,8 @@ packages:
|
||||
resolution: {integrity: sha512-hpd+HLON7HdZXqYchMM/+LaTTbdK0AU3NngIJ4KVyWbY9bfQqdL9cD+4yf6dUoU2Ap4VsU0JkQi6FxAI1B2mXQ==}
|
||||
engines: {node: '>= 18'}
|
||||
|
||||
source-map-js@1.2.1:
|
||||
resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==}
|
||||
source-map-js@1.2.2:
|
||||
resolution: {integrity: sha512-KGj/8Y43x35aZVDtt+J4mK1hoLGHULMYfSkODJNQjNDC3oW1PqPoxMwo0pLUsWM/UEGzON/NxeHywEfNXNP3Vw==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
stackback@0.0.2:
|
||||
@@ -1358,8 +1342,8 @@ packages:
|
||||
resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==}
|
||||
engines: {node: '>= 0.8.0'}
|
||||
|
||||
typescript-eslint@8.70.1:
|
||||
resolution: {integrity: sha512-AcWG7KDjZ2THNXsgwttMaGmzVi0VFRlFYfqFHYQRbDpF3owuYbuiL8c7UUrd2k8s3PoSfIQrWfrGXfcElrWLYA==}
|
||||
typescript-eslint@8.71.0:
|
||||
resolution: {integrity: sha512-fBdHYiqQ14RW6mOMXD14Svn82ZsCYAoQSzGRzyEjR59S5A2Krh/l7fGTOQ7iCr8gGy/mHVXtEF7s5fgjEdV0Pw==}
|
||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||
peerDependencies:
|
||||
eslint: ^8.57.0 || ^9.0.0 || ^10.0.0
|
||||
@@ -1704,9 +1688,9 @@ snapshots:
|
||||
'@esbuild/win32-x64@0.28.2':
|
||||
optional: true
|
||||
|
||||
'@eslint-community/eslint-utils@4.10.1(eslint@10.11.0)':
|
||||
'@eslint-community/eslint-utils@4.10.1(eslint@10.12.0)':
|
||||
dependencies:
|
||||
eslint: 10.11.0
|
||||
eslint: 10.12.0
|
||||
eslint-visitor-keys: 3.4.3
|
||||
|
||||
'@eslint-community/regexpp@4.12.2': {}
|
||||
@@ -1750,31 +1734,29 @@ snapshots:
|
||||
|
||||
'@humanwhocodes/retry@0.4.3': {}
|
||||
|
||||
'@inquirer/ansi@2.0.7': {}
|
||||
|
||||
'@inquirer/ansi@2.0.8': {}
|
||||
'@inquirer/ansi@2.0.9': {}
|
||||
|
||||
'@inquirer/checkbox@5.2.1(@types/node@20.19.43)':
|
||||
dependencies:
|
||||
'@inquirer/ansi': 2.0.7
|
||||
'@inquirer/ansi': 2.0.9
|
||||
'@inquirer/core': 11.2.1(@types/node@20.19.43)
|
||||
'@inquirer/figures': 2.0.7
|
||||
'@inquirer/type': 4.0.7(@types/node@20.19.43)
|
||||
'@inquirer/figures': 2.0.9
|
||||
'@inquirer/type': 4.2.0(@types/node@20.19.43)
|
||||
optionalDependencies:
|
||||
'@types/node': 20.19.43
|
||||
|
||||
'@inquirer/confirm@6.1.1(@types/node@20.19.43)':
|
||||
dependencies:
|
||||
'@inquirer/core': 11.2.1(@types/node@20.19.43)
|
||||
'@inquirer/type': 4.0.7(@types/node@20.19.43)
|
||||
'@inquirer/type': 4.2.0(@types/node@20.19.43)
|
||||
optionalDependencies:
|
||||
'@types/node': 20.19.43
|
||||
|
||||
'@inquirer/core@11.2.1(@types/node@20.19.43)':
|
||||
dependencies:
|
||||
'@inquirer/ansi': 2.0.7
|
||||
'@inquirer/figures': 2.0.7
|
||||
'@inquirer/type': 4.0.7(@types/node@20.19.43)
|
||||
'@inquirer/ansi': 2.0.9
|
||||
'@inquirer/figures': 2.0.9
|
||||
'@inquirer/type': 4.2.0(@types/node@20.19.43)
|
||||
cli-width: 4.1.0
|
||||
fast-wrap-ansi: 0.2.2
|
||||
mute-stream: 3.0.0
|
||||
@@ -1782,11 +1764,11 @@ snapshots:
|
||||
optionalDependencies:
|
||||
'@types/node': 20.19.43
|
||||
|
||||
'@inquirer/core@12.0.3(@types/node@20.19.43)':
|
||||
'@inquirer/core@12.0.4(@types/node@20.19.43)':
|
||||
dependencies:
|
||||
'@inquirer/ansi': 2.0.8
|
||||
'@inquirer/ansi': 2.0.9
|
||||
'@inquirer/figures': 2.0.9
|
||||
'@inquirer/type': 4.1.1(@types/node@20.19.43)
|
||||
'@inquirer/type': 4.2.0(@types/node@20.19.43)
|
||||
cli-width: 4.1.0
|
||||
fast-wrap-ansi: 0.2.2
|
||||
mute-stream: 3.0.0
|
||||
@@ -1798,14 +1780,14 @@ snapshots:
|
||||
dependencies:
|
||||
'@inquirer/core': 11.2.1(@types/node@20.19.43)
|
||||
'@inquirer/external-editor': 3.0.3(@types/node@20.19.43)
|
||||
'@inquirer/type': 4.0.7(@types/node@20.19.43)
|
||||
'@inquirer/type': 4.2.0(@types/node@20.19.43)
|
||||
optionalDependencies:
|
||||
'@types/node': 20.19.43
|
||||
|
||||
'@inquirer/expand@5.1.1(@types/node@20.19.43)':
|
||||
dependencies:
|
||||
'@inquirer/core': 11.2.1(@types/node@20.19.43)
|
||||
'@inquirer/type': 4.0.7(@types/node@20.19.43)
|
||||
'@inquirer/type': 4.2.0(@types/node@20.19.43)
|
||||
optionalDependencies:
|
||||
'@types/node': 20.19.43
|
||||
|
||||
@@ -1816,29 +1798,27 @@ snapshots:
|
||||
optionalDependencies:
|
||||
'@types/node': 20.19.43
|
||||
|
||||
'@inquirer/figures@2.0.7': {}
|
||||
|
||||
'@inquirer/figures@2.0.9': {}
|
||||
|
||||
'@inquirer/input@5.1.2(@types/node@20.19.43)':
|
||||
dependencies:
|
||||
'@inquirer/core': 11.2.1(@types/node@20.19.43)
|
||||
'@inquirer/type': 4.0.7(@types/node@20.19.43)
|
||||
'@inquirer/type': 4.2.0(@types/node@20.19.43)
|
||||
optionalDependencies:
|
||||
'@types/node': 20.19.43
|
||||
|
||||
'@inquirer/number@4.1.1(@types/node@20.19.43)':
|
||||
dependencies:
|
||||
'@inquirer/core': 11.2.1(@types/node@20.19.43)
|
||||
'@inquirer/type': 4.0.7(@types/node@20.19.43)
|
||||
'@inquirer/type': 4.2.0(@types/node@20.19.43)
|
||||
optionalDependencies:
|
||||
'@types/node': 20.19.43
|
||||
|
||||
'@inquirer/password@5.1.1(@types/node@20.19.43)':
|
||||
dependencies:
|
||||
'@inquirer/ansi': 2.0.7
|
||||
'@inquirer/ansi': 2.0.9
|
||||
'@inquirer/core': 11.2.1(@types/node@20.19.43)
|
||||
'@inquirer/type': 4.0.7(@types/node@20.19.43)
|
||||
'@inquirer/type': 4.2.0(@types/node@20.19.43)
|
||||
optionalDependencies:
|
||||
'@types/node': 20.19.43
|
||||
|
||||
@@ -1860,32 +1840,28 @@ snapshots:
|
||||
'@inquirer/rawlist@5.3.1(@types/node@20.19.43)':
|
||||
dependencies:
|
||||
'@inquirer/core': 11.2.1(@types/node@20.19.43)
|
||||
'@inquirer/type': 4.0.7(@types/node@20.19.43)
|
||||
'@inquirer/type': 4.2.0(@types/node@20.19.43)
|
||||
optionalDependencies:
|
||||
'@types/node': 20.19.43
|
||||
|
||||
'@inquirer/search@4.2.1(@types/node@20.19.43)':
|
||||
dependencies:
|
||||
'@inquirer/core': 11.2.1(@types/node@20.19.43)
|
||||
'@inquirer/figures': 2.0.7
|
||||
'@inquirer/type': 4.0.7(@types/node@20.19.43)
|
||||
'@inquirer/figures': 2.0.9
|
||||
'@inquirer/type': 4.2.0(@types/node@20.19.43)
|
||||
optionalDependencies:
|
||||
'@types/node': 20.19.43
|
||||
|
||||
'@inquirer/select@5.2.1(@types/node@20.19.43)':
|
||||
dependencies:
|
||||
'@inquirer/ansi': 2.0.7
|
||||
'@inquirer/ansi': 2.0.9
|
||||
'@inquirer/core': 11.2.1(@types/node@20.19.43)
|
||||
'@inquirer/figures': 2.0.7
|
||||
'@inquirer/type': 4.0.7(@types/node@20.19.43)
|
||||
'@inquirer/figures': 2.0.9
|
||||
'@inquirer/type': 4.2.0(@types/node@20.19.43)
|
||||
optionalDependencies:
|
||||
'@types/node': 20.19.43
|
||||
|
||||
'@inquirer/type@4.0.7(@types/node@20.19.43)':
|
||||
optionalDependencies:
|
||||
'@types/node': 20.19.43
|
||||
|
||||
'@inquirer/type@4.1.1(@types/node@20.19.43)':
|
||||
'@inquirer/type@4.2.0(@types/node@20.19.43)':
|
||||
optionalDependencies:
|
||||
'@types/node': 20.19.43
|
||||
|
||||
@@ -1933,79 +1909,79 @@ snapshots:
|
||||
|
||||
'@polka/url@1.0.0-next.29': {}
|
||||
|
||||
'@rollup/rollup-android-arm-eabi@4.63.5':
|
||||
'@rollup/rollup-android-arm-eabi@4.64.3':
|
||||
optional: true
|
||||
|
||||
'@rollup/rollup-android-arm64@4.63.5':
|
||||
'@rollup/rollup-android-arm64@4.64.3':
|
||||
optional: true
|
||||
|
||||
'@rollup/rollup-darwin-arm64@4.63.5':
|
||||
'@rollup/rollup-darwin-arm64@4.64.3':
|
||||
optional: true
|
||||
|
||||
'@rollup/rollup-darwin-x64@4.63.5':
|
||||
'@rollup/rollup-darwin-x64@4.64.3':
|
||||
optional: true
|
||||
|
||||
'@rollup/rollup-freebsd-arm64@4.63.5':
|
||||
'@rollup/rollup-freebsd-arm64@4.64.3':
|
||||
optional: true
|
||||
|
||||
'@rollup/rollup-freebsd-x64@4.63.5':
|
||||
'@rollup/rollup-freebsd-x64@4.64.3':
|
||||
optional: true
|
||||
|
||||
'@rollup/rollup-linux-arm-gnueabihf@4.63.5':
|
||||
'@rollup/rollup-linux-arm-gnueabihf@4.64.3':
|
||||
optional: true
|
||||
|
||||
'@rollup/rollup-linux-arm-musleabihf@4.63.5':
|
||||
'@rollup/rollup-linux-arm-musleabihf@4.64.3':
|
||||
optional: true
|
||||
|
||||
'@rollup/rollup-linux-arm64-gnu@4.63.5':
|
||||
'@rollup/rollup-linux-arm64-gnu@4.64.3':
|
||||
optional: true
|
||||
|
||||
'@rollup/rollup-linux-arm64-musl@4.63.5':
|
||||
'@rollup/rollup-linux-arm64-musl@4.64.3':
|
||||
optional: true
|
||||
|
||||
'@rollup/rollup-linux-loong64-gnu@4.63.5':
|
||||
'@rollup/rollup-linux-loong64-gnu@4.64.3':
|
||||
optional: true
|
||||
|
||||
'@rollup/rollup-linux-loong64-musl@4.63.5':
|
||||
'@rollup/rollup-linux-loong64-musl@4.64.3':
|
||||
optional: true
|
||||
|
||||
'@rollup/rollup-linux-ppc64-gnu@4.63.5':
|
||||
'@rollup/rollup-linux-ppc64-gnu@4.64.3':
|
||||
optional: true
|
||||
|
||||
'@rollup/rollup-linux-ppc64-musl@4.63.5':
|
||||
'@rollup/rollup-linux-ppc64-musl@4.64.3':
|
||||
optional: true
|
||||
|
||||
'@rollup/rollup-linux-riscv64-gnu@4.63.5':
|
||||
'@rollup/rollup-linux-riscv64-gnu@4.64.3':
|
||||
optional: true
|
||||
|
||||
'@rollup/rollup-linux-riscv64-musl@4.63.5':
|
||||
'@rollup/rollup-linux-riscv64-musl@4.64.3':
|
||||
optional: true
|
||||
|
||||
'@rollup/rollup-linux-s390x-gnu@4.63.5':
|
||||
'@rollup/rollup-linux-s390x-gnu@4.64.3':
|
||||
optional: true
|
||||
|
||||
'@rollup/rollup-linux-x64-gnu@4.63.5':
|
||||
'@rollup/rollup-linux-x64-gnu@4.64.3':
|
||||
optional: true
|
||||
|
||||
'@rollup/rollup-linux-x64-musl@4.63.5':
|
||||
'@rollup/rollup-linux-x64-musl@4.64.3':
|
||||
optional: true
|
||||
|
||||
'@rollup/rollup-openbsd-x64@4.63.5':
|
||||
'@rollup/rollup-openbsd-x64@4.64.3':
|
||||
optional: true
|
||||
|
||||
'@rollup/rollup-openharmony-arm64@4.63.5':
|
||||
'@rollup/rollup-openharmony-arm64@4.64.3':
|
||||
optional: true
|
||||
|
||||
'@rollup/rollup-win32-arm64-msvc@4.63.5':
|
||||
'@rollup/rollup-win32-arm64-msvc@4.64.3':
|
||||
optional: true
|
||||
|
||||
'@rollup/rollup-win32-ia32-msvc@4.63.5':
|
||||
'@rollup/rollup-win32-ia32-msvc@4.64.3':
|
||||
optional: true
|
||||
|
||||
'@rollup/rollup-win32-x64-gnu@4.63.5':
|
||||
'@rollup/rollup-win32-x64-gnu@4.64.3':
|
||||
optional: true
|
||||
|
||||
'@rollup/rollup-win32-x64-msvc@4.63.5':
|
||||
'@rollup/rollup-win32-x64-msvc@4.64.3':
|
||||
optional: true
|
||||
|
||||
'@standard-schema/spec@1.1.0': {}
|
||||
@@ -2026,15 +2002,15 @@ snapshots:
|
||||
dependencies:
|
||||
undici-types: 6.21.0
|
||||
|
||||
'@typescript-eslint/eslint-plugin@8.70.1(@typescript-eslint/parser@8.70.1(eslint@10.11.0)(typescript@6.0.3))(eslint@10.11.0)(typescript@6.0.3)':
|
||||
'@typescript-eslint/eslint-plugin@8.71.0(@typescript-eslint/parser@8.71.0(eslint@10.12.0)(typescript@6.0.3))(eslint@10.12.0)(typescript@6.0.3)':
|
||||
dependencies:
|
||||
'@eslint-community/regexpp': 4.12.2
|
||||
'@typescript-eslint/parser': 8.70.1(eslint@10.11.0)(typescript@6.0.3)
|
||||
'@typescript-eslint/scope-manager': 8.70.1
|
||||
'@typescript-eslint/type-utils': 8.70.1(eslint@10.11.0)(typescript@6.0.3)
|
||||
'@typescript-eslint/utils': 8.70.1(eslint@10.11.0)(typescript@6.0.3)
|
||||
'@typescript-eslint/visitor-keys': 8.70.1
|
||||
eslint: 10.11.0
|
||||
'@typescript-eslint/parser': 8.71.0(eslint@10.12.0)(typescript@6.0.3)
|
||||
'@typescript-eslint/scope-manager': 8.71.0
|
||||
'@typescript-eslint/type-utils': 8.71.0(eslint@10.12.0)(typescript@6.0.3)
|
||||
'@typescript-eslint/utils': 8.71.0(eslint@10.12.0)(typescript@6.0.3)
|
||||
'@typescript-eslint/visitor-keys': 8.71.0
|
||||
eslint: 10.12.0
|
||||
ignore: 7.0.10
|
||||
natural-compare: 1.4.0
|
||||
ts-api-utils: 2.5.0(typescript@6.0.3)
|
||||
@@ -2042,56 +2018,56 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@typescript-eslint/parser@8.70.1(eslint@10.11.0)(typescript@6.0.3)':
|
||||
'@typescript-eslint/parser@8.71.0(eslint@10.12.0)(typescript@6.0.3)':
|
||||
dependencies:
|
||||
'@typescript-eslint/scope-manager': 8.70.1
|
||||
'@typescript-eslint/types': 8.70.1
|
||||
'@typescript-eslint/typescript-estree': 8.70.1(typescript@6.0.3)
|
||||
'@typescript-eslint/visitor-keys': 8.70.1
|
||||
'@typescript-eslint/scope-manager': 8.71.0
|
||||
'@typescript-eslint/types': 8.71.0
|
||||
'@typescript-eslint/typescript-estree': 8.71.0(typescript@6.0.3)
|
||||
'@typescript-eslint/visitor-keys': 8.71.0
|
||||
debug: 4.4.3
|
||||
eslint: 10.11.0
|
||||
eslint: 10.12.0
|
||||
typescript: 6.0.3
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@typescript-eslint/project-service@8.70.1(typescript@6.0.3)':
|
||||
'@typescript-eslint/project-service@8.71.0(typescript@6.0.3)':
|
||||
dependencies:
|
||||
'@typescript-eslint/tsconfig-utils': 8.70.1(typescript@6.0.3)
|
||||
'@typescript-eslint/types': 8.70.1
|
||||
'@typescript-eslint/tsconfig-utils': 8.71.0(typescript@6.0.3)
|
||||
'@typescript-eslint/types': 8.71.0
|
||||
debug: 4.4.3
|
||||
typescript: 6.0.3
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@typescript-eslint/scope-manager@8.70.1':
|
||||
'@typescript-eslint/scope-manager@8.71.0':
|
||||
dependencies:
|
||||
'@typescript-eslint/types': 8.70.1
|
||||
'@typescript-eslint/visitor-keys': 8.70.1
|
||||
'@typescript-eslint/types': 8.71.0
|
||||
'@typescript-eslint/visitor-keys': 8.71.0
|
||||
|
||||
'@typescript-eslint/tsconfig-utils@8.70.1(typescript@6.0.3)':
|
||||
'@typescript-eslint/tsconfig-utils@8.71.0(typescript@6.0.3)':
|
||||
dependencies:
|
||||
typescript: 6.0.3
|
||||
|
||||
'@typescript-eslint/type-utils@8.70.1(eslint@10.11.0)(typescript@6.0.3)':
|
||||
'@typescript-eslint/type-utils@8.71.0(eslint@10.12.0)(typescript@6.0.3)':
|
||||
dependencies:
|
||||
'@typescript-eslint/types': 8.70.1
|
||||
'@typescript-eslint/typescript-estree': 8.70.1(typescript@6.0.3)
|
||||
'@typescript-eslint/utils': 8.70.1(eslint@10.11.0)(typescript@6.0.3)
|
||||
'@typescript-eslint/types': 8.71.0
|
||||
'@typescript-eslint/typescript-estree': 8.71.0(typescript@6.0.3)
|
||||
'@typescript-eslint/utils': 8.71.0(eslint@10.12.0)(typescript@6.0.3)
|
||||
debug: 4.4.3
|
||||
eslint: 10.11.0
|
||||
eslint: 10.12.0
|
||||
ts-api-utils: 2.5.0(typescript@6.0.3)
|
||||
typescript: 6.0.3
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@typescript-eslint/types@8.70.1': {}
|
||||
'@typescript-eslint/types@8.71.0': {}
|
||||
|
||||
'@typescript-eslint/typescript-estree@8.70.1(typescript@6.0.3)':
|
||||
'@typescript-eslint/typescript-estree@8.71.0(typescript@6.0.3)':
|
||||
dependencies:
|
||||
'@typescript-eslint/project-service': 8.70.1(typescript@6.0.3)
|
||||
'@typescript-eslint/tsconfig-utils': 8.70.1(typescript@6.0.3)
|
||||
'@typescript-eslint/types': 8.70.1
|
||||
'@typescript-eslint/visitor-keys': 8.70.1
|
||||
'@typescript-eslint/project-service': 8.71.0(typescript@6.0.3)
|
||||
'@typescript-eslint/tsconfig-utils': 8.71.0(typescript@6.0.3)
|
||||
'@typescript-eslint/types': 8.71.0
|
||||
'@typescript-eslint/visitor-keys': 8.71.0
|
||||
debug: 4.4.3
|
||||
minimatch: 10.2.6
|
||||
semver: 7.8.5
|
||||
@@ -2101,20 +2077,20 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@typescript-eslint/utils@8.70.1(eslint@10.11.0)(typescript@6.0.3)':
|
||||
'@typescript-eslint/utils@8.71.0(eslint@10.12.0)(typescript@6.0.3)':
|
||||
dependencies:
|
||||
'@eslint-community/eslint-utils': 4.10.1(eslint@10.11.0)
|
||||
'@typescript-eslint/scope-manager': 8.70.1
|
||||
'@typescript-eslint/types': 8.70.1
|
||||
'@typescript-eslint/typescript-estree': 8.70.1(typescript@6.0.3)
|
||||
eslint: 10.11.0
|
||||
'@eslint-community/eslint-utils': 4.10.1(eslint@10.12.0)
|
||||
'@typescript-eslint/scope-manager': 8.71.0
|
||||
'@typescript-eslint/types': 8.71.0
|
||||
'@typescript-eslint/typescript-estree': 8.71.0(typescript@6.0.3)
|
||||
eslint: 10.12.0
|
||||
typescript: 6.0.3
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@typescript-eslint/visitor-keys@8.70.1':
|
||||
'@typescript-eslint/visitor-keys@8.71.0':
|
||||
dependencies:
|
||||
'@typescript-eslint/types': 8.70.1
|
||||
'@typescript-eslint/types': 8.71.0
|
||||
eslint-visitor-keys: 5.0.1
|
||||
|
||||
'@vitest/expect@4.1.11':
|
||||
@@ -2282,9 +2258,9 @@ snapshots:
|
||||
|
||||
eslint-visitor-keys@5.0.1: {}
|
||||
|
||||
eslint@10.11.0:
|
||||
eslint@10.12.0:
|
||||
dependencies:
|
||||
'@eslint-community/eslint-utils': 4.10.1(eslint@10.11.0)
|
||||
'@eslint-community/eslint-utils': 4.10.1(eslint@10.12.0)
|
||||
'@eslint-community/regexpp': 4.12.2
|
||||
'@eslint/config-array': 0.23.5
|
||||
'@eslint/config-helpers': 0.7.0
|
||||
@@ -2464,7 +2440,7 @@ snapshots:
|
||||
launch-editor@2.14.1:
|
||||
dependencies:
|
||||
picocolors: 1.1.1
|
||||
shell-quote: 1.10.0
|
||||
shell-quote: 1.12.0
|
||||
|
||||
levn@0.4.1:
|
||||
dependencies:
|
||||
@@ -2503,7 +2479,7 @@ snapshots:
|
||||
|
||||
mute-stream@3.0.0: {}
|
||||
|
||||
nanoid@3.3.19: {}
|
||||
nanoid@3.3.20: {}
|
||||
|
||||
natural-compare@1.4.0: {}
|
||||
|
||||
@@ -2557,11 +2533,11 @@ snapshots:
|
||||
|
||||
picomatch@4.0.7: {}
|
||||
|
||||
postcss@8.5.28:
|
||||
postcss@8.5.29:
|
||||
dependencies:
|
||||
nanoid: 3.3.19
|
||||
nanoid: 3.3.20
|
||||
picocolors: 1.1.1
|
||||
source-map-js: 1.2.1
|
||||
source-map-js: 1.2.2
|
||||
|
||||
prelude-ls@1.2.1: {}
|
||||
|
||||
@@ -2580,36 +2556,36 @@ snapshots:
|
||||
|
||||
reusify@1.1.0: {}
|
||||
|
||||
rollup@4.63.5:
|
||||
rollup@4.64.3:
|
||||
dependencies:
|
||||
'@types/estree': 1.0.9
|
||||
optionalDependencies:
|
||||
'@napi-rs/lzma-linux-x64-gnu': 1.5.1
|
||||
'@rollup/rollup-android-arm-eabi': 4.63.5
|
||||
'@rollup/rollup-android-arm64': 4.63.5
|
||||
'@rollup/rollup-darwin-arm64': 4.63.5
|
||||
'@rollup/rollup-darwin-x64': 4.63.5
|
||||
'@rollup/rollup-freebsd-arm64': 4.63.5
|
||||
'@rollup/rollup-freebsd-x64': 4.63.5
|
||||
'@rollup/rollup-linux-arm-gnueabihf': 4.63.5
|
||||
'@rollup/rollup-linux-arm-musleabihf': 4.63.5
|
||||
'@rollup/rollup-linux-arm64-gnu': 4.63.5
|
||||
'@rollup/rollup-linux-arm64-musl': 4.63.5
|
||||
'@rollup/rollup-linux-loong64-gnu': 4.63.5
|
||||
'@rollup/rollup-linux-loong64-musl': 4.63.5
|
||||
'@rollup/rollup-linux-ppc64-gnu': 4.63.5
|
||||
'@rollup/rollup-linux-ppc64-musl': 4.63.5
|
||||
'@rollup/rollup-linux-riscv64-gnu': 4.63.5
|
||||
'@rollup/rollup-linux-riscv64-musl': 4.63.5
|
||||
'@rollup/rollup-linux-s390x-gnu': 4.63.5
|
||||
'@rollup/rollup-linux-x64-gnu': 4.63.5
|
||||
'@rollup/rollup-linux-x64-musl': 4.63.5
|
||||
'@rollup/rollup-openbsd-x64': 4.63.5
|
||||
'@rollup/rollup-openharmony-arm64': 4.63.5
|
||||
'@rollup/rollup-win32-arm64-msvc': 4.63.5
|
||||
'@rollup/rollup-win32-ia32-msvc': 4.63.5
|
||||
'@rollup/rollup-win32-x64-gnu': 4.63.5
|
||||
'@rollup/rollup-win32-x64-msvc': 4.63.5
|
||||
'@rollup/rollup-android-arm-eabi': 4.64.3
|
||||
'@rollup/rollup-android-arm64': 4.64.3
|
||||
'@rollup/rollup-darwin-arm64': 4.64.3
|
||||
'@rollup/rollup-darwin-x64': 4.64.3
|
||||
'@rollup/rollup-freebsd-arm64': 4.64.3
|
||||
'@rollup/rollup-freebsd-x64': 4.64.3
|
||||
'@rollup/rollup-linux-arm-gnueabihf': 4.64.3
|
||||
'@rollup/rollup-linux-arm-musleabihf': 4.64.3
|
||||
'@rollup/rollup-linux-arm64-gnu': 4.64.3
|
||||
'@rollup/rollup-linux-arm64-musl': 4.64.3
|
||||
'@rollup/rollup-linux-loong64-gnu': 4.64.3
|
||||
'@rollup/rollup-linux-loong64-musl': 4.64.3
|
||||
'@rollup/rollup-linux-ppc64-gnu': 4.64.3
|
||||
'@rollup/rollup-linux-ppc64-musl': 4.64.3
|
||||
'@rollup/rollup-linux-riscv64-gnu': 4.64.3
|
||||
'@rollup/rollup-linux-riscv64-musl': 4.64.3
|
||||
'@rollup/rollup-linux-s390x-gnu': 4.64.3
|
||||
'@rollup/rollup-linux-x64-gnu': 4.64.3
|
||||
'@rollup/rollup-linux-x64-musl': 4.64.3
|
||||
'@rollup/rollup-openbsd-x64': 4.64.3
|
||||
'@rollup/rollup-openharmony-arm64': 4.64.3
|
||||
'@rollup/rollup-win32-arm64-msvc': 4.64.3
|
||||
'@rollup/rollup-win32-ia32-msvc': 4.64.3
|
||||
'@rollup/rollup-win32-x64-gnu': 4.64.3
|
||||
'@rollup/rollup-win32-x64-msvc': 4.64.3
|
||||
fsevents: 2.3.3
|
||||
|
||||
run-parallel@1.2.0:
|
||||
@@ -2626,7 +2602,7 @@ snapshots:
|
||||
|
||||
shebang-regex@3.0.0: {}
|
||||
|
||||
shell-quote@1.10.0: {}
|
||||
shell-quote@1.12.0: {}
|
||||
|
||||
siginfo@2.0.0: {}
|
||||
|
||||
@@ -2642,7 +2618,7 @@ snapshots:
|
||||
|
||||
smol-toml@1.9.0: {}
|
||||
|
||||
source-map-js@1.2.1: {}
|
||||
source-map-js@1.2.2: {}
|
||||
|
||||
stackback@0.0.2: {}
|
||||
|
||||
@@ -2686,13 +2662,13 @@ snapshots:
|
||||
dependencies:
|
||||
prelude-ls: 1.2.1
|
||||
|
||||
typescript-eslint@8.70.1(eslint@10.11.0)(typescript@6.0.3):
|
||||
typescript-eslint@8.71.0(eslint@10.12.0)(typescript@6.0.3):
|
||||
dependencies:
|
||||
'@typescript-eslint/eslint-plugin': 8.70.1(@typescript-eslint/parser@8.70.1(eslint@10.11.0)(typescript@6.0.3))(eslint@10.11.0)(typescript@6.0.3)
|
||||
'@typescript-eslint/parser': 8.70.1(eslint@10.11.0)(typescript@6.0.3)
|
||||
'@typescript-eslint/typescript-estree': 8.70.1(typescript@6.0.3)
|
||||
'@typescript-eslint/utils': 8.70.1(eslint@10.11.0)(typescript@6.0.3)
|
||||
eslint: 10.11.0
|
||||
'@typescript-eslint/eslint-plugin': 8.71.0(@typescript-eslint/parser@8.71.0(eslint@10.12.0)(typescript@6.0.3))(eslint@10.12.0)(typescript@6.0.3)
|
||||
'@typescript-eslint/parser': 8.71.0(eslint@10.12.0)(typescript@6.0.3)
|
||||
'@typescript-eslint/typescript-estree': 8.71.0(typescript@6.0.3)
|
||||
'@typescript-eslint/utils': 8.71.0(eslint@10.12.0)(typescript@6.0.3)
|
||||
eslint: 10.12.0
|
||||
typescript: 6.0.3
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
@@ -2710,8 +2686,8 @@ snapshots:
|
||||
esbuild: 0.28.2
|
||||
fdir: 6.5.0(picomatch@4.0.7)
|
||||
picomatch: 4.0.7
|
||||
postcss: 8.5.28
|
||||
rollup: 4.63.5
|
||||
postcss: 8.5.29
|
||||
rollup: 4.64.3
|
||||
tinyglobby: 0.2.17
|
||||
optionalDependencies:
|
||||
'@types/node': 20.19.43
|
||||
|
||||
@@ -26,3 +26,20 @@ overrides:
|
||||
# Dev-only (transitive via @vitest/ui); never in the published CLI. Remove once
|
||||
# transitive fflate is >=0.8.3 (check: pnpm why fflate).
|
||||
fflate@<0.8.3: '>=0.8.3 <0.9'
|
||||
# GHSA-pqg4-j6r4-53mv — shell-quote `quote()` command injection via a line
|
||||
# terminator after a `{ comment }` token. Dev-only (transitive via
|
||||
# @changesets/cli > launch-editor); never in the published CLI. Remove once
|
||||
# transitive shell-quote is >=1.11.0 (check: pnpm why shell-quote).
|
||||
shell-quote@>=1.8.4 <1.11.0: '>=1.11.0 <2'
|
||||
|
||||
auditConfig:
|
||||
ignoreGhsas:
|
||||
# GHSA-vfj7-8cjw-p6xm — braces <=3.0.3 stack exhaustion on deeply nested
|
||||
# patterns. Runtime (fast-glob > micromatch > braces), and no patched braces
|
||||
# exists, so no override can fix it. Accepted residual risk: schema artifact
|
||||
# `generates` and `apply.tracks` patterns reach fast-glob through
|
||||
# resolveArtifactOutputs without a brace-depth guard, and the 10,000-character
|
||||
# input limit does not prevent stack exhaustion. A crafted project-local or
|
||||
# user schema can still terminate the openspec process. Remove this exception
|
||||
# once braces ships a patched release (check: pnpm why braces; pnpm audit --prod).
|
||||
- GHSA-vfj7-8cjw-p6xm
|
||||
|
||||
@@ -178,6 +178,8 @@ If the user mentions a change or you detect one is relevant:
|
||||
|
||||
`<capability-path>` is the spec directory relative to `specs/` (for example, `user-auth` or `identity/user-auth`). Preserve an existing capability's full path and follow the project's established organization for new capabilities.
|
||||
|
||||
The files below are the default spec-driven schema's. With another schema, capture each insight in whichever artifact from `artifactPaths` fits it, and name that artifact in your offer; never create an artifact the schema does not define.
|
||||
|
||||
| Insight Type | Where to Capture |
|
||||
|----------------------------|-------------------------------------|
|
||||
| New requirement discovered | `specs/<capability-path>/spec.md` |
|
||||
@@ -187,7 +189,7 @@ If the user mentions a change or you detect one is relevant:
|
||||
| New work identified | `tasks.md` |
|
||||
| Assumption invalidated | Relevant artifact |
|
||||
|
||||
Example offers:
|
||||
Example offers (default schema):
|
||||
- "That's a design decision. Capture it in design.md?"
|
||||
- "This is a new requirement. Add it to specs?"
|
||||
- "This changes scope. Update the proposal?"
|
||||
@@ -316,7 +318,7 @@ You: That changes everything.
|
||||
There's no required ending. Discovery might:
|
||||
|
||||
- **Flow into a proposal**: "Ready to start? Run `/openspec-propose` and this becomes a change."
|
||||
- **Result in artifact updates**: "Updated design.md with these decisions"
|
||||
- **Result in artifact updates**: "Updated the design with these decisions"
|
||||
- **Just provide clarity**: User has what they need, moves on
|
||||
- **Continue later**: "We can pick this up anytime"
|
||||
|
||||
|
||||
@@ -123,6 +123,14 @@ export function isCompletionRun(commandPath: string): boolean {
|
||||
return commandPath.split(':')[0] === 'completion' || commandPath === '__complete';
|
||||
}
|
||||
|
||||
/**
|
||||
* True for the hidden `__complete` resolver alone. It runs on every Tab press,
|
||||
* so the hooks skip telemetry and the completion tip for it entirely.
|
||||
*/
|
||||
export function isTabCompletionRun(commandPath: string): boolean {
|
||||
return commandPath === '__complete';
|
||||
}
|
||||
|
||||
/**
|
||||
* True when the first-run completions tip must be deferred rather than shown.
|
||||
*
|
||||
@@ -196,6 +204,13 @@ program.hook('preAction', async (thisCommand, actionCommand) => {
|
||||
process.env.NO_COLOR = '1';
|
||||
}
|
||||
|
||||
// `__complete` runs on every Tab press, with stderr sent to /dev/null by the
|
||||
// generated scripts. Tracking it would hold each press open for the request,
|
||||
// and the first-run notice would be marked seen without anyone reading it.
|
||||
if (isTabCompletionRun(getCommandPath(actionCommand))) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Show first-run telemetry notice (if not seen). It's written to stderr, so it
|
||||
// never pollutes stdout — but --json runs still defer it (see isJsonRun) so the
|
||||
// very first invocation stays free of any incidental output on either stream.
|
||||
@@ -210,6 +225,12 @@ program.hook('preAction', async (thisCommand, actionCommand) => {
|
||||
|
||||
// Shutdown telemetry after command completes
|
||||
program.hook('postAction', async (_thisCommand, actionCommand) => {
|
||||
// Nothing to flush (preAction tracked nothing), and the tip is always
|
||||
// deferred for completion runs, so skip loading either module.
|
||||
if (isTabCompletionRun(getCommandPath(actionCommand))) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Show the first-run shell-completions tip (on stderr, so piped stdout stays
|
||||
// clean). postAction, not preAction: the tip trails the command's own output
|
||||
// instead of pushing an error message or `init`'s setup summary down the
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import ora from 'ora';
|
||||
import { CompletionFactory } from '../core/completions/factory.js';
|
||||
import { COMMAND_REGISTRY } from '../core/completions/command-registry.js';
|
||||
import { detectShell, SupportedShell } from '../utils/shell-detection.js';
|
||||
@@ -125,6 +124,9 @@ export class CompletionCommand {
|
||||
const generator = CompletionFactory.createGenerator(shell);
|
||||
const installer = CompletionFactory.createInstaller(shell);
|
||||
|
||||
// Loaded here, not at the top: `__complete` shares this module and runs on
|
||||
// every Tab press, so it must not pay for the spinner.
|
||||
const { default: ora } = await import('ora');
|
||||
const spinner = ora(`Installing ${shell} completion script...`).start();
|
||||
|
||||
try {
|
||||
@@ -234,6 +236,7 @@ export class CompletionCommand {
|
||||
}
|
||||
}
|
||||
|
||||
const { default: ora } = await import('ora');
|
||||
const spinner = ora(`Uninstalling ${shell} completion script...`).start();
|
||||
|
||||
try {
|
||||
|
||||
+19
-7
@@ -1358,13 +1358,22 @@ export class ArchiveCommand {
|
||||
const archiveDir = root.archiveDir;
|
||||
const mainSpecsDir = root.specsDir;
|
||||
|
||||
for (const [allowedDirectory, managedDir] of [
|
||||
[root.path, changesDir],
|
||||
[changesDir, archiveDir],
|
||||
[root.path, mainSpecsDir],
|
||||
const openspecDir = path.dirname(changesDir);
|
||||
|
||||
// openspec/, changes/ and specs/ may be project-owned symlinks to another
|
||||
// directory; archive/ may not leave the changes directory it lives in.
|
||||
for (const [allowedDirectory, managedDir, allowLinkedLeaf] of [
|
||||
[root.path, openspecDir, true],
|
||||
[openspecDir, changesDir, true],
|
||||
[changesDir, archiveDir, false],
|
||||
[openspecDir, mainSpecsDir, true],
|
||||
] as const) {
|
||||
try {
|
||||
FileSystemUtils.assertPathWithin(allowedDirectory, managedDir);
|
||||
if (allowLinkedLeaf) {
|
||||
FileSystemUtils.assertPathWithinAllowingLinkedLeaf(allowedDirectory, managedDir);
|
||||
} else {
|
||||
FileSystemUtils.assertPathWithin(allowedDirectory, managedDir);
|
||||
}
|
||||
} catch {
|
||||
throw new ArchiveBlockedError(
|
||||
'archive_path_outside_root',
|
||||
@@ -2190,6 +2199,9 @@ export class ArchiveCommand {
|
||||
retirementAuthorizationFingerprint!
|
||||
);
|
||||
}
|
||||
// update.source is canonical (symlinks resolved), so measure it from
|
||||
// the canonical change directory; changes/ may itself be a symlink.
|
||||
const canonicalChangeDir = FileSystemUtils.canonicalizeExistingPath(changeDir);
|
||||
const verifyArchivedDeltas = async (
|
||||
stagedSource?: string
|
||||
): Promise<void> => {
|
||||
@@ -2213,7 +2225,7 @@ export class ArchiveCommand {
|
||||
for (const proposed of prepared) {
|
||||
const archivedSource = path.join(
|
||||
archivePath,
|
||||
path.relative(changeDir, proposed.update.source)
|
||||
path.relative(canonicalChangeDir, proposed.update.source)
|
||||
);
|
||||
if (
|
||||
(await fingerprintPortableContent(archivedSource)) !==
|
||||
@@ -2226,7 +2238,7 @@ export class ArchiveCommand {
|
||||
if (stagedSource) {
|
||||
const stagedDelta = path.join(
|
||||
stagedSource,
|
||||
path.relative(changeDir, proposed.update.source)
|
||||
path.relative(canonicalChangeDir, proposed.update.source)
|
||||
);
|
||||
if (
|
||||
(await fingerprintPortableContent(stagedDelta)) !==
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { getActiveChangeIds, getSpecIds } from '../../utils/item-discovery.js';
|
||||
import { listSchemas } from '../artifact-graph/index.js';
|
||||
|
||||
/**
|
||||
* Cache entry for completion data
|
||||
@@ -96,7 +95,10 @@ export class CompletionProvider {
|
||||
return this.schemaCache.data;
|
||||
}
|
||||
|
||||
// Fetch fresh data
|
||||
// Fetch fresh data. The resolver (and the yaml and zod it parses schemas
|
||||
// with) loads only here: completing changes or specs never needs it, and
|
||||
// the artifact-graph barrel would also pull in fast-glob.
|
||||
const { listSchemas } = await import('../artifact-graph/resolver.js');
|
||||
const schemaNames = listSchemas(this.projectRoot);
|
||||
|
||||
// Update cache
|
||||
|
||||
@@ -169,6 +169,16 @@ export interface OrphanedRequirement {
|
||||
line: number; // 1-based line number in the delta file
|
||||
}
|
||||
|
||||
/**
|
||||
* Authored text archive does not carry into the main spec: a non-delta `## `
|
||||
* section, text above the first `## `, or prose before the first requirement
|
||||
* in ADDED/MODIFIED. `section` is null for text above the first `## `.
|
||||
*/
|
||||
export interface UnappliedContent {
|
||||
section: string | null;
|
||||
line: number; // 1-based: the section header, or the first line of leading prose
|
||||
}
|
||||
|
||||
export interface DeltaPlan {
|
||||
added: RequirementBlock[];
|
||||
modified: RequirementBlock[];
|
||||
@@ -182,6 +192,7 @@ export interface DeltaPlan {
|
||||
unpairedRenames: UnpairedRename[];
|
||||
/** Canonical requirement blocks written outside every delta section. */
|
||||
orphanedRequirements: OrphanedRequirement[];
|
||||
unappliedContent: UnappliedContent[];
|
||||
skippedHeaders: SkippedHeader[]; // non-canonical ### headers the reader skipped
|
||||
sectionPresence: {
|
||||
added: boolean;
|
||||
@@ -256,6 +267,7 @@ export function parseDeltaSpec(content: string): DeltaPlan {
|
||||
renamed: renamedPairs,
|
||||
unpairedRenames,
|
||||
orphanedRequirements: findOrphanedRequirements(lines, fenceMask),
|
||||
unappliedContent: findUnappliedContent(lines, fenceMask),
|
||||
skippedHeaders,
|
||||
sectionPresence: {
|
||||
added: addedLookup.found,
|
||||
@@ -315,6 +327,57 @@ function findOrphanedRequirements(
|
||||
return orphans;
|
||||
}
|
||||
|
||||
export function describeUnappliedContent(content: UnappliedContent, sentenceStart = false): string {
|
||||
const text = content.section === null
|
||||
? 'text above the first "## " section'
|
||||
: DELTA_SECTION_TITLES.has(content.section.toLowerCase())
|
||||
? `text before the first requirement in "## ${content.section}"`
|
||||
: `section "## ${content.section}"`;
|
||||
return sentenceStart ? text.charAt(0).toUpperCase() + text.slice(1) : text;
|
||||
}
|
||||
|
||||
/**
|
||||
* Where text the merge never reads begins. HTML comments, `# ` titles, Purpose
|
||||
* (warned about separately), REMOVED/RENAMED bodies, and stray `###` headers
|
||||
* before the first requirement (INFO since #498) are not reported.
|
||||
*/
|
||||
function findUnappliedContent(lines: string[], fenceMask: boolean[]): UnappliedContent[] {
|
||||
// Blank out comments outside fences, keeping line count; an unterminated one
|
||||
// runs to EOF. Comments only decide whether a line holds text: sections come
|
||||
// from the same split the merge reads, where a `## ` line inside a comment is
|
||||
// still a boundary. Fenced lines keep their text, so a `<!--` in a code sample
|
||||
// never hides what follows the fence.
|
||||
const comments = lines
|
||||
.map((line, i) => (fenceMask[i] ? '' : line))
|
||||
.join('\n')
|
||||
.replace(/<!--[\s\S]*?(?:--!?>|$)/g, (m) => m.replace(/[^\n]/g, ' '))
|
||||
.split('\n');
|
||||
const masked = lines.map((line, i) => (fenceMask[i] ? line : comments[i]));
|
||||
const sections = splitTopLevelSections(lines, fenceMask);
|
||||
const firstText = (from: number, to: number, skip?: RegExp) => {
|
||||
for (let i = from; i < to; i++) if (masked[i].trim() && !skip?.test(masked[i])) return i;
|
||||
return -1;
|
||||
};
|
||||
const found: UnappliedContent[] = [];
|
||||
const preamble = firstText(0, sections.length ? sections[0].body.bodyStartLine - 2 : lines.length, /^#\s/);
|
||||
if (preamble !== -1) found.push({ section: null, line: preamble + 1 });
|
||||
for (const { title, body } of sections) {
|
||||
const folded = title.toLowerCase();
|
||||
const start = body.bodyStartLine - 1;
|
||||
if (folded === 'added requirements' || folded === 'modified requirements') {
|
||||
const block = body.lines.findIndex((l, i) => !body.fenceMask[i] && REQUIREMENT_HEADER_REGEX.test(l));
|
||||
const first = firstText(start, start + (block === -1 ? body.lines.length : block), /^###\s/);
|
||||
if (first !== -1) found.push({ section: title, line: first + 1 });
|
||||
} else if (!DELTA_SECTION_TITLES.has(folded)) {
|
||||
// The Purpose carry-over reads past commented-out headers, so only a
|
||||
// `## Purpose` outside a comment is the one archive handles.
|
||||
if (folded === 'purpose' && masked[start - 1].trim()) continue;
|
||||
if (firstText(start, start + body.lines.length) !== -1) found.push({ section: title, line: start });
|
||||
}
|
||||
}
|
||||
return found;
|
||||
}
|
||||
|
||||
/** One `## ` section of a delta file, in the order it was written. */
|
||||
interface DeltaSection {
|
||||
title: string;
|
||||
|
||||
@@ -13,6 +13,7 @@ import {
|
||||
extractRequirementsSection,
|
||||
diffScenarioNames,
|
||||
describeScenarioBalance,
|
||||
describeUnappliedContent,
|
||||
foldRequirementName,
|
||||
parseDeltaSpec,
|
||||
normalizeRequirementName,
|
||||
@@ -251,6 +252,11 @@ export async function buildUpdatedSpec(
|
||||
);
|
||||
}
|
||||
|
||||
// Archive carries requirement blocks only; name any other text it leaves behind.
|
||||
for (const lost of plan.unappliedContent) {
|
||||
warn(`${specName} - ${describeUnappliedContent(lost)} (line ${lost.line}) is not carried into the main spec.`);
|
||||
}
|
||||
|
||||
// Pre-validate duplicates within sections
|
||||
const addedNames = new Set<string>();
|
||||
for (const add of plan.added) {
|
||||
|
||||
@@ -217,6 +217,8 @@ If the user mentions a change or you detect one is relevant:
|
||||
|
||||
\`<capability-path>\` is the spec directory relative to \`specs/\` (for example, \`user-auth\` or \`identity/user-auth\`). Preserve an existing capability's full path and follow the project's established organization for new capabilities.
|
||||
|
||||
The files below are the default spec-driven schema's. With another schema, capture each insight in whichever artifact from \`artifactPaths\` fits it, and name that artifact in your offer; never create an artifact the schema does not define.
|
||||
|
||||
| Insight Type | Where to Capture |
|
||||
|----------------------------|-------------------------------------|
|
||||
| New requirement discovered | \`specs/<capability-path>/spec.md\` |
|
||||
@@ -226,7 +228,7 @@ If the user mentions a change or you detect one is relevant:
|
||||
| New work identified | \`tasks.md\` |
|
||||
| Assumption invalidated | Relevant artifact |
|
||||
|
||||
Example offers:
|
||||
Example offers (default schema):
|
||||
- "That's a design decision. Capture it in design.md?"
|
||||
- "This is a new requirement. Add it to specs?"
|
||||
- "This changes scope. Update the proposal?"
|
||||
@@ -355,7 +357,7 @@ You: That changes everything.
|
||||
There's no required ending. Discovery might:
|
||||
|
||||
- **Flow into a proposal**: "${DISCOVERY_END_HANDOFF}"
|
||||
- **Result in artifact updates**: "Updated design.md with these decisions"
|
||||
- **Result in artifact updates**: "Updated the design with these decisions"
|
||||
- **Just provide clarity**: User has what they need, moves on
|
||||
- **Continue later**: "We can pick this up anytime"
|
||||
|
||||
@@ -551,6 +553,8 @@ If the user mentions a change or you detect one is relevant:
|
||||
|
||||
\`<capability-path>\` is the spec directory relative to \`specs/\` (for example, \`user-auth\` or \`identity/user-auth\`). Preserve an existing capability's full path and follow the project's established organization for new capabilities.
|
||||
|
||||
The files below are the default spec-driven schema's. With another schema, capture each insight in whichever artifact from \`artifactPaths\` fits it, and name that artifact in your offer; never create an artifact the schema does not define.
|
||||
|
||||
| Insight Type | Where to Capture |
|
||||
|----------------------------|-------------------------------------|
|
||||
| New requirement discovered | \`specs/<capability-path>/spec.md\` |
|
||||
@@ -560,7 +564,7 @@ If the user mentions a change or you detect one is relevant:
|
||||
| New work identified | \`tasks.md\` |
|
||||
| Assumption invalidated | Relevant artifact |
|
||||
|
||||
Example offers:
|
||||
Example offers (default schema):
|
||||
- "That's a design decision. Capture it in design.md?"
|
||||
- "This is a new requirement. Add it to specs?"
|
||||
- "This changes scope. Update the proposal?"
|
||||
@@ -585,7 +589,7 @@ If the user mentions a change or you detect one is relevant:
|
||||
There's no required ending. Discovery might:
|
||||
|
||||
- **Flow into a proposal**: "${DISCOVERY_END_HANDOFF}"
|
||||
- **Result in artifact updates**: "Updated design.md with these decisions"
|
||||
- **Result in artifact updates**: "Updated the design with these decisions"
|
||||
- **Just provide clarity**: User has what they need, moves on
|
||||
- **Continue later**: "We can pick this up anytime"
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ import {
|
||||
extractRequirementsSection,
|
||||
diffScenarioNames,
|
||||
describeScenarioBalance,
|
||||
describeUnappliedContent,
|
||||
type RequirementBlock,
|
||||
} from '../parsers/requirement-blocks.js';
|
||||
import {
|
||||
@@ -257,6 +258,17 @@ export class Validator {
|
||||
});
|
||||
}
|
||||
|
||||
// Archive carries requirement blocks only, by design. Warn so text
|
||||
// written elsewhere is moved rather than lost without notice.
|
||||
for (const lost of plan.unappliedContent) {
|
||||
issues.push({
|
||||
level: 'WARNING',
|
||||
path: entryPath,
|
||||
line: lost.line,
|
||||
message: `${describeUnappliedContent(lost, true)} is not carried into the main spec by archive. Move it into a requirement, or into proposal.md or design.md.`,
|
||||
});
|
||||
}
|
||||
|
||||
const sectionNames: string[] = [];
|
||||
if (plan.sectionPresence.added) sectionNames.push('## ADDED Requirements');
|
||||
if (plan.sectionPresence.modified) sectionNames.push('## MODIFIED Requirements');
|
||||
|
||||
@@ -125,6 +125,33 @@ export class FileSystemUtils {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Like assertPathWithin, but a targetPath that is itself a symlink may
|
||||
* resolve outside allowedDirectory, so a project can keep a managed
|
||||
* directory (for example openspec/changes) in another repository. Its parent
|
||||
* must still be confined and the link must resolve to an existing path.
|
||||
*/
|
||||
static assertPathWithinAllowingLinkedLeaf(allowedDirectory: string, targetPath: string): void {
|
||||
const resolvedTarget = path.resolve(targetPath);
|
||||
let isLink = false;
|
||||
try {
|
||||
isLink = nodeFs.lstatSync(resolvedTarget).isSymbolicLink();
|
||||
} catch (error) {
|
||||
if ((error as NodeJS.ErrnoException).code !== 'ENOENT') {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
if (!isLink) {
|
||||
this.assertPathWithin(allowedDirectory, resolvedTarget);
|
||||
return;
|
||||
}
|
||||
|
||||
this.assertPathWithin(allowedDirectory, path.dirname(resolvedTarget));
|
||||
// Throws for a dangling link, which cannot be proven to be a directory.
|
||||
nodeFs.realpathSync.native(resolvedTarget);
|
||||
}
|
||||
|
||||
static resolveProjectArtifactPath(projectPath: string, artifactPath: string): string {
|
||||
if (path.isAbsolute(artifactPath)) {
|
||||
throw new Error(`Refusing to manage an artifact outside the project: ${artifactPath}`);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { afterAll, describe, expect, it } from 'vitest';
|
||||
import { spawnSync } from 'node:child_process';
|
||||
import { mkdtempSync, readFileSync, realpathSync, rmSync } from 'node:fs';
|
||||
import { existsSync, mkdtempSync, readFileSync, realpathSync, rmSync } from 'node:fs';
|
||||
import os from 'node:os';
|
||||
import path from 'node:path';
|
||||
import { fileURLToPath, pathToFileURL } from 'node:url';
|
||||
@@ -118,6 +118,101 @@ describe('CLI startup loads only what the command needs', () => {
|
||||
}
|
||||
);
|
||||
|
||||
// Shell completion scripts run `__complete` on every Tab press. It needs no
|
||||
// package beyond commander, and never telemetry: tracking would hold each
|
||||
// press open for the request.
|
||||
it.each(['changes', 'specs'])(
|
||||
'`openspec __complete %s` loads only commander and no telemetry',
|
||||
async (type) => {
|
||||
await ensureCliBuilt();
|
||||
const { own, packages, status } = loadedModules(['__complete', type]);
|
||||
|
||||
expect(status).toBe(0);
|
||||
expect([...packages]).toEqual(['commander']);
|
||||
expect(implementationsLoaded(own)).toEqual(['completion']);
|
||||
expect(own).not.toContain('telemetry/index.js');
|
||||
}
|
||||
);
|
||||
|
||||
it('`openspec __complete schemas` loads the schema reader but not fast-glob or ora', async () => {
|
||||
await ensureCliBuilt();
|
||||
const { own, packages, status } = loadedModules(['__complete', 'schemas']);
|
||||
|
||||
expect(status).toBe(0);
|
||||
expect([...packages]).not.toContain('fast-glob');
|
||||
expect([...packages]).not.toContain('ora');
|
||||
expect(own).not.toContain('telemetry/index.js');
|
||||
// The name promises a schema reader, so pin it: the resolver directly, not
|
||||
// the artifact-graph barrel, which re-exports it but also pulls outputs.js
|
||||
// and with it fast-glob.
|
||||
expect(own).toContain('core/artifact-graph/resolver.js');
|
||||
expect(own).not.toContain('core/artifact-graph/index.js');
|
||||
});
|
||||
|
||||
// Negative control for the two cases above. `isTabCompletionRun` matches the
|
||||
// command path exactly, unlike the adjacent `isCompletionRun`, which
|
||||
// prefix-matches `completion:*`. Widening one into the other would silently
|
||||
// stop tracking every user-facing command in that group, and every assertion
|
||||
// above would still pass.
|
||||
it.each([['list --json'], ['completion generate zsh']])(
|
||||
'`openspec %s` still reaches the telemetry hooks',
|
||||
async (invocation) => {
|
||||
await ensureCliBuilt();
|
||||
const { own } = loadedModules(invocation.split(' '));
|
||||
|
||||
expect(own).toContain('telemetry/index.js');
|
||||
expect(own).toContain('core/completion-tip.js');
|
||||
}
|
||||
);
|
||||
|
||||
// The module-count assertions above pin that telemetry is not LOADED. They do
|
||||
// not pin the user-visible consequence, which is the reason the skip exists:
|
||||
// the first-run disclosure goes to stderr, every generated completion script
|
||||
// sends `__complete` stderr to /dev/null, and `maybeShowTelemetryNotice` marks
|
||||
// `noticeSeen` before `trackCommand` re-reads it. So before this change a
|
||||
// user's first-ever interaction being a Tab press burned the disclosure where
|
||||
// nobody could read it and began collecting on that same press.
|
||||
//
|
||||
// Telemetry is deliberately left ENABLED here: the point is that an enabled
|
||||
// run still records nothing. HOME must be isolated as well as XDG_CONFIG_HOME,
|
||||
// or `migrateLegacyTelemetryConfig` imports the developer's real config from
|
||||
// ~/.config/openspec/config.json and the test passes vacuously.
|
||||
it('`openspec __complete` leaves the first-run telemetry notice unconsumed', async () => {
|
||||
await ensureCliBuilt();
|
||||
const home = mkdtempSync(path.join(os.tmpdir(), 'openspec-notice-'));
|
||||
workDirs.push(home);
|
||||
const configHome = path.join(home, 'config');
|
||||
|
||||
// These have to be DELETED, not blanked. Both opt-outs read as "set to
|
||||
// anything that is not an explicit on-value", so `OPENSPEC_TELEMETRY: ''`
|
||||
// and `DO_NOT_TRACK: ''` would switch telemetry OFF and the assertions
|
||||
// below would hold on any build, passing vacuously.
|
||||
const env = {
|
||||
...process.env,
|
||||
HOME: home,
|
||||
USERPROFILE: home,
|
||||
XDG_CONFIG_HOME: configHome,
|
||||
XDG_DATA_HOME: path.join(home, 'data'),
|
||||
};
|
||||
delete env.OPENSPEC_TELEMETRY;
|
||||
delete env.DO_NOT_TRACK;
|
||||
delete env.CI;
|
||||
|
||||
const result = spawnSync(process.execPath, [binPath, '__complete', 'changes'], {
|
||||
cwd: home,
|
||||
env,
|
||||
encoding: 'utf-8',
|
||||
timeout: 30_000,
|
||||
windowsHide: true,
|
||||
});
|
||||
|
||||
expect(result.status).toBe(0);
|
||||
// No disclosure written to a stream the completion scripts discard.
|
||||
expect(result.stderr).toBe('');
|
||||
// And no config file, so neither noticeSeen nor an anonymousId was minted.
|
||||
expect(existsSync(path.join(configHome, 'openspec', 'config.json'))).toBe(false);
|
||||
});
|
||||
|
||||
it.each([
|
||||
['config list --json', 'config'],
|
||||
['config path', 'config'],
|
||||
|
||||
@@ -619,6 +619,180 @@ describe('ArchiveCommand', () => {
|
||||
await expect(fs.readdir(outsideDir)).resolves.toEqual([]);
|
||||
});
|
||||
|
||||
it('archives when openspec/changes is a project-owned symlink to another directory', async () => {
|
||||
const changeName = 'linked-change';
|
||||
const docsRepo = realpathSync.native(
|
||||
await fs.mkdtemp(path.join(os.tmpdir(), 'openspec-docs-repo-'))
|
||||
);
|
||||
onTestFinished(() => fs.rm(docsRepo, { recursive: true, force: true }));
|
||||
const sharedChanges = path.join(docsRepo, 'changes');
|
||||
const changesLink = path.join(tempDir, 'openspec', 'changes');
|
||||
await fs.rm(changesLink, { recursive: true, force: true });
|
||||
await fs.mkdir(path.join(sharedChanges, changeName), { recursive: true });
|
||||
await fs.writeFile(path.join(sharedChanges, changeName, 'tasks.md'), '- [x] Task 1\n');
|
||||
await fs.symlink(sharedChanges, changesLink, process.platform === 'win32' ? 'junction' : 'dir');
|
||||
|
||||
await archiveCommand.execute(changeName, {
|
||||
yes: true,
|
||||
noValidate: true,
|
||||
skipSpecs: true,
|
||||
});
|
||||
|
||||
await expect(fs.access(path.join(sharedChanges, changeName))).rejects.toThrow();
|
||||
const archived = await fs.readdir(path.join(sharedChanges, 'archive'));
|
||||
expect(archived.some((name) => name.endsWith(`-${changeName}`))).toBe(true);
|
||||
});
|
||||
|
||||
it.each([
|
||||
{ linkChanges: true, linkSpecs: false },
|
||||
{ linkChanges: false, linkSpecs: true },
|
||||
{ linkChanges: true, linkSpecs: true },
|
||||
])(
|
||||
'applies a spec delta and archives with symlinked changes=$linkChanges specs=$linkSpecs',
|
||||
async ({ linkChanges, linkSpecs }) => {
|
||||
const changeName = 'linked-delta';
|
||||
const linkType = process.platform === 'win32' ? 'junction' : 'dir';
|
||||
const docsRepo = realpathSync.native(
|
||||
await fs.mkdtemp(path.join(os.tmpdir(), 'openspec-docs-repo-'))
|
||||
);
|
||||
onTestFinished(() => fs.rm(docsRepo, { recursive: true, force: true }));
|
||||
const openspecDir = path.join(tempDir, 'openspec');
|
||||
const changesDir = linkChanges ? path.join(docsRepo, 'changes') : path.join(openspecDir, 'changes');
|
||||
const specsDir = linkSpecs ? path.join(docsRepo, 'specs') : path.join(openspecDir, 'specs');
|
||||
if (linkChanges) {
|
||||
await fs.rm(path.join(openspecDir, 'changes'), { recursive: true, force: true });
|
||||
await fs.mkdir(changesDir, { recursive: true });
|
||||
await fs.symlink(changesDir, path.join(openspecDir, 'changes'), linkType);
|
||||
}
|
||||
if (linkSpecs) {
|
||||
await fs.rm(path.join(openspecDir, 'specs'), { recursive: true, force: true });
|
||||
await fs.mkdir(specsDir, { recursive: true });
|
||||
await fs.symlink(specsDir, path.join(openspecDir, 'specs'), linkType);
|
||||
}
|
||||
const deltaDir = path.join(changesDir, changeName, 'specs', 'linked-capability');
|
||||
await fs.mkdir(deltaDir, { recursive: true });
|
||||
await fs.writeFile(
|
||||
path.join(deltaDir, 'spec.md'),
|
||||
`# Linked Capability - Changes
|
||||
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: The system SHALL archive through linked directories
|
||||
|
||||
#### Scenario: Linked directories
|
||||
- **WHEN** the change is archived
|
||||
- **THEN** the delta is applied and the change is archived
|
||||
`
|
||||
);
|
||||
|
||||
await archiveCommand.execute(changeName, { yes: true, noValidate: true });
|
||||
|
||||
const mainSpec = await fs.readFile(
|
||||
path.join(specsDir, 'linked-capability', 'spec.md'),
|
||||
'utf-8'
|
||||
);
|
||||
expect(mainSpec).toContain('### Requirement: The system SHALL archive through linked directories');
|
||||
await expect(fs.access(path.join(changesDir, changeName))).rejects.toThrow();
|
||||
const archived = await fs.readdir(path.join(changesDir, 'archive'));
|
||||
const archivedName = archived.find((name) => name.endsWith(`-${changeName}`));
|
||||
expect(archivedName).toBeDefined();
|
||||
await expect(
|
||||
fs.access(path.join(changesDir, 'archive', archivedName!, 'specs', 'linked-capability', 'spec.md'))
|
||||
).resolves.not.toThrow();
|
||||
}
|
||||
);
|
||||
|
||||
it('archives when the whole openspec directory is a project-owned symlink', async () => {
|
||||
const changeName = 'linked-openspec';
|
||||
const docsRepo = realpathSync.native(
|
||||
await fs.mkdtemp(path.join(os.tmpdir(), 'openspec-docs-repo-'))
|
||||
);
|
||||
onTestFinished(() => fs.rm(docsRepo, { recursive: true, force: true }));
|
||||
const sharedOpenspec = path.join(docsRepo, 'openspec');
|
||||
await fs.rm(path.join(tempDir, 'openspec'), { recursive: true, force: true });
|
||||
await fs.mkdir(path.join(sharedOpenspec, 'changes', changeName), { recursive: true });
|
||||
await fs.mkdir(path.join(sharedOpenspec, 'specs'), { recursive: true });
|
||||
await fs.writeFile(
|
||||
path.join(sharedOpenspec, 'changes', changeName, 'tasks.md'),
|
||||
'- [x] Task 1\n'
|
||||
);
|
||||
await fs.symlink(
|
||||
sharedOpenspec,
|
||||
path.join(tempDir, 'openspec'),
|
||||
process.platform === 'win32' ? 'junction' : 'dir'
|
||||
);
|
||||
|
||||
await archiveCommand.execute(changeName, { yes: true, noValidate: true, skipSpecs: true });
|
||||
|
||||
const archived = await fs.readdir(path.join(sharedOpenspec, 'changes', 'archive'));
|
||||
expect(archived.some((name) => name.endsWith(`-${changeName}`))).toBe(true);
|
||||
});
|
||||
|
||||
it('rejects an archive directory that escapes a symlinked changes directory', async () => {
|
||||
const changeName = 'linked-escape';
|
||||
const docsRepo = realpathSync.native(
|
||||
await fs.mkdtemp(path.join(os.tmpdir(), 'openspec-docs-repo-'))
|
||||
);
|
||||
onTestFinished(() => fs.rm(docsRepo, { recursive: true, force: true }));
|
||||
const sharedChanges = path.join(docsRepo, 'changes');
|
||||
const outsideDir = path.join(docsRepo, 'outside');
|
||||
const linkType = process.platform === 'win32' ? 'junction' : 'dir';
|
||||
await fs.rm(path.join(tempDir, 'openspec', 'changes'), { recursive: true, force: true });
|
||||
await fs.mkdir(path.join(sharedChanges, changeName), { recursive: true });
|
||||
await fs.mkdir(outsideDir);
|
||||
await fs.writeFile(path.join(sharedChanges, changeName, 'tasks.md'), '- [x] Task 1\n');
|
||||
await fs.symlink(sharedChanges, path.join(tempDir, 'openspec', 'changes'), linkType);
|
||||
await fs.symlink(outsideDir, path.join(sharedChanges, 'archive'), linkType);
|
||||
|
||||
await expect(
|
||||
archiveCommand.execute(changeName, { yes: true, noValidate: true, skipSpecs: true })
|
||||
).rejects.toThrow(/outside the OpenSpec root/u);
|
||||
await expect(fs.access(path.join(sharedChanges, changeName))).resolves.not.toThrow();
|
||||
await expect(fs.readdir(outsideDir)).resolves.toEqual([]);
|
||||
});
|
||||
|
||||
it('verifies deltas through a symlinked changes directory on the cross-device fallback', async () => {
|
||||
const changeName = 'linked-exdev';
|
||||
const docsRepo = realpathSync.native(
|
||||
await fs.mkdtemp(path.join(os.tmpdir(), 'openspec-docs-repo-'))
|
||||
);
|
||||
onTestFinished(() => fs.rm(docsRepo, { recursive: true, force: true }));
|
||||
const sharedChanges = path.join(docsRepo, 'changes');
|
||||
const deltaDir = path.join(sharedChanges, changeName, 'specs', 'linked-capability');
|
||||
await fs.rm(path.join(tempDir, 'openspec', 'changes'), { recursive: true, force: true });
|
||||
await fs.mkdir(deltaDir, { recursive: true });
|
||||
await fs.writeFile(
|
||||
path.join(deltaDir, 'spec.md'),
|
||||
`## ADDED Requirements
|
||||
|
||||
### Requirement: The system SHALL survive EXDEV
|
||||
The system SHALL archive.
|
||||
|
||||
#### Scenario: Cross device
|
||||
- **WHEN** rename fails with EXDEV
|
||||
- **THEN** the copy fallback archives the change
|
||||
`
|
||||
);
|
||||
await fs.symlink(
|
||||
sharedChanges,
|
||||
path.join(tempDir, 'openspec', 'changes'),
|
||||
process.platform === 'win32' ? 'junction' : 'dir'
|
||||
);
|
||||
|
||||
const rename = vi.spyOn(fs, 'rename').mockRejectedValueOnce(
|
||||
Object.assign(new Error('cross-device move'), { code: 'EXDEV' })
|
||||
);
|
||||
try {
|
||||
await archiveCommand.execute(changeName, { yes: true, noValidate: true });
|
||||
} finally {
|
||||
rename.mockRestore();
|
||||
}
|
||||
|
||||
await expect(fs.access(path.join(sharedChanges, changeName))).rejects.toThrow();
|
||||
const archived = await fs.readdir(path.join(sharedChanges, 'archive'));
|
||||
expect(archived.some((name) => name.endsWith(`-${changeName}`))).toBe(true);
|
||||
});
|
||||
|
||||
it('archives normally when the project root is reached through a symlink alias', async () => {
|
||||
if (process.platform === 'win32') return;
|
||||
|
||||
|
||||
@@ -1,7 +1,12 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { Command, Option } from 'commander';
|
||||
|
||||
import { isJsonRun, isCompletionRun, shouldDeferCompletionTip } from '../../src/cli/index.js';
|
||||
import {
|
||||
isJsonRun,
|
||||
isCompletionRun,
|
||||
isTabCompletionRun,
|
||||
shouldDeferCompletionTip,
|
||||
} from '../../src/cli/index.js';
|
||||
|
||||
/**
|
||||
* Reproduce the three ways `--json` reaches a command in the real CLI, so a
|
||||
@@ -106,6 +111,23 @@ describe('isCompletionRun', () => {
|
||||
);
|
||||
});
|
||||
|
||||
describe('isTabCompletionRun', () => {
|
||||
/**
|
||||
* Only the hidden resolver skips telemetry. The user-facing `completion`
|
||||
* commands are run by a person and are tracked like any other command.
|
||||
*/
|
||||
it('matches the hidden resolver', () => {
|
||||
expect(isTabCompletionRun('__complete')).toBe(true);
|
||||
});
|
||||
|
||||
it.each(['completion', 'completion:install', 'completion:generate', 'list'])(
|
||||
'does not match "%s"',
|
||||
(commandPath) => {
|
||||
expect(isTabCompletionRun(commandPath)).toBe(false);
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
describe('shouldDeferCompletionTip', () => {
|
||||
/**
|
||||
* The tip must survive every run that cannot display it. Deferring (rather
|
||||
|
||||
@@ -421,5 +421,35 @@ describe('ZshGenerator', () => {
|
||||
expect(script).toContain('_openspec_view() {');
|
||||
expect(script).toContain('_arguments');
|
||||
});
|
||||
|
||||
// The bash, fish and powershell generator tests each pin this redirect;
|
||||
// zsh was the one that did not, and it has the most `__complete` call
|
||||
// sites and is the default shell on macOS. The redirect is what keeps a
|
||||
// Tab press silent, so anything `__complete` ever writes to stderr - a
|
||||
// deprecation notice, a Node experimental warning - would otherwise print
|
||||
// into the middle of the user's prompt with no test objecting.
|
||||
it('sends every __complete call stderr to /dev/null', () => {
|
||||
const commands: CommandDefinition[] = [
|
||||
{ name: 'archive', description: 'Archive a change', flags: [] },
|
||||
{ name: 'show', description: 'Show an item', flags: [] },
|
||||
{ name: 'new', description: 'Create a change', flags: [] },
|
||||
];
|
||||
|
||||
const script = generator.generate(commands);
|
||||
|
||||
expect(script).toContain('openspec __complete changes 2>/dev/null');
|
||||
expect(script).toContain('openspec __complete specs 2>/dev/null');
|
||||
expect(script).toContain('openspec __complete schemas 2>/dev/null');
|
||||
// No invocation may survive a template edit that drops a redirect. Only
|
||||
// real call sites count: the script also explains itself in comments
|
||||
// that name the command without running it.
|
||||
const invocations = script
|
||||
.split('\n')
|
||||
.filter((line) => line.includes('openspec __complete') && !line.trim().startsWith('#'));
|
||||
expect(invocations.length).toBeGreaterThan(0);
|
||||
for (const line of invocations) {
|
||||
expect(line, `unredirected __complete call: ${line.trim()}`).toContain('2>/dev/null');
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -0,0 +1,149 @@
|
||||
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
||||
import { promises as fs } from 'fs';
|
||||
import path from 'path';
|
||||
import os from 'os';
|
||||
import { parseDeltaSpec } from '../../../src/core/parsers/requirement-blocks.js';
|
||||
import { buildUpdatedSpec, findSpecUpdates } from '../../../src/core/specs-apply.js';
|
||||
import { Validator } from '../../../src/core/validation/validator.js';
|
||||
|
||||
// Archive carries requirement blocks only. Text written anywhere else in a delta
|
||||
// used to vanish with no warning while `validate --strict` passed.
|
||||
const DELTA = [
|
||||
'# billing delta', // 1 - a title is not reported
|
||||
'Context above the first section.', // 2
|
||||
'## ADDED Requirements', // 3
|
||||
'Intro prose.', // 4
|
||||
'### Requirement: Invoices', // 5
|
||||
'The system SHALL generate invoices.', // 6
|
||||
'', // 7
|
||||
'#### Scenario: Period closes', // 8
|
||||
'- **WHEN** a', // 9
|
||||
'- **THEN** b', // 10
|
||||
'Trailing note, carried with the block.', // 11
|
||||
'## Acceptance Criteria', // 12
|
||||
'- Invoices reconcile.', // 13
|
||||
].join('\n');
|
||||
|
||||
describe('parseDeltaSpec unappliedContent', () => {
|
||||
it('reports text above the first section, leading prose, and non-delta sections', () => {
|
||||
expect(parseDeltaSpec(DELTA).unappliedContent).toEqual([
|
||||
{ section: null, line: 2 },
|
||||
{ section: 'ADDED Requirements', line: 4 },
|
||||
{ section: 'Acceptance Criteria', line: 12 },
|
||||
]);
|
||||
});
|
||||
|
||||
it('does not exempt a commented-out Purpose, and honours the --!> terminator', () => {
|
||||
const plan = parseDeltaSpec(
|
||||
['<!--', '## Purpose', '--!>', 'Real prose.', '## ADDED Requirements', '### Requirement: A', 'a', '## Notes', 'n'].join(
|
||||
'\n'
|
||||
)
|
||||
);
|
||||
expect(plan.unappliedContent).toEqual([
|
||||
{ section: 'Purpose', line: 2 },
|
||||
{ section: 'Notes', line: 8 },
|
||||
]);
|
||||
});
|
||||
|
||||
// The merge splits sections on every `## ` line outside a fence, comment or not.
|
||||
// Reading boundaries from comment-masked lines disagreed with it both ways.
|
||||
it('reads section boundaries exactly as the merge does', () => {
|
||||
// `<!--` in inline code and a later `-->` are not a comment to the merge;
|
||||
// masking across them hid `## ADDED Requirements` and blamed a carried requirement.
|
||||
const inlineOpener = parseDeltaSpec(
|
||||
['# Delta: strip `<!--` openers', '## ADDED Requirements', '### Requirement: A', 'Flow: ingest --> render', 'The system SHALL a.'].join('\n')
|
||||
);
|
||||
expect(inlineOpener.added.map((b) => b.name)).toEqual(['A']);
|
||||
expect(inlineOpener.unappliedContent).toEqual([]);
|
||||
|
||||
// A header inside a comment is still a section to the merge, so the
|
||||
// requirement after the comment is carried, not text above the first section.
|
||||
const commentedHeader = parseDeltaSpec(
|
||||
['<!-- add requirements below', '## ADDED Requirements', '-->', '### Requirement: A', 'The system SHALL a.'].join('\n')
|
||||
);
|
||||
expect(commentedHeader.added.map((b) => b.name)).toEqual(['A']);
|
||||
expect(commentedHeader.unappliedContent).toEqual([]);
|
||||
|
||||
// ...and text after a commented-out `## Notes` is dropped by the merge.
|
||||
const commentedNotes = parseDeltaSpec(
|
||||
['## ADDED Requirements', '### Requirement: A', 'a', '<!--', '## Notes', '-->', 'Dropped.'].join('\n')
|
||||
);
|
||||
expect(commentedNotes.added[0].raw).not.toContain('Dropped.');
|
||||
expect(commentedNotes.unappliedContent).toEqual([{ section: 'Notes', line: 5 }]);
|
||||
});
|
||||
|
||||
it('does not let a `<!--` inside a code fence hide later sections', () => {
|
||||
const plan = parseDeltaSpec(
|
||||
['## ADDED Requirements', '### Requirement: A', 'a', '```html', '<!-- unterminated sample', '```', '## Notes', 'Dropped.'].join('\n')
|
||||
);
|
||||
expect(plan.unappliedContent).toEqual([{ section: 'Notes', line: 7 }]);
|
||||
});
|
||||
|
||||
it('ignores Purpose, REMOVED/RENAMED bodies, HTML comments, fences, and stray ### headers', () => {
|
||||
const plan = parseDeltaSpec(
|
||||
[
|
||||
'<!--',
|
||||
'Multi-line instructions.',
|
||||
'-->',
|
||||
'## Purpose',
|
||||
'A new capability.',
|
||||
'## ADDED Requirements',
|
||||
'<!-- add requirements below -->',
|
||||
'### Documentation Requirements',
|
||||
'### Requirement: A',
|
||||
'a',
|
||||
'```markdown',
|
||||
'## Notes',
|
||||
'```',
|
||||
'## REMOVED Requirements',
|
||||
'### Requirement: B',
|
||||
'**Reason**: obsolete',
|
||||
'## RENAMED Requirements',
|
||||
'- FROM: `### Requirement: C`',
|
||||
'- TO: `### Requirement: D`',
|
||||
].join('\n')
|
||||
);
|
||||
expect(plan.unappliedContent).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('archive and validate report unapplied content', () => {
|
||||
let tempDir: string;
|
||||
let changeDir: string;
|
||||
let specsRoot: string;
|
||||
|
||||
beforeEach(async () => {
|
||||
tempDir = await fs.mkdtemp(path.join(os.tmpdir(), 'openspec-unapplied-'));
|
||||
specsRoot = path.join(tempDir, 'openspec', 'specs');
|
||||
changeDir = path.join(tempDir, 'openspec', 'changes', 'c');
|
||||
await fs.mkdir(path.join(changeDir, 'specs', 'billing'), { recursive: true });
|
||||
await fs.mkdir(specsRoot, { recursive: true });
|
||||
await fs.writeFile(path.join(changeDir, 'specs', 'billing', 'spec.md'), DELTA);
|
||||
});
|
||||
afterEach(async () => {
|
||||
await fs.rm(tempDir, { recursive: true, force: true });
|
||||
});
|
||||
|
||||
it('archive warns and still carries only the requirement', async () => {
|
||||
const [update] = await findSpecUpdates(changeDir, specsRoot);
|
||||
const built = await buildUpdatedSpec(update, 'c', { silent: true });
|
||||
|
||||
expect(built.warnings).toContain(
|
||||
'billing - section "## Acceptance Criteria" (line 12) is not carried into the main spec.'
|
||||
);
|
||||
expect(built.rebuilt).toContain('### Requirement: Invoices');
|
||||
expect(built.rebuilt).not.toContain('Acceptance Criteria');
|
||||
});
|
||||
|
||||
it('validate warns, which fails --strict', async () => {
|
||||
const report = await new Validator(true).validateChangeDeltaSpecs(changeDir);
|
||||
const lost = report.issues.filter((issue) => /not carried into the main spec/.test(issue.message));
|
||||
|
||||
expect(lost.map((issue) => [issue.level, issue.line])).toEqual([
|
||||
['WARNING', 2],
|
||||
['WARNING', 4],
|
||||
['WARNING', 12],
|
||||
]);
|
||||
expect(report.valid).toBe(false);
|
||||
});
|
||||
});
|
||||
@@ -76,14 +76,14 @@ function specDrivenTitles(): Record<string, string> {
|
||||
}
|
||||
|
||||
const EXPECTED_FUNCTION_HASHES: Record<string, string> = {
|
||||
getExploreSkillTemplate: 'c1fddb294758004936add586f5826694cb06175cff935b75fd3a8d92332332e6',
|
||||
getExploreSkillTemplate: 'efeb143c50472bf0db0f031f7fc9e32f3c2662b569101f6473b2cbbb99425367',
|
||||
getNewChangeSkillTemplate: '0e5035b7b42198afc430206a1dbc9579096650ef0813d85e837d5a6cd0b98a85',
|
||||
getContinueChangeSkillTemplate: '32ec5965a2e90c033c9285f57a73cd6f39a31b58029d7546dba3cb6ba4c091f7',
|
||||
getApplyChangeSkillTemplate: 'c4f02c29137e19b34bf1dc59146941b8c920214aa2cf3ce80ae7f1b982e976fc',
|
||||
getFfChangeSkillTemplate: 'd091600476a815ba99f69b446bcd46af5bf73d1c2810215a0c6196937d019cf6',
|
||||
getSyncSpecsSkillTemplate: 'bc80fe9b07eaa289e5eb8a3ce65eb7df722a16d864e37283c678220712e4f230',
|
||||
getOnboardSkillTemplate: '84258a06c0ca88de708a23dd74e9a17efe11eff63a071b3864c781dcd5a0a4b7',
|
||||
getOpsxExploreCommandTemplate: '5d11f8ecb4c457140a3e874a8bf7aa72674e922e698c208832b1f34d3c617719',
|
||||
getOpsxExploreCommandTemplate: '477c1a97cf272778953d1ad032113e3f20afaefa332a5c0882e536d919c176e2',
|
||||
getOpsxNewCommandTemplate: '6d504fef1e0d4ced7c423f4cc9d9d2cee11b1a6224edf685e06a3f0757e0ebff',
|
||||
getOpsxContinueCommandTemplate: '659596cef4859162707d8acbd411afb863110f34a7b90ad037bae5eb84dd858d',
|
||||
getOpsxApplyCommandTemplate: '9b733946aa7216b45d084bb5affd7071086db6f91282c3666731a17543825ff1',
|
||||
@@ -104,7 +104,7 @@ const EXPECTED_FUNCTION_HASHES: Record<string, string> = {
|
||||
};
|
||||
|
||||
const EXPECTED_GENERATED_SKILL_CONTENT_HASHES: Record<string, string> = {
|
||||
'openspec-explore': '7d80caf9cd25a2565ba190b1297f1631c7f2c2db5e614597b4284abc0118ea70',
|
||||
'openspec-explore': '92055ecb0f60011b76369defb441883a985fb798f4cb90397f956f73383deda2',
|
||||
'openspec-new-change': '27e09d43785953827efc9a98bb9d6cf06db48fe6abe7e1c049409fe5b5061323',
|
||||
'openspec-continue-change': '4edf6d8b220f09146029e7671f140b7ef94a666d8e29fe7ad78ce40bc7ec9017',
|
||||
'openspec-apply-change': '991a1d4687f1c8c7147d8c3b68cf14e9c515c6c53bb026356dac3b80e909b827',
|
||||
@@ -1253,6 +1253,20 @@ describe('skill templates split parity', () => {
|
||||
expect(text, variant).not.toContain('Loose prose left under `## Requirements` does NOT block');
|
||||
}
|
||||
});
|
||||
|
||||
// The explore capture table names spec-driven files; without this caveat an
|
||||
// agent on a custom schema offers to write artifacts it never defines (#2084).
|
||||
it('scopes the explore capture table to the default schema', () => {
|
||||
const variants = [
|
||||
['explore skill', getExploreSkillTemplate().instructions],
|
||||
['explore command', getOpsxExploreCommandTemplate().content],
|
||||
] as const;
|
||||
for (const [variant, text] of variants) {
|
||||
expect(text, variant).toContain(
|
||||
"The files below are the default spec-driven schema's. With another schema, capture each insight in whichever artifact from `artifactPaths` fits it"
|
||||
);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('apply skill/command shared instruction core', () => {
|
||||
|
||||
@@ -22,8 +22,8 @@ import { fileURLToPath } from 'node:url';
|
||||
// unless you ask" is the wording this very test recommends.
|
||||
const REPO_ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
|
||||
|
||||
// The pages that pitch explore to a user. Both trees ship: `docs/` is what
|
||||
// README links today, `docs-lab/` is what the documentation site publishes
|
||||
// The pages that pitch explore to a user. Both trees ship: `docs/` is the
|
||||
// legacy tree still readable on GitHub, `docs-lab/` is what the documentation site publishes
|
||||
// (`website/content/docs` is generated from it and is not committed).
|
||||
const PAGES = [
|
||||
'README.md',
|
||||
|
||||
+11
-11
@@ -13,22 +13,22 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"beautiful-mermaid": "^1.1.3",
|
||||
"fumadocs-core": "^16.15.17",
|
||||
"fumadocs-mdx": "^15.3.1",
|
||||
"fumadocs-ui": "^16.15.17",
|
||||
"lucide-react": "^1.34.0",
|
||||
"fumadocs-core": "^16.16.1",
|
||||
"fumadocs-mdx": "^15.4.6",
|
||||
"fumadocs-ui": "^16.16.1",
|
||||
"lucide-react": "^1.52.0",
|
||||
"next": "16.3.8",
|
||||
"react": "^19.2.7",
|
||||
"react-dom": "^19.2.7",
|
||||
"zod": "^4.4.3"
|
||||
"react": "^19.3.0",
|
||||
"react-dom": "^19.3.0",
|
||||
"zod": "^4.6.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@tailwindcss/postcss": "^4.3.1",
|
||||
"@types/mdx": "^2.0.14",
|
||||
"@types/node": "^26.3.0",
|
||||
"@types/react": "^19.2.18",
|
||||
"@types/react-dom": "^19.2.7",
|
||||
"postcss": "^8.5.28",
|
||||
"@types/node": "^26.6.4",
|
||||
"@types/react": "^19.3.0",
|
||||
"@types/react-dom": "^19.3.0",
|
||||
"postcss": "^8.5.29",
|
||||
"serve": "^14.2.6",
|
||||
"tailwindcss": "^4.3.1",
|
||||
"typescript": "^6.0.3"
|
||||
|
||||
Generated
+518
-531
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user