create-claude-flow-codex-ui 0.1.0-alpha.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,82 @@
1
+ # create-claude-flow-codex-ui
2
+
3
+ `npm create` initializer that sets up Claude Flow Codex and adds a live agent-progress admin UI.
4
+
5
+ ## What it does
6
+
7
+ When you run the initializer, it:
8
+
9
+ 1. Runs:
10
+
11
+ ```bash
12
+ npx claude-flow@alpha init --codex --full
13
+ ```
14
+
15
+ 2. Adds admin dashboard files to:
16
+
17
+ ```text
18
+ src/admin/
19
+ ```
20
+
21
+ 3. Adds npm scripts in the target project:
22
+
23
+ - `claudeflow:admin:sync`
24
+ - `claudeflow:admin:sync:once`
25
+ - `claudeflow:admin:ui`
26
+
27
+ ## Usage
28
+
29
+ Recommended (`npm create`):
30
+
31
+ ```bash
32
+ npm create claude-flow-codex-ui@alpha my-project
33
+ ```
34
+
35
+ Initialize in current directory:
36
+
37
+ ```bash
38
+ npm create claude-flow-codex-ui@alpha .
39
+ ```
40
+
41
+ Equivalent direct invocation:
42
+
43
+ ```bash
44
+ npx create-claude-flow-codex-ui@alpha my-project
45
+ ```
46
+
47
+ Legacy explicit command still supported:
48
+
49
+ ```bash
50
+ npx create-claude-flow-codex-ui@alpha init my-project
51
+ ```
52
+
53
+ Options:
54
+
55
+ - `--force` overwrite existing `src/admin/*` files and script keys
56
+ - `--skip-claude-flow-init` skip running `npx claude-flow@alpha init --codex --full`
57
+
58
+ For `npm create`, pass initializer flags after `--`:
59
+
60
+ ```bash
61
+ npm create claude-flow-codex-ui@alpha my-project -- --force
62
+ ```
63
+
64
+ ## Run the admin UI in scaffolded project
65
+
66
+ Terminal 1:
67
+
68
+ ```bash
69
+ npm run claudeflow:admin:sync
70
+ ```
71
+
72
+ Terminal 2:
73
+
74
+ ```bash
75
+ npm run claudeflow:admin:ui
76
+ ```
77
+
78
+ Then open:
79
+
80
+ ```text
81
+ http://127.0.0.1:4173
82
+ ```
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+
3
+ import "./create-claude-flow-codex-ui.mjs";
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { run } from "../src/cli/init.mjs";
4
+
5
+ run(process.argv.slice(2)).catch((error) => {
6
+ process.stderr.write(`${error.message}\n`);
7
+ process.exitCode = 1;
8
+ });
package/package.json ADDED
@@ -0,0 +1,39 @@
1
+ {
2
+ "name": "create-claude-flow-codex-ui",
3
+ "version": "0.1.0-alpha.0",
4
+ "description": "npm create initializer for Claude Flow Codex full setup plus live swarm admin dashboard.",
5
+ "type": "module",
6
+ "bin": {
7
+ "create-claude-flow-codex-ui": "bin/create-claude-flow-codex-ui.mjs",
8
+ "claude-flow-codex-ui": "bin/create-claude-flow-codex-ui.mjs"
9
+ },
10
+ "files": [
11
+ "bin",
12
+ "src/cli",
13
+ "templates",
14
+ "README.md",
15
+ "LICENSE"
16
+ ],
17
+ "scripts": {
18
+ "check": "node --check bin/create-claude-flow-codex-ui.mjs && node --check src/cli/init.mjs",
19
+ "package:dry-run": "npm pack --dry-run"
20
+ },
21
+ "keywords": [
22
+ "create",
23
+ "initializer",
24
+ "boilerplate",
25
+ "claude-flow",
26
+ "codex",
27
+ "swarm",
28
+ "dashboard",
29
+ "telemetry"
30
+ ],
31
+ "engines": {
32
+ "node": ">=18"
33
+ },
34
+ "publishConfig": {
35
+ "access": "public",
36
+ "tag": "alpha"
37
+ },
38
+ "license": "MIT"
39
+ }
@@ -0,0 +1,246 @@
1
+ import { spawn } from "node:child_process";
2
+ import { access, copyFile, mkdir, readFile, readdir, writeFile } from "node:fs/promises";
3
+ import path from "node:path";
4
+ import { fileURLToPath } from "node:url";
5
+
6
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
7
+ const packageRoot = path.resolve(__dirname, "..", "..");
8
+ const templateRoot = path.join(packageRoot, "templates", "admin");
9
+
10
+ const RUNTIME_GITIGNORE_ENTRIES = [
11
+ ".codex/",
12
+ ".agents/",
13
+ ".claude-flow/",
14
+ ".swarm/",
15
+ ];
16
+
17
+ const ADMIN_SCRIPTS = {
18
+ "claudeflow:admin:sync": "node src/admin/sync-telemetry.mjs --interval 2000",
19
+ "claudeflow:admin:sync:once": "node src/admin/sync-telemetry.mjs --once",
20
+ "claudeflow:admin:ui": "node src/admin/dev-server.mjs",
21
+ };
22
+
23
+ export async function run(argv) {
24
+ const args = normalizeArgs(argv);
25
+
26
+ if (args.includes("-h") || args.includes("--help") || args[0] === "help") {
27
+ printHelp();
28
+ return;
29
+ }
30
+
31
+ const options = parseOptions(args);
32
+ const targetDir = path.resolve(process.cwd(), options.targetDir || ".");
33
+
34
+ await mkdir(targetDir, { recursive: true });
35
+
36
+ log(`Target directory: ${targetDir}`);
37
+
38
+ if (!options.skipClaudeFlowInit) {
39
+ await runClaudeFlowInit(targetDir);
40
+ } else {
41
+ log("Skipping claude-flow init (--skip-claude-flow-init).", "warn");
42
+ }
43
+
44
+ const copied = await copyTemplateTree(templateRoot, path.join(targetDir, "src", "admin"), {
45
+ force: options.force,
46
+ });
47
+ log(`Admin UI files copied: ${copied.copied} (skipped: ${copied.skipped})`);
48
+
49
+ const pkgResult = await patchTargetPackageJson(targetDir, { force: options.force });
50
+ log(`package.json ${pkgResult.created ? "created" : "updated"}; scripts added: ${pkgResult.addedScripts}`);
51
+
52
+ await ensureGitignoreEntries(targetDir, RUNTIME_GITIGNORE_ENTRIES);
53
+ log(".gitignore updated with Claude Flow runtime entries.");
54
+
55
+ log("Scaffold complete.", "success");
56
+ log("Run 'npm run claudeflow:admin:sync' and 'npm run claudeflow:admin:ui' in the target project.");
57
+ }
58
+
59
+ function normalizeArgs(argv) {
60
+ const args = [...argv];
61
+ if (!args.length) {
62
+ return [];
63
+ }
64
+ if (args[0] === "init") {
65
+ return args.slice(1);
66
+ }
67
+ return args;
68
+ }
69
+
70
+ function parseOptions(tokens) {
71
+ const options = {
72
+ targetDir: ".",
73
+ skipClaudeFlowInit: false,
74
+ force: false,
75
+ };
76
+
77
+ for (let i = 0; i < tokens.length; i += 1) {
78
+ const token = tokens[i];
79
+ if (token === "--skip-claude-flow-init") {
80
+ options.skipClaudeFlowInit = true;
81
+ continue;
82
+ }
83
+ if (token === "--force") {
84
+ options.force = true;
85
+ continue;
86
+ }
87
+ if (token.startsWith("--")) {
88
+ throw new Error(`Unknown option: ${token}`);
89
+ }
90
+ if (options.targetDir !== ".") {
91
+ throw new Error(`Unexpected argument: ${token}`);
92
+ }
93
+ options.targetDir = token;
94
+ }
95
+
96
+ return options;
97
+ }
98
+
99
+ async function runClaudeFlowInit(targetDir) {
100
+ log("Running: npx claude-flow@alpha init --codex --full");
101
+ const npxCommand = process.platform === "win32" ? "npx.cmd" : "npx";
102
+
103
+ await runCommand(npxCommand, ["claude-flow@alpha", "init", "--codex", "--full"], {
104
+ cwd: targetDir,
105
+ });
106
+ }
107
+
108
+ async function patchTargetPackageJson(targetDir, { force }) {
109
+ const packageJsonPath = path.join(targetDir, "package.json");
110
+ let pkg;
111
+ let created = false;
112
+
113
+ if (await fileExists(packageJsonPath)) {
114
+ const raw = await readFile(packageJsonPath, "utf8");
115
+ pkg = JSON.parse(raw);
116
+ } else {
117
+ created = true;
118
+ pkg = {
119
+ name: inferPackageName(targetDir),
120
+ version: "0.1.0",
121
+ private: true,
122
+ scripts: {},
123
+ };
124
+ }
125
+
126
+ if (!pkg.scripts || typeof pkg.scripts !== "object") {
127
+ pkg.scripts = {};
128
+ }
129
+
130
+ let addedScripts = 0;
131
+ for (const [name, command] of Object.entries(ADMIN_SCRIPTS)) {
132
+ if (!(name in pkg.scripts) || force) {
133
+ pkg.scripts[name] = command;
134
+ addedScripts += 1;
135
+ }
136
+ }
137
+
138
+ const serialized = `${JSON.stringify(pkg, null, 2)}\n`;
139
+ await writeFile(packageJsonPath, serialized, "utf8");
140
+
141
+ return { created, addedScripts };
142
+ }
143
+
144
+ async function ensureGitignoreEntries(targetDir, entries) {
145
+ const gitignorePath = path.join(targetDir, ".gitignore");
146
+ const current = (await fileExists(gitignorePath))
147
+ ? await readFile(gitignorePath, "utf8")
148
+ : "";
149
+
150
+ const lines = current.split(/\r?\n/);
151
+ const toAdd = entries.filter((entry) => !lines.includes(entry));
152
+
153
+ if (!toAdd.length) {
154
+ return;
155
+ }
156
+
157
+ const prefix = current && !current.endsWith("\n") ? "\n" : "";
158
+ const content = `${current}${prefix}${toAdd.join("\n")}\n`;
159
+ await writeFile(gitignorePath, content, "utf8");
160
+ }
161
+
162
+ async function copyTemplateTree(fromDir, toDir, { force }) {
163
+ const counters = { copied: 0, skipped: 0 };
164
+ await mkdir(toDir, { recursive: true });
165
+
166
+ const entries = await readdir(fromDir, { withFileTypes: true });
167
+ for (const entry of entries) {
168
+ const sourcePath = path.join(fromDir, entry.name);
169
+ const targetPath = path.join(toDir, entry.name);
170
+
171
+ if (entry.isDirectory()) {
172
+ const nested = await copyTemplateTree(sourcePath, targetPath, { force });
173
+ counters.copied += nested.copied;
174
+ counters.skipped += nested.skipped;
175
+ continue;
176
+ }
177
+
178
+ const exists = await fileExists(targetPath);
179
+ if (exists && !force) {
180
+ counters.skipped += 1;
181
+ continue;
182
+ }
183
+
184
+ await mkdir(path.dirname(targetPath), { recursive: true });
185
+ await copyFile(sourcePath, targetPath);
186
+ counters.copied += 1;
187
+ }
188
+
189
+ return counters;
190
+ }
191
+
192
+ async function runCommand(command, args, options = {}) {
193
+ await new Promise((resolve, reject) => {
194
+ const child = spawn(command, args, {
195
+ cwd: options.cwd || process.cwd(),
196
+ stdio: "inherit",
197
+ shell: false,
198
+ });
199
+
200
+ child.on("error", reject);
201
+ child.on("exit", (code) => {
202
+ if (code === 0) {
203
+ resolve();
204
+ return;
205
+ }
206
+ reject(new Error(`Command failed (${code}): ${command} ${args.join(" ")}`));
207
+ });
208
+ });
209
+ }
210
+
211
+ function inferPackageName(targetDir) {
212
+ const raw = path.basename(targetDir).toLowerCase();
213
+ const safe = raw
214
+ .replace(/[^a-z0-9._-]/g, "-")
215
+ .replace(/--+/g, "-")
216
+ .replace(/^-+|-+$/g, "");
217
+ return safe || "claude-flow-project";
218
+ }
219
+
220
+ async function fileExists(filePath) {
221
+ try {
222
+ await access(filePath);
223
+ return true;
224
+ } catch {
225
+ return false;
226
+ }
227
+ }
228
+
229
+ function log(message, level = "info") {
230
+ const tag = level.toUpperCase().padEnd(7, " ");
231
+ process.stdout.write(`[${tag}] ${message}\n`);
232
+ }
233
+
234
+ function printHelp() {
235
+ process.stdout.write(
236
+ `create-claude-flow-codex-ui\n\n` +
237
+ `Usage:\n` +
238
+ ` npm create claude-flow-codex-ui@alpha [target-dir] [-- --force] [-- --skip-claude-flow-init]\n` +
239
+ ` npx create-claude-flow-codex-ui@alpha [target-dir] [--force] [--skip-claude-flow-init]\n` +
240
+ ` npx create-claude-flow-codex-ui@alpha init [target-dir] [--force] [--skip-claude-flow-init]\n\n` +
241
+ `What it does:\n` +
242
+ ` 1) Runs: npx claude-flow@alpha init --codex --full\n` +
243
+ ` 2) Adds a live admin UI at src/admin for agent/task progress\n` +
244
+ ` 3) Adds npm scripts for telemetry sync and dashboard server\n`
245
+ );
246
+ }
@@ -0,0 +1,49 @@
1
+ # Swarm Admin Visualization
2
+
3
+ This dashboard visualizes live agent activity from `src/admin/state/swarm-state.json`.
4
+
5
+ ## 1) Start Telemetry Sync (real data)
6
+
7
+ ```bash
8
+ node src/admin/sync-telemetry.mjs --interval 2000
9
+ ```
10
+
11
+ By default it reads:
12
+ - `.claude-flow/agents/store.json`
13
+ - `.claude-flow/tasks/store.json`
14
+
15
+ And writes:
16
+ - `src/admin/state/swarm-state.json`
17
+
18
+ Optional flags:
19
+ - `--once` (single sync tick and exit)
20
+ - `--swarm-id <id>`
21
+ - `--orch-id <id>`
22
+ - `--agents-path <path>`
23
+ - `--tasks-path <path>`
24
+ - `--output <path>`
25
+ - `--max-events <n>`
26
+
27
+ ## 2) Start Dashboard Server
28
+
29
+ ```bash
30
+ node src/admin/dev-server.mjs
31
+ ```
32
+
33
+ Open:
34
+ - `http://127.0.0.1:4173`
35
+
36
+ The dashboard now uses a WebSocket stream (`/__swarm_ws`) for state updates and falls back to HTTP polling if the socket drops.
37
+
38
+ ## Validation (one-shot)
39
+
40
+ ```bash
41
+ node src/admin/sync-telemetry.mjs --once
42
+ ```
43
+
44
+ This confirms state JSON can be generated from live Claude Flow stores.
45
+
46
+ ## Simulation Mode
47
+
48
+ Add `?simulate=1` if you want visual playback in addition to real telemetry:
49
+ - `http://127.0.0.1:4173/?simulate=1`