@flowapt/flowiq-cli 0.6.7 → 0.7.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.
@@ -4,6 +4,14 @@
4
4
  // can never corrupt piped / --json output) when the installed version is older
5
5
  // than the latest published on npm.
6
6
  //
7
+ // ⚠️ It used to be gated on `process.stderr.isTTY` as well, which meant it was
8
+ // invisible to exactly the audience that most needs it: an AI assistant running
9
+ // `flowiq` through a tool call gets a PIPE, not a TTY, so isTTY is undefined and
10
+ // the hint never printed. Teammates' Claude sessions could therefore run a
11
+ // months-old CLI forever without ever being told. Writing to stderr is already
12
+ // the whole safety property (it cannot corrupt piped stdout or --json), so the
13
+ // TTY gate bought nothing and cost everything. Removed 9 Sep 2026.
14
+ //
7
15
  // The nudge is driven by a CACHED value read SYNCHRONOUSLY — zero network on the
8
16
  // hot path, so no command is ever slowed. The cache is refreshed at most once a
9
17
  // day by a DETACHED, unref'd child process, so even `flowiq --version` returns
@@ -88,16 +96,42 @@ async function refreshLatest(prevLatest) {
88
96
 
89
97
  // Print the nudge (from cache, synchronously) and, if the cache is stale, spawn
90
98
  // a detached child to refresh it for the NEXT run. Never throws, never blocks.
99
+ /** The cached npm `latest` + when it was refreshed — used by `flowiq doctor`. */
100
+ export function cachedLatest() {
101
+ const c = readCacheSync();
102
+ return { latest: c?.latest || null, checkedAt: c?.checked_at || null };
103
+ }
104
+
105
+ /** Ask npm for the latest version NOW (doctor only — never on the hot path). */
106
+ export async function fetchLatestNow(timeoutMs = FETCH_TIMEOUT_MS) {
107
+ const ctrl = new AbortController();
108
+ const timer = setTimeout(() => ctrl.abort(), timeoutMs);
109
+ try {
110
+ const res = await fetch(REGISTRY, { signal: ctrl.signal, headers: { Accept: "application/json" } });
111
+ if (!res.ok) return null;
112
+ const body = await res.json();
113
+ if (body?.version) await writeCache({ checked_at: Date.now(), latest: body.version });
114
+ return body?.version || null;
115
+ } catch {
116
+ return null;
117
+ } finally {
118
+ clearTimeout(timer);
119
+ }
120
+ }
121
+
91
122
  export function maybeNotifyUpdate(currentVersion) {
92
123
  try {
93
124
  if (disabled()) return;
94
125
  const cache = readCacheSync();
95
126
  const latest = cache?.latest;
96
127
 
97
- if (latest && process.stderr.isTTY && cmpVersion(latest, currentVersion) > 0) {
128
+ if (latest && cmpVersion(latest, currentVersion) > 0) {
129
+ // The marker line is deliberately stable and unambiguous: an assistant
130
+ // reading this in a tool result should update without needing to be asked.
98
131
  process.stderr.write(
99
- `\n ⬆ flowiq ${latest} is available (you're on ${currentVersion})\n` +
100
- ` update: npm i -g ${PKG}\n\n`
132
+ `\n ⬆ FLOWIQ CLI OUT OF DATE — installed ${currentVersion}, latest ${latest}\n` +
133
+ ` Run this before continuing: npm i -g ${PKG}@latest\n` +
134
+ ` (commands still work; newer ones may be missing or behave differently)\n\n`
101
135
  );
102
136
  }
103
137