@ai-setting/roy-task-plugin 0.7.4 → 0.7.6
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/dist/claude-plugin/agents/roy.md +10 -3
- package/dist/claude-plugin/commands/task-milestone.md +1 -1
- package/dist/cli.js +164 -124
- package/dist/index.js +208 -170
- package/dist/index.js.map +13 -12
- package/package.json +2 -2
|
@@ -46,13 +46,20 @@ Report progress as you go, not only at the end:
|
|
|
46
46
|
If the scope changes, `task_update` the description/goals instead of silently
|
|
47
47
|
doing something else.
|
|
48
48
|
|
|
49
|
-
### 4. Record milestones — `task_operation_create`
|
|
49
|
+
### 4. Record milestones — `task_operation_create` — **as you work**
|
|
50
|
+
Call this tool the moment a stage is reached — **not after the fact**,
|
|
51
|
+
not in a batch at the end. Finished a chunk of code? A test just went
|
|
52
|
+
green? Hit a blocker? Made a decision? Record it **immediately**, then
|
|
53
|
+
keep working. The record is the running log of the work, written while
|
|
54
|
+
it happens.
|
|
55
|
+
|
|
50
56
|
This tool has **two orthogonal dimensions** and you must keep them distinct:
|
|
51
57
|
- **state / milestone dimension** — `milestone_type` + `milestone_title` +
|
|
52
58
|
`milestone_description`: *what* was reached (e.g. "GREEN: all tests pass").
|
|
53
59
|
- **process dimension** — `process_description`: *how* you got there —
|
|
54
|
-
|
|
55
|
-
Too short = rejected (no value);
|
|
60
|
+
the operations just performed and why, written in the moment,
|
|
61
|
+
**100–500 characters**. Too short = rejected (no value);
|
|
62
|
+
too long = rejected (that is a log, not a summary).
|
|
56
63
|
|
|
57
64
|
Rules: do **not** use the process field to restate the milestone, and do not
|
|
58
65
|
use milestones as a substitute for progress. Milestones mark *state
|
|
@@ -32,7 +32,7 @@ If you do have the MCP server, call `task_operation_create` with:
|
|
|
32
32
|
`solution`, `decision`, `review`, `completed`
|
|
33
33
|
- `milestone_title` — one line
|
|
34
34
|
- `milestone_description` — what was reached
|
|
35
|
-
- `process_description` — **how** you got there,
|
|
35
|
+
- `process_description` — **how** you got there, 100–500 characters
|
|
36
36
|
(this is enforced; too short or too long is rejected)
|
|
37
37
|
|
|
38
38
|
Keep the two dimensions distinct: the milestone says *what* was reached, the
|
package/dist/cli.js
CHANGED
|
@@ -444,126 +444,6 @@ class NdjsonAssembler {
|
|
|
444
444
|
}
|
|
445
445
|
}
|
|
446
446
|
|
|
447
|
-
// ../../../packages/cli/src/daemon/runners/codex-stream.ts
|
|
448
|
-
var CODEX_ITEM_TYPES = Object.freeze({
|
|
449
|
-
AGENT_MESSAGE: "agent_message",
|
|
450
|
-
COMMAND_EXECUTION: "command_execution",
|
|
451
|
-
MCP_TOOL_CALL: "mcp_tool_call",
|
|
452
|
-
ERROR: "error"
|
|
453
|
-
});
|
|
454
|
-
|
|
455
|
-
class CodexStreamDecoder {
|
|
456
|
-
lines = new LineSplitter;
|
|
457
|
-
state = { tools: [], errors: [], turnCompleted: false };
|
|
458
|
-
mcpSeen = new Set;
|
|
459
|
-
pushChunk(chunk) {
|
|
460
|
-
for (const line of this.lines.pushChunk(chunk))
|
|
461
|
-
this.consume(line);
|
|
462
|
-
}
|
|
463
|
-
finish() {
|
|
464
|
-
for (const line of this.lines.finish())
|
|
465
|
-
this.consume(line);
|
|
466
|
-
return this.toResult();
|
|
467
|
-
}
|
|
468
|
-
snapshot() {
|
|
469
|
-
return { ...this.state, tools: [...this.state.tools], errors: [...this.state.errors] };
|
|
470
|
-
}
|
|
471
|
-
consume(rawLine) {
|
|
472
|
-
const line = rawLine.trim();
|
|
473
|
-
if (!line.startsWith("{"))
|
|
474
|
-
return;
|
|
475
|
-
let event;
|
|
476
|
-
try {
|
|
477
|
-
event = JSON.parse(line);
|
|
478
|
-
} catch {
|
|
479
|
-
return;
|
|
480
|
-
}
|
|
481
|
-
if (typeof event !== "object" || event === null)
|
|
482
|
-
return;
|
|
483
|
-
const ev = event;
|
|
484
|
-
if (ev.type === "thread.started") {
|
|
485
|
-
const id = ev.thread_id;
|
|
486
|
-
if (typeof id === "string" && id)
|
|
487
|
-
this.state.sessionId = id;
|
|
488
|
-
return;
|
|
489
|
-
}
|
|
490
|
-
if (ev.type === "turn.completed") {
|
|
491
|
-
this.state.turnCompleted = true;
|
|
492
|
-
return;
|
|
493
|
-
}
|
|
494
|
-
if (ev.type === "item.started" || ev.type === "item.completed") {
|
|
495
|
-
this.consumeItem(ev.item, ev.type === "item.completed");
|
|
496
|
-
}
|
|
497
|
-
}
|
|
498
|
-
consumeItem(raw, completed) {
|
|
499
|
-
if (typeof raw !== "object" || raw === null)
|
|
500
|
-
return;
|
|
501
|
-
const item = raw;
|
|
502
|
-
if (item.type === CODEX_ITEM_TYPES.AGENT_MESSAGE) {
|
|
503
|
-
if (typeof item.text === "string" && item.text.length > 0) {
|
|
504
|
-
this.state.text = item.text;
|
|
505
|
-
}
|
|
506
|
-
return;
|
|
507
|
-
}
|
|
508
|
-
if (item.type === CODEX_ITEM_TYPES.ERROR) {
|
|
509
|
-
if (typeof item.message === "string" && item.message.length > 0) {
|
|
510
|
-
this.state.errors.push(item.message);
|
|
511
|
-
}
|
|
512
|
-
return;
|
|
513
|
-
}
|
|
514
|
-
if (item.type === CODEX_ITEM_TYPES.COMMAND_EXECUTION) {
|
|
515
|
-
const id = item.id;
|
|
516
|
-
if (typeof id === "string" && id && this.state.tools.includes(id)) {} else {
|
|
517
|
-
this.state.tools.push(typeof id === "string" ? id : `cmd-${this.state.tools.length}`);
|
|
518
|
-
}
|
|
519
|
-
if (completed && this.state.createdTask === undefined) {
|
|
520
|
-
this.state.createdTask = extractCreatedTask(item.aggregated_output);
|
|
521
|
-
}
|
|
522
|
-
return;
|
|
523
|
-
}
|
|
524
|
-
if (item.type === CODEX_ITEM_TYPES.MCP_TOOL_CALL) {
|
|
525
|
-
const id = item.id;
|
|
526
|
-
if (typeof id === "string" && id) {
|
|
527
|
-
if (this.mcpSeen.has(id))
|
|
528
|
-
return;
|
|
529
|
-
this.mcpSeen.add(id);
|
|
530
|
-
}
|
|
531
|
-
const name = typeof item.tool === "string" && item.tool ? item.tool : undefined;
|
|
532
|
-
this.state.tools.push(name ?? (typeof id === "string" && id ? id : `mcp-${this.state.tools.length}`));
|
|
533
|
-
return;
|
|
534
|
-
}
|
|
535
|
-
}
|
|
536
|
-
toResult() {
|
|
537
|
-
const result = {
|
|
538
|
-
text: this.state.text ?? ""
|
|
539
|
-
};
|
|
540
|
-
if (this.state.sessionId)
|
|
541
|
-
result.sessionId = this.state.sessionId;
|
|
542
|
-
if (this.state.tools.length > 0) {
|
|
543
|
-
result.toolCalls = this.state.tools.length;
|
|
544
|
-
result.tools = [...this.state.tools];
|
|
545
|
-
}
|
|
546
|
-
if (this.state.createdTask)
|
|
547
|
-
result.createdTask = this.state.createdTask;
|
|
548
|
-
return result;
|
|
549
|
-
}
|
|
550
|
-
}
|
|
551
|
-
|
|
552
|
-
// ../../../packages/cli/src/daemon/runners/codex-sessions.ts
|
|
553
|
-
function createCodexSessionRegistry() {
|
|
554
|
-
const sessions = new Map;
|
|
555
|
-
return {
|
|
556
|
-
get: (key) => sessions.get(key),
|
|
557
|
-
set: (key, id) => {
|
|
558
|
-
sessions.set(key, id);
|
|
559
|
-
},
|
|
560
|
-
delete: (key) => {
|
|
561
|
-
sessions.delete(key);
|
|
562
|
-
},
|
|
563
|
-
size: () => sessions.size
|
|
564
|
-
};
|
|
565
|
-
}
|
|
566
|
-
|
|
567
447
|
// ../../../packages/cli/src/daemon/runners/claude-agents.ts
|
|
568
448
|
var CLAUDE_ROY_AGENT_NAME = "roy";
|
|
569
449
|
var ROY_AGENT_DESCRIPTION = "Roy — task-as-first-class-citizen agent. Uses the roy-agent task lifecycle " + "(task_tree → task_create → task_update → task_operation_create → task_complete) " + "so every piece of work is tracked in the local task store.";
|
|
@@ -631,13 +511,20 @@ Report progress as you go, not only at the end:
|
|
|
631
511
|
If the scope changes, \`task_update\` the description/goals instead of silently
|
|
632
512
|
doing something else.
|
|
633
513
|
|
|
634
|
-
### 4. Record milestones — \`task_operation_create\`
|
|
514
|
+
### 4. Record milestones — \`task_operation_create\` — **as you work**
|
|
515
|
+
Call this tool the moment a stage is reached — **not after the fact**,
|
|
516
|
+
not in a batch at the end. Finished a chunk of code? A test just went
|
|
517
|
+
green? Hit a blocker? Made a decision? Record it **immediately**, then
|
|
518
|
+
keep working. The record is the running log of the work, written while
|
|
519
|
+
it happens.
|
|
520
|
+
|
|
635
521
|
This tool has **two orthogonal dimensions** and you must keep them distinct:
|
|
636
522
|
- **state / milestone dimension** — \`milestone_type\` + \`milestone_title\` +
|
|
637
523
|
\`milestone_description\`: *what* was reached (e.g. "GREEN: all tests pass").
|
|
638
524
|
- **process dimension** — \`process_description\`: *how* you got there —
|
|
639
|
-
|
|
640
|
-
Too short = rejected (no value);
|
|
525
|
+
the operations just performed and why, written in the moment,
|
|
526
|
+
**100–500 characters**. Too short = rejected (no value);
|
|
527
|
+
too long = rejected (that is a log, not a summary).
|
|
641
528
|
|
|
642
529
|
Rules: do **not** use the process field to restate the milestone, and do not
|
|
643
530
|
use milestones as a substitute for progress. Milestones mark *state
|
|
@@ -714,6 +601,159 @@ function buildClaudeAgentsJson(opts = {}) {
|
|
|
714
601
|
return JSON.stringify({ [opts.name ?? CLAUDE_ROY_AGENT_NAME]: def });
|
|
715
602
|
}
|
|
716
603
|
|
|
604
|
+
// ../../../packages/cli/src/daemon/runners/codex-stream.ts
|
|
605
|
+
var CODEX_ITEM_TYPES = Object.freeze({
|
|
606
|
+
AGENT_MESSAGE: "agent_message",
|
|
607
|
+
COMMAND_EXECUTION: "command_execution",
|
|
608
|
+
MCP_TOOL_CALL: "mcp_tool_call",
|
|
609
|
+
ERROR: "error"
|
|
610
|
+
});
|
|
611
|
+
function isCreationTool(tool) {
|
|
612
|
+
if (isTaskCreateTool(tool))
|
|
613
|
+
return true;
|
|
614
|
+
const bare = tool.includes("/") ? tool.slice(tool.lastIndexOf("/") + 1) : tool;
|
|
615
|
+
return isTaskCreateTool(bare);
|
|
616
|
+
}
|
|
617
|
+
function isSuccessfulMcpStatus(status) {
|
|
618
|
+
return status === undefined || status === "completed";
|
|
619
|
+
}
|
|
620
|
+
function isRoyTaskServer(server) {
|
|
621
|
+
return server === undefined || server === ROY_TASK_MCP_SERVER;
|
|
622
|
+
}
|
|
623
|
+
function mcpResultText(result) {
|
|
624
|
+
if (typeof result !== "object" || result === null)
|
|
625
|
+
return;
|
|
626
|
+
const content = result.content;
|
|
627
|
+
if (!Array.isArray(content))
|
|
628
|
+
return;
|
|
629
|
+
for (const block of content) {
|
|
630
|
+
if (typeof block === "object" && block !== null && block.type === "text" && typeof block.text === "string") {
|
|
631
|
+
return block.text;
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
return;
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
class CodexStreamDecoder {
|
|
638
|
+
lines = new LineSplitter;
|
|
639
|
+
state = { tools: [], errors: [], turnCompleted: false };
|
|
640
|
+
mcpSeen = new Set;
|
|
641
|
+
pushChunk(chunk) {
|
|
642
|
+
for (const line of this.lines.pushChunk(chunk))
|
|
643
|
+
this.consume(line);
|
|
644
|
+
}
|
|
645
|
+
finish() {
|
|
646
|
+
for (const line of this.lines.finish())
|
|
647
|
+
this.consume(line);
|
|
648
|
+
return this.toResult();
|
|
649
|
+
}
|
|
650
|
+
snapshot() {
|
|
651
|
+
return { ...this.state, tools: [...this.state.tools], errors: [...this.state.errors] };
|
|
652
|
+
}
|
|
653
|
+
consume(rawLine) {
|
|
654
|
+
const line = rawLine.trim();
|
|
655
|
+
if (!line.startsWith("{"))
|
|
656
|
+
return;
|
|
657
|
+
let event;
|
|
658
|
+
try {
|
|
659
|
+
event = JSON.parse(line);
|
|
660
|
+
} catch {
|
|
661
|
+
return;
|
|
662
|
+
}
|
|
663
|
+
if (typeof event !== "object" || event === null)
|
|
664
|
+
return;
|
|
665
|
+
const ev = event;
|
|
666
|
+
if (ev.type === "thread.started") {
|
|
667
|
+
const id = ev.thread_id;
|
|
668
|
+
if (typeof id === "string" && id)
|
|
669
|
+
this.state.sessionId = id;
|
|
670
|
+
return;
|
|
671
|
+
}
|
|
672
|
+
if (ev.type === "turn.completed") {
|
|
673
|
+
this.state.turnCompleted = true;
|
|
674
|
+
return;
|
|
675
|
+
}
|
|
676
|
+
if (ev.type === "item.started" || ev.type === "item.completed") {
|
|
677
|
+
this.consumeItem(ev.item, ev.type === "item.completed");
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
consumeItem(raw, completed) {
|
|
681
|
+
if (typeof raw !== "object" || raw === null)
|
|
682
|
+
return;
|
|
683
|
+
const item = raw;
|
|
684
|
+
if (item.type === CODEX_ITEM_TYPES.AGENT_MESSAGE) {
|
|
685
|
+
if (typeof item.text === "string" && item.text.length > 0) {
|
|
686
|
+
this.state.text = item.text;
|
|
687
|
+
}
|
|
688
|
+
return;
|
|
689
|
+
}
|
|
690
|
+
if (item.type === CODEX_ITEM_TYPES.ERROR) {
|
|
691
|
+
if (typeof item.message === "string" && item.message.length > 0) {
|
|
692
|
+
this.state.errors.push(item.message);
|
|
693
|
+
}
|
|
694
|
+
return;
|
|
695
|
+
}
|
|
696
|
+
if (item.type === CODEX_ITEM_TYPES.COMMAND_EXECUTION) {
|
|
697
|
+
const id = item.id;
|
|
698
|
+
if (typeof id === "string" && id && this.state.tools.includes(id)) {} else {
|
|
699
|
+
this.state.tools.push(typeof id === "string" ? id : `cmd-${this.state.tools.length}`);
|
|
700
|
+
}
|
|
701
|
+
if (completed && this.state.createdTask === undefined) {
|
|
702
|
+
this.state.createdTask = extractCreatedTask(item.aggregated_output);
|
|
703
|
+
}
|
|
704
|
+
return;
|
|
705
|
+
}
|
|
706
|
+
if (item.type === CODEX_ITEM_TYPES.MCP_TOOL_CALL) {
|
|
707
|
+
const id = item.id;
|
|
708
|
+
const seen = typeof id === "string" && id && this.mcpSeen.has(id);
|
|
709
|
+
if (typeof id === "string" && id)
|
|
710
|
+
this.mcpSeen.add(id);
|
|
711
|
+
if (!seen) {
|
|
712
|
+
const name = typeof item.tool === "string" && item.tool ? item.tool : undefined;
|
|
713
|
+
this.state.tools.push(name ?? (typeof id === "string" && id ? id : `mcp-${this.state.tools.length}`));
|
|
714
|
+
}
|
|
715
|
+
if (completed && this.state.createdTask === undefined && isSuccessfulMcpStatus(item.status) && typeof item.tool === "string" && isCreationTool(item.tool) && isRoyTaskServer(item.server)) {
|
|
716
|
+
const text = mcpResultText(item.result);
|
|
717
|
+
if (text !== undefined) {
|
|
718
|
+
const args = item.arguments;
|
|
719
|
+
const hint = typeof args?.title === "string" ? args.title : undefined;
|
|
720
|
+
this.state.createdTask = extractCreatedTask(text, hint);
|
|
721
|
+
}
|
|
722
|
+
}
|
|
723
|
+
return;
|
|
724
|
+
}
|
|
725
|
+
}
|
|
726
|
+
toResult() {
|
|
727
|
+
const result = {
|
|
728
|
+
text: this.state.text ?? ""
|
|
729
|
+
};
|
|
730
|
+
if (this.state.sessionId)
|
|
731
|
+
result.sessionId = this.state.sessionId;
|
|
732
|
+
if (this.state.tools.length > 0) {
|
|
733
|
+
result.toolCalls = this.state.tools.length;
|
|
734
|
+
result.tools = [...this.state.tools];
|
|
735
|
+
}
|
|
736
|
+
if (this.state.createdTask)
|
|
737
|
+
result.createdTask = this.state.createdTask;
|
|
738
|
+
return result;
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
|
|
742
|
+
// ../../../packages/cli/src/daemon/runners/codex-sessions.ts
|
|
743
|
+
function createCodexSessionRegistry() {
|
|
744
|
+
const sessions = new Map;
|
|
745
|
+
return {
|
|
746
|
+
get: (key) => sessions.get(key),
|
|
747
|
+
set: (key, id) => {
|
|
748
|
+
sessions.set(key, id);
|
|
749
|
+
},
|
|
750
|
+
delete: (key) => {
|
|
751
|
+
sessions.delete(key);
|
|
752
|
+
},
|
|
753
|
+
size: () => sessions.size
|
|
754
|
+
};
|
|
755
|
+
}
|
|
756
|
+
|
|
717
757
|
// ../../../packages/cli/src/daemon/runners/claude.ts
|
|
718
758
|
import { createHash } from "node:crypto";
|
|
719
759
|
import { appendFileSync, existsSync } from "node:fs";
|
|
@@ -3230,7 +3270,7 @@ function formatStatus(report) {
|
|
|
3230
3270
|
}
|
|
3231
3271
|
|
|
3232
3272
|
// ../../../packages/cli/src/main.ts
|
|
3233
|
-
var VERSION = "0.7.
|
|
3273
|
+
var VERSION = "0.7.6";
|
|
3234
3274
|
async function main(argv) {
|
|
3235
3275
|
const parsed = parseArgs(argv);
|
|
3236
3276
|
switch (parsed.command) {
|