@staff0rd/assist 0.658.0 → 0.659.1

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/index.js CHANGED
@@ -6,7 +6,7 @@ import { Command } from "commander";
6
6
  // package.json
7
7
  var package_default = {
8
8
  name: "@staff0rd/assist",
9
- version: "0.658.0",
9
+ version: "0.659.1",
10
10
  type: "module",
11
11
  main: "dist/index.js",
12
12
  bin: {
@@ -561,6 +561,23 @@ function stripLegacyConfigKeys(config) {
561
561
  // src/shared/types.ts
562
562
  import { z as z3 } from "zod";
563
563
 
564
+ // src/shared/adviceFragmentNames.ts
565
+ var adviceFragmentNames = [
566
+ "assist-global",
567
+ "backlog-ids",
568
+ "backlog-prs",
569
+ "code-comments",
570
+ "drafting-messages",
571
+ "editing-files",
572
+ "filename-convention",
573
+ "jira-context",
574
+ "jira-smart-links",
575
+ "markdown",
576
+ "refactor",
577
+ "settings-json",
578
+ "verify"
579
+ ];
580
+
564
581
  // src/shared/runConfigSchema.ts
565
582
  import { z } from "zod";
566
583
  var runParamSchema = z.strictObject({
@@ -619,8 +636,8 @@ var DEFAULT_BACKUP_DIR = "~/.assist/backups";
619
636
  var DEFAULT_CLONE_DIR = "~/git";
620
637
  var assistConfigShape = {
621
638
  advice: z3.strictObject({
622
- include: z3.array(z3.string()).default([]),
623
- exclude: z3.array(z3.string()).default([]),
639
+ include: z3.array(z3.enum(adviceFragmentNames)).default([]),
640
+ exclude: z3.array(z3.enum(adviceFragmentNames)).default([]),
624
641
  extra: z3.string().optional(),
625
642
  verify: z3.string().optional()
626
643
  }).optional(),
@@ -2013,7 +2030,96 @@ function lint(options2 = {}) {
2013
2030
 
2014
2031
  // src/commands/new/registerNew/newCli/index.ts
2015
2032
  import { execSync as execSync12 } from "child_process";
2016
- import { basename as basename3, resolve as resolve6 } from "path";
2033
+ import { basename as basename4, resolve as resolve6 } from "path";
2034
+
2035
+ // src/commands/advise/loadAdviceFragments.ts
2036
+ import { readdirSync, readFileSync as readFileSync10 } from "fs";
2037
+ import { basename as basename2, join as join10 } from "path";
2038
+
2039
+ // src/commands/advise/adviceDir.ts
2040
+ import { existsSync as existsSync13 } from "fs";
2041
+ import { dirname as dirname10, join as join9 } from "path";
2042
+ import { fileURLToPath } from "url";
2043
+ function adviceDir() {
2044
+ let current = dirname10(fileURLToPath(import.meta.url));
2045
+ while (current !== dirname10(current)) {
2046
+ const candidate = join9(current, "claude", "advice");
2047
+ if (existsSync13(candidate)) return candidate;
2048
+ current = dirname10(current);
2049
+ }
2050
+ throw new Error("Could not locate the shipped claude/advice directory");
2051
+ }
2052
+
2053
+ // src/commands/advise/parseAdviceFragment.ts
2054
+ import { parse as parseYaml2 } from "yaml";
2055
+ var frontmatter = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?/;
2056
+ function parseAdviceFragment(name, content) {
2057
+ const match = frontmatter.exec(content);
2058
+ if (!match) throw new Error(`Advice fragment ${name} has no frontmatter`);
2059
+ const meta = parseYaml2(match[1]) ?? {};
2060
+ const title = meta.title;
2061
+ const when = meta.when;
2062
+ if (typeof title !== "string" || typeof when !== "string")
2063
+ throw new Error(
2064
+ `Advice fragment ${name} needs a title and a when condition`
2065
+ );
2066
+ return {
2067
+ name,
2068
+ title,
2069
+ when,
2070
+ body: content.slice(match[0].length).trim()
2071
+ };
2072
+ }
2073
+
2074
+ // src/commands/advise/loadAdviceFragments.ts
2075
+ function loadAdviceFragments(dir = adviceDir()) {
2076
+ return readdirSync(dir).filter((file) => file.endsWith(".md")).sort().map(
2077
+ (file) => parseAdviceFragment(
2078
+ basename2(file, ".md"),
2079
+ readFileSync10(join10(dir, file), "utf8")
2080
+ )
2081
+ );
2082
+ }
2083
+
2084
+ // src/commands/verify/reportVerifyProblems.ts
2085
+ function verifySection(label2, entries) {
2086
+ if (entries.length === 0) return void 0;
2087
+ const list5 = entries.sort().map((entry) => ` ${entry}`).join("\n");
2088
+ return `${label2}:
2089
+ ${list5}`;
2090
+ }
2091
+ function reportVerifyProblems(problems, success) {
2092
+ const found = problems.filter(
2093
+ (problem) => problem !== void 0
2094
+ );
2095
+ if (found.length > 0) {
2096
+ console.log(found.join("\n\n"));
2097
+ process.exit(1);
2098
+ }
2099
+ console.log(success);
2100
+ process.exit(0);
2101
+ }
2102
+
2103
+ // src/commands/verify/adviceFragments.ts
2104
+ function adviceFragments() {
2105
+ const shipped = new Set(
2106
+ loadAdviceFragments().map((fragment) => fragment.name)
2107
+ );
2108
+ const declared = new Set(adviceFragmentNames);
2109
+ reportVerifyProblems(
2110
+ [
2111
+ verifySection(
2112
+ "Shipped in claude/advice but missing from adviceFragmentNames (add them, or advice.include/exclude cannot name them)",
2113
+ [...shipped].filter((name) => !declared.has(name))
2114
+ ),
2115
+ verifySection(
2116
+ "Listed in adviceFragmentNames but no longer shipped in claude/advice (remove them)",
2117
+ [...declared].filter((name) => !shipped.has(name))
2118
+ )
2119
+ ],
2120
+ `All ${shipped.size} advice fragments are named in adviceFragmentNames.`
2121
+ );
2122
+ }
2017
2123
 
2018
2124
  // src/commands/verify/blockCodeComments/findComments.ts
2019
2125
  import { execSync as execSync4 } from "child_process";
@@ -2960,12 +3066,12 @@ var adviceConfigHelp = [
2960
3066
  {
2961
3067
  key: "advice.include",
2962
3068
  setter: 'assist config set advice.include "refactor"',
2963
- note: "fragment names included whatever their condition says"
3069
+ note: "fragment names included whatever their condition says; 'assist advise --explain' lists every name"
2964
3070
  },
2965
3071
  {
2966
3072
  key: "advice.exclude",
2967
3073
  setter: 'assist config set advice.exclude "jira-context"',
2968
- note: "fragment names dropped even when their condition matches"
3074
+ note: "fragment names dropped even when their condition matches; 'assist advise --explain' lists every name"
2969
3075
  },
2970
3076
  {
2971
3077
  key: "advice.extra",
@@ -3573,57 +3679,46 @@ var pendingConfigDocumentation = /* @__PURE__ */ new Set([
3573
3679
  ]);
3574
3680
 
3575
3681
  // src/commands/verify/configKeys.ts
3576
- function section(label2, keys) {
3577
- if (keys.length === 0) return void 0;
3578
- const list5 = keys.sort().map((key) => ` ${key}`).join("\n");
3579
- return `${label2}:
3580
- ${list5}`;
3581
- }
3582
3682
  function configKeys() {
3583
3683
  const schemaKeys = new Set(enumerateConfigLeafKeys(assistConfigSchema));
3584
3684
  const documented = getDocumentedConfigKeys();
3585
3685
  const pending = pendingConfigDocumentation;
3586
3686
  const aggregated = new Set(configHelpEntries.map((entry) => entry.key));
3587
- const problems = [
3588
- section(
3589
- "Config keys documented by no command (surface them with configHelp)",
3590
- [...schemaKeys].filter(
3591
- (key) => !documented.has(key) && !pending.has(key)
3687
+ reportVerifyProblems(
3688
+ [
3689
+ verifySection(
3690
+ "Config keys documented by no command (surface them with configHelp)",
3691
+ [...schemaKeys].filter(
3692
+ (key) => !documented.has(key) && !pending.has(key)
3693
+ )
3694
+ ),
3695
+ verifySection(
3696
+ "Documented config keys not present in assistConfigSchema",
3697
+ [...documented].filter((key) => !schemaKeys.has(key))
3698
+ ),
3699
+ verifySection(
3700
+ "Keys now documented but still in pendingConfigDocumentation (remove them)",
3701
+ [...pending].filter((key) => documented.has(key))
3702
+ ),
3703
+ verifySection(
3704
+ "pendingConfigDocumentation lists keys not in assistConfigSchema (remove them)",
3705
+ [...pending].filter((key) => !schemaKeys.has(key))
3706
+ ),
3707
+ verifySection(
3708
+ "Keys documented by a command but missing from configHelpEntries (add their module to src/commands/configHelpEntries.ts)",
3709
+ [...documented].filter((key) => !aggregated.has(key))
3710
+ ),
3711
+ verifySection(
3712
+ "configHelpEntries lists keys no command documents (remove them)",
3713
+ [...aggregated].filter((key) => !documented.has(key))
3592
3714
  )
3593
- ),
3594
- section(
3595
- "Documented config keys not present in assistConfigSchema",
3596
- [...documented].filter((key) => !schemaKeys.has(key))
3597
- ),
3598
- section(
3599
- "Keys now documented but still in pendingConfigDocumentation (remove them)",
3600
- [...pending].filter((key) => documented.has(key))
3601
- ),
3602
- section(
3603
- "pendingConfigDocumentation lists keys not in assistConfigSchema (remove them)",
3604
- [...pending].filter((key) => !schemaKeys.has(key))
3605
- ),
3606
- section(
3607
- "Keys documented by a command but missing from configHelpEntries (add their module to src/commands/configHelpEntries.ts)",
3608
- [...documented].filter((key) => !aggregated.has(key))
3609
- ),
3610
- section(
3611
- "configHelpEntries lists keys no command documents (remove them)",
3612
- [...aggregated].filter((key) => !documented.has(key))
3613
- )
3614
- ].filter((problem) => problem !== void 0);
3615
- if (problems.length > 0) {
3616
- console.log(problems.join("\n\n"));
3617
- process.exit(1);
3618
- }
3619
- console.log(
3715
+ ],
3620
3716
  `All ${schemaKeys.size} config keys are surfaced in --help or pending documentation.`
3621
3717
  );
3622
- process.exit(0);
3623
3718
  }
3624
3719
 
3625
3720
  // src/commands/verify/forbiddenStrings/index.ts
3626
- import { existsSync as existsSync13, readFileSync as readFileSync10 } from "fs";
3721
+ import { existsSync as existsSync14, readFileSync as readFileSync11 } from "fs";
3627
3722
 
3628
3723
  // src/commands/verify/forbiddenStrings/findForbiddenStrings.ts
3629
3724
  import { minimatch as minimatch2 } from "minimatch";
@@ -3664,13 +3759,13 @@ function forbiddenStrings() {
3664
3759
  const cache5 = /* @__PURE__ */ new Map();
3665
3760
  const readJson = (file) => {
3666
3761
  if (cache5.has(file)) return cache5.get(file);
3667
- if (!existsSync13(file)) {
3762
+ if (!existsSync14(file)) {
3668
3763
  console.log(`Forbidden-strings file not found: ${file}`);
3669
3764
  process.exit(1);
3670
3765
  }
3671
3766
  let parsed;
3672
3767
  try {
3673
- parsed = JSON.parse(readFileSync10(file, "utf8"));
3768
+ parsed = JSON.parse(readFileSync11(file, "utf8"));
3674
3769
  } catch (error) {
3675
3770
  console.log(`Could not parse ${file}: ${error.message}`);
3676
3771
  process.exit(1);
@@ -3743,7 +3838,7 @@ Total: ${lines2.length} hardcoded color(s)`);
3743
3838
  import * as path20 from "path";
3744
3839
 
3745
3840
  // src/shared/resolveRunConfigs.ts
3746
- import { dirname as dirname10, relative, resolve as resolve4 } from "path";
3841
+ import { dirname as dirname11, relative, resolve as resolve4 } from "path";
3747
3842
 
3748
3843
  // src/shared/assertNoDuplicateRunNames.ts
3749
3844
  function findDuplicateNames(configs) {
@@ -3765,14 +3860,14 @@ function assertNoDuplicateRunNames(configs) {
3765
3860
  }
3766
3861
 
3767
3862
  // src/shared/findLinkedConfigPath.ts
3768
- import { existsSync as existsSync14 } from "fs";
3769
- import { join as join9, resolve as resolve2 } from "path";
3863
+ import { existsSync as existsSync15 } from "fs";
3864
+ import { join as join11, resolve as resolve2 } from "path";
3770
3865
  function findLinkedConfigPath(linkPath, fromDir) {
3771
3866
  const resolved = resolve2(fromDir, linkPath);
3772
- const claudePath = join9(resolved, ".claude", "assist.yml");
3773
- if (existsSync14(claudePath)) return claudePath;
3774
- const rootPath = join9(resolved, "assist.yml");
3775
- if (existsSync14(rootPath)) return rootPath;
3867
+ const claudePath = join11(resolved, ".claude", "assist.yml");
3868
+ if (existsSync15(claudePath)) return claudePath;
3869
+ const rootPath = join11(resolved, "assist.yml");
3870
+ if (existsSync15(rootPath)) return rootPath;
3776
3871
  throw new Error(`No assist.yml found in linked project: ${resolved}`);
3777
3872
  }
3778
3873
 
@@ -3814,7 +3909,7 @@ function loadAndResolveLink(linkPath, configDir, ctx) {
3814
3909
  const entries = loadLinkedEntries(configPath, ctx.visited);
3815
3910
  const defaultCwd = relativeToRoot(ctx, resolve4(configDir, linkPath));
3816
3911
  return setDefaultCwd(
3817
- resolveRecursive(entries, dirname10(configPath), ctx),
3912
+ resolveRecursive(entries, dirname11(configPath), ctx),
3818
3913
  defaultCwd
3819
3914
  );
3820
3915
  }
@@ -3841,12 +3936,12 @@ function resolveLocalCwd(config, configDir, ctx) {
3841
3936
  }
3842
3937
 
3843
3938
  // src/shared/findRepoRoot.ts
3844
- import { existsSync as existsSync15 } from "fs";
3939
+ import { existsSync as existsSync16 } from "fs";
3845
3940
  import path19 from "path";
3846
3941
  function findRepoRoot(dir) {
3847
3942
  let current = dir;
3848
3943
  while (current !== path19.dirname(current)) {
3849
- if (existsSync15(path19.join(current, ".git"))) {
3944
+ if (existsSync16(path19.join(current, ".git"))) {
3850
3945
  return current;
3851
3946
  }
3852
3947
  current = path19.dirname(current);
@@ -3917,9 +4012,9 @@ function list() {
3917
4012
  }
3918
4013
 
3919
4014
  // src/commands/verify/migrations/index.ts
3920
- import { readFileSync as readFileSync11 } from "fs";
4015
+ import { readFileSync as readFileSync12 } from "fs";
3921
4016
  import path21 from "path";
3922
- import { fileURLToPath } from "url";
4017
+ import { fileURLToPath as fileURLToPath2 } from "url";
3923
4018
 
3924
4019
  // src/shared/db/migrations/migration0001Baseline.ts
3925
4020
  var sql = `
@@ -4267,15 +4362,15 @@ function checkSequential(migrations3, files) {
4267
4362
  }
4268
4363
 
4269
4364
  // src/commands/verify/migrations/listMigrationFiles.ts
4270
- import { readdirSync } from "fs";
4365
+ import { readdirSync as readdirSync2 } from "fs";
4271
4366
  var MIGRATION_FILE2 = /^migration\d+[A-Za-z0-9]*\.ts$/;
4272
4367
  function listMigrationFiles(dir) {
4273
- return readdirSync(dir).filter((file) => MIGRATION_FILE2.test(file));
4368
+ return readdirSync2(dir).filter((file) => MIGRATION_FILE2.test(file));
4274
4369
  }
4275
4370
 
4276
4371
  // src/commands/verify/migrations/readBaselineMigrations.ts
4277
4372
  import { execSync as execSync6 } from "child_process";
4278
- import { basename as basename2 } from "path";
4373
+ import { basename as basename3 } from "path";
4279
4374
  var MIGRATION_FILE3 = /^migration\d+[A-Za-z0-9]*\.ts$/;
4280
4375
  function readBaselineMigrations(repoRelativeDir, ref) {
4281
4376
  const baseline = /* @__PURE__ */ new Map();
@@ -4288,7 +4383,7 @@ function readBaselineMigrations(repoRelativeDir, ref) {
4288
4383
  } catch {
4289
4384
  return baseline;
4290
4385
  }
4291
- const paths = listing.split("\n").map((line) => line.trim()).filter(Boolean).filter((path91) => MIGRATION_FILE3.test(basename2(path91)));
4386
+ const paths = listing.split("\n").map((line) => line.trim()).filter(Boolean).filter((path91) => MIGRATION_FILE3.test(basename3(path91)));
4292
4387
  for (const path91 of paths) {
4293
4388
  try {
4294
4389
  const content = execSync6(`git show "${ref}:${path91}"`, {
@@ -4296,7 +4391,7 @@ function readBaselineMigrations(repoRelativeDir, ref) {
4296
4391
  maxBuffer: 16 * 1024 * 1024,
4297
4392
  stdio: ["pipe", "pipe", "pipe"]
4298
4393
  });
4299
- baseline.set(basename2(path91), content);
4394
+ baseline.set(basename3(path91), content);
4300
4395
  } catch {
4301
4396
  }
4302
4397
  }
@@ -4338,7 +4433,7 @@ function resolveBaselineRef() {
4338
4433
  var REPO_RELATIVE_DIR = "src/shared/db/migrations";
4339
4434
  function migrationsDir() {
4340
4435
  return path21.resolve(
4341
- path21.dirname(fileURLToPath(import.meta.url)),
4436
+ path21.dirname(fileURLToPath2(import.meta.url)),
4342
4437
  "../../../shared/db/migrations"
4343
4438
  );
4344
4439
  }
@@ -4356,7 +4451,7 @@ function migrations2() {
4356
4451
  if (ref) {
4357
4452
  const baseline = readBaselineMigrations(REPO_RELATIVE_DIR, ref);
4358
4453
  const current = new Map(
4359
- files.map((file) => [file, readFileSync11(path21.join(dir, file), "utf8")])
4454
+ files.map((file) => [file, readFileSync12(path21.join(dir, file), "utf8")])
4360
4455
  );
4361
4456
  for (const finding of checkAppendOnly(baseline, current)) {
4362
4457
  problems.push(
@@ -4415,15 +4510,15 @@ import * as net from "net";
4415
4510
 
4416
4511
  // src/commands/sessions/daemon/daemonPaths.ts
4417
4512
  import { homedir as homedir2 } from "os";
4418
- import { join as join10 } from "path";
4419
- var DAEMON_DIR = join10(homedir2(), ".assist", "daemon");
4513
+ import { join as join12 } from "path";
4514
+ var DAEMON_DIR = join12(homedir2(), ".assist", "daemon");
4420
4515
  var daemonPaths = {
4421
4516
  dir: DAEMON_DIR,
4422
- socket: process.platform === "win32" ? String.raw`\\.\pipe\assist-sessions-daemon` : join10(DAEMON_DIR, "daemon.sock"),
4423
- log: join10(DAEMON_DIR, "daemon.log"),
4424
- pid: join10(DAEMON_DIR, "daemon.pid"),
4425
- spawnLock: join10(DAEMON_DIR, "spawn.lock"),
4426
- hooksSettings: join10(DAEMON_DIR, "hooks-settings.json")
4517
+ socket: process.platform === "win32" ? String.raw`\\.\pipe\assist-sessions-daemon` : join12(DAEMON_DIR, "daemon.sock"),
4518
+ log: join12(DAEMON_DIR, "daemon.log"),
4519
+ pid: join12(DAEMON_DIR, "daemon.pid"),
4520
+ spawnLock: join12(DAEMON_DIR, "spawn.lock"),
4521
+ hooksSettings: join12(DAEMON_DIR, "hooks-settings.json")
4427
4522
  };
4428
4523
 
4429
4524
  // src/commands/sessions/daemon/connectToDaemon.ts
@@ -4731,7 +4826,7 @@ program.parse();
4731
4826
 
4732
4827
  // src/commands/new/registerNew/newCli/index.ts
4733
4828
  async function newCli() {
4734
- const name = basename3(resolve6("."));
4829
+ const name = basename4(resolve6("."));
4735
4830
  initGit();
4736
4831
  initPackageJson(name);
4737
4832
  console.log("Installing dependencies...");
@@ -4746,7 +4841,7 @@ async function newCli() {
4746
4841
 
4747
4842
  // src/commands/new/registerNew/newProject.ts
4748
4843
  import { execSync as execSync14 } from "child_process";
4749
- import { existsSync as existsSync17, readFileSync as readFileSync13, writeFileSync as writeFileSync13 } from "fs";
4844
+ import { existsSync as existsSync18, readFileSync as readFileSync14, writeFileSync as writeFileSync13 } from "fs";
4750
4845
 
4751
4846
  // src/commands/deploy/init/index.ts
4752
4847
  import { execSync as execSync13 } from "child_process";
@@ -4754,33 +4849,33 @@ import chalk27 from "chalk";
4754
4849
  import enquirer3 from "enquirer";
4755
4850
 
4756
4851
  // src/commands/deploy/init/updateWorkflow.ts
4757
- import { existsSync as existsSync16, mkdirSync as mkdirSync3, readFileSync as readFileSync12, writeFileSync as writeFileSync12 } from "fs";
4758
- import { dirname as dirname12, join as join11 } from "path";
4759
- import { fileURLToPath as fileURLToPath2 } from "url";
4852
+ import { existsSync as existsSync17, mkdirSync as mkdirSync3, readFileSync as readFileSync13, writeFileSync as writeFileSync12 } from "fs";
4853
+ import { dirname as dirname13, join as join13 } from "path";
4854
+ import { fileURLToPath as fileURLToPath3 } from "url";
4760
4855
  import chalk26 from "chalk";
4761
4856
  var WORKFLOW_PATH = ".github/workflows/build.yml";
4762
- var __dirname2 = dirname12(fileURLToPath2(import.meta.url));
4857
+ var __dirname2 = dirname13(fileURLToPath3(import.meta.url));
4763
4858
  function getExistingSiteId() {
4764
- if (!existsSync16(WORKFLOW_PATH)) {
4859
+ if (!existsSync17(WORKFLOW_PATH)) {
4765
4860
  return null;
4766
4861
  }
4767
- const content = readFileSync12(WORKFLOW_PATH, "utf8");
4862
+ const content = readFileSync13(WORKFLOW_PATH, "utf8");
4768
4863
  const match = content.match(/-s\s+([a-f0-9-]{36})/);
4769
4864
  return match ? match[1] : null;
4770
4865
  }
4771
4866
  function getTemplateContent(siteId) {
4772
- const templatePath = join11(__dirname2, "commands/deploy/build.yml");
4773
- const template = readFileSync12(templatePath, "utf8");
4867
+ const templatePath = join13(__dirname2, "commands/deploy/build.yml");
4868
+ const template = readFileSync13(templatePath, "utf8");
4774
4869
  return template.replace("{{NETLIFY_SITE_ID}}", siteId);
4775
4870
  }
4776
4871
  async function updateWorkflow(siteId) {
4777
4872
  const newContent = getTemplateContent(siteId);
4778
4873
  const workflowDir = ".github/workflows";
4779
- if (!existsSync16(workflowDir)) {
4874
+ if (!existsSync17(workflowDir)) {
4780
4875
  mkdirSync3(workflowDir, { recursive: true });
4781
4876
  }
4782
- if (existsSync16(WORKFLOW_PATH)) {
4783
- const oldContent = readFileSync12(WORKFLOW_PATH, "utf8");
4877
+ if (existsSync17(WORKFLOW_PATH)) {
4878
+ const oldContent = readFileSync13(WORKFLOW_PATH, "utf8");
4784
4879
  if (oldContent === newContent) {
4785
4880
  console.log(chalk26.green("build.yml is already up to date"));
4786
4881
  return;
@@ -4874,11 +4969,11 @@ async function newProject() {
4874
4969
  }
4875
4970
  function addViteBaseConfig() {
4876
4971
  const viteConfigPath = "vite.config.ts";
4877
- if (!existsSync17(viteConfigPath)) {
4972
+ if (!existsSync18(viteConfigPath)) {
4878
4973
  console.log("No vite.config.ts found, skipping base config");
4879
4974
  return;
4880
4975
  }
4881
- const content = readFileSync13(viteConfigPath, "utf8");
4976
+ const content = readFileSync14(viteConfigPath, "utf8");
4882
4977
  if (content.includes("base:")) {
4883
4978
  console.log("vite.config.ts already has base config");
4884
4979
  return;
@@ -5038,7 +5133,9 @@ function renderLineChart({
5038
5133
  seriesTitle,
5039
5134
  labels,
5040
5135
  values,
5041
- wholeNumbersOnly = false
5136
+ wholeNumbersOnly = false,
5137
+ minY,
5138
+ maxY
5042
5139
  }) {
5043
5140
  const input = keyboardInput();
5044
5141
  const screen = blessed.screen({
@@ -5053,7 +5150,9 @@ function renderLineChart({
5053
5150
  legend: { width: Math.max(12, seriesTitle.length + 2) },
5054
5151
  xLabelPadding: 3,
5055
5152
  xPadding: 5,
5056
- wholeNumbersOnly
5153
+ wholeNumbersOnly,
5154
+ minY,
5155
+ maxY
5057
5156
  });
5058
5157
  line.setData([
5059
5158
  {
@@ -5151,55 +5250,6 @@ function adviceContextFor(cwd) {
5151
5250
  };
5152
5251
  }
5153
5252
 
5154
- // src/commands/advise/loadAdviceFragments.ts
5155
- import { readdirSync as readdirSync2, readFileSync as readFileSync14 } from "fs";
5156
- import { basename as basename4, join as join13 } from "path";
5157
-
5158
- // src/commands/advise/adviceDir.ts
5159
- import { existsSync as existsSync18 } from "fs";
5160
- import { dirname as dirname13, join as join12 } from "path";
5161
- import { fileURLToPath as fileURLToPath3 } from "url";
5162
- function adviceDir() {
5163
- let current = dirname13(fileURLToPath3(import.meta.url));
5164
- while (current !== dirname13(current)) {
5165
- const candidate = join12(current, "claude", "advice");
5166
- if (existsSync18(candidate)) return candidate;
5167
- current = dirname13(current);
5168
- }
5169
- throw new Error("Could not locate the shipped claude/advice directory");
5170
- }
5171
-
5172
- // src/commands/advise/parseAdviceFragment.ts
5173
- import { parse as parseYaml2 } from "yaml";
5174
- var frontmatter = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?/;
5175
- function parseAdviceFragment(name, content) {
5176
- const match = frontmatter.exec(content);
5177
- if (!match) throw new Error(`Advice fragment ${name} has no frontmatter`);
5178
- const meta = parseYaml2(match[1]) ?? {};
5179
- const title = meta.title;
5180
- const when = meta.when;
5181
- if (typeof title !== "string" || typeof when !== "string")
5182
- throw new Error(
5183
- `Advice fragment ${name} needs a title and a when condition`
5184
- );
5185
- return {
5186
- name,
5187
- title,
5188
- when,
5189
- body: content.slice(match[0].length).trim()
5190
- };
5191
- }
5192
-
5193
- // src/commands/advise/loadAdviceFragments.ts
5194
- function loadAdviceFragments(dir = adviceDir()) {
5195
- return readdirSync2(dir).filter((file) => file.endsWith(".md")).sort().map(
5196
- (file) => parseAdviceFragment(
5197
- basename4(file, ".md"),
5198
- readFileSync14(join13(dir, file), "utf8")
5199
- )
5200
- );
5201
- }
5202
-
5203
5253
  // src/commands/advise/verifyRunCommandNames.ts
5204
5254
  function verifyRunCommandNames({
5205
5255
  config,
@@ -5268,9 +5318,11 @@ var adviceConditions = {
5268
5318
  // src/commands/advise/selectAdvice.ts
5269
5319
  function decide(fragment, context) {
5270
5320
  const advice = context.config.advice;
5271
- if (advice?.exclude.includes(fragment.name))
5321
+ const exclude = advice?.exclude ?? [];
5322
+ const include = advice?.include ?? [];
5323
+ if (exclude.includes(fragment.name))
5272
5324
  return { fragment, included: false, reason: "excluded by advice.exclude" };
5273
- if (advice?.include.includes(fragment.name))
5325
+ if (include.includes(fragment.name))
5274
5326
  return { fragment, included: true, reason: "included by advice.include" };
5275
5327
  const condition = adviceConditions[fragment.when];
5276
5328
  if (!condition)
@@ -6362,6 +6414,19 @@ function registerBackup(program2) {
6362
6414
  configHelp(backupCommand, backupConfigHelp);
6363
6415
  }
6364
6416
 
6417
+ // src/commands/chart/chartYRange.ts
6418
+ var toCentsWithoutFloatError = (value) => Number((value * 100).toFixed(6));
6419
+ function chartYRange(values) {
6420
+ const min = Math.min(...values);
6421
+ const max = Math.max(...values);
6422
+ const span = max - min;
6423
+ const pad2 = span === 0 ? Math.max(Math.abs(max) * 0.1, 1) : span * 0.2;
6424
+ return {
6425
+ minY: Math.floor(toCentsWithoutFloatError(min - pad2)) / 100,
6426
+ maxY: Math.ceil(toCentsWithoutFloatError(max + pad2)) / 100
6427
+ };
6428
+ }
6429
+
6365
6430
  // src/commands/chart/parseChartSeries.ts
6366
6431
  function parseChartSeries(lines2) {
6367
6432
  const points = [];
@@ -6412,12 +6477,14 @@ async function chart(options2) {
6412
6477
  return;
6413
6478
  }
6414
6479
  const title = options2.title ?? "Chart";
6480
+ const values = points.map((p) => p.value);
6415
6481
  renderLineChart({
6416
6482
  title,
6417
6483
  label: title,
6418
6484
  seriesTitle: title,
6419
6485
  labels: points.map((p) => p.label),
6420
- values: points.map((p) => p.value)
6486
+ values,
6487
+ ...chartYRange(values)
6421
6488
  });
6422
6489
  }
6423
6490
 
@@ -16800,13 +16867,13 @@ function phaseNotes(entry, position) {
16800
16867
  }
16801
16868
  function renderDiffPhase(entry, idx) {
16802
16869
  const notes = phaseNotes(entry, idx + 1);
16803
- const section3 = renderPhaseSection(
16870
+ const section2 = renderPhaseSection(
16804
16871
  { ...entry.phase, name: `${entry.phase.name} (${notes.join(", ")})` },
16805
16872
  idx
16806
16873
  );
16807
- if (!entry.previousTasks) return section3;
16874
+ if (!entry.previousTasks) return section2;
16808
16875
  return [
16809
- section3,
16876
+ section2,
16810
16877
  "**Previously:**",
16811
16878
  entry.previousTasks.map((task) => `- ${task}`).join("\n")
16812
16879
  ].join("\n\n");
@@ -26319,9 +26386,9 @@ function parsePrBody(body) {
26319
26386
  return sections;
26320
26387
  }
26321
26388
  function serializePrBody(sections) {
26322
- return sections.map((section3) => `## ${section3.heading}
26389
+ return sections.map((section2) => `## ${section2.heading}
26323
26390
 
26324
- ${section3.content}`).join("\n\n");
26391
+ ${section2.content}`).join("\n\n");
26325
26392
  }
26326
26393
 
26327
26394
  // src/commands/prs/editPrBody.ts
@@ -26370,11 +26437,11 @@ function findWallOfText(body) {
26370
26437
  }
26371
26438
  function splitParagraphs(body) {
26372
26439
  const paragraphs = [];
26373
- let section3 = "(intro)";
26440
+ let section2 = "(intro)";
26374
26441
  let lines2 = [];
26375
26442
  const flush = () => {
26376
26443
  if (lines2.length > 0) {
26377
- paragraphs.push({ section: section3, lines: lines2 });
26444
+ paragraphs.push({ section: section2, lines: lines2 });
26378
26445
  lines2 = [];
26379
26446
  }
26380
26447
  };
@@ -26382,7 +26449,7 @@ function splitParagraphs(body) {
26382
26449
  const heading2 = line.match(/^#{1,6}\s+(.*)$/);
26383
26450
  if (heading2) {
26384
26451
  flush();
26385
- section3 = heading2[1].trim();
26452
+ section2 = heading2[1].trim();
26386
26453
  } else if (line.trim() === "") {
26387
26454
  flush();
26388
26455
  } else {
@@ -29103,13 +29170,13 @@ function buildPlan2(functionName, sourceFile, sourcePath, destPath, project) {
29103
29170
  // src/commands/refactor/extract/displayPlan.ts
29104
29171
  import path50 from "path";
29105
29172
  import chalk199 from "chalk";
29106
- function section2(title) {
29173
+ function section(title) {
29107
29174
  return `
29108
29175
  ${chalk199.cyan(title)}`;
29109
29176
  }
29110
29177
  function displayImporters(plan2, cwd) {
29111
29178
  if (plan2.importersToUpdate.length === 0) return;
29112
- console.log(section2("Update importers:"));
29179
+ console.log(section("Update importers:"));
29113
29180
  for (const imp of plan2.importersToUpdate) {
29114
29181
  const rel = path50.relative(cwd, imp.file.getFilePath());
29115
29182
  console.log(` ${chalk199.dim(rel)}: \u2192 import from "${imp.relPath}"`);
@@ -29123,18 +29190,18 @@ function displayPlan(functionName, relDest, plan2, cwd) {
29123
29190
  console.log(` ${name}`);
29124
29191
  }
29125
29192
  if (plan2.imports.length > 0) {
29126
- console.log(section2("Imports to copy:"));
29193
+ console.log(section("Imports to copy:"));
29127
29194
  for (const imp of plan2.imports) {
29128
29195
  console.log(` ${formatImportLine(imp)}`);
29129
29196
  }
29130
29197
  }
29131
29198
  if (plan2.exportedDeps.length > 0) {
29132
- console.log(section2("New imports from source:"));
29199
+ console.log(section("New imports from source:"));
29133
29200
  console.log(
29134
29201
  ` import { ${plan2.exportedDeps.join(", ")} } from "${plan2.sourceRelPath}";`
29135
29202
  );
29136
29203
  }
29137
- console.log(section2("Source file changes:"));
29204
+ console.log(section("Source file changes:"));
29138
29205
  console.log(` Remove: ${plan2.extractedNames.join(", ")}`);
29139
29206
  if (plan2.sourceNeedsReimport) {
29140
29207
  console.log(
@@ -29143,7 +29210,7 @@ function displayPlan(functionName, relDest, plan2, cwd) {
29143
29210
  }
29144
29211
  displayImporters(plan2, cwd);
29145
29212
  if (plan2.barrel) {
29146
- console.log(section2("Barrel export:"));
29213
+ console.log(section("Barrel export:"));
29147
29214
  console.log(
29148
29215
  ` Add: export { ${functionName} } from "${plan2.barrelRelPath}";`
29149
29216
  );
@@ -34080,6 +34147,11 @@ var checks = [
34080
34147
  description: "Check every assistConfigSchema key is surfaced in some command's --help via configHelp",
34081
34148
  action: configKeys
34082
34149
  },
34150
+ {
34151
+ name: "advice-fragments",
34152
+ description: "Check ADVICE_FRAGMENT_NAMES matches the fragments shipped in claude/advice, so advice.include/exclude can name every one",
34153
+ action: adviceFragments
34154
+ },
34083
34155
  {
34084
34156
  name: "migrations",
34085
34157
  description: "Check DB migrations are sequentially numbered, append-only, and gate destructive DDL behind an acknowledgement marker",