kenzoboard 0.1.1 → 0.1.3
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/dist/index.js +200 -303
- package/dist/mcp.js +16018 -0
- package/dist/web/assets/index-BwQljj46.js +5 -0
- package/dist/web/assets/index-D8L4OSbt.css +1 -0
- package/dist/web/favicon.svg +4 -0
- package/dist/web/index.html +14 -0
- package/package.json +19 -8
package/dist/index.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
#!/usr/bin/env
|
|
2
|
-
// @bun
|
|
1
|
+
#!/usr/bin/env node
|
|
3
2
|
import { createRequire } from "node:module";
|
|
4
3
|
var __create = Object.create;
|
|
5
4
|
var __getProtoOf = Object.getPrototypeOf;
|
|
@@ -29,146 +28,65 @@ var __export = (target, all) => {
|
|
|
29
28
|
var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
|
|
30
29
|
var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
31
30
|
|
|
32
|
-
// ../shared/dist/adapters/sqlite-adapter.js
|
|
33
|
-
var exports_sqlite_adapter = {};
|
|
34
|
-
__export(exports_sqlite_adapter, {
|
|
35
|
-
createSqliteAdapter: () => createSqliteAdapter
|
|
36
|
-
});
|
|
37
|
-
import { Database } from "bun:sqlite";
|
|
38
|
-
import { existsSync as existsSync2, mkdirSync as mkdirSync2 } from "fs";
|
|
39
|
-
import { dirname as dirname2 } from "path";
|
|
40
|
-
function createSqliteAdapter(filePath) {
|
|
41
|
-
const dir = dirname2(filePath);
|
|
42
|
-
if (!existsSync2(dir)) {
|
|
43
|
-
mkdirSync2(dir, { recursive: true });
|
|
44
|
-
}
|
|
45
|
-
const db2 = new Database(filePath, { create: true });
|
|
46
|
-
db2.exec("PRAGMA journal_mode = WAL");
|
|
47
|
-
db2.exec("CREATE TABLE IF NOT EXISTS store (id INTEGER PRIMARY KEY CHECK (id = 1), data TEXT NOT NULL)");
|
|
48
|
-
const selectStmt = db2.prepare("SELECT data FROM store WHERE id = 1");
|
|
49
|
-
const insertStmt = db2.prepare("INSERT INTO store (id, data) VALUES (1, ?)");
|
|
50
|
-
const updateStmt = db2.prepare("UPDATE store SET data = ? WHERE id = 1");
|
|
51
|
-
let _data = { ...defaultData2 };
|
|
52
|
-
const readFromDb = () => {
|
|
53
|
-
const row = selectStmt.get();
|
|
54
|
-
if (row?.data) {
|
|
55
|
-
try {
|
|
56
|
-
return JSON.parse(row.data);
|
|
57
|
-
} catch {
|
|
58
|
-
return { ...defaultData2 };
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
return { ...defaultData2 };
|
|
62
|
-
};
|
|
63
|
-
return {
|
|
64
|
-
get data() {
|
|
65
|
-
return _data;
|
|
66
|
-
},
|
|
67
|
-
read() {
|
|
68
|
-
_data = readFromDb();
|
|
69
|
-
if (!selectStmt.get()) {
|
|
70
|
-
insertStmt.run(JSON.stringify(_data));
|
|
71
|
-
}
|
|
72
|
-
},
|
|
73
|
-
write() {
|
|
74
|
-
db2.transaction(() => {
|
|
75
|
-
const current = readFromDb();
|
|
76
|
-
const merged = {
|
|
77
|
-
projects: mergeById(current.projects, _data.projects),
|
|
78
|
-
epics: mergeById(current.epics, _data.epics),
|
|
79
|
-
tasks: mergeById(current.tasks, _data.tasks),
|
|
80
|
-
blobs: mergeById(current.blobs || [], _data.blobs || [])
|
|
81
|
-
};
|
|
82
|
-
const serialized = JSON.stringify(merged);
|
|
83
|
-
const row = selectStmt.get();
|
|
84
|
-
if (row) {
|
|
85
|
-
updateStmt.run(serialized);
|
|
86
|
-
} else {
|
|
87
|
-
insertStmt.run(serialized);
|
|
88
|
-
}
|
|
89
|
-
_data = merged;
|
|
90
|
-
})();
|
|
91
|
-
}
|
|
92
|
-
};
|
|
93
|
-
}
|
|
94
|
-
function mergeById(current, updated) {
|
|
95
|
-
const result = new Map;
|
|
96
|
-
for (const item of current) {
|
|
97
|
-
result.set(item.id, item);
|
|
98
|
-
}
|
|
99
|
-
for (const item of updated) {
|
|
100
|
-
result.set(item.id, item);
|
|
101
|
-
}
|
|
102
|
-
return Array.from(result.values());
|
|
103
|
-
}
|
|
104
|
-
var defaultData2;
|
|
105
|
-
var init_sqlite_adapter = __esm(() => {
|
|
106
|
-
defaultData2 = {
|
|
107
|
-
projects: [],
|
|
108
|
-
epics: [],
|
|
109
|
-
tasks: []
|
|
110
|
-
};
|
|
111
|
-
});
|
|
112
|
-
|
|
113
31
|
// ../shared/dist/blob-storage.js
|
|
114
32
|
var exports_blob_storage = {};
|
|
115
33
|
__export(exports_blob_storage, {
|
|
116
|
-
setBlobStorage: () =>
|
|
34
|
+
setBlobStorage: () => setBlobStorage,
|
|
117
35
|
getBlobStorage: () => getBlobStorage,
|
|
118
|
-
createFilesystemBlobStorage: () =>
|
|
36
|
+
createFilesystemBlobStorage: () => createFilesystemBlobStorage
|
|
119
37
|
});
|
|
120
|
-
import { existsSync as
|
|
121
|
-
import { join
|
|
122
|
-
import { createHash
|
|
123
|
-
function
|
|
38
|
+
import { existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2, unlinkSync } from "fs";
|
|
39
|
+
import { join } from "path";
|
|
40
|
+
import { createHash } from "crypto";
|
|
41
|
+
function blobPath(blobsDir, hash) {
|
|
124
42
|
const prefix = hash.slice(0, 2);
|
|
125
|
-
return
|
|
43
|
+
return join(blobsDir, prefix, hash);
|
|
126
44
|
}
|
|
127
|
-
function
|
|
128
|
-
|
|
45
|
+
function createFilesystemBlobStorage(blobsDir) {
|
|
46
|
+
mkdirSync2(blobsDir, { recursive: true });
|
|
129
47
|
return {
|
|
130
48
|
write(content) {
|
|
131
|
-
const hash =
|
|
49
|
+
const hash = createHash("sha256").update(content).digest("hex");
|
|
132
50
|
const size = content.length;
|
|
133
|
-
const filePath =
|
|
134
|
-
if (!
|
|
135
|
-
const prefixDir =
|
|
136
|
-
|
|
137
|
-
|
|
51
|
+
const filePath = blobPath(blobsDir, hash);
|
|
52
|
+
if (!existsSync2(filePath)) {
|
|
53
|
+
const prefixDir = join(blobsDir, hash.slice(0, 2));
|
|
54
|
+
mkdirSync2(prefixDir, { recursive: true });
|
|
55
|
+
writeFileSync2(filePath, content);
|
|
138
56
|
}
|
|
139
57
|
return { hash, size };
|
|
140
58
|
},
|
|
141
59
|
read(hash) {
|
|
142
|
-
const filePath =
|
|
143
|
-
if (!
|
|
60
|
+
const filePath = blobPath(blobsDir, hash);
|
|
61
|
+
if (!existsSync2(filePath))
|
|
144
62
|
return null;
|
|
145
|
-
return
|
|
63
|
+
return readFileSync2(filePath);
|
|
146
64
|
},
|
|
147
65
|
exists(hash) {
|
|
148
|
-
return
|
|
66
|
+
return existsSync2(blobPath(blobsDir, hash));
|
|
149
67
|
},
|
|
150
68
|
remove(hash) {
|
|
151
|
-
const filePath =
|
|
152
|
-
if (!
|
|
69
|
+
const filePath = blobPath(blobsDir, hash);
|
|
70
|
+
if (!existsSync2(filePath))
|
|
153
71
|
return false;
|
|
154
|
-
|
|
72
|
+
unlinkSync(filePath);
|
|
155
73
|
return true;
|
|
156
74
|
}
|
|
157
75
|
};
|
|
158
76
|
}
|
|
159
|
-
function
|
|
160
|
-
|
|
77
|
+
function setBlobStorage(bs) {
|
|
78
|
+
blobStorage = bs;
|
|
161
79
|
}
|
|
162
80
|
function getBlobStorage() {
|
|
163
|
-
return
|
|
81
|
+
return blobStorage;
|
|
164
82
|
}
|
|
165
|
-
var
|
|
83
|
+
var blobStorage = null;
|
|
166
84
|
var init_blob_storage = () => {};
|
|
167
85
|
|
|
168
86
|
// src/index.ts
|
|
169
|
-
import { resolve as resolve4, dirname as
|
|
87
|
+
import { resolve as resolve4, dirname as dirname4, basename as basename2 } from "path";
|
|
170
88
|
import { execSync } from "child_process";
|
|
171
|
-
import { existsSync as
|
|
89
|
+
import { existsSync as existsSync7, readFileSync as readFileSync6, writeFileSync as writeFileSync5, mkdirSync as mkdirSync3, appendFileSync, cpSync, realpathSync } from "fs";
|
|
172
90
|
import { fileURLToPath as fileURLToPath2, pathToFileURL } from "url";
|
|
173
91
|
import { createInterface } from "readline";
|
|
174
92
|
|
|
@@ -680,11 +598,14 @@ function createJsonAdapter(filePath) {
|
|
|
680
598
|
|
|
681
599
|
// ../shared/dist/adapters/index.js
|
|
682
600
|
var _createSqliteAdapter;
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
601
|
+
if ("Bun" in globalThis) {
|
|
602
|
+
try {
|
|
603
|
+
const runtimeImport = new Function("specifier", "return import(specifier)");
|
|
604
|
+
const mod = await runtimeImport("./sqlite-adapter.js");
|
|
605
|
+
_createSqliteAdapter = mod.createSqliteAdapter;
|
|
606
|
+
} catch {}
|
|
607
|
+
}
|
|
608
|
+
function createSqliteAdapter(filePath) {
|
|
688
609
|
if (!_createSqliteAdapter) {
|
|
689
610
|
throw new Error("SQLite adapter requires Bun runtime");
|
|
690
611
|
}
|
|
@@ -692,59 +613,17 @@ function createSqliteAdapter2(filePath) {
|
|
|
692
613
|
}
|
|
693
614
|
function createAdapter(filePath) {
|
|
694
615
|
if (filePath.endsWith(".sqlite") || filePath.endsWith(".db")) {
|
|
695
|
-
return
|
|
616
|
+
return createSqliteAdapter(filePath);
|
|
696
617
|
}
|
|
697
618
|
return createJsonAdapter(filePath);
|
|
698
619
|
}
|
|
699
620
|
|
|
700
|
-
//
|
|
701
|
-
|
|
702
|
-
import { join } from "path";
|
|
703
|
-
import { createHash } from "crypto";
|
|
704
|
-
function blobPath(blobsDir, hash) {
|
|
705
|
-
const prefix = hash.slice(0, 2);
|
|
706
|
-
return join(blobsDir, prefix, hash);
|
|
707
|
-
}
|
|
708
|
-
function createFilesystemBlobStorage(blobsDir) {
|
|
709
|
-
mkdirSync3(blobsDir, { recursive: true });
|
|
710
|
-
return {
|
|
711
|
-
write(content) {
|
|
712
|
-
const hash = createHash("sha256").update(content).digest("hex");
|
|
713
|
-
const size = content.length;
|
|
714
|
-
const filePath = blobPath(blobsDir, hash);
|
|
715
|
-
if (!existsSync3(filePath)) {
|
|
716
|
-
const prefixDir = join(blobsDir, hash.slice(0, 2));
|
|
717
|
-
mkdirSync3(prefixDir, { recursive: true });
|
|
718
|
-
writeFileSync2(filePath, content);
|
|
719
|
-
}
|
|
720
|
-
return { hash, size };
|
|
721
|
-
},
|
|
722
|
-
read(hash) {
|
|
723
|
-
const filePath = blobPath(blobsDir, hash);
|
|
724
|
-
if (!existsSync3(filePath))
|
|
725
|
-
return null;
|
|
726
|
-
return readFileSync2(filePath);
|
|
727
|
-
},
|
|
728
|
-
exists(hash) {
|
|
729
|
-
return existsSync3(blobPath(blobsDir, hash));
|
|
730
|
-
},
|
|
731
|
-
remove(hash) {
|
|
732
|
-
const filePath = blobPath(blobsDir, hash);
|
|
733
|
-
if (!existsSync3(filePath))
|
|
734
|
-
return false;
|
|
735
|
-
unlinkSync(filePath);
|
|
736
|
-
return true;
|
|
737
|
-
}
|
|
738
|
-
};
|
|
739
|
-
}
|
|
740
|
-
var blobStorage = null;
|
|
741
|
-
function setBlobStorage(bs) {
|
|
742
|
-
blobStorage = bs;
|
|
743
|
-
}
|
|
621
|
+
// src/index.ts
|
|
622
|
+
init_blob_storage();
|
|
744
623
|
|
|
745
624
|
// ../shared/dist/config.js
|
|
746
|
-
import { resolve, dirname as
|
|
747
|
-
import { existsSync as
|
|
625
|
+
import { resolve, dirname as dirname2 } from "path";
|
|
626
|
+
import { existsSync as existsSync3, readFileSync as readFileSync3, writeFileSync as writeFileSync3, chmodSync } from "fs";
|
|
748
627
|
function expandEnv(value) {
|
|
749
628
|
if (!value?.startsWith("$"))
|
|
750
629
|
return value;
|
|
@@ -755,20 +634,20 @@ function findFluxDir() {
|
|
|
755
634
|
return process.env.FLUX_DIR;
|
|
756
635
|
}
|
|
757
636
|
let dir = process.cwd();
|
|
758
|
-
while (
|
|
637
|
+
while (dirname2(dir) !== dir) {
|
|
759
638
|
const fluxDir = resolve(dir, ".flux");
|
|
760
|
-
if (
|
|
639
|
+
if (existsSync3(fluxDir)) {
|
|
761
640
|
return fluxDir;
|
|
762
641
|
}
|
|
763
|
-
dir =
|
|
642
|
+
dir = dirname2(dir);
|
|
764
643
|
}
|
|
765
644
|
const homeDir = process.env.HOME || process.env.USERPROFILE || "";
|
|
766
645
|
return resolve(homeDir, ".flux");
|
|
767
646
|
}
|
|
768
647
|
function loadEnvLocal(fluxDir) {
|
|
769
|
-
const repoRoot =
|
|
648
|
+
const repoRoot = dirname2(fluxDir);
|
|
770
649
|
const envPath = resolve(repoRoot, ".env.local");
|
|
771
|
-
if (
|
|
650
|
+
if (existsSync3(envPath)) {
|
|
772
651
|
const content = readFileSync3(envPath, "utf-8");
|
|
773
652
|
for (const line of content.split(`
|
|
774
653
|
`)) {
|
|
@@ -788,7 +667,7 @@ function loadEnvLocal(fluxDir) {
|
|
|
788
667
|
}
|
|
789
668
|
function readConfigRaw(fluxDir) {
|
|
790
669
|
const configPath = resolve(fluxDir, "config.json");
|
|
791
|
-
if (
|
|
670
|
+
if (existsSync3(configPath)) {
|
|
792
671
|
try {
|
|
793
672
|
return JSON.parse(readFileSync3(configPath, "utf-8"));
|
|
794
673
|
} catch {
|
|
@@ -2302,8 +2181,8 @@ var _baseMimes = {
|
|
|
2302
2181
|
var baseMimes = _baseMimes;
|
|
2303
2182
|
|
|
2304
2183
|
// ../../node_modules/.bun/@hono+node-server@1.19.8+fd698e00a3b2abce/node_modules/@hono/node-server/dist/serve-static.mjs
|
|
2305
|
-
import { createReadStream, statSync, existsSync as
|
|
2306
|
-
import { join as
|
|
2184
|
+
import { createReadStream, statSync, existsSync as existsSync4 } from "fs";
|
|
2185
|
+
import { join as join2 } from "path";
|
|
2307
2186
|
import { versions } from "process";
|
|
2308
2187
|
import { Readable as Readable2 } from "stream";
|
|
2309
2188
|
var COMPRESSIBLE_CONTENT_TYPE_REGEX = /^\s*(?:text\/[^;\s]+|application\/(?:javascript|json|xml|xml-dtd|ecmascript|dart|postscript|rtf|tar|toml|vnd\.dart|vnd\.ms-fontobject|vnd\.ms-opentype|wasm|x-httpd-php|x-javascript|x-ns-proxy-autoconfig|x-sh|x-tar|x-virtualbox-hdd|x-virtualbox-ova|x-virtualbox-ovf|x-virtualbox-vbox|x-virtualbox-vdi|x-virtualbox-vhd|x-virtualbox-vmdk|x-www-form-urlencoded)|font\/(?:otf|ttf)|image\/(?:bmp|vnd\.adobe\.photoshop|vnd\.microsoft\.icon|vnd\.ms-dds|x-icon|x-ms-bmp)|message\/rfc822|model\/gltf-binary|x-shader\/x-fragment|x-shader\/x-vertex|[^;\s]+?\+(?:json|text|xml|yaml))(?:[;\s]|$)/i;
|
|
@@ -2350,7 +2229,7 @@ var getStats = (path) => {
|
|
|
2350
2229
|
var serveStatic = (options = { root: "" }) => {
|
|
2351
2230
|
const root = options.root || "";
|
|
2352
2231
|
const optionPath = options.path;
|
|
2353
|
-
if (root !== "" && !
|
|
2232
|
+
if (root !== "" && !existsSync4(root)) {
|
|
2354
2233
|
console.error(`serveStatic: root path '${root}' is not found, are you sure it's correct?`);
|
|
2355
2234
|
}
|
|
2356
2235
|
return async (c, next) => {
|
|
@@ -2371,11 +2250,11 @@ var serveStatic = (options = { root: "" }) => {
|
|
|
2371
2250
|
return next();
|
|
2372
2251
|
}
|
|
2373
2252
|
}
|
|
2374
|
-
let path =
|
|
2253
|
+
let path = join2(root, !optionPath && options.rewriteRequestPath ? options.rewriteRequestPath(filename, c) : filename);
|
|
2375
2254
|
let stats = getStats(path);
|
|
2376
2255
|
if (stats && stats.isDirectory()) {
|
|
2377
2256
|
const indexFile = options.index ?? "index.html";
|
|
2378
|
-
path =
|
|
2257
|
+
path = join2(path, indexFile);
|
|
2379
2258
|
stats = getStats(path);
|
|
2380
2259
|
}
|
|
2381
2260
|
if (!stats) {
|
|
@@ -4033,32 +3912,12 @@ var cors = (options) => {
|
|
|
4033
3912
|
};
|
|
4034
3913
|
|
|
4035
3914
|
// src/commands/serve.ts
|
|
4036
|
-
import { join as
|
|
3915
|
+
import { join as join3, dirname as dirname3, resolve as resolve2, sep } from "path";
|
|
4037
3916
|
import { fileURLToPath } from "url";
|
|
4038
|
-
import { existsSync as
|
|
3917
|
+
import { existsSync as existsSync5, readFileSync as readFileSync4, statSync as statSync2 } from "fs";
|
|
4039
3918
|
import { spawn } from "child_process";
|
|
4040
|
-
|
|
4041
|
-
|
|
4042
|
-
var _createSqliteAdapter2;
|
|
4043
|
-
try {
|
|
4044
|
-
const mod = await Promise.resolve().then(() => (init_sqlite_adapter(), exports_sqlite_adapter));
|
|
4045
|
-
_createSqliteAdapter2 = mod.createSqliteAdapter;
|
|
4046
|
-
} catch {}
|
|
4047
|
-
function createSqliteAdapter3(filePath) {
|
|
4048
|
-
if (!_createSqliteAdapter2) {
|
|
4049
|
-
throw new Error("SQLite adapter requires Bun runtime");
|
|
4050
|
-
}
|
|
4051
|
-
return _createSqliteAdapter2(filePath);
|
|
4052
|
-
}
|
|
4053
|
-
function createAdapter2(filePath) {
|
|
4054
|
-
if (filePath.endsWith(".sqlite") || filePath.endsWith(".db")) {
|
|
4055
|
-
return createSqliteAdapter3(filePath);
|
|
4056
|
-
}
|
|
4057
|
-
return createJsonAdapter(filePath);
|
|
4058
|
-
}
|
|
4059
|
-
|
|
4060
|
-
// src/commands/serve.ts
|
|
4061
|
-
var __dirname2 = dirname4(fileURLToPath(import.meta.url));
|
|
3919
|
+
import { createServer } from "net";
|
|
3920
|
+
var __dirname2 = dirname3(fileURLToPath(import.meta.url));
|
|
4062
3921
|
function openBrowser(url) {
|
|
4063
3922
|
const command = process.platform === "darwin" ? "open" : process.platform === "win32" ? "cmd" : "xdg-open";
|
|
4064
3923
|
const args = process.platform === "win32" ? ["/c", "start", "", url] : [url];
|
|
@@ -4177,8 +4036,7 @@ function createApp() {
|
|
|
4177
4036
|
}
|
|
4178
4037
|
function isPortAvailable(port) {
|
|
4179
4038
|
return new Promise((resolve3) => {
|
|
4180
|
-
const
|
|
4181
|
-
const server = net.createServer();
|
|
4039
|
+
const server = createServer();
|
|
4182
4040
|
server.once("error", () => resolve3(false));
|
|
4183
4041
|
server.once("listening", () => {
|
|
4184
4042
|
server.close();
|
|
@@ -4212,25 +4070,27 @@ async function serveCommand(args, flags, options = {}) {
|
|
|
4212
4070
|
console.error(e.message);
|
|
4213
4071
|
process.exit(1);
|
|
4214
4072
|
}
|
|
4215
|
-
const adapter =
|
|
4073
|
+
const adapter = createAdapter(dataFile);
|
|
4216
4074
|
setStorageAdapter(adapter);
|
|
4217
4075
|
initStore();
|
|
4218
4076
|
const app = createApp();
|
|
4219
4077
|
const webDistPaths = [
|
|
4220
|
-
|
|
4221
|
-
|
|
4222
|
-
|
|
4078
|
+
join3(__dirname2, "web"),
|
|
4079
|
+
join3(__dirname2, "../web"),
|
|
4080
|
+
join3(__dirname2, "../../../web/dist"),
|
|
4081
|
+
join3(__dirname2, "../../web/dist"),
|
|
4082
|
+
join3(process.cwd(), "packages/web/dist")
|
|
4223
4083
|
];
|
|
4224
4084
|
let webDistPath = null;
|
|
4225
4085
|
for (const p of webDistPaths) {
|
|
4226
|
-
if (
|
|
4086
|
+
if (existsSync5(join3(p, "index.html"))) {
|
|
4227
4087
|
webDistPath = p;
|
|
4228
4088
|
break;
|
|
4229
4089
|
}
|
|
4230
4090
|
}
|
|
4231
4091
|
app.all("/api/*", (c) => c.json({ error: "Not found" }, 404));
|
|
4232
4092
|
if (webDistPath) {
|
|
4233
|
-
const indexHtml =
|
|
4093
|
+
const indexHtml = readFileSync4(join3(webDistPath, "index.html"), "utf-8");
|
|
4234
4094
|
const staticHandler = serveStatic({ root: webDistPath });
|
|
4235
4095
|
const staticExtensions = /\.(js|css|png|jpg|jpeg|gif|svg|ico|woff2?|ttf|eot|map|json|webp|webm|mp4|mp3|pdf)$/i;
|
|
4236
4096
|
app.use("/*", async (c, next) => {
|
|
@@ -4239,11 +4099,11 @@ async function serveCommand(args, flags, options = {}) {
|
|
|
4239
4099
|
return next();
|
|
4240
4100
|
}
|
|
4241
4101
|
if (staticExtensions.test(path)) {
|
|
4242
|
-
const filePath =
|
|
4102
|
+
const filePath = join3(webDistPath, path);
|
|
4243
4103
|
if (!filePath.startsWith(webDistPath + sep)) {
|
|
4244
4104
|
return c.notFound();
|
|
4245
4105
|
}
|
|
4246
|
-
if (
|
|
4106
|
+
if (existsSync5(filePath) && statSync2(filePath).isFile()) {
|
|
4247
4107
|
return staticHandler(c, next);
|
|
4248
4108
|
}
|
|
4249
4109
|
return c.notFound();
|
|
@@ -4254,15 +4114,22 @@ async function serveCommand(args, flags, options = {}) {
|
|
|
4254
4114
|
app.get("/", (c) => c.text("Web UI not found. API available at /api/*"));
|
|
4255
4115
|
}
|
|
4256
4116
|
const url = `http://localhost:${port}`;
|
|
4257
|
-
|
|
4258
|
-
|
|
4259
|
-
|
|
4260
|
-
console.log(`Web UI: ${webDistPath}`);
|
|
4117
|
+
if (options.compact) {
|
|
4118
|
+
console.log("");
|
|
4119
|
+
console.log(`Kenzo is running at ${url}`);
|
|
4261
4120
|
} else {
|
|
4262
|
-
console.log(
|
|
4121
|
+
console.log(`Starting Kenzo on ${url}`);
|
|
4122
|
+
console.log(`Data file: ${dataFile}`);
|
|
4123
|
+
if (webDistPath) {
|
|
4124
|
+
console.log(`Web UI: ${webDistPath}`);
|
|
4125
|
+
} else {
|
|
4126
|
+
console.log("Web UI: not found (API only mode)");
|
|
4127
|
+
}
|
|
4263
4128
|
}
|
|
4264
4129
|
if (options.open || flags.open === true) {
|
|
4265
|
-
|
|
4130
|
+
if (!options.compact) {
|
|
4131
|
+
console.log(`Opening ${url}`);
|
|
4132
|
+
}
|
|
4266
4133
|
openBrowser(url);
|
|
4267
4134
|
}
|
|
4268
4135
|
console.log("");
|
|
@@ -4494,11 +4361,11 @@ ${c.red}✗${c.reset} Auth timed out. Try again.`);
|
|
|
4494
4361
|
}
|
|
4495
4362
|
|
|
4496
4363
|
// src/commands/blob.ts
|
|
4497
|
-
import { readFileSync as
|
|
4364
|
+
import { readFileSync as readFileSync5, writeFileSync as writeFileSync4, existsSync as existsSync6 } from "fs";
|
|
4498
4365
|
import { basename, resolve as resolve3, extname } from "path";
|
|
4499
4366
|
function guessMimeType(filename) {
|
|
4500
4367
|
const ext = extname(filename).toLowerCase();
|
|
4501
|
-
const
|
|
4368
|
+
const types2 = {
|
|
4502
4369
|
".png": "image/png",
|
|
4503
4370
|
".jpg": "image/jpeg",
|
|
4504
4371
|
".jpeg": "image/jpeg",
|
|
@@ -4528,7 +4395,7 @@ function guessMimeType(filename) {
|
|
|
4528
4395
|
".xls": "application/vnd.ms-excel",
|
|
4529
4396
|
".xlsx": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
|
|
4530
4397
|
};
|
|
4531
|
-
return
|
|
4398
|
+
return types2[ext] || "application/octet-stream";
|
|
4532
4399
|
}
|
|
4533
4400
|
async function blobCommand(subcommand, args, flags, json) {
|
|
4534
4401
|
switch (subcommand) {
|
|
@@ -4539,11 +4406,11 @@ async function blobCommand(subcommand, args, flags, json) {
|
|
|
4539
4406
|
console.error("Usage: kenzoboard blob attach <task-id> <file-path>");
|
|
4540
4407
|
process.exit(1);
|
|
4541
4408
|
}
|
|
4542
|
-
if (!
|
|
4409
|
+
if (!existsSync6(filePath)) {
|
|
4543
4410
|
console.error(`File not found: ${filePath}`);
|
|
4544
4411
|
process.exit(1);
|
|
4545
4412
|
}
|
|
4546
|
-
const content =
|
|
4413
|
+
const content = readFileSync5(filePath);
|
|
4547
4414
|
const filename = basename(filePath);
|
|
4548
4415
|
const mime_type = guessMimeType(filename);
|
|
4549
4416
|
const blob = await uploadBlob(Buffer.from(content), filename, mime_type, taskId);
|
|
@@ -4568,7 +4435,7 @@ async function blobCommand(subcommand, args, flags, json) {
|
|
|
4568
4435
|
process.exit(1);
|
|
4569
4436
|
}
|
|
4570
4437
|
const destPath = outputPath || resolve3(process.cwd(), result.blob.filename);
|
|
4571
|
-
|
|
4438
|
+
writeFileSync4(destPath, result.content);
|
|
4572
4439
|
if (json) {
|
|
4573
4440
|
output({ path: destPath, ...result.blob }, true);
|
|
4574
4441
|
} else {
|
|
@@ -4626,6 +4493,7 @@ function formatSize(bytes) {
|
|
|
4626
4493
|
return `${(bytes / 1024).toFixed(1)}KB`;
|
|
4627
4494
|
return `${(bytes / (1024 * 1024)).toFixed(1)}MB`;
|
|
4628
4495
|
}
|
|
4496
|
+
|
|
4629
4497
|
// src/index.ts
|
|
4630
4498
|
var c2 = {
|
|
4631
4499
|
reset: "\x1B[0m",
|
|
@@ -4649,7 +4517,7 @@ function prompt(question) {
|
|
|
4649
4517
|
function isInteractive() {
|
|
4650
4518
|
return process.stdin.isTTY === true;
|
|
4651
4519
|
}
|
|
4652
|
-
var __dirname3 =
|
|
4520
|
+
var __dirname3 = dirname4(fileURLToPath2(import.meta.url));
|
|
4653
4521
|
function publicCommandName() {
|
|
4654
4522
|
const invoked = basename2(process.argv[1] || "");
|
|
4655
4523
|
return invoked === "flux" ? "flux" : "kenzoboard";
|
|
@@ -4666,22 +4534,11 @@ function isCliEntrypoint() {
|
|
|
4666
4534
|
function defaultServePort() {
|
|
4667
4535
|
return publicCommandName() === "flux" ? 3589 : 3000;
|
|
4668
4536
|
}
|
|
4669
|
-
function findExistingFluxDirFromCwd() {
|
|
4670
|
-
let dir = process.cwd();
|
|
4671
|
-
while (dirname5(dir) !== dir) {
|
|
4672
|
-
const fluxDir = resolve4(dir, ".flux");
|
|
4673
|
-
if (existsSync9(fluxDir)) {
|
|
4674
|
-
return fluxDir;
|
|
4675
|
-
}
|
|
4676
|
-
dir = dirname5(dir);
|
|
4677
|
-
}
|
|
4678
|
-
return null;
|
|
4679
|
-
}
|
|
4680
4537
|
function ensureFluxIgnored() {
|
|
4681
4538
|
const gitRoot = findGitRoot();
|
|
4682
4539
|
const gitignorePath = gitRoot ? resolve4(gitRoot, ".gitignore") : resolve4(process.cwd(), ".gitignore");
|
|
4683
4540
|
const gitignoreEntry = ".flux/";
|
|
4684
|
-
let gitignoreContent =
|
|
4541
|
+
let gitignoreContent = existsSync7(gitignorePath) ? readFileSync6(gitignorePath, "utf-8") : "";
|
|
4685
4542
|
if (!gitignoreContent.split(`
|
|
4686
4543
|
`).some((line) => line.trim() === gitignoreEntry)) {
|
|
4687
4544
|
const newline = gitignoreContent.length > 0 && !gitignoreContent.endsWith(`
|
|
@@ -4689,26 +4546,30 @@ function ensureFluxIgnored() {
|
|
|
4689
4546
|
` : "";
|
|
4690
4547
|
appendFileSync(gitignorePath, `${newline}${gitignoreEntry}
|
|
4691
4548
|
`);
|
|
4692
|
-
|
|
4549
|
+
return true;
|
|
4693
4550
|
}
|
|
4551
|
+
return false;
|
|
4694
4552
|
}
|
|
4695
4553
|
async function ensureKenzoWorkspace() {
|
|
4696
|
-
const fluxDir =
|
|
4554
|
+
const fluxDir = process.env.FLUX_DIR || resolve4(process.cwd(), ".flux");
|
|
4697
4555
|
const configPath = resolve4(fluxDir, "config.json");
|
|
4698
4556
|
const jsonPath = resolve4(fluxDir, "data.json");
|
|
4699
4557
|
const sqlitePath = resolve4(fluxDir, "data.sqlite");
|
|
4700
|
-
|
|
4701
|
-
|
|
4558
|
+
let createdWorkspace = false;
|
|
4559
|
+
let createdProject = false;
|
|
4560
|
+
let gitignoreUpdated = false;
|
|
4561
|
+
if (!existsSync7(fluxDir)) {
|
|
4562
|
+
mkdirSync3(fluxDir, { recursive: true });
|
|
4702
4563
|
writeConfig(fluxDir, {});
|
|
4703
|
-
|
|
4704
|
-
ensureFluxIgnored();
|
|
4705
|
-
|
|
4706
|
-
} else if (!
|
|
4564
|
+
writeFileSync5(jsonPath, JSON.stringify({ projects: [], epics: [], tasks: [] }, null, 2));
|
|
4565
|
+
gitignoreUpdated = ensureFluxIgnored();
|
|
4566
|
+
createdWorkspace = true;
|
|
4567
|
+
} else if (!existsSync7(configPath)) {
|
|
4707
4568
|
writeConfig(fluxDir, {});
|
|
4708
4569
|
}
|
|
4709
4570
|
const config = readConfigRaw(fluxDir);
|
|
4710
|
-
if (!config.server && !
|
|
4711
|
-
|
|
4571
|
+
if (!config.server && !existsSync7(jsonPath) && !existsSync7(sqlitePath)) {
|
|
4572
|
+
writeFileSync5(jsonPath, JSON.stringify({ projects: [], epics: [], tasks: [] }, null, 2));
|
|
4712
4573
|
}
|
|
4713
4574
|
const storage = initStorage();
|
|
4714
4575
|
const projects = await getProjects2();
|
|
@@ -4722,7 +4583,7 @@ async function ensureKenzoWorkspace() {
|
|
|
4722
4583
|
const nextConfig = readConfigRaw(fluxDir);
|
|
4723
4584
|
nextConfig.project = project.id;
|
|
4724
4585
|
writeConfig(fluxDir, nextConfig);
|
|
4725
|
-
|
|
4586
|
+
createdProject = true;
|
|
4726
4587
|
} else if (!projectId || !projects.some((project) => project.id === projectId)) {
|
|
4727
4588
|
const project = projects[0];
|
|
4728
4589
|
projectId = project.id;
|
|
@@ -4730,39 +4591,75 @@ async function ensureKenzoWorkspace() {
|
|
|
4730
4591
|
const nextConfig = readConfigRaw(fluxDir);
|
|
4731
4592
|
nextConfig.project = project.id;
|
|
4732
4593
|
writeConfig(fluxDir, nextConfig);
|
|
4733
|
-
console.log(`Using project: ${project.name} (${project.id})`);
|
|
4734
4594
|
}
|
|
4735
|
-
return { projectId, projectName };
|
|
4595
|
+
return { fluxDir, projectId, projectName, createdWorkspace, createdProject, gitignoreUpdated };
|
|
4736
4596
|
}
|
|
4737
|
-
function
|
|
4738
|
-
const
|
|
4597
|
+
function codexCliCommand() {
|
|
4598
|
+
const macAppCli = "/Applications/Codex.app/Contents/Resources/codex";
|
|
4599
|
+
return process.platform === "darwin" && existsSync7(macAppCli) ? macAppCli : "codex";
|
|
4600
|
+
}
|
|
4601
|
+
function quoteShell(value) {
|
|
4602
|
+
return `"${value.replace(/(["\\$`])/g, "\\$1")}"`;
|
|
4603
|
+
}
|
|
4604
|
+
function mcpPackageCommand() {
|
|
4739
4605
|
const localMcpPath = resolve4(process.cwd(), "packages/mcp/dist/index.js");
|
|
4740
|
-
|
|
4741
|
-
|
|
4742
|
-
|
|
4743
|
-
|
|
4744
|
-
|
|
4745
|
-
|
|
4746
|
-
|
|
4747
|
-
|
|
4748
|
-
|
|
4749
|
-
|
|
4750
|
-
|
|
4751
|
-
|
|
4606
|
+
return existsSync7(localMcpPath) ? `bun ${quoteShell(localMcpPath)}` : "npx -y --package kenzoboard kenzoboard-mcp";
|
|
4607
|
+
}
|
|
4608
|
+
function codexMcpCommand(fluxDir) {
|
|
4609
|
+
return `${codexCliCommand()} mcp add flux --env FLUX_DIR=${quoteShell(fluxDir)} -- ${mcpPackageCommand()}`;
|
|
4610
|
+
}
|
|
4611
|
+
function claudeMcpCommand(fluxDir) {
|
|
4612
|
+
return `claude mcp add flux --env FLUX_DIR=${quoteShell(fluxDir)} -- ${mcpPackageCommand()}`;
|
|
4613
|
+
}
|
|
4614
|
+
function printLaunchSummary(workspace, command = publicCommandName()) {
|
|
4615
|
+
const projectLabel = workspace.projectName && workspace.projectId ? `${workspace.projectName} (${workspace.projectId})` : "No project selected";
|
|
4616
|
+
console.log(`${c2.green}${c2.bold}Kenzo is ready.${c2.reset}`);
|
|
4617
|
+
console.log(`Project: ${projectLabel}`);
|
|
4618
|
+
if (workspace.createdWorkspace || workspace.createdProject || workspace.gitignoreUpdated) {
|
|
4619
|
+
const details = [
|
|
4620
|
+
workspace.createdWorkspace ? "local workspace" : undefined,
|
|
4621
|
+
workspace.createdProject ? "first project" : undefined,
|
|
4622
|
+
workspace.gitignoreUpdated ? ".flux/ in .gitignore" : undefined
|
|
4623
|
+
].filter(Boolean).join(", ");
|
|
4624
|
+
console.log(`${c2.dim}Set up ${details}.${c2.reset}`);
|
|
4752
4625
|
}
|
|
4753
4626
|
console.log("");
|
|
4754
|
-
console.log(
|
|
4627
|
+
console.log(`${c2.bold}Connect Codex:${c2.reset}`);
|
|
4628
|
+
console.log(` ${codexMcpCommand(workspace.fluxDir)}`);
|
|
4629
|
+
console.log("");
|
|
4630
|
+
console.log(`${c2.bold}Then ask Codex:${c2.reset}`);
|
|
4631
|
+
console.log(" Pick the next ready Kenzo task, implement it, and mark it done.");
|
|
4632
|
+
console.log("");
|
|
4633
|
+
console.log(`CLI fallback: ${c2.cyan}${command} ready${c2.reset}`);
|
|
4634
|
+
console.log(`More setup: ${c2.cyan}${command} mcp${c2.reset}`);
|
|
4635
|
+
}
|
|
4636
|
+
function printMcpSetup(command = publicCommandName(), fluxDir = findFluxDir()) {
|
|
4637
|
+
const installedMcpCommand = "kenzoboard-mcp";
|
|
4638
|
+
const dockerCommand = 'docker run -i --rm -v "$(pwd)/.flux:/app/packages/data" -e FLUX_DATA=/app/packages/data/flux.sqlite flux-mcp bun packages/mcp/dist/index.js';
|
|
4639
|
+
console.log(`${c2.bold}Agent setup${c2.reset} ${c2.dim}(MCP resources remain flux:// for compatibility)${c2.reset}`);
|
|
4640
|
+
console.log(`${c2.dim}Using Kenzo workspace: ${fluxDir}${c2.reset}
|
|
4641
|
+
`);
|
|
4642
|
+
console.log(`${c2.bold}Codex:${c2.reset}`);
|
|
4643
|
+
console.log(` ${codexMcpCommand(fluxDir)}`);
|
|
4644
|
+
console.log(`${c2.bold}Claude Code:${c2.reset}`);
|
|
4645
|
+
console.log(` ${claudeMcpCommand(fluxDir)}`);
|
|
4646
|
+
console.log("");
|
|
4647
|
+
console.log(`${c2.bold}Global install:${c2.reset}`);
|
|
4648
|
+
console.log(` ${installedMcpCommand}`);
|
|
4649
|
+
console.log("");
|
|
4650
|
+
console.log(`${c2.bold}Advanced Docker:${c2.reset}`);
|
|
4651
|
+
console.log(` ${dockerCommand}`);
|
|
4652
|
+
console.log("");
|
|
4653
|
+
console.log(`${c2.bold}Agent workflow:${c2.reset}`);
|
|
4654
|
+
console.log(` 1. Run ${c2.cyan}${command} ready${c2.reset} or ask the MCP server for ready tasks.`);
|
|
4655
|
+
console.log(" 2. Start one task, implement it, add notes as needed, then mark it done.");
|
|
4656
|
+
console.log(" 3. If MCP is not connected yet, Codex can still use the CLI commands from AGENTS.md.");
|
|
4755
4657
|
}
|
|
4756
4658
|
async function launchKenzoApp() {
|
|
4757
4659
|
const command = publicCommandName();
|
|
4758
|
-
const
|
|
4759
|
-
|
|
4760
|
-
|
|
4761
|
-
}
|
|
4762
|
-
console.log("");
|
|
4763
|
-
console.log(`${c2.bold}Next step after Kenzo opens:${c2.reset}`);
|
|
4764
|
-
printMcpSetup(command);
|
|
4765
|
-
await serveCommand([], { port: String(defaultServePort()) }, { defaultPort: defaultServePort(), open: true });
|
|
4660
|
+
const workspace = await ensureKenzoWorkspace();
|
|
4661
|
+
printLaunchSummary(workspace, command);
|
|
4662
|
+
await serveCommand([], { port: String(defaultServePort()) }, { defaultPort: defaultServePort(), open: true, compact: true });
|
|
4766
4663
|
}
|
|
4767
4664
|
var FLUX_INSTRUCTIONS = `<!-- FLUX:START -->
|
|
4768
4665
|
## Flux Task Management
|
|
@@ -4788,7 +4685,7 @@ function updateAgentInstructions() {
|
|
|
4788
4685
|
let targetFile = null;
|
|
4789
4686
|
for (const name of candidates) {
|
|
4790
4687
|
const path = resolve4(cwd, name);
|
|
4791
|
-
if (
|
|
4688
|
+
if (existsSync7(path)) {
|
|
4792
4689
|
targetFile = path;
|
|
4793
4690
|
break;
|
|
4794
4691
|
}
|
|
@@ -4796,7 +4693,7 @@ function updateAgentInstructions() {
|
|
|
4796
4693
|
if (!targetFile) {
|
|
4797
4694
|
targetFile = resolve4(cwd, "AGENTS.md");
|
|
4798
4695
|
}
|
|
4799
|
-
let content =
|
|
4696
|
+
let content = existsSync7(targetFile) ? readFileSync6(targetFile, "utf-8") : "";
|
|
4800
4697
|
const startMarker = "<!-- FLUX:START -->";
|
|
4801
4698
|
const endMarker = "<!-- FLUX:END -->";
|
|
4802
4699
|
const startIdx = content.indexOf(startMarker);
|
|
@@ -4809,7 +4706,7 @@ function updateAgentInstructions() {
|
|
|
4809
4706
|
` + FLUX_INSTRUCTIONS + `
|
|
4810
4707
|
`;
|
|
4811
4708
|
}
|
|
4812
|
-
|
|
4709
|
+
writeFileSync5(targetFile, content.trimStart());
|
|
4813
4710
|
return targetFile;
|
|
4814
4711
|
}
|
|
4815
4712
|
function findGitRoot() {
|
|
@@ -4821,7 +4718,7 @@ function findGitRoot() {
|
|
|
4821
4718
|
}
|
|
4822
4719
|
function ensureWorktree(gitRoot) {
|
|
4823
4720
|
const worktreePath = resolve4(gitRoot, ".git", "flux-worktree");
|
|
4824
|
-
if (
|
|
4721
|
+
if (existsSync7(worktreePath)) {
|
|
4825
4722
|
return worktreePath;
|
|
4826
4723
|
}
|
|
4827
4724
|
const branchExists = ["flux-data", "origin/flux-data"].some((ref) => {
|
|
@@ -4875,7 +4772,7 @@ async function doctorCommand(flags) {
|
|
|
4875
4772
|
`);
|
|
4876
4773
|
return;
|
|
4877
4774
|
}
|
|
4878
|
-
const fluxDirExists =
|
|
4775
|
+
const fluxDirExists = existsSync7(fluxDir);
|
|
4879
4776
|
if (!fluxDirExists) {
|
|
4880
4777
|
console.log(`${c2.yellow}!${c2.reset} .flux directory not found at ${fluxDir}`);
|
|
4881
4778
|
console.log(` Run ${c2.cyan}${publicCommandName()} init${c2.reset} to initialize
|
|
@@ -4884,7 +4781,7 @@ async function doctorCommand(flags) {
|
|
|
4884
4781
|
}
|
|
4885
4782
|
console.log(`${c2.green}OK${c2.reset} .flux directory: ${fluxDir}`);
|
|
4886
4783
|
const configPath = resolve4(fluxDir, "config.json");
|
|
4887
|
-
const configExists =
|
|
4784
|
+
const configExists = existsSync7(configPath);
|
|
4888
4785
|
if (configExists) {
|
|
4889
4786
|
const config2 = readConfig(fluxDir);
|
|
4890
4787
|
const mode = config2.server ? "server" : "local";
|
|
@@ -4896,8 +4793,8 @@ async function doctorCommand(flags) {
|
|
|
4896
4793
|
}
|
|
4897
4794
|
const jsonPath = resolve4(fluxDir, "data.json");
|
|
4898
4795
|
const sqlitePath = resolve4(fluxDir, "data.sqlite");
|
|
4899
|
-
const jsonExists =
|
|
4900
|
-
const sqliteExists =
|
|
4796
|
+
const jsonExists = existsSync7(jsonPath);
|
|
4797
|
+
const sqliteExists = existsSync7(sqlitePath);
|
|
4901
4798
|
const countRecords = (path) => {
|
|
4902
4799
|
try {
|
|
4903
4800
|
const adapter = createAdapter(path);
|
|
@@ -4968,8 +4865,8 @@ ${c2.cyan}Merging ${otherFile} into ${configuredFile}...${c2.reset}`);
|
|
|
4968
4865
|
console.log(`
|
|
4969
4866
|
${c2.dim}data.json is empty (likely created by old MCP/Server bug)${c2.reset}`);
|
|
4970
4867
|
if (fix) {
|
|
4971
|
-
const { unlinkSync:
|
|
4972
|
-
|
|
4868
|
+
const { unlinkSync: unlinkSync2 } = await import("fs");
|
|
4869
|
+
unlinkSync2(jsonPath);
|
|
4973
4870
|
console.log(`${c2.green}OK${c2.reset} Removed empty data.json`);
|
|
4974
4871
|
fixed++;
|
|
4975
4872
|
} else {
|
|
@@ -4980,8 +4877,8 @@ ${c2.dim}data.json is empty (likely created by old MCP/Server bug)${c2.reset}`);
|
|
|
4980
4877
|
console.log(`
|
|
4981
4878
|
${c2.dim}data.sqlite is empty${c2.reset}`);
|
|
4982
4879
|
if (fix) {
|
|
4983
|
-
const { unlinkSync:
|
|
4984
|
-
|
|
4880
|
+
const { unlinkSync: unlinkSync2 } = await import("fs");
|
|
4881
|
+
unlinkSync2(sqlitePath);
|
|
4985
4882
|
console.log(`${c2.green}OK${c2.reset} Removed empty data.sqlite`);
|
|
4986
4883
|
fixed++;
|
|
4987
4884
|
} else {
|
|
@@ -5073,14 +4970,14 @@ async function main() {
|
|
|
5073
4970
|
const dataFileName = useSqlite ? "data.sqlite" : "data.json";
|
|
5074
4971
|
const dataPath = resolve4(fluxDir, dataFileName);
|
|
5075
4972
|
const configPath = resolve4(fluxDir, "config.json");
|
|
5076
|
-
const isNew = !
|
|
5077
|
-
|
|
4973
|
+
const isNew = !existsSync7(resolve4(fluxDir, "data.json")) && !existsSync7(resolve4(fluxDir, "data.sqlite"));
|
|
4974
|
+
mkdirSync3(fluxDir, { recursive: true });
|
|
5078
4975
|
let serverUrl2 = parsed.flags.server;
|
|
5079
4976
|
let apiKey2 = parsed.flags["api-key"];
|
|
5080
4977
|
const useGit = parsed.flags.git === true;
|
|
5081
|
-
if (
|
|
4978
|
+
if (existsSync7(configPath)) {
|
|
5082
4979
|
try {
|
|
5083
|
-
const existing = JSON.parse(
|
|
4980
|
+
const existing = JSON.parse(readFileSync6(configPath, "utf-8"));
|
|
5084
4981
|
const warnings = [];
|
|
5085
4982
|
if (existing.server && (useGit || parsed.flags.sqlite)) {
|
|
5086
4983
|
warnings.push(`Repo uses server mode (${existing.server}), you're setting up git mode`);
|
|
@@ -5150,7 +5047,7 @@ Overwrite existing config? [y/N]: `);
|
|
|
5150
5047
|
const gitRoot = findGitRoot();
|
|
5151
5048
|
const gitignorePath = gitRoot ? resolve4(gitRoot, ".gitignore") : resolve4(process.cwd(), ".gitignore");
|
|
5152
5049
|
const gitignoreEntry = ".flux/";
|
|
5153
|
-
let gitignoreContent =
|
|
5050
|
+
let gitignoreContent = existsSync7(gitignorePath) ? readFileSync6(gitignorePath, "utf-8") : "";
|
|
5154
5051
|
if (!gitignoreContent.split(`
|
|
5155
5052
|
`).some((line) => line.trim() === gitignoreEntry)) {
|
|
5156
5053
|
const newline = gitignoreContent.length > 0 && !gitignoreContent.endsWith(`
|
|
@@ -5160,13 +5057,13 @@ Overwrite existing config? [y/N]: `);
|
|
|
5160
5057
|
`);
|
|
5161
5058
|
console.log(`Added .flux/ to ${gitRoot ? gitignorePath : ".gitignore"}`);
|
|
5162
5059
|
}
|
|
5163
|
-
if (!serverUrl2 && !
|
|
5060
|
+
if (!serverUrl2 && !existsSync7(dataPath)) {
|
|
5164
5061
|
if (useSqlite) {
|
|
5165
5062
|
const adapter = createAdapter(dataPath);
|
|
5166
5063
|
setStorageAdapter(adapter);
|
|
5167
5064
|
initStore();
|
|
5168
5065
|
} else {
|
|
5169
|
-
|
|
5066
|
+
writeFileSync5(dataPath, JSON.stringify({ projects: [], epics: [], tasks: [] }, null, 2));
|
|
5170
5067
|
}
|
|
5171
5068
|
}
|
|
5172
5069
|
if (isNew) {
|
|
@@ -5268,15 +5165,15 @@ Select a project:`);
|
|
|
5268
5165
|
const localBlobs = resolve4(fluxDir, "blobs");
|
|
5269
5166
|
execSync("git fetch origin flux-data", { stdio: "pipe", cwd: worktree });
|
|
5270
5167
|
execSync("git reset --hard origin/flux-data", { stdio: "pipe", cwd: worktree });
|
|
5271
|
-
if (
|
|
5272
|
-
|
|
5273
|
-
|
|
5168
|
+
if (existsSync7(worktreeData)) {
|
|
5169
|
+
mkdirSync3(fluxDir, { recursive: true });
|
|
5170
|
+
writeFileSync5(dataPath, readFileSync6(worktreeData, "utf-8"));
|
|
5274
5171
|
console.log("Pulled latest tasks from flux-data branch");
|
|
5275
5172
|
} else {
|
|
5276
5173
|
console.log("No .flux/data.json in flux-data branch yet");
|
|
5277
5174
|
}
|
|
5278
|
-
if (
|
|
5279
|
-
|
|
5175
|
+
if (existsSync7(worktreeBlobs)) {
|
|
5176
|
+
mkdirSync3(localBlobs, { recursive: true });
|
|
5280
5177
|
cpSync(worktreeBlobs, localBlobs, { recursive: true });
|
|
5281
5178
|
console.log("Pulled blobs from flux-data branch");
|
|
5282
5179
|
}
|
|
@@ -5286,7 +5183,7 @@ Select a project:`);
|
|
|
5286
5183
|
}
|
|
5287
5184
|
} else {
|
|
5288
5185
|
const msg = parsed.subcommand || "update tasks";
|
|
5289
|
-
if (!
|
|
5186
|
+
if (!existsSync7(dataPath)) {
|
|
5290
5187
|
console.error(`No .flux/data.json found. Run: ${publicCommandName()} init`);
|
|
5291
5188
|
process.exit(1);
|
|
5292
5189
|
}
|
|
@@ -5296,14 +5193,14 @@ Select a project:`);
|
|
|
5296
5193
|
const worktreeData = resolve4(worktreeFlux, "data.json");
|
|
5297
5194
|
const localBlobs = resolve4(fluxDir, "blobs");
|
|
5298
5195
|
const worktreeBlobs = resolve4(worktreeFlux, "blobs");
|
|
5299
|
-
|
|
5300
|
-
|
|
5301
|
-
if (
|
|
5302
|
-
|
|
5196
|
+
mkdirSync3(worktreeFlux, { recursive: true });
|
|
5197
|
+
writeFileSync5(worktreeData, readFileSync6(dataPath, "utf-8"));
|
|
5198
|
+
if (existsSync7(localBlobs)) {
|
|
5199
|
+
mkdirSync3(worktreeBlobs, { recursive: true });
|
|
5303
5200
|
cpSync(localBlobs, worktreeBlobs, { recursive: true });
|
|
5304
5201
|
}
|
|
5305
5202
|
execSync("git add .flux/data.json", { stdio: "pipe", cwd: worktree });
|
|
5306
|
-
if (
|
|
5203
|
+
if (existsSync7(worktreeBlobs)) {
|
|
5307
5204
|
execSync("git add .flux/blobs", { stdio: "pipe", cwd: worktree });
|
|
5308
5205
|
}
|
|
5309
5206
|
try {
|
|
@@ -5338,7 +5235,7 @@ Select a project:`);
|
|
|
5338
5235
|
if (parsed.command === "prime") {
|
|
5339
5236
|
const fluxDir = findFluxDir();
|
|
5340
5237
|
const configPath = resolve4(fluxDir, "config.json");
|
|
5341
|
-
if (!
|
|
5238
|
+
if (!existsSync7(configPath)) {
|
|
5342
5239
|
return;
|
|
5343
5240
|
}
|
|
5344
5241
|
try {
|
|
@@ -5382,7 +5279,7 @@ Select a project:`);
|
|
|
5382
5279
|
const output2 = JSON.stringify(data, null, 2);
|
|
5383
5280
|
const outFile = parsed.flags.o || parsed.flags.output;
|
|
5384
5281
|
if (outFile) {
|
|
5385
|
-
|
|
5282
|
+
writeFileSync5(outFile, output2);
|
|
5386
5283
|
console.log(`Exported to ${outFile}`);
|
|
5387
5284
|
} else {
|
|
5388
5285
|
console.log(output2);
|
|
@@ -5403,11 +5300,11 @@ Select a project:`);
|
|
|
5403
5300
|
}
|
|
5404
5301
|
content = Buffer.concat(chunks).toString("utf-8");
|
|
5405
5302
|
} else {
|
|
5406
|
-
if (!
|
|
5303
|
+
if (!existsSync7(file)) {
|
|
5407
5304
|
console.error(`File not found: ${file}`);
|
|
5408
5305
|
process.exit(1);
|
|
5409
5306
|
}
|
|
5410
|
-
content =
|
|
5307
|
+
content = readFileSync6(file, "utf-8");
|
|
5411
5308
|
}
|
|
5412
5309
|
const data = JSON.parse(content);
|
|
5413
5310
|
const merge = parsed.flags.merge === true;
|