github-portfolio-analyzer 1.1.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/CHANGELOG.md +26 -0
- package/README.md +686 -0
- package/analyzer.manifest.json +18 -0
- package/bin/github-portfolio-analyzer.js +8 -0
- package/package.json +39 -0
- package/schemas/portfolio-report.schema.json +266 -0
- package/src/cli.js +103 -0
- package/src/commands/analyze.js +172 -0
- package/src/commands/buildPortfolio.js +8 -0
- package/src/commands/ingestIdeas.js +8 -0
- package/src/commands/report.js +202 -0
- package/src/config.js +18 -0
- package/src/core/classification.js +47 -0
- package/src/core/ideas.js +170 -0
- package/src/core/portfolio.js +101 -0
- package/src/core/presentationOverrides.js +40 -0
- package/src/core/report.js +788 -0
- package/src/core/scoring.js +92 -0
- package/src/core/taxonomy.js +155 -0
- package/src/errors.js +7 -0
- package/src/github/client.js +76 -0
- package/src/github/repo-inspection.js +87 -0
- package/src/github/repos.js +58 -0
- package/src/io/csv.js +61 -0
- package/src/io/files.js +39 -0
- package/src/io/markdown.js +153 -0
- package/src/io/report.js +187 -0
- package/src/utils/args.js +32 -0
- package/src/utils/concurrency.js +26 -0
- package/src/utils/header.js +48 -0
- package/src/utils/nextAction.js +28 -0
- package/src/utils/output.js +15 -0
- package/src/utils/retry.js +67 -0
- package/src/utils/slug.js +8 -0
- package/src/utils/time.js +26 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
## [Unreleased]
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
- Colored terminal output — progress, success, warning, and error states with ANSI colors
|
|
9
|
+
- Terminal header with ASCII art, version info, user, token status, and policy status
|
|
10
|
+
- Per-repository progress logging during `analyze` (Analyzing N/total: repo-name)
|
|
11
|
+
- Elapsed time in analyze completion summary
|
|
12
|
+
- Fallback count in analyze summary when structural inspection fails
|
|
13
|
+
- Fatal error messages for missing token, auth failure, and rate limit
|
|
14
|
+
|
|
15
|
+
## [1.0.0] — 2026-03-31
|
|
16
|
+
|
|
17
|
+
### Added
|
|
18
|
+
- Initial public release
|
|
19
|
+
- `analyze` command: fetch and score GitHub repositories into inventory outputs
|
|
20
|
+
- `ingest-ideas` command: normalize and score project ideas from JSON input
|
|
21
|
+
- `build-portfolio` command: merge repos and ideas into unified portfolio artifacts
|
|
22
|
+
- `report` command: generate decision-oriented reports (JSON, Markdown, ASCII)
|
|
23
|
+
- `--version` flag
|
|
24
|
+
- `--quiet`, `--strict`, `--explain`, `--format`, `--policy`, `--output-dir` flags
|
|
25
|
+
- JSON Schema for `portfolio-report.json` at `schemas/portfolio-report.schema.json`
|
|
26
|
+
- `analyzer.manifest.json` integration contract for external orchestrators
|
package/README.md
ADDED
|
@@ -0,0 +1,686 @@
|
|
|
1
|
+
# github-portfolio-analyzer
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/github-portfolio-analyzer)
|
|
4
|
+
[](https://nodejs.org)
|
|
5
|
+
[](./LICENSE)
|
|
6
|
+
[](https://www.npmjs.com/package/github-portfolio-analyzer)
|
|
7
|
+
|
|
8
|
+
Build a decision-ready developer portfolio from real GitHub repositories and planned ideas in one deterministic CLI workflow.
|
|
9
|
+
This project turns raw repository metadata into actionable prioritization outputs for execution planning.
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
**Tagline:** From repository inventory to execution decisions in minutes.
|
|
13
|
+
|
|
14
|
+
```
|
|
15
|
+
◉──●──●──●──◉
|
|
16
|
+
\ /
|
|
17
|
+
◉──◉
|
|
18
|
+
↓
|
|
19
|
+
now ████ ↑↑↑
|
|
20
|
+
next ███░ ↑↑
|
|
21
|
+
later█░░░ ↑
|
|
22
|
+
↓
|
|
23
|
+
✓ report.json
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
**What this does:** you run one command. It reads all your GitHub repositories,
|
|
27
|
+
scores and prioritizes them, and writes a structured JSON report ready to feed
|
|
28
|
+
your portfolio site — automatically, without manual curation.
|
|
29
|
+
|
|
30
|
+
## Flow Overview
|
|
31
|
+
|
|
32
|
+
```mermaid
|
|
33
|
+
flowchart TD
|
|
34
|
+
A[analyze] --> I[output/inventory.json]
|
|
35
|
+
A --> IC[output/inventory.csv]
|
|
36
|
+
B[ingest-ideas] --> IJ[output/ideas.json]
|
|
37
|
+
I --> C[build-portfolio]
|
|
38
|
+
IJ --> C
|
|
39
|
+
C --> P[output/portfolio.json]
|
|
40
|
+
D[report] --> R[output/portfolio-report.json]
|
|
41
|
+
D --> RM[output/portfolio-report.md]
|
|
42
|
+
D --> RT[output/portfolio-report.txt]
|
|
43
|
+
POL[priorities/policy.json] --> D
|
|
44
|
+
D -->|--format json --quiet| STDOUT[(stdout JSON)]
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Table of Contents
|
|
48
|
+
|
|
49
|
+
- [Flow Overview](#flow-overview)
|
|
50
|
+
- [Why This Tool Exists](#why-this-tool-exists)
|
|
51
|
+
- [Project Overview](#project-overview)
|
|
52
|
+
- [Installation](#installation)
|
|
53
|
+
- [Quick Start](#quick-start)
|
|
54
|
+
- [CLI Commands](#cli-commands)
|
|
55
|
+
- [CLI Flags](#cli-flags)
|
|
56
|
+
- [Required and Optional Inputs](#required-and-optional-inputs)
|
|
57
|
+
- [Output Artifacts](#output-artifacts)
|
|
58
|
+
- [End-to-End Example](#end-to-end-example)
|
|
59
|
+
- [Machine Integration](#machine-integration)
|
|
60
|
+
- [3-Minute Quickstart](#3-minute-quickstart)
|
|
61
|
+
- [End-to-End Tutorial](#end-to-end-tutorial)
|
|
62
|
+
- [Command Reference](#command-reference)
|
|
63
|
+
- [Integration Contract](#integration-contract)
|
|
64
|
+
- [Exit Codes](#exit-codes)
|
|
65
|
+
- [Optional Policy Overlay and Explain Mode](#optional-policy-overlay-and-explain-mode)
|
|
66
|
+
- [Output Directory Map](#output-directory-map)
|
|
67
|
+
- [Data Contracts](#data-contracts)
|
|
68
|
+
- [Decision Model (Report)](#decision-model-report)
|
|
69
|
+
- [Determinism and Time Rules](#determinism-and-time-rules)
|
|
70
|
+
- [nextAction Validation](#nextaction-validation)
|
|
71
|
+
- [Architecture](#architecture)
|
|
72
|
+
- [Testing and Quality](#testing-and-quality)
|
|
73
|
+
- [Troubleshooting](#troubleshooting)
|
|
74
|
+
- [License and Contribution](#license-and-contribution)
|
|
75
|
+
|
|
76
|
+
## Documentation
|
|
77
|
+
|
|
78
|
+
- [Agent Guide](AGENT_GUIDE.md)
|
|
79
|
+
- [Integration Guide](docs/INTEGRATION.md)
|
|
80
|
+
- [Analyzer Manifest](analyzer.manifest.json)
|
|
81
|
+
- [Portfolio Report Schema](schemas/portfolio-report.schema.json)
|
|
82
|
+
|
|
83
|
+
## Why This Tool Exists
|
|
84
|
+
|
|
85
|
+
Most portfolios are incomplete: repositories are analyzed, but pending ideas live in notes and never enter prioritization.
|
|
86
|
+
`github-portfolio-analyzer` unifies both streams and emits stable artifacts for reporting, planning, and backlog strategy.
|
|
87
|
+
|
|
88
|
+
## Project Overview
|
|
89
|
+
|
|
90
|
+
`github-portfolio-analyzer` is a deterministic CLI that analyzes GitHub repositories and project ideas, then produces portfolio and decision-report artifacts for execution planning.
|
|
91
|
+
|
|
92
|
+
Design goals:
|
|
93
|
+
|
|
94
|
+
- deterministic outputs for the same inputs
|
|
95
|
+
- explainable ranking and priority signals
|
|
96
|
+
- tool-agnostic artifacts (JSON, Markdown, ASCII)
|
|
97
|
+
- orchestration-friendly CLI behavior for scripts, agents, and CI
|
|
98
|
+
|
|
99
|
+
## Installation
|
|
100
|
+
|
|
101
|
+
Install dependencies and run the CLI locally:
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
npm install
|
|
105
|
+
github-portfolio-analyzer --version
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
If the global binary is not available yet, use:
|
|
109
|
+
|
|
110
|
+
```bash
|
|
111
|
+
node bin/github-portfolio-analyzer.js --version
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
## Quick Start
|
|
115
|
+
|
|
116
|
+
Minimal workflow:
|
|
117
|
+
|
|
118
|
+
```bash
|
|
119
|
+
github-portfolio-analyzer analyze
|
|
120
|
+
github-portfolio-analyzer ingest-ideas
|
|
121
|
+
github-portfolio-analyzer build-portfolio
|
|
122
|
+
github-portfolio-analyzer report
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
What each step does:
|
|
126
|
+
|
|
127
|
+
- `analyze`: pulls repository metadata from GitHub API and writes inventory artifacts
|
|
128
|
+
- `ingest-ideas`: normalizes/scoring ideas into `ideas.json`
|
|
129
|
+
- `build-portfolio`: merges repositories and ideas into portfolio artifacts
|
|
130
|
+
- `report`: creates decision-oriented report artifacts (JSON/MD/TXT)
|
|
131
|
+
|
|
132
|
+
## CLI Commands
|
|
133
|
+
|
|
134
|
+
- `analyze`: fetch and score GitHub repositories into inventory outputs
|
|
135
|
+
- `ingest-ideas`: ingest idea records from JSON or interactive prompt
|
|
136
|
+
- `build-portfolio`: merge repository inventory and ideas into unified portfolio outputs
|
|
137
|
+
- `report`: generate decision reports from portfolio artifacts
|
|
138
|
+
|
|
139
|
+
## CLI Flags
|
|
140
|
+
|
|
141
|
+
- `--version`: print CLI version only
|
|
142
|
+
- `--policy <path>`: apply optional policy overlay when generating reports
|
|
143
|
+
- `--explain`: print NOW-band ranking explanation to console
|
|
144
|
+
- `--output <dir>`: report-only output directory override for report artifacts
|
|
145
|
+
- `--format json`: emit report JSON to stdout (artifacts are still written)
|
|
146
|
+
- `--quiet`: suppress non-error logs
|
|
147
|
+
- `--strict`: fail on unknown flags and invalid usage with exit code `2`
|
|
148
|
+
|
|
149
|
+
## Required and Optional Inputs
|
|
150
|
+
|
|
151
|
+
Required inputs depend on the command path:
|
|
152
|
+
|
|
153
|
+
- For `analyze`: `.env` with `GITHUB_TOKEN` (and usually `GITHUB_USERNAME`)
|
|
154
|
+
- For `report`: `output/portfolio.json` must exist
|
|
155
|
+
|
|
156
|
+
Optional inputs:
|
|
157
|
+
|
|
158
|
+
- `ideas/input.json` (or custom path via `ingest-ideas --input`)
|
|
159
|
+
- `priorities/policy.json` for manual priority overlays in `report`
|
|
160
|
+
- `--as-of YYYY-MM-DD` for deterministic snapshot control during `analyze`
|
|
161
|
+
|
|
162
|
+
Create a local policy file:
|
|
163
|
+
|
|
164
|
+
```bash
|
|
165
|
+
cp priorities/policy.example.json priorities/policy.json
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
`priorities/policy.json` is local and git-ignored by design.
|
|
169
|
+
|
|
170
|
+
## Output Artifacts
|
|
171
|
+
|
|
172
|
+
Primary artifacts:
|
|
173
|
+
|
|
174
|
+
- `output/inventory.json`
|
|
175
|
+
- `output/portfolio.json`
|
|
176
|
+
- `output/portfolio-report.json`
|
|
177
|
+
- `output/portfolio-report.md`
|
|
178
|
+
- `output/portfolio-report.txt`
|
|
179
|
+
|
|
180
|
+
Output directory control:
|
|
181
|
+
|
|
182
|
+
- `report --output <dir>` writes report artifacts to a custom directory
|
|
183
|
+
- Pipeline commands also support `--output-dir <dir>` for their output roots
|
|
184
|
+
|
|
185
|
+
## End-to-End Example
|
|
186
|
+
|
|
187
|
+
```bash
|
|
188
|
+
github-portfolio-analyzer analyze --as-of 2026-03-03
|
|
189
|
+
github-portfolio-analyzer ingest-ideas --input ./ideas/input.json
|
|
190
|
+
github-portfolio-analyzer build-portfolio
|
|
191
|
+
github-portfolio-analyzer report --format json
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
After this run, you should have (in `output/` by default):
|
|
195
|
+
|
|
196
|
+
- `inventory.json` and `inventory.csv`
|
|
197
|
+
- `ideas.json`
|
|
198
|
+
- `portfolio.json` and `portfolio-summary.md`
|
|
199
|
+
- `portfolio-report.json`, `portfolio-report.md`, `portfolio-report.txt`
|
|
200
|
+
|
|
201
|
+
## Machine Integration
|
|
202
|
+
|
|
203
|
+
Use this CLI from scripts, CI jobs, or agent runtimes with deterministic artifacts and predictable exit codes.
|
|
204
|
+
|
|
205
|
+
Programmatic JSON via stdout:
|
|
206
|
+
|
|
207
|
+
```bash
|
|
208
|
+
github-portfolio-analyzer report --format json --quiet
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
Custom report output directory:
|
|
212
|
+
|
|
213
|
+
```bash
|
|
214
|
+
github-portfolio-analyzer report --output ./runs/run-001
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
`--format json --quiet` is recommended for machine consumers because stdout contains only JSON unless an error occurs.
|
|
218
|
+
See [Integration Contract](#integration-contract) and the dedicated [Integration Guide](docs/INTEGRATION.md).
|
|
219
|
+
|
|
220
|
+
## 3-Minute Quickstart
|
|
221
|
+
|
|
222
|
+
### 1) Requirements
|
|
223
|
+
|
|
224
|
+
- Node.js `22+`
|
|
225
|
+
- GitHub Personal Access Token (PAT) for `analyze`
|
|
226
|
+
|
|
227
|
+
### 2) Create a GitHub PAT (short version)
|
|
228
|
+
|
|
229
|
+
Create a token in GitHub settings and store it in `.env`.
|
|
230
|
+
Use the minimum read permissions needed to list repos and inspect repository files/workflows:
|
|
231
|
+
|
|
232
|
+
- **Fine-grained token:** repository `Metadata: Read`, `Contents: Read`, `Actions: Read`
|
|
233
|
+
- **Classic token (fallback):** `repo` scope (read usage by this CLI)
|
|
234
|
+
|
|
235
|
+
### 3) Install and configure
|
|
236
|
+
|
|
237
|
+
```bash
|
|
238
|
+
npm install
|
|
239
|
+
cp .env.example .env
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
Set values in `.env`:
|
|
243
|
+
|
|
244
|
+
```dotenv
|
|
245
|
+
GITHUB_TOKEN=your_github_token_here
|
|
246
|
+
GITHUB_USERNAME=your_github_username_here
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
### 4) Run the core pipeline
|
|
250
|
+
|
|
251
|
+
```bash
|
|
252
|
+
github-portfolio-analyzer analyze --as-of 2026-03-03
|
|
253
|
+
github-portfolio-analyzer ingest-ideas
|
|
254
|
+
github-portfolio-analyzer build-portfolio
|
|
255
|
+
github-portfolio-analyzer report --format all
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
Example console snippet:
|
|
259
|
+
|
|
260
|
+
```bash
|
|
261
|
+
$ github-portfolio-analyzer analyze --as-of 2026-03-03
|
|
262
|
+
Analyzed 51 repositories for octocat.
|
|
263
|
+
Wrote output/inventory.json.
|
|
264
|
+
Wrote output/inventory.csv.
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
## End-to-End Tutorial
|
|
268
|
+
|
|
269
|
+
### Step 1: Analyze repositories
|
|
270
|
+
|
|
271
|
+
```bash
|
|
272
|
+
github-portfolio-analyzer analyze --as-of 2026-03-03
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
What happens:
|
|
276
|
+
|
|
277
|
+
- Authenticates with GitHub API
|
|
278
|
+
- Fetches all repos with pagination
|
|
279
|
+
- Computes structural health, activity, maturity, score, taxonomy
|
|
280
|
+
- Writes `inventory.json` and `inventory.csv`
|
|
281
|
+
|
|
282
|
+
### Step 2: Ingest ideas
|
|
283
|
+
|
|
284
|
+
Default file mode:
|
|
285
|
+
|
|
286
|
+
```bash
|
|
287
|
+
github-portfolio-analyzer ingest-ideas
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
Interactive mode:
|
|
291
|
+
|
|
292
|
+
```bash
|
|
293
|
+
github-portfolio-analyzer ingest-ideas --prompt
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
What happens:
|
|
297
|
+
|
|
298
|
+
- Normalizes idea records
|
|
299
|
+
- Scores ideas
|
|
300
|
+
- Applies taxonomy defaults/inference with provenance metadata
|
|
301
|
+
- Normalizes `nextAction` to canonical format
|
|
302
|
+
|
|
303
|
+
### Step 3: Build merged portfolio
|
|
304
|
+
|
|
305
|
+
```bash
|
|
306
|
+
github-portfolio-analyzer build-portfolio
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
What happens:
|
|
310
|
+
|
|
311
|
+
- Merges repos + ideas
|
|
312
|
+
- Preserves deterministic ordering
|
|
313
|
+
- Writes `portfolio.json`, per-project markdown pages, and `portfolio-summary.md`
|
|
314
|
+
|
|
315
|
+
### Step 4: Generate decision report
|
|
316
|
+
|
|
317
|
+
```bash
|
|
318
|
+
github-portfolio-analyzer report --format all
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
What happens:
|
|
322
|
+
|
|
323
|
+
- Reads `portfolio.json` (required)
|
|
324
|
+
- Optionally reads `inventory.json` for richer repo completion signals
|
|
325
|
+
- Computes completion level, effort estimate, and priority band
|
|
326
|
+
- Writes ASCII + Markdown + JSON report artifacts
|
|
327
|
+
|
|
328
|
+
## Command Reference
|
|
329
|
+
|
|
330
|
+
| Command | Purpose | Key Options |
|
|
331
|
+
|---|---|---|
|
|
332
|
+
| `analyze` | Build repository inventory from GitHub API | `--as-of YYYY-MM-DD`, `--output-dir PATH` |
|
|
333
|
+
| `ingest-ideas` | Add/update idea records | `--input PATH`, `--prompt`, `--output-dir PATH` |
|
|
334
|
+
| `build-portfolio` | Merge repos + ideas into portfolio outputs | `--output-dir PATH` |
|
|
335
|
+
| `report` | Produce decision-oriented report artifacts | `--output-dir PATH`, `--format ascii\|md\|json\|all` |
|
|
336
|
+
|
|
337
|
+
Default for `report --format` is `all`.
|
|
338
|
+
|
|
339
|
+
## Integration Contract
|
|
340
|
+
|
|
341
|
+
This section defines the stable integration points for external tools and orchestrators.
|
|
342
|
+
For a fast first run path, start with [Quick Start](#quick-start).
|
|
343
|
+
|
|
344
|
+
### Available commands
|
|
345
|
+
|
|
346
|
+
- `analyze`
|
|
347
|
+
- `ingest-ideas`
|
|
348
|
+
- `build-portfolio`
|
|
349
|
+
- `report`
|
|
350
|
+
|
|
351
|
+
### Relevant report flags
|
|
352
|
+
|
|
353
|
+
- `report --policy`
|
|
354
|
+
- `report --explain`
|
|
355
|
+
- `report --quiet`
|
|
356
|
+
|
|
357
|
+
### Canonical analyzer outputs
|
|
358
|
+
|
|
359
|
+
- `output/inventory.json`
|
|
360
|
+
- `output/portfolio.json`
|
|
361
|
+
- `output/portfolio-report.json`
|
|
362
|
+
|
|
363
|
+
### Machine-readable interface files
|
|
364
|
+
|
|
365
|
+
- `analyzer.manifest.json`: static command/output manifest for external orchestrators
|
|
366
|
+
- `schemas/portfolio-report.schema.json`: JSON Schema for `portfolio-report.json` validation
|
|
367
|
+
|
|
368
|
+
### Optional local configuration
|
|
369
|
+
|
|
370
|
+
- `priorities/policy.json`
|
|
371
|
+
- This file is local and git-ignored.
|
|
372
|
+
- Create it by copying:
|
|
373
|
+
`priorities/policy.example.json` -> `priorities/policy.json`
|
|
374
|
+
|
|
375
|
+
### Optional strict mode
|
|
376
|
+
|
|
377
|
+
- Use `--strict` to fail on unknown flags with exit code `2`.
|
|
378
|
+
- Without `--strict`, existing permissive parsing behavior remains unchanged.
|
|
379
|
+
|
|
380
|
+
### CLI version
|
|
381
|
+
|
|
382
|
+
You can get the CLI version with:
|
|
383
|
+
|
|
384
|
+
```bash
|
|
385
|
+
github-portfolio-analyzer --version
|
|
386
|
+
```
|
|
387
|
+
|
|
388
|
+
### JSON schema
|
|
389
|
+
|
|
390
|
+
- `schemas/portfolio-report.schema.json` validates the `output/portfolio-report.json` structure.
|
|
391
|
+
- External systems can use this schema together with `analyzer.manifest.json` as the integration contract.
|
|
392
|
+
|
|
393
|
+
## Exit Codes
|
|
394
|
+
|
|
395
|
+
- `0`: success
|
|
396
|
+
- `1`: operational failure (runtime/file/network/auth errors)
|
|
397
|
+
- `2`: invalid usage (for example invalid command or `--strict` unknown flag)
|
|
398
|
+
|
|
399
|
+
## Optional Policy Overlay and Explain Mode
|
|
400
|
+
|
|
401
|
+
The report command supports an optional policy overlay to guide prioritization without changing project taxonomy, state, or score.
|
|
402
|
+
When no policy is provided, ranking remains neutral and deterministic using the built-in heuristics.
|
|
403
|
+
|
|
404
|
+
### CLI examples
|
|
405
|
+
|
|
406
|
+
```bash
|
|
407
|
+
github-portfolio-analyzer report --format all
|
|
408
|
+
github-portfolio-analyzer report --policy ./priorities/policy.json --format json
|
|
409
|
+
github-portfolio-analyzer report --priorities ./priorities/policy.json --explain
|
|
410
|
+
```
|
|
411
|
+
|
|
412
|
+
### Local policy file setup
|
|
413
|
+
|
|
414
|
+
Use the example as a starting point and keep your real policy file local:
|
|
415
|
+
|
|
416
|
+
```bash
|
|
417
|
+
cp priorities/policy.example.json priorities/policy.json
|
|
418
|
+
```
|
|
419
|
+
|
|
420
|
+
`priorities/policy.json` is git-ignored on purpose (local preferences).
|
|
421
|
+
`priorities/policy.example.json` should remain tracked as the shared template.
|
|
422
|
+
|
|
423
|
+
### Minimal policy example
|
|
424
|
+
|
|
425
|
+
```json
|
|
426
|
+
{
|
|
427
|
+
"version": 1,
|
|
428
|
+
"rules": [
|
|
429
|
+
{
|
|
430
|
+
"id": "focus-core-tooling",
|
|
431
|
+
"match": {
|
|
432
|
+
"type": ["repo"],
|
|
433
|
+
"category": ["tooling"],
|
|
434
|
+
"state": ["active", "stale"]
|
|
435
|
+
},
|
|
436
|
+
"effects": {
|
|
437
|
+
"boost": 10,
|
|
438
|
+
"tag": "core"
|
|
439
|
+
},
|
|
440
|
+
"reason": "Prioritize currently maintainable internal tooling."
|
|
441
|
+
}
|
|
442
|
+
],
|
|
443
|
+
"pin": [
|
|
444
|
+
{
|
|
445
|
+
"slug": "developer-onboarding-checklist-generator",
|
|
446
|
+
"band": "now",
|
|
447
|
+
"tag": "manual-priority"
|
|
448
|
+
}
|
|
449
|
+
]
|
|
450
|
+
}
|
|
451
|
+
```
|
|
452
|
+
|
|
453
|
+
### Policy behavior guarantees
|
|
454
|
+
|
|
455
|
+
- Rules are applied in deterministic `id` order.
|
|
456
|
+
- Boosts are cumulative.
|
|
457
|
+
- Pin band has highest precedence.
|
|
458
|
+
- `forceBand` uses strongest precedence: `now > next > later > park`.
|
|
459
|
+
- Policy overlay only affects report-level priority fields (`finalPriorityScore`, `priorityBand`, tags/overrides).
|
|
460
|
+
- Taxonomy fields, item score, and item state remain unchanged.
|
|
461
|
+
- The tool remains deterministic across runs for the same inputs.
|
|
462
|
+
|
|
463
|
+
## Output Directory Map
|
|
464
|
+
|
|
465
|
+
```text
|
|
466
|
+
/output
|
|
467
|
+
/projects
|
|
468
|
+
{project-slug}.md
|
|
469
|
+
inventory.json
|
|
470
|
+
inventory.csv
|
|
471
|
+
ideas.json
|
|
472
|
+
portfolio.json
|
|
473
|
+
portfolio-summary.md
|
|
474
|
+
portfolio-report.json
|
|
475
|
+
portfolio-report.md
|
|
476
|
+
portfolio-report.txt
|
|
477
|
+
```
|
|
478
|
+
|
|
479
|
+
Artifact roles:
|
|
480
|
+
|
|
481
|
+
- `inventory.json`: repository-only enriched source (includes taxonomy + taxonomyMeta)
|
|
482
|
+
- `ideas.json`: ideas-only normalized source
|
|
483
|
+
- `portfolio.json`: merged source of truth
|
|
484
|
+
- `portfolio-summary.md`: high-level portfolio summary (state sections + top 10)
|
|
485
|
+
- `portfolio-report.*`: decision-oriented planning report in machine and human formats
|
|
486
|
+
|
|
487
|
+
## Data Contracts
|
|
488
|
+
|
|
489
|
+
### Taxonomy contract (all portfolio items)
|
|
490
|
+
|
|
491
|
+
Each `portfolio.json.items[]` entry includes:
|
|
492
|
+
|
|
493
|
+
- `type`: `repo | idea`
|
|
494
|
+
- `category`: `product | tooling | library | learning | content | infra | experiment | template`
|
|
495
|
+
- `state`: `idea | active | stale | abandoned | archived | reference-only`
|
|
496
|
+
- `strategy`: `strategic-core | strategic-support | opportunistic | maintenance | parked`
|
|
497
|
+
- `effort`: `xs | s | m | l | xl`
|
|
498
|
+
- `value`: `low | medium | high | very-high`
|
|
499
|
+
- `nextAction`: `"<Verb> <target> — Done when: <measurable condition>"`
|
|
500
|
+
- `taxonomyMeta`: per-field provenance (`default | user | inferred`)
|
|
501
|
+
|
|
502
|
+
`inventory.json.items[]` includes the same taxonomy fields and `taxonomyMeta` for repositories.
|
|
503
|
+
|
|
504
|
+
### Report contract
|
|
505
|
+
|
|
506
|
+
`portfolio-report.json` includes:
|
|
507
|
+
|
|
508
|
+
- `meta` (generatedAt, asOfDate, owner, counts)
|
|
509
|
+
- `summary` (state counts, top10 by score, now/next/later/park)
|
|
510
|
+
- `matrix.completionByEffort` (`CL0..CL5` by `xs..xl`)
|
|
511
|
+
- `items[]` with decision fields (`completionLevel`, `effortEstimate`, `priorityBand`, `priorityWhy`)
|
|
512
|
+
|
|
513
|
+
## Decision Model (Report)
|
|
514
|
+
|
|
515
|
+
### Completion Level
|
|
516
|
+
|
|
517
|
+
- `CL0`: no README
|
|
518
|
+
- `CL1`: has README
|
|
519
|
+
- `CL2`: has package.json, or non-JS repo with size >= 500 KB
|
|
520
|
+
- `CL3`: CL2 + CI
|
|
521
|
+
- `CL4`: CL3 + tests
|
|
522
|
+
- `CL5`: CL4 + score >= 70
|
|
523
|
+
- Ideas default to `CL0`
|
|
524
|
+
|
|
525
|
+
### Effort Estimate
|
|
526
|
+
|
|
527
|
+
Uses taxonomy `effort` unless `effort` source is `default`.
|
|
528
|
+
If defaulted, infer by size and completion:
|
|
529
|
+
|
|
530
|
+
- `xs`: size < 100 KB and CL <= 2
|
|
531
|
+
- `s`: size < 500 KB and CL <= 3
|
|
532
|
+
- `m`: size < 5000 KB
|
|
533
|
+
- `l`: size < 20000 KB
|
|
534
|
+
- `xl`: size >= 20000 KB
|
|
535
|
+
|
|
536
|
+
`effortEstimate` is a report field only; it does not overwrite taxonomy `effort`.
|
|
537
|
+
|
|
538
|
+
### Priority Band
|
|
539
|
+
|
|
540
|
+
Internal score calculation:
|
|
541
|
+
|
|
542
|
+
- base: `score`
|
|
543
|
+
- `+10` if state `active`
|
|
544
|
+
- `+5` if state `stale`
|
|
545
|
+
- `-20` if state `abandoned` or `archived`
|
|
546
|
+
- `+10` if completion is CL1..CL3
|
|
547
|
+
- `-10` if effortEstimate is `l` or `xl`
|
|
548
|
+
|
|
549
|
+
Band mapping:
|
|
550
|
+
|
|
551
|
+
- `now`: >= 80
|
|
552
|
+
- `next`: 65..79
|
|
553
|
+
- `later`: 45..64
|
|
554
|
+
- `park`: < 45
|
|
555
|
+
|
|
556
|
+
## Determinism and Time Rules
|
|
557
|
+
|
|
558
|
+
- `asOfDate` is UTC-based (`--as-of` or UTC today once per `analyze` run)
|
|
559
|
+
- `inventory.json.meta.asOfDate` persists snapshot date
|
|
560
|
+
- `portfolio.json.meta.asOfDate` copies inventory asOfDate, or `null` when inventory is missing
|
|
561
|
+
- Item-level timestamps are not persisted
|
|
562
|
+
- Deterministic ordering:
|
|
563
|
+
- inventory repos by `fullName` ascending
|
|
564
|
+
- ideas by `slug` ascending
|
|
565
|
+
- portfolio by `score` descending then `slug` ascending
|
|
566
|
+
|
|
567
|
+
## nextAction Validation
|
|
568
|
+
|
|
569
|
+
Required canonical format:
|
|
570
|
+
|
|
571
|
+
`"<Verb> <target> — Done when: <measurable condition>"`
|
|
572
|
+
|
|
573
|
+
Robust input support:
|
|
574
|
+
|
|
575
|
+
- Accepts fallback marker `" - Done when:"`
|
|
576
|
+
- Normalizes to em dash marker `"— Done when:"`
|
|
577
|
+
- Throws clear error for invalid format
|
|
578
|
+
|
|
579
|
+
## Architecture
|
|
580
|
+
|
|
581
|
+
```text
|
|
582
|
+
bin/
|
|
583
|
+
github-portfolio-analyzer.js
|
|
584
|
+
src/
|
|
585
|
+
commands/ (analyze, ingest-ideas, build-portfolio, report)
|
|
586
|
+
core/ (classification, scoring, taxonomy, ideas, portfolio, report)
|
|
587
|
+
github/ (api client, pagination, structural inspection)
|
|
588
|
+
io/ (json/csv/markdown/report writers)
|
|
589
|
+
utils/ (args, time, slug, retry, concurrency, nextAction)
|
|
590
|
+
```
|
|
591
|
+
|
|
592
|
+
### Architecture Overview
|
|
593
|
+
|
|
594
|
+
```mermaid
|
|
595
|
+
flowchart LR
|
|
596
|
+
|
|
597
|
+
subgraph User Layer
|
|
598
|
+
U[Developer / Script / Agent]
|
|
599
|
+
end
|
|
600
|
+
|
|
601
|
+
subgraph CLI Layer
|
|
602
|
+
A[analyze]
|
|
603
|
+
B[ingest-ideas]
|
|
604
|
+
C[build-portfolio]
|
|
605
|
+
D[report]
|
|
606
|
+
end
|
|
607
|
+
|
|
608
|
+
subgraph Core Engine
|
|
609
|
+
S1[GitHub API Client]
|
|
610
|
+
S2[Ideas Normalizer]
|
|
611
|
+
S3[Portfolio Builder]
|
|
612
|
+
S4[Decision Engine]
|
|
613
|
+
end
|
|
614
|
+
|
|
615
|
+
subgraph Artifacts
|
|
616
|
+
O1[inventory.json]
|
|
617
|
+
O2[ideas.json]
|
|
618
|
+
O3[portfolio.json]
|
|
619
|
+
O4[portfolio-report.json]
|
|
620
|
+
end
|
|
621
|
+
|
|
622
|
+
U --> A
|
|
623
|
+
U --> B
|
|
624
|
+
U --> C
|
|
625
|
+
U --> D
|
|
626
|
+
|
|
627
|
+
A --> S1
|
|
628
|
+
B --> S2
|
|
629
|
+
C --> S3
|
|
630
|
+
D --> S4
|
|
631
|
+
|
|
632
|
+
S1 --> O1
|
|
633
|
+
S2 --> O2
|
|
634
|
+
S3 --> O3
|
|
635
|
+
S4 --> O4
|
|
636
|
+
```
|
|
637
|
+
|
|
638
|
+
Implementation characteristics:
|
|
639
|
+
|
|
640
|
+
- Minimal dependencies (`dotenv` only)
|
|
641
|
+
- Built-in `fetch`
|
|
642
|
+
- GitHub API only (no repository cloning)
|
|
643
|
+
- Retry/backoff on 403/429 and transient failures
|
|
644
|
+
- Per-repo error isolation during analysis
|
|
645
|
+
|
|
646
|
+
## Testing and Quality
|
|
647
|
+
|
|
648
|
+
Run the full suite:
|
|
649
|
+
|
|
650
|
+
```bash
|
|
651
|
+
npm test
|
|
652
|
+
```
|
|
653
|
+
|
|
654
|
+
Coverage includes:
|
|
655
|
+
|
|
656
|
+
- activity/maturity/scoring boundaries
|
|
657
|
+
- taxonomy presence and provenance behavior
|
|
658
|
+
- `nextAction` validation and normalization
|
|
659
|
+
- portfolio merge determinism
|
|
660
|
+
- report completion logic, priority mapping, and deterministic model generation
|
|
661
|
+
|
|
662
|
+
## Troubleshooting
|
|
663
|
+
|
|
664
|
+
### Missing `GITHUB_TOKEN`
|
|
665
|
+
|
|
666
|
+
`analyze` fails fast with a clear error when token is missing.
|
|
667
|
+
`ingest-ideas`, `build-portfolio`, and `report` still run without GitHub authentication.
|
|
668
|
+
|
|
669
|
+
### Missing `portfolio.json` for report
|
|
670
|
+
|
|
671
|
+
`report` requires `output/portfolio.json` and will fail with:
|
|
672
|
+
|
|
673
|
+
- `Missing required input: output/portfolio.json. Run build-portfolio before report.`
|
|
674
|
+
|
|
675
|
+
### Report with no inventory
|
|
676
|
+
|
|
677
|
+
If `inventory.json` is absent:
|
|
678
|
+
|
|
679
|
+
- report still runs from `portfolio.json`
|
|
680
|
+
- owner is `null`
|
|
681
|
+
- completion signals are best-effort from portfolio fields
|
|
682
|
+
|
|
683
|
+
## License and Contribution
|
|
684
|
+
|
|
685
|
+
Use this repository as a base for portfolio automation workflows and adapt heuristics for your organization.
|
|
686
|
+
Contributions should preserve deterministic contracts and avoid adding non-essential dependencies.
|