@oss-autopilot/core 1.17.4 → 3.0.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 +1 -1
- package/dist/cli-registry.js +417 -326
- package/dist/cli.bundle.cjs +99 -96
- package/dist/commands/daily-render.d.ts +39 -0
- package/dist/commands/daily-render.js +189 -0
- package/dist/commands/dashboard-data.js +9 -3
- package/dist/commands/index.d.ts +4 -8
- package/dist/commands/index.js +3 -5
- package/dist/commands/list-move-tier.d.ts +46 -0
- package/dist/commands/list-move-tier.js +192 -0
- package/dist/commands/pr-template.js +2 -1
- package/dist/commands/state-cmd.d.ts +10 -1
- package/dist/commands/state-cmd.js +22 -3
- package/dist/commands/track.d.ts +7 -28
- package/dist/commands/track.js +8 -30
- package/dist/core/auth.d.ts +50 -0
- package/dist/core/auth.js +160 -0
- package/dist/core/concurrency.d.ts +7 -0
- package/dist/core/concurrency.js +9 -0
- package/dist/core/daily-logic.d.ts +10 -42
- package/dist/core/daily-logic.js +14 -201
- package/dist/core/dates.d.ts +37 -0
- package/dist/core/dates.js +60 -0
- package/dist/core/errors.d.ts +14 -0
- package/dist/core/errors.js +22 -0
- package/dist/core/gist-state-store.d.ts +48 -2
- package/dist/core/gist-state-store.js +120 -24
- package/dist/core/github-stats.js +1 -1
- package/dist/core/http-cache.js +1 -1
- package/dist/core/index.d.ts +5 -1
- package/dist/core/index.js +5 -1
- package/dist/core/issue-conversation.js +3 -2
- package/dist/core/paths.d.ts +68 -0
- package/dist/core/paths.js +106 -0
- package/dist/core/pr-monitor.js +3 -1
- package/dist/core/repo-score-manager.js +1 -1
- package/dist/core/state-persistence.js +1 -1
- package/dist/core/state.d.ts +16 -2
- package/dist/core/state.js +42 -7
- package/dist/core/types.d.ts +57 -0
- package/dist/core/urls.d.ts +63 -0
- package/dist/core/urls.js +101 -0
- package/dist/formatters/json.d.ts +464 -74
- package/dist/formatters/json.js +380 -0
- package/package.json +3 -3
- package/dist/commands/read.d.ts +0 -18
- package/dist/commands/read.js +0 -20
- package/dist/core/utils.d.ts +0 -303
- package/dist/core/utils.js +0 -529
package/dist/commands/track.d.ts
CHANGED
|
@@ -1,23 +1,16 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Track
|
|
2
|
+
* Track command (v2 semantics — see #1001)
|
|
3
3
|
*
|
|
4
|
-
* **
|
|
4
|
+
* **This command does not mutate state.** In v2, PRs are discovered and
|
|
5
5
|
* enriched automatically on every `daily` run — there is no local tracking
|
|
6
|
-
* list to add to
|
|
7
|
-
*
|
|
6
|
+
* list to add to. `runTrack` is an **informational lookup** that fetches PR
|
|
7
|
+
* metadata from GitHub and returns it; useful for inspecting a specific
|
|
8
|
+
* PR's shape without waiting for the next `daily` run. Nothing is persisted.
|
|
8
9
|
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
* without waiting for the next `daily` run. Nothing is persisted.
|
|
12
|
-
* - `runUntrack` is **deprecated** and always a no-op. Use `shelve` to hide
|
|
13
|
-
* a PR from the daily digest.
|
|
10
|
+
* The `runUntrack` v1→v2 stub was removed in v4 (#1133). Use `shelve`/
|
|
11
|
+
* `unshelve` to hide a PR from the daily digest.
|
|
14
12
|
*/
|
|
15
13
|
import type { TrackOutput } from '../formatters/json.js';
|
|
16
|
-
export interface UntrackOutput {
|
|
17
|
-
removed: boolean;
|
|
18
|
-
url: string;
|
|
19
|
-
message: string;
|
|
20
|
-
}
|
|
21
14
|
/**
|
|
22
15
|
* Fetch metadata for a PR URL (informational — does not persist).
|
|
23
16
|
*
|
|
@@ -33,17 +26,3 @@ export interface UntrackOutput {
|
|
|
33
26
|
export declare function runTrack(options: {
|
|
34
27
|
prUrl: string;
|
|
35
28
|
}): Promise<TrackOutput>;
|
|
36
|
-
/**
|
|
37
|
-
* @deprecated No-op in v2. Use `runShelve` to hide a PR from the daily digest.
|
|
38
|
-
*
|
|
39
|
-
* Kept for backwards compatibility with v1 callers. PRs are fetched fresh
|
|
40
|
-
* on each `daily` run, so there is no local tracking list to remove from.
|
|
41
|
-
*
|
|
42
|
-
* @param options - Untrack options
|
|
43
|
-
* @param options.prUrl - Full GitHub PR URL (validated but not used)
|
|
44
|
-
* @returns Output object with `removed: false` and a message explaining v2 behavior
|
|
45
|
-
* @throws {ValidationError} If the URL is not a valid GitHub PR URL
|
|
46
|
-
*/
|
|
47
|
-
export declare function runUntrack(options: {
|
|
48
|
-
prUrl: string;
|
|
49
|
-
}): Promise<UntrackOutput>;
|
package/dist/commands/track.js
CHANGED
|
@@ -1,21 +1,19 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Track
|
|
2
|
+
* Track command (v2 semantics — see #1001)
|
|
3
3
|
*
|
|
4
|
-
* **
|
|
4
|
+
* **This command does not mutate state.** In v2, PRs are discovered and
|
|
5
5
|
* enriched automatically on every `daily` run — there is no local tracking
|
|
6
|
-
* list to add to
|
|
7
|
-
*
|
|
6
|
+
* list to add to. `runTrack` is an **informational lookup** that fetches PR
|
|
7
|
+
* metadata from GitHub and returns it; useful for inspecting a specific
|
|
8
|
+
* PR's shape without waiting for the next `daily` run. Nothing is persisted.
|
|
8
9
|
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
* without waiting for the next `daily` run. Nothing is persisted.
|
|
12
|
-
* - `runUntrack` is **deprecated** and always a no-op. Use `shelve` to hide
|
|
13
|
-
* a PR from the daily digest.
|
|
10
|
+
* The `runUntrack` v1→v2 stub was removed in v4 (#1133). Use `shelve`/
|
|
11
|
+
* `unshelve` to hide a PR from the daily digest.
|
|
14
12
|
*/
|
|
15
13
|
import { getOctokit, requireGitHubToken } from '../core/index.js';
|
|
16
14
|
import { ValidationError } from '../core/errors.js';
|
|
17
15
|
import { validateUrl, PR_URL_PATTERN, validateGitHubUrl } from './validation.js';
|
|
18
|
-
import { parseGitHubUrl } from '../core/
|
|
16
|
+
import { parseGitHubUrl } from '../core/urls.js';
|
|
19
17
|
/**
|
|
20
18
|
* Fetch metadata for a PR URL (informational — does not persist).
|
|
21
19
|
*
|
|
@@ -48,23 +46,3 @@ export async function runTrack(options) {
|
|
|
48
46
|
},
|
|
49
47
|
};
|
|
50
48
|
}
|
|
51
|
-
/**
|
|
52
|
-
* @deprecated No-op in v2. Use `runShelve` to hide a PR from the daily digest.
|
|
53
|
-
*
|
|
54
|
-
* Kept for backwards compatibility with v1 callers. PRs are fetched fresh
|
|
55
|
-
* on each `daily` run, so there is no local tracking list to remove from.
|
|
56
|
-
*
|
|
57
|
-
* @param options - Untrack options
|
|
58
|
-
* @param options.prUrl - Full GitHub PR URL (validated but not used)
|
|
59
|
-
* @returns Output object with `removed: false` and a message explaining v2 behavior
|
|
60
|
-
* @throws {ValidationError} If the URL is not a valid GitHub PR URL
|
|
61
|
-
*/
|
|
62
|
-
export async function runUntrack(options) {
|
|
63
|
-
validateUrl(options.prUrl);
|
|
64
|
-
validateGitHubUrl(options.prUrl, PR_URL_PATTERN, 'PR');
|
|
65
|
-
return {
|
|
66
|
-
removed: false,
|
|
67
|
-
url: options.prUrl,
|
|
68
|
-
message: 'In v2, PRs are fetched fresh on each daily run — there is no local tracking list to remove from.',
|
|
69
|
-
};
|
|
70
|
-
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GitHub authentication: token resolution + username detection.
|
|
3
|
+
*
|
|
4
|
+
* Both sync and async token getters share a process-level cache so a
|
|
5
|
+
* successful async fetch makes subsequent sync calls instant.
|
|
6
|
+
*
|
|
7
|
+
* Extracted from utils.ts under #1116.
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* Retrieves a GitHub authentication token, checking sources in priority order.
|
|
11
|
+
*
|
|
12
|
+
* Checks `GITHUB_TOKEN` environment variable first, then falls back to `gh auth token`
|
|
13
|
+
* from the GitHub CLI. The result is cached after the first successful lookup (or first
|
|
14
|
+
* failed attempt), so subsequent calls are instant and do not spawn subprocesses.
|
|
15
|
+
*
|
|
16
|
+
* @returns The GitHub token string, or `null` if no token is available
|
|
17
|
+
*/
|
|
18
|
+
export declare function getGitHubToken(): string | null;
|
|
19
|
+
/**
|
|
20
|
+
* Returns a GitHub token or throws an error with setup instructions.
|
|
21
|
+
*
|
|
22
|
+
* Delegates to {@link getGitHubToken} and throws if no token is found. Use this
|
|
23
|
+
* in commands that cannot proceed without authentication.
|
|
24
|
+
*
|
|
25
|
+
* @throws {ConfigurationError} If no token is available, with instructions for `gh auth login` or setting `GITHUB_TOKEN`
|
|
26
|
+
*/
|
|
27
|
+
export declare function requireGitHubToken(): string;
|
|
28
|
+
/**
|
|
29
|
+
* Resets the cached GitHub token and fetch-attempted flag.
|
|
30
|
+
*
|
|
31
|
+
* Intended for use in tests to ensure a clean state between test cases.
|
|
32
|
+
* After calling this, the next call to {@link getGitHubToken} will re-fetch the token.
|
|
33
|
+
*/
|
|
34
|
+
export declare function resetGitHubTokenCache(): void;
|
|
35
|
+
/**
|
|
36
|
+
* Asynchronous version of {@link getGitHubToken}.
|
|
37
|
+
*
|
|
38
|
+
* Uses `execFile` (non-blocking) instead of `execFileSync` to avoid blocking
|
|
39
|
+
* the event loop during CLI cold start. Shares the same cache as the synchronous
|
|
40
|
+
* version, so a successful async fetch makes subsequent sync calls instant.
|
|
41
|
+
*/
|
|
42
|
+
export declare function getGitHubTokenAsync(): Promise<string | null>;
|
|
43
|
+
/**
|
|
44
|
+
* Detect the authenticated GitHub username via the `gh` CLI.
|
|
45
|
+
*
|
|
46
|
+
* Runs `gh api user --jq '.login'` asynchronously and validates the result
|
|
47
|
+
* against GitHub's username rules. Never throws — returns `null` on any failure
|
|
48
|
+
* (gh not installed, not authenticated, invalid output, etc.).
|
|
49
|
+
*/
|
|
50
|
+
export declare function detectGitHubUsername(): Promise<string | null>;
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GitHub authentication: token resolution + username detection.
|
|
3
|
+
*
|
|
4
|
+
* Both sync and async token getters share a process-level cache so a
|
|
5
|
+
* successful async fetch makes subsequent sync calls instant.
|
|
6
|
+
*
|
|
7
|
+
* Extracted from utils.ts under #1116.
|
|
8
|
+
*/
|
|
9
|
+
import { execFileSync, execFile } from 'child_process';
|
|
10
|
+
import { ConfigurationError } from './errors.js';
|
|
11
|
+
import { debug } from './logger.js';
|
|
12
|
+
const MODULE = 'auth';
|
|
13
|
+
// Cached GitHub token (fetched once per session)
|
|
14
|
+
let cachedGitHubToken = null;
|
|
15
|
+
let tokenFetchAttempted = false;
|
|
16
|
+
/**
|
|
17
|
+
* Retrieves a GitHub authentication token, checking sources in priority order.
|
|
18
|
+
*
|
|
19
|
+
* Checks `GITHUB_TOKEN` environment variable first, then falls back to `gh auth token`
|
|
20
|
+
* from the GitHub CLI. The result is cached after the first successful lookup (or first
|
|
21
|
+
* failed attempt), so subsequent calls are instant and do not spawn subprocesses.
|
|
22
|
+
*
|
|
23
|
+
* @returns The GitHub token string, or `null` if no token is available
|
|
24
|
+
*/
|
|
25
|
+
export function getGitHubToken() {
|
|
26
|
+
if (cachedGitHubToken) {
|
|
27
|
+
return cachedGitHubToken;
|
|
28
|
+
}
|
|
29
|
+
if (tokenFetchAttempted) {
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
tokenFetchAttempted = true;
|
|
33
|
+
if (process.env.GITHUB_TOKEN) {
|
|
34
|
+
cachedGitHubToken = process.env.GITHUB_TOKEN;
|
|
35
|
+
return cachedGitHubToken;
|
|
36
|
+
}
|
|
37
|
+
try {
|
|
38
|
+
const token = execFileSync('gh', ['auth', 'token'], {
|
|
39
|
+
encoding: 'utf-8',
|
|
40
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
41
|
+
timeout: 2000,
|
|
42
|
+
}).trim();
|
|
43
|
+
if (token && token.length > 0) {
|
|
44
|
+
cachedGitHubToken = token;
|
|
45
|
+
debug(MODULE, 'Using GitHub token from gh CLI');
|
|
46
|
+
return cachedGitHubToken;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
catch (err) {
|
|
50
|
+
debug(MODULE, 'gh auth token failed (CLI unavailable or not authenticated)', err);
|
|
51
|
+
}
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Returns a GitHub token or throws an error with setup instructions.
|
|
56
|
+
*
|
|
57
|
+
* Delegates to {@link getGitHubToken} and throws if no token is found. Use this
|
|
58
|
+
* in commands that cannot proceed without authentication.
|
|
59
|
+
*
|
|
60
|
+
* @throws {ConfigurationError} If no token is available, with instructions for `gh auth login` or setting `GITHUB_TOKEN`
|
|
61
|
+
*/
|
|
62
|
+
export function requireGitHubToken() {
|
|
63
|
+
const token = getGitHubToken();
|
|
64
|
+
if (!token) {
|
|
65
|
+
throw new ConfigurationError('GitHub authentication required.\n\n' +
|
|
66
|
+
'Options:\n' +
|
|
67
|
+
' 1. Use gh CLI: gh auth login\n' +
|
|
68
|
+
' 2. Set GITHUB_TOKEN environment variable\n\n' +
|
|
69
|
+
'The gh CLI is recommended - install from https://cli.github.com');
|
|
70
|
+
}
|
|
71
|
+
return token;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Resets the cached GitHub token and fetch-attempted flag.
|
|
75
|
+
*
|
|
76
|
+
* Intended for use in tests to ensure a clean state between test cases.
|
|
77
|
+
* After calling this, the next call to {@link getGitHubToken} will re-fetch the token.
|
|
78
|
+
*/
|
|
79
|
+
export function resetGitHubTokenCache() {
|
|
80
|
+
cachedGitHubToken = null;
|
|
81
|
+
tokenFetchAttempted = false;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Asynchronous version of {@link getGitHubToken}.
|
|
85
|
+
*
|
|
86
|
+
* Uses `execFile` (non-blocking) instead of `execFileSync` to avoid blocking
|
|
87
|
+
* the event loop during CLI cold start. Shares the same cache as the synchronous
|
|
88
|
+
* version, so a successful async fetch makes subsequent sync calls instant.
|
|
89
|
+
*/
|
|
90
|
+
export async function getGitHubTokenAsync() {
|
|
91
|
+
if (cachedGitHubToken) {
|
|
92
|
+
return cachedGitHubToken;
|
|
93
|
+
}
|
|
94
|
+
if (tokenFetchAttempted) {
|
|
95
|
+
return null;
|
|
96
|
+
}
|
|
97
|
+
tokenFetchAttempted = true;
|
|
98
|
+
if (process.env.GITHUB_TOKEN) {
|
|
99
|
+
cachedGitHubToken = process.env.GITHUB_TOKEN;
|
|
100
|
+
return cachedGitHubToken;
|
|
101
|
+
}
|
|
102
|
+
try {
|
|
103
|
+
const token = await new Promise((resolve, reject) => {
|
|
104
|
+
execFile('gh', ['auth', 'token'], { encoding: 'utf-8', timeout: 2000 }, (error, stdout) => {
|
|
105
|
+
if (error) {
|
|
106
|
+
reject(error);
|
|
107
|
+
}
|
|
108
|
+
else {
|
|
109
|
+
resolve(stdout.trim());
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
});
|
|
113
|
+
if (token && token.length > 0) {
|
|
114
|
+
cachedGitHubToken = token;
|
|
115
|
+
debug(MODULE, 'Using GitHub token from gh CLI (async)');
|
|
116
|
+
return cachedGitHubToken;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
catch (err) {
|
|
120
|
+
debug(MODULE, 'gh auth token failed (CLI unavailable or not authenticated)', err);
|
|
121
|
+
}
|
|
122
|
+
return null;
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* GitHub username validation pattern.
|
|
126
|
+
* Usernames must start with an alphanumeric character, can contain hyphens
|
|
127
|
+
* (but not consecutive ones and not at the end), and be 1-39 characters.
|
|
128
|
+
*/
|
|
129
|
+
const GITHUB_USERNAME_RE = /^[a-zA-Z0-9](?:[a-zA-Z0-9]|-(?=[a-zA-Z0-9])){0,38}$/;
|
|
130
|
+
/**
|
|
131
|
+
* Detect the authenticated GitHub username via the `gh` CLI.
|
|
132
|
+
*
|
|
133
|
+
* Runs `gh api user --jq '.login'` asynchronously and validates the result
|
|
134
|
+
* against GitHub's username rules. Never throws — returns `null` on any failure
|
|
135
|
+
* (gh not installed, not authenticated, invalid output, etc.).
|
|
136
|
+
*/
|
|
137
|
+
export async function detectGitHubUsername() {
|
|
138
|
+
try {
|
|
139
|
+
const login = await new Promise((resolve, reject) => {
|
|
140
|
+
execFile('gh', ['api', 'user', '--jq', '.login'], { encoding: 'utf-8', timeout: 5000 }, (error, stdout) => {
|
|
141
|
+
if (error) {
|
|
142
|
+
reject(error);
|
|
143
|
+
}
|
|
144
|
+
else {
|
|
145
|
+
resolve(stdout.trim());
|
|
146
|
+
}
|
|
147
|
+
});
|
|
148
|
+
});
|
|
149
|
+
if (login && GITHUB_USERNAME_RE.test(login)) {
|
|
150
|
+
debug(MODULE, `Detected GitHub username: ${login}`);
|
|
151
|
+
return login;
|
|
152
|
+
}
|
|
153
|
+
debug(MODULE, `gh api user returned invalid username: "${login}"`);
|
|
154
|
+
return null;
|
|
155
|
+
}
|
|
156
|
+
catch (err) {
|
|
157
|
+
debug(MODULE, 'detectGitHubUsername failed', err);
|
|
158
|
+
return null;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Concurrency primitives shared across modules.
|
|
3
|
+
*/
|
|
4
|
+
/** Default concurrency limit for parallel GitHub API requests. */
|
|
5
|
+
export declare const DEFAULT_CONCURRENCY = 5;
|
|
6
|
+
/** Async sleep — exported for mockability in tests. */
|
|
7
|
+
export declare function sleep(ms: number): Promise<void>;
|
|
1
8
|
/**
|
|
2
9
|
* Runs a worker pool that processes items with bounded concurrency.
|
|
3
10
|
* N workers consume from a shared index. On any worker error, remaining
|
package/dist/core/concurrency.js
CHANGED
|
@@ -1,3 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Concurrency primitives shared across modules.
|
|
3
|
+
*/
|
|
4
|
+
/** Default concurrency limit for parallel GitHub API requests. */
|
|
5
|
+
export const DEFAULT_CONCURRENCY = 5;
|
|
6
|
+
/** Async sleep — exported for mockability in tests. */
|
|
7
|
+
export function sleep(ms) {
|
|
8
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
9
|
+
}
|
|
1
10
|
/**
|
|
2
11
|
* Runs a worker pool that processes items with bounded concurrency.
|
|
3
12
|
* N workers consume from a shared index. On any worker error, remaining
|
|
@@ -1,17 +1,20 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Domain logic extracted from src/commands/daily.ts.
|
|
2
|
+
* Domain aggregation logic extracted from src/commands/daily.ts.
|
|
3
3
|
*
|
|
4
|
-
* Pure or near-pure functions that operate on FetchedPR data
|
|
4
|
+
* Pure or near-pure functions that operate on FetchedPR data and produce
|
|
5
|
+
* the data types that the renderers / JSON output consume:
|
|
5
6
|
* - computeRepoSignals / groupPRsByRepo — per-repo aggregations
|
|
6
7
|
* - assessCapacity — capacity assessment against active PRs
|
|
7
8
|
* - collectActionableIssues — ordered list of issues needing attention
|
|
8
9
|
* - computeActionMenu — pre-computed action menu for orchestration
|
|
9
10
|
* - toShelvedPRRef — lightweight projection for digest display
|
|
10
|
-
*
|
|
11
|
-
*
|
|
11
|
+
*
|
|
12
|
+
* Rendering functions (formatActionHint, formatBriefSummary, formatSummary,
|
|
13
|
+
* printDigest) live in src/commands/daily-render.ts (#1117). This file
|
|
14
|
+
* re-exports them at the bottom so existing imports keep working without
|
|
15
|
+
* a sweep.
|
|
12
16
|
*/
|
|
13
|
-
import type { FetchedPR, FetchedPRStatus, StalenessTier, ActionReason,
|
|
14
|
-
import type { CapacityAssessment, ActionableIssue, ActionMenu } from '../formatters/json.js';
|
|
17
|
+
import type { FetchedPR, FetchedPRStatus, StalenessTier, ActionReason, AgentState, ShelvedPRRef, ComputedRepoSignals, RepoGroup, CommentedIssue, CapacityAssessment, ActionableIssue, ActionMenu } from './types.js';
|
|
15
18
|
/**
|
|
16
19
|
* Statuses indicating action needed from the contributor.
|
|
17
20
|
* Used for auto-unshelving shelved PRs.
|
|
@@ -85,12 +88,6 @@ export declare function assessCapacity(activePRs: FetchedPR[], maxActivePRs: num
|
|
|
85
88
|
* @returns Ordered actionable issues grouped by priority
|
|
86
89
|
*/
|
|
87
90
|
export declare function collectActionableIssues(prs: FetchedPR[], lastDigestAt?: string): ActionableIssue[];
|
|
88
|
-
/**
|
|
89
|
-
* Format a maintainer action hint as a human-readable label.
|
|
90
|
-
* @param hint - The maintainer action hint enum value
|
|
91
|
-
* @returns Human-readable label (e.g., "tests requested")
|
|
92
|
-
*/
|
|
93
|
-
export declare function formatActionHint(hint: MaintainerActionHint): string;
|
|
94
91
|
/**
|
|
95
92
|
* Compute the action menu from PR data and capacity.
|
|
96
93
|
* The orchestration layer can insert issue-list options (e.g., "Pick from list")
|
|
@@ -102,33 +99,4 @@ export declare function formatActionHint(hint: MaintainerActionHint): string;
|
|
|
102
99
|
* @returns Action menu with context flags for orchestration
|
|
103
100
|
*/
|
|
104
101
|
export declare function computeActionMenu(actionableIssues: ActionableIssue[], capacity: CapacityAssessment, commentedIssues?: CommentedIssue[]): ActionMenu;
|
|
105
|
-
|
|
106
|
-
* Format a brief one-liner summary for the action-first flow.
|
|
107
|
-
*
|
|
108
|
-
* @param digest - The daily digest containing PR summary stats
|
|
109
|
-
* @param issueCount - Number of actionable issues needing attention
|
|
110
|
-
* @param issueResponseCount - Number of issue replies from maintainers
|
|
111
|
-
* @returns One-line status string (e.g., "3 Active PRs | 1 needs attention | 2 issue replies")
|
|
112
|
-
*/
|
|
113
|
-
export declare function formatBriefSummary(digest: DailyDigest, issueCount: number, issueResponseCount?: number): string;
|
|
114
|
-
/**
|
|
115
|
-
* Format the full dashboard summary as markdown.
|
|
116
|
-
* Used in JSON output for Claude to display verbatim — includes all PR sections,
|
|
117
|
-
* issue replies, and capacity status.
|
|
118
|
-
*
|
|
119
|
-
* @param digest - The daily digest with all PR categories
|
|
120
|
-
* @param capacity - Current capacity assessment for display
|
|
121
|
-
* @param issueResponses - Issue replies from maintainers to display
|
|
122
|
-
* @returns Multi-line markdown string suitable for terminal or chat display
|
|
123
|
-
*/
|
|
124
|
-
export declare function formatSummary(digest: DailyDigest, capacity: CapacityAssessment, issueResponses?: CommentedIssueWithResponse[]): string;
|
|
125
|
-
/**
|
|
126
|
-
* Print digest to console as plain text.
|
|
127
|
-
* Unified renderer: uses the same section ordering as {@link formatSummary}
|
|
128
|
-
* but outputs plain text with console.log instead of markdown links.
|
|
129
|
-
*
|
|
130
|
-
* @param digest - The daily digest with all PR categories
|
|
131
|
-
* @param capacity - Current capacity assessment for display
|
|
132
|
-
* @param commentedIssues - All commented issues (filtered to responses internally)
|
|
133
|
-
*/
|
|
134
|
-
export declare function printDigest(digest: DailyDigest, capacity: CapacityAssessment, commentedIssues?: CommentedIssue[]): void;
|
|
102
|
+
export { formatActionHint, formatBriefSummary, formatSummary, printDigest } from '../commands/daily-render.js';
|
package/dist/core/daily-logic.js
CHANGED
|
@@ -1,16 +1,19 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Domain logic extracted from src/commands/daily.ts.
|
|
2
|
+
* Domain aggregation logic extracted from src/commands/daily.ts.
|
|
3
3
|
*
|
|
4
|
-
* Pure or near-pure functions that operate on FetchedPR data
|
|
4
|
+
* Pure or near-pure functions that operate on FetchedPR data and produce
|
|
5
|
+
* the data types that the renderers / JSON output consume:
|
|
5
6
|
* - computeRepoSignals / groupPRsByRepo — per-repo aggregations
|
|
6
7
|
* - assessCapacity — capacity assessment against active PRs
|
|
7
8
|
* - collectActionableIssues — ordered list of issues needing attention
|
|
8
9
|
* - computeActionMenu — pre-computed action menu for orchestration
|
|
9
10
|
* - toShelvedPRRef — lightweight projection for digest display
|
|
10
|
-
*
|
|
11
|
-
*
|
|
11
|
+
*
|
|
12
|
+
* Rendering functions (formatActionHint, formatBriefSummary, formatSummary,
|
|
13
|
+
* printDigest) live in src/commands/daily-render.ts (#1117). This file
|
|
14
|
+
* re-exports them at the bottom so existing imports keep working without
|
|
15
|
+
* a sweep.
|
|
12
16
|
*/
|
|
13
|
-
import { formatRelativeTime } from './utils.js';
|
|
14
17
|
import { warn } from './logger.js';
|
|
15
18
|
import { errorMessage } from './errors.js';
|
|
16
19
|
import { getStateManager } from './state.js';
|
|
@@ -277,25 +280,6 @@ export function collectActionableIssues(prs, lastDigestAt) {
|
|
|
277
280
|
}
|
|
278
281
|
return issues;
|
|
279
282
|
}
|
|
280
|
-
/**
|
|
281
|
-
* Format a maintainer action hint as a human-readable label.
|
|
282
|
-
* @param hint - The maintainer action hint enum value
|
|
283
|
-
* @returns Human-readable label (e.g., "tests requested")
|
|
284
|
-
*/
|
|
285
|
-
export function formatActionHint(hint) {
|
|
286
|
-
switch (hint) {
|
|
287
|
-
case 'demo_requested':
|
|
288
|
-
return 'demo/screenshot requested';
|
|
289
|
-
case 'tests_requested':
|
|
290
|
-
return 'tests requested';
|
|
291
|
-
case 'changes_requested':
|
|
292
|
-
return 'code changes requested';
|
|
293
|
-
case 'docs_requested':
|
|
294
|
-
return 'documentation requested';
|
|
295
|
-
case 'rebase_requested':
|
|
296
|
-
return 'rebase requested';
|
|
297
|
-
}
|
|
298
|
-
}
|
|
299
283
|
/**
|
|
300
284
|
* Compute the action menu from PR data and capacity.
|
|
301
285
|
* The orchestration layer can insert issue-list options (e.g., "Pick from list")
|
|
@@ -369,181 +353,10 @@ export function computeActionMenu(actionableIssues, capacity, commentedIssues =
|
|
|
369
353
|
};
|
|
370
354
|
}
|
|
371
355
|
// ---------------------------------------------------------------------------
|
|
372
|
-
// Rendering
|
|
356
|
+
// Rendering re-exports (back-compat)
|
|
373
357
|
// ---------------------------------------------------------------------------
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
* @param issueResponseCount - Number of issue replies from maintainers
|
|
380
|
-
* @returns One-line status string (e.g., "3 Active PRs | 1 needs attention | 2 issue replies")
|
|
381
|
-
*/
|
|
382
|
-
export function formatBriefSummary(digest, issueCount, issueResponseCount = 0) {
|
|
383
|
-
const attentionText = issueCount > 0 ? `${issueCount} need${issueCount === 1 ? 's' : ''} attention` : 'all on track';
|
|
384
|
-
const issueReplyText = issueResponseCount > 0 ? ` | ${issueResponseCount} issue repl${issueResponseCount === 1 ? 'y' : 'ies'}` : '';
|
|
385
|
-
return `\u{1F4CA} ${digest.summary.totalActivePRs} Active PRs | ${attentionText}${issueReplyText}`;
|
|
386
|
-
}
|
|
387
|
-
/**
|
|
388
|
-
* Format the full dashboard summary as markdown.
|
|
389
|
-
* Used in JSON output for Claude to display verbatim — includes all PR sections,
|
|
390
|
-
* issue replies, and capacity status.
|
|
391
|
-
*
|
|
392
|
-
* @param digest - The daily digest with all PR categories
|
|
393
|
-
* @param capacity - Current capacity assessment for display
|
|
394
|
-
* @param issueResponses - Issue replies from maintainers to display
|
|
395
|
-
* @returns Multi-line markdown string suitable for terminal or chat display
|
|
396
|
-
*/
|
|
397
|
-
export function formatSummary(digest, capacity, issueResponses = []) {
|
|
398
|
-
const lines = [];
|
|
399
|
-
// Header
|
|
400
|
-
lines.push('## OSS Dashboard');
|
|
401
|
-
lines.push('');
|
|
402
|
-
lines.push(`\u{1F4CA} **${digest.summary.totalActivePRs} Active PRs** | ${digest.summary.totalMergedAllTime} Merged | ${digest.summary.mergeRate}% Merge Rate`);
|
|
403
|
-
lines.push('\u2713 Dashboard generated \u2014 say "open dashboard" to view in browser');
|
|
404
|
-
lines.push('');
|
|
405
|
-
// Needs Addressing
|
|
406
|
-
if (digest.needsAddressingPRs.length > 0) {
|
|
407
|
-
lines.push('### \u274C Needs Addressing');
|
|
408
|
-
for (const pr of digest.needsAddressingPRs) {
|
|
409
|
-
lines.push(`- [${pr.repo}#${pr.number}](${pr.url}): ${pr.title}`);
|
|
410
|
-
lines.push(` \u2514\u2500 ${pr.displayLabel} ${pr.displayDescription}`);
|
|
411
|
-
}
|
|
412
|
-
lines.push('');
|
|
413
|
-
}
|
|
414
|
-
// Waiting on Maintainer
|
|
415
|
-
if (digest.waitingOnMaintainerPRs.length > 0) {
|
|
416
|
-
lines.push('### \u23F3 Waiting on Maintainer');
|
|
417
|
-
for (const pr of digest.waitingOnMaintainerPRs) {
|
|
418
|
-
lines.push(`- [${pr.repo}#${pr.number}](${pr.url}): ${pr.title}`);
|
|
419
|
-
lines.push(` \u2514\u2500 ${pr.displayDescription}`);
|
|
420
|
-
}
|
|
421
|
-
lines.push('');
|
|
422
|
-
}
|
|
423
|
-
// Recently Merged (wins!)
|
|
424
|
-
if (digest.recentlyMergedPRs.length > 0) {
|
|
425
|
-
lines.push('### \u{1F389} Recently Merged');
|
|
426
|
-
for (const pr of digest.recentlyMergedPRs) {
|
|
427
|
-
const mergedDate = pr.mergedAt ? new Date(pr.mergedAt).toLocaleDateString() : '';
|
|
428
|
-
lines.push(`- [${pr.repo}#${pr.number}](${pr.url}): ${pr.title}${mergedDate ? ` (merged ${mergedDate})` : ''}`);
|
|
429
|
-
}
|
|
430
|
-
lines.push('');
|
|
431
|
-
}
|
|
432
|
-
// Recently Closed (closed without merge)
|
|
433
|
-
if (digest.recentlyClosedPRs.length > 0) {
|
|
434
|
-
lines.push('### \u{1F6AB} Recently Closed');
|
|
435
|
-
for (const pr of digest.recentlyClosedPRs) {
|
|
436
|
-
const closedDate = pr.closedAt ? new Date(pr.closedAt).toLocaleDateString() : '';
|
|
437
|
-
lines.push(`- [${pr.repo}#${pr.number}](${pr.url}): ${pr.title}${closedDate ? ` (closed ${closedDate})` : ''}`);
|
|
438
|
-
}
|
|
439
|
-
lines.push('');
|
|
440
|
-
}
|
|
441
|
-
// Auto-unshelved (important: maintainer engagement on shelved PRs)
|
|
442
|
-
if (digest.autoUnshelvedPRs.length > 0) {
|
|
443
|
-
lines.push('### \u{1F514} Auto-Unshelved');
|
|
444
|
-
lines.push('> These PRs were shelved but a maintainer engaged \u2014 moved back to active.');
|
|
445
|
-
for (const pr of digest.autoUnshelvedPRs) {
|
|
446
|
-
lines.push(`- [${pr.repo}#${pr.number}](${pr.url}): ${pr.title} (${pr.status.replace(/_/g, ' ')})`);
|
|
447
|
-
}
|
|
448
|
-
lines.push('');
|
|
449
|
-
}
|
|
450
|
-
// Shelved PRs (dimmed, excluded from capacity)
|
|
451
|
-
if (digest.shelvedPRs.length > 0) {
|
|
452
|
-
lines.push('### \u{1F4E6} Shelved');
|
|
453
|
-
for (const pr of digest.shelvedPRs) {
|
|
454
|
-
lines.push(`- [${pr.repo}#${pr.number}](${pr.url}): ${pr.title}`);
|
|
455
|
-
}
|
|
456
|
-
lines.push('');
|
|
457
|
-
}
|
|
458
|
-
// Issue Replies
|
|
459
|
-
if (issueResponses.length > 0) {
|
|
460
|
-
lines.push('### \u{1F4AC} Issue Replies');
|
|
461
|
-
for (const issue of issueResponses) {
|
|
462
|
-
lines.push(`- [${issue.repo}#${issue.number}](${issue.url}): ${issue.title}`);
|
|
463
|
-
const timeAgo = formatRelativeTime(issue.lastResponseAt);
|
|
464
|
-
lines.push(` \u2514\u2500 @${issue.lastResponseAuthor}: "${issue.lastResponseBody.slice(0, 80)}${issue.lastResponseBody.length > 80 ? '...' : ''}"${timeAgo ? ` (${timeAgo})` : ''}`);
|
|
465
|
-
}
|
|
466
|
-
lines.push('');
|
|
467
|
-
}
|
|
468
|
-
// Capacity
|
|
469
|
-
const capacityIcon = capacity.hasCapacity ? '\u2705' : '\u26A0\uFE0F';
|
|
470
|
-
const capacityLabel = capacity.hasCapacity ? 'Ready for new work' : 'Focus on existing PRs';
|
|
471
|
-
const shelvedNote = capacity.shelvedPRCount > 0 ? ` + ${capacity.shelvedPRCount} shelved` : '';
|
|
472
|
-
lines.push(`**Capacity:** ${capacityIcon} ${capacityLabel} (${capacity.activePRCount}/${capacity.maxActivePRs} PRs${shelvedNote})`);
|
|
473
|
-
return lines.join('\n');
|
|
474
|
-
}
|
|
475
|
-
/**
|
|
476
|
-
* Print digest to console as plain text.
|
|
477
|
-
* Unified renderer: uses the same section ordering as {@link formatSummary}
|
|
478
|
-
* but outputs plain text with console.log instead of markdown links.
|
|
479
|
-
*
|
|
480
|
-
* @param digest - The daily digest with all PR categories
|
|
481
|
-
* @param capacity - Current capacity assessment for display
|
|
482
|
-
* @param commentedIssues - All commented issues (filtered to responses internally)
|
|
483
|
-
*/
|
|
484
|
-
export function printDigest(digest, capacity, commentedIssues = []) {
|
|
485
|
-
console.log('\n\u{1F4CA} OSS Daily Check\n');
|
|
486
|
-
console.log(`Active PRs: ${digest.summary.totalActivePRs}`);
|
|
487
|
-
console.log(`Needing Attention: ${digest.summary.totalNeedingAttention}`);
|
|
488
|
-
console.log(`Merged (all time): ${digest.summary.totalMergedAllTime}`);
|
|
489
|
-
console.log(`Merge Rate: ${digest.summary.mergeRate}%`);
|
|
490
|
-
console.log(`\nCapacity: ${capacity.hasCapacity ? '\u2705 Ready for new work' : '\u26A0\uFE0F Focus on existing work'}`);
|
|
491
|
-
console.log(` ${capacity.reason}\n`);
|
|
492
|
-
if (digest.needsAddressingPRs.length > 0) {
|
|
493
|
-
console.log('\u274C Needs Addressing:');
|
|
494
|
-
for (const pr of digest.needsAddressingPRs) {
|
|
495
|
-
console.log(` - ${pr.repo}#${pr.number}: ${pr.title}`);
|
|
496
|
-
console.log(` ${pr.displayLabel} ${pr.displayDescription}`);
|
|
497
|
-
}
|
|
498
|
-
console.log('');
|
|
499
|
-
}
|
|
500
|
-
if (digest.waitingOnMaintainerPRs.length > 0) {
|
|
501
|
-
console.log('\u23F3 Waiting on Maintainer:');
|
|
502
|
-
for (const pr of digest.waitingOnMaintainerPRs) {
|
|
503
|
-
console.log(` - ${pr.repo}#${pr.number}: ${pr.title}`);
|
|
504
|
-
console.log(` ${pr.displayDescription}`);
|
|
505
|
-
}
|
|
506
|
-
console.log('');
|
|
507
|
-
}
|
|
508
|
-
if (digest.recentlyMergedPRs.length > 0) {
|
|
509
|
-
console.log('\u{1F389} Recently Merged:');
|
|
510
|
-
for (const pr of digest.recentlyMergedPRs) {
|
|
511
|
-
const mergedDate = pr.mergedAt ? new Date(pr.mergedAt).toLocaleDateString() : '';
|
|
512
|
-
console.log(` - ${pr.repo}#${pr.number}: ${pr.title}${mergedDate ? ` (merged ${mergedDate})` : ''}`);
|
|
513
|
-
}
|
|
514
|
-
console.log('');
|
|
515
|
-
}
|
|
516
|
-
if (digest.recentlyClosedPRs.length > 0) {
|
|
517
|
-
console.log('\u{1F6AB} Recently Closed:');
|
|
518
|
-
for (const pr of digest.recentlyClosedPRs) {
|
|
519
|
-
const closedDate = pr.closedAt ? new Date(pr.closedAt).toLocaleDateString() : '';
|
|
520
|
-
console.log(` - ${pr.repo}#${pr.number}: ${pr.title}${closedDate ? ` (closed ${closedDate})` : ''}`);
|
|
521
|
-
}
|
|
522
|
-
console.log('');
|
|
523
|
-
}
|
|
524
|
-
if (digest.autoUnshelvedPRs.length > 0) {
|
|
525
|
-
console.log('\u{1F514} Auto-Unshelved:');
|
|
526
|
-
for (const pr of digest.autoUnshelvedPRs) {
|
|
527
|
-
console.log(` - ${pr.repo}#${pr.number}: ${pr.title} (${pr.status.replace(/_/g, ' ')})`);
|
|
528
|
-
}
|
|
529
|
-
console.log('');
|
|
530
|
-
}
|
|
531
|
-
if (digest.shelvedPRs.length > 0) {
|
|
532
|
-
console.log('\u{1F4E6} Shelved:');
|
|
533
|
-
for (const pr of digest.shelvedPRs) {
|
|
534
|
-
console.log(` - ${pr.repo}#${pr.number}: ${pr.title}`);
|
|
535
|
-
}
|
|
536
|
-
console.log('');
|
|
537
|
-
}
|
|
538
|
-
const issueResponses = commentedIssues.filter((i) => i.status === 'new_response');
|
|
539
|
-
if (issueResponses.length > 0) {
|
|
540
|
-
console.log('\u{1F4AC} Issue Replies:');
|
|
541
|
-
for (const issue of issueResponses) {
|
|
542
|
-
console.log(` - ${issue.repo}#${issue.number}: ${issue.title}`);
|
|
543
|
-
console.log(` @${issue.lastResponseAuthor}: ${issue.lastResponseBody.slice(0, 80)}${issue.lastResponseBody.length > 80 ? '...' : ''}`);
|
|
544
|
-
}
|
|
545
|
-
console.log('');
|
|
546
|
-
}
|
|
547
|
-
console.log('Run with --json for structured output');
|
|
548
|
-
console.log('Run "dashboard serve" for browser view');
|
|
549
|
-
}
|
|
358
|
+
//
|
|
359
|
+
// The actual implementations live in src/commands/daily-render.ts. Existing
|
|
360
|
+
// callers that import these from `core/daily-logic` continue to work; new
|
|
361
|
+
// callers should import from `commands/daily-render` directly.
|
|
362
|
+
export { formatActionHint, formatBriefSummary, formatSummary, printDigest } from '../commands/daily-render.js';
|