Skip to content
Merged
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
2 changes: 2 additions & 0 deletions .changeset/streamlined-onboarding-report.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,5 @@
---

`taskless detect --json` now reports ast-grep and Vale configurations as linters (Taskless's own configuration under `.taskless/` excluded), the command-line tools on the host's `PATH` (`gh`, `git`, `jq`, and now `glab` for GitLab repositories) under `hostTools`, and an `agent` section of commands an agent may choose to run to check whether a source is usable, such as `gh auth status`. Detection still runs nothing and stays offline. `taskless info` lists `glab` among its tools too.

New `taskless report` publishes an unlisted Taskless report to tskl.es: a page showing the rules in this repository, what they were drawn from, and what they are projected to catch. With no flags it prints a draft of what the CLI knows and the facts an agent still has to supply; `--from <facts.json>` checks there are at least three working Taskless rules, withholds any fixture that looks like it holds a secret or contact detail, validates against the tskl.es schema, publishes, and records the report in `.taskless/reports/` (gitignored, since each record holds the URL that deletes it); `--delete <record>` takes a report down. The new `report` agent topic walks an agent through it. Migration 11 creates `.taskless/reports/`.
1 change: 1 addition & 0 deletions .taskless/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@
/.vale.ini
/.sgconfig.yml
/.tmp-*
/reports/
2 changes: 1 addition & 1 deletion .taskless/taskless.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"version": 10,
"version": 11,
"install": {
"targets": {
".taskless": {
Expand Down
4 changes: 2 additions & 2 deletions openspec/changes/streamlined-onboarding-report/design.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ agentCatches[tier] = round(Σ rule.fixed × 30 / d)

`contextTokens` and `callsPerCatch` are agent estimates from this user's session habits (how large their sessions run, how many extra turns a missed issue costs). tskl.es exposes them as sliders, so the published page lets a reader adjust them, and turns them into its token figures itself. The other inputs (`cloudComplexity`, `pricePerMillionTokens`, `minutesPerComment`, `reviewerHourlyUsd`) are CLI constants, matching the schema's example values until there is a reason to differ.

The gate is `≥ 3` Taskless rules: `cli` tier, or `legacy` rules the agent wrote into the repository's own linter, with tests neither `fail` nor `refused`. Cloud candidates are carried in addition and never count. The projection does not gate: the page lets a reader adjust its inputs, so a threshold on it would gate on a number nobody sees. The gate lives in the CLI so it has one home.
The gate is `≥ 3` Taskless rules: `cli` tier, or `legacy` rules the agent wrote into the repository's own linter, and in either tier only those whose tests are neither `fail` nor `refused`. Cloud candidates are carried in addition and never count. The projection does not gate: the page lets a reader adjust its inputs, so a threshold on it would gate on a number nobody sees. The gate lives in the CLI so it has one home.

### D4. Tiers and engines

Expand Down Expand Up @@ -79,7 +79,7 @@ For validation, the decision is zod 4's `z.fromJSONSchema()` over the vendored d

### D8. The record and the delete credential

`.taskless/reports/<UTC YYYY-MM-DD_HHMMSS>.json` holds `{ url, deleteUrl, expiresAt }` exactly as returned. The delete credential is the `deleteUrl` tskl.es returns once; the user's "delete key" is that value. It never reaches stdout or telemetry, and the directory is gitignored by migration 11. `report delete` reads the record and sends an HTTP `DELETE` to `deleteUrl`, the URL tskl.es returns for exactly that.
`.taskless/reports/<UTC YYYY-MM-DD_HHMMSS>.json` holds `{ url, deleteUrl, expiresAt }` exactly as returned. The delete credential is the `deleteUrl` tskl.es returns once; the user's "delete key" is that value. It never reaches stdout or telemetry, and the directory is gitignored by migration 11. `report --delete` reads the record and sends an HTTP `DELETE` to `deleteUrl`, per the schema's `x-taskless.delete` contract: 204 on success (caches may serve the page for up to a day), or 404 `not_available`, which is not retryable and gets its own code, `REPORT_NOT_AVAILABLE`, because its remedy differs from a failure's. The record survives a 404, since a report published moments ago can answer 404 before it has propagated.

### D9. Detect: one table for tools, presence, and hints

Expand Down
4 changes: 2 additions & 2 deletions openspec/changes/streamlined-onboarding-report/proposal.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ Onboarding is a conversation today: the agent asks which sources to mine, asks a
- **Onboarding is always anonymous.** It authors only rules the CLI can write logged out (`legacy`, `sg`, `vale`). Candidates the agent judges are better solved by a runtime rule are not authored; they go into the report as `cloud` tier.
- **Clarifying questions only when stuck.** The agent asks the user anything further only when it is unsure what rules to write AND the report gate refuses for fewer than three Taskless rules (`sg`, `vale`, or `legacy` rules it wrote). Cloud-tier candidates are carried in addition and do not count.
- **Onboarding completes itself.** The agent runs `onboard --mark-complete` once the rules are written, without a confirmation question, and then runs the report recipe. In parallel it sends onboarding feedback through `rule-feedback`, answering as the respondent. `onboard` no longer carries the survey invite.
- **New `taskless report` command.** With no flags it prints a draft: everything the CLI can know about this repository's rules, plus `missing[]`, the facts only the agent can supply. `taskless report --from <file>` takes the agent's facts, computes the projection, enforces the gate, redacts, validates against the vendored ReportV1 schema, publishes to `https://tskl.es/api/v1/reports`, records the result under `.taskless/reports/`, and prints a brief templated rule summary with the link. `taskless report delete` removes a published report using the recorded delete credential.
- **New `taskless report` command.** With no flags it prints a draft: everything the CLI can know about this repository's rules, plus `missing[]`, the facts only the agent can supply. `taskless report --from <file>` takes the agent's facts, computes the projection, enforces the gate, redacts, validates against the vendored ReportV1 schema, publishes to `https://tskl.es/api/v1/reports`, records the result under `.taskless/reports/`, and prints a brief templated rule summary with the link. `taskless report --delete` removes a published report using the recorded delete credential.
- **New `report` agent topic**: check what exists, supply what is missing, generate, delete on request. Onboarding hands off to it, and it serves "regenerate my Taskless report" later.
- **ReportV1 is vendored** from `https://tskl.es/api/v1/schema` by a generate script, the same way the cloud API schema is.
- **Migration 11** creates `.taskless/reports/` and adds `/reports/` to `.taskless/.gitignore`, since each record holds a credential that deletes a page anyone with the link can view.
Expand Down Expand Up @@ -48,4 +48,4 @@ Diffs 3 and 4 may pass ~1200 lines; that is accepted for this change, since the
- **Code**: `packages/cli/src/commands/` (new `report.ts`, `onboard.ts`), `packages/cli/src/agent/` (`onboard.md` v6, new `report.md`, `rule-feedback.md`), `packages/cli/src/detect/` (`scan.ts`, `host-tools.ts`, detect schema), `packages/cli/src/survey/constants.ts`, `packages/cli/src/filesystem/migrations/` (new 0011), `packages/cli/src/rules/report.ts` (renamed), `packages/cli/scripts/` (new schema fetch), `packages/cli/src/generated/` (vendored ReportV1), the skill and `tskl` command bodies, `packages/cli/src/prompts/` (topic classification).
- **Network**: a new outbound call to `tskl.es` (publish and delete), separate from the Taskless API and requiring no auth.
- **Privacy**: rule fixtures leave the machine on a page viewable by anyone with the link. tskl.es performs no redaction of its own; the agent and the CLI are solely responsible, and withhold rather than partially mask.
- **External dependency**: `report delete` sends an HTTP `DELETE` to the `deleteUrl` tskl.es returns on publish. The tskl.es side is adding that support; the schema's `x-taskless` block does not describe it yet.
- **External dependency**: `report --delete` sends an HTTP `DELETE` to the `deleteUrl` tskl.es returns on publish. The tskl.es side is adding that support; the schema's `x-taskless` block does not describe it yet.
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ Defines `taskless report`: the command that turns a repository's Taskless rules,

### Requirement: Report subcommand exists with a draft mode, a publish mode, and delete

The CLI SHALL provide a `taskless report` subcommand accepting the standard `--dir`/`-d` working-directory flag. Invoked with no mode flag it SHALL print a draft. Invoked with `--from <path>` it SHALL publish. `taskless report delete <record>` SHALL delete a previously published report. The subcommand SHALL require no authentication in any mode, and SHALL bootstrap `.taskless/` before reading or writing under it.
The CLI SHALL provide a `taskless report` subcommand accepting the standard `--dir`/`-d` working-directory flag. Invoked with no mode flag it SHALL print a draft. Invoked with `--from <path>` it SHALL publish. `taskless report --delete <record>` SHALL delete a previously published report. `--from` and `--delete` SHALL be mutually exclusive; supplying both SHALL exit non-zero with `INVALID_INPUT`. The subcommand SHALL require no authentication in any mode, and SHALL bootstrap `.taskless/` before reading or writing under it.

#### Scenario: Report is registered

Expand Down Expand Up @@ -51,10 +51,11 @@ The draft SHALL NOT perform any network call.

The facts file SHALL be validated against a published facts schema before anything else happens. The `report` recipe SHALL embed that schema.

#### Scenario: Facts override nothing the CLI knows
#### Scenario: Facts cannot set what the CLI knows

- **WHEN** the facts file names a rule id that exists under `.taskless/rules/` and gives it a different engine
- **THEN** the published report SHALL carry the engine the CLI established
- **WHEN** the facts file gives a rule under `.taskless/rules/` an engine, a language, or a test status
- **THEN** the command SHALL refuse the field as unknown under `REPORT_INVALID`
- **AND** a report that publishes SHALL carry the engine, language, and test status the CLI established

#### Scenario: Malformed facts are refused before any other work

Expand Down Expand Up @@ -84,7 +85,7 @@ The CLI SHALL compute `projection.perMonth` itself from the merged facts and SHA

### Requirement: Publishing is gated on a meaningful report

`taskless report --from` SHALL refuse to publish unless the merged report holds at least three Taskless rules. A Taskless rule is one in the `cli` tier, or one in the `legacy` tier that the agent authored into the repository's own linter, whose test status is neither `fail` nor `refused`. Rules in the `cloud` tier SHALL NOT count toward the three; they are carried in addition. The projection SHALL NOT gate publishing. A refusal SHALL exit non-zero with `REPORT_INSUFFICIENT`, SHALL state how many of the required three exist, and SHALL perform no network call.
`taskless report --from` SHALL refuse to publish unless the merged report holds at least three Taskless rules. A Taskless rule is one in the `cli` tier, or one in the `legacy` tier that the agent authored into the repository's own linter; either way, its test status SHALL be neither `fail` nor `refused`. Rules in the `cloud` tier SHALL NOT count toward the three; they are carried in addition. The projection SHALL NOT gate publishing. A refusal SHALL exit non-zero with `REPORT_INSUFFICIENT`, SHALL state how many of the required three exist, and SHALL perform no network call.

#### Scenario: Too few local rules

Expand Down Expand Up @@ -126,6 +127,11 @@ tskl.es performs no redaction of its own. Before publishing, the CLI SHALL scan
- **THEN** the command SHALL exit non-zero with `REPORT_REDACTION` naming that rule's title
- **AND** SHALL perform no network call

#### Scenario: Every agent-supplied rule string is screened

- **WHEN** the facts give a legacy rule a `tool` or `language`, or a cloud candidate a `language` or `id`, containing an email address or a host name
- **THEN** the command SHALL exit non-zero with `REPORT_REDACTION` naming that field

#### Scenario: The agent's counts are carried

- **WHEN** the facts declare `person: 2` and the CLI matches no person names
Expand Down Expand Up @@ -190,14 +196,27 @@ On a 201 response the CLI SHALL write `.taskless/reports/<YYYY-MM-DD_HHMMSS>.jso

### Requirement: A published report can be deleted from its record

`taskless report delete <record>` SHALL read the named record under `.taskless/reports/`, delete the report by sending an HTTP `DELETE` to the recorded `deleteUrl`, the URL tskl.es returned for that purpose, and on success remove the record and print a one-line confirmation. A record that does not exist SHALL exit non-zero with `REPORT_NOT_FOUND`. A failed delete SHALL leave the record in place.
`taskless report --delete <record>` SHALL read the named record under `.taskless/reports/`, delete the report by sending an HTTP `DELETE` to the recorded `deleteUrl`, the URL tskl.es returned for that purpose, and on a 204 remove the record and print a one-line confirmation that says cached copies may be served for up to a day, as the tskl.es delete contract states. A 404 (`not_available`: never issued, already deleted, expired, or not yet propagated) SHALL exit non-zero with `REPORT_NOT_AVAILABLE`, SHALL NOT be retried, and SHALL keep the record. A record named outside `.taskless/reports/`, or not a `.json` file, SHALL exit non-zero with `INVALID_INPUT` without reading or removing it. A record that does not exist SHALL exit non-zero with `REPORT_NOT_FOUND`. A failed delete SHALL leave the record in place.

#### Scenario: Deleting a report

- **WHEN** a user runs `taskless report delete 2026-10-13_142200.json` and the service confirms the deletion
- **WHEN** a user runs `taskless report --delete 2026-10-13_142200.json` and the service confirms the deletion
- **THEN** the record SHALL be removed
- **AND** the command SHALL print a confirmation and exit 0

#### Scenario: A report tskl.es no longer has

- **WHEN** the service answers the delete with 404
- **THEN** the command SHALL exit non-zero with `REPORT_NOT_AVAILABLE`
- **AND** SHALL send no second request
- **AND** the record SHALL remain on disk

#### Scenario: A record outside the records directory is refused

- **WHEN** a user runs `taskless report --delete ../notes.json`
- **THEN** the command SHALL exit non-zero with `INVALID_INPUT`
- **AND** SHALL send no request and remove no file

#### Scenario: A failed delete keeps the credential

- **WHEN** the service refuses or cannot be reached
Expand All @@ -206,7 +225,7 @@ On a 201 response the CLI SHALL write `.taskless/reports/<YYYY-MM-DD_HHMMSS>.jso

### Requirement: The report recipe drives the flow

The CLI SHALL embed a `report` recipe following the canonical recipe template. It SHALL instruct the agent to: run `taskless report` and read `missing`; supply each missing fact from its session, writing the facts to `.taskless/.tmp-report.json`; estimate context tokens and calls per catch from how this user's sessions actually run; run `taskless report --from` that path; on `REPORT_INSUFFICIENT`, ask the user only what would let it author the rules or establish the facts the refusal names, then retry; on success, show the CLI's summary as printed and tell the user the report can be deleted after review if they ask; delete the facts file; and on a request to delete, run `taskless report delete` with the record. The recipe SHALL embed the facts schema and SHALL state that tskl.es does not redact, so anything the agent cannot vouch for is to be withheld.
The CLI SHALL embed a `report` recipe following the canonical recipe template. It SHALL instruct the agent to: run `taskless report` and read `missing`; supply each missing fact from its session, writing the facts to `.taskless/.tmp-report.json`; estimate context tokens and calls per catch from how this user's sessions actually run; run `taskless report --from` that path; on `REPORT_INSUFFICIENT`, ask the user only what would let it author the rules or establish the facts the refusal names, then retry; on success, show the CLI's summary as printed and tell the user the report can be deleted after review if they ask; delete the facts file; and on a request to delete, run `taskless report --delete` with the record. The recipe SHALL embed the facts schema and SHALL state that tskl.es does not redact, so anything the agent cannot vouch for is to be withheld.

#### Scenario: The recipe asks questions only on refusal

Expand Down
Loading
Loading