@programinglive/dev-workflow-mcp-server 1.8.1 → 1.10.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/.env.example +21 -1
- package/AGENTS.md +25 -0
- package/README.md +372 -649
- package/factory-client.js +554 -0
- package/git-facade.js +35 -0
- package/index.js +9 -34
- package/package.json +21 -20
- package/scripts/bootstrap-unix.sh +55 -0
- package/scripts/bootstrap-windows.ps1 +75 -0
- package/scripts/install-husky.js +43 -0
- package/scripts/setup-stack.js +436 -0
- package/scripts/setup-test-dbs.sh +61 -0
- package/scripts/test-db-adapters.sh +71 -0
- package/scripts/verify-bundle.mjs +92 -0
- package/tools/definitions.js +143 -0
- package/tools/handler-modules/commit-handlers.js +2 -1
- package/tools/handler-modules/factory-handlers.js +276 -0
- package/tools/handler-modules/index.js +24 -7
- package/tools/handler-modules/shared.js +7 -41
- package/tools/handler-modules/status-handlers.js +25 -3
- package/tools/handler-modules/workflow-handlers.js +57 -10
- package/vite.config.js +6 -0
- package/workflow-state.js +176 -2
- package/.agent/workflows/dev-workflow-bugfix.md +0 -250
- package/.agent/workflows/dev-workflow-feature.md +0 -201
- package/.agent/workflows/dev-workflow-full-cycle.md +0 -400
- package/.agent/workflows/dev-workflow-refactor.md +0 -238
- package/.env.production +0 -16
- package/.github/ISSUE_TEMPLATE/bug_report.md +0 -34
- package/.github/ISSUE_TEMPLATE/feature_request.md +0 -25
- package/.github/pull_request_template.md +0 -22
- package/.github/workflows/github-release.yml +0 -131
- package/.husky/commit-msg +0 -3
- package/.husky/pre-commit +0 -2
- package/CHANGELOG.md +0 -1145
- package/docs/GCP_DEPLOYMENT.md +0 -298
- package/docs/NETLIFY_DEPLOYMENT.md +0 -125
- package/docs/PLESK_DEPLOYMENT.md +0 -170
- package/docs/PRD.md +0 -86
- package/docs/WORKFLOW-BEST-PRACTICES.md +0 -58
- package/docs/antigravity/EXAMPLES.md +0 -535
- package/docs/antigravity/GETTING-STARTED.md +0 -292
- package/docs/antigravity/TROUBLESHOOTING.md +0 -742
- package/docs/antigravity/WALKTHROUGH.md +0 -25
- package/docs/antigravity/WORKFLOW-TEMPLATES.md +0 -670
- package/docs/release-notes/RELEASE_NOTES.md +0 -524
- package/docs/web-dashboard.md +0 -175
- package/scripts/test-auth-flow.js +0 -75
- package/tailwind.config.js +0 -8
- package/tests/build.test.js +0 -74
- package/tests/db-adapters.test.js +0 -108
- package/tests/env-support.test.js +0 -46
- package/tests/exec.test.js +0 -35
- package/tests/handlers-commit.test.js +0 -174
- package/tests/handlers-release.test.js +0 -212
- package/tests/handlers-workflow.test.js +0 -68
- package/tests/handlers.test.js +0 -1142
- package/tests/hello-world.test.js +0 -13
- package/tests/mcpRegistry.test.js +0 -65
- package/tests/patch-release.test.js +0 -7
- package/tests/release-wrapper.test.js +0 -107
- package/tests/startup-resilience.test.js +0 -73
- package/tests/test-helpers.js +0 -50
- package/tests/workflowState.test.js +0 -520
- package/web/Dockerfile.api +0 -25
- package/web/README.md +0 -133
- package/web/__tests__/api/workflow-history.test.js +0 -63
- package/web/api-server.js +0 -211
- package/web/app/docs/page.tsx +0 -202
- package/web/app/favicon.ico +0 -0
- package/web/app/globals.css +0 -91
- package/web/app/history/page.tsx +0 -220
- package/web/app/layout.tsx +0 -39
- package/web/app/login/page.tsx +0 -142
- package/web/app/page.tsx +0 -13
- package/web/components/ApiReference.tsx +0 -20
- package/web/components/Features.tsx +0 -61
- package/web/components/Footer.tsx +0 -9
- package/web/components/Hero.tsx +0 -96
- package/web/components/History.tsx +0 -175
- package/web/components/Navbar.tsx +0 -208
- package/web/components/Terminal.tsx +0 -114
- package/web/components/ThemeProvider.tsx +0 -58
- package/web/eslint.config.mjs +0 -18
- package/web/lib/api-client.ts +0 -123
- package/web/lib/auth.js +0 -75
- package/web/lib/db.js +0 -131
- package/web/lib/docs.js +0 -73
- package/web/middleware/auth.js +0 -26
- package/web/netlify.toml +0 -8
- package/web/next.config.ts +0 -19
- package/web/package-lock.json +0 -10911
- package/web/package.json +0 -42
- package/web/postcss.config.mjs +0 -9
- package/web/public/assets/android-chrome-192x192.png +0 -0
- package/web/public/assets/android-chrome-512x512.png +0 -0
- package/web/public/assets/apple-touch-icon.png +0 -0
- package/web/public/assets/favicon-16x16.png +0 -0
- package/web/public/assets/favicon-32x32.png +0 -0
- package/web/public/assets/favicon.ico +0 -0
- package/web/public/assets/favicon.png +0 -0
- package/web/public/assets/logo.png +0 -0
- package/web/public/assets/og-preview.png +0 -0
- package/web/public/assets/site.webmanifest +0 -1
- package/web/public/file.svg +0 -1
- package/web/public/globe.svg +0 -1
- package/web/public/next.svg +0 -1
- package/web/public/vercel.svg +0 -1
- package/web/public/window.svg +0 -1
- package/web/scripts/db-query-history.js +0 -120
- package/web/scripts/smoke-test.js +0 -30
- package/web/server.js +0 -184
- package/web/tailwind.config.ts +0 -20
- package/web/tsconfig.json +0 -42
package/web/app/history/page.tsx
DELETED
|
@@ -1,220 +0,0 @@
|
|
|
1
|
-
'use client';
|
|
2
|
-
|
|
3
|
-
import { useEffect, useState, useMemo } from 'react';
|
|
4
|
-
import { useRouter } from 'next/navigation';
|
|
5
|
-
import { apiClient } from '@/lib/api-client';
|
|
6
|
-
import {
|
|
7
|
-
CheckCircle,
|
|
8
|
-
XCircle,
|
|
9
|
-
AlertTriangle,
|
|
10
|
-
GitCommit,
|
|
11
|
-
FileText,
|
|
12
|
-
Activity,
|
|
13
|
-
Search,
|
|
14
|
-
Filter,
|
|
15
|
-
Calendar,
|
|
16
|
-
BarChart3
|
|
17
|
-
} from 'lucide-react';
|
|
18
|
-
|
|
19
|
-
interface WorkflowHistoryItem {
|
|
20
|
-
id: number;
|
|
21
|
-
task_type: string;
|
|
22
|
-
description: string;
|
|
23
|
-
commit_message: string;
|
|
24
|
-
completed_at: string;
|
|
25
|
-
tests_passed: boolean;
|
|
26
|
-
documentation_type: string;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
export default function HistoryPage() {
|
|
30
|
-
const [isAuthenticated, setIsAuthenticated] = useState(false);
|
|
31
|
-
const [loading, setLoading] = useState(true);
|
|
32
|
-
const [history, setHistory] = useState<WorkflowHistoryItem[]>([]);
|
|
33
|
-
const [searchQuery, setSearchQuery] = useState('');
|
|
34
|
-
const [typeFilter, setTypeFilter] = useState<string>('all');
|
|
35
|
-
const router = useRouter();
|
|
36
|
-
|
|
37
|
-
useEffect(() => {
|
|
38
|
-
const loadData = async () => {
|
|
39
|
-
try {
|
|
40
|
-
// Verify auth
|
|
41
|
-
const user = await apiClient.getCurrentUser();
|
|
42
|
-
if (!user) {
|
|
43
|
-
throw new Error('Not authenticated');
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
setIsAuthenticated(true);
|
|
47
|
-
// Sync auth state
|
|
48
|
-
localStorage.setItem('isLoggedIn', 'true');
|
|
49
|
-
window.dispatchEvent(new Event('authUpdate'));
|
|
50
|
-
|
|
51
|
-
// Fetch history
|
|
52
|
-
const result = await apiClient.getHistory();
|
|
53
|
-
if (result?.history) {
|
|
54
|
-
setHistory(result.history);
|
|
55
|
-
}
|
|
56
|
-
} catch (error) {
|
|
57
|
-
console.error('History load error:', error);
|
|
58
|
-
router.push('/login');
|
|
59
|
-
} finally {
|
|
60
|
-
setLoading(false);
|
|
61
|
-
}
|
|
62
|
-
};
|
|
63
|
-
|
|
64
|
-
loadData();
|
|
65
|
-
}, [router]);
|
|
66
|
-
|
|
67
|
-
const stats = useMemo(() => {
|
|
68
|
-
const total = history.length;
|
|
69
|
-
const features = history.filter(h => h.task_type === 'feature').length;
|
|
70
|
-
const bugs = history.filter(h => h.task_type === 'bugfix').length;
|
|
71
|
-
const passedTests = history.filter(h => h.tests_passed).length;
|
|
72
|
-
const successRate = total > 0 ? Math.round((passedTests / total) * 100) : 0;
|
|
73
|
-
|
|
74
|
-
return { total, features, bugs, successRate };
|
|
75
|
-
}, [history]);
|
|
76
|
-
|
|
77
|
-
const filteredHistory = useMemo(() => {
|
|
78
|
-
return history.filter(item => {
|
|
79
|
-
const matchesSearch = item.description.toLowerCase().includes(searchQuery.toLowerCase()) ||
|
|
80
|
-
item.commit_message.toLowerCase().includes(searchQuery.toLowerCase());
|
|
81
|
-
const matchesType = typeFilter === 'all' || item.task_type === typeFilter;
|
|
82
|
-
return matchesSearch && matchesType;
|
|
83
|
-
});
|
|
84
|
-
}, [history, searchQuery, typeFilter]);
|
|
85
|
-
|
|
86
|
-
if (loading) {
|
|
87
|
-
return (
|
|
88
|
-
<div className="min-h-screen flex items-center justify-center bg-gray-50 dark:bg-[#0a0a0f]">
|
|
89
|
-
<div className="animate-spin rounded-full h-12 w-12 border-t-2 border-b-2 border-indigo-500"></div>
|
|
90
|
-
</div>
|
|
91
|
-
);
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
if (!isAuthenticated) return null;
|
|
95
|
-
|
|
96
|
-
return (
|
|
97
|
-
<div className="min-h-screen pt-24 pb-12 px-4 sm:px-6 lg:px-8 bg-gray-50 dark:bg-[#0a0a0f] transition-colors duration-300">
|
|
98
|
-
<div className="max-w-7xl mx-auto space-y-8">
|
|
99
|
-
{/* Header */}
|
|
100
|
-
<div>
|
|
101
|
-
<h1 className="text-3xl sm:text-4xl font-bold text-gray-900 dark:text-white mb-2">Workflow Insights</h1>
|
|
102
|
-
<p className="text-gray-600 dark:text-gray-400">Track and analyze your development velocity and quality.</p>
|
|
103
|
-
</div>
|
|
104
|
-
|
|
105
|
-
{/* Stats Grid */}
|
|
106
|
-
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4">
|
|
107
|
-
<StatCard icon={<Activity className="w-5 h-5 text-indigo-500" />} label="Total Tasks" value={stats.total} />
|
|
108
|
-
<StatCard icon={<CheckCircle className="w-5 h-5 text-green-500" />} label="Success Rate" value={`${stats.successRate}%`} />
|
|
109
|
-
<StatCard icon={<BarChart3 className="w-5 h-5 text-blue-500" />} label="Features Built" value={stats.features} />
|
|
110
|
-
<StatCard icon={<AlertTriangle className="w-5 h-5 text-red-500" />} label="Bugs Fixed" value={stats.bugs} />
|
|
111
|
-
</div>
|
|
112
|
-
|
|
113
|
-
{/* Controls */}
|
|
114
|
-
<div className="flex flex-col sm:flex-row gap-4 items-center justify-between bg-white dark:bg-white/5 p-4 rounded-xl border border-gray-200 dark:border-white/10">
|
|
115
|
-
<div className="relative w-full sm:w-96">
|
|
116
|
-
<Search className="absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4 text-gray-400" />
|
|
117
|
-
<input
|
|
118
|
-
type="text"
|
|
119
|
-
placeholder="Search tasks, commits..."
|
|
120
|
-
value={searchQuery}
|
|
121
|
-
onChange={(e) => setSearchQuery(e.target.value)}
|
|
122
|
-
className="w-full pl-10 pr-4 py-2 bg-gray-100 dark:bg-black/20 border border-gray-200 dark:border-white/10 rounded-lg focus:outline-none focus:ring-2 focus:ring-indigo-500 text-gray-900 dark:text-white placeholder-gray-500"
|
|
123
|
-
/>
|
|
124
|
-
</div>
|
|
125
|
-
<div className="flex gap-2 w-full sm:w-auto">
|
|
126
|
-
<select
|
|
127
|
-
value={typeFilter}
|
|
128
|
-
onChange={(e) => setTypeFilter(e.target.value)}
|
|
129
|
-
className="px-4 py-2 bg-gray-100 dark:bg-black/20 border border-gray-200 dark:border-white/10 rounded-lg focus:outline-none focus:ring-2 focus:ring-indigo-500 text-gray-900 dark:text-white"
|
|
130
|
-
>
|
|
131
|
-
<option value="all">All Types</option>
|
|
132
|
-
<option value="feature">Features</option>
|
|
133
|
-
<option value="bugfix">Bug Fixes</option>
|
|
134
|
-
<option value="refactor">Refactors</option>
|
|
135
|
-
</select>
|
|
136
|
-
</div>
|
|
137
|
-
</div>
|
|
138
|
-
|
|
139
|
-
{/* Timeline */}
|
|
140
|
-
<div className="space-y-6 relative before:absolute before:inset-0 before:ml-5 before:-translate-x-px md:before:mx-auto md:before:translate-x-0 before:h-full before:w-0.5 before:bg-gradient-to-b before:from-transparent before:via-gray-300 before:dark:via-white/20 before:to-transparent">
|
|
141
|
-
{filteredHistory.length === 0 ? (
|
|
142
|
-
<div className="text-center py-12">
|
|
143
|
-
<p className="text-gray-500 dark:text-gray-400">No workflow history found.</p>
|
|
144
|
-
</div>
|
|
145
|
-
) : (
|
|
146
|
-
filteredHistory.map((item, index) => (
|
|
147
|
-
<div key={item.id} className="relative flex items-center justify-between md:justify-normal md:odd:flex-row-reverse group is-active">
|
|
148
|
-
{/* Icon */}
|
|
149
|
-
<div className="flex items-center justify-center w-10 h-10 rounded-full border border-white dark:border-[#0a0a0f] bg-gray-100 dark:bg-white/10 shadow shrink-0 md:order-1 md:group-odd:-translate-x-1/2 md:group-even:translate-x-1/2 z-10">
|
|
150
|
-
{getTaskIcon(item.task_type)}
|
|
151
|
-
</div>
|
|
152
|
-
|
|
153
|
-
{/* Card */}
|
|
154
|
-
<div className="w-[calc(100%-4rem)] md:w-[calc(50%-2.5rem)] bg-white dark:bg-white/5 p-6 rounded-xl border border-gray-200 dark:border-white/10 shadow-sm hover:shadow-md transition-all duration-300">
|
|
155
|
-
<div className="flex items-center justify-between mb-3">
|
|
156
|
-
<span className={`px-2.5 py-0.5 rounded-full text-xs font-medium uppercase tracking-wide ${getTypeColor(item.task_type)}`}>
|
|
157
|
-
{item.task_type}
|
|
158
|
-
</span>
|
|
159
|
-
<time className="text-xs text-gray-500 dark:text-gray-400 font-mono">
|
|
160
|
-
{new Date(item.completed_at).toLocaleDateString()}
|
|
161
|
-
</time>
|
|
162
|
-
</div>
|
|
163
|
-
<h3 className="text-lg font-bold text-gray-900 dark:text-white mb-2">{item.description}</h3>
|
|
164
|
-
<div className="flex items-center gap-2 text-sm text-gray-600 dark:text-gray-400 mb-4 bg-gray-50 dark:bg-black/20 p-2 rounded-lg break-all font-mono">
|
|
165
|
-
<GitCommit className="w-4 h-4 shrink-0" />
|
|
166
|
-
{item.commit_message}
|
|
167
|
-
</div>
|
|
168
|
-
<div className="flex flex-wrap items-center gap-3 pt-3 border-t border-gray-100 dark:border-white/10">
|
|
169
|
-
<div className={`flex items-center gap-1.5 text-xs font-medium ${item.tests_passed ? 'text-green-600 dark:text-green-400' : 'text-yellow-600 dark:text-yellow-400'}`}>
|
|
170
|
-
{item.tests_passed ? <CheckCircle className="w-3.5 h-3.5" /> : <AlertTriangle className="w-3.5 h-3.5" />}
|
|
171
|
-
{item.tests_passed ? 'Tests Passed' : 'Tests Skipped'}
|
|
172
|
-
</div>
|
|
173
|
-
{item.documentation_type && (
|
|
174
|
-
<div className="flex items-center gap-1.5 text-xs font-medium text-blue-600 dark:text-blue-400">
|
|
175
|
-
<FileText className="w-3.5 h-3.5" />
|
|
176
|
-
{item.documentation_type}
|
|
177
|
-
</div>
|
|
178
|
-
)}
|
|
179
|
-
</div>
|
|
180
|
-
</div>
|
|
181
|
-
</div>
|
|
182
|
-
))
|
|
183
|
-
)}
|
|
184
|
-
</div>
|
|
185
|
-
</div>
|
|
186
|
-
</div>
|
|
187
|
-
);
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
function StatCard({ icon, label, value }: { icon: React.ReactNode, label: string, value: string | number }) {
|
|
191
|
-
return (
|
|
192
|
-
<div className="bg-white dark:bg-white/5 p-6 rounded-xl border border-gray-200 dark:border-white/10 shadow-sm">
|
|
193
|
-
<div className="flex items-center gap-4">
|
|
194
|
-
<div className="p-3 bg-gray-50 dark:bg-white/5 rounded-lg">
|
|
195
|
-
{icon}
|
|
196
|
-
</div>
|
|
197
|
-
<div>
|
|
198
|
-
<p className="text-sm font-medium text-gray-500 dark:text-gray-400">{label}</p>
|
|
199
|
-
<p className="text-2xl font-bold text-gray-900 dark:text-white">{value}</p>
|
|
200
|
-
</div>
|
|
201
|
-
</div>
|
|
202
|
-
</div>
|
|
203
|
-
);
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
function getTaskIcon(type: string) {
|
|
207
|
-
switch (type) {
|
|
208
|
-
case 'feature': return <CheckCircle className="w-5 h-5 text-blue-500" />;
|
|
209
|
-
case 'bugfix': return <XCircle className="w-5 h-5 text-red-500" />;
|
|
210
|
-
default: return <Activity className="w-5 h-5 text-gray-500" />;
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
function getTypeColor(type: string) {
|
|
215
|
-
switch (type) {
|
|
216
|
-
case 'feature': return 'bg-blue-100 text-blue-800 dark:bg-blue-900/30 dark:text-blue-300';
|
|
217
|
-
case 'bugfix': return 'bg-red-100 text-red-800 dark:bg-red-900/30 dark:text-red-300';
|
|
218
|
-
default: return 'bg-gray-100 text-gray-800 dark:bg-gray-800/50 dark:text-gray-300';
|
|
219
|
-
}
|
|
220
|
-
}
|
package/web/app/layout.tsx
DELETED
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
import type { Metadata } from "next";
|
|
2
|
-
import "./globals.css";
|
|
3
|
-
import Navbar from "@/components/Navbar";
|
|
4
|
-
import Footer from "@/components/Footer";
|
|
5
|
-
import { ThemeProvider } from "@/components/ThemeProvider";
|
|
6
|
-
|
|
7
|
-
export const metadata: Metadata = {
|
|
8
|
-
title: "Dev Workflow Dashboard",
|
|
9
|
-
description: "Enforce development discipline and best practices",
|
|
10
|
-
icons: {
|
|
11
|
-
icon: [
|
|
12
|
-
{ url: "/assets/favicon-16x16.png", sizes: "16x16", type: "image/png" },
|
|
13
|
-
{ url: "/assets/favicon-32x32.png", sizes: "32x32", type: "image/png" },
|
|
14
|
-
{ url: "/assets/favicon.ico" },
|
|
15
|
-
],
|
|
16
|
-
apple: [
|
|
17
|
-
{ url: "/assets/apple-touch-icon.png", sizes: "180x180", type: "image/png" },
|
|
18
|
-
],
|
|
19
|
-
},
|
|
20
|
-
manifest: "/assets/site.webmanifest",
|
|
21
|
-
};
|
|
22
|
-
|
|
23
|
-
export default function RootLayout({
|
|
24
|
-
children,
|
|
25
|
-
}: Readonly<{
|
|
26
|
-
children: React.ReactNode;
|
|
27
|
-
}>) {
|
|
28
|
-
return (
|
|
29
|
-
<html lang="en" className="scroll-smooth" data-scroll-behavior="smooth">
|
|
30
|
-
<body className="antialiased bg-white dark:bg-[#0a0a0f] text-gray-900 dark:text-white transition-colors duration-300">
|
|
31
|
-
<ThemeProvider>
|
|
32
|
-
<Navbar />
|
|
33
|
-
{children}
|
|
34
|
-
<Footer />
|
|
35
|
-
</ThemeProvider>
|
|
36
|
-
</body>
|
|
37
|
-
</html>
|
|
38
|
-
);
|
|
39
|
-
}
|
package/web/app/login/page.tsx
DELETED
|
@@ -1,142 +0,0 @@
|
|
|
1
|
-
'use client';
|
|
2
|
-
|
|
3
|
-
import { useState, useEffect } from 'react';
|
|
4
|
-
import { useRouter } from 'next/navigation';
|
|
5
|
-
import Image from 'next/image';
|
|
6
|
-
import { Lock, User } from 'lucide-react';
|
|
7
|
-
import { apiClient } from '@/lib/api-client';
|
|
8
|
-
|
|
9
|
-
export default function LoginPage() {
|
|
10
|
-
const [username, setUsername] = useState('');
|
|
11
|
-
const [password, setPassword] = useState('');
|
|
12
|
-
const [error, setError] = useState('');
|
|
13
|
-
const [loading, setLoading] = useState(false);
|
|
14
|
-
const [authChecking, setAuthChecking] = useState(true);
|
|
15
|
-
const router = useRouter();
|
|
16
|
-
|
|
17
|
-
useEffect(() => {
|
|
18
|
-
// Check if already logged in
|
|
19
|
-
const mightBeLoggedIn = localStorage.getItem('isLoggedIn') === 'true';
|
|
20
|
-
if (!mightBeLoggedIn) {
|
|
21
|
-
setAuthChecking(false);
|
|
22
|
-
return;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
apiClient.getCurrentUser()
|
|
26
|
-
.then(user => {
|
|
27
|
-
if (user) {
|
|
28
|
-
router.push('/history');
|
|
29
|
-
}
|
|
30
|
-
})
|
|
31
|
-
.catch(() => { })
|
|
32
|
-
.finally(() => setAuthChecking(false));
|
|
33
|
-
}, [router]);
|
|
34
|
-
|
|
35
|
-
const handleSubmit = async (e: React.FormEvent) => {
|
|
36
|
-
e.preventDefault();
|
|
37
|
-
setError('');
|
|
38
|
-
setLoading(true);
|
|
39
|
-
|
|
40
|
-
try {
|
|
41
|
-
const data = await apiClient.login(username, password);
|
|
42
|
-
|
|
43
|
-
if (data.success) {
|
|
44
|
-
localStorage.setItem('isLoggedIn', 'true');
|
|
45
|
-
window.dispatchEvent(new Event('authUpdate'));
|
|
46
|
-
router.push('/history');
|
|
47
|
-
} else {
|
|
48
|
-
setError('Login failed');
|
|
49
|
-
}
|
|
50
|
-
} catch (err: any) {
|
|
51
|
-
setError(err.message || 'Network error. Please try again.');
|
|
52
|
-
} finally {
|
|
53
|
-
setLoading(false);
|
|
54
|
-
}
|
|
55
|
-
};
|
|
56
|
-
|
|
57
|
-
if (authChecking) {
|
|
58
|
-
return null; // Or a loading spinner
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
return (
|
|
62
|
-
<div className="min-h-screen flex items-center justify-center bg-gray-50 dark:bg-[#0a0a0f] px-4 transition-colors duration-300">
|
|
63
|
-
<div className="max-w-md w-full space-y-8 bg-white dark:bg-white/5 p-8 rounded-2xl shadow-xl border border-gray-100 dark:border-white/10">
|
|
64
|
-
<div className="text-center">
|
|
65
|
-
<div className="mx-auto h-12 w-12 relative mb-4">
|
|
66
|
-
<div className="absolute inset-0 bg-blue-500 rounded-xl blur opacity-20 dark:opacity-40"></div>
|
|
67
|
-
<Image src="/assets/logo.png" alt="Logo" width={48} height={48} className="relative rounded-xl" />
|
|
68
|
-
</div>
|
|
69
|
-
<h2 className="text-3xl font-bold text-gray-900 dark:text-white tracking-tight">
|
|
70
|
-
Welcome Back
|
|
71
|
-
</h2>
|
|
72
|
-
<p className="mt-2 text-sm text-gray-500 dark:text-gray-400">
|
|
73
|
-
Sign in to access your workflow history
|
|
74
|
-
</p>
|
|
75
|
-
<p className="mt-1 text-xs text-gray-400 font-mono">
|
|
76
|
-
v1.7.15
|
|
77
|
-
</p>
|
|
78
|
-
</div>
|
|
79
|
-
|
|
80
|
-
<form className="mt-8 space-y-6" onSubmit={handleSubmit}>
|
|
81
|
-
{error && (
|
|
82
|
-
<div className="rounded-lg bg-red-50 dark:bg-red-900/20 p-4 border border-red-200 dark:border-red-900/50">
|
|
83
|
-
<p className="text-sm text-red-600 dark:text-red-400 text-center font-medium">{error}</p>
|
|
84
|
-
</div>
|
|
85
|
-
)}
|
|
86
|
-
|
|
87
|
-
<div className="space-y-4">
|
|
88
|
-
<div className="relative">
|
|
89
|
-
<label htmlFor="username" className="sr-only">Username</label>
|
|
90
|
-
<div className="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
|
|
91
|
-
<User className="h-5 w-5 text-gray-400" />
|
|
92
|
-
</div>
|
|
93
|
-
<input
|
|
94
|
-
id="username"
|
|
95
|
-
name="username"
|
|
96
|
-
type="text"
|
|
97
|
-
required
|
|
98
|
-
className="appearance-none block w-full pl-10 pr-3 py-3 border border-gray-300 dark:border-white/10 rounded-xl placeholder-gray-400 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent bg-white dark:bg-black/20 text-gray-900 dark:text-white sm:text-sm transition-all"
|
|
99
|
-
placeholder="Username"
|
|
100
|
-
value={username}
|
|
101
|
-
onChange={(e) => setUsername(e.target.value)}
|
|
102
|
-
disabled={loading}
|
|
103
|
-
/>
|
|
104
|
-
</div>
|
|
105
|
-
<div className="relative">
|
|
106
|
-
<label htmlFor="password" className="sr-only">Password</label>
|
|
107
|
-
<div className="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
|
|
108
|
-
<Lock className="h-5 w-5 text-gray-400" />
|
|
109
|
-
</div>
|
|
110
|
-
<input
|
|
111
|
-
id="password"
|
|
112
|
-
name="password"
|
|
113
|
-
type="password"
|
|
114
|
-
required
|
|
115
|
-
className="appearance-none block w-full pl-10 pr-3 py-3 border border-gray-300 dark:border-white/10 rounded-xl placeholder-gray-400 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent bg-white dark:bg-black/20 text-gray-900 dark:text-white sm:text-sm transition-all"
|
|
116
|
-
placeholder="Password"
|
|
117
|
-
value={password}
|
|
118
|
-
onChange={(e) => setPassword(e.target.value)}
|
|
119
|
-
disabled={loading}
|
|
120
|
-
/>
|
|
121
|
-
</div>
|
|
122
|
-
</div>
|
|
123
|
-
|
|
124
|
-
<div>
|
|
125
|
-
<button
|
|
126
|
-
type="submit"
|
|
127
|
-
disabled={loading}
|
|
128
|
-
className="group relative w-full flex justify-center py-3 px-4 border border-transparent text-sm font-semibold rounded-xl text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 disabled:opacity-50 disabled:cursor-not-allowed transition-all shadow-lg shadow-blue-500/30 hover:shadow-blue-500/40"
|
|
129
|
-
>
|
|
130
|
-
{loading ? (
|
|
131
|
-
<svg className="animate-spin -ml-1 mr-3 h-5 w-5 text-white" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
|
|
132
|
-
<circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4"></circle>
|
|
133
|
-
<path className="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
|
|
134
|
-
</svg>
|
|
135
|
-
) : 'Sign in'}
|
|
136
|
-
</button>
|
|
137
|
-
</div>
|
|
138
|
-
</form>
|
|
139
|
-
</div>
|
|
140
|
-
</div>
|
|
141
|
-
);
|
|
142
|
-
}
|
package/web/app/page.tsx
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import Hero from "@/components/Hero";
|
|
2
|
-
import Features from "@/components/Features";
|
|
3
|
-
import ApiReference from "@/components/ApiReference";
|
|
4
|
-
|
|
5
|
-
export default function Home() {
|
|
6
|
-
return (
|
|
7
|
-
<main>
|
|
8
|
-
<Hero />
|
|
9
|
-
<Features />
|
|
10
|
-
<ApiReference />
|
|
11
|
-
</main>
|
|
12
|
-
);
|
|
13
|
-
}
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
export default function ApiReference() {
|
|
2
|
-
return (
|
|
3
|
-
<section id="api" className="py-20 px-4">
|
|
4
|
-
<div className="max-w-7xl mx-auto">
|
|
5
|
-
<h2 className="text-3xl md:text-4xl font-bold mb-8">API Reference</h2>
|
|
6
|
-
<p className="text-gray-400 mb-12">Query workflow data programmatically with our JSON API.</p>
|
|
7
|
-
<div className="grid md:grid-cols-2 gap-6">
|
|
8
|
-
<div className="p-6 rounded-xl bg-white/5 border border-white/10">
|
|
9
|
-
<code className="text-indigo-400 font-mono text-sm">GET /api/history</code>
|
|
10
|
-
<p className="text-gray-400 mt-2">Retrieve paginated task history entries.</p>
|
|
11
|
-
</div>
|
|
12
|
-
<div className="p-6 rounded-xl bg-white/5 border border-white/10">
|
|
13
|
-
<code className="text-indigo-400 font-mono text-sm">GET /api/summary</code>
|
|
14
|
-
<p className="text-gray-400 mt-2">Get project health stats and recent tasks.</p>
|
|
15
|
-
</div>
|
|
16
|
-
</div>
|
|
17
|
-
</div>
|
|
18
|
-
</section>
|
|
19
|
-
);
|
|
20
|
-
}
|
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
import { ShieldCheck, TestTube, FileText, GitBranch, History, Zap } from "lucide-react";
|
|
2
|
-
|
|
3
|
-
export default function Features() {
|
|
4
|
-
return (
|
|
5
|
-
<section id="features" className="py-12 sm:py-16 lg:py-20 px-3 sm:px-4 bg-gray-50 dark:bg-transparent transition-colors duration-300">
|
|
6
|
-
<div className="max-w-7xl mx-auto">
|
|
7
|
-
<h2 className="text-2xl sm:text-3xl md:text-4xl font-bold text-center mb-8 sm:mb-12 lg:mb-16 text-gray-900 dark:text-white">Your Coding Conscience</h2>
|
|
8
|
-
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4 sm:gap-6">
|
|
9
|
-
<FeatureCard
|
|
10
|
-
icon={<ShieldCheck className="w-6 h-6 text-indigo-400" />}
|
|
11
|
-
title="Enforced Discipline"
|
|
12
|
-
description="Won't let you skip important steps. Follows a strict, proven development workflow."
|
|
13
|
-
bg="bg-indigo-500/20"
|
|
14
|
-
/>
|
|
15
|
-
<FeatureCard
|
|
16
|
-
icon={<TestTube className="w-6 h-6 text-purple-400" />}
|
|
17
|
-
title="Test-Driven"
|
|
18
|
-
description="Blocks commits if tests fail. Encourages writing tests before marking tasks complete."
|
|
19
|
-
bg="bg-purple-500/20"
|
|
20
|
-
/>
|
|
21
|
-
<FeatureCard
|
|
22
|
-
icon={<FileText className="w-6 h-6 text-emerald-400" />}
|
|
23
|
-
title="Documentation"
|
|
24
|
-
description="Reminds you to update documentation (README, docs, changelog) for every change."
|
|
25
|
-
bg="bg-emerald-500/20"
|
|
26
|
-
/>
|
|
27
|
-
<FeatureCard
|
|
28
|
-
icon={<GitBranch className="w-6 h-6 text-orange-400" />}
|
|
29
|
-
title="State Tracking"
|
|
30
|
-
description="Remembers exactly where you are in the workflow, even across sessions."
|
|
31
|
-
bg="bg-orange-500/20"
|
|
32
|
-
/>
|
|
33
|
-
<FeatureCard
|
|
34
|
-
icon={<History className="w-6 h-6 text-blue-400" />}
|
|
35
|
-
title="Task History"
|
|
36
|
-
description="Keeps a detailed log of all completed tasks, commits, and releases."
|
|
37
|
-
bg="bg-blue-500/20"
|
|
38
|
-
/>
|
|
39
|
-
<FeatureCard
|
|
40
|
-
icon={<Zap className="w-6 h-6 text-pink-400" />}
|
|
41
|
-
title="Smart Prompts"
|
|
42
|
-
description="Provides quick reminders and guidance on best practices as you work."
|
|
43
|
-
bg="bg-pink-500/20"
|
|
44
|
-
/>
|
|
45
|
-
</div>
|
|
46
|
-
</div>
|
|
47
|
-
</section>
|
|
48
|
-
);
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
function FeatureCard({ icon, title, description, bg }: { icon: React.ReactNode, title: string, description: string, bg: string }) {
|
|
52
|
-
return (
|
|
53
|
-
<div className="p-6 rounded-xl bg-white dark:bg-white/5 border border-gray-200 dark:border-white/10 hover:bg-gray-100 dark:hover:bg-white/10 transition">
|
|
54
|
-
<div className={`w-12 h-12 rounded-lg ${bg} flex items-center justify-center mb-4`}>
|
|
55
|
-
{icon}
|
|
56
|
-
</div>
|
|
57
|
-
<h3 className="text-xl font-semibold mb-2 text-gray-900 dark:text-white">{title}</h3>
|
|
58
|
-
<p className="text-gray-600 dark:text-gray-400">{description}</p>
|
|
59
|
-
</div>
|
|
60
|
-
);
|
|
61
|
-
}
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
export default function Footer() {
|
|
2
|
-
return (
|
|
3
|
-
<footer className="py-12 px-4 border-t border-gray-200 dark:border-white/10 bg-white dark:bg-transparent transition-colors duration-300">
|
|
4
|
-
<div className="max-w-7xl mx-auto text-center text-gray-600 dark:text-gray-400">
|
|
5
|
-
<p>Built with ❤️ for developers who care about quality</p>
|
|
6
|
-
</div>
|
|
7
|
-
</footer>
|
|
8
|
-
);
|
|
9
|
-
}
|
package/web/components/Hero.tsx
DELETED
|
@@ -1,96 +0,0 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
|
|
3
|
-
import Link from "next/link";
|
|
4
|
-
import { ArrowRight } from "lucide-react";
|
|
5
|
-
import Terminal from "./Terminal";
|
|
6
|
-
import { useEffect, useState } from "react";
|
|
7
|
-
|
|
8
|
-
export default function Hero() {
|
|
9
|
-
const [downloads, setDownloads] = useState<number | null>(null);
|
|
10
|
-
const [version, setVersion] = useState<string | null>(null);
|
|
11
|
-
|
|
12
|
-
useEffect(() => {
|
|
13
|
-
// Fetch npm downloads directly from npm API
|
|
14
|
-
fetch("https://api.npmjs.org/downloads/point/last-month/@programinglive/dev-workflow-mcp-server")
|
|
15
|
-
.then(res => res.json())
|
|
16
|
-
.then(data => setDownloads(data.downloads || 0))
|
|
17
|
-
.catch(() => setDownloads(null));
|
|
18
|
-
|
|
19
|
-
// Version is hardcoded for static export
|
|
20
|
-
setVersion("1.7.15");
|
|
21
|
-
}, []);
|
|
22
|
-
|
|
23
|
-
const formatDownloads = (num: number) => {
|
|
24
|
-
if (num >= 1000000) return `${(num / 1000000).toFixed(1)}M`;
|
|
25
|
-
if (num >= 1000) return `${(num / 1000).toFixed(1)}K`;
|
|
26
|
-
return num.toString();
|
|
27
|
-
};
|
|
28
|
-
|
|
29
|
-
return (
|
|
30
|
-
<section className="min-h-screen flex items-center justify-center px-3 sm:px-4 pt-16 relative overflow-hidden bg-white dark:bg-transparent transition-colors duration-300">
|
|
31
|
-
<div className="absolute inset-0 bg-gradient-to-br from-indigo-500/5 dark:from-indigo-500/10 via-purple-500/5 dark:via-purple-500/10 to-pink-500/5 dark:to-pink-500/10"></div>
|
|
32
|
-
<div className="max-w-7xl mx-auto grid xl:grid-cols-2 gap-6 sm:gap-8 lg:gap-12 items-center relative z-10">
|
|
33
|
-
{/* Left Column - Content */}
|
|
34
|
-
<div>
|
|
35
|
-
<div className="animate-fade-in-up" style={{ animationDelay: "0.1s" }}>
|
|
36
|
-
<div className="inline-flex items-center gap-2 px-3 py-1.5 rounded-full bg-emerald-500/10 border border-emerald-500/20 text-emerald-400 text-sm mb-8">
|
|
37
|
-
<span className="w-1.5 h-1.5 rounded-full bg-emerald-500 animate-pulse"></span>
|
|
38
|
-
<span>v{version ?? "..."} - Active</span>
|
|
39
|
-
</div>
|
|
40
|
-
</div>
|
|
41
|
-
|
|
42
|
-
<h1 className="text-3xl sm:text-4xl md:text-5xl lg:text-6xl xl:text-7xl font-bold mb-4 sm:mb-6 leading-tight animate-fade-in-up" style={{ animationDelay: "0.2s" }}>
|
|
43
|
-
Master Your<br />
|
|
44
|
-
<span className="bg-gradient-to-r from-indigo-400 via-purple-400 to-pink-400 bg-clip-text text-transparent">Workflow</span>
|
|
45
|
-
</h1>
|
|
46
|
-
<p className="text-base sm:text-lg md:text-xl text-gray-600 dark:text-gray-400 mb-6 sm:mb-8 max-w-xl animate-fade-in-up" style={{ animationDelay: "0.3s" }}>
|
|
47
|
-
An MCP server that acts as your coding conscience. Enforce test-driven development, ensure documentation, and never skip a step in your release process.
|
|
48
|
-
</p>
|
|
49
|
-
<div className="flex flex-col sm:flex-row gap-4 mb-8 animate-fade-in-up" style={{ animationDelay: "0.4s" }}>
|
|
50
|
-
<Link href="#history" className="px-6 py-3 bg-gradient-to-r from-indigo-600 to-purple-600 rounded-lg font-semibold hover:shadow-lg hover:shadow-indigo-500/50 transition inline-flex items-center justify-center gap-2 text-white">
|
|
51
|
-
View History <ArrowRight className="w-4 h-4" />
|
|
52
|
-
</Link>
|
|
53
|
-
<Link href="/docs" className="px-6 py-3 bg-gray-200 dark:bg-white/5 border border-gray-300 dark:border-white/10 rounded-lg font-semibold hover:bg-gray-300 dark:hover:bg-white/10 transition text-center text-gray-900 dark:text-white">
|
|
54
|
-
Documentation
|
|
55
|
-
</Link>
|
|
56
|
-
</div>
|
|
57
|
-
<div className="flex flex-wrap gap-4 sm:gap-6 lg:gap-8 text-xs sm:text-sm animate-fade-in-up" style={{ animationDelay: "0.5s" }}>
|
|
58
|
-
<div>
|
|
59
|
-
<div className="text-xl sm:text-2xl font-bold text-gray-900 dark:text-white">{version ?? "..."}</div>
|
|
60
|
-
<div className="text-gray-600 dark:text-gray-500">Latest Version</div>
|
|
61
|
-
</div>
|
|
62
|
-
<div>
|
|
63
|
-
<div className="text-xl sm:text-2xl font-bold text-gray-900 dark:text-white">MIT</div>
|
|
64
|
-
<div className="text-gray-600 dark:text-gray-500">License</div>
|
|
65
|
-
</div>
|
|
66
|
-
<div>
|
|
67
|
-
<div className="text-xl sm:text-2xl font-bold text-gray-900 dark:text-white">Node 18+</div>
|
|
68
|
-
<div className="text-gray-600 dark:text-gray-500">Compatibility</div>
|
|
69
|
-
</div>
|
|
70
|
-
{downloads !== null && (
|
|
71
|
-
<div>
|
|
72
|
-
<div className="text-xl sm:text-2xl font-bold text-gray-900 dark:text-white">{formatDownloads(downloads)}</div>
|
|
73
|
-
<div className="text-gray-600 dark:text-gray-500">Downloads/mo</div>
|
|
74
|
-
</div>
|
|
75
|
-
)}
|
|
76
|
-
</div>
|
|
77
|
-
</div>
|
|
78
|
-
{/* Right Column - Terminal */}
|
|
79
|
-
<div className="hidden xl:block">
|
|
80
|
-
<div className="relative">
|
|
81
|
-
<div className="absolute inset-0 bg-gradient-to-r from-indigo-500/20 to-purple-500/20 blur-3xl"></div>
|
|
82
|
-
<div className="relative bg-[#1a1a24] rounded-xl border border-white/10 p-4 sm:p-6 shadow-2xl">
|
|
83
|
-
<div className="flex items-center gap-2 mb-4">
|
|
84
|
-
<div className="w-3 h-3 rounded-full bg-red-500"></div>
|
|
85
|
-
<div className="w-3 h-3 rounded-full bg-yellow-500"></div>
|
|
86
|
-
<div className="w-3 h-3 rounded-full bg-green-500"></div>
|
|
87
|
-
<span className="ml-auto text-xs text-gray-500">terminal</span>
|
|
88
|
-
</div>
|
|
89
|
-
<Terminal />
|
|
90
|
-
</div>
|
|
91
|
-
</div>
|
|
92
|
-
</div>
|
|
93
|
-
</div>
|
|
94
|
-
</section>
|
|
95
|
-
);
|
|
96
|
-
}
|