@ainyc/canonry 4.35.0 → 4.36.0

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.
@@ -8,7 +8,7 @@ import {
8
8
  categoryLabel,
9
9
  determineAnswerMentioned,
10
10
  normalizeProjectDomain
11
- } from "./chunk-EM5GVF3C.js";
11
+ } from "./chunk-XJVYVURK.js";
12
12
 
13
13
  // src/intelligence-service.ts
14
14
  import { eq, desc, asc, and, or, inArray } from "drizzle-orm";
@@ -3303,14 +3303,14 @@ function gapTone(gapCount, totalCount) {
3303
3303
 
3304
3304
  // ../intelligence/src/visibility-score.ts
3305
3305
  function buildVisibilityScore(snapshots, options) {
3306
- const tooltip = 'Percentage of tracked queries where your domain is cited by at least one AI answer engine. A query is "visible" if any configured provider includes your site in its response.';
3306
+ const tooltip = "An LLM used a page on your domain as a source for its answer.";
3307
3307
  if (snapshots.length === 0) {
3308
3308
  return {
3309
- label: "Answer Visibility",
3309
+ label: "Citation Coverage",
3310
3310
  value: "No data",
3311
3311
  delta: "Run a sweep first",
3312
3312
  tone: "neutral",
3313
- description: "No visibility data yet. Trigger a run to start tracking.",
3313
+ description: "No citation data yet. Trigger a run to start tracking.",
3314
3314
  tooltip,
3315
3315
  trend: []
3316
3316
  };
@@ -3327,9 +3327,9 @@ function buildVisibilityScore(snapshots, options) {
3327
3327
  const runApiProviderCount = options.configuredApiProviders.filter((p) => runProviders.has(p)).length;
3328
3328
  const isPartialProviderRun = options.configuredApiProviders.length > 1 && runApiProviderCount < options.configuredApiProviders.length;
3329
3329
  return {
3330
- label: "Answer Visibility",
3330
+ label: "Citation Coverage",
3331
3331
  value: `${score}`,
3332
- delta: `${citedCount} of ${totalCount} queries visible`,
3332
+ delta: `${citedCount} of ${totalCount} queries cited`,
3333
3333
  tone: isPartialProviderRun ? "caution" : scoreTone(score),
3334
3334
  description: `${citedCount} of ${totalCount} tracked queries found your domain in at least one AI answer engine.`,
3335
3335
  tooltip,
@@ -3339,6 +3339,44 @@ function buildVisibilityScore(snapshots, options) {
3339
3339
  };
3340
3340
  }
3341
3341
 
3342
+ // ../intelligence/src/mention-coverage.ts
3343
+ function buildMentionCoverage(snapshots, options) {
3344
+ const tooltip = "Your domain or company name was in the answer returned by the LLM.";
3345
+ if (snapshots.length === 0) {
3346
+ return {
3347
+ label: "Mention Coverage",
3348
+ value: "No data",
3349
+ delta: "Run a sweep first",
3350
+ tone: "neutral",
3351
+ description: "No mention data yet. Trigger a run to start tracking.",
3352
+ tooltip,
3353
+ trend: []
3354
+ };
3355
+ }
3356
+ const queryMentioned = /* @__PURE__ */ new Map();
3357
+ for (const snap of snapshots) {
3358
+ if (!queryMentioned.has(snap.queryId)) queryMentioned.set(snap.queryId, false);
3359
+ if (snap.answerMentioned === true) queryMentioned.set(snap.queryId, true);
3360
+ }
3361
+ const totalCount = queryMentioned.size;
3362
+ const mentionedCount = [...queryMentioned.values()].filter(Boolean).length;
3363
+ const score = totalCount > 0 ? Math.round(mentionedCount / totalCount * 100) : 0;
3364
+ const runProviders = new Set(snapshots.map((s) => s.provider));
3365
+ const runApiProviderCount = options.configuredApiProviders.filter((p) => runProviders.has(p)).length;
3366
+ const isPartialProviderRun = options.configuredApiProviders.length > 1 && runApiProviderCount < options.configuredApiProviders.length;
3367
+ return {
3368
+ label: "Mention Coverage",
3369
+ value: `${score}`,
3370
+ delta: `${mentionedCount} of ${totalCount} queries mentioned`,
3371
+ tone: isPartialProviderRun ? "caution" : scoreTone(score),
3372
+ description: `${mentionedCount} of ${totalCount} tracked queries had your brand or domain in the AI answer text.`,
3373
+ tooltip,
3374
+ trend: [],
3375
+ progress: score,
3376
+ providerCoverage: isPartialProviderRun ? `${runApiProviderCount} of ${options.configuredApiProviders.length} providers` : void 0
3377
+ };
3378
+ }
3379
+
3342
3380
  // ../intelligence/src/gap-query-score.ts
3343
3381
  function buildGapQueryScore(snapshots) {
3344
3382
  const tooltip = "Tracked queries where a competitor is cited in the latest run but your domain is not.";
@@ -3611,8 +3649,13 @@ var IntelligenceService = class {
3611
3649
  /**
3612
3650
  * Analyze a single run given an explicit previous run (or null for first run).
3613
3651
  * Used by backfill where we control the run ordering.
3652
+ *
3653
+ * `dryRun: true` skips the DB write — `persistResult` is not called and
3654
+ * dismissed flags / health rows are untouched. Callers receive the same
3655
+ * AnalysisResult they would have, suitable for previewing what a write
3656
+ * would have produced.
3614
3657
  */
3615
- analyzeRunWithPrevious(runRecord, previousRunRecord, historyRecords) {
3658
+ analyzeRunWithPrevious(runRecord, previousRunRecord, historyRecords, opts) {
3616
3659
  const currentRun = this.buildRunData(
3617
3660
  runRecord.id,
3618
3661
  runRecord.projectId,
@@ -3632,23 +3675,44 @@ var IntelligenceService = class {
3632
3675
  const history = (historyRecords ?? []).map((r) => r.id === runRecord.id ? currentRun : this.buildRunData(r.id, r.projectId, r.finishedAt ?? r.createdAt, r.location ?? null));
3633
3676
  if (!previousRun) {
3634
3677
  const result2 = analyzeRuns(currentRun, currentRun, { trackedCompetitors, history });
3635
- this.persistResult(this.emptyAnalysisResult(result2), runRecord.id, runRecord.projectId);
3678
+ const emptyResult = this.emptyAnalysisResult(result2);
3679
+ if (!opts?.dryRun) this.persistResult(emptyResult, runRecord.id, runRecord.projectId);
3636
3680
  return result2;
3637
3681
  }
3638
3682
  const result = analyzeRuns(currentRun, previousRun, { trackedCompetitors, history });
3639
3683
  const tieredResult = this.tierResult(result, runRecord.id, runRecord.projectId);
3640
- this.persistResult(tieredResult, runRecord.id, runRecord.projectId);
3684
+ if (!opts?.dryRun) this.persistResult(tieredResult, runRecord.id, runRecord.projectId);
3641
3685
  return tieredResult;
3642
3686
  }
3643
3687
  /**
3644
3688
  * Backfill intelligence for all completed/partial runs of a project.
3645
3689
  * Processes runs in chronological order so each run compares against its predecessor.
3690
+ *
3691
+ * Scoping options:
3692
+ * - `fromRunId` / `toRunId`: bound the target range by exact run ID.
3693
+ * - `since`: bound the target range by `finishedAt >= <date>`. Accepts
3694
+ * any string that `Date.parse` understands (ISO 8601, `YYYY-MM-DD`,
3695
+ * etc.). Runs before the cutoff are *not* re-processed but stay
3696
+ * available for predecessor lookup, so transition detection at the
3697
+ * boundary stays correct. Composes with `fromRunId` / `toRunId` —
3698
+ * all three filters intersect.
3699
+ * - `dryRun`: compute the analysis without writing. The return value
3700
+ * includes a `delta` describing what would change (rows to delete vs
3701
+ * create per run + aggregate). DB is left untouched.
3646
3702
  */
3647
3703
  backfill(projectName, opts, onProgress) {
3648
3704
  const project = this.db.select().from(projects).where(eq(projects.name, projectName)).get();
3649
3705
  if (!project) {
3650
3706
  throw new Error(`Project "${projectName}" not found`);
3651
3707
  }
3708
+ let sinceTimestamp = null;
3709
+ if (opts?.since !== void 0) {
3710
+ const parsed = Date.parse(opts.since);
3711
+ if (Number.isNaN(parsed)) {
3712
+ throw new Error(`Invalid --since value "${opts.since}": expected a parseable date (ISO 8601 or YYYY-MM-DD)`);
3713
+ }
3714
+ sinceTimestamp = parsed;
3715
+ }
3652
3716
  const allRuns = this.db.select().from(runs).where(
3653
3717
  and(
3654
3718
  eq(runs.projectId, project.id),
@@ -3667,10 +3731,28 @@ var IntelligenceService = class {
3667
3731
  if (idx === -1) throw new Error(`Run "${opts.toRunId}" not found in project`);
3668
3732
  endIdx = idx + 1;
3669
3733
  }
3670
- const targetRuns = allRuns.slice(startIdx, endIdx);
3734
+ let targetRuns = allRuns.slice(startIdx, endIdx);
3735
+ if (sinceTimestamp !== null) {
3736
+ targetRuns = targetRuns.filter((r) => {
3737
+ const ts = r.finishedAt ?? r.createdAt;
3738
+ const t = Date.parse(ts);
3739
+ return !Number.isNaN(t) && t >= sinceTimestamp;
3740
+ });
3741
+ }
3671
3742
  let processed = 0;
3672
3743
  let skipped = 0;
3673
3744
  let totalInsights = 0;
3745
+ const isDryRun = opts?.dryRun === true;
3746
+ const perRunDelta = [];
3747
+ let wouldDeleteTotal = 0;
3748
+ const existingByRunId = /* @__PURE__ */ new Map();
3749
+ if (isDryRun && targetRuns.length > 0) {
3750
+ const rows = this.db.select({ runId: insights.runId }).from(insights).where(inArray(insights.runId, targetRuns.map((r) => r.id))).all();
3751
+ for (const r of rows) {
3752
+ if (r.runId == null) continue;
3753
+ existingByRunId.set(r.runId, (existingByRunId.get(r.runId) ?? 0) + 1);
3754
+ }
3755
+ }
3674
3756
  for (let i = 0; i < targetRuns.length; i++) {
3675
3757
  const run = targetRuns[i];
3676
3758
  const runLocation = run.location ?? null;
@@ -3679,16 +3761,35 @@ var IntelligenceService = class {
3679
3761
  const previousRun = sameLocIdx > 0 ? sameLocationRuns[sameLocIdx - 1] : null;
3680
3762
  const historyStart = Math.max(0, sameLocIdx - (HISTORY_WINDOW_RUNS - 1));
3681
3763
  const historyRecords = sameLocationRuns.slice(historyStart, sameLocIdx + 1);
3682
- const result = this.analyzeRunWithPrevious(run, previousRun, historyRecords);
3764
+ const result = this.analyzeRunWithPrevious(run, previousRun, historyRecords, { dryRun: isDryRun });
3683
3765
  if (result) {
3684
3766
  processed++;
3685
3767
  totalInsights += result.insights.length;
3768
+ if (isDryRun) {
3769
+ const existing = existingByRunId.get(run.id) ?? 0;
3770
+ wouldDeleteTotal += existing;
3771
+ perRunDelta.push({ runId: run.id, existingInsights: existing, newInsights: result.insights.length });
3772
+ }
3686
3773
  onProgress?.({ runId: run.id, index: i + 1, total: targetRuns.length, insights: result.insights.length });
3687
3774
  } else {
3688
3775
  skipped++;
3689
3776
  onProgress?.({ runId: run.id, index: i + 1, total: targetRuns.length, insights: 0 });
3690
3777
  }
3691
3778
  }
3779
+ if (isDryRun) {
3780
+ return {
3781
+ processed,
3782
+ skipped,
3783
+ totalInsights,
3784
+ dryRun: true,
3785
+ delta: {
3786
+ wouldDelete: wouldDeleteTotal,
3787
+ wouldCreate: totalInsights,
3788
+ netChange: totalInsights - wouldDeleteTotal,
3789
+ perRun: perRunDelta
3790
+ }
3791
+ };
3792
+ }
3692
3793
  return { processed, skipped, totalInsights };
3693
3794
  }
3694
3795
  loadTrackedCompetitors(projectId) {
@@ -3838,17 +3939,28 @@ var IntelligenceService = class {
3838
3939
  buildRunData(runId, projectId, completedAt, location = null) {
3839
3940
  const rows = this.db.select({
3840
3941
  query: queries.query,
3942
+ // Denormalized query text persisted by v58 — the fallback when the
3943
+ // joined queries.query has been hard-deleted (or the query_id was
3944
+ // nulled by the v58 dangling-FK cleanup).
3945
+ queryText: querySnapshots.queryText,
3841
3946
  provider: querySnapshots.provider,
3842
3947
  citationState: querySnapshots.citationState,
3843
3948
  citedDomains: querySnapshots.citedDomains,
3844
3949
  competitorOverlap: querySnapshots.competitorOverlap,
3845
3950
  snapshotLocation: querySnapshots.location
3846
3951
  }).from(querySnapshots).leftJoin(queries, eq(querySnapshots.queryId, queries.id)).where(eq(querySnapshots.runId, runId)).all();
3847
- const snapshots = rows.map((r) => {
3952
+ const snapshots = [];
3953
+ let orphanCount = 0;
3954
+ for (const r of rows) {
3955
+ const resolvedQuery = r.query ?? r.queryText ?? null;
3956
+ if (!resolvedQuery) {
3957
+ orphanCount++;
3958
+ continue;
3959
+ }
3848
3960
  const domains = parseJsonColumn(r.citedDomains, []);
3849
3961
  const competitors2 = parseJsonColumn(r.competitorOverlap, []);
3850
- return {
3851
- query: r.query ?? "",
3962
+ snapshots.push({
3963
+ query: resolvedQuery,
3852
3964
  provider: r.provider,
3853
3965
  cited: r.citationState === CitationStates.cited,
3854
3966
  citationUrl: domains[0] ?? void 0,
@@ -3863,8 +3975,11 @@ var IntelligenceService = class {
3863
3975
  // sources). Cause analysis uses it to name the displacing source
3864
3976
  // when no tracked competitor appears in the response.
3865
3977
  citedDomains: domains
3866
- };
3867
- });
3978
+ });
3979
+ }
3980
+ if (orphanCount > 0) {
3981
+ log.warn("snapshot.orphan-skip", { runId, projectId, orphanCount });
3982
+ }
3868
3983
  return { runId, projectId, completedAt, location, snapshots };
3869
3984
  }
3870
3985
  };
@@ -3928,6 +4043,7 @@ export {
3928
4043
  buildAiSourceOrigin,
3929
4044
  buildMovementSummary,
3930
4045
  buildVisibilityScore,
4046
+ buildMentionCoverage,
3931
4047
  buildGapQueryScore,
3932
4048
  buildCompetitorPressureScore,
3933
4049
  buildOverviewCompetitors,
@@ -22,7 +22,7 @@ import {
22
22
  trafficConnectVercelRequestSchema,
23
23
  trafficConnectWordpressRequestSchema,
24
24
  trafficEventKindSchema
25
- } from "./chunk-EM5GVF3C.js";
25
+ } from "./chunk-XJVYVURK.js";
26
26
 
27
27
  // src/config.ts
28
28
  import fs from "fs";
@@ -466,6 +466,9 @@ var ApiClient = class {
466
466
  async deleteProject(name) {
467
467
  await this.request("DELETE", `/projects/${encodeURIComponent(name)}`);
468
468
  }
469
+ async previewProjectDelete(name) {
470
+ return this.request("GET", `/projects/${encodeURIComponent(name)}/delete-preview`);
471
+ }
469
472
  async putQueries(project, queries) {
470
473
  await this.request("PUT", `/projects/${encodeURIComponent(project)}/queries`, { queries });
471
474
  }
@@ -1333,6 +1336,17 @@ var canonryMcpTools = [
1333
1336
  openApiOperations: ["GET /api/v1/projects/{name}"],
1334
1337
  handler: (client, input) => client.getProject(input.project)
1335
1338
  }),
1339
+ defineTool({
1340
+ name: "canonry_project_delete_preview",
1341
+ title: "Preview project delete impact",
1342
+ description: "Returns the cascade impact of deleting a project \u2014 how many queries, competitors, runs, snapshots, and insights would be removed, plus how many audit_log rows would be detached (project_id set NULL). Read-only. Use this BEFORE invoking project delete on any project you didn't create yourself; the underlying delete is irreversible.",
1343
+ access: "read",
1344
+ tier: "setup",
1345
+ inputSchema: projectInputSchema,
1346
+ annotations: readAnnotations(),
1347
+ openApiOperations: ["GET /api/v1/projects/{name}/delete-preview"],
1348
+ handler: (client, input) => client.previewProjectDelete(input.project)
1349
+ }),
1336
1350
  defineTool({
1337
1351
  name: "canonry_project_overview",
1338
1352
  title: "Get project overview (composite)",
@@ -1858,7 +1858,9 @@ var checkCategorySchema = z17.enum([
1858
1858
  "providers",
1859
1859
  "integrations",
1860
1860
  "database",
1861
- "schedules"
1861
+ "schedules",
1862
+ /** Discoverability checks for agent integrations (skills installed, MCP setup). */
1863
+ "agent"
1862
1864
  ]);
1863
1865
  var CheckCategories = checkCategorySchema.enum;
1864
1866
  var checkResultSchema = z17.object({
package/dist/cli.js CHANGED
@@ -21,7 +21,7 @@ import {
21
21
  setTelemetrySource,
22
22
  showFirstRunNotice,
23
23
  trackEvent
24
- } from "./chunk-NLV4MZZF.js";
24
+ } from "./chunk-F2G67CIU.js";
25
25
  import {
26
26
  CliError,
27
27
  EXIT_SYSTEM_ERROR,
@@ -37,14 +37,14 @@ import {
37
37
  saveConfig,
38
38
  saveConfigPatch,
39
39
  usageError
40
- } from "./chunk-B3FBOECD.js";
40
+ } from "./chunk-O7S623DL.js";
41
41
  import {
42
42
  apiKeys,
43
43
  createClient,
44
44
  migrate,
45
45
  projects,
46
46
  queries
47
- } from "./chunk-MLS5KJWK.js";
47
+ } from "./chunk-JQQXMCQ7.js";
48
48
  import {
49
49
  CcReleaseSyncStatuses,
50
50
  CheckScopes,
@@ -63,7 +63,7 @@ import {
63
63
  providerQuotaPolicySchema,
64
64
  resolveProviderInput,
65
65
  skillsClientSchema
66
- } from "./chunk-EM5GVF3C.js";
66
+ } from "./chunk-XJVYVURK.js";
67
67
 
68
68
  // src/cli.ts
69
69
  import { pathToFileURL } from "url";
@@ -113,17 +113,11 @@ function matchesPath(args, path10) {
113
113
  if (args.length < path10.length) return false;
114
114
  return path10.every((segment, index) => args[index] === segment);
115
115
  }
116
- function withFormatOption(options) {
117
- if (!options) {
118
- return {
119
- format: { type: "string" }
120
- };
121
- }
122
- if ("format" in options) return options;
123
- return {
124
- ...options,
125
- format: { type: "string" }
126
- };
116
+ function withGlobalOptions(options) {
117
+ const base = options ? { ...options } : {};
118
+ if (!("format" in base)) base.format = { type: "string" };
119
+ if (!("dry-run" in base)) base["dry-run"] = { type: "boolean" };
120
+ return base;
127
121
  }
128
122
  function toFormat(value, fallbackFormat) {
129
123
  return value === "json" ? "json" : fallbackFormat;
@@ -179,7 +173,7 @@ Usage: ${single.usage}
179
173
  try {
180
174
  const parsed = parseArgs({
181
175
  args: remainingArgs,
182
- options: withFormatOption(spec.options),
176
+ options: withGlobalOptions(spec.options),
183
177
  allowPositionals: spec.allowPositionals ?? true
184
178
  });
185
179
  values = parsed.values;
@@ -195,10 +189,21 @@ Usage: ${spec.usage}`, {
195
189
  }
196
190
  });
197
191
  }
192
+ const dryRunRequested = values["dry-run"] === true;
193
+ if (dryRunRequested && !spec.supportsDryRun) {
194
+ throw usageError(
195
+ `Command "${spec.path.join(" ")}" does not support --dry-run. Either drop the flag, or run a different command that supports preview mode (e.g. \`canonry doctor\` for health checks, \`canonry status <project>\` for read-only state).`,
196
+ {
197
+ message: `Command "${spec.path.join(" ")}" does not support --dry-run`,
198
+ details: { command: commandId(spec), usage: spec.usage }
199
+ }
200
+ );
201
+ }
198
202
  await spec.run({
199
203
  positionals,
200
204
  values,
201
- format: toFormat(values.format, fallbackFormat)
205
+ format: toFormat(values.format, fallbackFormat),
206
+ dryRun: dryRunRequested
202
207
  });
203
208
  return true;
204
209
  }
@@ -315,17 +320,21 @@ var BACKFILL_CLI_COMMANDS = [
315
320
  },
316
321
  {
317
322
  path: ["backfill", "insights"],
318
- usage: "canonry backfill insights <project> [--from-run <id>] [--to-run <id>] [--format json]",
323
+ usage: "canonry backfill insights <project> [--from-run <id>] [--to-run <id>] [--since <date>] [--dry-run] [--format json]",
319
324
  options: {
320
325
  "from-run": stringOption(),
321
- "to-run": stringOption()
326
+ "to-run": stringOption(),
327
+ "since": stringOption()
322
328
  },
329
+ supportsDryRun: true,
323
330
  run: async (input) => {
324
- const usage = "canonry backfill insights <project> [--from-run <id>] [--to-run <id>] [--format json]";
331
+ const usage = "canonry backfill insights <project> [--from-run <id>] [--to-run <id>] [--since <date>] [--dry-run] [--format json]";
325
332
  const project = requireProject(input, "backfill insights", usage);
326
333
  await backfillInsightsCommand(project, {
327
334
  fromRun: getString(input.values, "from-run"),
328
335
  toRun: getString(input.values, "to-run"),
336
+ since: getString(input.values, "since"),
337
+ dryRun: input.dryRun,
329
338
  format: input.format
330
339
  });
331
340
  }
@@ -5948,10 +5957,31 @@ async function updateProjectSettings(name, opts) {
5948
5957
  }
5949
5958
  console.log(`Project updated: ${result.name}`);
5950
5959
  }
5951
- async function deleteProject(name, format) {
5960
+ async function deleteProject(name, opts) {
5952
5961
  const client = getClient16();
5962
+ const isJson = opts?.format === "json";
5963
+ if (opts?.dryRun) {
5964
+ const preview = await client.previewProjectDelete(name);
5965
+ if (isJson) {
5966
+ console.log(JSON.stringify({ dryRun: true, ...preview }, null, 2));
5967
+ return;
5968
+ }
5969
+ const { cascadeRows: cr, detachedRows: dr } = preview;
5970
+ console.log(`Project delete preview for "${name}":`);
5971
+ console.log(` Cascade-deletes:`);
5972
+ console.log(` queries: ${cr.queries}`);
5973
+ console.log(` competitors: ${cr.competitors}`);
5974
+ console.log(` runs: ${cr.runs}`);
5975
+ console.log(` snapshots: ${cr.snapshots}`);
5976
+ console.log(` insights: ${cr.insights}`);
5977
+ console.log(` Detached (project_id set to NULL):`);
5978
+ console.log(` audit_log: ${dr.auditLog}`);
5979
+ console.log(``);
5980
+ console.log(`No DB writes performed. Re-run without --dry-run to delete.`);
5981
+ return;
5982
+ }
5953
5983
  await client.deleteProject(name);
5954
- if (format === "json") {
5984
+ if (isJson) {
5955
5985
  console.log(JSON.stringify({ name, deleted: true }, null, 2));
5956
5986
  return;
5957
5987
  }
@@ -6101,10 +6131,11 @@ var PROJECT_CLI_COMMANDS = [
6101
6131
  },
6102
6132
  {
6103
6133
  path: ["project", "delete"],
6104
- usage: "canonry project delete <name> [--format json]",
6134
+ usage: "canonry project delete <name> [--dry-run] [--format json]",
6135
+ supportsDryRun: true,
6105
6136
  run: async (input) => {
6106
- const name = requireProject(input, "project.delete", "canonry project delete <name> [--format json]");
6107
- await deleteProject(name, input.format);
6137
+ const name = requireProject(input, "project.delete", "canonry project delete <name> [--dry-run] [--format json]");
6138
+ await deleteProject(name, { dryRun: input.dryRun, format: input.format });
6108
6139
  }
6109
6140
  },
6110
6141
  {
@@ -6986,6 +7017,7 @@ Usage: canonry settings provider ${name} --api-key <key> [--model <model>] [--ma
6986
7017
 
6987
7018
  // src/commands/skills.ts
6988
7019
  import fs7 from "fs";
7020
+ import os2 from "os";
6989
7021
  import path4 from "path";
6990
7022
  import { fileURLToPath } from "url";
6991
7023
  var BUNDLED_SKILL_NAMES = ["canonry", "aero"];
@@ -7162,7 +7194,7 @@ function buildSummaryMessage(results) {
7162
7194
  return `Skills install summary: ${parts.join(", ")}.`;
7163
7195
  }
7164
7196
  async function installSkills(opts = {}) {
7165
- const targetDir = path4.resolve(opts.dir ?? process.cwd());
7197
+ const targetDir = opts.user ? os2.homedir() : path4.resolve(opts.dir ?? process.cwd());
7166
7198
  const client = opts.client ?? SkillsClients.all;
7167
7199
  const force = opts.force ?? false;
7168
7200
  const allSkills = getBundledSkills();
@@ -7248,9 +7280,10 @@ var SKILLS_CLI_COMMANDS = [
7248
7280
  },
7249
7281
  {
7250
7282
  path: ["skills", "install"],
7251
- usage: "canonry skills install [skill...] [--dir <path>] [--client claude|codex|all] [--force] [--format json]",
7283
+ usage: "canonry skills install [skill...] [--dir <path> | --user] [--client claude|codex|all] [--force] [--format json]",
7252
7284
  options: {
7253
7285
  dir: stringOption(),
7286
+ user: { type: "boolean" },
7254
7287
  client: stringOption(),
7255
7288
  force: { type: "boolean" }
7256
7289
  },
@@ -7258,6 +7291,7 @@ var SKILLS_CLI_COMMANDS = [
7258
7291
  run: async (input) => {
7259
7292
  const summary = await installSkills({
7260
7293
  dir: getString(input.values, "dir"),
7294
+ user: getBoolean(input.values, "user"),
7261
7295
  skills: input.positionals.length > 0 ? input.positionals : void 0,
7262
7296
  client: parseSkillsClient(getString(input.values, "client")),
7263
7297
  force: getBoolean(input.values, "force")
package/dist/index.js CHANGED
@@ -1,11 +1,11 @@
1
1
  import {
2
2
  createServer
3
- } from "./chunk-NLV4MZZF.js";
3
+ } from "./chunk-F2G67CIU.js";
4
4
  import {
5
5
  loadConfig
6
- } from "./chunk-B3FBOECD.js";
7
- import "./chunk-MLS5KJWK.js";
8
- import "./chunk-EM5GVF3C.js";
6
+ } from "./chunk-O7S623DL.js";
7
+ import "./chunk-JQQXMCQ7.js";
8
+ import "./chunk-XJVYVURK.js";
9
9
  export {
10
10
  createServer,
11
11
  loadConfig
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  IntelligenceService
3
- } from "./chunk-MLS5KJWK.js";
4
- import "./chunk-EM5GVF3C.js";
3
+ } from "./chunk-JQQXMCQ7.js";
4
+ import "./chunk-XJVYVURK.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-B3FBOECD.js";
6
- import "./chunk-EM5GVF3C.js";
5
+ } from "./chunk-O7S623DL.js";
6
+ import "./chunk-XJVYVURK.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.35.0",
3
+ "version": "4.36.0",
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",
@@ -62,18 +62,18 @@
62
62
  "tsx": "^4.19.0",
63
63
  "@ainyc/canonry-api-routes": "0.0.0",
64
64
  "@ainyc/canonry-config": "0.0.0",
65
- "@ainyc/canonry-contracts": "0.0.0",
66
65
  "@ainyc/canonry-db": "0.0.0",
67
66
  "@ainyc/canonry-intelligence": "0.0.0",
68
67
  "@ainyc/canonry-integration-bing": "0.0.0",
69
68
  "@ainyc/canonry-integration-cloud-run": "0.0.0",
70
- "@ainyc/canonry-integration-google": "0.0.0",
69
+ "@ainyc/canonry-contracts": "0.0.0",
71
70
  "@ainyc/canonry-integration-commoncrawl": "0.0.0",
72
71
  "@ainyc/canonry-integration-traffic": "0.0.0",
72
+ "@ainyc/canonry-integration-google": "0.0.0",
73
73
  "@ainyc/canonry-integration-wordpress": "0.0.0",
74
74
  "@ainyc/canonry-provider-cdp": "0.0.0",
75
- "@ainyc/canonry-provider-claude": "0.0.0",
76
75
  "@ainyc/canonry-provider-gemini": "0.0.0",
76
+ "@ainyc/canonry-provider-claude": "0.0.0",
77
77
  "@ainyc/canonry-provider-local": "0.0.0",
78
78
  "@ainyc/canonry-provider-openai": "0.0.0",
79
79
  "@ainyc/canonry-provider-perplexity": "0.0.0"