silvermoon 0.0.1 → 0.0.2

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/src/cli.js CHANGED
@@ -1,197 +1,201 @@
1
- import { readFileSync } from "node:fs";
2
- import { Command, CommanderError, Option } from "commander";
3
-
4
- import { checkRepository } from "./index.js";
5
- import { createIdea } from "./create-idea.js";
6
- import { whatsNext } from "./whatsnext.js";
7
-
8
- const { version: VERSION } = JSON.parse(
9
- readFileSync(new URL("../package.json", import.meta.url), "utf8"),
10
- );
11
-
12
- function write(method, value) {
13
- const text = value.replace(/\n$/, "");
14
- if (text) method(text);
15
- }
16
-
17
- function addCommonOptions(command) {
18
- return command
19
- .option("--json", "emit the complete machine-readable report")
20
- .option("-r, --root <path>", "repository root", process.cwd());
21
- }
22
-
23
- function renderDiagnostics(report, io) {
24
- for (const item of report.diagnostics) {
25
- const location = item.path ? ` ${item.path}` : "";
26
- const output = item.level === "error" ? io.error : io.log;
27
- output(`${item.level.toUpperCase()} ${item.code}${location}: ${item.message}`);
28
- output(` Fix: ${item.remediation}`);
29
- }
30
- }
31
-
32
- function renderIdeaIdentity(idea) {
33
- return idea.alias === undefined ? idea.id : `${idea.id} (${idea.alias})`;
34
- }
35
-
36
- function renderRemediation(remediation) {
37
- return remediation.kind === "command"
38
- ? `${remediation.executable} ${remediation.args.join(" ")}`
39
- : `${remediation.description} (${remediation.path})`;
40
- }
41
-
42
- function renderWhatsNext(result, io) {
43
- if (result.onboarding) {
44
- io.log(`onboarding ${result.onboarding.status}`);
45
- io.log(` runtime ${result.onboarding.executionSource.kind} ${result.onboarding.desiredVersion}`);
46
- for (const item of result.onboarding.requirements) {
47
- io.log(
48
- ` ${item.status.padEnd(12)} ${item.id}${item.blocking ? " [blocking]" : " [non-blocking]"}: ${item.title}`,
49
- );
50
- io.log(` depends ${item.dependencies.length ? item.dependencies.join(", ") : "none"}`);
51
- io.log(` observed ${JSON.stringify(item.observed)}`);
52
- if (item.remediation) io.log(` fix ${renderRemediation(item.remediation)}`);
53
- }
54
- if (result.onboarding.recommendedAction) {
55
- io.log(` next ${renderRemediation(result.onboarding.recommendedAction)}`);
56
- }
57
- io.log(` recheck ${renderRemediation(result.onboarding.recheck)}`);
58
- }
59
- io.log(`${result.action.code}: ${result.action.message}`);
60
- if (result.observedPrimaryCommit) io.log(` primary ${result.observedPrimaryCommit}`);
61
- if (result.selectedIdea) {
62
- io.log(` idea ${renderIdeaIdentity(result.selectedIdea)}`);
63
- io.log(` state ${result.selectedIdea.state}`);
64
- io.log(` ideal ${result.selectedIdea.idealRevision}`);
65
- io.log(` inner ${result.selectedIdea.implementationRevision}`);
66
- io.log(` outer ${result.selectedIdea.deploymentRevision}`);
67
- }
68
- if (result.action.details?.world) {
69
- const world = result.action.details.world;
70
- io.log(` world ${world.name} (${world.displayName})`);
71
- io.log(` entry ${world.documentPath}`);
72
- io.log(` support ${world.auxiliaryRoot}`);
73
- io.log(` decision ${world.decisionField}: ${world.revision}`);
74
- io.log(` cascade ${world.cascade}`);
75
- }
76
- for (const idea of result.action.details?.ideas ?? []) {
77
- io.log(` option ${renderIdeaIdentity(idea)} ${idea.state}`);
78
- }
79
- }
80
-
81
- function renderCreateIdea(result, io) {
82
- if (!result.createdIdea) {
83
- renderWhatsNext(result, io);
84
- return;
85
- }
86
- io.log(`created: ${result.createdIdea.id}`);
87
- io.log(` idea ${result.createdIdea.ideaPath}`);
88
- io.log(` ledger ${result.createdIdea.ledgerPath}`);
89
- io.log(` status ${result.createdIdea.statusPath}`);
90
- }
91
-
92
- export function render(report, json, io) {
93
- if (json) {
94
- io.log(JSON.stringify(report, null, 2));
95
- return;
96
- }
97
- renderDiagnostics(report, io);
98
- if (!report.ok) {
99
- io.error("FAILED");
100
- return;
101
- }
102
- if (report.command === "whats-next") {
103
- renderWhatsNext(report.result, io);
104
- return;
105
- }
106
- if (report.command === "create-idea") {
107
- renderCreateIdea(report.result, io);
108
- return;
109
- }
110
- io.log(`OK: ${report.command}`);
111
- io.log(` target ${report.result.target}`);
112
- if (report.result.commit) io.log(` commit ${report.result.commit}`);
113
- io.log(` ideas ${report.result.checked}`);
114
- }
115
-
116
- export function createProgram(io = console) {
117
- const program = new Command();
118
- program
119
- .name("silvermoon")
120
- .description("Derive and validate repository-owned idea state.")
121
- .version(VERSION, "-v, --version", "display the installed version")
122
- .addHelpCommand(false)
123
- .showHelpAfterError("(run with --help for usage)")
124
- .showSuggestionAfterError()
125
- .configureHelp({ sortOptions: true, sortSubcommands: true })
126
- .configureOutput({
127
- outputError: (value, output) => output(value),
128
- writeErr: (value) => write(io.error, value),
129
- writeOut: (value) => write(io.log, value),
130
- })
131
- .exitOverride()
132
- .addHelpText("after", `
133
- Examples:
134
- $ silvermoon whats-next
135
- $ silvermoon whats-next <idea>
136
- $ silvermoon whats-next <idea> --json
137
- $ silvermoon create-idea
138
- $ silvermoon check
139
- $ silvermoon check --worktree
140
- $ silvermoon check --staged
141
- $ silvermoon check --commit HEAD
142
- $ silvermoon check --remote`);
143
-
144
- addCommonOptions(
145
- program
146
- .command("whats-next [idea]")
147
- .description("fetch primary and render the highest-priority next action"),
148
- ).action(async (idea, options) => {
149
- const report = await whatsNext({ enforceOnboarding: true, idea, root: options.root });
150
- render(report, options.json, io);
151
- program.setOptionValue("resultCode", report.ok ? 0 : 1);
152
- });
153
-
154
- addCommonOptions(
155
- program
156
- .command("create-idea")
157
- .description("create one structured idea scaffold after primary hygiene"),
158
- ).action(async (options) => {
159
- const report = await createIdea({ root: options.root });
160
- render(report, options.json, io);
161
- program.setOptionValue("resultCode", report.ok ? 0 : 1);
162
- });
163
-
164
- addCommonOptions(
165
- program
166
- .command("check")
167
- .description("validate Silvermoon configuration, idea state, and an optional Git target")
168
- .addOption(new Option("--remote", "fetch and validate the primary tip selected by HEAD").conflicts(["commit", "staged", "worktree"]))
169
- .addOption(new Option("--commit <revision>", "validate one local commit snapshot").conflicts(["remote", "staged", "worktree"]))
170
- .addOption(new Option("--staged", "validate the index snapshot").conflicts(["remote", "commit", "worktree"]))
171
- .addOption(new Option("--worktree", "validate HEAD plus all staged, unstaged, and untracked changes").conflicts(["remote", "commit", "staged"])),
172
- ).action(async (options) => {
173
- const report = await checkRepository({
174
- commit: options.commit,
175
- remote: options.remote,
176
- root: options.root,
177
- staged: options.staged,
178
- worktree: options.worktree,
179
- });
180
- render(report, options.json, io);
181
- program.setOptionValue("resultCode", report.ok ? 0 : 1);
182
- });
183
-
184
- return program;
185
- }
186
-
187
- export async function runCli(args, io = console) {
188
- const program = createProgram(io);
189
- try {
190
- await program.parseAsync(args.length === 0 ? ["--help"] : args, { from: "user" });
191
- } catch (caught) {
192
- if (caught instanceof CommanderError) return caught.exitCode === 0 ? 0 : 2;
193
- io.error(`ERROR command.failed: ${caught.message}`);
194
- return 1;
195
- }
196
- return program.getOptionValue("resultCode") ?? 0;
1
+ import { readFileSync } from "node:fs";
2
+ import { Command, CommanderError, Option } from "commander";
3
+
4
+ import { checkRepository } from "./index.js";
5
+ import { createIdea } from "./create-idea.js";
6
+ import { whatsNext } from "./whatsnext.js";
7
+
8
+ const { version: VERSION } = JSON.parse(
9
+ readFileSync(new URL("../package.json", import.meta.url), "utf8"),
10
+ );
11
+
12
+ function write(method, value) {
13
+ const text = value.replace(/\n$/, "");
14
+ if (text) method(text);
15
+ }
16
+
17
+ function addCommonOptions(command) {
18
+ return command
19
+ .option("--json", "emit the complete machine-readable report")
20
+ .option("-r, --root <path>", "repository root", process.cwd());
21
+ }
22
+
23
+ function renderDiagnostics(report, io) {
24
+ for (const item of report.diagnostics) {
25
+ const location = item.path ? ` ${item.path}` : "";
26
+ const output = item.level === "error" ? io.error : io.log;
27
+ output(`${item.level.toUpperCase()} ${item.code}${location}: ${item.message}`);
28
+ output(` Fix: ${item.remediation}`);
29
+ }
30
+ }
31
+
32
+ function renderIdeaIdentity(idea) {
33
+ return idea.alias === undefined ? idea.id : `${idea.id} (${idea.alias})`;
34
+ }
35
+
36
+ function renderRemediation(remediation) {
37
+ return remediation.kind === "command"
38
+ ? `${remediation.executable} ${remediation.args.join(" ")}`
39
+ : `${remediation.description} (${remediation.path})`;
40
+ }
41
+
42
+ function renderWhatsNext(result, io) {
43
+ if (result.onboarding) {
44
+ io.log(`onboarding ${result.onboarding.status}`);
45
+ for (const item of result.onboarding.gaps) {
46
+ io.log(
47
+ ` ${item.status.padEnd(12)} ${item.id}${item.blocking ? " [blocking]" : " [non-blocking]"}: ${item.title}`,
48
+ );
49
+ io.log(` depends ${item.dependencies.length ? item.dependencies.join(", ") : "none"}`);
50
+ io.log(` observed ${JSON.stringify(item.observed)}`);
51
+ if (item.remediation) io.log(` fix ${renderRemediation(item.remediation)}`);
52
+ }
53
+ if (result.onboarding.recommendedAction) {
54
+ io.log(` next ${renderRemediation(result.onboarding.recommendedAction)}`);
55
+ }
56
+ io.log(` recheck ${renderRemediation(result.onboarding.recheck)}`);
57
+ }
58
+ io.log(`${result.action.code}: ${result.action.message}`);
59
+ io.log(` language ${result.language.tag} (${result.language.source})`);
60
+ if (result.observedPrimaryCommit) io.log(` primary ${result.observedPrimaryCommit}`);
61
+ if (result.selectedIdea) {
62
+ io.log(` idea ${renderIdeaIdentity(result.selectedIdea)}`);
63
+ io.log(` state ${result.selectedIdea.state}`);
64
+ io.log(` ideal ${result.selectedIdea.idealRevision}`);
65
+ io.log(` inner ${result.selectedIdea.implementationRevision}`);
66
+ io.log(` outer ${result.selectedIdea.deploymentRevision}`);
67
+ }
68
+ if (result.action.details?.world) {
69
+ const world = result.action.details.world;
70
+ io.log(` world ${world.name} (${world.displayName})`);
71
+ io.log(` entry ${world.documentPath}`);
72
+ io.log(` support ${world.auxiliaryRoot}`);
73
+ io.log(` decision ${world.decisionField}: ${world.revision}`);
74
+ io.log(` cascade ${world.cascade}`);
75
+ }
76
+ for (const idea of result.action.details?.ideas ?? []) {
77
+ io.log(` option ${renderIdeaIdentity(idea)} ${idea.state}`);
78
+ }
79
+ }
80
+
81
+ function renderCreateIdea(result, io) {
82
+ if (!result.createdIdea) {
83
+ renderWhatsNext(result, io);
84
+ return;
85
+ }
86
+ io.log(`created: ${result.createdIdea.id}`);
87
+ io.log(` idea ${result.createdIdea.ideaPath}`);
88
+ io.log(` ledger ${result.createdIdea.ledgerPath}`);
89
+ io.log(` status ${result.createdIdea.statusPath}`);
90
+ }
91
+
92
+ export function render(report, json, io) {
93
+ if (json) {
94
+ io.log(JSON.stringify(report, null, 2));
95
+ return;
96
+ }
97
+ renderDiagnostics(report, io);
98
+ if (!report.ok) {
99
+ io.error("FAILED");
100
+ return;
101
+ }
102
+ if (report.command === "whats-next") {
103
+ renderWhatsNext(report.result, io);
104
+ return;
105
+ }
106
+ if (report.command === "create-idea") {
107
+ renderCreateIdea(report.result, io);
108
+ return;
109
+ }
110
+ io.log(`OK: ${report.command}`);
111
+ io.log(` target ${report.result.target}`);
112
+ if (report.result.commit) io.log(` commit ${report.result.commit}`);
113
+ io.log(` ideas ${report.result.checked}`);
114
+ }
115
+
116
+ export function createProgram(io = console) {
117
+ const program = new Command();
118
+ program
119
+ .name("silvermoon")
120
+ .description("Derive and validate repository-owned idea state.")
121
+ .version(VERSION, "-v, --version", "display the installed version")
122
+ .addHelpCommand(false)
123
+ .showHelpAfterError("(run with --help for usage)")
124
+ .showSuggestionAfterError()
125
+ .configureHelp({ sortOptions: true, sortSubcommands: true })
126
+ .configureOutput({
127
+ outputError: (value, output) => output(value),
128
+ writeErr: (value) => write(io.error, value),
129
+ writeOut: (value) => write(io.log, value),
130
+ })
131
+ .exitOverride()
132
+ .addHelpText("after", `
133
+ Examples:
134
+ $ silvermoon whats-next
135
+ $ silvermoon whats-next <idea>
136
+ $ silvermoon whats-next <idea> --json
137
+ $ silvermoon create-idea
138
+ $ silvermoon check
139
+ $ silvermoon check --worktree
140
+ $ silvermoon check --staged
141
+ $ silvermoon check --commit HEAD
142
+ $ silvermoon check --remote`);
143
+
144
+ addCommonOptions(
145
+ program
146
+ .command("whats-next [idea]")
147
+ .description("fetch primary and render the highest-priority next action"),
148
+ ).action(async (idea, options) => {
149
+ const report = await whatsNext({ enforceOnboarding: true, idea, root: options.root });
150
+ render(report, options.json, io);
151
+ program.setOptionValue("resultCode", report.ok ? 0 : 1);
152
+ });
153
+
154
+ addCommonOptions(
155
+ program
156
+ .command("create-idea")
157
+ .description("create one structured idea scaffold after primary hygiene")
158
+ .option("--language <tag>", "persist a canonical language override on the new idea"),
159
+ ).action(async (options) => {
160
+ const report = await createIdea({
161
+ language: options.language,
162
+ root: options.root,
163
+ });
164
+ render(report, options.json, io);
165
+ program.setOptionValue("resultCode", report.ok ? 0 : 1);
166
+ });
167
+
168
+ addCommonOptions(
169
+ program
170
+ .command("check")
171
+ .description("validate Silvermoon configuration, idea state, and an optional Git target")
172
+ .addOption(new Option("--remote", "fetch and validate the primary tip selected by HEAD").conflicts(["commit", "staged", "worktree"]))
173
+ .addOption(new Option("--commit <revision>", "validate one local commit snapshot").conflicts(["remote", "staged", "worktree"]))
174
+ .addOption(new Option("--staged", "validate the index snapshot").conflicts(["remote", "commit", "worktree"]))
175
+ .addOption(new Option("--worktree", "validate HEAD plus all staged, unstaged, and untracked changes").conflicts(["remote", "commit", "staged"])),
176
+ ).action(async (options) => {
177
+ const report = await checkRepository({
178
+ commit: options.commit,
179
+ remote: options.remote,
180
+ root: options.root,
181
+ staged: options.staged,
182
+ worktree: options.worktree,
183
+ });
184
+ render(report, options.json, io);
185
+ program.setOptionValue("resultCode", report.ok ? 0 : 1);
186
+ });
187
+
188
+ return program;
189
+ }
190
+
191
+ export async function runCli(args, io = console) {
192
+ const program = createProgram(io);
193
+ try {
194
+ await program.parseAsync(args.length === 0 ? ["--help"] : args, { from: "user" });
195
+ } catch (caught) {
196
+ if (caught instanceof CommanderError) return caught.exitCode === 0 ? 0 : 2;
197
+ io.error(`ERROR command.failed: ${caught.message}`);
198
+ return 1;
199
+ }
200
+ return program.getOptionValue("resultCode") ?? 0;
197
201
  }