@ainyc/canonry 4.1.1 → 4.2.2

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.
@@ -12,7 +12,7 @@ import {
12
12
  queryGenerateRequestSchema,
13
13
  runTriggerRequestSchema,
14
14
  scheduleUpsertRequestSchema
15
- } from "./chunk-O5JZQUPX.js";
15
+ } from "./chunk-T2I6AO7D.js";
16
16
 
17
17
  // src/config.ts
18
18
  import fs from "fs";
@@ -864,8 +864,13 @@ var ApiClient = class {
864
864
  async getHealth(project) {
865
865
  return this.request("GET", `/projects/${encodeURIComponent(project)}/health/latest`);
866
866
  }
867
- async getProjectOverview(project) {
868
- return this.request("GET", `/projects/${encodeURIComponent(project)}/overview`);
867
+ async getProjectOverview(project, opts) {
868
+ const params = new URLSearchParams();
869
+ if (opts?.location) params.set("location", opts.location);
870
+ if (opts?.since) params.set("since", opts.since);
871
+ const query = params.toString();
872
+ const path2 = `/projects/${encodeURIComponent(project)}/overview${query ? `?${query}` : ""}`;
873
+ return this.request("GET", path2);
869
874
  }
870
875
  async searchProject(project, opts) {
871
876
  const params = new URLSearchParams({ q: opts.q });
@@ -1153,13 +1158,20 @@ var canonryMcpTools = [
1153
1158
  defineTool({
1154
1159
  name: "canonry_project_overview",
1155
1160
  title: "Get project overview (composite)",
1156
- description: 'One-call summary for "how is project X doing?" \u2014 bundles project info, latest run, top undismissed insights, latest health snapshot, query cited rate, per-provider breakdown, and gained/lost/emerging vs the previous run. Prefer this over fanning out to separate tools.',
1161
+ description: 'One-call summary for "how is project X doing?" \u2014 bundles project info, latest run, top undismissed insights, latest health snapshot, query cited rate, per-provider breakdown, gained/lost/emerging vs the previous run, the five score gauges (visibility, gap queries, index coverage, competitor pressure, run status), per-(provider, model) scores, configured competitors with pressure labels, an attention queue of critical/high insights, and a recent-runs sparkline. Filterable by location and time window. Prefer this over fanning out to separate tools.',
1157
1162
  access: "read",
1158
1163
  tier: "core",
1159
- inputSchema: projectInputSchema,
1164
+ inputSchema: z2.object({
1165
+ project: projectNameSchema,
1166
+ location: z2.string().optional().describe('Filter to runs from this location label (e.g. "Boston, MA, US"). Omit for all locations.'),
1167
+ since: z2.string().optional().describe("ISO 8601 datetime \u2014 only include runs at or after this time. Omit for full history.")
1168
+ }),
1160
1169
  annotations: readAnnotations(),
1161
1170
  openApiOperations: ["GET /api/v1/projects/{name}/overview"],
1162
- handler: (client, input) => client.getProjectOverview(input.project)
1171
+ handler: (client, input) => client.getProjectOverview(input.project, {
1172
+ location: input.location,
1173
+ since: input.since
1174
+ })
1163
1175
  }),
1164
1176
  defineTool({
1165
1177
  name: "canonry_report",
@@ -1509,7 +1509,16 @@ var projectSearchInsightHitSchema = z15.object({
1509
1509
  kind: z15.literal("insight"),
1510
1510
  id: z15.string(),
1511
1511
  runId: z15.string().nullable(),
1512
- type: z15.enum(["regression", "gain", "opportunity"]),
1512
+ type: z15.enum([
1513
+ "regression",
1514
+ "gain",
1515
+ "opportunity",
1516
+ "first-citation",
1517
+ "provider-pickup",
1518
+ "persistent-gap",
1519
+ "competitor-gained",
1520
+ "competitor-lost"
1521
+ ]),
1513
1522
  severity: z15.enum(["critical", "high", "medium", "low"]),
1514
1523
  title: z15.string(),
1515
1524
  query: z15.string(),
package/dist/cli.js CHANGED
@@ -18,7 +18,7 @@ import {
18
18
  setGoogleAuthConfig,
19
19
  showFirstRunNotice,
20
20
  trackEvent
21
- } from "./chunk-BQN6BBHI.js";
21
+ } from "./chunk-HJZY4EOE.js";
22
22
  import {
23
23
  CliError,
24
24
  EXIT_SYSTEM_ERROR,
@@ -33,7 +33,7 @@ import {
33
33
  saveConfig,
34
34
  saveConfigPatch,
35
35
  usageError
36
- } from "./chunk-KCETXLDF.js";
36
+ } from "./chunk-SR7TGHHG.js";
37
37
  import {
38
38
  apiKeys,
39
39
  competitors,
@@ -45,11 +45,12 @@ import {
45
45
  projects,
46
46
  querySnapshots,
47
47
  runs
48
- } from "./chunk-NCWCPBOT.js";
48
+ } from "./chunk-7YSI4GFA.js";
49
49
  import {
50
50
  CcReleaseSyncStatuses,
51
51
  CheckScopes,
52
52
  CheckStatuses,
53
+ CitationStates,
53
54
  CodingAgents,
54
55
  ProviderNames,
55
56
  RunKinds,
@@ -63,7 +64,7 @@ import {
63
64
  providerQuotaPolicySchema,
64
65
  resolveProviderInput,
65
66
  skillsClientSchema
66
- } from "./chunk-O5JZQUPX.js";
67
+ } from "./chunk-T2I6AO7D.js";
67
68
 
68
69
  // src/cli.ts
69
70
  import { pathToFileURL } from "url";
@@ -579,7 +580,7 @@ function readStoredGroundingSources(rawResponse) {
579
580
  return result;
580
581
  }
581
582
  async function backfillInsightsCommand(project, opts) {
582
- const { IntelligenceService } = await import("./intelligence-service-EITZP4KG.js");
583
+ const { IntelligenceService } = await import("./intelligence-service-CQGAXKKN.js");
583
584
  const config = loadConfig();
584
585
  const db = createClient(config.database);
585
586
  migrate(db);
@@ -4597,8 +4598,8 @@ async function testNotification(project, id, format) {
4597
4598
  var EVENT_DESCRIPTIONS = {
4598
4599
  "citation.lost": "A query lost its citation status",
4599
4600
  "citation.gained": "A query gained citation status",
4600
- "run.completed": "A visibility run completed successfully",
4601
- "run.failed": "A visibility run failed",
4601
+ "run.completed": "An AEO sweep completed successfully",
4602
+ "run.failed": "An AEO sweep failed",
4602
4603
  "insight.critical": "A critical-severity insight was generated",
4603
4604
  "insight.high": "A high-severity insight was generated"
4604
4605
  };
@@ -4889,7 +4890,7 @@ async function showEvidence(project, format) {
4889
4890
  if (format === "json") {
4890
4891
  const enriched = timeline.map((entry) => ({
4891
4892
  ...entry,
4892
- cited: entry.runs[entry.runs.length - 1]?.citationState === "cited"
4893
+ cited: entry.runs[entry.runs.length - 1]?.citationState === CitationStates.cited
4893
4894
  }));
4894
4895
  console.log(JSON.stringify(enriched, null, 2));
4895
4896
  return;
@@ -4903,13 +4904,13 @@ async function showEvidence(project, format) {
4903
4904
  for (const entry of timeline) {
4904
4905
  const latest = entry.runs[entry.runs.length - 1];
4905
4906
  if (!latest) continue;
4906
- const state = latest.citationState === "cited" ? "\u2713 cited" : "\u2717 not-cited";
4907
+ const state = latest.citationState === CitationStates.cited ? "\u2713 cited" : "\u2717 not-cited";
4907
4908
  const transition = latest.transition !== latest.citationState ? ` (${latest.transition})` : "";
4908
4909
  console.log(` ${state}${transition} ${entry.query}`);
4909
4910
  }
4910
4911
  console.log(`
4911
4912
  Queries: ${timeline.length}`);
4912
- const cited = timeline.filter((e) => e.runs[e.runs.length - 1]?.citationState === "cited").length;
4913
+ const cited = timeline.filter((e) => e.runs[e.runs.length - 1]?.citationState === CitationStates.cited).length;
4913
4914
  console.log(` Cited: ${cited} / ${timeline.length}`);
4914
4915
  }
4915
4916
 
@@ -5750,11 +5751,12 @@ function printRunDetail(run) {
5750
5751
  }
5751
5752
  if (run.snapshots && run.snapshots.length > 0) {
5752
5753
  console.log(`
5753
- Snapshots: ${run.snapshots.length}`);
5754
+ Snapshots: ${run.snapshots.length} (cell = [citation][mention]; C=cited c=not, M=mentioned m=not, \u2013=no data)`);
5754
5755
  for (const s of run.snapshots) {
5755
- const state = typeof s.answerMentioned === "boolean" ? s.answerMentioned ? " visible " : " not-vis " : s.citationState === "cited" ? " cited " : " not-cited";
5756
+ const citationGlyph = s.citationState === CitationStates.cited ? "C" : "c";
5757
+ const mentionGlyph = typeof s.answerMentioned === "boolean" ? s.answerMentioned ? "M" : "m" : "\u2013";
5756
5758
  const modelLabel = s.model ? ` (${s.model})` : "";
5757
- console.log(` ${state} ${s.provider}${modelLabel} ${s.query}`);
5759
+ console.log(` [${citationGlyph}${mentionGlyph}] ${s.provider}${modelLabel} ${s.query}`);
5758
5760
  }
5759
5761
  }
5760
5762
  }
@@ -6730,7 +6732,7 @@ function renderCover(pdf, report) {
6730
6732
  minute: "2-digit"
6731
6733
  }));
6732
6734
  pdf.keyValue("AEO Audit", `${report.audit.overallScore}/100 (${report.audit.overallGrade})`);
6733
- pdf.keyValue("Visibility Gap", report.summary.visibilityGap);
6735
+ pdf.keyValue("Visibility Gap (Citations + Mentions)", report.summary.visibilityGap);
6734
6736
  pdf.paragraph(report.profile.summary, { size: 11, color: INK, lineHeight: 16 });
6735
6737
  pdf.rule();
6736
6738
  }
@@ -6885,7 +6887,7 @@ function formatSnapshotMarkdown(report) {
6885
6887
  lines.push(`**Generated:** ${new Date(report.generatedAt).toLocaleString("en-US", { year: "numeric", month: "long", day: "numeric", hour: "numeric", minute: "2-digit" })}`);
6886
6888
  lines.push(`**AEO Audit Score:** ${report.audit.overallScore}/100 (${report.audit.overallGrade})`);
6887
6889
  lines.push("");
6888
- lines.push("## Visibility Gap");
6890
+ lines.push("## Visibility Gap (Citations + Mentions)");
6889
6891
  lines.push("");
6890
6892
  lines.push(report.summary.visibilityGap);
6891
6893
  lines.push("");
@@ -7140,17 +7142,37 @@ async function showHealth(project, opts) {
7140
7142
  // src/commands/overview.ts
7141
7143
  async function showOverview(project, opts) {
7142
7144
  const client = createApiClient();
7143
- const overview = await client.getProjectOverview(project);
7145
+ const overview = await client.getProjectOverview(project, {
7146
+ location: opts.location,
7147
+ since: opts.since
7148
+ });
7144
7149
  if (opts.format === "json") {
7145
7150
  console.log(JSON.stringify(overview, null, 2));
7146
7151
  return;
7147
7152
  }
7148
- const { project: meta, latestRun, health, topInsights, queryCounts, providers, transitions } = overview;
7149
- console.log(`Overview: ${meta.displayName ?? meta.name} (${meta.name})
7150
- `);
7153
+ renderHuman(overview);
7154
+ }
7155
+ function renderHuman(overview) {
7156
+ const {
7157
+ project: meta,
7158
+ latestRun,
7159
+ health,
7160
+ topInsights,
7161
+ queryCounts,
7162
+ providers,
7163
+ transitions,
7164
+ scores,
7165
+ movementSummary,
7166
+ competitors: competitors2,
7167
+ providerScores,
7168
+ attentionItems,
7169
+ runHistory,
7170
+ dateRangeLabel,
7171
+ contextLabel
7172
+ } = overview;
7173
+ console.log(`Overview: ${meta.displayName ?? meta.name} (${meta.name})`);
7151
7174
  console.log(` Domain: ${meta.canonicalDomain}`);
7152
- console.log(` Country: ${meta.country}`);
7153
- console.log(` Language: ${meta.language}`);
7175
+ console.log(` Context: ${contextLabel} \xB7 ${dateRangeLabel}`);
7154
7176
  if (latestRun.run) {
7155
7177
  const finished = latestRun.run.finishedAt ?? "\u2014";
7156
7178
  console.log(`
@@ -7159,17 +7181,48 @@ async function showOverview(project, opts) {
7159
7181
  } else {
7160
7182
  console.log("\n No runs yet.");
7161
7183
  }
7184
+ console.log("\nScores:");
7185
+ printScore("Visibility ", scores.visibility);
7186
+ printScore("Gap queries ", scores.gapQueries);
7187
+ printScore("Index coverage ", scores.indexCoverage);
7188
+ printScore("Competitor press.", scores.competitorPressure);
7189
+ printScore("Run status ", scores.runStatus);
7162
7190
  console.log(`
7163
7191
  Queries cited: ${queryCounts.citedQueries}/${queryCounts.totalQueries} (${pct(queryCounts.citedRate)})`);
7192
+ if (movementSummary.hasPreviousRun) {
7193
+ console.log(` Movement: +${movementSummary.gained} gained, -${movementSummary.lost} lost (${movementSummary.tone})`);
7194
+ } else if (movementSummary.gained > 0) {
7195
+ console.log(` Movement: ${movementSummary.gained} cited in first run`);
7196
+ }
7164
7197
  if (providers.length > 0) {
7165
- console.log(" Providers:");
7198
+ console.log("\n Providers:");
7166
7199
  for (const p of providers) {
7167
- console.log(` ${p.provider.padEnd(10)} ${p.cited}/${p.total} (${pct(p.citedRate)})`);
7200
+ console.log(` ${p.provider.padEnd(12)} ${p.cited}/${p.total} (${pct(p.citedRate)})`);
7201
+ }
7202
+ }
7203
+ if (providerScores.length > 0) {
7204
+ console.log("\n Models:");
7205
+ for (const m of providerScores) {
7206
+ const label = `${m.provider}/${m.model ?? "unknown"}`.padEnd(28);
7207
+ console.log(` ${label} ${m.cited}/${m.total} (${m.score}%)`);
7168
7208
  }
7169
7209
  }
7170
7210
  if (transitions.since) {
7171
7211
  console.log(`
7172
- vs run at ${transitions.since}: +${transitions.gained} gained, -${transitions.lost} lost, ${transitions.emerging} emerging`);
7212
+ Transitions since ${transitions.since}: +${transitions.gained} gained, -${transitions.lost} lost, ${transitions.emerging} emerging`);
7213
+ }
7214
+ if (competitors2.length > 0) {
7215
+ console.log("\n Competitors:");
7216
+ for (const c of competitors2) {
7217
+ console.log(` ${c.domain.padEnd(28)} ${c.citationCount}/${c.totalQueries} ${c.pressureLabel}`);
7218
+ }
7219
+ }
7220
+ if (attentionItems.length > 0) {
7221
+ console.log("\n Attention:");
7222
+ for (const item of attentionItems) {
7223
+ console.log(` [${item.actionLabel}] ${item.title}`);
7224
+ if (item.detail) console.log(` ${item.detail}`);
7225
+ }
7173
7226
  }
7174
7227
  if (health) {
7175
7228
  console.log(`
@@ -7181,6 +7234,19 @@ async function showOverview(project, opts) {
7181
7234
  console.log(` [${insight.severity.toUpperCase()}] ${insight.type} \u2014 ${insight.title}`);
7182
7235
  }
7183
7236
  }
7237
+ if (runHistory.length > 0) {
7238
+ console.log(`
7239
+ Run history (last ${runHistory.length}):`);
7240
+ for (const point of runHistory) {
7241
+ const bar = "\u2588".repeat(Math.round(point.citationRate / 10));
7242
+ console.log(` ${point.createdAt.slice(0, 10)} ${String(point.citationRate).padStart(3)}% ${bar}`);
7243
+ }
7244
+ }
7245
+ }
7246
+ function printScore(prefix, score) {
7247
+ const tone = `[${score.tone}]`.padEnd(11);
7248
+ const value = score.value.padEnd(8);
7249
+ console.log(` ${prefix} ${tone} ${value} ${score.delta}`);
7184
7250
  }
7185
7251
  function pct(value) {
7186
7252
  return `${(value * 100).toFixed(1)}%`;
@@ -7250,7 +7316,7 @@ function printSummary(data) {
7250
7316
  queriesMentionedOnly,
7251
7317
  queriesInvisible
7252
7318
  } = data.summary;
7253
- console.log("Citation visibility");
7319
+ console.log("AEO visibility (citations + mentions)");
7254
7320
  if (data.summary.latestRunAt) {
7255
7321
  console.log(`Latest run: ${data.summary.latestRunAt}`);
7256
7322
  }
@@ -7359,12 +7425,17 @@ var INTELLIGENCE_CLI_COMMANDS = [
7359
7425
  },
7360
7426
  {
7361
7427
  path: ["overview"],
7362
- usage: "canonry overview <project> [--format json]",
7363
- options: {},
7428
+ usage: "canonry overview <project> [--location <label>] [--since <iso>] [--format json]",
7429
+ options: {
7430
+ location: { type: "string" },
7431
+ since: { type: "string" }
7432
+ },
7364
7433
  run: async (input) => {
7365
- const usage = "canonry overview <project> [--format json]";
7434
+ const usage = "canonry overview <project> [--location <label>] [--since <iso>] [--format json]";
7366
7435
  const project = requireProject(input, "overview", usage);
7367
- await showOverview(project, { format: input.format });
7436
+ const location = getString(input.values, "location");
7437
+ const since = getString(input.values, "since");
7438
+ await showOverview(project, { format: input.format, location, since });
7368
7439
  }
7369
7440
  },
7370
7441
  {
package/dist/index.js CHANGED
@@ -1,11 +1,11 @@
1
1
  import {
2
2
  createServer
3
- } from "./chunk-BQN6BBHI.js";
3
+ } from "./chunk-HJZY4EOE.js";
4
4
  import {
5
5
  loadConfig
6
- } from "./chunk-KCETXLDF.js";
7
- import "./chunk-NCWCPBOT.js";
8
- import "./chunk-O5JZQUPX.js";
6
+ } from "./chunk-SR7TGHHG.js";
7
+ import "./chunk-7YSI4GFA.js";
8
+ import "./chunk-T2I6AO7D.js";
9
9
  export {
10
10
  createServer,
11
11
  loadConfig
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  IntelligenceService
3
- } from "./chunk-NCWCPBOT.js";
4
- import "./chunk-O5JZQUPX.js";
3
+ } from "./chunk-7YSI4GFA.js";
4
+ import "./chunk-T2I6AO7D.js";
5
5
  export {
6
6
  IntelligenceService
7
7
  };
package/dist/mcp.js CHANGED
@@ -2,8 +2,8 @@ import {
2
2
  CliError,
3
3
  canonryMcpTools,
4
4
  createApiClient
5
- } from "./chunk-KCETXLDF.js";
6
- import "./chunk-O5JZQUPX.js";
5
+ } from "./chunk-SR7TGHHG.js";
6
+ import "./chunk-T2I6AO7D.js";
7
7
 
8
8
  // src/mcp/cli.ts
9
9
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ainyc/canonry",
3
- "version": "4.1.1",
3
+ "version": "4.2.2",
4
4
  "type": "module",
5
5
  "description": "Agent-first open-source AEO operating platform - track how answer engines cite your domain",
6
6
  "license": "FSL-1.1-ALv2",
@@ -60,20 +60,20 @@
60
60
  "tsup": "^8.5.1",
61
61
  "tsx": "^4.19.0",
62
62
  "@ainyc/canonry-api-routes": "0.0.0",
63
- "@ainyc/canonry-config": "0.0.0",
64
63
  "@ainyc/canonry-contracts": "0.0.0",
65
64
  "@ainyc/canonry-db": "0.0.0",
66
65
  "@ainyc/canonry-intelligence": "0.0.0",
66
+ "@ainyc/canonry-config": "0.0.0",
67
67
  "@ainyc/canonry-integration-commoncrawl": "0.0.0",
68
- "@ainyc/canonry-integration-google": "0.0.0",
69
68
  "@ainyc/canonry-integration-bing": "0.0.0",
70
- "@ainyc/canonry-provider-cdp": "0.0.0",
71
69
  "@ainyc/canonry-integration-wordpress": "0.0.0",
72
70
  "@ainyc/canonry-provider-claude": "0.0.0",
73
71
  "@ainyc/canonry-provider-gemini": "0.0.0",
72
+ "@ainyc/canonry-provider-cdp": "0.0.0",
73
+ "@ainyc/canonry-integration-google": "0.0.0",
74
+ "@ainyc/canonry-provider-openai": "0.0.0",
74
75
  "@ainyc/canonry-provider-local": "0.0.0",
75
- "@ainyc/canonry-provider-perplexity": "0.0.0",
76
- "@ainyc/canonry-provider-openai": "0.0.0"
76
+ "@ainyc/canonry-provider-perplexity": "0.0.0"
77
77
  },
78
78
  "scripts": {
79
79
  "build": "tsx scripts/copy-agent-assets.ts && tsup && tsx build-web.ts",