@savvy-web/cli 3.5.1 → 3.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/cli/index.js +19 -3
- package/commands/changeset/commands/check.js +82 -21
- package/commands/changeset/commands/config-validate.js +9 -6
- package/commands/changeset/commands/deps-detect.js +2 -2
- package/commands/changeset/commands/deps-regen.js +94 -48
- package/commands/changeset/commands/init.js +56 -23
- package/commands/changeset/commands/transform.js +82 -8
- package/commands/changeset/commands/validate-file.js +12 -11
- package/commands/changeset/commands/version.js +48 -17
- package/commands/changeset/index.js +3 -3
- package/commands/changeset/utils/config-gate.js +12 -10
- package/commands/check-section.js +34 -0
- package/commands/check.js +48 -39
- package/commands/clean/progress.js +71 -0
- package/commands/clean/view.js +26 -0
- package/commands/clean.js +113 -50
- package/commands/commit/check.js +80 -77
- package/commands/commit/init.js +21 -10
- package/commands/commit/lint.js +2 -2
- package/commands/init.js +122 -34
- package/commands/lint/check.js +126 -171
- package/commands/lint/init.js +52 -33
- package/commands/lint/text.js +28 -13
- package/commands/repos/commands/add.js +14 -17
- package/commands/repos/commands/deregister.js +37 -18
- package/commands/repos/commands/note.js +78 -26
- package/commands/repos/commands/pin.js +39 -24
- package/commands/repos/commands/remove.js +30 -56
- package/commands/repos/commands/rename.js +32 -23
- package/commands/repos/commands/restore.js +42 -56
- package/commands/repos/commands/status.js +43 -47
- package/commands/repos/commands/sync.js +11 -40
- package/commands/repos/index.js +7 -10
- package/commands/repos/shared.js +129 -0
- package/index.d.ts +162 -61
- package/internal/command-error.js +60 -0
- package/internal/confirm.js +52 -0
- package/internal/failure-line.js +26 -12
- package/internal/report.js +101 -0
- package/main.js +23 -16
- package/package.json +17 -12
- package/version.js +1 -1
- package/internal/output.js +0 -52
package/internal/failure-line.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { CliDoc } from "@effected/cli";
|
|
2
2
|
|
|
3
3
|
//#region src/internal/failure-line.ts
|
|
4
4
|
/** Where a defect report asks to be filed; mirrors `package.json#bugs.url`. */
|
|
@@ -29,6 +29,8 @@ const describe = (error) => {
|
|
|
29
29
|
}
|
|
30
30
|
return String(error);
|
|
31
31
|
};
|
|
32
|
+
/** Whether a typed failure draws itself through the kit's `CliDoc` protocol. */
|
|
33
|
+
const drawsItself = (error) => typeof error === "object" && error !== null && CliDoc in error && typeof error[CliDoc] === "function";
|
|
32
34
|
/**
|
|
33
35
|
* Renders a failure for `CliRuntime.main`'s `render` option.
|
|
34
36
|
*
|
|
@@ -37,23 +39,35 @@ const describe = (error) => {
|
|
|
37
39
|
* or a defect, so nothing here guesses that from the error's shape.
|
|
38
40
|
*
|
|
39
41
|
* - A typed failure is an expected outcome and reads as one line. The kit's
|
|
40
|
-
* default
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
*
|
|
42
|
+
* default report (`details.defaultLines`) is a status line plus a cleaned
|
|
43
|
+
* stack, which is noise for an expected outcome, and a `Data.TaggedError`
|
|
44
|
+
* with no `message` loses the fields that say what went wrong (`CleanError`,
|
|
45
|
+
* with the `reason` lost), so this prefers the error's own message, then
|
|
46
|
+
* its tag with its fields, then `String`.
|
|
47
|
+
* - A typed failure whose error implements the kit's `CliDoc` protocol (the
|
|
48
|
+
* CLI's own `CommandError`) has already said how it reads, so it gets the
|
|
49
|
+
* kit's report of that document (`details.defaultLines`), drawn for the
|
|
50
|
+
* run's audience, instead of one line.
|
|
44
51
|
* - A defect — a `die`, a thrown exception — is a bug in savvy. It gets the
|
|
45
|
-
*
|
|
46
|
-
*
|
|
52
|
+
* kit's default report (`details.defaultLines`: the status line and the
|
|
53
|
+
* program's own stack frames, drawn for the run's audience) followed by
|
|
54
|
+
* where to report it.
|
|
47
55
|
*
|
|
48
56
|
* @internal
|
|
49
57
|
*/
|
|
50
58
|
var FailureLine = class {
|
|
51
59
|
constructor() {}
|
|
52
|
-
static render = (error, details) =>
|
|
53
|
-
`savvy
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
60
|
+
static render = (error, details) => {
|
|
61
|
+
if (details.isDefect) return [...details.defaultLines, `This is a bug in savvy. Please report it with the output above at ${ISSUES_URL}`];
|
|
62
|
+
return drawsItself(error) ? details.defaultLines : describe(error);
|
|
63
|
+
};
|
|
64
|
+
/**
|
|
65
|
+
* One line for a typed failure: its own message, else its tag with its
|
|
66
|
+
* fields, else `String`. What {@link FailureLine.render} prints for a typed
|
|
67
|
+
* failure that does not draw itself, and what `CommandError.from` keeps of
|
|
68
|
+
* a foreign error.
|
|
69
|
+
*/
|
|
70
|
+
static describe = (error) => describe(error);
|
|
57
71
|
};
|
|
58
72
|
|
|
59
73
|
//#endregion
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { Doc, Status } from "@effected/cli";
|
|
2
|
+
import { Effect } from "effect";
|
|
3
|
+
import { TerminalEnv } from "@effected/env";
|
|
4
|
+
|
|
5
|
+
//#region src/internal/report.ts
|
|
6
|
+
/** A status line, with any detail lines muted and indented under it. */
|
|
7
|
+
const item = (status, text, detail) => {
|
|
8
|
+
const head = [
|
|
9
|
+
Doc.status(Status.core, status),
|
|
10
|
+
" ",
|
|
11
|
+
...Array.isArray(text) ? text : [text]
|
|
12
|
+
];
|
|
13
|
+
return detail.length === 0 ? Doc.line(head) : Doc.lines([head, ...detail.map((line) => Doc.text(` ${line}`, "muted"))]);
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* The result a person ran a command to see, as `Doc` blocks.
|
|
17
|
+
*
|
|
18
|
+
* @remarks
|
|
19
|
+
* A command builds its report from these blocks and writes it once with
|
|
20
|
+
* {@link Report.print}, which renders for the run's audience on stdout: ANSI
|
|
21
|
+
* for a person at a terminal, plain text for an agent or a pipe, and a GitHub
|
|
22
|
+
* Actions log under Actions. Only the status glyph and headings are painted,
|
|
23
|
+
* so the text reads the same piped. Progress and diagnostics stay on
|
|
24
|
+
* `Effect.log*`, which the CLI logger sends to stderr; a failure is the
|
|
25
|
+
* runtime's to report, never a block here — fail with `CommandError`
|
|
26
|
+
* (`./command-error.ts`), which draws itself.
|
|
27
|
+
*
|
|
28
|
+
* `Report` covers what every command repeats: status items, headings, detail
|
|
29
|
+
* lines, the summary and the audience-aware print. Anything richer — a
|
|
30
|
+
* table, a titled section, a callout, a tree, a file link, a GitHub Actions
|
|
31
|
+
* annotation — is built with `Doc.*` from `@effected/cli` directly and mixed
|
|
32
|
+
* into the same document (both build `Block`s); `Report` deliberately does not
|
|
33
|
+
* re-wrap those constructors.
|
|
34
|
+
*
|
|
35
|
+
* @internal
|
|
36
|
+
*/
|
|
37
|
+
var Report = class {
|
|
38
|
+
constructor() {}
|
|
39
|
+
/** A passing item: `✓ text`, with optional muted detail lines under it. */
|
|
40
|
+
static ok = (text, ...detail) => item("success", text, detail);
|
|
41
|
+
/** An item that passed with a caveat: `⚠ text`. */
|
|
42
|
+
static warn = (text, ...detail) => item("warning", text, detail);
|
|
43
|
+
/** A finding — still output, not a crash: `✗ text`. */
|
|
44
|
+
static fail = (text, ...detail) => item("failure", text, detail);
|
|
45
|
+
/** An item that is absent or not applicable — neither a pass nor a finding: `↷ text`. */
|
|
46
|
+
static skip = (text, ...detail) => item("skip", text, detail);
|
|
47
|
+
/** A section title. */
|
|
48
|
+
static heading = (text) => Doc.heading(2, text);
|
|
49
|
+
/** Supporting detail under the block before it, indented and muted. */
|
|
50
|
+
static detail = (text) => Doc.line(Doc.text(` ${text}`, "muted"));
|
|
51
|
+
/**
|
|
52
|
+
* A line printed as given; an empty string is a blank separator between
|
|
53
|
+
* blocks of one document. A document holding only blank lines renders as
|
|
54
|
+
* nothing, so a separator between two separately printed documents is a
|
|
55
|
+
* `Console.log("")`, not a `Report.line("")`.
|
|
56
|
+
*/
|
|
57
|
+
static line = (text) => Doc.line(text);
|
|
58
|
+
/** Text already laid out (a diff, a config dump), kept verbatim. */
|
|
59
|
+
static verbatim = (text) => Doc.verbatim(text);
|
|
60
|
+
/** `3 ok, 1 warning, 2 failed`, zero parts left out; `nothing to do` when every count is zero. */
|
|
61
|
+
static summary = (counts) => {
|
|
62
|
+
const ok = counts.ok ?? 0;
|
|
63
|
+
const warn = counts.warn ?? 0;
|
|
64
|
+
const fail = counts.fail ?? 0;
|
|
65
|
+
if (ok + warn + fail === 0) return Doc.line("nothing to do");
|
|
66
|
+
return Doc.counts({
|
|
67
|
+
layout: "inline",
|
|
68
|
+
share: false,
|
|
69
|
+
counters: [
|
|
70
|
+
Doc.counter(Status.core, "success", {
|
|
71
|
+
key: "ok",
|
|
72
|
+
label: "ok",
|
|
73
|
+
n: ok
|
|
74
|
+
}),
|
|
75
|
+
Doc.counter(Status.core, "warning", {
|
|
76
|
+
key: "warn",
|
|
77
|
+
label: warn === 1 ? "warning" : "warnings",
|
|
78
|
+
n: warn
|
|
79
|
+
}),
|
|
80
|
+
Doc.counter(Status.core, "failure", {
|
|
81
|
+
key: "fail",
|
|
82
|
+
label: "failed",
|
|
83
|
+
n: fail
|
|
84
|
+
})
|
|
85
|
+
]
|
|
86
|
+
});
|
|
87
|
+
};
|
|
88
|
+
/**
|
|
89
|
+
* Render `doc` for the run's audience and write it to stdout.
|
|
90
|
+
*
|
|
91
|
+
* @remarks
|
|
92
|
+
* A terminal wraps to its width. A pipe has no width to honour — the kit
|
|
93
|
+
* would otherwise wrap a person's piped output at 80 columns and split a
|
|
94
|
+
* long path or a `file:line:col` finding a caller greps — so off a terminal
|
|
95
|
+
* nothing is wrapped.
|
|
96
|
+
*/
|
|
97
|
+
static print = (doc) => Effect.flatMap(TerminalEnv, (terminal) => Doc.print(doc, terminal.stdout.isTerminal ? {} : { width: Number.POSITIVE_INFINITY }));
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
//#endregion
|
|
101
|
+
export { Report };
|
package/main.js
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
import { AppLive, CliPlatform, rootCommand } from "./cli/index.js";
|
|
2
1
|
import { FailureLine } from "./internal/failure-line.js";
|
|
2
|
+
import { AppLive, CliConfigLive, CliPlatform, rootCommand } from "./cli/index.js";
|
|
3
3
|
import { VersionLine } from "./internal/version-line.js";
|
|
4
4
|
import { CLI_VERSION } from "./version.js";
|
|
5
|
-
import {
|
|
6
|
-
import { CliColor, CliRuntime } from "@effected/cli";
|
|
5
|
+
import { CliAudience, CliRuntime } from "@effected/cli";
|
|
7
6
|
import { Effect, Layer, Option } from "effect";
|
|
8
7
|
import * as NodeRuntime from "@effect/platform-node/NodeRuntime";
|
|
9
8
|
import { CurrentDistribution } from "@effected/engine";
|
|
@@ -14,15 +13,19 @@ import { CurrentDistribution } from "@effected/engine";
|
|
|
14
13
|
* never import this module from the barrel.
|
|
15
14
|
*
|
|
16
15
|
* @remarks
|
|
17
|
-
*
|
|
18
|
-
* `
|
|
19
|
-
*
|
|
20
|
-
* and the
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
16
|
+
* Runs `rootCommand` through `CliAudience.run`, which resolves `--audience`,
|
|
17
|
+
* `--human`, `--agent` and `--ci` before core parses, provides the merged
|
|
18
|
+
* `AppLive` stack and `CliConfigLive` (core's built-in flags minus
|
|
19
|
+
* `--wizard`), and hands the program to `@effected/cli`'s
|
|
20
|
+
* `CliRuntime.main`. Its `env` builds the presentation environment once — the
|
|
21
|
+
* audience (overridable through `SAVVY_AUDIENCE`), the terminal, the theme,
|
|
22
|
+
* editor links and the prompt gate — installs `CliLog` (diagnostics opt-in
|
|
23
|
+
* through `SAVVY_LOG_LEVEL`, every log line on stderr) and the coloured help
|
|
24
|
+
* formatter with the carrier-aware `--version` line. `main` reports failures
|
|
25
|
+
* through that logger (a typed failure as one line, a defect as a bug
|
|
26
|
+
* report), applies the `CliExit` code a command set, and exits `64` on a
|
|
27
|
+
* usage error with the help document on stderr, so stdout stays clean for a
|
|
28
|
+
* caller that parses it.
|
|
26
29
|
* No type casts: the layer graph is validated by the compiler.
|
|
27
30
|
*
|
|
28
31
|
* @packageDocumentation
|
|
@@ -35,11 +38,15 @@ import { CurrentDistribution } from "@effected/engine";
|
|
|
35
38
|
*/
|
|
36
39
|
const main = (options = {}) => {
|
|
37
40
|
const distribution = Option.fromNullishOr(options.distribution);
|
|
38
|
-
const
|
|
39
|
-
const program = Command.run(rootCommand, { version: CLI_VERSION }).pipe(Effect.provide(AppLive), Effect.provideService(CurrentDistribution, distribution));
|
|
40
|
-
const platform = VersionFormatterLive.pipe(Layer.provideMerge(CliPlatform));
|
|
41
|
+
const program = CliAudience.run(rootCommand, { version: CLI_VERSION }).pipe(Effect.provide(Layer.merge(AppLive, CliConfigLive)), Effect.provideService(CurrentDistribution, distribution));
|
|
41
42
|
NodeRuntime.runMain(CliRuntime.main(program, {
|
|
42
|
-
platform,
|
|
43
|
+
platform: CliPlatform,
|
|
44
|
+
env: {
|
|
45
|
+
audienceEnvVar: "SAVVY_AUDIENCE",
|
|
46
|
+
stderrIsTerminal: Effect.sync(() => process.stderr.isTTY === true),
|
|
47
|
+
log: { envVar: "SAVVY_LOG_LEVEL" },
|
|
48
|
+
formatter: { formatVersion: (name, version) => VersionLine.format(name, version, distribution) }
|
|
49
|
+
},
|
|
43
50
|
render: FailureLine.render,
|
|
44
51
|
helpOnUsageError: "stderr"
|
|
45
52
|
}));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@savvy-web/cli",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.6.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "The savvy CLI — unified commit, changeset, and lint commands for the Silk Suite",
|
|
6
6
|
"homepage": "https://github.com/savvy-web/systems/tree/main/packages/cli",
|
|
@@ -36,16 +36,21 @@
|
|
|
36
36
|
"savvy": "bin/savvy.js"
|
|
37
37
|
},
|
|
38
38
|
"dependencies": {
|
|
39
|
-
"@effect/platform-node": "4.0.0
|
|
40
|
-
"@effected/cli": "^0.
|
|
41
|
-
"@effected/commands": "^0.
|
|
42
|
-
"@effected/engine": "^0.
|
|
43
|
-
"@effected/
|
|
44
|
-
"@effected/
|
|
45
|
-
"@effected/
|
|
46
|
-
"@effected/
|
|
47
|
-
"@effected/
|
|
48
|
-
"@
|
|
49
|
-
"
|
|
39
|
+
"@effect/platform-node": "^4.0.0",
|
|
40
|
+
"@effected/cli": "^0.11.0",
|
|
41
|
+
"@effected/commands": "^0.11.0",
|
|
42
|
+
"@effected/engine": "^0.3.0",
|
|
43
|
+
"@effected/env": "^0.1.0",
|
|
44
|
+
"@effected/git": "^0.20.0",
|
|
45
|
+
"@effected/glob": "^0.10.0",
|
|
46
|
+
"@effected/jsonc": "^0.15.0",
|
|
47
|
+
"@effected/templates": "^0.10.0",
|
|
48
|
+
"@effected/walker": "^0.15.0",
|
|
49
|
+
"@effected/workspaces": "^0.31.0",
|
|
50
|
+
"@effected/yaml": "^0.19.0",
|
|
51
|
+
"@savvy-web/silk-effects": "9.4.0",
|
|
52
|
+
"effect": "^4.0.0",
|
|
53
|
+
"ink": "^7.1.1",
|
|
54
|
+
"react": "^19.3.0"
|
|
50
55
|
}
|
|
51
56
|
}
|
package/version.js
CHANGED
package/internal/output.js
DELETED
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
import { CliColor } from "@effected/cli";
|
|
2
|
-
import { Console, Effect } from "effect";
|
|
3
|
-
|
|
4
|
-
//#region src/internal/output.ts
|
|
5
|
-
/**
|
|
6
|
-
* Human-readable command output.
|
|
7
|
-
*
|
|
8
|
-
* @packageDocumentation
|
|
9
|
-
*/
|
|
10
|
-
const paint = (on, code, text) => on ? `\u001b[${code}m${text}\u001b[0m` : text;
|
|
11
|
-
const glyphLine = (code, glyph, text) => Effect.flatMap(CliColor.enabled, (on) => Console.log(`${paint(on, code, glyph)} ${text}`));
|
|
12
|
-
/**
|
|
13
|
-
* The result a person ran a command to see, written to stdout.
|
|
14
|
-
*
|
|
15
|
-
* @remarks
|
|
16
|
-
* Every line goes through `Console.log`, so stdout carries a command's product
|
|
17
|
-
* and nothing else; progress and diagnostics stay on `Effect.log*`, which the
|
|
18
|
-
* CLI logger sends to stderr. Colour follows `CliColor.enabled` — off when
|
|
19
|
-
* stdout is not a terminal or `NO_COLOR` is set — and only ever tints the
|
|
20
|
-
* glyph or heading, so the text reads the same piped.
|
|
21
|
-
*
|
|
22
|
-
* @internal
|
|
23
|
-
*/
|
|
24
|
-
var Output = class {
|
|
25
|
-
constructor() {}
|
|
26
|
-
/** A passing item: `✓ text`. */
|
|
27
|
-
static ok = (text) => glyphLine("32", "✓", text);
|
|
28
|
-
/** An item that passed with a caveat: `⚠ text`. */
|
|
29
|
-
static warn = (text) => glyphLine("33", "⚠", text);
|
|
30
|
-
/** A finding — still output, not a crash: `✗ text`. */
|
|
31
|
-
static fail = (text) => glyphLine("31", "✗", text);
|
|
32
|
-
/** An item that is absent or not applicable — neither a pass nor a finding: `• text`, dimmed. */
|
|
33
|
-
static skip = (text) => glyphLine("2", "•", text);
|
|
34
|
-
/** A section title, bold on a terminal. */
|
|
35
|
-
static heading = (text) => Effect.flatMap(CliColor.enabled, (on) => Console.log(paint(on, "1", text)));
|
|
36
|
-
/** Supporting detail under the line before it, indented and dimmed on a terminal. */
|
|
37
|
-
static detail = (text) => Effect.flatMap(CliColor.enabled, (on) => Console.log(` ${paint(on, "2", text)}`));
|
|
38
|
-
/** A line printed as given — a blank separator, or a block already formatted. */
|
|
39
|
-
static line = (text) => Console.log(text);
|
|
40
|
-
/** `3 ok · 1 warning · 2 failed`, zero parts left out; `nothing to do` when every count is zero. */
|
|
41
|
-
static summary = (counts) => {
|
|
42
|
-
const parts = [
|
|
43
|
-
counts.ok ? `${counts.ok} ok` : void 0,
|
|
44
|
-
counts.warn ? `${counts.warn} ${counts.warn === 1 ? "warning" : "warnings"}` : void 0,
|
|
45
|
-
counts.fail ? `${counts.fail} failed` : void 0
|
|
46
|
-
].filter((part) => part !== void 0);
|
|
47
|
-
return Console.log(parts.length === 0 ? "nothing to do" : parts.join(" · "));
|
|
48
|
-
};
|
|
49
|
-
};
|
|
50
|
-
|
|
51
|
-
//#endregion
|
|
52
|
-
export { Output };
|