@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.
Files changed (163) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +406 -0
  3. package/dist/index.d.ts +88 -0
  4. package/dist/index.d.ts.map +1 -0
  5. package/dist/index.js +107 -0
  6. package/dist/index.js.map +1 -0
  7. package/dist/lib/audit.d.ts +2 -0
  8. package/dist/lib/audit.d.ts.map +1 -0
  9. package/dist/lib/audit.js +42 -0
  10. package/dist/lib/audit.js.map +1 -0
  11. package/dist/lib/binding-manager.d.ts +35 -0
  12. package/dist/lib/binding-manager.d.ts.map +1 -0
  13. package/dist/lib/binding-manager.js +88 -0
  14. package/dist/lib/binding-manager.js.map +1 -0
  15. package/dist/lib/cli.d.ts +12 -0
  16. package/dist/lib/cli.d.ts.map +1 -0
  17. package/dist/lib/cli.js +69 -0
  18. package/dist/lib/cli.js.map +1 -0
  19. package/dist/lib/dispatch.d.ts +58 -0
  20. package/dist/lib/dispatch.d.ts.map +1 -0
  21. package/dist/lib/dispatch.js +163 -0
  22. package/dist/lib/dispatch.js.map +1 -0
  23. package/dist/lib/model-selector.d.ts +21 -0
  24. package/dist/lib/model-selector.d.ts.map +1 -0
  25. package/dist/lib/model-selector.js +74 -0
  26. package/dist/lib/model-selector.js.map +1 -0
  27. package/dist/lib/notify.d.ts +54 -0
  28. package/dist/lib/notify.d.ts.map +1 -0
  29. package/dist/lib/notify.js +143 -0
  30. package/dist/lib/notify.js.map +1 -0
  31. package/dist/lib/onboarding.d.ts +5 -0
  32. package/dist/lib/onboarding.d.ts.map +1 -0
  33. package/dist/lib/onboarding.js +124 -0
  34. package/dist/lib/onboarding.js.map +1 -0
  35. package/dist/lib/projects.d.ts +64 -0
  36. package/dist/lib/projects.d.ts.map +1 -0
  37. package/dist/lib/projects.js +127 -0
  38. package/dist/lib/projects.js.map +1 -0
  39. package/dist/lib/providers/github.d.ts +23 -0
  40. package/dist/lib/providers/github.d.ts.map +1 -0
  41. package/dist/lib/providers/github.js +130 -0
  42. package/dist/lib/providers/github.js.map +1 -0
  43. package/dist/lib/providers/gitlab.d.ts +23 -0
  44. package/dist/lib/providers/gitlab.d.ts.map +1 -0
  45. package/dist/lib/providers/gitlab.js +133 -0
  46. package/dist/lib/providers/gitlab.js.map +1 -0
  47. package/dist/lib/providers/index.d.ts +12 -0
  48. package/dist/lib/providers/index.d.ts.map +1 -0
  49. package/dist/lib/providers/index.js +25 -0
  50. package/dist/lib/providers/index.js.map +1 -0
  51. package/dist/lib/providers/provider.d.ts +35 -0
  52. package/dist/lib/providers/provider.d.ts.map +1 -0
  53. package/dist/lib/providers/provider.js +13 -0
  54. package/dist/lib/providers/provider.js.map +1 -0
  55. package/dist/lib/services/health.d.ts +38 -0
  56. package/dist/lib/services/health.d.ts.map +1 -0
  57. package/dist/lib/services/health.js +100 -0
  58. package/dist/lib/services/health.js.map +1 -0
  59. package/dist/lib/services/heartbeat.d.ts +38 -0
  60. package/dist/lib/services/heartbeat.d.ts.map +1 -0
  61. package/dist/lib/services/heartbeat.js +199 -0
  62. package/dist/lib/services/heartbeat.js.map +1 -0
  63. package/dist/lib/services/pipeline.d.ts +36 -0
  64. package/dist/lib/services/pipeline.d.ts.map +1 -0
  65. package/dist/lib/services/pipeline.js +90 -0
  66. package/dist/lib/services/pipeline.js.map +1 -0
  67. package/dist/lib/services/queue.d.ts +14 -0
  68. package/dist/lib/services/queue.d.ts.map +1 -0
  69. package/dist/lib/services/queue.js +31 -0
  70. package/dist/lib/services/queue.js.map +1 -0
  71. package/dist/lib/services/tick.d.ts +62 -0
  72. package/dist/lib/services/tick.d.ts.map +1 -0
  73. package/dist/lib/services/tick.js +160 -0
  74. package/dist/lib/services/tick.js.map +1 -0
  75. package/dist/lib/setup/agent.d.ts +14 -0
  76. package/dist/lib/setup/agent.d.ts.map +1 -0
  77. package/dist/lib/setup/agent.js +72 -0
  78. package/dist/lib/setup/agent.js.map +1 -0
  79. package/dist/lib/setup/config.d.ts +22 -0
  80. package/dist/lib/setup/config.d.ts.map +1 -0
  81. package/dist/lib/setup/config.js +67 -0
  82. package/dist/lib/setup/config.js.map +1 -0
  83. package/dist/lib/setup/index.d.ts +53 -0
  84. package/dist/lib/setup/index.d.ts.map +1 -0
  85. package/dist/lib/setup/index.js +68 -0
  86. package/dist/lib/setup/index.js.map +1 -0
  87. package/dist/lib/setup/workspace.d.ts +6 -0
  88. package/dist/lib/setup/workspace.d.ts.map +1 -0
  89. package/dist/lib/setup/workspace.js +69 -0
  90. package/dist/lib/setup/workspace.js.map +1 -0
  91. package/dist/lib/templates.d.ts +9 -0
  92. package/dist/lib/templates.d.ts.map +1 -0
  93. package/dist/lib/templates.js +163 -0
  94. package/dist/lib/templates.js.map +1 -0
  95. package/dist/lib/tiers.d.ts +55 -0
  96. package/dist/lib/tiers.d.ts.map +1 -0
  97. package/dist/lib/tiers.js +74 -0
  98. package/dist/lib/tiers.js.map +1 -0
  99. package/dist/lib/tool-helpers.d.ts +44 -0
  100. package/dist/lib/tool-helpers.d.ts.map +1 -0
  101. package/dist/lib/tool-helpers.js +65 -0
  102. package/dist/lib/tool-helpers.js.map +1 -0
  103. package/dist/lib/tools/health.d.ts +28 -0
  104. package/dist/lib/tools/health.d.ts.map +1 -0
  105. package/dist/lib/tools/health.js +61 -0
  106. package/dist/lib/tools/health.js.map +1 -0
  107. package/dist/lib/tools/onboard.d.ts +24 -0
  108. package/dist/lib/tools/onboard.d.ts.map +1 -0
  109. package/dist/lib/tools/onboard.js +27 -0
  110. package/dist/lib/tools/onboard.js.map +1 -0
  111. package/dist/lib/tools/project-register.d.ts +51 -0
  112. package/dist/lib/tools/project-register.d.ts.map +1 -0
  113. package/dist/lib/tools/project-register.js +172 -0
  114. package/dist/lib/tools/project-register.js.map +1 -0
  115. package/dist/lib/tools/queue-status.test.d.ts +2 -0
  116. package/dist/lib/tools/queue-status.test.d.ts.map +1 -0
  117. package/dist/lib/tools/queue-status.test.js +48 -0
  118. package/dist/lib/tools/queue-status.test.js.map +1 -0
  119. package/dist/lib/tools/setup.d.ts +76 -0
  120. package/dist/lib/tools/setup.d.ts.map +1 -0
  121. package/dist/lib/tools/setup.js +102 -0
  122. package/dist/lib/tools/setup.js.map +1 -0
  123. package/dist/lib/tools/status.d.ts +24 -0
  124. package/dist/lib/tools/status.d.ts.map +1 -0
  125. package/dist/lib/tools/status.js +53 -0
  126. package/dist/lib/tools/status.js.map +1 -0
  127. package/dist/lib/tools/task-comment.d.ts +40 -0
  128. package/dist/lib/tools/task-comment.d.ts.map +1 -0
  129. package/dist/lib/tools/task-comment.js +84 -0
  130. package/dist/lib/tools/task-comment.js.map +1 -0
  131. package/dist/lib/tools/task-create.d.ts +54 -0
  132. package/dist/lib/tools/task-create.d.ts.map +1 -0
  133. package/dist/lib/tools/task-create.js +77 -0
  134. package/dist/lib/tools/task-create.js.map +1 -0
  135. package/dist/lib/tools/task-update.d.ts +40 -0
  136. package/dist/lib/tools/task-update.d.ts.map +1 -0
  137. package/dist/lib/tools/task-update.js +79 -0
  138. package/dist/lib/tools/task-update.js.map +1 -0
  139. package/dist/lib/tools/task-update.test.d.ts +7 -0
  140. package/dist/lib/tools/task-update.test.d.ts.map +1 -0
  141. package/dist/lib/tools/task-update.test.js +55 -0
  142. package/dist/lib/tools/task-update.test.js.map +1 -0
  143. package/dist/lib/tools/work-finish.d.ts +43 -0
  144. package/dist/lib/tools/work-finish.d.ts.map +1 -0
  145. package/dist/lib/tools/work-finish.js +77 -0
  146. package/dist/lib/tools/work-finish.js.map +1 -0
  147. package/dist/lib/tools/work-start.d.ts +39 -0
  148. package/dist/lib/tools/work-start.d.ts.map +1 -0
  149. package/dist/lib/tools/work-start.js +129 -0
  150. package/dist/lib/tools/work-start.js.map +1 -0
  151. package/dist/lib/types.d.ts +17 -0
  152. package/dist/lib/types.d.ts.map +1 -0
  153. package/dist/lib/types.js +8 -0
  154. package/dist/lib/types.js.map +1 -0
  155. package/docs/ARCHITECTURE.md +662 -0
  156. package/docs/CONFIGURATION.md +336 -0
  157. package/docs/MANAGEMENT.md +120 -0
  158. package/docs/ONBOARDING.md +251 -0
  159. package/docs/QA_WORKFLOW.md +120 -0
  160. package/docs/ROADMAP.md +96 -0
  161. package/docs/TESTING.md +339 -0
  162. package/docs/TOOLS.md +361 -0
  163. package/package.json +55 -0
@@ -0,0 +1,55 @@
1
+ /**
2
+ * Developer level definitions and model resolution.
3
+ *
4
+ * Level names are plain: "junior", "senior", "reviewer", etc.
5
+ * Role context (dev/qa) is always provided by the caller or parent structure.
6
+ */
7
+ export declare const DEV_LEVELS: readonly ["junior", "medior", "senior"];
8
+ export declare const QA_LEVELS: readonly ["reviewer", "tester"];
9
+ export type DevLevel = (typeof DEV_LEVELS)[number];
10
+ export type QaLevel = (typeof QA_LEVELS)[number];
11
+ export type Level = DevLevel | QaLevel;
12
+ /** Default models, nested by role. */
13
+ export declare const DEFAULT_MODELS: {
14
+ dev: {
15
+ junior: string;
16
+ medior: string;
17
+ senior: string;
18
+ };
19
+ qa: {
20
+ reviewer: string;
21
+ tester: string;
22
+ };
23
+ };
24
+ /** Emoji used in announcements, nested by role. */
25
+ export declare const LEVEL_EMOJI: {
26
+ dev: {
27
+ junior: string;
28
+ medior: string;
29
+ senior: string;
30
+ };
31
+ qa: {
32
+ reviewer: string;
33
+ tester: string;
34
+ };
35
+ };
36
+ /** Check if a level belongs to the dev role. */
37
+ export declare function isDevLevel(value: string): value is DevLevel;
38
+ /** Check if a level belongs to the qa role. */
39
+ export declare function isQaLevel(value: string): value is QaLevel;
40
+ /** Determine the role a level belongs to. */
41
+ export declare function levelRole(level: string): "dev" | "qa" | undefined;
42
+ /** Get the default model for a role + level. */
43
+ export declare function defaultModel(role: "dev" | "qa", level: string): string | undefined;
44
+ /** Get the emoji for a role + level. */
45
+ export declare function levelEmoji(role: "dev" | "qa", level: string): string | undefined;
46
+ /**
47
+ * Resolve a level to a full model ID.
48
+ *
49
+ * Resolution order:
50
+ * 1. Plugin config `models.<role>.<level>`
51
+ * 2. DEFAULT_MODELS[role][level]
52
+ * 3. Passthrough (treat as raw model ID)
53
+ */
54
+ export declare function resolveModel(role: "dev" | "qa", level: string, pluginConfig?: Record<string, unknown>): string;
55
+ //# sourceMappingURL=tiers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tiers.d.ts","sourceRoot":"","sources":["../../lib/tiers.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,eAAO,MAAM,UAAU,yCAA0C,CAAC;AAClE,eAAO,MAAM,SAAS,iCAAkC,CAAC;AAEzD,MAAM,MAAM,QAAQ,GAAG,CAAC,OAAO,UAAU,CAAC,CAAC,MAAM,CAAC,CAAC;AACnD,MAAM,MAAM,OAAO,GAAG,CAAC,OAAO,SAAS,CAAC,CAAC,MAAM,CAAC,CAAC;AACjD,MAAM,MAAM,KAAK,GAAG,QAAQ,GAAG,OAAO,CAAC;AAEvC,sCAAsC;AACtC,eAAO,MAAM,cAAc;;;;;;;;;;CAU1B,CAAC;AAEF,mDAAmD;AACnD,eAAO,MAAM,WAAW;;;;;;;;;;CAUvB,CAAC;AAEF,gDAAgD;AAChD,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,KAAK,IAAI,QAAQ,CAE3D;AAED,+CAA+C;AAC/C,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,KAAK,IAAI,OAAO,CAEzD;AAED,6CAA6C;AAC7C,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,KAAK,GAAG,IAAI,GAAG,SAAS,CAIjE;AAED,gDAAgD;AAChD,wBAAgB,YAAY,CAAC,IAAI,EAAE,KAAK,GAAG,IAAI,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAElF;AAED,wCAAwC;AACxC,wBAAgB,UAAU,CAAC,IAAI,EAAE,KAAK,GAAG,IAAI,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAEhF;AAED;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAC1B,IAAI,EAAE,KAAK,GAAG,IAAI,EAClB,KAAK,EAAE,MAAM,EACb,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GACrC,MAAM,CASR"}
@@ -0,0 +1,74 @@
1
+ /**
2
+ * Developer level definitions and model resolution.
3
+ *
4
+ * Level names are plain: "junior", "senior", "reviewer", etc.
5
+ * Role context (dev/qa) is always provided by the caller or parent structure.
6
+ */
7
+ export const DEV_LEVELS = ["junior", "medior", "senior"];
8
+ export const QA_LEVELS = ["reviewer", "tester"];
9
+ /** Default models, nested by role. */
10
+ export const DEFAULT_MODELS = {
11
+ dev: {
12
+ junior: "anthropic/claude-haiku-4-5",
13
+ medior: "anthropic/claude-sonnet-4-5",
14
+ senior: "anthropic/claude-opus-4-5",
15
+ },
16
+ qa: {
17
+ reviewer: "anthropic/claude-sonnet-4-5",
18
+ tester: "anthropic/claude-haiku-4-5",
19
+ },
20
+ };
21
+ /** Emoji used in announcements, nested by role. */
22
+ export const LEVEL_EMOJI = {
23
+ dev: {
24
+ junior: "⚡",
25
+ medior: "🔧",
26
+ senior: "🧠",
27
+ },
28
+ qa: {
29
+ reviewer: "🔍",
30
+ tester: "👀",
31
+ },
32
+ };
33
+ /** Check if a level belongs to the dev role. */
34
+ export function isDevLevel(value) {
35
+ return DEV_LEVELS.includes(value);
36
+ }
37
+ /** Check if a level belongs to the qa role. */
38
+ export function isQaLevel(value) {
39
+ return QA_LEVELS.includes(value);
40
+ }
41
+ /** Determine the role a level belongs to. */
42
+ export function levelRole(level) {
43
+ if (isDevLevel(level))
44
+ return "dev";
45
+ if (isQaLevel(level))
46
+ return "qa";
47
+ return undefined;
48
+ }
49
+ /** Get the default model for a role + level. */
50
+ export function defaultModel(role, level) {
51
+ return DEFAULT_MODELS[role][level];
52
+ }
53
+ /** Get the emoji for a role + level. */
54
+ export function levelEmoji(role, level) {
55
+ return LEVEL_EMOJI[role][level];
56
+ }
57
+ /**
58
+ * Resolve a level to a full model ID.
59
+ *
60
+ * Resolution order:
61
+ * 1. Plugin config `models.<role>.<level>`
62
+ * 2. DEFAULT_MODELS[role][level]
63
+ * 3. Passthrough (treat as raw model ID)
64
+ */
65
+ export function resolveModel(role, level, pluginConfig) {
66
+ const models = pluginConfig?.models;
67
+ if (models && typeof models === "object") {
68
+ const roleModels = models[role];
69
+ if (roleModels?.[level])
70
+ return roleModels[level];
71
+ }
72
+ return defaultModel(role, level) ?? level;
73
+ }
74
+ //# sourceMappingURL=tiers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tiers.js","sourceRoot":"","sources":["../../lib/tiers.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,QAAQ,EAAE,QAAQ,EAAE,QAAQ,CAAU,CAAC;AAClE,MAAM,CAAC,MAAM,SAAS,GAAG,CAAC,UAAU,EAAE,QAAQ,CAAU,CAAC;AAMzD,sCAAsC;AACtC,MAAM,CAAC,MAAM,cAAc,GAAG;IAC5B,GAAG,EAAE;QACH,MAAM,EAAE,4BAA4B;QACpC,MAAM,EAAE,6BAA6B;QACrC,MAAM,EAAE,2BAA2B;KACpC;IACD,EAAE,EAAE;QACF,QAAQ,EAAE,6BAA6B;QACvC,MAAM,EAAE,4BAA4B;KACrC;CACF,CAAC;AAEF,mDAAmD;AACnD,MAAM,CAAC,MAAM,WAAW,GAAG;IACzB,GAAG,EAAE;QACH,MAAM,EAAE,GAAG;QACX,MAAM,EAAE,IAAI;QACZ,MAAM,EAAE,IAAI;KACb;IACD,EAAE,EAAE;QACF,QAAQ,EAAE,IAAI;QACd,MAAM,EAAE,IAAI;KACb;CACF,CAAC;AAEF,gDAAgD;AAChD,MAAM,UAAU,UAAU,CAAC,KAAa;IACtC,OAAQ,UAAgC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;AAC3D,CAAC;AAED,+CAA+C;AAC/C,MAAM,UAAU,SAAS,CAAC,KAAa;IACrC,OAAQ,SAA+B,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;AAC1D,CAAC;AAED,6CAA6C;AAC7C,MAAM,UAAU,SAAS,CAAC,KAAa;IACrC,IAAI,UAAU,CAAC,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC;IACpC,IAAI,SAAS,CAAC,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IAClC,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,gDAAgD;AAChD,MAAM,UAAU,YAAY,CAAC,IAAkB,EAAE,KAAa;IAC5D,OAAQ,cAAc,CAAC,IAAI,CAA4B,CAAC,KAAK,CAAC,CAAC;AACjE,CAAC;AAED,wCAAwC;AACxC,MAAM,UAAU,UAAU,CAAC,IAAkB,EAAE,KAAa;IAC1D,OAAQ,WAAW,CAAC,IAAI,CAA4B,CAAC,KAAK,CAAC,CAAC;AAC9D,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,YAAY,CAC1B,IAAkB,EAClB,KAAa,EACb,YAAsC;IAEtC,MAAM,MAAM,GAAI,YAAqD,EAAE,MAAM,CAAC;IAE9E,IAAI,MAAM,IAAI,OAAO,MAAM,KAAK,QAAQ,EAAE,CAAC;QACzC,MAAM,UAAU,GAAG,MAAM,CAAC,IAAI,CAAuC,CAAC;QACtE,IAAI,UAAU,EAAE,CAAC,KAAK,CAAC;YAAE,OAAO,UAAU,CAAC,KAAK,CAAC,CAAC;IACpD,CAAC;IAED,OAAO,YAAY,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,KAAK,CAAC;AAC5C,CAAC"}
@@ -0,0 +1,44 @@
1
+ /**
2
+ * tool-helpers.ts — Shared resolution helpers for tool execute() functions.
3
+ *
4
+ * Eliminates repeated boilerplate across tools: workspace validation,
5
+ * project resolution, provider creation.
6
+ */
7
+ import type { OpenClawPluginApi } from "openclaw/plugin-sdk";
8
+ import type { ToolContext } from "./types.js";
9
+ import { type Project, type ProjectsData } from "./projects.js";
10
+ import { type ProviderWithType } from "./providers/index.js";
11
+ import { type TickAction } from "./services/tick.js";
12
+ /**
13
+ * Require workspaceDir from context or throw a clear error.
14
+ */
15
+ export declare function requireWorkspaceDir(ctx: ToolContext): string;
16
+ /**
17
+ * Resolve project by groupId, throw if not found.
18
+ */
19
+ export declare function resolveProject(workspaceDir: string, groupId: string): Promise<{
20
+ data: ProjectsData;
21
+ project: Project;
22
+ }>;
23
+ /**
24
+ * Create an issue provider for a project.
25
+ */
26
+ export declare function resolveProvider(project: Project): ProviderWithType;
27
+ /**
28
+ * Get plugin config as a typed record (or undefined).
29
+ */
30
+ export declare function getPluginConfig(api: OpenClawPluginApi): Record<string, unknown> | undefined;
31
+ /**
32
+ * Run projectTick (non-fatal) and send workerStart notifications for any pickups.
33
+ * Returns the pickups array (empty on failure).
34
+ */
35
+ export declare function tickAndNotify(opts: {
36
+ workspaceDir: string;
37
+ groupId: string;
38
+ agentId?: string;
39
+ pluginConfig?: Record<string, unknown>;
40
+ sessionKey?: string;
41
+ targetRole?: "dev" | "qa";
42
+ channel?: string;
43
+ }): Promise<TickAction[]>;
44
+ //# sourceMappingURL=tool-helpers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tool-helpers.d.ts","sourceRoot":"","sources":["../../lib/tool-helpers.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAC7D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAC9C,OAAO,EAA4B,KAAK,OAAO,EAAE,KAAK,YAAY,EAAE,MAAM,eAAe,CAAC;AAC1F,OAAO,EAAkB,KAAK,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAC7E,OAAO,EAAe,KAAK,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAGlE;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,WAAW,GAAG,MAAM,CAK5D;AAED;;GAEG;AACH,wBAAsB,cAAc,CAClC,YAAY,EAAE,MAAM,EACpB,OAAO,EAAE,MAAM,GACd,OAAO,CAAC;IAAE,IAAI,EAAE,YAAY,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,CAAC,CAOnD;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,OAAO,GAAG,gBAAgB,CAElE;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,iBAAiB,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAE3F;AAED;;;GAGG;AACH,wBAAsB,aAAa,CAAC,IAAI,EAAE;IACxC,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACvC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,KAAK,GAAG,IAAI,CAAC;IAC1B,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,GAAG,OAAO,CAAC,UAAU,EAAE,CAAC,CAwBxB"}
@@ -0,0 +1,65 @@
1
+ import { readProjects, getProject } from "./projects.js";
2
+ import { createProvider } from "./providers/index.js";
3
+ import { projectTick } from "./services/tick.js";
4
+ import { notifyTickPickups, getNotificationConfig } from "./notify.js";
5
+ /**
6
+ * Require workspaceDir from context or throw a clear error.
7
+ */
8
+ export function requireWorkspaceDir(ctx) {
9
+ if (!ctx.workspaceDir) {
10
+ throw new Error("No workspace directory available in tool context");
11
+ }
12
+ return ctx.workspaceDir;
13
+ }
14
+ /**
15
+ * Resolve project by groupId, throw if not found.
16
+ */
17
+ export async function resolveProject(workspaceDir, groupId) {
18
+ const data = await readProjects(workspaceDir);
19
+ const project = getProject(data, groupId);
20
+ if (!project) {
21
+ throw new Error(`Project not found for groupId ${groupId}. Run project_register first.`);
22
+ }
23
+ return { data, project };
24
+ }
25
+ /**
26
+ * Create an issue provider for a project.
27
+ */
28
+ export function resolveProvider(project) {
29
+ return createProvider({ repo: project.repo });
30
+ }
31
+ /**
32
+ * Get plugin config as a typed record (or undefined).
33
+ */
34
+ export function getPluginConfig(api) {
35
+ return api.pluginConfig;
36
+ }
37
+ /**
38
+ * Run projectTick (non-fatal) and send workerStart notifications for any pickups.
39
+ * Returns the pickups array (empty on failure).
40
+ */
41
+ export async function tickAndNotify(opts) {
42
+ let pickups = [];
43
+ try {
44
+ const result = await projectTick({
45
+ workspaceDir: opts.workspaceDir,
46
+ groupId: opts.groupId,
47
+ agentId: opts.agentId,
48
+ pluginConfig: opts.pluginConfig,
49
+ sessionKey: opts.sessionKey,
50
+ targetRole: opts.targetRole,
51
+ });
52
+ pickups = result.pickups;
53
+ }
54
+ catch { /* non-fatal: tick failure shouldn't break the caller */ }
55
+ if (pickups.length) {
56
+ const notifyConfig = getNotificationConfig(opts.pluginConfig);
57
+ await notifyTickPickups(pickups, {
58
+ workspaceDir: opts.workspaceDir,
59
+ config: notifyConfig,
60
+ channel: opts.channel,
61
+ });
62
+ }
63
+ return pickups;
64
+ }
65
+ //# sourceMappingURL=tool-helpers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tool-helpers.js","sourceRoot":"","sources":["../../lib/tool-helpers.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,YAAY,EAAE,UAAU,EAAmC,MAAM,eAAe,CAAC;AAC1F,OAAO,EAAE,cAAc,EAAyB,MAAM,sBAAsB,CAAC;AAC7E,OAAO,EAAE,WAAW,EAAmB,MAAM,oBAAoB,CAAC;AAClE,OAAO,EAAE,iBAAiB,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AAEvE;;GAEG;AACH,MAAM,UAAU,mBAAmB,CAAC,GAAgB;IAClD,IAAI,CAAC,GAAG,CAAC,YAAY,EAAE,CAAC;QACtB,MAAM,IAAI,KAAK,CAAC,kDAAkD,CAAC,CAAC;IACtE,CAAC;IACD,OAAO,GAAG,CAAC,YAAY,CAAC;AAC1B,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,YAAoB,EACpB,OAAe;IAEf,MAAM,IAAI,GAAG,MAAM,YAAY,CAAC,YAAY,CAAC,CAAC;IAC9C,MAAM,OAAO,GAAG,UAAU,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IAC1C,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,MAAM,IAAI,KAAK,CAAC,iCAAiC,OAAO,+BAA+B,CAAC,CAAC;IAC3F,CAAC;IACD,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;AAC3B,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,eAAe,CAAC,OAAgB;IAC9C,OAAO,cAAc,CAAC,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC;AAChD,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,eAAe,CAAC,GAAsB;IACpD,OAAO,GAAG,CAAC,YAAmD,CAAC;AACjE,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,IAQnC;IACC,IAAI,OAAO,GAAiB,EAAE,CAAC;IAC/B,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,WAAW,CAAC;YAC/B,YAAY,EAAE,IAAI,CAAC,YAAY;YAC/B,OAAO,EAAE,IAAI,CAAC,OAAO;YACrB,OAAO,EAAE,IAAI,CAAC,OAAO;YACrB,YAAY,EAAE,IAAI,CAAC,YAAY;YAC/B,UAAU,EAAE,IAAI,CAAC,UAAU;YAC3B,UAAU,EAAE,IAAI,CAAC,UAAU;SAC5B,CAAC,CAAC;QACH,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC;IAC3B,CAAC;IAAC,MAAM,CAAC,CAAC,wDAAwD,CAAC,CAAC;IAEpE,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;QACnB,MAAM,YAAY,GAAG,qBAAqB,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;QAC9D,MAAM,iBAAiB,CAAC,OAAO,EAAE;YAC/B,YAAY,EAAE,IAAI,CAAC,YAAY;YAC/B,MAAM,EAAE,YAAY;YACpB,OAAO,EAAE,IAAI,CAAC,OAAO;SACtB,CAAC,CAAC;IACL,CAAC;IAED,OAAO,OAAO,CAAC;AACjB,CAAC"}
@@ -0,0 +1,28 @@
1
+ import type { ToolContext } from "../types.js";
2
+ export declare function createHealthTool(): (ctx: ToolContext) => {
3
+ name: string;
4
+ label: string;
5
+ description: string;
6
+ parameters: {
7
+ type: string;
8
+ properties: {
9
+ projectGroupId: {
10
+ type: string;
11
+ description: string;
12
+ };
13
+ fix: {
14
+ type: string;
15
+ description: string;
16
+ };
17
+ activeSessions: {
18
+ type: string;
19
+ items: {
20
+ type: string;
21
+ };
22
+ description: string;
23
+ };
24
+ };
25
+ };
26
+ execute(_id: string, params: Record<string, unknown>): Promise<import("@mariozechner/pi-agent-core").AgentToolResult<unknown>>;
27
+ };
28
+ //# sourceMappingURL=health.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"health.d.ts","sourceRoot":"","sources":["../../../lib/tools/health.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAM/C,wBAAgB,gBAAgB,KACtB,KAAK,WAAW;;;;;;;;;;;;;;;;;;;;;;;;iBAaH,MAAM,UAAU,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;EA0C7D"}
@@ -0,0 +1,61 @@
1
+ /**
2
+ * health — Worker health scan with optional auto-fix.
3
+ *
4
+ * Read-only by default (surfaces issues). Pass fix=true to apply fixes.
5
+ */
6
+ import { jsonResult } from "openclaw/plugin-sdk";
7
+ import { readProjects, getProject } from "../projects.js";
8
+ import { log as auditLog } from "../audit.js";
9
+ import { checkWorkerHealth } from "../services/health.js";
10
+ import { requireWorkspaceDir, resolveProvider } from "../tool-helpers.js";
11
+ export function createHealthTool() {
12
+ return (ctx) => ({
13
+ name: "health",
14
+ label: "Health",
15
+ description: `Scan worker health across projects. Detects zombies, stale workers, orphaned state. Pass fix=true to auto-fix. Context-aware: auto-filters in group chats.`,
16
+ parameters: {
17
+ type: "object",
18
+ properties: {
19
+ projectGroupId: { type: "string", description: "Filter to specific project. Omit for all." },
20
+ fix: { type: "boolean", description: "Apply fixes for detected issues. Default: false (read-only)." },
21
+ activeSessions: { type: "array", items: { type: "string" }, description: "Active session IDs for zombie detection." },
22
+ },
23
+ },
24
+ async execute(_id, params) {
25
+ const workspaceDir = requireWorkspaceDir(ctx);
26
+ const fix = params.fix ?? false;
27
+ const activeSessions = params.activeSessions ?? [];
28
+ const groupId = params.projectGroupId;
29
+ const data = await readProjects(workspaceDir);
30
+ const projectIds = groupId ? [groupId] : Object.keys(data.projects);
31
+ const issues = [];
32
+ for (const pid of projectIds) {
33
+ const project = getProject(data, pid);
34
+ if (!project)
35
+ continue;
36
+ const { provider } = resolveProvider(project);
37
+ for (const role of ["dev", "qa"]) {
38
+ const fixes = await checkWorkerHealth({
39
+ workspaceDir, groupId: pid, project, role, activeSessions,
40
+ autoFix: fix, provider,
41
+ });
42
+ issues.push(...fixes.map((f) => ({ ...f, project: project.name, role })));
43
+ }
44
+ }
45
+ await auditLog(workspaceDir, "health", {
46
+ projectCount: projectIds.length,
47
+ fix,
48
+ issuesFound: issues.length,
49
+ issuesFixed: issues.filter((i) => i.fixed).length,
50
+ });
51
+ return jsonResult({
52
+ success: true,
53
+ fix,
54
+ projectsScanned: projectIds.length,
55
+ issues,
56
+ note: activeSessions.length === 0 ? "No activeSessions provided — zombie detection skipped." : undefined,
57
+ });
58
+ },
59
+ });
60
+ }
61
+ //# sourceMappingURL=health.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"health.js","sourceRoot":"","sources":["../../../lib/tools/health.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAEjD,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAC1D,OAAO,EAAE,GAAG,IAAI,QAAQ,EAAE,MAAM,aAAa,CAAC;AAC9C,OAAO,EAAE,iBAAiB,EAAkB,MAAM,uBAAuB,CAAC;AAC1E,OAAO,EAAE,mBAAmB,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAE1E,MAAM,UAAU,gBAAgB;IAC9B,OAAO,CAAC,GAAgB,EAAE,EAAE,CAAC,CAAC;QAC5B,IAAI,EAAE,QAAQ;QACd,KAAK,EAAE,QAAQ;QACf,WAAW,EAAE,4JAA4J;QACzK,UAAU,EAAE;YACV,IAAI,EAAE,QAAQ;YACd,UAAU,EAAE;gBACV,cAAc,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,2CAA2C,EAAE;gBAC5F,GAAG,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,WAAW,EAAE,8DAA8D,EAAE;gBACrG,cAAc,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,WAAW,EAAE,0CAA0C,EAAE;aACtH;SACF;QAED,KAAK,CAAC,OAAO,CAAC,GAAW,EAAE,MAA+B;YACxD,MAAM,YAAY,GAAG,mBAAmB,CAAC,GAAG,CAAC,CAAC;YAC9C,MAAM,GAAG,GAAI,MAAM,CAAC,GAAe,IAAI,KAAK,CAAC;YAC7C,MAAM,cAAc,GAAI,MAAM,CAAC,cAA2B,IAAI,EAAE,CAAC;YAEjE,MAAM,OAAO,GAAG,MAAM,CAAC,cAAoC,CAAC;YAE5D,MAAM,IAAI,GAAG,MAAM,YAAY,CAAC,YAAY,CAAC,CAAC;YAC9C,MAAM,UAAU,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;YAEpE,MAAM,MAAM,GAAyD,EAAE,CAAC;YAExE,KAAK,MAAM,GAAG,IAAI,UAAU,EAAE,CAAC;gBAC7B,MAAM,OAAO,GAAG,UAAU,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;gBACtC,IAAI,CAAC,OAAO;oBAAE,SAAS;gBACvB,MAAM,EAAE,QAAQ,EAAE,GAAG,eAAe,CAAC,OAAO,CAAC,CAAC;gBAE9C,KAAK,MAAM,IAAI,IAAI,CAAC,KAAK,EAAE,IAAI,CAAU,EAAE,CAAC;oBAC1C,MAAM,KAAK,GAAG,MAAM,iBAAiB,CAAC;wBACpC,YAAY,EAAE,OAAO,EAAE,GAAG,EAAE,OAAO,EAAE,IAAI,EAAE,cAAc;wBACzD,OAAO,EAAE,GAAG,EAAE,QAAQ;qBACvB,CAAC,CAAC;oBACH,MAAM,CAAC,IAAI,CAAC,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;gBAC5E,CAAC;YACH,CAAC;YAED,MAAM,QAAQ,CAAC,YAAY,EAAE,QAAQ,EAAE;gBACrC,YAAY,EAAE,UAAU,CAAC,MAAM;gBAC/B,GAAG;gBACH,WAAW,EAAE,MAAM,CAAC,MAAM;gBAC1B,WAAW,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,MAAM;aAClD,CAAC,CAAC;YAEH,OAAO,UAAU,CAAC;gBAChB,OAAO,EAAE,IAAI;gBACb,GAAG;gBACH,eAAe,EAAE,UAAU,CAAC,MAAM;gBAClC,MAAM;gBACN,IAAI,EAAE,cAAc,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,wDAAwD,CAAC,CAAC,CAAC,SAAS;aACzG,CAAC,CAAC;QACL,CAAC;KACF,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,24 @@
1
+ /**
2
+ * onboard — Conversational DevClaw onboarding.
3
+ *
4
+ * Returns step-by-step guidance. Call this before setup.
5
+ */
6
+ import type { OpenClawPluginApi } from "openclaw/plugin-sdk";
7
+ import type { ToolContext } from "../types.js";
8
+ export declare function createOnboardTool(api: OpenClawPluginApi): (ctx: ToolContext) => {
9
+ name: string;
10
+ label: string;
11
+ description: string;
12
+ parameters: {
13
+ type: string;
14
+ properties: {
15
+ mode: {
16
+ type: string;
17
+ enum: string[];
18
+ description: string;
19
+ };
20
+ };
21
+ };
22
+ execute(_id: string, params: Record<string, unknown>): Promise<import("@mariozechner/pi-agent-core").AgentToolResult<unknown>>;
23
+ };
24
+ //# sourceMappingURL=onboard.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"onboard.d.ts","sourceRoot":"","sources":["../../../lib/tools/onboard.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAE7D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAG/C,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,iBAAiB,IAC9C,KAAK,WAAW;;;;;;;;;;;;;;iBAWH,MAAM,UAAU,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;EAc7D"}
@@ -0,0 +1,27 @@
1
+ import { jsonResult } from "openclaw/plugin-sdk";
2
+ import { isPluginConfigured, hasWorkspaceFiles, buildOnboardToolContext, buildReconfigContext } from "../onboarding.js";
3
+ export function createOnboardTool(api) {
4
+ return (ctx) => ({
5
+ name: "onboard",
6
+ label: "Onboard",
7
+ description: "Start DevClaw onboarding workflow. Returns step-by-step QA-style guidance. Call this first, then setup with collected answers.",
8
+ parameters: {
9
+ type: "object",
10
+ properties: {
11
+ mode: { type: "string", enum: ["first-run", "reconfigure"], description: "Auto-detected if omitted." },
12
+ },
13
+ },
14
+ async execute(_id, params) {
15
+ const configured = isPluginConfigured(api.pluginConfig);
16
+ const hasWorkspace = await hasWorkspaceFiles(ctx.workspaceDir);
17
+ const mode = params.mode ? params.mode
18
+ : configured && hasWorkspace ? "reconfigure" : "first-run";
19
+ const instructions = mode === "first-run" ? buildOnboardToolContext() : buildReconfigContext(api.pluginConfig);
20
+ return jsonResult({
21
+ success: true, mode, configured, instructions,
22
+ nextSteps: ["Follow instructions above", "Call setup with collected answers", mode === "first-run" ? "Register a project afterward" : null].filter(Boolean),
23
+ });
24
+ },
25
+ });
26
+ }
27
+ //# sourceMappingURL=onboard.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"onboard.js","sourceRoot":"","sources":["../../../lib/tools/onboard.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAEjD,OAAO,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,uBAAuB,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AAExH,MAAM,UAAU,iBAAiB,CAAC,GAAsB;IACtD,OAAO,CAAC,GAAgB,EAAE,EAAE,CAAC,CAAC;QAC5B,IAAI,EAAE,SAAS;QACf,KAAK,EAAE,SAAS;QAChB,WAAW,EAAE,gIAAgI;QAC7I,UAAU,EAAE;YACV,IAAI,EAAE,QAAQ;YACd,UAAU,EAAE;gBACV,IAAI,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,WAAW,EAAE,aAAa,CAAC,EAAE,WAAW,EAAE,2BAA2B,EAAE;aACvG;SACF;QAED,KAAK,CAAC,OAAO,CAAC,GAAW,EAAE,MAA+B;YACxD,MAAM,UAAU,GAAG,kBAAkB,CAAC,GAAG,CAAC,YAAuC,CAAC,CAAC;YACnF,MAAM,YAAY,GAAG,MAAM,iBAAiB,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC;YAC/D,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAE,MAAM,CAAC,IAAoC;gBACrE,CAAC,CAAC,UAAU,IAAI,YAAY,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,WAAW,CAAC;YAE7D,MAAM,YAAY,GAAG,IAAI,KAAK,WAAW,CAAC,CAAC,CAAC,uBAAuB,EAAE,CAAC,CAAC,CAAC,oBAAoB,CAAC,GAAG,CAAC,YAAuC,CAAC,CAAC;YAE1I,OAAO,UAAU,CAAC;gBAChB,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,UAAU,EAAE,YAAY;gBAC7C,SAAS,EAAE,CAAC,2BAA2B,EAAE,mCAAmC,EAAE,IAAI,KAAK,WAAW,CAAC,CAAC,CAAC,8BAA8B,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC;aAC5J,CAAC,CAAC;QACL,CAAC;KACF,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,51 @@
1
+ import type { ToolContext } from "../types.js";
2
+ export declare function createProjectRegisterTool(): (ctx: ToolContext) => {
3
+ name: string;
4
+ label: string;
5
+ description: string;
6
+ parameters: {
7
+ type: string;
8
+ required: string[];
9
+ properties: {
10
+ projectGroupId: {
11
+ type: string;
12
+ description: string;
13
+ };
14
+ name: {
15
+ type: string;
16
+ description: string;
17
+ };
18
+ repo: {
19
+ type: string;
20
+ description: string;
21
+ };
22
+ channel: {
23
+ type: string;
24
+ description: string;
25
+ };
26
+ groupName: {
27
+ type: string;
28
+ description: string;
29
+ };
30
+ baseBranch: {
31
+ type: string;
32
+ description: string;
33
+ };
34
+ deployBranch: {
35
+ type: string;
36
+ description: string;
37
+ };
38
+ deployUrl: {
39
+ type: string;
40
+ description: string;
41
+ };
42
+ roleExecution: {
43
+ type: string;
44
+ enum: string[];
45
+ description: string;
46
+ };
47
+ };
48
+ };
49
+ execute(_id: string, params: Record<string, unknown>): Promise<import("@mariozechner/pi-agent-core").AgentToolResult<unknown>>;
50
+ };
51
+ //# sourceMappingURL=project-register.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"project-register.d.ts","sourceRoot":"","sources":["../../../lib/tools/project-register.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAuC/C,wBAAgB,yBAAyB,KAC/B,KAAK,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAgDH,MAAM,UAAU,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;EA+F7D"}
@@ -0,0 +1,172 @@
1
+ /**
2
+ * project_register — Register a new project with DevClaw.
3
+ *
4
+ * Atomically: validates repo, detects GitHub/GitLab provider, creates all 8 state labels (idempotent),
5
+ * adds project entry to projects.json, and logs the event.
6
+ *
7
+ * Replaces the manual steps of running glab/gh label create + editing projects.json.
8
+ */
9
+ import { jsonResult } from "openclaw/plugin-sdk";
10
+ import fs from "node:fs/promises";
11
+ import path from "node:path";
12
+ import { readProjects, writeProjects, emptyWorkerState } from "../projects.js";
13
+ import { resolveRepoPath } from "../projects.js";
14
+ import { createProvider } from "../providers/index.js";
15
+ import { log as auditLog } from "../audit.js";
16
+ import { DEV_LEVELS, QA_LEVELS } from "../tiers.js";
17
+ import { DEFAULT_DEV_INSTRUCTIONS, DEFAULT_QA_INSTRUCTIONS } from "../templates.js";
18
+ /**
19
+ * Scaffold project-specific prompt files.
20
+ * Returns true if files were created, false if they already existed.
21
+ */
22
+ async function scaffoldPromptFiles(workspaceDir, projectName) {
23
+ const projectDir = path.join(workspaceDir, "projects", "roles", projectName);
24
+ await fs.mkdir(projectDir, { recursive: true });
25
+ const projectDev = path.join(projectDir, "dev.md");
26
+ const projectQa = path.join(projectDir, "qa.md");
27
+ let created = false;
28
+ try {
29
+ await fs.access(projectDev);
30
+ }
31
+ catch {
32
+ await fs.writeFile(projectDev, DEFAULT_DEV_INSTRUCTIONS, "utf-8");
33
+ created = true;
34
+ }
35
+ try {
36
+ await fs.access(projectQa);
37
+ }
38
+ catch {
39
+ await fs.writeFile(projectQa, DEFAULT_QA_INSTRUCTIONS, "utf-8");
40
+ created = true;
41
+ }
42
+ return created;
43
+ }
44
+ export function createProjectRegisterTool() {
45
+ return (ctx) => ({
46
+ name: "project_register",
47
+ label: "Project Register",
48
+ description: `Register a new project with DevClaw. Creates state labels, adds to projects.json. One-time setup per project.`,
49
+ parameters: {
50
+ type: "object",
51
+ required: ["projectGroupId", "name", "repo", "baseBranch"],
52
+ properties: {
53
+ projectGroupId: {
54
+ type: "string",
55
+ description: "Project group ID (e.g. Telegram/WhatsApp group ID)",
56
+ },
57
+ name: {
58
+ type: "string",
59
+ description: "Short project name (e.g. 'my-webapp')",
60
+ },
61
+ repo: {
62
+ type: "string",
63
+ description: "Path to git repo (e.g. '~/git/my-project')",
64
+ },
65
+ channel: {
66
+ type: "string",
67
+ description: "Channel type (e.g. 'telegram', 'whatsapp'). Defaults to 'telegram'.",
68
+ },
69
+ groupName: {
70
+ type: "string",
71
+ description: "Group display name (optional - defaults to 'Project: {name}')",
72
+ },
73
+ baseBranch: {
74
+ type: "string",
75
+ description: "Base branch for development (e.g. 'development', 'main')",
76
+ },
77
+ deployBranch: {
78
+ type: "string",
79
+ description: "Branch that triggers deployment. Defaults to baseBranch.",
80
+ },
81
+ deployUrl: {
82
+ type: "string",
83
+ description: "Deployment URL for the project",
84
+ },
85
+ roleExecution: {
86
+ type: "string",
87
+ enum: ["parallel", "sequential"],
88
+ description: "Project-level role execution mode: parallel (DEV and QA can work simultaneously) or sequential (only one role active at a time). Defaults to parallel.",
89
+ },
90
+ },
91
+ },
92
+ async execute(_id, params) {
93
+ const groupId = params.projectGroupId;
94
+ const name = params.name;
95
+ const repo = params.repo;
96
+ const channel = params.channel ?? "telegram";
97
+ const groupName = params.groupName ?? `Project: ${name}`;
98
+ const baseBranch = params.baseBranch;
99
+ const deployBranch = params.deployBranch ?? baseBranch;
100
+ const deployUrl = params.deployUrl ?? "";
101
+ const roleExecution = params.roleExecution ?? "parallel";
102
+ const workspaceDir = ctx.workspaceDir;
103
+ if (!workspaceDir) {
104
+ throw new Error("No workspace directory available in tool context");
105
+ }
106
+ // 1. Check project not already registered (allow re-register if incomplete)
107
+ const data = await readProjects(workspaceDir);
108
+ const existing = data.projects[groupId];
109
+ if (existing && existing.dev?.sessions && Object.keys(existing.dev.sessions).length > 0) {
110
+ throw new Error(`Project already registered for this group: "${existing.name}". Remove the existing entry first or use a different group.`);
111
+ }
112
+ // 2. Resolve repo path
113
+ const repoPath = resolveRepoPath(repo);
114
+ // 3. Create provider and verify it works
115
+ const { provider, type: providerType } = createProvider({ repo });
116
+ const healthy = await provider.healthCheck();
117
+ if (!healthy) {
118
+ const cliName = providerType === "github" ? "gh" : "glab";
119
+ const cliInstallUrl = providerType === "github"
120
+ ? "https://cli.github.com"
121
+ : "https://gitlab.com/gitlab-org/cli";
122
+ throw new Error(`${providerType.toUpperCase()} health check failed for ${repoPath}. ` +
123
+ `Detected provider: ${providerType}. ` +
124
+ `Ensure '${cliName}' CLI is installed, authenticated (${cliName} auth status), ` +
125
+ `and the repo has a ${providerType.toUpperCase()} remote. ` +
126
+ `Install ${cliName} from: ${cliInstallUrl}`);
127
+ }
128
+ // 4. Create all 8 state labels (idempotent)
129
+ await provider.ensureAllStateLabels();
130
+ // 5. Add project to projects.json
131
+ data.projects[groupId] = {
132
+ name,
133
+ repo,
134
+ groupName,
135
+ deployUrl,
136
+ baseBranch,
137
+ deployBranch,
138
+ channel,
139
+ roleExecution,
140
+ dev: emptyWorkerState([...DEV_LEVELS]),
141
+ qa: emptyWorkerState([...QA_LEVELS]),
142
+ };
143
+ await writeProjects(workspaceDir, data);
144
+ // 6. Scaffold prompt files
145
+ const promptsCreated = await scaffoldPromptFiles(workspaceDir, name);
146
+ // 7. Audit log
147
+ await auditLog(workspaceDir, "project_register", {
148
+ project: name,
149
+ groupId,
150
+ repo,
151
+ baseBranch,
152
+ deployBranch,
153
+ deployUrl: deployUrl || null,
154
+ });
155
+ // 8. Return announcement
156
+ const promptsNote = promptsCreated ? " Prompt files scaffolded." : "";
157
+ const announcement = `Project "${name}" registered for group ${groupName}. Labels created.${promptsNote} Ready for tasks.`;
158
+ return jsonResult({
159
+ success: true,
160
+ project: name,
161
+ groupId,
162
+ repo,
163
+ baseBranch,
164
+ deployBranch,
165
+ labelsCreated: 8,
166
+ promptsScaffolded: promptsCreated,
167
+ announcement,
168
+ });
169
+ },
170
+ });
171
+ }
172
+ //# sourceMappingURL=project-register.js.map