@flood-md/cli 0.0.0-stage → 0.1.1

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Oleg (tillwithered)
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,9 @@
1
- # Temporary Holding Version
1
+ # @flood-md/cli
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ `npx @flood-md/cli init [--dry-run]` connects an existing React/Next.js npm project.
4
+ `npx @flood-md/cli doctor` checks package versions and agent file presence.
5
+
6
+ Requires Node 22.12+. Installs matching tokens, React and agent packages; creates
7
+ flood.json, flood/DESIGN.md and .agents/skills/flood-ui/SKILL.md. Refuses existing
8
+ owned files or linked destinations. Does not rewrite source code or AGENTS.md.
9
+ After init, import @flood-md/react/styles.css once at the application root.
package/bin/flood.mjs ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env node
2
+ import { run } from "../lib/index.mjs";
3
+ try {
4
+ await run(process.argv.slice(2));
5
+ } catch (error) {
6
+ console.error(`Flood: ${error.message}`);
7
+ process.exitCode = 1;
8
+ }
package/lib/index.mjs ADDED
@@ -0,0 +1,158 @@
1
+ import { readFile, writeFile, mkdir, lstat, access } from "node:fs/promises";
2
+ import { resolve, join } from "node:path";
3
+ import { fileURLToPath } from "node:url";
4
+ import { spawnSync } from "node:child_process";
5
+
6
+ export const VERSION = "0.1.1";
7
+ const packages = ["@flood-md/tokens", "@flood-md/react", "@flood-md/agent"];
8
+ const json = async (path) => JSON.parse(await readFile(path, "utf8"));
9
+ const exists = async (path) => {
10
+ try {
11
+ await access(path);
12
+ return true;
13
+ } catch {
14
+ return false;
15
+ }
16
+ };
17
+ const agentFile = (name) =>
18
+ fileURLToPath(import.meta.resolve(`@flood-md/agent/${name}`));
19
+
20
+ // Refuse links and existing instruction files before changing any project data.
21
+ export async function checkTarget(root, relative) {
22
+ let current = root;
23
+ for (const part of relative.split("/")) {
24
+ current = join(current, part);
25
+ try {
26
+ if ((await lstat(current)).isSymbolicLink())
27
+ throw new Error(`Refusing linked path: ${relative}`);
28
+ } catch (error) {
29
+ if (error.code !== "ENOENT") throw error;
30
+ }
31
+ }
32
+ if (await exists(join(root, relative)))
33
+ throw new Error(`${relative} already exists. No files were overwritten.`);
34
+ }
35
+
36
+ export function install(root) {
37
+ const args = [
38
+ "install",
39
+ "--save-exact",
40
+ ...packages.map((name) => `${name}@${VERSION}`),
41
+ ];
42
+ // Only constant, internally generated arguments are sent to the Windows npm shim.
43
+ const result = spawnSync(
44
+ process.platform === "win32" ? "npm.cmd" : "npm",
45
+ args,
46
+ { cwd: root, stdio: "inherit", shell: process.platform === "win32" },
47
+ );
48
+ if (result.error) throw result.error;
49
+ if (result.status !== 0)
50
+ throw new Error(
51
+ "npm install failed. Configuration was not written; fix npm access and retry.",
52
+ );
53
+ }
54
+
55
+ export async function initProject(
56
+ root,
57
+ { dryRun = false, installer = install, log = console.log } = {},
58
+ ) {
59
+ const manifest = await json(join(root, "package.json")).catch(() => {
60
+ throw new Error("Run init inside a React project with a package.json.");
61
+ });
62
+ if (!(manifest.dependencies?.react || manifest.devDependencies?.react))
63
+ throw new Error(
64
+ "React was not found. Create a React or Next.js project first.",
65
+ );
66
+ const framework =
67
+ manifest.dependencies?.next || manifest.devDependencies?.next
68
+ ? "next"
69
+ : "react";
70
+ const files = [
71
+ "flood.json",
72
+ "flood/DESIGN.md",
73
+ ".agents/skills/flood-ui/SKILL.md",
74
+ ];
75
+ for (const file of files) await checkTarget(root, file);
76
+ log(
77
+ `Flood ${VERSION} · ${framework} · npm\nInstall: ${packages.map((p) => `${p}@${VERSION}`).join(", ")}\nCreate: ${files.join(", ")}`,
78
+ );
79
+ if (dryRun) return;
80
+ await installer(root);
81
+ const config = {
82
+ $schema: "https://flood.md/schema/config.json",
83
+ version: VERSION,
84
+ framework,
85
+ packageManager: "npm",
86
+ components: "@flood-md/react",
87
+ stylesheet: "@flood-md/react/styles.css",
88
+ instructions: "flood/DESIGN.md",
89
+ };
90
+ // Recheck after npm has run, before creating owned files.
91
+ for (const file of files) await checkTarget(root, file);
92
+ await mkdir(join(root, "flood"), { recursive: true });
93
+ await mkdir(join(root, ".agents/skills/flood-ui"), { recursive: true });
94
+ await writeFile(
95
+ join(root, files[0]),
96
+ JSON.stringify(config, null, 2) + "\n",
97
+ { flag: "wx" },
98
+ );
99
+ await writeFile(
100
+ join(root, files[1]),
101
+ await readFile(agentFile("DESIGN.md")),
102
+ { flag: "wx" },
103
+ );
104
+ await writeFile(join(root, files[2]), await readFile(agentFile("SKILL.md")), {
105
+ flag: "wx",
106
+ });
107
+ log(
108
+ `\nConnected. Import '@flood-md/react/styles.css' in ${framework === "next" ? "app/layout.tsx" : "your app entry"}.\nRead flood/DESIGN.md for component APIs. Run npx @flood-md/cli doctor to verify.`,
109
+ );
110
+ }
111
+
112
+ export async function doctor(root, log = console.log) {
113
+ const config = await json(join(root, "flood.json")).catch(() => {
114
+ throw new Error("No flood.json. Run init first.");
115
+ });
116
+ const failures = [];
117
+ for (const name of packages) {
118
+ let path = root,
119
+ found;
120
+ while (true) {
121
+ try {
122
+ found = await json(join(path, "node_modules", name, "package.json"));
123
+ break;
124
+ } catch {}
125
+ const parent = resolve(path, "..");
126
+ if (parent === path) break;
127
+ path = parent;
128
+ }
129
+ if (!found || found.version !== config.version)
130
+ failures.push(
131
+ `${name}: expected ${config.version}, found ${found?.version ?? "missing"}`,
132
+ );
133
+ }
134
+ for (const name of ["flood/DESIGN.md", ".agents/skills/flood-ui/SKILL.md"])
135
+ if (!(await exists(join(root, name)))) failures.push(`${name}: missing`);
136
+ if (failures.length) throw new Error(failures.join("\n"));
137
+ log(
138
+ `Flood ${config.version}: package versions and agent files are aligned.\nCheck the stylesheet import in your application entry; doctor does not parse application code.`,
139
+ );
140
+ }
141
+
142
+ export async function run(args) {
143
+ const [command, ...flags] = args;
144
+ if (
145
+ flags.some((flag) => flag !== "--dry-run") ||
146
+ (flags.length && command !== "init")
147
+ )
148
+ throw new Error("Unsupported option. Use --help.");
149
+ if (command === "init")
150
+ return initProject(process.cwd(), { dryRun: flags.includes("--dry-run") });
151
+ if (command === "doctor") return doctor(process.cwd());
152
+ if (command === "--version") return console.log(VERSION);
153
+ if (command && command !== "--help")
154
+ throw new Error(`Unknown command: ${command}. Use --help.`);
155
+ console.log(
156
+ `Flood ${VERSION}\n\nnpx @flood-md/cli init [--dry-run] Connect a React or Next.js project\nnpx @flood-md/cli doctor Check packages and agent files\n\nComponents ship together in @flood-md/react; import the ones you need.\nUpdates: review the changelog and upgrade Flood packages together.\nhttps://flood.md`,
157
+ );
158
+ }
package/package.json CHANGED
@@ -1,6 +1,29 @@
1
1
  {
2
2
  "name": "@flood-md/cli",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.1.1",
4
+ "type": "module",
5
+ "description": "Connect Flood to React and Next.js projects.",
6
+ "license": "MIT",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/tillwithered/flood.md.git",
10
+ "directory": "packages/cli"
11
+ },
12
+ "bin": {
13
+ "flood": "./bin/flood.mjs"
14
+ },
15
+ "files": [
16
+ "bin",
17
+ "lib",
18
+ "README.md"
19
+ ],
20
+ "engines": {
21
+ "node": ">=22.12.0"
22
+ },
23
+ "dependencies": {
24
+ "@flood-md/agent": "0.1.1"
25
+ },
26
+ "publishConfig": {
27
+ "access": "public"
28
+ }
29
+ }