@laurentenhoor/devclaw 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/LICENSE +21 -0
- package/README.md +406 -0
- package/dist/index.d.ts +88 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +107 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/audit.d.ts +2 -0
- package/dist/lib/audit.d.ts.map +1 -0
- package/dist/lib/audit.js +42 -0
- package/dist/lib/audit.js.map +1 -0
- package/dist/lib/binding-manager.d.ts +35 -0
- package/dist/lib/binding-manager.d.ts.map +1 -0
- package/dist/lib/binding-manager.js +88 -0
- package/dist/lib/binding-manager.js.map +1 -0
- package/dist/lib/cli.d.ts +12 -0
- package/dist/lib/cli.d.ts.map +1 -0
- package/dist/lib/cli.js +69 -0
- package/dist/lib/cli.js.map +1 -0
- package/dist/lib/dispatch.d.ts +58 -0
- package/dist/lib/dispatch.d.ts.map +1 -0
- package/dist/lib/dispatch.js +163 -0
- package/dist/lib/dispatch.js.map +1 -0
- package/dist/lib/model-selector.d.ts +21 -0
- package/dist/lib/model-selector.d.ts.map +1 -0
- package/dist/lib/model-selector.js +74 -0
- package/dist/lib/model-selector.js.map +1 -0
- package/dist/lib/notify.d.ts +54 -0
- package/dist/lib/notify.d.ts.map +1 -0
- package/dist/lib/notify.js +143 -0
- package/dist/lib/notify.js.map +1 -0
- package/dist/lib/onboarding.d.ts +5 -0
- package/dist/lib/onboarding.d.ts.map +1 -0
- package/dist/lib/onboarding.js +124 -0
- package/dist/lib/onboarding.js.map +1 -0
- package/dist/lib/projects.d.ts +64 -0
- package/dist/lib/projects.d.ts.map +1 -0
- package/dist/lib/projects.js +127 -0
- package/dist/lib/projects.js.map +1 -0
- package/dist/lib/providers/github.d.ts +23 -0
- package/dist/lib/providers/github.d.ts.map +1 -0
- package/dist/lib/providers/github.js +130 -0
- package/dist/lib/providers/github.js.map +1 -0
- package/dist/lib/providers/gitlab.d.ts +23 -0
- package/dist/lib/providers/gitlab.d.ts.map +1 -0
- package/dist/lib/providers/gitlab.js +133 -0
- package/dist/lib/providers/gitlab.js.map +1 -0
- package/dist/lib/providers/index.d.ts +12 -0
- package/dist/lib/providers/index.d.ts.map +1 -0
- package/dist/lib/providers/index.js +25 -0
- package/dist/lib/providers/index.js.map +1 -0
- package/dist/lib/providers/provider.d.ts +35 -0
- package/dist/lib/providers/provider.d.ts.map +1 -0
- package/dist/lib/providers/provider.js +13 -0
- package/dist/lib/providers/provider.js.map +1 -0
- package/dist/lib/services/health.d.ts +38 -0
- package/dist/lib/services/health.d.ts.map +1 -0
- package/dist/lib/services/health.js +100 -0
- package/dist/lib/services/health.js.map +1 -0
- package/dist/lib/services/heartbeat.d.ts +38 -0
- package/dist/lib/services/heartbeat.d.ts.map +1 -0
- package/dist/lib/services/heartbeat.js +199 -0
- package/dist/lib/services/heartbeat.js.map +1 -0
- package/dist/lib/services/pipeline.d.ts +36 -0
- package/dist/lib/services/pipeline.d.ts.map +1 -0
- package/dist/lib/services/pipeline.js +90 -0
- package/dist/lib/services/pipeline.js.map +1 -0
- package/dist/lib/services/queue.d.ts +14 -0
- package/dist/lib/services/queue.d.ts.map +1 -0
- package/dist/lib/services/queue.js +31 -0
- package/dist/lib/services/queue.js.map +1 -0
- package/dist/lib/services/tick.d.ts +62 -0
- package/dist/lib/services/tick.d.ts.map +1 -0
- package/dist/lib/services/tick.js +160 -0
- package/dist/lib/services/tick.js.map +1 -0
- package/dist/lib/setup/agent.d.ts +14 -0
- package/dist/lib/setup/agent.d.ts.map +1 -0
- package/dist/lib/setup/agent.js +72 -0
- package/dist/lib/setup/agent.js.map +1 -0
- package/dist/lib/setup/config.d.ts +22 -0
- package/dist/lib/setup/config.d.ts.map +1 -0
- package/dist/lib/setup/config.js +67 -0
- package/dist/lib/setup/config.js.map +1 -0
- package/dist/lib/setup/index.d.ts +53 -0
- package/dist/lib/setup/index.d.ts.map +1 -0
- package/dist/lib/setup/index.js +68 -0
- package/dist/lib/setup/index.js.map +1 -0
- package/dist/lib/setup/workspace.d.ts +6 -0
- package/dist/lib/setup/workspace.d.ts.map +1 -0
- package/dist/lib/setup/workspace.js +69 -0
- package/dist/lib/setup/workspace.js.map +1 -0
- package/dist/lib/templates.d.ts +9 -0
- package/dist/lib/templates.d.ts.map +1 -0
- package/dist/lib/templates.js +163 -0
- package/dist/lib/templates.js.map +1 -0
- package/dist/lib/tiers.d.ts +55 -0
- package/dist/lib/tiers.d.ts.map +1 -0
- package/dist/lib/tiers.js +74 -0
- package/dist/lib/tiers.js.map +1 -0
- package/dist/lib/tool-helpers.d.ts +44 -0
- package/dist/lib/tool-helpers.d.ts.map +1 -0
- package/dist/lib/tool-helpers.js +65 -0
- package/dist/lib/tool-helpers.js.map +1 -0
- package/dist/lib/tools/health.d.ts +28 -0
- package/dist/lib/tools/health.d.ts.map +1 -0
- package/dist/lib/tools/health.js +61 -0
- package/dist/lib/tools/health.js.map +1 -0
- package/dist/lib/tools/onboard.d.ts +24 -0
- package/dist/lib/tools/onboard.d.ts.map +1 -0
- package/dist/lib/tools/onboard.js +27 -0
- package/dist/lib/tools/onboard.js.map +1 -0
- package/dist/lib/tools/project-register.d.ts +51 -0
- package/dist/lib/tools/project-register.d.ts.map +1 -0
- package/dist/lib/tools/project-register.js +172 -0
- package/dist/lib/tools/project-register.js.map +1 -0
- package/dist/lib/tools/queue-status.test.d.ts +2 -0
- package/dist/lib/tools/queue-status.test.d.ts.map +1 -0
- package/dist/lib/tools/queue-status.test.js +48 -0
- package/dist/lib/tools/queue-status.test.js.map +1 -0
- package/dist/lib/tools/setup.d.ts +76 -0
- package/dist/lib/tools/setup.d.ts.map +1 -0
- package/dist/lib/tools/setup.js +102 -0
- package/dist/lib/tools/setup.js.map +1 -0
- package/dist/lib/tools/status.d.ts +24 -0
- package/dist/lib/tools/status.d.ts.map +1 -0
- package/dist/lib/tools/status.js +53 -0
- package/dist/lib/tools/status.js.map +1 -0
- package/dist/lib/tools/task-comment.d.ts +40 -0
- package/dist/lib/tools/task-comment.d.ts.map +1 -0
- package/dist/lib/tools/task-comment.js +84 -0
- package/dist/lib/tools/task-comment.js.map +1 -0
- package/dist/lib/tools/task-create.d.ts +54 -0
- package/dist/lib/tools/task-create.d.ts.map +1 -0
- package/dist/lib/tools/task-create.js +77 -0
- package/dist/lib/tools/task-create.js.map +1 -0
- package/dist/lib/tools/task-update.d.ts +40 -0
- package/dist/lib/tools/task-update.d.ts.map +1 -0
- package/dist/lib/tools/task-update.js +79 -0
- package/dist/lib/tools/task-update.js.map +1 -0
- package/dist/lib/tools/task-update.test.d.ts +7 -0
- package/dist/lib/tools/task-update.test.d.ts.map +1 -0
- package/dist/lib/tools/task-update.test.js +55 -0
- package/dist/lib/tools/task-update.test.js.map +1 -0
- package/dist/lib/tools/work-finish.d.ts +43 -0
- package/dist/lib/tools/work-finish.d.ts.map +1 -0
- package/dist/lib/tools/work-finish.js +77 -0
- package/dist/lib/tools/work-finish.js.map +1 -0
- package/dist/lib/tools/work-start.d.ts +39 -0
- package/dist/lib/tools/work-start.d.ts.map +1 -0
- package/dist/lib/tools/work-start.js +129 -0
- package/dist/lib/tools/work-start.js.map +1 -0
- package/dist/lib/types.d.ts +17 -0
- package/dist/lib/types.d.ts.map +1 -0
- package/dist/lib/types.js +8 -0
- package/dist/lib/types.js.map +1 -0
- package/docs/ARCHITECTURE.md +662 -0
- package/docs/CONFIGURATION.md +336 -0
- package/docs/MANAGEMENT.md +120 -0
- package/docs/ONBOARDING.md +251 -0
- package/docs/QA_WORKFLOW.md +120 -0
- package/docs/ROADMAP.md +96 -0
- package/docs/TESTING.md +339 -0
- package/docs/TOOLS.md +361 -0
- package/package.json +55 -0
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import type { TickAction } from "./services/tick.js";
|
|
2
|
+
/** Per-event-type toggle. All default to true — set to false to suppress. */
|
|
3
|
+
export type NotificationConfig = Partial<Record<NotifyEvent["type"], boolean>>;
|
|
4
|
+
export type NotifyEvent = {
|
|
5
|
+
type: "workerStart";
|
|
6
|
+
project: string;
|
|
7
|
+
groupId: string;
|
|
8
|
+
issueId: number;
|
|
9
|
+
issueTitle: string;
|
|
10
|
+
issueUrl: string;
|
|
11
|
+
role: "dev" | "qa";
|
|
12
|
+
level: string;
|
|
13
|
+
sessionAction: "spawn" | "send";
|
|
14
|
+
} | {
|
|
15
|
+
type: "workerComplete";
|
|
16
|
+
project: string;
|
|
17
|
+
groupId: string;
|
|
18
|
+
issueId: number;
|
|
19
|
+
issueUrl: string;
|
|
20
|
+
role: "dev" | "qa";
|
|
21
|
+
result: "done" | "pass" | "fail" | "refine" | "blocked";
|
|
22
|
+
summary?: string;
|
|
23
|
+
nextState?: string;
|
|
24
|
+
};
|
|
25
|
+
/**
|
|
26
|
+
* Send a notification for a worker lifecycle event.
|
|
27
|
+
*
|
|
28
|
+
* Returns true if notification was sent, false on error.
|
|
29
|
+
*/
|
|
30
|
+
export declare function notify(event: NotifyEvent, opts: {
|
|
31
|
+
workspaceDir: string;
|
|
32
|
+
config?: NotificationConfig;
|
|
33
|
+
/** Target for project-scoped notifications (groupId) */
|
|
34
|
+
groupId?: string;
|
|
35
|
+
/** Channel type for routing (e.g. "telegram", "whatsapp", "discord", "slack") */
|
|
36
|
+
channel?: string;
|
|
37
|
+
}): Promise<boolean>;
|
|
38
|
+
/**
|
|
39
|
+
* Send workerStart notifications for each tick pickup.
|
|
40
|
+
*
|
|
41
|
+
* Called after projectTick() returns pickups — callers pass the array
|
|
42
|
+
* so each dispatched task gets a visible start notification in the project group.
|
|
43
|
+
*/
|
|
44
|
+
export declare function notifyTickPickups(pickups: TickAction[], opts: {
|
|
45
|
+
workspaceDir: string;
|
|
46
|
+
config?: NotificationConfig;
|
|
47
|
+
channel?: string;
|
|
48
|
+
}): Promise<void>;
|
|
49
|
+
/**
|
|
50
|
+
* Extract notification config from plugin config.
|
|
51
|
+
* All event types default to enabled (true).
|
|
52
|
+
*/
|
|
53
|
+
export declare function getNotificationConfig(pluginConfig?: Record<string, unknown>): NotificationConfig;
|
|
54
|
+
//# sourceMappingURL=notify.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"notify.d.ts","sourceRoot":"","sources":["../../lib/notify.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAIrD,6EAA6E;AAC7E,MAAM,MAAM,kBAAkB,GAAG,OAAO,CAAC,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC;AAE/E,MAAM,MAAM,WAAW,GACnB;IACE,IAAI,EAAE,aAAa,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,KAAK,GAAG,IAAI,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,aAAa,EAAE,OAAO,GAAG,MAAM,CAAC;CACjC,GACD;IACE,IAAI,EAAE,gBAAgB,CAAC;IACvB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,KAAK,GAAG,IAAI,CAAC;IACnB,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,GAAG,QAAQ,GAAG,SAAS,CAAC;IACxD,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAmFN;;;;GAIG;AACH,wBAAsB,MAAM,CAC1B,KAAK,EAAE,WAAW,EAClB,IAAI,EAAE;IACJ,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,kBAAkB,CAAC;IAC5B,wDAAwD;IACxD,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,iFAAiF;IACjF,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,GACA,OAAO,CAAC,OAAO,CAAC,CAuBlB;AAED;;;;;GAKG;AACH,wBAAsB,iBAAiB,CACrC,OAAO,EAAE,UAAU,EAAE,EACrB,IAAI,EAAE;IACJ,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,kBAAkB,CAAC;IAC5B,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,GACA,OAAO,CAAC,IAAI,CAAC,CAsBf;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CACnC,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GACrC,kBAAkB,CAEpB"}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* notify.ts — Programmatic alerting for worker lifecycle events.
|
|
3
|
+
*
|
|
4
|
+
* Sends notifications to project groups for visibility into the DevClaw pipeline.
|
|
5
|
+
*
|
|
6
|
+
* Event types:
|
|
7
|
+
* - workerStart: Worker spawned/resumed for a task (→ project group)
|
|
8
|
+
* - workerComplete: Worker completed task (→ project group)
|
|
9
|
+
*/
|
|
10
|
+
import { execFile } from "node:child_process";
|
|
11
|
+
import { promisify } from "node:util";
|
|
12
|
+
import { log as auditLog } from "./audit.js";
|
|
13
|
+
const execFileAsync = promisify(execFile);
|
|
14
|
+
/**
|
|
15
|
+
* Build a human-readable message for a notification event.
|
|
16
|
+
*/
|
|
17
|
+
function buildMessage(event) {
|
|
18
|
+
switch (event.type) {
|
|
19
|
+
case "workerStart": {
|
|
20
|
+
const action = event.sessionAction === "spawn" ? "🚀 Started" : "▶️ Resumed";
|
|
21
|
+
return `${action} ${event.role.toUpperCase()} (${event.level}) on #${event.issueId}: ${event.issueTitle}\n🔗 ${event.issueUrl}`;
|
|
22
|
+
}
|
|
23
|
+
case "workerComplete": {
|
|
24
|
+
const icons = {
|
|
25
|
+
done: "✅",
|
|
26
|
+
pass: "🎉",
|
|
27
|
+
fail: "❌",
|
|
28
|
+
refine: "🤔",
|
|
29
|
+
blocked: "🚫",
|
|
30
|
+
};
|
|
31
|
+
const icon = icons[event.result] ?? "📋";
|
|
32
|
+
const resultText = {
|
|
33
|
+
done: "completed",
|
|
34
|
+
pass: "PASSED",
|
|
35
|
+
fail: "FAILED",
|
|
36
|
+
refine: "needs refinement",
|
|
37
|
+
blocked: "BLOCKED",
|
|
38
|
+
};
|
|
39
|
+
const text = resultText[event.result] ?? event.result;
|
|
40
|
+
let msg = `${icon} ${event.role.toUpperCase()} ${text} #${event.issueId}`;
|
|
41
|
+
if (event.summary) {
|
|
42
|
+
msg += ` — ${event.summary}`;
|
|
43
|
+
}
|
|
44
|
+
if (event.nextState) {
|
|
45
|
+
msg += ` → ${event.nextState}`;
|
|
46
|
+
}
|
|
47
|
+
msg += `\n🔗 ${event.issueUrl}`;
|
|
48
|
+
return msg;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Send a notification message via the native OpenClaw messaging CLI.
|
|
54
|
+
*
|
|
55
|
+
* Uses `openclaw message send` which handles target resolution, chunking,
|
|
56
|
+
* retries, and error reporting for all supported channels.
|
|
57
|
+
* Fails silently (logs error but doesn't throw) to avoid breaking the main flow.
|
|
58
|
+
*/
|
|
59
|
+
async function sendMessage(target, message, channel, workspaceDir) {
|
|
60
|
+
try {
|
|
61
|
+
await execFileAsync("openclaw", [
|
|
62
|
+
"message",
|
|
63
|
+
"send",
|
|
64
|
+
"--channel",
|
|
65
|
+
channel,
|
|
66
|
+
"--target",
|
|
67
|
+
target,
|
|
68
|
+
"--message",
|
|
69
|
+
message,
|
|
70
|
+
"--json",
|
|
71
|
+
], { timeout: 30_000 });
|
|
72
|
+
return true;
|
|
73
|
+
}
|
|
74
|
+
catch (err) {
|
|
75
|
+
// Log but don't throw — notifications shouldn't break the main flow
|
|
76
|
+
await auditLog(workspaceDir, "notify_error", {
|
|
77
|
+
target,
|
|
78
|
+
channel,
|
|
79
|
+
error: err.message,
|
|
80
|
+
});
|
|
81
|
+
return false;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Send a notification for a worker lifecycle event.
|
|
86
|
+
*
|
|
87
|
+
* Returns true if notification was sent, false on error.
|
|
88
|
+
*/
|
|
89
|
+
export async function notify(event, opts) {
|
|
90
|
+
if (opts.config?.[event.type] === false)
|
|
91
|
+
return true;
|
|
92
|
+
const channel = opts.channel ?? "telegram";
|
|
93
|
+
const message = buildMessage(event);
|
|
94
|
+
const target = opts.groupId ?? event.groupId;
|
|
95
|
+
if (!target) {
|
|
96
|
+
await auditLog(opts.workspaceDir, "notify_skip", {
|
|
97
|
+
eventType: event.type,
|
|
98
|
+
reason: "no target",
|
|
99
|
+
});
|
|
100
|
+
return true; // Not an error, just nothing to do
|
|
101
|
+
}
|
|
102
|
+
await auditLog(opts.workspaceDir, "notify", {
|
|
103
|
+
eventType: event.type,
|
|
104
|
+
target,
|
|
105
|
+
channel,
|
|
106
|
+
message,
|
|
107
|
+
});
|
|
108
|
+
return sendMessage(target, message, channel, opts.workspaceDir);
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Send workerStart notifications for each tick pickup.
|
|
112
|
+
*
|
|
113
|
+
* Called after projectTick() returns pickups — callers pass the array
|
|
114
|
+
* so each dispatched task gets a visible start notification in the project group.
|
|
115
|
+
*/
|
|
116
|
+
export async function notifyTickPickups(pickups, opts) {
|
|
117
|
+
for (const pickup of pickups) {
|
|
118
|
+
await notify({
|
|
119
|
+
type: "workerStart",
|
|
120
|
+
project: pickup.project,
|
|
121
|
+
groupId: pickup.groupId,
|
|
122
|
+
issueId: pickup.issueId,
|
|
123
|
+
issueTitle: pickup.issueTitle,
|
|
124
|
+
issueUrl: pickup.issueUrl,
|
|
125
|
+
role: pickup.role,
|
|
126
|
+
level: pickup.level,
|
|
127
|
+
sessionAction: pickup.sessionAction,
|
|
128
|
+
}, {
|
|
129
|
+
workspaceDir: opts.workspaceDir,
|
|
130
|
+
config: opts.config,
|
|
131
|
+
groupId: pickup.groupId,
|
|
132
|
+
channel: opts.channel,
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Extract notification config from plugin config.
|
|
138
|
+
* All event types default to enabled (true).
|
|
139
|
+
*/
|
|
140
|
+
export function getNotificationConfig(pluginConfig) {
|
|
141
|
+
return pluginConfig?.notifications ?? {};
|
|
142
|
+
}
|
|
143
|
+
//# sourceMappingURL=notify.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"notify.js","sourceRoot":"","sources":["../../lib/notify.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export declare function isPluginConfigured(pluginConfig?: Record<string, unknown>): boolean;
|
|
2
|
+
export declare function hasWorkspaceFiles(workspaceDir?: string): Promise<boolean>;
|
|
3
|
+
export declare function buildReconfigContext(pluginConfig?: Record<string, unknown>): string;
|
|
4
|
+
export declare function buildOnboardToolContext(): string;
|
|
5
|
+
//# sourceMappingURL=onboarding.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"onboarding.d.ts","sourceRoot":"","sources":["../../lib/onboarding.ts"],"names":[],"mappings":"AAaA,wBAAgB,kBAAkB,CAChC,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GACrC,OAAO,CAGT;AAED,wBAAsB,iBAAiB,CACrC,YAAY,CAAC,EAAE,MAAM,GACpB,OAAO,CAAC,OAAO,CAAC,CAWlB;AAkBD,wBAAgB,oBAAoB,CAClC,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GACrC,MAAM,CAgBR;AAED,wBAAgB,uBAAuB,IAAI,MAAM,CAkEhD"}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* onboarding.ts — Conversational onboarding context templates.
|
|
3
|
+
*
|
|
4
|
+
* Provides context templates for the onboard tool.
|
|
5
|
+
*/
|
|
6
|
+
import fs from "node:fs/promises";
|
|
7
|
+
import path from "node:path";
|
|
8
|
+
import { DEFAULT_MODELS } from "./tiers.js";
|
|
9
|
+
// ---------------------------------------------------------------------------
|
|
10
|
+
// Detection
|
|
11
|
+
// ---------------------------------------------------------------------------
|
|
12
|
+
export function isPluginConfigured(pluginConfig) {
|
|
13
|
+
const models = pluginConfig?.models;
|
|
14
|
+
return !!models && Object.keys(models).length > 0;
|
|
15
|
+
}
|
|
16
|
+
export async function hasWorkspaceFiles(workspaceDir) {
|
|
17
|
+
if (!workspaceDir)
|
|
18
|
+
return false;
|
|
19
|
+
try {
|
|
20
|
+
const content = await fs.readFile(path.join(workspaceDir, "AGENTS.md"), "utf-8");
|
|
21
|
+
return content.includes("DevClaw") && content.includes("work_start");
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
return false;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
// ---------------------------------------------------------------------------
|
|
28
|
+
// Context templates
|
|
29
|
+
// ---------------------------------------------------------------------------
|
|
30
|
+
function buildModelTable(pluginConfig) {
|
|
31
|
+
const cfg = pluginConfig?.models;
|
|
32
|
+
const lines = [];
|
|
33
|
+
for (const [role, levels] of Object.entries(DEFAULT_MODELS)) {
|
|
34
|
+
for (const [level, defaultModel] of Object.entries(levels)) {
|
|
35
|
+
const model = cfg?.[role]?.[level] || defaultModel;
|
|
36
|
+
lines.push(` - **${role} ${level}**: ${model} (default: ${defaultModel})`);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return lines.join("\n");
|
|
40
|
+
}
|
|
41
|
+
export function buildReconfigContext(pluginConfig) {
|
|
42
|
+
const modelTable = buildModelTable(pluginConfig);
|
|
43
|
+
return `# DevClaw Reconfiguration
|
|
44
|
+
|
|
45
|
+
The user wants to reconfigure DevClaw. Current model configuration:
|
|
46
|
+
|
|
47
|
+
${modelTable}
|
|
48
|
+
|
|
49
|
+
## What can be changed
|
|
50
|
+
1. **Model levels** — call \`setup\` with a \`models\` object containing only the levels to change
|
|
51
|
+
2. **Workspace files** — \`setup\` re-writes AGENTS.md, HEARTBEAT.md (backs up existing files)
|
|
52
|
+
3. **Register new projects** — use \`project_register\`
|
|
53
|
+
|
|
54
|
+
Ask what they want to change, then call the appropriate tool.
|
|
55
|
+
\`setup\` is safe to re-run — it backs up existing files before overwriting.
|
|
56
|
+
`;
|
|
57
|
+
}
|
|
58
|
+
export function buildOnboardToolContext() {
|
|
59
|
+
// Build the model table dynamically from DEFAULT_MODELS
|
|
60
|
+
const rows = [];
|
|
61
|
+
const purposes = {
|
|
62
|
+
junior: "Typos, single-file fixes",
|
|
63
|
+
medior: "Features, bug fixes",
|
|
64
|
+
senior: "Architecture, refactoring",
|
|
65
|
+
reviewer: "Code review",
|
|
66
|
+
tester: "Testing",
|
|
67
|
+
};
|
|
68
|
+
for (const [role, levels] of Object.entries(DEFAULT_MODELS)) {
|
|
69
|
+
for (const [level, model] of Object.entries(levels)) {
|
|
70
|
+
rows.push(`| ${role} | ${level} | ${model} | ${purposes[level] ?? ""} |`);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
const modelTable = rows.join("\n");
|
|
74
|
+
return `# DevClaw Onboarding
|
|
75
|
+
|
|
76
|
+
## What is DevClaw?
|
|
77
|
+
DevClaw turns each Telegram group into an autonomous development team:
|
|
78
|
+
- An **orchestrator** that manages backlogs and delegates work
|
|
79
|
+
- **DEV workers** (junior/medior/senior levels) that write code in isolated sessions
|
|
80
|
+
- **QA workers** that review code and run tests
|
|
81
|
+
- Atomic tools for label transitions, session dispatch, state management, and audit logging
|
|
82
|
+
|
|
83
|
+
## Setup Steps
|
|
84
|
+
|
|
85
|
+
**Step 1: Agent Selection**
|
|
86
|
+
Ask: "Do you want to configure DevClaw for the current agent, or create a new dedicated agent?"
|
|
87
|
+
- Current agent → no \`newAgentName\` needed
|
|
88
|
+
- New agent → ask for:
|
|
89
|
+
1. Agent name
|
|
90
|
+
2. **Channel binding**: "Which channel should this agent listen to? (telegram/whatsapp/none)"
|
|
91
|
+
- If telegram/whatsapp selected:
|
|
92
|
+
a) Check openclaw.json for existing channel bindings
|
|
93
|
+
b) If channel not configured/enabled → warn and recommend skipping binding for now
|
|
94
|
+
c) If channel-wide binding exists on another agent → ask: "Migrate binding from {agentName}?"
|
|
95
|
+
d) Collect migration decision
|
|
96
|
+
- If none selected, user can add bindings manually later via openclaw.json
|
|
97
|
+
|
|
98
|
+
**Step 2: Model Configuration**
|
|
99
|
+
Show the default level-to-model mapping and ask if they want to customize:
|
|
100
|
+
|
|
101
|
+
| Role | Level | Default Model | Purpose |
|
|
102
|
+
|------|-------|---------------|---------|
|
|
103
|
+
${modelTable}
|
|
104
|
+
|
|
105
|
+
If the defaults are fine, proceed. If customizing, ask which levels to change.
|
|
106
|
+
|
|
107
|
+
**Step 3: Run Setup**
|
|
108
|
+
Call \`setup\` with the collected answers:
|
|
109
|
+
- Current agent: \`setup({})\` or \`setup({ models: { dev: { ... }, qa: { ... } } })\`
|
|
110
|
+
- New agent: \`setup({ newAgentName: "<name>", channelBinding: "telegram"|"whatsapp"|null, migrateFrom: "<agentId>"|null, models: { ... } })\`
|
|
111
|
+
- \`migrateFrom\`: Include if user wants to migrate an existing channel-wide binding
|
|
112
|
+
|
|
113
|
+
**Step 4: Optional Project Registration**
|
|
114
|
+
After setup, ask: "Would you like to register a project now?"
|
|
115
|
+
If yes, collect: project name, repo path, Telegram group ID, group name, base branch.
|
|
116
|
+
Then call \`project_register\`.
|
|
117
|
+
|
|
118
|
+
## Guidelines
|
|
119
|
+
- Be conversational and friendly. Ask one question at a time.
|
|
120
|
+
- Show defaults so the user can accept them quickly.
|
|
121
|
+
- After setup, summarize what was configured (including channel binding if applicable).
|
|
122
|
+
`;
|
|
123
|
+
}
|
|
124
|
+
//# sourceMappingURL=onboarding.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"onboarding.js","sourceRoot":"","sources":["../../lib/onboarding.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
export type WorkerState = {
|
|
2
|
+
active: boolean;
|
|
3
|
+
issueId: string | null;
|
|
4
|
+
startTime: string | null;
|
|
5
|
+
level: string | null;
|
|
6
|
+
sessions: Record<string, string | null>;
|
|
7
|
+
};
|
|
8
|
+
export type Project = {
|
|
9
|
+
name: string;
|
|
10
|
+
repo: string;
|
|
11
|
+
groupName: string;
|
|
12
|
+
deployUrl: string;
|
|
13
|
+
baseBranch: string;
|
|
14
|
+
deployBranch: string;
|
|
15
|
+
/** Messaging channel for this project's group (e.g. "telegram", "whatsapp", "discord", "slack"). Stored at registration time. */
|
|
16
|
+
channel?: string;
|
|
17
|
+
/** Project-level role execution: parallel (DEV+QA can run simultaneously) or sequential (only one role at a time). Default: parallel */
|
|
18
|
+
roleExecution?: "parallel" | "sequential";
|
|
19
|
+
maxDevWorkers?: number;
|
|
20
|
+
maxQaWorkers?: number;
|
|
21
|
+
dev: WorkerState;
|
|
22
|
+
qa: WorkerState;
|
|
23
|
+
};
|
|
24
|
+
export type ProjectsData = {
|
|
25
|
+
projects: Record<string, Project>;
|
|
26
|
+
};
|
|
27
|
+
/**
|
|
28
|
+
* Create a blank WorkerState with null sessions for given level names.
|
|
29
|
+
*/
|
|
30
|
+
export declare function emptyWorkerState(levels: string[]): WorkerState;
|
|
31
|
+
/**
|
|
32
|
+
* Get session key for a specific level from a worker's sessions map.
|
|
33
|
+
*/
|
|
34
|
+
export declare function getSessionForLevel(worker: WorkerState, level: string): string | null;
|
|
35
|
+
export declare function readProjects(workspaceDir: string): Promise<ProjectsData>;
|
|
36
|
+
export declare function writeProjects(workspaceDir: string, data: ProjectsData): Promise<void>;
|
|
37
|
+
export declare function getProject(data: ProjectsData, groupId: string): Project | undefined;
|
|
38
|
+
export declare function getWorker(project: Project, role: "dev" | "qa"): WorkerState;
|
|
39
|
+
/**
|
|
40
|
+
* Update worker state for a project. Only provided fields are updated.
|
|
41
|
+
* Sessions are merged (not replaced) when both existing and new sessions are present.
|
|
42
|
+
*/
|
|
43
|
+
export declare function updateWorker(workspaceDir: string, groupId: string, role: "dev" | "qa", updates: Partial<WorkerState>): Promise<ProjectsData>;
|
|
44
|
+
/**
|
|
45
|
+
* Mark a worker as active with a new task.
|
|
46
|
+
* Stores session key in sessions[level] when a new session is spawned.
|
|
47
|
+
*/
|
|
48
|
+
export declare function activateWorker(workspaceDir: string, groupId: string, role: "dev" | "qa", params: {
|
|
49
|
+
issueId: string;
|
|
50
|
+
level: string;
|
|
51
|
+
sessionKey?: string;
|
|
52
|
+
startTime?: string;
|
|
53
|
+
}): Promise<ProjectsData>;
|
|
54
|
+
/**
|
|
55
|
+
* Mark a worker as inactive after task completion.
|
|
56
|
+
* Preserves sessions map and level for reuse via updateWorker's spread.
|
|
57
|
+
* Clears startTime to prevent stale timestamps on inactive workers.
|
|
58
|
+
*/
|
|
59
|
+
export declare function deactivateWorker(workspaceDir: string, groupId: string, role: "dev" | "qa"): Promise<ProjectsData>;
|
|
60
|
+
/**
|
|
61
|
+
* Resolve repo path from projects.json repo field (handles ~/ expansion).
|
|
62
|
+
*/
|
|
63
|
+
export declare function resolveRepoPath(repoField: string): string;
|
|
64
|
+
//# sourceMappingURL=projects.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"projects.d.ts","sourceRoot":"","sources":["../../lib/projects.ts"],"names":[],"mappings":"AAOA,MAAM,MAAM,WAAW,GAAG;IACxB,MAAM,EAAE,OAAO,CAAC;IAChB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC,CAAC;CACzC,CAAC;AAEF,MAAM,MAAM,OAAO,GAAG;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,iIAAiI;IACjI,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,wIAAwI;IACxI,aAAa,CAAC,EAAE,UAAU,GAAG,YAAY,CAAC;IAC1C,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,GAAG,EAAE,WAAW,CAAC;IACjB,EAAE,EAAE,WAAW,CAAC;CACjB,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG;IACzB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACnC,CAAC;AAYF;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,WAAW,CAY9D;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,WAAW,EACnB,KAAK,EAAE,MAAM,GACZ,MAAM,GAAG,IAAI,CAEf;AAMD,wBAAsB,YAAY,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,CAiB9E;AAED,wBAAsB,aAAa,CACjC,YAAY,EAAE,MAAM,EACpB,IAAI,EAAE,YAAY,GACjB,OAAO,CAAC,IAAI,CAAC,CAKf;AAED,wBAAgB,UAAU,CACxB,IAAI,EAAE,YAAY,EAClB,OAAO,EAAE,MAAM,GACd,OAAO,GAAG,SAAS,CAErB;AAED,wBAAgB,SAAS,CACvB,OAAO,EAAE,OAAO,EAChB,IAAI,EAAE,KAAK,GAAG,IAAI,GACjB,WAAW,CAEb;AAED;;;GAGG;AACH,wBAAsB,YAAY,CAChC,YAAY,EAAE,MAAM,EACpB,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,KAAK,GAAG,IAAI,EAClB,OAAO,EAAE,OAAO,CAAC,WAAW,CAAC,GAC5B,OAAO,CAAC,YAAY,CAAC,CAiBvB;AAED;;;GAGG;AACH,wBAAsB,cAAc,CAClC,YAAY,EAAE,MAAM,EACpB,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,KAAK,GAAG,IAAI,EAClB,MAAM,EAAE;IACN,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,GACA,OAAO,CAAC,YAAY,CAAC,CAavB;AAED;;;;GAIG;AACH,wBAAsB,gBAAgB,CACpC,YAAY,EAAE,MAAM,EACpB,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,KAAK,GAAG,IAAI,GACjB,OAAO,CAAC,YAAY,CAAC,CAMvB;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAKzD"}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Atomic projects.json read/write operations.
|
|
3
|
+
* All state mutations go through this module to prevent corruption.
|
|
4
|
+
*/
|
|
5
|
+
import fs from "node:fs/promises";
|
|
6
|
+
import path from "node:path";
|
|
7
|
+
import { homedir } from "node:os";
|
|
8
|
+
function parseWorkerState(worker) {
|
|
9
|
+
return {
|
|
10
|
+
active: worker.active,
|
|
11
|
+
issueId: worker.issueId,
|
|
12
|
+
startTime: worker.startTime,
|
|
13
|
+
level: (worker.level ?? worker.tier ?? null),
|
|
14
|
+
sessions: worker.sessions ?? {},
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Create a blank WorkerState with null sessions for given level names.
|
|
19
|
+
*/
|
|
20
|
+
export function emptyWorkerState(levels) {
|
|
21
|
+
const sessions = {};
|
|
22
|
+
for (const l of levels) {
|
|
23
|
+
sessions[l] = null;
|
|
24
|
+
}
|
|
25
|
+
return {
|
|
26
|
+
active: false,
|
|
27
|
+
issueId: null,
|
|
28
|
+
startTime: null,
|
|
29
|
+
level: null,
|
|
30
|
+
sessions,
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Get session key for a specific level from a worker's sessions map.
|
|
35
|
+
*/
|
|
36
|
+
export function getSessionForLevel(worker, level) {
|
|
37
|
+
return worker.sessions[level] ?? null;
|
|
38
|
+
}
|
|
39
|
+
function projectsPath(workspaceDir) {
|
|
40
|
+
return path.join(workspaceDir, "projects", "projects.json");
|
|
41
|
+
}
|
|
42
|
+
export async function readProjects(workspaceDir) {
|
|
43
|
+
const raw = await fs.readFile(projectsPath(workspaceDir), "utf-8");
|
|
44
|
+
const data = JSON.parse(raw);
|
|
45
|
+
for (const project of Object.values(data.projects)) {
|
|
46
|
+
project.dev = project.dev
|
|
47
|
+
? parseWorkerState(project.dev)
|
|
48
|
+
: emptyWorkerState([]);
|
|
49
|
+
project.qa = project.qa
|
|
50
|
+
? parseWorkerState(project.qa)
|
|
51
|
+
: emptyWorkerState([]);
|
|
52
|
+
if (!project.channel) {
|
|
53
|
+
project.channel = "telegram";
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return data;
|
|
57
|
+
}
|
|
58
|
+
export async function writeProjects(workspaceDir, data) {
|
|
59
|
+
const filePath = projectsPath(workspaceDir);
|
|
60
|
+
const tmpPath = filePath + ".tmp";
|
|
61
|
+
await fs.writeFile(tmpPath, JSON.stringify(data, null, 2) + "\n", "utf-8");
|
|
62
|
+
await fs.rename(tmpPath, filePath);
|
|
63
|
+
}
|
|
64
|
+
export function getProject(data, groupId) {
|
|
65
|
+
return data.projects[groupId];
|
|
66
|
+
}
|
|
67
|
+
export function getWorker(project, role) {
|
|
68
|
+
return project[role];
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Update worker state for a project. Only provided fields are updated.
|
|
72
|
+
* Sessions are merged (not replaced) when both existing and new sessions are present.
|
|
73
|
+
*/
|
|
74
|
+
export async function updateWorker(workspaceDir, groupId, role, updates) {
|
|
75
|
+
const data = await readProjects(workspaceDir);
|
|
76
|
+
const project = data.projects[groupId];
|
|
77
|
+
if (!project) {
|
|
78
|
+
throw new Error(`Project not found for groupId: ${groupId}`);
|
|
79
|
+
}
|
|
80
|
+
const worker = project[role];
|
|
81
|
+
if (updates.sessions && worker.sessions) {
|
|
82
|
+
updates.sessions = { ...worker.sessions, ...updates.sessions };
|
|
83
|
+
}
|
|
84
|
+
project[role] = { ...worker, ...updates };
|
|
85
|
+
await writeProjects(workspaceDir, data);
|
|
86
|
+
return data;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Mark a worker as active with a new task.
|
|
90
|
+
* Stores session key in sessions[level] when a new session is spawned.
|
|
91
|
+
*/
|
|
92
|
+
export async function activateWorker(workspaceDir, groupId, role, params) {
|
|
93
|
+
const updates = {
|
|
94
|
+
active: true,
|
|
95
|
+
issueId: params.issueId,
|
|
96
|
+
level: params.level,
|
|
97
|
+
};
|
|
98
|
+
if (params.sessionKey !== undefined) {
|
|
99
|
+
updates.sessions = { [params.level]: params.sessionKey };
|
|
100
|
+
}
|
|
101
|
+
if (params.startTime !== undefined) {
|
|
102
|
+
updates.startTime = params.startTime;
|
|
103
|
+
}
|
|
104
|
+
return updateWorker(workspaceDir, groupId, role, updates);
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Mark a worker as inactive after task completion.
|
|
108
|
+
* Preserves sessions map and level for reuse via updateWorker's spread.
|
|
109
|
+
* Clears startTime to prevent stale timestamps on inactive workers.
|
|
110
|
+
*/
|
|
111
|
+
export async function deactivateWorker(workspaceDir, groupId, role) {
|
|
112
|
+
return updateWorker(workspaceDir, groupId, role, {
|
|
113
|
+
active: false,
|
|
114
|
+
issueId: null,
|
|
115
|
+
startTime: null,
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Resolve repo path from projects.json repo field (handles ~/ expansion).
|
|
120
|
+
*/
|
|
121
|
+
export function resolveRepoPath(repoField) {
|
|
122
|
+
if (repoField.startsWith("~/")) {
|
|
123
|
+
return repoField.replace("~", homedir());
|
|
124
|
+
}
|
|
125
|
+
return repoField;
|
|
126
|
+
}
|
|
127
|
+
//# sourceMappingURL=projects.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"projects.js","sourceRoot":"","sources":["../../lib/projects.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { type IssueProvider, type Issue, type StateLabel } from "./provider.js";
|
|
2
|
+
export declare class GitHubProvider implements IssueProvider {
|
|
3
|
+
private repoPath;
|
|
4
|
+
constructor(opts: {
|
|
5
|
+
repoPath: string;
|
|
6
|
+
});
|
|
7
|
+
private gh;
|
|
8
|
+
ensureLabel(name: string, color: string): Promise<void>;
|
|
9
|
+
ensureAllStateLabels(): Promise<void>;
|
|
10
|
+
createIssue(title: string, description: string, label: StateLabel, assignees?: string[]): Promise<Issue>;
|
|
11
|
+
listIssuesByLabel(label: StateLabel): Promise<Issue[]>;
|
|
12
|
+
getIssue(issueId: number): Promise<Issue>;
|
|
13
|
+
transitionLabel(issueId: number, from: StateLabel, to: StateLabel): Promise<void>;
|
|
14
|
+
closeIssue(issueId: number): Promise<void>;
|
|
15
|
+
reopenIssue(issueId: number): Promise<void>;
|
|
16
|
+
hasStateLabel(issue: Issue, expected: StateLabel): boolean;
|
|
17
|
+
getCurrentStateLabel(issue: Issue): StateLabel | null;
|
|
18
|
+
hasMergedMR(issueId: number): Promise<boolean>;
|
|
19
|
+
getMergedMRUrl(issueId: number): Promise<string | null>;
|
|
20
|
+
addComment(issueId: number, body: string): Promise<void>;
|
|
21
|
+
healthCheck(): Promise<boolean>;
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=github.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"github.d.ts","sourceRoot":"","sources":["../../../lib/providers/github.ts"],"names":[],"mappings":"AAQA,OAAO,EACL,KAAK,aAAa,EAClB,KAAK,KAAK,EACV,KAAK,UAAU,EAGhB,MAAM,eAAe,CAAC;AAoBvB,qBAAa,cAAe,YAAW,aAAa;IAClD,OAAO,CAAC,QAAQ,CAAS;gBACb,IAAI,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAA;KAAE;YAExB,EAAE;IAKV,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAKvD,oBAAoB,IAAI,OAAO,CAAC,IAAI,CAAC;IAIrC,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,KAAK,EAAE,UAAU,EAAE,SAAS,CAAC,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,KAAK,CAAC;IAaxG,iBAAiB,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO,CAAC,KAAK,EAAE,CAAC;IAOtD,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC;IAKzC,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,EAAE,EAAE,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC;IASjF,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAC1C,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAEjD,aAAa,CAAC,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,UAAU,GAAG,OAAO;IAC1D,oBAAoB,CAAC,KAAK,EAAE,KAAK,GAAG,UAAU,GAAG,IAAI;IAI/C,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAS9C,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IASvD,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAOxD,WAAW,IAAI,OAAO,CAAC,OAAO,CAAC;CAGtC"}
|