claude-code-workflow 6.0.2 → 6.0.4
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/ccw/src/commands/view.js
CHANGED
|
@@ -1,14 +1,105 @@
|
|
|
1
1
|
import { serveCommand } from './serve.js';
|
|
2
|
+
import { launchBrowser } from '../utils/browser-launcher.js';
|
|
3
|
+
import { validatePath } from '../utils/path-resolver.js';
|
|
4
|
+
import chalk from 'chalk';
|
|
2
5
|
|
|
3
6
|
/**
|
|
4
|
-
*
|
|
7
|
+
* Check if server is already running on the specified port
|
|
8
|
+
* @param {number} port - Port to check
|
|
9
|
+
* @returns {Promise<boolean>} True if server is running
|
|
10
|
+
*/
|
|
11
|
+
async function isServerRunning(port) {
|
|
12
|
+
try {
|
|
13
|
+
const controller = new AbortController();
|
|
14
|
+
const timeoutId = setTimeout(() => controller.abort(), 1000);
|
|
15
|
+
|
|
16
|
+
const response = await fetch(`http://localhost:${port}/api/health`, {
|
|
17
|
+
signal: controller.signal
|
|
18
|
+
});
|
|
19
|
+
clearTimeout(timeoutId);
|
|
20
|
+
|
|
21
|
+
return response.ok;
|
|
22
|
+
} catch {
|
|
23
|
+
return false;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Switch workspace on running server
|
|
29
|
+
* @param {number} port - Server port
|
|
30
|
+
* @param {string} path - New workspace path
|
|
31
|
+
* @returns {Promise<Object>} Result with success status
|
|
32
|
+
*/
|
|
33
|
+
async function switchWorkspace(port, path) {
|
|
34
|
+
try {
|
|
35
|
+
const response = await fetch(
|
|
36
|
+
`http://localhost:${port}/api/switch-path?path=${encodeURIComponent(path)}`
|
|
37
|
+
);
|
|
38
|
+
return await response.json();
|
|
39
|
+
} catch (err) {
|
|
40
|
+
return { success: false, error: err.message };
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* View command handler - opens dashboard for current workspace
|
|
46
|
+
* If server is already running, switches workspace and opens browser
|
|
47
|
+
* If not running, starts a new server
|
|
5
48
|
* @param {Object} options - Command options
|
|
6
49
|
*/
|
|
7
50
|
export async function viewCommand(options) {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
51
|
+
const port = options.port || 3456;
|
|
52
|
+
|
|
53
|
+
// Resolve workspace path
|
|
54
|
+
let workspacePath = process.cwd();
|
|
55
|
+
if (options.path) {
|
|
56
|
+
const pathValidation = validatePath(options.path, { mustExist: true });
|
|
57
|
+
if (!pathValidation.valid) {
|
|
58
|
+
console.error(chalk.red(`\n Error: ${pathValidation.error}\n`));
|
|
59
|
+
process.exit(1);
|
|
60
|
+
}
|
|
61
|
+
workspacePath = pathValidation.path;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Check if server is already running
|
|
65
|
+
const serverRunning = await isServerRunning(port);
|
|
66
|
+
|
|
67
|
+
if (serverRunning) {
|
|
68
|
+
// Server is running - switch workspace and open browser
|
|
69
|
+
console.log(chalk.blue.bold('\n CCW Dashboard\n'));
|
|
70
|
+
console.log(chalk.gray(` Server already running on port ${port}`));
|
|
71
|
+
console.log(chalk.cyan(` Switching workspace to: ${workspacePath}`));
|
|
72
|
+
|
|
73
|
+
const result = await switchWorkspace(port, workspacePath);
|
|
74
|
+
|
|
75
|
+
if (result.success) {
|
|
76
|
+
console.log(chalk.green(` Workspace switched successfully`));
|
|
77
|
+
|
|
78
|
+
// Open browser with the new path
|
|
79
|
+
const url = `http://localhost:${port}/?path=${encodeURIComponent(result.path)}`;
|
|
80
|
+
|
|
81
|
+
if (options.browser !== false) {
|
|
82
|
+
console.log(chalk.cyan(' Opening in browser...'));
|
|
83
|
+
try {
|
|
84
|
+
await launchBrowser(url);
|
|
85
|
+
console.log(chalk.green.bold('\n Dashboard opened!\n'));
|
|
86
|
+
} catch (err) {
|
|
87
|
+
console.log(chalk.yellow(`\n Could not open browser: ${err.message}`));
|
|
88
|
+
console.log(chalk.gray(` Open manually: ${url}\n`));
|
|
89
|
+
}
|
|
90
|
+
} else {
|
|
91
|
+
console.log(chalk.gray(`\n URL: ${url}\n`));
|
|
92
|
+
}
|
|
93
|
+
} else {
|
|
94
|
+
console.error(chalk.red(`\n Failed to switch workspace: ${result.error}\n`));
|
|
95
|
+
process.exit(1);
|
|
96
|
+
}
|
|
97
|
+
} else {
|
|
98
|
+
// Server not running - start new server
|
|
99
|
+
await serveCommand({
|
|
100
|
+
path: workspacePath,
|
|
101
|
+
port: port,
|
|
102
|
+
browser: options.browser
|
|
103
|
+
});
|
|
104
|
+
}
|
|
14
105
|
}
|
package/ccw/src/core/server.js
CHANGED
|
@@ -126,6 +126,40 @@ export async function startServer(options = {}) {
|
|
|
126
126
|
return;
|
|
127
127
|
}
|
|
128
128
|
|
|
129
|
+
// API: Switch workspace path (for ccw view command)
|
|
130
|
+
if (pathname === '/api/switch-path') {
|
|
131
|
+
const newPath = url.searchParams.get('path');
|
|
132
|
+
if (!newPath) {
|
|
133
|
+
res.writeHead(400, { 'Content-Type': 'application/json' });
|
|
134
|
+
res.end(JSON.stringify({ error: 'Path is required' }));
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const resolved = resolvePath(newPath);
|
|
139
|
+
if (!existsSync(resolved)) {
|
|
140
|
+
res.writeHead(404, { 'Content-Type': 'application/json' });
|
|
141
|
+
res.end(JSON.stringify({ error: 'Path does not exist' }));
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// Track the path and return success
|
|
146
|
+
trackRecentPath(resolved);
|
|
147
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
148
|
+
res.end(JSON.stringify({
|
|
149
|
+
success: true,
|
|
150
|
+
path: resolved,
|
|
151
|
+
recentPaths: getRecentPaths()
|
|
152
|
+
}));
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// API: Health check (for ccw view to detect running server)
|
|
157
|
+
if (pathname === '/api/health') {
|
|
158
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
159
|
+
res.end(JSON.stringify({ status: 'ok', timestamp: Date.now() }));
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
|
|
129
163
|
// API: Remove a recent path
|
|
130
164
|
if (pathname === '/api/remove-recent-path' && req.method === 'POST') {
|
|
131
165
|
handlePostRequest(req, res, async (body) => {
|
|
@@ -20,7 +20,17 @@ document.addEventListener('DOMContentLoaded', async () => {
|
|
|
20
20
|
// Server mode: load data from API
|
|
21
21
|
try {
|
|
22
22
|
if (window.SERVER_MODE) {
|
|
23
|
-
|
|
23
|
+
// Check URL for path parameter (from ccw view command)
|
|
24
|
+
const urlParams = new URLSearchParams(window.location.search);
|
|
25
|
+
const urlPath = urlParams.get('path');
|
|
26
|
+
const initialPath = urlPath || window.INITIAL_PATH || projectPath;
|
|
27
|
+
|
|
28
|
+
await switchToPath(initialPath);
|
|
29
|
+
|
|
30
|
+
// Clean up URL after loading (remove query param)
|
|
31
|
+
if (urlPath && window.history.replaceState) {
|
|
32
|
+
window.history.replaceState({}, '', window.location.pathname);
|
|
33
|
+
}
|
|
24
34
|
} else {
|
|
25
35
|
renderDashboard();
|
|
26
36
|
}
|
|
@@ -7929,3 +7929,186 @@ code.ctx-meta-chip-value {
|
|
|
7929
7929
|
width: 16px;
|
|
7930
7930
|
border-radius: 3px;
|
|
7931
7931
|
}
|
|
7932
|
+
|
|
7933
|
+
/* ===================================
|
|
7934
|
+
Path Selection Modal
|
|
7935
|
+
=================================== */
|
|
7936
|
+
|
|
7937
|
+
.path-modal-overlay {
|
|
7938
|
+
position: fixed;
|
|
7939
|
+
top: 0;
|
|
7940
|
+
left: 0;
|
|
7941
|
+
right: 0;
|
|
7942
|
+
bottom: 0;
|
|
7943
|
+
background: rgba(0, 0, 0, 0.5);
|
|
7944
|
+
display: flex;
|
|
7945
|
+
align-items: center;
|
|
7946
|
+
justify-content: center;
|
|
7947
|
+
z-index: 1000;
|
|
7948
|
+
backdrop-filter: blur(2px);
|
|
7949
|
+
}
|
|
7950
|
+
|
|
7951
|
+
.path-modal {
|
|
7952
|
+
background: hsl(var(--card));
|
|
7953
|
+
border: 1px solid hsl(var(--border));
|
|
7954
|
+
border-radius: 0.75rem;
|
|
7955
|
+
width: 90%;
|
|
7956
|
+
max-width: 480px;
|
|
7957
|
+
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.2);
|
|
7958
|
+
animation: modal-enter 0.2s ease-out;
|
|
7959
|
+
}
|
|
7960
|
+
|
|
7961
|
+
@keyframes modal-enter {
|
|
7962
|
+
from {
|
|
7963
|
+
opacity: 0;
|
|
7964
|
+
transform: scale(0.95) translateY(-10px);
|
|
7965
|
+
}
|
|
7966
|
+
to {
|
|
7967
|
+
opacity: 1;
|
|
7968
|
+
transform: scale(1) translateY(0);
|
|
7969
|
+
}
|
|
7970
|
+
}
|
|
7971
|
+
|
|
7972
|
+
.path-modal-header {
|
|
7973
|
+
display: flex;
|
|
7974
|
+
align-items: center;
|
|
7975
|
+
gap: 0.75rem;
|
|
7976
|
+
padding: 1.25rem 1.5rem;
|
|
7977
|
+
border-bottom: 1px solid hsl(var(--border));
|
|
7978
|
+
}
|
|
7979
|
+
|
|
7980
|
+
.path-modal-icon {
|
|
7981
|
+
font-size: 1.5rem;
|
|
7982
|
+
color: hsl(var(--primary));
|
|
7983
|
+
}
|
|
7984
|
+
|
|
7985
|
+
.path-modal-header h3 {
|
|
7986
|
+
margin: 0;
|
|
7987
|
+
font-size: 1.1rem;
|
|
7988
|
+
font-weight: 600;
|
|
7989
|
+
color: hsl(var(--foreground));
|
|
7990
|
+
}
|
|
7991
|
+
|
|
7992
|
+
.path-modal-body {
|
|
7993
|
+
padding: 1.5rem;
|
|
7994
|
+
}
|
|
7995
|
+
|
|
7996
|
+
.path-modal-body p {
|
|
7997
|
+
margin: 0 0 1rem;
|
|
7998
|
+
color: hsl(var(--muted-foreground));
|
|
7999
|
+
font-size: 0.9rem;
|
|
8000
|
+
line-height: 1.5;
|
|
8001
|
+
}
|
|
8002
|
+
|
|
8003
|
+
.path-modal-command {
|
|
8004
|
+
display: flex;
|
|
8005
|
+
align-items: center;
|
|
8006
|
+
gap: 0.75rem;
|
|
8007
|
+
background: hsl(var(--muted));
|
|
8008
|
+
padding: 0.75rem 1rem;
|
|
8009
|
+
border-radius: 0.5rem;
|
|
8010
|
+
font-family: var(--font-mono);
|
|
8011
|
+
}
|
|
8012
|
+
|
|
8013
|
+
.path-modal-command code {
|
|
8014
|
+
flex: 1;
|
|
8015
|
+
font-size: 0.85rem;
|
|
8016
|
+
color: hsl(var(--foreground));
|
|
8017
|
+
word-break: break-all;
|
|
8018
|
+
}
|
|
8019
|
+
|
|
8020
|
+
.path-modal-command .copy-btn {
|
|
8021
|
+
display: flex;
|
|
8022
|
+
align-items: center;
|
|
8023
|
+
gap: 0.375rem;
|
|
8024
|
+
padding: 0.375rem 0.75rem;
|
|
8025
|
+
background: hsl(var(--primary));
|
|
8026
|
+
color: white;
|
|
8027
|
+
border: none;
|
|
8028
|
+
border-radius: 0.375rem;
|
|
8029
|
+
font-size: 0.8rem;
|
|
8030
|
+
cursor: pointer;
|
|
8031
|
+
transition: all 0.15s;
|
|
8032
|
+
white-space: nowrap;
|
|
8033
|
+
}
|
|
8034
|
+
|
|
8035
|
+
.path-modal-command .copy-btn:hover {
|
|
8036
|
+
background: hsl(var(--primary) / 0.9);
|
|
8037
|
+
}
|
|
8038
|
+
|
|
8039
|
+
.path-modal-note {
|
|
8040
|
+
font-size: 0.85rem !important;
|
|
8041
|
+
color: hsl(var(--muted-foreground)) !important;
|
|
8042
|
+
}
|
|
8043
|
+
|
|
8044
|
+
.path-modal-note code {
|
|
8045
|
+
background: hsl(var(--muted));
|
|
8046
|
+
padding: 0.125rem 0.375rem;
|
|
8047
|
+
border-radius: 0.25rem;
|
|
8048
|
+
font-size: 0.8rem;
|
|
8049
|
+
}
|
|
8050
|
+
|
|
8051
|
+
.path-modal-input {
|
|
8052
|
+
width: 100%;
|
|
8053
|
+
padding: 0.75rem 1rem;
|
|
8054
|
+
background: hsl(var(--background));
|
|
8055
|
+
border: 1px solid hsl(var(--border));
|
|
8056
|
+
border-radius: 0.5rem;
|
|
8057
|
+
font-size: 0.9rem;
|
|
8058
|
+
color: hsl(var(--foreground));
|
|
8059
|
+
outline: none;
|
|
8060
|
+
transition: border-color 0.15s;
|
|
8061
|
+
}
|
|
8062
|
+
|
|
8063
|
+
.path-modal-input:focus {
|
|
8064
|
+
border-color: hsl(var(--primary));
|
|
8065
|
+
}
|
|
8066
|
+
|
|
8067
|
+
.path-modal-input::placeholder {
|
|
8068
|
+
color: hsl(var(--muted-foreground));
|
|
8069
|
+
}
|
|
8070
|
+
|
|
8071
|
+
.path-modal-footer {
|
|
8072
|
+
display: flex;
|
|
8073
|
+
justify-content: flex-end;
|
|
8074
|
+
gap: 0.75rem;
|
|
8075
|
+
padding: 1rem 1.5rem;
|
|
8076
|
+
border-top: 1px solid hsl(var(--border));
|
|
8077
|
+
background: hsl(var(--muted) / 0.3);
|
|
8078
|
+
border-radius: 0 0 0.75rem 0.75rem;
|
|
8079
|
+
}
|
|
8080
|
+
|
|
8081
|
+
.path-modal-close {
|
|
8082
|
+
padding: 0.5rem 1.25rem;
|
|
8083
|
+
background: hsl(var(--muted));
|
|
8084
|
+
color: hsl(var(--foreground));
|
|
8085
|
+
border: none;
|
|
8086
|
+
border-radius: 0.375rem;
|
|
8087
|
+
font-size: 0.875rem;
|
|
8088
|
+
cursor: pointer;
|
|
8089
|
+
transition: all 0.15s;
|
|
8090
|
+
}
|
|
8091
|
+
|
|
8092
|
+
.path-modal-close:hover {
|
|
8093
|
+
background: hsl(var(--hover));
|
|
8094
|
+
}
|
|
8095
|
+
|
|
8096
|
+
.path-modal-confirm {
|
|
8097
|
+
padding: 0.5rem 1.25rem;
|
|
8098
|
+
background: hsl(var(--primary));
|
|
8099
|
+
color: white;
|
|
8100
|
+
border: none;
|
|
8101
|
+
border-radius: 0.375rem;
|
|
8102
|
+
font-size: 0.875rem;
|
|
8103
|
+
cursor: pointer;
|
|
8104
|
+
transition: all 0.15s;
|
|
8105
|
+
}
|
|
8106
|
+
|
|
8107
|
+
.path-modal-confirm:hover {
|
|
8108
|
+
background: hsl(var(--primary) / 0.9);
|
|
8109
|
+
}
|
|
8110
|
+
|
|
8111
|
+
.path-modal-confirm:disabled {
|
|
8112
|
+
opacity: 0.5;
|
|
8113
|
+
cursor: not-allowed;
|
|
8114
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claude-code-workflow",
|
|
3
|
-
"version": "6.0.
|
|
3
|
+
"version": "6.0.4",
|
|
4
4
|
"description": "JSON-driven multi-agent development framework with intelligent CLI orchestration (Gemini/Qwen/Codex), context-first architecture, and automated workflow execution",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "ccw/src/index.js",
|