@floomhq/floom 3.0.2 → 3.0.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/README.md +168 -0
- package/dist/index.js +80 -28
- package/dist/version.js +1 -1
- package/package.json +1 -1
package/README.md
ADDED
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
# @floomhq/floom
|
|
2
|
+
|
|
3
|
+
The Floom CLI: one shared skill library, synced across every AI agent.
|
|
4
|
+
|
|
5
|
+
Docs: <https://floom.dev/docs>
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm i -g @floomhq/floom
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Pinned no-install form for automation:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npx -y @floomhq/floom@3.0.2 --help
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Login
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
floom login
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
The login command starts a browser device flow. Sign in at floom.dev with
|
|
26
|
+
Google or email, approve the device code, and the CLI stores the session at
|
|
27
|
+
`~/.floom/auth.json`.
|
|
28
|
+
|
|
29
|
+
Check the active account:
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
floom account
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## First Skill
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
floom push ~/.claude/skills/my-skill
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
The folder must contain `SKILL.md`. Each push creates a Library version.
|
|
42
|
+
|
|
43
|
+
Create a starter skill when the folder does not exist yet:
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
floom new my-skill --agent claude
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Bulk Sync
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
floom sync
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Zero-arg `floom sync` detects installed agents and supported scopes, reviews
|
|
56
|
+
local-to-Library and Library-to-local differences, and applies the selected
|
|
57
|
+
plan. Use `--dry-run` to preview the plan without changing files or Library
|
|
58
|
+
records.
|
|
59
|
+
|
|
60
|
+
Library-to-local only:
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
floom pull
|
|
64
|
+
floom pull --agent claude,codex
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Local-to-Library only:
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
floom push
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Use the installed `floom` command for interactive terminal work. Use the pinned
|
|
74
|
+
`npx -y @floomhq/floom@3.0.2 <command>` form for automation and agent-run
|
|
75
|
+
commands that need reproducible CLI behavior.
|
|
76
|
+
|
|
77
|
+
## Releasing
|
|
78
|
+
|
|
79
|
+
Releases publish to npm and bump the Homebrew tap formula automatically via
|
|
80
|
+
`.github/workflows/publish-cli.yml`, triggered by a `cli-v*` tag.
|
|
81
|
+
|
|
82
|
+
### Hard pre-publish smoke gate
|
|
83
|
+
|
|
84
|
+
Every `npm publish` is gated by `packages/cli/scripts/pre-publish-smoke.sh`,
|
|
85
|
+
which packs the tarball, installs it into an isolated `$HOME`, and drives
|
|
86
|
+
every subcommand the way a real user would. If anything fails, the workflow
|
|
87
|
+
fails BEFORE `npm publish` runs — no broken CLI can reach npm `latest`.
|
|
88
|
+
|
|
89
|
+
Background: in 2026-05 we shipped `3.0.0` → `3.0.1` → `3.0.2` within hours
|
|
90
|
+
because `3.0.0` hit npm `latest` with three P0s that code review missed:
|
|
91
|
+
`--help` ran the destructive body, `--help | head` threw an unhandled EPIPE,
|
|
92
|
+
and unknown commands silently rendered the dashboard. Only an out-of-band
|
|
93
|
+
real-terminal smoke caught them. This script is that smoke, baked into the
|
|
94
|
+
publish pipeline.
|
|
95
|
+
|
|
96
|
+
### Before pushing a `cli-v*` tag
|
|
97
|
+
|
|
98
|
+
Run the local pre-tag check from the repo root:
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
./scripts/pre-tag-check.sh
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
It:
|
|
105
|
+
|
|
106
|
+
1. Confirms the working tree is clean.
|
|
107
|
+
2. Rebuilds the CLI from a clean `dist/`.
|
|
108
|
+
3. Runs the CLI unit test suite (`pnpm --filter @floomhq/floom test`).
|
|
109
|
+
4. Runs the full pre-publish smoke (same script the workflow runs).
|
|
110
|
+
|
|
111
|
+
If everything passes, the script prints the exact `git tag` + `git push origin`
|
|
112
|
+
commands. You then push the tag and the workflow takes over.
|
|
113
|
+
|
|
114
|
+
If anything fails, fix the issue first. Do not push the tag.
|
|
115
|
+
|
|
116
|
+
### Smoke-only workflow run
|
|
117
|
+
|
|
118
|
+
To validate the smoke script itself without publishing (e.g. after editing
|
|
119
|
+
`scripts/pre-publish-smoke.sh`):
|
|
120
|
+
|
|
121
|
+
```
|
|
122
|
+
GitHub → Actions → Publish @floomhq/floom → Run workflow
|
|
123
|
+
smoke_only: true
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
The workflow will build, run the smoke, and stop. No `npm publish`, no
|
|
127
|
+
Homebrew dispatch.
|
|
128
|
+
|
|
129
|
+
### Manual publish (rare)
|
|
130
|
+
|
|
131
|
+
To publish without a tag (e.g. retag an existing version under a different
|
|
132
|
+
dist-tag), use `workflow_dispatch` with `smoke_only: false` and the desired
|
|
133
|
+
`tag` input. The smoke gate still runs first.
|
|
134
|
+
|
|
135
|
+
## What the pre-publish smoke checks
|
|
136
|
+
|
|
137
|
+
See `scripts/pre-publish-smoke.sh` for the full assertion list. Summary:
|
|
138
|
+
|
|
139
|
+
| Stage | Asserts |
|
|
140
|
+
|------|---------|
|
|
141
|
+
| 1 | Tarball builds, packs, installs cleanly into isolated `$HOME`; installed `floom --version` matches `package.json` |
|
|
142
|
+
| 2 | **Every** subcommand `--help` exits 0, starts with `Usage: floom`, writes no `~/.floom/auth.json`, prints no "Signed (in|out)" / "Waiting for browser" (P0 #1 from 3.0.0) |
|
|
143
|
+
| 3 | `floom --help \| head -3` and `floom push --help \| head -1` print no EPIPE / "Unhandled error event" (P0 #2 from 3.0.0) |
|
|
144
|
+
| 4 | `floom <unknown>` exits non-zero with "unknown command" + "did you mean" hint (P0 #3 from 3.0.0) |
|
|
145
|
+
| 5 | Bare `floom --help` exits 0 with grouped help |
|
|
146
|
+
| 6 | Unauth `whoami` / `account` / `logout` exit 0 idempotent; unauth `status` / `sync --json` / `push --json` exit non-zero with no stack trace |
|
|
147
|
+
| 7 | `floom logout extra-positional` is rejected with non-zero exit AND `~/.floom/auth.json` is preserved (no destructive body) |
|
|
148
|
+
| 8 | **Agent detection** picks up fake `.claude/`, `.codex/`, `.cursor/` marker dirs in the isolated `$HOME` (verified by parsing `floom doctor --json`); bare `floom sync` / `floom pull` with planted agents do not crash unauth (clean `signedIn:false` envelope). Note: fan-out *behaviour itself* requires real auth, so it is exercised at the **unit-test layer** by `src/commands/sync.fanout.test.ts`, which the publish workflow runs as a separate hard gate before the smoke. |
|
|
149
|
+
|
|
150
|
+
If any assertion fails, the publish job fails. The smoke script exits non-zero
|
|
151
|
+
on the first failure and prints a summary of every failed check.
|
|
152
|
+
|
|
153
|
+
## Files
|
|
154
|
+
|
|
155
|
+
```
|
|
156
|
+
packages/cli/
|
|
157
|
+
├── src/ # TypeScript source
|
|
158
|
+
│ ├── index.ts # CLI entry point (commander)
|
|
159
|
+
│ ├── commands/ # one file per subcommand
|
|
160
|
+
│ ├── lib/ # shared helpers
|
|
161
|
+
│ └── cli-smoke.test.ts # unit-level smoke (P0 lock-ins)
|
|
162
|
+
├── scripts/
|
|
163
|
+
│ ├── build-bundle.mjs # esbuild → dist/index.js
|
|
164
|
+
│ ├── verify-package.mjs # pre-pack file allowlist check
|
|
165
|
+
│ └── pre-publish-smoke.sh # E2E smoke driven against tarball
|
|
166
|
+
├── package.json
|
|
167
|
+
└── CHANGELOG.md
|
|
168
|
+
```
|
package/dist/index.js
CHANGED
|
@@ -3253,7 +3253,7 @@ async function getMachineIdentity() {
|
|
|
3253
3253
|
}
|
|
3254
3254
|
|
|
3255
3255
|
// src/version.ts
|
|
3256
|
-
var VERSION = "3.0.
|
|
3256
|
+
var VERSION = "3.0.3";
|
|
3257
3257
|
|
|
3258
3258
|
// src/api-client.ts
|
|
3259
3259
|
var DEFAULT_TIMEOUT_MS = 2e4;
|
|
@@ -3721,17 +3721,35 @@ async function loginCommand() {
|
|
|
3721
3721
|
}
|
|
3722
3722
|
|
|
3723
3723
|
// src/commands/logout.ts
|
|
3724
|
-
async function logoutCommand() {
|
|
3725
|
-
const
|
|
3724
|
+
async function logoutCommand(opts = {}, deps = {}) {
|
|
3725
|
+
const readAuth2 = deps.readAuth ?? readAuth;
|
|
3726
|
+
const clearAuth2 = deps.clearAuth ?? clearAuth;
|
|
3727
|
+
const api2 = deps.api ?? api;
|
|
3728
|
+
const auth = await readAuth2();
|
|
3729
|
+
let remoteRevokeFailed = false;
|
|
3726
3730
|
if (auth) {
|
|
3727
3731
|
try {
|
|
3728
|
-
await
|
|
3732
|
+
await api2("/cli/session/revoke", {
|
|
3733
|
+
method: "POST",
|
|
3734
|
+
authRequired: true,
|
|
3735
|
+
query: opts.all ? { scope: "all" } : void 0
|
|
3736
|
+
});
|
|
3729
3737
|
} catch (error) {
|
|
3730
|
-
|
|
3738
|
+
if (opts.all) {
|
|
3739
|
+
remoteRevokeFailed = true;
|
|
3740
|
+
process.exitCode = 1;
|
|
3741
|
+
log.err(
|
|
3742
|
+
"Local session cleared. Other devices are still authenticated.\nSign in at https://floom.dev/settings to revoke other sessions from the web."
|
|
3743
|
+
);
|
|
3744
|
+
} else {
|
|
3745
|
+
log.warn(`Remote session revoke failed: ${error.message}`);
|
|
3746
|
+
}
|
|
3731
3747
|
}
|
|
3732
3748
|
}
|
|
3733
|
-
await
|
|
3734
|
-
log.ok(
|
|
3749
|
+
await clearAuth2();
|
|
3750
|
+
log.ok(
|
|
3751
|
+
remoteRevokeFailed ? "Local sign-out only \u2014 remote revoke failed." : opts.all ? "Signed out of Floom CLI on all machines." : "Signed out of Floom on this machine."
|
|
3752
|
+
);
|
|
3735
3753
|
log.blank();
|
|
3736
3754
|
log.info("Your local skill files in ~/.claude/skills, ~/.codex/skills, etc. were not changed.");
|
|
3737
3755
|
printNext([{ command: "floom login", description: "sign in again" }]);
|
|
@@ -5674,6 +5692,9 @@ async function pullCommand(skillArg, rawOpts = {}) {
|
|
|
5674
5692
|
}
|
|
5675
5693
|
}
|
|
5676
5694
|
|
|
5695
|
+
// src/commands/sync.ts
|
|
5696
|
+
import chalk5 from "chalk";
|
|
5697
|
+
|
|
5677
5698
|
// src/commands/sync-runner.ts
|
|
5678
5699
|
init_src();
|
|
5679
5700
|
import { cp as cp2, mkdtemp, readdir as readdir4, rm as rm2, stat as stat6 } from "node:fs/promises";
|
|
@@ -5845,7 +5866,9 @@ async function runSyncForTarget(options = {}, deps = {}) {
|
|
|
5845
5866
|
process.exitCode = 1;
|
|
5846
5867
|
return { ok: false, pushFailures, hasConflicts: false };
|
|
5847
5868
|
}
|
|
5848
|
-
|
|
5869
|
+
if (!options.quietSuccess) {
|
|
5870
|
+
log.ok(`Sync complete. Pulled ${plan.pull.length} skills. Pushed ${pushed}/${plan.push.length} skills.`);
|
|
5871
|
+
}
|
|
5849
5872
|
return { ok: true, pushFailures: [], hasConflicts: false };
|
|
5850
5873
|
}
|
|
5851
5874
|
|
|
@@ -5877,6 +5900,9 @@ function buildApplyJson(workspaceName, applied, opts) {
|
|
|
5877
5900
|
next: ["floom status"]
|
|
5878
5901
|
};
|
|
5879
5902
|
}
|
|
5903
|
+
function formatAgentLabel(plan) {
|
|
5904
|
+
return `${AGENT_LABELS[plan.agent]}${plan.scope === "project" ? " (project)" : ""}`;
|
|
5905
|
+
}
|
|
5880
5906
|
async function planForAgent(agent, scope, skillsDir, statusFn = statusLibrary) {
|
|
5881
5907
|
try {
|
|
5882
5908
|
const status = await statusFn(agent, { installDir: skillsDir });
|
|
@@ -6073,7 +6099,7 @@ async function syncCommand(rawOpts = {}, deps = {}) {
|
|
|
6073
6099
|
const applied = [];
|
|
6074
6100
|
for (const p of toSync) {
|
|
6075
6101
|
try {
|
|
6076
|
-
const result = await runSyncFn({ target: p.agent, yes: true, installDir: p.skillsDir });
|
|
6102
|
+
const result = await runSyncFn({ target: p.agent, yes: true, installDir: p.skillsDir, quietSuccess: true });
|
|
6077
6103
|
const entry = appliedFromResult(p, result);
|
|
6078
6104
|
if (!entry.ok) failed = true;
|
|
6079
6105
|
applied.push(entry);
|
|
@@ -6088,6 +6114,32 @@ async function syncCommand(rawOpts = {}, deps = {}) {
|
|
|
6088
6114
|
if (failed || hasSkipped) process.exitCode = 1;
|
|
6089
6115
|
return;
|
|
6090
6116
|
}
|
|
6117
|
+
if (failed) {
|
|
6118
|
+
const failedCount = applied.filter((a) => !a.ok).length;
|
|
6119
|
+
const succeededCount = applied.filter((a) => a.ok).length;
|
|
6120
|
+
log.blank();
|
|
6121
|
+
log.info(`Sync completed with ${failedCount} target(s) failing (${succeededCount} succeeded).`);
|
|
6122
|
+
for (const entry of applied) {
|
|
6123
|
+
const marker = entry.ok ? chalk5.green("\u2713") : chalk5.red("\u2717");
|
|
6124
|
+
log.info(` ${marker} ${formatAgentLabel(entry.plan)} ${entry.message}`);
|
|
6125
|
+
}
|
|
6126
|
+
log.blank();
|
|
6127
|
+
printNext([{ command: "floom status" }]);
|
|
6128
|
+
if (hasSkipped) {
|
|
6129
|
+
for (const p of plans) {
|
|
6130
|
+
for (const slug of p.conflicts) {
|
|
6131
|
+
log.warn(`${AGENT_LABELS[p.agent]} skipped ${slug}, changed in two places`);
|
|
6132
|
+
}
|
|
6133
|
+
}
|
|
6134
|
+
}
|
|
6135
|
+
process.exitCode = 1;
|
|
6136
|
+
return;
|
|
6137
|
+
}
|
|
6138
|
+
if (!hasSkipped) {
|
|
6139
|
+
const pulled = applied.reduce((n, a) => n + a.plan.pull.length, 0);
|
|
6140
|
+
const pushed = applied.reduce((n, a) => n + a.plan.push.length, 0);
|
|
6141
|
+
log.ok(`Sync complete. Pulled ${pulled} skill${pulled === 1 ? "" : "s"}. Pushed ${pushed}/${pushed} skill${pushed === 1 ? "" : "s"}.`);
|
|
6142
|
+
}
|
|
6091
6143
|
for (const p of plans) {
|
|
6092
6144
|
for (const slug of p.conflicts) {
|
|
6093
6145
|
log.warn(`${AGENT_LABELS[p.agent]} skipped ${slug}, changed in two places`);
|
|
@@ -6104,7 +6156,7 @@ async function syncCommand(rawOpts = {}, deps = {}) {
|
|
|
6104
6156
|
log.info("Backups of any replaced skills are in each agent's .floom/backups/ folder (run floom restore --list to see them).");
|
|
6105
6157
|
printNext([{ command: "floom status" }]);
|
|
6106
6158
|
}
|
|
6107
|
-
if (
|
|
6159
|
+
if (hasSkipped) process.exitCode = 1;
|
|
6108
6160
|
} finally {
|
|
6109
6161
|
cleanup.dispose();
|
|
6110
6162
|
}
|
|
@@ -6112,20 +6164,20 @@ async function syncCommand(rawOpts = {}, deps = {}) {
|
|
|
6112
6164
|
|
|
6113
6165
|
// src/commands/status.ts
|
|
6114
6166
|
import { resolve as resolve7 } from "node:path";
|
|
6115
|
-
import
|
|
6167
|
+
import chalk6 from "chalk";
|
|
6116
6168
|
init_runtime();
|
|
6117
6169
|
var AGENT_TIMEOUT_MS = 1e4;
|
|
6118
6170
|
var MAX_ATTENTION_ROWS = 15;
|
|
6119
6171
|
function toFloomState(state) {
|
|
6120
6172
|
switch (state) {
|
|
6121
6173
|
case "active":
|
|
6122
|
-
return { state: "up_to_date", label: "up to date", color:
|
|
6174
|
+
return { state: "up_to_date", label: "up to date", color: chalk6.green };
|
|
6123
6175
|
case "stale":
|
|
6124
|
-
return { state: "update_available", label: "update available", color:
|
|
6176
|
+
return { state: "update_available", label: "update available", color: chalk6.yellow };
|
|
6125
6177
|
case "dirty":
|
|
6126
|
-
return { state: "local_changes", label: "local changes", color:
|
|
6178
|
+
return { state: "local_changes", label: "local changes", color: chalk6.yellow };
|
|
6127
6179
|
case "conflict":
|
|
6128
|
-
return { state: "changed_in_two_places", label: "changed in two places", color:
|
|
6180
|
+
return { state: "changed_in_two_places", label: "changed in two places", color: chalk6.red };
|
|
6129
6181
|
case "missing":
|
|
6130
6182
|
return { state: "not_installed", label: "not installed", color: (s) => s };
|
|
6131
6183
|
case "unsupported_target":
|
|
@@ -6308,7 +6360,7 @@ async function statusCommand(rawOpts = {}) {
|
|
|
6308
6360
|
log.blank();
|
|
6309
6361
|
const shown = attention.slice(0, MAX_ATTENTION_ROWS);
|
|
6310
6362
|
for (const item of shown) {
|
|
6311
|
-
log.info(` ${
|
|
6363
|
+
log.info(` ${chalk6.bold(item.slug)}`);
|
|
6312
6364
|
for (const loc of item.locations) {
|
|
6313
6365
|
const c = toFloomLabel(loc.state);
|
|
6314
6366
|
log.info(` ${AGENT_LABELS[loc.agent].padEnd(10)} ${c.color(c.label.padEnd(22))}${tildePath(loc.path)}`);
|
|
@@ -6385,13 +6437,13 @@ function summarize(skills) {
|
|
|
6385
6437
|
function toFloomLabel(state) {
|
|
6386
6438
|
switch (state) {
|
|
6387
6439
|
case "up_to_date":
|
|
6388
|
-
return { label: "up to date", color:
|
|
6440
|
+
return { label: "up to date", color: chalk6.green };
|
|
6389
6441
|
case "local_changes":
|
|
6390
|
-
return { label: "local changes", color:
|
|
6442
|
+
return { label: "local changes", color: chalk6.yellow };
|
|
6391
6443
|
case "update_available":
|
|
6392
|
-
return { label: "update available", color:
|
|
6444
|
+
return { label: "update available", color: chalk6.yellow };
|
|
6393
6445
|
case "changed_in_two_places":
|
|
6394
|
-
return { label: "changed in two places", color:
|
|
6446
|
+
return { label: "changed in two places", color: chalk6.red };
|
|
6395
6447
|
case "not_in_library_never_published":
|
|
6396
6448
|
return { label: "not in Library", color: (s) => s };
|
|
6397
6449
|
case "not_in_library_removed":
|
|
@@ -7907,7 +7959,7 @@ async function dashboardCommand() {
|
|
|
7907
7959
|
}
|
|
7908
7960
|
|
|
7909
7961
|
// src/lib/help.ts
|
|
7910
|
-
import
|
|
7962
|
+
import chalk7 from "chalk";
|
|
7911
7963
|
var GROUPS = [
|
|
7912
7964
|
{
|
|
7913
7965
|
title: "Sign in",
|
|
@@ -7977,21 +8029,21 @@ var COMMON_FLAGS = `Common flags
|
|
|
7977
8029
|
--no-secret-check with push, skip the pre-publish secret scan`;
|
|
7978
8030
|
function printGroupedHelp() {
|
|
7979
8031
|
const out2 = process.stdout;
|
|
7980
|
-
out2.write("\n" +
|
|
7981
|
-
out2.write(` ${
|
|
8032
|
+
out2.write("\n" + chalk7.bold("Usage: floom [command]") + "\n\n");
|
|
8033
|
+
out2.write(` ${chalk7.cyan.bold("floom")} your dashboard \u2014 Library and agent status at a glance
|
|
7982
8034
|
`);
|
|
7983
8035
|
const allNames = GROUPS.flatMap((g) => g.rows.map((r) => r.name));
|
|
7984
8036
|
const width = Math.max(...allNames.map((n) => n.length), 14);
|
|
7985
8037
|
for (const group of GROUPS) {
|
|
7986
|
-
out2.write("\n" +
|
|
8038
|
+
out2.write("\n" + chalk7.bold(group.title) + "\n");
|
|
7987
8039
|
for (const row of group.rows) {
|
|
7988
|
-
const aliasNote = row.alias ?
|
|
7989
|
-
out2.write(` ${
|
|
8040
|
+
const aliasNote = row.alias ? chalk7.dim(` ${row.alias} (alias)`) : "";
|
|
8041
|
+
out2.write(` ${chalk7.cyan.bold(row.name.padEnd(width + 2))}${row.description}${aliasNote}
|
|
7990
8042
|
`);
|
|
7991
8043
|
}
|
|
7992
8044
|
}
|
|
7993
8045
|
out2.write("\n" + COMMON_FLAGS + "\n");
|
|
7994
|
-
out2.write("\n" +
|
|
8046
|
+
out2.write("\n" + chalk7.dim("More help: https://floom.dev/docs") + "\n");
|
|
7995
8047
|
}
|
|
7996
8048
|
|
|
7997
8049
|
// src/index.ts
|
|
@@ -8054,7 +8106,7 @@ function editDistance(a, b) {
|
|
|
8054
8106
|
return curr[b.length];
|
|
8055
8107
|
}
|
|
8056
8108
|
helpOpt(program.command("login").description("sign in to your Floom workspace")).action(loginCommand);
|
|
8057
|
-
helpOpt(program.command("logout").description("sign out on this machine")).action(logoutCommand);
|
|
8109
|
+
helpOpt(program.command("logout").description("sign out on this machine").option("--all", "sign out all CLI sessions for this account")).action((opts) => logoutCommand(opts));
|
|
8058
8110
|
helpOpt(program.command("account").description("show account details").option("--json", "print account state as JSON")).action((opts) => accountCommand(opts));
|
|
8059
8111
|
helpOpt(program.command("whoami").description("show account details (alias)").option("--json", "print account state as JSON")).action((opts) => {
|
|
8060
8112
|
aliasNotice("whoami", "account");
|
package/dist/version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const VERSION = "3.0.
|
|
1
|
+
export const VERSION = "3.0.3";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@floomhq/floom",
|
|
3
|
-
"version": "3.0.
|
|
3
|
+
"version": "3.0.3",
|
|
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",
|