Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
162 changes: 115 additions & 47 deletions openspec/specs/cli-rules/spec.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@

## Purpose

Defines the `rules` subcommand group for the Taskless CLI, including `create`, `improve`, `delete`, and `verify` subcommands for managing ast-grep rules. Also documents the server-side API contract for rule generation endpoints.
Defines the `rules` subcommand group for the Taskless CLI, including `create`, `improve`, `delete`, and `meta` subcommands for managing ast-grep rules. Also documents the server-side API contract for rule generation endpoints.

## Requirements

### Requirement: Rules subcommand group exists

The CLI SHALL expose the rule operations under the `rule` (singular) subcommand group. The user-facing surface SHALL be `taskless rule create`, `taskless rule improve`, `taskless rule delete`, `taskless rule verify`, and `taskless rule meta`. The internal source filename (`packages/cli/src/commands/rules.ts`) MAY remain plural — only the user-visible subcommand name changes.
The CLI SHALL expose the rule operations under the `rule` (singular) subcommand group. The user-facing surface SHALL be `taskless rule create`, `taskless rule improve`, `taskless rule delete`, and `taskless rule meta`. Rule validation is not part of this group — it is addressed by path through the top-level `verify` and `test` commands, specified by the `cli-rule-validation` capability. The internal source filename (`packages/cli/src/commands/rules.ts`) MAY remain plural — only the user-visible subcommand name changes.

The previous plural form `taskless rules <subcommand>` SHALL NOT work in v0.7.0 — there is no compatibility alias.

Expand All @@ -34,7 +34,13 @@ The `taskless rule create` command SHALL accept a `--from <file>` flag specifyin

### Requirement: Rules create resolves identity from JWT and git remote

`taskless rule create` resolves user identity from the stored JWT and the git remote per the existing identity resolution requirements. (Renamed to singular.)
`taskless rule create` SHALL resolve user identity from the stored JWT and the git remote per the existing identity resolution requirements. (Renamed to singular.)

#### Scenario: Identity comes from the token and the remote

- **WHEN** an authenticated user runs `taskless rule create`
- **THEN** the CLI SHALL take the organization from the stored JWT
- **AND** it SHALL take the repository from the git remote rather than prompting for either

### Requirement: Rules create requires authentication

Expand All @@ -52,7 +58,13 @@ The `taskless rule create` command SHALL accept a `--from <file>` flag specifyin

### Requirement: Rules create submits to API and polls for results

`taskless rule create` (without `--anonymous`) submits to the API and polls per the existing requirement. (Renamed to singular.)
`taskless rule create` without `--anonymous` SHALL submit the request to the API and poll for the result per the existing requirement. (Renamed to singular.)

#### Scenario: Submission returns a request to poll

- **WHEN** an authenticated user runs `taskless rule create` without `--anonymous`
- **THEN** the CLI SHALL submit the request to the API
- **AND** it SHALL poll for the result until the generation completes or fails

### Requirement: Rules create uses a network interface with stub

Expand All @@ -70,60 +82,128 @@ The API calls for rule generation (`POST /cli/api/request` and `GET /cli/api/req

### Requirement: Rules create writes rule files to disk

`taskless rule create` SHALL write the generated rule file to `.taskless/rules/<id>.yml` regardless of whether `--anonymous` was set. The agent invoking the command SHALL NOT be expected to write rule files itself. (Renamed to singular; this strengthens the existing requirement to apply to both branches.)
`taskless rule create` SHALL write the generated rule file into the rule's own directory, at `.taskless/rules/sg/<id>/<id>.yml`, regardless of whether `--anonymous` was set. The agent invoking the command SHALL NOT be expected to write rule files itself. (Renamed to singular; this strengthens the existing requirement to apply to both branches.)

#### Scenario: Both branches write rule files

- **WHEN** `taskless rule create` succeeds (with or without `--anonymous`)
- **THEN** `.taskless/rules/<id>.yml` SHALL exist on disk
- **THEN** `.taskless/rules/sg/<id>/<id>.yml` SHALL exist on disk

### Requirement: Rules create writes test files to disk

`taskless rule create` SHALL write generated test files to `.taskless/rule-tests/<id>.yml` regardless of whether `--anonymous` was set. (Renamed; strengthened.)
`taskless rule create` SHALL write generated test files into the rule's own directory, at `.taskless/rules/sg/<id>/.tests/`, regardless of whether `--anonymous` was set. (Renamed; strengthened; repathed for the rule-directory layout.)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Two path inconsistencies introduced/exposed by this repath:

  1. The sibling requirement above ("Rules create writes rule files to disk", unchanged, ~line 85) still writes the rule file to the flat path .taskless/rules/<id>.yml, while this requirement now nests test files under .taskless/rules/sg/<id>/.tests/. Per the "Rules delete" requirement below (also repathed by this PR, line 182), the rule file itself is expected to live inside that same .taskless/rules/<engine>/<id>/ directory — and the actual implementation (packages/cli/src/rules/files.ts) agrees, writing to .taskless/rules/sg/{kebab-id}/{kebab-id}.yml. create and delete now describe two different locations for the rule file.
  2. The anonymous local-only flow further down (line 270, 4. Write any generated test files to \.taskless/rule-tests/.yml`) still uses the old pre-layout path and wasn't updated to match this new .taskless/rules/sg//.tests/` convention.


Comment on lines 92 to +95
#### Scenario: Tests land inside the rule they cover

- **WHEN** `taskless rule create` generates test cases for rule `<id>`
- **THEN** the CLI SHALL write them under `.taskless/rules/sg/<id>/.tests/`
- **AND** it SHALL do so whether or not `--anonymous` was set

### Requirement: Rules create outputs results

`taskless rule create` outputs results per the existing requirement. (Renamed to singular.) Output SHALL be human-readable by default; `--json` produces machine-readable output. On failure with `--json` set, the output SHALL be the standardized error envelope `{ ok: false, code: "<CODE>", message: "<...>" }` per the `cli` capability requirements.
`taskless rule create` SHALL output results per the existing requirement. (Renamed to singular.) Output SHALL be human-readable by default; `--json` produces machine-readable output. On failure with `--json` set, the output SHALL be the standardized error envelope `{ ok: false, code: "<CODE>", message: "<...>" }` per the `cli` capability requirements.

#### Scenario: Failure under --json uses the error envelope

- **WHEN** `taskless rule create --json` fails
- **THEN** the CLI SHALL print `{ ok: false, code, message }` rather than prose

### Requirement: Rules create shows progress during polling

`taskless rule create` shows progress per the existing requirement when polling the API (the `--anonymous` branch does not poll an API and SHOULD show progress for the local agent-driven steps if applicable). (Renamed to singular.)
`taskless rule create` SHALL show progress while polling the API. The `--anonymous` branch polls nothing and SHOULD show progress for the local agent-driven steps where applicable. (Renamed to singular.)

#### Scenario: Polling reports progress

- **WHEN** `taskless rule create` is waiting on the API
- **THEN** the CLI SHALL report progress rather than appearing to hang

### Requirement: Rules improve reads request from file

`taskless rule improve` SHALL accept a `--from <file>` flag specifying a JSON file containing the iterate request. (Renamed to singular.)

#### Scenario: The request is read from the named file

- **WHEN** a user runs `taskless rule improve --from request.json`
- **THEN** the CLI SHALL read the iterate request from that file

### Requirement: Rules improve requires authentication

`taskless rule improve` SHALL require authentication unless `--anonymous` is set. (Renamed; new anonymous branch.)

#### Scenario: Authentication is required without --anonymous

- **WHEN** a logged-out user runs `taskless rule improve` without `--anonymous`
- **THEN** the CLI SHALL exit non-zero and direct the user to authenticate

#### Scenario: The anonymous branch skips authentication

- **WHEN** a logged-out user runs `taskless rule improve --anonymous`
- **THEN** the CLI SHALL run the local-only flow without requiring a login

### Requirement: Rules improve submits to iterate API and polls for results

`taskless rule improve` (without `--anonymous`) submits and polls per the existing requirement. (Renamed.)
`taskless rule improve` without `--anonymous` SHALL submit to the iterate API and poll for the result per the existing requirement. (Renamed.)

#### Scenario: Submission returns a request to poll

- **WHEN** an authenticated user runs `taskless rule improve` without `--anonymous`
- **THEN** the CLI SHALL submit to the iterate API
- **AND** it SHALL poll until the iteration completes or fails

### Requirement: Rules improve writes updated files to disk

`taskless rule improve` SHALL write updated rule files to disk in both branches. (Renamed; strengthened.)

#### Scenario: Both branches persist the updated rule

- **WHEN** `taskless rule improve` completes, with or without `--anonymous`
- **THEN** the CLI SHALL write the updated rule to its canonical location on disk

### Requirement: Rules improve outputs results

`taskless rule improve` outputs results per the existing requirement. (Renamed.) Failure output with `--json` SHALL use the standardized error envelope.
`taskless rule improve` SHALL output results per the existing requirement. (Renamed.) Failure output with `--json` SHALL use the standardized error envelope.

#### Scenario: Failure under --json uses the error envelope

- **WHEN** `taskless rule improve --json` fails
- **THEN** the CLI SHALL print `{ ok: false, code, message }`

### Requirement: Rules improve has an agent recipe

### Requirement: Rules improve has a help entry
`taskless agent improve-rule` SHALL return the recipe per `cli-help` requirements. The recipe file is `improve-rule.txt`, with an `improve-rule.anonymous.txt` variant for the local-only flow.

`taskless help rule improve` SHALL return the recipe per `cli-help` requirements. (Renamed; the help filename becomes `rule-improve.txt` with an optional `rule-improve.anonymous.txt` variant.)
#### Scenario: The recipe resolves by its single-token name

- **WHEN** a user runs `taskless agent improve-rule`
- **THEN** the CLI SHALL print the contents of `improve-rule.txt`

### Requirement: Rules delete removes rule and test files

`taskless rule delete <id>` SHALL remove the corresponding rule file and any test files. (Renamed.) Accepts `--anonymous` as a no-op.
`taskless rule delete <id>` SHALL remove the rule and everything that defines it. Under the rule-directory layout that is one directory, `.taskless/rules/<engine>/<id>/`, which carries the rule, any per-engine config, and its tests. (Renamed; repathed.) Accepts `--anonymous` as a no-op.

#### Scenario: Deleting a rule removes its whole directory

- **WHEN** a user runs `taskless rule delete no-eval`
- **THEN** the CLI SHALL remove the rule's directory including its `.tests/`
- **AND** no file belonging to that rule SHALL remain

### Requirement: Rules delete does not require authentication

`taskless rule delete` does not require authentication per the existing requirement. (Renamed.)
`taskless rule delete` SHALL NOT require authentication. Deleting a local file is not a service operation. (Renamed.)

#### Scenario: Deletion works logged out

- **WHEN** a logged-out user runs `taskless rule delete no-eval`
- **THEN** the CLI SHALL delete the rule without requiring a login

### Requirement: Rules delete accepts the id argument

`taskless rule delete <id>` accepts the rule ID as a positional argument per the existing requirement. (Renamed.)
`taskless rule delete <id>` SHALL accept the rule ID as a positional argument per the existing requirement. (Renamed.)

#### Scenario: The id is positional

- **WHEN** a user runs `taskless rule delete no-eval`
- **THEN** the CLI SHALL treat `no-eval` as the rule ID

### Requirement: Codegen script fetches official ast-grep rule schema

Expand Down Expand Up @@ -160,49 +240,34 @@ The codegen script SHALL extract the ast-grep version from `packages/cli/package
- **THEN** the codegen script SHALL exit with a non-zero code and a descriptive error message
- **AND** SHALL NOT overwrite an existing generated schema file

### Requirement: The rule subcommand group no longer validates rules

`taskless rule verify` SHALL NOT exist. Rule validation is addressed by path through the top-level `verify` and `test` commands, specified by the `cli-rule-validation` capability.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This new requirement states taskless rule verify SHALL NOT exist, but two unchanged passages elsewhere in this same file still assume it does:

  • The "Generated schema is importable at build time" scenario a few lines below ("Schema imported in verify command") still reads: **WHEN** the \rule verify` command needs the ast-grep schema`.
  • "Rule improve supports anonymous local-only flow" (further down) still says the flow supports the feedback loop "by exposing the rule verify primitive that the agent invokes between edits."

Since this PR is specifically about making every requirement internally consistent and strict-clean, these two leftover references to the removed rule verify command are worth fixing in the same pass (the codegen-schema scenario should probably reference verify/test from cli-rule-validation instead, matching the wording used here).


#### Scenario: The removed subcommand does not resolve

- **WHEN** a user runs `taskless rule verify no-eval`
- **THEN** the CLI SHALL exit non-zero
- **AND** it SHALL NOT validate a rule

### Requirement: Generated schema is importable at build time

The generated JSON Schema file SHALL be importable by the CLI bundle via Vite. The import SHALL make the full JSON Schema object available at runtime without filesystem reads or network fetches.

#### Scenario: Schema imported in verify command

- **WHEN** the `rule verify` command needs the ast-grep schema
- **WHEN** the `verify` command needs the ast-grep schema
- **THEN** it SHALL import the schema from `../generated/ast-grep-rule-schema.json`
- **AND** the schema object SHALL be available synchronously at runtime

### Requirement: Verify subcommand validates rules against ast-grep schema

`taskless rule verify` SHALL validate rules against the ast-grep schema per the existing requirement. (Renamed from `rules verify` to `rule verify`.) Accepts `--anonymous` as a no-op.

### Requirement: Verify performs three layers of validation

`taskless rule verify` performs the three layers of validation per the existing requirement. (Renamed.)

### Requirement: Verify supports JSON output

`taskless rule verify --json` outputs results in the documented JSON shape. On failure, the standardized error envelope is used. (Renamed.)

### Requirement: Verify schema mode dumps combined schema for agent consumption

The `taskless rule verify --schema` mode is REMOVED in v0.7.0 — schemas are now embedded in `tskl help rule create` recipe output via `zod-to-json-schema`. (Renamed and superseded.)

#### Scenario: --schema flag is no longer accepted

- **WHEN** a user runs `taskless rule verify --schema`
- **THEN** the CLI SHALL exit with an error indicating the flag is unknown

### Requirement: Verify respects global flags

`taskless rule verify` respects global flags including `--dir` per the existing requirement. (Renamed.) Also accepts the new `--anonymous` flag as a no-op.

### Requirement: Rule create supports anonymous local-only flow

When `taskless rule create --anonymous` is invoked, the CLI SHALL execute the local-only rule-creation flow (previously implemented as the `taskless-create-rule-anonymous` skill body). The flow SHALL:

1. NOT submit any request to the Taskless API
2. Generate the ast-grep rule using local logic (Claude SDK, agent-driven generation, or whatever the migrated implementation prefers — see design.md)
3. Write the rule file to `.taskless/rules/<id>.yml`
4. Write any generated test files to `.taskless/rule-tests/<id>.yml`
3. Write the rule file to `.taskless/rules/sg/<id>/<id>.yml`
4. Write any generated test files into that rule's own directory, under `.taskless/rules/sg/<id>/.tests/`
5. NOT write a metadata sidecar (the API-backed branch does)
6. Return the same output format as the API-backed branch (paths to created files)

Expand All @@ -223,7 +288,7 @@ When `taskless rule improve --anonymous` is invoked, the CLI SHALL execute the l

1. NOT submit any request to the Taskless API iterate endpoint
2. Update the rule file in place using local logic
3. Support the verify feedback loop by exposing the `rule verify` primitive that the agent invokes between edits
3. Support the verify feedback loop by exposing the top-level `verify` primitive that the agent invokes between edits
4. Return the same output format as the API-backed branch

#### Scenario: rule improve --anonymous skips API
Expand All @@ -232,7 +297,10 @@ When `taskless rule improve --anonymous` is invoked, the CLI SHALL execute the l
- **THEN** the CLI SHALL NOT make any HTTP request to the Taskless API
- **AND** SHALL update the target rule file

## API Contract
**API contract.** The requirements below describe the service endpoints the
`rule` subcommands call. They are grouped by a bold line rather than a
heading: a second `##` inside this section ends it, and everything after it
stops being read as a requirement.

### Requirement: Rule generation request endpoint accepts a request and returns a requestId

Expand Down Expand Up @@ -352,7 +420,7 @@ Each rule in the `rules` array SHALL contain an `id` (string), a `content` objec

### Requirement: Generated rules may include test cases

Each rule in the `rules` array MAY include a `tests` object containing `valid` (array of stringscode that should NOT trigger the rule) and `invalid` (array of stringscode that SHOULD trigger the rule).
Each rule in the `rules` array MAY include a `tests` object. When present it SHALL contain `valid` (array of strings, code that must not trigger the rule) and `invalid` (array of strings, code that must trigger it).

#### Scenario: Rule with test cases

Expand Down
9 changes: 0 additions & 9 deletions openspec/specs/cli-update-engine/spec.md

This file was deleted.

14 changes: 9 additions & 5 deletions openspec/specs/infrastructure/spec.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,11 @@

Defines build tooling, CI pipelines, and repository configuration including version sync, command generation, Turborepo setup, and GitHub Actions workflows.

## Build Tooling
## Requirements

**Build tooling.** Grouped by a bold line rather than a heading: a second `##`
inside the requirements section ends it, and every requirement after it
stops being read.

### Requirement: tsx is available for build scripts

Expand Down Expand Up @@ -41,7 +45,7 @@ A `scripts/sync-skill-versions.ts` script SHALL read the version from `packages/

### Requirement: Slash command files are hand-authored

Since the v0.7 consolidation, the single `commands/tskl/tskl.md` slash command is hand-authored rather than generated from a `SKILL.md` body. The command body intentionally differs from the skill body (it is a `$ARGUMENTS`-aware router), so the prior "copy SKILL.md body to command" generation script no longer applies.
The single `commands/tskl/tskl.md` slash command SHALL be hand-authored rather than generated from a `SKILL.md` body. Its body intentionally differs from the skill body (it is a `$ARGUMENTS`-aware router), so the prior "copy SKILL.md body to command" generation script SHALL NOT be reintroduced.

#### Scenario: Single hand-authored command file exists

Expand Down Expand Up @@ -84,7 +88,7 @@ The `packages/cli/package.json` SHALL NOT have a `release` script. Build and pub
- **WHEN** inspecting `packages/cli/package.json` scripts
- **THEN** there SHALL be no `release` key

## Repository Configuration
**Repository configuration.**

### Requirement: Turborepo is configured at the repo root

Expand Down Expand Up @@ -134,7 +138,7 @@ The root `pnpm typecheck` command SHALL invoke `turbo run typecheck`, which runs
- **WHEN** `pnpm typecheck` is run at the repo root
- **THEN** Turborepo SHALL execute `typecheck` in `@taskless/cli`

## Continuous Integration
**Continuous integration.**

### Requirement: CI workflow exists

Expand Down Expand Up @@ -247,7 +251,7 @@ The workflow SHALL NOT include any publish, release, or npm registry push steps.
- **WHEN** inspecting the workflow file
- **THEN** there SHALL be no steps that run `pnpm publish`, `npm publish`, or interact with an npm registry

## Requirements
**Release and publishing.**

### Requirement: Script-versioned packages are excluded from changesets

Expand Down
Loading