@jiggai/recipes 0.2.24 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +33 -6
- package/docs/AGENTS_AND_SKILLS.md +9 -13
- package/docs/ARCHITECTURE.md +109 -0
- package/docs/BUNDLED_RECIPES.md +3 -14
- package/docs/COMMANDS.md +92 -48
- package/docs/INSTALLATION.md +5 -5
- package/docs/RECIPE_FORMAT.md +2 -2
- package/docs/TEAM_WORKFLOW.md +4 -1
- package/docs/TUTORIAL_CREATE_RECIPE.md +7 -8
- package/openclaw.plugin.json +1 -1
- package/package.json +1 -1
- package/recipes/default/business-team.md +20 -0
- package/recipes/default/development-team.md +70 -69
- package/src/handlers/cron.ts +96 -20
- package/docs/CLEANUP_TODO.md +0 -31
- package/docs/CODE_SMELLS_TRACKER.md +0 -42
- package/docs/SMELLS_TODO.md +0 -23
- package/docs/TEST_COVERAGE_PROGRESS.md +0 -37
package/README.md
CHANGED
|
@@ -11,7 +11,7 @@ If you like durable workflows: ClawRecipes is built around a **file-first team w
|
|
|
11
11
|
## Quickstart
|
|
12
12
|
### 1) Install
|
|
13
13
|
#### Option A (preferred): install from npm
|
|
14
|
-
|
|
14
|
+
When published on npm:
|
|
15
15
|
|
|
16
16
|
```bash
|
|
17
17
|
openclaw plugins install @jiggai/recipes
|
|
@@ -58,7 +58,8 @@ openclaw recipes dispatch \
|
|
|
58
58
|
- `openclaw recipes list|show|status`
|
|
59
59
|
- `openclaw recipes scaffold` (agent → `workspace-<agentId>` + writes workspace recipe `~/.openclaw/workspace/recipes/<agentId>.md` by default)
|
|
60
60
|
- `openclaw recipes scaffold-team` (team → `workspace-<teamId>` + `roles/<role>/` + writes workspace recipe `~/.openclaw/workspace/recipes/<teamId>.md` by default)
|
|
61
|
-
- `openclaw recipes install <idOrSlug> [--yes] [--global|--agent-id <id>|--team-id <id>]` (skills: global or scoped)
|
|
61
|
+
- `openclaw recipes install-skill <idOrSlug> [--yes] [--global|--agent-id <id>|--team-id <id>]` (skills: global or scoped)
|
|
62
|
+
- `openclaw recipes install <slug>` (marketplace recipe)
|
|
62
63
|
- `openclaw recipes bind|unbind|bindings` (multi-agent routing)
|
|
63
64
|
- `openclaw recipes dispatch ...` (request → inbox + ticket + assignment)
|
|
64
65
|
- `openclaw recipes tickets|move-ticket|assign|take|handoff|complete` (file-first ticket workflow)
|
|
@@ -79,15 +80,26 @@ The plugin supports these config keys (with defaults):
|
|
|
79
80
|
Config schema is defined in `openclaw.plugin.json`.
|
|
80
81
|
|
|
81
82
|
## Documentation
|
|
82
|
-
|
|
83
|
-
- Installation
|
|
84
|
-
- Agents
|
|
85
|
-
- Tutorial
|
|
83
|
+
**For users:**
|
|
84
|
+
- [Installation](docs/INSTALLATION.md) — install the plugin
|
|
85
|
+
- [Agents & skills](docs/AGENTS_AND_SKILLS.md) — mental model, tool policies
|
|
86
|
+
- [Tutorial](docs/TUTORIAL_CREATE_RECIPE.md) — create your first recipe
|
|
87
|
+
- [Commands](docs/COMMANDS.md) — full command reference
|
|
88
|
+
- [Team workflow](docs/TEAM_WORKFLOW.md) — file-first workflow
|
|
89
|
+
|
|
90
|
+
**For contributors:**
|
|
91
|
+
- [Architecture](docs/ARCHITECTURE.md) — codebase structure
|
|
92
|
+
- [Contributing](CONTRIBUTING.md) — setup, tests, PR workflow
|
|
86
93
|
|
|
87
94
|
## Development
|
|
88
95
|
### Unit tests (vitest)
|
|
89
96
|
Run:
|
|
90
97
|
- `npm test`
|
|
98
|
+
- `npm run test:coverage` — coverage with CI thresholds (see `vitest.config.ts`)
|
|
99
|
+
- `npm run smell-check` — quality checks (ESLint, jscpd, pattern grep)
|
|
100
|
+
|
|
101
|
+
### Pre-commit hooks
|
|
102
|
+
Husky runs on commit. Run `npm ci` first to install hooks.
|
|
91
103
|
|
|
92
104
|
### Scaffold smoke test (regression)
|
|
93
105
|
A lightweight smoke check validates scaffold-team output contains the required testing workflow docs (ticket 0004).
|
|
@@ -98,6 +110,11 @@ Run:
|
|
|
98
110
|
Notes:
|
|
99
111
|
- Creates a temporary `workspace-smoke-<timestamp>-team` under `~/.openclaw/` and then deletes it.
|
|
100
112
|
- Exits non-zero on mismatch.
|
|
113
|
+
- Requires OpenClaw and workspace config.
|
|
114
|
+
|
|
115
|
+
### For contributors
|
|
116
|
+
- [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md) — codebase structure
|
|
117
|
+
- [CONTRIBUTING.md](CONTRIBUTING.md) — setup, commands, pre-commit, CI
|
|
101
118
|
|
|
102
119
|
Reference:
|
|
103
120
|
- Commands: `docs/COMMANDS.md`
|
|
@@ -139,8 +156,10 @@ Notes:
|
|
|
139
156
|
- [Recipe format](https://github.com/JIGGAI/ClawRecipes/blob/main/docs/RECIPE_FORMAT.md): `docs/RECIPE_FORMAT.md`
|
|
140
157
|
- [Team workflow](https://github.com/JIGGAI/ClawRecipes/blob/main/docs/TEAM_WORKFLOW.md): `docs/TEAM_WORKFLOW.md`
|
|
141
158
|
- [Agents & Skills](https://github.com/JIGGAI/ClawRecipes/blob/main/docs/AGENTS_AND_SKILLS.md): `docs/AGENTS_AND_SKILLS.md`
|
|
159
|
+
- [Architecture](https://github.com/JIGGAI/ClawRecipes/blob/main/docs/ARCHITECTURE.md): `docs/ARCHITECTURE.md`
|
|
142
160
|
- [Bundled](https://github.com/JIGGAI/ClawRecipes/blob/main/docs/BUNDLED_RECIPES.md): `docs/BUNDLED_RECIPES.md`
|
|
143
161
|
- [Create Recipe Tutorial](https://github.com/JIGGAI/ClawRecipes/blob/main/docs/TUTORIAL_CREATE_RECIPE.md): `docs/TUTORIAL_CREATE_RECIPE.md`
|
|
162
|
+
- [Contributing](https://github.com/JIGGAI/ClawRecipes/blob/main/CONTRIBUTING.md): `CONTRIBUTING.md`
|
|
144
163
|
|
|
145
164
|
## Note
|
|
146
165
|
ClawRecipes is meant to be *installed* and then used to build **agents + teams**.
|
|
@@ -149,3 +168,11 @@ Most users should focus on:
|
|
|
149
168
|
- authoring recipes in their OpenClaw workspace (`<workspace>/recipes/*.md`)
|
|
150
169
|
- scaffolding teams (`openclaw recipes scaffold-team ...`)
|
|
151
170
|
- running the file-first workflow (dispatch → backlog → in-progress → testing → done)
|
|
171
|
+
|
|
172
|
+
## Goals
|
|
173
|
+
- Release Clawmarket, https://github.com/JIGGAI/ClawMarket, public url https://clawkitchen.ai
|
|
174
|
+
- Release ClawKitchen, https://github.com/JIGGAI/ClawKitchen
|
|
175
|
+
- Merge at least 1 community pull request
|
|
176
|
+
- Daily shipping/pull requests of ClawRecipes features
|
|
177
|
+
- Improve recipes with more detailed agent files
|
|
178
|
+
- Add ability to install skills for agents through ClawKitchen
|
|
@@ -95,30 +95,26 @@ openclaw gateway restart
|
|
|
95
95
|
|
|
96
96
|
> Tip: if you later re-run scaffold with `--apply-config`, the recipe’s tool policy may overwrite your manual edits. If you want a change to stick, encode it in the recipe.
|
|
97
97
|
|
|
98
|
-
## Installing skills
|
|
99
|
-
ClawRecipes favors **workspace-local** installs so each
|
|
98
|
+
## Installing skills
|
|
99
|
+
ClawRecipes favors **workspace-local** installs so each agent/team workspace is self-contained.
|
|
100
100
|
|
|
101
101
|
### Install a skill slug
|
|
102
102
|
```bash
|
|
103
|
-
openclaw recipes install <skill-slug>
|
|
103
|
+
openclaw recipes install-skill <skill-slug>
|
|
104
104
|
# or non-interactive:
|
|
105
|
-
openclaw recipes install <skill-slug> --yes
|
|
105
|
+
openclaw recipes install-skill <skill-slug> --yes
|
|
106
106
|
```
|
|
107
107
|
|
|
108
|
-
|
|
109
|
-
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
- standalone agent workspace: `~/.openclaw/workspace-<agentId>/skills/<skill-slug>`
|
|
113
|
-
- team workspace: `~/.openclaw/workspace-<teamId>/skills/<skill-slug>`
|
|
114
|
-
|
|
115
|
-
> Note: in the new workspace policy, standalone agents live in `~/.openclaw/workspace-<agentId>` and teams live in `~/.openclaw/workspace-<teamId>`. Skill install targeting is still being refined during the experimental phase.
|
|
108
|
+
By default, installs **globally** into `~/.openclaw/skills/`. Use flags to target a specific workspace:
|
|
109
|
+
- `--global` — shared across all agents (default)
|
|
110
|
+
- `--agent-id <id>` — `~/.openclaw/workspace-<agentId>/skills/<skill-slug>`
|
|
111
|
+
- `--team-id <id>` — `~/.openclaw/workspace-<teamId>/skills/<skill-slug>`
|
|
116
112
|
|
|
117
113
|
### Install the skills required by a recipe
|
|
118
114
|
If a recipe declares skills in `requiredSkills` or `optionalSkills`:
|
|
119
115
|
|
|
120
116
|
```bash
|
|
121
|
-
openclaw recipes install <recipe-id>
|
|
117
|
+
openclaw recipes install-skill <recipe-id>
|
|
122
118
|
```
|
|
123
119
|
|
|
124
120
|
That installs the recipe’s declared skills.
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
# Architecture
|
|
2
|
+
|
|
3
|
+
Overview of the ClawRecipes codebase for maintainers.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
`index.ts` acts as thin CLI wiring: it registers commands via `api.registerCli` and delegates to handlers. Business logic lives in `src/handlers/` and `src/lib/`. The plugin exports `__internal` for test-only access to handlers and lib functions.
|
|
8
|
+
|
|
9
|
+
## File structure
|
|
10
|
+
|
|
11
|
+
```
|
|
12
|
+
ClawRecipes/
|
|
13
|
+
├── index.ts # Entry point: CLI wiring, delegates to handlers
|
|
14
|
+
├── src/
|
|
15
|
+
│ ├── handlers/ # One file per command group
|
|
16
|
+
│ │ ├── cron.ts # Cron reconciliation (used during scaffold)
|
|
17
|
+
│ │ ├── install.ts # install-skill, install (marketplace recipe)
|
|
18
|
+
│ │ ├── recipes.ts # list, show, status, bind, unbind, bindings
|
|
19
|
+
│ │ ├── scaffold.ts # scaffold (single agent)
|
|
20
|
+
│ │ ├── team.ts # scaffold-team, migrate-team, remove-team
|
|
21
|
+
│ │ └── tickets.ts # tickets, move-ticket, assign, take, handoff, dispatch, complete
|
|
22
|
+
│ └── lib/ # Shared logic
|
|
23
|
+
│ ├── recipes-config.ts # OpenClaw config load/write, bindings, agent snippets
|
|
24
|
+
│ ├── recipes.ts # Recipe listing, loading, workspace paths
|
|
25
|
+
│ ├── recipe-id.ts # Pick recipe id (auto-increment)
|
|
26
|
+
│ ├── scaffold-utils.ts # Shared scaffold logic
|
|
27
|
+
│ ├── ticket-workflow.ts # Ticket stages, move, assign, handoff
|
|
28
|
+
│ ├── ticket-finder.ts # Ticket lookup by id/number
|
|
29
|
+
│ ├── lanes.ts # Workflow stages (backlog, in-progress, testing, done)
|
|
30
|
+
│ ├── cleanup-workspaces.ts
|
|
31
|
+
│ ├── remove-team.ts # Team uninstall logic
|
|
32
|
+
│ └── ... # prompt, template, skill-install, fs-utils, etc.
|
|
33
|
+
├── recipes/ # Bundled recipe markdown files
|
|
34
|
+
├── scripts/ # Smell-check, scaffold smoke, etc.
|
|
35
|
+
└── tests/ # Vitest unit tests
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Handler-to-command map
|
|
39
|
+
|
|
40
|
+
| Handler | Commands |
|
|
41
|
+
|-----------|----------|
|
|
42
|
+
| recipes | list, show, status, bind, unbind, bindings |
|
|
43
|
+
| scaffold | scaffold |
|
|
44
|
+
| team | scaffold-team, migrate-team, remove-team |
|
|
45
|
+
| tickets | tickets, move-ticket, assign, take, handoff, dispatch, complete |
|
|
46
|
+
| install | install-skill (ClawHub skills), install (marketplace recipe), install-recipe (alias) |
|
|
47
|
+
| cron | Reconciled during scaffold (no standalone command) |
|
|
48
|
+
|
|
49
|
+
## Shared scaffold flow
|
|
50
|
+
|
|
51
|
+
Both `scaffold` and `scaffold-team` use:
|
|
52
|
+
|
|
53
|
+
- `scaffoldAgentFromRecipe` — creates workspace, writes recipe-managed files, applies agent config
|
|
54
|
+
- `reconcileRecipeCronJobs` — when a recipe declares `cronJobs`, installs/updates cron jobs per `cronInstallation` config
|
|
55
|
+
|
|
56
|
+
Cron behavior applies to both commands when the recipe has `cronJobs` in frontmatter.
|
|
57
|
+
|
|
58
|
+
## Data flow
|
|
59
|
+
|
|
60
|
+
```mermaid
|
|
61
|
+
flowchart LR
|
|
62
|
+
subgraph cli [CLI]
|
|
63
|
+
Cmd[recipes list/show/status/...]
|
|
64
|
+
end
|
|
65
|
+
subgraph idx [index.ts]
|
|
66
|
+
IndexTS[registerCli]
|
|
67
|
+
end
|
|
68
|
+
subgraph handlers [Handlers]
|
|
69
|
+
HRecipes[recipes.ts]
|
|
70
|
+
HScaffold[scaffold.ts]
|
|
71
|
+
HTeam[team.ts]
|
|
72
|
+
HTickets[tickets.ts]
|
|
73
|
+
HInstall[install.ts]
|
|
74
|
+
end
|
|
75
|
+
subgraph lib [Lib]
|
|
76
|
+
LibRecipes[recipes-config]
|
|
77
|
+
LibScaffold[scaffold-utils]
|
|
78
|
+
LibTickets[ticket-workflow]
|
|
79
|
+
end
|
|
80
|
+
Cmd --> IndexTS
|
|
81
|
+
IndexTS --> HRecipes
|
|
82
|
+
IndexTS --> HScaffold
|
|
83
|
+
IndexTS --> HTeam
|
|
84
|
+
IndexTS --> HTickets
|
|
85
|
+
IndexTS --> HInstall
|
|
86
|
+
HRecipes --> LibRecipes
|
|
87
|
+
HScaffold --> LibScaffold
|
|
88
|
+
HTeam --> LibScaffold
|
|
89
|
+
HTickets --> LibTickets
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## Key decisions
|
|
93
|
+
|
|
94
|
+
- **Tool policy preservation**: When a recipe omits `tools`, the scaffold preserves the existing agent's tool policy (rather than resetting it). See scaffold logic and tests.
|
|
95
|
+
- **`__internal` export**: Unit tests import handlers and lib helpers via `__internal`; these are not part of the public plugin API.
|
|
96
|
+
|
|
97
|
+
## Quality automation
|
|
98
|
+
|
|
99
|
+
- **smell-check**: `npm run smell-check` runs:
|
|
100
|
+
- **ESLint**: `no-explicit-any`, `complexity`, `max-lines-per-function`, `max-params` (src/; index.ts exempt from complexity/lines)
|
|
101
|
+
- **jscpd**: Duplicate code detection (≥8 lines, ≥50 tokens)
|
|
102
|
+
- **Pattern grep**: `as any` in src/ (max 10), TODO/FIXME/XXX (max 20)
|
|
103
|
+
- **lint**: `npm run lint` / `npm run lint:fix`
|
|
104
|
+
- **tests**: `npm test` (vitest), `npm run test:coverage`
|
|
105
|
+
- **CI**: `.github/workflows/ci.yml` runs test:coverage, smell-check, npm audit
|
|
106
|
+
|
|
107
|
+
---
|
|
108
|
+
|
|
109
|
+
If this doc is outdated, please submit a PR to update it.
|
package/docs/BUNDLED_RECIPES.md
CHANGED
|
@@ -19,8 +19,8 @@ openclaw recipes scaffold project-manager --agent-id pm --name "Project Manager"
|
|
|
19
19
|
```
|
|
20
20
|
|
|
21
21
|
What it writes:
|
|
22
|
-
- `
|
|
23
|
-
- `
|
|
22
|
+
- `workspace-pm/SOUL.md`
|
|
23
|
+
- `workspace-pm/AGENTS.md`
|
|
24
24
|
|
|
25
25
|
Default tool policy (recipe-defined):
|
|
26
26
|
- allows: `group:fs`, `group:web`, plus `cron` and `message`
|
|
@@ -272,18 +272,7 @@ openclaw recipes scaffold-team financial-planner-team --team-id financial-planne
|
|
|
272
272
|
Roles:
|
|
273
273
|
- lead, advisor, analyst, tax, insurance, ops
|
|
274
274
|
|
|
275
|
-
## 17) `
|
|
276
|
-
**Use when:** you want a trading workflow: research/signals/risk/journaling.
|
|
277
|
-
|
|
278
|
-
Scaffold:
|
|
279
|
-
```bash
|
|
280
|
-
openclaw recipes scaffold-team stock-trader-team --team-id stock-trader-team-team --apply-config
|
|
281
|
-
```
|
|
282
|
-
|
|
283
|
-
Roles:
|
|
284
|
-
- lead, researcher, signals, risk, journal, ops
|
|
285
|
-
|
|
286
|
-
## 18) `crypto-trader-team` (team)
|
|
275
|
+
## 17) `crypto-trader-team` (team)
|
|
287
276
|
**Use when:** you want a crypto trading workflow with onchain research.
|
|
288
277
|
|
|
289
278
|
Scaffold:
|
package/docs/COMMANDS.md
CHANGED
|
@@ -46,18 +46,13 @@ Options:
|
|
|
46
46
|
- `--overwrite-recipe` (overwrite the generated workspace recipe file if it already exists)
|
|
47
47
|
- `--overwrite` (overwrite recipe-managed files)
|
|
48
48
|
- `--apply-config` (write/update `agents.list[]` in OpenClaw config)
|
|
49
|
+
- Cron: see Cron installation under scaffold-team.
|
|
49
50
|
|
|
50
51
|
Also writes a workspace recipe file:
|
|
51
52
|
- `~/.openclaw/workspace/recipes/<recipeId>.md`
|
|
52
53
|
|
|
53
54
|
## `scaffold-team <recipeId>`
|
|
54
55
|
|
|
55
|
-
### Cron installation config
|
|
56
|
-
If a recipe declares `cronJobs`, scaffold will reconcile those jobs using the plugin config key:
|
|
57
|
-
- `plugins.entries.recipes.config.cronInstallation`: `off | prompt | on`
|
|
58
|
-
- `off`: never install/reconcile
|
|
59
|
-
- `prompt` (default): prompt each run (default answer is **No**)
|
|
60
|
-
- `on`: install/reconcile; new jobs follow `enabledByDefault`
|
|
61
56
|
Scaffold a shared **team workspace** + multiple agents from a **team** recipe.
|
|
62
57
|
|
|
63
58
|
```bash
|
|
@@ -91,30 +86,55 @@ Standard folders:
|
|
|
91
86
|
Also creates agent config entries under `agents.list[]` (when `--apply-config`), with agent ids:
|
|
92
87
|
- `<teamId>-<role>`
|
|
93
88
|
|
|
94
|
-
|
|
89
|
+
### Cron installation
|
|
90
|
+
If a recipe declares `cronJobs`, scaffold and scaffold-team reconcile those jobs using the plugin config key:
|
|
91
|
+
- `plugins.entries.recipes.config.cronInstallation`: `off | prompt | on`
|
|
92
|
+
- `off`: never install/reconcile
|
|
93
|
+
- `prompt` (default): prompt each run (default answer is **No**)
|
|
94
|
+
- `on`: install/reconcile; new jobs follow `enabledByDefault`
|
|
95
|
+
|
|
96
|
+
Applies to both `scaffold` and `scaffold-team` when the recipe declares `cronJobs`.
|
|
97
|
+
|
|
98
|
+
## `install-skill <idOrSlug> [--yes]`
|
|
95
99
|
Install skills from ClawHub (confirmation-gated).
|
|
96
100
|
|
|
97
|
-
Default
|
|
101
|
+
Default: **global** into `~/.openclaw/skills`.
|
|
98
102
|
|
|
99
103
|
```bash
|
|
100
104
|
# Global (shared across all agents)
|
|
101
|
-
openclaw recipes install agentchat --yes
|
|
105
|
+
openclaw recipes install-skill agentchat --yes
|
|
102
106
|
|
|
103
107
|
# Agent-scoped (into workspace-<agentId>/skills)
|
|
104
|
-
openclaw recipes install agentchat --yes --agent-id dev
|
|
108
|
+
openclaw recipes install-skill agentchat --yes --agent-id dev
|
|
105
109
|
|
|
106
110
|
# Team-scoped (into workspace-<teamId>/skills)
|
|
107
|
-
openclaw recipes install agentchat --yes --team-id development-team-team
|
|
111
|
+
openclaw recipes install-skill agentchat --yes --team-id development-team-team
|
|
108
112
|
```
|
|
109
113
|
|
|
114
|
+
Options:
|
|
115
|
+
- `--yes` — skip confirmation
|
|
116
|
+
- `--global` — install into global skills (default when no scope flags)
|
|
117
|
+
- `--agent-id <id>` — install into agent workspace
|
|
118
|
+
- `--team-id <id>` — install into team workspace
|
|
119
|
+
|
|
110
120
|
Behavior:
|
|
111
121
|
- If `idOrSlug` matches a recipe id, installs that recipe’s `requiredSkills` + `optionalSkills`.
|
|
112
122
|
- Otherwise treats it as a ClawHub skill slug.
|
|
113
|
-
- Installs via
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
123
|
+
- Installs via `npx clawhub@latest ...`. Confirmation-gated unless `--yes`. In non-interactive mode (no TTY), requires `--yes`.
|
|
124
|
+
|
|
125
|
+
## `install <slug>`
|
|
126
|
+
Install a marketplace recipe into your workspace recipes dir (by slug).
|
|
127
|
+
|
|
128
|
+
```bash
|
|
129
|
+
openclaw recipes install development-team
|
|
130
|
+
openclaw recipes install development-team --overwrite
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
Options:
|
|
134
|
+
- `--registry-base <url>` — Marketplace API base URL (default: `https://clawkitchen.ai`)
|
|
135
|
+
- `--overwrite` — overwrite existing recipe file
|
|
136
|
+
|
|
137
|
+
Use `install-recipe` as an alias for this command.
|
|
118
138
|
|
|
119
139
|
## `bind`
|
|
120
140
|
Add/update a multi-agent routing binding (writes `bindings[]` in `~/.openclaw/openclaw.json`).
|
|
@@ -166,10 +186,58 @@ Options:
|
|
|
166
186
|
- `--mode move|copy`
|
|
167
187
|
- `--overwrite` (merge into existing destination)
|
|
168
188
|
|
|
189
|
+
## `remove-team`
|
|
190
|
+
Safe uninstall: remove a scaffolded team workspace, agents from config, and stamped cron jobs.
|
|
191
|
+
|
|
192
|
+
```bash
|
|
193
|
+
openclaw recipes remove-team --team-id development-team-team --plan --json
|
|
194
|
+
openclaw recipes remove-team --team-id development-team-team --yes
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
Options:
|
|
198
|
+
- `--team-id <teamId>` (required)
|
|
199
|
+
- `--plan` — print plan and exit without applying
|
|
200
|
+
- `--json` — output JSON
|
|
201
|
+
- `--yes` — skip confirmation (apply destructive changes)
|
|
202
|
+
- `--include-ambiguous` — also remove cron jobs that only loosely match the team (dangerous)
|
|
203
|
+
|
|
204
|
+
Notes:
|
|
205
|
+
- Confirmation-gated by default. Use `--yes` to apply without prompting.
|
|
206
|
+
- Cron cleanup removes only cron jobs stamped with `recipes.teamId=<teamId>`.
|
|
207
|
+
- Restart required after removal: `openclaw gateway restart`
|
|
208
|
+
|
|
169
209
|
## `dispatch`
|
|
170
|
-
Convert a natural-language request into file-first execution artifacts.
|
|
210
|
+
Convert a natural-language request into file-first execution artifacts (inbox + backlog ticket + assignment stubs).
|
|
211
|
+
|
|
212
|
+
```bash
|
|
213
|
+
openclaw recipes dispatch \
|
|
214
|
+
--team-id development-team-team \
|
|
215
|
+
--request "Add a customer-support team recipe" \
|
|
216
|
+
--owner lead
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
Options:
|
|
220
|
+
- `--team-id <teamId>` (required)
|
|
221
|
+
- `--request <text>` (optional; prompts in TTY)
|
|
222
|
+
- `--owner dev|devops|lead|test` (default: `dev`)
|
|
223
|
+
- `--yes` (skip review prompt)
|
|
224
|
+
|
|
225
|
+
Creates:
|
|
226
|
+
- `workspace-<teamId>/inbox/<timestamp>-<slug>.md`
|
|
227
|
+
- `workspace-<teamId>/work/backlog/<NNNN>-<slug>.md`
|
|
228
|
+
- `workspace-<teamId>/work/assignments/<NNNN>-assigned-<owner>.md`
|
|
229
|
+
|
|
230
|
+
Ticket numbering:
|
|
231
|
+
- Scans `work/backlog`, `work/in-progress`, `work/testing`, `work/done` and uses max+1.
|
|
171
232
|
|
|
172
|
-
|
|
233
|
+
Review-before-write:
|
|
234
|
+
- Prints a JSON plan and asks for confirmation unless `--yes`.
|
|
235
|
+
|
|
236
|
+
## Ticket workflow commands
|
|
237
|
+
|
|
238
|
+
The following commands manage the file-first ticket flow (`work/backlog` → `in-progress` → `testing` → `done`).
|
|
239
|
+
|
|
240
|
+
### `tickets`
|
|
173
241
|
List tickets for a team across the standard workflow stages.
|
|
174
242
|
|
|
175
243
|
```bash
|
|
@@ -202,7 +270,7 @@ openclaw recipes cleanup-workspaces --prefix smoke- --prefix qa- --yes
|
|
|
202
270
|
openclaw recipes cleanup-workspaces --json
|
|
203
271
|
```
|
|
204
272
|
|
|
205
|
-
|
|
273
|
+
### `move-ticket`
|
|
206
274
|
Move a ticket file between workflow stages and update the ticket’s `Status:` field.
|
|
207
275
|
|
|
208
276
|
```bash
|
|
@@ -217,7 +285,7 @@ Stages:
|
|
|
217
285
|
- `testing` → `Status: testing`
|
|
218
286
|
- `done` → `Status: done` (optional `Completed:` timestamp)
|
|
219
287
|
|
|
220
|
-
|
|
288
|
+
### `assign`
|
|
221
289
|
Assign a ticket to an owner (updates `Owner:` and creates an assignment stub).
|
|
222
290
|
|
|
223
291
|
```bash
|
|
@@ -227,14 +295,14 @@ openclaw recipes assign --team-id <teamId> --ticket 0007 --owner lead
|
|
|
227
295
|
|
|
228
296
|
Owners (current): `dev|devops|lead|test`.
|
|
229
297
|
|
|
230
|
-
|
|
298
|
+
### `take`
|
|
231
299
|
Shortcut: assign + move to in-progress.
|
|
232
300
|
|
|
233
301
|
```bash
|
|
234
302
|
openclaw recipes take --team-id <teamId> --ticket 0007 --owner dev
|
|
235
303
|
```
|
|
236
304
|
|
|
237
|
-
|
|
305
|
+
### `handoff`
|
|
238
306
|
QA handoff in one step: move a ticket to `work/testing/`, set `Status: testing`, assign to a tester (default `test`), and write/update the assignment stub.
|
|
239
307
|
|
|
240
308
|
```bash
|
|
@@ -246,33 +314,9 @@ Notes:
|
|
|
246
314
|
- Creates `work/testing/` if missing.
|
|
247
315
|
- Idempotent: if the ticket is already in `work/testing/`, it won’t re-move it; it will ensure fields + assignment stub.
|
|
248
316
|
|
|
249
|
-
|
|
250
|
-
Shortcut: move to done + ensure `Status: done` + add `Completed:` timestamp.
|
|
317
|
+
### `complete`
|
|
318
|
+
Shortcut: move to done + ensure `Status: done` + add `Completed:` timestamp. No confirmation prompt.
|
|
251
319
|
|
|
252
320
|
```bash
|
|
253
321
|
openclaw recipes complete --team-id <teamId> --ticket 0007
|
|
254
322
|
```
|
|
255
|
-
|
|
256
|
-
```bash
|
|
257
|
-
openclaw recipes dispatch \
|
|
258
|
-
--team-id development-team-team \
|
|
259
|
-
--request "Add a customer-support team recipe" \
|
|
260
|
-
--owner lead
|
|
261
|
-
```
|
|
262
|
-
|
|
263
|
-
Options:
|
|
264
|
-
- `--team-id <teamId>` (required)
|
|
265
|
-
- `--request <text>` (optional; prompts in TTY)
|
|
266
|
-
- `--owner dev|devops|lead|test` (default: `dev`)
|
|
267
|
-
- `--yes` (skip review prompt)
|
|
268
|
-
|
|
269
|
-
Creates (createOnly):
|
|
270
|
-
- `workspace-<teamId>/inbox/<timestamp>-<slug>.md`
|
|
271
|
-
- `workspace-<teamId>/work/backlog/<NNNN>-<slug>.md`
|
|
272
|
-
- `workspace-<teamId>/work/assignments/<NNNN>-assigned-<owner>.md`
|
|
273
|
-
|
|
274
|
-
Ticket numbering:
|
|
275
|
-
- Scans `work/backlog`, `work/in-progress`, `work/testing`, `work/done` and uses max+1.
|
|
276
|
-
|
|
277
|
-
Review-before-write:
|
|
278
|
-
- Prints a JSON plan and asks for confirmation unless `--yes`.
|
package/docs/INSTALLATION.md
CHANGED
|
@@ -7,11 +7,11 @@ This repo is an **OpenClaw plugin** (not a standalone CLI). OpenClaw loads it an
|
|
|
7
7
|
## Prerequisites
|
|
8
8
|
- OpenClaw installed and working (`openclaw --version`)
|
|
9
9
|
- Node.js available (OpenClaw uses Node to load plugins)
|
|
10
|
-
- (For `recipes install`) you’ll need access to ClawHub (the command runs `npx clawhub@latest ...`).
|
|
10
|
+
- (For `recipes install-skill`) you’ll need access to ClawHub (the command runs `npx clawhub@latest ...`).
|
|
11
11
|
|
|
12
12
|
## Install
|
|
13
13
|
### Option A (preferred): install from npm
|
|
14
|
-
|
|
14
|
+
When published on npm, you can install directly:
|
|
15
15
|
|
|
16
16
|
```bash
|
|
17
17
|
openclaw plugins install @jiggai/recipes
|
|
@@ -34,7 +34,7 @@ openclaw gateway restart
|
|
|
34
34
|
openclaw plugins list
|
|
35
35
|
```
|
|
36
36
|
|
|
37
|
-
### Option
|
|
37
|
+
### Option C: already cloned
|
|
38
38
|
```bash
|
|
39
39
|
openclaw plugins install --link ~/clawrecipes
|
|
40
40
|
openclaw gateway restart
|
|
@@ -47,7 +47,7 @@ openclaw plugins list
|
|
|
47
47
|
# look for id: recipes
|
|
48
48
|
```
|
|
49
49
|
|
|
50
|
-
|
|
50
|
+
Try a basic command:
|
|
51
51
|
```bash
|
|
52
52
|
openclaw recipes list
|
|
53
53
|
```
|
|
@@ -77,7 +77,7 @@ openclaw gateway restart
|
|
|
77
77
|
- Check: `openclaw plugins list`
|
|
78
78
|
- Verify `openclaw.plugin.json` exists at repo root and has `id: "recipes"`.
|
|
79
79
|
|
|
80
|
-
### `recipes install` fails
|
|
80
|
+
### `recipes install-skill` fails
|
|
81
81
|
- Run `npx clawhub@latest --help` to confirm the CLI can run.
|
|
82
82
|
- Ensure you are logged into ClawHub if required (`npx clawhub@latest login`).
|
|
83
83
|
- Confirm the install scope you intended:
|
package/docs/RECIPE_FORMAT.md
CHANGED
|
@@ -9,7 +9,7 @@ They can be:
|
|
|
9
9
|
## File locations
|
|
10
10
|
Recipes are discovered from:
|
|
11
11
|
- Built-in: `recipes/default/*.md` inside this plugin
|
|
12
|
-
- Workspace-local: `<
|
|
12
|
+
- Workspace-local: `<openclawWorkspace>/recipes/*.md` (default). `<openclawWorkspace>` is your OpenClaw workspace root (typically `~/.openclaw/workspace` or the directory configured in OpenClaw)
|
|
13
13
|
|
|
14
14
|
## Frontmatter (common)
|
|
15
15
|
```yaml
|
|
@@ -32,7 +32,7 @@ These are **ClawHub skill slugs**.
|
|
|
32
32
|
|
|
33
33
|
They’re used by:
|
|
34
34
|
- `openclaw recipes status` (detect missing skills)
|
|
35
|
-
- `openclaw recipes install <recipeId>` (install the listed skills)
|
|
35
|
+
- `openclaw recipes install-skill <recipeId>` (install the listed skills)
|
|
36
36
|
|
|
37
37
|
## Agent recipes
|
|
38
38
|
Agent recipes use templates + files.
|
package/docs/TEAM_WORKFLOW.md
CHANGED
|
@@ -21,6 +21,9 @@ When you scaffold a team:
|
|
|
21
21
|
```
|
|
22
22
|
|
|
23
23
|
## The loop
|
|
24
|
+
|
|
25
|
+
CLI commands: `dispatch`, `tickets`, `move-ticket`, `assign`, `take`, `handoff`, `complete`. See [COMMANDS.md](COMMANDS.md) for full reference.
|
|
26
|
+
|
|
24
27
|
1) **Intake**
|
|
25
28
|
- New requests land in `inbox/`.
|
|
26
29
|
|
|
@@ -88,7 +91,7 @@ Then restart the gateway.
|
|
|
88
91
|
Tip: use the `agents_list` tool to see what’s currently allowed.
|
|
89
92
|
|
|
90
93
|
## Dispatcher command
|
|
91
|
-
The lead can convert a natural-language request into artifacts with:
|
|
94
|
+
The lead can convert a natural-language request into artifacts with [dispatch](COMMANDS.md#dispatch):
|
|
92
95
|
|
|
93
96
|
```bash
|
|
94
97
|
openclaw recipes dispatch --team-id <teamId> --request "..." --owner dev
|
|
@@ -29,7 +29,7 @@ kind: team
|
|
|
29
29
|
version: 0.1.0
|
|
30
30
|
description: A tiny demo team
|
|
31
31
|
|
|
32
|
-
# Optional: skill slugs to install with `openclaw recipes install my-first-team`
|
|
32
|
+
# Optional: skill slugs to install with `openclaw recipes install-skill my-first-team`
|
|
33
33
|
requiredSkills: []
|
|
34
34
|
|
|
35
35
|
team:
|
|
@@ -133,13 +133,12 @@ This will propose (or write, with `--yes`) three artifacts:
|
|
|
133
133
|
Tickets move through lanes:
|
|
134
134
|
- `work/backlog/` → `work/in-progress/` → `work/testing/` → `work/done/`
|
|
135
135
|
|
|
136
|
-
|
|
137
|
-
-
|
|
138
|
-
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
-
|
|
142
|
-
- Move the ticket to `work/done/` and add a short completion report
|
|
136
|
+
You can move tickets manually (edit files) or use the CLI:
|
|
137
|
+
- `openclaw recipes take --team-id my-first-team-team --ticket 0001 --owner worker` — assign and move to in-progress
|
|
138
|
+
- `openclaw recipes handoff --team-id my-first-team-team --ticket 0001` — move to testing, assign to test
|
|
139
|
+
- `openclaw recipes complete --team-id my-first-team-team --ticket 0001` — move to done
|
|
140
|
+
|
|
141
|
+
See `docs/COMMANDS.md` for `move-ticket`, `assign`, and other ticket commands.
|
|
143
142
|
|
|
144
143
|
## Common mistakes
|
|
145
144
|
- **Forgetting the `-team` suffix** on `--team-id` (required).
|
package/openclaw.plugin.json
CHANGED
package/package.json
CHANGED
|
@@ -268,6 +268,26 @@ templates:
|
|
|
268
268
|
- Research briefs → outbox/research/
|
|
269
269
|
- Metrics definitions/dashboards notes → shared-context/metrics/
|
|
270
270
|
|
|
271
|
+
files:
|
|
272
|
+
- path: SOUL.md
|
|
273
|
+
template: soul
|
|
274
|
+
mode: createOnly
|
|
275
|
+
- path: AGENTS.md
|
|
276
|
+
template: agents
|
|
277
|
+
mode: createOnly
|
|
278
|
+
- path: TOOLS.md
|
|
279
|
+
template: tools
|
|
280
|
+
mode: createOnly
|
|
281
|
+
- path: STATUS.md
|
|
282
|
+
template: status
|
|
283
|
+
mode: createOnly
|
|
284
|
+
- path: NOTES.md
|
|
285
|
+
template: notes
|
|
286
|
+
mode: createOnly
|
|
287
|
+
tools:
|
|
288
|
+
profile: "business"
|
|
289
|
+
allow: ["group:fs", "group:web"]
|
|
290
|
+
deny: ["exec"]
|
|
271
291
|
---
|
|
272
292
|
|
|
273
293
|
# Business Team Recipe
|
|
@@ -7,17 +7,25 @@ kind: team
|
|
|
7
7
|
cronJobs:
|
|
8
8
|
- id: lead-triage-loop
|
|
9
9
|
name: "Lead triage loop"
|
|
10
|
-
schedule: "*/30
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
10
|
+
schedule: "*/30 8-23 * * 1-5"
|
|
11
|
+
agentId: "{{teamId}}-lead"
|
|
12
|
+
channel: "last"
|
|
13
|
+
message: "Lead triage loop (automated). Goal: keep tickets moving. Steps: run ticket hygiene; inspect board; pull next actionable work into in-progress; assign owners; write updates to tickets (## Comments) and notes/status.md. If lowest-numbered in-progress is hard-blocked, advance the next unblocked ticket (or pull from backlog)."
|
|
14
|
+
enabledByDefault: false
|
|
14
15
|
- id: execution-loop
|
|
15
16
|
name: "Execution loop"
|
|
16
|
-
schedule: "*/30
|
|
17
|
-
|
|
18
|
-
|
|
17
|
+
schedule: "*/30 8-23 * * 1-5"
|
|
18
|
+
agentId: "{{teamId}}-lead"
|
|
19
|
+
channel: "last"
|
|
20
|
+
message: "Execution loop (automated). Goal: make concrete progress on the lowest-numbered in-progress ticket. Run ticket hygiene; perform repo lint/build checks (avoid noisy tests unless defined); update ticket + notes/status.md. Send a message only when there is a material change."
|
|
21
|
+
enabledByDefault: false
|
|
22
|
+
- id: pr-watcher
|
|
23
|
+
name: "PR watcher"
|
|
24
|
+
schedule: "*/30 8-23 * * 1-5"
|
|
25
|
+
agentId: "{{teamId}}-lead"
|
|
26
|
+
channel: "last"
|
|
27
|
+
message: "PR watcher (automated). Goal: watch ticket-linked PR URLs; summarize failing checks; if merged, move tickets to done with a short completion report. Requires GitHub access/auth on the controller."
|
|
19
28
|
enabledByDefault: false
|
|
20
|
-
# pr-watcher omitted (enable only when a real PR integration exists)
|
|
21
29
|
requiredSkills: []
|
|
22
30
|
team:
|
|
23
31
|
teamId: development-team
|
|
@@ -64,6 +72,7 @@ templates:
|
|
|
64
72
|
|
|
65
73
|
Team: {{teamId}}
|
|
66
74
|
Shared workspace: {{teamDir}}
|
|
75
|
+
Role: lead
|
|
67
76
|
|
|
68
77
|
## Guardrails (read → act → write)
|
|
69
78
|
|
|
@@ -72,24 +81,26 @@ templates:
|
|
|
72
81
|
- `notes/plan.md`
|
|
73
82
|
- `notes/status.md`
|
|
74
83
|
- `shared-context/priorities.md`
|
|
75
|
-
- the
|
|
84
|
+
- the current ticket
|
|
85
|
+
|
|
86
|
+
During work (every loop):
|
|
87
|
+
1) Run hygiene:
|
|
88
|
+
- `./scripts/ticket-hygiene.sh`
|
|
76
89
|
|
|
77
90
|
After you act:
|
|
78
91
|
1) Write back:
|
|
79
|
-
- Update
|
|
80
|
-
- Keep `notes/status.md` current (3–5 bullets per active ticket).
|
|
92
|
+
- Update the ticket with decisions/assignments and a dated entry under `## Comments`.
|
|
93
|
+
- Keep `notes/status.md` current (add 3–5 bullets per active ticket).
|
|
94
|
+
- Append detailed logs/output to `shared-context/agent-outputs/` (append-only).
|
|
81
95
|
|
|
82
96
|
## Curator model
|
|
83
97
|
|
|
84
|
-
You
|
|
98
|
+
You curate:
|
|
85
99
|
- `notes/plan.md`
|
|
86
100
|
- `shared-context/priorities.md`
|
|
87
101
|
|
|
88
|
-
|
|
102
|
+
Other agents should not edit curated files; they append to:
|
|
89
103
|
- `shared-context/agent-outputs/` (append-only)
|
|
90
|
-
- `shared-context/feedback/`
|
|
91
|
-
|
|
92
|
-
Your job is to periodically distill those inputs into the curated files.
|
|
93
104
|
|
|
94
105
|
## File-first workflow (tickets)
|
|
95
106
|
|
|
@@ -101,29 +112,9 @@ templates:
|
|
|
101
112
|
- `work/in-progress/` — tickets currently being executed
|
|
102
113
|
- `work/testing/` — tickets awaiting QA verification
|
|
103
114
|
- `work/done/` — completed tickets + completion notes
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
- `
|
|
107
|
-
|
|
108
|
-
### Ticket numbering (critical)
|
|
109
|
-
- Backlog tickets MUST be named `0001-...md`, `0002-...md`, etc.
|
|
110
|
-
- The developer pulls the lowest-numbered ticket assigned to them.
|
|
111
|
-
|
|
112
|
-
### Ticket format
|
|
113
|
-
See `TICKETS.md` in the team root. Every ticket should include:
|
|
114
|
-
- Context
|
|
115
|
-
- Requirements
|
|
116
|
-
- Acceptance criteria
|
|
117
|
-
- Owner (dev/devops)
|
|
118
|
-
- Status
|
|
119
|
-
|
|
120
|
-
### Your responsibilities
|
|
121
|
-
- For every new request in `inbox/`, create a normalized ticket in `work/backlog/`.
|
|
122
|
-
- Curate `notes/plan.md` and `shared-context/priorities.md`.
|
|
123
|
-
- Keep `notes/status.md` updated.
|
|
124
|
-
- When work is ready for QA, move the ticket to `work/testing/` and assign it to the tester.
|
|
125
|
-
- Only after QA verification, move the ticket to `work/done/` (or use `openclaw recipes complete`).
|
|
126
|
-
- When a completion appears in `work/done/`, write a short summary into `outbox/`.
|
|
115
|
+
|
|
116
|
+
Handoff to QA (keeps assignment stubs consistent):
|
|
117
|
+
- `openclaw recipes handoff --team-id {{teamId}} --ticket <NNNN> --yes`
|
|
127
118
|
|
|
128
119
|
dev.soul: |
|
|
129
120
|
# SOUL.md
|
|
@@ -134,26 +125,32 @@ templates:
|
|
|
134
125
|
dev.agents: |
|
|
135
126
|
# AGENTS.md
|
|
136
127
|
|
|
137
|
-
Team: {teamId}
|
|
138
|
-
Shared workspace: {teamDir}
|
|
128
|
+
Team: {{teamId}}
|
|
129
|
+
Shared workspace: {{teamDir}}
|
|
139
130
|
Role: dev
|
|
140
131
|
|
|
141
132
|
## Guardrails (read → act → write)
|
|
142
|
-
Before you act:
|
|
143
|
-
1) Read:
|
|
144
|
-
- `notes/plan.md`
|
|
145
|
-
- `notes/status.md`
|
|
146
|
-
- relevant ticket(s) in `work/in-progress/`
|
|
147
|
-
- any relevant shared context under `shared-context/`
|
|
148
133
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
134
|
+
Before you change anything, read:
|
|
135
|
+
- `notes/plan.md`
|
|
136
|
+
- `notes/status.md`
|
|
137
|
+
- `shared-context/priorities.md`
|
|
138
|
+
- the current ticket
|
|
153
139
|
|
|
154
|
-
|
|
155
|
-
-
|
|
156
|
-
|
|
140
|
+
Quick hygiene around stage moves:
|
|
141
|
+
- `./scripts/ticket-hygiene-dev.sh`
|
|
142
|
+
|
|
143
|
+
After you act, write back:
|
|
144
|
+
- Update the ticket with what you did + verification steps.
|
|
145
|
+
- Check/respond in the ticket’s `## Comments` section (especially if you were pinged).
|
|
146
|
+
- Add 3–5 bullets to `notes/status.md`.
|
|
147
|
+
- Append detailed logs/output to `shared-context/agent-outputs/` (append-only).
|
|
148
|
+
|
|
149
|
+
## Pull system
|
|
150
|
+
|
|
151
|
+
- Continue any ticket already assigned to you in `work/in-progress/`.
|
|
152
|
+
- Otherwise pull the lowest-numbered assigned ticket from `work/backlog/` and move it to `work/in-progress/`.
|
|
153
|
+
- Keep changes small and reversible.
|
|
157
154
|
devops.soul: |
|
|
158
155
|
# SOUL.md
|
|
159
156
|
|
|
@@ -241,26 +238,30 @@ templates:
|
|
|
241
238
|
test.agents: |
|
|
242
239
|
# AGENTS.md
|
|
243
240
|
|
|
244
|
-
Team: {teamId}
|
|
245
|
-
Shared workspace: {teamDir}
|
|
241
|
+
Team: {{teamId}}
|
|
242
|
+
Shared workspace: {{teamDir}}
|
|
246
243
|
Role: test
|
|
247
244
|
|
|
248
245
|
## Guardrails (read → act → write)
|
|
249
|
-
Before you act:
|
|
250
|
-
1) Read:
|
|
251
|
-
- `notes/plan.md`
|
|
252
|
-
- `notes/status.md`
|
|
253
|
-
- relevant ticket(s) in `work/in-progress/`
|
|
254
|
-
- any relevant shared context under `shared-context/`
|
|
255
246
|
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
247
|
+
Before you verify anything, read:
|
|
248
|
+
- `notes/plan.md`
|
|
249
|
+
- `notes/status.md`
|
|
250
|
+
- `shared-context/priorities.md`
|
|
251
|
+
- the ticket in `work/testing/`
|
|
260
252
|
|
|
261
|
-
|
|
262
|
-
-
|
|
263
|
-
|
|
253
|
+
After you verify, write back:
|
|
254
|
+
- Record QA verification (preferred): create `work/testing/<ticket>.testing-verified.md`.
|
|
255
|
+
- Alternative: add a clear `## QA verification` section in the ticket.
|
|
256
|
+
- If failing: write a concise bug note, then move the ticket back to `work/in-progress/` and assign to the right owner.
|
|
257
|
+
- Check/respond in the ticket’s `## Comments` section.
|
|
258
|
+
- Add 3–5 bullets to `notes/status.md`.
|
|
259
|
+
- Append detailed logs/output to `shared-context/agent-outputs/` (append-only).
|
|
260
|
+
|
|
261
|
+
## Testing lane workflow
|
|
262
|
+
|
|
263
|
+
- Work from `work/testing/`.
|
|
264
|
+
- Follow the ticket’s “How to test” steps and validate acceptance criteria.
|
|
264
265
|
test.tools: |
|
|
265
266
|
# TOOLS.md
|
|
266
267
|
|
package/src/handlers/cron.ts
CHANGED
|
@@ -9,6 +9,36 @@ import { toolsInvoke, type ToolTextResult } from "../toolsInvoke";
|
|
|
9
9
|
|
|
10
10
|
export type CronInstallMode = "off" | "prompt" | "on";
|
|
11
11
|
|
|
12
|
+
function interpolateTemplate(input: string | undefined, vars: Record<string, string>): string | undefined {
|
|
13
|
+
if (input == null) return undefined;
|
|
14
|
+
let out = String(input);
|
|
15
|
+
for (const [k, v] of Object.entries(vars)) {
|
|
16
|
+
out = out.replaceAll(`{{${k}}}`, v);
|
|
17
|
+
}
|
|
18
|
+
return out;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function applyCronJobVars(
|
|
22
|
+
scope: CronReconcileScope,
|
|
23
|
+
j: { id: string; name?: string; schedule?: string; timezone?: string; channel?: string; to?: string; agentId?: string; description?: string; message?: string; enabledByDefault?: boolean },
|
|
24
|
+
): typeof j {
|
|
25
|
+
const vars: Record<string, string> = {
|
|
26
|
+
recipeId: scope.recipeId,
|
|
27
|
+
...(scope.kind === "team" ? { teamId: scope.teamId } : { agentId: scope.agentId }),
|
|
28
|
+
};
|
|
29
|
+
return {
|
|
30
|
+
...j,
|
|
31
|
+
name: interpolateTemplate(j.name, vars),
|
|
32
|
+
schedule: interpolateTemplate(j.schedule, vars),
|
|
33
|
+
timezone: interpolateTemplate(j.timezone, vars),
|
|
34
|
+
channel: interpolateTemplate(j.channel, vars),
|
|
35
|
+
to: interpolateTemplate(j.to, vars),
|
|
36
|
+
agentId: interpolateTemplate(j.agentId, vars),
|
|
37
|
+
description: interpolateTemplate(j.description, vars),
|
|
38
|
+
message: interpolateTemplate(j.message, vars),
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
|
|
12
42
|
type OpenClawCronJob = {
|
|
13
43
|
id: string;
|
|
14
44
|
name?: string;
|
|
@@ -145,15 +175,29 @@ async function resolveCronUserOptIn(
|
|
|
145
175
|
mode: CronInstallMode,
|
|
146
176
|
recipeId: string,
|
|
147
177
|
desiredCount: number
|
|
148
|
-
): Promise<
|
|
178
|
+
): Promise<
|
|
179
|
+
| { userOptIn: boolean; enableInstalled: boolean }
|
|
180
|
+
| { return: { ok: true; changed: false; note: string; desiredCount: number } }
|
|
181
|
+
> {
|
|
149
182
|
if (mode === "off") return { return: { ok: true, changed: false, note: "cron-installation-off" as const, desiredCount } };
|
|
150
|
-
if (mode === "on") return { userOptIn: true };
|
|
183
|
+
if (mode === "on") return { userOptIn: true, enableInstalled: true };
|
|
184
|
+
|
|
185
|
+
// mode === "prompt"
|
|
186
|
+
// In non-interactive runs we still reconcile (create/update) cron jobs, but always DISABLED.
|
|
187
|
+
// This keeps scaffold idempotent and avoids silently skipping cron job stamping.
|
|
188
|
+
if (!process.stdin.isTTY) {
|
|
189
|
+
console.error(
|
|
190
|
+
`Non-interactive mode: cronInstallation=prompt; reconciling ${desiredCount} cron job(s) as disabled (no prompt).`
|
|
191
|
+
);
|
|
192
|
+
return { userOptIn: false, enableInstalled: false };
|
|
193
|
+
}
|
|
151
194
|
|
|
152
195
|
const header = `Recipe ${recipeId} defines ${desiredCount} cron job(s).\nThese run automatically on a schedule. Install them?`;
|
|
153
196
|
const userOptIn = await promptYesNo(header);
|
|
154
197
|
if (!userOptIn) return { return: { ok: true, changed: false, note: "cron-installation-declined" as const, desiredCount } };
|
|
155
|
-
|
|
156
|
-
|
|
198
|
+
|
|
199
|
+
const enableInstalled = await promptYesNo("Enable the installed cron jobs now? (You can always enable later)");
|
|
200
|
+
return { userOptIn, enableInstalled };
|
|
157
201
|
}
|
|
158
202
|
|
|
159
203
|
async function createNewCronJob(opts: {
|
|
@@ -183,12 +227,13 @@ async function updateExistingCronJob(opts: {
|
|
|
183
227
|
prevSpecHash: string | undefined;
|
|
184
228
|
specHash: string;
|
|
185
229
|
userOptIn: boolean;
|
|
230
|
+
enableInstalled: boolean;
|
|
186
231
|
key: string;
|
|
187
232
|
now: number;
|
|
188
233
|
state: Awaited<ReturnType<typeof loadCronMappingState>>;
|
|
189
234
|
results: CronReconcileResult[];
|
|
190
235
|
}) {
|
|
191
|
-
const { api, j, name, existing, prevSpecHash, specHash, userOptIn, key, now, state, results } = opts;
|
|
236
|
+
const { api, j, name, existing, prevSpecHash, specHash, userOptIn, enableInstalled, key, now, state, results } = opts;
|
|
192
237
|
if (prevSpecHash !== specHash) {
|
|
193
238
|
await cronUpdate(api, existing.id, buildCronJobPatch(j, name));
|
|
194
239
|
results.push({ action: "updated", key, installedCronId: existing.id });
|
|
@@ -199,6 +244,11 @@ async function updateExistingCronJob(opts: {
|
|
|
199
244
|
await cronUpdate(api, existing.id, { enabled: false });
|
|
200
245
|
results.push({ action: "disabled", key, installedCronId: existing.id });
|
|
201
246
|
}
|
|
247
|
+
|
|
248
|
+
if (userOptIn && enableInstalled && !existing.enabled) {
|
|
249
|
+
await cronUpdate(api, existing.id, { enabled: true });
|
|
250
|
+
results.push({ action: "updated", key, installedCronId: existing.id });
|
|
251
|
+
}
|
|
202
252
|
state.entries[key] = { installedCronId: existing.id, specHash, updatedAtMs: now, orphaned: false };
|
|
203
253
|
}
|
|
204
254
|
|
|
@@ -212,31 +262,47 @@ async function reconcileOneCronJob(
|
|
|
212
262
|
results: CronReconcileResult[];
|
|
213
263
|
},
|
|
214
264
|
j: (ReturnType<typeof normalizeCronJobs>)[number],
|
|
215
|
-
userOptIn: boolean
|
|
265
|
+
userOptIn: boolean,
|
|
266
|
+
enableInstalled: boolean
|
|
216
267
|
) {
|
|
217
268
|
const { api, scope, state, byId, now, results } = ctx;
|
|
218
|
-
const
|
|
219
|
-
const
|
|
269
|
+
const jj = applyCronJobVars(scope, j);
|
|
270
|
+
const key = cronKey(scope, jj.id);
|
|
271
|
+
const name =
|
|
272
|
+
jj.name ?? `${scope.kind === "team" ? scope.teamId : scope.agentId} • ${scope.recipeId} • ${jj.id}`;
|
|
220
273
|
const specHash = hashSpec({
|
|
221
|
-
schedule:
|
|
222
|
-
message:
|
|
223
|
-
timezone:
|
|
224
|
-
channel:
|
|
225
|
-
to:
|
|
226
|
-
agentId:
|
|
274
|
+
schedule: jj.schedule,
|
|
275
|
+
message: jj.message,
|
|
276
|
+
timezone: jj.timezone ?? "",
|
|
277
|
+
channel: jj.channel ?? "last",
|
|
278
|
+
to: jj.to ?? "",
|
|
279
|
+
agentId: jj.agentId ?? "",
|
|
227
280
|
name,
|
|
228
|
-
description:
|
|
281
|
+
description: jj.description ?? "",
|
|
229
282
|
});
|
|
230
283
|
|
|
231
284
|
const prev = state.entries[key];
|
|
232
285
|
const existing = prev?.installedCronId ? byId.get(prev.installedCronId) : undefined;
|
|
233
|
-
const wantEnabled = userOptIn ? Boolean(
|
|
286
|
+
const wantEnabled = userOptIn ? (enableInstalled ? true : Boolean(jj.enabledByDefault)) : false;
|
|
234
287
|
|
|
235
288
|
if (!existing) {
|
|
236
|
-
await createNewCronJob({ api, scope, j, wantEnabled, key, specHash, now, state, results });
|
|
289
|
+
await createNewCronJob({ api, scope, j: jj, wantEnabled, key, specHash, now, state, results });
|
|
237
290
|
return;
|
|
238
291
|
}
|
|
239
|
-
await updateExistingCronJob({
|
|
292
|
+
await updateExistingCronJob({
|
|
293
|
+
api,
|
|
294
|
+
j: jj,
|
|
295
|
+
name,
|
|
296
|
+
existing,
|
|
297
|
+
prevSpecHash: prev?.specHash,
|
|
298
|
+
specHash,
|
|
299
|
+
userOptIn,
|
|
300
|
+
enableInstalled,
|
|
301
|
+
key,
|
|
302
|
+
now,
|
|
303
|
+
state,
|
|
304
|
+
results,
|
|
305
|
+
});
|
|
240
306
|
}
|
|
241
307
|
|
|
242
308
|
async function reconcileDesiredCronJobs(opts: {
|
|
@@ -244,6 +310,7 @@ async function reconcileDesiredCronJobs(opts: {
|
|
|
244
310
|
scope: CronReconcileScope;
|
|
245
311
|
desired: ReturnType<typeof normalizeCronJobs>;
|
|
246
312
|
userOptIn: boolean;
|
|
313
|
+
enableInstalled: boolean;
|
|
247
314
|
state: Awaited<ReturnType<typeof loadCronMappingState>>;
|
|
248
315
|
byId: Map<string, OpenClawCronJob>;
|
|
249
316
|
now: number;
|
|
@@ -258,7 +325,7 @@ async function reconcileDesiredCronJobs(opts: {
|
|
|
258
325
|
results: opts.results,
|
|
259
326
|
};
|
|
260
327
|
for (const j of opts.desired) {
|
|
261
|
-
await reconcileOneCronJob(ctx, j, opts.userOptIn);
|
|
328
|
+
await reconcileOneCronJob(ctx, j, opts.userOptIn, opts.enableInstalled);
|
|
262
329
|
}
|
|
263
330
|
}
|
|
264
331
|
|
|
@@ -290,7 +357,16 @@ export async function reconcileRecipeCronJobs(opts: {
|
|
|
290
357
|
const desiredIds = new Set(desired.map((j) => j.id));
|
|
291
358
|
const results: CronReconcileResult[] = [];
|
|
292
359
|
|
|
293
|
-
await reconcileDesiredCronJobs({
|
|
360
|
+
await reconcileDesiredCronJobs({
|
|
361
|
+
...opts,
|
|
362
|
+
desired,
|
|
363
|
+
userOptIn: optIn.userOptIn,
|
|
364
|
+
enableInstalled: optIn.enableInstalled,
|
|
365
|
+
state,
|
|
366
|
+
byId,
|
|
367
|
+
now,
|
|
368
|
+
results,
|
|
369
|
+
});
|
|
294
370
|
await disableOrphanedCronJobs({
|
|
295
371
|
api: opts.api,
|
|
296
372
|
state,
|
package/docs/CLEANUP_TODO.md
DELETED
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
# Cleanup TODO
|
|
2
|
-
|
|
3
|
-
## Infrastructure
|
|
4
|
-
|
|
5
|
-
- [x] Add @vitest/coverage-v8, test:coverage script
|
|
6
|
-
- [x] Create TEST_COVERAGE_PROGRESS.md, CODE_SMELLS_TRACKER.md
|
|
7
|
-
- [x] Run baseline coverage, populate progress doc
|
|
8
|
-
- [x] Update TEST_COVERAGE_PROGRESS.md after each coverage batch
|
|
9
|
-
|
|
10
|
-
## Code Smells
|
|
11
|
-
|
|
12
|
-
- [x] Extract fileExists to src/lib/fs-utils
|
|
13
|
-
- [x] Extract stableStringify to src/lib/stable-stringify
|
|
14
|
-
- [x] Unify parseFrontmatter/normalizeCronJobs (use recipe-frontmatter)
|
|
15
|
-
- [x] Replace magic numbers in toolsInvoke
|
|
16
|
-
|
|
17
|
-
## Test Coverage
|
|
18
|
-
|
|
19
|
-
- [x] lanes.ts
|
|
20
|
-
- [x] ticket-finder.ts
|
|
21
|
-
- [x] cleanup-workspaces (expand)
|
|
22
|
-
- [x] remove-team (expand)
|
|
23
|
-
- [x] marketplaceFetch (with fetch mock)
|
|
24
|
-
- [x] toolsInvoke (with fetch mock)
|
|
25
|
-
- [x] Extracted config, template, cron-utils, agent-config
|
|
26
|
-
- [x] index.ts handlers (via extraction or integration)
|
|
27
|
-
|
|
28
|
-
## Final
|
|
29
|
-
|
|
30
|
-
- [x] Set coverage thresholds (phased: 25% baseline; target 95%)
|
|
31
|
-
- [x] Verify npm run test:coverage passes
|
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
# Code Smells Tracker
|
|
2
|
-
|
|
3
|
-
| # | Smell | Status | Priority | Notes |
|
|
4
|
-
|---|-------|--------|----------|-------|
|
|
5
|
-
| 1 | fileExists duplicated (index, ticket-finder, ticket-workflow, lanes, remove-team) | done | high | Extracted to src/lib/fs-utils |
|
|
6
|
-
| 2 | stableStringify duplicated (index, bindings) | done | high | Extracted to src/lib/stable-stringify |
|
|
7
|
-
| 3 | parseFrontmatter/normalizeCronJobs duplicated (index, recipe-frontmatter) | done | high | Unified; recipe-frontmatter extended with message/task/prompt |
|
|
8
|
-
| 4 | Magic numbers in toolsInvoke (30_000, 150*attempt) | done | medium | Extracted as TOOLS_INVOKE_TIMEOUT_MS, RETRY_DELAY_BASE_MS |
|
|
9
|
-
| 5 | index.ts god file (~2500 lines) | done | low | Extracted handlers to src/handlers/; index now ~670 lines (Phase 3) |
|
|
10
|
-
| 6 | Duplicate ticket-finding logic (ticket-workflow vs ticket-finder) | done | medium | ticket-workflow now delegates to ticket-finder |
|
|
11
|
-
| 7 | Duplicated ticket regex patterns | done | low | TICKET_FILENAME_REGEX in ticket-finder; tickets uses it |
|
|
12
|
-
| 8 | Hardcoded "recipes" in scaffold | done | low | scaffold uses cfg.workspaceRecipesDir |
|
|
13
|
-
| 9 | Lane path duplication in tickets | done | low | tickets uses ticketStageDir from lanes |
|
|
14
|
-
| 10 | Magic numbers (1000, 18789, 0000) | done | low | MAX_RECIPE_ID_AUTO_INCREMENT, GATEWAY_DEFAULT_PORT, DEFAULT_TICKET_NUMBER in constants |
|
|
15
|
-
| 11 | Overlapping TicketLane/TicketStage types | done | low | TicketLane = Exclude<TicketStage, "assignments"> in lanes; ticket-finder re-exports |
|
|
16
|
-
| 12 | Config load/write duplicated | done | medium | loadOpenClawConfig, writeOpenClawConfig in recipes-config; all callers use them |
|
|
17
|
-
| 13 | nextTicketNumber inline in dispatch | done | low | Extracted computeNextTicketNumber to ticket-finder |
|
|
18
|
-
| 14 | bindings.ts vs recipes-config duplication | done | low | bindings re-exports from recipes-config; bindings.test uses recipes-config |
|
|
19
|
-
| 15 | Silent catch in handleDispatch | done | low | Documented: non-critical enqueueSystemEvent, nudgeQueued reflects skip |
|
|
20
|
-
| 16 | Duplicate pickRecipeId (scaffold vs team) | done | medium | Extracted to src/lib/recipe-id |
|
|
21
|
-
| 17 | Hardcoded "recipes" in team | done | low | team uses cfg.workspaceRecipesDir |
|
|
22
|
-
| 18 | as any in recipe-frontmatter normalizeCronJobs | done | low | CronJobInput type |
|
|
23
|
-
| 19 | as any in cron.ts (scope, created response) | done | low | Proper types; CronAddResponse, CronReconcileResult |
|
|
24
|
-
| 20 | getCfg trivial wrapper in recipes | done | very low | Inlined getRecipesConfig |
|
|
25
|
-
| 21 | results: any[] in cron | done | low | CronReconcileResult union type |
|
|
26
|
-
|
|
27
|
-
## Automated smell detection
|
|
28
|
-
|
|
29
|
-
Run `npm run smell-check` to:
|
|
30
|
-
- **ESLint**: `no-explicit-any`, `complexity`, `max-lines-per-function`, `max-params` (src/; index.ts exempt from complexity/lines)
|
|
31
|
-
- **jscpd**: Duplicate code detection (≥8 lines, ≥50 tokens)
|
|
32
|
-
- **Pattern grep**: `as any` in src/ (max 10), TODO/FIXME/XXX (max 20)
|
|
33
|
-
|
|
34
|
-
Scripts: `npm run lint`, `npm run lint:fix`, `npm run jscpd`, `npm run smell-check`
|
|
35
|
-
|
|
36
|
-
## Resolution log
|
|
37
|
-
|
|
38
|
-
- **Smell 5**: Index handler extraction (Phase 2–3) moved ~1200 lines to src/handlers/{cron,recipes,scaffold,team,tickets,install}.ts. index.ts now thin CLI wiring only.
|
|
39
|
-
- **Smells 6–11**: Additional cleanup: consolidated ticket-finding, extracted regex/constants, used config for recipes dir, ticketStageDir, magic-number constants, unified lane types.
|
|
40
|
-
- **Smells 12–15**: Config helpers (load/write), computeNextTicketNumber, bindings consolidation, silent-catch documentation.
|
|
41
|
-
- **Smells 16–21**: pickRecipeId extracted to recipe-id; team uses workspaceRecipesDir; CronJobInput type; cron types (CronAddResponse, CronReconcileResult); getCfg inlined; config double-lookup comment.
|
|
42
|
-
- **Post-cleanup complete** (Feb 2026): All 21 smells resolved; smell-check passes with 0 warnings. Additional quality improvements: pre-commit hooks (husky + lint-staged), CI coverage enforcement, tsconfig.json, JSDoc for public APIs.
|
package/docs/SMELLS_TODO.md
DELETED
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
# Remaining Code Smells — TODO
|
|
2
|
-
|
|
3
|
-
## Type safety (`as any` / `any`) — DONE
|
|
4
|
-
|
|
5
|
-
- [x] **cron.ts** — Replaced with OpenClawCronJob, CronJobPatch, OpenClawPluginApi
|
|
6
|
-
- [x] **team.ts** — AgentScaffoldResult, MigrateStep types
|
|
7
|
-
- [x] **recipes-config.ts** — OpenClawConfigMutable
|
|
8
|
-
- [x] **remove-team.ts** — Record<string, unknown>
|
|
9
|
-
- [ ] **index.ts** — ~16 options: any remain (partial: install, scaffold use typed opts)
|
|
10
|
-
- [x] **Misc** — recipes, tickets, agent-config, config, cron-utils, marketplaceFetch, toolsInvoke, lanes, cleanup-workspaces, recipe-frontmatter
|
|
11
|
-
|
|
12
|
-
## Duplicate code (jscpd) — DONE
|
|
13
|
-
|
|
14
|
-
- [x] **ticket-workflow.ts** — patchTicketFields extracted
|
|
15
|
-
- [x] **tickets.ts** — dryRunTicketMove extracted
|
|
16
|
-
- [x] **index.ts** — runInstallRecipe, logScaffoldResult extracted
|
|
17
|
-
|
|
18
|
-
## Remaining (deferred)
|
|
19
|
-
|
|
20
|
-
- [ ] **Complexity** — reconcileRecipeCronJobs (55), handleScaffoldTeam (27), etc.
|
|
21
|
-
- [ ] **Long functions** — handleScaffoldTeam (163), reconcileRecipeCronJobs (139)
|
|
22
|
-
- [ ] **scaffold.ts ↔ team.ts** — pickRecipeId already shared; further unification optional
|
|
23
|
-
- [ ] **Console in src/lib** — Logging abstraction
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
# Test Coverage Progress
|
|
2
|
-
|
|
3
|
-
Target: 95% line coverage.
|
|
4
|
-
|
|
5
|
-
## Baseline (before cleanup)
|
|
6
|
-
|
|
7
|
-
| File | % Stmts | % Branch | % Funcs | % Lines | Notes |
|
|
8
|
-
|------|---------|----------|---------|---------|-------|
|
|
9
|
-
| index.ts | 4.16 | 61.53 | 18.42 | 4.16 | God file; most logic untested |
|
|
10
|
-
| src/marketplaceFetch.ts | 0 | 100 | 100 | 0 | Needs fetch mock |
|
|
11
|
-
| src/toolsInvoke.ts | 0 | 100 | 0 | 0 | Needs fetch mock |
|
|
12
|
-
| src/lib/bindings.ts | 91.42 | 66.66 | 100 | 91.42 | Lines 50-52 uncovered |
|
|
13
|
-
| src/lib/cleanup-workspaces.ts | 78.51 | 61.9 | 100 | 78.51 | |
|
|
14
|
-
| src/lib/lanes.ts | 60 | 83.33 | 66.66 | 60 | |
|
|
15
|
-
| src/lib/recipe-frontmatter.ts | 93.54 | 70.83 | 100 | 93.54 | |
|
|
16
|
-
| src/lib/remove-team.ts | 81.51 | 70.58 | 63.63 | 81.51 | |
|
|
17
|
-
| src/lib/scaffold-templates.ts | 100 | 100 | 100 | 100 | |
|
|
18
|
-
| src/lib/shared-context.ts | 100 | 85.71 | 100 | 100 | |
|
|
19
|
-
| src/lib/ticket-finder.ts | 0 | 0 | 0 | 0 | Not yet exercised by tests |
|
|
20
|
-
| src/lib/ticket-workflow.ts | 96.52 | 37.5 | 100 | 96.52 | |
|
|
21
|
-
| **All files** | **18.25** | **58.85** | **48.83** | **18.25** | |
|
|
22
|
-
|
|
23
|
-
## Gaps to address
|
|
24
|
-
|
|
25
|
-
- index.ts: extract logic to src/lib and test there; add integration tests for command handlers
|
|
26
|
-
- ticket-finder.ts: add tests
|
|
27
|
-
- marketplaceFetch, toolsInvoke: mock fetch, add tests
|
|
28
|
-
- lanes.ts, cleanup-workspaces, remove-team: expand tests
|
|
29
|
-
|
|
30
|
-
## Updates
|
|
31
|
-
|
|
32
|
-
- **Baseline**: Initial coverage run before cleanup work.
|
|
33
|
-
- **Post-cleanup**: 25% overall; src/lib at 91.53%, src/ at 98.36%. index.ts remains low (3.91%) — extraction moved logic to lib. Thresholds set at 25% baseline; target 95% as further index extraction proceeds.
|
|
34
|
-
- **Index handler extraction (Phase 2–3)**: Handler logic moved to src/handlers/; exercised via __internal in integration tests.
|
|
35
|
-
- **Post-extraction baseline** (npm run test:coverage): 56.55% overall; src/ at 98.36%; src/handlers at 66.44%; src/lib at 94.63%; index.ts at 9.62% (thin CLI wiring).
|
|
36
|
-
- **Comprehensive coverage plan** (Phase 1–3): cron-handler, install-handler, prompt, workspace, scaffold, team, tickets, recipes tests added. Thresholds raised to 60% lines/statements, 90% functions. index.ts remains thin wiring; logic exercised via __internal.
|
|
37
|
-
- **CI coverage enforcement** (Feb 2026): CI now runs `npm run test:coverage`; build fails if thresholds are not met. Thresholds: lines 60%, statements 60%, functions 90%, branches 65%.
|