@staff0rd/assist 0.208.0 → 0.209.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/README.md CHANGED
@@ -181,7 +181,7 @@ After installation, the `assist` command will be available globally. You can als
181
181
  - `assist sql auth remove <name>` - Remove a configured connection
182
182
  - `assist sql set-connection <name>` - Set the default SQL connection
183
183
  - `assist sql query "<sql>" [connection]` - Execute a read-only SQL statement and print results in table format (rejects INSERT/UPDATE/DELETE/DROP/CREATE/ALTER/TRUNCATE/MERGE/GRANT/REVOKE/EXEC)
184
- - `assist sql mutate "<sql>" [connection]` - Execute a mutating SQL statement (not yet implemented)
184
+ - `assist sql mutate "<sql>" [connection]` - Execute a mutating SQL statement and print rows affected (rejects non-mutating statements like pure SELECTs)
185
185
  - `assist sql tables [connection]` - List tables in the connected database (via INFORMATION_SCHEMA.TABLES)
186
186
  - `assist sql columns <table> [connection]` - List columns for a table (use `schema.table` for non-default schema; via INFORMATION_SCHEMA.COLUMNS)
187
187
  - `assist screenshot <process>` - Capture a screenshot of a running application window (e.g. `assist screenshot notepad`). Output directory is configurable via `screenshot.outputDir` (default `./screenshots`)
@@ -19,7 +19,7 @@ The user wants to query a MSSQL database. Use the `assist sql` CLI commands belo
19
19
  ## Querying
20
20
 
21
21
  - `assist sql query "<sql>" [connection]` — execute a read-only SQL statement and print results in table format. Rejects mutating statements (INSERT, UPDATE, DELETE, DROP, CREATE, ALTER, TRUNCATE, MERGE, GRANT, REVOKE, EXEC)
22
- - `assist sql mutate "<sql>" [connection]` — execute a mutating SQL statement (not yet implemented; will throw)
22
+ - `assist sql mutate "<sql>" [connection]` — execute a mutating SQL statement (INSERT, UPDATE, DELETE, DROP, CREATE, ALTER, TRUNCATE, MERGE, GRANT, REVOKE, EXEC) and print rows affected. Rejects non-mutating statements (pure SELECTs, comment-only inputs)
23
23
 
24
24
  ## Workflow
25
25
 
@@ -27,6 +27,6 @@ The user wants to query a MSSQL database. Use the `assist sql` CLI commands belo
27
27
  2. If the user doesn't specify a connection, omit it to use the default.
28
28
  3. Prefer `tables` and `columns` for schema discovery before crafting queries.
29
29
  4. Quote SQL strings carefully — wrap the statement in double quotes and escape inner double quotes if needed.
30
- 5. Use `query` for SELECTs only. If the user asks for a mutation, tell them `assist sql mutate` is not yet implemented rather than working around the guard.
30
+ 5. Use `query` for SELECTs only and `mutate` for mutating statements each command rejects the other kind, so do not work around the guards.
31
31
  6. Display query results to the user. If the output is large, summarise key rows and highlight anything notable.
32
32
  7. If the user asks follow-up questions, refine the query and re-run.
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.208.0",
9
+ version: "0.209.0",
10
10
  type: "module",
11
11
  main: "dist/index.js",
12
12
  bin: {
@@ -11901,11 +11901,6 @@ async function sqlColumns(table, connectionName) {
11901
11901
  }
11902
11902
 
11903
11903
  // src/commands/sql/sqlMutate.ts
11904
- async function sqlMutate(_query, _connectionName) {
11905
- throw new Error("assist sql mutate is not yet implemented");
11906
- }
11907
-
11908
- // src/commands/sql/sqlQuery.ts
11909
11904
  import chalk137 from "chalk";
11910
11905
 
11911
11906
  // src/commands/sql/isMutation.ts
@@ -11936,11 +11931,32 @@ function isMutation(sql2) {
11936
11931
  return /\bSELECT\b[\s\S]+\bINTO\s+\w/i.test(stripped);
11937
11932
  }
11938
11933
 
11934
+ // src/commands/sql/sqlMutate.ts
11935
+ async function sqlMutate(query, connectionName) {
11936
+ if (!isMutation(query)) {
11937
+ console.error(
11938
+ chalk137.red(
11939
+ "assist sql mutate refuses non-mutating statements. Use `assist sql query` instead."
11940
+ )
11941
+ );
11942
+ process.exit(1);
11943
+ }
11944
+ const conn = resolveConnection3(connectionName);
11945
+ const pool = await sqlConnect(conn);
11946
+ try {
11947
+ const result = await pool.request().query(query);
11948
+ console.log(chalk137.dim(`${result.rowsAffected.join(", ")} row(s) affected`));
11949
+ } finally {
11950
+ await pool.close();
11951
+ }
11952
+ }
11953
+
11939
11954
  // src/commands/sql/sqlQuery.ts
11955
+ import chalk138 from "chalk";
11940
11956
  async function sqlQuery(query, connectionName) {
11941
11957
  if (isMutation(query)) {
11942
11958
  console.error(
11943
- chalk137.red(
11959
+ chalk138.red(
11944
11960
  "assist sql query refuses mutating statements. Use `assist sql mutate` instead."
11945
11961
  )
11946
11962
  );
@@ -11955,7 +11971,7 @@ async function sqlQuery(query, connectionName) {
11955
11971
  printTable(rows);
11956
11972
  } else {
11957
11973
  console.log(
11958
- chalk137.dim(`${result.rowsAffected.join(", ")} row(s) affected`)
11974
+ chalk138.dim(`${result.rowsAffected.join(", ")} row(s) affected`)
11959
11975
  );
11960
11976
  }
11961
11977
  } finally {
@@ -12001,7 +12017,9 @@ function registerSql(program2) {
12001
12017
  cmd.command("query <sql> [connection]").description("Execute a read-only SQL query (rejects mutating statements)").action(
12002
12018
  (query, connection) => sqlQuery(query, connection)
12003
12019
  );
12004
- cmd.command("mutate <sql> [connection]").description("Execute a mutating SQL statement (not yet implemented)").action(
12020
+ cmd.command("mutate <sql> [connection]").description(
12021
+ "Execute a mutating SQL statement (rejects non-mutating statements)"
12022
+ ).action(
12005
12023
  (query, connection) => sqlMutate(query, connection)
12006
12024
  );
12007
12025
  cmd.command("tables [connection]").description("List tables in the connected database").action((connection) => sqlTables(connection));
@@ -12533,14 +12551,14 @@ import {
12533
12551
  import { dirname as dirname22, join as join38 } from "path";
12534
12552
 
12535
12553
  // src/commands/transcript/summarise/processStagedFile/validateStagedContent.ts
12536
- import chalk138 from "chalk";
12554
+ import chalk139 from "chalk";
12537
12555
  var FULL_TRANSCRIPT_REGEX = /^\[Full Transcript\]\(([^)]+)\)/;
12538
12556
  function validateStagedContent(filename, content) {
12539
12557
  const firstLine = content.split("\n")[0];
12540
12558
  const match = firstLine.match(FULL_TRANSCRIPT_REGEX);
12541
12559
  if (!match) {
12542
12560
  console.error(
12543
- chalk138.red(
12561
+ chalk139.red(
12544
12562
  `Staged file ${filename} missing [Full Transcript](<path>) link on first line.`
12545
12563
  )
12546
12564
  );
@@ -12549,7 +12567,7 @@ function validateStagedContent(filename, content) {
12549
12567
  const contentAfterLink = content.slice(firstLine.length).trim();
12550
12568
  if (!contentAfterLink) {
12551
12569
  console.error(
12552
- chalk138.red(
12570
+ chalk139.red(
12553
12571
  `Staged file ${filename} has no summary content after the transcript link.`
12554
12572
  )
12555
12573
  );
@@ -12945,7 +12963,7 @@ function registerVoice(program2) {
12945
12963
 
12946
12964
  // src/commands/roam/auth.ts
12947
12965
  import { randomBytes } from "crypto";
12948
- import chalk139 from "chalk";
12966
+ import chalk140 from "chalk";
12949
12967
 
12950
12968
  // src/lib/openBrowser.ts
12951
12969
  import { execSync as execSync37 } from "child_process";
@@ -13120,13 +13138,13 @@ async function auth() {
13120
13138
  saveGlobalConfig(config);
13121
13139
  const state = randomBytes(16).toString("hex");
13122
13140
  console.log(
13123
- chalk139.yellow("\nEnsure this Redirect URI is set in your Roam OAuth app:")
13141
+ chalk140.yellow("\nEnsure this Redirect URI is set in your Roam OAuth app:")
13124
13142
  );
13125
- console.log(chalk139.white("http://localhost:14523/callback\n"));
13126
- console.log(chalk139.blue("Opening browser for authorization..."));
13127
- console.log(chalk139.dim("Waiting for authorization callback..."));
13143
+ console.log(chalk140.white("http://localhost:14523/callback\n"));
13144
+ console.log(chalk140.blue("Opening browser for authorization..."));
13145
+ console.log(chalk140.dim("Waiting for authorization callback..."));
13128
13146
  const { code, redirectUri } = await authorizeInBrowser(clientId, state);
13129
- console.log(chalk139.dim("Exchanging code for tokens..."));
13147
+ console.log(chalk140.dim("Exchanging code for tokens..."));
13130
13148
  const tokens = await exchangeToken({
13131
13149
  code,
13132
13150
  clientId,
@@ -13142,7 +13160,7 @@ async function auth() {
13142
13160
  };
13143
13161
  saveGlobalConfig(config);
13144
13162
  console.log(
13145
- chalk139.green("Roam credentials and tokens saved to ~/.assist.yml")
13163
+ chalk140.green("Roam credentials and tokens saved to ~/.assist.yml")
13146
13164
  );
13147
13165
  }
13148
13166
 
@@ -13555,7 +13573,7 @@ import { execSync as execSync39 } from "child_process";
13555
13573
  import { existsSync as existsSync43, mkdirSync as mkdirSync16, unlinkSync as unlinkSync12, writeFileSync as writeFileSync30 } from "fs";
13556
13574
  import { tmpdir as tmpdir7 } from "os";
13557
13575
  import { join as join49, resolve as resolve13 } from "path";
13558
- import chalk140 from "chalk";
13576
+ import chalk141 from "chalk";
13559
13577
 
13560
13578
  // src/commands/screenshot/captureWindowPs1.ts
13561
13579
  var captureWindowPs1 = `
@@ -13706,20 +13724,20 @@ function screenshot(processName) {
13706
13724
  const config = loadConfig();
13707
13725
  const outputDir = resolve13(config.screenshot.outputDir);
13708
13726
  const outputPath = buildOutputPath(outputDir, processName);
13709
- console.log(chalk140.gray(`Capturing window for process "${processName}" ...`));
13727
+ console.log(chalk141.gray(`Capturing window for process "${processName}" ...`));
13710
13728
  try {
13711
13729
  runPowerShellScript(processName, outputPath);
13712
- console.log(chalk140.green(`Screenshot saved: ${outputPath}`));
13730
+ console.log(chalk141.green(`Screenshot saved: ${outputPath}`));
13713
13731
  } catch (error) {
13714
13732
  const msg = error instanceof Error ? error.message : String(error);
13715
- console.error(chalk140.red(`Failed to capture screenshot: ${msg}`));
13733
+ console.error(chalk141.red(`Failed to capture screenshot: ${msg}`));
13716
13734
  process.exit(1);
13717
13735
  }
13718
13736
  }
13719
13737
 
13720
13738
  // src/commands/sessions/summarise/index.ts
13721
13739
  import * as fs27 from "fs";
13722
- import chalk141 from "chalk";
13740
+ import chalk142 from "chalk";
13723
13741
 
13724
13742
  // src/commands/sessions/summarise/shared.ts
13725
13743
  import * as fs25 from "fs";
@@ -13859,22 +13877,22 @@ ${firstMessage}`);
13859
13877
  async function summarise3(options2) {
13860
13878
  const files = await discoverSessionJsonlPaths();
13861
13879
  if (files.length === 0) {
13862
- console.log(chalk141.yellow("No sessions found."));
13880
+ console.log(chalk142.yellow("No sessions found."));
13863
13881
  return;
13864
13882
  }
13865
13883
  const toProcess = selectCandidates(files, options2);
13866
13884
  if (toProcess.length === 0) {
13867
- console.log(chalk141.green("All sessions already summarised."));
13885
+ console.log(chalk142.green("All sessions already summarised."));
13868
13886
  return;
13869
13887
  }
13870
13888
  console.log(
13871
- chalk141.cyan(
13889
+ chalk142.cyan(
13872
13890
  `Summarising ${toProcess.length} session(s) (${files.length} total)\u2026`
13873
13891
  )
13874
13892
  );
13875
13893
  const { succeeded, failed } = processSessions(toProcess);
13876
13894
  console.log(
13877
- chalk141.green(`Done: ${succeeded} summarised`) + (failed > 0 ? chalk141.yellow(`, ${failed} skipped`) : "")
13895
+ chalk142.green(`Done: ${succeeded} summarised`) + (failed > 0 ? chalk142.yellow(`, ${failed} skipped`) : "")
13878
13896
  );
13879
13897
  }
13880
13898
  function selectCandidates(files, options2) {
@@ -13894,16 +13912,16 @@ function processSessions(files) {
13894
13912
  let failed = 0;
13895
13913
  for (let i = 0; i < files.length; i++) {
13896
13914
  const file = files[i];
13897
- process.stdout.write(chalk141.dim(` [${i + 1}/${files.length}] `));
13915
+ process.stdout.write(chalk142.dim(` [${i + 1}/${files.length}] `));
13898
13916
  const summary = summariseSession(file);
13899
13917
  if (summary) {
13900
13918
  writeSummary(file, summary);
13901
13919
  succeeded++;
13902
- process.stdout.write(`${chalk141.green("\u2713")} ${summary}
13920
+ process.stdout.write(`${chalk142.green("\u2713")} ${summary}
13903
13921
  `);
13904
13922
  } else {
13905
13923
  failed++;
13906
- process.stdout.write(` ${chalk141.yellow("skip")}
13924
+ process.stdout.write(` ${chalk142.yellow("skip")}
13907
13925
  `);
13908
13926
  }
13909
13927
  }
@@ -13918,10 +13936,10 @@ function registerSessions(program2) {
13918
13936
  }
13919
13937
 
13920
13938
  // src/commands/statusLine.ts
13921
- import chalk143 from "chalk";
13939
+ import chalk144 from "chalk";
13922
13940
 
13923
13941
  // src/commands/buildLimitsSegment.ts
13924
- import chalk142 from "chalk";
13942
+ import chalk143 from "chalk";
13925
13943
  var FIVE_HOUR_SECONDS = 5 * 3600;
13926
13944
  var SEVEN_DAY_SECONDS = 7 * 86400;
13927
13945
  function formatTimeLeft(resetsAt) {
@@ -13944,10 +13962,10 @@ function projectUsage(pct, resetsAt, windowSeconds) {
13944
13962
  function colorizeRateLimit(pct, resetsAt, windowSeconds) {
13945
13963
  const label2 = `${Math.round(pct)}%`;
13946
13964
  const projected = projectUsage(pct, resetsAt, windowSeconds);
13947
- if (projected == null) return chalk142.green(label2);
13948
- if (projected > 100) return chalk142.red(label2);
13949
- if (projected > 75) return chalk142.yellow(label2);
13950
- return chalk142.green(label2);
13965
+ if (projected == null) return chalk143.green(label2);
13966
+ if (projected > 100) return chalk143.red(label2);
13967
+ if (projected > 75) return chalk143.yellow(label2);
13968
+ return chalk143.green(label2);
13951
13969
  }
13952
13970
  function formatLimit(pct, resetsAt, windowSeconds, fallbackLabel) {
13953
13971
  const timeLabel = resetsAt ? formatTimeLeft(resetsAt) : fallbackLabel;
@@ -13973,14 +13991,14 @@ function buildLimitsSegment(rateLimits) {
13973
13991
  }
13974
13992
 
13975
13993
  // src/commands/statusLine.ts
13976
- chalk143.level = 3;
13994
+ chalk144.level = 3;
13977
13995
  function formatNumber(num) {
13978
13996
  return num.toLocaleString("en-US");
13979
13997
  }
13980
13998
  function colorizePercent(pct) {
13981
13999
  const label2 = `${Math.round(pct)}%`;
13982
- if (pct > 80) return chalk143.red(label2);
13983
- if (pct > 40) return chalk143.yellow(label2);
14000
+ if (pct > 80) return chalk144.red(label2);
14001
+ if (pct > 40) return chalk144.yellow(label2);
13984
14002
  return label2;
13985
14003
  }
13986
14004
  async function statusLine() {
@@ -14003,7 +14021,7 @@ import { fileURLToPath as fileURLToPath7 } from "url";
14003
14021
  // src/commands/sync/syncClaudeMd.ts
14004
14022
  import * as fs28 from "fs";
14005
14023
  import * as path49 from "path";
14006
- import chalk144 from "chalk";
14024
+ import chalk145 from "chalk";
14007
14025
  async function syncClaudeMd(claudeDir, targetBase, options2) {
14008
14026
  const source = path49.join(claudeDir, "CLAUDE.md");
14009
14027
  const target = path49.join(targetBase, "CLAUDE.md");
@@ -14012,12 +14030,12 @@ async function syncClaudeMd(claudeDir, targetBase, options2) {
14012
14030
  const targetContent = fs28.readFileSync(target, "utf-8");
14013
14031
  if (sourceContent !== targetContent) {
14014
14032
  console.log(
14015
- chalk144.yellow("\n\u26A0\uFE0F Warning: CLAUDE.md differs from existing file")
14033
+ chalk145.yellow("\n\u26A0\uFE0F Warning: CLAUDE.md differs from existing file")
14016
14034
  );
14017
14035
  console.log();
14018
14036
  printDiff(targetContent, sourceContent);
14019
14037
  const confirm = options2?.yes || await promptConfirm(
14020
- chalk144.red("Overwrite existing CLAUDE.md?"),
14038
+ chalk145.red("Overwrite existing CLAUDE.md?"),
14021
14039
  false
14022
14040
  );
14023
14041
  if (!confirm) {
@@ -14033,7 +14051,7 @@ async function syncClaudeMd(claudeDir, targetBase, options2) {
14033
14051
  // src/commands/sync/syncSettings.ts
14034
14052
  import * as fs29 from "fs";
14035
14053
  import * as path50 from "path";
14036
- import chalk145 from "chalk";
14054
+ import chalk146 from "chalk";
14037
14055
  async function syncSettings(claudeDir, targetBase, options2) {
14038
14056
  const source = path50.join(claudeDir, "settings.json");
14039
14057
  const target = path50.join(targetBase, "settings.json");
@@ -14049,14 +14067,14 @@ async function syncSettings(claudeDir, targetBase, options2) {
14049
14067
  if (mergedContent !== normalizedTarget) {
14050
14068
  if (!options2?.yes) {
14051
14069
  console.log(
14052
- chalk145.yellow(
14070
+ chalk146.yellow(
14053
14071
  "\n\u26A0\uFE0F Warning: settings.json differs from existing file"
14054
14072
  )
14055
14073
  );
14056
14074
  console.log();
14057
14075
  printDiff(targetContent, mergedContent);
14058
14076
  const confirm = await promptConfirm(
14059
- chalk145.red("Overwrite existing settings.json?"),
14077
+ chalk146.red("Overwrite existing settings.json?"),
14060
14078
  false
14061
14079
  );
14062
14080
  if (!confirm) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@staff0rd/assist",
3
- "version": "0.208.0",
3
+ "version": "0.209.0",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "bin": {