@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.
Files changed (170) hide show
  1. package/README.md +98 -0
  2. package/dist/cli.d.ts +34 -0
  3. package/dist/cli.js +356 -0
  4. package/dist/cli.test.d.ts +1 -0
  5. package/dist/cli.test.js +367 -0
  6. package/dist/handlers/__test-utils__.d.ts +72 -0
  7. package/dist/handlers/__test-utils__.js +176 -0
  8. package/dist/handlers/blockers.d.ts +18 -0
  9. package/dist/handlers/blockers.js +81 -0
  10. package/dist/handlers/bodies-of-work.d.ts +34 -0
  11. package/dist/handlers/bodies-of-work.js +614 -0
  12. package/dist/handlers/checkouts.d.ts +37 -0
  13. package/dist/handlers/checkouts.js +377 -0
  14. package/dist/handlers/cost.d.ts +39 -0
  15. package/dist/handlers/cost.js +247 -0
  16. package/dist/handlers/decisions.d.ts +16 -0
  17. package/dist/handlers/decisions.js +64 -0
  18. package/dist/handlers/deployment.d.ts +36 -0
  19. package/dist/handlers/deployment.js +1062 -0
  20. package/dist/handlers/discovery.d.ts +14 -0
  21. package/dist/handlers/discovery.js +870 -0
  22. package/dist/handlers/fallback.d.ts +18 -0
  23. package/dist/handlers/fallback.js +216 -0
  24. package/dist/handlers/findings.d.ts +18 -0
  25. package/dist/handlers/findings.js +110 -0
  26. package/dist/handlers/git-issues.d.ts +22 -0
  27. package/dist/handlers/git-issues.js +247 -0
  28. package/dist/handlers/ideas.d.ts +19 -0
  29. package/dist/handlers/ideas.js +188 -0
  30. package/dist/handlers/index.d.ts +29 -0
  31. package/dist/handlers/index.js +65 -0
  32. package/dist/handlers/knowledge-query.d.ts +22 -0
  33. package/dist/handlers/knowledge-query.js +253 -0
  34. package/dist/handlers/knowledge.d.ts +12 -0
  35. package/dist/handlers/knowledge.js +108 -0
  36. package/dist/handlers/milestones.d.ts +20 -0
  37. package/dist/handlers/milestones.js +179 -0
  38. package/dist/handlers/organizations.d.ts +36 -0
  39. package/dist/handlers/organizations.js +428 -0
  40. package/dist/handlers/progress.d.ts +14 -0
  41. package/dist/handlers/progress.js +149 -0
  42. package/dist/handlers/project.d.ts +20 -0
  43. package/dist/handlers/project.js +278 -0
  44. package/dist/handlers/requests.d.ts +16 -0
  45. package/dist/handlers/requests.js +131 -0
  46. package/dist/handlers/roles.d.ts +30 -0
  47. package/dist/handlers/roles.js +281 -0
  48. package/dist/handlers/session.d.ts +20 -0
  49. package/dist/handlers/session.js +791 -0
  50. package/dist/handlers/tasks.d.ts +52 -0
  51. package/dist/handlers/tasks.js +1111 -0
  52. package/dist/handlers/tasks.test.d.ts +1 -0
  53. package/dist/handlers/tasks.test.js +431 -0
  54. package/dist/handlers/types.d.ts +94 -0
  55. package/dist/handlers/types.js +1 -0
  56. package/dist/handlers/validation.d.ts +16 -0
  57. package/dist/handlers/validation.js +188 -0
  58. package/dist/index.d.ts +2 -0
  59. package/dist/index.js +2707 -0
  60. package/dist/knowledge.d.ts +6 -0
  61. package/dist/knowledge.js +121 -0
  62. package/dist/tools.d.ts +2 -0
  63. package/dist/tools.js +2498 -0
  64. package/dist/utils.d.ts +149 -0
  65. package/dist/utils.js +317 -0
  66. package/dist/utils.test.d.ts +1 -0
  67. package/dist/utils.test.js +532 -0
  68. package/dist/validators.d.ts +35 -0
  69. package/dist/validators.js +111 -0
  70. package/dist/validators.test.d.ts +1 -0
  71. package/dist/validators.test.js +176 -0
  72. package/package.json +44 -0
  73. package/src/cli.test.ts +442 -0
  74. package/src/cli.ts +439 -0
  75. package/src/handlers/__test-utils__.ts +217 -0
  76. package/src/handlers/blockers.test.ts +390 -0
  77. package/src/handlers/blockers.ts +110 -0
  78. package/src/handlers/bodies-of-work.test.ts +1276 -0
  79. package/src/handlers/bodies-of-work.ts +783 -0
  80. package/src/handlers/cost.test.ts +436 -0
  81. package/src/handlers/cost.ts +322 -0
  82. package/src/handlers/decisions.test.ts +401 -0
  83. package/src/handlers/decisions.ts +86 -0
  84. package/src/handlers/deployment.test.ts +516 -0
  85. package/src/handlers/deployment.ts +1289 -0
  86. package/src/handlers/discovery.test.ts +254 -0
  87. package/src/handlers/discovery.ts +969 -0
  88. package/src/handlers/fallback.test.ts +687 -0
  89. package/src/handlers/fallback.ts +260 -0
  90. package/src/handlers/findings.test.ts +565 -0
  91. package/src/handlers/findings.ts +153 -0
  92. package/src/handlers/ideas.test.ts +753 -0
  93. package/src/handlers/ideas.ts +247 -0
  94. package/src/handlers/index.ts +69 -0
  95. package/src/handlers/milestones.test.ts +584 -0
  96. package/src/handlers/milestones.ts +217 -0
  97. package/src/handlers/organizations.test.ts +997 -0
  98. package/src/handlers/organizations.ts +550 -0
  99. package/src/handlers/progress.test.ts +369 -0
  100. package/src/handlers/progress.ts +188 -0
  101. package/src/handlers/project.test.ts +562 -0
  102. package/src/handlers/project.ts +352 -0
  103. package/src/handlers/requests.test.ts +531 -0
  104. package/src/handlers/requests.ts +150 -0
  105. package/src/handlers/session.test.ts +459 -0
  106. package/src/handlers/session.ts +912 -0
  107. package/src/handlers/tasks.test.ts +602 -0
  108. package/src/handlers/tasks.ts +1393 -0
  109. package/src/handlers/types.ts +88 -0
  110. package/src/handlers/validation.test.ts +880 -0
  111. package/src/handlers/validation.ts +223 -0
  112. package/src/index.ts +3205 -0
  113. package/src/knowledge.ts +132 -0
  114. package/src/tmpclaude-0078-cwd +1 -0
  115. package/src/tmpclaude-0ee1-cwd +1 -0
  116. package/src/tmpclaude-2dd5-cwd +1 -0
  117. package/src/tmpclaude-344c-cwd +1 -0
  118. package/src/tmpclaude-3860-cwd +1 -0
  119. package/src/tmpclaude-4b63-cwd +1 -0
  120. package/src/tmpclaude-5c73-cwd +1 -0
  121. package/src/tmpclaude-5ee3-cwd +1 -0
  122. package/src/tmpclaude-6795-cwd +1 -0
  123. package/src/tmpclaude-709e-cwd +1 -0
  124. package/src/tmpclaude-9839-cwd +1 -0
  125. package/src/tmpclaude-d829-cwd +1 -0
  126. package/src/tmpclaude-e072-cwd +1 -0
  127. package/src/tmpclaude-f6ee-cwd +1 -0
  128. package/src/utils.test.ts +681 -0
  129. package/src/utils.ts +375 -0
  130. package/src/validators.test.ts +223 -0
  131. package/src/validators.ts +122 -0
  132. package/tmpclaude-0439-cwd +1 -0
  133. package/tmpclaude-132f-cwd +1 -0
  134. package/tmpclaude-15bb-cwd +1 -0
  135. package/tmpclaude-165a-cwd +1 -0
  136. package/tmpclaude-1ba9-cwd +1 -0
  137. package/tmpclaude-21a3-cwd +1 -0
  138. package/tmpclaude-2a38-cwd +1 -0
  139. package/tmpclaude-2adf-cwd +1 -0
  140. package/tmpclaude-2f56-cwd +1 -0
  141. package/tmpclaude-3626-cwd +1 -0
  142. package/tmpclaude-3727-cwd +1 -0
  143. package/tmpclaude-40bc-cwd +1 -0
  144. package/tmpclaude-436f-cwd +1 -0
  145. package/tmpclaude-4783-cwd +1 -0
  146. package/tmpclaude-4b6d-cwd +1 -0
  147. package/tmpclaude-4ba4-cwd +1 -0
  148. package/tmpclaude-51e6-cwd +1 -0
  149. package/tmpclaude-5ecf-cwd +1 -0
  150. package/tmpclaude-6f97-cwd +1 -0
  151. package/tmpclaude-7fb2-cwd +1 -0
  152. package/tmpclaude-825c-cwd +1 -0
  153. package/tmpclaude-8baf-cwd +1 -0
  154. package/tmpclaude-8d9f-cwd +1 -0
  155. package/tmpclaude-975c-cwd +1 -0
  156. package/tmpclaude-9983-cwd +1 -0
  157. package/tmpclaude-a045-cwd +1 -0
  158. package/tmpclaude-ac4a-cwd +1 -0
  159. package/tmpclaude-b593-cwd +1 -0
  160. package/tmpclaude-b891-cwd +1 -0
  161. package/tmpclaude-c032-cwd +1 -0
  162. package/tmpclaude-cf43-cwd +1 -0
  163. package/tmpclaude-d040-cwd +1 -0
  164. package/tmpclaude-dcdd-cwd +1 -0
  165. package/tmpclaude-dcee-cwd +1 -0
  166. package/tmpclaude-e16b-cwd +1 -0
  167. package/tmpclaude-ecd2-cwd +1 -0
  168. package/tmpclaude-f48d-cwd +1 -0
  169. package/tsconfig.json +16 -0
  170. 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 {};