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.
@@ -92,13 +92,24 @@ The [`publish-npm.yml`](../.github/workflows/publish-npm.yml) workflow then:
92
92
  1. verifies the tagged commit is reachable from refreshed `origin/main`;
93
93
  2. installs the frozen pnpm dependencies;
94
94
  3. runs the allowlisted release planner and confirms the version is absent
95
- from npm;
95
+ from npm, or records that an exact-version rerun must skip publication;
96
96
  4. runs `pnpm test:unit`, `pnpm test:contract`, `pnpm test:integration`, and
97
97
  `pnpm check:skills`;
98
- 5. runs the selected package's `npm run pack:check` and
99
- `npm run test:e2e`; and
100
- 6. publishes only the selected directory with provenance and the derived npm
101
- dist-tag.
98
+ 5. creates an isolated `git archive` staging tree and generates commit-pinned
99
+ English and Chinese package READMEs without changing the tagged checkout;
100
+ 6. creates one tarball, records its path, SHA-256, npm shasum, registry
101
+ integrity, and complete file list, then passes that same file to
102
+ `npm run pack:check` and `npm run test:e2e`;
103
+ 7. publishes that exact tarball with provenance and the derived npm dist-tag
104
+ when the version was absent; and
105
+ 8. runs `verify-npm-release.mjs` until the registry is consistent or the
106
+ bounded retry window expires.
107
+
108
+ The post-publication verifier requires the exact version and dist-tag, registry
109
+ integrity and tarball bytes, package-level README, both tarball READMEs, npm
110
+ publish and SLSA provenance attestations, tagged Git commit, and commit-pinned
111
+ jsDelivr SVG responses to match the candidate. A successful run prints
112
+ `VERIFY_NPM_RELEASE_OK` with the verified identities.
102
113
 
103
114
  Release runs are serialized within this repository. The registry preflight and
104
115
  `npm publish` cannot form one cross-system transaction, so an external
@@ -110,13 +121,20 @@ duplicate publication without replacing the existing version.
110
121
  Publication stops before `npm publish` when the tag is malformed, the release
111
122
  key is unknown, the package is private, the package name or version differs
112
123
  from the mapping and tag, the npm publish configuration is not public npmjs,
113
- the commit is outside `origin/main`, the version already exists, registry state
114
- cannot be verified, or validation fails.
124
+ the commit is outside `origin/main`, registry state cannot be observed, or
125
+ validation fails. If the exact version already exists during a workflow rerun,
126
+ the publish step is skipped and the verifier must prove that registry content
127
+ is byte-identical to the rebuilt candidate and carries the expected provenance.
128
+ Any mismatch is a hard failure.
115
129
 
116
130
  For a transient GitHub or registry failure before publication, rerun the same
117
131
  workflow run. Do not move or recreate the tag. For a source, manifest, or
118
132
  validation failure, make a new commit on `main`, choose a new version, and push
119
133
  a new tag after the fix is merged. npm versions and release tags are immutable.
134
+ For a transient verification failure after publication, rerun only the failed
135
+ workflow job when GitHub offers that option; the planner recognizes the
136
+ existing version, skips `npm publish`, and performs the same complete
137
+ verification again.
120
138
 
121
139
  ## Add another package
122
140
 
@@ -9,6 +9,7 @@ publishing Silvermoon ideas.
9
9
  npx silvermoon whats-next
10
10
  npx silvermoon whats-next 01M36QGPNTXEPP61DA4KP4AVZF
11
11
  npx silvermoon whats-next publish-documentation --json
12
+ npx silvermoon whats-next publish-documentation --language zh-CN
12
13
  ```
13
14
 
14
15
  Without a selector, Silvermoon asks for a choice among multiple active ideas,
@@ -17,10 +18,13 @@ one, or many active ideas: only a selector chooses work. Completed and abandoned
17
18
  ideas are excluded from default candidates. A selector is either a canonical
18
19
  uppercase ULID or an exact, unique, case-sensitive alias.
19
20
 
20
- The shared observation includes a resolved non-empty `preferredLanguage`.
21
- Instructions use it for world entries, same-world supporting artifacts, the
22
- ledger, and user-facing explanations. Commands, identifiers, schema fields,
23
- protocol markers, and verbatim tool output retain their original form.
21
+ The shared observation exposes the resolved content preference as
22
+ `configuration.preferredLanguage` and the built-in rendering locale as
23
+ `outputLanguage`. Instructions use the content preference for world entries,
24
+ same-world supporting artifacts, and the ledger. A caller may use
25
+ `--language en-US|zh-CN` to override only Silvermoon-owned output for one
26
+ `whats-next` invocation. Commands, identifiers, schema fields, protocol
27
+ markers, and verbatim tool output retain their original form.
24
28
 
25
29
  Every invocation follows three strict layers. It first checks Git,
26
30
  configuration compatibility, and `.agents/skills/silvermoon`. It then checks
@@ -48,10 +52,12 @@ require local HEAD to be aligned with the remote tip. Its preparation reports
48
52
  omit the active-idea inventory because existing work does not replace explicit
49
53
  creation intent.
50
54
 
51
- `--language` is the only command-level language override. It normalizes a valid
52
- BCP 47 tag and writes it to the new idea's `status.yaml`. Without the option,
53
- creation writes no language field and the idea dynamically inherits project,
54
- user, or `en-US` defaults. `whats-next` and `check` do not accept the option.
55
+ For `create-idea`, `--language` normalizes any valid BCP 47 tag and writes it
56
+ to the new idea's `status.yaml` as a persistent content-language preference.
57
+ Without the option, creation writes no language field and the idea dynamically
58
+ inherits project, user, or `en-US` defaults. This is distinct from the
59
+ non-persistent `en-US|zh-CN` output override accepted by `whats-next` and
60
+ `check`.
55
61
 
56
62
  The generated files remain untracked for review. Complete `Idea.md`, optionally
57
63
  add a concise unique alias, inspect every path, and publish the initial idea
@@ -79,7 +85,8 @@ diff. Preserve unknown and concurrent work. Never force-push, use
79
85
  `reset --hard`, broadly clean the worktree, or silently rewrite history.
80
86
 
81
87
  After an observable change, run `whats-next` again. Do not poll an unchanged
82
- observation.
88
+ observation. When the invocation used `--language`, every generated retry
89
+ command preserves the same canonical output-language option.
83
90
 
84
91
  ## Author World Contracts
85
92
 
@@ -115,6 +122,7 @@ Validate the complete candidate:
115
122
 
116
123
  ```sh
117
124
  silvermoon check --worktree
125
+ silvermoon check --worktree --language zh-CN
118
126
  git add .silvermoon/ideas/<ULID>/status.yaml
119
127
  silvermoon check --staged
120
128
  ```
@@ -129,6 +137,10 @@ means the chosen project snapshot is valid; `1` means invalid or unavailable
129
137
  and must block the commit; `2` means invalid CLI usage. Do not treat the
130
138
  existence of a report as permission to commit.
131
139
 
140
+ `check --language en-US|zh-CN` changes only Silvermoon-owned report framing.
141
+ It is orthogonal to every check target and never changes the selected snapshot,
142
+ validation conclusion, problems, or exit status.
143
+
132
144
  `check` does not infer that a status field was newly written, prove who
133
145
  authorized it, compare it with a commit parent, or search history for its
134
146
  origin. Those are workflow responsibilities: match an explicit human decision
package/docs/reference.md CHANGED
@@ -67,13 +67,21 @@ world revision and does not change lifecycle decisions. Explicit
67
67
  `abandoned: false`, derived state, criteria mirrors, source locators, unknown
68
68
  keys, YAML aliases or anchors, comments, and noncanonical YAML are rejected.
69
69
 
70
- Effective language resolves as `idea language > project preferredLanguage >
71
- user preferredLanguage > en-US`. Every complete observation exposes the
72
- resolved non-empty tag as `configuration.preferredLanguage`. Missing idea
73
- language remains dynamically inherited and is never written back. Dialogue
74
- project status presents this effective value as `interaction language`
75
- (`交互语言`), meaning the language Silvermoon should use to interact with the
76
- user rather than a programming language used by the project.
70
+ Effective content language resolves as `idea language > project
71
+ preferredLanguage > user preferredLanguage > en-US`. Every complete
72
+ observation exposes the resolved non-empty tag as
73
+ `configuration.preferredLanguage`. Missing idea language remains dynamically
74
+ inherited and is never written back. Dialogue project status presents this
75
+ effective value as `content language` (`内容语言`).
76
+
77
+ Every command observation also exposes an independent `outputLanguage`.
78
+ Silvermoon's built-in output locales are exactly `en-US` and `zh-CN`.
79
+ `whats-next --language <tag>` and `check --language <tag>` normalize casing
80
+ before enforcing that allowlist, apply only to the current invocation, and
81
+ take display precedence over content language. Without an explicit output
82
+ override, content tags beginning with `zh` select `zh-CN`; every other content
83
+ tag selects `en-US`. Output selection never writes configuration or idea
84
+ status.
77
85
 
78
86
  State is derived in order:
79
87
 
@@ -88,9 +96,9 @@ State is derived in order:
88
96
  ## Public Commands
89
97
 
90
98
  ```sh
91
- silvermoon whats-next [idea] [--trace <file.trace.jsonl>]
99
+ silvermoon whats-next [idea] [--language <en-US|zh-CN>] [--trace <file.trace.jsonl>]
92
100
  silvermoon create-idea [--language <tag>] [--trace <file.trace.jsonl>]
93
- silvermoon check [--remote | --commit <revision> | --staged | --worktree] [--trace <file.trace.jsonl>]
101
+ silvermoon check [--remote | --commit <revision> | --staged | --worktree] [--language <en-US|zh-CN>] [--trace <file.trace.jsonl>]
94
102
  ```
95
103
 
96
104
  `whats-next` and `create-idea` build one dialogue envelope:
@@ -100,19 +108,24 @@ silvermoon check [--remote | --commit <revision> | --staged | --worktree] [--tra
100
108
  "intention": {
101
109
  "command": "whats-next",
102
110
  "args": {
103
- "idea": null
111
+ "idea": null,
112
+ "language": null
104
113
  }
105
114
  },
106
- "observation": {},
115
+ "observation": {
116
+ "outputLanguage": "en-US"
117
+ },
107
118
  "outcomes": [],
108
119
  "instructions": ""
109
120
  }
110
121
  ```
111
122
 
112
- - `intention` contains the command and normalized business arguments. Output
113
- format is not part of the intention.
123
+ - `intention` contains the command and normalized business arguments. For
124
+ `whats-next` and `check`, `args.language` is the canonical temporary output
125
+ override or `null`. Output format is not part of the intention.
114
126
  - `observation` describes the reliable facts for the requested command and
115
- intent, not a mandatory global idea inventory.
127
+ intent, not a mandatory global idea inventory. `outputLanguage` is present
128
+ even when Git, configuration, or a requested snapshot is unavailable.
116
129
  - `outcomes` records only high-level repository side effects actually
117
130
  attempted by this invocation. Each item has `type`, `status` (`success` or
118
131
  `failure`), and `summary`. An empty array means no side effect was attempted.
@@ -150,7 +163,11 @@ stdout, or stderr. The path is relative to the caller's working directory when
150
163
  not absolute. Events are buffered so a trace inside the repository cannot
151
164
  affect that invocation's worktree observation. The file is written after the
152
165
  measured command work finishes and uses exclusive creation, so an existing
153
- normalized target is never overwritten.
166
+ normalized target is never overwritten. The top command span records the
167
+ canonical output-language override or `null` for `whats-next` and `check`.
168
+
169
+ Unsupported output locales, empty values, and invalid BCP 47 tags are usage
170
+ errors with exit status `2`, before repository inspection or fetch.
154
171
 
155
172
  Observations are discriminated by command intent and `state`:
156
173
 
@@ -198,9 +215,9 @@ to discuss and run `create-idea`.
198
215
  `create-idea` requires the configured primary branch and upstream plus a clean
199
216
  worktree, then creates one canonical scaffold. It does not fetch, compare
200
217
  remote ancestry, or require local HEAD to match the remote tip. Its optional
201
- language override is normalized before preflight and is the only command
202
- override; without it the status remains dynamically inherited. It does not
203
- stage, commit, push, or record approval.
218
+ content-language choice accepts any canonical BCP 47 tag, is normalized before
219
+ preflight, and is persisted to the new idea; without it the status remains
220
+ dynamically inherited. It does not stage, commit, push, or record approval.
204
221
 
205
222
  ## Validation Targets
206
223
 
@@ -233,12 +250,16 @@ Unlike dialogue commands, `check --json` contains **only** `intention` and
233
250
  {
234
251
  "intention": {
235
252
  "command": "check",
236
- "args": { "target": { "type": "staged" } }
253
+ "args": {
254
+ "target": { "type": "staged" },
255
+ "language": null
256
+ }
237
257
  },
238
258
  "observation": {
239
259
  "state": "project-ready",
240
260
  "root": "D:\\Code\\silvermoon",
241
261
  "version": { "type": "staged" },
262
+ "outputLanguage": "en-US",
242
263
  "configuration": {
243
264
  "primaryRepository": "https://example.com/owner/repository.git",
244
265
  "primaryBranch": "main",
@@ -267,6 +288,10 @@ Unlike dialogue commands, `check --json` contains **only** `intention` and
267
288
  reports `commit: null` and a problem. Staged and worktree versions only
268
289
  contain `type`.
269
290
 
291
+ `--language en-US|zh-CN` is orthogonal to every target. It changes only
292
+ Silvermoon-owned natural-language framing and leaves the target, snapshot,
293
+ validation state, problems, and exit status unchanged.
294
+
270
295
  The `check` observation has its own discriminated states:
271
296
 
272
297
  - `project-ready` has root, version, resolved configuration, idea summary,
@@ -11,8 +11,10 @@ optional transport and are not protocol state.
11
11
 
12
12
  ```sh
13
13
  silvermoon whats-next [idea]
14
+ silvermoon whats-next [idea] --language zh-CN
14
15
  silvermoon create-idea
15
16
  silvermoon check
17
+ silvermoon check --language en-US
16
18
  silvermoon check --worktree
17
19
  silvermoon check --staged
18
20
  silvermoon check --commit HEAD
@@ -33,6 +35,13 @@ HEAD to match the remote tip. If hygiene blocks explicit creation, perform only
33
35
  that blocking action and then retry `create-idea` so active-idea selection
34
36
  cannot replace the pending create intent.
35
37
 
38
+ Use `--language en-US|zh-CN` with `whats-next` or `check` only when the user
39
+ explicitly requests a temporary output locale. Preserve the canonical option
40
+ on every hygiene retry. It never changes the content language recorded in
41
+ project, user, or idea configuration. `create-idea --language <tag>` remains a
42
+ separate persistent content-language choice and accepts any canonical BCP 47
43
+ tag.
44
+
36
45
  ## Decisions And Publication
37
46
 
38
47
  `whats-next` may fetch after local readiness passes, but it does not move the
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "silvermoon",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "The project spirit that checks repository state and answers what's next.",
5
5
  "packageManager": "pnpm@11.22.0",
6
6
  "type": "module",
@@ -19,6 +19,10 @@ repository or external actions with ordinary tools and Git.
19
19
  - Bare `whats-next` lists active ideas and offers creation, even when exactly one idea is active.
20
20
  Never infer selection; require an explicit ULID or alias.
21
21
  - Use `--json` only when a programmatic consumer needs the envelope.
22
+ - Use `--language en-US|zh-CN` with `whats-next` or `check` only when the user
23
+ explicitly requests a temporary output locale. Preserve the same canonical
24
+ option in every hygiene retry. It changes Silvermoon-owned rendering for
25
+ that invocation only, never project or idea content language.
22
26
  - Treat each command's `intention`, `observation`, `outcomes`, and `instructions`
23
27
  as one report. Read every problem and all ordered instructions; never combine
24
28
  reports.
@@ -73,8 +77,9 @@ review; never ask for approval of unpublished content. Publication is not
73
77
  approval and must not change `status.yaml`.
74
78
 
75
79
  An Agent may derive a concise, unique alias; do not ask for a name solely for
76
- the alias. Pass `--language <tag>` only when the user explicitly requests a
77
- stable language override. Do not use that option with `whats-next` or `check`.
80
+ the alias. Pass `create-idea --language <tag>` only when the user explicitly
81
+ requests a stable content-language override. Unlike the temporary output
82
+ override, creation accepts any canonical BCP 47 tag and persists it.
78
83
 
79
84
  ## Continue The Selected Idea
80
85
 
package/src/cli.js CHANGED
@@ -6,7 +6,10 @@ import {
6
6
  Option,
7
7
  } from "commander";
8
8
 
9
- import { canonicalizeLanguageTag } from "./language.js";
9
+ import {
10
+ canonicalizeLanguageTag,
11
+ canonicalizeOutputLanguage,
12
+ } from "./language.js";
10
13
  import { renderCheck, renderDialogue } from "./dialogue.js";
11
14
  import { checkRepository } from "./index.js";
12
15
  import { createIdea } from "./create-idea.js";
@@ -51,7 +54,7 @@ export function render(report, json, io) {
51
54
  );
52
55
  }
53
56
 
54
- function languageArgument(value) {
57
+ function contentLanguageArgument(value) {
55
58
  try {
56
59
  return canonicalizeLanguageTag(value);
57
60
  } catch (caught) {
@@ -59,6 +62,14 @@ function languageArgument(value) {
59
62
  }
60
63
  }
61
64
 
65
+ function outputLanguageArgument(value) {
66
+ try {
67
+ return canonicalizeOutputLanguage(value);
68
+ } catch (caught) {
69
+ throw new InvalidArgumentError(caught.message);
70
+ }
71
+ }
72
+
62
73
  export function createProgram(io = console) {
63
74
  const program = new Command();
64
75
  program
@@ -79,10 +90,12 @@ export function createProgram(io = console) {
79
90
  Examples:
80
91
  $ silvermoon whats-next
81
92
  $ silvermoon whats-next <idea>
93
+ $ silvermoon whats-next <idea> --language zh-CN
82
94
  $ silvermoon whats-next <idea> --json
83
95
  $ silvermoon whats-next <idea> --trace whats-next.trace.jsonl
84
96
  $ silvermoon create-idea
85
97
  $ silvermoon check
98
+ $ silvermoon check --language en-US
86
99
  $ silvermoon check --worktree
87
100
  $ silvermoon check --staged
88
101
  $ silvermoon check --commit HEAD
@@ -91,13 +104,26 @@ Examples:
91
104
  addCommonOptions(
92
105
  program
93
106
  .command("whats-next [idea]")
94
- .description("observe project, repository, and idea readiness"),
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
+ ),
95
113
  ).action(async (idea, options) => {
96
114
  const report = await withTraceFile(
97
115
  options.trace,
98
116
  "command.whats-next",
99
- { command: "whats-next", processId: process.pid },
100
- () => whatsNext({ idea, root: options.root }),
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
+ }),
101
127
  );
102
128
  render(report, options.json, io);
103
129
  program.setOptionValue("resultCode", 0);
@@ -109,14 +135,18 @@ Examples:
109
135
  .description("create one structured idea scaffold after readiness checks")
110
136
  .option(
111
137
  "--language <tag>",
112
- "persist a canonical language override on the new idea",
113
- languageArgument,
138
+ "persist a canonical content language override on the new idea",
139
+ contentLanguageArgument,
114
140
  ),
115
141
  ).action(async (options) => {
116
142
  const report = await withTraceFile(
117
143
  options.trace,
118
144
  "command.create-idea",
119
- { command: "create-idea", processId: process.pid },
145
+ {
146
+ command: "create-idea",
147
+ contentLanguage: options.language ?? null,
148
+ processId: process.pid,
149
+ },
120
150
  () => createIdea({
121
151
  language: options.language,
122
152
  root: options.root,
@@ -130,6 +160,11 @@ Examples:
130
160
  program
131
161
  .command("check")
132
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
+ )
133
168
  .addOption(new Option("--remote", "fetch and validate the configured primary tip").conflicts(["commit", "staged", "worktree"]))
134
169
  .addOption(new Option("--commit <revision>", "validate one local commit snapshot").conflicts(["remote", "staged", "worktree"]))
135
170
  .addOption(new Option("--staged", "validate the index snapshot").conflicts(["remote", "commit", "worktree"]))
@@ -138,9 +173,14 @@ Examples:
138
173
  const report = await withTraceFile(
139
174
  options.trace,
140
175
  "command.check",
141
- { command: "check", processId: process.pid },
176
+ {
177
+ command: "check",
178
+ outputLanguage: options.language ?? null,
179
+ processId: process.pid,
180
+ },
142
181
  () => checkRepository({
143
182
  commit: options.commit,
183
+ language: options.language,
144
184
  remote: options.remote,
145
185
  root: options.root,
146
186
  staged: options.staged,
@@ -194,7 +194,7 @@ export async function createIdea({
194
194
  projectInstructions(
195
195
  observed,
196
196
  observed.observation.root,
197
- observed.language,
197
+ observed.outputLanguage,
198
198
  recheckCommand,
199
199
  ),
200
200
  );
@@ -243,7 +243,7 @@ export async function createIdea({
243
243
  observation: readiness.observation,
244
244
  outcomes,
245
245
  cleanup: caught.cleanup,
246
- language: observed.language,
246
+ language: observed.outputLanguage,
247
247
  });
248
248
  }
249
249
 
@@ -270,7 +270,7 @@ export async function createIdea({
270
270
  observation: readiness.observation,
271
271
  outcomes,
272
272
  cleanup,
273
- language: observed.language,
273
+ language: observed.outputLanguage,
274
274
  });
275
275
  }
276
276
 
@@ -318,7 +318,7 @@ export async function createIdea({
318
318
  "create-idea-scaffold",
319
319
  "success",
320
320
  localize(
321
- observed.language,
321
+ observed.outputLanguage,
322
322
  `Created idea ${id} at ${paths.ideaPath}.`,
323
323
  `已在 ${paths.ideaPath} 创建 idea ${id}。`,
324
324
  ),
@@ -330,9 +330,9 @@ export async function createIdea({
330
330
  }),
331
331
  outcomes,
332
332
  localize(
333
- observed.language,
334
- `Use ${observed.language} for natural-language content while describing the requested Ideal World in ${paths.ideaDocumentPath}; keep the stable IDs and placeholders in ${paths.implementationDocumentPath}, ${paths.deploymentDocumentPath}, and ${paths.ledgerPath} synchronized.`,
335
- `使用 ${observed.language} 在 ${paths.ideaDocumentPath} 中描述请求的理想契约,并保持 ${paths.implementationDocumentPath}、${paths.deploymentDocumentPath} 与 ${paths.ledgerPath} 中的稳定 ID 和占位同步。`,
333
+ observed.outputLanguage,
334
+ `Use ${observed.contentLanguage} for natural-language content while describing the requested Ideal World in ${paths.ideaDocumentPath}; keep the stable IDs and placeholders in ${paths.implementationDocumentPath}, ${paths.deploymentDocumentPath}, and ${paths.ledgerPath} synchronized.`,
335
+ `使用 ${observed.contentLanguage} 在 ${paths.ideaDocumentPath} 中描述请求的理想契约,并保持 ${paths.implementationDocumentPath}、${paths.deploymentDocumentPath} 与 ${paths.ledgerPath} 中的稳定 ID 和占位同步。`,
336
336
  ),
337
337
  );
338
338
  } catch (caught) {
@@ -365,7 +365,7 @@ export async function createIdea({
365
365
  observation: readiness.observation,
366
366
  outcomes,
367
367
  cleanup,
368
- language: observed.language,
368
+ language: observed.outputLanguage,
369
369
  });
370
370
  }
371
371
  }
@@ -379,6 +379,6 @@ export async function createIdea({
379
379
  observation: readiness.observation,
380
380
  outcomes,
381
381
  cleanup,
382
- language: observed.language,
382
+ language: observed.outputLanguage,
383
383
  });
384
384
  }
package/src/dialogue.js CHANGED
@@ -1,3 +1,8 @@
1
+ import {
2
+ DEFAULT_LANGUAGE,
3
+ resolveOutputLanguage,
4
+ } from "./language.js";
5
+
1
6
  const ACTIVE_STATES = new Set(["preparing", "implementing", "deploying"]);
2
7
  const IDEA_STATES = [
3
8
  "preparing",
@@ -71,11 +76,19 @@ export function createEnvelope(intention, observation, outcomes, instructions) {
71
76
  }
72
77
 
73
78
  export function dialogueReadyObservation(observation, state, details = {}) {
74
- const { root, version, configuration, problems } = observation;
75
- return { state, root, version, configuration, problems, ...details };
79
+ const { root, version, configuration, outputLanguage, problems } = observation;
80
+ return {
81
+ state,
82
+ root,
83
+ version,
84
+ configuration,
85
+ outputLanguage,
86
+ problems,
87
+ ...details,
88
+ };
76
89
  }
77
90
 
78
- function renderIntention(intention, language) {
91
+ function renderIntention(intention, language, contentLanguage) {
79
92
  if (intention.command === "whats-next") {
80
93
  return intention.args.idea === null
81
94
  ? localize(
@@ -93,8 +106,8 @@ function renderIntention(intention, language) {
93
106
  return intention.args.language === null
94
107
  ? localize(
95
108
  language,
96
- `Create one new idea and use ${language} for natural-language content.`,
97
- `创建一个新 idea,并使用 ${language} 撰写自然语言内容。`,
109
+ `Create one new idea and use ${contentLanguage} for natural-language content.`,
110
+ `创建一个新 idea,并使用 ${contentLanguage} 撰写自然语言内容。`,
98
111
  )
99
112
  : localize(
100
113
  language,
@@ -198,7 +211,7 @@ function renderObservation(observation, language) {
198
211
  `- ${localize(language, "primary", "主仓库")}: ${codeSpan(`${observation.configuration.primaryRepository}#${observation.configuration.primaryBranch}`)}`,
199
212
  );
200
213
  lines.push(
201
- `- ${localize(language, "interaction language", "交互语言")}: ${observation.configuration.preferredLanguage}`,
214
+ `- ${localize(language, "content language", "内容语言")}: ${observation.configuration.preferredLanguage}`,
202
215
  );
203
216
  }
204
217
  if (observation.ideas) {
@@ -240,9 +253,16 @@ function renderOutcomes(outcomes, language) {
240
253
  }
241
254
 
242
255
  export function renderDialogue(report) {
243
- const language =
256
+ const contentLanguage =
244
257
  report.observation.configuration?.preferredLanguage
245
- ?? "en-US";
258
+ ?? DEFAULT_LANGUAGE;
259
+ const language = report.observation.outputLanguage
260
+ ?? resolveOutputLanguage({
261
+ content: contentLanguage,
262
+ override: report.intention.command === "whats-next"
263
+ ? report.intention.args.language ?? undefined
264
+ : undefined,
265
+ }).tag;
246
266
  const visibleOutcomes = report.outcomes.filter((item) =>
247
267
  !(item.type === "fetch-primary"
248
268
  && item.status === "success"
@@ -252,7 +272,7 @@ export function renderDialogue(report) {
252
272
  const sections = [
253
273
  [
254
274
  localize(language, "Current instruction", "本次指示"),
255
- renderIntention(report.intention, language),
275
+ renderIntention(report.intention, language, contentLanguage),
256
276
  ],
257
277
  [
258
278
  localize(language, "Project status", "项目现状"),
@@ -273,7 +293,11 @@ export function renderDialogue(report) {
273
293
  }
274
294
 
275
295
  export function renderCheck(report) {
276
- const language = report.observation.configuration?.preferredLanguage ?? "en-US";
296
+ const language = report.observation.outputLanguage
297
+ ?? resolveOutputLanguage({
298
+ content: report.observation.configuration?.preferredLanguage,
299
+ override: report.intention.args.language ?? undefined,
300
+ }).tag;
277
301
  const { target } = report.intention.args;
278
302
  const version = renderVersion(report.observation.version);
279
303
  const label = target.type === "commit"