@ainyc/canonry 4.35.0 → 4.39.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.
@@ -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-Q57CMOL6.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-JS6KRBBZ.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-DOUV45KI.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
  }
@@ -287,45 +292,53 @@ Usage: ${config.usage}`, {
287
292
  var BACKFILL_CLI_COMMANDS = [
288
293
  {
289
294
  path: ["backfill", "answer-visibility"],
290
- usage: "canonry backfill answer-visibility [--project <name>] [--format json]",
295
+ usage: "canonry backfill answer-visibility [--project <name>] [--dry-run] [--format json]",
291
296
  options: {
292
297
  project: stringOption()
293
298
  },
294
299
  allowPositionals: false,
300
+ supportsDryRun: true,
295
301
  run: async (input) => {
296
302
  await backfillAnswerVisibilityCommand({
297
303
  project: getString(input.values, "project"),
304
+ dryRun: input.dryRun,
298
305
  format: input.format
299
306
  });
300
307
  }
301
308
  },
302
309
  {
303
310
  path: ["backfill", "answer-mentions"],
304
- usage: "canonry backfill answer-mentions [--project <name>] [--format json]",
311
+ usage: "canonry backfill answer-mentions [--project <name>] [--dry-run] [--format json]",
305
312
  options: {
306
313
  project: stringOption()
307
314
  },
308
315
  allowPositionals: false,
316
+ supportsDryRun: true,
309
317
  run: async (input) => {
310
318
  await backfillAnswerMentionsCommand({
311
319
  project: getString(input.values, "project"),
320
+ dryRun: input.dryRun,
312
321
  format: input.format
313
322
  });
314
323
  }
315
324
  },
316
325
  {
317
326
  path: ["backfill", "insights"],
318
- usage: "canonry backfill insights <project> [--from-run <id>] [--to-run <id>] [--format json]",
327
+ usage: "canonry backfill insights <project> [--from-run <id>] [--to-run <id>] [--since <date>] [--dry-run] [--format json]",
319
328
  options: {
320
329
  "from-run": stringOption(),
321
- "to-run": stringOption()
330
+ "to-run": stringOption(),
331
+ "since": stringOption()
322
332
  },
333
+ supportsDryRun: true,
323
334
  run: async (input) => {
324
- const usage = "canonry backfill insights <project> [--from-run <id>] [--to-run <id>] [--format json]";
335
+ const usage = "canonry backfill insights <project> [--from-run <id>] [--to-run <id>] [--since <date>] [--dry-run] [--format json]";
325
336
  const project = requireProject(input, "backfill insights", usage);
326
337
  await backfillInsightsCommand(project, {
327
338
  fromRun: getString(input.values, "from-run"),
328
339
  toRun: getString(input.values, "to-run"),
340
+ since: getString(input.values, "since"),
341
+ dryRun: input.dryRun,
329
342
  format: input.format
330
343
  });
331
344
  }
@@ -4682,10 +4695,33 @@ async function addQueries(project, queries2, format) {
4682
4695
  }
4683
4696
  console.log(`Added ${queries2.length} ${queries2.length === 1 ? "query" : "queries"} to "${project}".`);
4684
4697
  }
4685
- async function replaceQueries(project, queries2, format) {
4698
+ async function replaceQueries(project, queries2, opts) {
4686
4699
  const client = getClient10();
4700
+ const isJson = opts?.format === "json";
4701
+ if (opts?.dryRun) {
4702
+ const preview = await client.previewReplaceQueries(project, queries2);
4703
+ if (isJson) {
4704
+ console.log(JSON.stringify({ dryRun: true, ...preview }, null, 2));
4705
+ return;
4706
+ }
4707
+ const { diff, snapshotImpact } = preview;
4708
+ console.log(`Query replace preview for "${project}":`);
4709
+ console.log(` Current: ${preview.current.length} ${preview.current.length === 1 ? "query" : "queries"}`);
4710
+ console.log(` Proposed: ${preview.proposed.length} ${preview.proposed.length === 1 ? "query" : "queries"}`);
4711
+ console.log(` Diff:`);
4712
+ console.log(` + added: ${diff.added.length}${diff.added.length ? ` (${diff.added.join(", ")})` : ""}`);
4713
+ console.log(` - removed: ${diff.removed.length}${diff.removed.length ? ` (${diff.removed.join(", ")})` : ""}`);
4714
+ console.log(` = unchanged: ${diff.unchanged.length}${diff.unchanged.length ? ` (${diff.unchanged.join(", ")})` : ""}`);
4715
+ console.log(` Snapshot impact:`);
4716
+ console.log(` Replace wipes every queries row and re-inserts with new IDs, so ALL`);
4717
+ console.log(` existing snapshots get detached (queryId \u2192 NULL; queryText preserved).`);
4718
+ console.log(` Snapshots affected: ${snapshotImpact.snapshotsDetached} across ${snapshotImpact.affectedQueries} ${snapshotImpact.affectedQueries === 1 ? "query" : "queries"}`);
4719
+ console.log(``);
4720
+ console.log(`No DB writes performed. Re-run without --dry-run to apply.`);
4721
+ return;
4722
+ }
4687
4723
  await client.putQueries(project, queries2);
4688
- if (format === "json") {
4724
+ if (isJson) {
4689
4725
  console.log(JSON.stringify({
4690
4726
  project,
4691
4727
  queries: queries2,
@@ -4825,20 +4861,21 @@ var QUERY_CLI_COMMANDS = [
4825
4861
  },
4826
4862
  {
4827
4863
  path: ["query", "replace"],
4828
- usage: "canonry query replace <project> <query...> [--format json]",
4864
+ usage: "canonry query replace <project> <query...> [--dry-run] [--format json]",
4865
+ supportsDryRun: true,
4829
4866
  run: async (input) => {
4830
- const project = requireProject(input, "query.replace", "canonry query replace <project> <query...> [--format json]");
4867
+ const project = requireProject(input, "query.replace", "canonry query replace <project> <query...> [--dry-run] [--format json]");
4831
4868
  const queries2 = input.positionals.slice(1);
4832
4869
  if (queries2.length === 0) {
4833
- throw usageError("Error: project name and at least one query required\nUsage: canonry query replace <project> <query...> [--format json]", {
4870
+ throw usageError("Error: project name and at least one query required\nUsage: canonry query replace <project> <query...> [--dry-run] [--format json]", {
4834
4871
  message: "project name and at least one query required",
4835
4872
  details: {
4836
4873
  command: "query.replace",
4837
- usage: "canonry query replace <project> <query...> [--format json]"
4874
+ usage: "canonry query replace <project> <query...> [--dry-run] [--format json]"
4838
4875
  }
4839
4876
  });
4840
4877
  }
4841
- await replaceQueries(project, queries2, input.format);
4878
+ await replaceQueries(project, queries2, { dryRun: input.dryRun, format: input.format });
4842
4879
  }
4843
4880
  },
4844
4881
  {
@@ -5948,10 +5985,31 @@ async function updateProjectSettings(name, opts) {
5948
5985
  }
5949
5986
  console.log(`Project updated: ${result.name}`);
5950
5987
  }
5951
- async function deleteProject(name, format) {
5988
+ async function deleteProject(name, opts) {
5952
5989
  const client = getClient16();
5990
+ const isJson = opts?.format === "json";
5991
+ if (opts?.dryRun) {
5992
+ const preview = await client.previewProjectDelete(name);
5993
+ if (isJson) {
5994
+ console.log(JSON.stringify({ dryRun: true, ...preview }, null, 2));
5995
+ return;
5996
+ }
5997
+ const { cascadeRows: cr, detachedRows: dr } = preview;
5998
+ console.log(`Project delete preview for "${name}":`);
5999
+ console.log(` Cascade-deletes:`);
6000
+ console.log(` queries: ${cr.queries}`);
6001
+ console.log(` competitors: ${cr.competitors}`);
6002
+ console.log(` runs: ${cr.runs}`);
6003
+ console.log(` snapshots: ${cr.snapshots}`);
6004
+ console.log(` insights: ${cr.insights}`);
6005
+ console.log(` Detached (project_id set to NULL):`);
6006
+ console.log(` audit_log: ${dr.auditLog}`);
6007
+ console.log(``);
6008
+ console.log(`No DB writes performed. Re-run without --dry-run to delete.`);
6009
+ return;
6010
+ }
5953
6011
  await client.deleteProject(name);
5954
- if (format === "json") {
6012
+ if (isJson) {
5955
6013
  console.log(JSON.stringify({ name, deleted: true }, null, 2));
5956
6014
  return;
5957
6015
  }
@@ -6101,10 +6159,11 @@ var PROJECT_CLI_COMMANDS = [
6101
6159
  },
6102
6160
  {
6103
6161
  path: ["project", "delete"],
6104
- usage: "canonry project delete <name> [--format json]",
6162
+ usage: "canonry project delete <name> [--dry-run] [--format json]",
6163
+ supportsDryRun: true,
6105
6164
  run: async (input) => {
6106
- const name = requireProject(input, "project.delete", "canonry project delete <name> [--format json]");
6107
- await deleteProject(name, input.format);
6165
+ const name = requireProject(input, "project.delete", "canonry project delete <name> [--dry-run] [--format json]");
6166
+ await deleteProject(name, { dryRun: input.dryRun, format: input.format });
6108
6167
  }
6109
6168
  },
6110
6169
  {
@@ -6986,6 +7045,7 @@ Usage: canonry settings provider ${name} --api-key <key> [--model <model>] [--ma
6986
7045
 
6987
7046
  // src/commands/skills.ts
6988
7047
  import fs7 from "fs";
7048
+ import os2 from "os";
6989
7049
  import path4 from "path";
6990
7050
  import { fileURLToPath } from "url";
6991
7051
  var BUNDLED_SKILL_NAMES = ["canonry", "aero"];
@@ -7162,7 +7222,7 @@ function buildSummaryMessage(results) {
7162
7222
  return `Skills install summary: ${parts.join(", ")}.`;
7163
7223
  }
7164
7224
  async function installSkills(opts = {}) {
7165
- const targetDir = path4.resolve(opts.dir ?? process.cwd());
7225
+ const targetDir = opts.user ? os2.homedir() : path4.resolve(opts.dir ?? process.cwd());
7166
7226
  const client = opts.client ?? SkillsClients.all;
7167
7227
  const force = opts.force ?? false;
7168
7228
  const allSkills = getBundledSkills();
@@ -7224,6 +7284,31 @@ function emitInstallSummary(summary, format) {
7224
7284
  Target: ${summary.targetDir}`);
7225
7285
  console.log(summary.message);
7226
7286
  }
7287
+ function getMissingUserSkillsNudge(home) {
7288
+ if (!home) return null;
7289
+ const skillsBase = path4.join(home, ".claude", "skills");
7290
+ const installed = [];
7291
+ const missing = [];
7292
+ for (const name of BUNDLED_SKILL_NAMES) {
7293
+ const skillFile = path4.join(skillsBase, name, "SKILL.md");
7294
+ if (existsSafe(skillFile)) installed.push(name);
7295
+ else missing.push(name);
7296
+ }
7297
+ if (missing.length === 0) return null;
7298
+ const fix = missing.length === BUNDLED_SKILL_NAMES.length ? "canonry skills install --user" : `canonry skills install ${missing.join(" ")} --user`;
7299
+ return {
7300
+ message: `Tip: ${missing.join(" + ")} skill${missing.length === 1 ? "" : "s"} not installed in ~/.claude/skills/. Run \`${fix}\` so Claude/Codex sessions on this host auto-load the canonry reference docs.`,
7301
+ missing,
7302
+ installed
7303
+ };
7304
+ }
7305
+ function existsSafe(p) {
7306
+ try {
7307
+ return fs7.existsSync(p);
7308
+ } catch {
7309
+ return false;
7310
+ }
7311
+ }
7227
7312
  function parseSkillsClient(value) {
7228
7313
  if (!value) return SkillsClients.all;
7229
7314
  const parsed = skillsClientSchema.safeParse(value);
@@ -7248,9 +7333,10 @@ var SKILLS_CLI_COMMANDS = [
7248
7333
  },
7249
7334
  {
7250
7335
  path: ["skills", "install"],
7251
- usage: "canonry skills install [skill...] [--dir <path>] [--client claude|codex|all] [--force] [--format json]",
7336
+ usage: "canonry skills install [skill...] [--dir <path> | --user] [--client claude|codex|all] [--force] [--format json]",
7252
7337
  options: {
7253
7338
  dir: stringOption(),
7339
+ user: { type: "boolean" },
7254
7340
  client: stringOption(),
7255
7341
  force: { type: "boolean" }
7256
7342
  },
@@ -7258,6 +7344,7 @@ var SKILLS_CLI_COMMANDS = [
7258
7344
  run: async (input) => {
7259
7345
  const summary = await installSkills({
7260
7346
  dir: getString(input.values, "dir"),
7347
+ user: getBoolean(input.values, "user"),
7261
7348
  skills: input.positionals.length > 0 ? input.positionals : void 0,
7262
7349
  client: parseSkillsClient(getString(input.values, "client")),
7263
7350
  force: getBoolean(input.values, "force")
@@ -9137,6 +9224,9 @@ Received ${signal}, stopping server...`);
9137
9224
  console.log(`
9138
9225
  Canonry server running at ${url}`);
9139
9226
  console.log("Press Ctrl+C to stop.\n");
9227
+ const nudge = getMissingUserSkillsNudge(process.env.HOME);
9228
+ if (nudge) process.stderr.write(`${nudge.message}
9229
+ `);
9140
9230
  }
9141
9231
  setTelemetrySource("cli-server");
9142
9232
  const providerNames = Object.keys(config.providers ?? {}).filter(
package/dist/index.js CHANGED
@@ -1,11 +1,11 @@
1
1
  import {
2
2
  createServer
3
- } from "./chunk-NLV4MZZF.js";
3
+ } from "./chunk-Q57CMOL6.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-JS6KRBBZ.js";
7
+ import "./chunk-DOUV45KI.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-DOUV45KI.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-JS6KRBBZ.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.39.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",
@@ -60,23 +60,23 @@
60
60
  "@types/node-cron": "^3.0.11",
61
61
  "tsup": "^8.5.1",
62
62
  "tsx": "^4.19.0",
63
- "@ainyc/canonry-api-routes": "0.0.0",
64
63
  "@ainyc/canonry-config": "0.0.0",
65
- "@ainyc/canonry-contracts": "0.0.0",
64
+ "@ainyc/canonry-api-routes": "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",
68
+ "@ainyc/canonry-contracts": "0.0.0",
69
69
  "@ainyc/canonry-integration-cloud-run": "0.0.0",
70
- "@ainyc/canonry-integration-google": "0.0.0",
71
70
  "@ainyc/canonry-integration-commoncrawl": "0.0.0",
72
71
  "@ainyc/canonry-integration-traffic": "0.0.0",
73
72
  "@ainyc/canonry-integration-wordpress": "0.0.0",
74
- "@ainyc/canonry-provider-cdp": "0.0.0",
75
73
  "@ainyc/canonry-provider-claude": "0.0.0",
74
+ "@ainyc/canonry-integration-google": "0.0.0",
76
75
  "@ainyc/canonry-provider-gemini": "0.0.0",
77
- "@ainyc/canonry-provider-local": "0.0.0",
76
+ "@ainyc/canonry-provider-cdp": "0.0.0",
78
77
  "@ainyc/canonry-provider-openai": "0.0.0",
79
- "@ainyc/canonry-provider-perplexity": "0.0.0"
78
+ "@ainyc/canonry-provider-perplexity": "0.0.0",
79
+ "@ainyc/canonry-provider-local": "0.0.0"
80
80
  },
81
81
  "scripts": {
82
82
  "build": "tsx scripts/copy-agent-assets.ts && tsup && tsx build-web.ts",