moflo 4.9.3 → 4.9.4
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/src/cli/version.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "moflo",
|
|
3
|
-
"version": "4.9.
|
|
3
|
+
"version": "4.9.4",
|
|
4
4
|
"description": "MoFlo — AI agent orchestration for Claude Code. A standalone, opinionated toolkit with semantic memory, learned routing, gates, spells, and the /flo issue-execution skill.",
|
|
5
5
|
"main": "dist/src/cli/index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -39,14 +39,15 @@
|
|
|
39
39
|
"!.claude/**/*.map",
|
|
40
40
|
"README.md",
|
|
41
41
|
"LICENSE",
|
|
42
|
-
"scripts/prune-native-binaries.mjs"
|
|
42
|
+
"scripts/prune-native-binaries.mjs",
|
|
43
|
+
"scripts/post-install-notice.mjs"
|
|
43
44
|
],
|
|
44
45
|
"scripts": {
|
|
45
46
|
"dev": "tsx watch src/cli/index.ts",
|
|
46
47
|
"prebuild": "node scripts/sync-version.mjs && node scripts/clean-dist.mjs",
|
|
47
48
|
"build": "tsc",
|
|
48
49
|
"prepublishOnly": "npm run build",
|
|
49
|
-
"postinstall": "node scripts/prune-native-binaries.mjs",
|
|
50
|
+
"postinstall": "node scripts/prune-native-binaries.mjs && node scripts/post-install-notice.mjs",
|
|
50
51
|
"test": "node scripts/test-runner.mjs",
|
|
51
52
|
"test:ui": "vitest --ui",
|
|
52
53
|
"test:smoke": "node harness/consumer-smoke/run.mjs",
|
|
@@ -79,7 +80,7 @@
|
|
|
79
80
|
"@typescript-eslint/eslint-plugin": "^7.18.0",
|
|
80
81
|
"@typescript-eslint/parser": "^7.18.0",
|
|
81
82
|
"eslint": "^8.0.0",
|
|
82
|
-
"moflo": "^4.9.
|
|
83
|
+
"moflo": "^4.9.3",
|
|
83
84
|
"tsx": "^4.21.0",
|
|
84
85
|
"typescript": "^5.9.3",
|
|
85
86
|
"vitest": "^4.0.0"
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Postinstall restart-nudge banner.
|
|
4
|
+
*
|
|
5
|
+
* When `npm install` runs inside Claude Code (typically because the user
|
|
6
|
+
* asked Claude to upgrade moflo), the just-installed bits are sitting on
|
|
7
|
+
* disk but the running session still has the OLD launcher, hooks, MCP
|
|
8
|
+
* server, and statusline loaded. The session-start launcher only re-reads
|
|
9
|
+
* them on the NEXT session-start — so the upgrade is inert until the user
|
|
10
|
+
* exits and reopens Claude Code.
|
|
11
|
+
*
|
|
12
|
+
* This script prints a banner that npm relays back to Claude as the install
|
|
13
|
+
* stdout. The phrasing names Claude Code explicitly so the assistant
|
|
14
|
+
* surfaces it to the user as a restart prompt.
|
|
15
|
+
*
|
|
16
|
+
* Gating:
|
|
17
|
+
* - Only prints when CLAUDE_PROJECT_DIR or CLAUDECODE is set (avoids
|
|
18
|
+
* noise on CI and non-Claude installs).
|
|
19
|
+
* - Dedupes by version: only prints once per (consumer-project, version)
|
|
20
|
+
* pair, so unrelated `npm install` runs that re-trigger postinstall
|
|
21
|
+
* don't re-spam the banner. Tracker lives at
|
|
22
|
+
* `<project>/.moflo/last-install-banner.json`.
|
|
23
|
+
*
|
|
24
|
+
* Failure posture: never blocks an install. Errors are swallowed; exit 0.
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'node:fs';
|
|
28
|
+
import { dirname, join, resolve } from 'node:path';
|
|
29
|
+
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
30
|
+
|
|
31
|
+
const SCRIPT_PATH = fileURLToPath(import.meta.url);
|
|
32
|
+
|
|
33
|
+
function isClaudeSession() {
|
|
34
|
+
return Boolean(process.env.CLAUDE_PROJECT_DIR || process.env.CLAUDECODE);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function consumerProjectRoot() {
|
|
38
|
+
// npm sets INIT_CWD to the original directory where the user ran `npm
|
|
39
|
+
// install` — the consumer's project root, regardless of which package's
|
|
40
|
+
// postinstall is running.
|
|
41
|
+
return process.env.INIT_CWD || process.cwd();
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function installedVersion() {
|
|
45
|
+
// package.json sits one level above scripts/.
|
|
46
|
+
const pkgPath = resolve(dirname(SCRIPT_PATH), '..', 'package.json');
|
|
47
|
+
if (!existsSync(pkgPath)) return null;
|
|
48
|
+
try {
|
|
49
|
+
return JSON.parse(readFileSync(pkgPath, 'utf-8')).version || null;
|
|
50
|
+
} catch {
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function readLastBanner(trackerPath) {
|
|
56
|
+
if (!existsSync(trackerPath)) return null;
|
|
57
|
+
try {
|
|
58
|
+
return JSON.parse(readFileSync(trackerPath, 'utf-8')).version || null;
|
|
59
|
+
} catch {
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function writeLastBanner(trackerPath, version) {
|
|
65
|
+
try {
|
|
66
|
+
mkdirSync(dirname(trackerPath), { recursive: true });
|
|
67
|
+
writeFileSync(trackerPath, JSON.stringify({
|
|
68
|
+
version,
|
|
69
|
+
shownAt: new Date().toISOString(),
|
|
70
|
+
}, null, 2));
|
|
71
|
+
} catch { /* non-fatal */ }
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function printBanner(version) {
|
|
75
|
+
// Phrased to name Claude Code by name so the assistant surfaces it as a
|
|
76
|
+
// restart instruction rather than a generic install log line.
|
|
77
|
+
const lines = [
|
|
78
|
+
'',
|
|
79
|
+
'═══════════════════════════════════════════════════════════════════',
|
|
80
|
+
` MoFlo ${version} installed.`,
|
|
81
|
+
'',
|
|
82
|
+
' ⚠ Claude Code: please restart your session to load the new MoFlo.',
|
|
83
|
+
'',
|
|
84
|
+
' Hooks, MCP tools, statusline, and the session-start launcher are',
|
|
85
|
+
' loaded once at session-start — the running session is still on',
|
|
86
|
+
' the previous moflo until you exit and reopen Claude Code.',
|
|
87
|
+
'═══════════════════════════════════════════════════════════════════',
|
|
88
|
+
'',
|
|
89
|
+
];
|
|
90
|
+
process.stdout.write(lines.join('\n'));
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function run() {
|
|
94
|
+
if (!isClaudeSession()) return { fired: false, reason: 'not-claude' };
|
|
95
|
+
|
|
96
|
+
const version = installedVersion();
|
|
97
|
+
if (!version) return { fired: false, reason: 'no-version' };
|
|
98
|
+
|
|
99
|
+
const projectRoot = consumerProjectRoot();
|
|
100
|
+
const trackerPath = join(projectRoot, '.moflo', 'last-install-banner.json');
|
|
101
|
+
const lastShown = readLastBanner(trackerPath);
|
|
102
|
+
if (lastShown === version) return { fired: false, reason: 'already-shown' };
|
|
103
|
+
|
|
104
|
+
printBanner(version);
|
|
105
|
+
writeLastBanner(trackerPath, version);
|
|
106
|
+
return { fired: true, version };
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
|
|
110
|
+
try {
|
|
111
|
+
run();
|
|
112
|
+
} catch { /* never block install */ }
|
|
113
|
+
process.exit(0);
|
|
114
|
+
}
|