@floomhq/floom 2.0.6 → 2.0.7
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 +130 -20
- package/dist/version.js +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1916,7 +1916,7 @@ import { promisify } from "node:util";
|
|
|
1916
1916
|
var scrypt = promisify(scryptCb);
|
|
1917
1917
|
|
|
1918
1918
|
// ../shared/src/install-targets.ts
|
|
1919
|
-
import { homedir } from "node:os";
|
|
1919
|
+
import { homedir, platform } from "node:os";
|
|
1920
1920
|
import { stat } from "node:fs/promises";
|
|
1921
1921
|
import { isAbsolute, join, relative, resolve, sep } from "node:path";
|
|
1922
1922
|
var INSTALL_TARGETS = [
|
|
@@ -1956,6 +1956,27 @@ var TARGET_PARENT_DIRS = {
|
|
|
1956
1956
|
opencode: ".opencode"
|
|
1957
1957
|
};
|
|
1958
1958
|
var SYSTEM_ROOTS = /* @__PURE__ */ new Set(["/", "/etc", "/usr", "/bin", "/sbin", "/var", "/sys", "/proc"]);
|
|
1959
|
+
function xdgConfigHome(homeDir) {
|
|
1960
|
+
const xdg = process.env.XDG_CONFIG_HOME?.trim();
|
|
1961
|
+
if (xdg) return resolve(xdg);
|
|
1962
|
+
if (platform() === "win32") {
|
|
1963
|
+
const appdata = process.env.APPDATA?.trim();
|
|
1964
|
+
if (appdata) return resolve(appdata);
|
|
1965
|
+
}
|
|
1966
|
+
return join(homeDir ?? homedir(), ".config");
|
|
1967
|
+
}
|
|
1968
|
+
var TARGET_SENTINELS = {
|
|
1969
|
+
claude: [],
|
|
1970
|
+
// ~/.claude/ is claude-code-specific; directory alone is fine
|
|
1971
|
+
codex: [],
|
|
1972
|
+
// ~/.codex/ is codex-specific; directory alone is fine
|
|
1973
|
+
cursor: ["mcp.json", "cli-config.json"],
|
|
1974
|
+
// require a Cursor-written file
|
|
1975
|
+
gemini: [],
|
|
1976
|
+
// ~/.gemini/ is gemini-cli-specific; directory alone is fine
|
|
1977
|
+
opencode: []
|
|
1978
|
+
// ~/.config/opencode/ checked below; directory alone is fine
|
|
1979
|
+
};
|
|
1959
1980
|
function isInsideDir(path, parent) {
|
|
1960
1981
|
const rel = relative(parent, path);
|
|
1961
1982
|
return rel === "" || rel !== ".." && !rel.startsWith(`..${sep}`) && !isAbsolute(rel);
|
|
@@ -1979,7 +2000,8 @@ function envDirForTarget(target, homeDir) {
|
|
|
1979
2000
|
}
|
|
1980
2001
|
function presetDir(target, opts) {
|
|
1981
2002
|
const cwd = opts.cwd ?? process.cwd();
|
|
1982
|
-
const
|
|
2003
|
+
const homeDir = opts.homeDir ?? homedir();
|
|
2004
|
+
const root = opts.global ? homeDir : cwd;
|
|
1983
2005
|
switch (target) {
|
|
1984
2006
|
case "claude":
|
|
1985
2007
|
return join(root, ".claude", "skills");
|
|
@@ -1990,7 +2012,7 @@ function presetDir(target, opts) {
|
|
|
1990
2012
|
case "cursor":
|
|
1991
2013
|
return join(root, ".cursor", "skills");
|
|
1992
2014
|
case "opencode":
|
|
1993
|
-
return join(
|
|
2015
|
+
return opts.global ? join(xdgConfigHome(homeDir), "opencode", "skills") : join(cwd, ".opencode", "skills");
|
|
1994
2016
|
}
|
|
1995
2017
|
}
|
|
1996
2018
|
function resolveInstallDir(args) {
|
|
@@ -2020,14 +2042,33 @@ function resolveInstallDir(args) {
|
|
|
2020
2042
|
compatibleAgents: COMPATIBLE_AGENTS[target]
|
|
2021
2043
|
};
|
|
2022
2044
|
}
|
|
2045
|
+
function agentConfigRoot(target, homeDir) {
|
|
2046
|
+
if (target === "opencode") return join(xdgConfigHome(homeDir), "opencode");
|
|
2047
|
+
return join(homeDir, TARGET_PARENT_DIRS[target]);
|
|
2048
|
+
}
|
|
2023
2049
|
async function detectInstalledTargets(opts = {}) {
|
|
2024
2050
|
const homeDir = opts.homeDir ?? homedir();
|
|
2025
2051
|
const detected = [];
|
|
2026
2052
|
for (const target of INSTALL_TARGETS) {
|
|
2027
|
-
const
|
|
2053
|
+
const configRoot = agentConfigRoot(target, homeDir);
|
|
2028
2054
|
try {
|
|
2029
|
-
const
|
|
2030
|
-
if (
|
|
2055
|
+
const dirStat = await stat(configRoot);
|
|
2056
|
+
if (!dirStat.isDirectory()) continue;
|
|
2057
|
+
const sentinels = TARGET_SENTINELS[target];
|
|
2058
|
+
if (sentinels.length === 0) {
|
|
2059
|
+
detected.push(target);
|
|
2060
|
+
continue;
|
|
2061
|
+
}
|
|
2062
|
+
let found = false;
|
|
2063
|
+
for (const sentinel of sentinels) {
|
|
2064
|
+
try {
|
|
2065
|
+
await stat(join(configRoot, sentinel));
|
|
2066
|
+
found = true;
|
|
2067
|
+
break;
|
|
2068
|
+
} catch {
|
|
2069
|
+
}
|
|
2070
|
+
}
|
|
2071
|
+
if (found) detected.push(target);
|
|
2031
2072
|
} catch {
|
|
2032
2073
|
}
|
|
2033
2074
|
}
|
|
@@ -2675,7 +2716,7 @@ function normalizeApiUrl(apiUrl) {
|
|
|
2675
2716
|
}
|
|
2676
2717
|
|
|
2677
2718
|
// src/lib/machine.ts
|
|
2678
|
-
import { hostname, platform, type, release } from "node:os";
|
|
2719
|
+
import { hostname, platform as platform2, type, release } from "node:os";
|
|
2679
2720
|
import { join as join4 } from "node:path";
|
|
2680
2721
|
import { mkdir as mkdir3, readFile as readFile4, writeFile as writeFile2 } from "node:fs/promises";
|
|
2681
2722
|
import { homedir as homedir3 } from "node:os";
|
|
@@ -2709,7 +2750,7 @@ async function tryCommand(cmd, args) {
|
|
|
2709
2750
|
});
|
|
2710
2751
|
}
|
|
2711
2752
|
async function nativeDeviceName() {
|
|
2712
|
-
const p =
|
|
2753
|
+
const p = platform2();
|
|
2713
2754
|
if (p === "darwin") {
|
|
2714
2755
|
return tryCommand("scutil", ["--get", "ComputerName"]);
|
|
2715
2756
|
}
|
|
@@ -2722,7 +2763,7 @@ async function defaultLabel() {
|
|
|
2722
2763
|
const native = await nativeDeviceName();
|
|
2723
2764
|
const host = (hostname() || "").trim();
|
|
2724
2765
|
const base = native || host || `${type()}`.slice(0, 40);
|
|
2725
|
-
const p =
|
|
2766
|
+
const p = platform2();
|
|
2726
2767
|
const os = p === "darwin" ? "macOS" : p === "linux" ? "Linux" : p === "win32" ? "Windows" : type();
|
|
2727
2768
|
const label = base.toLowerCase().includes(os.toLowerCase()) ? base : `${base} \xB7 ${os}`;
|
|
2728
2769
|
return label.slice(0, 80);
|
|
@@ -2931,13 +2972,13 @@ import { spawn as spawn2 } from "node:child_process";
|
|
|
2931
2972
|
import chalk2 from "chalk";
|
|
2932
2973
|
function tryOpenBrowser(url) {
|
|
2933
2974
|
if (process.env.FLOOM_NO_OPEN === "1") return;
|
|
2934
|
-
const
|
|
2975
|
+
const platform3 = process.platform;
|
|
2935
2976
|
let cmd;
|
|
2936
2977
|
let args;
|
|
2937
|
-
if (
|
|
2978
|
+
if (platform3 === "darwin") {
|
|
2938
2979
|
cmd = "open";
|
|
2939
2980
|
args = [url];
|
|
2940
|
-
} else if (
|
|
2981
|
+
} else if (platform3 === "win32") {
|
|
2941
2982
|
cmd = "cmd";
|
|
2942
2983
|
args = ["/c", "start", "", url];
|
|
2943
2984
|
} else {
|
|
@@ -3016,6 +3057,7 @@ import { z as z3 } from "zod";
|
|
|
3016
3057
|
import { createHash as createHash3, randomUUID as randomUUID2 } from "node:crypto";
|
|
3017
3058
|
import { cp, lstat as lstat2, mkdir as mkdir4, readdir as readdir2, readFile as readFile5, rename, rm, stat as stat3, writeFile as writeFile3 } from "node:fs/promises";
|
|
3018
3059
|
import { dirname, join as join5, resolve as resolve2, sep as sep3 } from "node:path";
|
|
3060
|
+
import { homedir as osHomedir } from "node:os";
|
|
3019
3061
|
import { createInterface } from "node:readline/promises";
|
|
3020
3062
|
import { ZodError } from "zod";
|
|
3021
3063
|
import chalk3 from "chalk";
|
|
@@ -3149,9 +3191,72 @@ async function dirExists(path) {
|
|
|
3149
3191
|
return false;
|
|
3150
3192
|
}
|
|
3151
3193
|
}
|
|
3152
|
-
async function
|
|
3194
|
+
async function findGitRoot(startDir, stopAt) {
|
|
3195
|
+
const stop = stopAt ?? osHomedir();
|
|
3196
|
+
let current = resolve2(startDir);
|
|
3197
|
+
while (true) {
|
|
3198
|
+
try {
|
|
3199
|
+
const gitDir = join5(current, ".git");
|
|
3200
|
+
const s = await stat3(gitDir);
|
|
3201
|
+
if (s.isDirectory() || s.isFile()) return current;
|
|
3202
|
+
} catch {
|
|
3203
|
+
}
|
|
3204
|
+
const parent = dirname(current);
|
|
3205
|
+
if (parent === current || current === stop) break;
|
|
3206
|
+
current = parent;
|
|
3207
|
+
}
|
|
3208
|
+
return null;
|
|
3209
|
+
}
|
|
3210
|
+
function targetParentDirName(target) {
|
|
3211
|
+
switch (target) {
|
|
3212
|
+
case "claude":
|
|
3213
|
+
return ".claude";
|
|
3214
|
+
case "codex":
|
|
3215
|
+
return ".codex";
|
|
3216
|
+
case "cursor":
|
|
3217
|
+
return ".cursor";
|
|
3218
|
+
case "gemini":
|
|
3219
|
+
return ".gemini";
|
|
3220
|
+
case "opencode":
|
|
3221
|
+
return ".opencode";
|
|
3222
|
+
}
|
|
3223
|
+
}
|
|
3224
|
+
async function findProjectLocalSkillsDir(target, cwd) {
|
|
3225
|
+
const parentName = targetParentDirName(target);
|
|
3226
|
+
if (!parentName) return null;
|
|
3227
|
+
const home = resolve2(osHomedir());
|
|
3228
|
+
const gitRoot = await findGitRoot(cwd);
|
|
3229
|
+
const stopAt = gitRoot ?? home;
|
|
3230
|
+
let current = resolve2(cwd);
|
|
3231
|
+
while (true) {
|
|
3232
|
+
if (current !== home) {
|
|
3233
|
+
const agentDir = join5(current, parentName);
|
|
3234
|
+
if (await dirExists(agentDir)) {
|
|
3235
|
+
return join5(agentDir, "skills");
|
|
3236
|
+
}
|
|
3237
|
+
}
|
|
3238
|
+
const parent = dirname(current);
|
|
3239
|
+
if (parent === current || current === stopAt) break;
|
|
3240
|
+
current = parent;
|
|
3241
|
+
}
|
|
3242
|
+
if (stopAt !== home) {
|
|
3243
|
+
const agentDir = join5(stopAt, parentName);
|
|
3244
|
+
if (await dirExists(agentDir)) {
|
|
3245
|
+
return join5(agentDir, "skills");
|
|
3246
|
+
}
|
|
3247
|
+
}
|
|
3248
|
+
return null;
|
|
3249
|
+
}
|
|
3250
|
+
async function resolvePullDirs(target, cwd = process.cwd(), homeDir, globalOnly = false) {
|
|
3153
3251
|
const primary = resolveInstallDir({ target, global: true, homeDir }).dir;
|
|
3154
|
-
|
|
3252
|
+
if (globalOnly) {
|
|
3253
|
+
return uniqueResolvedDirs([primary]);
|
|
3254
|
+
}
|
|
3255
|
+
const projectLocalSkillsDir = await findProjectLocalSkillsDir(target, cwd);
|
|
3256
|
+
if (!projectLocalSkillsDir) {
|
|
3257
|
+
return uniqueResolvedDirs([primary]);
|
|
3258
|
+
}
|
|
3259
|
+
return uniqueResolvedDirs([primary, projectLocalSkillsDir]);
|
|
3155
3260
|
}
|
|
3156
3261
|
async function hasStatusVisibleProjectLocalDir(dir) {
|
|
3157
3262
|
if (await fileExists(manifestPath(dir))) return true;
|
|
@@ -3543,7 +3648,7 @@ async function pullCommand(options, deps = {}) {
|
|
|
3543
3648
|
}
|
|
3544
3649
|
const dirsByTarget = /* @__PURE__ */ new Map();
|
|
3545
3650
|
for (const target2 of targets) {
|
|
3546
|
-
const dirs2 = await resolvePullDirs(target2);
|
|
3651
|
+
const dirs2 = await resolvePullDirs(target2, process.cwd(), void 0, options.globalOnly);
|
|
3547
3652
|
dirsByTarget.set(target2, dirs2);
|
|
3548
3653
|
for (const dir of dirs2) cleanup.trackDir(join5(dir, ".floom", "tmp"));
|
|
3549
3654
|
}
|
|
@@ -3563,21 +3668,26 @@ async function pullCommand(options, deps = {}) {
|
|
|
3563
3668
|
}
|
|
3564
3669
|
log.heading("Pull summary:");
|
|
3565
3670
|
for (const result of results) {
|
|
3566
|
-
if (result.ok)
|
|
3567
|
-
|
|
3671
|
+
if (result.ok) {
|
|
3672
|
+
log.ok(`${result.target} ${result.skillCount} skills`);
|
|
3673
|
+
for (const dir of result.dirs) log.info(` \u2192 ${dir}`);
|
|
3674
|
+
} else {
|
|
3675
|
+
log.err(`${result.target} ${result.error.message}`);
|
|
3676
|
+
}
|
|
3568
3677
|
}
|
|
3569
3678
|
if (results.some((result) => !result.ok)) process.exitCode = 1;
|
|
3570
3679
|
return;
|
|
3571
3680
|
}
|
|
3572
3681
|
const target = assertInstallTarget(options.target);
|
|
3573
|
-
const dirs = await resolvePullDirs(target);
|
|
3682
|
+
const dirs = await resolvePullDirs(target, process.cwd(), void 0, options.globalOnly);
|
|
3574
3683
|
for (const dir of dirs) cleanup.trackDir(join5(dir, ".floom", "tmp"));
|
|
3575
3684
|
let skillCount = 0;
|
|
3576
3685
|
for (const dir of dirs) {
|
|
3577
3686
|
const result = await pullLibrary(target, { ...deps, installDir: dir });
|
|
3578
3687
|
skillCount = result.skillCount;
|
|
3579
3688
|
}
|
|
3580
|
-
log.ok(`Pulled ${skillCount} skills into ${target}
|
|
3689
|
+
log.ok(`Pulled ${skillCount} skills into ${target}.`);
|
|
3690
|
+
for (const dir of dirs) log.info(` \u2192 ${dir}`);
|
|
3581
3691
|
log.info(`This syncs ${target} only. For another agent: npx -y @floomhq/floom pull --target <claude|codex|cursor|gemini|opencode>`);
|
|
3582
3692
|
} finally {
|
|
3583
3693
|
cleanup.dispose();
|
|
@@ -4490,7 +4600,7 @@ program.command("logout").description("Sign out and clear local auth.").action(l
|
|
|
4490
4600
|
program.command("whoami").description("Show who you are signed in as.").action(whoamiCommand);
|
|
4491
4601
|
program.command("push [dir]").description("Publish a skill folder to your workspace.").option("--concurrency <n>", "Bulk push concurrency, 1-16", "6").action(pushCommand);
|
|
4492
4602
|
program.command("delete <slug>").description("Delete a skill from your workspace.").option("--yes", "Skip confirmation").action((slug, opts) => deleteCommand(slug, opts));
|
|
4493
|
-
program.command("pull").description("Pull the workspace library into your AI agents.").option("--target <target>", "claude | codex | cursor | gemini | opencode").action(pullCommand);
|
|
4603
|
+
program.command("pull").description("Pull the workspace library into your AI agents.").option("--target <target>", "claude | codex | cursor | gemini | opencode").option("--global-only", "Write only to the global agent dir; skip project-local .claude/skills/ etc.").action((opts) => pullCommand({ target: opts.target, globalOnly: opts.globalOnly }));
|
|
4494
4604
|
program.command("sync").description("Pull remote changes, then push any local edits.").option("--target <target>", "claude | codex | cursor | gemini | opencode").option("--yes", "Skip confirmation").action(syncCommand);
|
|
4495
4605
|
program.command("list").description("List workspace skills.").action(listCommand);
|
|
4496
4606
|
program.command("status").description("Show local workspace sync status.").option("--target <target>", "claude | codex | cursor | gemini | opencode").action(statusCommand);
|
package/dist/version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const VERSION = "2.0.
|
|
1
|
+
export const VERSION = "2.0.7";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@floomhq/floom",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.7",
|
|
4
4
|
"description": "Floom CLI \u2014 one shared skill library, pulled into the AI agent you choose (Claude, Codex, Cursor, Gemini, OpenCode).",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"homepage": "https://floom.dev",
|