@staff0rd/assist 0.207.1 → 0.208.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
@@ -56,6 +56,7 @@ After installation, the `assist` command will be available globally. You can als
56
56
  - `/screenshot` - Capture a screenshot of a running application window
57
57
  - `/raven` - Query and manage RavenDB connections and collections
58
58
  - `/seq` - Query Seq logs from a URL or filter expression
59
+ - `/sql` - Query a MSSQL database via assist sql
59
60
  - `/verify` - Run all verification commands in parallel
60
61
  - `/transcript-format` - Format meeting transcripts from VTT files
61
62
  - `/transcript-summarise` - Summarise transcripts missing summaries
@@ -175,6 +176,14 @@ After installation, the `assist` command will be available globally. You can als
175
176
  - `assist seq query <filter> -n <count>` - Fetch a specific number of events (default 50)
176
177
  - `assist seq query <filter> --from <date>` - Start of query window (UTC date or relative e.g. 5m, 1h, 2d)
177
178
  - `assist seq query <filter> --to <date>` - End of query window (UTC date or relative e.g. 5m, 1h, 2d)
179
+ - `assist sql auth add` - Add a new MSSQL connection (prompts for name, server, port, user, password, database)
180
+ - `assist sql auth list` - List configured SQL connections
181
+ - `assist sql auth remove <name>` - Remove a configured connection
182
+ - `assist sql set-connection <name>` - Set the default SQL connection
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)
185
+ - `assist sql tables [connection]` - List tables in the connected database (via INFORMATION_SCHEMA.TABLES)
186
+ - `assist sql columns <table> [connection]` - List columns for a table (use `schema.table` for non-default schema; via INFORMATION_SCHEMA.COLUMNS)
178
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`)
179
188
  - `assist mermaid export [file.md]` - Render each fenced mermaid block to `<stem>-<index>.svg` via [Kroki](https://kroki.io). With no file, scans `*.md` in the current directory (non-recursive). Use `--out <dir>` to override the output directory. Use `--index <n>` to render only the nth mermaid block (1-based; requires a file argument). Endpoint is configurable via `mermaid.krokiUrl` (default `https://kroki.io`).
180
189
  - `assist prompts` - Show top 10 denied tool calls by frequency with count and repo breakdown
package/allowed.cli-reads CHANGED
@@ -41,6 +41,10 @@ assist roam show-claude-code-icon
41
41
  assist screenshot
42
42
  assist seq auth list
43
43
  assist seq query
44
+ assist sql auth list
45
+ assist sql columns
46
+ assist sql query
47
+ assist sql tables
44
48
  assist status-line
45
49
  assist verify
46
50
  assist voice
@@ -0,0 +1,32 @@
1
+ ---
2
+ description: Query a MSSQL database via assist sql
3
+ ---
4
+
5
+ The user wants to query a MSSQL database. Use the `assist sql` CLI commands below.
6
+
7
+ ## Connection management
8
+
9
+ - `assist sql auth add` — interactively add a named connection (name, server, port, user, password, database)
10
+ - `assist sql auth list` — list configured connections
11
+ - `assist sql auth remove <name>` — remove a connection
12
+ - `assist sql set-connection <name>` — set the default connection
13
+
14
+ ## Schema introspection
15
+
16
+ - `assist sql tables [connection]` — list tables in the database
17
+ - `assist sql columns <table> [connection]` — list columns for a table (use `schema.table` for a non-default schema)
18
+
19
+ ## Querying
20
+
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)
23
+
24
+ ## Workflow
25
+
26
+ 1. If no connections are configured, tell the user to run `assist sql auth add`. Do NOT attempt to add connections yourself.
27
+ 2. If the user doesn't specify a connection, omit it to use the default.
28
+ 3. Prefer `tables` and `columns` for schema discovery before crafting queries.
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.
31
+ 6. Display query results to the user. If the output is large, summarise key rows and highlight anything notable.
32
+ 7. If the user asks follow-up questions, refine the query and re-run.
@@ -55,6 +55,7 @@
55
55
  "SlashCommand(/jira)",
56
56
  "Skill(seq)",
57
57
  "SlashCommand(/seq)",
58
+ "SlashCommand(/sql)",
58
59
  "Skill(inspect)",
59
60
  "SlashCommand(/inspect)",
60
61
  "Skill(prompts)",
@@ -41,6 +41,10 @@ assist roam show-claude-code-icon
41
41
  assist screenshot
42
42
  assist seq auth list
43
43
  assist seq query
44
+ assist sql auth list
45
+ assist sql columns
46
+ assist sql query
47
+ assist sql tables
44
48
  assist status-line
45
49
  assist verify
46
50
  assist voice
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.207.1",
9
+ version: "0.208.0",
10
10
  type: "module",
11
11
  main: "dist/index.js",
12
12
  bin: {
@@ -48,6 +48,7 @@ var package_default = {
48
48
  entities: "^7.0.1",
49
49
  "is-wsl": "^3.1.0",
50
50
  minimatch: "^10.1.1",
51
+ mssql: "^12.5.0",
51
52
  "node-notifier": "^10.0.1",
52
53
  "node-pty": "^1.1.0",
53
54
  semver: "^7.7.3",
@@ -69,6 +70,7 @@ var package_default = {
69
70
  "@semantic-release/git": "^10.0.1",
70
71
  "@types/better-sqlite3": "^7.6.13",
71
72
  "@types/blessed": "^0.1.27",
73
+ "@types/mssql": "^12.3.0",
72
74
  "@types/node": "^24.10.1",
73
75
  "@types/node-notifier": "^8.0.5",
74
76
  "@types/react": "^19.2.14",
@@ -1787,6 +1789,19 @@ var assistConfigSchema = z3.strictObject({
1787
1789
  ).default([]),
1788
1790
  defaultConnection: z3.string().optional()
1789
1791
  }).optional(),
1792
+ sql: z3.strictObject({
1793
+ connections: z3.array(
1794
+ z3.strictObject({
1795
+ name: z3.string(),
1796
+ server: z3.string(),
1797
+ port: z3.number(),
1798
+ user: z3.string(),
1799
+ password: z3.string(),
1800
+ database: z3.string()
1801
+ })
1802
+ ).default([]),
1803
+ defaultConnection: z3.string().optional()
1804
+ }).optional(),
1790
1805
  screenshot: z3.strictObject({
1791
1806
  outputDir: z3.string().default("./screenshots")
1792
1807
  }).default({ outputDir: "./screenshots" }),
@@ -11452,14 +11467,19 @@ function setDefaultConnection(name) {
11452
11467
  saveGlobalConfig(raw);
11453
11468
  }
11454
11469
 
11455
- // src/commands/seq/promptConnection.ts
11470
+ // src/shared/assertUniqueName.ts
11456
11471
  import chalk127 from "chalk";
11457
- async function promptConnection2(existingNames) {
11458
- const name = await promptInput("name", "Connection name:", "default");
11472
+ function assertUniqueName(existingNames, name) {
11459
11473
  if (existingNames.includes(name)) {
11460
11474
  console.error(chalk127.red(`Connection "${name}" already exists.`));
11461
11475
  process.exit(1);
11462
11476
  }
11477
+ }
11478
+
11479
+ // src/commands/seq/promptConnection.ts
11480
+ async function promptConnection2(existingNames) {
11481
+ const name = await promptInput("name", "Connection name:", "default");
11482
+ assertUniqueName(existingNames, name);
11463
11483
  const url = await promptInput("url", "Seq URL:", "http://localhost:5341");
11464
11484
  const apiToken = await promptPassword("apiToken", "API token:");
11465
11485
  return { name, url, apiToken };
@@ -11503,8 +11523,8 @@ function filterToSql(filter) {
11503
11523
  // src/commands/seq/fetchSeqData.ts
11504
11524
  async function fetchSeqData(conn, filter, count, from, to) {
11505
11525
  const sqlFilter = filterToSql(filter);
11506
- const sql = `select @Timestamp, @Level, @Exception, @Message from stream where ${sqlFilter} order by @Timestamp desc limit ${count}`;
11507
- const params = new URLSearchParams({ q: sql });
11526
+ const sql2 = `select @Timestamp, @Level, @Exception, @Message from stream where ${sqlFilter} order by @Timestamp desc limit ${count}`;
11527
+ const params = new URLSearchParams({ q: sql2 });
11508
11528
  if (from) params.set("fromDateUtc", from);
11509
11529
  if (to) params.set("toDateUtc", to);
11510
11530
  const response = await fetchSeq(conn, "/api/data", params);
@@ -11638,25 +11658,37 @@ function rejectTimestampFilter(filter) {
11638
11658
  }
11639
11659
  }
11640
11660
 
11641
- // src/commands/seq/resolveConnection.ts
11661
+ // src/shared/resolveNamedConnection.ts
11642
11662
  import chalk131 from "chalk";
11643
- function resolveConnection2(name) {
11644
- const connections = loadConnections2();
11663
+ function resolveNamedConnection(connections, requested, defaultName, kind, authCommand) {
11645
11664
  if (connections.length === 0) {
11646
11665
  console.error(
11647
- chalk131.red("No Seq connections configured. Run 'assist seq auth' first.")
11666
+ chalk131.red(
11667
+ `No ${kind} connections configured. Run '${authCommand}' first.`
11668
+ )
11648
11669
  );
11649
11670
  process.exit(1);
11650
11671
  }
11651
- const target = name ?? getDefaultConnection() ?? connections[0].name;
11672
+ const target = requested ?? defaultName ?? connections[0].name;
11652
11673
  const connection = connections.find((c) => c.name === target);
11653
11674
  if (!connection) {
11654
- console.error(chalk131.red(`Seq connection "${target}" not found.`));
11675
+ console.error(chalk131.red(`${kind} connection "${target}" not found.`));
11655
11676
  process.exit(1);
11656
11677
  }
11657
11678
  return connection;
11658
11679
  }
11659
11680
 
11681
+ // src/commands/seq/resolveConnection.ts
11682
+ function resolveConnection2(name) {
11683
+ return resolveNamedConnection(
11684
+ loadConnections2(),
11685
+ name,
11686
+ getDefaultConnection(),
11687
+ "Seq",
11688
+ "assist seq auth"
11689
+ );
11690
+ }
11691
+
11660
11692
  // src/commands/seq/seqQuery.ts
11661
11693
  async function seqQuery(filter, options2) {
11662
11694
  rejectTimestampFilter(filter);
@@ -11691,16 +11723,25 @@ ${events.length} events`));
11691
11723
  }
11692
11724
  }
11693
11725
 
11694
- // src/commands/seq/seqSetConnection.ts
11726
+ // src/shared/setNamedDefaultConnection.ts
11695
11727
  import chalk133 from "chalk";
11696
- function seqSetConnection(name) {
11697
- const connections = loadConnections2();
11728
+ function setNamedDefaultConnection(connections, name, setDefault, kind) {
11698
11729
  if (!connections.find((c) => c.name === name)) {
11699
11730
  console.error(chalk133.red(`Connection "${name}" not found.`));
11700
11731
  process.exit(1);
11701
11732
  }
11702
- setDefaultConnection(name);
11703
- console.log(`Default Seq connection set to "${name}".`);
11733
+ setDefault(name);
11734
+ console.log(`Default ${kind} connection set to "${name}".`);
11735
+ }
11736
+
11737
+ // src/commands/seq/seqSetConnection.ts
11738
+ function seqSetConnection(name) {
11739
+ setNamedDefaultConnection(
11740
+ loadConnections2(),
11741
+ name,
11742
+ setDefaultConnection,
11743
+ "Seq"
11744
+ );
11704
11745
  }
11705
11746
 
11706
11747
  // src/commands/registerSeq.ts
@@ -11720,6 +11761,257 @@ function registerSeq(program2) {
11720
11761
  ).option("--json", "Output raw JSON").action((filter, options2) => seqQuery(filter, options2));
11721
11762
  }
11722
11763
 
11764
+ // src/commands/sql/sqlAuth.ts
11765
+ import chalk135 from "chalk";
11766
+
11767
+ // src/commands/sql/loadConnections.ts
11768
+ function loadConnections3() {
11769
+ const raw = loadGlobalConfigRaw();
11770
+ const sql2 = raw.sql;
11771
+ return sql2?.connections ?? [];
11772
+ }
11773
+ function saveConnections3(connections) {
11774
+ const raw = loadGlobalConfigRaw();
11775
+ const sql2 = raw.sql ?? {};
11776
+ sql2.connections = connections;
11777
+ raw.sql = sql2;
11778
+ saveGlobalConfig(raw);
11779
+ }
11780
+ function getDefaultConnection2() {
11781
+ const raw = loadGlobalConfigRaw();
11782
+ const sql2 = raw.sql;
11783
+ return sql2?.defaultConnection;
11784
+ }
11785
+ function setDefaultConnection2(name) {
11786
+ const raw = loadGlobalConfigRaw();
11787
+ const sql2 = raw.sql ?? {};
11788
+ sql2.defaultConnection = name;
11789
+ raw.sql = sql2;
11790
+ saveGlobalConfig(raw);
11791
+ }
11792
+
11793
+ // src/commands/sql/promptConnection.ts
11794
+ import chalk134 from "chalk";
11795
+ async function promptConnection3(existingNames) {
11796
+ const name = await promptInput("name", "Connection name:", "default");
11797
+ assertUniqueName(existingNames, name);
11798
+ const server = await promptInput("server", "Server:", "localhost");
11799
+ const portStr = await promptInput("port", "Port:", "1433");
11800
+ const port = Number.parseInt(portStr, 10);
11801
+ if (!Number.isFinite(port)) {
11802
+ console.error(chalk134.red(`Invalid port "${portStr}".`));
11803
+ process.exit(1);
11804
+ }
11805
+ const user = await promptInput("user", "User:");
11806
+ const password = await promptPassword("password", "Password:");
11807
+ const database = await promptInput("database", "Database:");
11808
+ return { name, server, port, user, password, database };
11809
+ }
11810
+
11811
+ // src/commands/sql/sqlAuth.ts
11812
+ var sqlAuth = createConnectionAuth({
11813
+ load: loadConnections3,
11814
+ save: saveConnections3,
11815
+ format: (c) => `${chalk135.bold(c.name)} ${c.server}:${c.port}/${c.database} (${c.user})`,
11816
+ promptNew: promptConnection3,
11817
+ onFirst: (c) => setDefaultConnection2(c.name)
11818
+ });
11819
+
11820
+ // src/commands/sql/printTable.ts
11821
+ import chalk136 from "chalk";
11822
+ function formatCell(value) {
11823
+ if (value === null || value === void 0) return "";
11824
+ if (value instanceof Date) return value.toISOString();
11825
+ if (typeof value === "object") return JSON.stringify(value);
11826
+ return String(value);
11827
+ }
11828
+ function printTable(rows) {
11829
+ if (rows.length === 0) {
11830
+ console.log(chalk136.yellow("(no rows)"));
11831
+ return;
11832
+ }
11833
+ const columns = Object.keys(rows[0]);
11834
+ const widths = columns.map(
11835
+ (col) => Math.max(col.length, ...rows.map((r) => formatCell(r[col]).length))
11836
+ );
11837
+ const header = columns.map((c, i) => c.padEnd(widths[i])).join(" ");
11838
+ console.log(chalk136.dim(header));
11839
+ console.log(chalk136.dim("-".repeat(header.length)));
11840
+ for (const row of rows) {
11841
+ const line = columns.map((c, i) => formatCell(row[c]).padEnd(widths[i])).join(" ");
11842
+ console.log(line);
11843
+ }
11844
+ console.log(chalk136.dim(`
11845
+ ${rows.length} row${rows.length === 1 ? "" : "s"}`));
11846
+ }
11847
+
11848
+ // src/commands/sql/resolveConnection.ts
11849
+ function resolveConnection3(name) {
11850
+ return resolveNamedConnection(
11851
+ loadConnections3(),
11852
+ name,
11853
+ getDefaultConnection2(),
11854
+ "SQL",
11855
+ "assist sql auth add"
11856
+ );
11857
+ }
11858
+
11859
+ // src/commands/sql/sqlConnect.ts
11860
+ import sql from "mssql";
11861
+ async function sqlConnect(conn) {
11862
+ return await sql.connect({
11863
+ server: conn.server,
11864
+ port: conn.port,
11865
+ user: conn.user,
11866
+ password: conn.password,
11867
+ database: conn.database,
11868
+ options: {
11869
+ encrypt: false,
11870
+ trustServerCertificate: true
11871
+ }
11872
+ });
11873
+ }
11874
+
11875
+ // src/commands/sql/sqlColumns.ts
11876
+ async function sqlColumns(table, connectionName) {
11877
+ const conn = resolveConnection3(connectionName);
11878
+ const pool = await sqlConnect(conn);
11879
+ try {
11880
+ const parts = table.split(".");
11881
+ const schema = parts.length > 1 ? parts[0] : void 0;
11882
+ const name = parts.length > 1 ? parts[1] : parts[0];
11883
+ const request = pool.request().input("table", name);
11884
+ let where = "TABLE_NAME = @table";
11885
+ if (schema) {
11886
+ request.input("schema", schema);
11887
+ where += " AND TABLE_SCHEMA = @schema";
11888
+ }
11889
+ const result = await request.query(
11890
+ `SELECT TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME, ORDINAL_POSITION,
11891
+ DATA_TYPE, CHARACTER_MAXIMUM_LENGTH, IS_NULLABLE, COLUMN_DEFAULT
11892
+ FROM INFORMATION_SCHEMA.COLUMNS
11893
+ WHERE ${where}
11894
+ ORDER BY TABLE_SCHEMA, TABLE_NAME, ORDINAL_POSITION`
11895
+ );
11896
+ const rows = result.recordset ?? [];
11897
+ printTable(rows);
11898
+ } finally {
11899
+ await pool.close();
11900
+ }
11901
+ }
11902
+
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
+ import chalk137 from "chalk";
11910
+
11911
+ // src/commands/sql/isMutation.ts
11912
+ var MUTATION_KEYWORDS = [
11913
+ "INSERT",
11914
+ "UPDATE",
11915
+ "DELETE",
11916
+ "DROP",
11917
+ "CREATE",
11918
+ "ALTER",
11919
+ "TRUNCATE",
11920
+ "MERGE",
11921
+ "GRANT",
11922
+ "REVOKE",
11923
+ "EXEC",
11924
+ "EXECUTE"
11925
+ ];
11926
+ var MUTATION_PATTERN = new RegExp(
11927
+ `\\b(${MUTATION_KEYWORDS.join("|")})\\b`,
11928
+ "i"
11929
+ );
11930
+ function stripComments(sql2) {
11931
+ return sql2.replace(/\/\*[\s\S]*?\*\//g, " ").replace(/--[^\n]*/g, " ");
11932
+ }
11933
+ function isMutation(sql2) {
11934
+ const stripped = stripComments(sql2);
11935
+ if (MUTATION_PATTERN.test(stripped)) return true;
11936
+ return /\bSELECT\b[\s\S]+\bINTO\s+\w/i.test(stripped);
11937
+ }
11938
+
11939
+ // src/commands/sql/sqlQuery.ts
11940
+ async function sqlQuery(query, connectionName) {
11941
+ if (isMutation(query)) {
11942
+ console.error(
11943
+ chalk137.red(
11944
+ "assist sql query refuses mutating statements. Use `assist sql mutate` instead."
11945
+ )
11946
+ );
11947
+ process.exit(1);
11948
+ }
11949
+ const conn = resolveConnection3(connectionName);
11950
+ const pool = await sqlConnect(conn);
11951
+ try {
11952
+ const result = await pool.request().query(query);
11953
+ const rows = result.recordset ?? [];
11954
+ if (result.recordset) {
11955
+ printTable(rows);
11956
+ } else {
11957
+ console.log(
11958
+ chalk137.dim(`${result.rowsAffected.join(", ")} row(s) affected`)
11959
+ );
11960
+ }
11961
+ } finally {
11962
+ await pool.close();
11963
+ }
11964
+ }
11965
+
11966
+ // src/commands/sql/sqlSetConnection.ts
11967
+ function sqlSetConnection(name) {
11968
+ setNamedDefaultConnection(
11969
+ loadConnections3(),
11970
+ name,
11971
+ setDefaultConnection2,
11972
+ "SQL"
11973
+ );
11974
+ }
11975
+
11976
+ // src/commands/sql/sqlTables.ts
11977
+ async function sqlTables(connectionName) {
11978
+ const conn = resolveConnection3(connectionName);
11979
+ const pool = await sqlConnect(conn);
11980
+ try {
11981
+ const result = await pool.request().query(
11982
+ `SELECT TABLE_SCHEMA, TABLE_NAME, TABLE_TYPE
11983
+ FROM INFORMATION_SCHEMA.TABLES
11984
+ ORDER BY TABLE_SCHEMA, TABLE_NAME`
11985
+ );
11986
+ const rows = result.recordset ?? [];
11987
+ printTable(rows);
11988
+ } finally {
11989
+ await pool.close();
11990
+ }
11991
+ }
11992
+
11993
+ // src/commands/registerSql.ts
11994
+ function registerSql(program2) {
11995
+ const cmd = program2.command("sql").description("MSSQL query utilities");
11996
+ const auth2 = cmd.command("auth").description("Configure a SQL connection");
11997
+ auth2.command("add").description("Add a new connection").action(() => sqlAuth.add());
11998
+ auth2.command("list").description("List configured connections").action(() => sqlAuth.list());
11999
+ auth2.command("remove <name>").description("Remove a configured connection").action((name) => sqlAuth.remove(name));
12000
+ cmd.command("set-connection <name>").description("Set the default SQL connection").action((name) => sqlSetConnection(name));
12001
+ cmd.command("query <sql> [connection]").description("Execute a read-only SQL query (rejects mutating statements)").action(
12002
+ (query, connection) => sqlQuery(query, connection)
12003
+ );
12004
+ cmd.command("mutate <sql> [connection]").description("Execute a mutating SQL statement (not yet implemented)").action(
12005
+ (query, connection) => sqlMutate(query, connection)
12006
+ );
12007
+ cmd.command("tables [connection]").description("List tables in the connected database").action((connection) => sqlTables(connection));
12008
+ cmd.command("columns <table> [connection]").description(
12009
+ "List columns for a table (use schema.table for non-default schema)"
12010
+ ).action(
12011
+ (table, connection) => sqlColumns(table, connection)
12012
+ );
12013
+ }
12014
+
11723
12015
  // src/commands/transcript/shared.ts
11724
12016
  import { existsSync as existsSync32, readdirSync as readdirSync6, statSync as statSync4 } from "fs";
11725
12017
  import { basename as basename8, join as join34, relative as relative2 } from "path";
@@ -12241,14 +12533,14 @@ import {
12241
12533
  import { dirname as dirname22, join as join38 } from "path";
12242
12534
 
12243
12535
  // src/commands/transcript/summarise/processStagedFile/validateStagedContent.ts
12244
- import chalk134 from "chalk";
12536
+ import chalk138 from "chalk";
12245
12537
  var FULL_TRANSCRIPT_REGEX = /^\[Full Transcript\]\(([^)]+)\)/;
12246
12538
  function validateStagedContent(filename, content) {
12247
12539
  const firstLine = content.split("\n")[0];
12248
12540
  const match = firstLine.match(FULL_TRANSCRIPT_REGEX);
12249
12541
  if (!match) {
12250
12542
  console.error(
12251
- chalk134.red(
12543
+ chalk138.red(
12252
12544
  `Staged file ${filename} missing [Full Transcript](<path>) link on first line.`
12253
12545
  )
12254
12546
  );
@@ -12257,7 +12549,7 @@ function validateStagedContent(filename, content) {
12257
12549
  const contentAfterLink = content.slice(firstLine.length).trim();
12258
12550
  if (!contentAfterLink) {
12259
12551
  console.error(
12260
- chalk134.red(
12552
+ chalk138.red(
12261
12553
  `Staged file ${filename} has no summary content after the transcript link.`
12262
12554
  )
12263
12555
  );
@@ -12653,7 +12945,7 @@ function registerVoice(program2) {
12653
12945
 
12654
12946
  // src/commands/roam/auth.ts
12655
12947
  import { randomBytes } from "crypto";
12656
- import chalk135 from "chalk";
12948
+ import chalk139 from "chalk";
12657
12949
 
12658
12950
  // src/lib/openBrowser.ts
12659
12951
  import { execSync as execSync37 } from "child_process";
@@ -12828,13 +13120,13 @@ async function auth() {
12828
13120
  saveGlobalConfig(config);
12829
13121
  const state = randomBytes(16).toString("hex");
12830
13122
  console.log(
12831
- chalk135.yellow("\nEnsure this Redirect URI is set in your Roam OAuth app:")
13123
+ chalk139.yellow("\nEnsure this Redirect URI is set in your Roam OAuth app:")
12832
13124
  );
12833
- console.log(chalk135.white("http://localhost:14523/callback\n"));
12834
- console.log(chalk135.blue("Opening browser for authorization..."));
12835
- console.log(chalk135.dim("Waiting for authorization callback..."));
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..."));
12836
13128
  const { code, redirectUri } = await authorizeInBrowser(clientId, state);
12837
- console.log(chalk135.dim("Exchanging code for tokens..."));
13129
+ console.log(chalk139.dim("Exchanging code for tokens..."));
12838
13130
  const tokens = await exchangeToken({
12839
13131
  code,
12840
13132
  clientId,
@@ -12850,7 +13142,7 @@ async function auth() {
12850
13142
  };
12851
13143
  saveGlobalConfig(config);
12852
13144
  console.log(
12853
- chalk135.green("Roam credentials and tokens saved to ~/.assist.yml")
13145
+ chalk139.green("Roam credentials and tokens saved to ~/.assist.yml")
12854
13146
  );
12855
13147
  }
12856
13148
 
@@ -13263,7 +13555,7 @@ import { execSync as execSync39 } from "child_process";
13263
13555
  import { existsSync as existsSync43, mkdirSync as mkdirSync16, unlinkSync as unlinkSync12, writeFileSync as writeFileSync30 } from "fs";
13264
13556
  import { tmpdir as tmpdir7 } from "os";
13265
13557
  import { join as join49, resolve as resolve13 } from "path";
13266
- import chalk136 from "chalk";
13558
+ import chalk140 from "chalk";
13267
13559
 
13268
13560
  // src/commands/screenshot/captureWindowPs1.ts
13269
13561
  var captureWindowPs1 = `
@@ -13414,20 +13706,20 @@ function screenshot(processName) {
13414
13706
  const config = loadConfig();
13415
13707
  const outputDir = resolve13(config.screenshot.outputDir);
13416
13708
  const outputPath = buildOutputPath(outputDir, processName);
13417
- console.log(chalk136.gray(`Capturing window for process "${processName}" ...`));
13709
+ console.log(chalk140.gray(`Capturing window for process "${processName}" ...`));
13418
13710
  try {
13419
13711
  runPowerShellScript(processName, outputPath);
13420
- console.log(chalk136.green(`Screenshot saved: ${outputPath}`));
13712
+ console.log(chalk140.green(`Screenshot saved: ${outputPath}`));
13421
13713
  } catch (error) {
13422
13714
  const msg = error instanceof Error ? error.message : String(error);
13423
- console.error(chalk136.red(`Failed to capture screenshot: ${msg}`));
13715
+ console.error(chalk140.red(`Failed to capture screenshot: ${msg}`));
13424
13716
  process.exit(1);
13425
13717
  }
13426
13718
  }
13427
13719
 
13428
13720
  // src/commands/sessions/summarise/index.ts
13429
13721
  import * as fs27 from "fs";
13430
- import chalk137 from "chalk";
13722
+ import chalk141 from "chalk";
13431
13723
 
13432
13724
  // src/commands/sessions/summarise/shared.ts
13433
13725
  import * as fs25 from "fs";
@@ -13567,22 +13859,22 @@ ${firstMessage}`);
13567
13859
  async function summarise3(options2) {
13568
13860
  const files = await discoverSessionJsonlPaths();
13569
13861
  if (files.length === 0) {
13570
- console.log(chalk137.yellow("No sessions found."));
13862
+ console.log(chalk141.yellow("No sessions found."));
13571
13863
  return;
13572
13864
  }
13573
13865
  const toProcess = selectCandidates(files, options2);
13574
13866
  if (toProcess.length === 0) {
13575
- console.log(chalk137.green("All sessions already summarised."));
13867
+ console.log(chalk141.green("All sessions already summarised."));
13576
13868
  return;
13577
13869
  }
13578
13870
  console.log(
13579
- chalk137.cyan(
13871
+ chalk141.cyan(
13580
13872
  `Summarising ${toProcess.length} session(s) (${files.length} total)\u2026`
13581
13873
  )
13582
13874
  );
13583
13875
  const { succeeded, failed } = processSessions(toProcess);
13584
13876
  console.log(
13585
- chalk137.green(`Done: ${succeeded} summarised`) + (failed > 0 ? chalk137.yellow(`, ${failed} skipped`) : "")
13877
+ chalk141.green(`Done: ${succeeded} summarised`) + (failed > 0 ? chalk141.yellow(`, ${failed} skipped`) : "")
13586
13878
  );
13587
13879
  }
13588
13880
  function selectCandidates(files, options2) {
@@ -13602,16 +13894,16 @@ function processSessions(files) {
13602
13894
  let failed = 0;
13603
13895
  for (let i = 0; i < files.length; i++) {
13604
13896
  const file = files[i];
13605
- process.stdout.write(chalk137.dim(` [${i + 1}/${files.length}] `));
13897
+ process.stdout.write(chalk141.dim(` [${i + 1}/${files.length}] `));
13606
13898
  const summary = summariseSession(file);
13607
13899
  if (summary) {
13608
13900
  writeSummary(file, summary);
13609
13901
  succeeded++;
13610
- process.stdout.write(`${chalk137.green("\u2713")} ${summary}
13902
+ process.stdout.write(`${chalk141.green("\u2713")} ${summary}
13611
13903
  `);
13612
13904
  } else {
13613
13905
  failed++;
13614
- process.stdout.write(` ${chalk137.yellow("skip")}
13906
+ process.stdout.write(` ${chalk141.yellow("skip")}
13615
13907
  `);
13616
13908
  }
13617
13909
  }
@@ -13626,10 +13918,10 @@ function registerSessions(program2) {
13626
13918
  }
13627
13919
 
13628
13920
  // src/commands/statusLine.ts
13629
- import chalk139 from "chalk";
13921
+ import chalk143 from "chalk";
13630
13922
 
13631
13923
  // src/commands/buildLimitsSegment.ts
13632
- import chalk138 from "chalk";
13924
+ import chalk142 from "chalk";
13633
13925
  var FIVE_HOUR_SECONDS = 5 * 3600;
13634
13926
  var SEVEN_DAY_SECONDS = 7 * 86400;
13635
13927
  function formatTimeLeft(resetsAt) {
@@ -13652,10 +13944,10 @@ function projectUsage(pct, resetsAt, windowSeconds) {
13652
13944
  function colorizeRateLimit(pct, resetsAt, windowSeconds) {
13653
13945
  const label2 = `${Math.round(pct)}%`;
13654
13946
  const projected = projectUsage(pct, resetsAt, windowSeconds);
13655
- if (projected == null) return chalk138.green(label2);
13656
- if (projected > 100) return chalk138.red(label2);
13657
- if (projected > 75) return chalk138.yellow(label2);
13658
- return chalk138.green(label2);
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);
13659
13951
  }
13660
13952
  function formatLimit(pct, resetsAt, windowSeconds, fallbackLabel) {
13661
13953
  const timeLabel = resetsAt ? formatTimeLeft(resetsAt) : fallbackLabel;
@@ -13681,14 +13973,14 @@ function buildLimitsSegment(rateLimits) {
13681
13973
  }
13682
13974
 
13683
13975
  // src/commands/statusLine.ts
13684
- chalk139.level = 3;
13976
+ chalk143.level = 3;
13685
13977
  function formatNumber(num) {
13686
13978
  return num.toLocaleString("en-US");
13687
13979
  }
13688
13980
  function colorizePercent(pct) {
13689
13981
  const label2 = `${Math.round(pct)}%`;
13690
- if (pct > 80) return chalk139.red(label2);
13691
- if (pct > 40) return chalk139.yellow(label2);
13982
+ if (pct > 80) return chalk143.red(label2);
13983
+ if (pct > 40) return chalk143.yellow(label2);
13692
13984
  return label2;
13693
13985
  }
13694
13986
  async function statusLine() {
@@ -13711,7 +14003,7 @@ import { fileURLToPath as fileURLToPath7 } from "url";
13711
14003
  // src/commands/sync/syncClaudeMd.ts
13712
14004
  import * as fs28 from "fs";
13713
14005
  import * as path49 from "path";
13714
- import chalk140 from "chalk";
14006
+ import chalk144 from "chalk";
13715
14007
  async function syncClaudeMd(claudeDir, targetBase, options2) {
13716
14008
  const source = path49.join(claudeDir, "CLAUDE.md");
13717
14009
  const target = path49.join(targetBase, "CLAUDE.md");
@@ -13720,12 +14012,12 @@ async function syncClaudeMd(claudeDir, targetBase, options2) {
13720
14012
  const targetContent = fs28.readFileSync(target, "utf-8");
13721
14013
  if (sourceContent !== targetContent) {
13722
14014
  console.log(
13723
- chalk140.yellow("\n\u26A0\uFE0F Warning: CLAUDE.md differs from existing file")
14015
+ chalk144.yellow("\n\u26A0\uFE0F Warning: CLAUDE.md differs from existing file")
13724
14016
  );
13725
14017
  console.log();
13726
14018
  printDiff(targetContent, sourceContent);
13727
14019
  const confirm = options2?.yes || await promptConfirm(
13728
- chalk140.red("Overwrite existing CLAUDE.md?"),
14020
+ chalk144.red("Overwrite existing CLAUDE.md?"),
13729
14021
  false
13730
14022
  );
13731
14023
  if (!confirm) {
@@ -13741,7 +14033,7 @@ async function syncClaudeMd(claudeDir, targetBase, options2) {
13741
14033
  // src/commands/sync/syncSettings.ts
13742
14034
  import * as fs29 from "fs";
13743
14035
  import * as path50 from "path";
13744
- import chalk141 from "chalk";
14036
+ import chalk145 from "chalk";
13745
14037
  async function syncSettings(claudeDir, targetBase, options2) {
13746
14038
  const source = path50.join(claudeDir, "settings.json");
13747
14039
  const target = path50.join(targetBase, "settings.json");
@@ -13757,14 +14049,14 @@ async function syncSettings(claudeDir, targetBase, options2) {
13757
14049
  if (mergedContent !== normalizedTarget) {
13758
14050
  if (!options2?.yes) {
13759
14051
  console.log(
13760
- chalk141.yellow(
14052
+ chalk145.yellow(
13761
14053
  "\n\u26A0\uFE0F Warning: settings.json differs from existing file"
13762
14054
  )
13763
14055
  );
13764
14056
  console.log();
13765
14057
  printDiff(targetContent, mergedContent);
13766
14058
  const confirm = await promptConfirm(
13767
- chalk141.red("Overwrite existing settings.json?"),
14059
+ chalk145.red("Overwrite existing settings.json?"),
13768
14060
  false
13769
14061
  );
13770
14062
  if (!confirm) {
@@ -13878,6 +14170,7 @@ registerDotnet(program);
13878
14170
  registerNews(program);
13879
14171
  registerRavendb(program);
13880
14172
  registerSeq(program);
14173
+ registerSql(program);
13881
14174
  registerTranscript(program);
13882
14175
  registerVoice(program);
13883
14176
  registerSessions(program);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@staff0rd/assist",
3
- "version": "0.207.1",
3
+ "version": "0.208.0",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
@@ -42,6 +42,7 @@
42
42
  "entities": "^7.0.1",
43
43
  "is-wsl": "^3.1.0",
44
44
  "minimatch": "^10.1.1",
45
+ "mssql": "^12.5.0",
45
46
  "node-notifier": "^10.0.1",
46
47
  "node-pty": "^1.1.0",
47
48
  "semver": "^7.7.3",
@@ -63,6 +64,7 @@
63
64
  "@semantic-release/git": "^10.0.1",
64
65
  "@types/better-sqlite3": "^7.6.13",
65
66
  "@types/blessed": "^0.1.27",
67
+ "@types/mssql": "^12.3.0",
66
68
  "@types/node": "^24.10.1",
67
69
  "@types/node-notifier": "^8.0.5",
68
70
  "@types/react": "^19.2.14",