@chappibunny/repolens 1.12.2 → 1.13.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 (53) hide show
  1. package/CHANGELOG.md +100 -0
  2. package/README.md +25 -36
  3. package/bin/repolens.js +21 -1
  4. package/package.json +20 -11
  5. package/src/ai/provider.js +1 -2
  6. package/src/analyzers/monorepo-detector.js +8 -20
  7. package/src/cli-args.js +60 -0
  8. package/src/cli.js +80 -100
  9. package/src/core/config-schema.js +47 -1
  10. package/src/core/config.js +5 -2
  11. package/src/core/diff.js +43 -20
  12. package/src/core/scan.js +24 -4
  13. package/src/delivery/comment.js +5 -2
  14. package/src/docs/write-doc-set.js +9 -7
  15. package/src/doctor.js +16 -3
  16. package/src/help.js +54 -0
  17. package/src/index.js +13 -0
  18. package/src/init.js +99 -62
  19. package/src/integrations/discord.js +16 -2
  20. package/src/migrate.js +2 -2
  21. package/src/publishers/confluence.js +17 -3
  22. package/src/publishers/github-wiki.js +61 -32
  23. package/src/publishers/index.js +24 -9
  24. package/src/publishers/markdown.js +3 -2
  25. package/src/publishers/notion.js +10 -4
  26. package/src/publishers/obsidian.js +716 -0
  27. package/src/utils/branch.js +37 -42
  28. package/src/utils/github-repo.js +8 -0
  29. package/src/utils/interactive.js +20 -0
  30. package/src/utils/logger.js +5 -1
  31. package/src/utils/retry.js +30 -6
  32. package/src/utils/safe-path.js +50 -0
  33. package/src/utils/secrets.js +3 -1
  34. package/src/utils/telemetry.js +34 -6
  35. package/src/utils/update-check.js +23 -6
  36. package/src/utils/validate.js +70 -1
  37. package/src/utils/yaml.js +27 -0
  38. package/src/watch.js +1 -1
  39. package/docs/AI.md +0 -329
  40. package/docs/ARCHITECTURE.md +0 -145
  41. package/docs/CONFIGURATION.md +0 -121
  42. package/docs/DEVELOPMENT.md +0 -79
  43. package/docs/ENVIRONMENT.md +0 -79
  44. package/docs/INSTALLATION.md +0 -45
  45. package/docs/KNOWN_ISSUES.md +0 -309
  46. package/docs/MIGRATION.md +0 -311
  47. package/docs/ONBOARDING.md +0 -382
  48. package/docs/PRODUCTION_CHECKLIST.md +0 -470
  49. package/docs/RELEASE.md +0 -76
  50. package/docs/ROADMAP.md +0 -151
  51. package/docs/STABILITY.md +0 -265
  52. package/docs/TELEMETRY.md +0 -166
  53. package/docs/TROUBLESHOOTING.md +0 -377
package/CHANGELOG.md CHANGED
@@ -2,6 +2,106 @@
2
2
 
3
3
  All notable changes to RepoLens will be documented in this file.
4
4
 
5
+ ## 1.13.0
6
+
7
+ ### Obsidian vault publisher
8
+
9
+ Publish generated docs into a folder in a local Obsidian vault. No network calls and no credentials.
10
+
11
+ - New publisher: `obsidian` (`src/publishers/obsidian.js`)
12
+ - Notes get YAML frontmatter (`title`, `aliases`, `tags`, `type`, `project`, `branch`) and an index note (`RepoLens.md`) with wikilinks
13
+ - Markdown links to generated pages become `[[wikilinks]]`. External URLs and fenced code blocks are left alone
14
+ - Re-running publish is idempotent: identical notes are not rewritten, and notes RepoLens previously published but no longer generates are removed. Other files in the folder stay
15
+ - `obsidian.folder` must resolve inside `obsidian.vault`. Absolute folders, `..`, and symlinks that leave the vault are rejected
16
+ - Optional config on schema v1: `obsidian.vault`, `obsidian.folder`, `obsidian.branches`, `obsidian.index`, `obsidian.frontmatter`, `obsidian.wikilinks`. Existing configs keep working
17
+ - `repolens init` can select the Obsidian publisher and ask for a vault path
18
+ - `repolens doctor` checks that `obsidian.vault` is set and does not require env vars for it
19
+ - Wikilink rewriting is a linear scan with bounded label and target lengths. The previous link regex was polynomial on hostile markdown (CodeQL)
20
+
21
+ ### Security
22
+
23
+ - `repolens init` workflows grant `contents: read` and `pull-requests: write`. Architecture Diff comments use the Issues comments API on the pull request, which accepts `pull-requests: write`. `contents: write` is not granted. GitHub Wiki pushes still need `contents: write` on that job only
24
+
25
+ ### Docs
26
+
27
+ - `docs/ROADMAP.md` now summarizes what shipped from v1.1.0 through v1.12.4, and marks the VS Code extension and the Obsidian publisher as shipped
28
+ - README roadmap matches `docs/ROADMAP.md`
29
+ - The README no longer hard-codes the release version. It points at `package.json`, which is the source of the version
30
+ - Test totals are no longer hard-coded in the README, development guide, or security guide. The tests badge follows the `test.yml` workflow status
31
+ - Handbook pages (README, `docs/*`, `SECURITY.md`, `CONTRIBUTING.md`, and `.github/copilot-instructions.md`) describe 1.13.0 and the Obsidian publisher
32
+ - Corrected drift against the code: `scan.ignore` (there is no `scan.exclude`), `publish` has no `--dry-run`, CI installs with `npm ci`, the publish workflow is `repolens.yml`, diagrams are Unicode, and local files use two filename schemes. `outputs.pages` stays the frozen allowlist; the generator still emits the full 17-document plan unless `documentation.sections` or `features` filters it
33
+
34
+ ## 1.12.4
35
+
36
+ ### Security
37
+
38
+ - GitHub Wiki git commands use an argument array. The token is passed with `http.extraheader` and is no longer written into the clone URL or `origin` remote
39
+ - Wiki page names and generated document names cannot leave their output directory
40
+ - `documentation.output_dir` must stay inside the repository. Absolute paths and `..` are rejected
41
+ - Scan results that resolve outside the repository root are dropped, and absolute `scan.include` patterns are rejected
42
+ - Monorepo workspace globs that point outside the repository are ignored
43
+ - YAML configs are size-capped and alias-capped before `js-yaml` parses them. The js-yaml 4 schema is unchanged
44
+ - Notion, Confluence, GitHub, and Discord error bodies are truncated and redacted before they are logged
45
+ - Confluence URLs with embedded credentials, or a scheme other than http/https, are rejected
46
+ - Discord webhooks must be `https` and time out after 15 seconds
47
+ - AI startup logs no longer print a prefix of the API key
48
+ - Secret detection covers fine-grained GitHub tokens (`github_pat_`), `gho_` / `ghu_` / `ghr_` tokens, and npm tokens
49
+ - `Retry-After` values that are not seconds or an HTTP date no longer become `NaN` delays
50
+ - Browser signup links are opened without a shell
51
+ - GitHub Actions: test and security jobs are `contents: read`. `id-token: write` is only on the npm publish job. The token-present check no longer prints the token length
52
+ - Test workflow runs on Node.js 18, 20, and 22. Node 18 runs `scripts/node18-runtime.js` because Vitest 4 cannot start there (`util.styleText` needs Node 20.12). Node 20 and 22 run the full Vitest suite
53
+
54
+ ### Fixes
55
+
56
+ - Update checks query `@chappibunny/repolens` on the npm registry. The cache file uses the real home directory on Windows and Unix
57
+ - `repolens init` in CI, tests, and non-TTY sessions scaffolds files instead of exiting before writing anything
58
+ - `repolens demo --target` looks for `.repolens.yml` from the target directory, so a demo of another folder does not scan the shell's current repository
59
+ - `repolens feedback` exits 2 when there is no terminal, and exits 1 when sending fails
60
+ - Unknown flags and missing `--config` / `--target` values exit 2. `--help` and `--version` work before or after the command
61
+ - `repolens --version` and `repolens --help` from the installed binary do not load the scanner or telemetry
62
+ - Empty repositories finish the scan with a warning instead of looking like a hung run. Binary files and files over 1 MB are not parsed as source
63
+ - Git renames in `git diff --name-status` record the new path. Unsafe diff refs are ignored instead of being interpolated into a shell command. A missing repo or ref no longer prints git's usage text, and the diff runs in the scanned repository rather than whatever directory the shell happened to be in
64
+ - Branch filters escape regex metacharacters, so a pattern like `release/*)` does not throw
65
+ - Confluence page-cache filenames cannot contain path separators from the branch name
66
+ - Notion block pagination stops if a cursor repeats
67
+
68
+ ### Dependencies
69
+
70
+ - `dotenv` 18.0.8 (`import "dotenv/config"` is unchanged; Node `-r dotenv/config` preload was removed upstream)
71
+ - `tinyexec` 1.3.1 (dev dependency, Node 18+)
72
+ - js-yaml stays on 4.x, Vitest stays on 4.x, and `@sentry/node` stays on 10.x so `engines` can remain `>=18`
73
+
74
+ ## 1.12.3
75
+
76
+ ### Security
77
+
78
+ - Bump runtime `js-yaml` to 4.3.2 (merge-key CPU limit and `!!omap` duplicate-key complexity fixes)
79
+ - Bump transitive `source-map-js` to 1.2.2 (CVE-2026-93749)
80
+ - Bump transitive `postcss` to 8.5.31 (Dependabot #8 requested 8.5.25; 8.5.31 includes those fixes)
81
+ - Bump `vitest` and `@vitest/mocker` to 4.1.12 (Dependabot #10 requested 4.1.11; 4.1.12 is the current 4.1 release and includes that fix)
82
+ - No patched `braces` release exists for GHSA-vfj7-8cjw-p6xm (latest is 3.0.3, which the advisory covers). The release audit now checks production dependencies only and waives that one advisory. Any other high or critical runtime finding still fails the job. See `scripts/audit-waivers.json` and `SECURITY.md`.
83
+
84
+ ### Packaging
85
+
86
+ - Require Node.js `>=18` (`engines`), matching built-in `fetch` and `node:readline/promises`
87
+ - Add an `exports` map. `main` now points at `src/index.js`, so importing the package does not run the CLI
88
+ - Keep the `repolens` binary on `bin/repolens.js`
89
+ - Replace `node-fetch` with the built-in `fetch` (`AbortSignal.timeout` for the update check)
90
+ - Move `@sentry/node` to `optionalDependencies`. Telemetry and feedback no-op when it is not installed
91
+ - Stop publishing the `docs/` handbook. Runtime document generation stays in `src/docs/`
92
+
93
+ ### CI
94
+
95
+ - Publish Documentation runs on pushes to `main` and manual dispatch, and skips `dependabot[bot]`, because publisher secrets are not available on Dependabot branches
96
+ - Add a Test workflow that runs `npm test` on pull requests and branch pushes
97
+ - CI installs with `npm ci` so the committed lockfile is what runs. A fresh `npm install` on npm 10 crashes while resolving Vitest 4.1 optional peers
98
+ - Release workflow publishes with `npm publish --provenance` (requires an `NPM_TOKEN` Actions secret)
99
+
100
+ ### Fixes
101
+
102
+ - `docs:publish` discovers `.repolens.yml` from the current directory instead of `../../.repolens.yml`
103
+ - `release:check` runs `npm pack --dry-run`
104
+
5
105
  ## 1.12.0
6
106
 
7
107
  ### 🛡️ Security Pattern Detection
package/README.md CHANGED
@@ -10,14 +10,14 @@
10
10
 
11
11
  [![npm version](https://img.shields.io/npm/v/@chappibunny/repolens)](https://www.npmjs.com/package/@chappibunny/repolens)
12
12
  [![VS Code Extension](https://img.shields.io/visual-studio-marketplace/v/CHAPIBUNNY.repolens-architecture?label=VS%20Code)](https://marketplace.visualstudio.com/items?itemName=CHAPIBUNNY.repolens-architecture)
13
- [![Tests](https://img.shields.io/badge/tests-480%20passing-brightgreen)](https://github.com/CHAPIBUNNY/repolens/actions)
13
+ [![Tests](https://img.shields.io/github/actions/workflow/status/CHAPIBUNNY/repolens/test.yml?label=tests)](https://github.com/CHAPIBUNNY/repolens/actions/workflows/test.yml)
14
14
  [![License](https://img.shields.io/badge/license-MIT-blue)](LICENSE)
15
15
 
16
16
  **Your architecture docs are already outdated.** RepoLens fixes that.
17
17
 
18
- RepoLens scans your repository, generates living architecture documentation, and publishes it to Notion, Confluence, GitHub Wiki, or Markdown — automatically on every push. Engineers get technical docs. Stakeholders get readable system overviews. Nobody writes a word.
18
+ RepoLens scans your repository, generates living architecture documentation, and publishes it to Notion, Confluence, GitHub Wiki, Obsidian, or Markdown — automatically on every push. Engineers get technical docs. Stakeholders get readable system overviews. Nobody writes a word.
19
19
 
20
- > Stable as of v1.0 — [API guarantees](docs/STABILITY.md) · [Security hardened](SECURITY.md) · v1.12.0
20
+ > Stable as of v1.0 — [API guarantees](docs/STABILITY.md) · [Security hardened](SECURITY.md) · version from [`package.json`](package.json)
21
21
 
22
22
  ---
23
23
 
@@ -50,7 +50,7 @@ Plus framework detection: **Django** · **FastAPI** · **Flask** · **Gin** · *
50
50
 
51
51
  ## 🚀 Quick Start (60 seconds)
52
52
 
53
- **Step 1: Install**
53
+ **Step 1: Install** (Node.js 18+)
54
54
  ```bash
55
55
  npm install @chappibunny/repolens
56
56
  ```
@@ -83,7 +83,7 @@ CONFLUENCE_SPACE_KEY=DOCS
83
83
  npx @chappibunny/repolens publish
84
84
  ```
85
85
 
86
- **Done!** Your docs are now live in Notion, Confluence, and/or `.repolens/` directory.
86
+ **Done!** Numbered notes and the Markdown publisher’s files are in `.repolens/`. Notion, Confluence, GitHub Wiki, and Obsidian publish only when you configure them. Obsidian needs `obsidian.vault` in `.repolens.yml` and does not use an API key.
87
87
 
88
88
  **🔄 Upgrading from v0.3.0 or earlier?**
89
89
  Run `npx @chappibunny/repolens migrate` to automatically update your workflow files. See [MIGRATION.md](docs/MIGRATION.md) for details.
@@ -96,9 +96,9 @@ Run `npx @chappibunny/repolens migrate` to automatically update your workflow fi
96
96
 
97
97
  | Audience | Documents |
98
98
  |---|---|
99
- | **Stakeholders** (founders, PMs, ops) | Executive Summary · Business Domains · Data Flows |
100
- | **Everyone** | System Overview · Developer Onboarding · Change Impact · Architecture Drift · Code Health |
101
- | **Engineers** | Architecture Overview · Module Catalog · API Surface · Route Map · System Map · GraphQL Schema · TypeScript Type Graph · Dependency Graph · Security Hotspots |
99
+ | **Stakeholders** | Executive Summary |
100
+ | **Mixed** | System Overview · Business Domains · Route Map · Data Flows · Change Impact · System Map · Architecture Drift · Code Health |
101
+ | **Engineers** | Architecture Overview · Module Catalog · API Surface · Developer Onboarding · GraphQL Schema · TypeScript Type Graph · Dependency Graph · Security Hotspots |
102
102
 
103
103
  **Two modes:** Deterministic (free, fast, always works) or AI-Enhanced (optional — GitHub Models, OpenAI, Anthropic, Google, Azure, Ollama).
104
104
 
@@ -110,7 +110,7 @@ Run `npx @chappibunny/repolens migrate` to automatically update your workflow fi
110
110
  |---|---|
111
111
  | 🤖 **Autonomous** | Runs on every push via GitHub Actions — docs stay evergreen |
112
112
  | 👥 **Multi-Audience** | Technical docs + stakeholder-readable overviews from one scan |
113
- | 📤 **Multi-Publisher** | Notion, Confluence, GitHub Wiki, Markdown — or all four at once |
113
+ | 📤 **Multi-Publisher** | Notion, Confluence, GitHub Wiki, Obsidian, Markdown — together or one at a time |
114
114
  | 🧠 **AI-Assisted** | Optional AI with zero-hallucination policy (structured context only) |
115
115
  | 🔍 **Smart Detection** | Frameworks, domains, data flows, dependencies, drift — all automatic |
116
116
  | 🔌 **Extensible** | Plugin system for custom renderers, publishers, and hooks |
@@ -155,7 +155,7 @@ The extension reads your `.repolens.yml` configuration and provides an interacti
155
155
 
156
156
  ## 🎓 Onboarding Guide
157
157
 
158
- Step-by-step setup for publishers, AI features, Notion, Confluence, GitHub Wiki, Discord, and CI/CD automation.
158
+ Step-by-step setup for publishers, AI features, Notion, Confluence, GitHub Wiki, Obsidian, Discord, and CI/CD automation.
159
159
 
160
160
  **[→ Full Onboarding Guide](docs/ONBOARDING.md)**
161
161
 
@@ -183,28 +183,11 @@ Step-by-step setup for publishers, AI features, Notion, Confluence, GitHub Wiki,
183
183
 
184
184
  ## 📸 Example Output
185
185
 
186
- ### System Map with Dependencies
187
-
188
- ```mermaid
189
- graph LR
190
- CLI[bin/repolens<br/>1 file] --> Core[src/core<br/>4 files]
191
- Publishers[src/publishers<br/>6 files] --> Core
192
- Publishers --> Renderers[src/renderers<br/>4 files]
193
- Publishers --> Utils[src/utils<br/>10 files]
194
- Renderers --> Core
195
- Delivery[src/delivery<br/>1 file] --> Publishers
196
- Tests[tests<br/>15 files] -. tests .-> CLI
197
- Tests -. tests .-> Core
198
- Tests -. tests .-> Publishers
199
-
200
- style CLI fill:#9b59b6,color:#fff
201
- style Core fill:#f39c12,color:#000
202
- style Publishers fill:#27ae60,color:#fff
203
- style Renderers fill:#27ae60,color:#fff
204
- style Delivery fill:#16a085,color:#fff
205
- style Utils fill:#95a5a6,color:#000
206
- style Tests fill:#e67e22,color:#fff
207
- ```
186
+ ### System map
187
+
188
+ Published system maps are Unicode diagrams drawn from import edges. RepoLens does not emit Mermaid.
189
+
190
+ The publisher set in 1.13.0 is Notion, Confluence, GitHub Wiki, Obsidian, and Markdown (`src/publishers`, 7 files). Layout and counts are in [ARCHITECTURE.md](docs/ARCHITECTURE.md).
208
191
 
209
192
  ### System Overview (Technical Profile)
210
193
 
@@ -255,11 +238,11 @@ When you open a pull request, RepoLens posts:
255
238
 
256
239
  | Guide | Description |
257
240
  |---|---|
258
- | [Onboarding Guide](docs/ONBOARDING.md) | Step-by-step setup: publishers, AI, Notion, Confluence, Discord |
241
+ | [Onboarding Guide](docs/ONBOARDING.md) | Step-by-step setup: publishers, AI, Notion, Confluence, GitHub Wiki, Obsidian, Discord |
259
242
  | [Configuration](docs/CONFIGURATION.md) | Complete `.repolens.yml` schema and examples |
260
243
  | [Environment Variables](docs/ENVIRONMENT.md) | All env vars by publisher and feature |
261
244
  | [Architecture](docs/ARCHITECTURE.md) | Pipeline diagram, project structure |
262
- | [Development](docs/DEVELOPMENT.md) | Setup, tests (380 across 22 files), release process |
245
+ | [Development](docs/DEVELOPMENT.md) | Setup, tests, and the release process |
263
246
  | [Security](SECURITY.md) | Threat model, secret detection, validation layers |
264
247
  | [Telemetry](docs/TELEMETRY.md) | Opt-in privacy-first usage analytics |
265
248
  | [Troubleshooting](docs/TROUBLESHOOTING.md) | Common issues and fixes |
@@ -285,10 +268,16 @@ v1.0+ features complete — CLI, config schema, and plugin interface are frozen.
285
268
 
286
269
  **Completed:**
287
270
  - [x] VS Code extension ([available on Marketplace](https://marketplace.visualstudio.com/items?itemName=CHAPIBUNNY.repolens-architecture))
271
+ - [x] Obsidian vault publisher (local notes with wikilinks and frontmatter)
288
272
 
289
273
  **Next:**
290
- - [ ] Obsidian publisher
291
- - [ ] GitHub App
274
+ - [ ] GitHub App for automated setup and status checks
275
+ - [ ] Slack notifications
276
+ - [ ] Custom webhook support
277
+ - [ ] Cross-repository architecture analysis
278
+ - [ ] Security boundary validation
279
+ - [ ] Interactive HTML documentation output
280
+ - [ ] Architecture decision records (ADR) generation
292
281
 
293
282
  See [ROADMAP.md](docs/ROADMAP.md) for detailed planning.
294
283
 
package/bin/repolens.js CHANGED
@@ -1,2 +1,22 @@
1
1
  #!/usr/bin/env node
2
- import "../src/cli.js";
2
+ import { readFileSync } from "node:fs";
3
+
4
+ const argv = process.argv.slice(2);
5
+ const wantsHelp = argv.includes("--help") || argv.includes("-h") || argv[0] === "help";
6
+ const wantsVersion = argv.includes("--version") || argv.includes("-v") || argv[0] === "version";
7
+
8
+ // --version and --help must not load the scanner, publishers, or telemetry.
9
+ if (wantsVersion && !wantsHelp) {
10
+ const pkg = JSON.parse(readFileSync(new URL("../package.json", import.meta.url), "utf8"));
11
+ console.log(pkg.version || "0.0.0");
12
+ process.exit(0);
13
+ }
14
+
15
+ if (wantsHelp) {
16
+ const { printHelp } = await import("../src/help.js");
17
+ printHelp();
18
+ process.exit(0);
19
+ }
20
+
21
+ const { runCli } = await import("../src/cli.js");
22
+ runCli();
package/package.json CHANGED
@@ -1,17 +1,23 @@
1
1
  {
2
2
  "name": "@chappibunny/repolens",
3
- "version": "1.12.2",
3
+ "version": "1.13.0",
4
4
  "description": "AI-assisted documentation intelligence system for technical and non-technical audiences",
5
5
  "license": "MIT",
6
6
  "type": "module",
7
- "main": "src/cli.js",
7
+ "engines": {
8
+ "node": ">=18"
9
+ },
10
+ "main": "./src/index.js",
11
+ "exports": {
12
+ ".": "./src/index.js",
13
+ "./package.json": "./package.json"
14
+ },
8
15
  "bin": {
9
16
  "repolens": "bin/repolens.js"
10
17
  },
11
18
  "files": [
12
19
  "bin",
13
20
  "src",
14
- "docs",
15
21
  "package.json",
16
22
  "README.md",
17
23
  "CHANGELOG.md"
@@ -30,6 +36,7 @@
30
36
  "metrics",
31
37
  "team-collaboration",
32
38
  "discord",
39
+ "obsidian",
33
40
  "ai-documentation"
34
41
  ],
35
42
  "repository": {
@@ -47,26 +54,28 @@
47
54
  },
48
55
  "scripts": {
49
56
  "test:notion": "node notion-test.js",
50
- "docs:publish": "node src/cli.js publish --config ../../.repolens.yml",
57
+ "docs:publish": "node bin/repolens.js publish",
51
58
  "init:test": "node src/cli.js init --target /tmp/repolens-init-test",
52
59
  "test": "NODE_ENV=test vitest run --no-watch --reporter=verbose",
53
60
  "pack:check": "npm pack",
54
- "release:check": "npm test && npm pack",
61
+ "audit:runtime": "node scripts/audit-runtime.js",
62
+ "release:check": "npm test && npm run audit:runtime && npm pack --dry-run",
55
63
  "test:install": "./test-install.sh",
56
64
  "release:patch": "npm version patch",
57
65
  "release:minor": "npm version minor",
58
66
  "release:major": "npm version major"
59
67
  },
60
68
  "dependencies": {
61
- "@sentry/node": "^10.42.0",
62
- "dotenv": "^17.3.1",
69
+ "dotenv": "^18.0.8",
63
70
  "fast-glob": "^3.3.3",
64
- "js-yaml": "^4.1.0",
65
- "node-fetch": "^3.3.2"
71
+ "js-yaml": "^4.3.2"
72
+ },
73
+ "optionalDependencies": {
74
+ "@sentry/node": "^10.42.0"
66
75
  },
67
76
  "devDependencies": {
68
- "tinyexec": "1.0.2",
69
- "vitest": "^4.1.0"
77
+ "tinyexec": "^1.3.1",
78
+ "vitest": "^4.1.11"
70
79
  },
71
80
  "overrides": {
72
81
  "yauzl": ">=3.2.1",
@@ -83,8 +83,7 @@ export async function generateText({ system, user, temperature, maxTokens, confi
83
83
 
84
84
  // Log configuration once per run
85
85
  if (!hasLoggedConfig) {
86
- const keyPreview = apiKey.substring(0, 8) + "...";
87
- info(`AI Config: provider=${provider}, model=${model}, key=${keyPreview}`);
86
+ info(`AI Config: provider=${provider}, model=${model}, key=set`);
88
87
  hasLoggedConfig = true;
89
88
  }
90
89
 
@@ -4,6 +4,8 @@
4
4
  import fs from "node:fs/promises";
5
5
  import path from "node:path";
6
6
  import { info } from "../utils/logger.js";
7
+ import { isInsideRoot } from "../utils/safe-path.js";
8
+ import { loadYaml } from "../utils/yaml.js";
7
9
 
8
10
  /**
9
11
  * Detect monorepo workspaces in a repository.
@@ -68,24 +70,8 @@ async function detectPnpmWorkspaces(repoRoot) {
68
70
  try {
69
71
  const yamlPath = path.join(repoRoot, "pnpm-workspace.yaml");
70
72
  const raw = await fs.readFile(yamlPath, "utf8");
71
-
72
- // Simple YAML parsing for packages array (avoid adding js-yaml dependency for this)
73
- const patterns = [];
74
- let inPackages = false;
75
- for (const line of raw.split("\n")) {
76
- const trimmed = line.trim();
77
- if (trimmed === "packages:") {
78
- inPackages = true;
79
- continue;
80
- }
81
- if (inPackages) {
82
- if (trimmed.startsWith("- ")) {
83
- patterns.push(trimmed.slice(2).replace(/['"]/g, "").trim());
84
- } else if (trimmed && !trimmed.startsWith("#")) {
85
- break; // End of packages list
86
- }
87
- }
88
- }
73
+ const doc = loadYaml(raw);
74
+ const patterns = Array.isArray(doc?.packages) ? doc.packages.filter((item) => typeof item === "string") : [];
89
75
 
90
76
  return await resolveWorkspacePatterns(repoRoot, patterns);
91
77
  } catch {
@@ -116,8 +102,10 @@ async function resolveWorkspacePatterns(repoRoot, patterns) {
116
102
  for (const pattern of patterns) {
117
103
  // Convert glob pattern to directory search
118
104
  // e.g. "packages/*" → list dirs in packages/
119
- const basePath = pattern.replace(/\/?\*.*$/, "");
120
- const searchDir = path.join(repoRoot, basePath);
105
+ if (typeof pattern !== "string" || pattern.includes("\0")) continue;
106
+ const basePath = pattern.replace(/\/?\*.*$/, "") || ".";
107
+ const searchDir = path.resolve(repoRoot, basePath);
108
+ if (!isInsideRoot(repoRoot, searchDir)) continue;
121
109
 
122
110
  try {
123
111
  const entries = await fs.readdir(searchDir, { withFileTypes: true });
@@ -0,0 +1,60 @@
1
+ const FLAGS_WITH_VALUE = new Set(["--config", "--target"]);
2
+ const BOOLEAN_FLAGS = new Set([
3
+ "--quick",
4
+ "--non-interactive",
5
+ "--dry-run",
6
+ "--force",
7
+ "--verbose",
8
+ "--help",
9
+ "-h",
10
+ "--version",
11
+ "-v",
12
+ ]);
13
+
14
+ /**
15
+ * Parse RepoLens argv (already sliced past node and the script path).
16
+ * Global options may appear before or after the command.
17
+ */
18
+ export function parseCliArgs(argv) {
19
+ const flags = {};
20
+ const positionals = [];
21
+ const errors = [];
22
+
23
+ for (let i = 0; i < argv.length; i += 1) {
24
+ const arg = argv[i];
25
+ if (arg === "--") {
26
+ positionals.push(...argv.slice(i + 1));
27
+ break;
28
+ }
29
+ if (arg.startsWith("-")) {
30
+ if (FLAGS_WITH_VALUE.has(arg)) {
31
+ const value = argv[i + 1];
32
+ if (value == null || value.startsWith("-")) {
33
+ errors.push(`${arg} requires a value`);
34
+ } else {
35
+ flags[arg] = value;
36
+ i += 1;
37
+ }
38
+ } else if (BOOLEAN_FLAGS.has(arg)) {
39
+ flags[arg] = true;
40
+ } else {
41
+ errors.push(`Unknown option: ${arg}`);
42
+ }
43
+ } else {
44
+ positionals.push(arg);
45
+ }
46
+ }
47
+
48
+ if (positionals.length > 1) {
49
+ errors.push(`Unexpected argument: ${positionals.slice(1).join(" ")}`);
50
+ }
51
+
52
+ const command = positionals[0];
53
+ return {
54
+ command,
55
+ flags,
56
+ errors,
57
+ help: Boolean(flags["--help"] || flags["-h"] || command === "help"),
58
+ version: Boolean(flags["--version"] || flags["-v"] || command === "version"),
59
+ };
60
+ }