@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 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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@clue-ai/cli",
3
- "version": "0.0.16",
3
+ "version": "0.0.18",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "clue-ai": "bin/clue-cli.mjs"
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/ingest/browser-tokens",
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,
@@ -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 NEXT_PUBLIC_CLUE_INGEST_ENDPOINT from the frontend .env.local block.",
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
- "The local backend token endpoint is part of the customer app, not the Clue API. It may use the customer's route convention, but it must call Clue server-side at /api/v1/ingest/browser-tokens.",
579
- "The frontend browserTokenProvider must 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.",
580
- "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.",
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
- browser_token_endpoint_path: "/api/v1/ingest/browser-tokens",
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
  },
@@ -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");