@clue-ai/cli 0.0.15 → 0.0.17

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.15",
3
+ "version": "0.0.17",
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.",
@@ -575,9 +586,11 @@ const buildLifecyclePrompt = ({ request, files }) =>
575
586
  "Never place CLUE_API_KEY in frontend code, frontend env files, browser bundles, or client-readable config.",
576
587
  "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
588
  "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.",
589
+ "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.",
590
+ "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.",
579
591
  "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.",
592
+ "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.",
593
+ "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
594
  "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
595
  "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
596
  "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 +613,8 @@ const buildLifecyclePrompt = ({ request, files }) =>
600
613
  tooling_api_base_url_env: "CLUE_API_BASE_URL",
601
614
  browser_ingest_endpoint_env: "framework_specific",
602
615
  nextjs_browser_ingest_endpoint_env: "NEXT_PUBLIC_CLUE_INGEST_ENDPOINT",
603
- browser_token_endpoint_path: "/api/v1/ingest/browser-tokens",
616
+ client_backend_browser_token_proxy_path: "/api/v1/clue/browser-tokens",
617
+ clue_backend_browser_token_issue_path: "/api/v1/ingest/browser-tokens",
604
618
  nextjs_browser_service_key_env: "NEXT_PUBLIC_CLUE_SERVICE_KEY",
605
619
  service_key: request.service_key,
606
620
  },
@@ -7,6 +7,7 @@ const DEFAULT_EXCLUDES = [
7
7
  ".venv",
8
8
  ".next",
9
9
  "venv",
10
+ "site-packages",
10
11
  "node_modules",
11
12
  "dist",
12
13
  "build",
@@ -17,9 +18,15 @@ const DEFAULT_EXCLUDES = [
17
18
  "__pycache__",
18
19
  ];
19
20
 
21
+ const DEFAULT_EXCLUDED_PREFIXES = [".venv-", ".venv_", "venv-", "venv_"];
22
+
20
23
  const hasExcludedPart = (relativePath, excludes) => {
21
24
  const parts = relativePath.split(/[\\/]+/).filter(Boolean);
22
- return parts.some((part) => excludes.includes(part));
25
+ return parts.some(
26
+ (part) =>
27
+ excludes.includes(part) ||
28
+ DEFAULT_EXCLUDED_PREFIXES.some((prefix) => part.startsWith(prefix)),
29
+ );
23
30
  };
24
31
 
25
32
  const isInsideRoot = (root, absolutePath) => {
@@ -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");