diff --git a/openspec/specs/cli-rules/spec.md b/openspec/specs/cli-rules/spec.md index e6794a6b..c8623f24 100644 --- a/openspec/specs/cli-rules/spec.md +++ b/openspec/specs/cli-rules/spec.md @@ -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 ` SHALL NOT work in v0.7.0 — there is no compatibility alias. @@ -34,7 +34,13 @@ The `taskless rule create` command SHALL accept a `--from ` 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 @@ -52,7 +58,13 @@ The `taskless rule create` command SHALL accept a `--from ` 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 @@ -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/.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//.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/.yml` SHALL exist on disk +- **THEN** `.taskless/rules/sg//.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/.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//.tests/`, regardless of whether `--anonymous` was set. (Renamed; strengthened; repathed for the rule-directory layout.) + +#### Scenario: Tests land inside the rule they cover + +- **WHEN** `taskless rule create` generates test cases for rule `` +- **THEN** the CLI SHALL write them under `.taskless/rules/sg//.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: "", 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: "", 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 ` 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 ` SHALL remove the corresponding rule file and any test files. (Renamed.) Accepts `--anonymous` as a no-op. +`taskless rule delete ` SHALL remove the rule and everything that defines it. Under the rule-directory layout that is one directory, `.taskless/rules///`, 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 ` accepts the rule ID as a positional argument per the existing requirement. (Renamed.) +`taskless rule delete ` 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 @@ -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. + +#### 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/.yml` -4. Write any generated test files to `.taskless/rule-tests/.yml` +3. Write the rule file to `.taskless/rules/sg//.yml` +4. Write any generated test files into that rule's own directory, under `.taskless/rules/sg//.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) @@ -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 @@ -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 @@ -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 strings — code that should NOT trigger the rule) and `invalid` (array of strings — code 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 diff --git a/openspec/specs/cli-update-engine/spec.md b/openspec/specs/cli-update-engine/spec.md deleted file mode 100644 index 1ce0f1ab..00000000 --- a/openspec/specs/cli-update-engine/spec.md +++ /dev/null @@ -1,9 +0,0 @@ -# CLI Update Engine - -## Purpose - -This capability has been removed. The `update-engine` subcommand and all associated backend endpoints have been decommissioned. The CLI no longer manages scaffold upgrades. - -## Requirements - -_All requirements have been removed. This spec is retained for historical reference._ diff --git a/openspec/specs/infrastructure/spec.md b/openspec/specs/infrastructure/spec.md index 0a49ce8b..0c3da89d 100644 --- a/openspec/specs/infrastructure/spec.md +++ b/openspec/specs/infrastructure/spec.md @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 diff --git a/openspec/specs/skills/spec.md b/openspec/specs/skills/spec.md index 0b8dc956..894ae640 100644 --- a/openspec/specs/skills/spec.md +++ b/openspec/specs/skills/spec.md @@ -10,7 +10,7 @@ Defines the structure, conventions, and distribution model for Taskless skills, The single skill SHALL be defined at `skills/taskless/SKILL.md` with YAML frontmatter (`name`, `description`, `metadata`) followed by markdown instructions. The `name` field SHALL be exactly `taskless` (no per-task prefix). The `metadata` field SHALL include `author`, `version`, and `commandName: tskl` keys. The `version` SHALL be used for staleness detection when the skill is installed into target repositories. -The skill body SHALL begin by instructing the agent that it does NOT have step-by-step instructions for any Taskless action and that recipes must be fetched via `npx @taskless/cli help ` before proceeding. The body SHALL NOT contain inline step-by-step recipes for any individual task — those live in `packages/cli/src/help/.txt` files served by the help subcommand. +The skill body SHALL begin by instructing the agent that it does NOT have step-by-step instructions for any Taskless action and that recipes must be fetched via `npx @taskless/cli agent ` before proceeding. The body SHALL NOT contain inline step-by-step recipes for any individual task — those live in `packages/cli/src/help/.txt` files served by the help subcommand. #### Scenario: Skill directory contains valid SKILL.md @@ -23,10 +23,10 @@ The skill body SHALL begin by instructing the agent that it does NOT have step-b #### Scenario: Skill body delegates to CLI help - **WHEN** the skill body is read -- **THEN** it SHALL instruct the agent to fetch the canonical recipe via `npx @taskless/cli help ` before performing any Taskless action +- **THEN** it SHALL instruct the agent to fetch the canonical recipe via `npx @taskless/cli agent ` before performing any Taskless action - **AND** SHALL NOT duplicate recipe content inline -## Distribution +**Distribution.** ### Requirement: Skills live in the standard discovery path @@ -59,7 +59,7 @@ The single skill name SHALL be `taskless` (without a per-task suffix). When inst ### Requirement: Commands directory contains Claude Code command files -The `commands/tskl/` directory SHALL contain exactly one command file (`tskl.md`) that maps to the consolidated skill. The command body SHALL accept a free-form `$ARGUMENTS` ask and route via the same flow as the skill (fetch `npx @taskless/cli help `, follow the recipe). When `$ARGUMENTS` is empty or ambiguous, the command body SHALL instruct the agent to ask the user what they want to do. +The `commands/tskl/` directory SHALL contain exactly one command file (`tskl.md`) that maps to the consolidated skill. The command body SHALL accept a free-form `$ARGUMENTS` ask and route via the same flow as the skill (fetch `npx @taskless/cli agent `, follow the recipe). When `$ARGUMENTS` is empty or ambiguous, the command body SHALL instruct the agent to ask the user what they want to do. #### Scenario: Single command file exists