@clue-ai/cli 0.0.9 → 0.0.10
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 +37 -26
- package/bin/clue-cli.mjs +93 -54
- package/package.json +1 -1
- package/src/cli-invocation.mjs +34 -0
- package/src/command-spec.mjs +3 -0
- package/src/init-tool.mjs +2 -1
- package/src/lifecycle-guard.mjs +168 -103
- package/src/lifecycle-init.mjs +593 -187
- package/src/semantic-agent-runner.mjs +3 -1
- package/src/setup-check.mjs +641 -47
- package/src/setup-help.mjs +69 -0
- package/src/setup-prepare.mjs +75 -15
- package/src/setup-tool.mjs +421 -388
package/src/setup-tool.mjs
CHANGED
|
@@ -1,421 +1,454 @@
|
|
|
1
1
|
import { mkdir, writeFile } from "node:fs/promises";
|
|
2
2
|
import { join, resolve } from "node:path";
|
|
3
3
|
import readline from "node:readline/promises";
|
|
4
|
+
import {
|
|
5
|
+
CLUE_CLI_BINARY_NAME,
|
|
6
|
+
CLUE_CLI_PACKAGE_NAME,
|
|
7
|
+
CLUE_CLI_RECOMMENDED_PREFIX,
|
|
8
|
+
clueCliCommand,
|
|
9
|
+
} from "./cli-invocation.mjs";
|
|
4
10
|
|
|
5
11
|
const SKILL_NAMES = [
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
12
|
+
"clue-setup-orchestrator",
|
|
13
|
+
"clue-route-semantic-snapshot",
|
|
14
|
+
"clue-semantic-gen",
|
|
15
|
+
"clue-sdk-instrumentation",
|
|
16
|
+
"clue-setup-audit",
|
|
17
|
+
"clue-local-verification",
|
|
18
|
+
"clue-setup-report",
|
|
13
19
|
];
|
|
20
|
+
const SETUP_SKILL_CONTENT_VERSION = "2026-05-10.lifecycle-placement-only.v1";
|
|
14
21
|
|
|
15
22
|
const TARGETS = new Set(["codex", "claude_code"]);
|
|
16
23
|
|
|
17
24
|
const TARGET_SKILL_ROOTS = {
|
|
18
|
-
|
|
19
|
-
|
|
25
|
+
codex: [".agents", "skills"],
|
|
26
|
+
claude_code: [".claude", "skills"],
|
|
20
27
|
};
|
|
21
28
|
|
|
22
29
|
const normalizeTarget = (target) => {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
30
|
+
const normalized = String(target ?? "")
|
|
31
|
+
.trim()
|
|
32
|
+
.toLowerCase()
|
|
33
|
+
.replace(/[\s-]+/g, "_");
|
|
34
|
+
if (!TARGETS.has(normalized)) {
|
|
35
|
+
throw new Error("AIツールは codex または claude_code を指定してください");
|
|
36
|
+
}
|
|
37
|
+
return normalized;
|
|
31
38
|
};
|
|
32
39
|
|
|
33
40
|
const skillBody = (name) => {
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
41
|
+
const descriptions = {
|
|
42
|
+
"clue-setup-orchestrator":
|
|
43
|
+
"Use first when running Clue setup so lifecycle placement remains the only implementation workstream and read-only checks stay separate.",
|
|
44
|
+
"clue-route-semantic-snapshot":
|
|
45
|
+
"Use when checking backend route coverage and semantic snapshot readiness without hand-authoring generated snapshot files.",
|
|
46
|
+
"clue-semantic-gen":
|
|
47
|
+
"Use when verifying the generated Clue semantic snapshot CI workflow without editing it during lifecycle placement.",
|
|
48
|
+
"clue-sdk-instrumentation":
|
|
49
|
+
"Use when adding ClueInit, ClueIdentify, ClueSetAccount, and ClueLogout lifecycle calls to a customer repository.",
|
|
50
|
+
"clue-setup-audit":
|
|
51
|
+
"Use when reviewing Clue setup changes for missing lifecycle calls, unsafe instrumentation, leaked secrets, or bad insertion points.",
|
|
52
|
+
"clue-local-verification":
|
|
53
|
+
"Use when verifying local Clue setup artifacts before checking event delivery in the Clue setup screen.",
|
|
54
|
+
"clue-setup-report":
|
|
55
|
+
"Use when producing the final Clue setup report with changed files, blockers, env names, and next steps.",
|
|
56
|
+
};
|
|
50
57
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
58
|
+
const agentRoles = {
|
|
59
|
+
"clue-setup-orchestrator":
|
|
60
|
+
"Coordinator agent. Owns sequencing, agent assignment, gates, and blocker handling. It must not edit product code directly.",
|
|
61
|
+
"clue-route-semantic-snapshot":
|
|
62
|
+
"Semantic route readiness agent. Owns backend route inventory/readiness validation only. It must not author generated snapshot content or SDK code.",
|
|
63
|
+
"clue-semantic-gen":
|
|
64
|
+
"Semantic generation CI verification agent. Owns read-only machine-owned CI workflow verification only during lifecycle placement. It must not hand-write snapshot content, refresh CI, or edit SDK lifecycle code.",
|
|
65
|
+
"clue-sdk-instrumentation":
|
|
66
|
+
"SDK lifecycle placement agent. Owns Clue SDK dependency, initialization, and ClueInit/ClueIdentify/ClueSetAccount/ClueLogout placement only.",
|
|
67
|
+
"clue-setup-audit":
|
|
68
|
+
"Read-only monitoring agent. Owns P0/P1 review for one completed workstream at a time. It must not edit files.",
|
|
69
|
+
"clue-local-verification":
|
|
70
|
+
"Read-only verification agent. Owns static setup checks, dependency/import/startup evidence, and user verification handoff. It must not edit files or run setup-watch automatically.",
|
|
71
|
+
"clue-setup-report":
|
|
72
|
+
"Final reporting agent. Owns concise completion evidence only after execution and monitoring gates pass.",
|
|
73
|
+
};
|
|
67
74
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
75
|
+
const owns = {
|
|
76
|
+
"clue-setup-orchestrator": [
|
|
77
|
+
"read `.clue/setup-manifest.json` first",
|
|
78
|
+
"assign exactly one implementation agent for SDK lifecycle placement",
|
|
79
|
+
"assign multiple read-only monitoring agents",
|
|
80
|
+
"stop on manifest blockers or P0/P1 findings",
|
|
81
|
+
],
|
|
82
|
+
"clue-route-semantic-snapshot": [
|
|
83
|
+
"backend route discovery readiness",
|
|
84
|
+
"route coverage gaps and unsupported-framework blockers",
|
|
85
|
+
"privacy-safe evidence needed by semantic generation",
|
|
86
|
+
],
|
|
87
|
+
"clue-semantic-gen": [
|
|
88
|
+
"`.github/workflows/clue-semantic-snapshot.yml` generated workflow shape",
|
|
89
|
+
"`semantic-gen` command wiring",
|
|
90
|
+
"GitHub secrets/variables referenced by name only",
|
|
91
|
+
"privacy boundary between customer repo CI and Clue API",
|
|
92
|
+
],
|
|
93
|
+
"clue-sdk-instrumentation": [
|
|
94
|
+
"real Clue SDK imports/dependencies",
|
|
95
|
+
"ClueInit bootstrap placement",
|
|
96
|
+
"ClueIdentify login-success coverage",
|
|
97
|
+
"ClueSetAccount account/workspace/tenant coverage",
|
|
98
|
+
"ClueLogout logout/session-reset coverage",
|
|
99
|
+
"failure isolation so host behavior never depends on Clue success",
|
|
100
|
+
],
|
|
101
|
+
"clue-setup-audit": [
|
|
102
|
+
"line-by-line diff review",
|
|
103
|
+
"responsibility boundary checks",
|
|
104
|
+
"P0/P1 findings before the next workstream continues",
|
|
105
|
+
],
|
|
106
|
+
"clue-local-verification": [
|
|
107
|
+
"`setup-check` evidence",
|
|
108
|
+
"user-operated `setup-watch --local` handoff readiness",
|
|
109
|
+
"local URL confirmation without assuming ports",
|
|
110
|
+
],
|
|
111
|
+
"clue-setup-report": [
|
|
112
|
+
"changed files",
|
|
113
|
+
"commands run",
|
|
114
|
+
"skills and agents used",
|
|
115
|
+
"remaining blockers and required env names",
|
|
116
|
+
],
|
|
117
|
+
};
|
|
111
118
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
119
|
+
const mustNot = {
|
|
120
|
+
"clue-setup-orchestrator": [
|
|
121
|
+
"do product-code implementation itself",
|
|
122
|
+
"merge workstreams into one broad task",
|
|
123
|
+
"continue after a blocker without reporting it",
|
|
124
|
+
],
|
|
125
|
+
"clue-route-semantic-snapshot": [
|
|
126
|
+
"create SDK lifecycle calls",
|
|
127
|
+
"create or edit the CI workflow",
|
|
128
|
+
"hand-author generated semantic snapshot content",
|
|
129
|
+
],
|
|
130
|
+
"clue-semantic-gen": [
|
|
131
|
+
"inspect `.env` or secret files",
|
|
132
|
+
"send raw source files, prompts, completions, or secret values to Clue",
|
|
133
|
+
"hand-edit generated snapshot content",
|
|
134
|
+
],
|
|
135
|
+
"clue-sdk-instrumentation": [
|
|
136
|
+
"create no-op wrappers or placeholder lifecycle functions",
|
|
137
|
+
"place ClueInit on repeated UI/request paths",
|
|
138
|
+
"block login/logout/account/request flows on Clue success",
|
|
139
|
+
"guess service keys or ports",
|
|
140
|
+
],
|
|
141
|
+
"clue-setup-audit": [
|
|
142
|
+
"make edits",
|
|
143
|
+
"approve unclear lifecycle points",
|
|
144
|
+
"approve secret values in code, diff, or report",
|
|
145
|
+
],
|
|
146
|
+
"clue-local-verification": [
|
|
147
|
+
"make edits",
|
|
148
|
+
"assume localhost ports",
|
|
149
|
+
"run `setup-watch --local` automatically during implementation",
|
|
150
|
+
"treat user-operated setup-watch or setup-screen event delivery as complete without user-provided evidence",
|
|
151
|
+
],
|
|
152
|
+
"clue-setup-report": [
|
|
153
|
+
"claim complete with unverified workstreams",
|
|
154
|
+
"include secret values",
|
|
155
|
+
"hide blockers or skipped checks",
|
|
156
|
+
],
|
|
157
|
+
};
|
|
150
158
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
159
|
+
const outputs = {
|
|
160
|
+
"clue-setup-orchestrator": [
|
|
161
|
+
"agent plan with execution agents, monitoring agents, file ownership, and gate order",
|
|
162
|
+
"blocker list if setup cannot proceed",
|
|
163
|
+
],
|
|
164
|
+
"clue-route-semantic-snapshot": [
|
|
165
|
+
"route readiness result: ready or blocked",
|
|
166
|
+
"route coverage evidence and blocker details",
|
|
167
|
+
],
|
|
168
|
+
"clue-semantic-gen": [
|
|
169
|
+
"CI workflow verification result",
|
|
170
|
+
"blocker details when generated CI is missing or stale",
|
|
171
|
+
],
|
|
172
|
+
"clue-sdk-instrumentation": [
|
|
173
|
+
"implemented lifecycle locations",
|
|
174
|
+
"dependency/import changes",
|
|
175
|
+
"tests or verification commands",
|
|
176
|
+
],
|
|
177
|
+
"clue-setup-audit": [
|
|
178
|
+
"P0/P1 findings with file references",
|
|
179
|
+
"approval or blocked status for the reviewed workstream",
|
|
180
|
+
],
|
|
181
|
+
"clue-local-verification": [
|
|
182
|
+
"setup-check, dependency/import/startup command evidence",
|
|
183
|
+
"user-operated setup-watch/setup-screen handoff status",
|
|
184
|
+
"passed, blocked, or cannot-run status with reasons",
|
|
185
|
+
],
|
|
186
|
+
"clue-setup-report": [
|
|
187
|
+
"final report with changed files, commands, skills, agents, verification, blockers, and env names",
|
|
188
|
+
],
|
|
189
|
+
};
|
|
181
190
|
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
191
|
+
const steps = {
|
|
192
|
+
"clue-setup-orchestrator": [
|
|
193
|
+
"Use one implementation agent for SDK lifecycle placement.",
|
|
194
|
+
"That implementation agent owns only ClueInit, ClueIdentify, ClueSetAccount, and ClueLogout placement plus minimal SDK wiring.",
|
|
195
|
+
"Required implementation agent: SDK Lifecycle Placement Agent only.",
|
|
196
|
+
"The AI implementation task is only to decide where ClueInit, ClueIdentify, ClueSetAccount, and ClueLogout belong in existing code and apply the minimal SDK wiring for those calls.",
|
|
197
|
+
"Run execution agents in parallel only when file ownership does not conflict; otherwise run them sequentially with monitor gates between workstreams.",
|
|
198
|
+
"Use multiple monitoring agents for read-only checks, or named review passes if subagents are unavailable.",
|
|
199
|
+
`The initial \`${clueCliCommand("setup --clue-api-key <key> --clue-api-base-url <url> --project-key <key> --environment <environment>")}\` command already performs repository discovery, semantic CI workflow generation, setup manifest generation, and writes service-specific environment guidance to \`.env.clue\` when backend routes can be detected.`,
|
|
200
|
+
"Before implementation, read `.clue/setup-manifest.json` and treat it as the mechanical setup source of truth.",
|
|
201
|
+
"Read `.clue/setup-manifest.json` `cli_invocation` before running any Clue CLI subcommand.",
|
|
202
|
+
`Before editing a customer repository, run \`${clueCliCommand("help --json")}\` and follow its setup_execution_contract.`,
|
|
203
|
+
"Use the service keys and watch targets from `.clue/setup-manifest.json`; do not invent service keys.",
|
|
204
|
+
"If `.clue/setup-manifest.json` has status `blocked`, stop and report its blockers instead of guessing.",
|
|
205
|
+
"Treat semantic snapshot readiness and semantic CI as generated/static verification surfaces, not AI implementation workstreams.",
|
|
206
|
+
"Do not implement or refresh semantic snapshot CI during lifecycle placement; report a blocker if generated semantic artifacts are missing or stale.",
|
|
207
|
+
"Before lifecycle placement, read and apply `clue-sdk-instrumentation`.",
|
|
208
|
+
"After lifecycle placement, run a monitoring check with `clue-setup-audit` before continuing.",
|
|
209
|
+
"For final local verification, read and apply `clue-local-verification`.",
|
|
210
|
+
"For the final report, read and apply `clue-setup-report`.",
|
|
211
|
+
"Do not continue past P0/P1 monitoring findings until fixed or reported as blocked.",
|
|
212
|
+
],
|
|
213
|
+
"clue-route-semantic-snapshot": [
|
|
214
|
+
"Use this skill as the source of truth for semantic snapshot readiness and route coverage verification.",
|
|
215
|
+
"Do not hand-author semantic snapshot content files.",
|
|
216
|
+
"Do not create or commit `.clue/semantic-request.runtime.json`; the semantic CI request must be passed through the generated workflow environment instead of a repository file.",
|
|
217
|
+
"Keep route coverage/readiness checks separate from CI workflow creation and SDK lifecycle implementation.",
|
|
218
|
+
"Do not create CI workflow files or SDK lifecycle calls from this skill.",
|
|
219
|
+
`Do not create or commit \`.clue/semantic-routes.json\`; route inventory is dynamic and must be recomputed mechanically by \`${clueCliCommand("semantic-inventory")}\`, \`${clueCliCommand("setup-check")}\`, or \`${clueCliCommand("semantic-gen")}\`.`,
|
|
220
|
+
`If route inventory must be inspected locally, run \`${clueCliCommand("semantic-inventory --framework <framework> --backend-root-path <path> --repo .")}\` and review stdout instead of writing a repo file.`,
|
|
221
|
+
"Inspect only allowed source paths.",
|
|
222
|
+
"Identify the backend framework, backend root path, route files, controllers, handlers, and route declaration patterns from privacy-safe evidence.",
|
|
223
|
+
`Run \`${clueCliCommand("semantic-inventory --framework <framework> --backend-root-path <path> --repo .")}\` whenever possible to verify route discovery without AI or secrets.`,
|
|
224
|
+
"If npm/npx cannot fetch the Clue CLI package, report a blocker with the exact command and error instead of searching for another CLI path.",
|
|
225
|
+
"Verify that every API route can be discovered from the selected backend root path and that unsupported frameworks are reported as blockers.",
|
|
226
|
+
"The semantic snapshot CI command must mechanically enumerate operation_source_key, method, path_template, route fingerprints, and privacy-safe evidence before AI interpretation.",
|
|
227
|
+
"AI interpretation may summarize each mechanically discovered route, but it must not create missing routes or operation_source_key values.",
|
|
228
|
+
"The expected generated snapshot structure includes route entries with operation_source_key, method/path_template when available, route_summary, route_confidence, confidence_reason, and source_evidence_refs.",
|
|
229
|
+
"The expected generated snapshot structure includes layer_evidence for data effects, side effects, validation, permissions, failures, and component fingerprints when available.",
|
|
230
|
+
"The expected generated snapshot structure includes operation_effects only when target object evidence and domain behavior evidence are sufficient.",
|
|
231
|
+
"The expected generated snapshot structure preserves unresolved_operation_effects with missing_context instead of fabricating unknown operation effects.",
|
|
232
|
+
"Report route coverage gaps, unsupported backend frameworks, and unclear backend roots as blockers instead of guessing.",
|
|
233
|
+
],
|
|
234
|
+
"clue-semantic-gen": [
|
|
235
|
+
"Use this skill as the source of truth for semantic snapshot CI workflow format.",
|
|
236
|
+
"Keep CI workflow creation separate from route coverage/readiness checks and SDK lifecycle implementation.",
|
|
237
|
+
"Do not author semantic snapshot content, runtime request files, or SDK lifecycle calls from this skill.",
|
|
238
|
+
`Treat \`.github/workflows/clue-semantic-snapshot.yml\` as a machine-owned artifact generated by \`${clueCliCommand("setup")}\`; do not hand-edit it.`,
|
|
239
|
+
"During lifecycle placement, do not create, refresh, or hand-edit `.github/workflows/clue-semantic-snapshot.yml`; report a blocker if the generated workflow is missing or stale.",
|
|
240
|
+
"If npm/npx cannot fetch the Clue CLI package, report a blocker with the exact command and error instead of hand-writing the workflow.",
|
|
241
|
+
`The workflow must pass \`CLUE_SEMANTIC_REQUEST_JSON\` through the workflow environment and then call \`${clueCliCommand("semantic-gen --request-env CLUE_SEMANTIC_REQUEST_JSON --repo .")}\`.`,
|
|
242
|
+
"Do not create, commit, or stage `.clue/semantic-request.runtime.json` in the customer repository.",
|
|
243
|
+
"The workflow must not send GitHub actor, triggering_actor, sender, repository owner, repository name, or default branch to Clue.",
|
|
244
|
+
"The workflow should send only repository id, commit sha, workflow run id, project key variable, service key, framework, source path allowlist, source path denylist, and Clue API base URL variable.",
|
|
245
|
+
"Use minimal GitHub permissions and checkout without persisted credentials when generating the workflow.",
|
|
246
|
+
"Reference GitHub Secrets and Variables by name only.",
|
|
247
|
+
"Required GitHub secrets are `CLUE_API_KEY` and `CLUE_AI_PROVIDER_API_KEY`.",
|
|
248
|
+
"Required GitHub variables are `CLUE_AI_PROVIDER` and `CLUE_AI_MODEL`.",
|
|
249
|
+
"Semantic generation runs inside the customer repository CI with the customer AI provider key; do not send raw source code, prompts, or completions to Clue.",
|
|
250
|
+
"Do not print or commit secret values.",
|
|
251
|
+
],
|
|
252
|
+
"clue-sdk-instrumentation": [
|
|
253
|
+
"Use this skill as the source of truth for Clue SDK lifecycle implementation.",
|
|
254
|
+
"The implementation scope is only ClueInit, ClueIdentify, ClueSetAccount, and ClueLogout placement in existing lifecycle boundaries.",
|
|
255
|
+
"Keep SDK lifecycle implementation separate from semantic snapshot and CI workflow work.",
|
|
256
|
+
"Do not create semantic snapshot content or semantic snapshot CI workflow files from this skill.",
|
|
257
|
+
"Do not create no-op wrappers around nonexistent `window.Clue*` globals or local placeholder functions.",
|
|
258
|
+
"Lifecycle calls must resolve to real Clue SDK imports or a real repository adapter that forwards to the Clue SDK.",
|
|
259
|
+
"Official Clue SDK public lifecycle APIs are no-throw and own SDK failure isolation.",
|
|
260
|
+
"Do not add per-call try/catch, try/except, `.catch`, or custom safe wrappers solely around official Clue SDK public lifecycle calls.",
|
|
261
|
+
"Never await a Clue lifecycle call in a way that can block login, logout, account selection, request handling, page rendering, or API responses.",
|
|
262
|
+
"Add or report the required SDK dependency instead of fabricating lifecycle APIs.",
|
|
263
|
+
"For frontend code, add the real `@clue-ai/browser-sdk` dependency when missing. Do not invent `clue-js-sdk`, `@clue/browser-sdk`, local placeholder modules, or dynamic imports that hide a missing SDK.",
|
|
264
|
+
`When lifecycle edits are clear, write an exact replacement plan to a temporary local JSON file and apply it with \`${clueCliCommand("lifecycle-apply --plan <plan-file> --repo .")}\`.`,
|
|
265
|
+
"If npm/npx cannot fetch the Clue CLI package, report a blocker with the exact command and error instead of manually applying replacement plans.",
|
|
266
|
+
"Delete the temporary lifecycle plan file after applying it unless the user explicitly asks to keep it for review.",
|
|
267
|
+
"Use environment variable names for Clue configuration values; do not paste project keys or API keys into code.",
|
|
268
|
+
`For local env files, use the service-specific env blocks written to \`.env.clue\` by \`${clueCliCommand("setup")}\`; do not ask the user to guess \`CLUE_SERVICE_KEY\`.`,
|
|
269
|
+
"For browser code, use `CLUE_PROJECT_KEY`, `CLUE_ENVIRONMENT`, `CLUE_SERVICE_KEY`, and `CLUE_INGEST_ENDPOINT`. Let the target framework expose or inject those values safely without hard-coding a Next.js-only prefix.",
|
|
270
|
+
"Never put `CLUE_API_KEY` in frontend code, frontend env files, browser bundles, or client-readable config.",
|
|
271
|
+
"When browser SDK ingest is configured, implement a backend-owned browser token endpoint that reads server-side `CLUE_API_KEY` and requests `POST /api/v1/ingest/browser-tokens` from Clue.",
|
|
272
|
+
"Configure frontend `ClueInit` with `browserTokenProvider` that calls the local backend token endpoint and returns the token string.",
|
|
273
|
+
"The browser token request must include project key, environment, service key, and the current browser origin; the backend must attach `x-clue-api-key` server-side when calling Clue.",
|
|
274
|
+
"For FastAPI code, add `clue-fastapi-sdk` to the backend dependency file when missing, import `clue_init_fastapi` plus `ClueIdentify`, `ClueSetAccount`, and `ClueLogout` where needed, and use `CLUE_PROJECT_KEY`, `CLUE_ENVIRONMENT`, `CLUE_API_KEY`, and `CLUE_INGEST_ENDPOINT`.",
|
|
275
|
+
"Use `CLUE_SERVICE_KEY` as the canonical local service identifier. Do not ask the user to manage a separate producer id; SDKs should send producer id as the service key for setup verification compatibility.",
|
|
276
|
+
"For frontend code, pass `serviceKey` from `CLUE_SERVICE_KEY` to `ClueInit`. Do not require a separate producer id unless the repository already has one for compatibility.",
|
|
277
|
+
"For Django code, use `clue-django-sdk` only after package-manager or registry verification confirms it is installable; if it is not published or cannot be verified, report a blocker instead of adding a guessed dependency or import.",
|
|
278
|
+
"For other backend frameworks, use the matching Clue backend SDK if one exists; if no backend SDK exists, report a blocker instead of silently frontend-only setup.",
|
|
279
|
+
"Do not send raw email, raw person names, tokens, workspace names, organization names, or tenant names as lifecycle traits unless the repository already has an explicit Clue privacy policy allowing them.",
|
|
280
|
+
"Prefer stable ids and non-PII booleans/counts for ClueIdentify and ClueSetAccount traits.",
|
|
281
|
+
"Find the app/bootstrap entrypoint and add ClueInit only when the location is clear.",
|
|
282
|
+
"Place ClueInit in a stable app bootstrap, SDK adapter, or client singleton; do not place ClueInit inside React component lifecycle hooks, page components, sidebars, login/register success callbacks, or other paths that can run repeatedly.",
|
|
283
|
+
"If the repository needs lifecycle calls from UI hooks, import a shared initialized Clue adapter instead of calling ClueInit again.",
|
|
284
|
+
"Find every clear frontend and backend login success path and add ClueIdentify to every one when the user identity is available.",
|
|
285
|
+
"Find every clear frontend and backend account, workspace, organization, or tenant resolution path and add ClueSetAccount to every one when the subject is available.",
|
|
286
|
+
"Find every clear frontend and backend logout/sign-out/session reset completion path and add ClueLogout to every one when the reset point is clear.",
|
|
287
|
+
"Skip unclear lifecycle points and report blockers.",
|
|
288
|
+
],
|
|
289
|
+
"clue-setup-audit": [
|
|
290
|
+
"Act as a read-only monitoring agent, not the execution agent.",
|
|
291
|
+
"Check one completed workstream at a time and report P0/P1 issues before more implementation continues.",
|
|
292
|
+
"Review changed files line by line.",
|
|
293
|
+
"Verify semantic snapshot, semantic CI, and SDK lifecycle responsibilities did not bleed into each other.",
|
|
294
|
+
"Reject hand-authored semantic snapshot content and runtime request files.",
|
|
295
|
+
`Reject semantic CI workflows that were not generated by or equivalent to \`${clueCliCommand("semantic-workflow")}\`.`,
|
|
296
|
+
"Reject semantic CI workflows that send GitHub actor, triggering_actor, sender, repository owner, repository name, or default branch to Clue.",
|
|
297
|
+
"Reject no-op lifecycle wrappers or lifecycle calls that do not resolve to a real Clue SDK import.",
|
|
298
|
+
"Reject wrong SDK package names. Frontend must use `@clue-ai/browser-sdk`; FastAPI must use `clue-fastapi-sdk`; Django must use `clue-django-sdk`.",
|
|
299
|
+
"Reject Django SDK setup when `clue-django-sdk` installability has not been verified.",
|
|
300
|
+
"Reject backend setup when backend routes exist but no backend Clue SDK dependency/import/init was added.",
|
|
301
|
+
"Reject awaited lifecycle calls that can block host service behavior.",
|
|
302
|
+
"Reject setup that covers only one login path when multiple login success paths are clearly present.",
|
|
303
|
+
"Reject ClueInit inside React component lifecycle hooks, page components, sidebars, login/register success callbacks, or any repeated user interaction path.",
|
|
304
|
+
"Reject broad ClueTrack instrumentation and DOM clue tags.",
|
|
305
|
+
"Reject ClueTrack instrumentation unless the user explicitly requested product event tracking.",
|
|
306
|
+
"Confirm no project key, API key, secret, or env value appears in diff or report.",
|
|
307
|
+
"Confirm lifecycle insertions are minimal and reviewable.",
|
|
308
|
+
"Reject unrelated refactors, renames, file moves, formatting churn, broad cleanup, business logic rewrites, auth/session rewrites beyond minimal Clue hook insertion, and UI changes unrelated to Clue setup.",
|
|
309
|
+
],
|
|
310
|
+
"clue-local-verification": [
|
|
311
|
+
"Act as a read-only monitoring agent for local verification evidence.",
|
|
312
|
+
"Verify each workstream independently before the final setup report.",
|
|
313
|
+
"Confirm generated skill files exist.",
|
|
314
|
+
"Confirm workflow files and SDK lifecycle imports/calls exist when those phases have run.",
|
|
315
|
+
"Confirm backend routes have a backend Clue SDK dependency/import/init when a backend exists.",
|
|
316
|
+
"Confirm install/import readiness separately from static lifecycle checks. `setup-check --require-sdk-lifecycle` is a static source check only and does not prove that npm/pip install, imports, app startup, or event delivery work.",
|
|
317
|
+
"Verify frontend SDK installability/import when frontend lifecycle code was added. Run the repository's package-manager install command when possible, then run a package-manager-level import/build/typecheck command. If this cannot run, report `blocked` or `partially complete`; do not call setup complete.",
|
|
318
|
+
'Verify backend SDK installability/import when backend lifecycle code was added. Run the repository\'s Python dependency install command when possible, then run an import check such as `python -c "import clue_fastapi_sdk"` or `python -c "import clue_django_sdk"` in the target environment. If this cannot run, report `blocked` or `partially complete`; do not call setup complete.',
|
|
319
|
+
`Confirm \`.github/workflows/clue-semantic-snapshot.yml\` calls \`${clueCliCommand("semantic-gen --request-env CLUE_SEMANTIC_REQUEST_JSON --repo .")}\`.`,
|
|
320
|
+
"Confirm the semantic workflow does not send GitHub actor, triggering_actor, sender, repository owner, repository name, or default branch to Clue.",
|
|
321
|
+
"Confirm `.clue/semantic-request.runtime.json` is not created, committed, or staged.",
|
|
322
|
+
`Run \`${clueCliCommand("setup-check --framework <framework> --backend-root-path <path> --repo . --target <codex|claude_code> --require-sdk-lifecycle")}\` when possible.`,
|
|
323
|
+
`Do not run \`${clueCliCommand("setup-watch --local")}\` automatically. setup-watch requires the user to operate real local frontend/backend services and login/logout/account flows.`,
|
|
324
|
+
"If the user has not provided setup-watch or setup-screen evidence, report event delivery verification as `user_verification_pending` and do not state `setup completed`.",
|
|
325
|
+
"Local static verification passed does not mean setup complete unless dependency install, SDK imports, app startup, and user-provided setup-watch or setup-screen event delivery were all verified.",
|
|
326
|
+
"Only include lifecycle checks that the implementation ownership plan expects for that service. If a service emits an undeclared lifecycle event, treat it as a possible duplicate instrumentation issue.",
|
|
327
|
+
"Never assume localhost ports. Ask the repository scripts, env examples, or the running service output for the actual frontend/backend URLs.",
|
|
328
|
+
"If npm/npx cannot fetch the Clue CLI package, report a blocker with the exact command and error instead of searching for another CLI path.",
|
|
329
|
+
"Confirm only env names are reported.",
|
|
330
|
+
"Leave event delivery verification to the user-operated Clue setup screen or user-operated setup-watch.",
|
|
331
|
+
],
|
|
332
|
+
"clue-setup-report": [
|
|
333
|
+
"Use this skill only after execution and monitoring passes are finished.",
|
|
334
|
+
"Never claim `setup completed` from `setup-check --require-sdk-lifecycle` alone. That check is static and does not verify dependency installation, imports, app startup, or event delivery.",
|
|
335
|
+
"Completion requires all applicable evidence: SDK dependencies install successfully, SDK imports work in the target frontend/backend environments, the app starts, `setup-check --require-sdk-lifecycle` passes, and user-provided `setup-watch --local` or Clue setup screen evidence confirms expected event delivery.",
|
|
336
|
+
"If any SDK package is unpublished, install/import checks were not run, app startup was not verified, or user-operated setup-watch/setup-screen evidence was not provided, the final status must be `blocked`, `partially complete`, or `user_verification_pending`; it must not be `complete`.",
|
|
337
|
+
"For every completion claim, include the evidence source: command, exit status, output summary, file path, runtime URL, user-provided setup-watch result, or user-provided setup-screen result.",
|
|
338
|
+
"Summarize changed files.",
|
|
339
|
+
"List completed setup phases.",
|
|
340
|
+
"List skills used for each workstream.",
|
|
341
|
+
"List execution agent and monitoring agents, or named review passes if subagents were unavailable.",
|
|
342
|
+
"List blockers with exact file or environment names when available.",
|
|
343
|
+
"List required env names without values.",
|
|
344
|
+
"List P0/P1 monitoring findings and fixes.",
|
|
345
|
+
"State that commit and push were not performed.",
|
|
346
|
+
],
|
|
347
|
+
};
|
|
328
348
|
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
349
|
+
return [
|
|
350
|
+
"---",
|
|
351
|
+
`name: ${name}`,
|
|
352
|
+
`description: ${descriptions[name]}`,
|
|
353
|
+
`setup_skill_version: ${SETUP_SKILL_CONTENT_VERSION}`,
|
|
354
|
+
"---",
|
|
355
|
+
"",
|
|
356
|
+
"# Agent Role",
|
|
357
|
+
"",
|
|
358
|
+
agentRoles[name],
|
|
359
|
+
"",
|
|
360
|
+
"# Owns",
|
|
361
|
+
"",
|
|
362
|
+
...owns[name].map((item) => `- ${item}`),
|
|
363
|
+
"",
|
|
364
|
+
"# Must Not",
|
|
365
|
+
"",
|
|
366
|
+
...mustNot[name].map((item) => `- ${item}`),
|
|
367
|
+
"",
|
|
368
|
+
"# Output Contract",
|
|
369
|
+
"",
|
|
370
|
+
...outputs[name].map((item) => `- ${item}`),
|
|
371
|
+
"",
|
|
372
|
+
"# Shared Rules",
|
|
373
|
+
"",
|
|
374
|
+
"- For full Clue setup, use one SDK lifecycle placement implementation agent and multiple monitoring agents for read-only checks.",
|
|
375
|
+
`- Clue CLI public npm package: \`${CLUE_CLI_PACKAGE_NAME}\`.`,
|
|
376
|
+
`- Clue CLI binary name exposed by that package: \`${CLUE_CLI_BINARY_NAME}\`; a global \`${CLUE_CLI_BINARY_NAME}\` install is not required.`,
|
|
377
|
+
`- Use \`${CLUE_CLI_RECOMMENDED_PREFIX} <command>\` for Clue CLI commands unless \`.clue/setup-manifest.json\` explicitly provides a different invocation.`,
|
|
378
|
+
`- If checking Clue CLI availability, run \`${CLUE_CLI_RECOMMENDED_PREFIX} --version\` or \`${CLUE_CLI_RECOMMENDED_PREFIX} --help\` and report the exact fetch/runtime error if it fails.`,
|
|
379
|
+
`- Before editing a customer repository, run \`${clueCliCommand("help --json")}\` and follow its setup_execution_contract.`,
|
|
380
|
+
`- Do not search for a global \`${CLUE_CLI_BINARY_NAME}\` binary or block on \`which ${CLUE_CLI_BINARY_NAME}\`; missing global binary is normal.`,
|
|
381
|
+
"- The AI implementation task is only to decide where ClueInit, ClueIdentify, ClueSetAccount, and ClueLogout belong in existing code and apply the minimal SDK wiring for those calls.",
|
|
382
|
+
"- Only changes required to place ClueInit, ClueIdentify, ClueSetAccount, and ClueLogout are allowed. Do not perform ClueTrack instrumentation unless the user explicitly requested product event tracking.",
|
|
383
|
+
"- Do not perform unrelated refactors, renames, file moves, formatting churn, broad cleanup, business logic rewrites, auth/session rewrites beyond minimal Clue hook insertion, UI changes unrelated to Clue setup, or unrelated dependency upgrades.",
|
|
384
|
+
"- Do not implement or refresh semantic snapshot CI during lifecycle placement; report a blocker if generated semantic artifacts are missing or stale.",
|
|
385
|
+
`- Do not run \`${clueCliCommand("setup-watch --local")}\` automatically. setup-watch and the Clue setup screen are user-operated verification steps, not implementation-agent responsibility.`,
|
|
386
|
+
"- The full setup must start with `clue-setup-orchestrator`.",
|
|
387
|
+
"- The implementation agent owns only lifecycle placement; monitoring agents review one surface at a time and report P0/P1 issues.",
|
|
388
|
+
"- Do not continue past a P0/P1 monitoring finding until it is fixed or explicitly reported as blocked.",
|
|
389
|
+
"- If subagents are unavailable, run the same structure as separate named review passes and say so in the final report.",
|
|
390
|
+
"- Do not expose project keys, API keys, secrets, tokens, or environment variable values.",
|
|
391
|
+
"- Do not ask the user to paste secret values.",
|
|
392
|
+
"- Do not read `.env`, `.env.*`, secret files, logs, dumps, build output, coverage output, `node_modules`, vendor directories, dependency directories, or dependency output.",
|
|
393
|
+
"- Report only environment variable names, never values.",
|
|
394
|
+
"- Do not commit or push changes.",
|
|
395
|
+
"- Execution agents must not approve, certify, or mark their own work complete. Completion evidence must come from independent monitoring passes, command output, runtime checks, or user-provided setup-watch/setup-screen evidence.",
|
|
396
|
+
"- Prefer existing repository patterns and minimal diffs.",
|
|
397
|
+
"- If evidence is unclear, report a blocker instead of guessing.",
|
|
398
|
+
"- Do not merge semantic snapshot, semantic CI, SDK lifecycle, audit, verification, and report responsibilities into one undifferentiated task.",
|
|
399
|
+
"",
|
|
400
|
+
"# Workflow",
|
|
401
|
+
"",
|
|
402
|
+
...steps[name].map((step, index) => `${index + 1}. ${step}`),
|
|
403
|
+
"",
|
|
404
|
+
].join("\n");
|
|
372
405
|
};
|
|
373
406
|
|
|
374
407
|
const askTarget = async ({
|
|
375
|
-
|
|
376
|
-
|
|
408
|
+
input = process.stdin,
|
|
409
|
+
output = process.stdout,
|
|
377
410
|
} = {}) => {
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
411
|
+
const rl = readline.createInterface({ input, output });
|
|
412
|
+
try {
|
|
413
|
+
const answer = await rl.question(
|
|
414
|
+
"ClueのセットアップSkillsをどのAIツールに追加しますか? [codex/claude_code] ",
|
|
415
|
+
);
|
|
416
|
+
return normalizeTarget(answer);
|
|
417
|
+
} finally {
|
|
418
|
+
rl.close();
|
|
419
|
+
}
|
|
387
420
|
};
|
|
388
421
|
|
|
389
422
|
export const installSetupSkills = async ({
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
423
|
+
repoRoot,
|
|
424
|
+
target,
|
|
425
|
+
input,
|
|
426
|
+
output,
|
|
394
427
|
} = {}) => {
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
428
|
+
const resolvedTarget = target
|
|
429
|
+
? normalizeTarget(target)
|
|
430
|
+
: await askTarget({ input, output });
|
|
431
|
+
const resolvedRepoRoot = resolve(repoRoot ?? ".");
|
|
432
|
+
const skillRoot = join(
|
|
433
|
+
resolvedRepoRoot,
|
|
434
|
+
...TARGET_SKILL_ROOTS[resolvedTarget],
|
|
435
|
+
);
|
|
436
|
+
const installed = [];
|
|
404
437
|
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
438
|
+
for (const skillName of SKILL_NAMES) {
|
|
439
|
+
const skillDir = join(skillRoot, skillName);
|
|
440
|
+
const skillPath = join(skillDir, "SKILL.md");
|
|
441
|
+
await mkdir(skillDir, { recursive: true });
|
|
442
|
+
await writeFile(skillPath, skillBody(skillName), "utf8");
|
|
443
|
+
installed.push(skillPath);
|
|
444
|
+
}
|
|
412
445
|
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
446
|
+
return {
|
|
447
|
+
target: resolvedTarget,
|
|
448
|
+
skill_root: join(...TARGET_SKILL_ROOTS[resolvedTarget]),
|
|
449
|
+
skills: SKILL_NAMES,
|
|
450
|
+
installed_files: installed.map((path) =>
|
|
451
|
+
path.replace(`${resolvedRepoRoot}/`, ""),
|
|
452
|
+
),
|
|
453
|
+
};
|
|
421
454
|
};
|