silvermoon 0.0.3 → 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/README.md +36 -13
- package/README.zh-CN.md +32 -13
- package/assets/silvermoon-avatar.svg +395 -0
- package/docs/core-concepts.md +14 -12
- package/docs/getting-started.md +71 -29
- package/docs/maintaining.md +15 -3
- package/docs/npm-package-releases.md +25 -7
- package/docs/operations.md +96 -37
- package/docs/reference.md +223 -38
- package/docs/repository-tasks.md +34 -13
- package/package.json +2 -2
- package/schema/v1/idea-status.schema.json +3 -3
- package/skills/silvermoon/SKILL.md +147 -187
- package/skills/silvermoon/references/adoption.md +34 -20
- package/src/adoption.js +136 -260
- package/src/cli.js +123 -110
- package/src/create-idea.js +222 -110
- package/src/dialogue.js +327 -0
- package/src/git.js +172 -97
- package/src/idea-layout.js +59 -123
- package/src/index.js +240 -170
- package/src/language.js +37 -0
- package/src/observation.js +350 -0
- package/src/trace.js +171 -0
- package/src/whatsnext.js +541 -378
package/src/cli.js
CHANGED
|
@@ -1,116 +1,73 @@
|
|
|
1
1
|
import { readFileSync } from "node:fs";
|
|
2
|
-
import {
|
|
3
|
-
|
|
2
|
+
import {
|
|
3
|
+
Command,
|
|
4
|
+
CommanderError,
|
|
5
|
+
InvalidArgumentError,
|
|
6
|
+
Option,
|
|
7
|
+
} from "commander";
|
|
8
|
+
|
|
9
|
+
import {
|
|
10
|
+
canonicalizeLanguageTag,
|
|
11
|
+
canonicalizeOutputLanguage,
|
|
12
|
+
} from "./language.js";
|
|
13
|
+
import { renderCheck, renderDialogue } from "./dialogue.js";
|
|
4
14
|
import { checkRepository } from "./index.js";
|
|
5
15
|
import { createIdea } from "./create-idea.js";
|
|
16
|
+
import { withTraceFile } from "./trace.js";
|
|
6
17
|
import { whatsNext } from "./whatsnext.js";
|
|
7
18
|
|
|
8
19
|
const { version: VERSION } = JSON.parse(
|
|
9
20
|
readFileSync(new URL("../package.json", import.meta.url), "utf8"),
|
|
10
21
|
);
|
|
11
22
|
|
|
23
|
+
const TRACE_FILE_SUFFIX = ".trace.jsonl";
|
|
24
|
+
|
|
12
25
|
function write(method, value) {
|
|
13
26
|
const text = value.replace(/\n$/, "");
|
|
14
27
|
if (text) method(text);
|
|
15
28
|
}
|
|
16
29
|
|
|
30
|
+
export function normalizeTraceFileName(value) {
|
|
31
|
+
return value.endsWith(TRACE_FILE_SUFFIX)
|
|
32
|
+
? value
|
|
33
|
+
: `${value}${TRACE_FILE_SUFFIX}`;
|
|
34
|
+
}
|
|
35
|
+
|
|
17
36
|
function addCommonOptions(command) {
|
|
18
37
|
return command
|
|
19
|
-
.option("--json", "
|
|
38
|
+
.option("--json", "serialize the conversation envelope as JSON")
|
|
39
|
+
.option(
|
|
40
|
+
"--trace <file.trace.jsonl>",
|
|
41
|
+
"write task timing spans as JSONL (suffix is appended when omitted)",
|
|
42
|
+
normalizeTraceFileName,
|
|
43
|
+
)
|
|
20
44
|
.option("-r, --root <path>", "repository root", process.cwd());
|
|
21
45
|
}
|
|
22
46
|
|
|
23
|
-
function
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
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
|
-
}
|
|
47
|
+
export function render(report, json, io) {
|
|
48
|
+
io.log(
|
|
49
|
+
json
|
|
50
|
+
? JSON.stringify(report, null, 2)
|
|
51
|
+
: report.intention.command === "check"
|
|
52
|
+
? renderCheck(report)
|
|
53
|
+
: renderDialogue(report),
|
|
54
|
+
);
|
|
79
55
|
}
|
|
80
56
|
|
|
81
|
-
function
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
57
|
+
function contentLanguageArgument(value) {
|
|
58
|
+
try {
|
|
59
|
+
return canonicalizeLanguageTag(value);
|
|
60
|
+
} catch (caught) {
|
|
61
|
+
throw new InvalidArgumentError(caught.message);
|
|
85
62
|
}
|
|
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
63
|
}
|
|
91
64
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
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;
|
|
65
|
+
function outputLanguageArgument(value) {
|
|
66
|
+
try {
|
|
67
|
+
return canonicalizeOutputLanguage(value);
|
|
68
|
+
} catch (caught) {
|
|
69
|
+
throw new InvalidArgumentError(caught.message);
|
|
109
70
|
}
|
|
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
71
|
}
|
|
115
72
|
|
|
116
73
|
export function createProgram(io = console) {
|
|
@@ -133,9 +90,12 @@ export function createProgram(io = console) {
|
|
|
133
90
|
Examples:
|
|
134
91
|
$ silvermoon whats-next
|
|
135
92
|
$ silvermoon whats-next <idea>
|
|
93
|
+
$ silvermoon whats-next <idea> --language zh-CN
|
|
136
94
|
$ silvermoon whats-next <idea> --json
|
|
95
|
+
$ silvermoon whats-next <idea> --trace whats-next.trace.jsonl
|
|
137
96
|
$ silvermoon create-idea
|
|
138
97
|
$ silvermoon check
|
|
98
|
+
$ silvermoon check --language en-US
|
|
139
99
|
$ silvermoon check --worktree
|
|
140
100
|
$ silvermoon check --staged
|
|
141
101
|
$ silvermoon check --commit HEAD
|
|
@@ -144,45 +104,94 @@ Examples:
|
|
|
144
104
|
addCommonOptions(
|
|
145
105
|
program
|
|
146
106
|
.command("whats-next [idea]")
|
|
147
|
-
.description("
|
|
107
|
+
.description("observe project, repository, and idea readiness")
|
|
108
|
+
.option(
|
|
109
|
+
"--language <tag>",
|
|
110
|
+
"use a built-in output language (en-US or zh-CN) for this invocation",
|
|
111
|
+
outputLanguageArgument,
|
|
112
|
+
),
|
|
148
113
|
).action(async (idea, options) => {
|
|
149
|
-
const report = await
|
|
114
|
+
const report = await withTraceFile(
|
|
115
|
+
options.trace,
|
|
116
|
+
"command.whats-next",
|
|
117
|
+
{
|
|
118
|
+
command: "whats-next",
|
|
119
|
+
outputLanguage: options.language ?? null,
|
|
120
|
+
processId: process.pid,
|
|
121
|
+
},
|
|
122
|
+
() => whatsNext({
|
|
123
|
+
idea,
|
|
124
|
+
language: options.language,
|
|
125
|
+
root: options.root,
|
|
126
|
+
}),
|
|
127
|
+
);
|
|
150
128
|
render(report, options.json, io);
|
|
151
|
-
program.setOptionValue("resultCode",
|
|
129
|
+
program.setOptionValue("resultCode", 0);
|
|
152
130
|
});
|
|
153
131
|
|
|
154
132
|
addCommonOptions(
|
|
155
133
|
program
|
|
156
134
|
.command("create-idea")
|
|
157
|
-
.description("create one structured idea scaffold after
|
|
158
|
-
.option(
|
|
135
|
+
.description("create one structured idea scaffold after readiness checks")
|
|
136
|
+
.option(
|
|
137
|
+
"--language <tag>",
|
|
138
|
+
"persist a canonical content language override on the new idea",
|
|
139
|
+
contentLanguageArgument,
|
|
140
|
+
),
|
|
159
141
|
).action(async (options) => {
|
|
160
|
-
const report = await
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
142
|
+
const report = await withTraceFile(
|
|
143
|
+
options.trace,
|
|
144
|
+
"command.create-idea",
|
|
145
|
+
{
|
|
146
|
+
command: "create-idea",
|
|
147
|
+
contentLanguage: options.language ?? null,
|
|
148
|
+
processId: process.pid,
|
|
149
|
+
},
|
|
150
|
+
() => createIdea({
|
|
151
|
+
language: options.language,
|
|
152
|
+
root: options.root,
|
|
153
|
+
}),
|
|
154
|
+
);
|
|
164
155
|
render(report, options.json, io);
|
|
165
|
-
program.setOptionValue("resultCode",
|
|
156
|
+
program.setOptionValue("resultCode", 0);
|
|
166
157
|
});
|
|
167
158
|
|
|
168
159
|
addCommonOptions(
|
|
169
160
|
program
|
|
170
161
|
.command("check")
|
|
171
|
-
.description("validate Silvermoon configuration
|
|
172
|
-
.
|
|
162
|
+
.description("validate Silvermoon configuration and idea state")
|
|
163
|
+
.option(
|
|
164
|
+
"--language <tag>",
|
|
165
|
+
"use a built-in output language (en-US or zh-CN) for this invocation",
|
|
166
|
+
outputLanguageArgument,
|
|
167
|
+
)
|
|
168
|
+
.addOption(new Option("--remote", "fetch and validate the configured primary tip").conflicts(["commit", "staged", "worktree"]))
|
|
173
169
|
.addOption(new Option("--commit <revision>", "validate one local commit snapshot").conflicts(["remote", "staged", "worktree"]))
|
|
174
170
|
.addOption(new Option("--staged", "validate the index snapshot").conflicts(["remote", "commit", "worktree"]))
|
|
175
|
-
.addOption(new Option("--worktree", "validate HEAD plus
|
|
171
|
+
.addOption(new Option("--worktree", "validate HEAD plus staged, unstaged, and untracked changes").conflicts(["remote", "commit", "staged"])),
|
|
176
172
|
).action(async (options) => {
|
|
177
|
-
const report = await
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
173
|
+
const report = await withTraceFile(
|
|
174
|
+
options.trace,
|
|
175
|
+
"command.check",
|
|
176
|
+
{
|
|
177
|
+
command: "check",
|
|
178
|
+
outputLanguage: options.language ?? null,
|
|
179
|
+
processId: process.pid,
|
|
180
|
+
},
|
|
181
|
+
() => checkRepository({
|
|
182
|
+
commit: options.commit,
|
|
183
|
+
language: options.language,
|
|
184
|
+
remote: options.remote,
|
|
185
|
+
root: options.root,
|
|
186
|
+
staged: options.staged,
|
|
187
|
+
worktree: options.worktree,
|
|
188
|
+
}),
|
|
189
|
+
);
|
|
184
190
|
render(report, options.json, io);
|
|
185
|
-
program.setOptionValue(
|
|
191
|
+
program.setOptionValue(
|
|
192
|
+
"resultCode",
|
|
193
|
+
report.observation.state === "project-ready" ? 0 : 1,
|
|
194
|
+
);
|
|
186
195
|
});
|
|
187
196
|
|
|
188
197
|
return program;
|
|
@@ -194,8 +203,12 @@ export async function runCli(args, io = console) {
|
|
|
194
203
|
await program.parseAsync(args.length === 0 ? ["--help"] : args, { from: "user" });
|
|
195
204
|
} catch (caught) {
|
|
196
205
|
if (caught instanceof CommanderError) return caught.exitCode === 0 ? 0 : 2;
|
|
206
|
+
if (caught.exitCode === 2) {
|
|
207
|
+
io.error(`ERROR usage: ${caught.message}`);
|
|
208
|
+
return 2;
|
|
209
|
+
}
|
|
197
210
|
io.error(`ERROR command.failed: ${caught.message}`);
|
|
198
211
|
return 1;
|
|
199
212
|
}
|
|
200
213
|
return program.getOptionValue("resultCode") ?? 0;
|
|
201
|
-
}
|
|
214
|
+
}
|