@ainyc/canonry 4.14.0 → 4.15.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.
package/dist/cli.js CHANGED
@@ -3,6 +3,7 @@ import {
3
3
  coerceAgentProvider,
4
4
  computeCompetitorOverlap,
5
5
  createServer,
6
+ detectAndTrackUpgrade,
6
7
  determineCitationState,
7
8
  extractRecommendedCompetitors,
8
9
  formatAuditFactorScore,
@@ -16,9 +17,10 @@ import {
16
17
  reparseStoredResult3,
17
18
  reparseStoredResult4,
18
19
  setGoogleAuthConfig,
20
+ setTelemetrySource,
19
21
  showFirstRunNotice,
20
22
  trackEvent
21
- } from "./chunk-UQHWSCTE.js";
23
+ } from "./chunk-DLSQXNUN.js";
22
24
  import {
23
25
  CliError,
24
26
  EXIT_SYSTEM_ERROR,
@@ -29,11 +31,12 @@ import {
29
31
  getConfigPath,
30
32
  isEndpointMissing,
31
33
  loadConfig,
34
+ loadConfigRaw,
32
35
  printCliError,
33
36
  saveConfig,
34
37
  saveConfigPatch,
35
38
  usageError
36
- } from "./chunk-5NYG5EC7.js";
39
+ } from "./chunk-C32VL5BB.js";
37
40
  import {
38
41
  apiKeys,
39
42
  competitors,
@@ -43,6 +46,7 @@ import {
43
46
  migrate,
44
47
  parseJsonColumn,
45
48
  projects,
49
+ queries,
46
50
  querySnapshots,
47
51
  runs
48
52
  } from "./chunk-7HBZCGRL.js";
@@ -69,6 +73,42 @@ import {
69
73
  // src/cli.ts
70
74
  import { pathToFileURL } from "url";
71
75
 
76
+ // src/setup-state.ts
77
+ import fs from "fs";
78
+ function buildSetupState() {
79
+ if (!configExists()) return void 0;
80
+ let provider_count = 0;
81
+ let has_keywords = false;
82
+ let project_count = 0;
83
+ let is_first_run = true;
84
+ let dbPath;
85
+ try {
86
+ const raw = loadConfigRaw();
87
+ if (raw) {
88
+ is_first_run = !raw.anonymousId;
89
+ if (raw.providers) {
90
+ provider_count = Object.values(raw.providers).filter(
91
+ (p) => Boolean(p?.apiKey) || Boolean(p?.baseUrl)
92
+ ).length;
93
+ }
94
+ if (typeof raw.database === "string" && raw.database.length > 0) {
95
+ dbPath = raw.database;
96
+ }
97
+ }
98
+ } catch {
99
+ }
100
+ if (dbPath && fs.existsSync(dbPath)) {
101
+ try {
102
+ const db = createClient(dbPath);
103
+ project_count = db.select({ id: projects.id }).from(projects).all().length;
104
+ const firstQuery = db.select({ id: queries.id }).from(queries).limit(1).all();
105
+ has_keywords = firstQuery.length > 0;
106
+ } catch {
107
+ }
108
+ }
109
+ return { provider_count, has_keywords, project_count, is_first_run };
110
+ }
111
+
72
112
  // src/cli-dispatch.ts
73
113
  import { parseArgs } from "util";
74
114
  function commandId(spec) {
@@ -2018,9 +2058,9 @@ async function gaConnect(project, opts) {
2018
2058
  propertyId: opts.propertyId
2019
2059
  };
2020
2060
  if (opts.keyFile) {
2021
- const fs12 = await import("fs");
2061
+ const fs13 = await import("fs");
2022
2062
  try {
2023
- const content = fs12.readFileSync(opts.keyFile, "utf-8");
2063
+ const content = fs13.readFileSync(opts.keyFile, "utf-8");
2024
2064
  JSON.parse(content);
2025
2065
  body.keyJson = content;
2026
2066
  } catch (e) {
@@ -2698,10 +2738,10 @@ async function trafficConnectCloudRun(project, opts) {
2698
2738
  details: { project }
2699
2739
  });
2700
2740
  }
2701
- const fs12 = await import("fs");
2741
+ const fs13 = await import("fs");
2702
2742
  let keyJson;
2703
2743
  try {
2704
- keyJson = fs12.readFileSync(opts.serviceAccountKey, "utf-8");
2744
+ keyJson = fs13.readFileSync(opts.serviceAccountKey, "utf-8");
2705
2745
  JSON.parse(keyJson);
2706
2746
  } catch (e) {
2707
2747
  const msg = e instanceof Error ? e.message : String(e);
@@ -3837,7 +3877,7 @@ var GOOGLE_CLI_COMMANDS = [
3837
3877
  ];
3838
3878
 
3839
3879
  // src/commands/keyword.ts
3840
- import fs from "fs";
3880
+ import fs2 from "fs";
3841
3881
  function getClient8() {
3842
3882
  return createApiClient();
3843
3883
  }
@@ -3902,7 +3942,7 @@ async function listKeywords(project, format) {
3902
3942
  }
3903
3943
  }
3904
3944
  async function importKeywords(project, filePath, format) {
3905
- if (!fs.existsSync(filePath)) {
3945
+ if (!fs2.existsSync(filePath)) {
3906
3946
  throw new CliError({
3907
3947
  code: "KEYWORD_IMPORT_FILE_NOT_FOUND",
3908
3948
  message: `File not found: ${filePath}`,
@@ -3913,7 +3953,7 @@ async function importKeywords(project, filePath, format) {
3913
3953
  }
3914
3954
  });
3915
3955
  }
3916
- const content = fs.readFileSync(filePath, "utf-8");
3956
+ const content = fs2.readFileSync(filePath, "utf-8");
3917
3957
  const keywords = content.split("\n").map((line) => line.trim()).filter((line) => line.length > 0 && !line.startsWith("#"));
3918
3958
  if (keywords.length === 0) {
3919
3959
  if (format === "json") {
@@ -4114,46 +4154,46 @@ var KEYWORD_CLI_COMMANDS = [
4114
4154
  ];
4115
4155
 
4116
4156
  // src/commands/query.ts
4117
- import fs2 from "fs";
4157
+ import fs3 from "fs";
4118
4158
  function getClient9() {
4119
4159
  return createApiClient();
4120
4160
  }
4121
- async function addQueries(project, queries, format) {
4161
+ async function addQueries(project, queries2, format) {
4122
4162
  const client = getClient9();
4123
- await client.appendQueries(project, queries);
4163
+ await client.appendQueries(project, queries2);
4124
4164
  if (format === "json") {
4125
4165
  console.log(JSON.stringify({
4126
4166
  project,
4127
- queries,
4128
- addedCount: queries.length
4167
+ queries: queries2,
4168
+ addedCount: queries2.length
4129
4169
  }, null, 2));
4130
4170
  return;
4131
4171
  }
4132
- console.log(`Added ${queries.length} ${queries.length === 1 ? "query" : "queries"} to "${project}".`);
4172
+ console.log(`Added ${queries2.length} ${queries2.length === 1 ? "query" : "queries"} to "${project}".`);
4133
4173
  }
4134
- async function replaceQueries(project, queries, format) {
4174
+ async function replaceQueries(project, queries2, format) {
4135
4175
  const client = getClient9();
4136
- await client.putQueries(project, queries);
4176
+ await client.putQueries(project, queries2);
4137
4177
  if (format === "json") {
4138
4178
  console.log(JSON.stringify({
4139
4179
  project,
4140
- queries,
4141
- replacedCount: queries.length
4180
+ queries: queries2,
4181
+ replacedCount: queries2.length
4142
4182
  }, null, 2));
4143
4183
  return;
4144
4184
  }
4145
- console.log(`Set ${queries.length} ${queries.length === 1 ? "query" : "queries"} for "${project}".`);
4185
+ console.log(`Set ${queries2.length} ${queries2.length === 1 ? "query" : "queries"} for "${project}".`);
4146
4186
  }
4147
- async function removeQueries(project, queries, format) {
4187
+ async function removeQueries(project, queries2, format) {
4148
4188
  const client = getClient9();
4149
4189
  const existing = await client.listQueries(project);
4150
4190
  const existingSet = new Set(existing.map((q) => q.query));
4151
- const removedQueries = queries.filter((q) => existingSet.has(q));
4152
- await client.deleteQueries(project, queries);
4191
+ const removedQueries = queries2.filter((q) => existingSet.has(q));
4192
+ await client.deleteQueries(project, queries2);
4153
4193
  if (format === "json") {
4154
4194
  console.log(JSON.stringify({
4155
4195
  project,
4156
- queries,
4196
+ queries: queries2,
4157
4197
  removedQueries,
4158
4198
  removedCount: removedQueries.length
4159
4199
  }, null, 2));
@@ -4179,7 +4219,7 @@ async function listQueries(project, format) {
4179
4219
  }
4180
4220
  }
4181
4221
  async function importQueries(project, filePath, format) {
4182
- if (!fs2.existsSync(filePath)) {
4222
+ if (!fs3.existsSync(filePath)) {
4183
4223
  throw new CliError({
4184
4224
  code: "QUERY_IMPORT_FILE_NOT_FOUND",
4185
4225
  message: `File not found: ${filePath}`,
@@ -4190,9 +4230,9 @@ async function importQueries(project, filePath, format) {
4190
4230
  }
4191
4231
  });
4192
4232
  }
4193
- const content = fs2.readFileSync(filePath, "utf-8");
4194
- const queries = content.split("\n").map((line) => line.trim()).filter((line) => line.length > 0 && !line.startsWith("#"));
4195
- if (queries.length === 0) {
4233
+ const content = fs3.readFileSync(filePath, "utf-8");
4234
+ const queries2 = content.split("\n").map((line) => line.trim()).filter((line) => line.length > 0 && !line.startsWith("#"));
4235
+ if (queries2.length === 0) {
4196
4236
  if (format === "json") {
4197
4237
  console.log(JSON.stringify({
4198
4238
  project,
@@ -4206,17 +4246,17 @@ async function importQueries(project, filePath, format) {
4206
4246
  return;
4207
4247
  }
4208
4248
  const client = getClient9();
4209
- await client.appendQueries(project, queries);
4249
+ await client.appendQueries(project, queries2);
4210
4250
  if (format === "json") {
4211
4251
  console.log(JSON.stringify({
4212
4252
  project,
4213
4253
  filePath,
4214
- queries,
4215
- importedCount: queries.length
4254
+ queries: queries2,
4255
+ importedCount: queries2.length
4216
4256
  }, null, 2));
4217
4257
  return;
4218
4258
  }
4219
- console.log(`Imported ${queries.length} ${queries.length === 1 ? "query" : "queries"} to "${project}".`);
4259
+ console.log(`Imported ${queries2.length} ${queries2.length === 1 ? "query" : "queries"} to "${project}".`);
4220
4260
  }
4221
4261
  async function generateQueries(project, provider, opts) {
4222
4262
  const client = getClient9();
@@ -4259,8 +4299,8 @@ var QUERY_CLI_COMMANDS = [
4259
4299
  usage: "canonry query add <project> <query...> [--format json]",
4260
4300
  run: async (input) => {
4261
4301
  const project = requireProject(input, "query.add", "canonry query add <project> <query...> [--format json]");
4262
- const queries = input.positionals.slice(1);
4263
- if (queries.length === 0) {
4302
+ const queries2 = input.positionals.slice(1);
4303
+ if (queries2.length === 0) {
4264
4304
  throw usageError("Error: project name and at least one query required\nUsage: canonry query add <project> <query...> [--format json]", {
4265
4305
  message: "project name and at least one query required",
4266
4306
  details: {
@@ -4269,7 +4309,7 @@ var QUERY_CLI_COMMANDS = [
4269
4309
  }
4270
4310
  });
4271
4311
  }
4272
- await addQueries(project, queries, input.format);
4312
+ await addQueries(project, queries2, input.format);
4273
4313
  }
4274
4314
  },
4275
4315
  {
@@ -4277,8 +4317,8 @@ var QUERY_CLI_COMMANDS = [
4277
4317
  usage: "canonry query replace <project> <query...> [--format json]",
4278
4318
  run: async (input) => {
4279
4319
  const project = requireProject(input, "query.replace", "canonry query replace <project> <query...> [--format json]");
4280
- const queries = input.positionals.slice(1);
4281
- if (queries.length === 0) {
4320
+ const queries2 = input.positionals.slice(1);
4321
+ if (queries2.length === 0) {
4282
4322
  throw usageError("Error: project name and at least one query required\nUsage: canonry query replace <project> <query...> [--format json]", {
4283
4323
  message: "project name and at least one query required",
4284
4324
  details: {
@@ -4287,7 +4327,7 @@ var QUERY_CLI_COMMANDS = [
4287
4327
  }
4288
4328
  });
4289
4329
  }
4290
- await replaceQueries(project, queries, input.format);
4330
+ await replaceQueries(project, queries2, input.format);
4291
4331
  }
4292
4332
  },
4293
4333
  {
@@ -4295,8 +4335,8 @@ var QUERY_CLI_COMMANDS = [
4295
4335
  usage: "canonry query remove <project> <query...> [--format json]",
4296
4336
  run: async (input) => {
4297
4337
  const project = requireProject(input, "query.remove", "canonry query remove <project> <query...> [--format json]");
4298
- const queries = input.positionals.slice(1);
4299
- if (queries.length === 0) {
4338
+ const queries2 = input.positionals.slice(1);
4339
+ if (queries2.length === 0) {
4300
4340
  throw usageError("Error: project name and at least one query required\nUsage: canonry query remove <project> <query...> [--format json]", {
4301
4341
  message: "project name and at least one query required",
4302
4342
  details: {
@@ -4305,7 +4345,7 @@ var QUERY_CLI_COMMANDS = [
4305
4345
  }
4306
4346
  });
4307
4347
  }
4308
- await removeQueries(project, queries, input.format);
4348
+ await removeQueries(project, queries2, input.format);
4309
4349
  }
4310
4350
  },
4311
4351
  {
@@ -4313,8 +4353,8 @@ var QUERY_CLI_COMMANDS = [
4313
4353
  usage: "canonry query delete <project> <query...> [--format json]",
4314
4354
  run: async (input) => {
4315
4355
  const project = requireProject(input, "query.delete", "canonry query delete <project> <query...> [--format json]");
4316
- const queries = input.positionals.slice(1);
4317
- if (queries.length === 0) {
4356
+ const queries2 = input.positionals.slice(1);
4357
+ if (queries2.length === 0) {
4318
4358
  throw usageError("Error: project name and at least one query required\nUsage: canonry query delete <project> <query...> [--format json]", {
4319
4359
  message: "project name and at least one query required",
4320
4360
  details: {
@@ -4323,7 +4363,7 @@ var QUERY_CLI_COMMANDS = [
4323
4363
  }
4324
4364
  });
4325
4365
  }
4326
- await removeQueries(project, queries, input.format);
4366
+ await removeQueries(project, queries2, input.format);
4327
4367
  }
4328
4368
  },
4329
4369
  {
@@ -4391,7 +4431,7 @@ var QUERY_CLI_COMMANDS = [
4391
4431
  ];
4392
4432
 
4393
4433
  // src/commands/mcp.ts
4394
- import fs3 from "fs";
4434
+ import fs4 from "fs";
4395
4435
  import path2 from "path";
4396
4436
  import { createRequire } from "module";
4397
4437
 
@@ -4497,8 +4537,8 @@ function renderClientSnippet(client, serverName, entry) {
4497
4537
  return renderJsonSnippet(serverName, entry, client.format);
4498
4538
  }
4499
4539
  function readJsonConfig(configPath) {
4500
- if (!fs3.existsSync(configPath)) return {};
4501
- const raw = fs3.readFileSync(configPath, "utf-8").trim();
4540
+ if (!fs4.existsSync(configPath)) return {};
4541
+ const raw = fs4.readFileSync(configPath, "utf-8").trim();
4502
4542
  if (!raw) return {};
4503
4543
  try {
4504
4544
  const parsed = JSON.parse(raw);
@@ -4516,14 +4556,14 @@ function readJsonConfig(configPath) {
4516
4556
  }
4517
4557
  }
4518
4558
  function writeJsonConfig(configPath, value) {
4519
- fs3.mkdirSync(path2.dirname(configPath), { recursive: true });
4520
- fs3.writeFileSync(configPath, `${JSON.stringify(value, null, 2)}
4559
+ fs4.mkdirSync(path2.dirname(configPath), { recursive: true });
4560
+ fs4.writeFileSync(configPath, `${JSON.stringify(value, null, 2)}
4521
4561
  `, "utf-8");
4522
4562
  }
4523
4563
  function backupConfigIfPresent(configPath) {
4524
- if (!fs3.existsSync(configPath)) return void 0;
4564
+ if (!fs4.existsSync(configPath)) return void 0;
4525
4565
  const backupPath = `${configPath}.canonry.bak`;
4526
- fs3.copyFileSync(configPath, backupPath);
4566
+ fs4.copyFileSync(configPath, backupPath);
4527
4567
  return backupPath;
4528
4568
  }
4529
4569
  function findClientOrThrow(id) {
@@ -4870,13 +4910,13 @@ var NOTIFY_CLI_COMMANDS = [
4870
4910
  ];
4871
4911
 
4872
4912
  // src/commands/apply.ts
4873
- import fs4 from "fs";
4913
+ import fs5 from "fs";
4874
4914
  import { parseAllDocuments } from "yaml";
4875
4915
  async function applyConfigFile(filePath) {
4876
- if (!fs4.existsSync(filePath)) {
4916
+ if (!fs5.existsSync(filePath)) {
4877
4917
  throw new Error(`File not found: ${filePath}`);
4878
4918
  }
4879
- const content = fs4.readFileSync(filePath, "utf-8");
4919
+ const content = fs5.readFileSync(filePath, "utf-8");
4880
4920
  const docs = parseAllDocuments(content);
4881
4921
  const client = createApiClient();
4882
4922
  const errors = [];
@@ -5620,7 +5660,7 @@ var PROJECT_CLI_COMMANDS = [
5620
5660
  ];
5621
5661
 
5622
5662
  // src/commands/report.ts
5623
- import fs5 from "fs";
5663
+ import fs6 from "fs";
5624
5664
  import path3 from "path";
5625
5665
  function defaultOutputPath(project, audience) {
5626
5666
  const date = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
@@ -5637,10 +5677,10 @@ async function runReportCommand(project, opts = {}) {
5637
5677
  const html = renderReportHtml(report, { audience });
5638
5678
  const targetPath = opts.output ? path3.resolve(opts.output) : defaultOutputPath(project, audience);
5639
5679
  const dir = path3.dirname(targetPath);
5640
- if (!fs5.existsSync(dir)) {
5641
- fs5.mkdirSync(dir, { recursive: true });
5680
+ if (!fs6.existsSync(dir)) {
5681
+ fs6.mkdirSync(dir, { recursive: true });
5642
5682
  }
5643
- fs5.writeFileSync(targetPath, html, "utf-8");
5683
+ fs6.writeFileSync(targetPath, html, "utf-8");
5644
5684
  console.log(`Report written to ${targetPath}`);
5645
5685
  }
5646
5686
 
@@ -6381,7 +6421,7 @@ Usage: canonry settings provider ${name} --api-key <key> [--model <model>] [--ma
6381
6421
  ];
6382
6422
 
6383
6423
  // src/commands/skills.ts
6384
- import fs6 from "fs";
6424
+ import fs7 from "fs";
6385
6425
  import path4 from "path";
6386
6426
  import { fileURLToPath } from "url";
6387
6427
  var BUNDLED_SKILL_NAMES = ["canonry-setup", "aero"];
@@ -6393,7 +6433,7 @@ function resolveBundledSkillsRoot(pkgDir) {
6393
6433
  path4.join(here, "../../../../skills")
6394
6434
  ];
6395
6435
  for (const candidate of candidates) {
6396
- if (BUNDLED_SKILL_NAMES.every((name) => fs6.existsSync(path4.join(candidate, name, "SKILL.md")))) {
6436
+ if (BUNDLED_SKILL_NAMES.every((name) => fs7.existsSync(path4.join(candidate, name, "SKILL.md")))) {
6397
6437
  return candidate;
6398
6438
  }
6399
6439
  }
@@ -6416,13 +6456,13 @@ function getBundledSkills(pkgDir) {
6416
6456
  return BUNDLED_SKILL_NAMES.map((name) => {
6417
6457
  const skillDir = path4.join(root, name);
6418
6458
  const skillFile = path4.join(skillDir, "SKILL.md");
6419
- const content = fs6.readFileSync(skillFile, "utf-8");
6459
+ const content = fs7.readFileSync(skillFile, "utf-8");
6420
6460
  return { name, description: parseDescription(content), bundledPath: skillDir };
6421
6461
  });
6422
6462
  }
6423
6463
  function walkRelative(dir, prefix = "") {
6424
6464
  const out = [];
6425
- for (const entry of fs6.readdirSync(dir, { withFileTypes: true })) {
6465
+ for (const entry of fs7.readdirSync(dir, { withFileTypes: true })) {
6426
6466
  const rel = prefix ? path4.join(prefix, entry.name) : entry.name;
6427
6467
  const full = path4.join(dir, entry.name);
6428
6468
  if (entry.isDirectory()) {
@@ -6434,28 +6474,28 @@ function walkRelative(dir, prefix = "") {
6434
6474
  return out.sort();
6435
6475
  }
6436
6476
  function compareDirContent(srcDir, destDir) {
6437
- if (!fs6.existsSync(destDir)) return "missing";
6438
- if (!fs6.statSync(destDir).isDirectory()) return "different";
6477
+ if (!fs7.existsSync(destDir)) return "missing";
6478
+ if (!fs7.statSync(destDir).isDirectory()) return "different";
6439
6479
  const srcFiles = walkRelative(srcDir);
6440
6480
  const destFiles = walkRelative(destDir);
6441
6481
  if (srcFiles.length !== destFiles.length) return "different";
6442
6482
  for (let i = 0; i < srcFiles.length; i++) {
6443
6483
  if (srcFiles[i] !== destFiles[i]) return "different";
6444
- const srcBytes = fs6.readFileSync(path4.join(srcDir, srcFiles[i]));
6445
- const destBytes = fs6.readFileSync(path4.join(destDir, destFiles[i]));
6484
+ const srcBytes = fs7.readFileSync(path4.join(srcDir, srcFiles[i]));
6485
+ const destBytes = fs7.readFileSync(path4.join(destDir, destFiles[i]));
6446
6486
  if (!srcBytes.equals(destBytes)) return "different";
6447
6487
  }
6448
6488
  return "match";
6449
6489
  }
6450
6490
  function copyDirRecursive(src, dest) {
6451
- fs6.mkdirSync(dest, { recursive: true });
6452
- for (const entry of fs6.readdirSync(src, { withFileTypes: true })) {
6491
+ fs7.mkdirSync(dest, { recursive: true });
6492
+ for (const entry of fs7.readdirSync(src, { withFileTypes: true })) {
6453
6493
  const srcPath = path4.join(src, entry.name);
6454
6494
  const destPath = path4.join(dest, entry.name);
6455
6495
  if (entry.isDirectory()) {
6456
6496
  copyDirRecursive(srcPath, destPath);
6457
6497
  } else if (entry.isFile()) {
6458
- fs6.copyFileSync(srcPath, destPath);
6498
+ fs7.copyFileSync(srcPath, destPath);
6459
6499
  }
6460
6500
  }
6461
6501
  }
@@ -6480,7 +6520,7 @@ function installClaudeSkill(skill, targetDir, force) {
6480
6520
  });
6481
6521
  }
6482
6522
  if (compare === "different") {
6483
- fs6.rmSync(targetPath, { recursive: true, force: true });
6523
+ fs7.rmSync(targetPath, { recursive: true, force: true });
6484
6524
  }
6485
6525
  copyDirRecursive(skill.bundledPath, targetPath);
6486
6526
  return {
@@ -6495,15 +6535,15 @@ function installCodexSymlink(skill, targetDir, force) {
6495
6535
  const codexPath = path4.join(targetDir, ".codex", "skills", skill.name);
6496
6536
  const claudePath = path4.join(targetDir, ".claude", "skills", skill.name);
6497
6537
  const linkTarget = path4.relative(path4.dirname(codexPath), claudePath);
6498
- fs6.mkdirSync(path4.dirname(codexPath), { recursive: true });
6538
+ fs7.mkdirSync(path4.dirname(codexPath), { recursive: true });
6499
6539
  let stat;
6500
6540
  try {
6501
- stat = fs6.lstatSync(codexPath);
6541
+ stat = fs7.lstatSync(codexPath);
6502
6542
  } catch {
6503
6543
  stat = void 0;
6504
6544
  }
6505
6545
  if (stat?.isSymbolicLink()) {
6506
- const existing = fs6.readlinkSync(codexPath);
6546
+ const existing = fs7.readlinkSync(codexPath);
6507
6547
  if (existing === linkTarget) {
6508
6548
  return {
6509
6549
  skill: skill.name,
@@ -6521,8 +6561,8 @@ function installCodexSymlink(skill, targetDir, force) {
6521
6561
  exitCode: 1
6522
6562
  });
6523
6563
  }
6524
- fs6.unlinkSync(codexPath);
6525
- fs6.symlinkSync(linkTarget, codexPath);
6564
+ fs7.unlinkSync(codexPath);
6565
+ fs7.symlinkSync(linkTarget, codexPath);
6526
6566
  return {
6527
6567
  skill: skill.name,
6528
6568
  client: CodingAgents.codex,
@@ -6540,9 +6580,9 @@ function installCodexSymlink(skill, targetDir, force) {
6540
6580
  exitCode: 1
6541
6581
  });
6542
6582
  }
6543
- fs6.rmSync(codexPath, { recursive: true, force: true });
6583
+ fs7.rmSync(codexPath, { recursive: true, force: true });
6544
6584
  }
6545
- fs6.symlinkSync(linkTarget, codexPath);
6585
+ fs7.symlinkSync(linkTarget, codexPath);
6546
6586
  return {
6547
6587
  skill: skill.name,
6548
6588
  client: CodingAgents.codex,
@@ -6574,7 +6614,7 @@ async function installSkills(opts = {}) {
6574
6614
  });
6575
6615
  }
6576
6616
  const skillsToInstall = allSkills.filter((s) => requestedNames.includes(s.name));
6577
- fs6.mkdirSync(targetDir, { recursive: true });
6617
+ fs7.mkdirSync(targetDir, { recursive: true });
6578
6618
  const results = [];
6579
6619
  for (const skill of skillsToInstall) {
6580
6620
  results.push(installClaudeSkill(skill, targetDir, force));
@@ -6675,11 +6715,11 @@ var SKILLS_CLI_COMMANDS = [
6675
6715
  ];
6676
6716
 
6677
6717
  // src/commands/snapshot.ts
6678
- import fs8 from "fs";
6718
+ import fs9 from "fs";
6679
6719
  import path6 from "path";
6680
6720
 
6681
6721
  // src/snapshot-pdf.ts
6682
- import fs7 from "fs";
6722
+ import fs8 from "fs";
6683
6723
  import path5 from "path";
6684
6724
  import { PDFDocument, StandardFonts, rgb } from "pdf-lib";
6685
6725
  var PAGE_WIDTH = 612;
@@ -6890,8 +6930,8 @@ async function writeSnapshotPdf(report, outputPath) {
6890
6930
  renderQueries(pdf, report);
6891
6931
  const bytes = await doc.save();
6892
6932
  const resolvedPath = path5.resolve(outputPath);
6893
- fs7.mkdirSync(path5.dirname(resolvedPath), { recursive: true });
6894
- fs7.writeFileSync(resolvedPath, bytes);
6933
+ fs8.mkdirSync(path5.dirname(resolvedPath), { recursive: true });
6934
+ fs8.writeFileSync(resolvedPath, bytes);
6895
6935
  return resolvedPath;
6896
6936
  }
6897
6937
  function renderCover(pdf, report) {
@@ -7050,8 +7090,8 @@ PDF saved: ${savedPdfPath}`);
7050
7090
  }
7051
7091
  function writeSnapshotMarkdown(report, outputPath) {
7052
7092
  const resolvedPath = path6.resolve(outputPath);
7053
- fs8.mkdirSync(path6.dirname(resolvedPath), { recursive: true });
7054
- fs8.writeFileSync(resolvedPath, formatSnapshotMarkdown(report), "utf-8");
7093
+ fs9.mkdirSync(path6.dirname(resolvedPath), { recursive: true });
7094
+ fs9.writeFileSync(resolvedPath, formatSnapshotMarkdown(report), "utf-8");
7055
7095
  return resolvedPath;
7056
7096
  }
7057
7097
  function formatSnapshotMarkdown(report) {
@@ -7993,7 +8033,7 @@ async function bootstrapCommand(_opts) {
7993
8033
 
7994
8034
  // src/commands/daemon.ts
7995
8035
  import { spawn } from "child_process";
7996
- import fs9 from "fs";
8036
+ import fs10 from "fs";
7997
8037
  import path8 from "path";
7998
8038
  function getPidPath() {
7999
8039
  return path8.join(getConfigDir(), "canonry.pid");
@@ -8023,8 +8063,8 @@ async function waitForReady(host, port, maxMs = 1e4) {
8023
8063
  async function startDaemon(opts) {
8024
8064
  const pidPath = getPidPath();
8025
8065
  const format = opts.format ?? "text";
8026
- if (fs9.existsSync(pidPath)) {
8027
- const existingPid = parseInt(fs9.readFileSync(pidPath, "utf-8").trim(), 10);
8066
+ if (fs10.existsSync(pidPath)) {
8067
+ const existingPid = parseInt(fs10.readFileSync(pidPath, "utf-8").trim(), 10);
8028
8068
  if (!isNaN(existingPid) && isProcessAlive(existingPid)) {
8029
8069
  throw new CliError({
8030
8070
  code: "DAEMON_ALREADY_RUNNING",
@@ -8035,7 +8075,7 @@ async function startDaemon(opts) {
8035
8075
  }
8036
8076
  });
8037
8077
  }
8038
- fs9.unlinkSync(pidPath);
8078
+ fs10.unlinkSync(pidPath);
8039
8079
  }
8040
8080
  const cliPath = path8.resolve(new URL(import.meta.url).pathname);
8041
8081
  const inSourceMode = new URL(import.meta.url).pathname.endsWith(".ts");
@@ -8056,10 +8096,10 @@ async function startDaemon(opts) {
8056
8096
  });
8057
8097
  }
8058
8098
  const configDir = getConfigDir();
8059
- if (!fs9.existsSync(configDir)) {
8060
- fs9.mkdirSync(configDir, { recursive: true });
8099
+ if (!fs10.existsSync(configDir)) {
8100
+ fs10.mkdirSync(configDir, { recursive: true });
8061
8101
  }
8062
- fs9.writeFileSync(pidPath, String(child.pid), "utf-8");
8102
+ fs10.writeFileSync(pidPath, String(child.pid), "utf-8");
8063
8103
  const port = opts.port ?? "4100";
8064
8104
  const host = opts.host ?? "127.0.0.1";
8065
8105
  if (format !== "json") {
@@ -8068,7 +8108,7 @@ async function startDaemon(opts) {
8068
8108
  const ready = await waitForReady(host, port);
8069
8109
  if (!ready) {
8070
8110
  try {
8071
- fs9.unlinkSync(pidPath);
8111
+ fs10.unlinkSync(pidPath);
8072
8112
  } catch {
8073
8113
  }
8074
8114
  throw new CliError({
@@ -8100,7 +8140,7 @@ async function startDaemon(opts) {
8100
8140
  }
8101
8141
  function stopDaemon(format = "text") {
8102
8142
  const pidPath = getPidPath();
8103
- if (!fs9.existsSync(pidPath)) {
8143
+ if (!fs10.existsSync(pidPath)) {
8104
8144
  if (format === "json") {
8105
8145
  console.log(JSON.stringify({
8106
8146
  stopped: false,
@@ -8111,7 +8151,7 @@ function stopDaemon(format = "text") {
8111
8151
  console.log("Canonry is not running (no PID file found)");
8112
8152
  return;
8113
8153
  }
8114
- const pid = parseInt(fs9.readFileSync(pidPath, "utf-8").trim(), 10);
8154
+ const pid = parseInt(fs10.readFileSync(pidPath, "utf-8").trim(), 10);
8115
8155
  if (isNaN(pid)) {
8116
8156
  if (format === "json") {
8117
8157
  console.log(JSON.stringify({
@@ -8122,7 +8162,7 @@ function stopDaemon(format = "text") {
8122
8162
  } else {
8123
8163
  console.error("Invalid PID file. Removing it.");
8124
8164
  }
8125
- fs9.unlinkSync(pidPath);
8165
+ fs10.unlinkSync(pidPath);
8126
8166
  return;
8127
8167
  }
8128
8168
  if (!isProcessAlive(pid)) {
@@ -8136,12 +8176,12 @@ function stopDaemon(format = "text") {
8136
8176
  } else {
8137
8177
  console.log(`Canonry is not running (stale PID: ${pid}). Cleaning up.`);
8138
8178
  }
8139
- fs9.unlinkSync(pidPath);
8179
+ fs10.unlinkSync(pidPath);
8140
8180
  return;
8141
8181
  }
8142
8182
  try {
8143
8183
  process.kill(pid, "SIGTERM");
8144
- fs9.unlinkSync(pidPath);
8184
+ fs10.unlinkSync(pidPath);
8145
8185
  if (format === "json") {
8146
8186
  console.log(JSON.stringify({
8147
8187
  stopped: true,
@@ -8165,7 +8205,7 @@ function stopDaemon(format = "text") {
8165
8205
 
8166
8206
  // src/commands/init.ts
8167
8207
  import crypto2 from "crypto";
8168
- import fs10 from "fs";
8208
+ import fs11 from "fs";
8169
8209
  import readline from "readline";
8170
8210
  import path9 from "path";
8171
8211
  function prompt(question) {
@@ -8189,7 +8229,7 @@ var PROJECT_MARKERS = [".git", "canonry.yaml", "canonry.yml", "package.json"];
8189
8229
  function cwdLooksLikeProject(dir) {
8190
8230
  const home = process.env.HOME ?? "";
8191
8231
  if (home && path9.resolve(dir) === path9.resolve(home)) return false;
8192
- return PROJECT_MARKERS.some((marker) => fs10.existsSync(path9.join(dir, marker)));
8232
+ return PROJECT_MARKERS.some((marker) => fs11.existsSync(path9.join(dir, marker)));
8193
8233
  }
8194
8234
  var DEFAULT_AGENT_MODELS = {
8195
8235
  anthropic: "anthropic/claude-sonnet-4-6",
@@ -8219,8 +8259,8 @@ async function initCommand(opts) {
8219
8259
  return void 0;
8220
8260
  }
8221
8261
  const configDir = getConfigDir();
8222
- if (!fs10.existsSync(configDir)) {
8223
- fs10.mkdirSync(configDir, { recursive: true });
8262
+ if (!fs11.existsSync(configDir)) {
8263
+ fs11.mkdirSync(configDir, { recursive: true });
8224
8264
  }
8225
8265
  const bootstrapEnv = getBootstrapEnv(process.env, {
8226
8266
  GEMINI_API_KEY: opts?.geminiKey,
@@ -8530,6 +8570,7 @@ Received ${signal}, stopping server...`);
8530
8570
  Canonry server running at ${url}`);
8531
8571
  console.log("Press Ctrl+C to stop.\n");
8532
8572
  }
8573
+ setTelemetrySource("cli-server");
8533
8574
  const providerNames = Object.keys(config.providers ?? {}).filter(
8534
8575
  (k) => config.providers?.[k]?.apiKey || config.providers?.[k]?.baseUrl
8535
8576
  );
@@ -8815,7 +8856,7 @@ var SYSTEM_CLI_COMMANDS = [
8815
8856
  ];
8816
8857
 
8817
8858
  // src/cli-commands/wordpress.ts
8818
- import fs11 from "fs";
8859
+ import fs12 from "fs";
8819
8860
 
8820
8861
  // src/commands/wordpress.ts
8821
8862
  function getClient20() {
@@ -9051,12 +9092,12 @@ async function wordpressSetMeta(project, body) {
9051
9092
  printPageDetail(result);
9052
9093
  }
9053
9094
  async function wordpressBulkSetMeta(project, opts) {
9054
- const fs12 = await import("fs/promises");
9095
+ const fs13 = await import("fs/promises");
9055
9096
  const path10 = await import("path");
9056
9097
  const filePath = path10.resolve(opts.from);
9057
9098
  let raw;
9058
9099
  try {
9059
- raw = await fs12.readFile(filePath, "utf8");
9100
+ raw = await fs13.readFile(filePath, "utf8");
9060
9101
  } catch {
9061
9102
  throw new CliError({
9062
9103
  code: "FILE_READ_ERROR",
@@ -9153,13 +9194,13 @@ async function wordpressSetSchema(project, body) {
9153
9194
  printManualAssist(`Schema update for "${body.slug}"`, result);
9154
9195
  }
9155
9196
  async function wordpressSchemaDeploy(project, opts) {
9156
- const fs12 = await import("fs/promises");
9197
+ const fs13 = await import("fs/promises");
9157
9198
  const path10 = await import("path");
9158
9199
  const yaml = await import("yaml").catch(() => null);
9159
9200
  const filePath = path10.resolve(opts.profile);
9160
9201
  let raw;
9161
9202
  try {
9162
- raw = await fs12.readFile(filePath, "utf8");
9203
+ raw = await fs13.readFile(filePath, "utf8");
9163
9204
  } catch {
9164
9205
  throw new CliError({
9165
9206
  code: "FILE_READ_ERROR",
@@ -9264,13 +9305,13 @@ async function wordpressOnboard(project, opts) {
9264
9305
  }
9265
9306
  let profileData;
9266
9307
  if (opts.profile) {
9267
- const fs12 = await import("fs/promises");
9308
+ const fs13 = await import("fs/promises");
9268
9309
  const path10 = await import("path");
9269
9310
  const yaml = await import("yaml").catch(() => null);
9270
9311
  const filePath = path10.resolve(opts.profile);
9271
9312
  let raw;
9272
9313
  try {
9273
- raw = await fs12.readFile(filePath, "utf8");
9314
+ raw = await fs13.readFile(filePath, "utf8");
9274
9315
  } catch {
9275
9316
  throw new CliError({
9276
9317
  code: "FILE_READ_ERROR",
@@ -9419,7 +9460,7 @@ function resolveContent(input, command, usage, options) {
9419
9460
  }
9420
9461
  if (contentFile) {
9421
9462
  try {
9422
- return fs11.readFileSync(contentFile, "utf-8");
9463
+ return fs12.readFileSync(contentFile, "utf-8");
9423
9464
  } catch (error) {
9424
9465
  const message = error instanceof Error ? error.message : String(error);
9425
9466
  throw usageError(`Error: could not read --content-file "${contentFile}": ${message}`, {
@@ -10465,7 +10506,12 @@ async function runCli(args = process.argv.slice(2)) {
10465
10506
  resolvedCommand = command;
10466
10507
  }
10467
10508
  if (!isHelpRequest && command !== "telemetry") {
10468
- trackEvent("cli.command", { command: resolvedCommand });
10509
+ detectAndTrackUpgrade();
10510
+ const setupState = buildSetupState();
10511
+ trackEvent("cli.command", {
10512
+ command: resolvedCommand,
10513
+ ...setupState ? { setup_state: setupState } : {}
10514
+ });
10469
10515
  }
10470
10516
  try {
10471
10517
  if (await dispatchRegisteredCommand(args, format, REGISTERED_CLI_COMMANDS)) {