@famonfa/mono 0.0.0-stage → 0.1.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/bin/mono.mjs +210 -0
- package/node_modules/@anthropic-ai/claude-agent-sdk/LICENSE.md +1 -0
- package/node_modules/@anthropic-ai/claude-agent-sdk/README.md +69 -0
- package/node_modules/@anthropic-ai/claude-agent-sdk/agentSdkTypes.d.ts +1 -0
- package/node_modules/@anthropic-ai/claude-agent-sdk/bridge.d.ts +396 -0
- package/node_modules/@anthropic-ai/claude-agent-sdk/bridge.mjs +142 -0
- package/node_modules/@anthropic-ai/claude-agent-sdk/browser-sdk.d.ts +208 -0
- package/node_modules/@anthropic-ai/claude-agent-sdk/browser-sdk.js +145 -0
- package/node_modules/@anthropic-ai/claude-agent-sdk/core-36zyfan5.mjs +52 -0
- package/node_modules/@anthropic-ai/claude-agent-sdk/core-5682eevd.mjs +27 -0
- package/node_modules/@anthropic-ai/claude-agent-sdk/core-nne0e2ds.mjs +40 -0
- package/node_modules/@anthropic-ai/claude-agent-sdk/core-xpxe8nzb.mjs +26 -0
- package/node_modules/@anthropic-ai/claude-agent-sdk/core.d.ts +22 -0
- package/node_modules/@anthropic-ai/claude-agent-sdk/core.mjs +34 -0
- package/node_modules/@anthropic-ai/claude-agent-sdk/extractFromBunfs.d.ts +1 -0
- package/node_modules/@anthropic-ai/claude-agent-sdk/extractFromBunfs.js +156 -0
- package/node_modules/@anthropic-ai/claude-agent-sdk/manifest.json +82 -0
- package/node_modules/@anthropic-ai/claude-agent-sdk/manifest.zst.json +90 -0
- package/node_modules/@anthropic-ai/claude-agent-sdk/package.json +94 -0
- package/node_modules/@anthropic-ai/claude-agent-sdk/sdk-tools.d.ts +4222 -0
- package/node_modules/@anthropic-ai/claude-agent-sdk/sdk.d.ts +10277 -0
- package/node_modules/@anthropic-ai/claude-agent-sdk/sdk.mjs +169 -0
- package/node_modules/ws/LICENSE +20 -0
- package/node_modules/ws/README.md +548 -0
- package/node_modules/ws/browser.js +8 -0
- package/node_modules/ws/index.js +22 -0
- package/node_modules/ws/lib/buffer-util.js +131 -0
- package/node_modules/ws/lib/constants.js +19 -0
- package/node_modules/ws/lib/event-target.js +292 -0
- package/node_modules/ws/lib/extension.js +203 -0
- package/node_modules/ws/lib/limiter.js +55 -0
- package/node_modules/ws/lib/permessage-deflate.js +530 -0
- package/node_modules/ws/lib/receiver.js +743 -0
- package/node_modules/ws/lib/sender.js +607 -0
- package/node_modules/ws/lib/stream.js +161 -0
- package/node_modules/ws/lib/subprotocol.js +62 -0
- package/node_modules/ws/lib/validation.js +152 -0
- package/node_modules/ws/lib/websocket-server.js +562 -0
- package/node_modules/ws/lib/websocket.js +1421 -0
- package/node_modules/ws/package.json +74 -0
- package/node_modules/ws/wrapper.mjs +21 -0
- package/package.json +126 -3
- package/skills/workspace/SKILL.md +108 -0
- package/src/api.mjs +279 -0
- package/src/chats.mjs +419 -0
- package/src/commands.mjs +100 -0
- package/src/groups.mjs +84 -0
- package/src/launcher.mjs +212 -0
- package/src/main.mjs +42 -0
- package/src/paths.mjs +44 -0
- package/src/projects.mjs +199 -0
- package/src/refresh.mjs +124 -0
- package/src/remote.mjs +129 -0
- package/src/sessions.mjs +422 -0
- package/src/store.mjs +253 -0
- package/web/assets/index-BMbPwlF6.js +55 -0
- package/web/assets/index-Ke-WlOuz.css +1 -0
- package/web/icon.svg +1 -0
- package/web/index.html +17 -0
- package/web/manifest.webmanifest +9 -0
- package/README.md +0 -3
package/src/store.mjs
ADDED
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { DATA_DIR, PKG_DIR, REPO_ROOT } from "./paths.mjs";
|
|
4
|
+
|
|
5
|
+
export { DATA_DIR };
|
|
6
|
+
export const ROOT = REPO_ROOT ?? PKG_DIR;
|
|
7
|
+
export const TASKS_DIR = path.join(DATA_DIR, "tasks");
|
|
8
|
+
|
|
9
|
+
const SAFE_ID = /^[A-Za-z0-9._-]+$/;
|
|
10
|
+
const IMAGE_EXT = /\.(png|jpe?g|gif|webp|svg)$/i;
|
|
11
|
+
|
|
12
|
+
export function assertSafe(...parts) {
|
|
13
|
+
for (const part of parts) {
|
|
14
|
+
if (!SAFE_ID.test(part)) throw new Error(`Nombre inválido: ${part}`);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const taskDir = (id) => (assertSafe(id), path.join(TASKS_DIR, id));
|
|
19
|
+
const boardPath = (id, boardId) => (assertSafe(boardId), path.join(taskDir(id), "boards", `${boardId}.json`));
|
|
20
|
+
const docPath = (id, name) => (assertSafe(name), path.join(taskDir(id), "docs", name));
|
|
21
|
+
|
|
22
|
+
export function readJson(file, fallback) {
|
|
23
|
+
try {
|
|
24
|
+
return JSON.parse(fs.readFileSync(file, "utf8"));
|
|
25
|
+
} catch {
|
|
26
|
+
return fallback;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function writeJson(file, value) {
|
|
31
|
+
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
32
|
+
const tmp = `${file}.tmp`;
|
|
33
|
+
fs.writeFileSync(tmp, JSON.stringify(value, null, 2) + "\n");
|
|
34
|
+
fs.renameSync(tmp, file);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function listFiles(dir, filter) {
|
|
38
|
+
try {
|
|
39
|
+
return fs
|
|
40
|
+
.readdirSync(dir)
|
|
41
|
+
.filter((f) => !f.startsWith(".") && filter(f))
|
|
42
|
+
.sort();
|
|
43
|
+
} catch {
|
|
44
|
+
return [];
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function listTasks() {
|
|
49
|
+
return listFiles(TASKS_DIR, (f) => fs.statSync(path.join(TASKS_DIR, f)).isDirectory())
|
|
50
|
+
.map((id) => getTask(id))
|
|
51
|
+
.filter(Boolean)
|
|
52
|
+
.sort((a, b) => (b.updatedAt ?? "").localeCompare(a.updatedAt ?? ""));
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export function getTask(id) {
|
|
56
|
+
const dir = taskDir(id);
|
|
57
|
+
if (!fs.existsSync(dir)) return null;
|
|
58
|
+
const meta = readJson(path.join(dir, "task.json"), {});
|
|
59
|
+
const boards = listFiles(path.join(dir, "boards"), (f) => f.endsWith(".json")).map((f) => {
|
|
60
|
+
const board = readJson(path.join(dir, "boards", f), {});
|
|
61
|
+
const boardId = f.replace(/\.json$/, "");
|
|
62
|
+
return { id: boardId, title: board.title ?? boardId, kind: board.kind ?? "free", order: board.order ?? 99, nodeCount: board.nodes?.filter((n) => n.type !== "lane").length ?? 0 };
|
|
63
|
+
});
|
|
64
|
+
boards.sort((a, b) => a.order - b.order || a.title.localeCompare(b.title));
|
|
65
|
+
return {
|
|
66
|
+
status: "en-curso",
|
|
67
|
+
title: id,
|
|
68
|
+
...meta,
|
|
69
|
+
id,
|
|
70
|
+
boards,
|
|
71
|
+
docs: listFiles(path.join(dir, "docs"), (f) => f.endsWith(".md")),
|
|
72
|
+
assets: listFiles(path.join(dir, "assets"), (f) => IMAGE_EXT.test(f)),
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export function saveTask(id, patch) {
|
|
77
|
+
const file = path.join(taskDir(id), "task.json");
|
|
78
|
+
const current = readJson(file, {});
|
|
79
|
+
const { boards, docs, assets, ...rest } = patch;
|
|
80
|
+
const next = { ...current, ...rest, id, updatedAt: new Date().toISOString() };
|
|
81
|
+
writeJson(file, next);
|
|
82
|
+
return getTask(id);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export function trashTask(id) {
|
|
86
|
+
const trash = path.join(DATA_DIR, ".trash");
|
|
87
|
+
fs.mkdirSync(trash, { recursive: true });
|
|
88
|
+
fs.renameSync(taskDir(id), path.join(trash, `${id}-${Date.now()}`));
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export function getBoard(id, boardId) {
|
|
92
|
+
return readJson(boardPath(id, boardId), null);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export function saveBoard(id, boardId, board) {
|
|
96
|
+
writeJson(boardPath(id, boardId), { ...board, id: boardId });
|
|
97
|
+
touchTask(id);
|
|
98
|
+
return getBoard(id, boardId);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const HISTORY_LIMIT = 30;
|
|
102
|
+
const historyDir = (id, boardId) => (assertSafe(boardId), path.join(DATA_DIR, ".history", id, boardId));
|
|
103
|
+
|
|
104
|
+
/** Copies the board as it is now, before an agent overwrites it, so the change can be undone. */
|
|
105
|
+
export function snapshotBoard(id, boardId) {
|
|
106
|
+
const file = boardPath(id, boardId);
|
|
107
|
+
if (!fs.existsSync(file)) return null;
|
|
108
|
+
const dir = historyDir(id, boardId);
|
|
109
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
110
|
+
const name = `${new Date().toISOString().replace(/[:.]/g, "-")}.json`;
|
|
111
|
+
fs.copyFileSync(file, path.join(dir, name));
|
|
112
|
+
const all = fs.readdirSync(dir).sort();
|
|
113
|
+
for (const old of all.slice(0, Math.max(0, all.length - HISTORY_LIMIT))) fs.rmSync(path.join(dir, old));
|
|
114
|
+
return name;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
export function boardHistory(id, boardId) {
|
|
118
|
+
try {
|
|
119
|
+
return fs.readdirSync(historyDir(id, boardId)).sort().reverse();
|
|
120
|
+
} catch {
|
|
121
|
+
return [];
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/** Puts back the most recent snapshot and removes it from the history (one step of undo). */
|
|
126
|
+
export function restoreBoard(id, boardId) {
|
|
127
|
+
const [latest] = boardHistory(id, boardId);
|
|
128
|
+
if (!latest) throw new Error("No hay versiones anteriores de este board");
|
|
129
|
+
const snapshot = path.join(historyDir(id, boardId), latest);
|
|
130
|
+
writeJson(boardPath(id, boardId), JSON.parse(fs.readFileSync(snapshot, "utf8")));
|
|
131
|
+
fs.rmSync(snapshot);
|
|
132
|
+
touchTask(id);
|
|
133
|
+
return latest;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
export function deleteBoard(id, boardId) {
|
|
137
|
+
fs.rmSync(boardPath(id, boardId), { force: true });
|
|
138
|
+
touchTask(id);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
export function getDoc(id, name) {
|
|
142
|
+
try {
|
|
143
|
+
return fs.readFileSync(docPath(id, name), "utf8");
|
|
144
|
+
} catch {
|
|
145
|
+
return null;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
export function saveDoc(id, name, content) {
|
|
150
|
+
const file = docPath(id, name);
|
|
151
|
+
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
152
|
+
fs.writeFileSync(file, content);
|
|
153
|
+
touchTask(id);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
function touchTask(id) {
|
|
157
|
+
const file = path.join(taskDir(id), "task.json");
|
|
158
|
+
const current = readJson(file, { id, title: id });
|
|
159
|
+
writeJson(file, { ...current, updatedAt: new Date().toISOString() });
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
const DATA_KEYS = ["label", "detail", "status", "test", "repo", "files", "image", "lane", "activity", "color", "kind", "link"];
|
|
163
|
+
|
|
164
|
+
/** Accepts the flat node shape the AI writes ({id, type, label, status, ...}) and returns a React Flow node. */
|
|
165
|
+
export function normalizeNode(node) {
|
|
166
|
+
const data = { ...(node.data ?? {}) };
|
|
167
|
+
for (const key of DATA_KEYS) {
|
|
168
|
+
if (node[key] !== undefined && data[key] === undefined) data[key] = node[key];
|
|
169
|
+
}
|
|
170
|
+
const out = { id: String(node.id), type: node.type ?? "card", data };
|
|
171
|
+
if (node.position) out.position = node.position;
|
|
172
|
+
if (node.parentId) out.parentId = node.parentId;
|
|
173
|
+
if (node.width) out.width = node.width;
|
|
174
|
+
if (node.height) out.height = node.height;
|
|
175
|
+
if (node.style) out.style = node.style;
|
|
176
|
+
return out;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
export function normalizeEdge(edge, index) {
|
|
180
|
+
const source = String(edge.source ?? edge.from);
|
|
181
|
+
const target = String(edge.target ?? edge.to);
|
|
182
|
+
return {
|
|
183
|
+
id: String(edge.id ?? `${source}->${target}${index ? `#${index}` : ""}`),
|
|
184
|
+
source,
|
|
185
|
+
target,
|
|
186
|
+
...(edge.label ? { label: edge.label } : {}),
|
|
187
|
+
...(edge.animated ? { animated: true } : {}),
|
|
188
|
+
...(edge.dashed ? { data: { dashed: true } } : {}),
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Merges an AI-generated board into what is on disk without destroying the user's work.
|
|
194
|
+
* With `patch`, incoming nodes and edges are upserted and nothing existing is removed.
|
|
195
|
+
* Otherwise:
|
|
196
|
+
* positions the user arranged are kept, nodes edited in the UI keep their data,
|
|
197
|
+
* and nodes or edges created in the UI survive regeneration.
|
|
198
|
+
*/
|
|
199
|
+
export function mergeBoard(existing, incoming, { relayout = false, patch = false } = {}) {
|
|
200
|
+
const prevNodes = new Map((existing?.nodes ?? []).map((n) => [n.id, n]));
|
|
201
|
+
// Layout belongs to the app: agent-supplied lanes, parents and positions are ignored (a lane without
|
|
202
|
+
// a position once blanked a whole board). Lanes come from each node's `lane` name instead.
|
|
203
|
+
const incomingNodes = (incoming.nodes ?? [])
|
|
204
|
+
.map(normalizeNode)
|
|
205
|
+
.filter((n) => n.type !== "lane")
|
|
206
|
+
.map(({ position, parentId, width, height, ...n }) => n);
|
|
207
|
+
const incomingIds = new Set(incomingNodes.map((n) => n.id));
|
|
208
|
+
|
|
209
|
+
const nodes = incomingNodes.map((node) => {
|
|
210
|
+
const prev = prevNodes.get(node.id);
|
|
211
|
+
if (!prev) return node;
|
|
212
|
+
const data = prev.data?.edited ? prev.data : { ...node.data, edited: undefined };
|
|
213
|
+
const merged = { ...node, data };
|
|
214
|
+
if (!relayout && prev.position) {
|
|
215
|
+
merged.position = prev.position;
|
|
216
|
+
if (prev.parentId) merged.parentId = prev.parentId;
|
|
217
|
+
if (prev.width) merged.width = prev.width;
|
|
218
|
+
if (prev.height) merged.height = prev.height;
|
|
219
|
+
}
|
|
220
|
+
return merged;
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
const isUserOwned = (n) => patch || n.data?.manual || (n.type === "lane" && !relayout);
|
|
224
|
+
for (const prev of prevNodes.values()) {
|
|
225
|
+
if (!incomingIds.has(prev.id) && isUserOwned(prev)) nodes.push(prev);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
const keptIds = new Set(nodes.map((n) => n.id));
|
|
229
|
+
const incomingEdges = (incoming.edges ?? []).map(normalizeEdge);
|
|
230
|
+
const incomingEdgeIds = new Set(incomingEdges.map((e) => e.id));
|
|
231
|
+
const manualEdges = (existing?.edges ?? []).filter((e) => (patch || e.data?.manual) && !incomingEdgeIds.has(e.id));
|
|
232
|
+
const edges = [...incomingEdges, ...manualEdges].filter((e) => keptIds.has(e.source) && keptIds.has(e.target));
|
|
233
|
+
|
|
234
|
+
if (relayout) {
|
|
235
|
+
for (const n of nodes) {
|
|
236
|
+
delete n.position;
|
|
237
|
+
delete n.parentId;
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
const finalNodes = relayout ? nodes.filter((n) => n.type !== "lane") : nodes;
|
|
241
|
+
|
|
242
|
+
return {
|
|
243
|
+
...(existing ?? {}),
|
|
244
|
+
title: incoming.title ?? existing?.title,
|
|
245
|
+
kind: incoming.kind ?? existing?.kind ?? "free",
|
|
246
|
+
layout: incoming.layout ?? existing?.layout ?? "lanes",
|
|
247
|
+
direction: incoming.direction ?? existing?.direction ?? "LR",
|
|
248
|
+
order: incoming.order ?? existing?.order,
|
|
249
|
+
description: incoming.description ?? existing?.description,
|
|
250
|
+
nodes: finalNodes,
|
|
251
|
+
edges,
|
|
252
|
+
};
|
|
253
|
+
}
|