@staff0rd/assist 0.36.1 → 0.38.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.
@@ -0,0 +1,3 @@
1
+ After any code change, run `/verify` to ensure all checks pass.
2
+
3
+ The tool is invoked using the `assist` command and is installed globally.
package/dist/index.js CHANGED
@@ -7,7 +7,7 @@ import { Command } from "commander";
7
7
  // package.json
8
8
  var package_default = {
9
9
  name: "@staff0rd/assist",
10
- version: "0.36.1",
10
+ version: "0.38.0",
11
11
  type: "module",
12
12
  main: "dist/index.js",
13
13
  bin: {
@@ -560,27 +560,28 @@ async function maintainability(pattern2 = "**/*.ts", options = {}) {
560
560
  }
561
561
  });
562
562
  const results = [];
563
- let hasViolation = false;
563
+ const { threshold } = options;
564
564
  for (const [file, metrics] of fileMetrics) {
565
565
  if (metrics.functions.length === 0) continue;
566
566
  const avgMaintainability = metrics.functions.reduce((a, b) => a + b, 0) / metrics.functions.length;
567
567
  const minMaintainability = Math.min(...metrics.functions);
568
568
  results.push({ file, avgMaintainability, minMaintainability });
569
- if (options.threshold !== void 0 && minMaintainability < options.threshold) {
570
- hasViolation = true;
571
- }
572
569
  }
573
570
  results.sort((a, b) => a.minMaintainability - b.minMaintainability);
574
- for (const { file, avgMaintainability, minMaintainability } of results) {
575
- const exceedsThreshold = options.threshold !== void 0 && minMaintainability < options.threshold;
576
- const color = exceedsThreshold ? chalk5.red : chalk5.white;
577
- console.log(
578
- `${color(file)} \u2192 avg: ${chalk5.cyan(avgMaintainability.toFixed(1))}, min: ${chalk5.yellow(minMaintainability.toFixed(1))}`
579
- );
571
+ const filtered = threshold !== void 0 ? results.filter((r) => r.minMaintainability < threshold) : results;
572
+ if (threshold !== void 0 && filtered.length === 0) {
573
+ console.log(chalk5.green("All files pass maintainability threshold"));
574
+ } else {
575
+ for (const { file, avgMaintainability, minMaintainability } of filtered) {
576
+ const color = threshold !== void 0 ? chalk5.red : chalk5.white;
577
+ console.log(
578
+ `${color(file)} \u2192 avg: ${chalk5.cyan(avgMaintainability.toFixed(1))}, min: ${chalk5.yellow(minMaintainability.toFixed(1))}`
579
+ );
580
+ }
580
581
  }
581
582
  console.log(chalk5.dim(`
582
583
  Analyzed ${results.length} files`));
583
- if (hasViolation) {
584
+ if (filtered.length > 0 && threshold !== void 0) {
584
585
  process.exit(1);
585
586
  }
586
587
  });
@@ -621,8 +622,8 @@ Total: ${total} lines across ${files.length} files`)
621
622
  // src/commands/config/index.ts
622
623
  import chalk7 from "chalk";
623
624
  import { stringify as stringifyYaml2 } from "yaml";
624
- function getNestedValue(obj, path18) {
625
- const keys = path18.split(".");
625
+ function getNestedValue(obj, path19) {
626
+ const keys = path19.split(".");
626
627
  let current = obj;
627
628
  for (const key of keys) {
628
629
  if (current === null || current === void 0 || typeof current !== "object") {
@@ -632,8 +633,8 @@ function getNestedValue(obj, path18) {
632
633
  }
633
634
  return current;
634
635
  }
635
- function setNestedValue(obj, path18, value) {
636
- const keys = path18.split(".");
636
+ function setNestedValue(obj, path19, value) {
637
+ const keys = path19.split(".");
637
638
  const result = { ...obj };
638
639
  let current = result;
639
640
  for (let i = 0; i < keys.length - 1; i++) {
@@ -660,8 +661,8 @@ function configSet(key, value) {
660
661
  const result = assistConfigSchema.safeParse(updated);
661
662
  if (!result.success) {
662
663
  for (const issue of result.error.issues) {
663
- const path18 = issue.path.length > 0 ? issue.path.join(".") : key;
664
- console.error(chalk7.red(`${path18}: ${issue.message}`));
664
+ const path19 = issue.path.length > 0 ? issue.path.join(".") : key;
665
+ console.error(chalk7.red(`${path19}: ${issue.message}`));
665
666
  }
666
667
  process.exit(1);
667
668
  }
@@ -2949,31 +2950,61 @@ async function statusLine() {
2949
2950
  }
2950
2951
 
2951
2952
  // src/commands/sync.ts
2952
- import * as fs17 from "fs";
2953
+ import * as fs18 from "fs";
2953
2954
  import * as os from "os";
2954
- import * as path16 from "path";
2955
+ import * as path17 from "path";
2955
2956
  import { fileURLToPath as fileURLToPath3 } from "url";
2956
2957
 
2957
- // src/commands/sync/syncSettings.ts
2958
+ // src/commands/sync/syncClaudeMd.ts
2958
2959
  import * as fs16 from "fs";
2959
2960
  import * as path15 from "path";
2960
2961
  import chalk35 from "chalk";
2961
- async function syncSettings(claudeDir, targetBase) {
2962
- const source = path15.join(claudeDir, "settings.json");
2963
- const target = path15.join(targetBase, "settings.json");
2962
+ async function syncClaudeMd(claudeDir, targetBase) {
2963
+ const source = path15.join(claudeDir, "CLAUDE.md");
2964
+ const target = path15.join(targetBase, "CLAUDE.md");
2964
2965
  const sourceContent = fs16.readFileSync(source, "utf-8");
2965
- const normalizedSource = JSON.stringify(JSON.parse(sourceContent), null, 2);
2966
2966
  if (fs16.existsSync(target)) {
2967
2967
  const targetContent = fs16.readFileSync(target, "utf-8");
2968
+ if (sourceContent !== targetContent) {
2969
+ console.log(
2970
+ chalk35.yellow("\n\u26A0\uFE0F Warning: CLAUDE.md differs from existing file")
2971
+ );
2972
+ console.log();
2973
+ printDiff(targetContent, sourceContent);
2974
+ const confirm = await promptConfirm(
2975
+ chalk35.red("Overwrite existing CLAUDE.md?"),
2976
+ false
2977
+ );
2978
+ if (!confirm) {
2979
+ console.log("Skipped CLAUDE.md");
2980
+ return;
2981
+ }
2982
+ }
2983
+ }
2984
+ fs16.copyFileSync(source, target);
2985
+ console.log("Copied CLAUDE.md to ~/.claude/CLAUDE.md");
2986
+ }
2987
+
2988
+ // src/commands/sync/syncSettings.ts
2989
+ import * as fs17 from "fs";
2990
+ import * as path16 from "path";
2991
+ import chalk36 from "chalk";
2992
+ async function syncSettings(claudeDir, targetBase) {
2993
+ const source = path16.join(claudeDir, "settings.json");
2994
+ const target = path16.join(targetBase, "settings.json");
2995
+ const sourceContent = fs17.readFileSync(source, "utf-8");
2996
+ const normalizedSource = JSON.stringify(JSON.parse(sourceContent), null, 2);
2997
+ if (fs17.existsSync(target)) {
2998
+ const targetContent = fs17.readFileSync(target, "utf-8");
2968
2999
  const normalizedTarget = JSON.stringify(JSON.parse(targetContent), null, 2);
2969
3000
  if (normalizedSource !== normalizedTarget) {
2970
3001
  console.log(
2971
- chalk35.yellow("\n\u26A0\uFE0F Warning: settings.json differs from existing file")
3002
+ chalk36.yellow("\n\u26A0\uFE0F Warning: settings.json differs from existing file")
2972
3003
  );
2973
3004
  console.log();
2974
3005
  printDiff(targetContent, sourceContent);
2975
3006
  const confirm = await promptConfirm(
2976
- chalk35.red("Overwrite existing settings.json?"),
3007
+ chalk36.red("Overwrite existing settings.json?"),
2977
3008
  false
2978
3009
  );
2979
3010
  if (!confirm) {
@@ -2982,34 +3013,35 @@ async function syncSettings(claudeDir, targetBase) {
2982
3013
  }
2983
3014
  }
2984
3015
  }
2985
- fs16.copyFileSync(source, target);
3016
+ fs17.copyFileSync(source, target);
2986
3017
  console.log("Copied settings.json to ~/.claude/settings.json");
2987
3018
  }
2988
3019
 
2989
3020
  // src/commands/sync.ts
2990
3021
  var __filename2 = fileURLToPath3(import.meta.url);
2991
- var __dirname4 = path16.dirname(__filename2);
3022
+ var __dirname4 = path17.dirname(__filename2);
2992
3023
  async function sync() {
2993
- const claudeDir = path16.join(__dirname4, "..", "claude");
2994
- const targetBase = path16.join(os.homedir(), ".claude");
3024
+ const claudeDir = path17.join(__dirname4, "..", "claude");
3025
+ const targetBase = path17.join(os.homedir(), ".claude");
2995
3026
  syncCommands(claudeDir, targetBase);
2996
3027
  await syncSettings(claudeDir, targetBase);
3028
+ await syncClaudeMd(claudeDir, targetBase);
2997
3029
  }
2998
3030
  function syncCommands(claudeDir, targetBase) {
2999
- const sourceDir = path16.join(claudeDir, "commands");
3000
- const targetDir = path16.join(targetBase, "commands");
3001
- fs17.mkdirSync(targetDir, { recursive: true });
3002
- const files = fs17.readdirSync(sourceDir);
3031
+ const sourceDir = path17.join(claudeDir, "commands");
3032
+ const targetDir = path17.join(targetBase, "commands");
3033
+ fs18.mkdirSync(targetDir, { recursive: true });
3034
+ const files = fs18.readdirSync(sourceDir);
3003
3035
  for (const file of files) {
3004
- fs17.copyFileSync(path16.join(sourceDir, file), path16.join(targetDir, file));
3036
+ fs18.copyFileSync(path17.join(sourceDir, file), path17.join(targetDir, file));
3005
3037
  console.log(`Copied ${file} to ${targetDir}`);
3006
3038
  }
3007
3039
  console.log(`Synced ${files.length} command(s) to ~/.claude/commands`);
3008
3040
  }
3009
3041
 
3010
3042
  // src/commands/transcript/shared.ts
3011
- import { existsSync as existsSync14, readdirSync as readdirSync3, statSync } from "fs";
3012
- import { basename as basename3, join as join13, relative } from "path";
3043
+ import { existsSync as existsSync15, readdirSync as readdirSync3, statSync } from "fs";
3044
+ import { basename as basename3, join as join14, relative } from "path";
3013
3045
  import * as readline2 from "readline";
3014
3046
  var DATE_PREFIX_REGEX = /^\d{4}-\d{2}-\d{2}/;
3015
3047
  function getDatePrefix(daysOffset = 0) {
@@ -3024,13 +3056,13 @@ function isValidDatePrefix(filename) {
3024
3056
  return DATE_PREFIX_REGEX.test(filename);
3025
3057
  }
3026
3058
  function findFilesRecursive(dir, baseDir, extension, createEntry) {
3027
- if (!existsSync14(dir)) {
3059
+ if (!existsSync15(dir)) {
3028
3060
  return [];
3029
3061
  }
3030
3062
  const results = [];
3031
3063
  const entries = readdirSync3(dir);
3032
3064
  for (const entry of entries) {
3033
- const fullPath = join13(dir, entry);
3065
+ const fullPath = join14(dir, entry);
3034
3066
  const stat = statSync(fullPath);
3035
3067
  if (stat.isDirectory()) {
3036
3068
  results.push(
@@ -3120,8 +3152,8 @@ async function configure() {
3120
3152
  }
3121
3153
 
3122
3154
  // src/commands/transcript/format.ts
3123
- import { existsSync as existsSync15, mkdirSync as mkdirSync5, readFileSync as readFileSync13, writeFileSync as writeFileSync11 } from "fs";
3124
- import { basename as basename4, dirname as dirname11, join as join15 } from "path";
3155
+ import { existsSync as existsSync16, mkdirSync as mkdirSync5, readFileSync as readFileSync14, writeFileSync as writeFileSync11 } from "fs";
3156
+ import { basename as basename4, dirname as dirname11, join as join16 } from "path";
3125
3157
 
3126
3158
  // src/commands/transcript/parseVtt.ts
3127
3159
  function parseTimestamp(ts2) {
@@ -3283,7 +3315,7 @@ function formatChatLog(messages) {
3283
3315
 
3284
3316
  // src/commands/transcript/promptForDateFix.ts
3285
3317
  import { renameSync } from "fs";
3286
- import { join as join14 } from "path";
3318
+ import { join as join15 } from "path";
3287
3319
  async function promptForDateFix(vttFile, vttDir) {
3288
3320
  const rl = createReadlineInterface();
3289
3321
  console.log(
@@ -3324,8 +3356,8 @@ Error: File "${vttFile}" does not start with YYYY-MM-DD format.`
3324
3356
  rl.close();
3325
3357
  if (newPrefix) {
3326
3358
  const newFilename = `${newPrefix}.${vttFile}`;
3327
- const oldPath = join14(vttDir, vttFile);
3328
- const newPath = join14(vttDir, newFilename);
3359
+ const oldPath = join15(vttDir, vttFile);
3360
+ const newPath = join15(vttDir, newFilename);
3329
3361
  renameSync(oldPath, newPath);
3330
3362
  console.log(`Renamed to: ${newFilename}`);
3331
3363
  return newFilename;
@@ -3340,7 +3372,7 @@ Error: File "${vttFile}" does not start with YYYY-MM-DD format.`
3340
3372
  // src/commands/transcript/format.ts
3341
3373
  function processFile(inputPath, outputPath) {
3342
3374
  console.log(`Reading: ${inputPath}`);
3343
- const content = readFileSync13(inputPath, "utf-8");
3375
+ const content = readFileSync14(inputPath, "utf-8");
3344
3376
  const cues = parseVtt(content);
3345
3377
  console.log(`Parsed ${cues.length} cues`);
3346
3378
  const dedupedCues = deduplicateCues(cues);
@@ -3357,11 +3389,11 @@ function processFile(inputPath, outputPath) {
3357
3389
  }
3358
3390
  async function format() {
3359
3391
  const { vttDir, transcriptsDir } = getTranscriptConfig();
3360
- if (!existsSync15(vttDir)) {
3392
+ if (!existsSync16(vttDir)) {
3361
3393
  console.error(`VTT directory not found: ${vttDir}`);
3362
3394
  process.exit(1);
3363
3395
  }
3364
- if (!existsSync15(transcriptsDir)) {
3396
+ if (!existsSync16(transcriptsDir)) {
3365
3397
  mkdirSync5(transcriptsDir, { recursive: true });
3366
3398
  console.log(`Created output directory: ${transcriptsDir}`);
3367
3399
  }
@@ -3378,12 +3410,12 @@ async function format() {
3378
3410
  const vttFileDir = dirname11(vttFile.absolutePath);
3379
3411
  const newFilename = await promptForDateFix(vttFile.filename, vttFileDir);
3380
3412
  if (newFilename) {
3381
- const newRelativePath = join15(
3413
+ const newRelativePath = join16(
3382
3414
  dirname11(vttFile.relativePath),
3383
3415
  newFilename
3384
3416
  );
3385
3417
  vttFiles[i] = {
3386
- absolutePath: join15(vttFileDir, newFilename),
3418
+ absolutePath: join16(vttFileDir, newFilename),
3387
3419
  relativePath: newRelativePath,
3388
3420
  filename: newFilename
3389
3421
  };
@@ -3400,14 +3432,14 @@ async function format() {
3400
3432
  baseName = baseName.replace(/\s*Transcription\s*/g, " ").trim();
3401
3433
  const mdFile = `${baseName}.md`;
3402
3434
  const relativeDir = dirname11(vttFile.relativePath);
3403
- const outputDir = relativeDir === "." ? transcriptsDir : join15(transcriptsDir, relativeDir);
3404
- const outputPath = join15(outputDir, mdFile);
3405
- if (!existsSync15(outputDir)) {
3435
+ const outputDir = relativeDir === "." ? transcriptsDir : join16(transcriptsDir, relativeDir);
3436
+ const outputPath = join16(outputDir, mdFile);
3437
+ if (!existsSync16(outputDir)) {
3406
3438
  mkdirSync5(outputDir, { recursive: true });
3407
3439
  console.log(`Created output directory: ${outputDir}`);
3408
3440
  }
3409
- if (existsSync15(outputPath)) {
3410
- console.log(`Skipping (already exists): ${join15(relativeDir, mdFile)}`);
3441
+ if (existsSync16(outputPath)) {
3442
+ console.log(`Skipping (already exists): ${join16(relativeDir, mdFile)}`);
3411
3443
  skipped++;
3412
3444
  continue;
3413
3445
  }
@@ -3420,18 +3452,18 @@ Summary: ${processed} processed, ${skipped} skipped`);
3420
3452
 
3421
3453
  // src/commands/transcript/summarise.ts
3422
3454
  import {
3423
- existsSync as existsSync16,
3455
+ existsSync as existsSync17,
3424
3456
  mkdirSync as mkdirSync6,
3425
- readFileSync as readFileSync14,
3457
+ readFileSync as readFileSync15,
3426
3458
  renameSync as renameSync2,
3427
3459
  rmSync
3428
3460
  } from "fs";
3429
- import { basename as basename5, dirname as dirname12, join as join16, relative as relative2 } from "path";
3430
- import chalk36 from "chalk";
3431
- var STAGING_DIR = join16(process.cwd(), ".assist", "transcript");
3461
+ import { basename as basename5, dirname as dirname12, join as join17, relative as relative2 } from "path";
3462
+ import chalk37 from "chalk";
3463
+ var STAGING_DIR = join17(process.cwd(), ".assist", "transcript");
3432
3464
  var FULL_TRANSCRIPT_REGEX = /^\[Full Transcript\]\(([^)]+)\)/;
3433
3465
  function processStagedFile() {
3434
- if (!existsSync16(STAGING_DIR)) {
3466
+ if (!existsSync17(STAGING_DIR)) {
3435
3467
  return false;
3436
3468
  }
3437
3469
  const stagedFiles = findMdFilesRecursive(STAGING_DIR);
@@ -3440,12 +3472,12 @@ function processStagedFile() {
3440
3472
  }
3441
3473
  const { transcriptsDir, summaryDir } = getTranscriptConfig();
3442
3474
  const stagedFile = stagedFiles[0];
3443
- const content = readFileSync14(stagedFile.absolutePath, "utf-8");
3475
+ const content = readFileSync15(stagedFile.absolutePath, "utf-8");
3444
3476
  const firstLine = content.split("\n")[0];
3445
3477
  const match = firstLine.match(FULL_TRANSCRIPT_REGEX);
3446
3478
  if (!match) {
3447
3479
  console.error(
3448
- chalk36.red(
3480
+ chalk37.red(
3449
3481
  `Staged file ${stagedFile.filename} missing [Full Transcript](<path>) link on first line.`
3450
3482
  )
3451
3483
  );
@@ -3454,7 +3486,7 @@ function processStagedFile() {
3454
3486
  const contentAfterLink = content.slice(firstLine.length).trim();
3455
3487
  if (!contentAfterLink) {
3456
3488
  console.error(
3457
- chalk36.red(
3489
+ chalk37.red(
3458
3490
  `Staged file ${stagedFile.filename} has no summary content after the transcript link.`
3459
3491
  )
3460
3492
  );
@@ -3467,16 +3499,16 @@ function processStagedFile() {
3467
3499
  );
3468
3500
  if (!matchingTranscript) {
3469
3501
  console.error(
3470
- chalk36.red(
3502
+ chalk37.red(
3471
3503
  `No transcript found matching staged file: ${stagedFile.filename}`
3472
3504
  )
3473
3505
  );
3474
3506
  process.exit(1);
3475
3507
  }
3476
3508
  const relativePath = matchingTranscript.relativePath;
3477
- const destPath = join16(summaryDir, relativePath);
3509
+ const destPath = join17(summaryDir, relativePath);
3478
3510
  const destDir = dirname12(destPath);
3479
- if (!existsSync16(destDir)) {
3511
+ if (!existsSync17(destDir)) {
3480
3512
  mkdirSync6(destDir, { recursive: true });
3481
3513
  }
3482
3514
  renameSync2(stagedFile.absolutePath, destPath);
@@ -3489,7 +3521,7 @@ function processStagedFile() {
3489
3521
  function summarise() {
3490
3522
  processStagedFile();
3491
3523
  const { transcriptsDir, summaryDir } = getTranscriptConfig();
3492
- if (!existsSync16(transcriptsDir)) {
3524
+ if (!existsSync17(transcriptsDir)) {
3493
3525
  console.log("No transcripts directory found.");
3494
3526
  return;
3495
3527
  }
@@ -3503,14 +3535,14 @@ function summarise() {
3503
3535
  summaryFiles.map((f) => {
3504
3536
  const relDir = dirname12(f.relativePath);
3505
3537
  const baseName = basename5(f.filename, ".md");
3506
- return relDir === "." ? baseName : join16(relDir, baseName);
3538
+ return relDir === "." ? baseName : join17(relDir, baseName);
3507
3539
  })
3508
3540
  );
3509
3541
  const missing = [];
3510
3542
  for (const transcript of transcriptFiles) {
3511
3543
  const transcriptBaseName = getTranscriptBaseName(transcript.filename);
3512
3544
  const relDir = dirname12(transcript.relativePath);
3513
- const fullKey = relDir === "." ? transcriptBaseName : join16(relDir, transcriptBaseName);
3545
+ const fullKey = relDir === "." ? transcriptBaseName : join17(relDir, transcriptBaseName);
3514
3546
  if (!summaryRelativePaths.has(fullKey)) {
3515
3547
  missing.push(transcript);
3516
3548
  }
@@ -3521,8 +3553,8 @@ function summarise() {
3521
3553
  }
3522
3554
  const next2 = missing[0];
3523
3555
  const outputFilename = `${getTranscriptBaseName(next2.filename)}.md`;
3524
- const outputPath = join16(STAGING_DIR, outputFilename);
3525
- const summaryFileDir = join16(summaryDir, dirname12(next2.relativePath));
3556
+ const outputPath = join17(STAGING_DIR, outputFilename);
3557
+ const summaryFileDir = join17(summaryDir, dirname12(next2.relativePath));
3526
3558
  const relativeTranscriptPath = encodeURI(
3527
3559
  relative2(summaryFileDir, next2.absolutePath).replace(/\\/g, "/")
3528
3560
  );
@@ -3573,7 +3605,7 @@ Total: ${lines.length} hardcoded color(s)`);
3573
3605
 
3574
3606
  // src/commands/verify/run.ts
3575
3607
  import { spawn as spawn4 } from "child_process";
3576
- import * as path17 from "path";
3608
+ import * as path18 from "path";
3577
3609
  function formatDuration(ms) {
3578
3610
  if (ms < 1e3) {
3579
3611
  return `${ms}ms`;
@@ -3603,7 +3635,7 @@ async function run2(options = {}) {
3603
3635
  return;
3604
3636
  }
3605
3637
  const { packageJsonPath, verifyScripts } = result;
3606
- const packageDir = path17.dirname(packageJsonPath);
3638
+ const packageDir = path18.dirname(packageJsonPath);
3607
3639
  console.log(`Running ${verifyScripts.length} verify script(s) in parallel:`);
3608
3640
  for (const script of verifyScripts) {
3609
3641
  console.log(` - ${script}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@staff0rd/assist",
3
- "version": "0.36.1",
3
+ "version": "0.38.0",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "bin": {