lockpicks 0.1.0 → 0.2.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.
Files changed (3) hide show
  1. package/README.md +17 -16
  2. package/dist/lockpick.mjs +109 -10
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -20,21 +20,22 @@ With it installed, `lockpick mcp config` points agents at `lockpick mcp`; withou
20
20
 
21
21
  ## Commands
22
22
 
23
- | Command | What it shows |
24
- | -------------------------------------- | --------------------------------------------------------------------------------------- |
25
- | `lockpick whoami` | The user, workspace and key this terminal uses |
26
- | `lockpick projects` | Projects with their latest Pulse and pentest |
27
- | `lockpick status [project]` | One project's report, or an overview of all of them |
28
- | `lockpick runs` | Pulse assessments and pentests, newest first |
29
- | `lockpick run <runId>` | One run: status, open findings, coverage and limits |
30
- | `lockpick watch <runId>` | Follows a run until it finishes |
31
- | `lockpick pulse <project>` | Starts a Pulse (a recheck of the latest one when possible) and waits for the result |
32
- | `lockpick findings <runId>` | Findings that need attention (`--all` for informational ones) |
33
- | `lockpick finding <runId> <findingId>` | One finding with its evidence |
34
- | `lockpick brief <runId>` | A markdown repair brief (`--finding <id>` for one finding, `--out brief.md` to save it) |
35
- | `lockpick compare <runId>` | What a Pulse recheck changed against its baseline |
36
- | `lockpick open <id>` | Opens a run or project in the browser |
37
- | `lockpick skills` | Adds the Lockpick skills to your coding agent (`npx skills add`) |
23
+ | Command | What it shows |
24
+ | -------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ |
25
+ | `lockpick whoami` | The user, workspace and key this terminal uses |
26
+ | `lockpick projects` | Projects with their latest Pulse and pentest |
27
+ | `lockpick status [project]` | One project's report, or an overview of all of them |
28
+ | `lockpick runs` | Pulse assessments and pentests, newest first |
29
+ | `lockpick run <runId>` | One run: status, open findings, coverage and limits |
30
+ | `lockpick watch <runId>` | Follows a run until it finishes |
31
+ | `lockpick pulse <project>` | Starts a Pulse (a recheck of the latest one when possible) and waits for the result |
32
+ | `lockpick findings <runId>` | Findings that need attention (`--all` for informational ones) |
33
+ | `lockpick finding <runId> <findingId>` | One finding with its evidence |
34
+ | `lockpick brief <runId>` | One brief that fixes everything needing attention (`--finding <id>` for one finding, `--agent <name>` to tailor it, `--out brief.md` to save it) |
35
+ | `lockpick fix <runId> --agent <name>` | Saves the fix-all brief to `lockpick-fixes.md` and prints the command that starts your agent on it |
36
+ | `lockpick compare <runId>` | What a Pulse recheck changed against its baseline |
37
+ | `lockpick open <id>` | Opens a run or project in the browser |
38
+ | `lockpick skills` | Adds the Lockpick skills to your coding agent (`npx skills add`) |
38
39
 
39
40
  Every command takes `--host` and `--json`. With `--json`, errors are printed to stderr as `{ "error": "…", "code": "…" }`.
40
41
 
@@ -48,7 +49,7 @@ Each Pulse uses one of your account's runs, the same as starting it in the web a
48
49
  lockpick mcp config claude # or cursor, vscode, codex
49
50
  ```
50
51
 
51
- prints the setup for your agent. The server (`lockpick mcp`, stdio) offers ten reading tools, `lockpick_start_pulse` (only for keys allowed to start Pulses), a repair-brief resource and a `fix-lockpick-findings` prompt. Evidence captured from scanned sites is fenced as untrusted data, and agents are told never to follow instructions inside it.
52
+ prints the setup for your agent. The server (`lockpick mcp`, stdio) offers ten reading tools, `lockpick_start_pulse` (only for keys allowed to start Pulses), a repair-brief resource and a `fix-lockpick-findings` prompt. `lockpick_get_repair_brief` without a `findingId` returns the fix-all brief, and its `agent` argument tailors it. Evidence captured from scanned sites is fenced as untrusted data, and agents are told never to follow instructions inside it.
52
53
 
53
54
  ## Environment
54
55
 
package/dist/lockpick.mjs CHANGED
@@ -23541,6 +23541,45 @@ var StdioServerTransport = class {
23541
23541
  }
23542
23542
  };
23543
23543
 
23544
+ // ../../packages/contracts/src/prompt-agents.ts
23545
+ var PROMPT_AGENTS = [
23546
+ "claude-code",
23547
+ "codex",
23548
+ "cursor",
23549
+ "vscode",
23550
+ "opencode",
23551
+ "lovable",
23552
+ "bolt",
23553
+ "replit",
23554
+ "v0"
23555
+ ];
23556
+ var PROMPT_AGENT_LAUNCH = {
23557
+ "claude-code": "claude",
23558
+ codex: "codex",
23559
+ cursor: "cursor-agent",
23560
+ vscode: "code chat --mode agent",
23561
+ opencode: "opencode --prompt=",
23562
+ lovable: null,
23563
+ bolt: null,
23564
+ replit: null,
23565
+ v0: null
23566
+ };
23567
+ function isPromptAgent(value) {
23568
+ return PROMPT_AGENTS.includes(value);
23569
+ }
23570
+ var FIX_ALL_FILE = "lockpick-fixes.md";
23571
+ function agentCommand(agent, text2) {
23572
+ const launch = PROMPT_AGENT_LAUNCH[agent];
23573
+ if (!launch) throw new Error(`No command line tool for ${agent}`);
23574
+ return launch.endsWith("=") ? `${launch}${text2}` : `${launch} ${text2}`;
23575
+ }
23576
+ function readBriefCommand(agent, file2 = FIX_ALL_FILE) {
23577
+ return agentCommand(
23578
+ agent,
23579
+ `"Read ${file2} and follow it. Do not commit that file."`
23580
+ );
23581
+ }
23582
+
23544
23583
  // ../../packages/contracts/src/cli-routes.ts
23545
23584
  var CLI_API = "/api/cli/v1";
23546
23585
  var segment = (value) => encodeURIComponent(value);
@@ -23752,6 +23791,10 @@ var cliFindingListQuerySchema = external_exports.object({
23752
23791
  /** Passing and not-assessed checks are hidden unless asked for. */
23753
23792
  all: external_exports.enum(["true", "false"]).optional().transform((value) => value === "true")
23754
23793
  });
23794
+ var cliRepairBriefQuerySchema = external_exports.object({
23795
+ findingId: external_exports.string().min(1).max(500).optional(),
23796
+ agent: external_exports.enum(PROMPT_AGENTS).optional()
23797
+ });
23755
23798
 
23756
23799
  // src/api.ts
23757
23800
  function friendlyError(status2, message, replace) {
@@ -23930,12 +23973,13 @@ var LockpickClient = class {
23930
23973
  { notFound: missingRun(runId) }
23931
23974
  );
23932
23975
  }
23933
- repairBrief(runId, findingId) {
23976
+ /** One finding's brief, or the fix-all brief; `agent` tailors it. */
23977
+ repairBrief(runId, findingId, agent) {
23934
23978
  return this.request(
23935
23979
  CLI_API_ROUTES.repairBrief(runId),
23936
23980
  cliRepairBriefSchema,
23937
23981
  {
23938
- query: { findingId },
23982
+ query: { findingId, agent },
23939
23983
  notFound: missingRun(runId)
23940
23984
  }
23941
23985
  );
@@ -24347,7 +24391,7 @@ import { delimiter, join as join2, sep } from "node:path";
24347
24391
  import { createInterface } from "node:readline/promises";
24348
24392
 
24349
24393
  // src/version.ts
24350
- var CLI_VERSION = true ? "0.1.0" : "0.0.0-dev";
24394
+ var CLI_VERSION = true ? "0.2.0" : "0.0.0-dev";
24351
24395
  var CLI_PACKAGE = true ? "lockpicks" : "lockpicks";
24352
24396
 
24353
24397
  // src/install.ts
@@ -34410,15 +34454,16 @@ Still ${run2.status}. Call lockpick_wait_for_run with runId \`${run2.id}\` to ke
34410
34454
  "lockpick_get_repair_brief",
34411
34455
  {
34412
34456
  title: "Get repair brief",
34413
- description: "A markdown repair brief for one finding, or for every finding needing attention when findingId is omitted. Uses the brief a teammate saved in Lockpick when there is one.",
34457
+ description: "A markdown repair brief for one finding, or, when findingId is omitted, one fix-all brief covering every finding that needs attention: a numbered index, a section per fix, the steps only the owner can take and one set of boundaries. Uses the brief a teammate saved in Lockpick when there is one. Pass agent to append working instructions for that agent.",
34414
34458
  inputSchema: {
34415
34459
  runId: external_exports.string().min(1),
34416
- findingId: external_exports.string().min(1).optional()
34460
+ findingId: external_exports.string().min(1).optional(),
34461
+ agent: external_exports.enum(PROMPT_AGENTS).optional().describe("Tailor the brief for this agent")
34417
34462
  },
34418
34463
  annotations: readOnly
34419
34464
  },
34420
- ({ runId, findingId }) => call(async (client) => {
34421
- const brief2 = await client.repairBrief(runId, findingId);
34465
+ ({ runId, findingId, agent }) => call(async (client) => {
34466
+ const brief2 = await client.repairBrief(runId, findingId, agent);
34422
34467
  if (brief2.findingIds.length === 0)
34423
34468
  return text("No findings in this run need attention.");
34424
34469
  return text(brief2.markdown);
@@ -34447,7 +34492,7 @@ Still ${run2.status}. Call lockpick_wait_for_run with runId \`${run2.id}\` to ke
34447
34492
  }),
34448
34493
  {
34449
34494
  title: "Repair brief",
34450
- description: "Markdown repair brief for every finding in a run that needs attention.",
34495
+ description: "The fix-all brief: one markdown document covering every finding in a run that needs attention.",
34451
34496
  mimeType: "text/markdown"
34452
34497
  },
34453
34498
  async (uri, { runId }) => {
@@ -34586,6 +34631,16 @@ function parse3(schema, value, name, allowed) {
34586
34631
  );
34587
34632
  }
34588
34633
  }
34634
+ function agentArg(value) {
34635
+ if (value === void 0) return void 0;
34636
+ if (isPromptAgent(value)) return value;
34637
+ throw new LockpickApiError(
34638
+ `--agent must be one of: ${PROMPT_AGENTS.join(", ")}`,
34639
+ 400,
34640
+ "VALIDATION_ERROR"
34641
+ );
34642
+ }
34643
+ var agentHelp = `Tailor it for this agent: ${PROMPT_AGENTS.join(", ")}`;
34589
34644
  async function findProject(client, ref) {
34590
34645
  const projects2 = await client.listProjects();
34591
34646
  const exact = projects2.find((project) => project.id === ref);
@@ -34927,17 +34982,19 @@ var finding = defineCommand({
34927
34982
  var brief = defineCommand({
34928
34983
  meta: {
34929
34984
  name: "brief",
34930
- description: "Markdown repair brief for a run (or one finding)"
34985
+ description: "Markdown brief that fixes everything in a run that needs attention (or one finding)"
34931
34986
  },
34932
34987
  args: {
34933
34988
  runId: { type: "positional", description: "Run id", required: true },
34934
34989
  finding: { type: "string", description: "Only this finding" },
34990
+ agent: { type: "string", description: agentHelp },
34935
34991
  out: { type: "string", description: "Write the markdown to this file" },
34936
34992
  ...common
34937
34993
  },
34938
34994
  run: ({ args }) => action(async () => {
34995
+ const agent = agentArg(args.agent);
34939
34996
  const client = await clientFor({ host: args.host });
34940
- const result = await client.repairBrief(args.runId, args.finding);
34997
+ const result = await client.repairBrief(args.runId, args.finding, agent);
34941
34998
  if (args.out) {
34942
34999
  await writeFile2(args.out, `${result.markdown}
34943
35000
  `);
@@ -34950,6 +35007,47 @@ var brief = defineCommand({
34950
35007
  emit(args.json, result, () => result.markdown);
34951
35008
  })
34952
35009
  });
35010
+ var fix = defineCommand({
35011
+ meta: {
35012
+ name: "fix",
35013
+ description: `Save the fix-all brief to ${FIX_ALL_FILE} and print the command that starts your agent on it`
35014
+ },
35015
+ args: {
35016
+ runId: { type: "positional", description: "Run id", required: true },
35017
+ agent: { type: "string", description: agentHelp },
35018
+ out: {
35019
+ type: "string",
35020
+ description: `Where to save the brief (default: ${FIX_ALL_FILE})`
35021
+ },
35022
+ ...common
35023
+ },
35024
+ run: ({ args }) => action(async () => {
35025
+ const agent = agentArg(args.agent);
35026
+ const client = await clientFor({ host: args.host });
35027
+ const result = await client.repairBrief(args.runId, void 0, agent);
35028
+ if (result.findingIds.length === 0) {
35029
+ if (args.json)
35030
+ return emit(true, { ...result, file: null, command: null }, () => "");
35031
+ return out("No findings in this run need attention.");
35032
+ }
35033
+ const file2 = args.out ?? FIX_ALL_FILE;
35034
+ await writeFile2(file2, `${result.markdown}
35035
+ `);
35036
+ const command = agent && PROMPT_AGENT_LAUNCH[agent] ? readBriefCommand(agent, file2) : null;
35037
+ const count = result.findingIds.length;
35038
+ if (args.json) return emit(true, { ...result, file: file2, command }, () => "");
35039
+ err(
35040
+ `Saved ${count} ${count === 1 ? "fix" : "fixes"} to ${file2}. Keep it out of your commits.`
35041
+ );
35042
+ if (command) {
35043
+ err("Start your agent on it from your project folder:");
35044
+ out(command);
35045
+ } else
35046
+ out(
35047
+ agent ? `Paste ${file2} into the ${agent} chat, or attach it.` : `Give ${file2} to your coding agent, or add --agent to get its command.`
35048
+ );
35049
+ })
35050
+ });
34953
35051
  var compare = defineCommand({
34954
35052
  meta: {
34955
35053
  name: "compare",
@@ -35091,6 +35189,7 @@ var main = defineCommand({
35091
35189
  findings,
35092
35190
  finding,
35093
35191
  brief,
35192
+ fix,
35094
35193
  compare,
35095
35194
  open,
35096
35195
  mcp,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lockpicks",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Lockpick from the terminal: device login, project status, findings, repair briefs, Pulse checks and an MCP server for coding agents",
5
5
  "keywords": [
6
6
  "lockpick",