mainflow 0.6.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.
- package/LICENSE +21 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.js +324 -0
- package/dist/dist-BktkzRuK.js +1615 -0
- package/dist/dist-D2NK-QQP.js +511 -0
- package/dist/facts.d.ts +518 -0
- package/dist/facts.js +516 -0
- package/dist/hooks.d.ts +41 -0
- package/dist/hooks.js +171 -0
- package/dist/host.d.ts +24 -0
- package/dist/host.js +89 -0
- package/dist/launch-BR70uSMc.js +584 -0
- package/dist/launch.d.ts +44 -0
- package/dist/launch.js +2 -0
- package/dist/web/assets/index-BshtZ25f.js +78 -0
- package/dist/web/assets/index-qO7SaeFe.css +1 -0
- package/dist/web/assets/index.browser-C_4gp6WJ.js +169 -0
- package/dist/web/index.html +13 -0
- package/package.json +48 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Carlos
|
|
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/dist/cli.d.ts
ADDED
package/dist/cli.js
ADDED
|
@@ -0,0 +1,324 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { A as createFinding, C as startSession, E as sessionBriefing, F as setFindingStatus, H as TASK_FILE, I as setIssueArchived, L as HubError, M as planIssue, N as reopenIssue, O as appendOps, P as setFindingArchived, R as loadHub, S as setSessionStatus, T as updateAction, V as MAINLINE_FILE, W as WriteError, a as createDecision, b as setSessionArchived, c as dropChecklistItem, d as setChecklistItem, f as setCriterion, g as addAction, h as updateTask, i as addRepo, j as createIssue, k as closeIssue, l as initHub, m as supersedeDecision, n as acceptDecision, o as createMainline, p as setMainlineStatus, q as findHubRoot, r as addHub, s as createTask, t as migrateHub, u as setArchived, y as readSession } from "./dist-BktkzRuK.js";
|
|
3
|
+
import { r as launchUi, s as listProgress } from "./launch-BR70uSMc.js";
|
|
4
|
+
import { COMMAND_SPECS, OPTIONS } from "./facts.js";
|
|
5
|
+
import { runHook } from "./hooks.js";
|
|
6
|
+
import { readFileSync, realpathSync } from "node:fs";
|
|
7
|
+
import { join, relative } from "node:path";
|
|
8
|
+
import { fileURLToPath } from "node:url";
|
|
9
|
+
import { parseArgs } from "node:util";
|
|
10
|
+
//#region src/cli.ts
|
|
11
|
+
const RUNS = {
|
|
12
|
+
init: ({ root, args }) => done(`created ${relative(root, initHub(root, args[0]))}`),
|
|
13
|
+
status: ({ root, opts }) => printStatus(loadHub(root), opts.all ?? false),
|
|
14
|
+
check: ({ root }) => {
|
|
15
|
+
const hub = loadHub(root);
|
|
16
|
+
printProblems(hub.warnings, "!");
|
|
17
|
+
if (hub.problems.length === 0) return done(`✓ ${hub.mainlines.length} mainlines, no problems${hub.warnings.length ? `, ${hub.warnings.length} ${hub.warnings.length === 1 ? "warning" : "warnings"}` : ""}`);
|
|
18
|
+
printProblems(hub.problems);
|
|
19
|
+
return 1;
|
|
20
|
+
},
|
|
21
|
+
migrate: ({ root, opts }) => {
|
|
22
|
+
const dryRun = opts["dry-run"] ?? false;
|
|
23
|
+
const report = migrateHub(root, { dryRun });
|
|
24
|
+
console.log(`format ${report.from} → ${report.to}${dryRun ? " (dry run, nothing written)" : ""}`);
|
|
25
|
+
for (const file of report.written) console.log(` ${dryRun ? "would write" : "wrote"} ${file}`);
|
|
26
|
+
for (const file of report.removed) console.log(` ${dryRun ? "would remove" : "removed"} ${file}`);
|
|
27
|
+
if (report.missingDefinitions.length) {
|
|
28
|
+
console.log(`${report.missingDefinitions.length} open entries still need a definition of done; fill them in the files:`);
|
|
29
|
+
for (const entry of report.missingDefinitions) console.log(` ${entry}`);
|
|
30
|
+
}
|
|
31
|
+
return 0;
|
|
32
|
+
},
|
|
33
|
+
ui: ({ root, opts }) => {
|
|
34
|
+
const config = loadHub(root).config;
|
|
35
|
+
launchUi(root, config.hub, config.ui?.port, {
|
|
36
|
+
port: opts.port,
|
|
37
|
+
open: !opts["no-open"]
|
|
38
|
+
}).catch((error) => {
|
|
39
|
+
console.error(error instanceof WriteError ? `✗ ${error.message}` : error);
|
|
40
|
+
process.exitCode = 1;
|
|
41
|
+
});
|
|
42
|
+
return 0;
|
|
43
|
+
},
|
|
44
|
+
"repo add": ({ root, args }) => done(`registered ${addRepo(root, args[0], args[1])} ${args[1]}`),
|
|
45
|
+
"hub add": ({ root, args }) => done(`registered hub ${addHub(root, args[0], args[1])} ${args[1]}`),
|
|
46
|
+
"session start": ({ root, opts }) => {
|
|
47
|
+
const session = startSession(root, {
|
|
48
|
+
id: opts.id,
|
|
49
|
+
agent: opts.agent ?? "manual",
|
|
50
|
+
cwd: process.cwd(),
|
|
51
|
+
delegatedFrom: opts["delegated-from"],
|
|
52
|
+
handoffFrom: opts["handoff-from"]
|
|
53
|
+
});
|
|
54
|
+
return done(`started ${session.id}${session.actions.length ? ` with ${session.actions.length} carried actions` : ""}`);
|
|
55
|
+
},
|
|
56
|
+
"session show": ({ root, args, opts }) => {
|
|
57
|
+
const id = sessionOf(opts, args[0]);
|
|
58
|
+
const session = readSession(root, id);
|
|
59
|
+
if (!session) throw new WriteError(`session ${id} not found; start one with "mainflow session start"`);
|
|
60
|
+
return done(sessionBriefing(root, session, { cli: CLI }));
|
|
61
|
+
},
|
|
62
|
+
"session set": ({ root, args, opts }) => done(`updated ${args[0]} (${setSessionStatus(root, args[0], required(opts, "status", "--status paused")).status})`),
|
|
63
|
+
"session end": ({ root, args, opts }) => done(`ended ${setSessionStatus(root, sessionOf(opts, args[0]), "ended").id}`),
|
|
64
|
+
"session archive": ({ root, args }) => done(`archived ${setSessionArchived(root, args[0], true).id}`),
|
|
65
|
+
"session unarchive": ({ root, args }) => done(`unarchived ${setSessionArchived(root, args[0], false).id}`),
|
|
66
|
+
"action add": ({ root, args, opts }) => done(`created ${addAction(root, sessionOf(opts), {
|
|
67
|
+
do: args[0],
|
|
68
|
+
serves: list(opts.serves),
|
|
69
|
+
before: opts.before
|
|
70
|
+
})}`),
|
|
71
|
+
"action set": ({ root, args, opts }) => {
|
|
72
|
+
const ref = /^a\d{3}$/.test(args[0]) ? `${sessionOf(opts)}#${args[0]}` : args[0];
|
|
73
|
+
return done(`updated ${ref} (${updateAction(root, ref, {
|
|
74
|
+
status: opts.status,
|
|
75
|
+
do: opts.text,
|
|
76
|
+
serves: opts.serves === void 0 ? void 0 : list(opts.serves),
|
|
77
|
+
before: opts.before,
|
|
78
|
+
note: opts.note
|
|
79
|
+
}).status})`);
|
|
80
|
+
},
|
|
81
|
+
hook: ({ args }) => {
|
|
82
|
+
let payload = {};
|
|
83
|
+
try {
|
|
84
|
+
payload = JSON.parse(readFileSync(0, "utf8") || "{}");
|
|
85
|
+
} catch {
|
|
86
|
+
console.error("mainflow hook: stdin is not JSON; nothing to do");
|
|
87
|
+
return 0;
|
|
88
|
+
}
|
|
89
|
+
const { stdout, error } = runHook(args[0], args[1], {
|
|
90
|
+
payload,
|
|
91
|
+
envFile: process.env.CLAUDE_ENV_FILE,
|
|
92
|
+
cli: CLI
|
|
93
|
+
});
|
|
94
|
+
if (error) console.error(error);
|
|
95
|
+
if (stdout) console.log(stdout);
|
|
96
|
+
return 0;
|
|
97
|
+
},
|
|
98
|
+
"new mainline": ({ root, args, opts }) => {
|
|
99
|
+
const { id, dir } = createMainline(root, {
|
|
100
|
+
slug: args[0],
|
|
101
|
+
title: required(opts, "title", "--title \"重建 mainline 任務管理\"")
|
|
102
|
+
});
|
|
103
|
+
return done(`created ${id} ${relative(root, join(dir, MAINLINE_FILE))}`);
|
|
104
|
+
},
|
|
105
|
+
"new task": ({ root, args, opts }) => {
|
|
106
|
+
const { ref, dir } = createTask(root, args[0], {
|
|
107
|
+
slug: args[1],
|
|
108
|
+
title: required(opts, "title", "--title \"定義資料格式 schema\""),
|
|
109
|
+
kind: opts.kind,
|
|
110
|
+
parent: opts.parent,
|
|
111
|
+
repo: opts.repo
|
|
112
|
+
});
|
|
113
|
+
return done(`created ${ref} ${relative(root, join(dir, TASK_FILE))}`);
|
|
114
|
+
},
|
|
115
|
+
"new decision": ({ root, args, opts }) => {
|
|
116
|
+
const { ref, file } = createDecision(root, args[0], {
|
|
117
|
+
slug: args[1],
|
|
118
|
+
title: required(opts, "title", "--title \"以文字檔為準\""),
|
|
119
|
+
status: opts.status,
|
|
120
|
+
by: opts.by,
|
|
121
|
+
affects: opts.affects ? list(opts.affects) : void 0
|
|
122
|
+
});
|
|
123
|
+
return done(`created ${ref} ${relative(root, file)}`);
|
|
124
|
+
},
|
|
125
|
+
"new issue": ({ root, args, opts }) => {
|
|
126
|
+
const { id, file } = createIssue(root, {
|
|
127
|
+
slug: args[0],
|
|
128
|
+
title: required(opts, "title", "--title \"重試不穩定的 VM\""),
|
|
129
|
+
origin: opts.origin
|
|
130
|
+
});
|
|
131
|
+
return done(`created ${id} ${relative(root, file)}`);
|
|
132
|
+
},
|
|
133
|
+
"new finding": ({ root, args, opts }) => {
|
|
134
|
+
const { id, file } = createFinding(root, {
|
|
135
|
+
slug: args[0],
|
|
136
|
+
title: required(opts, "title", "--title \"先 await 再寫入 state\""),
|
|
137
|
+
origin: opts.origin,
|
|
138
|
+
labels: opts.labels ? list(opts.labels) : void 0
|
|
139
|
+
});
|
|
140
|
+
return done(`created ${id} ${relative(root, file)}`);
|
|
141
|
+
},
|
|
142
|
+
"mainline set": ({ root, args, opts }) => {
|
|
143
|
+
const m = setMainlineStatus(root, args[0], required(opts, "status", "--status active"));
|
|
144
|
+
return done(`updated ${m.id} (${m.status})`);
|
|
145
|
+
},
|
|
146
|
+
"mainline archive": ({ root, args }) => done(`archived ${setArchived(root, args[0], true).id}`),
|
|
147
|
+
"mainline unarchive": ({ root, args }) => done(`unarchived ${setArchived(root, args[0], false).id}`),
|
|
148
|
+
"criterion check": ({ root, args, opts }) => {
|
|
149
|
+
setCriterion(root, args[0], true, opts.evidence);
|
|
150
|
+
return done(`checked ${args[0]}`);
|
|
151
|
+
},
|
|
152
|
+
"criterion uncheck": ({ root, args }) => {
|
|
153
|
+
setCriterion(root, args[0], false);
|
|
154
|
+
return done(`unchecked ${args[0]}`);
|
|
155
|
+
},
|
|
156
|
+
"task set": ({ root, args, opts }) => {
|
|
157
|
+
const blockedBy = opts.reason ? {
|
|
158
|
+
reason: opts.reason,
|
|
159
|
+
...opts["waiting-for"] ? { waitingFor: opts["waiting-for"] } : {},
|
|
160
|
+
...opts.signal ? { signal: opts.signal } : {}
|
|
161
|
+
} : void 0;
|
|
162
|
+
const t = updateTask(root, args[0], {
|
|
163
|
+
status: opts.status,
|
|
164
|
+
repo: opts.repo,
|
|
165
|
+
blockedBy,
|
|
166
|
+
branch: opts.branch,
|
|
167
|
+
worktree: opts.worktree,
|
|
168
|
+
base: opts.base,
|
|
169
|
+
merge: opts.merge
|
|
170
|
+
});
|
|
171
|
+
return done(`updated ${args[0]} (${t.status})`);
|
|
172
|
+
},
|
|
173
|
+
"item check": ({ root, args, opts }) => {
|
|
174
|
+
setChecklistItem(root, args[0], true, opts.evidence);
|
|
175
|
+
return done(`checked ${args[0]}`);
|
|
176
|
+
},
|
|
177
|
+
"item uncheck": ({ root, args }) => {
|
|
178
|
+
setChecklistItem(root, args[0], false);
|
|
179
|
+
return done(`unchecked ${args[0]}`);
|
|
180
|
+
},
|
|
181
|
+
"decision accept": ({ root, args, opts }) => done(`accepted ${args[0]} (${acceptDecision(root, args[0], opts.by).decidedAt})`),
|
|
182
|
+
"item drop": ({ root, args, opts }) => {
|
|
183
|
+
dropChecklistItem(root, args[0], {
|
|
184
|
+
supersededBy: opts.by,
|
|
185
|
+
note: opts.note
|
|
186
|
+
});
|
|
187
|
+
return done(`dropped ${args[0]}`);
|
|
188
|
+
},
|
|
189
|
+
"issue plan": ({ root, args }) => done(`planned ${planIssue(root, args[0], args[1]).id} in ${args[1]}`),
|
|
190
|
+
"issue close": ({ root, args, opts }) => done(`closed ${closeIssue(root, args[0], opts.by ?? "").id}`),
|
|
191
|
+
"issue reopen": ({ root, args }) => done(`reopened ${reopenIssue(root, args[0]).id}`),
|
|
192
|
+
"issue archive": ({ root, args }) => done(`archived ${setIssueArchived(root, args[0], true).id}`),
|
|
193
|
+
"issue unarchive": ({ root, args }) => done(`unarchived ${setIssueArchived(root, args[0], false).id}`),
|
|
194
|
+
"issue list": ({ root, opts }) => {
|
|
195
|
+
const shown = loadHub(root).issues.filter((i) => opts.all || i.status !== "closed");
|
|
196
|
+
for (const i of shown) console.log(` ${i.id} ${i.status.padEnd(7)} ${i.title}${i.plannedIn ? ` → ${i.plannedIn}` : ""}${i.origin ? ` (from ${i.origin})` : ""}`);
|
|
197
|
+
return done(`${shown.length} issues`);
|
|
198
|
+
},
|
|
199
|
+
"finding set": ({ root, args, opts }) => done(`updated ${setFindingStatus(root, args[0], required(opts, "status", "--status obsolete")).id}`),
|
|
200
|
+
"finding archive": ({ root, args }) => done(`archived ${setFindingArchived(root, args[0], true).id}`),
|
|
201
|
+
"finding unarchive": ({ root, args }) => done(`unarchived ${setFindingArchived(root, args[0], false).id}`),
|
|
202
|
+
"finding list": ({ root, opts }) => {
|
|
203
|
+
const shown = loadHub(root).findings.filter((f) => opts.all || f.status === "valid");
|
|
204
|
+
for (const f of shown) console.log(` ${f.id} ${f.created} ${f.title}${f.labels?.length ? ` [${f.labels.join(", ")}]` : ""}`);
|
|
205
|
+
return done(`${shown.length} findings`);
|
|
206
|
+
},
|
|
207
|
+
"ops add": ({ root, opts }) => {
|
|
208
|
+
const entry = appendOps(root, {
|
|
209
|
+
target: required(opts, "target", "--target github.com/owner/repo"),
|
|
210
|
+
operation: required(opts, "op", "--op \"push tag v1.2.0\""),
|
|
211
|
+
result: required(opts, "result", "--result success"),
|
|
212
|
+
...opts.ref ? { ref: opts.ref } : {}
|
|
213
|
+
});
|
|
214
|
+
return done(`logged ${entry.at} ${entry.operation}`);
|
|
215
|
+
},
|
|
216
|
+
"ops list": ({ root, opts }) => {
|
|
217
|
+
const shown = loadHub(root).ops.filter((o) => !opts.month || o.at.startsWith(opts.month));
|
|
218
|
+
for (const o of shown) console.log(` ${o.at} ${o.target} ${o.operation} ${o.result}${o.ref ? ` ${o.ref}` : ""}`);
|
|
219
|
+
return done(`${shown.length} operations`);
|
|
220
|
+
},
|
|
221
|
+
"decision supersede": ({ root, args, opts }) => {
|
|
222
|
+
const d = supersedeDecision(root, args[0], required(opts, "by", "--by d002"));
|
|
223
|
+
return done(`superseded ${args[0]} by ${d.supersededBy}`);
|
|
224
|
+
}
|
|
225
|
+
};
|
|
226
|
+
const COMMANDS = Object.fromEntries(Object.keys(COMMAND_SPECS).map((name) => [name, {
|
|
227
|
+
...COMMAND_SPECS[name],
|
|
228
|
+
run: RUNS[name]
|
|
229
|
+
}]));
|
|
230
|
+
const USAGE = [
|
|
231
|
+
"Usage: mainflow <command>",
|
|
232
|
+
"",
|
|
233
|
+
"Commands:",
|
|
234
|
+
...Object.values(COMMANDS).map((c) => ` ${c.usage}\n ${c.summary}`),
|
|
235
|
+
"",
|
|
236
|
+
"Options:",
|
|
237
|
+
" --version Print the version",
|
|
238
|
+
" --help Print this message"
|
|
239
|
+
].join("\n");
|
|
240
|
+
function run(argv, cwd) {
|
|
241
|
+
let parsed;
|
|
242
|
+
try {
|
|
243
|
+
parsed = parseArgs({
|
|
244
|
+
args: argv,
|
|
245
|
+
allowPositionals: true,
|
|
246
|
+
options: OPTIONS
|
|
247
|
+
});
|
|
248
|
+
} catch (error) {
|
|
249
|
+
return fail(`${error.message}\n\n${USAGE}`);
|
|
250
|
+
}
|
|
251
|
+
const { values: opts, positionals } = parsed;
|
|
252
|
+
if (opts.version) return done(JSON.parse(readFileSync(new URL("../package.json", import.meta.url), "utf8")).version);
|
|
253
|
+
if (opts.help || positionals.length === 0) {
|
|
254
|
+
console.log(USAGE);
|
|
255
|
+
return opts.help ? 0 : 1;
|
|
256
|
+
}
|
|
257
|
+
const pair = positionals.slice(0, 2).join(" ");
|
|
258
|
+
const key = COMMANDS[pair] ? pair : positionals[0];
|
|
259
|
+
const command = COMMANDS[key];
|
|
260
|
+
if (!command) return fail(`Unknown command "${positionals.join(" ")}".\n\n${USAGE}`);
|
|
261
|
+
const args = positionals.slice(key.split(" ").length);
|
|
262
|
+
if (args.length < command.args || args.length > command.args + (command.optionalArgs ?? 0)) return fail(`Usage: mainflow ${command.usage}`);
|
|
263
|
+
let root = cwd;
|
|
264
|
+
if (command.needsHub) {
|
|
265
|
+
const found = findHubRoot(cwd);
|
|
266
|
+
if (!found) return fail("No .mainflow/config.yaml found in this directory or any parent. Run mainflow inside a hub repository.");
|
|
267
|
+
root = found;
|
|
268
|
+
}
|
|
269
|
+
try {
|
|
270
|
+
return command.run({
|
|
271
|
+
root,
|
|
272
|
+
args,
|
|
273
|
+
opts
|
|
274
|
+
});
|
|
275
|
+
} catch (error) {
|
|
276
|
+
if (error instanceof HubError) {
|
|
277
|
+
printProblems(error.problems);
|
|
278
|
+
return fail(error.message);
|
|
279
|
+
}
|
|
280
|
+
if (error instanceof WriteError) return fail(`✗ ${error.message}`);
|
|
281
|
+
throw error;
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
function done(line) {
|
|
285
|
+
console.log(line);
|
|
286
|
+
return 0;
|
|
287
|
+
}
|
|
288
|
+
function fail(message) {
|
|
289
|
+
console.error(message);
|
|
290
|
+
return 1;
|
|
291
|
+
}
|
|
292
|
+
/** How the executor runs this CLI inside a hub; used in the texts hooks inject. */
|
|
293
|
+
const CLI = "pnpm exec mainflow";
|
|
294
|
+
/** The session a command acts on: an explicit argument, then --session, then MAINFLOW_SESSION set by the SessionStart hook. */
|
|
295
|
+
function sessionOf(opts, arg) {
|
|
296
|
+
const id = arg ?? opts.session ?? process.env.MAINFLOW_SESSION;
|
|
297
|
+
if (!id) throw new WriteError("no session given; pass --session <id> or set MAINFLOW_SESSION (the SessionStart hook sets it)");
|
|
298
|
+
return id;
|
|
299
|
+
}
|
|
300
|
+
const list = (value) => (value ?? "").split(",").map((v) => v.trim()).filter(Boolean);
|
|
301
|
+
function required(opts, name, example) {
|
|
302
|
+
const value = opts[name];
|
|
303
|
+
if (!value) throw new WriteError(`--${name} is required, e.g. ${example}`);
|
|
304
|
+
return value;
|
|
305
|
+
}
|
|
306
|
+
function printStatus(hub, all) {
|
|
307
|
+
const shown = hub.mainlines.filter((m) => all || !m.archivedAt);
|
|
308
|
+
console.log(`${hub.config.hub}: ${shown.length} mainlines${all ? "" : " (archived hidden)"}`);
|
|
309
|
+
for (const m of shown) {
|
|
310
|
+
const items = listProgress(m.tasks.flatMap((t) => t.checklist));
|
|
311
|
+
const archived = m.archivedAt ? " [archived]" : "";
|
|
312
|
+
console.log(` ${m.id} ${m.status.padEnd(8)} ${items.done}/${items.total} ${m.title}${archived}`);
|
|
313
|
+
}
|
|
314
|
+
if (hub.problems.length === 0) return 0;
|
|
315
|
+
console.error(`${hub.problems.length} problems found; run "mainflow check" for details`);
|
|
316
|
+
return 1;
|
|
317
|
+
}
|
|
318
|
+
function printProblems(problems, mark = "✗") {
|
|
319
|
+
for (const problem of problems) console.error(`${mark} ${problem.file}${problem.field ? ` › ${problem.field}` : ""}: ${problem.message}`);
|
|
320
|
+
}
|
|
321
|
+
const entry = process.argv[1];
|
|
322
|
+
if (entry && realpathSync(entry) === realpathSync(fileURLToPath(import.meta.url))) process.exitCode = run(process.argv.slice(2), process.cwd());
|
|
323
|
+
//#endregion
|
|
324
|
+
export { run };
|