@fui-org/fui-cli 2.1.1 → 2.1.2
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/fui.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import{enableCompileCache as a}from"node:module";a?.();var{run:e}=await import("./fui-
|
|
2
|
+
import{enableCompileCache as a}from"node:module";a?.();var{run:e}=await import("./fui-x86j3m2z.js");await e();
|
package/package.json
CHANGED
package/skills/fui/SKILL.md
CHANGED
|
@@ -67,7 +67,7 @@ description: Use the `fui` CLI for all FUI (low-code platform) work — projects
|
|
|
67
67
|
- Unchanged files are skipped via the publish ledger.
|
|
68
68
|
5. `fui file status` lists what is still unpublished.
|
|
69
69
|
|
|
70
|
-
`fui remote module --part ui|html|script|css|load|components|imports` and `fui remote project --part config|modules|…` show the server copy without touching the workspace. `fui remote diff
|
|
70
|
+
`fui remote module --part ui|html|script|css|load|components|imports` and `fui remote project --part config|modules|…` show the server copy without touching the workspace. `fui remote diff [path...]` compares local files with the server (no path: the whole target module); `--stat` adds the +/- line counts.
|
|
71
71
|
|
|
72
72
|
Ask the user before `module delete`, `component delete`, `import delete` and `user remove`.
|
|
73
73
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { atom, read, update } from "claude-code";
|
|
2
2
|
import type { EngineInterface as Engine, Register } from "claude-code";
|
|
3
3
|
|
|
4
|
-
import type { FuiTarget } from "../types";
|
|
4
|
+
import type { FuiDiff, FuiTarget } from "../types";
|
|
5
5
|
|
|
6
6
|
type SessionTarget = { projectId?: string; moduleId?: string; moduleDir?: string; workspaceRoot?: string };
|
|
7
7
|
|
|
@@ -12,11 +12,47 @@ const FUI_CALL = /(?:^|[;&|(]|\bthen|\bdo)\s*(?:[A-Za-z_]\w*=\S*\s+)*(?:bunx\s+)
|
|
|
12
12
|
|
|
13
13
|
// Mirrors sessionFile() in src/core/session/state.ts.
|
|
14
14
|
async function sessionFile($: Engine, command: string) {
|
|
15
|
-
const key = command
|
|
15
|
+
const key = sessionKey(command) ?? (await $.env.get("FUI_SESSION")) ?? "default";
|
|
16
16
|
const dir = (await $.env.get("FUI_SESSION_DIR")) ?? `${await $.env.get("HOME")}/.fui/session`;
|
|
17
17
|
return `${dir}/cli-${key}.json`;
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
+
const EDIT_TOOLS = new Set(["Edit", "Write", "MultiEdit", "NotebookEdit"]);
|
|
21
|
+
|
|
22
|
+
function sessionKey(command: string) {
|
|
23
|
+
return command.match(/FUI_SESSION=([A-Za-z0-9_-]+)/)?.[1];
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
async function runFui($: Engine, args: string[], session: string | undefined) {
|
|
27
|
+
const env = session ? { FUI_SESSION: session } : undefined;
|
|
28
|
+
try {
|
|
29
|
+
return await $.process.run(["fui", ...args], { env, timeoutMs: 30_000 });
|
|
30
|
+
} catch {
|
|
31
|
+
// fui is often on a PATH only the login shell sets up (bun, nvm).
|
|
32
|
+
const shell = (await $.env.get("SHELL")) ?? "/bin/zsh";
|
|
33
|
+
return await $.process.run([shell, "-lc", `fui ${args.join(" ")}`], { env, timeoutMs: 30_000 });
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Runs in the background: the numbers land on the line, never in the model's context.
|
|
38
|
+
async function remoteDiff($: Engine, session: string | undefined): Promise<FuiDiff | undefined> {
|
|
39
|
+
const { exitCode, stdout } = await runFui($, ["remote", "diff", "--stat", "--json"], session).catch(() => ({
|
|
40
|
+
exitCode: 1,
|
|
41
|
+
stdout: "",
|
|
42
|
+
}));
|
|
43
|
+
if (exitCode !== 0) return undefined;
|
|
44
|
+
try {
|
|
45
|
+
const rows = JSON.parse(stdout.slice(stdout.indexOf("["))) as { added?: number; removed?: number }[];
|
|
46
|
+
return {
|
|
47
|
+
files: rows.length,
|
|
48
|
+
added: rows.reduce((sum, row) => sum + (row.added ?? 0), 0),
|
|
49
|
+
removed: rows.reduce((sum, row) => sum + (row.removed ?? 0), 0),
|
|
50
|
+
};
|
|
51
|
+
} catch {
|
|
52
|
+
return undefined;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
20
56
|
async function readJson($: Engine, path: string): Promise<unknown> {
|
|
21
57
|
try {
|
|
22
58
|
return JSON.parse(await $.fs.read(path));
|
|
@@ -71,24 +107,65 @@ async function sync($: Engine, command: string, lastCommand: string) {
|
|
|
71
107
|
if (session?.projectId) {
|
|
72
108
|
found.project = session.projectId;
|
|
73
109
|
found.module = session.moduleId;
|
|
110
|
+
found.moduleDir = session.moduleDir;
|
|
74
111
|
found.projectUrl = await projectUrl($, session);
|
|
75
112
|
found.moduleUrl = session.moduleId ? await moduleUrl($, session) : undefined;
|
|
76
113
|
}
|
|
77
114
|
await update($, target, () => found);
|
|
78
115
|
}
|
|
79
116
|
|
|
117
|
+
let session: string | undefined;
|
|
118
|
+
let isStale = false;
|
|
119
|
+
let running: Promise<void> | undefined;
|
|
120
|
+
|
|
121
|
+
async function showDiff($: Engine, diff: FuiTarget["diff"]) {
|
|
122
|
+
const current: FuiTarget | null = await read($, target);
|
|
123
|
+
if (current?.moduleDir) await update($, target, () => ({ ...current, diff }));
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function refreshDiff($: Engine) {
|
|
127
|
+
isStale = false;
|
|
128
|
+
running ??= (async () => {
|
|
129
|
+
do {
|
|
130
|
+
isStale = false;
|
|
131
|
+
await showDiff($, "pending");
|
|
132
|
+
await showDiff($, await remoteDiff($, session));
|
|
133
|
+
} while (isStale);
|
|
134
|
+
})().finally(() => {
|
|
135
|
+
running = undefined;
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
|
|
80
139
|
export const register: Register = (on) => {
|
|
81
140
|
on("prompt.submit", async ($, e, next) => {
|
|
82
141
|
await update($, target, () => null);
|
|
83
142
|
return next(e);
|
|
84
143
|
});
|
|
85
144
|
|
|
145
|
+
on("tool.call", async ($, e, next) => {
|
|
146
|
+
const result = await next(e);
|
|
147
|
+
if (!EDIT_TOOLS.has(e.tool)) return result;
|
|
148
|
+
const current: FuiTarget | null = await read($, target);
|
|
149
|
+
const path =
|
|
150
|
+
(e as { file_path?: string; notebook_path?: string }).file_path ??
|
|
151
|
+
(e as { notebook_path?: string }).notebook_path;
|
|
152
|
+
if (current?.moduleDir && path?.startsWith(`${current.moduleDir}/`)) isStale = true;
|
|
153
|
+
return result;
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
on("turn.complete", async ($, e, next) => {
|
|
157
|
+
if (isStale) refreshDiff($);
|
|
158
|
+
return next(e);
|
|
159
|
+
});
|
|
160
|
+
|
|
86
161
|
on("tool.call", { tool: "Bash" }, async ($, e, next) => {
|
|
87
162
|
const result = await next(e);
|
|
88
163
|
const call = e.command.match(FUI_CALL);
|
|
89
164
|
if (!call) return result;
|
|
90
165
|
|
|
166
|
+
session = sessionKey(e.command);
|
|
91
167
|
await sync($, e.command, [call[1], call[2]].filter(Boolean).join(" "));
|
|
168
|
+
if (!(call[1] === "remote" && call[2] === "diff")) refreshDiff($);
|
|
92
169
|
return result;
|
|
93
170
|
});
|
|
94
171
|
|
|
@@ -120,7 +197,23 @@ export const register: Register = (on) => {
|
|
|
120
197
|
) : (
|
|
121
198
|
<Text dimColor>no active module</Text>
|
|
122
199
|
)}
|
|
123
|
-
{current.
|
|
200
|
+
{current.diff === "pending" && <Text dimColor> · diff…</Text>}
|
|
201
|
+
{current.diff && current.diff !== "pending" && (
|
|
202
|
+
<Text>
|
|
203
|
+
<Text dimColor> · </Text>
|
|
204
|
+
{current.diff.files === 0 ? (
|
|
205
|
+
<Text dimColor>in sync</Text>
|
|
206
|
+
) : (
|
|
207
|
+
<Text>
|
|
208
|
+
<Text>{`${current.diff.files} file${current.diff.files === 1 ? "" : "s"} `}</Text>
|
|
209
|
+
<Text color="green">{`+${current.diff.added}`}</Text>
|
|
210
|
+
<Text> </Text>
|
|
211
|
+
<Text color="red">{`−${current.diff.removed}`}</Text>
|
|
212
|
+
</Text>
|
|
213
|
+
)}
|
|
214
|
+
</Text>
|
|
215
|
+
)}
|
|
216
|
+
{current.lastCommand && <Text dimColor>{` · fui ${current.lastCommand}`}</Text>}
|
|
124
217
|
</Box>
|
|
125
218
|
);
|
|
126
219
|
});
|
|
@@ -1,9 +1,13 @@
|
|
|
1
|
+
export type FuiDiff = { files: number; added: number; removed: number };
|
|
2
|
+
|
|
1
3
|
export type FuiTarget = {
|
|
2
4
|
project?: string;
|
|
3
5
|
module?: string;
|
|
6
|
+
moduleDir?: string;
|
|
4
7
|
projectUrl?: string;
|
|
5
8
|
moduleUrl?: string;
|
|
6
9
|
lastCommand?: string;
|
|
10
|
+
diff?: FuiDiff | "pending";
|
|
7
11
|
};
|
|
8
12
|
|
|
9
13
|
declare module "claude-code" {
|