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/README.md +0 -236
- package/README.zh-CN.md +114 -199
- package/assets/silvermoon.svg +176 -175
- package/bin/silvermoon.js +0 -0
- package/docs/assets/silvermoon-avatar.svg +395 -0
- package/docs/core-concepts.md +69 -0
- package/docs/getting-started.md +115 -0
- package/docs/maintaining.md +56 -0
- package/docs/npm-package-releases.md +139 -0
- package/docs/operations.md +110 -0
- package/docs/reference.md +138 -0
- package/docs/repository-tasks.md +61 -0
- package/package.json +69 -65
- package/schema/v1/config.schema.json +27 -24
- package/schema/v1/definitions.schema.json +30 -25
- package/schema/v1/idea-status.schema.json +44 -41
- package/schema/v1/user-config.schema.json +19 -0
- package/skills/silvermoon/SKILL.md +216 -209
- package/skills/silvermoon/references/adoption.md +122 -122
- package/src/adoption.js +313 -313
- package/src/cli.js +200 -196
- package/src/config.js +193 -176
- package/src/create-idea.js +272 -235
- package/src/git.js +284 -284
- package/src/idea-layout.js +439 -439
- package/src/idea-templates.js +109 -109
- package/src/ideas.js +140 -131
- package/src/index.js +260 -260
- package/src/language.js +29 -0
- package/src/layout.js +21 -21
- package/src/repository.js +87 -87
- package/src/user-config.js +135 -0
- package/src/whatsnext.js +453 -378
- package/src/yaml.js +66 -66
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
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
);
|
|
50
|
-
io.log(`
|
|
51
|
-
io.log(`
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
}
|
|
59
|
-
io.log(
|
|
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
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
program
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
.
|
|
171
|
-
.
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
}
|
|
196
|
-
|
|
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
|
}
|