@clue-ai/cli 0.0.18 → 0.0.19
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/bin/clue-cli.mjs +15 -0
- package/package.json +1 -1
- package/src/ai-provider.mjs +92 -2
- package/src/fastapi-analyzer.mjs +36 -2
- package/src/lifecycle-init.mjs +225 -14
- package/src/public-schema.cjs +1 -0
- package/src/semantic-ci.mjs +89 -9
- package/src/setup-agent.mjs +448 -0
- package/src/setup-ai-contract.mjs +2 -1
- package/src/setup-check.mjs +91 -8
- package/src/setup-help.mjs +28 -1
- package/src/setup-tool.mjs +8 -6
package/src/setup-check.mjs
CHANGED
|
@@ -39,7 +39,9 @@ const REQUIRED_SETUP_SKILL_PHRASES = {
|
|
|
39
39
|
"For Next.js browser/client code, use only `NEXT_PUBLIC_CLUE_PROJECT_KEY`",
|
|
40
40
|
"Do not read `process.env.CLUE_PROJECT_KEY`",
|
|
41
41
|
"CLUE_API_BASE_URL` is not part of backend SDK initialization",
|
|
42
|
-
"
|
|
42
|
+
"Install Clue SDK dependencies through the latest channel",
|
|
43
|
+
"`@clue-ai/browser-sdk` must use `latest`",
|
|
44
|
+
"Python backend SDK dependencies must not be pinned",
|
|
43
45
|
"Whitespace-only changes are allowed only on lines directly changed for Clue SDK wiring",
|
|
44
46
|
"The local backend token endpoint is part of the customer app, not the Clue API",
|
|
45
47
|
"send the same service key used by `ClueInit`",
|
|
@@ -60,6 +62,7 @@ const REQUIRED_SETUP_SKILL_PHRASES = {
|
|
|
60
62
|
"Reject frontend browser token providers that derive the Clue proxy URL from `NEXT_PUBLIC_API_URL`",
|
|
61
63
|
"Reject frontend adapters that mix stale browser-token paths",
|
|
62
64
|
"Reject frontend adapters that set `initialized = true` before calling `ClueInit`",
|
|
65
|
+
"Reject Clue SDK dependency entries that pin stale fixed versions",
|
|
63
66
|
"Audit the setup diff against the Clue setup contract even when the code was written by another agent",
|
|
64
67
|
"Reject whitespace-only edits, import sorting, formatter churn",
|
|
65
68
|
"Reject unrelated refactors, renames, file moves",
|
|
@@ -147,6 +150,12 @@ const DEPENDENCY_FILE_CANDIDATES = [
|
|
|
147
150
|
"poetry.lock",
|
|
148
151
|
"Pipfile",
|
|
149
152
|
];
|
|
153
|
+
const PYTHON_BACKEND_DEPENDENCY_DECLARATION_FILES = [
|
|
154
|
+
"requirements.txt",
|
|
155
|
+
"requirements-dev.txt",
|
|
156
|
+
"pyproject.toml",
|
|
157
|
+
"Pipfile",
|
|
158
|
+
];
|
|
150
159
|
const exists = async (path) => {
|
|
151
160
|
try {
|
|
152
161
|
await access(path);
|
|
@@ -394,6 +403,20 @@ const setupSourcePaths = async ({ repoRoot, request, includeFrontend }) => {
|
|
|
394
403
|
return existing.length ? existing : requested;
|
|
395
404
|
};
|
|
396
405
|
|
|
406
|
+
const setupBackendRootPaths = (request) => {
|
|
407
|
+
const configuredRoots = [
|
|
408
|
+
...(Array.isArray(request?.backend_root_paths)
|
|
409
|
+
? request.backend_root_paths
|
|
410
|
+
: []),
|
|
411
|
+
request?.backend_root_path,
|
|
412
|
+
]
|
|
413
|
+
.filter((root) => typeof root === "string" && root.trim())
|
|
414
|
+
.map((root) => root.trim());
|
|
415
|
+
return configuredRoots.length
|
|
416
|
+
? [...new Set(configuredRoots)]
|
|
417
|
+
: (request?.allowed_source_paths ?? []);
|
|
418
|
+
};
|
|
419
|
+
|
|
397
420
|
const secretLeakPatterns = [
|
|
398
421
|
/pk_(live|test)_[A-Za-z0-9_-]+/,
|
|
399
422
|
/sk_(live|test)_[A-Za-z0-9_-]+/,
|
|
@@ -776,17 +799,71 @@ const findNextLifecycleNonPublicEnvFiles = ({
|
|
|
776
799
|
.map((source) => source.file_path);
|
|
777
800
|
};
|
|
778
801
|
|
|
779
|
-
const
|
|
802
|
+
const findNonLatestFrontendSdkDependencyFiles = (dependencySources) =>
|
|
780
803
|
packageJsonSourcesWithDependency(dependencySources, FRONTEND_SDK_PACKAGE)
|
|
781
804
|
.filter((source) => {
|
|
782
805
|
const version = packageJsonDependencyVersion(
|
|
783
806
|
source.text,
|
|
784
807
|
FRONTEND_SDK_PACKAGE,
|
|
785
808
|
);
|
|
786
|
-
return version
|
|
809
|
+
return version !== "latest";
|
|
787
810
|
})
|
|
788
811
|
.map((source) => source.file_path);
|
|
789
812
|
|
|
813
|
+
const sourceIsPythonBackendDependencyDeclaration = (source) =>
|
|
814
|
+
PYTHON_BACKEND_DEPENDENCY_DECLARATION_FILES.some((fileName) =>
|
|
815
|
+
source.file_path.endsWith(fileName),
|
|
816
|
+
);
|
|
817
|
+
|
|
818
|
+
const backendSdkDependencyLineHasPinnedVersion = (line, packageName) => {
|
|
819
|
+
const stripped = line.trim();
|
|
820
|
+
if (!stripped || stripped.startsWith("#")) return false;
|
|
821
|
+
const withoutInlineComment = stripped.replace(/\s+#.*$/, "");
|
|
822
|
+
const packagePattern = new RegExp(
|
|
823
|
+
`(^|[\\s"'=,{\\[]+)${escapeRegex(packageName)}($|[\\s"'=<>~!@,;)}\\]]+)`,
|
|
824
|
+
"i",
|
|
825
|
+
);
|
|
826
|
+
if (!packagePattern.test(withoutInlineComment)) return false;
|
|
827
|
+
|
|
828
|
+
const requirementPattern = new RegExp(
|
|
829
|
+
`${escapeRegex(packageName)}(?:\\[[^\\]]+\\])?([^\\s"',;}\\]]*)`,
|
|
830
|
+
"i",
|
|
831
|
+
);
|
|
832
|
+
const requirementMatch = requirementPattern.exec(withoutInlineComment);
|
|
833
|
+
const suffix = requirementMatch?.[1] ?? "";
|
|
834
|
+
if (suffix && suffix !== ";") {
|
|
835
|
+
return true;
|
|
836
|
+
}
|
|
837
|
+
|
|
838
|
+
const keyPattern = new RegExp(
|
|
839
|
+
`^\\s*["']?${escapeRegex(packageName)}["']?\\s*=\\s*(.+)$`,
|
|
840
|
+
"i",
|
|
841
|
+
);
|
|
842
|
+
const keyMatch = keyPattern.exec(withoutInlineComment);
|
|
843
|
+
if (!keyMatch) return false;
|
|
844
|
+
const value = keyMatch[1].trim();
|
|
845
|
+
if (/^["']\*["']$/.test(value)) return false;
|
|
846
|
+
if (/version\s*=\s*["']\*["']/.test(value)) return false;
|
|
847
|
+
return true;
|
|
848
|
+
};
|
|
849
|
+
|
|
850
|
+
const findPinnedBackendSdkDependencyFiles = (
|
|
851
|
+
dependencySources,
|
|
852
|
+
packageNames,
|
|
853
|
+
) =>
|
|
854
|
+
dependencySources
|
|
855
|
+
.filter(sourceIsPythonBackendDependencyDeclaration)
|
|
856
|
+
.filter((source) =>
|
|
857
|
+
source.text
|
|
858
|
+
.split(/\r?\n/)
|
|
859
|
+
.some((line) =>
|
|
860
|
+
packageNames.some((packageName) =>
|
|
861
|
+
backendSdkDependencyLineHasPinnedVersion(line, packageName),
|
|
862
|
+
),
|
|
863
|
+
),
|
|
864
|
+
)
|
|
865
|
+
.map((source) => source.file_path);
|
|
866
|
+
|
|
790
867
|
const sourceLooksLikeBrowserTokenProvider = (source) => {
|
|
791
868
|
const text = stripSourceNoise(source.text);
|
|
792
869
|
return (
|
|
@@ -1053,6 +1130,10 @@ const checkSdkLifecycle = ({
|
|
|
1053
1130
|
const backendSdkDependencyPresent =
|
|
1054
1131
|
!backendPresent ||
|
|
1055
1132
|
dependencyHasAnyPackage(dependencySources, backendSpec.packages);
|
|
1133
|
+
const pinnedBackendSdkDependencyFiles = findPinnedBackendSdkDependencyFiles(
|
|
1134
|
+
dependencySources,
|
|
1135
|
+
backendSpec.packages,
|
|
1136
|
+
);
|
|
1056
1137
|
const backendSdkImportPresent =
|
|
1057
1138
|
!backendPresent ||
|
|
1058
1139
|
sourcesHavePythonImport(backendSources, backendSpec.imports);
|
|
@@ -1079,8 +1160,8 @@ const checkSdkLifecycle = ({
|
|
|
1079
1160
|
dependencySources,
|
|
1080
1161
|
frontendSources,
|
|
1081
1162
|
});
|
|
1082
|
-
const
|
|
1083
|
-
|
|
1163
|
+
const nonLatestFrontendSdkDependencyFiles =
|
|
1164
|
+
findNonLatestFrontendSdkDependencyFiles(dependencySources);
|
|
1084
1165
|
const browserTokenProviderMissingServiceKeyFiles =
|
|
1085
1166
|
findBrowserTokenProviderMissingServiceKeyFiles(frontendSources);
|
|
1086
1167
|
const browserTokenProviderWrongProxyPathFiles =
|
|
@@ -1143,6 +1224,7 @@ const checkSdkLifecycle = ({
|
|
|
1143
1224
|
? null
|
|
1144
1225
|
: backendSpec.blocker,
|
|
1145
1226
|
sdk_dependency_present: backendSdkDependencyPresent,
|
|
1227
|
+
pinned_sdk_dependency_files: pinnedBackendSdkDependencyFiles,
|
|
1146
1228
|
sdk_import_present: backendSdkImportPresent,
|
|
1147
1229
|
sdk_dependency_or_import_present: backendSdkPresent,
|
|
1148
1230
|
sdk_init_present: backendInitPresent,
|
|
@@ -1164,7 +1246,7 @@ const checkSdkLifecycle = ({
|
|
|
1164
1246
|
frontendLifecycleFilesWithoutVerifiedSdk,
|
|
1165
1247
|
wrong_sdk_packages: wrongFrontendSdkPackages,
|
|
1166
1248
|
next_lifecycle_non_public_env_files: nextLifecycleNonPublicEnvFiles,
|
|
1167
|
-
|
|
1249
|
+
non_latest_sdk_dependency_files: nonLatestFrontendSdkDependencyFiles,
|
|
1168
1250
|
browser_token_provider_missing_service_key_files:
|
|
1169
1251
|
browserTokenProviderMissingServiceKeyFiles,
|
|
1170
1252
|
browser_token_provider_wrong_proxy_path_files:
|
|
@@ -1194,13 +1276,14 @@ const checkSdkLifecycle = ({
|
|
|
1194
1276
|
missingApis.length === 0 &&
|
|
1195
1277
|
backendSdkPresent &&
|
|
1196
1278
|
backendSdkInstallabilityVerified &&
|
|
1279
|
+
pinnedBackendSdkDependencyFiles.length === 0 &&
|
|
1197
1280
|
backendInitPresent &&
|
|
1198
1281
|
backendMissingApis.length === 0 &&
|
|
1199
1282
|
frontendSdkPresent &&
|
|
1200
1283
|
frontendLifecycleFilesWithoutVerifiedSdk.length === 0 &&
|
|
1201
1284
|
wrongFrontendSdkPackages.length === 0 &&
|
|
1202
1285
|
nextLifecycleNonPublicEnvFiles.length === 0 &&
|
|
1203
|
-
|
|
1286
|
+
nonLatestFrontendSdkDependencyFiles.length === 0 &&
|
|
1204
1287
|
browserTokenProviderMissingServiceKeyFiles.length === 0 &&
|
|
1205
1288
|
browserTokenProviderWrongProxyPathFiles.length === 0 &&
|
|
1206
1289
|
browserTokenProviderNonClueEndpointEnvFiles.length === 0 &&
|
|
@@ -1414,7 +1497,7 @@ export const runSetupCheck = async ({
|
|
|
1414
1497
|
|
|
1415
1498
|
if (requireSdkLifecycle) {
|
|
1416
1499
|
const sdkLifecycle = checkSdkLifecycle({
|
|
1417
|
-
backendRootPaths: request
|
|
1500
|
+
backendRootPaths: setupBackendRootPaths(request),
|
|
1418
1501
|
dependencySources,
|
|
1419
1502
|
framework: request?.framework,
|
|
1420
1503
|
sources,
|
package/src/setup-help.mjs
CHANGED
|
@@ -55,6 +55,21 @@ export const buildAiSetupHelp = () => ({
|
|
|
55
55
|
"whitespace-only edits, import sorting, formatter churn, or comment/style cleanup outside the exact Clue SDK wiring lines",
|
|
56
56
|
],
|
|
57
57
|
},
|
|
58
|
+
sdk_dependency_contract: {
|
|
59
|
+
frontend: {
|
|
60
|
+
package: "@clue-ai/browser-sdk",
|
|
61
|
+
install: "package-manager add @clue-ai/browser-sdk@latest",
|
|
62
|
+
rule:
|
|
63
|
+
"Frontend package.json must declare @clue-ai/browser-sdk as latest. Fixed versions and * are rejected by setup-check because stale SDKs break setup contracts.",
|
|
64
|
+
},
|
|
65
|
+
backend: {
|
|
66
|
+
fastapi_package: "clue-fastapi-sdk",
|
|
67
|
+
django_package: "clue-django-sdk",
|
|
68
|
+
install: "pip install --upgrade <backend-sdk-package>",
|
|
69
|
+
rule:
|
|
70
|
+
"Python backend SDK dependency declarations must be unpinned so pip resolves the latest release. Fixed ==, ~=, <=, >=, <, >, local path, and exact-version declarations are rejected by setup-check.",
|
|
71
|
+
},
|
|
72
|
+
},
|
|
58
73
|
environment_contract: {
|
|
59
74
|
nextjs_frontend_client_env: {
|
|
60
75
|
variables: [
|
|
@@ -86,9 +101,21 @@ export const buildAiSetupHelp = () => ({
|
|
|
86
101
|
owner: "ai_or_user",
|
|
87
102
|
ai_agent_may_run: true,
|
|
88
103
|
command: clueCliCommand("setup-doctor --local"),
|
|
89
|
-
rule: "Run setup-doctor after local frontend/backend services and required env are available. It checks API connectivity only and does not replace user-operated setup-watch.",
|
|
104
|
+
rule: "Run setup-doctor after local frontend/backend services and required env are available. setup-agent blocks when required setup-doctor inputs are missing or connectivity fails, unless setup-doctor is explicitly skipped. It checks API connectivity only and does not replace user-operated setup-watch.",
|
|
90
105
|
checked_hops: Object.keys(API_CONNECTIVITY_CONTRACT.hops),
|
|
91
106
|
},
|
|
107
|
+
setup_agent: {
|
|
108
|
+
owner: "clue_cli",
|
|
109
|
+
command: clueCliCommand("setup-agent --repo ."),
|
|
110
|
+
providers: ["openai", "anthropic"],
|
|
111
|
+
env:
|
|
112
|
+
"Set exactly CLUE_AI_PROVIDER, CLUE_AI_PROVIDER_API_KEY, and CLUE_AI_MODEL. setup-agent uses the same AI provider configuration as the rest of the Clue CLI. Optional CLI overrides are --ai-provider, --ai-provider-api-key, and --ai-model; do not use ambiguous --api_key flags.",
|
|
113
|
+
model_role:
|
|
114
|
+
"Return a strict structured lifecycle plan only; the CLI owns scanning, edit application, checks, doctor preflight, retry, and final status.",
|
|
115
|
+
tool_calling:
|
|
116
|
+
"OpenAI uses Responses API forced function tools with strict schema and parallel_tool_calls false. Anthropic uses Messages API tool_choice with the same input schema.",
|
|
117
|
+
setup_watch_auto_run: false,
|
|
118
|
+
},
|
|
92
119
|
completion_boundary: {
|
|
93
120
|
ai_may_claim: [
|
|
94
121
|
"Clue setup code changes were applied",
|
package/src/setup-tool.mjs
CHANGED
|
@@ -316,7 +316,7 @@ const skillBody = (name, { documentsUrl } = {}) => {
|
|
|
316
316
|
"Do not add per-call try/catch, try/except, `.catch`, or custom safe wrappers solely around official Clue SDK public lifecycle calls.",
|
|
317
317
|
"Never await a Clue lifecycle call in a way that can block login, logout, account selection, request handling, page rendering, or API responses.",
|
|
318
318
|
"Add or report the required SDK dependency instead of fabricating lifecycle APIs.",
|
|
319
|
-
"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.",
|
|
319
|
+
"For frontend code, add the real `@clue-ai/browser-sdk` dependency when missing. Use the latest channel (`@clue-ai/browser-sdk@latest`) so setup receives current SDK fixes. Do not invent `clue-js-sdk`, `@clue/browser-sdk`, local placeholder modules, or dynamic imports that hide a missing SDK.",
|
|
320
320
|
`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 .")}\`.`,
|
|
321
321
|
"If npm/npx cannot fetch the Clue CLI package, report a blocker with the exact command and error instead of manually applying replacement plans.",
|
|
322
322
|
"Delete the temporary lifecycle plan file after applying it unless the user explicitly asks to keep it for review.",
|
|
@@ -339,9 +339,11 @@ const skillBody = (name, { documentsUrl } = {}) => {
|
|
|
339
339
|
"The backend browser token proxy must derive origin from trusted request headers or server request metadata. Do not forward `origin`, `projectKey`, or `environment` from JSON/body payload fields under server `CLUE_API_KEY`.",
|
|
340
340
|
"For browser token proxy code, the service key sent to Clue must be the frontend `ClueInit` serviceKey from the browser request, not the backend service's `CLUE_SERVICE_KEY`.",
|
|
341
341
|
"If a backend-owned browser token endpoint is implemented, read `CLUE_API_BASE_URL` from the backend env block and normalize it so values with or without a trailing `/api/v1` do not produce duplicate paths.",
|
|
342
|
-
"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` from the backend env block.",
|
|
342
|
+
"For FastAPI code, add unpinned `clue-fastapi-sdk` to the backend dependency file when missing so pip resolves the latest release, 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` from the backend env block.",
|
|
343
343
|
"`CLUE_API_BASE_URL` is not part of backend SDK initialization. Use it only when the application backend owns a browser-token proxy for a frontend service.",
|
|
344
|
-
"
|
|
344
|
+
"Python backend SDK dependencies must not be pinned.",
|
|
345
|
+
"`@clue-ai/browser-sdk` must use `latest`.",
|
|
346
|
+
"Install Clue SDK dependencies through the latest channel. Frontend package managers must use `@clue-ai/browser-sdk@latest`; Python backend dependency declarations must not pin `clue-fastapi-sdk` or `clue-django-sdk` to a fixed version.",
|
|
345
347
|
"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.",
|
|
346
348
|
"For frontend code, pass `serviceKey` from the frontend service env name written by `.env.clue` to `ClueInit`; in Next.js browser/client code that name is `NEXT_PUBLIC_CLUE_SERVICE_KEY`.",
|
|
347
349
|
"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.",
|
|
@@ -381,7 +383,7 @@ const skillBody = (name, { documentsUrl } = {}) => {
|
|
|
381
383
|
"Reject broad ClueTrack instrumentation and DOM clue tags.",
|
|
382
384
|
"Reject ClueTrack instrumentation unless the user explicitly requested product event tracking.",
|
|
383
385
|
"Reject Next.js browser/client code that reads non-public `process.env.CLUE_*` variables.",
|
|
384
|
-
"Reject Clue SDK dependency entries that use
|
|
386
|
+
"Reject Clue SDK dependency entries that pin stale fixed versions. Frontend package.json must use `@clue-ai/browser-sdk: latest`; Python backend SDK dependency declarations must be unpinned or package-manager wildcard-latest.",
|
|
385
387
|
"Reject Next.js browser token providers that do not read `NEXT_PUBLIC_CLUE_BROWSER_TOKEN_ENDPOINT`.",
|
|
386
388
|
"Confirm no project key, API key, secret, or env value appears in diff or report.",
|
|
387
389
|
"Confirm lifecycle insertions are minimal and reviewable.",
|
|
@@ -402,7 +404,7 @@ const skillBody = (name, { documentsUrl } = {}) => {
|
|
|
402
404
|
"Confirm the semantic workflow does not send GitHub actor, triggering_actor, sender, repository owner, repository name, or default branch to Clue.",
|
|
403
405
|
"Confirm `.clue/semantic-request.runtime.json` is not created, committed, or staged.",
|
|
404
406
|
`Run \`${clueCliCommand("setup-check --framework <framework> --backend-root-path <path> --repo . --target <codex|claude_code> --require-sdk-lifecycle")}\` when possible.`,
|
|
405
|
-
`Run \`${clueCliCommand("setup-doctor --local")}\` when local frontend/backend services are running
|
|
407
|
+
`Run \`${clueCliCommand("setup-doctor --local")}\` when local frontend/backend services are running. Missing setup-doctor inputs or failed API hops are blockers unless the user explicitly skipped setup-doctor.`,
|
|
406
408
|
`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.`,
|
|
407
409
|
"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`.",
|
|
408
410
|
"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.",
|
|
@@ -475,7 +477,7 @@ const skillBody = (name, { documentsUrl } = {}) => {
|
|
|
475
477
|
`- Setup API connectivity has four distinct hops: ${Object.entries(API_CONNECTIVITY_CONTRACT.hops)
|
|
476
478
|
.map(([name, hop]) => `${name}=${hop.method} ${hop.path}`)
|
|
477
479
|
.join(", ")}.`,
|
|
478
|
-
`- Run \`${clueCliCommand("setup-doctor --local")}\` when local services
|
|
480
|
+
`- Run \`${clueCliCommand("setup-doctor --local")}\` when local services are running. Missing setup-doctor inputs or failed API hops are blockers unless the user explicitly skipped setup-doctor. This checks API connectivity only; it does not replace user-operated setup-watch.`,
|
|
479
481
|
"- Do not implement or refresh semantic snapshot CI during lifecycle placement; report a blocker if generated semantic artifacts are missing or stale.",
|
|
480
482
|
`- Do not run \`${clueCliCommand("setup-watch --local")}\` automatically. setup-watch and the Clue setup screen are user-operated verification steps, not implementation-agent responsibility.`,
|
|
481
483
|
"- The full setup must start with `clue-setup-orchestrator`.",
|