@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,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* task_update — Change issue state programmatically.
|
|
3
|
+
*
|
|
4
|
+
* Use cases:
|
|
5
|
+
* - Orchestrator or worker needs to change state without full pickup/complete flow
|
|
6
|
+
* - Manual status adjustments (e.g., Planning → To Do after approval)
|
|
7
|
+
* - Failed auto-transitions that need correction
|
|
8
|
+
*/
|
|
9
|
+
import type { OpenClawPluginApi } from "openclaw/plugin-sdk";
|
|
10
|
+
import type { ToolContext } from "../types.js";
|
|
11
|
+
export declare function createTaskUpdateTool(api: OpenClawPluginApi): (ctx: ToolContext) => {
|
|
12
|
+
name: string;
|
|
13
|
+
label: string;
|
|
14
|
+
description: string;
|
|
15
|
+
parameters: {
|
|
16
|
+
type: string;
|
|
17
|
+
required: string[];
|
|
18
|
+
properties: {
|
|
19
|
+
projectGroupId: {
|
|
20
|
+
type: string;
|
|
21
|
+
description: string;
|
|
22
|
+
};
|
|
23
|
+
issueId: {
|
|
24
|
+
type: string;
|
|
25
|
+
description: string;
|
|
26
|
+
};
|
|
27
|
+
state: {
|
|
28
|
+
type: string;
|
|
29
|
+
enum: readonly ["Planning", "To Do", "Doing", "To Test", "Testing", "Done", "To Improve", "Refining"];
|
|
30
|
+
description: string;
|
|
31
|
+
};
|
|
32
|
+
reason: {
|
|
33
|
+
type: string;
|
|
34
|
+
description: string;
|
|
35
|
+
};
|
|
36
|
+
};
|
|
37
|
+
};
|
|
38
|
+
execute(_id: string, params: Record<string, unknown>): Promise<import("@mariozechner/pi-agent-core").AgentToolResult<unknown>>;
|
|
39
|
+
};
|
|
40
|
+
//# sourceMappingURL=task-update.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"task-update.d.ts","sourceRoot":"","sources":["../../../lib/tools/task-update.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAE7D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAK/C,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,iBAAiB,IACjD,KAAK,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAsCH,MAAM,UAAU,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;EAyC7D"}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { jsonResult } from "openclaw/plugin-sdk";
|
|
2
|
+
import { log as auditLog } from "../audit.js";
|
|
3
|
+
import { STATE_LABELS } from "../providers/provider.js";
|
|
4
|
+
import { requireWorkspaceDir, resolveProject, resolveProvider } from "../tool-helpers.js";
|
|
5
|
+
export function createTaskUpdateTool(api) {
|
|
6
|
+
return (ctx) => ({
|
|
7
|
+
name: "task_update",
|
|
8
|
+
label: "Task Update",
|
|
9
|
+
description: `Change issue state programmatically. Use this when you need to update an issue's status without going through the full pickup/complete flow.
|
|
10
|
+
|
|
11
|
+
Use cases:
|
|
12
|
+
- Orchestrator or worker needs to change state manually
|
|
13
|
+
- Manual status adjustments (e.g., Planning → To Do after approval)
|
|
14
|
+
- Failed auto-transitions that need correction
|
|
15
|
+
- Bulk state changes
|
|
16
|
+
|
|
17
|
+
Examples:
|
|
18
|
+
- Simple: { projectGroupId: "-123456789", issueId: 42, state: "To Do" }
|
|
19
|
+
- With reason: { projectGroupId: "-123456789", issueId: 42, state: "To Do", reason: "Approved for development" }`,
|
|
20
|
+
parameters: {
|
|
21
|
+
type: "object",
|
|
22
|
+
required: ["projectGroupId", "issueId", "state"],
|
|
23
|
+
properties: {
|
|
24
|
+
projectGroupId: {
|
|
25
|
+
type: "string",
|
|
26
|
+
description: "Telegram/WhatsApp group ID (key in projects.json)",
|
|
27
|
+
},
|
|
28
|
+
issueId: {
|
|
29
|
+
type: "number",
|
|
30
|
+
description: "Issue ID to update",
|
|
31
|
+
},
|
|
32
|
+
state: {
|
|
33
|
+
type: "string",
|
|
34
|
+
enum: STATE_LABELS,
|
|
35
|
+
description: `New state for the issue. One of: ${STATE_LABELS.join(", ")}`,
|
|
36
|
+
},
|
|
37
|
+
reason: {
|
|
38
|
+
type: "string",
|
|
39
|
+
description: "Optional audit log reason for the state change",
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
async execute(_id, params) {
|
|
44
|
+
const groupId = params.projectGroupId;
|
|
45
|
+
const issueId = params.issueId;
|
|
46
|
+
const newState = params.state;
|
|
47
|
+
const reason = params.reason ?? undefined;
|
|
48
|
+
const workspaceDir = requireWorkspaceDir(ctx);
|
|
49
|
+
const { project } = await resolveProject(workspaceDir, groupId);
|
|
50
|
+
const { provider, type: providerType } = resolveProvider(project);
|
|
51
|
+
const issue = await provider.getIssue(issueId);
|
|
52
|
+
const currentState = provider.getCurrentStateLabel(issue);
|
|
53
|
+
if (!currentState) {
|
|
54
|
+
throw new Error(`Issue #${issueId} has no recognized state label. Cannot perform transition.`);
|
|
55
|
+
}
|
|
56
|
+
if (currentState === newState) {
|
|
57
|
+
return jsonResult({
|
|
58
|
+
success: true, issueId, state: newState, changed: false,
|
|
59
|
+
message: `Issue #${issueId} is already in state "${newState}".`,
|
|
60
|
+
project: project.name, provider: providerType,
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
await provider.transitionLabel(issueId, currentState, newState);
|
|
64
|
+
await auditLog(workspaceDir, "task_update", {
|
|
65
|
+
project: project.name, groupId, issueId,
|
|
66
|
+
fromState: currentState, toState: newState,
|
|
67
|
+
reason: reason ?? null, provider: providerType,
|
|
68
|
+
});
|
|
69
|
+
return jsonResult({
|
|
70
|
+
success: true, issueId, issueTitle: issue.title,
|
|
71
|
+
state: newState, changed: true,
|
|
72
|
+
labelTransition: `${currentState} → ${newState}`,
|
|
73
|
+
project: project.name, provider: providerType,
|
|
74
|
+
announcement: `🔄 Updated #${issueId}: "${currentState}" → "${newState}"${reason ? ` (${reason})` : ""}`,
|
|
75
|
+
});
|
|
76
|
+
},
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
//# sourceMappingURL=task-update.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"task-update.js","sourceRoot":"","sources":["../../../lib/tools/task-update.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"task-update.test.d.ts","sourceRoot":"","sources":["../../../lib/tools/task-update.test.ts"],"names":[],"mappings":"AAAA;;;;GAIG"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Integration test for task_update tool.
|
|
3
|
+
*
|
|
4
|
+
* Run manually: node --loader ts-node/esm lib/tools/task-update.test.ts
|
|
5
|
+
*/
|
|
6
|
+
import { describe, it } from "node:test";
|
|
7
|
+
import assert from "node:assert";
|
|
8
|
+
describe("task_update tool", () => {
|
|
9
|
+
it("has correct schema", () => {
|
|
10
|
+
// Verify the tool signature matches requirements
|
|
11
|
+
const requiredParams = ["projectGroupId", "issueId", "state"];
|
|
12
|
+
const optionalParams = ["reason"];
|
|
13
|
+
// Schema validation would go here in a real test
|
|
14
|
+
assert.ok(true, "Schema structure is valid");
|
|
15
|
+
});
|
|
16
|
+
it("supports all state labels", () => {
|
|
17
|
+
const validStates = [
|
|
18
|
+
"Planning",
|
|
19
|
+
"To Do",
|
|
20
|
+
"Doing",
|
|
21
|
+
"To Test",
|
|
22
|
+
"Testing",
|
|
23
|
+
"Done",
|
|
24
|
+
"To Improve",
|
|
25
|
+
"Refining",
|
|
26
|
+
];
|
|
27
|
+
// In a real test, we'd verify these against the tool's enum
|
|
28
|
+
assert.strictEqual(validStates.length, 8);
|
|
29
|
+
});
|
|
30
|
+
it("validates required parameters", () => {
|
|
31
|
+
// Test cases:
|
|
32
|
+
// - Missing projectGroupId → Error
|
|
33
|
+
// - Missing issueId → Error
|
|
34
|
+
// - Missing state → Error
|
|
35
|
+
// - Invalid state → Error
|
|
36
|
+
// - Valid params → Success
|
|
37
|
+
assert.ok(true, "Parameter validation works");
|
|
38
|
+
});
|
|
39
|
+
it("handles same-state transitions gracefully", () => {
|
|
40
|
+
// When current state === new state, should return success without changes
|
|
41
|
+
assert.ok(true, "No-op transitions handled correctly");
|
|
42
|
+
});
|
|
43
|
+
it("logs to audit trail", () => {
|
|
44
|
+
// Verify auditLog is called with correct parameters
|
|
45
|
+
assert.ok(true, "Audit logging works");
|
|
46
|
+
});
|
|
47
|
+
});
|
|
48
|
+
// Test scenarios for manual verification:
|
|
49
|
+
// 1. task_update({ projectGroupId: "-5239235162", issueId: 28, state: "Planning" })
|
|
50
|
+
// → Should transition from "To Do" to "Planning"
|
|
51
|
+
// 2. task_update({ projectGroupId: "-5239235162", issueId: 28, state: "Planning", reason: "Needs more discussion" })
|
|
52
|
+
// → Should log reason in audit trail
|
|
53
|
+
// 3. task_update({ projectGroupId: "-5239235162", issueId: 28, state: "To Do" })
|
|
54
|
+
// → Should transition back from "Planning" to "To Do"
|
|
55
|
+
//# sourceMappingURL=task-update.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"task-update.test.js","sourceRoot":"","sources":["../../../lib/tools/task-update.test.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,MAAM,MAAM,aAAa,CAAC;AAEjC,QAAQ,CAAC,kBAAkB,EAAE,GAAG,EAAE;IAChC,EAAE,CAAC,oBAAoB,EAAE,GAAG,EAAE;QAC5B,iDAAiD;QACjD,MAAM,cAAc,GAAG,CAAC,gBAAgB,EAAE,SAAS,EAAE,OAAO,CAAC,CAAC;QAC9D,MAAM,cAAc,GAAG,CAAC,QAAQ,CAAC,CAAC;QAElC,iDAAiD;QACjD,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,2BAA2B,CAAC,CAAC;IAC/C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,2BAA2B,EAAE,GAAG,EAAE;QACnC,MAAM,WAAW,GAAG;YAClB,UAAU;YACV,OAAO;YACP,OAAO;YACP,SAAS;YACT,SAAS;YACT,MAAM;YACN,YAAY;YACZ,UAAU;SACX,CAAC;QAEF,4DAA4D;QAC5D,MAAM,CAAC,WAAW,CAAC,WAAW,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;IAC5C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,+BAA+B,EAAE,GAAG,EAAE;QACvC,cAAc;QACd,mCAAmC;QACnC,4BAA4B;QAC5B,0BAA0B;QAC1B,0BAA0B;QAC1B,2BAA2B;QAC3B,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,4BAA4B,CAAC,CAAC;IAChD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,2CAA2C,EAAE,GAAG,EAAE;QACnD,0EAA0E;QAC1E,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,qCAAqC,CAAC,CAAC;IACzD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,qBAAqB,EAAE,GAAG,EAAE;QAC7B,oDAAoD;QACpD,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,qBAAqB,CAAC,CAAC;IACzC,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,0CAA0C;AAC1C,oFAAoF;AACpF,oDAAoD;AACpD,qHAAqH;AACrH,wCAAwC;AACxC,iFAAiF;AACjF,yDAAyD"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* work_finish — Complete a task (DEV done, QA pass/fail/refine/blocked).
|
|
3
|
+
*
|
|
4
|
+
* Delegates side-effects to pipeline service, then ticks the project queue
|
|
5
|
+
* to fill free slots, sends notifications, and logs to audit.
|
|
6
|
+
*/
|
|
7
|
+
import type { OpenClawPluginApi } from "openclaw/plugin-sdk";
|
|
8
|
+
import type { ToolContext } from "../types.js";
|
|
9
|
+
export declare function createWorkFinishTool(api: OpenClawPluginApi): (ctx: ToolContext) => {
|
|
10
|
+
name: string;
|
|
11
|
+
label: string;
|
|
12
|
+
description: string;
|
|
13
|
+
parameters: {
|
|
14
|
+
type: string;
|
|
15
|
+
required: string[];
|
|
16
|
+
properties: {
|
|
17
|
+
role: {
|
|
18
|
+
type: string;
|
|
19
|
+
enum: string[];
|
|
20
|
+
description: string;
|
|
21
|
+
};
|
|
22
|
+
result: {
|
|
23
|
+
type: string;
|
|
24
|
+
enum: string[];
|
|
25
|
+
description: string;
|
|
26
|
+
};
|
|
27
|
+
projectGroupId: {
|
|
28
|
+
type: string;
|
|
29
|
+
description: string;
|
|
30
|
+
};
|
|
31
|
+
summary: {
|
|
32
|
+
type: string;
|
|
33
|
+
description: string;
|
|
34
|
+
};
|
|
35
|
+
prUrl: {
|
|
36
|
+
type: string;
|
|
37
|
+
description: string;
|
|
38
|
+
};
|
|
39
|
+
};
|
|
40
|
+
};
|
|
41
|
+
execute(_id: string, params: Record<string, unknown>): Promise<import("@mariozechner/pi-agent-core").AgentToolResult<unknown>>;
|
|
42
|
+
};
|
|
43
|
+
//# sourceMappingURL=work-finish.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"work-finish.d.ts","sourceRoot":"","sources":["../../../lib/tools/work-finish.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAE7D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAO/C,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,iBAAiB,IACjD,KAAK,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAgBH,MAAM,UAAU,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;EA+D7D"}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { jsonResult } from "openclaw/plugin-sdk";
|
|
2
|
+
import { getWorker, resolveRepoPath } from "../projects.js";
|
|
3
|
+
import { executeCompletion, getRule, NEXT_STATE } from "../services/pipeline.js";
|
|
4
|
+
import { log as auditLog } from "../audit.js";
|
|
5
|
+
import { notify, getNotificationConfig } from "../notify.js";
|
|
6
|
+
import { requireWorkspaceDir, resolveProject, resolveProvider, getPluginConfig, tickAndNotify } from "../tool-helpers.js";
|
|
7
|
+
export function createWorkFinishTool(api) {
|
|
8
|
+
return (ctx) => ({
|
|
9
|
+
name: "work_finish",
|
|
10
|
+
label: "Work Finish",
|
|
11
|
+
description: `Complete a task: DEV done/blocked, QA pass/fail/refine/blocked. Handles label transition, state update, issue close/reopen, notifications, audit, and auto-ticks the queue to fill free slots.`,
|
|
12
|
+
parameters: {
|
|
13
|
+
type: "object",
|
|
14
|
+
required: ["role", "result", "projectGroupId"],
|
|
15
|
+
properties: {
|
|
16
|
+
role: { type: "string", enum: ["dev", "qa"], description: "Worker role" },
|
|
17
|
+
result: { type: "string", enum: ["done", "pass", "fail", "refine", "blocked"], description: "Completion result" },
|
|
18
|
+
projectGroupId: { type: "string", description: "Project group ID" },
|
|
19
|
+
summary: { type: "string", description: "Brief summary" },
|
|
20
|
+
prUrl: { type: "string", description: "PR/MR URL (auto-detected if omitted)" },
|
|
21
|
+
},
|
|
22
|
+
},
|
|
23
|
+
async execute(_id, params) {
|
|
24
|
+
const role = params.role;
|
|
25
|
+
const result = params.result;
|
|
26
|
+
const groupId = params.projectGroupId;
|
|
27
|
+
const summary = params.summary;
|
|
28
|
+
const prUrl = params.prUrl;
|
|
29
|
+
const workspaceDir = requireWorkspaceDir(ctx);
|
|
30
|
+
// Validate role:result
|
|
31
|
+
if (role === "dev" && result !== "done" && result !== "blocked")
|
|
32
|
+
throw new Error(`DEV can only complete with "done" or "blocked", got "${result}"`);
|
|
33
|
+
if (role === "qa" && result === "done")
|
|
34
|
+
throw new Error(`QA cannot use "done". Use "pass", "fail", "refine", or "blocked".`);
|
|
35
|
+
if (!getRule(role, result))
|
|
36
|
+
throw new Error(`Invalid completion: ${role}:${result}`);
|
|
37
|
+
// Resolve project + worker
|
|
38
|
+
const { project } = await resolveProject(workspaceDir, groupId);
|
|
39
|
+
const worker = getWorker(project, role);
|
|
40
|
+
if (!worker.active)
|
|
41
|
+
throw new Error(`${role.toUpperCase()} worker not active on ${project.name}`);
|
|
42
|
+
const issueId = worker.issueId ? Number(worker.issueId.split(",")[0]) : null;
|
|
43
|
+
if (!issueId)
|
|
44
|
+
throw new Error(`No issueId for active ${role.toUpperCase()} on ${project.name}`);
|
|
45
|
+
const { provider } = resolveProvider(project);
|
|
46
|
+
const repoPath = resolveRepoPath(project.repo);
|
|
47
|
+
const issue = await provider.getIssue(issueId);
|
|
48
|
+
// Execute completion (pipeline service)
|
|
49
|
+
const completion = await executeCompletion({
|
|
50
|
+
workspaceDir, groupId, role, result, issueId, summary, prUrl, provider, repoPath,
|
|
51
|
+
});
|
|
52
|
+
const output = {
|
|
53
|
+
success: true, project: project.name, groupId, issueId, role, result,
|
|
54
|
+
...completion,
|
|
55
|
+
};
|
|
56
|
+
// Notify completion
|
|
57
|
+
const pluginConfig = getPluginConfig(api);
|
|
58
|
+
const notifyConfig = getNotificationConfig(pluginConfig);
|
|
59
|
+
await notify({ type: "workerComplete", project: project.name, groupId, issueId, issueUrl: issue.web_url, role, result: result, summary, nextState: NEXT_STATE[`${role}:${result}`] }, { workspaceDir, config: notifyConfig, groupId, channel: project.channel ?? "telegram" });
|
|
60
|
+
// Tick: fill free slots + notify starts
|
|
61
|
+
const tickPickups = await tickAndNotify({
|
|
62
|
+
workspaceDir, groupId, agentId: ctx.agentId, pluginConfig, sessionKey: ctx.sessionKey,
|
|
63
|
+
channel: project.channel ?? "telegram",
|
|
64
|
+
});
|
|
65
|
+
if (tickPickups.length)
|
|
66
|
+
output.tickPickups = tickPickups;
|
|
67
|
+
// Audit
|
|
68
|
+
await auditLog(workspaceDir, "work_finish", {
|
|
69
|
+
project: project.name, groupId, issue: issueId, role, result,
|
|
70
|
+
summary: summary ?? null, labelTransition: completion.labelTransition,
|
|
71
|
+
tickPickups: tickPickups.length,
|
|
72
|
+
});
|
|
73
|
+
return jsonResult(output);
|
|
74
|
+
},
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
//# sourceMappingURL=work-finish.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"work-finish.js","sourceRoot":"","sources":["../../../lib/tools/work-finish.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* work_start — Pick up a task from the issue queue.
|
|
3
|
+
*
|
|
4
|
+
* Context-aware: ONLY works in project group chats.
|
|
5
|
+
* Auto-detects: projectGroupId, role, level, issueId.
|
|
6
|
+
* Picks up only the explicitly requested issue (auto-tick disabled).
|
|
7
|
+
*/
|
|
8
|
+
import type { OpenClawPluginApi } from "openclaw/plugin-sdk";
|
|
9
|
+
import type { ToolContext } from "../types.js";
|
|
10
|
+
export declare function createWorkStartTool(api: OpenClawPluginApi): (ctx: ToolContext) => {
|
|
11
|
+
name: string;
|
|
12
|
+
label: string;
|
|
13
|
+
description: string;
|
|
14
|
+
parameters: {
|
|
15
|
+
type: string;
|
|
16
|
+
required: string[];
|
|
17
|
+
properties: {
|
|
18
|
+
projectGroupId: {
|
|
19
|
+
type: string;
|
|
20
|
+
description: string;
|
|
21
|
+
};
|
|
22
|
+
issueId: {
|
|
23
|
+
type: string;
|
|
24
|
+
description: string;
|
|
25
|
+
};
|
|
26
|
+
role: {
|
|
27
|
+
type: string;
|
|
28
|
+
enum: string[];
|
|
29
|
+
description: string;
|
|
30
|
+
};
|
|
31
|
+
level: {
|
|
32
|
+
type: string;
|
|
33
|
+
description: string;
|
|
34
|
+
};
|
|
35
|
+
};
|
|
36
|
+
};
|
|
37
|
+
execute(_id: string, params: Record<string, unknown>): Promise<import("@mariozechner/pi-agent-core").AgentToolResult<unknown>>;
|
|
38
|
+
};
|
|
39
|
+
//# sourceMappingURL=work-start.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"work-start.d.ts","sourceRoot":"","sources":["../../../lib/tools/work-start.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAE7D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAU/C,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,iBAAiB,IAChD,KAAK,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAeH,MAAM,UAAU,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;EAyF7D"}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import { jsonResult } from "openclaw/plugin-sdk";
|
|
2
|
+
import { selectLevel } from "../model-selector.js";
|
|
3
|
+
import { getWorker } from "../projects.js";
|
|
4
|
+
import { dispatchTask } from "../dispatch.js";
|
|
5
|
+
import { notify, getNotificationConfig } from "../notify.js";
|
|
6
|
+
import { findNextIssue, detectRoleFromLabel, detectLevelFromLabels } from "../services/tick.js";
|
|
7
|
+
import { isDevLevel } from "../tiers.js";
|
|
8
|
+
import { requireWorkspaceDir, resolveProject, resolveProvider, getPluginConfig } from "../tool-helpers.js";
|
|
9
|
+
export function createWorkStartTool(api) {
|
|
10
|
+
return (ctx) => ({
|
|
11
|
+
name: "work_start",
|
|
12
|
+
label: "Work Start",
|
|
13
|
+
description: `Pick up a task from the issue queue. ONLY works in project group chats. Handles label transition, level assignment, session creation, dispatch, and audit. Picks up only the explicitly requested issue.`,
|
|
14
|
+
parameters: {
|
|
15
|
+
type: "object",
|
|
16
|
+
required: ["projectGroupId"],
|
|
17
|
+
properties: {
|
|
18
|
+
projectGroupId: { type: "string", description: "Project group ID." },
|
|
19
|
+
issueId: { type: "number", description: "Issue ID. If omitted, picks next by priority." },
|
|
20
|
+
role: { type: "string", enum: ["dev", "qa"], description: "Worker role. Auto-detected from label if omitted." },
|
|
21
|
+
level: { type: "string", description: "Developer level (junior/medior/senior/reviewer). Auto-detected if omitted." },
|
|
22
|
+
},
|
|
23
|
+
},
|
|
24
|
+
async execute(_id, params) {
|
|
25
|
+
const issueIdParam = params.issueId;
|
|
26
|
+
const roleParam = params.role;
|
|
27
|
+
const groupId = params.projectGroupId;
|
|
28
|
+
const levelParam = (params.level ?? params.tier);
|
|
29
|
+
const workspaceDir = requireWorkspaceDir(ctx);
|
|
30
|
+
if (!groupId)
|
|
31
|
+
throw new Error("projectGroupId is required");
|
|
32
|
+
const { project } = await resolveProject(workspaceDir, groupId);
|
|
33
|
+
const { provider } = resolveProvider(project);
|
|
34
|
+
// Find issue
|
|
35
|
+
let issue;
|
|
36
|
+
let currentLabel;
|
|
37
|
+
if (issueIdParam !== undefined) {
|
|
38
|
+
issue = await provider.getIssue(issueIdParam);
|
|
39
|
+
const label = provider.getCurrentStateLabel(issue);
|
|
40
|
+
if (!label)
|
|
41
|
+
throw new Error(`Issue #${issueIdParam} has no recognized state label`);
|
|
42
|
+
currentLabel = label;
|
|
43
|
+
}
|
|
44
|
+
else {
|
|
45
|
+
const next = await findNextIssue(provider, roleParam);
|
|
46
|
+
if (!next)
|
|
47
|
+
return jsonResult({ success: false, error: `No issues available. Queue is empty.` });
|
|
48
|
+
issue = next.issue;
|
|
49
|
+
currentLabel = next.label;
|
|
50
|
+
}
|
|
51
|
+
// Detect role
|
|
52
|
+
const detectedRole = detectRoleFromLabel(currentLabel);
|
|
53
|
+
if (!detectedRole)
|
|
54
|
+
throw new Error(`Label "${currentLabel}" doesn't map to a role`);
|
|
55
|
+
const role = roleParam ?? detectedRole;
|
|
56
|
+
if (roleParam && roleParam !== detectedRole)
|
|
57
|
+
throw new Error(`Role mismatch: "${currentLabel}" → ${detectedRole}, requested ${roleParam}`);
|
|
58
|
+
// Check worker availability
|
|
59
|
+
const worker = getWorker(project, role);
|
|
60
|
+
if (worker.active)
|
|
61
|
+
throw new Error(`${role.toUpperCase()} already active on ${project.name} (issue: ${worker.issueId})`);
|
|
62
|
+
if ((project.roleExecution ?? "parallel") === "sequential") {
|
|
63
|
+
const other = role === "dev" ? "qa" : "dev";
|
|
64
|
+
if (getWorker(project, other).active)
|
|
65
|
+
throw new Error(`Sequential roleExecution: ${other.toUpperCase()} is active`);
|
|
66
|
+
}
|
|
67
|
+
// Select level
|
|
68
|
+
const targetLabel = role === "dev" ? "Doing" : "Testing";
|
|
69
|
+
let selectedLevel, levelReason, levelSource;
|
|
70
|
+
if (levelParam) {
|
|
71
|
+
selectedLevel = levelParam;
|
|
72
|
+
levelReason = "LLM-selected";
|
|
73
|
+
levelSource = "llm";
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
const labelLevel = detectLevelFromLabels(issue.labels);
|
|
77
|
+
if (labelLevel) {
|
|
78
|
+
if (role === "qa" && isDevLevel(labelLevel)) {
|
|
79
|
+
const s = selectLevel(issue.title, issue.description ?? "", role);
|
|
80
|
+
selectedLevel = s.level;
|
|
81
|
+
levelReason = `QA overrides dev level "${labelLevel}"`;
|
|
82
|
+
levelSource = "role-override";
|
|
83
|
+
}
|
|
84
|
+
else if (role === "dev" && !isDevLevel(labelLevel)) {
|
|
85
|
+
const s = selectLevel(issue.title, issue.description ?? "", role);
|
|
86
|
+
selectedLevel = s.level;
|
|
87
|
+
levelReason = s.reason;
|
|
88
|
+
levelSource = "heuristic";
|
|
89
|
+
}
|
|
90
|
+
else {
|
|
91
|
+
selectedLevel = labelLevel;
|
|
92
|
+
levelReason = `Label: "${labelLevel}"`;
|
|
93
|
+
levelSource = "label";
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
else {
|
|
97
|
+
const s = selectLevel(issue.title, issue.description ?? "", role);
|
|
98
|
+
selectedLevel = s.level;
|
|
99
|
+
levelReason = s.reason;
|
|
100
|
+
levelSource = "heuristic";
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
// Dispatch
|
|
104
|
+
const pluginConfig = getPluginConfig(api);
|
|
105
|
+
const dr = await dispatchTask({
|
|
106
|
+
workspaceDir, agentId: ctx.agentId, groupId, project, issueId: issue.iid,
|
|
107
|
+
issueTitle: issue.title, issueDescription: issue.description ?? "", issueUrl: issue.web_url,
|
|
108
|
+
role, level: selectedLevel, fromLabel: currentLabel, toLabel: targetLabel,
|
|
109
|
+
transitionLabel: (id, from, to) => provider.transitionLabel(id, from, to),
|
|
110
|
+
pluginConfig, sessionKey: ctx.sessionKey,
|
|
111
|
+
});
|
|
112
|
+
// Notify
|
|
113
|
+
const notifyConfig = getNotificationConfig(pluginConfig);
|
|
114
|
+
await notify({ type: "workerStart", project: project.name, groupId, issueId: issue.iid, issueTitle: issue.title, issueUrl: issue.web_url, role, level: dr.level, sessionAction: dr.sessionAction }, { workspaceDir, config: notifyConfig, groupId, channel: project.channel ?? "telegram" });
|
|
115
|
+
// Auto-tick disabled per issue #125 - work_start should only pick up the explicitly requested issue
|
|
116
|
+
// The heartbeat service fills parallel slots automatically
|
|
117
|
+
const output = {
|
|
118
|
+
success: true, project: project.name, groupId, issueId: issue.iid, issueTitle: issue.title,
|
|
119
|
+
role, level: dr.level, model: dr.model, sessionAction: dr.sessionAction,
|
|
120
|
+
announcement: dr.announcement, labelTransition: `${currentLabel} → ${targetLabel}`,
|
|
121
|
+
levelReason, levelSource,
|
|
122
|
+
autoDetected: { role: !roleParam, issueId: issueIdParam === undefined, level: !levelParam },
|
|
123
|
+
};
|
|
124
|
+
// tickPickups removed with auto-tick
|
|
125
|
+
return jsonResult(output);
|
|
126
|
+
},
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
//# sourceMappingURL=work-start.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"work-start.js","sourceRoot":"","sources":["../../../lib/tools/work-start.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared types for the DevClaw plugin.
|
|
3
|
+
*
|
|
4
|
+
* OpenClawPluginToolContext is declared in the plugin-sdk but not exported.
|
|
5
|
+
* We define a compatible local type for use in tool factory functions.
|
|
6
|
+
*/
|
|
7
|
+
export type ToolContext = {
|
|
8
|
+
config?: Record<string, unknown>;
|
|
9
|
+
workspaceDir?: string;
|
|
10
|
+
agentDir?: string;
|
|
11
|
+
agentId?: string;
|
|
12
|
+
sessionKey?: string;
|
|
13
|
+
messageChannel?: string;
|
|
14
|
+
agentAccountId?: string;
|
|
15
|
+
sandboxed?: boolean;
|
|
16
|
+
};
|
|
17
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../lib/types.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,MAAM,MAAM,WAAW,GAAG;IACxB,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACjC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../lib/types.ts"],"names":[],"mappings":"AAAA;;;;;GAKG"}
|