@vibescope/mcp-server 0.2.9 → 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/CHANGELOG.md +84 -84
- package/README.md +194 -194
- package/dist/api-client.d.ts +36 -0
- package/dist/api-client.js +34 -0
- package/dist/cli.d.ts +1 -1
- package/dist/cli.js +30 -38
- package/dist/handlers/discovery.js +2 -0
- package/dist/handlers/session.d.ts +11 -0
- package/dist/handlers/session.js +101 -0
- package/dist/handlers/tasks.d.ts +8 -0
- package/dist/handlers/tasks.js +163 -3
- package/dist/handlers/tool-docs.js +840 -828
- package/dist/handlers/validation.js +45 -2
- package/dist/index.js +73 -73
- package/dist/setup.js +6 -6
- package/dist/templates/agent-guidelines.js +185 -185
- package/dist/templates/help-content.js +1622 -1544
- package/dist/tools.js +126 -74
- package/dist/utils.d.ts +15 -11
- package/dist/utils.js +53 -28
- package/docs/TOOLS.md +2406 -2053
- package/package.json +51 -51
- package/scripts/generate-docs.ts +212 -212
- package/scripts/version-bump.ts +203 -203
- package/src/api-client.test.ts +723 -723
- package/src/api-client.ts +2561 -2499
- package/src/cli.test.ts +24 -8
- package/src/cli.ts +204 -212
- package/src/handlers/__test-setup__.ts +236 -236
- package/src/handlers/__test-utils__.ts +87 -87
- package/src/handlers/blockers.test.ts +468 -468
- package/src/handlers/blockers.ts +163 -163
- package/src/handlers/bodies-of-work.test.ts +704 -704
- package/src/handlers/bodies-of-work.ts +526 -526
- package/src/handlers/connectors.test.ts +834 -834
- package/src/handlers/connectors.ts +229 -229
- package/src/handlers/cost.test.ts +462 -462
- package/src/handlers/cost.ts +285 -285
- package/src/handlers/decisions.test.ts +382 -382
- package/src/handlers/decisions.ts +153 -153
- package/src/handlers/deployment.test.ts +551 -551
- package/src/handlers/deployment.ts +541 -541
- package/src/handlers/discovery.test.ts +206 -206
- package/src/handlers/discovery.ts +392 -390
- package/src/handlers/fallback.test.ts +537 -537
- package/src/handlers/fallback.ts +194 -194
- package/src/handlers/file-checkouts.test.ts +750 -750
- package/src/handlers/file-checkouts.ts +185 -185
- package/src/handlers/findings.test.ts +633 -633
- package/src/handlers/findings.ts +239 -239
- package/src/handlers/git-issues.test.ts +631 -631
- package/src/handlers/git-issues.ts +136 -136
- package/src/handlers/ideas.test.ts +644 -644
- package/src/handlers/ideas.ts +207 -207
- package/src/handlers/index.ts +84 -84
- package/src/handlers/milestones.test.ts +475 -475
- package/src/handlers/milestones.ts +180 -180
- package/src/handlers/organizations.test.ts +826 -826
- package/src/handlers/organizations.ts +315 -315
- package/src/handlers/progress.test.ts +269 -269
- package/src/handlers/progress.ts +77 -77
- package/src/handlers/project.test.ts +546 -546
- package/src/handlers/project.ts +239 -239
- package/src/handlers/requests.test.ts +303 -303
- package/src/handlers/requests.ts +99 -99
- package/src/handlers/roles.test.ts +305 -305
- package/src/handlers/roles.ts +219 -219
- package/src/handlers/session.test.ts +998 -875
- package/src/handlers/session.ts +839 -730
- package/src/handlers/sprints.test.ts +732 -732
- package/src/handlers/sprints.ts +537 -537
- package/src/handlers/tasks.test.ts +931 -907
- package/src/handlers/tasks.ts +1121 -945
- package/src/handlers/tool-categories.test.ts +66 -66
- package/src/handlers/tool-docs.ts +1109 -1096
- package/src/handlers/types.test.ts +259 -259
- package/src/handlers/types.ts +175 -175
- package/src/handlers/validation.test.ts +582 -582
- package/src/handlers/validation.ts +159 -113
- package/src/index.test.ts +674 -0
- package/src/index.ts +792 -792
- package/src/setup.test.ts +233 -233
- package/src/setup.ts +404 -403
- package/src/templates/agent-guidelines.ts +210 -210
- package/src/templates/help-content.ts +1751 -1673
- package/src/token-tracking.test.ts +463 -463
- package/src/token-tracking.ts +166 -166
- package/src/tools.test.ts +416 -0
- package/src/tools.ts +3607 -3555
- package/src/utils.test.ts +785 -683
- package/src/utils.ts +469 -436
- package/src/validators.test.ts +223 -223
- package/src/validators.ts +249 -249
- package/tsconfig.json +16 -16
- package/vitest.config.ts +14 -14
package/dist/cli.js
CHANGED
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
*/
|
|
14
14
|
import { execSync } from 'child_process';
|
|
15
15
|
import { runSetup } from './setup.js';
|
|
16
|
+
import { normalizeGitUrl as normalizeGitUrlFn } from './utils.js';
|
|
16
17
|
// ============================================================================
|
|
17
18
|
// Configuration (read at runtime for testability)
|
|
18
19
|
// ============================================================================
|
|
@@ -25,17 +26,8 @@ function getApiUrl() {
|
|
|
25
26
|
// ============================================================================
|
|
26
27
|
// Git URL Detection
|
|
27
28
|
// ============================================================================
|
|
28
|
-
export
|
|
29
|
-
|
|
30
|
-
let normalized = url.replace(/\.git$/, '');
|
|
31
|
-
// Convert SSH to HTTPS format
|
|
32
|
-
if (normalized.startsWith('git@')) {
|
|
33
|
-
normalized = normalized
|
|
34
|
-
.replace(/^git@/, 'https://')
|
|
35
|
-
.replace(/:([^/])/, '/$1');
|
|
36
|
-
}
|
|
37
|
-
return normalized;
|
|
38
|
-
}
|
|
29
|
+
// Re-export normalizeGitUrl from utils for backwards compatibility with tests
|
|
30
|
+
export { normalizeGitUrl } from './utils.js';
|
|
39
31
|
export function detectGitUrl() {
|
|
40
32
|
try {
|
|
41
33
|
const url = execSync('git config --get remote.origin.url', {
|
|
@@ -43,7 +35,7 @@ export function detectGitUrl() {
|
|
|
43
35
|
timeout: 5000,
|
|
44
36
|
stdio: ['pipe', 'pipe', 'pipe'],
|
|
45
37
|
}).trim();
|
|
46
|
-
return
|
|
38
|
+
return normalizeGitUrlFn(url);
|
|
47
39
|
}
|
|
48
40
|
catch {
|
|
49
41
|
return null;
|
|
@@ -124,32 +116,32 @@ async function main() {
|
|
|
124
116
|
}
|
|
125
117
|
}
|
|
126
118
|
else if (command === 'help' || command === '--help' || command === '-h') {
|
|
127
|
-
console.log(`
|
|
128
|
-
Vibescope CLI - Setup wizard and enforcement verification tool
|
|
129
|
-
|
|
130
|
-
Usage:
|
|
131
|
-
vibescope-cli setup Interactive setup wizard for your IDE
|
|
132
|
-
vibescope-cli verify [options] Check Vibescope compliance before exit
|
|
133
|
-
|
|
134
|
-
Setup:
|
|
135
|
-
Configures Vibescope MCP integration for:
|
|
136
|
-
- Claude Code (CLI)
|
|
137
|
-
- Claude Desktop
|
|
138
|
-
- Cursor
|
|
139
|
-
- Gemini CLI
|
|
140
|
-
|
|
141
|
-
Verify Options:
|
|
142
|
-
--git-url <url> Git repository URL (auto-detected if not provided)
|
|
143
|
-
--project-id <id> Vibescope project UUID
|
|
144
|
-
|
|
145
|
-
Exit Codes (verify):
|
|
146
|
-
0 Compliant - agent can exit
|
|
147
|
-
1 Non-compliant - agent should continue work
|
|
148
|
-
2 Error - allow exit with warning
|
|
149
|
-
|
|
150
|
-
Environment Variables:
|
|
151
|
-
VIBESCOPE_API_KEY Required for verify - Your Vibescope API key
|
|
152
|
-
VIBESCOPE_API_URL Optional - API URL (default: https://vibescope.dev)
|
|
119
|
+
console.log(`
|
|
120
|
+
Vibescope CLI - Setup wizard and enforcement verification tool
|
|
121
|
+
|
|
122
|
+
Usage:
|
|
123
|
+
vibescope-cli setup Interactive setup wizard for your IDE
|
|
124
|
+
vibescope-cli verify [options] Check Vibescope compliance before exit
|
|
125
|
+
|
|
126
|
+
Setup:
|
|
127
|
+
Configures Vibescope MCP integration for:
|
|
128
|
+
- Claude Code (CLI)
|
|
129
|
+
- Claude Desktop
|
|
130
|
+
- Cursor
|
|
131
|
+
- Gemini CLI
|
|
132
|
+
|
|
133
|
+
Verify Options:
|
|
134
|
+
--git-url <url> Git repository URL (auto-detected if not provided)
|
|
135
|
+
--project-id <id> Vibescope project UUID
|
|
136
|
+
|
|
137
|
+
Exit Codes (verify):
|
|
138
|
+
0 Compliant - agent can exit
|
|
139
|
+
1 Non-compliant - agent should continue work
|
|
140
|
+
2 Error - allow exit with warning
|
|
141
|
+
|
|
142
|
+
Environment Variables:
|
|
143
|
+
VIBESCOPE_API_KEY Required for verify - Your Vibescope API key
|
|
144
|
+
VIBESCOPE_API_URL Optional - API URL (default: https://vibescope.dev)
|
|
153
145
|
`);
|
|
154
146
|
process.exit(0);
|
|
155
147
|
}
|
|
@@ -40,6 +40,7 @@ export const TOOL_CATEGORIES = {
|
|
|
40
40
|
{ name: 'get_token_usage', brief: 'View token stats' },
|
|
41
41
|
{ name: 'report_token_usage', brief: 'Report actual Claude API usage' },
|
|
42
42
|
{ name: 'heartbeat', brief: 'Maintain active status' },
|
|
43
|
+
{ name: 'signal_idle', brief: 'Signal agent is idle' },
|
|
43
44
|
{ name: 'end_work_session', brief: 'End session, release tasks' },
|
|
44
45
|
{ name: 'confirm_agent_setup', brief: 'Mark agent setup as complete' },
|
|
45
46
|
],
|
|
@@ -69,6 +70,7 @@ export const TOOL_CATEGORIES = {
|
|
|
69
70
|
{ name: 'complete_task', brief: 'Mark task done' },
|
|
70
71
|
{ name: 'delete_task', brief: 'Remove a task' },
|
|
71
72
|
{ name: 'cancel_task', brief: 'Cancel task with reason' },
|
|
73
|
+
{ name: 'release_task', brief: 'Unclaim task to pending' },
|
|
72
74
|
{ name: 'batch_update_tasks', brief: 'Update multiple tasks' },
|
|
73
75
|
{ name: 'batch_complete_tasks', brief: 'Complete multiple tasks' },
|
|
74
76
|
{ name: 'add_task_reference', brief: 'Add URL to task' },
|
|
@@ -26,6 +26,17 @@ export declare const reportTokenUsage: Handler;
|
|
|
26
26
|
* This marks the agent type as onboarded, so future sessions won't receive setup instructions.
|
|
27
27
|
*/
|
|
28
28
|
export declare const confirmAgentSetup: Handler;
|
|
29
|
+
/**
|
|
30
|
+
* Signal that the agent is idle (no more tasks to work on).
|
|
31
|
+
* This immediately updates the session status to 'idle', providing real-time
|
|
32
|
+
* visibility on the dashboard instead of waiting for heartbeat timeout.
|
|
33
|
+
*
|
|
34
|
+
* Call this when:
|
|
35
|
+
* - complete_task returns no next_task
|
|
36
|
+
* - get_next_task returns no tasks
|
|
37
|
+
* - There's genuinely no work to do
|
|
38
|
+
*/
|
|
39
|
+
export declare const signalIdle: Handler;
|
|
29
40
|
/**
|
|
30
41
|
* Session handlers registry
|
|
31
42
|
*/
|
package/dist/handlers/session.js
CHANGED
|
@@ -13,6 +13,7 @@ import { parseArgs, createEnumValidator } from '../validators.js';
|
|
|
13
13
|
import { getApiClient } from '../api-client.js';
|
|
14
14
|
import { getAgentGuidelinesTemplate, getAgentGuidelinesSummary } from '../templates/agent-guidelines.js';
|
|
15
15
|
import { getFallbackHelpContent, getAvailableHelpTopics } from '../templates/help-content.js';
|
|
16
|
+
import { normalizeGitUrl } from '../utils.js';
|
|
16
17
|
// Auto-detect machine hostname for worktree tracking
|
|
17
18
|
const MACHINE_HOSTNAME = os.hostname();
|
|
18
19
|
const VALID_MODES = ['lite', 'full'];
|
|
@@ -41,6 +42,9 @@ export const startWorkSession = async (args, ctx) => {
|
|
|
41
42
|
const { project_id, git_url, mode, model, role, hostname: providedHostname, agent_type } = parseArgs(args, startWorkSessionSchema);
|
|
42
43
|
// Use auto-detected hostname if not provided - enables machine-aware worktree filtering
|
|
43
44
|
const hostname = providedHostname || MACHINE_HOSTNAME;
|
|
45
|
+
// Normalize git_url and track if it was changed - helps agents understand URL matching
|
|
46
|
+
const normalizedGitUrl = git_url ? normalizeGitUrl(git_url) : null;
|
|
47
|
+
const gitUrlWasNormalized = git_url && normalizedGitUrl && git_url !== normalizedGitUrl;
|
|
44
48
|
const { session, updateSession } = ctx;
|
|
45
49
|
// Reset token tracking for new session with model info
|
|
46
50
|
// Model is now open-ended - use as-is (normalize Claude model names for consistency)
|
|
@@ -145,6 +149,19 @@ export const startWorkSession = async (args, ctx) => {
|
|
|
145
149
|
result.persona = data.persona;
|
|
146
150
|
result.role = data.role;
|
|
147
151
|
result.project = data.project;
|
|
152
|
+
// Inform agent if git_url was normalized (helps explain URL matching behavior)
|
|
153
|
+
if (gitUrlWasNormalized) {
|
|
154
|
+
result.git_url_normalized = {
|
|
155
|
+
message: 'Your git URL was normalized for project lookup. All URL formats for the same repository resolve to the same project.',
|
|
156
|
+
original: git_url,
|
|
157
|
+
normalized: normalizedGitUrl,
|
|
158
|
+
examples: [
|
|
159
|
+
'git@github.com:owner/repo.git → https://github.com/owner/repo',
|
|
160
|
+
'https://GITHUB.COM/Owner/Repo/ → https://github.com/owner/repo',
|
|
161
|
+
'http://github.com/owner/repo.git → https://github.com/owner/repo',
|
|
162
|
+
],
|
|
163
|
+
};
|
|
164
|
+
}
|
|
148
165
|
// Add task data
|
|
149
166
|
if (data.next_task) {
|
|
150
167
|
result.next_task = data.next_task;
|
|
@@ -205,6 +222,31 @@ export const startWorkSession = async (args, ctx) => {
|
|
|
205
222
|
command: `git worktree add ../<project>-<persona>-<task> -b feature/<task-id> ${baseBranch}`,
|
|
206
223
|
help: 'Run get_help("git") for full instructions',
|
|
207
224
|
};
|
|
225
|
+
// Add FIRST_TIME_CONNECTION guidance for git-flow
|
|
226
|
+
if (data.project.git_workflow === 'git-flow') {
|
|
227
|
+
result.FIRST_TIME_CONNECTION = {
|
|
228
|
+
workflow: 'git-flow',
|
|
229
|
+
steps: [
|
|
230
|
+
`1. git checkout ${data.project.git_develop_branch || 'develop'}`,
|
|
231
|
+
`2. git pull origin ${data.project.git_develop_branch || 'develop'}`,
|
|
232
|
+
'3. All feature branches must be created from develop',
|
|
233
|
+
],
|
|
234
|
+
warning: 'Working from main or stale branches causes merge conflicts.',
|
|
235
|
+
base_branch: data.project.git_develop_branch || 'develop',
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
else if (data.project.git_workflow === 'github-flow') {
|
|
239
|
+
result.FIRST_TIME_CONNECTION = {
|
|
240
|
+
workflow: 'github-flow',
|
|
241
|
+
steps: [
|
|
242
|
+
`1. git checkout ${data.project.git_main_branch || 'main'}`,
|
|
243
|
+
`2. git pull origin ${data.project.git_main_branch || 'main'}`,
|
|
244
|
+
'3. All feature branches must be created from main',
|
|
245
|
+
],
|
|
246
|
+
warning: 'Working from stale branches causes merge conflicts.',
|
|
247
|
+
base_branch: data.project.git_main_branch || 'main',
|
|
248
|
+
};
|
|
249
|
+
}
|
|
208
250
|
}
|
|
209
251
|
}
|
|
210
252
|
// Add agent setup instructions if this is a new agent type for the project
|
|
@@ -601,6 +643,64 @@ export const confirmAgentSetup = async (args, _ctx) => {
|
|
|
601
643
|
},
|
|
602
644
|
};
|
|
603
645
|
};
|
|
646
|
+
const signalIdleSchema = {
|
|
647
|
+
session_id: { type: 'string' },
|
|
648
|
+
};
|
|
649
|
+
/**
|
|
650
|
+
* Signal that the agent is idle (no more tasks to work on).
|
|
651
|
+
* This immediately updates the session status to 'idle', providing real-time
|
|
652
|
+
* visibility on the dashboard instead of waiting for heartbeat timeout.
|
|
653
|
+
*
|
|
654
|
+
* Call this when:
|
|
655
|
+
* - complete_task returns no next_task
|
|
656
|
+
* - get_next_task returns no tasks
|
|
657
|
+
* - There's genuinely no work to do
|
|
658
|
+
*/
|
|
659
|
+
export const signalIdle = async (args, ctx) => {
|
|
660
|
+
const { session_id } = parseArgs(args, signalIdleSchema);
|
|
661
|
+
const { session } = ctx;
|
|
662
|
+
const targetSession = session_id || session.currentSessionId;
|
|
663
|
+
if (!targetSession) {
|
|
664
|
+
return {
|
|
665
|
+
result: {
|
|
666
|
+
error: 'No active session. Call start_work_session first.',
|
|
667
|
+
},
|
|
668
|
+
};
|
|
669
|
+
}
|
|
670
|
+
const apiClient = getApiClient();
|
|
671
|
+
const response = await apiClient.signalIdle(targetSession);
|
|
672
|
+
if (!response.ok) {
|
|
673
|
+
return {
|
|
674
|
+
result: {
|
|
675
|
+
error: response.error || 'Failed to signal idle',
|
|
676
|
+
},
|
|
677
|
+
};
|
|
678
|
+
}
|
|
679
|
+
return {
|
|
680
|
+
result: {
|
|
681
|
+
success: true,
|
|
682
|
+
session_id: targetSession,
|
|
683
|
+
status: 'idle',
|
|
684
|
+
message: 'Session marked as idle. Dashboard will show you as inactive until you start a new task.',
|
|
685
|
+
next_steps: [
|
|
686
|
+
'Wait for new tasks to be added by the user',
|
|
687
|
+
'Or call start_fallback_activity() to do background work',
|
|
688
|
+
'Or call end_work_session() if you are done for the session',
|
|
689
|
+
],
|
|
690
|
+
AUTONOMY_RULES: {
|
|
691
|
+
message: 'Agents must be autonomous. Follow these rules:',
|
|
692
|
+
rules: [
|
|
693
|
+
'Never ask "should I continue?" → Just continue',
|
|
694
|
+
'Never ask "should I clear context?" → Just clear it',
|
|
695
|
+
'Never say "let me know what to do" → Use get_next_task or fallback activities',
|
|
696
|
+
'When context grows large: /clear → start_work_session (don\'t ask, just do it)',
|
|
697
|
+
],
|
|
698
|
+
preferred_activities: ['code_review', 'security_review', 'test_coverage', 'documentation_review', 'dependency_audit'],
|
|
699
|
+
directive: 'BE PROACTIVE: Start a fallback activity now rather than waiting for user input.',
|
|
700
|
+
},
|
|
701
|
+
},
|
|
702
|
+
};
|
|
703
|
+
};
|
|
604
704
|
/**
|
|
605
705
|
* Session handlers registry
|
|
606
706
|
*/
|
|
@@ -608,6 +708,7 @@ export const sessionHandlers = {
|
|
|
608
708
|
start_work_session: startWorkSession,
|
|
609
709
|
heartbeat: heartbeat,
|
|
610
710
|
end_work_session: endWorkSession,
|
|
711
|
+
signal_idle: signalIdle,
|
|
611
712
|
get_help: getHelp,
|
|
612
713
|
get_token_usage: getTokenUsage,
|
|
613
714
|
report_token_usage: reportTokenUsage,
|
package/dist/handlers/tasks.d.ts
CHANGED
|
@@ -12,6 +12,8 @@
|
|
|
12
12
|
* - update_task
|
|
13
13
|
* - complete_task
|
|
14
14
|
* - delete_task
|
|
15
|
+
* - release_task
|
|
16
|
+
* - cancel_task
|
|
15
17
|
* - add_task_reference
|
|
16
18
|
* - remove_task_reference
|
|
17
19
|
* - batch_update_tasks
|
|
@@ -39,6 +41,12 @@ export declare const addTask: Handler;
|
|
|
39
41
|
export declare const updateTask: Handler;
|
|
40
42
|
export declare const completeTask: Handler;
|
|
41
43
|
export declare const deleteTask: Handler;
|
|
44
|
+
/**
|
|
45
|
+
* Release a task back to pending status.
|
|
46
|
+
* Use when an agent needs to give up a claimed task (context limits, conflicts, user request).
|
|
47
|
+
*/
|
|
48
|
+
export declare const releaseTask: Handler;
|
|
49
|
+
export declare const cancelTask: Handler;
|
|
42
50
|
export declare const addTaskReference: Handler;
|
|
43
51
|
export declare const removeTaskReference: Handler;
|
|
44
52
|
export declare const batchUpdateTasks: Handler;
|
package/dist/handlers/tasks.js
CHANGED
|
@@ -12,6 +12,8 @@
|
|
|
12
12
|
* - update_task
|
|
13
13
|
* - complete_task
|
|
14
14
|
* - delete_task
|
|
15
|
+
* - release_task
|
|
16
|
+
* - cancel_task
|
|
15
17
|
* - add_task_reference
|
|
16
18
|
* - remove_task_reference
|
|
17
19
|
* - batch_update_tasks
|
|
@@ -65,6 +67,19 @@ const completeTaskSchema = {
|
|
|
65
67
|
const deleteTaskSchema = {
|
|
66
68
|
task_id: { type: 'string', required: true, validate: uuidValidator },
|
|
67
69
|
};
|
|
70
|
+
const releaseTaskSchema = {
|
|
71
|
+
task_id: { type: 'string', required: true, validate: uuidValidator },
|
|
72
|
+
reason: { type: 'string' },
|
|
73
|
+
};
|
|
74
|
+
// Valid reasons for task cancellation
|
|
75
|
+
const VALID_CANCELLED_REASONS = [
|
|
76
|
+
'pr_closed', 'superseded', 'user_cancelled', 'validation_failed', 'obsolete', 'blocked'
|
|
77
|
+
];
|
|
78
|
+
const cancelTaskSchema = {
|
|
79
|
+
task_id: { type: 'string', required: true, validate: uuidValidator },
|
|
80
|
+
cancelled_reason: { type: 'string', validate: createEnumValidator(VALID_CANCELLED_REASONS) },
|
|
81
|
+
cancellation_note: { type: 'string' },
|
|
82
|
+
};
|
|
68
83
|
const addTaskReferenceSchema = {
|
|
69
84
|
task_id: { type: 'string', required: true, validate: uuidValidator },
|
|
70
85
|
url: { type: 'string', required: true },
|
|
@@ -194,6 +209,21 @@ export const getNextTask = async (args, ctx) => {
|
|
|
194
209
|
}
|
|
195
210
|
else {
|
|
196
211
|
result.task = null;
|
|
212
|
+
// Add IDLE_GUIDANCE when no tasks are available
|
|
213
|
+
result.IDLE_GUIDANCE = {
|
|
214
|
+
message: 'No tasks available. Follow these steps:',
|
|
215
|
+
steps: [
|
|
216
|
+
'1. Call signal_idle() to update dashboard immediately - shows you are available',
|
|
217
|
+
'2. Start a fallback_activity (code_review, security_review, test_coverage, etc.)',
|
|
218
|
+
'3. Never ask "what should I do?" - be autonomous',
|
|
219
|
+
],
|
|
220
|
+
autonomy_rules: [
|
|
221
|
+
'Never ask "should I continue?" → Just continue',
|
|
222
|
+
'Never say "let me know what to do" → Use fallback activities',
|
|
223
|
+
'When context grows large: /clear → start_work_session (don\'t ask, just do it)',
|
|
224
|
+
],
|
|
225
|
+
next_action: `signal_idle() then start_fallback_activity(project_id: "${project_id}", activity: "code_review")`,
|
|
226
|
+
};
|
|
197
227
|
}
|
|
198
228
|
if (data.blocking_task)
|
|
199
229
|
result.blocking_task = true;
|
|
@@ -281,11 +311,15 @@ export const updateTask = async (args, ctx) => {
|
|
|
281
311
|
},
|
|
282
312
|
};
|
|
283
313
|
}
|
|
284
|
-
if (response.error?.includes('task_claimed') || response.error?.includes('being worked on')) {
|
|
314
|
+
if (response.error?.includes('task_claimed') || response.error?.includes('task_already_claimed') || response.error?.includes('being worked on') || response.error?.includes('already being worked on')) {
|
|
315
|
+
const data = response.data;
|
|
285
316
|
return {
|
|
286
317
|
result: {
|
|
287
|
-
error: '
|
|
288
|
-
message: response.error,
|
|
318
|
+
error: 'task_already_claimed',
|
|
319
|
+
message: data?.message || response.error || 'Task is already claimed by another agent',
|
|
320
|
+
claimed_by: data?.claimed_by,
|
|
321
|
+
claimed_session_id: data?.claimed_session_id,
|
|
322
|
+
suggestion: 'Use get_next_task() to get a different available task, or wait for the claiming agent to finish.',
|
|
289
323
|
},
|
|
290
324
|
};
|
|
291
325
|
}
|
|
@@ -330,6 +364,63 @@ export const updateTask = async (args, ctx) => {
|
|
|
330
364
|
test_patterns: ['*.test.ts', '*.spec.ts', '*.test.js', '*.spec.js', '__tests__/*'],
|
|
331
365
|
note: 'Validators will check for test file changes during review. Documentation-only or config changes may not require tests.',
|
|
332
366
|
};
|
|
367
|
+
// Add comprehensive WORKTREE RULES for branching workflows
|
|
368
|
+
// This reminds agents of the critical workflow order
|
|
369
|
+
result.WORKTREE_RULES = {
|
|
370
|
+
mandatory: true,
|
|
371
|
+
rules: [
|
|
372
|
+
'1. Create worktree BEFORE any file edits - reading is fine, editing requires worktree first',
|
|
373
|
+
'2. Naming: ../PROJECT-PERSONA-short-desc (max 24 chars for description)',
|
|
374
|
+
'3. Command: git worktree add ../PROJECT-PERSONA-desc -b feature/TASKID-desc BASE_BRANCH',
|
|
375
|
+
'4. Report location: heartbeat(current_worktree_path: "...")',
|
|
376
|
+
'5. Store path: update_task(task_id, worktree_path: "...")',
|
|
377
|
+
'6. REBASE before PR: git fetch origin && git rebase origin/BASE_BRANCH && git push --force-with-lease',
|
|
378
|
+
],
|
|
379
|
+
rebase_before_pr: {
|
|
380
|
+
mandatory: true,
|
|
381
|
+
why: 'Without rebasing, your branch may contain old versions of files that other agents modified. When merged, your old version overwrites their changes.',
|
|
382
|
+
commands: [
|
|
383
|
+
'git fetch origin',
|
|
384
|
+
'git rebase origin/develop # or origin/main for github-flow',
|
|
385
|
+
'git push --force-with-lease',
|
|
386
|
+
],
|
|
387
|
+
},
|
|
388
|
+
wrong_order: {
|
|
389
|
+
violation: 'Edit file → stash → create worktree → pop → commit',
|
|
390
|
+
why: 'Even if you eventually use a worktree, editing before creating one is a violation',
|
|
391
|
+
},
|
|
392
|
+
right_order: {
|
|
393
|
+
correct: 'Read to understand → create worktree → cd into it → THEN edit',
|
|
394
|
+
why: 'Worktrees must exist BEFORE any file modifications',
|
|
395
|
+
},
|
|
396
|
+
};
|
|
397
|
+
// Add HOTFIX_WORKFLOW guidance when branch name indicates hotfix
|
|
398
|
+
if (git_branch && git_branch.includes('hotfix/')) {
|
|
399
|
+
result.HOTFIX_WORKFLOW = {
|
|
400
|
+
message: 'HOTFIX detected - special workflow applies:',
|
|
401
|
+
steps: [
|
|
402
|
+
'1. Create worktree from MAIN (not develop): git worktree add ../PROJECT-PERSONA-hotfix-desc -b hotfix/TASKID-desc main',
|
|
403
|
+
'2. Work in worktree and make your fix',
|
|
404
|
+
'3. Commit: git add -A && git commit -m "fix: description"',
|
|
405
|
+
'4. Push: git push -u origin hotfix/TASKID-desc',
|
|
406
|
+
'5. Create PR targeting MAIN: gh pr create --base main --title "fix: ..." --body "Hotfix for production"',
|
|
407
|
+
'6. Remove worktree immediately after PR',
|
|
408
|
+
],
|
|
409
|
+
important: 'Hotfixes go to MAIN, not develop. They are later merged to develop separately.',
|
|
410
|
+
worktree_required: true,
|
|
411
|
+
};
|
|
412
|
+
}
|
|
413
|
+
// Guidance for when investigation reveals fix already exists
|
|
414
|
+
result.FIX_ALREADY_EXISTS_GUIDANCE = {
|
|
415
|
+
message: 'If investigation reveals the fix already exists but needs deployment:',
|
|
416
|
+
steps: [
|
|
417
|
+
'1. Add finding: add_finding(project_id, title: "Fix exists, awaits deployment", category: "other", severity: "info", description: "...", related_task_id: task_id)',
|
|
418
|
+
'2. Complete task: complete_task(task_id, summary: "Fix already exists in codebase (PR #XXX). Needs deployment.")',
|
|
419
|
+
'3. Check deployment: check_deployment_status(project_id)',
|
|
420
|
+
'4. Request deployment if not pending: request_deployment(project_id, notes: "Includes fix for [issue]")',
|
|
421
|
+
],
|
|
422
|
+
rationale: 'This prevents tasks from being blocked waiting for deployment when the actual work is done.',
|
|
423
|
+
};
|
|
333
424
|
}
|
|
334
425
|
return { result };
|
|
335
426
|
};
|
|
@@ -365,6 +456,28 @@ export const completeTask = async (args, ctx) => {
|
|
|
365
456
|
// Git workflow instructions are already in API response but we need to fetch
|
|
366
457
|
// task details if we want to include them (API should return these)
|
|
367
458
|
result.next_action = data.next_action;
|
|
459
|
+
// Add mandatory action reminders for complete_task
|
|
460
|
+
result.MANDATORY_ACTIONS = {
|
|
461
|
+
message: 'Before marking task complete, ensure you have done the following:',
|
|
462
|
+
checklist: [
|
|
463
|
+
'If you made code changes: Commit and push all changes to your branch',
|
|
464
|
+
'REBASE before PR: git fetch origin && git rebase origin/BASE_BRANCH && git push --force-with-lease',
|
|
465
|
+
'If project uses PR workflow: Create PR targeting correct branch (develop for git-flow, main for github-flow)',
|
|
466
|
+
'If using worktree: Remove worktree IMMEDIATELY after PR is created',
|
|
467
|
+
],
|
|
468
|
+
sequence: 'Commit → Rebase → Push → PR created → complete_task() → remove worktree → next task',
|
|
469
|
+
important: 'DO NOT wait for PR review/merge - validation handles that. Complete task immediately after PR.',
|
|
470
|
+
rebase_warning: 'Always rebase before creating PR to avoid overwriting other agents\' work.',
|
|
471
|
+
};
|
|
472
|
+
// Add worktree cleanup reminder if worktree was used
|
|
473
|
+
if (data.context?.worktree_path) {
|
|
474
|
+
result.worktree_cleanup = {
|
|
475
|
+
required: true,
|
|
476
|
+
path: data.context.worktree_path,
|
|
477
|
+
command: `git worktree remove ${data.context.worktree_path}`,
|
|
478
|
+
timing: 'Remove immediately after PR is created and complete_task is called',
|
|
479
|
+
};
|
|
480
|
+
}
|
|
368
481
|
return { result };
|
|
369
482
|
};
|
|
370
483
|
export const deleteTask = async (args, ctx) => {
|
|
@@ -376,6 +489,51 @@ export const deleteTask = async (args, ctx) => {
|
|
|
376
489
|
}
|
|
377
490
|
return { result: { success: true, deleted_id: task_id } };
|
|
378
491
|
};
|
|
492
|
+
/**
|
|
493
|
+
* Release a task back to pending status.
|
|
494
|
+
* Use when an agent needs to give up a claimed task (context limits, conflicts, user request).
|
|
495
|
+
*/
|
|
496
|
+
export const releaseTask = async (args, ctx) => {
|
|
497
|
+
const { task_id, reason } = parseArgs(args, releaseTaskSchema);
|
|
498
|
+
const api = getApiClient();
|
|
499
|
+
const response = await api.releaseTask(task_id, {
|
|
500
|
+
reason,
|
|
501
|
+
session_id: ctx.session.currentSessionId || undefined,
|
|
502
|
+
});
|
|
503
|
+
if (!response.ok) {
|
|
504
|
+
return { result: { error: response.error || 'Failed to release task' }, isError: true };
|
|
505
|
+
}
|
|
506
|
+
return {
|
|
507
|
+
result: {
|
|
508
|
+
success: true,
|
|
509
|
+
task_id,
|
|
510
|
+
message: response.data?.message || 'Task released and returned to pending status',
|
|
511
|
+
reason: reason || null,
|
|
512
|
+
hint: 'The task is now available for other agents to claim. Call get_next_task() to get a new task.',
|
|
513
|
+
},
|
|
514
|
+
};
|
|
515
|
+
};
|
|
516
|
+
export const cancelTask = async (args, ctx) => {
|
|
517
|
+
const { task_id, cancelled_reason, cancellation_note } = parseArgs(args, cancelTaskSchema);
|
|
518
|
+
const api = getApiClient();
|
|
519
|
+
// Cast cancelled_reason to the expected union type - validation already ensures it's valid
|
|
520
|
+
const response = await api.cancelTask(task_id, {
|
|
521
|
+
cancelled_reason: cancelled_reason,
|
|
522
|
+
cancellation_note,
|
|
523
|
+
session_id: ctx.session.currentSessionId || undefined,
|
|
524
|
+
});
|
|
525
|
+
if (!response.ok) {
|
|
526
|
+
return { result: { error: response.error || 'Failed to cancel task' }, isError: true };
|
|
527
|
+
}
|
|
528
|
+
return {
|
|
529
|
+
result: {
|
|
530
|
+
success: true,
|
|
531
|
+
task_id,
|
|
532
|
+
cancelled_reason: cancelled_reason || null,
|
|
533
|
+
message: response.data?.message || `Task cancelled${cancelled_reason ? ` (${cancelled_reason})` : ''}`,
|
|
534
|
+
},
|
|
535
|
+
};
|
|
536
|
+
};
|
|
379
537
|
export const addTaskReference = async (args, ctx) => {
|
|
380
538
|
const { task_id, url, label } = parseArgs(args, addTaskReferenceSchema);
|
|
381
539
|
const api = getApiClient();
|
|
@@ -740,6 +898,8 @@ export const taskHandlers = {
|
|
|
740
898
|
update_task: updateTask,
|
|
741
899
|
complete_task: completeTask,
|
|
742
900
|
delete_task: deleteTask,
|
|
901
|
+
release_task: releaseTask,
|
|
902
|
+
cancel_task: cancelTask,
|
|
743
903
|
add_task_reference: addTaskReference,
|
|
744
904
|
remove_task_reference: removeTaskReference,
|
|
745
905
|
batch_update_tasks: batchUpdateTasks,
|