@ridit/lens 0.4.6 → 0.4.8

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/dist/index.mjs CHANGED
@@ -78205,16 +78205,46 @@ function getLastDeniedAction(messages) {
78205
78205
  async function runHeadless(opts) {
78206
78206
  const repoPath = opts.path;
78207
78207
  let session = opts.sessionId ? loadSession(opts.sessionId) ?? createSessionWithId(opts.sessionId, repoPath) : opts.single ? getLatestSession(repoPath) ?? createSession(repoPath) : createSession(repoPath);
78208
- let prompt = opts.prompt;
78209
- if (opts.forceAll && APPROVAL_WORDS.has(prompt.trim().toLowerCase())) {
78210
- const pending = getLastDeniedAction(getMessages(session));
78208
+ if (opts.resume) {
78209
+ const msgs = getMessages(session);
78210
+ const pending = getLastDeniedAction(msgs);
78211
+ let trimAt = -1;
78212
+ for (let i = msgs.length - 1;i >= 0; i--) {
78213
+ const msg = msgs[i];
78214
+ if (!msg || msg.role !== "tool")
78215
+ continue;
78216
+ const content = Array.isArray(msg.content) ? msg.content : [];
78217
+ const isDenied = content.some((p) => typeof p === "object" && p !== null && ("type" in p) && p.type === "tool-result" && ("result" in p) && typeof p.result === "string" && p.result.includes("Permission denied"));
78218
+ if (isDenied) {
78219
+ for (let j = i - 1;j >= 0; j--) {
78220
+ if (msgs[j]?.role === "assistant") {
78221
+ trimAt = j;
78222
+ break;
78223
+ }
78224
+ }
78225
+ break;
78226
+ }
78227
+ }
78228
+ if (trimAt >= 0) {
78229
+ session = { ...session, messages: msgs.slice(0, trimAt) };
78230
+ }
78211
78231
  if (pending) {
78212
- prompt = `Proceed with the previously denied operation: use the ${pending.tool} tool on "${pending.description}".`;
78232
+ session = addMessage(session, "user", `Proceed with the previously denied operation: use the ${pending.tool} tool on "${pending.description}". Permission has been granted.`);
78233
+ }
78234
+ if (!opts.single || opts.sessionId)
78235
+ saveSession(session);
78236
+ } else {
78237
+ let prompt = opts.prompt;
78238
+ if (opts.forceAll && APPROVAL_WORDS.has(prompt.trim().toLowerCase())) {
78239
+ const pending = getLastDeniedAction(getMessages(session));
78240
+ if (pending) {
78241
+ prompt = `Proceed with the previously denied operation: use the ${pending.tool} tool on "${pending.description}".`;
78242
+ }
78213
78243
  }
78244
+ session = addMessage(session, "user", prompt);
78245
+ if (!opts.single || opts.sessionId)
78246
+ saveSession(session);
78214
78247
  }
78215
- session = addMessage(session, "user", prompt);
78216
- if (!opts.single || opts.sessionId)
78217
- saveSession(session);
78218
78248
  const toolLog = [];
78219
78249
  const denied = [];
78220
78250
  const runtimeToolNames = new Set;
@@ -78264,10 +78294,10 @@ async function runHeadless(opts) {
78264
78294
  });
78265
78295
  }
78266
78296
  var program = new Command().enablePositionalOptions();
78267
- program.command("chat").description("Chat with your codebase — ask questions or make changes").option("-p, --path <path>", "Path to the repo", ".").option("-d, --dev", "Output structured JSON (no UI)").option("--single", "Single-shot: run one message then exit").option("--session <id>", "Resume session by ID, or create one with that ID").option("--id <id>", "Alias for --session").option("--force-all", "Auto-approve all tools").option("--prompt <text>", "Run a prompt non-interactively").option("--runtime-tools <path>", "path to runtime tools JSON file").action((opts) => {
78297
+ program.command("chat").description("Chat with your codebase — ask questions or make changes").option("-p, --path <path>", "Path to the repo", ".").option("-d, --dev", "Output structured JSON (no UI)").option("--single", "Single-shot: run one message then exit").option("--session <id>", "Resume session by ID, or create one with that ID").option("--id <id>", "Alias for --session").option("--force-all", "Auto-approve all tools").option("--prompt <text>", "Run a prompt non-interactively").option("--resume", "Resume from last permission-denied tool call (no new prompt needed)").option("--runtime-tools <path>", "path to runtime tools JSON file").action((opts) => {
78268
78298
  const sessionId = opts.session ?? opts.id;
78269
- if (opts.prompt && (opts.dev || opts.single)) {
78270
- runHeadless({ path: opts.path, prompt: opts.prompt, sessionId, single: opts.single, forceAll: opts.forceAll, runtimeTools: opts.runtimeTools });
78299
+ if ((opts.prompt || opts.resume) && (opts.dev || opts.single)) {
78300
+ runHeadless({ path: opts.path, prompt: opts.prompt, sessionId, single: opts.single, forceAll: opts.forceAll ?? opts.resume, runtimeTools: opts.runtimeTools, resume: opts.resume });
78271
78301
  return;
78272
78302
  }
78273
78303
  render(/* @__PURE__ */ jsxDEV21(ChatCommand, {
@@ -78365,13 +78395,13 @@ program.command("run <cmd>").description("Run your dev server. Lens watches and
78365
78395
  });
78366
78396
  var firstArg = process.argv[2];
78367
78397
  if (!firstArg || firstArg.startsWith("-")) {
78368
- const defaultFlags = new Command().option("-p, --path <path>", "Path to the repo", ".").option("--session <id>", "Resume session by ID").option("--single", "Single-shot mode").option("--prompt <text>", "Run a prompt").option("-d, --dev", "Output JSON (no UI)").option("--force-all", "Auto-approve all tools").allowUnknownOption().exitOverride();
78398
+ const defaultFlags = new Command().option("-p, --path <path>", "Path to the repo", ".").option("--session <id>", "Resume session by ID").option("--single", "Single-shot mode").option("--prompt <text>", "Run a prompt").option("-d, --dev", "Output JSON (no UI)").option("--force-all", "Auto-approve all tools").option("--resume", "Resume from last permission-denied tool call").allowUnknownOption().exitOverride();
78369
78399
  try {
78370
78400
  defaultFlags.parse(process.argv);
78371
78401
  } catch {}
78372
78402
  const opts = defaultFlags.opts();
78373
- if (opts.prompt && (opts.dev || opts.single)) {
78374
- runHeadless({ path: opts.path ?? ".", prompt: opts.prompt, sessionId: opts.session, single: opts.single, forceAll: opts.forceAll });
78403
+ if ((opts.prompt || opts.resume) && (opts.dev || opts.single)) {
78404
+ runHeadless({ path: opts.path ?? ".", prompt: opts.prompt, sessionId: opts.session, single: opts.single, forceAll: opts.forceAll ?? opts.resume, resume: opts.resume });
78375
78405
  } else {
78376
78406
  render(/* @__PURE__ */ jsxDEV21(ChatCommand, {
78377
78407
  path: opts.path ?? ".",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ridit/lens",
3
- "version": "0.4.6",
3
+ "version": "0.4.8",
4
4
  "description": "Understand your codebase.",
5
5
  "author": "Ridit Jangra <riditjangra09@gmail.com> (https://ridit.space)",
6
6
  "license": "MIT",
package/src/index.tsx CHANGED
@@ -72,11 +72,12 @@ function getLastDeniedAction(messages: ReturnType<typeof getMessages>): { tool:
72
72
 
73
73
  async function runHeadless(opts: {
74
74
  path: string;
75
- prompt: string;
75
+ prompt?: string;
76
76
  sessionId?: string;
77
77
  single?: boolean;
78
78
  forceAll?: boolean;
79
79
  runtimeTools?: string;
80
+ resume?: boolean;
80
81
  }) {
81
82
  const repoPath = opts.path;
82
83
 
@@ -86,18 +87,64 @@ async function runHeadless(opts: {
86
87
  ? (getLatestSession(repoPath) ?? createSession(repoPath))
87
88
  : createSession(repoPath);
88
89
 
89
- // if user is approving a prior denial, make the intent unambiguous
90
- let prompt = opts.prompt;
91
- if (opts.forceAll && APPROVAL_WORDS.has(prompt.trim().toLowerCase())) {
92
- const pending = getLastDeniedAction(getMessages(session));
90
+ if (opts.resume) {
91
+ // Rewind the session: strip the last denied tool-call (assistant message),
92
+ // its "Permission denied" tool result, and the assistant's text response after it.
93
+ // This leaves the session ending at the last successful state so the agent
94
+ // can re-attempt the denied tool with forceAll: true.
95
+ const msgs = getMessages(session);
96
+ // Capture the pending denied action before trimming so we can add an explicit proceed instruction
97
+ const pending = getLastDeniedAction(msgs);
98
+ let trimAt = -1;
99
+ for (let i = msgs.length - 1; i >= 0; i--) {
100
+ const msg = msgs[i];
101
+ if (!msg || msg.role !== "tool") continue;
102
+ const content = Array.isArray(msg.content) ? msg.content : [];
103
+ const isDenied = content.some(
104
+ (p: unknown) =>
105
+ typeof p === "object" && p !== null &&
106
+ "type" in p && (p as { type: string }).type === "tool-result" &&
107
+ "result" in p && typeof (p as { result: unknown }).result === "string" &&
108
+ ((p as { result: string }).result).includes("Permission denied"),
109
+ );
110
+ if (isDenied) {
111
+ // Find the assistant message immediately before this tool result (the tool-call message)
112
+ for (let j = i - 1; j >= 0; j--) {
113
+ if (msgs[j]?.role === "assistant") {
114
+ trimAt = j;
115
+ break;
116
+ }
117
+ }
118
+ break;
119
+ }
120
+ }
121
+ if (trimAt >= 0) {
122
+ session = { ...session, messages: msgs.slice(0, trimAt) };
123
+ }
124
+ // Add an explicit instruction to proceed with the denied tool so the model doesn't ask again
93
125
  if (pending) {
94
- prompt = `Proceed with the previously denied operation: use the ${pending.tool} tool on "${pending.description}".`;
126
+ session = addMessage(
127
+ session,
128
+ "user",
129
+ `Proceed with the previously denied operation: use the ${pending.tool} tool on "${pending.description}". Permission has been granted.`,
130
+ );
131
+ }
132
+ // Save trimmed session so context is clean for this run
133
+ if (!opts.single || opts.sessionId) saveSession(session);
134
+ } else {
135
+ // if user is approving a prior denial, make the intent unambiguous
136
+ let prompt = opts.prompt!;
137
+ if (opts.forceAll && APPROVAL_WORDS.has(prompt.trim().toLowerCase())) {
138
+ const pending = getLastDeniedAction(getMessages(session));
139
+ if (pending) {
140
+ prompt = `Proceed with the previously denied operation: use the ${pending.tool} tool on "${pending.description}".`;
141
+ }
95
142
  }
96
- }
97
143
 
98
- session = addMessage(session, "user", prompt);
99
- // save now so context is available on follow-up messages even if we exit early
100
- if (!opts.single || opts.sessionId) saveSession(session);
144
+ session = addMessage(session, "user", prompt);
145
+ // save now so context is available on follow-up messages even if we exit early
146
+ if (!opts.single || opts.sessionId) saveSession(session);
147
+ }
101
148
 
102
149
  const toolLog: { tool: string; args: unknown; result: unknown }[] = [];
103
150
  const denied: { tool: string; description: string }[] = [];
@@ -170,6 +217,7 @@ program
170
217
  .option("--id <id>", "Alias for --session")
171
218
  .option("--force-all", "Auto-approve all tools")
172
219
  .option("--prompt <text>", "Run a prompt non-interactively")
220
+ .option("--resume", "Resume from last permission-denied tool call (no new prompt needed)")
173
221
  .option("--runtime-tools <path>", "path to runtime tools JSON file")
174
222
  .action(
175
223
  (opts: {
@@ -180,12 +228,13 @@ program
180
228
  id?: string;
181
229
  forceAll?: boolean;
182
230
  prompt?: string;
231
+ resume?: boolean;
183
232
  runtimeTools?: string;
184
233
  }) => {
185
234
  const sessionId = opts.session ?? opts.id;
186
- // headless: dev+prompt or single+prompt → no UI, output JSON and exit
187
- if (opts.prompt && (opts.dev || opts.single)) {
188
- runHeadless({ path: opts.path, prompt: opts.prompt, sessionId, single: opts.single, forceAll: opts.forceAll, runtimeTools: opts.runtimeTools });
235
+ // headless: dev+prompt, single+prompt, or --resume → no UI, output JSON and exit
236
+ if ((opts.prompt || opts.resume) && (opts.dev || opts.single)) {
237
+ runHeadless({ path: opts.path, prompt: opts.prompt, sessionId, single: opts.single, forceAll: opts.forceAll ?? opts.resume, runtimeTools: opts.runtimeTools, resume: opts.resume });
189
238
  return;
190
239
  }
191
240
  render(
@@ -367,6 +416,7 @@ if (!firstArg || firstArg.startsWith("-")) {
367
416
  .option("--prompt <text>", "Run a prompt")
368
417
  .option("-d, --dev", "Output JSON (no UI)")
369
418
  .option("--force-all", "Auto-approve all tools")
419
+ .option("--resume", "Resume from last permission-denied tool call")
370
420
  .allowUnknownOption()
371
421
  .exitOverride();
372
422
 
@@ -379,10 +429,11 @@ if (!firstArg || firstArg.startsWith("-")) {
379
429
  prompt?: string;
380
430
  dev?: boolean;
381
431
  forceAll?: boolean;
432
+ resume?: boolean;
382
433
  }>();
383
434
 
384
- if (opts.prompt && (opts.dev || opts.single)) {
385
- runHeadless({ path: opts.path ?? ".", prompt: opts.prompt, sessionId: opts.session, single: opts.single, forceAll: opts.forceAll });
435
+ if ((opts.prompt || opts.resume) && (opts.dev || opts.single)) {
436
+ runHeadless({ path: opts.path ?? ".", prompt: opts.prompt, sessionId: opts.session, single: opts.single, forceAll: opts.forceAll ?? opts.resume, resume: opts.resume });
386
437
  } else {
387
438
  render(
388
439
  <ChatCommand