@oss-autopilot/core 1.8.0 → 1.10.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/dist/cli-registry.js +2 -2
- package/dist/cli.bundle.cjs +61 -61
- package/dist/commands/comments.js +11 -0
- package/dist/commands/config.js +0 -9
- package/dist/commands/daily.js +25 -2
- package/dist/commands/index.d.ts +1 -0
- package/dist/commands/setup.d.ts +0 -1
- package/dist/commands/setup.js +0 -26
- package/dist/commands/startup.js +25 -3
- package/dist/core/errors.d.ts +6 -0
- package/dist/core/errors.js +37 -0
- package/dist/core/gist-state-store.d.ts +169 -0
- package/dist/core/gist-state-store.js +424 -0
- package/dist/core/index.d.ts +3 -2
- package/dist/core/index.js +3 -2
- package/dist/core/state-persistence.d.ts +14 -2
- package/dist/core/state-persistence.js +46 -12
- package/dist/core/state-schema.d.ts +19 -42
- package/dist/core/state-schema.js +11 -19
- package/dist/core/state.d.ts +38 -29
- package/dist/core/state.js +121 -53
- package/dist/core/types.d.ts +2 -2
- package/dist/core/types.js +2 -2
- package/dist/core/utils.d.ts +30 -0
- package/dist/core/utils.js +36 -0
- package/dist/formatters/json.d.ts +5 -2
- package/dist/formatters/json.js +4 -3
- package/package.json +1 -1
|
@@ -32,14 +32,6 @@ export declare const IssueScopeSchema: z.ZodEnum<{
|
|
|
32
32
|
beginner: "beginner";
|
|
33
33
|
intermediate: "intermediate";
|
|
34
34
|
}>;
|
|
35
|
-
export declare const StateEventTypeSchema: z.ZodEnum<{
|
|
36
|
-
pr_tracked: "pr_tracked";
|
|
37
|
-
pr_merged: "pr_merged";
|
|
38
|
-
pr_closed: "pr_closed";
|
|
39
|
-
pr_dormant: "pr_dormant";
|
|
40
|
-
daily_check: "daily_check";
|
|
41
|
-
comment_posted: "comment_posted";
|
|
42
|
-
}>;
|
|
43
35
|
export declare const RepoSignalsSchema: z.ZodObject<{
|
|
44
36
|
hasActiveMaintainers: z.ZodBoolean;
|
|
45
37
|
isResponsive: z.ZodBoolean;
|
|
@@ -61,28 +53,22 @@ export declare const RepoScoreSchema: z.ZodObject<{
|
|
|
61
53
|
stargazersCount: z.ZodOptional<z.ZodNumber>;
|
|
62
54
|
language: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
63
55
|
}, z.core.$strip>;
|
|
64
|
-
export declare const StateEventSchema: z.ZodObject<{
|
|
65
|
-
id: z.ZodString;
|
|
66
|
-
type: z.ZodEnum<{
|
|
67
|
-
pr_tracked: "pr_tracked";
|
|
68
|
-
pr_merged: "pr_merged";
|
|
69
|
-
pr_closed: "pr_closed";
|
|
70
|
-
pr_dormant: "pr_dormant";
|
|
71
|
-
daily_check: "daily_check";
|
|
72
|
-
comment_posted: "comment_posted";
|
|
73
|
-
}>;
|
|
74
|
-
at: z.ZodString;
|
|
75
|
-
data: z.ZodRecord<z.ZodString, z.ZodUnknown>;
|
|
76
|
-
}, z.core.$strip>;
|
|
77
56
|
export declare const StoredMergedPRSchema: z.ZodObject<{
|
|
78
57
|
url: z.ZodString;
|
|
79
58
|
title: z.ZodString;
|
|
80
59
|
mergedAt: z.ZodString;
|
|
60
|
+
learningsExtractedAt: z.ZodOptional<z.ZodString>;
|
|
81
61
|
}, z.core.$strip>;
|
|
82
62
|
export declare const StoredClosedPRSchema: z.ZodObject<{
|
|
83
63
|
url: z.ZodString;
|
|
84
64
|
title: z.ZodString;
|
|
85
65
|
closedAt: z.ZodString;
|
|
66
|
+
learningsExtractedAt: z.ZodOptional<z.ZodString>;
|
|
67
|
+
}, z.core.$strip>;
|
|
68
|
+
export declare const AnalyzedIssueConversationSchema: z.ZodObject<{
|
|
69
|
+
url: z.ZodString;
|
|
70
|
+
repo: z.ZodString;
|
|
71
|
+
analyzedAt: z.ZodString;
|
|
86
72
|
}, z.core.$strip>;
|
|
87
73
|
export declare const ContributionGuidelinesSchema: z.ZodObject<{
|
|
88
74
|
branchNamingConvention: z.ZodOptional<z.ZodString>;
|
|
@@ -209,10 +195,8 @@ export declare const AgentConfigSchema: z.ZodObject<{
|
|
|
209
195
|
trustedProjects: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
210
196
|
githubUsername: z.ZodDefault<z.ZodString>;
|
|
211
197
|
minRepoScoreThreshold: z.ZodDefault<z.ZodNumber>;
|
|
212
|
-
scoreThreshold: z.ZodDefault<z.ZodNumber>;
|
|
213
198
|
starredRepos: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
214
199
|
starredReposLastFetched: z.ZodOptional<z.ZodString>;
|
|
215
|
-
showHealthCheck: z.ZodOptional<z.ZodBoolean>;
|
|
216
200
|
squashByDefault: z.ZodDefault<z.ZodUnion<readonly [z.ZodBoolean, z.ZodLiteral<"ask">]>>;
|
|
217
201
|
localRepoScanPaths: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
218
202
|
minStars: z.ZodDefault<z.ZodNumber>;
|
|
@@ -229,6 +213,7 @@ export declare const AgentConfigSchema: z.ZodObject<{
|
|
|
229
213
|
lastActivityAt: z.ZodString;
|
|
230
214
|
}, z.core.$strip>>>;
|
|
231
215
|
issueListPath: z.ZodOptional<z.ZodString>;
|
|
216
|
+
skippedIssuesPath: z.ZodOptional<z.ZodString>;
|
|
232
217
|
projectCategories: z.ZodDefault<z.ZodArray<z.ZodEnum<{
|
|
233
218
|
nonprofit: "nonprofit";
|
|
234
219
|
devtools: "devtools";
|
|
@@ -319,7 +304,8 @@ export declare const DailyDigestSchema: z.ZodObject<{
|
|
|
319
304
|
}, z.core.$strip>;
|
|
320
305
|
}, z.core.$strip>;
|
|
321
306
|
export declare const AgentStateSchema: z.ZodObject<{
|
|
322
|
-
version: z.ZodLiteral<
|
|
307
|
+
version: z.ZodLiteral<3>;
|
|
308
|
+
gistId: z.ZodOptional<z.ZodString>;
|
|
323
309
|
repoScores: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
324
310
|
repo: z.ZodString;
|
|
325
311
|
score: z.ZodNumber;
|
|
@@ -355,10 +341,8 @@ export declare const AgentStateSchema: z.ZodObject<{
|
|
|
355
341
|
trustedProjects: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
356
342
|
githubUsername: z.ZodDefault<z.ZodString>;
|
|
357
343
|
minRepoScoreThreshold: z.ZodDefault<z.ZodNumber>;
|
|
358
|
-
scoreThreshold: z.ZodDefault<z.ZodNumber>;
|
|
359
344
|
starredRepos: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
360
345
|
starredReposLastFetched: z.ZodOptional<z.ZodString>;
|
|
361
|
-
showHealthCheck: z.ZodOptional<z.ZodBoolean>;
|
|
362
346
|
squashByDefault: z.ZodDefault<z.ZodUnion<readonly [z.ZodBoolean, z.ZodLiteral<"ask">]>>;
|
|
363
347
|
localRepoScanPaths: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
364
348
|
minStars: z.ZodDefault<z.ZodNumber>;
|
|
@@ -375,6 +359,7 @@ export declare const AgentStateSchema: z.ZodObject<{
|
|
|
375
359
|
lastActivityAt: z.ZodString;
|
|
376
360
|
}, z.core.$strip>>>;
|
|
377
361
|
issueListPath: z.ZodOptional<z.ZodString>;
|
|
362
|
+
skippedIssuesPath: z.ZodOptional<z.ZodString>;
|
|
378
363
|
projectCategories: z.ZodDefault<z.ZodArray<z.ZodEnum<{
|
|
379
364
|
nonprofit: "nonprofit";
|
|
380
365
|
devtools: "devtools";
|
|
@@ -385,19 +370,6 @@ export declare const AgentStateSchema: z.ZodObject<{
|
|
|
385
370
|
}>>>;
|
|
386
371
|
preferredOrgs: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
387
372
|
}, z.core.$strip>>;
|
|
388
|
-
events: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
389
|
-
id: z.ZodString;
|
|
390
|
-
type: z.ZodEnum<{
|
|
391
|
-
pr_tracked: "pr_tracked";
|
|
392
|
-
pr_merged: "pr_merged";
|
|
393
|
-
pr_closed: "pr_closed";
|
|
394
|
-
pr_dormant: "pr_dormant";
|
|
395
|
-
daily_check: "daily_check";
|
|
396
|
-
comment_posted: "comment_posted";
|
|
397
|
-
}>;
|
|
398
|
-
at: z.ZodString;
|
|
399
|
-
data: z.ZodRecord<z.ZodString, z.ZodUnknown>;
|
|
400
|
-
}, z.core.$strip>>>;
|
|
401
373
|
lastRunAt: z.ZodDefault<z.ZodString>;
|
|
402
374
|
lastDigestAt: z.ZodOptional<z.ZodString>;
|
|
403
375
|
lastDigest: z.ZodOptional<z.ZodObject<{
|
|
@@ -452,7 +424,6 @@ export declare const AgentStateSchema: z.ZodObject<{
|
|
|
452
424
|
monthlyMergedCounts: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodNumber>>;
|
|
453
425
|
monthlyClosedCounts: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodNumber>>;
|
|
454
426
|
monthlyOpenedCounts: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodNumber>>;
|
|
455
|
-
dailyActivityCounts: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodNumber>>;
|
|
456
427
|
localRepoCache: z.ZodOptional<z.ZodObject<{
|
|
457
428
|
repos: z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
458
429
|
path: z.ZodString;
|
|
@@ -466,11 +437,18 @@ export declare const AgentStateSchema: z.ZodObject<{
|
|
|
466
437
|
url: z.ZodString;
|
|
467
438
|
title: z.ZodString;
|
|
468
439
|
mergedAt: z.ZodString;
|
|
440
|
+
learningsExtractedAt: z.ZodOptional<z.ZodString>;
|
|
469
441
|
}, z.core.$strip>>>;
|
|
470
442
|
closedPRs: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
471
443
|
url: z.ZodString;
|
|
472
444
|
title: z.ZodString;
|
|
473
445
|
closedAt: z.ZodString;
|
|
446
|
+
learningsExtractedAt: z.ZodOptional<z.ZodString>;
|
|
447
|
+
}, z.core.$strip>>>;
|
|
448
|
+
analyzedIssueConversations: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
449
|
+
url: z.ZodString;
|
|
450
|
+
repo: z.ZodString;
|
|
451
|
+
analyzedAt: z.ZodString;
|
|
474
452
|
}, z.core.$strip>>>;
|
|
475
453
|
activeIssues: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
476
454
|
id: z.ZodNumber;
|
|
@@ -521,12 +499,11 @@ export type IssueStatus = z.infer<typeof IssueStatusSchema>;
|
|
|
521
499
|
export type FetchedPRStatus = z.infer<typeof FetchedPRStatusSchema>;
|
|
522
500
|
export type ProjectCategory = z.infer<typeof ProjectCategorySchema>;
|
|
523
501
|
export type IssueScope = z.infer<typeof IssueScopeSchema>;
|
|
524
|
-
export type StateEventType = z.infer<typeof StateEventTypeSchema>;
|
|
525
502
|
export type RepoSignals = z.infer<typeof RepoSignalsSchema>;
|
|
526
503
|
export type RepoScore = z.infer<typeof RepoScoreSchema>;
|
|
527
|
-
export type StateEvent = z.infer<typeof StateEventSchema>;
|
|
528
504
|
export type StoredMergedPR = z.infer<typeof StoredMergedPRSchema>;
|
|
529
505
|
export type StoredClosedPR = z.infer<typeof StoredClosedPRSchema>;
|
|
506
|
+
export type AnalyzedIssueConversation = z.infer<typeof AnalyzedIssueConversationSchema>;
|
|
530
507
|
export type ContributionGuidelines = z.infer<typeof ContributionGuidelinesSchema>;
|
|
531
508
|
export type IssueVettingResult = z.infer<typeof IssueVettingResultSchema>;
|
|
532
509
|
export type TrackedIssue = z.infer<typeof TrackedIssueSchema>;
|
|
@@ -21,14 +21,6 @@ export const ProjectCategorySchema = z.enum([
|
|
|
21
21
|
'education',
|
|
22
22
|
]);
|
|
23
23
|
export const IssueScopeSchema = z.enum(['beginner', 'intermediate', 'advanced']);
|
|
24
|
-
export const StateEventTypeSchema = z.enum([
|
|
25
|
-
'pr_tracked',
|
|
26
|
-
'pr_merged',
|
|
27
|
-
'pr_closed',
|
|
28
|
-
'pr_dormant',
|
|
29
|
-
'daily_check',
|
|
30
|
-
'comment_posted',
|
|
31
|
-
]);
|
|
32
24
|
// ── 2. Leaf schemas ──────────────────────────────────────────────────
|
|
33
25
|
export const RepoSignalsSchema = z.object({
|
|
34
26
|
hasActiveMaintainers: z.boolean(),
|
|
@@ -47,21 +39,22 @@ export const RepoScoreSchema = z.object({
|
|
|
47
39
|
stargazersCount: z.number().optional(),
|
|
48
40
|
language: z.string().nullable().optional(),
|
|
49
41
|
});
|
|
50
|
-
export const StateEventSchema = z.object({
|
|
51
|
-
id: z.string(),
|
|
52
|
-
type: StateEventTypeSchema,
|
|
53
|
-
at: z.string(),
|
|
54
|
-
data: z.record(z.string(), z.unknown()),
|
|
55
|
-
});
|
|
56
42
|
export const StoredMergedPRSchema = z.object({
|
|
57
43
|
url: z.string(),
|
|
58
44
|
title: z.string(),
|
|
59
45
|
mergedAt: z.string(),
|
|
46
|
+
learningsExtractedAt: z.string().optional(),
|
|
60
47
|
});
|
|
61
48
|
export const StoredClosedPRSchema = z.object({
|
|
62
49
|
url: z.string(),
|
|
63
50
|
title: z.string(),
|
|
64
51
|
closedAt: z.string(),
|
|
52
|
+
learningsExtractedAt: z.string().optional(),
|
|
53
|
+
});
|
|
54
|
+
export const AnalyzedIssueConversationSchema = z.object({
|
|
55
|
+
url: z.string(),
|
|
56
|
+
repo: z.string(),
|
|
57
|
+
analyzedAt: z.string(),
|
|
65
58
|
});
|
|
66
59
|
// ── 3. Contribution schemas ──────────────────────────────────────────
|
|
67
60
|
export const ContributionGuidelinesSchema = z.object({
|
|
@@ -135,10 +128,8 @@ export const AgentConfigSchema = z.object({
|
|
|
135
128
|
trustedProjects: z.array(z.string()).default([]),
|
|
136
129
|
githubUsername: z.string().default(''),
|
|
137
130
|
minRepoScoreThreshold: z.number().default(4),
|
|
138
|
-
scoreThreshold: z.number().int().min(1).max(10).default(6),
|
|
139
131
|
starredRepos: z.array(z.string()).default([]),
|
|
140
132
|
starredReposLastFetched: z.string().optional(),
|
|
141
|
-
showHealthCheck: z.boolean().optional(),
|
|
142
133
|
squashByDefault: z.union([z.boolean(), z.literal('ask')]).default(true),
|
|
143
134
|
localRepoScanPaths: z.array(z.string()).optional(),
|
|
144
135
|
minStars: z.number().default(50),
|
|
@@ -148,6 +139,7 @@ export const AgentConfigSchema = z.object({
|
|
|
148
139
|
dismissedIssues: z.record(z.string(), z.string()).default({}),
|
|
149
140
|
statusOverrides: z.record(z.string(), StatusOverrideSchema).optional(),
|
|
150
141
|
issueListPath: z.string().optional(),
|
|
142
|
+
skippedIssuesPath: z.string().optional(),
|
|
151
143
|
projectCategories: z.array(ProjectCategorySchema).default([]),
|
|
152
144
|
preferredOrgs: z.array(z.string()).default([]),
|
|
153
145
|
});
|
|
@@ -197,19 +189,19 @@ export const DailyDigestSchema = z.object({
|
|
|
197
189
|
});
|
|
198
190
|
// ── 8. Root schema ───────────────────────────────────────────────────
|
|
199
191
|
export const AgentStateSchema = z.object({
|
|
200
|
-
version: z.literal(
|
|
192
|
+
version: z.literal(3),
|
|
193
|
+
gistId: z.string().optional(),
|
|
201
194
|
repoScores: z.record(z.string(), RepoScoreSchema).default({}),
|
|
202
195
|
config: AgentConfigSchema.default(() => AgentConfigSchema.parse({})),
|
|
203
|
-
events: z.array(StateEventSchema).default([]),
|
|
204
196
|
lastRunAt: z.string().default(() => new Date().toISOString()),
|
|
205
197
|
lastDigestAt: z.string().optional(),
|
|
206
198
|
lastDigest: DailyDigestSchema.optional(),
|
|
207
199
|
monthlyMergedCounts: z.record(z.string(), z.number()).optional(),
|
|
208
200
|
monthlyClosedCounts: z.record(z.string(), z.number()).optional(),
|
|
209
201
|
monthlyOpenedCounts: z.record(z.string(), z.number()).optional(),
|
|
210
|
-
dailyActivityCounts: z.record(z.string(), z.number()).optional(),
|
|
211
202
|
localRepoCache: LocalRepoCacheSchema.optional(),
|
|
212
203
|
mergedPRs: z.array(StoredMergedPRSchema).optional(),
|
|
213
204
|
closedPRs: z.array(StoredClosedPRSchema).optional(),
|
|
205
|
+
analyzedIssueConversations: z.array(AnalyzedIssueConversationSchema).optional(),
|
|
214
206
|
activeIssues: z.array(TrackedIssueSchema).default([]),
|
|
215
207
|
});
|
package/dist/core/state.d.ts
CHANGED
|
@@ -3,23 +3,26 @@
|
|
|
3
3
|
* Thin coordinator that delegates persistence to state-persistence.ts
|
|
4
4
|
* and scoring logic to repo-score-manager.ts.
|
|
5
5
|
*/
|
|
6
|
-
import { AgentState, TrackedIssue, RepoScore, RepoScoreUpdate,
|
|
6
|
+
import { AgentState, TrackedIssue, RepoScore, RepoScoreUpdate, DailyDigest, LocalRepoCache, StatusOverride, FetchedPRStatus, StoredMergedPR, StoredClosedPR } from './types.js';
|
|
7
7
|
import type { Stats } from './repo-score-manager.js';
|
|
8
|
+
import { GistStateStore } from './gist-state-store.js';
|
|
8
9
|
export { acquireLock, releaseLock, atomicWriteFileSync } from './state-persistence.js';
|
|
9
10
|
export type { Stats } from './repo-score-manager.js';
|
|
10
11
|
/**
|
|
11
12
|
* Singleton manager for persistent agent state stored in ~/.oss-autopilot/state.json.
|
|
12
13
|
*
|
|
13
14
|
* Delegates file I/O to state-persistence.ts and scoring logic to repo-score-manager.ts.
|
|
14
|
-
* Retains lightweight CRUD operations for config,
|
|
15
|
+
* Retains lightweight CRUD operations for config, issues, shelving, dismissal,
|
|
15
16
|
* and status overrides.
|
|
16
17
|
*/
|
|
17
18
|
export declare class StateManager {
|
|
18
|
-
|
|
19
|
-
|
|
19
|
+
protected state: AgentState;
|
|
20
|
+
protected inMemoryOnly: boolean;
|
|
20
21
|
private lastLoadedMtimeMs;
|
|
21
22
|
private _batching;
|
|
22
23
|
private _batchDirty;
|
|
24
|
+
protected gistStore: GistStateStore | null;
|
|
25
|
+
protected gistDegraded: boolean;
|
|
23
26
|
/**
|
|
24
27
|
* Create a new StateManager instance.
|
|
25
28
|
* @param inMemoryOnly - When true, state is held only in memory and never read from or
|
|
@@ -27,6 +30,15 @@ export declare class StateManager {
|
|
|
27
30
|
* Defaults to false (normal persistent mode).
|
|
28
31
|
*/
|
|
29
32
|
constructor(inMemoryOnly?: boolean);
|
|
33
|
+
/**
|
|
34
|
+
* Async factory that creates a StateManager backed by a GitHub Gist.
|
|
35
|
+
*
|
|
36
|
+
* The regular constructor is synchronous (for backwards-compat), but Gist
|
|
37
|
+
* bootstrapping requires network calls, so this factory is async.
|
|
38
|
+
*
|
|
39
|
+
* @param token - GitHub personal access token with `gist` scope
|
|
40
|
+
*/
|
|
41
|
+
static createWithGist(token: string): Promise<StateManager>;
|
|
30
42
|
/**
|
|
31
43
|
* Attempt PR count reconciliation, logging a warning on failure.
|
|
32
44
|
* Called after every state load from disk.
|
|
@@ -58,8 +70,17 @@ export declare class StateManager {
|
|
|
58
70
|
/**
|
|
59
71
|
* Persist the current state to disk, creating a timestamped backup of the previous
|
|
60
72
|
* state file first. In in-memory mode, only updates `lastRunAt` without any file I/O.
|
|
73
|
+
*
|
|
74
|
+
* In Gist mode, writes to a local cache file (not the main state file) so the Gist
|
|
75
|
+
* remains the source of truth. Use `checkpoint()` to push state to the Gist.
|
|
61
76
|
*/
|
|
62
77
|
save(): void;
|
|
78
|
+
/** Push current state to Gist (async). Call at well-defined moments (end of daily, after claim). */
|
|
79
|
+
checkpoint(): Promise<boolean>;
|
|
80
|
+
/** Whether this StateManager is backed by a Gist. */
|
|
81
|
+
isGistMode(): boolean;
|
|
82
|
+
/** Whether the Gist is in degraded mode (using local cache fallback). */
|
|
83
|
+
isGistDegraded(): boolean;
|
|
63
84
|
/**
|
|
64
85
|
* Get the current state as a read-only snapshot.
|
|
65
86
|
*/
|
|
@@ -89,11 +110,6 @@ export declare class StateManager {
|
|
|
89
110
|
* @param counts - Monthly opened PR counts keyed by YYYY-MM
|
|
90
111
|
*/
|
|
91
112
|
setMonthlyOpenedCounts(counts: Record<string, number>): void;
|
|
92
|
-
/**
|
|
93
|
-
* Update daily activity counts for dashboard display.
|
|
94
|
-
* @param counts - Daily activity counts keyed by YYYY-MM-DD
|
|
95
|
-
*/
|
|
96
|
-
setDailyActivityCounts(counts: Record<string, number>): void;
|
|
97
113
|
/**
|
|
98
114
|
* Update the local repository cache.
|
|
99
115
|
* @param cache - Local repository cache mapping repo names to paths
|
|
@@ -122,26 +138,6 @@ export declare class StateManager {
|
|
|
122
138
|
* @param config - Partial config object to merge
|
|
123
139
|
*/
|
|
124
140
|
updateConfig(config: Partial<AgentState['config']>): void;
|
|
125
|
-
/**
|
|
126
|
-
* Append a new event to the event log and auto-persist.
|
|
127
|
-
* Events are capped at 1000 to prevent unbounded growth.
|
|
128
|
-
* @param type - The event type identifier
|
|
129
|
-
* @param data - Arbitrary event payload
|
|
130
|
-
*/
|
|
131
|
-
appendEvent(type: StateEventType, data: Record<string, unknown>): void;
|
|
132
|
-
/**
|
|
133
|
-
* Filter events by type.
|
|
134
|
-
* @param type - The event type to filter by
|
|
135
|
-
* @returns Events matching the given type
|
|
136
|
-
*/
|
|
137
|
-
getEventsByType(type: StateEventType): StateEvent[];
|
|
138
|
-
/**
|
|
139
|
-
* Filter events within a date range.
|
|
140
|
-
* @param since - Start of range (inclusive)
|
|
141
|
-
* @param until - End of range (inclusive), defaults to now
|
|
142
|
-
* @returns Events within the date range
|
|
143
|
-
*/
|
|
144
|
-
getEventsInRange(since: Date, until?: Date): StateEvent[];
|
|
145
141
|
/**
|
|
146
142
|
* Track a new issue. No-op if the issue URL is already tracked.
|
|
147
143
|
* @param issue - The issue to track
|
|
@@ -287,6 +283,19 @@ export declare class StateManager {
|
|
|
287
283
|
* ```
|
|
288
284
|
*/
|
|
289
285
|
export declare function getStateManager(): StateManager;
|
|
286
|
+
/**
|
|
287
|
+
* Get or create a StateManager with Gist-backed persistence.
|
|
288
|
+
* If a StateManager already exists (from sync init), returns it.
|
|
289
|
+
* If a token is provided and no manager exists, creates one with Gist backing.
|
|
290
|
+
* Falls back to sync initialization if no token is provided.
|
|
291
|
+
*
|
|
292
|
+
* **Important:** This must be called (and awaited) before any command runs for
|
|
293
|
+
* Gist mode to be active. It pre-sets the singleton so that subsequent
|
|
294
|
+
* `getStateManager()` calls return the Gist-backed instance. If this is not
|
|
295
|
+
* called first, `getStateManager()` will lazily create a local-only
|
|
296
|
+
* StateManager and Gist checkpoints will be no-ops.
|
|
297
|
+
*/
|
|
298
|
+
export declare function getStateManagerAsync(token?: string): Promise<StateManager>;
|
|
290
299
|
/**
|
|
291
300
|
* Reset the singleton StateManager instance to null. Intended for test isolation.
|
|
292
301
|
*/
|
package/dist/core/state.js
CHANGED
|
@@ -3,19 +3,20 @@
|
|
|
3
3
|
* Thin coordinator that delegates persistence to state-persistence.ts
|
|
4
4
|
* and scoring logic to repo-score-manager.ts.
|
|
5
5
|
*/
|
|
6
|
-
import
|
|
6
|
+
import * as fs from 'fs';
|
|
7
|
+
import { loadState, saveState, reloadStateIfChanged, createFreshState, atomicWriteFileSync, } from './state-persistence.js';
|
|
7
8
|
import * as repoScoring from './repo-score-manager.js';
|
|
8
9
|
import { debug, warn } from './logger.js';
|
|
9
10
|
import { errorMessage } from './errors.js';
|
|
11
|
+
import { GistStateStore } from './gist-state-store.js';
|
|
12
|
+
import { getStatePath, getStateCachePath } from './utils.js';
|
|
10
13
|
export { acquireLock, releaseLock, atomicWriteFileSync } from './state-persistence.js';
|
|
11
14
|
const MODULE = 'state';
|
|
12
|
-
// Maximum number of events to retain in the event log
|
|
13
|
-
const MAX_EVENTS = 1000;
|
|
14
15
|
/**
|
|
15
16
|
* Singleton manager for persistent agent state stored in ~/.oss-autopilot/state.json.
|
|
16
17
|
*
|
|
17
18
|
* Delegates file I/O to state-persistence.ts and scoring logic to repo-score-manager.ts.
|
|
18
|
-
* Retains lightweight CRUD operations for config,
|
|
19
|
+
* Retains lightweight CRUD operations for config, issues, shelving, dismissal,
|
|
19
20
|
* and status overrides.
|
|
20
21
|
*/
|
|
21
22
|
export class StateManager {
|
|
@@ -24,6 +25,8 @@ export class StateManager {
|
|
|
24
25
|
lastLoadedMtimeMs = 0;
|
|
25
26
|
_batching = false;
|
|
26
27
|
_batchDirty = false;
|
|
28
|
+
gistStore = null;
|
|
29
|
+
gistDegraded = false;
|
|
27
30
|
/**
|
|
28
31
|
* Create a new StateManager instance.
|
|
29
32
|
* @param inMemoryOnly - When true, state is held only in memory and never read from or
|
|
@@ -42,6 +45,53 @@ export class StateManager {
|
|
|
42
45
|
this.tryReconcilePRCounts();
|
|
43
46
|
}
|
|
44
47
|
}
|
|
48
|
+
/**
|
|
49
|
+
* Async factory that creates a StateManager backed by a GitHub Gist.
|
|
50
|
+
*
|
|
51
|
+
* The regular constructor is synchronous (for backwards-compat), but Gist
|
|
52
|
+
* bootstrapping requires network calls, so this factory is async.
|
|
53
|
+
*
|
|
54
|
+
* @param token - GitHub personal access token with `gist` scope
|
|
55
|
+
*/
|
|
56
|
+
static async createWithGist(token) {
|
|
57
|
+
// Dynamic import to avoid circular dependencies
|
|
58
|
+
const { getOctokit } = await import('./github.js');
|
|
59
|
+
const octokit = getOctokit(token);
|
|
60
|
+
const gistStore = new GistStateStore(octokit);
|
|
61
|
+
// Check if local state exists for migration
|
|
62
|
+
const statePath = getStatePath();
|
|
63
|
+
let result;
|
|
64
|
+
if (fs.existsSync(statePath)) {
|
|
65
|
+
// Existing user: load local state and migrate it into the Gist if no Gist exists yet.
|
|
66
|
+
const localStateResult = loadState();
|
|
67
|
+
const migrationResult = await gistStore.bootstrapWithMigration(localStateResult.state);
|
|
68
|
+
result = migrationResult;
|
|
69
|
+
// If a new Gist was just created from local state, rename the local file
|
|
70
|
+
// so it no longer competes as the source of truth on future startups.
|
|
71
|
+
if (migrationResult.migrated) {
|
|
72
|
+
try {
|
|
73
|
+
const preGistPath = statePath + '.pre-gist-migration';
|
|
74
|
+
fs.renameSync(statePath, preGistPath);
|
|
75
|
+
debug(MODULE, `Renamed ${statePath} to ${preGistPath} after Gist migration`);
|
|
76
|
+
}
|
|
77
|
+
catch (err) {
|
|
78
|
+
warn(MODULE, `Failed to rename state.json after Gist migration: ${err}`);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
else {
|
|
83
|
+
result = await gistStore.bootstrap();
|
|
84
|
+
}
|
|
85
|
+
const manager = new StateManager(true); // start in-memory
|
|
86
|
+
manager.state = result.state;
|
|
87
|
+
if (result.gistId) {
|
|
88
|
+
manager.state.gistId = result.gistId;
|
|
89
|
+
}
|
|
90
|
+
manager.gistStore = gistStore;
|
|
91
|
+
manager.gistDegraded = result.degraded ?? false;
|
|
92
|
+
manager.inMemoryOnly = false; // re-enable persistence
|
|
93
|
+
return manager;
|
|
94
|
+
}
|
|
45
95
|
/**
|
|
46
96
|
* Attempt PR count reconciliation, logging a warning on failure.
|
|
47
97
|
* Called after every state load from disk.
|
|
@@ -119,14 +169,44 @@ export class StateManager {
|
|
|
119
169
|
/**
|
|
120
170
|
* Persist the current state to disk, creating a timestamped backup of the previous
|
|
121
171
|
* state file first. In in-memory mode, only updates `lastRunAt` without any file I/O.
|
|
172
|
+
*
|
|
173
|
+
* In Gist mode, writes to a local cache file (not the main state file) so the Gist
|
|
174
|
+
* remains the source of truth. Use `checkpoint()` to push state to the Gist.
|
|
122
175
|
*/
|
|
123
176
|
save() {
|
|
124
177
|
this.state.lastRunAt = new Date().toISOString();
|
|
125
178
|
if (this.inMemoryOnly) {
|
|
126
179
|
return;
|
|
127
180
|
}
|
|
181
|
+
if (this.gistStore) {
|
|
182
|
+
// In Gist mode, write to local cache (not main state file).
|
|
183
|
+
// The Gist is the source of truth; local cache is for fallback.
|
|
184
|
+
try {
|
|
185
|
+
atomicWriteFileSync(getStateCachePath(), JSON.stringify(this.state, null, 2), 0o600);
|
|
186
|
+
}
|
|
187
|
+
catch {
|
|
188
|
+
// Best-effort cache write
|
|
189
|
+
}
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
// Local file mode (existing behavior)
|
|
128
193
|
this.lastLoadedMtimeMs = saveState(this.state);
|
|
129
194
|
}
|
|
195
|
+
/** Push current state to Gist (async). Call at well-defined moments (end of daily, after claim). */
|
|
196
|
+
async checkpoint() {
|
|
197
|
+
if (!this.gistStore)
|
|
198
|
+
return true; // not in Gist mode
|
|
199
|
+
this.gistStore.setState(JSON.stringify(this.state, null, 2));
|
|
200
|
+
return this.gistStore.push();
|
|
201
|
+
}
|
|
202
|
+
/** Whether this StateManager is backed by a Gist. */
|
|
203
|
+
isGistMode() {
|
|
204
|
+
return this.gistStore !== null;
|
|
205
|
+
}
|
|
206
|
+
/** Whether the Gist is in degraded mode (using local cache fallback). */
|
|
207
|
+
isGistDegraded() {
|
|
208
|
+
return this.gistDegraded;
|
|
209
|
+
}
|
|
130
210
|
/**
|
|
131
211
|
* Get the current state as a read-only snapshot.
|
|
132
212
|
*/
|
|
@@ -140,6 +220,8 @@ export class StateManager {
|
|
|
140
220
|
reloadIfChanged() {
|
|
141
221
|
if (this.inMemoryOnly)
|
|
142
222
|
return false;
|
|
223
|
+
if (this.gistStore)
|
|
224
|
+
return false; // Gist is the source of truth; skip local file reload
|
|
143
225
|
const result = reloadStateIfChanged(this.lastLoadedMtimeMs);
|
|
144
226
|
if (!result)
|
|
145
227
|
return false;
|
|
@@ -182,14 +264,6 @@ export class StateManager {
|
|
|
182
264
|
this.state.monthlyOpenedCounts = counts;
|
|
183
265
|
this.autoSave();
|
|
184
266
|
}
|
|
185
|
-
/**
|
|
186
|
-
* Update daily activity counts for dashboard display.
|
|
187
|
-
* @param counts - Daily activity counts keyed by YYYY-MM-DD
|
|
188
|
-
*/
|
|
189
|
-
setDailyActivityCounts(counts) {
|
|
190
|
-
this.state.dailyActivityCounts = counts;
|
|
191
|
-
this.autoSave();
|
|
192
|
-
}
|
|
193
267
|
/**
|
|
194
268
|
* Update the local repository cache.
|
|
195
269
|
* @param cache - Local repository cache mapping repo names to paths
|
|
@@ -261,47 +335,6 @@ export class StateManager {
|
|
|
261
335
|
this.state.config = { ...this.state.config, ...config };
|
|
262
336
|
this.autoSave();
|
|
263
337
|
}
|
|
264
|
-
// === Event Logging ===
|
|
265
|
-
/**
|
|
266
|
-
* Append a new event to the event log and auto-persist.
|
|
267
|
-
* Events are capped at 1000 to prevent unbounded growth.
|
|
268
|
-
* @param type - The event type identifier
|
|
269
|
-
* @param data - Arbitrary event payload
|
|
270
|
-
*/
|
|
271
|
-
appendEvent(type, data) {
|
|
272
|
-
const event = {
|
|
273
|
-
id: `evt_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`,
|
|
274
|
-
type,
|
|
275
|
-
at: new Date().toISOString(),
|
|
276
|
-
data,
|
|
277
|
-
};
|
|
278
|
-
this.state.events.push(event);
|
|
279
|
-
// Cap the events array to prevent unbounded growth
|
|
280
|
-
if (this.state.events.length > MAX_EVENTS) {
|
|
281
|
-
this.state.events = this.state.events.slice(-MAX_EVENTS);
|
|
282
|
-
}
|
|
283
|
-
this.autoSave();
|
|
284
|
-
}
|
|
285
|
-
/**
|
|
286
|
-
* Filter events by type.
|
|
287
|
-
* @param type - The event type to filter by
|
|
288
|
-
* @returns Events matching the given type
|
|
289
|
-
*/
|
|
290
|
-
getEventsByType(type) {
|
|
291
|
-
return this.state.events.filter((e) => e.type === type);
|
|
292
|
-
}
|
|
293
|
-
/**
|
|
294
|
-
* Filter events within a date range.
|
|
295
|
-
* @param since - Start of range (inclusive)
|
|
296
|
-
* @param until - End of range (inclusive), defaults to now
|
|
297
|
-
* @returns Events within the date range
|
|
298
|
-
*/
|
|
299
|
-
getEventsInRange(since, until = new Date()) {
|
|
300
|
-
return this.state.events.filter((e) => {
|
|
301
|
-
const eventTime = new Date(e.at);
|
|
302
|
-
return eventTime >= since && eventTime <= until;
|
|
303
|
-
});
|
|
304
|
-
}
|
|
305
338
|
// === Issue Management ===
|
|
306
339
|
/**
|
|
307
340
|
* Track a new issue. No-op if the issue URL is already tracked.
|
|
@@ -590,6 +623,7 @@ export class StateManager {
|
|
|
590
623
|
}
|
|
591
624
|
// Singleton instance
|
|
592
625
|
let stateManager = null;
|
|
626
|
+
let asyncManagerPromise = null;
|
|
593
627
|
/**
|
|
594
628
|
* Get the singleton StateManager instance, creating it on first call.
|
|
595
629
|
* @returns The shared StateManager instance
|
|
@@ -609,9 +643,43 @@ export function getStateManager() {
|
|
|
609
643
|
}
|
|
610
644
|
return stateManager;
|
|
611
645
|
}
|
|
646
|
+
/**
|
|
647
|
+
* Get or create a StateManager with Gist-backed persistence.
|
|
648
|
+
* If a StateManager already exists (from sync init), returns it.
|
|
649
|
+
* If a token is provided and no manager exists, creates one with Gist backing.
|
|
650
|
+
* Falls back to sync initialization if no token is provided.
|
|
651
|
+
*
|
|
652
|
+
* **Important:** This must be called (and awaited) before any command runs for
|
|
653
|
+
* Gist mode to be active. It pre-sets the singleton so that subsequent
|
|
654
|
+
* `getStateManager()` calls return the Gist-backed instance. If this is not
|
|
655
|
+
* called first, `getStateManager()` will lazily create a local-only
|
|
656
|
+
* StateManager and Gist checkpoints will be no-ops.
|
|
657
|
+
*/
|
|
658
|
+
export async function getStateManagerAsync(token) {
|
|
659
|
+
if (stateManager)
|
|
660
|
+
return stateManager;
|
|
661
|
+
if (asyncManagerPromise)
|
|
662
|
+
return asyncManagerPromise;
|
|
663
|
+
if (token) {
|
|
664
|
+
asyncManagerPromise = StateManager.createWithGist(token)
|
|
665
|
+
.then((mgr) => {
|
|
666
|
+
stateManager = mgr;
|
|
667
|
+
asyncManagerPromise = null;
|
|
668
|
+
return mgr;
|
|
669
|
+
})
|
|
670
|
+
.catch((err) => {
|
|
671
|
+
asyncManagerPromise = null;
|
|
672
|
+
warn(MODULE, `Unhandled Gist initialization error, falling back to local-only mode (not a normal degraded bootstrap): ${err}`);
|
|
673
|
+
return getStateManager(); // fall back to sync/local
|
|
674
|
+
});
|
|
675
|
+
return asyncManagerPromise;
|
|
676
|
+
}
|
|
677
|
+
return getStateManager();
|
|
678
|
+
}
|
|
612
679
|
/**
|
|
613
680
|
* Reset the singleton StateManager instance to null. Intended for test isolation.
|
|
614
681
|
*/
|
|
615
682
|
export function resetStateManager() {
|
|
616
683
|
stateManager = null;
|
|
684
|
+
asyncManagerPromise = null;
|
|
617
685
|
}
|
package/dist/core/types.d.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Core types for the Open Source Contribution Agent
|
|
3
3
|
*/
|
|
4
4
|
import type { FetchedPRStatus, RepoSignals, TrackedIssue, IssueVettingResult, IssueScope, AgentConfig, AgentState } from './state-schema.js';
|
|
5
|
-
export type { IssueStatus, FetchedPRStatus, ProjectCategory, IssueScope,
|
|
5
|
+
export type { IssueStatus, FetchedPRStatus, ProjectCategory, IssueScope, RepoSignals, RepoScore, StoredMergedPR, StoredClosedPR, AnalyzedIssueConversation, ContributionGuidelines, IssueVettingResult, TrackedIssue, ShelvedPRRef, StatusOverride, AgentConfig, LocalRepoCache, ClosedPR, MergedPR, DailyDigest, AgentState, } from './state-schema.js';
|
|
6
6
|
/** CI pipeline status for a PR's latest commit. */
|
|
7
7
|
export type CIStatus = 'passing' | 'failing' | 'pending' | 'unknown';
|
|
8
8
|
/**
|
|
@@ -233,7 +233,7 @@ interface CommentedIssueWithoutResponse extends CommentedIssueBase {
|
|
|
233
233
|
export type CommentedIssue = CommentedIssueWithResponse | CommentedIssueWithoutResponse;
|
|
234
234
|
/** Default configuration applied to new state files. All fields can be overridden via `/setup-oss`. */
|
|
235
235
|
export declare const DEFAULT_CONFIG: AgentConfig;
|
|
236
|
-
/** Initial state written to `~/.oss-autopilot/state.json` on first run. Uses
|
|
236
|
+
/** Initial state written to `~/.oss-autopilot/state.json` on first run. Uses v3 architecture. */
|
|
237
237
|
export declare const INITIAL_STATE: AgentState;
|
|
238
238
|
export declare const PROJECT_CATEGORIES: ("nonprofit" | "devtools" | "infrastructure" | "web-frameworks" | "data-ml" | "education")[];
|
|
239
239
|
export declare const ISSUE_SCOPES: ("advanced" | "beginner" | "intermediate")[];
|
package/dist/core/types.js
CHANGED
|
@@ -12,8 +12,8 @@ export function isBelowMinStars(stargazersCount, minStars) {
|
|
|
12
12
|
// ── Schema-derived constants ─────────────────────────────────────────
|
|
13
13
|
/** Default configuration applied to new state files. All fields can be overridden via `/setup-oss`. */
|
|
14
14
|
export const DEFAULT_CONFIG = AgentConfigSchema.parse({});
|
|
15
|
-
/** Initial state written to `~/.oss-autopilot/state.json` on first run. Uses
|
|
16
|
-
export const INITIAL_STATE = AgentStateSchema.parse({ version:
|
|
15
|
+
/** Initial state written to `~/.oss-autopilot/state.json` on first run. Uses v3 architecture. */
|
|
16
|
+
export const INITIAL_STATE = AgentStateSchema.parse({ version: 3 });
|
|
17
17
|
// ── Const arrays (derived from Zod schemas for runtime iteration) ────
|
|
18
18
|
export const PROJECT_CATEGORIES = ProjectCategorySchema.options;
|
|
19
19
|
export const ISSUE_SCOPES = IssueScopeSchema.options;
|