@programinglive/dev-workflow-mcp-server 1.8.1 → 1.9.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 (106) hide show
  1. package/.env.example +21 -1
  2. package/AGENTS.md +25 -0
  3. package/README.md +257 -672
  4. package/index.js +5 -4
  5. package/package.json +11 -6
  6. package/scripts/bootstrap-unix.sh +55 -0
  7. package/scripts/bootstrap-windows.ps1 +75 -0
  8. package/scripts/install-husky.js +43 -0
  9. package/scripts/setup-stack.js +436 -0
  10. package/tools/definitions.js +19 -0
  11. package/tools/handler-modules/commit-handlers.js +2 -1
  12. package/tools/handler-modules/index.js +13 -7
  13. package/tools/handler-modules/shared.js +7 -41
  14. package/tools/handler-modules/status-handlers.js +3 -1
  15. package/tools/handler-modules/workflow-handlers.js +26 -8
  16. package/workflow-state.js +10 -0
  17. package/.agent/workflows/dev-workflow-bugfix.md +0 -250
  18. package/.agent/workflows/dev-workflow-feature.md +0 -201
  19. package/.agent/workflows/dev-workflow-full-cycle.md +0 -400
  20. package/.agent/workflows/dev-workflow-refactor.md +0 -238
  21. package/.env.production +0 -16
  22. package/.github/ISSUE_TEMPLATE/bug_report.md +0 -34
  23. package/.github/ISSUE_TEMPLATE/feature_request.md +0 -25
  24. package/.github/pull_request_template.md +0 -22
  25. package/.github/workflows/github-release.yml +0 -131
  26. package/.husky/commit-msg +0 -3
  27. package/.husky/pre-commit +0 -2
  28. package/CHANGELOG.md +0 -1145
  29. package/docs/GCP_DEPLOYMENT.md +0 -298
  30. package/docs/NETLIFY_DEPLOYMENT.md +0 -125
  31. package/docs/PLESK_DEPLOYMENT.md +0 -170
  32. package/docs/PRD.md +0 -86
  33. package/docs/WORKFLOW-BEST-PRACTICES.md +0 -58
  34. package/docs/antigravity/EXAMPLES.md +0 -535
  35. package/docs/antigravity/GETTING-STARTED.md +0 -292
  36. package/docs/antigravity/TROUBLESHOOTING.md +0 -742
  37. package/docs/antigravity/WALKTHROUGH.md +0 -25
  38. package/docs/antigravity/WORKFLOW-TEMPLATES.md +0 -670
  39. package/docs/release-notes/RELEASE_NOTES.md +0 -524
  40. package/docs/web-dashboard.md +0 -175
  41. package/tailwind.config.js +0 -8
  42. package/tests/build.test.js +0 -74
  43. package/tests/db-adapters.test.js +0 -108
  44. package/tests/env-support.test.js +0 -46
  45. package/tests/exec.test.js +0 -35
  46. package/tests/handlers-commit.test.js +0 -174
  47. package/tests/handlers-release.test.js +0 -212
  48. package/tests/handlers-workflow.test.js +0 -68
  49. package/tests/handlers.test.js +0 -1142
  50. package/tests/hello-world.test.js +0 -13
  51. package/tests/mcpRegistry.test.js +0 -65
  52. package/tests/patch-release.test.js +0 -7
  53. package/tests/release-wrapper.test.js +0 -107
  54. package/tests/startup-resilience.test.js +0 -73
  55. package/tests/test-helpers.js +0 -50
  56. package/tests/workflowState.test.js +0 -520
  57. package/web/Dockerfile.api +0 -25
  58. package/web/README.md +0 -133
  59. package/web/__tests__/api/workflow-history.test.js +0 -63
  60. package/web/api-server.js +0 -211
  61. package/web/app/docs/page.tsx +0 -202
  62. package/web/app/favicon.ico +0 -0
  63. package/web/app/globals.css +0 -91
  64. package/web/app/history/page.tsx +0 -220
  65. package/web/app/layout.tsx +0 -39
  66. package/web/app/login/page.tsx +0 -142
  67. package/web/app/page.tsx +0 -13
  68. package/web/components/ApiReference.tsx +0 -20
  69. package/web/components/Features.tsx +0 -61
  70. package/web/components/Footer.tsx +0 -9
  71. package/web/components/Hero.tsx +0 -96
  72. package/web/components/History.tsx +0 -175
  73. package/web/components/Navbar.tsx +0 -208
  74. package/web/components/Terminal.tsx +0 -114
  75. package/web/components/ThemeProvider.tsx +0 -58
  76. package/web/eslint.config.mjs +0 -18
  77. package/web/lib/api-client.ts +0 -123
  78. package/web/lib/auth.js +0 -75
  79. package/web/lib/db.js +0 -131
  80. package/web/lib/docs.js +0 -73
  81. package/web/middleware/auth.js +0 -26
  82. package/web/netlify.toml +0 -8
  83. package/web/next.config.ts +0 -19
  84. package/web/package-lock.json +0 -10911
  85. package/web/package.json +0 -42
  86. package/web/postcss.config.mjs +0 -9
  87. package/web/public/assets/android-chrome-192x192.png +0 -0
  88. package/web/public/assets/android-chrome-512x512.png +0 -0
  89. package/web/public/assets/apple-touch-icon.png +0 -0
  90. package/web/public/assets/favicon-16x16.png +0 -0
  91. package/web/public/assets/favicon-32x32.png +0 -0
  92. package/web/public/assets/favicon.ico +0 -0
  93. package/web/public/assets/favicon.png +0 -0
  94. package/web/public/assets/logo.png +0 -0
  95. package/web/public/assets/og-preview.png +0 -0
  96. package/web/public/assets/site.webmanifest +0 -1
  97. package/web/public/file.svg +0 -1
  98. package/web/public/globe.svg +0 -1
  99. package/web/public/next.svg +0 -1
  100. package/web/public/vercel.svg +0 -1
  101. package/web/public/window.svg +0 -1
  102. package/web/scripts/db-query-history.js +0 -120
  103. package/web/scripts/smoke-test.js +0 -30
  104. package/web/server.js +0 -184
  105. package/web/tailwind.config.ts +0 -20
  106. package/web/tsconfig.json +0 -42
@@ -1,123 +0,0 @@
1
- // API configuration
2
- const API_BASE_URL = 'https://hollywood-andrew-cycle-genre.trycloudflare.com';
3
-
4
- export const apiClient = {
5
- async login(username: string, password: string) {
6
- const response = await fetch(`${API_BASE_URL}/api/auth/login`, {
7
- method: 'POST',
8
- headers: {
9
- 'Content-Type': 'application/json',
10
- },
11
- credentials: 'include',
12
- body: JSON.stringify({ username, password }),
13
- });
14
-
15
- if (!response.ok) {
16
- const error = await response.json();
17
- throw new Error(error.error || 'Login failed');
18
- }
19
-
20
- const data = await response.json();
21
-
22
- // Store JWT token if present
23
- if (data.token) {
24
- localStorage.setItem('token', data.token);
25
- }
26
-
27
- return data;
28
- },
29
-
30
- async logout() {
31
- const response = await fetch(`${API_BASE_URL}/api/auth/logout`, {
32
- method: 'POST',
33
- credentials: 'include',
34
- });
35
-
36
- if (!response.ok) {
37
- throw new Error('Logout failed');
38
- }
39
-
40
- // Clear token
41
- localStorage.removeItem('token');
42
-
43
- return response.json();
44
- },
45
-
46
- async getCurrentUser() {
47
- try {
48
- const token = localStorage.getItem('token');
49
- const headers: Record<string, string> = {};
50
-
51
- if (token) {
52
- headers['Authorization'] = `Bearer ${token}`;
53
- }
54
-
55
- const response = await fetch(`${API_BASE_URL}/api/auth/me`, {
56
- headers,
57
- credentials: 'include',
58
- });
59
-
60
- if (!response.ok) {
61
- return null;
62
- }
63
-
64
- return response.json();
65
- } catch (error) {
66
- return null;
67
- }
68
- },
69
-
70
- async getHistory() {
71
- const token = localStorage.getItem('token');
72
- const headers: Record<string, string> = {
73
- 'Content-Type': 'application/json',
74
- };
75
-
76
- if (token) {
77
- headers['Authorization'] = `Bearer ${token}`;
78
- }
79
-
80
- const response = await fetch(`${API_BASE_URL}/api/history`, {
81
- headers,
82
- credentials: 'include',
83
- });
84
-
85
- if (!response.ok) {
86
- const error = await response.json();
87
- throw new Error(error.error || 'Failed to fetch history');
88
- }
89
-
90
- return response.json();
91
- },
92
-
93
- async createHistory(data: {
94
- task_type: string;
95
- description: string;
96
- commit_message?: string;
97
- tests_passed?: boolean;
98
- documentation_type?: string;
99
- }) {
100
- const token = localStorage.getItem('token');
101
- const headers: Record<string, string> = {
102
- 'Content-Type': 'application/json',
103
- };
104
-
105
- if (token) {
106
- headers['Authorization'] = `Bearer ${token}`;
107
- }
108
-
109
- const response = await fetch(`${API_BASE_URL}/api/history`, {
110
- method: 'POST',
111
- headers,
112
- credentials: 'include',
113
- body: JSON.stringify(data),
114
- });
115
-
116
- if (!response.ok) {
117
- const error = await response.json();
118
- throw new Error(error.error || 'Failed to create history entry');
119
- }
120
-
121
- return response.json();
122
- },
123
- };
package/web/lib/auth.js DELETED
@@ -1,75 +0,0 @@
1
- import bcrypt from 'bcrypt';
2
- import pg from 'pg';
3
-
4
- const SALT_ROUNDS = 10;
5
-
6
- /**
7
- * Hash a password using bcrypt
8
- */
9
- export async function hashPassword(password) {
10
- return bcrypt.hash(password, SALT_ROUNDS);
11
- }
12
-
13
- /**
14
- * Verify a password against a hash
15
- */
16
- export async function verifyPassword(password, hash) {
17
- return bcrypt.compare(password, hash);
18
- }
19
-
20
- /**
21
- * Create a new user
22
- */
23
- export async function createUser(pool, username, password, email = null) {
24
- const passwordHash = await hashPassword(password);
25
-
26
- const result = await pool.query(
27
- 'INSERT INTO users (username, password_hash, email) VALUES ($1, $2, $3) RETURNING id, username, email, created_at',
28
- [username, passwordHash, email]
29
- );
30
-
31
- return result.rows[0];
32
- }
33
-
34
- /**
35
- * Authenticate a user
36
- */
37
- export async function authenticateUser(pool, username, password) {
38
- const result = await pool.query(
39
- 'SELECT id, username, password_hash, email FROM users WHERE username = $1',
40
- [username]
41
- );
42
-
43
- if (result.rows.length === 0) {
44
- return null;
45
- }
46
-
47
- const user = result.rows[0];
48
- const isValid = await verifyPassword(password, user.password_hash);
49
-
50
- if (!isValid) {
51
- return null;
52
- }
53
-
54
- // Update last login
55
- await pool.query(
56
- 'UPDATE users SET last_login = CURRENT_TIMESTAMP WHERE id = $1',
57
- [user.id]
58
- );
59
-
60
- // Return user without password hash
61
- const { password_hash, ...userWithoutPassword } = user;
62
- return userWithoutPassword;
63
- }
64
-
65
- /**
66
- * Get user by ID
67
- */
68
- export async function getUserById(pool, id) {
69
- const result = await pool.query(
70
- 'SELECT id, username, email, created_at, last_login FROM users WHERE id = $1',
71
- [id]
72
- );
73
-
74
- return result.rows[0] || null;
75
- }
package/web/lib/db.js DELETED
@@ -1,131 +0,0 @@
1
- import Database from "better-sqlite3";
2
- import { existsSync } from "fs";
3
- import path from "path";
4
-
5
- const DB_FILENAME = "dev-workflow.db";
6
- const FREQUENCY_FORMATS = {
7
- daily: "%Y-%m-%d",
8
- monthly: "%Y-%m",
9
- yearly: "%Y",
10
- };
11
-
12
- function getDbPath() {
13
- const initCwd = process.env.INIT_CWD || process.cwd();
14
- let projectRoot = initCwd;
15
-
16
- // Look for .state folder specifically.
17
- while (projectRoot !== path.dirname(projectRoot)) {
18
- if (existsSync(path.join(projectRoot, ".state"))) return path.join(projectRoot, ".state", DB_FILENAME);
19
- projectRoot = path.dirname(projectRoot);
20
- }
21
-
22
- // Fallback to relative path from this file if possible
23
- return path.join(process.cwd(), "..", ".state", DB_FILENAME);
24
- }
25
-
26
- let db;
27
- export function getDb() {
28
- if (!db) {
29
- const dbPath = getDbPath();
30
- db = new Database(dbPath);
31
- db.pragma("journal_mode = WAL");
32
- migrate();
33
- }
34
- return db;
35
- }
36
-
37
- function migrate() {
38
- const createHistory = `
39
- CREATE TABLE IF NOT EXISTS workflow_history (
40
- id INTEGER PRIMARY KEY AUTOINCREMENT,
41
- user_id TEXT NOT NULL,
42
- task_description TEXT NOT NULL,
43
- task_type TEXT NOT NULL,
44
- commit_message TEXT,
45
- timestamp DATETIME DEFAULT CURRENT_TIMESTAMP,
46
- forced INTEGER DEFAULT 0,
47
- force_reason TEXT,
48
- dropped INTEGER DEFAULT 0,
49
- drop_reason TEXT,
50
- UNIQUE(user_id, timestamp, task_description)
51
- )
52
- `;
53
- const createSummary = `
54
- CREATE TABLE IF NOT EXISTS project_summary (
55
- user_id TEXT PRIMARY KEY,
56
- total_tasks INTEGER DEFAULT 0,
57
- task_types TEXT DEFAULT '{}',
58
- last_active DATETIME,
59
- updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
60
- recent_tasks TEXT DEFAULT '[]'
61
- )
62
- `;
63
- const idxHistory = `CREATE INDEX IF NOT EXISTS idx_workflow_history_user ON workflow_history(user_id)`;
64
- const idxSummary = `CREATE INDEX IF NOT EXISTS idx_project_summary_user ON project_summary(user_id)`;
65
-
66
- db.exec(createHistory);
67
- db.exec(createSummary);
68
- db.exec(idxHistory);
69
- db.exec(idxSummary);
70
- }
71
-
72
- export function getSummaryForUser(userId) {
73
- const stmt = getDb().prepare(`
74
- SELECT * FROM project_summary WHERE user_id = ?
75
- `);
76
- const row = stmt.get(userId);
77
- if (!row) return null;
78
- return {
79
- totalTasks: row.total_tasks,
80
- taskTypes: JSON.parse(row.task_types),
81
- lastActive: row.last_active,
82
- recentTasks: JSON.parse(row.recent_tasks),
83
- updatedAt: row.updated_at,
84
- };
85
- }
86
-
87
- function buildHistoryFilters({ startDate, endDate }) {
88
- const clauses = [];
89
- const params = [];
90
- if (startDate) {
91
- clauses.push("DATE(timestamp) >= DATE(?)");
92
- params.push(startDate);
93
- }
94
- if (endDate) {
95
- clauses.push("DATE(timestamp) <= DATE(?)");
96
- params.push(endDate);
97
- }
98
- return { clauses, params };
99
- }
100
-
101
- export function getHistoryForUser(userId, options = {}) {
102
- const { page = 1, pageSize = 20, startDate, endDate } = options;
103
- const sanitizedPage = Math.max(1, Number.isFinite(Number(page)) ? Math.floor(Number(page)) : 1);
104
- const sanitizedPageSize = Math.min(100, Math.max(1, Number.isFinite(Number(pageSize)) ? Math.floor(Number(pageSize)) : 20));
105
- const offset = (sanitizedPage - 1) * sanitizedPageSize;
106
-
107
- const { clauses, params } = buildHistoryFilters({ startDate, endDate });
108
- const where = ["user_id = ?", ...clauses].join(" AND ");
109
- const dbInstance = getDb();
110
-
111
- const countStmt = dbInstance.prepare(`SELECT COUNT(*) as total FROM workflow_history WHERE ${where}`);
112
- const total = countStmt.get(userId, ...params).total;
113
-
114
- const rowsStmt = dbInstance.prepare(`
115
- SELECT *
116
- FROM workflow_history
117
- WHERE ${where}
118
- ORDER BY timestamp DESC
119
- LIMIT ?
120
- OFFSET ?
121
- `);
122
- const entries = rowsStmt.all(userId, ...params, sanitizedPageSize, offset);
123
-
124
- return {
125
- data: entries,
126
- total,
127
- page: sanitizedPage,
128
- pageSize: sanitizedPageSize,
129
- totalPages: Math.ceil(total / sanitizedPageSize)
130
- };
131
- }
package/web/lib/docs.js DELETED
@@ -1,73 +0,0 @@
1
- import { readFile, access } from "fs/promises";
2
- import { join, dirname } from "path";
3
- import { existsSync } from "fs";
4
- import path from "path";
5
-
6
- function getProjectRoot() {
7
- const initCwd = process.env.INIT_CWD || process.cwd();
8
- let projectRoot = initCwd;
9
- while (projectRoot !== path.dirname(projectRoot)) {
10
- if (existsSync(path.join(projectRoot, "package.json")) && existsSync(path.join(projectRoot, "docs"))) return projectRoot;
11
- projectRoot = path.dirname(projectRoot);
12
- }
13
- return path.join(process.cwd(), "..");
14
- }
15
-
16
- const projectRoot = getProjectRoot();
17
-
18
- const docSources = {
19
- "README.md": {
20
- markdown: join(projectRoot, "README.md"),
21
- html: join(projectRoot, "dist", "docs", "README.html"),
22
- },
23
- "web-dashboard.md": {
24
- markdown: join(projectRoot, "docs", "web-dashboard.md"),
25
- html: join(projectRoot, "dist", "docs", "web-dashboard.html"),
26
- },
27
- "GETTING-STARTED.md": {
28
- markdown: join(projectRoot, "docs", "antigravity", "GETTING-STARTED.md"),
29
- html: join(projectRoot, "dist", "docs", "GETTING-STARTED.html"),
30
- },
31
- "EXAMPLES.md": {
32
- markdown: join(projectRoot, "docs", "antigravity", "EXAMPLES.md"),
33
- html: join(projectRoot, "dist", "docs", "EXAMPLES.html"),
34
- },
35
- "WORKFLOW-TEMPLATES.md": {
36
- markdown: join(projectRoot, "docs", "antigravity", "WORKFLOW-TEMPLATES.md"),
37
- html: join(projectRoot, "dist", "docs", "WORKFLOW-TEMPLATES.html"),
38
- },
39
- "TROUBLESHOOTING.md": {
40
- markdown: join(projectRoot, "docs", "antigravity", "TROUBLESHOOTING.md"),
41
- html: join(projectRoot, "dist", "docs", "TROUBLESHOOTING.html"),
42
- },
43
- "RELEASE_NOTES.md": {
44
- markdown: join(projectRoot, "docs", "release-notes", "RELEASE_NOTES.md"),
45
- html: join(projectRoot, "dist", "docs", "RELEASE_NOTES.html"),
46
- },
47
- };
48
-
49
- export async function resolveDocContent(docKey) {
50
- const entry = docSources[docKey];
51
- if (!entry) {
52
- return { exists: false };
53
- }
54
-
55
- // Try HTML first, fall back to Markdown in development or if HTML missing.
56
- const candidates = [entry.html, entry.markdown];
57
- for (const candidate of candidates) {
58
- try {
59
- await access(candidate);
60
- const content = await readFile(candidate, "utf-8");
61
- const format = candidate.endsWith(".html") ? "html" : "markdown";
62
- return { exists: true, content, format };
63
- } catch {
64
- // try next candidate
65
- }
66
- }
67
-
68
- return { exists: false };
69
- }
70
-
71
- export function getAvailableDocs() {
72
- return Object.keys(docSources);
73
- }
@@ -1,26 +0,0 @@
1
- /**
2
- * Middleware to require authentication
3
- */
4
- export function requireAuth(req, res, next) {
5
- if (req.session && req.session.userId) {
6
- return next();
7
- }
8
-
9
- // If it's an API request, return 401
10
- if (req.path.startsWith('/api/')) {
11
- return res.status(401).json({ error: 'Authentication required' });
12
- }
13
-
14
- // Otherwise redirect to login
15
- res.redirect('/login');
16
- }
17
-
18
- /**
19
- * Middleware to redirect if already authenticated
20
- */
21
- export function redirectIfAuthenticated(req, res, next) {
22
- if (req.session && req.session.userId) {
23
- return res.redirect('/');
24
- }
25
- next();
26
- }
package/web/netlify.toml DELETED
@@ -1,8 +0,0 @@
1
- [build]
2
- command = "npm run build"
3
- publish = "out"
4
-
5
- [[redirects]]
6
- from = "/*"
7
- to = "/index.html"
8
- status = 200
@@ -1,19 +0,0 @@
1
- import type { NextConfig } from "next";
2
-
3
- const nextConfig: NextConfig = {
4
- // Static export for Netlify deployment
5
- output: 'export',
6
- trailingSlash: true,
7
- // Set turbopack root to silence multi-lockfile warning
8
- turbopack: {
9
- root: __dirname,
10
- },
11
- // Disable image optimization for static export
12
- images: {
13
- unoptimized: true,
14
- },
15
- // Exclude native modules from bundling
16
- serverExternalPackages: ['pg'],
17
- };
18
-
19
- export default nextConfig;