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