@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 +1 -1
- package/claude/commands/sql.md +2 -2
- package/dist/index.js +64 -46
- package/package.json +1 -1
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 (
|
|
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`)
|
package/claude/commands/sql.md
CHANGED
|
@@ -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 (
|
|
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
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
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(
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
13141
|
+
chalk140.yellow("\nEnsure this Redirect URI is set in your Roam OAuth app:")
|
|
13124
13142
|
);
|
|
13125
|
-
console.log(
|
|
13126
|
-
console.log(
|
|
13127
|
-
console.log(
|
|
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(
|
|
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
|
-
|
|
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
|
|
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(
|
|
13727
|
+
console.log(chalk141.gray(`Capturing window for process "${processName}" ...`));
|
|
13710
13728
|
try {
|
|
13711
13729
|
runPowerShellScript(processName, outputPath);
|
|
13712
|
-
console.log(
|
|
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(
|
|
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
|
|
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(
|
|
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(
|
|
13885
|
+
console.log(chalk142.green("All sessions already summarised."));
|
|
13868
13886
|
return;
|
|
13869
13887
|
}
|
|
13870
13888
|
console.log(
|
|
13871
|
-
|
|
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
|
-
|
|
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(
|
|
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(`${
|
|
13920
|
+
process.stdout.write(`${chalk142.green("\u2713")} ${summary}
|
|
13903
13921
|
`);
|
|
13904
13922
|
} else {
|
|
13905
13923
|
failed++;
|
|
13906
|
-
process.stdout.write(` ${
|
|
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
|
|
13939
|
+
import chalk144 from "chalk";
|
|
13922
13940
|
|
|
13923
13941
|
// src/commands/buildLimitsSegment.ts
|
|
13924
|
-
import
|
|
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
|
|
13948
|
-
if (projected > 100) return
|
|
13949
|
-
if (projected > 75) return
|
|
13950
|
-
return
|
|
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
|
-
|
|
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
|
|
13983
|
-
if (pct > 40) return
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
14077
|
+
chalk146.red("Overwrite existing settings.json?"),
|
|
14060
14078
|
false
|
|
14061
14079
|
);
|
|
14062
14080
|
if (!confirm) {
|