getprismo 0.1.34 → 0.1.36

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.
@@ -12,8 +12,11 @@ module.exports = function createAgent(deps) {
12
12
  runGuard,
13
13
  runShield,
14
14
  runOptimize,
15
+ openUrl,
15
16
  } = deps;
16
17
 
18
+ const DEFAULT_WORKSPACE_URL = "https://getprismo.dev/dashboard/dev";
19
+
17
20
  const DEFAULT_API_URL = "https://api.getprismo.dev";
18
21
  const TERMINAL_STATUSES = new Set(["completed", "failed", "cancelled"]);
19
22
  const SAFE_SHIELD_COMMANDS = new Set(["npm", "pnpm", "yarn", "bun", "npx", "pytest", "python", "python3", "node"]);
@@ -128,6 +131,49 @@ module.exports = function createAgent(deps) {
128
131
  return response.data;
129
132
  }
130
133
 
134
+ function runAutoDetect(rootDir, options = {}) {
135
+ const mode = options.mode || "autopilot";
136
+ const startedAt = new Date().toISOString();
137
+ const result = runDoctor(rootDir, { limit: 3, applySuggestions: mode === "autopilot", json: true, dryRun: mode === "observe" });
138
+ const score = result.after?.score ?? result.scan?.score ?? null;
139
+ const issues = result.scan?.issues || [];
140
+ const generatedFiles = result.generatedFiles || result.optimize?.generatedFiles || [];
141
+ const findings = [];
142
+
143
+ if (score !== null && score < 80) {
144
+ findings.push({ type: "low-score", score, message: `Context health score is ${score}/100.` });
145
+ }
146
+ for (const issue of issues.slice(0, 5)) {
147
+ findings.push({ type: "issue", severity: issue.severity, message: issue.message || issue.recommendation || issue.label });
148
+ }
149
+
150
+ return {
151
+ startedAt,
152
+ completedAt: new Date().toISOString(),
153
+ mode,
154
+ score,
155
+ findings,
156
+ generatedFiles,
157
+ applied: mode === "autopilot",
158
+ needsApproval: mode === "suggest" && findings.length > 0,
159
+ };
160
+ }
161
+
162
+ async function reportAutoDetect(config, detectResult, options = {}) {
163
+ const endpoint = options.detectEndpoint || `${apiBase(config)}/v1/dev/workspace/auto-detect`;
164
+ try {
165
+ await requestJson("POST", endpoint, config.token, detectResult, options.timeoutMs || 10000);
166
+ } catch (_) {}
167
+ }
168
+
169
+ function openWorkspace(config) {
170
+ const url = config?.workspaceUrl || DEFAULT_WORKSPACE_URL;
171
+ if (openUrl) {
172
+ openUrl(url);
173
+ }
174
+ return url;
175
+ }
176
+
131
177
  async function executeAction(action, rootDir, options = {}) {
132
178
  const root = repoRoot(path.resolve(rootDir || process.cwd()), action);
133
179
  const parsed = parseCommand(action.command);
@@ -261,6 +307,12 @@ module.exports = function createAgent(deps) {
261
307
  await sendHeartbeat(config, { mode, status: "online", lastPollAt: pollTime }, options);
262
308
  } catch (_) {}
263
309
 
310
+ let autoDetectResult = null;
311
+ if (options.autoDetect) {
312
+ autoDetectResult = runAutoDetect(rootDir, { mode });
313
+ await reportAutoDetect(config, autoDetectResult, options);
314
+ }
315
+
264
316
  const actions = await claimActions(config, options);
265
317
  const results = [];
266
318
  for (const action of actions) {
@@ -299,6 +351,7 @@ module.exports = function createAgent(deps) {
299
351
  actionsCompleted: results.filter((item) => item.status === "completed").length,
300
352
  actionsFailed: results.filter((item) => item.status === "failed").length,
301
353
  actionsObserved: results.filter((item) => item.status === "observed" || item.status === "pending_approval").length,
354
+ autoDetect: autoDetectResult,
302
355
  results,
303
356
  privacy: {
304
357
  rawPrompts: false,
@@ -332,6 +385,21 @@ module.exports = function createAgent(deps) {
332
385
  if (result.actionsObserved > 0) {
333
386
  lines.push(`Observed/Suggested: ${result.actionsObserved}`);
334
387
  }
388
+ if (result.autoDetect) {
389
+ lines.push("");
390
+ lines.push("Auto-detect");
391
+ lines.push(` Score: ${result.autoDetect.score ?? "unknown"}/100`);
392
+ lines.push(` Findings: ${result.autoDetect.findings.length}`);
393
+ if (result.autoDetect.applied) lines.push(" Status: auto-fixed");
394
+ else if (result.autoDetect.needsApproval) lines.push(" Status: needs approval in workspace");
395
+ else lines.push(" Status: observed");
396
+ if (result.autoDetect.generatedFiles.length) {
397
+ lines.push(` Generated: ${result.autoDetect.generatedFiles.join(", ")}`);
398
+ }
399
+ result.autoDetect.findings.forEach((f) => {
400
+ lines.push(` - ${f.message}`);
401
+ });
402
+ }
335
403
  if (result.results.length) {
336
404
  lines.push("");
337
405
  lines.push("Actions");
@@ -339,7 +407,7 @@ module.exports = function createAgent(deps) {
339
407
  lines.push(`- ${item.status}: ${item.label}`);
340
408
  if (item.statusMessage) lines.push(` ${item.statusMessage}`);
341
409
  });
342
- } else {
410
+ } else if (!result.autoDetect) {
343
411
  lines.push("");
344
412
  lines.push("No queued workspace actions.");
345
413
  }
@@ -352,6 +420,12 @@ module.exports = function createAgent(deps) {
352
420
  const intervalMs = Math.max(5, Number(options.interval || 15)) * 1000;
353
421
  let running = true;
354
422
  let sleepResolve = null;
423
+ let firstRun = true;
424
+
425
+ if (options.open) {
426
+ const config = loadConfig();
427
+ openWorkspace(config);
428
+ }
355
429
 
356
430
  async function shutdown() {
357
431
  if (!running) return;
@@ -371,7 +445,9 @@ module.exports = function createAgent(deps) {
371
445
  process.on("SIGTERM", shutdown);
372
446
 
373
447
  while (running) {
374
- const result = await runAgentOnce(rootDir, options);
448
+ const runOptions = { ...options, autoDetect: firstRun && options.autoDetect !== false };
449
+ firstRun = false;
450
+ const result = await runAgentOnce(rootDir, runOptions);
375
451
  if (!running) break;
376
452
  if (options.json) console.log(JSON.stringify(result, null, 2));
377
453
  else console.log(renderAgentTerminal(result));
@@ -388,10 +464,13 @@ module.exports = function createAgent(deps) {
388
464
  return {
389
465
  claimActions,
390
466
  executeAction,
467
+ openWorkspace,
391
468
  parseCommand,
392
469
  renderAgentTerminal,
470
+ reportAutoDetect,
393
471
  runAgent,
394
472
  runAgentOnce,
473
+ runAutoDetect,
395
474
  sendHeartbeat,
396
475
  updateAction,
397
476
  VALID_MODES,
@@ -25,6 +25,7 @@ function createCli(deps) {
25
25
  NPX_COMMAND,
26
26
  DEFAULT_PRISMO_PROXY_URL,
27
27
  AGENT_VALID_MODES,
28
+ openUrl,
28
29
  printStep,
29
30
  getPositionals,
30
31
  parsePositiveInt,
@@ -321,6 +322,9 @@ function createCli(deps) {
321
322
  device: deviceIndex >= 0 ? rest[deviceIndex + 1] : null,
322
323
  limit: parsePositiveInt(limitIndex >= 0 ? rest[limitIndex + 1] : null, 20),
323
324
  });
325
+ if (result.connected && (rest.includes("--open") || !rest.includes("--no-open"))) {
326
+ openUrl("https://getprismo.dev/dashboard/dev");
327
+ }
324
328
  if (json) console.log(JSON.stringify(result, null, 2));
325
329
  else console.log(renderConnectTerminal(result));
326
330
  return;
@@ -372,6 +376,8 @@ function createCli(deps) {
372
376
  json,
373
377
  mode: modeValue,
374
378
  watch: rest.includes("--watch") && !rest.includes("--once"),
379
+ open: rest.includes("--open"),
380
+ autoDetect: !rest.includes("--no-detect"),
375
381
  interval: parsePositiveInt(intervalIndex >= 0 ? rest[intervalIndex + 1] : null, 15),
376
382
  limit: parsePositiveInt(limitIndex >= 0 ? rest[limitIndex + 1] : null, 5),
377
383
  tokenBudget: parseTokenBudget(budgetIndex >= 0 ? rest[budgetIndex + 1] : null) || 600000,
@@ -455,7 +455,7 @@ Output:
455
455
  agent: `PrismoDev Agent
456
456
 
457
457
  Usage:
458
- prismo agent [--json] [--once] [--watch] [--interval N] [--limit N] [--mode MODE] [path]
458
+ prismo agent [--json] [--once] [--watch] [--open] [--no-detect] [--interval N] [--limit N] [--mode MODE] [path]
459
459
 
460
460
  Modes:
461
461
  observe Watch and report actions without executing. No changes made.
@@ -467,10 +467,17 @@ Examples:
467
467
  prismo agent --watch --interval 15
468
468
  prismo agent --watch --mode observe
469
469
  prismo agent --watch --mode suggest
470
- prismo agent --json --once
470
+ prismo agent --watch --open
471
+ prismo agent --json --once --no-detect
472
+
473
+ Options:
474
+ --open Open the Prismo workspace in the browser on start.
475
+ --no-detect Skip the initial auto-detect scan on first poll.
471
476
 
472
477
  Output:
473
- Claims safe workspace actions queued from Prismo Cloud, runs them locally, and reports status back to the workspace.
478
+ On first poll, the agent proactively runs doctor to detect context issues.
479
+ In autopilot mode it auto-fixes safe issues. In suggest mode it reports them as pending_approval in the workspace.
480
+ Claims safe workspace actions queued from Prismo Cloud, runs them locally, and reports status back.
474
481
  Sends heartbeat to Prismo Cloud on each poll so the dashboard shows agent online/offline status.
475
482
  Handles SIGINT/SIGTERM gracefully and marks the agent offline before exiting.
476
483
  Supported actions are doctor, sync, guard, context/optimize, and shield with a conservative command allowlist.
@@ -3,8 +3,18 @@ const http = require("http");
3
3
  const https = require("https");
4
4
  const os = require("os");
5
5
  const path = require("path");
6
+ const { execSync } = require("child_process");
6
7
  const { version: PACKAGE_VERSION } = require("../package.json");
7
8
 
9
+ function openUrl(url) {
10
+ try {
11
+ const cmd = process.platform === "darwin" ? "open"
12
+ : process.platform === "win32" ? "start"
13
+ : "xdg-open";
14
+ execSync(`${cmd} ${JSON.stringify(url)}`, { stdio: "ignore" });
15
+ } catch (_) {}
16
+ }
17
+
8
18
  const {
9
19
  HIGH_RISK_DIRS,
10
20
  HIGH_RISK_FILE_NAMES,
@@ -320,6 +330,7 @@ const {
320
330
  runGuard,
321
331
  runShield,
322
332
  runOptimize,
333
+ openUrl,
323
334
  });
324
335
 
325
336
  const {
@@ -383,6 +394,7 @@ const { runCli } = require("./prismo-dev/cli")({
383
394
  NPX_COMMAND,
384
395
  DEFAULT_PRISMO_PROXY_URL,
385
396
  AGENT_VALID_MODES,
397
+ openUrl,
386
398
  printStep,
387
399
  getPositionals,
388
400
  parsePositiveInt,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "getprismo",
3
- "version": "0.1.34",
3
+ "version": "0.1.36",
4
4
  "description": "Local AI coding workflow scanner for Codex, Claude Code, Cursor, and token-waste diagnostics.",
5
5
  "license": "MIT",
6
6
  "homepage": "https://github.com/shanirsh/prismodev#readme",