silvermoon 0.1.0 → 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/src/index.js CHANGED
@@ -14,6 +14,10 @@ import {
14
14
  withTemporaryTree,
15
15
  withTemporaryWorktree,
16
16
  } from "./git.js";
17
+ import {
18
+ canonicalizeOutputLanguage,
19
+ DEFAULT_LANGUAGE,
20
+ } from "./language.js";
17
21
  import {
18
22
  observeSnapshot,
19
23
  unavailableObservation,
@@ -29,18 +33,25 @@ function targetArgument({ commit, remote, staged, worktree }) {
29
33
 
30
34
  function failureReport({
31
35
  intention,
36
+ outputLanguage,
32
37
  problem,
33
38
  root,
34
39
  version,
35
40
  }) {
36
41
  return {
37
42
  intention,
38
- observation: unavailableObservation({ problem, root, version }),
43
+ observation: unavailableObservation({
44
+ outputLanguage,
45
+ problem,
46
+ root,
47
+ version,
48
+ }),
39
49
  };
40
50
  }
41
51
 
42
52
  async function inspectTree({
43
53
  gitRoot,
54
+ outputLanguage,
44
55
  root,
45
56
  tree,
46
57
  userHome,
@@ -50,6 +61,7 @@ async function inspectTree({
50
61
  observeSnapshot({
51
62
  contentRoot,
52
63
  gitRoot,
64
+ outputLanguage,
53
65
  root,
54
66
  projectOnly: true,
55
67
  snapshotTree: tree,
@@ -65,12 +77,17 @@ function finishCheck(intention, observed) {
65
77
 
66
78
  export async function checkRepository({
67
79
  commit,
80
+ language,
68
81
  remote = false,
69
82
  root = process.cwd(),
70
83
  staged = false,
71
84
  userHome,
72
85
  worktree = false,
73
86
  } = {}) {
87
+ const canonicalLanguage = language === undefined
88
+ ? undefined
89
+ : canonicalizeOutputLanguage(language);
90
+ const fallbackOutputLanguage = canonicalLanguage ?? DEFAULT_LANGUAGE;
74
91
  const requestedRoot = resolve(root);
75
92
  const targetCount = [remote, commit !== undefined, staged, worktree]
76
93
  .filter(Boolean).length;
@@ -82,10 +99,17 @@ export async function checkRepository({
82
99
  throw error;
83
100
  }
84
101
  const target = targetArgument({ commit, remote, staged, worktree });
85
- const intention = { command: "check", args: { target } };
102
+ const intention = {
103
+ command: "check",
104
+ args: {
105
+ target,
106
+ language: canonicalLanguage ?? null,
107
+ },
108
+ };
86
109
  const repository = runGit(requestedRoot, ["rev-parse", "--show-toplevel"]);
87
110
  if (!repository.ok) {
88
111
  const observed = await observeSnapshot({
112
+ outputLanguage: canonicalLanguage,
89
113
  projectOnly: true,
90
114
  root: requestedRoot,
91
115
  userHome,
@@ -106,6 +130,7 @@ export async function checkRepository({
106
130
  } catch (caught) {
107
131
  return failureReport({
108
132
  intention,
133
+ outputLanguage: fallbackOutputLanguage,
109
134
  problem: {
110
135
  type: "head-unavailable",
111
136
  summary: sanitizeGitMessage(caught.message),
@@ -124,6 +149,7 @@ export async function checkRepository({
124
149
  } catch (caught) {
125
150
  return failureReport({
126
151
  intention,
152
+ outputLanguage: fallbackOutputLanguage,
127
153
  problem: {
128
154
  type: "snapshot-inspection-failed",
129
155
  summary: sanitizeGitMessage(caught.message),
@@ -140,8 +166,9 @@ export async function checkRepository({
140
166
  observedThrough: "version",
141
167
  root: repositoryRoot,
142
168
  version: { type: "remote", commit: null },
169
+ outputLanguage: fallbackOutputLanguage,
143
170
  problems: bootstrap.diagnostics.map((diagnostic) =>
144
- diagnosticProblem(diagnostic)
171
+ diagnosticProblem(diagnostic, fallbackOutputLanguage)
145
172
  ),
146
173
  },
147
174
  };
@@ -154,6 +181,7 @@ export async function checkRepository({
154
181
  const summary = sanitizeGitMessage(caught.message);
155
182
  return failureReport({
156
183
  intention,
184
+ outputLanguage: fallbackOutputLanguage,
157
185
  root: repositoryRoot,
158
186
  version: { type: "remote", commit: null },
159
187
  problem: { type: "primary-fetch-failed", summary },
@@ -166,6 +194,7 @@ export async function checkRepository({
166
194
  (contentRoot, tree) => observeSnapshot({
167
195
  contentRoot,
168
196
  gitRoot: repositoryRoot,
197
+ outputLanguage: canonicalLanguage,
169
198
  root: repositoryRoot,
170
199
  projectOnly: true,
171
200
  snapshotTree: tree,
@@ -177,6 +206,7 @@ export async function checkRepository({
177
206
  } catch (caught) {
178
207
  return failureReport({
179
208
  intention,
209
+ outputLanguage: fallbackOutputLanguage,
180
210
  problem: {
181
211
  type: "snapshot-inspection-failed",
182
212
  summary: sanitizeGitMessage(caught.message),
@@ -195,6 +225,7 @@ export async function checkRepository({
195
225
  } catch (caught) {
196
226
  return failureReport({
197
227
  intention,
228
+ outputLanguage: fallbackOutputLanguage,
198
229
  problem: {
199
230
  type: "commit-unavailable",
200
231
  summary: sanitizeGitMessage(caught.message),
@@ -210,6 +241,7 @@ export async function checkRepository({
210
241
  (contentRoot, tree) => observeSnapshot({
211
242
  contentRoot,
212
243
  gitRoot: repositoryRoot,
244
+ outputLanguage: canonicalLanguage,
213
245
  root: repositoryRoot,
214
246
  projectOnly: true,
215
247
  snapshotTree: tree,
@@ -221,6 +253,7 @@ export async function checkRepository({
221
253
  } catch (caught) {
222
254
  return failureReport({
223
255
  intention,
256
+ outputLanguage: fallbackOutputLanguage,
224
257
  problem: {
225
258
  type: "snapshot-inspection-failed",
226
259
  summary: sanitizeGitMessage(caught.message),
@@ -236,6 +269,7 @@ export async function checkRepository({
236
269
  const snapshot = indexSnapshot(repositoryRoot);
237
270
  const observed = await inspectTree({
238
271
  gitRoot: repositoryRoot,
272
+ outputLanguage: canonicalLanguage,
239
273
  root: repositoryRoot,
240
274
  tree: snapshot.tree,
241
275
  userHome,
@@ -245,6 +279,7 @@ export async function checkRepository({
245
279
  } catch (caught) {
246
280
  return failureReport({
247
281
  intention,
282
+ outputLanguage: fallbackOutputLanguage,
248
283
  problem: {
249
284
  type: "snapshot-inspection-failed",
250
285
  summary: sanitizeGitMessage(caught.message),
@@ -259,6 +294,7 @@ export async function checkRepository({
259
294
  const snapshot = worktreeSnapshot(repositoryRoot);
260
295
  const observed = await inspectTree({
261
296
  gitRoot: repositoryRoot,
297
+ outputLanguage: canonicalLanguage,
262
298
  root: repositoryRoot,
263
299
  tree: snapshot.tree,
264
300
  userHome,
@@ -268,6 +304,7 @@ export async function checkRepository({
268
304
  } catch (caught) {
269
305
  return failureReport({
270
306
  intention,
307
+ outputLanguage: fallbackOutputLanguage,
271
308
  problem: {
272
309
  type: "snapshot-inspection-failed",
273
310
  summary: sanitizeGitMessage(caught.message),
package/src/language.js CHANGED
@@ -1,4 +1,7 @@
1
1
  export const DEFAULT_LANGUAGE = "en-US";
2
+ export const OUTPUT_LANGUAGES = Object.freeze(["en-US", "zh-CN"]);
3
+
4
+ const OUTPUT_LANGUAGE_SET = new Set(OUTPUT_LANGUAGES);
2
5
 
3
6
  export function canonicalizeLanguageTag(value) {
4
7
  if (typeof value !== "string" || value.length === 0 || value.trim() !== value) {
@@ -21,9 +24,43 @@ export function isCanonicalLanguageTag(value) {
21
24
  }
22
25
  }
23
26
 
27
+ export function canonicalizeOutputLanguage(value) {
28
+ let canonical;
29
+ try {
30
+ canonical = canonicalizeLanguageTag(value);
31
+ } catch (caught) {
32
+ caught.exitCode = 2;
33
+ throw caught;
34
+ }
35
+ if (!OUTPUT_LANGUAGE_SET.has(canonical)) {
36
+ const error = new Error(
37
+ `unsupported output language: ${canonical}; expected ${OUTPUT_LANGUAGES.join(" or ")}`,
38
+ );
39
+ error.exitCode = 2;
40
+ throw error;
41
+ }
42
+ return canonical;
43
+ }
44
+
24
45
  export function resolveLanguage({ idea, project, global } = {}) {
25
46
  if (idea !== undefined) return { tag: idea, source: "idea" };
26
47
  if (project !== undefined) return { tag: project, source: "project" };
27
48
  if (global !== undefined) return { tag: global, source: "global" };
28
49
  return { tag: DEFAULT_LANGUAGE, source: "default" };
29
50
  }
51
+
52
+ export function resolveOutputLanguage({ content, override } = {}) {
53
+ if (override !== undefined) {
54
+ return {
55
+ tag: canonicalizeOutputLanguage(override),
56
+ source: "override",
57
+ };
58
+ }
59
+ if (content?.toLowerCase().startsWith("zh")) {
60
+ return { tag: "zh-CN", source: "content" };
61
+ }
62
+ return {
63
+ tag: DEFAULT_LANGUAGE,
64
+ source: content === undefined ? "default" : "content",
65
+ };
66
+ }
@@ -7,13 +7,14 @@ import {
7
7
  summarizeIdeas,
8
8
  } from "./dialogue.js";
9
9
  import { inspectIdeaLayout } from "./idea-layout.js";
10
- import { resolveLanguage } from "./language.js";
10
+ import { resolveLanguage, resolveOutputLanguage } from "./language.js";
11
11
  import { traceAsync } from "./trace.js";
12
12
  import { loadUserConfig } from "./user-config.js";
13
13
 
14
14
  function projectObservation({
15
15
  configuration,
16
16
  ideas,
17
+ outputLanguage,
17
18
  problems,
18
19
  root,
19
20
  version,
@@ -23,6 +24,7 @@ function projectObservation({
23
24
  state: "project-setup-required",
24
25
  observedThrough: "root",
25
26
  root,
27
+ outputLanguage,
26
28
  problems,
27
29
  };
28
30
  }
@@ -32,6 +34,7 @@ function projectObservation({
32
34
  observedThrough: "version",
33
35
  root,
34
36
  version,
37
+ outputLanguage,
35
38
  problems,
36
39
  };
37
40
  }
@@ -42,6 +45,7 @@ function projectObservation({
42
45
  root,
43
46
  version,
44
47
  configuration,
48
+ outputLanguage,
45
49
  problems,
46
50
  };
47
51
  }
@@ -52,6 +56,7 @@ function projectObservation({
52
56
  version,
53
57
  configuration,
54
58
  ideas,
59
+ outputLanguage,
55
60
  problems,
56
61
  };
57
62
  }
@@ -85,6 +90,7 @@ function localizeFinding(finding, language) {
85
90
  export function incompleteObservation({
86
91
  configuration,
87
92
  ideas,
93
+ outputLanguage,
88
94
  problem,
89
95
  root,
90
96
  version,
@@ -92,17 +98,24 @@ export function incompleteObservation({
92
98
  return projectObservation({
93
99
  configuration,
94
100
  ideas,
101
+ outputLanguage,
95
102
  problems: [problem],
96
103
  root,
97
104
  version,
98
105
  });
99
106
  }
100
107
 
101
- export function unavailableObservation({ root, version, problem }) {
108
+ export function unavailableObservation({
109
+ outputLanguage,
110
+ root,
111
+ version,
112
+ problem,
113
+ }) {
102
114
  return {
103
115
  state: "check-unavailable",
104
116
  root,
105
117
  version,
118
+ outputLanguage,
106
119
  problems: [problem],
107
120
  };
108
121
  }
@@ -120,12 +133,16 @@ async function observeSnapshotInternal({
120
133
  contentRoot,
121
134
  gitRoot,
122
135
  ideaLanguage,
136
+ outputLanguage: requestedOutputLanguage,
123
137
  projectOnly = false,
124
138
  root,
125
139
  snapshotTree,
126
140
  userHome,
127
141
  version,
128
142
  }) {
143
+ const outputLanguageOverride = requestedOutputLanguage === undefined
144
+ ? undefined
145
+ : resolveOutputLanguage({ override: requestedOutputLanguage }).tag;
129
146
  const adoption = await traceAsync(
130
147
  "adoption.inspect",
131
148
  {},
@@ -144,55 +161,69 @@ async function observeSnapshotInternal({
144
161
  }));
145
162
  const baseFindings = [...adoption.findings, ...userFindings]
146
163
  .sort((left, right) => left.priority - right.priority);
147
- const fallbackLanguage = resolveLanguage({
164
+ const fallbackContentLanguage = resolveLanguage({
148
165
  global: user.config?.preferredLanguage,
149
166
  }).tag;
167
+ const fallbackOutputLanguage = resolveOutputLanguage({
168
+ content: fallbackContentLanguage,
169
+ override: outputLanguageOverride,
170
+ }).tag;
150
171
 
151
172
  if (!adoption.gitReady) {
152
173
  const findings = baseFindings.map((finding) =>
153
- localizeFinding(finding, fallbackLanguage)
174
+ localizeFinding(finding, fallbackOutputLanguage)
154
175
  );
155
176
  const observation = projectObservation({
177
+ outputLanguage: fallbackOutputLanguage,
156
178
  problems: findings.map(({ problem }) => problem),
157
179
  root: adoption.root,
158
180
  });
159
181
  return {
160
182
  config: null,
183
+ contentLanguage: fallbackContentLanguage,
161
184
  findings,
162
- language: fallbackLanguage,
163
185
  layout: null,
164
186
  observation,
187
+ outputLanguage: fallbackOutputLanguage,
188
+ outputLanguageOverride,
165
189
  projectReady: false,
166
190
  };
167
191
  }
168
192
 
169
193
  if (!adoption.config) {
170
194
  const findings = baseFindings.map((finding) =>
171
- localizeFinding(finding, fallbackLanguage)
195
+ localizeFinding(finding, fallbackOutputLanguage)
172
196
  );
173
197
  const observation = projectObservation({
198
+ outputLanguage: fallbackOutputLanguage,
174
199
  problems: findings.map(({ problem }) => problem),
175
200
  root: adoption.root,
176
201
  version,
177
202
  });
178
203
  return {
179
204
  config: null,
205
+ contentLanguage: fallbackContentLanguage,
180
206
  findings,
181
- language: fallbackLanguage,
182
207
  layout: null,
183
208
  observation,
209
+ outputLanguage: fallbackOutputLanguage,
210
+ outputLanguageOverride,
184
211
  projectReady: false,
185
212
  };
186
213
  }
187
214
 
188
- const language = resolveLanguage({
215
+ const contentLanguage = resolveLanguage({
189
216
  idea: ideaLanguage,
190
217
  project: adoption.config.preferredLanguage,
191
218
  global: user.config?.preferredLanguage,
192
219
  }).tag;
193
- const configuration = resolvedConfiguration(adoption.config, language);
220
+ const outputLanguage = resolveOutputLanguage({
221
+ content: contentLanguage,
222
+ override: outputLanguageOverride,
223
+ }).tag;
224
+ const configuration = resolvedConfiguration(adoption.config, contentLanguage);
194
225
  const findings = baseFindings.map((finding) =>
195
- localizeFinding(finding, language)
226
+ localizeFinding(finding, outputLanguage)
196
227
  );
197
228
  let layout;
198
229
  try {
@@ -225,8 +256,8 @@ async function observeSnapshotInternal({
225
256
 
226
257
  const layoutFindings = layout.diagnostics.map((diagnostic) => ({
227
258
  priority: 50,
228
- problem: diagnosticProblem(diagnostic, language),
229
- instruction: diagnosticInstruction(diagnostic, language),
259
+ problem: diagnosticProblem(diagnostic, outputLanguage),
260
+ instruction: diagnosticInstruction(diagnostic, outputLanguage),
230
261
  }));
231
262
  findings.push(...layoutFindings);
232
263
  findings.sort((left, right) => left.priority - right.priority);
@@ -234,16 +265,19 @@ async function observeSnapshotInternal({
234
265
  if (layout.diagnostics.length > 0) {
235
266
  const observation = projectObservation({
236
267
  configuration,
268
+ outputLanguage,
237
269
  problems: findings.map(({ problem }) => problem),
238
270
  root: adoption.root,
239
271
  version,
240
272
  });
241
273
  return {
242
274
  config: adoption.config,
275
+ contentLanguage,
243
276
  findings,
244
- language,
245
277
  layout,
246
278
  observation,
279
+ outputLanguage,
280
+ outputLanguageOverride,
247
281
  projectReady: false,
248
282
  };
249
283
  }
@@ -253,24 +287,27 @@ async function observeSnapshotInternal({
253
287
  const observation = projectObservation({
254
288
  configuration,
255
289
  ideas,
290
+ outputLanguage,
256
291
  problems: findings.map(({ problem }) => problem),
257
292
  root: adoption.root,
258
293
  version,
259
294
  });
260
295
  return {
261
296
  config: adoption.config,
297
+ contentLanguage,
262
298
  findings,
263
- language,
264
299
  layout,
265
300
  observation,
301
+ outputLanguage,
302
+ outputLanguageOverride,
266
303
  projectReady: false,
267
304
  };
268
305
  }
269
306
 
270
307
  return {
271
308
  config: adoption.config,
309
+ contentLanguage,
272
310
  findings,
273
- language,
274
311
  layout,
275
312
  observation: {
276
313
  state: projectOnly
@@ -280,8 +317,11 @@ async function observeSnapshotInternal({
280
317
  version,
281
318
  configuration,
282
319
  ideas,
320
+ outputLanguage,
283
321
  problems: [],
284
322
  },
323
+ outputLanguage,
324
+ outputLanguageOverride,
285
325
  projectReady: true,
286
326
  };
287
327
  }
@@ -296,10 +336,15 @@ export async function observeSnapshot(options) {
296
336
 
297
337
  export function withObservationLanguage(observed, ideaLanguage) {
298
338
  if (!observed.config || !ideaLanguage) return observed;
299
- const language = resolveLanguage({ idea: ideaLanguage }).tag;
339
+ const contentLanguage = resolveLanguage({ idea: ideaLanguage }).tag;
340
+ const outputLanguage = resolveOutputLanguage({
341
+ content: contentLanguage,
342
+ override: observed.outputLanguageOverride,
343
+ }).tag;
300
344
  const observation = {
301
345
  ...observed.observation,
302
- configuration: resolvedConfiguration(observed.config, language),
346
+ configuration: resolvedConfiguration(observed.config, contentLanguage),
347
+ outputLanguage,
303
348
  };
304
- return { ...observed, language, observation };
349
+ return { ...observed, contentLanguage, observation, outputLanguage };
305
350
  }
package/src/whatsnext.js CHANGED
@@ -14,6 +14,7 @@ import {
14
14
  resolveHead,
15
15
  sanitizeGitMessage,
16
16
  } from "./git.js";
17
+ import { canonicalizeOutputLanguage } from "./language.js";
17
18
  import {
18
19
  observeSnapshot,
19
20
  repositoryProblemObservation,
@@ -217,7 +218,7 @@ async function assessRepositoryReadinessInternal({
217
218
  root,
218
219
  synchronizePrimary = true,
219
220
  }) {
220
- const language = observed.language;
221
+ const language = observed.outputLanguage;
221
222
  let changes;
222
223
  try {
223
224
  changes = inspectWorktreeChanges(root);
@@ -515,19 +516,30 @@ export async function assessIdeaCreationReadiness(options) {
515
516
 
516
517
  export async function whatsNext({
517
518
  idea: selector,
519
+ language,
518
520
  root = process.cwd(),
519
521
  userHome,
520
522
  } = {}) {
523
+ const canonicalLanguage = language === undefined
524
+ ? undefined
525
+ : canonicalizeOutputLanguage(language);
521
526
  const requestedRoot = resolve(root);
522
527
  const intention = {
523
528
  command: "whats-next",
524
- args: { idea: selector ?? null },
529
+ args: {
530
+ idea: selector ?? null,
531
+ language: canonicalLanguage ?? null,
532
+ },
525
533
  };
526
- const recheckCommand = selector === undefined
534
+ const baseRecheckCommand = selector === undefined
527
535
  ? "silvermoon whats-next"
528
536
  : `silvermoon whats-next ${JSON.stringify(selector)}`;
537
+ const recheckCommand = canonicalLanguage === undefined
538
+ ? baseRecheckCommand
539
+ : `${baseRecheckCommand} --language ${canonicalLanguage}`;
529
540
  const outcomes = [];
530
541
  let observed = await observeSnapshot({
542
+ outputLanguage: canonicalLanguage,
531
543
  root: requestedRoot,
532
544
  userHome,
533
545
  version: { type: "worktree" },
@@ -540,7 +552,7 @@ export async function whatsNext({
540
552
  projectInstructions(
541
553
  observed,
542
554
  observed.observation.root,
543
- observed.language,
555
+ observed.outputLanguage,
544
556
  recheckCommand,
545
557
  ),
546
558
  );
@@ -570,7 +582,7 @@ export async function whatsNext({
570
582
  intention,
571
583
  { ...readiness.observation, state: "navigation-ready" },
572
584
  outcomes,
573
- navigationInstruction(observed.layout.ideas, observed.language),
585
+ navigationInstruction(observed.layout.ideas, observed.outputLanguage),
574
586
  );
575
587
  }
576
588
  if (!selected) {
@@ -582,11 +594,11 @@ export async function whatsNext({
582
594
  outcomes,
583
595
  joinInstructions([
584
596
  localize(
585
- observed.language,
597
+ observed.outputLanguage,
586
598
  `Idea ${selector} does not match an observed ULID or unique alias.`,
587
599
  `Idea ${selector} 未匹配任何已观察到的 ULID 或唯一 alias。`,
588
600
  ),
589
- navigationInstruction(observed.layout.ideas, observed.language),
601
+ navigationInstruction(observed.layout.ideas, observed.outputLanguage),
590
602
  ]),
591
603
  );
592
604
  }
@@ -600,6 +612,6 @@ export async function whatsNext({
600
612
  },
601
613
  }),
602
614
  outcomes,
603
- lifecycleInstruction(selected, observed.language),
615
+ lifecycleInstruction(selected, observed.outputLanguage),
604
616
  );
605
617
  }