create-claude-workspace 2.3.19 → 2.3.20
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 +9 -0
- package/dist/scheduler/agents/worker-pool.mjs +2 -0
- package/dist/scheduler/git/issue-creator.mjs +54 -0
- package/dist/scheduler/index.mjs +4 -0
- package/dist/scheduler/loop.mjs +33 -11
- package/dist/scheduler/tasks/inbox.mjs +1 -0
- package/dist/scheduler/tasks/issue-source.mjs +1 -0
- package/dist/scheduler/tasks/parser.mjs +1 -0
- package/dist/scheduler/tools/report-issue.mjs +94 -0
- package/dist/scheduler/tools/scheduler-tools.mjs +17 -0
- package/dist/template/.claude/agents/angular-engineer.md +5 -1
- package/dist/template/.claude/agents/backend-ts-architect.md +4 -0
- package/dist/template/.claude/agents/orchestrator.md +9 -61
- package/dist/template/.claude/agents/react-engineer.md +4 -0
- package/dist/template/.claude/agents/senior-code-reviewer.md +4 -0
- package/dist/template/.claude/agents/svelte-engineer.md +4 -0
- package/dist/template/.claude/agents/test-engineer.md +4 -0
- package/dist/template/.claude/agents/ui-engineer.md +5 -1
- package/dist/template/.claude/agents/vue-engineer.md +4 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -65,6 +65,15 @@ Write to `.claude/scheduler/inbox.json` while the scheduler is running:
|
|
|
65
65
|
|
|
66
66
|
The scheduler reads the inbox every iteration and processes messages immediately.
|
|
67
67
|
|
|
68
|
+
#### Proactive problem resolution
|
|
69
|
+
|
|
70
|
+
Agents never dismiss problems as "pre-existing." The scheduler injects a `report_issue` MCP tool into every agent. When an agent discovers an out-of-scope problem:
|
|
71
|
+
|
|
72
|
+
- **Non-blocker**: calls `report_issue(severity: 'non-blocker')` and continues working
|
|
73
|
+
- **Blocker**: calls `report_issue(severity: 'blocker')`, wraps up, and returns
|
|
74
|
+
|
|
75
|
+
The scheduler records the issue locally (`discovered.ndjson`) and creates a platform issue (GitHub/GitLab) if available. Discovered issues enter the task queue on the next iteration.
|
|
76
|
+
|
|
68
77
|
### npx Options
|
|
69
78
|
|
|
70
79
|
```bash
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
// ─── Platform issue creation (GitHub/GitLab) ───
|
|
2
|
+
// Creates issues on the remote platform for discovered problems.
|
|
3
|
+
// Non-fatal — returns null on failure (local record always exists).
|
|
4
|
+
import { execFileSync } from 'node:child_process';
|
|
5
|
+
const CLI_TIMEOUT = 30_000;
|
|
6
|
+
function run(cmd, args, cwd) {
|
|
7
|
+
return execFileSync(cmd, args, { cwd, timeout: CLI_TIMEOUT, stdio: 'pipe', encoding: 'utf-8' }).trim();
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Create an issue on the remote platform.
|
|
11
|
+
* Returns the created issue info, or null if creation failed (non-fatal).
|
|
12
|
+
*/
|
|
13
|
+
export function createPlatformIssue(cwd, platform, opts) {
|
|
14
|
+
try {
|
|
15
|
+
if (platform === 'github')
|
|
16
|
+
return createGitHubIssue(cwd, opts);
|
|
17
|
+
return createGitLabIssue(cwd, opts);
|
|
18
|
+
}
|
|
19
|
+
catch {
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
function createGitHubIssue(cwd, opts) {
|
|
24
|
+
const args = ['issue', 'create', '--title', opts.title, '--body', opts.body];
|
|
25
|
+
for (const label of opts.labels) {
|
|
26
|
+
args.push('--label', label);
|
|
27
|
+
}
|
|
28
|
+
if (opts.milestone)
|
|
29
|
+
args.push('--milestone', opts.milestone);
|
|
30
|
+
const output = run('gh', args, cwd);
|
|
31
|
+
// gh issue create prints the URL: https://github.com/owner/repo/issues/42
|
|
32
|
+
const urlMatch = output.match(/https:\/\/github\.com\/[^/]+\/[^/]+\/issues\/(\d+)/);
|
|
33
|
+
if (!urlMatch)
|
|
34
|
+
return null;
|
|
35
|
+
return { number: parseInt(urlMatch[1], 10), url: urlMatch[0] };
|
|
36
|
+
}
|
|
37
|
+
function createGitLabIssue(cwd, opts) {
|
|
38
|
+
const args = ['issue', 'create', '--title', opts.title, '--description', opts.body];
|
|
39
|
+
if (opts.labels.length > 0) {
|
|
40
|
+
args.push('--label', opts.labels.join(','));
|
|
41
|
+
}
|
|
42
|
+
if (opts.milestone)
|
|
43
|
+
args.push('--milestone', opts.milestone);
|
|
44
|
+
const output = run('glab', args, cwd);
|
|
45
|
+
// glab outputs: Creating issue... or the URL
|
|
46
|
+
const urlMatch = output.match(/https:\/\/gitlab\.[^/]+\/[^/]+\/[^/]+\/-\/issues\/(\d+)/);
|
|
47
|
+
if (urlMatch)
|
|
48
|
+
return { number: parseInt(urlMatch[1], 10), url: urlMatch[0] };
|
|
49
|
+
// Fallback: look for issue number in output like "#42" or "issue #42"
|
|
50
|
+
const numMatch = output.match(/#(\d+)/);
|
|
51
|
+
if (numMatch)
|
|
52
|
+
return { number: parseInt(numMatch[1], 10), url: output };
|
|
53
|
+
return null;
|
|
54
|
+
}
|
package/dist/scheduler/index.mjs
CHANGED
|
@@ -14,6 +14,7 @@ import { runIteration } from './loop.mjs';
|
|
|
14
14
|
import { checkAuth } from './agents/health-checker.mjs';
|
|
15
15
|
import { pollForNewWork } from './util/idle-poll.mjs';
|
|
16
16
|
import { TUI } from './ui/tui.mjs';
|
|
17
|
+
import { DiscoveredIssueStore } from './tools/report-issue.mjs';
|
|
17
18
|
// ─── Args ───
|
|
18
19
|
export function parseSchedulerArgs(argv) {
|
|
19
20
|
const opts = { ...SCHEDULER_DEFAULTS };
|
|
@@ -281,6 +282,8 @@ export async function runScheduler(opts) {
|
|
|
281
282
|
const onMessage = (msg) => tui.handleMessage(msg);
|
|
282
283
|
const onSpawnStart = (name) => tui.pushAgent(name);
|
|
283
284
|
const onSpawnEnd = () => tui.popAgent();
|
|
285
|
+
// Discovered issue store (shared across all agent spawns)
|
|
286
|
+
const discoveredIssueStore = new DiscoveredIssueStore();
|
|
284
287
|
// Orchestrator client
|
|
285
288
|
const orchestrator = new OrchestratorClient({
|
|
286
289
|
pool,
|
|
@@ -317,6 +320,7 @@ export async function runScheduler(opts) {
|
|
|
317
320
|
state,
|
|
318
321
|
opts,
|
|
319
322
|
logger,
|
|
323
|
+
discoveredIssueStore,
|
|
320
324
|
onMessage,
|
|
321
325
|
onSpawnStart,
|
|
322
326
|
onSpawnEnd,
|
package/dist/scheduler/loop.mjs
CHANGED
|
@@ -15,6 +15,8 @@ import { detectCIPlatform, fetchFailureLogs } from './git/ci-watcher.mjs';
|
|
|
15
15
|
import { createRelease } from './git/release.mjs';
|
|
16
16
|
import { processInbox, addTaskMessageToTask } from './tasks/inbox.mjs';
|
|
17
17
|
import { buildPlanPrompt, buildImplementPrompt, buildQAPrompt, buildReviewPrompt, buildReworkPrompt, buildCIFixPrompt, buildPRCommentPrompt, } from './agents/prompt-builder.mjs';
|
|
18
|
+
import { discoveredIssueToTask } from './tools/report-issue.mjs';
|
|
19
|
+
import { createSchedulerToolServer } from './tools/scheduler-tools.mjs';
|
|
18
20
|
const MAX_REVIEW_CYCLES = 5;
|
|
19
21
|
const MAX_BUILD_FIXES = 3;
|
|
20
22
|
const MAX_CI_FIXES = 3;
|
|
@@ -28,15 +30,16 @@ export async function runIteration(deps) {
|
|
|
28
30
|
// Rotate log if needed
|
|
29
31
|
rotateLog(projectDir);
|
|
30
32
|
// Process inbox — immediate, non-blocking
|
|
33
|
+
const inboxTasks = [];
|
|
31
34
|
const inboxMessages = processInbox(projectDir);
|
|
32
35
|
for (const msg of inboxMessages) {
|
|
33
36
|
if (msg.type === 'add-task') {
|
|
34
37
|
const addMsg = msg;
|
|
35
38
|
const nextId = `inbox-${Date.now()}`;
|
|
36
39
|
const newTask = addTaskMessageToTask(addMsg, state.currentPhase, nextId);
|
|
40
|
+
inboxTasks.push(newTask);
|
|
37
41
|
logger.info(`[inbox] New task: ${newTask.title}`);
|
|
38
42
|
appendEvent(projectDir, createEvent('task_started', { taskId: nextId, detail: `inbox: ${newTask.title}` }));
|
|
39
|
-
// Task will be picked up when we load tasks below
|
|
40
43
|
}
|
|
41
44
|
else if (msg.type === 'message') {
|
|
42
45
|
const freeMsg = msg;
|
|
@@ -129,6 +132,18 @@ export async function runIteration(deps) {
|
|
|
129
132
|
}
|
|
130
133
|
}
|
|
131
134
|
}
|
|
135
|
+
// Merge inbox tasks into loaded tasks
|
|
136
|
+
if (inboxTasks.length > 0) {
|
|
137
|
+
tasks.push(...inboxTasks);
|
|
138
|
+
}
|
|
139
|
+
// Drain discovered issues from agent report_issue tool calls (previous iteration)
|
|
140
|
+
const discovered = deps.discoveredIssueStore.drain();
|
|
141
|
+
for (const issue of discovered) {
|
|
142
|
+
const task = discoveredIssueToTask(issue, state.currentPhase);
|
|
143
|
+
tasks.push(task);
|
|
144
|
+
logger.info(`[discovered] ${issue.severity}: ${issue.title} (reported by ${issue.reportedBy})`);
|
|
145
|
+
appendEvent(projectDir, createEvent('issue_discovered', { taskId: task.id, detail: `${issue.severity}: ${issue.title}` }));
|
|
146
|
+
}
|
|
132
147
|
// Reconcile task status with scheduler state (handles restart without --resume)
|
|
133
148
|
for (const task of tasks) {
|
|
134
149
|
if (state.completedTasks.includes(task.id) && task.status !== 'done') {
|
|
@@ -261,6 +276,7 @@ export async function runIteration(deps) {
|
|
|
261
276
|
issueMarker: taskId,
|
|
262
277
|
kitUpgrade: false,
|
|
263
278
|
lineNumber: 0,
|
|
279
|
+
source: 'todo',
|
|
264
280
|
status: 'in-progress',
|
|
265
281
|
changelog: 'changed',
|
|
266
282
|
};
|
|
@@ -359,6 +375,10 @@ export async function runIteration(deps) {
|
|
|
359
375
|
async function runTaskPipeline(task, workerId, agents, deps) {
|
|
360
376
|
const { pool, orchestrator, state, opts, logger, onMessage, onSpawnStart, onSpawnEnd } = deps;
|
|
361
377
|
const projectDir = opts.projectDir;
|
|
378
|
+
// Create per-pipeline MCP server with scheduler tools (report_issue, etc.)
|
|
379
|
+
const ciPlatform = detectCIPlatform(projectDir);
|
|
380
|
+
const mcpServer = createSchedulerToolServer(deps.discoveredIssueStore, task.id, projectDir, ciPlatform === 'none' ? 'none' : ciPlatform);
|
|
381
|
+
const mcpServers = { 'scheduler-tools': mcpServer };
|
|
362
382
|
// Check for existing pipeline (recovered from previous run)
|
|
363
383
|
const existing = state.pipelines[task.id];
|
|
364
384
|
const resumeStep = existing?.step;
|
|
@@ -414,7 +434,7 @@ async function runTaskPipeline(task, workerId, agents, deps) {
|
|
|
414
434
|
cwd: worktreePath,
|
|
415
435
|
prompt: buildPlanPrompt({ task, worktreePath, projectDir }),
|
|
416
436
|
model: getAgentModel(pipeline.assignedAgent, agents, task),
|
|
417
|
-
}, state, task.id, logger, onMessage, onSpawnStart, onSpawnEnd);
|
|
437
|
+
}, state, task.id, logger, onMessage, onSpawnStart, onSpawnEnd, mcpServers);
|
|
418
438
|
if (!planResult.success) {
|
|
419
439
|
logger.error(`[${task.id}] Planning failed: ${planResult.error}`);
|
|
420
440
|
return false;
|
|
@@ -442,7 +462,7 @@ async function runTaskPipeline(task, workerId, agents, deps) {
|
|
|
442
462
|
apiContract: pipeline.apiContract ?? undefined,
|
|
443
463
|
}),
|
|
444
464
|
model: getAgentModel(implAgent, agents, task),
|
|
445
|
-
}, state, task.id, logger, onMessage, onSpawnStart, onSpawnEnd);
|
|
465
|
+
}, state, task.id, logger, onMessage, onSpawnStart, onSpawnEnd, mcpServers);
|
|
446
466
|
if (!implResult.success) {
|
|
447
467
|
logger.error(`[${task.id}] Implementation failed: ${implResult.error}`);
|
|
448
468
|
return false;
|
|
@@ -461,7 +481,7 @@ async function runTaskPipeline(task, workerId, agents, deps) {
|
|
|
461
481
|
reviewFindings: 'This task was interrupted. The branch has existing work but may have build/lint failures, merge conflicts, or failing CI. Please:\n1. Run git fetch origin main && git rebase origin/main (resolve any conflicts)\n2. Run the build and lint scripts from package.json\n3. Fix any errors found\n4. Ensure all tests pass',
|
|
462
482
|
}),
|
|
463
483
|
model: getAgentModel(pipeline.assignedAgent, agents, task),
|
|
464
|
-
}, state, task.id, logger, onMessage, onSpawnStart, onSpawnEnd);
|
|
484
|
+
}, state, task.id, logger, onMessage, onSpawnStart, onSpawnEnd, mcpServers);
|
|
465
485
|
}
|
|
466
486
|
// STEP 3: QA (E2E tests, integration tests, acceptance criteria verification)
|
|
467
487
|
// Only for tasks that need it — skip for pure refactoring, config changes, etc.
|
|
@@ -480,7 +500,7 @@ async function runTaskPipeline(task, workerId, agents, deps) {
|
|
|
480
500
|
testingSection: pipeline.testingSection ?? undefined,
|
|
481
501
|
}),
|
|
482
502
|
model: getAgentModel(qaRouting.agent, agents, task),
|
|
483
|
-
}, state, task.id, logger, onMessage, onSpawnStart, onSpawnEnd);
|
|
503
|
+
}, state, task.id, logger, onMessage, onSpawnStart, onSpawnEnd, mcpServers);
|
|
484
504
|
if (!qaResult.success) {
|
|
485
505
|
pipeline.buildFixes++;
|
|
486
506
|
if (pipeline.buildFixes >= MAX_BUILD_FIXES) {
|
|
@@ -513,7 +533,7 @@ async function runTaskPipeline(task, workerId, agents, deps) {
|
|
|
513
533
|
testingSection: pipeline.testingSection ?? undefined,
|
|
514
534
|
}),
|
|
515
535
|
model: getAgentModel(reviewRouting.agent, agents, task),
|
|
516
|
-
}, state, task.id, logger, onMessage, onSpawnStart, onSpawnEnd);
|
|
536
|
+
}, state, task.id, logger, onMessage, onSpawnStart, onSpawnEnd, mcpServers);
|
|
517
537
|
if (reviewResult.output.includes('**PASS**') || reviewResult.output.includes('PASS')) {
|
|
518
538
|
reviewPassed = true;
|
|
519
539
|
}
|
|
@@ -535,7 +555,7 @@ async function runTaskPipeline(task, workerId, agents, deps) {
|
|
|
535
555
|
reviewFindings: pipeline.reviewFindings,
|
|
536
556
|
}),
|
|
537
557
|
model: getAgentModel(pipeline.assignedAgent, agents, task),
|
|
538
|
-
}, state, task.id, logger, onMessage, onSpawnStart, onSpawnEnd);
|
|
558
|
+
}, state, task.id, logger, onMessage, onSpawnStart, onSpawnEnd, mcpServers);
|
|
539
559
|
pipeline.step = 're-review';
|
|
540
560
|
}
|
|
541
561
|
}
|
|
@@ -676,7 +696,7 @@ async function runTaskPipeline(task, workerId, agents, deps) {
|
|
|
676
696
|
}
|
|
677
697
|
}
|
|
678
698
|
// ─── Helpers ───
|
|
679
|
-
async function spawnAgent(pool, slotId, opts, state, taskId, logger, onMessage, onSpawnStart, onSpawnEnd) {
|
|
699
|
+
async function spawnAgent(pool, slotId, opts, state, taskId, logger, onMessage, onSpawnStart, onSpawnEnd, mcpServers) {
|
|
680
700
|
const agentName = opts.agent ?? 'claude';
|
|
681
701
|
onSpawnStart?.(agentName);
|
|
682
702
|
// Check for existing session (resume on crash)
|
|
@@ -685,6 +705,7 @@ async function spawnAgent(pool, slotId, opts, state, taskId, logger, onMessage,
|
|
|
685
705
|
...opts,
|
|
686
706
|
resume: existingSession ?? undefined,
|
|
687
707
|
onMessage,
|
|
708
|
+
mcpServers,
|
|
688
709
|
});
|
|
689
710
|
onSpawnEnd?.();
|
|
690
711
|
// Record session for crash recovery
|
|
@@ -1052,6 +1073,7 @@ function loadTasksJson(path) {
|
|
|
1052
1073
|
issueMarker: t.issueMarker ?? null,
|
|
1053
1074
|
kitUpgrade: t.kitUpgrade ?? false,
|
|
1054
1075
|
lineNumber: t.lineNumber ?? 0,
|
|
1076
|
+
source: t.source ?? 'todo',
|
|
1055
1077
|
changelog: t.changelog ?? 'added',
|
|
1056
1078
|
})));
|
|
1057
1079
|
}
|
|
@@ -1113,7 +1135,7 @@ async function checkPRWatch(taskId, pipeline, projectDir, agents, deps) {
|
|
|
1113
1135
|
const task = {
|
|
1114
1136
|
id: taskId, title: `Resolve merge conflict for ${taskId}`, phase: state.currentPhase,
|
|
1115
1137
|
type: 'fullstack', complexity: 'S', dependsOn: [], issueMarker: taskId,
|
|
1116
|
-
kitUpgrade: false, lineNumber: 0, status: 'in-progress', changelog: 'fixed',
|
|
1138
|
+
kitUpgrade: false, lineNumber: 0, source: 'todo', status: 'in-progress', changelog: 'fixed',
|
|
1117
1139
|
};
|
|
1118
1140
|
// Fetch the main branch into the worktree so agent can see what changed
|
|
1119
1141
|
try {
|
|
@@ -1240,7 +1262,7 @@ async function checkPRWatch(taskId, pipeline, projectDir, agents, deps) {
|
|
|
1240
1262
|
const task = {
|
|
1241
1263
|
id: taskId, title: `Fix CI for ${taskId}`, phase: state.currentPhase,
|
|
1242
1264
|
type: 'fullstack', complexity: 'S', dependsOn: [], issueMarker: taskId,
|
|
1243
|
-
kitUpgrade: false, lineNumber: 0, status: 'in-progress', changelog: 'fixed',
|
|
1265
|
+
kitUpgrade: false, lineNumber: 0, source: 'todo', status: 'in-progress', changelog: 'fixed',
|
|
1244
1266
|
};
|
|
1245
1267
|
const fixResult = await spawnAgent(pool, slot.id, {
|
|
1246
1268
|
agent: pipeline.assignedAgent ?? undefined,
|
|
@@ -1272,7 +1294,7 @@ async function checkPRWatch(taskId, pipeline, projectDir, agents, deps) {
|
|
|
1272
1294
|
const task = {
|
|
1273
1295
|
id: taskId, title: `Address PR comments for ${taskId}`, phase: state.currentPhase,
|
|
1274
1296
|
type: 'fullstack', complexity: 'S', dependsOn: [], issueMarker: taskId,
|
|
1275
|
-
kitUpgrade: false, lineNumber: 0, status: 'in-progress', changelog: 'fixed',
|
|
1297
|
+
kitUpgrade: false, lineNumber: 0, source: 'todo', status: 'in-progress', changelog: 'fixed',
|
|
1276
1298
|
};
|
|
1277
1299
|
const result = await spawnAgent(pool, slot.id, {
|
|
1278
1300
|
agent: pipeline.assignedAgent ?? undefined,
|
|
@@ -56,6 +56,7 @@ export function issueToTask(issue, fallbackPhase) {
|
|
|
56
56
|
issueMarker: `#${issue.issueNumber}`,
|
|
57
57
|
kitUpgrade: issue.labels.some(l => l.toLowerCase().includes('kit-upgrade')),
|
|
58
58
|
lineNumber: 0, // not applicable for platform issues
|
|
59
|
+
source: 'platform',
|
|
59
60
|
status,
|
|
60
61
|
changelog: inferChangelog(issue.title, issue.labels),
|
|
61
62
|
};
|
|
@@ -111,6 +111,7 @@ export function parseTodoMd(content) {
|
|
|
111
111
|
issueMarker: pendingTask.issueMarker,
|
|
112
112
|
kitUpgrade: meta.kitUpgrade,
|
|
113
113
|
lineNumber: pendingTask.lineNumber,
|
|
114
|
+
source: 'todo',
|
|
114
115
|
changelog: inferChangelogCategory(title, pendingTask.status),
|
|
115
116
|
});
|
|
116
117
|
pendingTask = null;
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
// ─── report_issue MCP tool for agents ───
|
|
2
|
+
// Allows agents to report discovered issues outside their current task scope.
|
|
3
|
+
// Handler runs in-process in the scheduler — records locally + creates platform issues.
|
|
4
|
+
import { appendFileSync } from 'node:fs';
|
|
5
|
+
import { resolve } from 'node:path';
|
|
6
|
+
import { tool } from '@anthropic-ai/claude-agent-sdk';
|
|
7
|
+
import { z } from 'zod/v4';
|
|
8
|
+
import { createPlatformIssue } from '../git/issue-creator.mjs';
|
|
9
|
+
// ─── In-memory store ───
|
|
10
|
+
export class DiscoveredIssueStore {
|
|
11
|
+
issues = [];
|
|
12
|
+
add(issue) {
|
|
13
|
+
this.issues.push(issue);
|
|
14
|
+
}
|
|
15
|
+
/** Returns all issues and clears the store. */
|
|
16
|
+
drain() {
|
|
17
|
+
return this.issues.splice(0);
|
|
18
|
+
}
|
|
19
|
+
/** Read without clearing. */
|
|
20
|
+
peek() {
|
|
21
|
+
return this.issues;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
// ─── Tool definition factory ───
|
|
25
|
+
const DISCOVERED_NDJSON = '.claude/scheduler/discovered.ndjson';
|
|
26
|
+
export function buildReportIssueTool(store, currentTaskId, projectDir, platform) {
|
|
27
|
+
return tool('report_issue', 'Report a discovered issue outside your current task scope. Use severity "non-blocker" for issues that don\'t prevent your work (then continue normally), or "blocker" for issues that prevent you from completing your task (then wrap up what you can and return).', {
|
|
28
|
+
title: z.string().describe('Concise issue title'),
|
|
29
|
+
type: z.enum(['frontend', 'backend', 'fullstack']).describe('Affected layer'),
|
|
30
|
+
complexity: z.enum(['S', 'M', 'L']).default('M').describe('Estimated complexity'),
|
|
31
|
+
severity: z.enum(['blocker', 'non-blocker']).describe('Whether this blocks your current task'),
|
|
32
|
+
description: z.string().describe('What is broken, where, and what the fix should look like'),
|
|
33
|
+
evidence: z.string().default('').describe('Error messages, file paths, stack traces'),
|
|
34
|
+
}, async (args) => {
|
|
35
|
+
const id = `discovered-${Date.now()}-${Math.random().toString(36).slice(2, 6)}`;
|
|
36
|
+
const issue = {
|
|
37
|
+
id,
|
|
38
|
+
title: args.title,
|
|
39
|
+
type: args.type,
|
|
40
|
+
complexity: args.complexity,
|
|
41
|
+
severity: args.severity,
|
|
42
|
+
description: args.description,
|
|
43
|
+
evidence: args.evidence,
|
|
44
|
+
reportedBy: currentTaskId,
|
|
45
|
+
timestamp: Date.now(),
|
|
46
|
+
};
|
|
47
|
+
// Record in memory (scheduler drains on next iteration)
|
|
48
|
+
store.add(issue);
|
|
49
|
+
// Persist for crash recovery
|
|
50
|
+
try {
|
|
51
|
+
const ndjsonPath = resolve(projectDir, DISCOVERED_NDJSON);
|
|
52
|
+
appendFileSync(ndjsonPath, JSON.stringify(issue) + '\n', 'utf-8');
|
|
53
|
+
}
|
|
54
|
+
catch { /* best-effort — in-memory store is primary */ }
|
|
55
|
+
// Create platform issue if available
|
|
56
|
+
if (platform !== 'none') {
|
|
57
|
+
try {
|
|
58
|
+
const labels = [`discovered`, `type::${args.type}`, `complexity::${args.complexity}`, 'status::todo'];
|
|
59
|
+
const created = createPlatformIssue(projectDir, platform, {
|
|
60
|
+
title: args.title,
|
|
61
|
+
body: `**Discovered during:** ${currentTaskId}\n**Severity:** ${args.severity}\n\n${args.description}\n\n**Evidence:**\n\`\`\`\n${args.evidence}\n\`\`\``,
|
|
62
|
+
labels,
|
|
63
|
+
});
|
|
64
|
+
if (created) {
|
|
65
|
+
issue.platformIssue = created;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
catch { /* non-fatal */ }
|
|
69
|
+
}
|
|
70
|
+
const platformNote = issue.platformIssue
|
|
71
|
+
? ` Platform issue: ${issue.platformIssue.url}`
|
|
72
|
+
: '';
|
|
73
|
+
return {
|
|
74
|
+
content: [{ type: 'text', text: `Issue recorded: ${args.title} (${id}).${platformNote}` }],
|
|
75
|
+
};
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
// ─── Convert discovered issue to Task ───
|
|
79
|
+
export function discoveredIssueToTask(issue, currentPhase) {
|
|
80
|
+
return {
|
|
81
|
+
id: issue.id,
|
|
82
|
+
title: issue.title,
|
|
83
|
+
phase: currentPhase,
|
|
84
|
+
type: issue.type,
|
|
85
|
+
complexity: issue.complexity,
|
|
86
|
+
dependsOn: [],
|
|
87
|
+
issueMarker: issue.platformIssue ? `#${issue.platformIssue.number}` : null,
|
|
88
|
+
kitUpgrade: false,
|
|
89
|
+
lineNumber: 0,
|
|
90
|
+
source: 'discovered',
|
|
91
|
+
status: 'todo',
|
|
92
|
+
changelog: 'added',
|
|
93
|
+
};
|
|
94
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
// ─── In-process MCP server with scheduler-provided tools ───
|
|
2
|
+
// Creates a per-agent MCP server instance injected into query() options.
|
|
3
|
+
// Each agent gets its own server (currentTaskId differs per spawn).
|
|
4
|
+
import { createSdkMcpServer } from '@anthropic-ai/claude-agent-sdk';
|
|
5
|
+
import { buildReportIssueTool } from './report-issue.mjs';
|
|
6
|
+
/**
|
|
7
|
+
* Create an in-process MCP server with scheduler tools for a specific agent.
|
|
8
|
+
* Returns a config that can be passed to query() via options.mcpServers.
|
|
9
|
+
*/
|
|
10
|
+
export function createSchedulerToolServer(store, currentTaskId, projectDir, platform) {
|
|
11
|
+
const reportIssueTool = buildReportIssueTool(store, currentTaskId, projectDir, platform);
|
|
12
|
+
return createSdkMcpServer({
|
|
13
|
+
name: 'scheduler-tools',
|
|
14
|
+
version: '1.0.0',
|
|
15
|
+
tools: [reportIssueTool],
|
|
16
|
+
});
|
|
17
|
+
}
|
|
@@ -561,4 +561,8 @@ When reviewing Angular code, check:
|
|
|
561
561
|
- @defer for heavy below-fold components
|
|
562
562
|
- Naming: PascalCase components, lib- selectors, kebab-case files
|
|
563
563
|
- i18n: ALL user-facing text has `i18n` attribute (templates) or `$localize` (TS) — no bare text
|
|
564
|
-
- VRT: new pages/routes have corresponding `*.vrt.spec.ts` with 3-viewport coverage (375px, 768px, 1280px), baseline snapshots committed
|
|
564
|
+
- VRT: new pages/routes have corresponding `*.vrt.spec.ts` with 3-viewport coverage (375px, 768px, 1280px), baseline snapshots committed
|
|
565
|
+
|
|
566
|
+
## Out-of-Scope Issues
|
|
567
|
+
|
|
568
|
+
If you discover problems outside your current task (broken infrastructure, misconfigurations, deprecated APIs in other code), use the `report_issue` tool to create a tracked issue. Set `severity: 'non-blocker'` and continue your work, or `severity: 'blocker'` if the problem prevents you from completing your task — in that case, finish what you can and return. Never dismiss problems as "pre-existing."
|
|
@@ -136,3 +136,7 @@ When called for **direct implementation**, provide complete production-ready cod
|
|
|
136
136
|
- Explain architectural decisions and trade-offs
|
|
137
137
|
- Proactively identify potential issues
|
|
138
138
|
- No fluff — every word serves a purpose
|
|
139
|
+
|
|
140
|
+
## Out-of-Scope Issues
|
|
141
|
+
|
|
142
|
+
If you discover problems outside your current task (broken infrastructure, misconfigurations, deprecated APIs in other code), use the `report_issue` tool to create a tracked issue. Set `severity: 'non-blocker'` and continue your work, or `severity: 'blocker'` if the problem prevents you from completing your task — in that case, finish what you can and return. Never dismiss problems as "pre-existing."
|
|
@@ -870,70 +870,18 @@ If a merge conflict occurs:
|
|
|
870
870
|
4. Push: `git push origin HEAD`
|
|
871
871
|
5. Then clean up: `git worktree remove .worktrees/feat/{slug} && git branch -d feat/{slug}`
|
|
872
872
|
|
|
873
|
-
##
|
|
873
|
+
## Proactive Problem Resolution
|
|
874
874
|
|
|
875
|
-
|
|
875
|
+
All agents have access to a `report_issue` tool provided by the scheduler. When an agent encounters a problem outside its current task scope, it calls this tool to create a tracked issue. The scheduler picks it up on the next iteration and routes it through the normal pipeline.
|
|
876
876
|
|
|
877
|
-
|
|
877
|
+
**Agent behavior:**
|
|
878
|
+
- **In-scope problem** → fix it directly (it's the agent's job)
|
|
879
|
+
- **Out-of-scope, non-blocking** → call `report_issue` with `severity: 'non-blocker'`, then continue working
|
|
880
|
+
- **Out-of-scope, blocking** → call `report_issue` with `severity: 'blocker'`, finish what's possible, then return
|
|
881
|
+
- **Warnings are actionable** — unless they originate from inside a third-party package's own source code
|
|
882
|
+
- **Never dismiss** — "pre-existing" and "not in scope" are not valid reasons to ignore a problem
|
|
878
883
|
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
```
|
|
882
|
-
## DISCOVERED ISSUES
|
|
883
|
-
|
|
884
|
-
### [NON-BLOCKER] Title describing the problem
|
|
885
|
-
- **Scope**: which agent should handle this (e.g., `deployment-engineer`, `backend-ts-architect`)
|
|
886
|
-
- **Type**: frontend | backend | fullstack | devops
|
|
887
|
-
- **Complexity**: S | M
|
|
888
|
-
- **Description**: What is broken, where, and what the fix should look like
|
|
889
|
-
- **Evidence**: error message, file:line, warning output
|
|
890
|
-
|
|
891
|
-
### [BLOCKER] Title describing the blocking problem
|
|
892
|
-
- **Scope**: which agent should handle this
|
|
893
|
-
- **Type**: frontend | backend | fullstack | devops
|
|
894
|
-
- **Complexity**: S | M
|
|
895
|
-
- **Description**: What is broken and why it blocks the current task
|
|
896
|
-
- **Evidence**: error message, file:line, warning output
|
|
897
|
-
- **Impact**: What cannot proceed until this is resolved
|
|
898
|
-
```
|
|
899
|
-
|
|
900
|
-
**Agent rules:**
|
|
901
|
-
- **In-scope problem** → fix it directly, do NOT add to DISCOVERED ISSUES (it's your job)
|
|
902
|
-
- **Out-of-scope, non-blocking** → add as `[NON-BLOCKER]`, then **continue and deliver your task**
|
|
903
|
-
- **Out-of-scope, blocking** → add as `[BLOCKER]`, finish what you can, then return. The orchestrator will handle delegation
|
|
904
|
-
- **Warnings count** — treat project warnings as issues unless they originate from inside a third-party package's own source code. Misconfiguration or misuse of a third-party tool is a project problem.
|
|
905
|
-
- **"Third-party issue" is narrow** — only valid when the bug is in the third-party package's source code itself
|
|
906
|
-
- **Never dismiss** — if you see a problem, either fix it or report it. "Pre-existing" and "not in scope" are not valid reasons to ignore a problem.
|
|
907
|
-
|
|
908
|
-
### Orchestrator-side: Processing DISCOVERED ISSUES
|
|
909
|
-
|
|
910
|
-
**After EVERY agent delegation** (STEP 2, 3, 4, 5, 6, 7, 8), scan the agent's response for a `## DISCOVERED ISSUES` section. If present:
|
|
911
|
-
|
|
912
|
-
#### NON-BLOCKER items → create tasks, continue current workflow
|
|
913
|
-
1. **Local mode**: Add each item to TODO.md in the CURRENT phase (after the current task). Format:
|
|
914
|
-
```
|
|
915
|
-
- [ ] **[Title from DISCOVERED ISSUES]** — [Description]. Discovered during #[current-task-id] (Complexity: [S|M], Type: [type])
|
|
916
|
-
```
|
|
917
|
-
2. **Platform mode**: Delegate to `devops-integrator`: "Create issues for these discovered problems: [list items]. Label: `status::todo`, `discovered`. Milestone: current phase. Do NOT perform git operations."
|
|
918
|
-
3. **Continue with the current task** — non-blockers do NOT interrupt the workflow.
|
|
919
|
-
|
|
920
|
-
#### BLOCKER items → resolve before continuing
|
|
921
|
-
1. **Assess if truly blocking**: Can the current task still be completed without resolving this? If the agent completed their deliverable despite reporting `[BLOCKER]`, reclassify as `[NON-BLOCKER]` and process accordingly.
|
|
922
|
-
2. **If blocking and in another agent's scope**: Delegate to the appropriate agent IMMEDIATELY (before continuing the current step). Include the evidence and description from the DISCOVERED ISSUES entry. This is an interruption to the current workflow — resume the current step after the blocker is resolved.
|
|
923
|
-
3. **If blocking and unfixable now** (requires human action, external dependency, missing credentials): Mark the current task as `[~]` SKIPPED with reason referencing the blocker. Create the task for the blocker issue. Move to next task.
|
|
924
|
-
4. **Max 1 blocker delegation per step** — if resolving a blocker reveals another blocker, skip the current task and create tasks for both blockers.
|
|
925
|
-
|
|
926
|
-
#### Deduplication
|
|
927
|
-
Before creating a task from a discovered issue:
|
|
928
|
-
- Check TODO.md / platform issues for existing tasks with similar title or description
|
|
929
|
-
- Check if the issue was already reported in a previous iteration (grep TODO.md for keywords)
|
|
930
|
-
- Skip duplicates — do NOT create redundant tasks
|
|
931
|
-
|
|
932
|
-
### Including the protocol in delegation prompts
|
|
933
|
-
|
|
934
|
-
**Add this paragraph to ALL agent delegation prompts** (STEP 2, 3, 4, 6 — all Agent tool calls to specialist agents):
|
|
935
|
-
|
|
936
|
-
> "If you encounter any problems, warnings, or broken infrastructure outside the scope of this task, include a `## DISCOVERED ISSUES` section at the end of your response. Use `[NON-BLOCKER]` for issues that don't prevent you from completing this task, `[BLOCKER]` for issues that do. Format: title, scope (which agent), type (frontend/backend/fullstack/devops), complexity (S/M), description, evidence. Do not dismiss issues as pre-existing — report everything you find."
|
|
884
|
+
**Orchestrator does NOT process discovered issues.** The scheduler handles everything — creating tasks from `report_issue` calls, creating platform issues, and routing them to the right agent via the normal task queue. The orchestrator just picks tasks and delegates as usual.
|
|
937
885
|
|
|
938
886
|
## When Stuck
|
|
939
887
|
|
|
@@ -393,3 +393,7 @@ When reviewing React code, check:
|
|
|
393
393
|
- No unstable nested component definitions (components defined inside render)
|
|
394
394
|
- `React.memo` only on components with measured re-render cost — not as a default
|
|
395
395
|
- VRT: new pages/routes have corresponding `*.vrt.spec.ts` with 3-viewport coverage (375px, 768px, 1280px), baseline snapshots committed
|
|
396
|
+
|
|
397
|
+
## Out-of-Scope Issues
|
|
398
|
+
|
|
399
|
+
If you discover problems outside your current task (broken infrastructure, misconfigurations, deprecated APIs in other code), use the `report_issue` tool to create a tracked issue. Set `severity: 'non-blocker'` and continue your work, or `severity: 'blocker'` if the problem prevents you from completing your task — in that case, finish what you can and return. Never dismiss problems as "pre-existing."
|
|
@@ -195,3 +195,7 @@ What is done well. Correct patterns, clean code, good decisions. Be specific.
|
|
|
195
195
|
4. **Prioritize impact** — security > correctness > performance > style
|
|
196
196
|
5. **Respect project standards** — align with CLAUDE.md conventions
|
|
197
197
|
6. **Don't offer to fix** — this is not interactive, the user cannot respond
|
|
198
|
+
|
|
199
|
+
## Out-of-Scope Issues
|
|
200
|
+
|
|
201
|
+
If you discover problems outside your current task (broken infrastructure, misconfigurations, deprecated APIs in other code), use the `report_issue` tool to create a tracked issue. Set `severity: 'non-blocker'` and continue your work, or `severity: 'blocker'` if the problem prevents you from completing your task — in that case, finish what you can and return. Never dismiss problems as "pre-existing."
|
|
@@ -394,3 +394,7 @@ When reviewing Svelte code, check:
|
|
|
394
394
|
- Callback props for outputs (not `createEventDispatcher` — that's Svelte 4)
|
|
395
395
|
- Resource cleanup in `onDestroy` or `$effect` return for manual subscriptions
|
|
396
396
|
- VRT: new pages/routes have corresponding `*.vrt.spec.ts` with 3-viewport coverage (375px, 768px, 1280px), baseline snapshots committed
|
|
397
|
+
|
|
398
|
+
## Out-of-Scope Issues
|
|
399
|
+
|
|
400
|
+
If you discover problems outside your current task (broken infrastructure, misconfigurations, deprecated APIs in other code), use the `report_issue` tool to create a tracked issue. Set `severity: 'non-blocker'` and continue your work, or `severity: 'blocker'` if the problem prevents you from completing your task — in that case, finish what you can and return. Never dismiss problems as "pre-existing."
|
|
@@ -538,3 +538,7 @@ Tests should be easy to write. If they are not, the production code needs refact
|
|
|
538
538
|
- **No flaky tests** — if a test fails intermittently, fix it or delete it
|
|
539
539
|
- **Test public API** — don't test private methods directly
|
|
540
540
|
- **Pure functions are easy to test** — prefer pure transformations over stateful operations. If a function is hard to test, it likely has too many responsibilities or side effects.
|
|
541
|
+
|
|
542
|
+
## Out-of-Scope Issues
|
|
543
|
+
|
|
544
|
+
If you discover problems outside your current task (broken infrastructure, misconfigurations, deprecated APIs in other code), use the `report_issue` tool to create a tracked issue. Set `severity: 'non-blocker'` and continue your work, or `severity: 'blocker'` if the problem prevents you from completing your task — in that case, finish what you can and return. Never dismiss problems as "pre-existing."
|
|
@@ -144,4 +144,8 @@ When called for **direct implementation**, provide:
|
|
|
144
144
|
- TypeScript interfaces and types
|
|
145
145
|
- Follow project conventions from CLAUDE.md (App Separation Principle, Onion Architecture layers)
|
|
146
146
|
- Monorepo-compatible solutions for shared functionality
|
|
147
|
-
- When uncertain about approach, ask for clarification before proceeding
|
|
147
|
+
- When uncertain about approach, ask for clarification before proceeding
|
|
148
|
+
|
|
149
|
+
## Out-of-Scope Issues
|
|
150
|
+
|
|
151
|
+
If you discover problems outside your current task (broken infrastructure, misconfigurations, deprecated APIs in other code), use the `report_issue` tool to create a tracked issue. Set `severity: 'non-blocker'` and continue your work, or `severity: 'blocker'` if the problem prevents you from completing your task — in that case, finish what you can and return. Never dismiss problems as "pre-existing."
|
|
@@ -423,3 +423,7 @@ When reviewing Vue code, check:
|
|
|
423
423
|
- Naming: PascalCase components, `Base` prefix for atoms, `use` prefix for composables
|
|
424
424
|
- i18n: ALL user-facing text goes through `$t()` / `t()` — no bare strings in templates or script
|
|
425
425
|
- VRT: new pages/routes have corresponding `*.vrt.spec.ts` with 3-viewport coverage (375px, 768px, 1280px), baseline snapshots committed
|
|
426
|
+
|
|
427
|
+
## Out-of-Scope Issues
|
|
428
|
+
|
|
429
|
+
If you discover problems outside your current task (broken infrastructure, misconfigurations, deprecated APIs in other code), use the `report_issue` tool to create a tracked issue. Set `severity: 'non-blocker'` and continue your work, or `severity: 'blocker'` if the problem prevents you from completing your task — in that case, finish what you can and return. Never dismiss problems as "pre-existing."
|