@clue-ai/cli 0.0.16 → 0.0.18
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 +9 -0
- package/bin/clue-cli.mjs +11 -0
- package/package.json +1 -1
- package/src/contracts.mjs +1 -1
- package/src/lifecycle-init.mjs +26 -5
- package/src/public-schema.cjs +81 -0
- package/src/semantic-ci.mjs +560 -45
- package/src/setup-ai-contract.mjs +114 -0
- package/src/setup-check.mjs +174 -2
- package/src/setup-doctor.mjs +442 -0
- package/src/setup-help.mjs +23 -2
- package/src/setup-prepare.mjs +27 -0
- package/src/setup-tool.mjs +31 -7
package/README.md
CHANGED
|
@@ -19,6 +19,7 @@ npx -y @clue-ai/cli semantic-inventory --framework fastapi --backend-root-path b
|
|
|
19
19
|
npx -y @clue-ai/cli semantic-workflow --framework fastapi --backend-root-path backend --repo .
|
|
20
20
|
npx -y @clue-ai/cli lifecycle-apply --plan clue-lifecycle-plan.json --repo .
|
|
21
21
|
npx -y @clue-ai/cli setup-check --framework fastapi --backend-root-path backend --repo . --target codex --require-sdk-lifecycle
|
|
22
|
+
npx -y @clue-ai/cli setup-doctor --local
|
|
22
23
|
npx -y @clue-ai/cli setup-watch --project-key <key> --environment dev --clue-api-base-url <clue-api-base-url> --watch-targets frontend:web[init,identify,set-account,event-sent]=<frontend-url>,backend:api[init,identify,set-account,logout,event-sent]=<backend-url>
|
|
23
24
|
npx -y @clue-ai/cli init --request clue-init-request.json --repo .
|
|
24
25
|
npx -y @clue-ai/cli semantic-gen --request clue-semantic-request.json --repo .
|
|
@@ -62,6 +63,14 @@ leaks, and SDK lifecycle presence when requested. With
|
|
|
62
63
|
installation, SDK imports in the target environments, app startup, and event
|
|
63
64
|
delivery remain required before setup can be called complete.
|
|
64
65
|
|
|
66
|
+
`npx -y @clue-ai/cli setup-doctor --local` checks API connectivity before
|
|
67
|
+
user-operated lifecycle verification. It verifies the four setup hops:
|
|
68
|
+
customer frontend to customer backend `/api/v1/clue/browser-tokens`, customer
|
|
69
|
+
backend to Clue `/api/v1/ingest/browser-tokens`, customer frontend to Clue
|
|
70
|
+
`/api/v1/ingest/browser`, and customer backend to Clue
|
|
71
|
+
`/api/v1/ingest/backend`. It does not replace setup-watch because it does not
|
|
72
|
+
exercise real login, account, or logout flows.
|
|
73
|
+
|
|
65
74
|
`npx -y @clue-ai/cli setup-watch` polls the Clue API setup-check endpoint in
|
|
66
75
|
remote mode while you operate the service. `--clue-api-base-url` is the Clue API
|
|
67
76
|
URL, not the customer frontend URL. Use `--watch-targets` to list every
|
package/bin/clue-cli.mjs
CHANGED
|
@@ -19,6 +19,7 @@ import { applyLifecyclePlan } from "../src/lifecycle-init.mjs";
|
|
|
19
19
|
import { runSemanticCi, runSemanticInventory } from "../src/semantic-ci.mjs";
|
|
20
20
|
import { runSetupCheck } from "../src/setup-check.mjs";
|
|
21
21
|
import { runSetupDetect } from "../src/setup-detect.mjs";
|
|
22
|
+
import { runSetupDoctor } from "../src/setup-doctor.mjs";
|
|
22
23
|
import { buildAiSetupHelp } from "../src/setup-help.mjs";
|
|
23
24
|
import { runSetupPrepare } from "../src/setup-prepare.mjs";
|
|
24
25
|
import { installSetupSkills } from "../src/setup-tool.mjs";
|
|
@@ -855,6 +856,7 @@ const usage = () =>
|
|
|
855
856
|
` ${clueCliCommand("semantic-workflow --framework fastapi --backend-root-path backend --repo .")}`,
|
|
856
857
|
` ${clueCliCommand("lifecycle-apply --plan clue-lifecycle-plan.json --repo .")}`,
|
|
857
858
|
` ${clueCliCommand("setup-check --framework fastapi --backend-root-path backend --repo . --target codex")}`,
|
|
859
|
+
` ${clueCliCommand("setup-doctor --local")}`,
|
|
858
860
|
` ${clueCliCommand("init --request clue-init-request.json --repo .")}`,
|
|
859
861
|
` ${clueCliCommand("semantic-gen --request clue-semantic-request.json --repo . [--previous-snapshot-file previous.json]")}`,
|
|
860
862
|
` ${clueCliCommand("semantic-gen --request-env CLUE_SEMANTIC_REQUEST_JSON --repo .")}`,
|
|
@@ -919,6 +921,15 @@ const main = async () => {
|
|
|
919
921
|
return;
|
|
920
922
|
}
|
|
921
923
|
|
|
924
|
+
if (command === "setup-doctor") {
|
|
925
|
+
const report = await runSetupDoctor({ flags, repoRoot });
|
|
926
|
+
process.stdout.write(`${JSON.stringify(report, null, 2)}\n`);
|
|
927
|
+
if (!report.passed) {
|
|
928
|
+
process.exitCode = 1;
|
|
929
|
+
}
|
|
930
|
+
return;
|
|
931
|
+
}
|
|
932
|
+
|
|
922
933
|
if (command === "setup") {
|
|
923
934
|
const report = await installSetupSkills({
|
|
924
935
|
repoRoot,
|
package/package.json
CHANGED
package/src/contracts.mjs
CHANGED
|
@@ -81,7 +81,7 @@ export const buildInitReport = ({
|
|
|
81
81
|
frontend_api_key_exposed: false,
|
|
82
82
|
browser_token_endpoint_required: true,
|
|
83
83
|
browser_token_provider_required: true,
|
|
84
|
-
browser_token_endpoint_path: "/api/v1/
|
|
84
|
+
browser_token_endpoint_path: "/api/v1/clue/browser-tokens",
|
|
85
85
|
clue_track_inserted: false,
|
|
86
86
|
clue_dom_tag_inserted: false,
|
|
87
87
|
allowed_source_paths: request.allowed_source_paths,
|
package/src/lifecycle-init.mjs
CHANGED
|
@@ -8,6 +8,11 @@ import {
|
|
|
8
8
|
stripSourceNoise,
|
|
9
9
|
} from "./lifecycle-guard.mjs";
|
|
10
10
|
import { listAllowedSourceFiles } from "./path-policy.mjs";
|
|
11
|
+
import {
|
|
12
|
+
API_CONNECTIVITY_CONTRACT,
|
|
13
|
+
DETERMINISTIC_CONTROL_MODEL,
|
|
14
|
+
SETUP_DOCTRINE,
|
|
15
|
+
} from "./setup-ai-contract.mjs";
|
|
11
16
|
|
|
12
17
|
const API_NAMES = new Set([
|
|
13
18
|
"ClueInit",
|
|
@@ -543,8 +548,14 @@ const readContextFiles = async ({ repoRoot, request }) => {
|
|
|
543
548
|
const buildLifecyclePrompt = ({ request, files }) =>
|
|
544
549
|
JSON.stringify({
|
|
545
550
|
task: "Add Clue SDK lifecycle API calls to this repository using exact text replacements.",
|
|
551
|
+
setup_doctrine: SETUP_DOCTRINE,
|
|
552
|
+
deterministic_control_model: DETERMINISTIC_CONTROL_MODEL,
|
|
553
|
+
api_connectivity_contract: API_CONNECTIVITY_CONTRACT,
|
|
546
554
|
rules: [
|
|
547
555
|
"Return JSON only.",
|
|
556
|
+
"Understand the setup doctrine before choosing edits. This is an external Clue integration, not a host app refactor or redesign task.",
|
|
557
|
+
"Use AI judgment only for lifecycle boundary placement. Let the CLI, generated skills, documentation contract, lifecycle plan schema, and setup-check static guards control deterministic mechanics.",
|
|
558
|
+
"If SDK signatures, environment names, browser token behavior, package installability, or verification ownership are unclear, return status blocked instead of guessing.",
|
|
548
559
|
"Use only exact replacements. Each find string must be copied exactly from source.",
|
|
549
560
|
"Add ClueInit, ClueIdentify, ClueSetAccount, and ClueLogout where repository code has clear lifecycle points.",
|
|
550
561
|
"Official Clue SDK public lifecycle APIs are no-throw and own SDK failure isolation.",
|
|
@@ -569,15 +580,22 @@ const buildLifecyclePrompt = ({ request, files }) =>
|
|
|
569
580
|
"Use environment variable names for Clue configuration values.",
|
|
570
581
|
"For Python/FastAPI code, read CLUE_PROJECT_KEY, CLUE_ENVIRONMENT, CLUE_API_KEY, and CLUE_INGEST_ENDPOINT from the backend env block.",
|
|
571
582
|
"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.",
|
|
572
|
-
"For Next.js browser/client code, read NEXT_PUBLIC_CLUE_PROJECT_KEY, NEXT_PUBLIC_CLUE_ENVIRONMENT, NEXT_PUBLIC_CLUE_SERVICE_KEY, and
|
|
583
|
+
"For Next.js browser/client code, read NEXT_PUBLIC_CLUE_PROJECT_KEY, NEXT_PUBLIC_CLUE_ENVIRONMENT, NEXT_PUBLIC_CLUE_SERVICE_KEY, NEXT_PUBLIC_CLUE_INGEST_ENDPOINT, and NEXT_PUBLIC_CLUE_BROWSER_TOKEN_ENDPOINT from the frontend .env.local block.",
|
|
573
584
|
"Do not read process.env.CLUE_PROJECT_KEY, process.env.CLUE_ENVIRONMENT, process.env.CLUE_SERVICE_KEY, or process.env.CLUE_INGEST_ENDPOINT in Next.js browser/client code, and do not add non-public CLUE_* fallbacks there.",
|
|
585
|
+
"Frontend SDK adapter code is contract-owned Clue setup wiring. The AI may choose the existing import/mount point, but must not invent token URL, env, or initialization semantics.",
|
|
586
|
+
"For Next.js frontend adapters, read the full customer-backend browser-token proxy URL from NEXT_PUBLIC_CLUE_BROWSER_TOKEN_ENDPOINT. Do not derive it from NEXT_PUBLIC_API_URL, generic app API env names, detected backend ports, or relative frontend-origin paths.",
|
|
587
|
+
"Do not mix stale browser-token paths such as /api/clue/browser-token, /clue/browser-tokens, or /browser-tokens with the canonical /api/v1/clue/browser-tokens path.",
|
|
588
|
+
"Do not call ClueInit with empty-string fallbacks for required NEXT_PUBLIC_CLUE_* values. If required Clue env is absent, skip initialization and report the missing env names.",
|
|
589
|
+
"If a singleton guard is used, do not set initialized = true before ClueInit has actually been called with required config present.",
|
|
574
590
|
"For non-Next.js browser code, use the exact frontend env names written in .env.clue for that service instead of inventing a framework-specific prefix.",
|
|
575
591
|
"Never place CLUE_API_KEY in frontend code, frontend env files, browser bundles, or client-readable config.",
|
|
576
592
|
"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.",
|
|
577
593
|
"Configure frontend ClueInit with browserTokenProvider that calls the local backend token endpoint and returns the token string.",
|
|
578
|
-
"
|
|
579
|
-
"The
|
|
580
|
-
"The
|
|
594
|
+
"Keep the four setup API hops distinct: customer frontend -> customer backend /api/v1/clue/browser-tokens, customer backend -> Clue /api/v1/ingest/browser-tokens, customer frontend -> Clue /api/v1/ingest/browser, and customer backend -> Clue /api/v1/ingest/backend.",
|
|
595
|
+
"The local backend token endpoint is part of the customer app, not the Clue API. Place it under a Clue-reserved local route such as /api/v1/clue/browser-tokens; do not use a generic path such as /browser-tokens that could be confused with product behavior. It must call Clue server-side at /api/v1/ingest/browser-tokens.",
|
|
596
|
+
"The frontend browserTokenProvider must call NEXT_PUBLIC_CLUE_BROWSER_TOKEN_ENDPOINT and send the same service key used by ClueInit to the customer backend token endpoint. For Next.js this value comes from NEXT_PUBLIC_CLUE_SERVICE_KEY.",
|
|
597
|
+
"The browser token request must include the frontend service key used by ClueInit. Project key and environment may be included only as public consistency hints; the backend must use server configuration or validate them against server configuration before calling Clue.",
|
|
598
|
+
"The backend browser token proxy must derive request origin from trusted request headers or server request metadata. Do not trust origin, projectKey, or environment from JSON/body payload fields when calling Clue with server CLUE_API_KEY.",
|
|
581
599
|
"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.",
|
|
582
600
|
"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.",
|
|
583
601
|
"Do not add @clue-ai/browser-sdk or backend SDK dependencies with * or latest; use a concrete published version or package-manager-resolved semver range and update the repository lockfile when one exists.",
|
|
@@ -600,7 +618,10 @@ const buildLifecyclePrompt = ({ request, files }) =>
|
|
|
600
618
|
tooling_api_base_url_env: "CLUE_API_BASE_URL",
|
|
601
619
|
browser_ingest_endpoint_env: "framework_specific",
|
|
602
620
|
nextjs_browser_ingest_endpoint_env: "NEXT_PUBLIC_CLUE_INGEST_ENDPOINT",
|
|
603
|
-
|
|
621
|
+
client_backend_browser_token_proxy_path: "/api/v1/clue/browser-tokens",
|
|
622
|
+
nextjs_browser_token_endpoint_env:
|
|
623
|
+
"NEXT_PUBLIC_CLUE_BROWSER_TOKEN_ENDPOINT",
|
|
624
|
+
clue_backend_browser_token_issue_path: "/api/v1/ingest/browser-tokens",
|
|
604
625
|
nextjs_browser_service_key_env: "NEXT_PUBLIC_CLUE_SERVICE_KEY",
|
|
605
626
|
service_key: request.service_key,
|
|
606
627
|
},
|
package/src/public-schema.cjs
CHANGED
|
@@ -174,6 +174,19 @@ const semanticSnapshotRouteOriginValues = [
|
|
|
174
174
|
"changed_route_needs_review",
|
|
175
175
|
"fallback",
|
|
176
176
|
];
|
|
177
|
+
const semanticSnapshotPurposeChangeStateValues = [
|
|
178
|
+
"same_purpose",
|
|
179
|
+
"purpose_added",
|
|
180
|
+
"purpose_changed",
|
|
181
|
+
"purpose_removed",
|
|
182
|
+
"insufficient_evidence",
|
|
183
|
+
"new_route",
|
|
184
|
+
];
|
|
185
|
+
const semanticSnapshotActiveSemanticSourceValues = [
|
|
186
|
+
"previous_reused",
|
|
187
|
+
"new_confirmed",
|
|
188
|
+
"previous_kept_pending_review",
|
|
189
|
+
];
|
|
177
190
|
const targetObjectKeySchema = nonEmptyStringSchema.regex(snakeSegmentPattern, {
|
|
178
191
|
message: "must be a lowercase snake_case key",
|
|
179
192
|
});
|
|
@@ -237,6 +250,12 @@ const semanticSnapshotAnalysisSummarySchema = zod_1.z
|
|
|
237
250
|
.int()
|
|
238
251
|
.nonnegative()
|
|
239
252
|
.default(0),
|
|
253
|
+
same_purpose_routes: zod_1.z.number().int().nonnegative().default(0),
|
|
254
|
+
purpose_added_routes: zod_1.z.number().int().nonnegative().default(0),
|
|
255
|
+
purpose_changed_routes: zod_1.z.number().int().nonnegative().default(0),
|
|
256
|
+
purpose_removed_routes: zod_1.z.number().int().nonnegative().default(0),
|
|
257
|
+
insufficient_evidence_routes: zod_1.z.number().int().nonnegative().default(0),
|
|
258
|
+
new_route_purpose_routes: zod_1.z.number().int().nonnegative().default(0),
|
|
240
259
|
})
|
|
241
260
|
.strict();
|
|
242
261
|
const semanticSnapshotGenerationContractSchema = zod_1.z
|
|
@@ -408,6 +427,28 @@ const semanticSnapshotUnresolvedOperationEffectSchema = zod_1.z
|
|
|
408
427
|
missing_context: zod_1.z.array(nonEmptyStringSchema).default([]),
|
|
409
428
|
})
|
|
410
429
|
.strict();
|
|
430
|
+
const semanticSnapshotEvidencePacketSummarySchema = zod_1.z
|
|
431
|
+
.object({
|
|
432
|
+
contract_summary: nonEmptyStringSchema,
|
|
433
|
+
behavior_summary: nonEmptyStringSchema,
|
|
434
|
+
data_effect_hints: zod_1.z.array(nonEmptyStringSchema).default([]),
|
|
435
|
+
side_effect_hints: zod_1.z.array(nonEmptyStringSchema).default([]),
|
|
436
|
+
validation_hints: zod_1.z.array(nonEmptyStringSchema).default([]),
|
|
437
|
+
auth_hints: zod_1.z.array(nonEmptyStringSchema).default([]),
|
|
438
|
+
missing_context: zod_1.z.array(nonEmptyStringSchema).default([]),
|
|
439
|
+
})
|
|
440
|
+
.strict();
|
|
441
|
+
const semanticSnapshotSemanticStabilitySchema = zod_1.z
|
|
442
|
+
.object({
|
|
443
|
+
purpose_change_state: zod_1.z.enum(semanticSnapshotPurposeChangeStateValues),
|
|
444
|
+
confidence: confidenceSchema,
|
|
445
|
+
reason: nonEmptyStringSchema,
|
|
446
|
+
previous_route_input_hash: semanticSnapshotHashSchema.optional(),
|
|
447
|
+
previous_route_semantic_hash: semanticSnapshotHashSchema.optional(),
|
|
448
|
+
previous_semantic_snapshot_version: nonEmptyStringSchema.optional(),
|
|
449
|
+
missing_context: zod_1.z.array(nonEmptyStringSchema).default([]),
|
|
450
|
+
})
|
|
451
|
+
.strict();
|
|
411
452
|
const semanticMeaningCandidateValueSchema = zod_1.z.union([
|
|
412
453
|
nonEmptyStringSchema,
|
|
413
454
|
zod_1.z.number(),
|
|
@@ -470,6 +511,14 @@ const routeSemanticSnapshotEntrySchema = zod_1.z
|
|
|
470
511
|
.default([]),
|
|
471
512
|
source_evidence_refs: zod_1.z.array(nonEmptyStringSchema).default([]),
|
|
472
513
|
confidence_reason: nonEmptyStringSchema,
|
|
514
|
+
purpose_change_state: zod_1.z
|
|
515
|
+
.enum(semanticSnapshotPurposeChangeStateValues)
|
|
516
|
+
.optional(),
|
|
517
|
+
active_semantic_source: zod_1.z
|
|
518
|
+
.enum(semanticSnapshotActiveSemanticSourceValues)
|
|
519
|
+
.optional(),
|
|
520
|
+
semantic_stability: semanticSnapshotSemanticStabilitySchema.optional(),
|
|
521
|
+
evidence_packet_summary: semanticSnapshotEvidencePacketSummarySchema.optional(),
|
|
473
522
|
})
|
|
474
523
|
.strict();
|
|
475
524
|
const addDuplicateIssues = (ctx, values, path, label) => {
|
|
@@ -544,6 +593,38 @@ const semanticSnapshotRequestSchema = zod_1.z
|
|
|
544
593
|
message: "semantic_snapshot_version is required for schema_version >= 2",
|
|
545
594
|
});
|
|
546
595
|
}
|
|
596
|
+
if (snapshot.schema_version >= 3) {
|
|
597
|
+
snapshot.routes.forEach((route, routeIndex) => {
|
|
598
|
+
if (!route.purpose_change_state) {
|
|
599
|
+
ctx.addIssue({
|
|
600
|
+
code: "custom",
|
|
601
|
+
path: ["routes", routeIndex, "purpose_change_state"],
|
|
602
|
+
message: "routes[].purpose_change_state is required for schema_version >= 3",
|
|
603
|
+
});
|
|
604
|
+
}
|
|
605
|
+
if (!route.active_semantic_source) {
|
|
606
|
+
ctx.addIssue({
|
|
607
|
+
code: "custom",
|
|
608
|
+
path: ["routes", routeIndex, "active_semantic_source"],
|
|
609
|
+
message: "routes[].active_semantic_source is required for schema_version >= 3",
|
|
610
|
+
});
|
|
611
|
+
}
|
|
612
|
+
if (!route.semantic_stability) {
|
|
613
|
+
ctx.addIssue({
|
|
614
|
+
code: "custom",
|
|
615
|
+
path: ["routes", routeIndex, "semantic_stability"],
|
|
616
|
+
message: "routes[].semantic_stability is required for schema_version >= 3",
|
|
617
|
+
});
|
|
618
|
+
}
|
|
619
|
+
if (!route.evidence_packet_summary) {
|
|
620
|
+
ctx.addIssue({
|
|
621
|
+
code: "custom",
|
|
622
|
+
path: ["routes", routeIndex, "evidence_packet_summary"],
|
|
623
|
+
message: "routes[].evidence_packet_summary is required for schema_version >= 3",
|
|
624
|
+
});
|
|
625
|
+
}
|
|
626
|
+
});
|
|
627
|
+
}
|
|
547
628
|
addDuplicateIssues(ctx, snapshot.target_object_profiles.map((profile) => profile.id), ["target_object_profiles"], "target_object_profiles[].id");
|
|
548
629
|
addDuplicateIssues(ctx, snapshot.target_object_mappings.map((mapping) => mapping.id), ["target_object_mappings"], "target_object_mappings[].id");
|
|
549
630
|
addDuplicateIssues(ctx, snapshot.target_object_catalog.map((entry) => entry.target_object_key), ["target_object_catalog"], "target_object_catalog[].target_object_key");
|