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
5 changes: 5 additions & 0 deletions .changeset/agent-ok-preflight.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@taskless/cli": patch
---

New `taskless agent ok` topic: it prints one line confirming the CLI can run, through the invocation it was launched with, and does nothing else. The Taskless skill and the `/tskl` command now run it once per session before any other Taskless command, so a harness that blocks download-and-run commands (Claude Code's auto mode, for one) stops the agent before it starts work rather than partway through. On a blocked command the agent now names the exact command to the user, offers to have them approve it, run it themselves with `!`, or allow exactly that invocation, and never proposes an allow rule that wildcards the package name or version.
9 changes: 9 additions & 0 deletions commands/tskl/tskl.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,15 @@ with Taskless before proceeding.

Otherwise, follow the same flow as the `taskless` skill:

0. Preflight, once per session, before any other Taskless command: run
`%(TASKLESS_CLI)s agent ok` and proceed once it prints its confirmation
line. If the harness or the user blocks it, or blocks any later Taskless
command, stop and do not try to get around the block. Tell the user the
exact command and that it downloads and runs a package from npm, then
offer: they approve that exact command in their reply, they run it
themselves as `! <exact command>`, or you add an allow rule for exactly
the invocation `%(TASKLESS_CLI)s`. Never propose a rule wider than that
invocation, such as a wildcard over the package name or its version.
1. Identify the topic from `$ARGUMENTS` using the table below.
2. Fetch the canonical recipe with `%(TASKLESS_CLI)s agent <topic>` (or
`%(TASKLESS_CLI)s agent <topic> --anonymous` if the user is offline or
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
schema: spec-driven
created: 2026-10-14
35 changes: 35 additions & 0 deletions openspec/changes/archive/2026-10-15-agent-ok-preflight/design.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
## Context

The `taskless` skill is a router: every action starts by running `%(TASKLESS_CLI)s agent <topic>`, which on a release is `npx @taskless/cli …` and on a nightly `npx @taskless/cli-nightly@<pin> …`. Harnesses increasingly gate download-and-run commands. Claude Code's auto mode classifier blocked one in a real session, and its docs say a block can be cleared by a user message that names the exact action, or by a narrow `permissions.allow` rule. Broad rules (blanket `Bash(*)`, wildcarded interpreters, package-manager run commands) are dropped on entering auto mode.

The session that hit the block had a vague request ("let's use taskless"), ran two read-only commands, then hit the block on `agent onboard`. Onboard's consent question was never reached, because it is in the recipe.

## Goals / Non-Goals

**Goals:**

- An agent learns whether it may run the CLI before it starts any task, at the cost of one trivial command.
- When it may not, the agent asks once, names the exact command, and suggests only remedies scoped to that command.

**Non-Goals:**

- Writing harness permission files (`.claude/settings.json` and others) from the installer. Whether a pinned `npx` allow rule survives auto mode is undocumented, and is a follow-up to test by hand before anything depends on it.
- Changing onboard's own consent question.

## Decisions

**`ok` is built into `agent`, not a `.md` recipe.** A recipe must carry the `# Topic:` header, Goal/Preconditions/Steps/Errors/See Also, and the served copy gains the fetch-time directive ("fetch again next task"). All of that is noise around one line, and the directive is wrong here: a confirmation is not a recipe to refetch. The branch sits before recipe lookup in `agent.ts`, and the index lists it beside the commands. Alternative considered: a minimal `ok.md` exempted from the template requirement. Rejected, since an exemption in the format test is a second rule to maintain for one line.

**The line names the detected invocation, via `detectCliInvocation`.** That is the value recipes are rendered with, so the preflight and every later recipe agree on spelling. When it returns `undefined` the line says "via the command you just ran" instead of falling back to `npx` the way `getCliPrefix` does, because a guessed launcher in text an agent copies is worse than none.

**No project side effects.** `ok` does not bootstrap `.taskless/`, read the manifest, or check auth. It must be safe to run in any directory, first, and a preflight that writes project files would be one an agent cannot run speculatively. It does emit `cli_agent` with `topic: "ok"`, like every other topic: preflight volume against task volume is the measurement for how often agents stop at the gate. That capture carries telemetry's usual costs, which this change does not alter: unless telemetry is disabled, it writes the anonymous ID under the user's config directory and runs a `git` subprocess for repository context. Making `ok` skip telemetry entirely would lose the measurement, so it is left as every other topic has it.

**The remedies are scoped to the exact invocation.** The skill names three: the user approves the exact command in their reply (Claude Code reads an approval that names the specific action as clearing the block), the user runs `! <command>`, or an allow rule for exactly `%(TASKLESS_CLI)s`. It forbids widening the rule, because the agent in the incident improvised `npx @taskless/cli-nightly:*`, which approves every future nightly rather than the one installed, and which auto mode may drop anyway.

**Once per session, not per task.** Permission does not change between tasks in a session; recipes do. The skill's existing "fetch the recipe for every task" rule is unchanged.

## Risks / Trade-offs

- [The classifier allows `agent ok` but blocks a later command] → An approval or classifier judgement can be per-action. The skill's blocked-command guidance is written for any Taskless command, not only the preflight, so a later block gets the same handling.
- [The download block cannot be cleared by chat approval at all] → Then the `!` route and the allow rule remain. A hand test under auto mode, run after this change and outside it, decides whether a follow-up should make the skill lead with one of those instead.
- [One more command before every session's first task] → It is cached by npx after the first run and prints one line; the first-run latency note in the skill already covers the cold fetch.
35 changes: 35 additions & 0 deletions openspec/changes/archive/2026-10-15-agent-ok-preflight/proposal.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
## Why

A harness can refuse the CLI before Taskless says a word. In a real session, Claude Code's auto mode blocked `npx @taskless/cli-nightly@<pin> agent onboard` because it downloads and runs a package from npm. The block is correct, but it landed on the first command that does work, and the one consent question onboarding asks lives inside that recipe, behind the call that was refused. The agent stopped, improvised an allow rule (`npx @taskless/cli-nightly:*`) broader than the pin it was installed with, and asked its question after the fact.

Nothing tells an agent, before it starts a task, whether it is allowed to run Taskless at all. That is cheap to find out and expensive to discover halfway through.

## What Changes

- Add `taskless agent ok`, a built-in topic that does nothing but print one line: `This message confirms you can run Taskless via <invocation>.` It reads no project state, needs no install, and exits 0. Reaching it at all is the signal.
- The `taskless` skill and the `tskl` command run `agent ok` once per session, before any other Taskless command.
- When the harness blocks that preflight, the skill tells the agent to stop before doing any work and ask the user, naming the exact command and that it downloads a package from npm. It offers three ways forward: the user approves that exact command in their reply, the user runs `! <exact command>` themselves, or the agent adds an allow rule for exactly the invocation the skill names, never a wildcard over the package name or version.
- `ok` appears in the `taskless agent` index.

## Delivery shape

**Single PR.** One built-in topic, two skill-body sections, and their tests fit one reviewable diff well under the size guidance, and none of it is useful split: the topic without the skill instruction is never called, and the instruction without the topic names a command that does not exist.

## Capabilities

### New Capabilities

None.

### Modified Capabilities

- `cli-agent`: adds the `ok` preflight topic.
- `skill-taskless`: adds the preflight step and the blocked-preflight guidance to the skill and `tskl` command bodies.

## Impact

- `packages/cli/src/commands/agent.ts`: the `ok` branch and its index row.
- `skills/taskless/SKILL.md`, `commands/tskl/tskl.md`: the preflight section. The skill body stays within its 150-line cap.
- Tests for the topic output, the index row, telemetry, and the skill body.
- A `patch` changeset (pre-1.0, additive).
- Out of scope: writing harness permission settings from the installer. Whether a narrow `npx` allow rule survives Claude Code's auto mode is undocumented, and is a follow-up to test by hand before anything depends on it.
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
## ADDED Requirements

### Requirement: ok topic confirms the CLI can run

`taskless agent ok` SHALL print exactly one line, `This message confirms you can run Taskless via <invocation>.`, and exit 0. `<invocation>` SHALL be the invocation detected for this process. When the launcher cannot be determined, the line SHALL read `This message confirms you can run Taskless via the command you just ran.` rather than guessing a launcher.

The topic is built into the `agent` command, not a recipe file. It SHALL NOT carry the `# Topic:` header, the fetch-time directive, or a survey invite, and it SHALL NOT read or create `.taskless/`. It SHALL emit the command's `cli_agent` intent event with `topic: "ok"`. The topic SHALL appear in the `taskless agent` index.

#### Scenario: ok prints one confirmation line

- **WHEN** `taskless agent ok` is run
- **THEN** stdout SHALL be a single line beginning `This message confirms you can run Taskless via`
- **AND** the CLI SHALL exit with code 0

#### Scenario: ok names the detected invocation

- **WHEN** `taskless agent ok` is run by a launcher the CLI detects
- **THEN** the line SHALL name that launcher and the pinned package specifier

#### Scenario: ok does not guess an unknown launcher

- **WHEN** `taskless agent ok` is run and the launcher cannot be determined
- **THEN** the line SHALL say `via the command you just ran` and SHALL NOT name a launcher

#### Scenario: ok runs outside a Taskless project

- **WHEN** `taskless agent ok` is run in a directory with no `.taskless/`
- **THEN** it SHALL print the confirmation line and exit 0
- **AND** SHALL NOT create `.taskless/`

#### Scenario: ok is in the index

- **WHEN** `taskless agent` is run with no arguments
- **THEN** the index SHALL include a row for `ok`

#### Scenario: ok captures its topic

- **WHEN** `taskless agent ok` is run
- **THEN** PostHog SHALL receive a `cli_agent` event whose `topic` property is `ok`
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
## ADDED Requirements

### Requirement: Skill preflights the CLI before the first Taskless command

The skill body and the `tskl` command body SHALL instruct the agent to run `%(TASKLESS_CLI)s agent ok` once per session, before any other Taskless command, and to proceed only once it prints its confirmation line.

When the preflight is blocked by the harness or the user, the bodies SHALL instruct the agent to:

1. Stop before doing any Taskless work, and not attempt to work around the block.
2. Tell the user the exact command, and that it downloads and runs a package from npm.
3. Offer the user ways forward: approve that exact command in their reply, run `! <exact command>` themselves, or have the agent add an allow rule for exactly the invocation the skill names.
4. Never propose an allow rule that widens the invocation, such as a wildcard over the package name or its version.

#### Scenario: Skill body runs the preflight first

- **WHEN** the skill body or the `tskl` command body is read from source
- **THEN** it SHALL instruct the agent to run `%(TASKLESS_CLI)s agent ok` before any other Taskless command in the session

#### Scenario: Skill body handles a blocked preflight

- **WHEN** the skill body or the `tskl` command body is read from source
- **THEN** it SHALL instruct the agent, on a blocked preflight, to stop and name the exact command to the user
- **AND** SHALL offer both the `!` route and an allow rule for the exact invocation
- **AND** SHALL forbid an allow rule that wildcards the package name or version
16 changes: 16 additions & 0 deletions openspec/changes/archive/2026-10-15-agent-ok-preflight/tasks.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
## 1. The `ok` topic

- [x] 1.1 Add the `ok` branch to `packages/cli/src/commands/agent.ts`, before recipe lookup: print the confirmation line from `detectCliInvocation`, with the "command you just ran" fallback, and capture `cli_agent` with `topic: "ok"`
- [x] 1.2 List `ok` in the `taskless agent` index
- [x] 1.3 Tests: one-line output and exit 0, detected and unknown launcher, no `.taskless/` created, index row, telemetry topic

## 2. Skill and command bodies

- [x] 2.1 Add the preflight section to `skills/taskless/SKILL.md`: run `%(TASKLESS_CLI)s agent ok` once per session first; on a block, stop, name the exact command, offer chat approval, `!`, or an exact-invocation allow rule, and forbid widening it. Keep the body within 150 lines
- [x] 2.2 Add the same step to `commands/tskl/tskl.md`
- [x] 2.3 Tests asserting both bodies carry the preflight and the blocked-preflight guidance

## 3. Release

- [x] 3.1 Add a `patch` changeset
- [x] 3.2 Run `pnpm typecheck` and `pnpm lint`
38 changes: 38 additions & 0 deletions openspec/specs/cli-agent/spec.md
Original file line number Diff line number Diff line change
Expand Up @@ -394,3 +394,41 @@ An agent topic `report` SHALL be registered. The CLI SHALL embed `packages/cli/s

- **WHEN** an agent runs `taskless agent report`
- **THEN** PostHog SHALL receive a `cli_agent` event whose `topic` property is `report`

### Requirement: ok topic confirms the CLI can run

`taskless agent ok` SHALL print exactly one line, `This message confirms you can run Taskless via <invocation>.`, and exit 0. `<invocation>` SHALL be the invocation detected for this process. When the launcher cannot be determined, the line SHALL read `This message confirms you can run Taskless via the command you just ran.` rather than guessing a launcher.

The topic is built into the `agent` command, not a recipe file. It SHALL NOT carry the `# Topic:` header, the fetch-time directive, or a survey invite, and it SHALL NOT read or create `.taskless/`. It SHALL emit the command's `cli_agent` intent event with `topic: "ok"`. The topic SHALL appear in the `taskless agent` index.

#### Scenario: ok prints one confirmation line

- **WHEN** `taskless agent ok` is run
- **THEN** stdout SHALL be a single line beginning `This message confirms you can run Taskless via`
- **AND** the CLI SHALL exit with code 0

#### Scenario: ok names the detected invocation

- **WHEN** `taskless agent ok` is run by a launcher the CLI detects
- **THEN** the line SHALL name that launcher and the pinned package specifier

#### Scenario: ok does not guess an unknown launcher

- **WHEN** `taskless agent ok` is run and the launcher cannot be determined
- **THEN** the line SHALL say `via the command you just ran` and SHALL NOT name a launcher

#### Scenario: ok runs outside a Taskless project

- **WHEN** `taskless agent ok` is run in a directory with no `.taskless/`
- **THEN** it SHALL print the confirmation line and exit 0
- **AND** SHALL NOT create `.taskless/`

#### Scenario: ok is in the index

- **WHEN** `taskless agent` is run with no arguments
- **THEN** the index SHALL include a row for `ok`

#### Scenario: ok captures its topic

- **WHEN** `taskless agent ok` is run
- **THEN** PostHog SHALL receive a `cli_agent` event whose `topic` property is `ok`
23 changes: 23 additions & 0 deletions openspec/specs/skill-taskless/spec.md
Original file line number Diff line number Diff line change
Expand Up @@ -182,3 +182,26 @@ The skill body's topic table SHALL map a request to make, publish, regenerate, o

- **WHEN** the skill body is read
- **THEN** it SHALL NOT describe how to assemble, publish, or delete a report

### Requirement: Skill preflights the CLI before the first Taskless command

The skill body and the `tskl` command body SHALL instruct the agent to run `%(TASKLESS_CLI)s agent ok` once per session, before any other Taskless command, and to proceed only once it prints its confirmation line.

When the preflight is blocked by the harness or the user, the bodies SHALL instruct the agent to:

1. Stop before doing any Taskless work, and not attempt to work around the block.
2. Tell the user the exact command, and that it downloads and runs a package from npm.
3. Offer the user ways forward: approve that exact command in their reply, run `! <exact command>` themselves, or have the agent add an allow rule for exactly the invocation the skill names.
4. Never propose an allow rule that widens the invocation, such as a wildcard over the package name or its version.

#### Scenario: Skill body runs the preflight first

- **WHEN** the skill body or the `tskl` command body is read from source
- **THEN** it SHALL instruct the agent to run `%(TASKLESS_CLI)s agent ok` before any other Taskless command in the session

#### Scenario: Skill body handles a blocked preflight

- **WHEN** the skill body or the `tskl` command body is read from source
- **THEN** it SHALL instruct the agent, on a blocked preflight, to stop and name the exact command to the user
- **AND** SHALL offer both the `!` route and an allow rule for the exact invocation
- **AND** SHALL forbid an allow rule that wildcards the package name or version
Loading
Loading