@stackbilt/aegis-core 0.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/package.json +96 -0
- package/schema.sql +586 -0
- package/src/adapters/voice/cloudflare-agent.ts +34 -0
- package/src/auth.ts +124 -0
- package/src/bluesky.ts +464 -0
- package/src/claude-tools/content.ts +188 -0
- package/src/claude-tools/email.ts +69 -0
- package/src/claude-tools/github.ts +440 -0
- package/src/claude-tools/goals.ts +116 -0
- package/src/claude-tools/index.ts +353 -0
- package/src/claude-tools/web.ts +59 -0
- package/src/claude.ts +406 -0
- package/src/codebeast.ts +200 -0
- package/src/composite.ts +715 -0
- package/src/content/column.ts +80 -0
- package/src/content/hero-image.ts +47 -0
- package/src/content/index.ts +27 -0
- package/src/content/journal.ts +91 -0
- package/src/content/roundtable.ts +163 -0
- package/src/core.ts +309 -0
- package/src/dashboard.ts +620 -0
- package/src/decision-docs.ts +284 -0
- package/src/dispatch.ts +13 -0
- package/src/edge-env.ts +58 -0
- package/src/email.ts +850 -0
- package/src/exports.ts +156 -0
- package/src/github-projects.ts +312 -0
- package/src/github.ts +670 -0
- package/src/groq.ts +247 -0
- package/src/health-page.ts +578 -0
- package/src/index.ts +89 -0
- package/src/kernel/argus-actions.ts +397 -0
- package/src/kernel/argus-correlation.ts +639 -0
- package/src/kernel/board.ts +91 -0
- package/src/kernel/briefing.ts +177 -0
- package/src/kernel/classify-memory-topic.ts +166 -0
- package/src/kernel/cognition.ts +377 -0
- package/src/kernel/court-cards.ts +163 -0
- package/src/kernel/dispatch.ts +587 -0
- package/src/kernel/domain.ts +50 -0
- package/src/kernel/dynamic-tools.ts +322 -0
- package/src/kernel/executor-port.ts +45 -0
- package/src/kernel/executors/claude.ts +73 -0
- package/src/kernel/executors/direct.ts +237 -0
- package/src/kernel/executors/groq.ts +18 -0
- package/src/kernel/executors/index.ts +87 -0
- package/src/kernel/executors/tarotscript.ts +104 -0
- package/src/kernel/executors/workers-ai.ts +54 -0
- package/src/kernel/insight-cache.ts +76 -0
- package/src/kernel/memory/agenda.ts +200 -0
- package/src/kernel/memory/blocks.ts +188 -0
- package/src/kernel/memory/consolidation.ts +194 -0
- package/src/kernel/memory/episodic.ts +241 -0
- package/src/kernel/memory/goals.ts +156 -0
- package/src/kernel/memory/graph.ts +290 -0
- package/src/kernel/memory/index.ts +11 -0
- package/src/kernel/memory/insights.ts +316 -0
- package/src/kernel/memory/procedural.ts +467 -0
- package/src/kernel/memory/pruning.ts +67 -0
- package/src/kernel/memory/recall.ts +367 -0
- package/src/kernel/memory/semantic.ts +315 -0
- package/src/kernel/memory/synthesis.ts +161 -0
- package/src/kernel/memory-adapter.ts +369 -0
- package/src/kernel/memory-guardrails.ts +76 -0
- package/src/kernel/port.ts +23 -0
- package/src/kernel/resilience.ts +322 -0
- package/src/kernel/router.ts +471 -0
- package/src/kernel/scheduled/agent-dispatch.ts +252 -0
- package/src/kernel/scheduled/argus-analytics.ts +247 -0
- package/src/kernel/scheduled/argus-heartbeat.ts +320 -0
- package/src/kernel/scheduled/argus-notify.ts +348 -0
- package/src/kernel/scheduled/board-sync.ts +110 -0
- package/src/kernel/scheduled/ci-watcher.ts +125 -0
- package/src/kernel/scheduled/cognitive-metrics.ts +377 -0
- package/src/kernel/scheduled/consolidation.ts +229 -0
- package/src/kernel/scheduled/content-drip.ts +47 -0
- package/src/kernel/scheduled/content.ts +6 -0
- package/src/kernel/scheduled/conversation-facts.ts +204 -0
- package/src/kernel/scheduled/cost-report.ts +84 -0
- package/src/kernel/scheduled/curiosity.ts +219 -0
- package/src/kernel/scheduled/dev-activity.ts +44 -0
- package/src/kernel/scheduled/digest.ts +317 -0
- package/src/kernel/scheduled/dreaming/agenda-triage.ts +115 -0
- package/src/kernel/scheduled/dreaming/facts.ts +239 -0
- package/src/kernel/scheduled/dreaming/index.ts +8 -0
- package/src/kernel/scheduled/dreaming/llm.ts +33 -0
- package/src/kernel/scheduled/dreaming/pattern-synthesis.ts +124 -0
- package/src/kernel/scheduled/dreaming/persona.ts +75 -0
- package/src/kernel/scheduled/dreaming/symbolic.ts +31 -0
- package/src/kernel/scheduled/dreaming/task-proposals.ts +80 -0
- package/src/kernel/scheduled/dreaming.ts +66 -0
- package/src/kernel/scheduled/entropy.ts +149 -0
- package/src/kernel/scheduled/escalation.ts +192 -0
- package/src/kernel/scheduled/feed-watcher.ts +206 -0
- package/src/kernel/scheduled/goals.ts +214 -0
- package/src/kernel/scheduled/governance.ts +41 -0
- package/src/kernel/scheduled/heartbeat.ts +220 -0
- package/src/kernel/scheduled/inbox-processor.ts +174 -0
- package/src/kernel/scheduled/index.ts +245 -0
- package/src/kernel/scheduled/issue-proposer.ts +478 -0
- package/src/kernel/scheduled/issue-watcher.ts +128 -0
- package/src/kernel/scheduled/pr-automerge.ts +213 -0
- package/src/kernel/scheduled/product-health.ts +107 -0
- package/src/kernel/scheduled/reflection.ts +373 -0
- package/src/kernel/scheduled/self-improvement.ts +114 -0
- package/src/kernel/scheduled/social-engage.ts +175 -0
- package/src/kernel/scheduled/task-audit.ts +60 -0
- package/src/kernel/symbolic.ts +156 -0
- package/src/kernel/types.ts +145 -0
- package/src/landing.ts +1190 -0
- package/src/lib/audit-chain/chain.ts +28 -0
- package/src/lib/audit-chain/types.ts +12 -0
- package/src/lib/observability/errors.ts +55 -0
- package/src/markdown.ts +164 -0
- package/src/mcp/handlers.ts +647 -0
- package/src/mcp/server.ts +184 -0
- package/src/mcp/tools.ts +316 -0
- package/src/mcp-client.ts +275 -0
- package/src/mcp-server.ts +2 -0
- package/src/operator/config.example.ts +60 -0
- package/src/operator/config.ts +60 -0
- package/src/operator/index.ts +46 -0
- package/src/operator/persona.example.ts +34 -0
- package/src/operator/persona.ts +34 -0
- package/src/operator/prompt-builder.ts +190 -0
- package/src/operator/types.ts +43 -0
- package/src/pulse.ts +1179 -0
- package/src/routes/bluesky.ts +116 -0
- package/src/routes/cc-tasks.ts +328 -0
- package/src/routes/codebeast.ts +1 -0
- package/src/routes/content.ts +194 -0
- package/src/routes/conversations.ts +25 -0
- package/src/routes/dynamic-tools.ts +111 -0
- package/src/routes/feedback.ts +192 -0
- package/src/routes/health.ts +147 -0
- package/src/routes/messages.ts +228 -0
- package/src/routes/observability.ts +82 -0
- package/src/routes/operator-logs.ts +42 -0
- package/src/routes/pages.ts +96 -0
- package/src/routes/sessions.ts +54 -0
- package/src/sanitize.ts +73 -0
- package/src/schema-enums.ts +155 -0
- package/src/search.ts +112 -0
- package/src/task-intelligence.ts +497 -0
- package/src/types.ts +194 -0
- package/src/ui.ts +5 -0
- package/src/version.ts +3 -0
- package/src/workers-ai-chat.ts +333 -0
package/src/exports.ts
ADDED
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @stackbilt/aegis-core — Main barrel export.
|
|
3
|
+
*
|
|
4
|
+
* This is the primary entry point for consumers who install
|
|
5
|
+
* aegis-core as a dependency. It re-exports the extension
|
|
6
|
+
* interfaces, factory function, and core subsystem modules.
|
|
7
|
+
*
|
|
8
|
+
* Usage:
|
|
9
|
+
* import { createAegisApp, dispatch } from '@stackbilt/aegis-core';
|
|
10
|
+
*
|
|
11
|
+
* For deeper imports, use subpath exports:
|
|
12
|
+
* import { recordMemory } from '@stackbilt/aegis-core/kernel/memory';
|
|
13
|
+
* import { buildEdgeEnv } from '@stackbilt/aegis-core/edge-env';
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
// ─── Factory + Extension Contracts ──────────────────────────
|
|
17
|
+
export {
|
|
18
|
+
createAegisApp,
|
|
19
|
+
type AegisApp,
|
|
20
|
+
type AegisAppConfig,
|
|
21
|
+
type ScheduledTaskPlugin,
|
|
22
|
+
type TaskPhase,
|
|
23
|
+
type ExecutorPlugin,
|
|
24
|
+
type ExecutorResult,
|
|
25
|
+
type RoutePlugin,
|
|
26
|
+
type McpToolPlugin,
|
|
27
|
+
type McpToolDefinition,
|
|
28
|
+
} from './core.js';
|
|
29
|
+
|
|
30
|
+
// ─── Core Types ─────────────────────────────────────────────
|
|
31
|
+
export type {
|
|
32
|
+
// Worker bindings
|
|
33
|
+
Env,
|
|
34
|
+
MemoryServiceBinding,
|
|
35
|
+
MemoryStoreRequest,
|
|
36
|
+
MemoryStoreResult,
|
|
37
|
+
MemoryRecallQuery,
|
|
38
|
+
MemoryFragmentResult,
|
|
39
|
+
MemoryForgetFilter,
|
|
40
|
+
MemoryStatsResult,
|
|
41
|
+
MessageMetadata,
|
|
42
|
+
} from './types.js';
|
|
43
|
+
|
|
44
|
+
export type {
|
|
45
|
+
// Kernel types
|
|
46
|
+
EdgeEnv,
|
|
47
|
+
} from './kernel/dispatch.js';
|
|
48
|
+
|
|
49
|
+
export type {
|
|
50
|
+
KernelIntent,
|
|
51
|
+
DispatchResult,
|
|
52
|
+
Executor,
|
|
53
|
+
ExecutionPlan,
|
|
54
|
+
CognitiveState,
|
|
55
|
+
Channel,
|
|
56
|
+
EpisodicEntry,
|
|
57
|
+
ProceduralEntry,
|
|
58
|
+
ProceduralStatus,
|
|
59
|
+
MemoryEntry,
|
|
60
|
+
Refinement,
|
|
61
|
+
} from './kernel/types.js';
|
|
62
|
+
|
|
63
|
+
export type {
|
|
64
|
+
OperatorConfig,
|
|
65
|
+
Product,
|
|
66
|
+
SelfModel,
|
|
67
|
+
} from './operator/types.js';
|
|
68
|
+
|
|
69
|
+
// ─── Dispatch ───────────────────────────────────────────────
|
|
70
|
+
export {
|
|
71
|
+
dispatch,
|
|
72
|
+
dispatchStream,
|
|
73
|
+
createIntent,
|
|
74
|
+
} from './kernel/dispatch.js';
|
|
75
|
+
|
|
76
|
+
// ─── Memory ─────────────────────────────────────────────────
|
|
77
|
+
export {
|
|
78
|
+
// Episodic
|
|
79
|
+
recordEpisode,
|
|
80
|
+
getEpisodeStats,
|
|
81
|
+
getConversationHistory,
|
|
82
|
+
budgetConversationHistory,
|
|
83
|
+
// Procedural
|
|
84
|
+
getProcedure,
|
|
85
|
+
getAllProcedures,
|
|
86
|
+
upsertProcedure,
|
|
87
|
+
addRefinement,
|
|
88
|
+
degradeProcedure,
|
|
89
|
+
procedureKey,
|
|
90
|
+
// Semantic
|
|
91
|
+
recordMemory,
|
|
92
|
+
searchMemoryByKeywords,
|
|
93
|
+
recallMemory,
|
|
94
|
+
getAllMemoryForContext,
|
|
95
|
+
// Consolidation
|
|
96
|
+
consolidateEpisodicToSemantic,
|
|
97
|
+
// Pruning
|
|
98
|
+
pruneMemory,
|
|
99
|
+
// Insights
|
|
100
|
+
publishInsight,
|
|
101
|
+
validateInsight,
|
|
102
|
+
promoteInsight,
|
|
103
|
+
// Agenda
|
|
104
|
+
getActiveAgendaItems,
|
|
105
|
+
addAgendaItem,
|
|
106
|
+
resolveAgendaItem,
|
|
107
|
+
getAgendaContext,
|
|
108
|
+
// Goals
|
|
109
|
+
getActiveGoals,
|
|
110
|
+
addGoal,
|
|
111
|
+
updateGoalStatus,
|
|
112
|
+
recordGoalAction,
|
|
113
|
+
// Graph
|
|
114
|
+
extractNodes,
|
|
115
|
+
createEdges,
|
|
116
|
+
activateGraph,
|
|
117
|
+
} from './kernel/memory/index.js';
|
|
118
|
+
|
|
119
|
+
// ─── Executors ──────────────────────────────────────────────
|
|
120
|
+
export {
|
|
121
|
+
executeClaude,
|
|
122
|
+
executeClaudeOpus,
|
|
123
|
+
executeClaudeStream,
|
|
124
|
+
executeGroq,
|
|
125
|
+
executeWorkersAi,
|
|
126
|
+
executeGptOss,
|
|
127
|
+
executeDirect,
|
|
128
|
+
executeCodeTask,
|
|
129
|
+
executeTarotScript,
|
|
130
|
+
executeWithAnthropicFailover,
|
|
131
|
+
buildMcpRegistry,
|
|
132
|
+
} from './kernel/executors/index.js';
|
|
133
|
+
|
|
134
|
+
// ─── Scheduled Tasks ────────────────────────────────────────
|
|
135
|
+
export { runScheduledTasks } from './kernel/scheduled/index.js';
|
|
136
|
+
|
|
137
|
+
// ─── Operator ───────────────────────────────────────────────
|
|
138
|
+
export { operatorConfig, setOperatorConfig, renderTemplate } from './operator/index.js';
|
|
139
|
+
|
|
140
|
+
// ─── Auth ───────────────────────────────────────────────────
|
|
141
|
+
export { bearerAuth } from './auth.js';
|
|
142
|
+
|
|
143
|
+
// ─── Edge Env ───────────────────────────────────────────────
|
|
144
|
+
export { buildEdgeEnv } from './edge-env.js';
|
|
145
|
+
|
|
146
|
+
// ─── MCP ────────────────────────────────────────────────────
|
|
147
|
+
export { handleMcpRequest } from './mcp-server.js';
|
|
148
|
+
|
|
149
|
+
// ─── Router ─────────────────────────────────────────────────
|
|
150
|
+
export { route } from './kernel/router.js';
|
|
151
|
+
|
|
152
|
+
// ─── Version ────────────────────────────────────────────────
|
|
153
|
+
export { VERSION } from './version.js';
|
|
154
|
+
|
|
155
|
+
// ─── Observability ──────────────────────────────────────────
|
|
156
|
+
export { InMemoryErrorTracker } from './lib/observability/errors.js';
|
|
@@ -0,0 +1,312 @@
|
|
|
1
|
+
// GitHub Projects v2 -- GraphQL API
|
|
2
|
+
// Separate from github.ts (REST) because GraphQL has fundamentally
|
|
3
|
+
// different request patterns (single endpoint, query strings, variables).
|
|
4
|
+
|
|
5
|
+
const GITHUB_GRAPHQL = 'https://api.github.com/graphql';
|
|
6
|
+
const RETRY_STATUSES = new Set([500, 502, 503]);
|
|
7
|
+
const MAX_RETRIES = 2;
|
|
8
|
+
const RETRY_BASE_MS = 500;
|
|
9
|
+
|
|
10
|
+
// --- Types ---
|
|
11
|
+
|
|
12
|
+
export interface ProjectField {
|
|
13
|
+
id: string;
|
|
14
|
+
name: string;
|
|
15
|
+
options?: { id: string; name: string }[];
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface ProjectItem {
|
|
19
|
+
id: string;
|
|
20
|
+
content: {
|
|
21
|
+
__typename: string;
|
|
22
|
+
number: number;
|
|
23
|
+
title: string;
|
|
24
|
+
state: string;
|
|
25
|
+
repository: { nameWithOwner: string };
|
|
26
|
+
labels: { nodes: { name: string }[] };
|
|
27
|
+
assignees: { nodes: { login: string }[] };
|
|
28
|
+
} | null;
|
|
29
|
+
fieldValues: {
|
|
30
|
+
nodes: { field?: { name: string }; name?: string }[];
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export interface BoardStatus {
|
|
35
|
+
backlog: string; // option ID
|
|
36
|
+
queued: string;
|
|
37
|
+
in_progress: string;
|
|
38
|
+
blocked: string;
|
|
39
|
+
shipped: string;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// --- Core GraphQL fetch ---
|
|
43
|
+
|
|
44
|
+
export async function ghGraphQL<T = unknown>(
|
|
45
|
+
token: string,
|
|
46
|
+
query: string,
|
|
47
|
+
variables: Record<string, unknown> = {},
|
|
48
|
+
): Promise<T> {
|
|
49
|
+
for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
|
|
50
|
+
const res = await fetch(GITHUB_GRAPHQL, {
|
|
51
|
+
method: 'POST',
|
|
52
|
+
headers: {
|
|
53
|
+
Authorization: `Bearer ${token}`,
|
|
54
|
+
'Content-Type': 'application/json',
|
|
55
|
+
'User-Agent': 'aegis-worker/1.0',
|
|
56
|
+
},
|
|
57
|
+
body: JSON.stringify({ query, variables }),
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
// Rate limit
|
|
61
|
+
if (res.status === 403 || res.status === 429) {
|
|
62
|
+
const body = await res.text();
|
|
63
|
+
throw new Error(`GitHub GraphQL ${res.status}: ${body}`);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Transient server errors
|
|
67
|
+
if (RETRY_STATUSES.has(res.status) && attempt < MAX_RETRIES) {
|
|
68
|
+
const delayMs = RETRY_BASE_MS * Math.pow(2, attempt);
|
|
69
|
+
console.warn(`[github-projects] ${res.status}, retrying in ${delayMs}ms`);
|
|
70
|
+
await new Promise(r => setTimeout(r, delayMs));
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
if (!res.ok) {
|
|
75
|
+
const body = await res.text();
|
|
76
|
+
throw new Error(`GitHub GraphQL ${res.status}: ${body}`);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const json = (await res.json()) as { data?: T; errors?: { message: string }[] };
|
|
80
|
+
if (json.errors?.length) {
|
|
81
|
+
throw new Error(`GitHub GraphQL errors: ${json.errors.map(e => e.message).join('; ')}`);
|
|
82
|
+
}
|
|
83
|
+
return json.data as T;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
throw new Error('GitHub GraphQL: exhausted retries');
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// --- Project Discovery ---
|
|
90
|
+
|
|
91
|
+
/** Find an existing org-level project by title, or create one. */
|
|
92
|
+
export async function findOrCreateProject(
|
|
93
|
+
token: string,
|
|
94
|
+
orgLogin: string,
|
|
95
|
+
title: string,
|
|
96
|
+
): Promise<string> {
|
|
97
|
+
// Search existing projects
|
|
98
|
+
const searchData = await ghGraphQL<{
|
|
99
|
+
organization: {
|
|
100
|
+
projectsV2: { nodes: { id: string; title: string }[] };
|
|
101
|
+
};
|
|
102
|
+
}>(token, `
|
|
103
|
+
query($org: String!, $search: String!) {
|
|
104
|
+
organization(login: $org) {
|
|
105
|
+
projectsV2(first: 20, query: $search) {
|
|
106
|
+
nodes { id title }
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
`, { org: orgLogin, search: title });
|
|
111
|
+
|
|
112
|
+
const existing = searchData.organization.projectsV2.nodes.find(p => p.title === title);
|
|
113
|
+
if (existing) return existing.id;
|
|
114
|
+
|
|
115
|
+
// Create new project — first get org node ID
|
|
116
|
+
const orgData = await ghGraphQL<{
|
|
117
|
+
organization: { id: string };
|
|
118
|
+
}>(token, `
|
|
119
|
+
query($org: String!) {
|
|
120
|
+
organization(login: $org) { id }
|
|
121
|
+
}
|
|
122
|
+
`, { org: orgLogin });
|
|
123
|
+
|
|
124
|
+
const createData = await ghGraphQL<{
|
|
125
|
+
createProjectV2: { projectV2: { id: string } };
|
|
126
|
+
}>(token, `
|
|
127
|
+
mutation($ownerId: ID!, $title: String!) {
|
|
128
|
+
createProjectV2(input: { ownerId: $ownerId, title: $title }) {
|
|
129
|
+
projectV2 { id }
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
`, { ownerId: orgData.organization.id, title });
|
|
133
|
+
|
|
134
|
+
return createData.createProjectV2.projectV2.id;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// --- Field Management ---
|
|
138
|
+
|
|
139
|
+
/** Get the Status field and its option IDs. */
|
|
140
|
+
export async function getStatusField(
|
|
141
|
+
token: string,
|
|
142
|
+
projectId: string,
|
|
143
|
+
): Promise<{ fieldId: string; options: BoardStatus } | null> {
|
|
144
|
+
const data = await ghGraphQL<{
|
|
145
|
+
node: {
|
|
146
|
+
fields: {
|
|
147
|
+
nodes: {
|
|
148
|
+
__typename: string;
|
|
149
|
+
id: string;
|
|
150
|
+
name: string;
|
|
151
|
+
options?: { id: string; name: string }[];
|
|
152
|
+
}[];
|
|
153
|
+
};
|
|
154
|
+
};
|
|
155
|
+
}>(token, `
|
|
156
|
+
query($projectId: ID!) {
|
|
157
|
+
node(id: $projectId) {
|
|
158
|
+
... on ProjectV2 {
|
|
159
|
+
fields(first: 20) {
|
|
160
|
+
nodes {
|
|
161
|
+
... on ProjectV2SingleSelectField {
|
|
162
|
+
__typename id name
|
|
163
|
+
options { id name }
|
|
164
|
+
}
|
|
165
|
+
... on ProjectV2Field {
|
|
166
|
+
__typename id name
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
`, { projectId });
|
|
174
|
+
|
|
175
|
+
const statusField = data.node.fields.nodes.find(
|
|
176
|
+
f => f.name === 'Status' && f.__typename === 'ProjectV2SingleSelectField',
|
|
177
|
+
);
|
|
178
|
+
if (!statusField?.options) return null;
|
|
179
|
+
|
|
180
|
+
const findOption = (name: string) => statusField.options!.find(o => o.name === name)?.id ?? '';
|
|
181
|
+
return {
|
|
182
|
+
fieldId: statusField.id,
|
|
183
|
+
options: {
|
|
184
|
+
backlog: findOption('Backlog'),
|
|
185
|
+
queued: findOption('Queued'),
|
|
186
|
+
in_progress: findOption('In Progress'),
|
|
187
|
+
blocked: findOption('Blocked'),
|
|
188
|
+
shipped: findOption('Shipped'),
|
|
189
|
+
},
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// --- Item Operations ---
|
|
194
|
+
|
|
195
|
+
/** Add an issue or PR to the project board. Returns the project item ID. */
|
|
196
|
+
export async function addItemToProject(
|
|
197
|
+
token: string,
|
|
198
|
+
projectId: string,
|
|
199
|
+
contentNodeId: string,
|
|
200
|
+
): Promise<string> {
|
|
201
|
+
const data = await ghGraphQL<{
|
|
202
|
+
addProjectV2ItemById: { item: { id: string } };
|
|
203
|
+
}>(token, `
|
|
204
|
+
mutation($projectId: ID!, $contentId: ID!) {
|
|
205
|
+
addProjectV2ItemById(input: { projectId: $projectId, contentId: $contentId }) {
|
|
206
|
+
item { id }
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
`, { projectId, contentId: contentNodeId });
|
|
210
|
+
|
|
211
|
+
return data.addProjectV2ItemById.item.id;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/** Update the Status field of a project item. */
|
|
215
|
+
export async function updateItemStatus(
|
|
216
|
+
token: string,
|
|
217
|
+
projectId: string,
|
|
218
|
+
itemId: string,
|
|
219
|
+
statusFieldId: string,
|
|
220
|
+
optionId: string,
|
|
221
|
+
): Promise<void> {
|
|
222
|
+
await ghGraphQL(token, `
|
|
223
|
+
mutation($projectId: ID!, $itemId: ID!, $fieldId: ID!, $optionId: String!) {
|
|
224
|
+
updateProjectV2ItemFieldValue(input: {
|
|
225
|
+
projectId: $projectId
|
|
226
|
+
itemId: $itemId
|
|
227
|
+
fieldId: $fieldId
|
|
228
|
+
value: { singleSelectOptionId: $optionId }
|
|
229
|
+
}) {
|
|
230
|
+
projectV2Item { id }
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
`, { projectId, itemId, fieldId: statusFieldId, optionId });
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/** List project items with pagination. */
|
|
237
|
+
export async function listProjectItems(
|
|
238
|
+
token: string,
|
|
239
|
+
projectId: string,
|
|
240
|
+
cursor?: string,
|
|
241
|
+
): Promise<{ items: ProjectItem[]; nextCursor: string | null }> {
|
|
242
|
+
const data = await ghGraphQL<{
|
|
243
|
+
node: {
|
|
244
|
+
items: {
|
|
245
|
+
pageInfo: { hasNextPage: boolean; endCursor: string | null };
|
|
246
|
+
nodes: ProjectItem[];
|
|
247
|
+
};
|
|
248
|
+
};
|
|
249
|
+
}>(token, `
|
|
250
|
+
query($projectId: ID!, $cursor: String) {
|
|
251
|
+
node(id: $projectId) {
|
|
252
|
+
... on ProjectV2 {
|
|
253
|
+
items(first: 50, after: $cursor) {
|
|
254
|
+
pageInfo { hasNextPage endCursor }
|
|
255
|
+
nodes {
|
|
256
|
+
id
|
|
257
|
+
content {
|
|
258
|
+
... on Issue {
|
|
259
|
+
__typename number title state
|
|
260
|
+
repository { nameWithOwner }
|
|
261
|
+
labels(first: 10) { nodes { name } }
|
|
262
|
+
assignees(first: 3) { nodes { login } }
|
|
263
|
+
}
|
|
264
|
+
... on PullRequest {
|
|
265
|
+
__typename number title state
|
|
266
|
+
repository { nameWithOwner }
|
|
267
|
+
labels(first: 10) { nodes { name } }
|
|
268
|
+
assignees(first: 3) { nodes { login } }
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
fieldValues(first: 10) {
|
|
272
|
+
nodes {
|
|
273
|
+
... on ProjectV2ItemFieldSingleSelectValue {
|
|
274
|
+
field { ... on ProjectV2SingleSelectField { name } }
|
|
275
|
+
name
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
`, { projectId, cursor: cursor ?? null });
|
|
285
|
+
|
|
286
|
+
return {
|
|
287
|
+
items: data.node.items.nodes,
|
|
288
|
+
nextCursor: data.node.items.pageInfo.hasNextPage
|
|
289
|
+
? data.node.items.pageInfo.endCursor
|
|
290
|
+
: null,
|
|
291
|
+
};
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
/** Get the node ID for an issue (needed to add it to a project). */
|
|
295
|
+
export async function getIssueNodeId(
|
|
296
|
+
token: string,
|
|
297
|
+
repo: string,
|
|
298
|
+
number: number,
|
|
299
|
+
): Promise<string> {
|
|
300
|
+
const [owner, name] = repo.split('/');
|
|
301
|
+
const data = await ghGraphQL<{
|
|
302
|
+
repository: { issue: { id: string } };
|
|
303
|
+
}>(token, `
|
|
304
|
+
query($owner: String!, $name: String!, $number: Int!) {
|
|
305
|
+
repository(owner: $owner, name: $name) {
|
|
306
|
+
issue(number: $number) { id }
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
`, { owner, name, number });
|
|
310
|
+
|
|
311
|
+
return data.repository.issue.id;
|
|
312
|
+
}
|