Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
63 commits
Select commit Hold shift + click to select a range
68ee85b
feat(lockfile): implement intent lockfile structure and serialization…
LadyBluenotes Jul 5, 2026
e6a1b4c
feat(hash): add hashing functions and tests for skill folder and sour…
LadyBluenotes Jul 5, 2026
45600e4
feat(hash): optimize file reading with file descriptor management
LadyBluenotes Jul 5, 2026
a1f1309
feat(lockfile): implement lockfile diffing and state management
LadyBluenotes Jul 5, 2026
56ff8af
feat: optimize sorting and comparison functions across multiple files
LadyBluenotes Jul 5, 2026
915b1a8
feat(skills): implement scan and diff commands for skills management
LadyBluenotes Jul 5, 2026
b982fa0
feat(skills): enhance scan and diff commands to handle unlisted skill…
LadyBluenotes Jul 6, 2026
7959986
feat(skills): add 'approve' action to skills command for managing int…
LadyBluenotes Jul 6, 2026
e7269ac
refactor(lockfile): change export to const and update interface visib…
LadyBluenotes Jul 6, 2026
e330d7c
feat: implement frozen mode checks to prevent network calls and subpr…
LadyBluenotes Jul 6, 2026
4dba814
feat(skills): add 'update' action to skills command for managing inte…
LadyBluenotes Jul 6, 2026
8120e0d
refactor(skills): replace parseSourceArg with resolveSourceArg for im…
LadyBluenotes Jul 6, 2026
4c5179c
feat: add skills support to lockfile and related structures
LadyBluenotes Jul 6, 2026
a3bb1f3
feat(skills): enhance update command to report pending added/removed …
LadyBluenotes Jul 9, 2026
50adc0e
fix: update --no-frozen option behavior to override INTENT_FROZEN and…
LadyBluenotes Jul 9, 2026
aee4218
feat: add documentation for intent skills and lockfile management
LadyBluenotes Jul 9, 2026
54321cd
feat: add skills stale command for checking staleness of skills again…
LadyBluenotes Jul 9, 2026
db658f1
feat: implement skills manifest generation and enhance CLI commands
LadyBluenotes Jul 9, 2026
5229dfa
feat: update documentation to include new `stale` and `generate-manif…
LadyBluenotes Jul 9, 2026
014c3cc
feat: enhance hashing logic to include reference files in skill folders
LadyBluenotes Jul 9, 2026
cc5f509
feat: enhance source content hash computation with support file modif…
LadyBluenotes Jul 9, 2026
0946533
feat: refactor file reading logic to use a customizable filesystem in…
LadyBluenotes Jul 9, 2026
cc3e63e
feat: enhance manifest reading and lockfile source building with cust…
LadyBluenotes Jul 9, 2026
4e10b0d
feat: improve package registration logic to handle duplicates and enh…
LadyBluenotes Jul 9, 2026
999deb8
feat: enhance package registrar and skill load path handling with pac…
LadyBluenotes Jul 9, 2026
24eb5de
feat: preserve staleness metadata in lockfile during approve and upda…
LadyBluenotes Jul 9, 2026
813c4af
feat: preserve metadata during source approval and update processes
LadyBluenotes Jul 9, 2026
4529da4
feat: enhance provenance tracking for skills and packages across comm…
LadyBluenotes Jul 9, 2026
13dc1fe
feat: update provenance messaging to indicate when provenance is unknown
LadyBluenotes Jul 9, 2026
1ba0f08
feat: enhance provenance tracking for direct and transitive skill pac…
LadyBluenotes Jul 9, 2026
af042bf
feat: enhance exclude matching to support kind-qualified package patt…
LadyBluenotes Jul 9, 2026
cba03c4
feat: implement validation for canonical package relative paths in lo…
LadyBluenotes Jul 9, 2026
daeea5e
refactor: simplify path validation logic in resolveCanonicalPackagePa…
LadyBluenotes Jul 9, 2026
00d1f37
feat: add canonicalization for manifest serialization and enhance MCP…
LadyBluenotes Jul 10, 2026
b686e87
feat: enhance currentBlobSha function with error handling for directo…
LadyBluenotes Jul 10, 2026
2f13bbe
feat: add validation for hash limits in source content and skill fold…
LadyBluenotes Jul 10, 2026
15f1d86
feat: add readSkillFolderContents function and update generateManifes…
LadyBluenotes Jul 10, 2026
d2d0a83
feat: update lockfile documentation and enhance frozen mode error mes…
LadyBluenotes Jul 10, 2026
d8c62c2
feat: update lockfile documentation to include consumer CI instructio…
LadyBluenotes Jul 10, 2026
5b545d5
feat: add lockfile scan benchmarks and update writeFile function to a…
LadyBluenotes Jul 10, 2026
5637107
feat: export lockfile types and add tests for public lockfile types
LadyBluenotes Jul 10, 2026
e00c502
feat: enhance documentation and error handling for lockfile and skill…
LadyBluenotes Jul 10, 2026
34d4644
feat: enhance skills generate manifest command to handle frozen mode …
LadyBluenotes Jul 10, 2026
570df5f
feat: enhance baseline drift computation to skip installed dependenci…
LadyBluenotes Jul 10, 2026
b43215b
feat: implement frozen mode checks for unlisted skill-bearing sources…
LadyBluenotes Jul 10, 2026
6dfdbb9
feat: enhance intent skills and lockfile management
LadyBluenotes Jul 10, 2026
8d43fd3
changeset
LadyBluenotes Jul 10, 2026
3d59f20
feat: enhance skills update command to require approval for trust-bea…
LadyBluenotes Jul 10, 2026
215e2db
ci: apply automated fixes
autofix-ci[bot] Jul 10, 2026
625e532
refactor: optimize package grouping in resolveSkillUse function and u…
LadyBluenotes Jul 10, 2026
39177d2
preserve non-UTF-8 bytes in skill hashes
LadyBluenotes Jul 12, 2026
7a88e87
show provenance for unlisted skill sources
LadyBluenotes Jul 12, 2026
6479c95
defer manifest generation
LadyBluenotes Jul 12, 2026
921ac87
format
LadyBluenotes Jul 12, 2026
699c73d
ci: apply automated fixes
autofix-ci[bot] Jul 12, 2026
1e15807
internalize manifest consumer helpers
LadyBluenotes Jul 12, 2026
648b5c2
defer lockfile staleness analysis
LadyBluenotes Jul 12, 2026
dffa662
align M2 trust and lockfile guidance
LadyBluenotes Jul 12, 2026
f400d31
qualify hidden sources by kind
LadyBluenotes Jul 12, 2026
3f8c4ff
correct lockfile hash scope comment
LadyBluenotes Jul 12, 2026
a3f752e
enforce reviewable lock approval and activation
LadyBluenotes Jul 12, 2026
65cfaf4
hide misleading frozen flag default
LadyBluenotes Jul 12, 2026
3e325fe
escape hidden source diagnostics
LadyBluenotes Jul 12, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions .changeset/many-forks-cough.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
---
'@tanstack/intent': minor
---

Add consumer-managed `intent.lock` files for reviewing and pinning the exact skill content approved for a project.

- Add `intent skills scan`, `diff`, `approve`, and `update` for inspecting drift, reviewing exact current text and binary summaries, and maintaining approved sources.
- Track sources by `(kind, id)` so same-named workspace and npm packages remain distinct approvals.
- Hash each skill’s `SKILL.md` plus supported `references/`, `assets/`, and `scripts/` files with deterministic SHA-256 content hashes.
- Add frozen-mode enforcement for CI through `--frozen`, `INTENT_FROZEN`, and non-interactive `CI` detection. Frozen mode rejects missing or malformed lockfiles, unapproved source changes, hidden skill-bearing sources, and lockfile mutations.
- When an `intent.lock` exists, reject drift during ordinary skill loading and agent catalog generation so interactive agents cannot silently consume content that differs from the approved state.
- Validate manifests against the installed package identity, skill paths, and content hashes. Manifest metadata changes appear in lockfile diffs and require approval before frozen checks pass.
- Export lockfile metadata types from `@tanstack/intent`.
19 changes: 19 additions & 0 deletions benchmarks/intent/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Intent Benchmarks

## Lockfile Scan Baseline

Run from the repository root:

```sh
pnpm --dir benchmarks/intent exec vitest bench --config ./vitest.config.ts ./lockfile-scan.bench.ts
```

Local baseline recorded on 2026-07-09:

| Case | Fixture | Mean |
| ----------------- | --------------------------------------------------------------------- | ---------: |
| Clean lockfile | 8 packages, 3 skills per package, 3 support files per skill at 1 KiB | 9.2180 ms |
| Changed skill | Same fixture, one `SKILL.md` changed after approval | 9.1127 ms |
| Large support set | 24 packages, 4 skills per package, 6 support files per skill at 8 KiB | 50.7923 ms |

The fixture creates `intent.lock` with `intent skills approve --all --yes` before each scan. Re-run these cases after changing lockfile discovery or hashing and compare the same fixture means.
2 changes: 1 addition & 1 deletion benchmarks/intent/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ export function createTempDir(name: string): string {
return mkdtempSync(join(tmpdir(), `intent-bench-${name}-`))
}

export function writeFile(filePath: string, content: string): void {
export function writeFile(filePath: string, content: string | Buffer): void {
mkdirSync(dirname(filePath), { recursive: true })
writeFileSync(filePath, content)
}
Expand Down
137 changes: 137 additions & 0 deletions benchmarks/intent/lockfile-scan.bench.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
import { rmSync } from 'node:fs'
import { join } from 'node:path'
import { afterAll, beforeAll, bench, describe } from 'vitest'
import {
createBenchOptions,
createCliRunner,
createConsoleSilencer,
createTempDir,
writeFile,
writeJson,
writePackage,
} from './helpers.js'

type LockfileFixture = {
root: string
runner: ReturnType<typeof createCliRunner>
}

type FixtureOptions = {
changedSkill?: boolean
packageCount: number
skillCount: number
supportFileCount: number
supportFileSize: number
}

function createFixture(name: string, options: FixtureOptions): LockfileFixture {
const root = createTempDir(name)
const packageNames = Array.from(
{ length: options.packageCount },
(_, index) => `@bench/lock-${index}`,
)
writeJson(join(root, 'package.json'), {
name: `intent-lockfile-${name}-benchmark`,
private: true,
intent: { skills: packageNames },
})

for (const packageName of packageNames) {
const skills = Array.from(
{ length: options.skillCount },
(_, index) => `skill-${index}`,
)
writePackage(join(root, 'node_modules'), packageName, '1.0.0', { skills })
const packageRoot = join(root, 'node_modules', ...packageName.split('/'))
for (const skillName of skills) {
const skillDir = join(packageRoot, 'skills', skillName)
for (let index = 0; index < options.supportFileCount; index++) {
const directory = ['references', 'assets', 'scripts'][index % 3]!
const extension = directory === 'scripts' ? 'mjs' : 'dat'
const content =
directory === 'assets'
? Buffer.alloc(options.supportFileSize, index)
: 'x'.repeat(options.supportFileSize)
writeFile(
join(skillDir, directory, `support-${index}.${extension}`),
content,
)
}
}
}

return { root, runner: createCliRunner({ cwd: root }) }
}

function defineScenario(name: string, options: FixtureOptions): void {
const consoleSilencer = createConsoleSilencer()
let fixture: LockfileFixture | null = null

async function setup(): Promise<void> {
if (fixture) return

consoleSilencer.silence()
fixture = createFixture(name, options)
await fixture.runner.setup()
await fixture.runner.run(['skills', 'approve', '--all', '--yes'])
if (options.changedSkill) {
writeFile(
join(
fixture.root,
'node_modules',
'@bench',
'lock-0',
'skills',
'skill-0',
'SKILL.md',
),
'---\nname: skill-0\ndescription: changed benchmark skill\n---\n\nChanged.\n',
)
}
}

function teardown(): void {
if (fixture) {
fixture.runner.teardown()
rmSync(fixture.root, { recursive: true, force: true })
fixture = null
}
consoleSilencer.restore()
}

describe(`intent skills scan --json (${name})`, () => {
beforeAll(setup)
afterAll(teardown)

bench(
'scans lockfile state',
async () => {
if (!fixture) throw new Error('Lockfile fixture was not initialized')
await fixture.runner.run(['skills', 'scan', '--json'])
},
createBenchOptions(setup, teardown),
)
})
}

defineScenario('clean-eight-packages', {
packageCount: 8,
skillCount: 3,
supportFileCount: 3,
supportFileSize: 1024,
})

defineScenario('changed-skill', {
changedSkill: true,
packageCount: 8,
skillCount: 3,
supportFileCount: 3,
supportFileSize: 1024,
})

defineScenario('large-support-set', {
packageCount: 24,
skillCount: 4,
supportFileCount: 6,
supportFileSize: 8 * 1024,
})
6 changes: 4 additions & 2 deletions docs/cli/intent-list.md
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,9 @@ The list as a whole has three special forms:
- **Empty** (`"skills": []`): no package is surfaced, with an info notice printed to stderr.
- **Wildcard** (`"skills": ["*"]`): every discovered package is surfaced, with an acknowledged-risk notice printed to stderr.

A package that ships skills but is not listed is dropped. When packages are dropped this way, Intent prints one summary line naming them so you can opt in. In agent sessions, hidden sources are reported by count only; run `intent list --show-hidden` outside the agent session to review candidates. A listed package that was not discovered is reported as well. Matching is currently by package name. See [Configuration](../concepts/configuration) and [Trust model](../concepts/trust-model).
A package that ships skills but is not listed is dropped. Human-facing output includes bounded dependency provenance when available, otherwise `provenance unknown`. In agent sessions, hidden sources are reported by count only; run `intent list --show-hidden` outside the agent session to review candidates. A listed package that was not discovered is reported as well. Matching uses `(kind, id)`. See [Configuration](../concepts/configuration) and [Trust model](../concepts/trust-model).

When an npm and workspace source share a name and the requested skill, `intent load` rejects the unqualified use as ambiguous.

## Excludes

Expand All @@ -133,7 +135,7 @@ Manage persistent excludes with `intent exclude add|remove|list`.
}
```

A pattern without `#` excludes a whole package. A pattern with `#` excludes a single skill (`@scope/pkg#search-params`), and the skill segment may itself be a glob (`@scope/pkg#experimental-*`). A pattern may cross package boundaries at skill granularity (`*#experimental-*`). The `#*` shortcut (`@scope/pkg#*`) excludes the whole package. Only exact names and `*` wildcards are supported on each segment. Bare package-name patterns keep working unchanged.
A pattern without `#` excludes a whole package. A pattern with `#` excludes a single skill (`@scope/pkg#search-params`), and the skill segment may itself be a glob (`@scope/pkg#experimental-*`). A pattern may cross package boundaries at skill granularity (`*#experimental-*`). The `#*` shortcut (`@scope/pkg#*`) excludes the whole package. Prefix a package segment with `npm:` or `workspace:` to target one source kind. Only exact names and `*` wildcards are supported on each segment. Bare package-name patterns keep working unchanged.

An excluded package never triggers the unlisted-source warning, because an exclude is an explicit decision rather than an oversight.

Expand Down
95 changes: 95 additions & 0 deletions docs/cli/intent-skills.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
---
title: intent skills
id: intent-skills
---

`intent skills` manages `intent.lock`, the committed record of which skill-bearing sources you've approved and what their content looked like when you approved it. Four subcommands: `scan`, `diff` (read-only), `approve`, and `update` (mutating).

```bash
npx intent skills <scan|diff|approve|update> [source] [--json] [--all] [--yes] [--frozen] [--no-frozen]
```

See [Lockfile and frozen mode](../security/lockfile) for what `intent.lock` is and what frozen mode guarantees.

## `intent skills scan`

```bash
npx intent skills scan [--json] [--frozen] [--no-frozen]
```

Read-only. Discovers current skill-bearing sources, computes each source's `contentHash`, and reports drift against `intent.lock`.

- No lock found: prints `No intent.lock found. Run \`intent skills approve --all\` to create one.`
- Lock is clean: prints `intent.lock is up to date.`
- Lock is stale: prints `intent.lock is out of date: N added, N removed, N changed.`
- Discovered sources not in `intent.skills`: names each source with bounded dependency provenance when available, falls back to `provenance unknown`, and points at `intent.skills`/`intent.exclude`. Agent-mode output remains count-only.
- `--json` prints `{ frozen, hiddenSourceCount, hasLockfile, added, removed, changed, isClean }`

## `intent skills diff`

```bash
npx intent skills diff [--json] [--frozen] [--no-frozen]
```

Read-only. Same underlying computation as `scan`, but change-focused: prints `Added:`/`Removed:`/`Changed:` sections with per-field diffs (`version`, `resolution`, `skills`, `contentHash`, `manifestHash`, `capabilities`). It then displays the complete current canonical text for every added or changed source. Binary files are summarized by path, byte length, and hash. Control and bidirectional characters are escaped in the line-numbered text display so package content cannot manipulate terminal output. Unchanged sources are omitted.

```
Changed:
~ npm:@acme/query
version: "1.0.0" -> "1.1.0"
resolution: "npm:@acme/query@1.0.0" -> "npm:@acme/query@1.1.0"
contentHash: "sha256-492ac4..." -> "sha256-2631b3..."
```

## `intent skills approve [source]`

```bash
npx intent skills approve [source] [--all] [--yes]
```

Writes `intent.lock`. This is the trust decision. Before any prompt or non-interactive write, Intent displays the current canonical text and binary summaries for the affected sources. Removed sources display their locked skill paths and aggregate hash because the old file bytes are not stored in `intent.lock`.

- **No arg, no `--all`/`--yes`:** interactive per-pending-change prompt (approve/skip each). Fails if stdin isn't a TTY.
- **`--all` or `--yes`:** displays every pending change, then accepts them without prompting. This is the first-run path that creates the initial lock.
- **A single source:** `approve npm:@tanstack/query`, `approve workspace:my-package`, or a bare name (`approve foo`) if it resolves unambiguously against currently-discovered sources. Two sources sharing a bare name across kinds (`npm:foo` and `workspace:foo`) error instead of guessing — pass `kind:id` explicitly.
- Re-serializes the whole file deterministically: identical inputs produce a byte-identical `intent.lock`.
- Only touches the targeted entry (single-source form) or all pending changes (`--all`/`--yes`) — never silently drops an entry you didn't act on.
- Refuses in frozen mode (exit `5`).

## `intent skills update [source]`

```bash
npx intent skills update [source] [--all] [--yes]
```

Writes `intent.lock`. It mechanically re-syncs version and resolution for matching **already-locked** entries. Changes to skills, content hashes, manifests, capabilities, declared secrets, or MCP metadata require `--yes`; before writing them, `update` displays the same current content review as `diff` and `approve`.

- Only touches sources present in **both** the lock and the current scan. It never adds a newly-discovered source (that's `approve`'s job) and never drops a source that's no longer discovered (also `approve`'s job — removing a source from the trust boundary is itself a trust decision).
- Reports pending added/removed drift it didn't touch: `N added, M removed source(s) still pending. Run \`intent skills approve\` to review.`
- Makes zero network calls and zero subprocess calls — it only reads what's already on disk.
- Refuses in frozen mode (exit `5`).

## Options

- `--json`: with `scan`/`diff`, print the structured diff instead of text
- `--all`: with `approve`/`update`, act on all pending changes without prompting
- `--yes`: with `approve`, accept all pending changes non-interactively; with `update`, accept reviewed trust-bearing changes
- `--frozen`: force frozen mode, regardless of `INTENT_FROZEN`/`CI` auto-detection
- `--no-frozen`: force interactive mode — overrides `INTENT_FROZEN` and the `CI` auto-detect (highest-precedence explicit override)

## Exit codes

| Code | Meaning |
| --- | --- |
| `0` | ok |
| `1` | generic CLI usage or parse error |
| `2` | drift found under frozen mode |
| `3` | unapproved/unlisted skill-bearing source found under frozen mode |
| `4` | no `intent.lock` found under frozen mode |
| `5` | `approve`/`update` refused because frozen mode disallows mutation |
| `6` | `intent.lock` is malformed or from an unsupported (newer) `lockfileVersion` |

## Related

- [Lockfile and frozen mode](../security/lockfile)
- [Trust model](../concepts/trust-model)
8 changes: 6 additions & 2 deletions docs/concepts/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,9 @@ Each array entry names one source:
| `workspace:@scope/pkg` | workspace | A package in the current workspace. |
| `git:<host>/<repo>#<ref>` | git | Reserved. Not yet supported, and rejected until a future version adds it. |

A malformed entry fails the whole command, and every bad entry is reported at once. Intent currently matches an allowlist entry against a discovered package by name. This matching will tighten in a future version.
A malformed entry fails the whole command, and every bad entry is reported at once. Intent matches a source by `(kind, id)`: `workspace:foo` never authorizes an npm-installed `foo`.

If both `npm:foo` and `workspace:foo` provide the same requested skill, `intent load foo#skill` fails as ambiguous instead of selecting one source by discovery order. Narrow the allowlist to one source before loading it.

### Special forms

Expand All @@ -40,7 +42,7 @@ The list as a whole has three special forms:
- **Empty.** `"skills": []`. No package is surfaced. Intent prints an info notice to stderr.
- **Wildcard.** `"skills": ["*"]`. Every discovered package is surfaced. Intent prints an acknowledged-risk notice to stderr, since unvetted skills may reach your agent.

A package that ships skills but is not listed is dropped. When packages are dropped this way, Intent prints one summary line naming them so you can opt in. A listed package that was not discovered is reported as well.
A package that ships skills but is not listed is dropped. Human-facing output includes bounded dependency provenance when available, otherwise `provenance unknown`. A listed package that was not discovered is reported as well.

### Existing projects

Expand Down Expand Up @@ -85,4 +87,6 @@ Pattern grammar:
- A pattern may cross package boundaries at skill granularity: `*#experimental-*`.
- The `#*` shortcut excludes the whole package: `@scope/pkg#*`.

Prefix a package segment with `npm:` or `workspace:` to target one source kind, for example `workspace:foo` or `npm:foo#experimental-*`. Bare package patterns remain broad and match either kind.

Only exact names and `*` wildcards are supported on each segment. Bare package-name patterns keep working unchanged. An excluded package does not trigger the unlisted-source warning, because an exclude is an explicit decision.
6 changes: 4 additions & 2 deletions docs/concepts/trust-model.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,10 @@ Intent reads package data as files. It never imports, requires, or executes the

One exception is sanctioned: in Yarn Plug'n'Play projects, Intent loads Yarn's PnP runtime (`.pnp.cjs`) to map package identities to readable locations. It loads no package entry points, bins, lifecycle scripts, or other package-provided JavaScript. An ESLint rule enforces this invariant in the discovery code.

## What the allowlist does not cover yet
## Source identity and remaining limits

Matching is currently by package name. A `workspace:foo` entry and a bare `foo` entry both authorize a discovered package named `foo`, because the scanner does not yet distinguish a workspace member from a published package of the same name. This errs toward permitting a same-named package, never toward denying one you listed. A future version tightens matching once the scanner carries that signal.
Intent matches allowlist entries by `(kind, id)`. `workspace:foo` authorizes only the workspace member named `foo`; it does not authorize an installed `npm:foo`. A bare `foo` entry is normalized to the npm source kind. Same-named workspace and npm packages remain distinct sources throughout discovery, locking, and approval.

Source trust and content approval remain separate decisions. When `intent.lock` exists, ordinary skill loading and agent catalog generation reject installed content that differs from the approved hashes. A missing lockfile does not silently approve anything; outside frozen mode it preserves the bootstrap path until the user reviews content with `intent skills diff` and writes the first lock with `intent skills approve`.

The `git:` source kind is reserved. Intent parses and validates the shape, then rejects it until a future version can pin the resolved ref and content hash. A git entry never loads silently.
8 changes: 8 additions & 0 deletions docs/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@
{
"label": "Configuration",
"to": "concepts/configuration"
},
{
"label": "Lockfile and frozen mode",
"to": "security/lockfile"
}
]
},
Expand Down Expand Up @@ -82,6 +86,10 @@
{
"label": "intent stale",
"to": "cli/intent-stale"
},
{
"label": "intent skills",
"to": "cli/intent-skills"
}
]
}
Expand Down
Loading
Loading