@vibescope/mcp-server 0.0.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 +98 -0
- package/dist/cli.d.ts +34 -0
- package/dist/cli.js +356 -0
- package/dist/cli.test.d.ts +1 -0
- package/dist/cli.test.js +367 -0
- package/dist/handlers/__test-utils__.d.ts +72 -0
- package/dist/handlers/__test-utils__.js +176 -0
- package/dist/handlers/blockers.d.ts +18 -0
- package/dist/handlers/blockers.js +81 -0
- package/dist/handlers/bodies-of-work.d.ts +34 -0
- package/dist/handlers/bodies-of-work.js +614 -0
- package/dist/handlers/checkouts.d.ts +37 -0
- package/dist/handlers/checkouts.js +377 -0
- package/dist/handlers/cost.d.ts +39 -0
- package/dist/handlers/cost.js +247 -0
- package/dist/handlers/decisions.d.ts +16 -0
- package/dist/handlers/decisions.js +64 -0
- package/dist/handlers/deployment.d.ts +36 -0
- package/dist/handlers/deployment.js +1062 -0
- package/dist/handlers/discovery.d.ts +14 -0
- package/dist/handlers/discovery.js +870 -0
- package/dist/handlers/fallback.d.ts +18 -0
- package/dist/handlers/fallback.js +216 -0
- package/dist/handlers/findings.d.ts +18 -0
- package/dist/handlers/findings.js +110 -0
- package/dist/handlers/git-issues.d.ts +22 -0
- package/dist/handlers/git-issues.js +247 -0
- package/dist/handlers/ideas.d.ts +19 -0
- package/dist/handlers/ideas.js +188 -0
- package/dist/handlers/index.d.ts +29 -0
- package/dist/handlers/index.js +65 -0
- package/dist/handlers/knowledge-query.d.ts +22 -0
- package/dist/handlers/knowledge-query.js +253 -0
- package/dist/handlers/knowledge.d.ts +12 -0
- package/dist/handlers/knowledge.js +108 -0
- package/dist/handlers/milestones.d.ts +20 -0
- package/dist/handlers/milestones.js +179 -0
- package/dist/handlers/organizations.d.ts +36 -0
- package/dist/handlers/organizations.js +428 -0
- package/dist/handlers/progress.d.ts +14 -0
- package/dist/handlers/progress.js +149 -0
- package/dist/handlers/project.d.ts +20 -0
- package/dist/handlers/project.js +278 -0
- package/dist/handlers/requests.d.ts +16 -0
- package/dist/handlers/requests.js +131 -0
- package/dist/handlers/roles.d.ts +30 -0
- package/dist/handlers/roles.js +281 -0
- package/dist/handlers/session.d.ts +20 -0
- package/dist/handlers/session.js +791 -0
- package/dist/handlers/tasks.d.ts +52 -0
- package/dist/handlers/tasks.js +1111 -0
- package/dist/handlers/tasks.test.d.ts +1 -0
- package/dist/handlers/tasks.test.js +431 -0
- package/dist/handlers/types.d.ts +94 -0
- package/dist/handlers/types.js +1 -0
- package/dist/handlers/validation.d.ts +16 -0
- package/dist/handlers/validation.js +188 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2707 -0
- package/dist/knowledge.d.ts +6 -0
- package/dist/knowledge.js +121 -0
- package/dist/tools.d.ts +2 -0
- package/dist/tools.js +2498 -0
- package/dist/utils.d.ts +149 -0
- package/dist/utils.js +317 -0
- package/dist/utils.test.d.ts +1 -0
- package/dist/utils.test.js +532 -0
- package/dist/validators.d.ts +35 -0
- package/dist/validators.js +111 -0
- package/dist/validators.test.d.ts +1 -0
- package/dist/validators.test.js +176 -0
- package/package.json +44 -0
- package/src/cli.test.ts +442 -0
- package/src/cli.ts +439 -0
- package/src/handlers/__test-utils__.ts +217 -0
- package/src/handlers/blockers.test.ts +390 -0
- package/src/handlers/blockers.ts +110 -0
- package/src/handlers/bodies-of-work.test.ts +1276 -0
- package/src/handlers/bodies-of-work.ts +783 -0
- package/src/handlers/cost.test.ts +436 -0
- package/src/handlers/cost.ts +322 -0
- package/src/handlers/decisions.test.ts +401 -0
- package/src/handlers/decisions.ts +86 -0
- package/src/handlers/deployment.test.ts +516 -0
- package/src/handlers/deployment.ts +1289 -0
- package/src/handlers/discovery.test.ts +254 -0
- package/src/handlers/discovery.ts +969 -0
- package/src/handlers/fallback.test.ts +687 -0
- package/src/handlers/fallback.ts +260 -0
- package/src/handlers/findings.test.ts +565 -0
- package/src/handlers/findings.ts +153 -0
- package/src/handlers/ideas.test.ts +753 -0
- package/src/handlers/ideas.ts +247 -0
- package/src/handlers/index.ts +69 -0
- package/src/handlers/milestones.test.ts +584 -0
- package/src/handlers/milestones.ts +217 -0
- package/src/handlers/organizations.test.ts +997 -0
- package/src/handlers/organizations.ts +550 -0
- package/src/handlers/progress.test.ts +369 -0
- package/src/handlers/progress.ts +188 -0
- package/src/handlers/project.test.ts +562 -0
- package/src/handlers/project.ts +352 -0
- package/src/handlers/requests.test.ts +531 -0
- package/src/handlers/requests.ts +150 -0
- package/src/handlers/session.test.ts +459 -0
- package/src/handlers/session.ts +912 -0
- package/src/handlers/tasks.test.ts +602 -0
- package/src/handlers/tasks.ts +1393 -0
- package/src/handlers/types.ts +88 -0
- package/src/handlers/validation.test.ts +880 -0
- package/src/handlers/validation.ts +223 -0
- package/src/index.ts +3205 -0
- package/src/knowledge.ts +132 -0
- package/src/tmpclaude-0078-cwd +1 -0
- package/src/tmpclaude-0ee1-cwd +1 -0
- package/src/tmpclaude-2dd5-cwd +1 -0
- package/src/tmpclaude-344c-cwd +1 -0
- package/src/tmpclaude-3860-cwd +1 -0
- package/src/tmpclaude-4b63-cwd +1 -0
- package/src/tmpclaude-5c73-cwd +1 -0
- package/src/tmpclaude-5ee3-cwd +1 -0
- package/src/tmpclaude-6795-cwd +1 -0
- package/src/tmpclaude-709e-cwd +1 -0
- package/src/tmpclaude-9839-cwd +1 -0
- package/src/tmpclaude-d829-cwd +1 -0
- package/src/tmpclaude-e072-cwd +1 -0
- package/src/tmpclaude-f6ee-cwd +1 -0
- package/src/utils.test.ts +681 -0
- package/src/utils.ts +375 -0
- package/src/validators.test.ts +223 -0
- package/src/validators.ts +122 -0
- package/tmpclaude-0439-cwd +1 -0
- package/tmpclaude-132f-cwd +1 -0
- package/tmpclaude-15bb-cwd +1 -0
- package/tmpclaude-165a-cwd +1 -0
- package/tmpclaude-1ba9-cwd +1 -0
- package/tmpclaude-21a3-cwd +1 -0
- package/tmpclaude-2a38-cwd +1 -0
- package/tmpclaude-2adf-cwd +1 -0
- package/tmpclaude-2f56-cwd +1 -0
- package/tmpclaude-3626-cwd +1 -0
- package/tmpclaude-3727-cwd +1 -0
- package/tmpclaude-40bc-cwd +1 -0
- package/tmpclaude-436f-cwd +1 -0
- package/tmpclaude-4783-cwd +1 -0
- package/tmpclaude-4b6d-cwd +1 -0
- package/tmpclaude-4ba4-cwd +1 -0
- package/tmpclaude-51e6-cwd +1 -0
- package/tmpclaude-5ecf-cwd +1 -0
- package/tmpclaude-6f97-cwd +1 -0
- package/tmpclaude-7fb2-cwd +1 -0
- package/tmpclaude-825c-cwd +1 -0
- package/tmpclaude-8baf-cwd +1 -0
- package/tmpclaude-8d9f-cwd +1 -0
- package/tmpclaude-975c-cwd +1 -0
- package/tmpclaude-9983-cwd +1 -0
- package/tmpclaude-a045-cwd +1 -0
- package/tmpclaude-ac4a-cwd +1 -0
- package/tmpclaude-b593-cwd +1 -0
- package/tmpclaude-b891-cwd +1 -0
- package/tmpclaude-c032-cwd +1 -0
- package/tmpclaude-cf43-cwd +1 -0
- package/tmpclaude-d040-cwd +1 -0
- package/tmpclaude-dcdd-cwd +1 -0
- package/tmpclaude-dcee-cwd +1 -0
- package/tmpclaude-e16b-cwd +1 -0
- package/tmpclaude-ecd2-cwd +1 -0
- package/tmpclaude-f48d-cwd +1 -0
- package/tsconfig.json +16 -0
- package/vitest.config.ts +13 -0
package/README.md
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
# @vibescope/mcp-server
|
|
2
|
+
|
|
3
|
+
MCP server for Vibescope - enables AI agents to track project progress, tasks, blockers, and decisions.
|
|
4
|
+
|
|
5
|
+
## Installation Options
|
|
6
|
+
|
|
7
|
+
### Option 1: From GitHub Package Registry (Recommended)
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
# Authenticate with GitHub (one-time)
|
|
11
|
+
npm login --registry=https://npm.pkg.github.com
|
|
12
|
+
|
|
13
|
+
# Install globally
|
|
14
|
+
npm install -g @vibescope/mcp-server --registry=https://npm.pkg.github.com
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
### Option 2: From Source (Local Development)
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
# Clone the repo
|
|
21
|
+
git clone https://github.com/Nonatomic/Vibescope.git
|
|
22
|
+
cd Vibescope
|
|
23
|
+
|
|
24
|
+
# Install dependencies and build
|
|
25
|
+
pnpm install
|
|
26
|
+
pnpm build
|
|
27
|
+
|
|
28
|
+
# Link globally
|
|
29
|
+
cd packages/mcp-server
|
|
30
|
+
npm link
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
### Option 3: Direct Path (No Installation)
|
|
34
|
+
|
|
35
|
+
Point your MCP config directly to the built file:
|
|
36
|
+
|
|
37
|
+
```json
|
|
38
|
+
{
|
|
39
|
+
"mcpServers": {
|
|
40
|
+
"vibescope": {
|
|
41
|
+
"command": "node",
|
|
42
|
+
"args": ["/path/to/Vibescope/packages/mcp-server/dist/index.js"],
|
|
43
|
+
"env": {
|
|
44
|
+
"VIBESCOPE_API_KEY": "your-api-key"
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Configuration
|
|
52
|
+
|
|
53
|
+
Add to your Claude Code MCP config (`~/.claude/claude_desktop_config.json` or project `.claude/settings.local.json`):
|
|
54
|
+
|
|
55
|
+
```json
|
|
56
|
+
{
|
|
57
|
+
"mcpServers": {
|
|
58
|
+
"vibescope": {
|
|
59
|
+
"command": "vibescope-mcp",
|
|
60
|
+
"env": {
|
|
61
|
+
"VIBESCOPE_API_KEY": "your-api-key"
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## Getting an API Key
|
|
69
|
+
|
|
70
|
+
1. Sign up at [vibescope.dev](https://vibescope.dev)
|
|
71
|
+
2. Go to Settings
|
|
72
|
+
3. Generate an API key
|
|
73
|
+
|
|
74
|
+
## Available Tools
|
|
75
|
+
|
|
76
|
+
The MCP server provides these tools for AI agents:
|
|
77
|
+
|
|
78
|
+
| Tool | Description |
|
|
79
|
+
|------|-------------|
|
|
80
|
+
| `start_work_session` | Initialize a work session for a project |
|
|
81
|
+
| `get_project_context` | Get full project context including tasks, blockers, and user updates |
|
|
82
|
+
| `get_tasks` | List tasks, optionally filtered by status |
|
|
83
|
+
| `get_next_task` | Get the highest priority pending task |
|
|
84
|
+
| `add_task` | Create a new task |
|
|
85
|
+
| `update_task` | Update task status, progress, or details |
|
|
86
|
+
| `complete_task` | Mark a task as completed |
|
|
87
|
+
| `log_progress` | Log a progress update |
|
|
88
|
+
| `add_blocker` | Flag a blocker needing human input |
|
|
89
|
+
| `resolve_blocker` | Mark a blocker as resolved |
|
|
90
|
+
| `log_decision` | Record an architectural or technical decision |
|
|
91
|
+
| `add_idea` | Record an improvement idea |
|
|
92
|
+
| `create_project` | Create a new project |
|
|
93
|
+
| `update_project` | Update project details |
|
|
94
|
+
|
|
95
|
+
## Rate Limits
|
|
96
|
+
|
|
97
|
+
- 60 requests per minute per API key
|
|
98
|
+
- Warning shown when quota drops below 10 requests
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Vibescope CLI - Enforcement verification tool
|
|
4
|
+
*
|
|
5
|
+
* Used by Claude Code Stop hook to verify agent compliance with Vibescope tracking.
|
|
6
|
+
* Exit codes:
|
|
7
|
+
* 0 = Compliant (allow exit)
|
|
8
|
+
* 1 = Non-compliant (block exit, loop back)
|
|
9
|
+
* 2 = Error (allow exit with warning)
|
|
10
|
+
*/
|
|
11
|
+
export interface AuthContext {
|
|
12
|
+
userId: string;
|
|
13
|
+
apiKeyId: string;
|
|
14
|
+
}
|
|
15
|
+
export interface VerificationResult {
|
|
16
|
+
status: 'compliant' | 'non_compliant' | 'no_session' | 'error';
|
|
17
|
+
reason: string;
|
|
18
|
+
continuation_prompt?: string;
|
|
19
|
+
details?: {
|
|
20
|
+
session_started: boolean;
|
|
21
|
+
project_id: string | null;
|
|
22
|
+
project_name: string | null;
|
|
23
|
+
git_url: string | null;
|
|
24
|
+
in_progress_tasks: number;
|
|
25
|
+
tasks_completed_this_session: number;
|
|
26
|
+
progress_logs_this_session: number;
|
|
27
|
+
blockers_logged_this_session: number;
|
|
28
|
+
session_duration_minutes: number | null;
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
export declare function detectGitUrl(): string | null;
|
|
32
|
+
export declare function hashApiKey(key: string): string;
|
|
33
|
+
export declare function validateApiKey(supabase: any, apiKey: string): Promise<AuthContext | null>;
|
|
34
|
+
export declare function verify(gitUrl?: string, projectId?: string): Promise<VerificationResult>;
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,356 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Vibescope CLI - Enforcement verification tool
|
|
4
|
+
*
|
|
5
|
+
* Used by Claude Code Stop hook to verify agent compliance with Vibescope tracking.
|
|
6
|
+
* Exit codes:
|
|
7
|
+
* 0 = Compliant (allow exit)
|
|
8
|
+
* 1 = Non-compliant (block exit, loop back)
|
|
9
|
+
* 2 = Error (allow exit with warning)
|
|
10
|
+
*/
|
|
11
|
+
import { createClient } from '@supabase/supabase-js';
|
|
12
|
+
import { createHash } from 'crypto';
|
|
13
|
+
import { execSync } from 'child_process';
|
|
14
|
+
import { normalizeGitUrl } from './utils.js';
|
|
15
|
+
// ============================================================================
|
|
16
|
+
// Configuration
|
|
17
|
+
// ============================================================================
|
|
18
|
+
const SUPABASE_URL = process.env.SUPABASE_URL || process.env.PUBLIC_SUPABASE_URL;
|
|
19
|
+
const SUPABASE_SERVICE_KEY = process.env.SUPABASE_SERVICE_KEY;
|
|
20
|
+
const API_KEY = process.env.VIBESCOPE_API_KEY;
|
|
21
|
+
// ============================================================================
|
|
22
|
+
// Git URL Detection
|
|
23
|
+
// ============================================================================
|
|
24
|
+
export function detectGitUrl() {
|
|
25
|
+
try {
|
|
26
|
+
const url = execSync('git config --get remote.origin.url', {
|
|
27
|
+
encoding: 'utf8',
|
|
28
|
+
timeout: 5000,
|
|
29
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
30
|
+
}).trim();
|
|
31
|
+
// Normalize: remove .git suffix, convert SSH to HTTPS format
|
|
32
|
+
return normalizeGitUrl(url);
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
// ============================================================================
|
|
39
|
+
// Authentication (reused from index.ts)
|
|
40
|
+
// ============================================================================
|
|
41
|
+
export function hashApiKey(key) {
|
|
42
|
+
return createHash('sha256').update(key).digest('hex');
|
|
43
|
+
}
|
|
44
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
45
|
+
export async function validateApiKey(supabase, apiKey) {
|
|
46
|
+
const keyHash = hashApiKey(apiKey);
|
|
47
|
+
const { data, error } = await supabase
|
|
48
|
+
.from('api_keys')
|
|
49
|
+
.select('id, user_id')
|
|
50
|
+
.eq('key_hash', keyHash)
|
|
51
|
+
.single();
|
|
52
|
+
if (error || !data) {
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
// Cast to expected shape since we're using untyped client
|
|
56
|
+
const row = data;
|
|
57
|
+
return {
|
|
58
|
+
userId: row.user_id,
|
|
59
|
+
apiKeyId: row.id,
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
// ============================================================================
|
|
63
|
+
// Verification Logic
|
|
64
|
+
// ============================================================================
|
|
65
|
+
export async function verify(gitUrl, projectId) {
|
|
66
|
+
// Check environment
|
|
67
|
+
if (!SUPABASE_URL || !SUPABASE_SERVICE_KEY) {
|
|
68
|
+
return {
|
|
69
|
+
status: 'error',
|
|
70
|
+
reason: 'Missing SUPABASE_URL or SUPABASE_SERVICE_KEY environment variables',
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
if (!API_KEY) {
|
|
74
|
+
return {
|
|
75
|
+
status: 'error',
|
|
76
|
+
reason: 'VIBESCOPE_API_KEY environment variable not set',
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
const supabase = createClient(SUPABASE_URL, SUPABASE_SERVICE_KEY);
|
|
80
|
+
// Validate API key
|
|
81
|
+
const auth = await validateApiKey(supabase, API_KEY);
|
|
82
|
+
if (!auth) {
|
|
83
|
+
return {
|
|
84
|
+
status: 'error',
|
|
85
|
+
reason: 'Invalid VIBESCOPE_API_KEY',
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
// Auto-detect git URL if not provided
|
|
89
|
+
if (!gitUrl && !projectId) {
|
|
90
|
+
gitUrl = detectGitUrl() || undefined;
|
|
91
|
+
}
|
|
92
|
+
// Find project
|
|
93
|
+
let projectQuery = supabase
|
|
94
|
+
.from('projects')
|
|
95
|
+
.select('id, name, git_url')
|
|
96
|
+
.eq('user_id', auth.userId);
|
|
97
|
+
if (projectId) {
|
|
98
|
+
projectQuery = projectQuery.eq('id', projectId);
|
|
99
|
+
}
|
|
100
|
+
else if (gitUrl) {
|
|
101
|
+
projectQuery = projectQuery.eq('git_url', gitUrl);
|
|
102
|
+
}
|
|
103
|
+
else {
|
|
104
|
+
return {
|
|
105
|
+
status: 'no_session',
|
|
106
|
+
reason: 'Could not detect git URL and no project_id provided',
|
|
107
|
+
continuation_prompt: 'Could not detect which project you are working on. Please call start_work_session(git_url: "...") with your repository URL.',
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
const { data: project, error: projectError } = await projectQuery.single();
|
|
111
|
+
if (projectError || !project) {
|
|
112
|
+
// Project not found - this is OK, might be an untracked repo
|
|
113
|
+
return {
|
|
114
|
+
status: 'compliant',
|
|
115
|
+
reason: `No Vibescope project found for ${gitUrl || projectId}. Untracked repository - exit allowed.`,
|
|
116
|
+
details: {
|
|
117
|
+
session_started: false,
|
|
118
|
+
project_id: null,
|
|
119
|
+
project_name: null,
|
|
120
|
+
git_url: gitUrl || null,
|
|
121
|
+
in_progress_tasks: 0,
|
|
122
|
+
tasks_completed_this_session: 0,
|
|
123
|
+
progress_logs_this_session: 0,
|
|
124
|
+
blockers_logged_this_session: 0,
|
|
125
|
+
session_duration_minutes: null,
|
|
126
|
+
},
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
// Check for agent session
|
|
130
|
+
const { data: session } = await supabase
|
|
131
|
+
.from('agent_sessions')
|
|
132
|
+
.select('id, last_synced_at, created_at')
|
|
133
|
+
.eq('api_key_id', auth.apiKeyId)
|
|
134
|
+
.eq('project_id', project.id)
|
|
135
|
+
.single();
|
|
136
|
+
if (!session) {
|
|
137
|
+
return {
|
|
138
|
+
status: 'non_compliant',
|
|
139
|
+
reason: 'No Vibescope session started for this project',
|
|
140
|
+
continuation_prompt: `[VIBESCOPE ENFORCEMENT] You have not started a Vibescope work session.
|
|
141
|
+
|
|
142
|
+
BEFORE you can exit, you MUST call:
|
|
143
|
+
start_work_session(git_url: "${project.git_url || project.id}")
|
|
144
|
+
|
|
145
|
+
This registers your session and gives you project context.`,
|
|
146
|
+
details: {
|
|
147
|
+
session_started: false,
|
|
148
|
+
project_id: project.id,
|
|
149
|
+
project_name: project.name,
|
|
150
|
+
git_url: project.git_url,
|
|
151
|
+
in_progress_tasks: 0,
|
|
152
|
+
tasks_completed_this_session: 0,
|
|
153
|
+
progress_logs_this_session: 0,
|
|
154
|
+
blockers_logged_this_session: 0,
|
|
155
|
+
session_duration_minutes: null,
|
|
156
|
+
},
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
const sessionStartTime = new Date(session.created_at);
|
|
160
|
+
const lastSyncTime = new Date(session.last_synced_at);
|
|
161
|
+
const now = new Date();
|
|
162
|
+
const sessionDurationMinutes = Math.round((now.getTime() - sessionStartTime.getTime()) / 60000);
|
|
163
|
+
// Grace period: if session is less than 1 minute, allow exit
|
|
164
|
+
if (sessionDurationMinutes < 1) {
|
|
165
|
+
return {
|
|
166
|
+
status: 'compliant',
|
|
167
|
+
reason: 'Very short session (< 1 minute) - exit allowed',
|
|
168
|
+
details: {
|
|
169
|
+
session_started: true,
|
|
170
|
+
project_id: project.id,
|
|
171
|
+
project_name: project.name,
|
|
172
|
+
git_url: project.git_url,
|
|
173
|
+
in_progress_tasks: 0,
|
|
174
|
+
tasks_completed_this_session: 0,
|
|
175
|
+
progress_logs_this_session: 0,
|
|
176
|
+
blockers_logged_this_session: 0,
|
|
177
|
+
session_duration_minutes: sessionDurationMinutes,
|
|
178
|
+
},
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
// Check for in-progress tasks
|
|
182
|
+
const { data: inProgressTasks } = await supabase
|
|
183
|
+
.from('tasks')
|
|
184
|
+
.select('id, title, progress_percentage')
|
|
185
|
+
.eq('project_id', project.id)
|
|
186
|
+
.eq('status', 'in_progress');
|
|
187
|
+
const inProgressCount = inProgressTasks?.length || 0;
|
|
188
|
+
if (inProgressCount > 0) {
|
|
189
|
+
const taskList = inProgressTasks
|
|
190
|
+
.map((t) => ` - ${t.title} (${t.progress_percentage}% complete)`)
|
|
191
|
+
.join('\n');
|
|
192
|
+
return {
|
|
193
|
+
status: 'non_compliant',
|
|
194
|
+
reason: `You have ${inProgressCount} task(s) still in_progress`,
|
|
195
|
+
continuation_prompt: `[VIBESCOPE ENFORCEMENT] You have ${inProgressCount} task(s) still marked as in_progress:
|
|
196
|
+
|
|
197
|
+
${taskList}
|
|
198
|
+
|
|
199
|
+
You MUST either:
|
|
200
|
+
1. Complete them: complete_task(task_id: "...", summary: "...")
|
|
201
|
+
2. Log why you're stopping: log_progress(project_id: "${project.id}", summary: "Stopping because...")`,
|
|
202
|
+
details: {
|
|
203
|
+
session_started: true,
|
|
204
|
+
project_id: project.id,
|
|
205
|
+
project_name: project.name,
|
|
206
|
+
git_url: project.git_url,
|
|
207
|
+
in_progress_tasks: inProgressCount,
|
|
208
|
+
tasks_completed_this_session: 0,
|
|
209
|
+
progress_logs_this_session: 0,
|
|
210
|
+
blockers_logged_this_session: 0,
|
|
211
|
+
session_duration_minutes: sessionDurationMinutes,
|
|
212
|
+
},
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
// Check for activity this session
|
|
216
|
+
const sessionStartIso = sessionStartTime.toISOString();
|
|
217
|
+
const [completedResult, progressResult, blockerResult] = await Promise.all([
|
|
218
|
+
// Tasks completed after session start
|
|
219
|
+
supabase
|
|
220
|
+
.from('tasks')
|
|
221
|
+
.select('id', { count: 'exact' })
|
|
222
|
+
.eq('project_id', project.id)
|
|
223
|
+
.eq('status', 'completed')
|
|
224
|
+
.gte('completed_at', sessionStartIso),
|
|
225
|
+
// Progress logs by agent after session start
|
|
226
|
+
supabase
|
|
227
|
+
.from('progress_logs')
|
|
228
|
+
.select('id', { count: 'exact' })
|
|
229
|
+
.eq('project_id', project.id)
|
|
230
|
+
.eq('created_by', 'agent')
|
|
231
|
+
.gte('created_at', sessionStartIso),
|
|
232
|
+
// Blockers logged by agent after session start
|
|
233
|
+
supabase
|
|
234
|
+
.from('blockers')
|
|
235
|
+
.select('id', { count: 'exact' })
|
|
236
|
+
.eq('project_id', project.id)
|
|
237
|
+
.eq('created_by', 'agent')
|
|
238
|
+
.gte('created_at', sessionStartIso),
|
|
239
|
+
]);
|
|
240
|
+
const tasksCompleted = completedResult.count || 0;
|
|
241
|
+
const progressLogsCount = progressResult.count || 0;
|
|
242
|
+
const blockersLogged = blockerResult.count || 0;
|
|
243
|
+
// Check if any tracked work was done
|
|
244
|
+
const anyWorkTracked = tasksCompleted > 0 || progressLogsCount > 0 || blockersLogged > 0;
|
|
245
|
+
if (!anyWorkTracked && sessionDurationMinutes >= 5) {
|
|
246
|
+
return {
|
|
247
|
+
status: 'non_compliant',
|
|
248
|
+
reason: `Session active for ${sessionDurationMinutes} minutes but no work was tracked`,
|
|
249
|
+
continuation_prompt: `[VIBESCOPE ENFORCEMENT] Your session has been active for ${sessionDurationMinutes} minutes but no work was tracked.
|
|
250
|
+
|
|
251
|
+
The human is watching the dashboard and will see an empty session.
|
|
252
|
+
|
|
253
|
+
You MUST either:
|
|
254
|
+
1. Pick a task and work on it: get_next_task(project_id: "${project.id}")
|
|
255
|
+
2. Log why you did nothing: log_progress(project_id: "${project.id}", summary: "No work done because...")`,
|
|
256
|
+
details: {
|
|
257
|
+
session_started: true,
|
|
258
|
+
project_id: project.id,
|
|
259
|
+
project_name: project.name,
|
|
260
|
+
git_url: project.git_url,
|
|
261
|
+
in_progress_tasks: 0,
|
|
262
|
+
tasks_completed_this_session: tasksCompleted,
|
|
263
|
+
progress_logs_this_session: progressLogsCount,
|
|
264
|
+
blockers_logged_this_session: blockersLogged,
|
|
265
|
+
session_duration_minutes: sessionDurationMinutes,
|
|
266
|
+
},
|
|
267
|
+
};
|
|
268
|
+
}
|
|
269
|
+
// All checks passed - compliant!
|
|
270
|
+
return {
|
|
271
|
+
status: 'compliant',
|
|
272
|
+
reason: 'All tracked work completed properly',
|
|
273
|
+
details: {
|
|
274
|
+
session_started: true,
|
|
275
|
+
project_id: project.id,
|
|
276
|
+
project_name: project.name,
|
|
277
|
+
git_url: project.git_url,
|
|
278
|
+
in_progress_tasks: 0,
|
|
279
|
+
tasks_completed_this_session: tasksCompleted,
|
|
280
|
+
progress_logs_this_session: progressLogsCount,
|
|
281
|
+
blockers_logged_this_session: blockersLogged,
|
|
282
|
+
session_duration_minutes: sessionDurationMinutes,
|
|
283
|
+
},
|
|
284
|
+
};
|
|
285
|
+
}
|
|
286
|
+
// ============================================================================
|
|
287
|
+
// CLI Entry Point
|
|
288
|
+
// ============================================================================
|
|
289
|
+
async function main() {
|
|
290
|
+
const args = process.argv.slice(2);
|
|
291
|
+
const command = args[0];
|
|
292
|
+
if (command === 'verify') {
|
|
293
|
+
// Parse --git-url and --project-id flags
|
|
294
|
+
let gitUrl;
|
|
295
|
+
let projectId;
|
|
296
|
+
for (let i = 1; i < args.length; i++) {
|
|
297
|
+
if (args[i] === '--git-url' && args[i + 1]) {
|
|
298
|
+
gitUrl = args[++i];
|
|
299
|
+
}
|
|
300
|
+
else if (args[i] === '--project-id' && args[i + 1]) {
|
|
301
|
+
projectId = args[++i];
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
const result = await verify(gitUrl, projectId);
|
|
305
|
+
console.log(JSON.stringify(result, null, 2));
|
|
306
|
+
// Exit codes: 0=compliant, 1=non-compliant, 2=error
|
|
307
|
+
if (result.status === 'compliant') {
|
|
308
|
+
process.exit(0);
|
|
309
|
+
}
|
|
310
|
+
else if (result.status === 'error') {
|
|
311
|
+
process.exit(2);
|
|
312
|
+
}
|
|
313
|
+
else {
|
|
314
|
+
process.exit(1);
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
else if (command === 'help' || command === '--help' || command === '-h') {
|
|
318
|
+
console.log(`
|
|
319
|
+
Vibescope CLI - Enforcement verification tool
|
|
320
|
+
|
|
321
|
+
Usage:
|
|
322
|
+
vibescope-cli verify [options] Check Vibescope compliance before exit
|
|
323
|
+
|
|
324
|
+
Options:
|
|
325
|
+
--git-url <url> Git repository URL (auto-detected if not provided)
|
|
326
|
+
--project-id <id> Vibescope project UUID
|
|
327
|
+
|
|
328
|
+
Exit Codes:
|
|
329
|
+
0 Compliant - agent can exit
|
|
330
|
+
1 Non-compliant - agent should continue work
|
|
331
|
+
2 Error - allow exit with warning
|
|
332
|
+
|
|
333
|
+
Environment Variables:
|
|
334
|
+
VIBESCOPE_API_KEY Required - Your Vibescope API key
|
|
335
|
+
SUPABASE_URL Required - Supabase project URL
|
|
336
|
+
SUPABASE_SERVICE_KEY Required - Supabase service role key
|
|
337
|
+
`);
|
|
338
|
+
process.exit(0);
|
|
339
|
+
}
|
|
340
|
+
else {
|
|
341
|
+
console.error('Usage: vibescope-cli verify [--git-url <url>] [--project-id <id>]');
|
|
342
|
+
console.error(' vibescope-cli --help');
|
|
343
|
+
process.exit(2);
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
// Only run main when executed directly (not when imported for testing)
|
|
347
|
+
const isMainModule = import.meta.url === `file://${process.argv[1]?.replace(/\\/g, '/')}`;
|
|
348
|
+
if (isMainModule || process.argv[1]?.endsWith('cli.js')) {
|
|
349
|
+
main().catch((err) => {
|
|
350
|
+
console.error(JSON.stringify({
|
|
351
|
+
status: 'error',
|
|
352
|
+
reason: err instanceof Error ? err.message : 'Unknown error',
|
|
353
|
+
}));
|
|
354
|
+
process.exit(2);
|
|
355
|
+
});
|
|
356
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|