silvermoon 0.0.2 → 0.1.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/README.md +168 -0
- package/README.zh-CN.md +19 -4
- package/docs/core-concepts.md +14 -12
- package/docs/getting-started.md +53 -27
- package/docs/maintaining.md +11 -2
- package/docs/operations.md +77 -30
- package/docs/reference.md +197 -37
- package/docs/repository-tasks.md +25 -13
- package/package.json +2 -2
- package/schema/v1/idea-status.schema.json +3 -3
- package/skills/silvermoon/SKILL.md +142 -187
- package/skills/silvermoon/references/adoption.md +34 -20
- package/src/adoption.js +136 -260
- package/src/cli.js +86 -113
- package/src/create-idea.js +222 -110
- package/src/dialogue.js +303 -0
- package/src/git.js +172 -97
- package/src/idea-layout.js +59 -123
- package/src/index.js +203 -170
- package/src/observation.js +305 -0
- package/src/trace.js +171 -0
- package/src/whatsnext.js +529 -378
package/src/index.js
CHANGED
|
@@ -1,87 +1,66 @@
|
|
|
1
1
|
import { resolve } from "node:path";
|
|
2
2
|
|
|
3
|
+
import {
|
|
4
|
+
diagnosticProblem,
|
|
5
|
+
} from "./dialogue.js";
|
|
3
6
|
import { loadConfig } from "./config.js";
|
|
4
7
|
import {
|
|
5
8
|
fetchPrimary,
|
|
6
9
|
indexSnapshot,
|
|
7
10
|
resolveCommit,
|
|
8
11
|
runGit,
|
|
12
|
+
sanitizeGitMessage,
|
|
9
13
|
worktreeSnapshot,
|
|
10
14
|
withTemporaryTree,
|
|
11
15
|
withTemporaryWorktree,
|
|
12
16
|
} from "./git.js";
|
|
13
|
-
import {
|
|
17
|
+
import {
|
|
18
|
+
observeSnapshot,
|
|
19
|
+
unavailableObservation,
|
|
20
|
+
} from "./observation.js";
|
|
14
21
|
|
|
15
|
-
function
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
state: idea.state,
|
|
22
|
-
};
|
|
23
|
-
if (idea.alias !== undefined) value.alias = idea.alias;
|
|
24
|
-
return value;
|
|
22
|
+
function targetArgument({ commit, remote, staged, worktree }) {
|
|
23
|
+
if (remote) return { type: "remote" };
|
|
24
|
+
if (commit !== undefined) return { type: "commit", revision: commit };
|
|
25
|
+
if (staged) return { type: "staged" };
|
|
26
|
+
if (worktree) return { type: "worktree" };
|
|
27
|
+
return { type: "head" };
|
|
25
28
|
}
|
|
26
29
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
historyCommit,
|
|
30
|
+
function failureReport({
|
|
31
|
+
intention,
|
|
32
|
+
problem,
|
|
31
33
|
root,
|
|
32
|
-
|
|
33
|
-
target = "local",
|
|
34
|
-
validateCandidate = false,
|
|
34
|
+
version,
|
|
35
35
|
}) {
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
? await inspectIdeaLayout({
|
|
40
|
-
baseRevision,
|
|
41
|
-
config: loaded.config,
|
|
42
|
-
gitRoot,
|
|
43
|
-
historyCommit,
|
|
44
|
-
root: repositoryRoot,
|
|
45
|
-
snapshotTree,
|
|
46
|
-
validateCandidate,
|
|
47
|
-
})
|
|
48
|
-
: { diagnostics: [], ideas: [] };
|
|
49
|
-
const diagnostics = [
|
|
50
|
-
...loaded.diagnostics,
|
|
51
|
-
...layout.diagnostics,
|
|
52
|
-
];
|
|
53
|
-
|
|
54
|
-
const report = {
|
|
55
|
-
command: "check",
|
|
56
|
-
ok: diagnostics.every(({ level }) => level !== "error"),
|
|
57
|
-
root: repositoryRoot,
|
|
58
|
-
diagnostics,
|
|
59
|
-
result: {
|
|
60
|
-
target,
|
|
61
|
-
checked: layout.ideas.length,
|
|
62
|
-
ideas: layout.ideas.map(ideaSummary),
|
|
63
|
-
},
|
|
36
|
+
return {
|
|
37
|
+
intention,
|
|
38
|
+
observation: unavailableObservation({ problem, root, version }),
|
|
64
39
|
};
|
|
65
|
-
return { config: loaded.config, layout, report };
|
|
66
40
|
}
|
|
67
41
|
|
|
68
|
-
function
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
42
|
+
async function inspectTree({
|
|
43
|
+
gitRoot,
|
|
44
|
+
root,
|
|
45
|
+
tree,
|
|
46
|
+
userHome,
|
|
47
|
+
version,
|
|
48
|
+
}) {
|
|
49
|
+
return withTemporaryTree(gitRoot, tree, (contentRoot) =>
|
|
50
|
+
observeSnapshot({
|
|
51
|
+
contentRoot,
|
|
52
|
+
gitRoot,
|
|
53
|
+
root,
|
|
54
|
+
projectOnly: true,
|
|
55
|
+
snapshotTree: tree,
|
|
56
|
+
userHome,
|
|
57
|
+
version,
|
|
58
|
+
})
|
|
59
|
+
);
|
|
76
60
|
}
|
|
77
61
|
|
|
78
|
-
function
|
|
79
|
-
|
|
80
|
-
report.root = root;
|
|
81
|
-
report.result = { ...report.result, target };
|
|
82
|
-
if (commit) report.result.commit = commit;
|
|
83
|
-
report.ok = report.diagnostics.every(({ level }) => level !== "error");
|
|
84
|
-
return report;
|
|
62
|
+
function finishCheck(intention, observed) {
|
|
63
|
+
return { intention, observation: observed.observation };
|
|
85
64
|
}
|
|
86
65
|
|
|
87
66
|
export async function checkRepository({
|
|
@@ -89,160 +68,214 @@ export async function checkRepository({
|
|
|
89
68
|
remote = false,
|
|
90
69
|
root = process.cwd(),
|
|
91
70
|
staged = false,
|
|
71
|
+
userHome,
|
|
92
72
|
worktree = false,
|
|
93
73
|
} = {}) {
|
|
94
|
-
const
|
|
95
|
-
const targetCount = [remote, commit !== undefined, staged, worktree]
|
|
74
|
+
const requestedRoot = resolve(root);
|
|
75
|
+
const targetCount = [remote, commit !== undefined, staged, worktree]
|
|
76
|
+
.filter(Boolean).length;
|
|
96
77
|
if (targetCount > 1) {
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
"check.target.conflict",
|
|
100
|
-
"Check targets are mutually exclusive.",
|
|
101
|
-
"Choose exactly one of remote, commit, staged, or worktree.",
|
|
78
|
+
const error = new Error(
|
|
79
|
+
"Check targets are mutually exclusive; choose one of remote, commit, staged, or worktree.",
|
|
102
80
|
);
|
|
81
|
+
error.exitCode = 2;
|
|
82
|
+
throw error;
|
|
83
|
+
}
|
|
84
|
+
const target = targetArgument({ commit, remote, staged, worktree });
|
|
85
|
+
const intention = { command: "check", args: { target } };
|
|
86
|
+
const repository = runGit(requestedRoot, ["rev-parse", "--show-toplevel"]);
|
|
87
|
+
if (!repository.ok) {
|
|
88
|
+
const observed = await observeSnapshot({
|
|
89
|
+
projectOnly: true,
|
|
90
|
+
root: requestedRoot,
|
|
91
|
+
userHome,
|
|
92
|
+
version: target.type === "commit" || target.type === "head"
|
|
93
|
+
? { type: "commit", commit: null }
|
|
94
|
+
: target.type === "remote"
|
|
95
|
+
? { type: "remote", commit: null }
|
|
96
|
+
: { type: target.type },
|
|
97
|
+
});
|
|
98
|
+
return finishCheck(intention, observed);
|
|
103
99
|
}
|
|
100
|
+
const repositoryRoot = resolve(repository.stdout);
|
|
104
101
|
|
|
105
102
|
if (remote) {
|
|
103
|
+
let head;
|
|
106
104
|
try {
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
}
|
|
105
|
+
head = resolveCommit(repositoryRoot, "HEAD");
|
|
106
|
+
} catch (caught) {
|
|
107
|
+
return failureReport({
|
|
108
|
+
intention,
|
|
109
|
+
problem: {
|
|
110
|
+
type: "head-unavailable",
|
|
111
|
+
summary: sanitizeGitMessage(caught.message),
|
|
112
|
+
},
|
|
113
|
+
root: repositoryRoot,
|
|
114
|
+
version: { type: "remote", commit: null },
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
let bootstrap;
|
|
118
|
+
try {
|
|
119
|
+
bootstrap = await withTemporaryWorktree(
|
|
120
|
+
repositoryRoot,
|
|
121
|
+
head,
|
|
122
|
+
(contentRoot) => loadConfig({ root: contentRoot }),
|
|
115
123
|
);
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
124
|
+
} catch (caught) {
|
|
125
|
+
return failureReport({
|
|
126
|
+
intention,
|
|
127
|
+
problem: {
|
|
128
|
+
type: "snapshot-inspection-failed",
|
|
129
|
+
summary: sanitizeGitMessage(caught.message),
|
|
130
|
+
},
|
|
131
|
+
root: repositoryRoot,
|
|
132
|
+
version: { type: "remote", commit: null },
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
if (!bootstrap.config) {
|
|
136
|
+
return {
|
|
137
|
+
intention,
|
|
138
|
+
observation: {
|
|
139
|
+
state: "project-setup-required",
|
|
140
|
+
observedThrough: "version",
|
|
141
|
+
root: repositoryRoot,
|
|
142
|
+
version: { type: "remote", commit: null },
|
|
143
|
+
problems: bootstrap.diagnostics.map((diagnostic) =>
|
|
144
|
+
diagnosticProblem(diagnostic)
|
|
145
|
+
),
|
|
146
|
+
},
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
let primary;
|
|
151
|
+
try {
|
|
152
|
+
primary = fetchPrimary(repositoryRoot, bootstrap.config);
|
|
153
|
+
} catch (caught) {
|
|
154
|
+
const summary = sanitizeGitMessage(caught.message);
|
|
155
|
+
return failureReport({
|
|
156
|
+
intention,
|
|
157
|
+
root: repositoryRoot,
|
|
158
|
+
version: { type: "remote", commit: null },
|
|
159
|
+
problem: { type: "primary-fetch-failed", summary },
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
try {
|
|
163
|
+
const observed = await withTemporaryWorktree(
|
|
164
|
+
repositoryRoot,
|
|
165
|
+
primary,
|
|
166
|
+
(contentRoot, tree) => observeSnapshot({
|
|
167
|
+
contentRoot,
|
|
122
168
|
gitRoot: repositoryRoot,
|
|
123
|
-
|
|
124
|
-
|
|
169
|
+
root: repositoryRoot,
|
|
170
|
+
projectOnly: true,
|
|
125
171
|
snapshotTree: tree,
|
|
126
|
-
|
|
172
|
+
userHome,
|
|
173
|
+
version: { type: "remote", commit: primary },
|
|
127
174
|
}),
|
|
128
175
|
);
|
|
129
|
-
return
|
|
130
|
-
|
|
131
|
-
|
|
176
|
+
return finishCheck(intention, observed);
|
|
177
|
+
} catch (caught) {
|
|
178
|
+
return failureReport({
|
|
179
|
+
intention,
|
|
180
|
+
problem: {
|
|
181
|
+
type: "snapshot-inspection-failed",
|
|
182
|
+
summary: sanitizeGitMessage(caught.message),
|
|
183
|
+
},
|
|
132
184
|
root: repositoryRoot,
|
|
133
|
-
|
|
185
|
+
version: { type: "remote", commit: primary },
|
|
134
186
|
});
|
|
135
|
-
} catch (caught) {
|
|
136
|
-
return targetFailure(
|
|
137
|
-
repositoryRoot,
|
|
138
|
-
"git.fetch.failed",
|
|
139
|
-
caught.message,
|
|
140
|
-
"Check remote access and retry.",
|
|
141
|
-
);
|
|
142
187
|
}
|
|
143
188
|
}
|
|
144
189
|
|
|
145
190
|
if (commit !== undefined || (!staged && !worktree)) {
|
|
146
191
|
const revision = commit ?? "HEAD";
|
|
147
|
-
const target = commit === undefined ? "head" : "commit";
|
|
148
192
|
let resolvedCommit;
|
|
149
193
|
try {
|
|
150
194
|
resolvedCommit = resolveCommit(repositoryRoot, revision);
|
|
151
195
|
} catch (caught) {
|
|
152
|
-
return
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
196
|
+
return failureReport({
|
|
197
|
+
intention,
|
|
198
|
+
problem: {
|
|
199
|
+
type: "commit-unavailable",
|
|
200
|
+
summary: sanitizeGitMessage(caught.message),
|
|
201
|
+
},
|
|
202
|
+
root: repositoryRoot,
|
|
203
|
+
version: { type: "commit", commit: null },
|
|
204
|
+
});
|
|
160
205
|
}
|
|
161
206
|
try {
|
|
162
|
-
const
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
207
|
+
const observed = await withTemporaryWorktree(
|
|
208
|
+
repositoryRoot,
|
|
209
|
+
resolvedCommit,
|
|
210
|
+
(contentRoot, tree) => observeSnapshot({
|
|
211
|
+
contentRoot,
|
|
166
212
|
gitRoot: repositoryRoot,
|
|
167
|
-
root:
|
|
213
|
+
root: repositoryRoot,
|
|
214
|
+
projectOnly: true,
|
|
168
215
|
snapshotTree: tree,
|
|
169
|
-
|
|
170
|
-
|
|
216
|
+
userHome,
|
|
217
|
+
version: { type: "commit", commit: resolvedCommit },
|
|
171
218
|
}),
|
|
172
219
|
);
|
|
173
|
-
return
|
|
174
|
-
|
|
175
|
-
|
|
220
|
+
return finishCheck(intention, observed);
|
|
221
|
+
} catch (caught) {
|
|
222
|
+
return failureReport({
|
|
223
|
+
intention,
|
|
224
|
+
problem: {
|
|
225
|
+
type: "snapshot-inspection-failed",
|
|
226
|
+
summary: sanitizeGitMessage(caught.message),
|
|
227
|
+
},
|
|
176
228
|
root: repositoryRoot,
|
|
177
|
-
|
|
229
|
+
version: { type: "commit", commit: resolvedCommit },
|
|
178
230
|
});
|
|
179
|
-
} catch (caught) {
|
|
180
|
-
return targetFailure(
|
|
181
|
-
repositoryRoot,
|
|
182
|
-
"git.snapshot.failed",
|
|
183
|
-
caught.message,
|
|
184
|
-
"Resolve the repository state and retry the commit check.",
|
|
185
|
-
);
|
|
186
231
|
}
|
|
187
232
|
}
|
|
188
233
|
|
|
189
234
|
if (staged) {
|
|
190
235
|
try {
|
|
191
236
|
const snapshot = indexSnapshot(repositoryRoot);
|
|
192
|
-
const
|
|
193
|
-
|
|
194
|
-
baseRevision: "HEAD",
|
|
195
|
-
gitRoot: repositoryRoot,
|
|
196
|
-
root: worktree,
|
|
197
|
-
snapshotTree: snapshot.tree,
|
|
198
|
-
target: "staged",
|
|
199
|
-
validateCandidate: true,
|
|
200
|
-
}),
|
|
201
|
-
);
|
|
202
|
-
return finishTarget({
|
|
203
|
-
inspected,
|
|
237
|
+
const observed = await inspectTree({
|
|
238
|
+
gitRoot: repositoryRoot,
|
|
204
239
|
root: repositoryRoot,
|
|
205
|
-
|
|
240
|
+
tree: snapshot.tree,
|
|
241
|
+
userHome,
|
|
242
|
+
version: { type: "staged" },
|
|
206
243
|
});
|
|
244
|
+
return finishCheck(intention, observed);
|
|
207
245
|
} catch (caught) {
|
|
208
|
-
return
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
if (worktree) {
|
|
218
|
-
try {
|
|
219
|
-
const snapshot = worktreeSnapshot(repositoryRoot);
|
|
220
|
-
const inspected = await withTemporaryTree(repositoryRoot, snapshot.tree, (worktree) =>
|
|
221
|
-
inspectSnapshot({
|
|
222
|
-
baseRevision: "HEAD",
|
|
223
|
-
gitRoot: repositoryRoot,
|
|
224
|
-
root: worktree,
|
|
225
|
-
snapshotTree: snapshot.tree,
|
|
226
|
-
target: "worktree",
|
|
227
|
-
validateCandidate: true,
|
|
228
|
-
}),
|
|
229
|
-
);
|
|
230
|
-
return finishTarget({
|
|
231
|
-
inspected,
|
|
246
|
+
return failureReport({
|
|
247
|
+
intention,
|
|
248
|
+
problem: {
|
|
249
|
+
type: "snapshot-inspection-failed",
|
|
250
|
+
summary: sanitizeGitMessage(caught.message),
|
|
251
|
+
},
|
|
232
252
|
root: repositoryRoot,
|
|
233
|
-
|
|
253
|
+
version: { type: "staged" },
|
|
234
254
|
});
|
|
235
|
-
} catch (caught) {
|
|
236
|
-
return targetFailure(
|
|
237
|
-
repositoryRoot,
|
|
238
|
-
"git.snapshot.failed",
|
|
239
|
-
caught.message,
|
|
240
|
-
"Resolve the worktree state and retry the worktree check.",
|
|
241
|
-
);
|
|
242
255
|
}
|
|
243
256
|
}
|
|
244
257
|
|
|
245
|
-
|
|
258
|
+
try {
|
|
259
|
+
const snapshot = worktreeSnapshot(repositoryRoot);
|
|
260
|
+
const observed = await inspectTree({
|
|
261
|
+
gitRoot: repositoryRoot,
|
|
262
|
+
root: repositoryRoot,
|
|
263
|
+
tree: snapshot.tree,
|
|
264
|
+
userHome,
|
|
265
|
+
version: { type: "worktree" },
|
|
266
|
+
});
|
|
267
|
+
return finishCheck(intention, observed);
|
|
268
|
+
} catch (caught) {
|
|
269
|
+
return failureReport({
|
|
270
|
+
intention,
|
|
271
|
+
problem: {
|
|
272
|
+
type: "snapshot-inspection-failed",
|
|
273
|
+
summary: sanitizeGitMessage(caught.message),
|
|
274
|
+
},
|
|
275
|
+
root: repositoryRoot,
|
|
276
|
+
version: { type: "worktree" },
|
|
277
|
+
});
|
|
278
|
+
}
|
|
246
279
|
}
|
|
247
280
|
|
|
248
281
|
export {
|