# Sidekick Pack

Workspace-local productivity pack for the LumenFlow kernel. Sidekick manages tasks, memory,
channels, routines, and status under `.sidekick/`, while keeping every tool call inside the normal
kernel scope, policy, and evidence pipeline.

## Status

**Version**: `0.1.0` (pre-release)  
**License**: `AGPL-3.0-only`

## Tool Groups

| Group   | Tools                                                                                      | Description                                                |
| ------- | ------------------------------------------------------------------------------------------ | ---------------------------------------------------------- |
| Task    | `task:create`, `task:list`, `task:update`, `task:cancel`, `task:complete`, `task:schedule` | Create, manage, cancel, complete, and schedule local tasks |
| Memory  | `memory:store`, `memory:recall`, `memory:update`, `memory:forget`                          | Persist and edit reusable workspace knowledge              |
| Channel | `channel:configure`, `channel:list`, `channel:delete`, `channel:send`, `channel:receive`   | Discover and manage named local message channels           |
| Routine | `routine:create`, `routine:list`, `routine:update`, `routine:delete`, `routine:run`        | Define, stop, inspect, and remove plan-only routines       |
| System  | `sidekick:init`, `sidekick:status`, `sidekick:export`                                      | Bootstrap, health check, and export Sidekick-managed state |

**Total**: 23 tools

## Behavior Notes

- `.sidekick/` is the pack-owned storage root.
- `routine:run` is plan-only. It returns the resolved steps and never executes them.
- `routine:update` with `enabled: false` is the stop primitive for routines.
- `task:cancel` is the destructive task terminal state; it is separate from `task:complete`.
- `sidekick:export` is read-only. It returns data and does not write export artifacts.
- All write-capable and destructive tools support `dry_run`.
- Destructive Sidekick tools are approval-gated through the pack policy factory.

### Approval-Gated Destructive Tools

These tools require policy evaluation to return `approval_required` before execution:

- `task:cancel`
- `memory:forget`
- `channel:delete`
- `routine:delete`

The permission label helps classify them as destructive, but the actual pause comes from the pack
policy path and kernel approval flow.

## Manual Smoke Flow

Use this sequence after installation or whenever you want to smoke test the current contract.

### 1. Initialize storage

```text
tool: sidekick:init
input: {}
```

Expected: `initialized: true` and a `.sidekick/` root path.

### 2. Create and update a task

```text
tool: task:create
input: { "title": "Review docs", "priority": "P1", "tags": ["docs"] }
```

```text
tool: task:update
input: {
  "id": "<task-id>",
  "description": "Check final Sidekick contract",
  "due_at": "2026-03-31T00:00:00Z"
}
```

Expected: `task:list` returns the updated task metadata.

### 3. Store and update memory

```text
tool: memory:store
input: { "type": "note", "content": "Sidekick docs updated", "tags": ["docs"] }
```

```text
tool: memory:update
input: { "id": "<memory-id>", "type": "snippet", "content": "task:cancel is approval-gated" }
```

Expected: `memory:recall` returns the edited record.

### 4. Create and stop a routine

```text
tool: routine:create
input: {
  "name": "daily-review",
  "steps": [
    { "tool": "task:list", "input": { "status": "pending" } },
    { "tool": "sidekick:status", "input": {} }
  ],
  "enabled": true
}
```

```text
tool: routine:update
input: { "id": "<routine-id>", "enabled": false }
```

Expected: `routine:list` still shows the routine, while `routine:list { "enabled_only": true }`
excludes it.

### 5. Inspect a routine plan

```text
tool: routine:run
input: { "id": "<routine-id>" }
```

Expected: A plan object listing the ordered steps. No side effects occur.

### 6. Check channel discovery

```text
tool: channel:send
input: { "channel": "alerts", "content": "Docs smoke flow" }
```

```text
tool: channel:list
input: {}
```

Expected: A local channel entry with stable metadata such as `message_count` and
`last_message_at`.

### 7. Verify a destructive dry run

```text
tool: task:cancel
input: { "id": "<task-id>", "dry_run": true }
```

Expected: The response previews the canceled task and includes `dry_run: true`, while
`task:list { "status": "pending" }` still shows the task as pending.

### 8. Verify approval-gated destructive behavior

Run `task:cancel`, `memory:forget`, `channel:delete`, or `routine:delete` through the kernel
runtime or host surface that evaluates policies.

Expected: The first attempt returns `APPROVAL_REQUIRED`. After approval resolution, the tool runs
and the audit trail reflects the final delete or cancel transition.

### 9. Export

```text
tool: sidekick:export
input: { "include_audit": true }
```

Expected: A full JSON snapshot of stores plus audit events.

## Validation

```bash
# Pack contract and integrity
pnpm pack:validate --id sidekick

# Sidekick tool and manifest tests
pnpm vitest run packages/@lumenflow/packs/sidekick/__tests__/

# Kernel approval coverage for Sidekick destructive tools
pnpm vitest run packages/@lumenflow/kernel/src/__tests__/runtime.test.ts
```

## Storage Layout

Sidekick uses a `StoragePort` abstraction with a filesystem adapter. The persisted layout is:

```text
.sidekick/
  audit/
    events.jsonl
  channels/
    channels.json
    messages.json
  memory/
    memories.json
  routines/
    routines.json
  tasks/
    tasks.json
```

## Contributing

This pack follows the LumenFlow workflow. See the repository `LUMENFLOW.md` and workspace rules for
delivery expectations.
