@calltelemetry/openclaw-linear 0.8.0 → 0.8.1
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 +152 -34
- package/openclaw.plugin.json +3 -2
- package/package.json +1 -1
- package/prompts.yaml +27 -1
- package/src/agent/agent.test.ts +49 -0
- package/src/agent/agent.ts +26 -1
- package/src/infra/doctor.ts +2 -2
- package/src/pipeline/e2e-planning.test.ts +77 -54
- package/src/pipeline/intent-classify.test.ts +285 -0
- package/src/pipeline/intent-classify.ts +259 -0
- package/src/pipeline/planner.test.ts +159 -40
- package/src/pipeline/planner.ts +98 -32
- package/src/pipeline/webhook.ts +322 -226
- package/src/tools/claude-tool.ts +6 -0
- package/src/tools/code-tool.test.ts +3 -3
- package/src/tools/code-tool.ts +2 -2
- package/src/tools/planner-tools.test.ts +1 -1
- package/src/tools/planner-tools.ts +10 -2
package/README.md
CHANGED
|
@@ -11,8 +11,9 @@ Connect Linear to AI agents. Issues get triaged, implemented, and audited — au
|
|
|
11
11
|
|
|
12
12
|
- **New issue?** Agent estimates story points, adds labels, sets priority.
|
|
13
13
|
- **Assign to agent?** A worker implements it, an independent auditor verifies it, done.
|
|
14
|
-
- **Comment
|
|
15
|
-
- **Say "plan
|
|
14
|
+
- **Comment anything?** The bot understands natural language — no magic commands needed.
|
|
15
|
+
- **Say "let's plan the features"?** A planner interviews you, writes user stories, and builds your full issue hierarchy.
|
|
16
|
+
- **Plan looks good?** A different AI model automatically audits the plan before dispatch.
|
|
16
17
|
- **Agent goes silent?** A watchdog kills it and retries automatically.
|
|
17
18
|
- **Want updates?** Get notified on Discord, Slack, Telegram, or Signal.
|
|
18
19
|
|
|
@@ -284,53 +285,99 @@ If something went wrong, start with `log.jsonl` — it shows every phase, how lo
|
|
|
284
285
|
|
|
285
286
|
---
|
|
286
287
|
|
|
288
|
+
## Comment Routing — Talk Naturally
|
|
289
|
+
|
|
290
|
+
You don't need to memorize magic commands. The bot uses an LLM-based intent classifier to understand what you want from any comment.
|
|
291
|
+
|
|
292
|
+
```
|
|
293
|
+
User comment → Intent Classifier (small model, ~2s) → Route to handler
|
|
294
|
+
↓ (on failure)
|
|
295
|
+
Regex fallback → Route to handler
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
**What the bot understands:**
|
|
299
|
+
|
|
300
|
+
| What you say | What happens |
|
|
301
|
+
|---|---|
|
|
302
|
+
| "let's plan the features for this" | Starts planning interview |
|
|
303
|
+
| "looks good, ship it" (during planning) | Runs plan audit + cross-model review |
|
|
304
|
+
| "nevermind, cancel this" (during planning) | Exits planning mode |
|
|
305
|
+
| "hey kaylee can you look at this?" | Routes to Kaylee (no `@` needed) |
|
|
306
|
+
| "what can I do here?" | Default agent responds (not silently dropped) |
|
|
307
|
+
| "fix the search bug" | Default agent dispatches work |
|
|
308
|
+
|
|
309
|
+
`@mentions` still work as a fast path — if you write `@kaylee`, the classifier is skipped entirely for speed.
|
|
310
|
+
|
|
311
|
+
> **Tip:** Configure `classifierAgentId` to point to a small/fast model agent (like Haiku) for low-latency, low-cost intent classification. The classifier only needs ~300 tokens per call.
|
|
312
|
+
|
|
313
|
+
---
|
|
314
|
+
|
|
287
315
|
## Planning a Project
|
|
288
316
|
|
|
289
317
|
For larger work, the planner helps you break a project into issues with dependencies, then dispatches them automatically.
|
|
290
318
|
|
|
291
319
|
### Start planning
|
|
292
320
|
|
|
293
|
-
Comment on any issue that belongs to a Linear project:
|
|
321
|
+
Comment on any issue that belongs to a Linear project — use natural language:
|
|
294
322
|
|
|
295
|
-
>
|
|
323
|
+
> "let's plan out the features for this project"
|
|
296
324
|
|
|
297
|
-
The planner enters **interview mode** and asks you questions:
|
|
325
|
+
The planner enters **interview mode** and asks you questions one at a time:
|
|
298
326
|
|
|
299
|
-
>
|
|
327
|
+
> I'm entering planning mode for **Search Feature**. I'll interview you about the features you want to build, then structure everything into Linear issues.
|
|
328
|
+
>
|
|
329
|
+
> Let's start — what is this project about, and what are the main feature areas?
|
|
300
330
|
|
|
301
331
|
### Build the plan
|
|
302
332
|
|
|
303
|
-
Reply with your ideas. The planner creates issues
|
|
333
|
+
Reply with your ideas. The planner creates issues with **user stories** and **acceptance criteria**, sets dependencies, and asks follow-up questions:
|
|
304
334
|
|
|
305
335
|
> I've created 3 issues:
|
|
306
336
|
> - **PROJ-2:** Build search API endpoint (3 pts, blocks PROJ-3)
|
|
307
337
|
> - **PROJ-3:** Search results page (2 pts, blocked by PROJ-2)
|
|
308
338
|
> - **PROJ-4:** Autocomplete suggestions (1 pt, independent)
|
|
309
339
|
>
|
|
310
|
-
>
|
|
340
|
+
> For PROJ-2, here's what I wrote for acceptance criteria:
|
|
341
|
+
> - *Given* a user sends a search query, *When* results exist, *Then* they are returned with pagination
|
|
342
|
+
>
|
|
343
|
+
> Does that cover it? Should the autocomplete call a separate endpoint or share the search API?
|
|
344
|
+
|
|
345
|
+
The planner proactively asks for:
|
|
346
|
+
- **User stories** — "As a [role], I want [feature] so that [benefit]"
|
|
347
|
+
- **Acceptance criteria** — Given/When/Then format
|
|
348
|
+
- **UAT test scenarios** — How to manually verify the feature
|
|
311
349
|
|
|
312
350
|
Keep replying until the plan looks right. The planner updates issues in real time.
|
|
313
351
|
|
|
314
|
-
### Finalize
|
|
352
|
+
### Finalize & Cross-Model Review
|
|
315
353
|
|
|
316
|
-
When you're happy
|
|
354
|
+
When you're happy, say something like "looks good" or "finalize plan". The planner runs a validation check:
|
|
355
|
+
- Every issue has a description (50+ characters) with acceptance criteria
|
|
356
|
+
- Every non-epic issue has an estimate and priority
|
|
357
|
+
- No circular dependencies in the DAG
|
|
317
358
|
|
|
318
|
-
|
|
359
|
+
**If validation passes, a cross-model review runs automatically:**
|
|
319
360
|
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
361
|
+
> ## Plan Passed Checks
|
|
362
|
+
>
|
|
363
|
+
> **3 issues** with valid dependency graph.
|
|
364
|
+
>
|
|
365
|
+
> Let me have **Codex** audit this and make recommendations.
|
|
325
366
|
|
|
326
|
-
|
|
367
|
+
A different AI model (always the complement of your primary model) reviews the plan for gaps:
|
|
327
368
|
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
369
|
+
| Your primary model | Auto-reviewer |
|
|
370
|
+
|---|---|
|
|
371
|
+
| Claude / Anthropic | Codex |
|
|
372
|
+
| Codex / OpenAI | Gemini |
|
|
373
|
+
| Gemini / Google | Codex |
|
|
374
|
+
| Other (Kimi, Mistral, etc.) | Gemini |
|
|
375
|
+
|
|
376
|
+
After the review, the planner summarizes recommendations and asks you to approve:
|
|
332
377
|
|
|
333
|
-
|
|
378
|
+
> Codex suggested adding error handling scenarios to PROJ-2 and noted PROJ-4 could be split into frontend/backend. I've updated PROJ-2's acceptance criteria. The PROJ-4 split is optional — your call.
|
|
379
|
+
>
|
|
380
|
+
> If you're happy with this plan, say **approve plan** to start dispatching.
|
|
334
381
|
|
|
335
382
|
**If validation fails:**
|
|
336
383
|
|
|
@@ -340,13 +387,16 @@ The project enters **DAG dispatch mode** — issues are assigned to the agent au
|
|
|
340
387
|
> - PROJ-2: description too short (< 50 chars)
|
|
341
388
|
> - PROJ-3: missing estimate
|
|
342
389
|
>
|
|
390
|
+
> **Warnings:**
|
|
391
|
+
> - PROJ-4: no acceptance criteria found in description
|
|
392
|
+
>
|
|
343
393
|
> Please address these issues, then say "finalize plan" again.
|
|
344
394
|
|
|
345
|
-
Fix the issues and try again. You can also say
|
|
395
|
+
Fix the issues and try again. You can also say "cancel" or "stop planning" to exit without dispatching.
|
|
346
396
|
|
|
347
397
|
### DAG dispatch progress
|
|
348
398
|
|
|
349
|
-
|
|
399
|
+
After approval, issues are assigned to the agent automatically in dependency order. Up to 3 issues run in parallel.
|
|
350
400
|
|
|
351
401
|
> `📊 Search Feature: 2/3 complete`
|
|
352
402
|
|
|
@@ -364,11 +414,14 @@ If an issue gets stuck (all retries failed), dependent issues are blocked and yo
|
|
|
364
414
|
|---|---|
|
|
365
415
|
| Create a new issue | Agent triages — adds estimate, labels, priority |
|
|
366
416
|
| Assign an issue to the agent | Worker → Audit → Done (or retry, or escalate) |
|
|
367
|
-
| Comment
|
|
368
|
-
|
|
|
369
|
-
|
|
|
370
|
-
|
|
|
371
|
-
|
|
|
417
|
+
| Comment anything on an issue | Intent classifier routes to the right handler |
|
|
418
|
+
| Mention an agent by name (with or without `@`) | That agent responds |
|
|
419
|
+
| Ask a question or request work | Default agent handles it |
|
|
420
|
+
| Say "plan this project" (on a project issue) | Planning interview starts |
|
|
421
|
+
| Reply during planning | Issues created/updated with user stories & AC |
|
|
422
|
+
| Say "looks good" / "finalize plan" | Validates → cross-model review → approval |
|
|
423
|
+
| Say "approve plan" (after review) | Dispatches all issues in dependency order |
|
|
424
|
+
| Say "cancel" / "abandon planning" | Exits planning mode |
|
|
372
425
|
| `/dispatch list` | Shows all active dispatches |
|
|
373
426
|
| `/dispatch retry CT-123` | Re-runs a stuck dispatch |
|
|
374
427
|
| `/dispatch status CT-123` | Detailed dispatch info |
|
|
@@ -401,6 +454,8 @@ Add settings under the plugin entry in `openclaw.json`:
|
|
|
401
454
|
| Key | Type | Default | What it does |
|
|
402
455
|
|---|---|---|---|
|
|
403
456
|
| `defaultAgentId` | string | `"default"` | Which agent runs the pipeline |
|
|
457
|
+
| `classifierAgentId` | string | — | Agent for intent classification (use a small/fast model like Haiku) |
|
|
458
|
+
| `plannerReviewModel` | string | auto | Cross-model plan reviewer: `"claude"`, `"codex"`, or `"gemini"`. Auto-detects the complement of your primary model. |
|
|
404
459
|
| `enableAudit` | boolean | `true` | Run auditor after implementation |
|
|
405
460
|
| `enableOrchestration` | boolean | `true` | Allow `spawn_agent` / `ask_agent` tools |
|
|
406
461
|
| `maxReworkAttempts` | number | `2` | Max audit failures before escalation |
|
|
@@ -408,11 +463,13 @@ Add settings under the plugin entry in `openclaw.json`:
|
|
|
408
463
|
| `worktreeBaseDir` | string | `"~/.openclaw/worktrees"` | Where worktrees are created |
|
|
409
464
|
| `repos` | object | — | Multi-repo map (see [Multi-Repo](#multi-repo)) |
|
|
410
465
|
| `dispatchStatePath` | string | `"~/.openclaw/linear-dispatch-state.json"` | Dispatch state file |
|
|
466
|
+
| `planningStatePath` | string | `"~/.openclaw/linear-planning-state.json"` | Planning session state file |
|
|
411
467
|
| `promptsPath` | string | — | Custom prompts file path |
|
|
412
468
|
| `notifications` | object | — | Notification targets (see [Notifications](#notifications)) |
|
|
413
469
|
| `inactivitySec` | number | `120` | Kill agent if silent this long |
|
|
414
470
|
| `maxTotalSec` | number | `7200` | Max total agent session time |
|
|
415
471
|
| `toolTimeoutSec` | number | `600` | Max single `code_run` time |
|
|
472
|
+
| `claudeApiKey` | string | — | Anthropic API key for Claude CLI (passed as `ANTHROPIC_API_KEY` env var). Required if using Claude backend. |
|
|
416
473
|
|
|
417
474
|
### Environment Variables
|
|
418
475
|
|
|
@@ -456,9 +513,17 @@ One agent must have `"isDefault": true` — that's the one that handles triage a
|
|
|
456
513
|
|
|
457
514
|
Create `coding-tools.json` in the plugin root to configure which CLI backend agents use:
|
|
458
515
|
|
|
516
|
+
> **Warning — Claude Code (Anthropic) and headless/automated usage**
|
|
517
|
+
>
|
|
518
|
+
> Calling Claude Code via CLI in a headless or automated context (which is how this plugin
|
|
519
|
+
> uses it) may violate [Anthropic's Terms of Service](https://www.anthropic.com/terms).
|
|
520
|
+
> The default backend is **Codex CLI** (OpenAI). **Gemini CLI** (Google) is used as the
|
|
521
|
+
> cross-model reviewer. If you choose to use Claude despite this, you do so at your own risk.
|
|
522
|
+
> See [Claude API Key](#claude-api-key) below for opt-in configuration.
|
|
523
|
+
|
|
459
524
|
```json
|
|
460
525
|
{
|
|
461
|
-
"codingTool": "
|
|
526
|
+
"codingTool": "codex",
|
|
462
527
|
"agentCodingTools": {},
|
|
463
528
|
"backends": {
|
|
464
529
|
"claude": { "aliases": ["claude", "claude code", "anthropic"] },
|
|
@@ -468,7 +533,33 @@ Create `coding-tools.json` in the plugin root to configure which CLI backend age
|
|
|
468
533
|
}
|
|
469
534
|
```
|
|
470
535
|
|
|
471
|
-
The agent calls `code_run` without knowing which backend is active. Resolution order: explicit `backend` parameter > per-agent override > global default > `"
|
|
536
|
+
The agent calls `code_run` without knowing which backend is active. Resolution order: explicit `backend` parameter > per-agent override > global default > `"codex"`.
|
|
537
|
+
|
|
538
|
+
#### Claude API Key
|
|
539
|
+
|
|
540
|
+
If you opt in to using Claude as a backend (despite the TOS concerns noted above), you can
|
|
541
|
+
provide an Anthropic API key so the Claude CLI authenticates via API key instead of its
|
|
542
|
+
built-in interactive auth.
|
|
543
|
+
|
|
544
|
+
Set `claudeApiKey` in the plugin config:
|
|
545
|
+
|
|
546
|
+
```json
|
|
547
|
+
{
|
|
548
|
+
"plugins": {
|
|
549
|
+
"entries": {
|
|
550
|
+
"openclaw-linear": {
|
|
551
|
+
"config": {
|
|
552
|
+
"claudeApiKey": "sk-ant-..."
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
```
|
|
559
|
+
|
|
560
|
+
The key is passed to the Claude CLI subprocess as the `ANTHROPIC_API_KEY` environment variable.
|
|
561
|
+
You can also set `ANTHROPIC_API_KEY` as a process-level environment variable (e.g., in your
|
|
562
|
+
systemd unit file) as a fallback. The plugin config value takes precedence if both are set.
|
|
472
563
|
|
|
473
564
|
---
|
|
474
565
|
|
|
@@ -569,6 +660,10 @@ rework:
|
|
|
569
660
|
| `{{tier}}` | Complexity tier (junior/medior/senior) |
|
|
570
661
|
| `{{attempt}}` | Current attempt number |
|
|
571
662
|
| `{{gaps}}` | Audit gaps from previous attempt |
|
|
663
|
+
| `{{projectName}}` | Project name (planner prompts) |
|
|
664
|
+
| `{{planSnapshot}}` | Current plan structure (planner prompts) |
|
|
665
|
+
| `{{reviewModel}}` | Name of cross-model reviewer (planner review) |
|
|
666
|
+
| `{{crossModelFeedback}}` | Review recommendations (planner review) |
|
|
572
667
|
|
|
573
668
|
### CLI
|
|
574
669
|
|
|
@@ -762,8 +857,8 @@ Example output:
|
|
|
762
857
|
✔ Default agent: coder
|
|
763
858
|
|
|
764
859
|
Coding Tools
|
|
765
|
-
✔ coding-tools.json loaded (default:
|
|
766
|
-
✔
|
|
860
|
+
✔ coding-tools.json loaded (default: codex)
|
|
861
|
+
✔ codex: found at /usr/local/bin/codex
|
|
767
862
|
|
|
768
863
|
Files & Directories
|
|
769
864
|
✔ Dispatch state: 1 active, 5 completed
|
|
@@ -785,7 +880,7 @@ Every warning and error includes a `→` line telling you what to do. Run `docto
|
|
|
785
880
|
|
|
786
881
|
### Unit tests
|
|
787
882
|
|
|
788
|
-
|
|
883
|
+
454 tests covering the full pipeline — triage, dispatch, audit, planning, intent classification, cross-model review, notifications, and infrastructure:
|
|
789
884
|
|
|
790
885
|
```bash
|
|
791
886
|
cd ~/claw-extensions/linear
|
|
@@ -806,6 +901,7 @@ npx tsx scripts/uat-linear.ts
|
|
|
806
901
|
npx tsx scripts/uat-linear.ts --test dispatch
|
|
807
902
|
npx tsx scripts/uat-linear.ts --test planning
|
|
808
903
|
npx tsx scripts/uat-linear.ts --test mention
|
|
904
|
+
npx tsx scripts/uat-linear.ts --test intent
|
|
809
905
|
```
|
|
810
906
|
|
|
811
907
|
**What each scenario does:**
|
|
@@ -868,6 +964,25 @@ npx tsx scripts/uat-linear.ts --test mention
|
|
|
868
964
|
[mention] Total: 18s
|
|
869
965
|
```
|
|
870
966
|
|
|
967
|
+
#### `--test intent` (Natural language routing)
|
|
968
|
+
|
|
969
|
+
1. Creates a test issue and posts a question (no `@mention`)
|
|
970
|
+
2. Verifies the bot responds (not silently dropped)
|
|
971
|
+
3. Posts a comment with an agent name but no `@` prefix
|
|
972
|
+
4. Verifies that agent responds
|
|
973
|
+
5. Tests plan review flow with cross-model audit
|
|
974
|
+
|
|
975
|
+
**Expected output:**
|
|
976
|
+
|
|
977
|
+
```
|
|
978
|
+
[intent] Created issue ENG-202
|
|
979
|
+
[intent] Posted "what can I do with this?" — waiting for response...
|
|
980
|
+
[intent] ✔ Bot responded to question (12s)
|
|
981
|
+
[intent] Posted "hey kaylee analyze this" — waiting for response...
|
|
982
|
+
[intent] ✔ Kaylee responded without @mention (15s)
|
|
983
|
+
[intent] Total: 27s
|
|
984
|
+
```
|
|
985
|
+
|
|
871
986
|
### Verify notifications
|
|
872
987
|
|
|
873
988
|
```bash
|
|
@@ -947,6 +1062,9 @@ journalctl --user -u openclaw-gateway -f # Watch live logs
|
|
|
947
1062
|
| Audit always fails | Run `openclaw openclaw-linear prompts validate` to check prompt syntax. |
|
|
948
1063
|
| Multi-repo not detected | Markers must be `<!-- repos: name1, name2 -->`. Names must match `repos` config keys. |
|
|
949
1064
|
| `/dispatch` not responding | Restart gateway. Check plugin loaded with `openclaw doctor`. |
|
|
1065
|
+
| Comments ignored (no response) | Check logs for intent classification results. If classifier fails, regex fallback may not match. |
|
|
1066
|
+
| Intent classifier slow | Set `classifierAgentId` to a small model agent (Haiku). Default uses your primary model. |
|
|
1067
|
+
| Cross-model review fails | The reviewer model CLI must be installed. Check logs for "cross-model review unavailable". |
|
|
950
1068
|
| Rich notifications are plain text | Set `"richFormat": true` in notifications config. |
|
|
951
1069
|
| Gateway rejects config keys | Strict validator. Run `openclaw doctor --fix`. |
|
|
952
1070
|
|
package/openclaw.plugin.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"id": "openclaw-linear",
|
|
3
3
|
"name": "Linear Agent",
|
|
4
4
|
"description": "Linear integration with OAuth support, agent pipeline, and webhook-driven AI agent lifecycle",
|
|
5
|
-
"version": "0.8.
|
|
5
|
+
"version": "0.8.1",
|
|
6
6
|
"configSchema": {
|
|
7
7
|
"type": "object",
|
|
8
8
|
"additionalProperties": false,
|
|
@@ -58,7 +58,8 @@
|
|
|
58
58
|
"maxReworkAttempts": { "type": "number", "description": "Max audit failures before escalation", "default": 2 },
|
|
59
59
|
"inactivitySec": { "type": "number", "description": "Kill sessions with no I/O for this many seconds (default: 120)", "default": 120 },
|
|
60
60
|
"maxTotalSec": { "type": "number", "description": "Max total runtime for agent sessions in seconds (default: 7200)", "default": 7200 },
|
|
61
|
-
"toolTimeoutSec": { "type": "number", "description": "Max runtime for a single code_run CLI invocation in seconds (default: 600)", "default": 600 }
|
|
61
|
+
"toolTimeoutSec": { "type": "number", "description": "Max runtime for a single code_run CLI invocation in seconds (default: 600)", "default": 600 },
|
|
62
|
+
"claudeApiKey": { "type": "string", "description": "Anthropic API key for Claude CLI backend (passed as ANTHROPIC_API_KEY env var)", "sensitive": true }
|
|
62
63
|
}
|
|
63
64
|
}
|
|
64
65
|
}
|
package/package.json
CHANGED
package/prompts.yaml
CHANGED
|
@@ -96,13 +96,20 @@ planner:
|
|
|
96
96
|
- Issues under epics are concrete deliverables with acceptance criteria.
|
|
97
97
|
- Sub-issues are atomic work units that together complete a parent issue.
|
|
98
98
|
- Use "blocks" relationships to express ordering: if A must finish before B starts, A blocks B.
|
|
99
|
-
- Every issue description must include clear acceptance criteria.
|
|
100
99
|
- Every non-epic issue needs a story point estimate and priority.
|
|
100
|
+
- Every issue description must include:
|
|
101
|
+
- A user story: "As a [role], I want [feature] so that [benefit]"
|
|
102
|
+
- Acceptance criteria in Given/When/Then format
|
|
103
|
+
- At least one UAT test scenario describing how to verify the feature manually
|
|
104
|
+
- If the user skips acceptance criteria, write reasonable defaults and confirm with them.
|
|
101
105
|
|
|
102
106
|
INTERVIEW APPROACH:
|
|
103
107
|
- Ask ONE focused question at a time. Never dump a questionnaire.
|
|
104
108
|
- After each user response, create or update issues to capture what you learned.
|
|
105
109
|
- Briefly summarize what you added before asking your next question.
|
|
110
|
+
- After capturing a feature, ask for acceptance criteria: "How would you know this is working? What should a user be able to do?"
|
|
111
|
+
- Proactively suggest UAT scenarios: "Here's how I'd test this — does that cover it?"
|
|
112
|
+
- Don't front-load all questions — weave user stories and acceptance criteria into the natural conversation.
|
|
106
113
|
- When the plan feels complete, invite the user to say "finalize plan".
|
|
107
114
|
- If the user is vague ("make it better", "you decide"), propose concrete options and ask them to pick.
|
|
108
115
|
- If you've gathered enough info after several turns with no new details, suggest: "This looks ready — say **finalize plan** when you're happy with it."
|
|
@@ -138,3 +145,22 @@ planner:
|
|
|
138
145
|
- If you want to stop, say **"abandon planning"**
|
|
139
146
|
|
|
140
147
|
Let's start — what is this project about, and what are the main feature areas?
|
|
148
|
+
|
|
149
|
+
review: |
|
|
150
|
+
## Plan Review for {{projectName}}
|
|
151
|
+
|
|
152
|
+
The plan passed all deterministic checks ({{issueCount}} issues, valid DAG, all have estimates and priorities).
|
|
153
|
+
|
|
154
|
+
### Current Plan
|
|
155
|
+
{{planSnapshot}}
|
|
156
|
+
|
|
157
|
+
### {{reviewModel}}'s Recommendations
|
|
158
|
+
{{crossModelFeedback}}
|
|
159
|
+
|
|
160
|
+
Your job:
|
|
161
|
+
1. Evaluate the recommendations above — which ones are worth applying?
|
|
162
|
+
2. If any are good, use your tools to update the relevant issues now
|
|
163
|
+
3. Summarize what you changed (if anything) and what you didn't change (and why)
|
|
164
|
+
4. End with: "If you're happy with this plan, say **approve plan** to start dispatching."
|
|
165
|
+
|
|
166
|
+
Post your review as a comment.
|
package/src/agent/agent.test.ts
CHANGED
|
@@ -204,6 +204,55 @@ describe("runAgent subprocess", () => {
|
|
|
204
204
|
});
|
|
205
205
|
});
|
|
206
206
|
|
|
207
|
+
describe("runAgent date/time injection", () => {
|
|
208
|
+
it("injects current date/time into the message sent to subprocess", async () => {
|
|
209
|
+
const api = createApi();
|
|
210
|
+
const runCmd = vi.fn().mockResolvedValue({
|
|
211
|
+
code: 0,
|
|
212
|
+
stdout: JSON.stringify({ result: { payloads: [{ text: "done" }] } }),
|
|
213
|
+
stderr: "",
|
|
214
|
+
});
|
|
215
|
+
(api.runtime.system as any).runCommandWithTimeout = runCmd;
|
|
216
|
+
|
|
217
|
+
await runAgent({
|
|
218
|
+
api,
|
|
219
|
+
agentId: "test",
|
|
220
|
+
sessionId: "s1",
|
|
221
|
+
message: "do something",
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
// The --message arg should contain the date context prefix
|
|
225
|
+
const args: string[] = runCmd.mock.calls[0][0];
|
|
226
|
+
const msgIdx = args.indexOf("--message");
|
|
227
|
+
const passedMessage = args[msgIdx + 1];
|
|
228
|
+
expect(passedMessage).toMatch(/^\[Current date\/time:.*\d{4}.*\]/);
|
|
229
|
+
expect(passedMessage).toContain("do something");
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
it("includes ISO timestamp in the injected context", async () => {
|
|
233
|
+
const api = createApi();
|
|
234
|
+
const runCmd = vi.fn().mockResolvedValue({
|
|
235
|
+
code: 0,
|
|
236
|
+
stdout: "ok",
|
|
237
|
+
stderr: "",
|
|
238
|
+
});
|
|
239
|
+
(api.runtime.system as any).runCommandWithTimeout = runCmd;
|
|
240
|
+
|
|
241
|
+
await runAgent({
|
|
242
|
+
api,
|
|
243
|
+
agentId: "test",
|
|
244
|
+
sessionId: "s1",
|
|
245
|
+
message: "test task",
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
const args: string[] = runCmd.mock.calls[0][0];
|
|
249
|
+
const msgIdx = args.indexOf("--message");
|
|
250
|
+
const passedMessage = args[msgIdx + 1];
|
|
251
|
+
// Should contain ISO format like 2026-02-19T05:45:00.000Z
|
|
252
|
+
expect(passedMessage).toMatch(/\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}/);
|
|
253
|
+
});
|
|
254
|
+
});
|
|
255
|
+
|
|
207
256
|
describe("runAgent retry wrapper", () => {
|
|
208
257
|
it("returns success on first attempt when no watchdog kill", async () => {
|
|
209
258
|
const api = createApi();
|
package/src/agent/agent.ts
CHANGED
|
@@ -95,6 +95,27 @@ export async function runAgent(params: {
|
|
|
95
95
|
return { success: false, output: "Watchdog retry exhausted" };
|
|
96
96
|
}
|
|
97
97
|
|
|
98
|
+
// ---------------------------------------------------------------------------
|
|
99
|
+
// Date/time injection — every LLM request gets the current timestamp so models
|
|
100
|
+
// don't hallucinate the year (Kimi K2.5 thinks it's 2025).
|
|
101
|
+
// ---------------------------------------------------------------------------
|
|
102
|
+
|
|
103
|
+
function buildDateContext(): string {
|
|
104
|
+
const now = new Date();
|
|
105
|
+
const iso = now.toISOString();
|
|
106
|
+
// Human-readable: "Tuesday, February 18, 2026, 11:42 PM CST"
|
|
107
|
+
const human = now.toLocaleString("en-US", {
|
|
108
|
+
weekday: "long",
|
|
109
|
+
year: "numeric",
|
|
110
|
+
month: "long",
|
|
111
|
+
day: "numeric",
|
|
112
|
+
hour: "numeric",
|
|
113
|
+
minute: "2-digit",
|
|
114
|
+
timeZoneName: "short",
|
|
115
|
+
});
|
|
116
|
+
return `[Current date/time: ${human} (${iso})]`;
|
|
117
|
+
}
|
|
118
|
+
|
|
98
119
|
/**
|
|
99
120
|
* Single attempt to run an agent (no retry logic).
|
|
100
121
|
*/
|
|
@@ -106,7 +127,11 @@ async function runAgentOnce(params: {
|
|
|
106
127
|
timeoutMs?: number;
|
|
107
128
|
streaming?: AgentStreamCallbacks;
|
|
108
129
|
}): Promise<AgentRunResult> {
|
|
109
|
-
const { api, agentId, sessionId,
|
|
130
|
+
const { api, agentId, sessionId, streaming } = params;
|
|
131
|
+
|
|
132
|
+
// Inject current timestamp into every LLM request
|
|
133
|
+
const message = `${buildDateContext()}\n\n${params.message}`;
|
|
134
|
+
|
|
110
135
|
const pluginConfig = (api as any).pluginConfig as Record<string, unknown> | undefined;
|
|
111
136
|
const wdConfig = resolveWatchdogConfig(agentId, pluginConfig);
|
|
112
137
|
const timeoutMs = params.timeoutMs ?? wdConfig.maxTotalMs;
|
package/src/infra/doctor.ts
CHANGED
|
@@ -321,13 +321,13 @@ export function checkCodingTools(): CheckResult[] {
|
|
|
321
321
|
const config = loadCodingConfig();
|
|
322
322
|
const hasConfig = !!config.codingTool || !!config.backends;
|
|
323
323
|
if (hasConfig) {
|
|
324
|
-
checks.push(pass(`coding-tools.json loaded (default: ${config.codingTool ?? "
|
|
324
|
+
checks.push(pass(`coding-tools.json loaded (default: ${config.codingTool ?? "codex"})`));
|
|
325
325
|
} else {
|
|
326
326
|
checks.push(warn("coding-tools.json not found or empty (using defaults)", undefined, { fix: "Create coding-tools.json in the plugin root — see README for format" }));
|
|
327
327
|
}
|
|
328
328
|
|
|
329
329
|
// Validate default backend
|
|
330
|
-
const defaultBackend = config.codingTool ?? "
|
|
330
|
+
const defaultBackend = config.codingTool ?? "codex";
|
|
331
331
|
if (VALID_BACKENDS.includes(defaultBackend)) {
|
|
332
332
|
// already reported in the line above
|
|
333
333
|
} else {
|