projscan 1.2.1 → 1.4.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
@@ -20,7 +20,7 @@
20
20
 
21
21
  AI coding agents are becoming the primary interface to code. Today, when you ask your agent *"which files implement auth?"* or *"what breaks if I bump React from 18 to 19?"* - it either guesses from names, or it shells out to grep and reads raw output not built for it.
22
22
 
23
- **projscan is the first code-intelligence tool built for agents, not for humans.** Your agent gets a fast, AST-accurate, context-budget-aware view of your codebase through 20 structured MCP tools. It can query the import graph, find symbol definitions, preview upgrades, rank hotspots, diff structural changes between refs, surface coupling/cycle hotspots, get a one-call PR review, request structured fix-action prompts for any open issue, and ask "what breaks if I change this?" via transitive blast-radius analysis - without loading the file tree into its context.
23
+ **projscan is the first code-intelligence tool built for agents, not for humans.** Your agent gets a fast, AST-accurate, context-budget-aware view of your codebase through 21 structured MCP tools. It can query the import graph, find symbol definitions, preview upgrades, rank hotspots, diff structural changes between refs, surface coupling/cycle hotspots, get a one-call PR review, request structured fix-action prompts for any open issue, ask "what breaks if I change this?" via transitive blast-radius analysis, and share a durable session across multiple agent invocations - without loading the file tree into its context.
24
24
 
25
25
  Humans get the same thing through the CLI.
26
26
 
@@ -515,25 +515,104 @@ Coverage is also automatically joined into `projscan hotspots` when one of those
515
515
 
516
516
  **This is the primary way to use projscan.** `projscan mcp` starts an [MCP](https://modelcontextprotocol.io) server over stdio so AI coding agents can query your codebase with real structural accuracy - not regex, not grep.
517
517
 
518
+ <img src="docs/projscan-agent-demo.gif" alt="projscan answering two agent questions: what breaks if I rename buildCodeGraph (impact analysis with definitions, direct callers, transitive reach), and where should I fix first (ranked hotspots with cyclomatic complexity)" width="700">
519
+
520
+ Two questions an agent asks; structural answers in milliseconds. *"What breaks if I rename `buildCodeGraph`?"* → 31 direct callers, 97 files reachable. *"Where should I fix first?"* → ranked hotspots with AST cyclomatic complexity, churn, and ownership signals.
521
+
518
522
  ### Claude Code
519
523
 
524
+ One-liner — adds projscan as an MCP server in the current project:
525
+
520
526
  ```bash
521
- claude mcp add projscan -- npx projscan mcp
527
+ claude mcp add projscan -- npx -y projscan mcp
528
+ ```
529
+
530
+ ### Cursor
531
+
532
+ Add to `~/.cursor/mcp.json` (global) or `.cursor/mcp.json` (per-project):
533
+
534
+ ```json
535
+ {
536
+ "mcpServers": {
537
+ "projscan": {
538
+ "command": "npx",
539
+ "args": ["-y", "projscan", "mcp"]
540
+ }
541
+ }
542
+ }
543
+ ```
544
+
545
+ ### Windsurf
546
+
547
+ Add to `~/.codeium/windsurf/mcp_config.json`:
548
+
549
+ ```json
550
+ {
551
+ "mcpServers": {
552
+ "projscan": {
553
+ "command": "npx",
554
+ "args": ["-y", "projscan", "mcp"]
555
+ }
556
+ }
557
+ }
522
558
  ```
523
559
 
524
- ### Cursor / Windsurf / any MCP client
560
+ ### Cline (VS Code extension)
561
+
562
+ In Cline's MCP settings panel (or the underlying `cline_mcp_settings.json`):
525
563
 
526
564
  ```json
527
565
  {
528
566
  "mcpServers": {
529
567
  "projscan": {
530
568
  "command": "npx",
531
- "args": ["projscan", "mcp"]
569
+ "args": ["-y", "projscan", "mcp"]
532
570
  }
533
571
  }
534
572
  }
535
573
  ```
536
574
 
575
+ ### Continue.dev
576
+
577
+ Add to `~/.continue/config.yaml`:
578
+
579
+ ```yaml
580
+ mcpServers:
581
+ - name: projscan
582
+ command: npx
583
+ args:
584
+ - -y
585
+ - projscan
586
+ - mcp
587
+ ```
588
+
589
+ ### Zed
590
+
591
+ Add to `~/.config/zed/settings.json` under `context_servers`:
592
+
593
+ ```json
594
+ {
595
+ "context_servers": {
596
+ "projscan": {
597
+ "command": {
598
+ "path": "npx",
599
+ "args": ["-y", "projscan", "mcp"]
600
+ }
601
+ }
602
+ }
603
+ }
604
+ ```
605
+
606
+ ### Any other MCP-aware client
607
+
608
+ The transport is **stdio**. Wire your client to invoke `npx -y projscan mcp` as a subprocess; the server speaks JSON-RPC 2.0 over stdin/stdout. If your client wants `notifications/file_changed` push notifications when the repo changes, append `--watch`:
609
+
610
+ ```bash
611
+ npx -y projscan mcp --watch
612
+ ```
613
+
614
+ Capability is advertised under `experimental.fileChanged` on `initialize` so clients can detect support.
615
+
537
616
  ### What agents can ask
538
617
 
539
618
  - *"Who imports `src/auth/jwt.ts`?"* → `projscan_graph { file, direction: "importers" }`
@@ -543,7 +622,7 @@ claude mcp add projscan -- npx projscan mcp
543
622
  - *"What breaks if I bump chalk to 6?"* → `projscan_upgrade { package: "chalk" }`
544
623
  - *"Where should I refactor first?"* → `projscan_hotspots`
545
624
 
546
- ### The 20 MCP tools
625
+ ### The 21 MCP tools
547
626
 
548
627
  **Structural (0.6.0 / 0.11 / 0.13 / 0.14 / 0.15 - agent-native):**
549
628
  - **`projscan_graph`** - query the AST-based code graph. Directions: `imports`, `exports`, `importers`, `symbol_defs`, `package_importers`. Millisecond responses on a warm cache.
@@ -573,6 +652,9 @@ claude mcp add projscan -- npx projscan mcp
573
652
  **Workspace (0.11):**
574
653
  - `projscan_workspaces` - list monorepo packages (npm/yarn/pnpm/Nx/Turbo/Lerna). Use the `name` as the `package` arg on `projscan_hotspots` / `projscan_coupling` to scope.
575
654
 
655
+ **Session (1.4):**
656
+ - **`projscan_session`** *(1.4)* - durable cross-invocation session. Subactions: `current` (id + counts), `touched` (files touched this session, sorted by recency, filterable by source: `tool-result` / `fs-watch` / `explicit`), `events` (chronological log), `reset` (start a fresh session). Auto-populated from every tool result and from `notifications/file_changed` push events when `--watch` is on. Lets multiple agents in the same project see "what's been touched here" without re-running git.
657
+
576
658
  ### Context-window budgeting
577
659
 
578
660
  **Every MCP tool accepts an optional `max_tokens` argument.** Set it and projscan serializes the result, and - if over budget - truncates the largest array field record-by-record until it fits. Responses include a `_budget` sidecar when truncated so your agent knows it got a partial view.
@@ -6,11 +6,12 @@ export function registerMcp() {
6
6
  program
7
7
  .command('mcp')
8
8
  .description('Run projscan as an MCP server (stdio) for AI coding agents')
9
- .action(async () => {
9
+ .option('--watch', "emit notifications/file_changed when source files change (1.3+; off by default — agents that subscribe stop polling)")
10
+ .action(async (opts) => {
10
11
  setLogLevel('quiet');
11
12
  const rootPath = getRootPath();
12
13
  try {
13
- await runMcpServer(rootPath);
14
+ await runMcpServer(rootPath, { watch: opts.watch === true });
14
15
  }
15
16
  catch (error) {
16
17
  console.error(chalk.red(error instanceof Error ? error.message : String(error)));
@@ -1 +1 @@
1
- {"version":3,"file":"mcp.js","sourceRoot":"","sources":["../../../src/cli/commands/mcp.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,OAAO,EAAE,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AACrD,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AACpD,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAEnD,MAAM,UAAU,WAAW;IACzB,OAAO;SACJ,OAAO,CAAC,KAAK,CAAC;SACd,WAAW,CAAC,4DAA4D,CAAC;SACzE,MAAM,CAAC,KAAK,IAAI,EAAE;QACjB,WAAW,CAAC,OAAO,CAAC,CAAC;QACrB,MAAM,QAAQ,GAAG,WAAW,EAAE,CAAC;QAC/B,IAAI,CAAC;YACH,MAAM,YAAY,CAAC,QAAQ,CAAC,CAAC;QAC/B,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;YACjF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;IACH,CAAC,CAAC,CAAC;AACP,CAAC"}
1
+ {"version":3,"file":"mcp.js","sourceRoot":"","sources":["../../../src/cli/commands/mcp.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,OAAO,EAAE,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AACrD,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AACpD,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAEnD,MAAM,UAAU,WAAW;IACzB,OAAO;SACJ,OAAO,CAAC,KAAK,CAAC;SACd,WAAW,CAAC,4DAA4D,CAAC;SACzE,MAAM,CACL,SAAS,EACT,sHAAsH,CACvH;SACA,MAAM,CAAC,KAAK,EAAE,IAAyB,EAAE,EAAE;QAC1C,WAAW,CAAC,OAAO,CAAC,CAAC;QACrB,MAAM,QAAQ,GAAG,WAAW,EAAE,CAAC;QAC/B,IAAI,CAAC;YACH,MAAM,YAAY,CAAC,QAAQ,EAAE,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,KAAK,IAAI,EAAE,CAAC,CAAC;QAC/D,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;YACjF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;IACH,CAAC,CAAC,CAAC;AACP,CAAC"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * `projscan session` — inspect the durable cross-invocation session that
3
+ * the MCP server populates as agents work. Mirrors the `projscan_session`
4
+ * MCP tool but for terminal use.
5
+ *
6
+ * Subcommands:
7
+ * projscan session — current session summary (default)
8
+ * projscan session touched — list files touched this session
9
+ * projscan session events — chronological event log
10
+ * projscan session reset — discard the current session
11
+ */
12
+ export declare function registerSession(): void;
@@ -0,0 +1,200 @@
1
+ import chalk from 'chalk';
2
+ import { program, getRootPath, getFormat, setupLogLevel, maybeCompactBanner } from '../_shared.js';
3
+ import { loadSession, resetSession, } from '../../core/session.js';
4
+ /**
5
+ * `projscan session` — inspect the durable cross-invocation session that
6
+ * the MCP server populates as agents work. Mirrors the `projscan_session`
7
+ * MCP tool but for terminal use.
8
+ *
9
+ * Subcommands:
10
+ * projscan session — current session summary (default)
11
+ * projscan session touched — list files touched this session
12
+ * projscan session events — chronological event log
13
+ * projscan session reset — discard the current session
14
+ */
15
+ export function registerSession() {
16
+ const session = program
17
+ .command('session')
18
+ .description('Inspect or reset the durable cross-invocation session (1.4+)')
19
+ .action(async () => {
20
+ await runSummary();
21
+ });
22
+ session
23
+ .command('touched')
24
+ .description('List files touched this session (sorted by last-touched desc)')
25
+ .option('--source <source>', 'restrict to tool-result | fs-watch | explicit')
26
+ .option('--limit <n>', 'show at most N entries', (v) => parseInt(v, 10))
27
+ .action(async (opts) => {
28
+ await runTouched(opts);
29
+ });
30
+ session
31
+ .command('events')
32
+ .description('Show the session event log, newest first')
33
+ .option('--limit <n>', 'show at most N entries', (v) => parseInt(v, 10))
34
+ .action(async (opts) => {
35
+ await runEvents(opts);
36
+ });
37
+ session
38
+ .command('reset')
39
+ .description('Discard the current session and start a fresh one')
40
+ .action(async () => {
41
+ await runReset();
42
+ });
43
+ }
44
+ async function runSummary() {
45
+ setupLogLevel();
46
+ maybeCompactBanner();
47
+ const rootPath = getRootPath();
48
+ const format = getFormat();
49
+ try {
50
+ const { session: sess, created } = await loadSession(rootPath);
51
+ if (format === 'json') {
52
+ console.log(JSON.stringify({ session: sess, created }, null, 2));
53
+ return;
54
+ }
55
+ printSummary(sess, created);
56
+ }
57
+ catch (error) {
58
+ console.error(chalk.red(error instanceof Error ? error.message : String(error)));
59
+ process.exit(1);
60
+ }
61
+ }
62
+ async function runTouched(opts) {
63
+ setupLogLevel();
64
+ maybeCompactBanner();
65
+ const rootPath = getRootPath();
66
+ const format = getFormat();
67
+ try {
68
+ const { session: sess } = await loadSession(rootPath);
69
+ const all = Object.values(sess.touchedFiles);
70
+ const filtered = opts.source ? all.filter((t) => t.source === opts.source) : all;
71
+ filtered.sort((a, b) => b.lastTouchedAt.localeCompare(a.lastTouchedAt));
72
+ const limited = typeof opts.limit === 'number' && opts.limit > 0 ? filtered.slice(0, opts.limit) : filtered;
73
+ if (format === 'json') {
74
+ console.log(JSON.stringify({ sessionId: sess.id, total: filtered.length, touched: limited }, null, 2));
75
+ return;
76
+ }
77
+ printTouched(sess, limited, filtered.length);
78
+ }
79
+ catch (error) {
80
+ console.error(chalk.red(error instanceof Error ? error.message : String(error)));
81
+ process.exit(1);
82
+ }
83
+ }
84
+ async function runEvents(opts) {
85
+ setupLogLevel();
86
+ maybeCompactBanner();
87
+ const rootPath = getRootPath();
88
+ const format = getFormat();
89
+ try {
90
+ const { session: sess } = await loadSession(rootPath);
91
+ const reversed = [...sess.events].reverse();
92
+ const limited = typeof opts.limit === 'number' && opts.limit > 0 ? reversed.slice(0, opts.limit) : reversed;
93
+ if (format === 'json') {
94
+ console.log(JSON.stringify({ sessionId: sess.id, total: reversed.length, events: limited }, null, 2));
95
+ return;
96
+ }
97
+ printEvents(sess, limited, reversed.length);
98
+ }
99
+ catch (error) {
100
+ console.error(chalk.red(error instanceof Error ? error.message : String(error)));
101
+ process.exit(1);
102
+ }
103
+ }
104
+ async function runReset() {
105
+ setupLogLevel();
106
+ maybeCompactBanner();
107
+ const rootPath = getRootPath();
108
+ const format = getFormat();
109
+ try {
110
+ const fresh = await resetSession(rootPath);
111
+ if (format === 'json') {
112
+ console.log(JSON.stringify({ reset: true, session: fresh }, null, 2));
113
+ return;
114
+ }
115
+ console.log(chalk.green('✓ Session reset.'));
116
+ console.log(` New session id: ${fresh.id}`);
117
+ console.log(` Started at: ${fresh.startedAt}`);
118
+ }
119
+ catch (error) {
120
+ console.error(chalk.red(error instanceof Error ? error.message : String(error)));
121
+ process.exit(1);
122
+ }
123
+ }
124
+ function printSummary(session, created) {
125
+ const startedAtMs = Date.parse(session.startedAt);
126
+ const ageMs = Number.isFinite(startedAtMs) ? Date.now() - startedAtMs : null;
127
+ console.log('');
128
+ console.log(chalk.bold('Session'));
129
+ console.log(chalk.dim('────────────────────────────────────────'));
130
+ console.log(` id: ${session.id}`);
131
+ console.log(` status: ${created ? chalk.cyan('fresh (just created)') : chalk.green('active')}`);
132
+ console.log(` started: ${session.startedAt}`);
133
+ console.log(` last activity: ${session.lastActivityAt}`);
134
+ if (ageMs !== null) {
135
+ console.log(` age: ${formatDuration(ageMs)}`);
136
+ }
137
+ console.log('');
138
+ console.log(` touched files: ${Object.keys(session.touchedFiles).length}`);
139
+ console.log(` events: ${session.events.length}`);
140
+ console.log('');
141
+ console.log(chalk.dim(' Tip: run `projscan session touched` for the file list.'));
142
+ }
143
+ function printTouched(session, items, total) {
144
+ console.log('');
145
+ console.log(chalk.bold(`Touched files (${session.id.slice(0, 8)})`));
146
+ console.log(chalk.dim('────────────────────────────────────────'));
147
+ if (items.length === 0) {
148
+ console.log(chalk.dim(' No files touched in this session yet.'));
149
+ return;
150
+ }
151
+ for (const t of items) {
152
+ const sourceTag = formatSourceTag(t.source);
153
+ console.log(` ${sourceTag} ${t.file} ${chalk.dim(`(×${t.count}, ${t.lastTouchedAt})`)}`);
154
+ }
155
+ if (items.length < total) {
156
+ console.log('');
157
+ console.log(chalk.dim(` ${total - items.length} more — pass --limit ${total} to see all.`));
158
+ }
159
+ }
160
+ function printEvents(session, items, total) {
161
+ console.log('');
162
+ console.log(chalk.bold(`Events (${session.id.slice(0, 8)})`));
163
+ console.log(chalk.dim('────────────────────────────────────────'));
164
+ if (items.length === 0) {
165
+ console.log(chalk.dim(' No events recorded in this session yet.'));
166
+ return;
167
+ }
168
+ for (const e of items) {
169
+ console.log(` ${chalk.dim(e.at)} ${e.kind}`);
170
+ }
171
+ if (items.length < total) {
172
+ console.log('');
173
+ console.log(chalk.dim(` ${total - items.length} more — pass --limit ${total} to see all.`));
174
+ }
175
+ }
176
+ function formatSourceTag(source) {
177
+ switch (source) {
178
+ case 'tool-result':
179
+ return chalk.cyan('[tool] ');
180
+ case 'fs-watch':
181
+ return chalk.yellow('[fs-watch]');
182
+ case 'explicit':
183
+ return chalk.magenta('[explicit]');
184
+ default:
185
+ return chalk.dim('[unknown] ');
186
+ }
187
+ }
188
+ function formatDuration(ms) {
189
+ if (ms < 1000)
190
+ return `${ms}ms`;
191
+ const seconds = Math.floor(ms / 1000);
192
+ if (seconds < 60)
193
+ return `${seconds}s`;
194
+ const minutes = Math.floor(seconds / 60);
195
+ if (minutes < 60)
196
+ return `${minutes}m ${seconds % 60}s`;
197
+ const hours = Math.floor(minutes / 60);
198
+ return `${hours}h ${minutes % 60}m`;
199
+ }
200
+ //# sourceMappingURL=session.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session.js","sourceRoot":"","sources":["../../../src/cli/commands/session.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,OAAO,EAAE,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,aAAa,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AACnG,OAAO,EACL,WAAW,EACX,YAAY,GAGb,MAAM,uBAAuB,CAAC;AAE/B;;;;;;;;;;GAUG;AACH,MAAM,UAAU,eAAe;IAC7B,MAAM,OAAO,GAAG,OAAO;SACpB,OAAO,CAAC,SAAS,CAAC;SAClB,WAAW,CAAC,8DAA8D,CAAC;SAC3E,MAAM,CAAC,KAAK,IAAI,EAAE;QACjB,MAAM,UAAU,EAAE,CAAC;IACrB,CAAC,CAAC,CAAC;IAEL,OAAO;SACJ,OAAO,CAAC,SAAS,CAAC;SAClB,WAAW,CAAC,+DAA+D,CAAC;SAC5E,MAAM,CAAC,mBAAmB,EAAE,+CAA+C,CAAC;SAC5E,MAAM,CAAC,aAAa,EAAE,wBAAwB,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,QAAQ,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;SACvE,MAAM,CAAC,KAAK,EAAE,IAAyC,EAAE,EAAE;QAC1D,MAAM,UAAU,CAAC,IAAI,CAAC,CAAC;IACzB,CAAC,CAAC,CAAC;IAEL,OAAO;SACJ,OAAO,CAAC,QAAQ,CAAC;SACjB,WAAW,CAAC,0CAA0C,CAAC;SACvD,MAAM,CAAC,aAAa,EAAE,wBAAwB,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,QAAQ,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;SACvE,MAAM,CAAC,KAAK,EAAE,IAAwB,EAAE,EAAE;QACzC,MAAM,SAAS,CAAC,IAAI,CAAC,CAAC;IACxB,CAAC,CAAC,CAAC;IAEL,OAAO;SACJ,OAAO,CAAC,OAAO,CAAC;SAChB,WAAW,CAAC,mDAAmD,CAAC;SAChE,MAAM,CAAC,KAAK,IAAI,EAAE;QACjB,MAAM,QAAQ,EAAE,CAAC;IACnB,CAAC,CAAC,CAAC;AACP,CAAC;AAED,KAAK,UAAU,UAAU;IACvB,aAAa,EAAE,CAAC;IAChB,kBAAkB,EAAE,CAAC;IACrB,MAAM,QAAQ,GAAG,WAAW,EAAE,CAAC;IAC/B,MAAM,MAAM,GAAG,SAAS,EAAE,CAAC;IAC3B,IAAI,CAAC;QACH,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,MAAM,WAAW,CAAC,QAAQ,CAAC,CAAC;QAC/D,IAAI,MAAM,KAAK,MAAM,EAAE,CAAC;YACtB,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;YACjE,OAAO;QACT,CAAC;QACD,YAAY,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IAC9B,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;QACjF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;AACH,CAAC;AAED,KAAK,UAAU,UAAU,CAAC,IAAyC;IACjE,aAAa,EAAE,CAAC;IAChB,kBAAkB,EAAE,CAAC;IACrB,MAAM,QAAQ,GAAG,WAAW,EAAE,CAAC;IAC/B,MAAM,MAAM,GAAG,SAAS,EAAE,CAAC;IAC3B,IAAI,CAAC;QACH,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,GAAG,MAAM,WAAW,CAAC,QAAQ,CAAC,CAAC;QACtD,MAAM,GAAG,GAAG,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;QAC7C,MAAM,QAAQ,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC;QACjF,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,aAAa,CAAC,aAAa,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC;QACxE,MAAM,OAAO,GAAG,OAAO,IAAI,CAAC,KAAK,KAAK,QAAQ,IAAI,IAAI,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC;QAC5G,IAAI,MAAM,KAAK,MAAM,EAAE,CAAC;YACtB,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,SAAS,EAAE,IAAI,CAAC,EAAE,EAAE,KAAK,EAAE,QAAQ,CAAC,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;YACvG,OAAO;QACT,CAAC;QACD,YAAY,CAAC,IAAI,EAAE,OAAO,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;IAC/C,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;QACjF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;AACH,CAAC;AAED,KAAK,UAAU,SAAS,CAAC,IAAwB;IAC/C,aAAa,EAAE,CAAC;IAChB,kBAAkB,EAAE,CAAC;IACrB,MAAM,QAAQ,GAAG,WAAW,EAAE,CAAC;IAC/B,MAAM,MAAM,GAAG,SAAS,EAAE,CAAC;IAC3B,IAAI,CAAC;QACH,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,GAAG,MAAM,WAAW,CAAC,QAAQ,CAAC,CAAC;QACtD,MAAM,QAAQ,GAAG,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,OAAO,EAAE,CAAC;QAC5C,MAAM,OAAO,GAAG,OAAO,IAAI,CAAC,KAAK,KAAK,QAAQ,IAAI,IAAI,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC;QAC5G,IAAI,MAAM,KAAK,MAAM,EAAE,CAAC;YACtB,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,SAAS,EAAE,IAAI,CAAC,EAAE,EAAE,KAAK,EAAE,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;YACtG,OAAO;QACT,CAAC;QACD,WAAW,CAAC,IAAI,EAAE,OAAO,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;IAC9C,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;QACjF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;AACH,CAAC;AAED,KAAK,UAAU,QAAQ;IACrB,aAAa,EAAE,CAAC;IAChB,kBAAkB,EAAE,CAAC;IACrB,MAAM,QAAQ,GAAG,WAAW,EAAE,CAAC;IAC/B,MAAM,MAAM,GAAG,SAAS,EAAE,CAAC;IAC3B,IAAI,CAAC;QACH,MAAM,KAAK,GAAG,MAAM,YAAY,CAAC,QAAQ,CAAC,CAAC;QAC3C,IAAI,MAAM,KAAK,MAAM,EAAE,CAAC;YACtB,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;YACtE,OAAO;QACT,CAAC;QACD,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,kBAAkB,CAAC,CAAC,CAAC;QAC7C,OAAO,CAAC,GAAG,CAAC,qBAAqB,KAAK,CAAC,EAAE,EAAE,CAAC,CAAC;QAC7C,OAAO,CAAC,GAAG,CAAC,iBAAiB,KAAK,CAAC,SAAS,EAAE,CAAC,CAAC;IAClD,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;QACjF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;AACH,CAAC;AAED,SAAS,YAAY,CAAC,OAAgB,EAAE,OAAgB;IACtD,MAAM,WAAW,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IAClD,MAAM,KAAK,GAAG,MAAM,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC;IAC7E,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAChB,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC;IACnC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,0CAA0C,CAAC,CAAC,CAAC;IACnE,OAAO,CAAC,GAAG,CAAC,mBAAmB,OAAO,CAAC,EAAE,EAAE,CAAC,CAAC;IAC7C,OAAO,CAAC,GAAG,CAAC,mBAAmB,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,sBAAsB,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;IACvG,OAAO,CAAC,GAAG,CAAC,mBAAmB,OAAO,CAAC,SAAS,EAAE,CAAC,CAAC;IACpD,OAAO,CAAC,GAAG,CAAC,oBAAoB,OAAO,CAAC,cAAc,EAAE,CAAC,CAAC;IAC1D,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;QACnB,OAAO,CAAC,GAAG,CAAC,mBAAmB,cAAc,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IAC1D,CAAC;IACD,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAChB,OAAO,CAAC,GAAG,CAAC,oBAAoB,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC;IAC5E,OAAO,CAAC,GAAG,CAAC,oBAAoB,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC;IACzD,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAChB,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,0DAA0D,CAAC,CAAC,CAAC;AACrF,CAAC;AAED,SAAS,YAAY,CAAC,OAAgB,EAAE,KAAqB,EAAE,KAAa;IAC1E,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAChB,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,kBAAkB,OAAO,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;IACrE,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,0CAA0C,CAAC,CAAC,CAAC;IACnE,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACvB,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,yCAAyC,CAAC,CAAC,CAAC;QAClE,OAAO;IACT,CAAC;IACD,KAAK,MAAM,CAAC,IAAI,KAAK,EAAE,CAAC;QACtB,MAAM,SAAS,GAAG,eAAe,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;QAC5C,OAAO,CAAC,GAAG,CAAC,KAAK,SAAS,IAAI,CAAC,CAAC,IAAI,KAAK,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,KAAK,KAAK,CAAC,CAAC,aAAa,GAAG,CAAC,EAAE,CAAC,CAAC;IAC7F,CAAC;IACD,IAAI,KAAK,CAAC,MAAM,GAAG,KAAK,EAAE,CAAC;QACzB,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAChB,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,KAAK,GAAG,KAAK,CAAC,MAAM,wBAAwB,KAAK,cAAc,CAAC,CAAC,CAAC;IAC/F,CAAC;AACH,CAAC;AAED,SAAS,WAAW,CAAC,OAAgB,EAAE,KAA4B,EAAE,KAAa;IAChF,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAChB,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,WAAW,OAAO,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;IAC9D,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,0CAA0C,CAAC,CAAC,CAAC;IACnE,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACvB,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,2CAA2C,CAAC,CAAC,CAAC;QACpE,OAAO;IACT,CAAC;IACD,KAAK,MAAM,CAAC,IAAI,KAAK,EAAE,CAAC;QACtB,OAAO,CAAC,GAAG,CAAC,KAAK,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;IACjD,CAAC;IACD,IAAI,KAAK,CAAC,MAAM,GAAG,KAAK,EAAE,CAAC;QACzB,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAChB,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,KAAK,GAAG,KAAK,CAAC,MAAM,wBAAwB,KAAK,cAAc,CAAC,CAAC,CAAC;IAC/F,CAAC;AACH,CAAC;AAED,SAAS,eAAe,CAAC,MAA8B;IACrD,QAAQ,MAAM,EAAE,CAAC;QACf,KAAK,aAAa;YAChB,OAAO,KAAK,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;QAClC,KAAK,UAAU;YACb,OAAO,KAAK,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC;QACpC,KAAK,UAAU;YACb,OAAO,KAAK,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC;QACrC;YACE,OAAO,KAAK,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC;IACnC,CAAC;AACH,CAAC;AAED,SAAS,cAAc,CAAC,EAAU;IAChC,IAAI,EAAE,GAAG,IAAI;QAAE,OAAO,GAAG,EAAE,IAAI,CAAC;IAChC,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,GAAG,IAAI,CAAC,CAAC;IACtC,IAAI,OAAO,GAAG,EAAE;QAAE,OAAO,GAAG,OAAO,GAAG,CAAC;IACvC,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,GAAG,EAAE,CAAC,CAAC;IACzC,IAAI,OAAO,GAAG,EAAE;QAAE,OAAO,GAAG,OAAO,KAAK,OAAO,GAAG,EAAE,GAAG,CAAC;IACxD,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,GAAG,EAAE,CAAC,CAAC;IACvC,OAAO,GAAG,KAAK,KAAK,OAAO,GAAG,EAAE,GAAG,CAAC;AACtC,CAAC"}
@@ -9,8 +9,9 @@ import { reportUpgradeMarkdown } from '../../reporters/markdownReporter.js';
9
9
  export function registerUpgrade() {
10
10
  program
11
11
  .command('upgrade <package>')
12
- .description('Preview the impact of upgrading a package (offline - reads local CHANGELOG + importers)')
13
- .action(async (pkgName) => {
12
+ .description('Preview the impact of upgrading a package (offline by default - reads local CHANGELOG + importers)')
13
+ .option('--check-registry', 'fetch the actual latest version from npm (1.3+; otherwise latest = installed)')
14
+ .action(async (pkgName, opts) => {
14
15
  setupLogLevel();
15
16
  maybeCompactBanner();
16
17
  const rootPath = getRootPath();
@@ -19,7 +20,9 @@ export function registerUpgrade() {
19
20
  const spinner = format === 'console' ? ora(`Previewing ${pkgName}...`).start() : null;
20
21
  try {
21
22
  const scan = await scanRepository(rootPath, { ignore: config.ignore });
22
- const preview = await previewUpgrade(rootPath, pkgName, scan.files);
23
+ const preview = await previewUpgrade(rootPath, pkgName, scan.files, {
24
+ checkRegistry: opts.checkRegistry === true,
25
+ });
23
26
  if (spinner)
24
27
  spinner.stop();
25
28
  switch (format) {
@@ -1 +1 @@
1
- {"version":3,"file":"upgrade.js","sourceRoot":"","sources":["../../../src/cli/commands/upgrade.ts"],"names":[],"mappings":"AAAA,OAAO,GAAG,MAAM,KAAK,CAAC;AACtB,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,OAAO,EACL,OAAO,EACP,SAAS,EACT,WAAW,EACX,iBAAiB,EACjB,aAAa,EACb,kBAAkB,GACnB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,cAAc,EAAE,MAAM,iCAAiC,CAAC;AACjE,OAAO,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAC9D,OAAO,EAAE,aAAa,EAAE,MAAM,oCAAoC,CAAC;AACnE,OAAO,EAAE,iBAAiB,EAAE,MAAM,iCAAiC,CAAC;AACpE,OAAO,EAAE,qBAAqB,EAAE,MAAM,qCAAqC,CAAC;AAE5E,MAAM,UAAU,eAAe;IAC7B,OAAO;SACJ,OAAO,CAAC,mBAAmB,CAAC;SAC5B,WAAW,CAAC,yFAAyF,CAAC;SACtG,MAAM,CAAC,KAAK,EAAE,OAAe,EAAE,EAAE;QAChC,aAAa,EAAE,CAAC;QAChB,kBAAkB,EAAE,CAAC;QACrB,MAAM,QAAQ,GAAG,WAAW,EAAE,CAAC;QAC/B,MAAM,MAAM,GAAG,SAAS,EAAE,CAAC;QAC3B,MAAM,MAAM,GAAG,MAAM,iBAAiB,EAAE,CAAC;QACzC,MAAM,OAAO,GAAG,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,GAAG,CAAC,cAAc,OAAO,KAAK,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;QAEtF,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,MAAM,cAAc,CAAC,QAAQ,EAAE,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC;YACvE,MAAM,OAAO,GAAG,MAAM,cAAc,CAAC,QAAQ,EAAE,OAAO,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC;YACpE,IAAI,OAAO;gBAAE,OAAO,CAAC,IAAI,EAAE,CAAC;YAE5B,QAAQ,MAAM,EAAE,CAAC;gBACf,KAAK,MAAM;oBACT,iBAAiB,CAAC,OAAO,CAAC,CAAC;oBAC3B,MAAM;gBACR,KAAK,UAAU;oBACb,qBAAqB,CAAC,OAAO,CAAC,CAAC;oBAC/B,MAAM;gBACR;oBACE,aAAa,CAAC,OAAO,CAAC,CAAC;YAC3B,CAAC;QACH,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,OAAO;gBAAE,OAAO,CAAC,IAAI,CAAC,wBAAwB,CAAC,CAAC;YACpD,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;YACjF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;IACH,CAAC,CAAC,CAAC;AACP,CAAC"}
1
+ {"version":3,"file":"upgrade.js","sourceRoot":"","sources":["../../../src/cli/commands/upgrade.ts"],"names":[],"mappings":"AAAA,OAAO,GAAG,MAAM,KAAK,CAAC;AACtB,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,OAAO,EACL,OAAO,EACP,SAAS,EACT,WAAW,EACX,iBAAiB,EACjB,aAAa,EACb,kBAAkB,GACnB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,cAAc,EAAE,MAAM,iCAAiC,CAAC;AACjE,OAAO,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAC9D,OAAO,EAAE,aAAa,EAAE,MAAM,oCAAoC,CAAC;AACnE,OAAO,EAAE,iBAAiB,EAAE,MAAM,iCAAiC,CAAC;AACpE,OAAO,EAAE,qBAAqB,EAAE,MAAM,qCAAqC,CAAC;AAE5E,MAAM,UAAU,eAAe;IAC7B,OAAO;SACJ,OAAO,CAAC,mBAAmB,CAAC;SAC5B,WAAW,CAAC,oGAAoG,CAAC;SACjH,MAAM,CAAC,kBAAkB,EAAE,+EAA+E,CAAC;SAC3G,MAAM,CAAC,KAAK,EAAE,OAAe,EAAE,IAAiC,EAAE,EAAE;QACnE,aAAa,EAAE,CAAC;QAChB,kBAAkB,EAAE,CAAC;QACrB,MAAM,QAAQ,GAAG,WAAW,EAAE,CAAC;QAC/B,MAAM,MAAM,GAAG,SAAS,EAAE,CAAC;QAC3B,MAAM,MAAM,GAAG,MAAM,iBAAiB,EAAE,CAAC;QACzC,MAAM,OAAO,GAAG,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,GAAG,CAAC,cAAc,OAAO,KAAK,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;QAEtF,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,MAAM,cAAc,CAAC,QAAQ,EAAE,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC;YACvE,MAAM,OAAO,GAAG,MAAM,cAAc,CAAC,QAAQ,EAAE,OAAO,EAAE,IAAI,CAAC,KAAK,EAAE;gBAClE,aAAa,EAAE,IAAI,CAAC,aAAa,KAAK,IAAI;aAC3C,CAAC,CAAC;YACH,IAAI,OAAO;gBAAE,OAAO,CAAC,IAAI,EAAE,CAAC;YAE5B,QAAQ,MAAM,EAAE,CAAC;gBACf,KAAK,MAAM;oBACT,iBAAiB,CAAC,OAAO,CAAC,CAAC;oBAC3B,MAAM;gBACR,KAAK,UAAU;oBACb,qBAAqB,CAAC,OAAO,CAAC,CAAC;oBAC/B,MAAM;gBACR;oBACE,aAAa,CAAC,OAAO,CAAC,CAAC;YAC3B,CAAC;QACH,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,OAAO;gBAAE,OAAO,CAAC,IAAI,CAAC,wBAAwB,CAAC,CAAC;YACpD,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;YACjF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;IACH,CAAC,CAAC,CAAC;AACP,CAAC"}
package/dist/cli/index.js CHANGED
@@ -25,6 +25,7 @@ import { registerUpgrade } from './commands/upgrade.js';
25
25
  import { registerSearch } from './commands/search.js';
26
26
  import { registerCoverage } from './commands/coverage.js';
27
27
  import { registerMcp } from './commands/mcp.js';
28
+ import { registerSession } from './commands/session.js';
28
29
  import { registerBadge } from './commands/badge.js';
29
30
  import { registerHelp } from './commands/help.js';
30
31
  registerAnalyze();
@@ -52,6 +53,7 @@ registerUpgrade();
52
53
  registerSearch();
53
54
  registerCoverage();
54
55
  registerMcp();
56
+ registerSession();
55
57
  registerBadge();
56
58
  registerHelp();
57
59
  program.parse();
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACvC,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAC5D,OAAO,EAAE,oBAAoB,EAAE,MAAM,4BAA4B,CAAC;AAClE,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC1D,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC1D,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAC9D,OAAO,EAAE,oBAAoB,EAAE,MAAM,4BAA4B,CAAC;AAClE,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAC9D,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC1D,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC1D,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAElD,eAAe,EAAE,CAAC;AAClB,cAAc,EAAE,CAAC;AACjB,UAAU,EAAE,CAAC;AACb,YAAY,EAAE,CAAC;AACf,WAAW,EAAE,CAAC;AACd,YAAY,EAAE,CAAC;AACf,eAAe,EAAE,CAAC;AAClB,eAAe,EAAE,CAAC;AAClB,iBAAiB,EAAE,CAAC;AACpB,oBAAoB,EAAE,CAAC;AACvB,gBAAgB,EAAE,CAAC;AACnB,gBAAgB,EAAE,CAAC;AACnB,cAAc,EAAE,CAAC;AACjB,cAAc,EAAE,CAAC;AACjB,kBAAkB,EAAE,CAAC;AACrB,oBAAoB,EAAE,CAAC;AACvB,cAAc,EAAE,CAAC;AACjB,aAAa,EAAE,CAAC;AAChB,kBAAkB,EAAE,CAAC;AACrB,gBAAgB,EAAE,CAAC;AACnB,aAAa,EAAE,CAAC;AAChB,eAAe,EAAE,CAAC;AAClB,cAAc,EAAE,CAAC;AACjB,gBAAgB,EAAE,CAAC;AACnB,WAAW,EAAE,CAAC;AACd,aAAa,EAAE,CAAC;AAChB,YAAY,EAAE,CAAC;AAEf,OAAO,CAAC,KAAK,EAAE,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACvC,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAC5D,OAAO,EAAE,oBAAoB,EAAE,MAAM,4BAA4B,CAAC;AAClE,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC1D,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC1D,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAC9D,OAAO,EAAE,oBAAoB,EAAE,MAAM,4BAA4B,CAAC;AAClE,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAC9D,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC1D,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC1D,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAElD,eAAe,EAAE,CAAC;AAClB,cAAc,EAAE,CAAC;AACjB,UAAU,EAAE,CAAC;AACb,YAAY,EAAE,CAAC;AACf,WAAW,EAAE,CAAC;AACd,YAAY,EAAE,CAAC;AACf,eAAe,EAAE,CAAC;AAClB,eAAe,EAAE,CAAC;AAClB,iBAAiB,EAAE,CAAC;AACpB,oBAAoB,EAAE,CAAC;AACvB,gBAAgB,EAAE,CAAC;AACnB,gBAAgB,EAAE,CAAC;AACnB,cAAc,EAAE,CAAC;AACjB,cAAc,EAAE,CAAC;AACjB,kBAAkB,EAAE,CAAC;AACrB,oBAAoB,EAAE,CAAC;AACvB,cAAc,EAAE,CAAC;AACjB,aAAa,EAAE,CAAC;AAChB,kBAAkB,EAAE,CAAC;AACrB,gBAAgB,EAAE,CAAC;AACnB,aAAa,EAAE,CAAC;AAChB,eAAe,EAAE,CAAC;AAClB,cAAc,EAAE,CAAC;AACjB,gBAAgB,EAAE,CAAC;AACnB,WAAW,EAAE,CAAC;AACd,eAAe,EAAE,CAAC;AAClB,aAAa,EAAE,CAAC;AAChB,YAAY,EAAE,CAAC;AAEf,OAAO,CAAC,KAAK,EAAE,CAAC"}
@@ -0,0 +1,94 @@
1
+ /**
2
+ * Session — durable cross-invocation state for projscan (1.4+).
3
+ *
4
+ * Persisted at `.projscan-cache/session.json`. A new session starts when
5
+ * the previous session has been idle for longer than `IDLE_TIMEOUT_MS`
6
+ * (default 1 hour) or when none exists. Multiple agents calling the MCP
7
+ * server against the same project share the same session as long as
8
+ * they're within the idle window.
9
+ *
10
+ * Session purpose: let one agent answer "what has been touched since I
11
+ * arrived?" without having to call git or grep. Touched files come from
12
+ * (a) tool results that surface file paths, and (b) `notifications/file_changed`
13
+ * push events from `projscan mcp --watch`.
14
+ *
15
+ * Schema is versioned (`schemaVersion`) so future evolution can detect
16
+ * and migrate older session files instead of crashing.
17
+ */
18
+ export declare const SESSION_SCHEMA_VERSION = 1;
19
+ export declare const DEFAULT_IDLE_TIMEOUT_MS: number;
20
+ export type TouchSource = 'tool-result' | 'fs-watch' | 'explicit';
21
+ export interface SessionTouch {
22
+ /** Repo-relative path. Always POSIX-separator (`/`). */
23
+ file: string;
24
+ /** Where the touch came from. */
25
+ source: TouchSource;
26
+ /** ISO 8601 timestamp of the most recent touch. */
27
+ lastTouchedAt: string;
28
+ /** Total times this file has been touched in this session. */
29
+ count: number;
30
+ }
31
+ export interface SessionEvent {
32
+ /** ISO 8601 timestamp. */
33
+ at: string;
34
+ /** Free-form kind tag (e.g., "tool-call:projscan_hotspots"). */
35
+ kind: string;
36
+ /** Optional structured payload. */
37
+ data?: Record<string, unknown>;
38
+ }
39
+ export interface Session {
40
+ schemaVersion: number;
41
+ /** UUID assigned at session creation. Stable until idle expiry. */
42
+ id: string;
43
+ /** ISO 8601. */
44
+ startedAt: string;
45
+ /** ISO 8601. Updated on every recordTouch / recordEvent / save. */
46
+ lastActivityAt: string;
47
+ /** Map keyed by repo-relative file path. */
48
+ touchedFiles: Record<string, SessionTouch>;
49
+ /** Bounded event log. Caps at MAX_EVENTS most-recent. */
50
+ events: SessionEvent[];
51
+ }
52
+ /**
53
+ * Load the session for `rootPath`. If the on-disk session has been idle
54
+ * for longer than `idleTimeoutMs`, returns a fresh session. If no session
55
+ * exists, returns a fresh session. Failures to read or parse the file
56
+ * (corruption, schema mismatch) also produce a fresh session — this
57
+ * function never throws under normal operation.
58
+ *
59
+ * Returns the session object PLUS a `created` flag so the caller can tell
60
+ * whether they're attaching to an existing session or starting a new one.
61
+ */
62
+ export declare function loadSession(rootPath: string, idleTimeoutMs?: number): Promise<{
63
+ session: Session;
64
+ created: boolean;
65
+ }>;
66
+ /**
67
+ * Record a file touch in-place on the session. Updates the per-file
68
+ * count + lastTouchedAt and the session's lastActivityAt. Caller is
69
+ * responsible for `saveSession()` afterwards (batched persist).
70
+ *
71
+ * `file` should be repo-relative POSIX-separator. Paths with `..` or
72
+ * absolute paths are silently ignored — sessions only track in-repo state.
73
+ */
74
+ export declare function recordTouch(session: Session, file: string, source: TouchSource): void;
75
+ /**
76
+ * Append an event to the session's bounded log. The log keeps the last
77
+ * MAX_EVENTS entries and drops the oldest when over capacity.
78
+ */
79
+ export declare function recordEvent(session: Session, kind: string, data?: Record<string, unknown>): void;
80
+ /**
81
+ * Persist the session to disk. Best-effort; failures are swallowed so
82
+ * a transient write error doesn't break the calling tool.
83
+ *
84
+ * Last-write-wins semantics for now. If two MCP servers run against the
85
+ * same repo concurrently, the later writer overwrites the earlier; this
86
+ * is acceptable for the v1 scope (the multi-process case is rare and the
87
+ * downside is just a slightly stale touched-file count, not corruption).
88
+ */
89
+ export declare function saveSession(rootPath: string, session: Session): Promise<void>;
90
+ /**
91
+ * Reset the session: discard the on-disk file and return a fresh one.
92
+ * Useful for the `projscan_session { action: "reset" }` MCP path.
93
+ */
94
+ export declare function resetSession(rootPath: string): Promise<Session>;