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/remove-wizard-login-prompt.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@taskless/cli": patch
---

The setup wizard no longer asks "Log in to taskless.io now?". Setup needs no account, since local `sg` and `vale` rules run without one. `taskless auth login` is unchanged, and commands that need an account still say so.
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
## Why

The setup wizard (bare `taskless` in a terminal) stops between choosing tools
and installing to ask **"Log in to taskless.io now?"**, after an
"Authentication" note about conversation history. Issue #402 asks for it to go:

- Every question in first-run setup is friction between a person and a working
`check`.
- The free tier is local-only. Authoring and running `sg` and `vale` rules needs
no account, so asking a free user to log in during setup sells them something
they do not need yet.
- Login stays reachable where it is needed. `taskless auth login` is unchanged,
and the commands that need an account already say so (`create-remote-rule`
"(login)", the `AUTH_REQUIRED` errors and their `taskless auth login` remedy).

`taskless init` already runs without prompts and without an auth step, so the
wizard is the only place the prompt lives.

## What Changes

- **`cli-init`**: the requirement that the wizard explains the auth tradeoff and
offers to log in is removed, and a requirement that the wizard does not offer
to log in replaces it. The shared login routine requirement and the wizard
cancellation requirement each carry one scenario about the auth step. A
MODIFIED block cannot drop a scenario, so both are removed and re-added
under new titles with every other scenario intact: "A single interactive
login routine serves auth login" and "Cancelling the wizard writes nothing".
- The wizard's auth step (`wizard/steps/auth.ts`) is deleted.
- `WizardResult` drops `authPromptShown` and `authCompleted`. Nothing read
them, and no telemetry event carried them, so no event changes shape. The
per-run `cli_run` event keeps reporting `loggedIn`, resolved from the token,
which is unaffected.

## Out of scope

How paid accounts authenticate for remote generation or runtime rules. That
path, `taskless auth login`, and every "run `taskless auth login`" remedy are
unchanged.

## Delivery shape

**Single PR.** The change is a deletion of roughly twenty lines of code plus
its spec, and each half is only correct with the other. It lands and archives
together.

## Impact

- `packages/cli/src/wizard/index.ts`, `packages/cli/src/wizard/steps/auth.ts`
(deleted), `packages/cli/src/auth/login-interactive.ts` (comment only)
- `packages/cli/test/wizard-integration.test.ts`: the clack mock no longer
routes a "log in" confirm, and a new test runs the wizard with no token and
asserts nothing offers to log in
- `openspec/specs/cli-init/spec.md`
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
## ADDED Requirements

### Requirement: Wizard does not offer to log in

The wizard SHALL NOT prompt the user to log in, and SHALL NOT display an authentication explanation, at any step. It SHALL proceed from the tool-selection step directly to the install summary whether or not a token is available. Setup requires no account: authoring and running local rules needs none, and `taskless auth login` remains available, as do the "run `taskless auth login`" remedies on commands that require authentication.

#### Scenario: Wizard advances from tools to summary without a login prompt

- **WHEN** the wizard completes the tool-selection step and no valid token is resolvable
- **THEN** the wizard SHALL NOT display an authentication note or a login prompt
- **AND** SHALL advance to the install summary

#### Scenario: Wizard does not start the login flow

- **WHEN** the wizard runs to completion
- **THEN** it SHALL NOT call `loginInteractive()`

### Requirement: A single interactive login routine serves auth login

The CLI SHALL expose a single `loginInteractive()` function that performs the device-code login flow and returns once the token is stored or cancelled. The `auth login` subcommand SHALL call this function. No duplicate login implementation SHALL exist.

#### Scenario: Auth login uses the shared routine

- **WHEN** a user runs `taskless auth login`
- **THEN** the command handler SHALL call `loginInteractive()`

### Requirement: Cancelling the wizard writes nothing

If the user cancels the wizard at any step (Ctrl-C, Esc, or equivalent clack cancel signal) before the install step completes, the CLI SHALL NOT write any skill files, command files, or manifest updates. The CLI SHALL exit with a non-zero exit code and print a short message indicating how to resume (`taskless init`).

#### Scenario: Cancel at locations step

- **WHEN** the user cancels the wizard during the locations step
- **THEN** no files SHALL be written
- **AND** the CLI SHALL exit non-zero

#### Scenario: Cancel at summary confirm

- **WHEN** the user declines the summary confirm
- **THEN** no files SHALL be written
- **AND** the CLI SHALL exit non-zero

## REMOVED Requirements

### Requirement: Wizard explains the auth tradeoff and offers to log in

**Reason**: Every first-run question is friction, and the free tier is local-only, so asking during setup sells an account the user does not need yet (#402).

**Migration**: Run `taskless auth login` when an account is wanted. Commands that require authentication already report that and name the command.

### Requirement: Shared interactive login routine

**Reason**: Its "Wizard uses the shared routine" scenario describes the removed auth step. A MODIFIED block cannot drop a scenario, so the requirement is restated without it as "A single interactive login routine serves auth login".

**Migration**: None. `auth login` still calls `loginInteractive()`, and it is still the only login implementation.

### Requirement: Wizard cancellation aborts without filesystem writes

**Reason**: Its "Cancel at auth step" scenario describes a step that no longer exists. The requirement is restated without it as "Cancelling the wizard writes nothing".

**Migration**: None. Cancellation behavior at the remaining steps is unchanged.
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
## 1. Spec

- [x] 1.1 Remove "Wizard explains the auth tradeoff and offers to log in" and
add "Wizard does not offer to log in" in its place.
- [x] 1.2 Retitle "Shared interactive login routine" and "Wizard cancellation
aborts without filesystem writes" as REMOVED plus ADDED, carrying every
scenario except the one naming the wizard's auth step.
- [x] 1.3 Dry-run `openspec archive` and confirm every other scenario in
`cli-init` survives.

## 2. Code

- [x] 2.1 Delete `wizard/steps/auth.ts` and its call in `runWizard`.
- [x] 2.2 Drop `authPromptShown` / `authCompleted` from `WizardResult`.
- [x] 2.3 Update the `loginInteractive` comments that named the wizard.
- [x] 2.4 Remove the "log in" branch from the wizard test's clack mock, so a
reintroduced login confirm would be answered as the summary confirm and
fail the tests' expectations on its message.
- [x] 2.5 Add a wizard test that runs with no token and asserts no confirm
mentions logging in. Checked by inserting a login confirm into the
wizard: the test fails, and passes once it is removed.

## 3. Release

- [x] 3.1 Add a `patch` changeset.
103 changes: 40 additions & 63 deletions openspec/specs/cli-init/spec.md
Original file line number Diff line number Diff line change
Expand Up @@ -464,47 +464,6 @@ Each selected directory SHALL produce exactly one `reference` stub target, even
- **THEN** `.taskless/` SHALL NOT appear as a selectable option
- **AND** `.taskless/` SHALL NOT be pre-checked even though the manifest records it as a target

### Requirement: Wizard explains the auth tradeoff and offers to log in

The wizard SHALL present a short informational screen describing the tradeoff between anonymous and authenticated use: authenticated rules retain conversation history across teammates, enabling rule provenance (answering "why do we have this rule?"). The screen SHALL be followed by a yes/no prompt asking the user whether they want to log in now. If the user accepts, the wizard SHALL invoke the shared interactive login routine (the same routine used by `taskless auth login`) and SHALL block until the login flow completes or is cancelled. If the user declines, the wizard SHALL print a one-line hint that they can run `taskless auth login` later and proceed to the install step. If a valid token is already present, the wizard SHALL skip this step entirely.

#### Scenario: Auth step is shown when not logged in

- **WHEN** the wizard reaches the auth step and no valid token is resolvable
- **THEN** the wizard SHALL display the tradeoff explanation
- **AND** SHALL prompt the user to log in

#### Scenario: Accepting login blocks until completion

- **WHEN** the user accepts the login prompt
- **THEN** the wizard SHALL invoke the shared interactive login routine
- **AND** SHALL NOT advance to the install step until the login routine resolves

#### Scenario: Declining login advances with a hint

- **WHEN** the user declines the login prompt
- **THEN** the wizard SHALL print a hint mentioning `taskless auth login`
- **AND** SHALL advance to the install step

#### Scenario: Auth step is skipped when already logged in

- **WHEN** the wizard reaches the auth step and a valid token already exists for the working directory
- **THEN** the wizard SHALL skip the auth explanation and prompt entirely

### Requirement: Shared interactive login routine

The CLI SHALL expose a single `loginInteractive()` function that performs the device-code login flow and returns once the token is stored or cancelled. Both the `auth login` subcommand and the wizard's auth step SHALL call this function. No duplicate login implementation SHALL exist.

#### Scenario: Auth login uses the shared routine

- **WHEN** a user runs `taskless auth login`
- **THEN** the command handler SHALL call `loginInteractive()`

#### Scenario: Wizard uses the shared routine

- **WHEN** the wizard user accepts the login prompt
- **THEN** the wizard SHALL call `loginInteractive()`

### Requirement: Wizard shows a diff-style summary before writing

Before any filesystem writes, the wizard SHALL display a summary of planned actions grouped by target location. The summary SHALL include:
Expand Down Expand Up @@ -532,28 +491,6 @@ If the summary contains any removals, the wizard SHALL require an explicit `conf
- **WHEN** the summary contains only additions and unchanged entries
- **THEN** the wizard MAY proceed directly to writes without an extra confirm

### Requirement: Wizard cancellation aborts without filesystem writes

If the user cancels the wizard at any step (Ctrl-C, Esc, or equivalent clack cancel signal) before the install step completes, the CLI SHALL NOT write any skill files, command files, or manifest updates. The CLI SHALL exit with a non-zero exit code and print a short message indicating how to resume (`taskless init`).

#### Scenario: Cancel at locations step

- **WHEN** the user cancels the wizard during the locations step
- **THEN** no files SHALL be written
- **AND** the CLI SHALL exit non-zero

#### Scenario: Cancel at auth step

- **WHEN** the user cancels the wizard during the auth step
- **THEN** no skill files or manifest updates SHALL be written
- **AND** the CLI SHALL exit non-zero

#### Scenario: Cancel at summary confirm

- **WHEN** the user declines the summary confirm
- **THEN** no files SHALL be written
- **AND** the CLI SHALL exit non-zero

### Requirement: Install manifest records what was installed per target

The install manifest in `.taskless/taskless.json` continues to record what was written per target. Each target entry SHALL additionally record a `mode` field (`canonical` or `reference`) as defined by the per-target install mode requirement. The `.taskless` target records the canonical store; tool-directory targets record the stubs written for that directory.
Expand Down Expand Up @@ -825,3 +762,43 @@ The `migrated` field is unchanged: present with the migration report when a migr
- **WHEN** `taskless init --json` runs
- **THEN** the envelope SHALL contain `cliVersion.previous` (a string or `null`), `cliVersion.installed`, a `targets` array with one entry per install target, and a boolean `changed`
- **AND** `changed` SHALL be `true` exactly when `migrated` is present, any target's written or removed list is non-empty, or `cliVersion.previous` is non-null and differs from `cliVersion.installed`

### Requirement: Wizard does not offer to log in

The wizard SHALL NOT prompt the user to log in, and SHALL NOT display an authentication explanation, at any step. It SHALL proceed from the tool-selection step directly to the install summary whether or not a token is available. Setup requires no account: authoring and running local rules needs none, and `taskless auth login` remains available, as do the "run `taskless auth login`" remedies on commands that require authentication.

#### Scenario: Wizard advances from tools to summary without a login prompt

- **WHEN** the wizard completes the tool-selection step and no valid token is resolvable
- **THEN** the wizard SHALL NOT display an authentication note or a login prompt
- **AND** SHALL advance to the install summary

#### Scenario: Wizard does not start the login flow

- **WHEN** the wizard runs to completion
- **THEN** it SHALL NOT call `loginInteractive()`

### Requirement: A single interactive login routine serves auth login

The CLI SHALL expose a single `loginInteractive()` function that performs the device-code login flow and returns once the token is stored or cancelled. The `auth login` subcommand SHALL call this function. No duplicate login implementation SHALL exist.

#### Scenario: Auth login uses the shared routine

- **WHEN** a user runs `taskless auth login`
- **THEN** the command handler SHALL call `loginInteractive()`

### Requirement: Cancelling the wizard writes nothing

If the user cancels the wizard at any step (Ctrl-C, Esc, or equivalent clack cancel signal) before the install step completes, the CLI SHALL NOT write any skill files, command files, or manifest updates. The CLI SHALL exit with a non-zero exit code and print a short message indicating how to resume (`taskless init`).

#### Scenario: Cancel at locations step

- **WHEN** the user cancels the wizard during the locations step
- **THEN** no files SHALL be written
- **AND** the CLI SHALL exit non-zero

#### Scenario: Cancel at summary confirm

- **WHEN** the user declines the summary confirm
- **THEN** no files SHALL be written
- **AND** the CLI SHALL exit non-zero
8 changes: 4 additions & 4 deletions packages/cli/src/auth/login-interactive.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ export interface LoginInteractiveOptions {
* polls the Taskless auth endpoint, persists the token on success, and
* returns a tagged result the caller can surface however it wants.
*
* Does NOT emit telemetry — the caller (wizard or `auth login` command) is
* Does NOT emit telemetry — the caller (the `auth login` command) is
* responsible for its own telemetry events so the events stay scoped to
* their originating flow.
*/
Expand All @@ -37,9 +37,9 @@ export async function loginInteractive(
const out = options.out ?? ((line) => console.log(line));
const error_ = options.err ?? ((line) => console.error(line));

// Silent: we're inside an interactive (clack) flow — stderr warnings from
// legacy/tracked-token checks would corrupt the UI. Pre-checks happen in
// promptAuth() before we get here.
// Silent: this is only an "already logged in?" probe. Stderr warnings from
// legacy/tracked-token checks belong to the commands that use the token,
// not to the login flow that is about to replace it.
const existing = await getToken(cwd, { silent: true });
if (existing) {
return { status: "already_logged_in" };
Expand Down
10 changes: 0 additions & 10 deletions packages/cli/src/wizard/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ import { CLIError } from "../util/cli-error";
import { WizardCancelled } from "./ask";
import { getCliVersion, renderIntro } from "./intro";
import { promptLocations } from "./steps/locations";
import { promptAuth } from "./steps/auth";
import { renderSummaryAndConfirm } from "./steps/summary";

export interface RunWizardOptions {
Expand All @@ -28,8 +27,6 @@ export interface WizardResult {
status: "completed" | "cancelled";
locations: string[];
optionalSkills: string[];
authPromptShown: boolean;
authCompleted: boolean;
durationMs: number;
cancelledStep?: string;
}
Expand All @@ -44,17 +41,12 @@ export async function runWizard(
let locations: string[] = [];
// Optional skills no longer exist post-consolidation — always empty.
const optionalSkills: string[] = [];
let authPromptShown = false;
let authCompleted = false;

console.error(renderIntro());
intro(" Taskless setup ");

try {
locations = await promptLocations(options.cwd);
const authResult = await promptAuth(options.cwd);
authPromptShown = authResult.prompted;
authCompleted = authResult.loggedIn;

// Catalog has one entry now (`taskless`); install all embedded skills.
const plan = buildInstallPlan(
Expand Down Expand Up @@ -129,8 +121,6 @@ export async function runWizard(
status: args.status,
locations,
optionalSkills,
authPromptShown,
authCompleted,
durationMs,
cancelledStep,
};
Expand Down
48 changes: 0 additions & 48 deletions packages/cli/src/wizard/steps/auth.ts

This file was deleted.

Loading
Loading