forked from laurentenhoor/devclaw
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathconfig.ts
More file actions
178 lines (159 loc) · 6.11 KB
/
config.ts
File metadata and controls
178 lines (159 loc) · 6.11 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
/**
* config — Config management tool for DevClaw workspaces.
*
* Subcommands:
* - reset: Reset config files to package defaults (with .bak backups)
* - diff: Show differences between current workflow.yaml and package default
* - version: Show current and workspace DevClaw versions
*/
import fs from "node:fs/promises";
import path from "node:path";
import { jsonResult } from "../../json-result.js";
import type { ToolContext } from "../../types.js";
import type { PluginContext } from "../../context.js";
import { writeAllDefaults, backupAndWrite, fileExists } from "../../setup/workspace.js";
import { WORKFLOW_YAML_TEMPLATE, DEFAULT_ROLE_INSTRUCTIONS } from "../../setup/templates.js";
import { DATA_DIR } from "../../setup/migrate-layout.js";
import { getCurrentVersion, readVersionFile } from "../../setup/version.js";
export function createConfigTool(ctx: PluginContext) {
return (toolCtx: ToolContext) => ({
name: "config",
label: "Config",
description: `Manage DevClaw workspace configuration.
Actions:
- **reset**: Reset config files to package defaults. Creates .bak backups of existing files.
Scope: --prompts (prompts only), --workflow (workflow.yaml only), --all (everything).
- **diff**: Show differences between current workflow.yaml and the package default template.
- **version**: Show DevClaw package version and workspace tracked version.
Examples:
config({ action: "reset", scope: "workflow" })
config({ action: "reset", scope: "all" })
config({ action: "diff" })
config({ action: "version" })`,
parameters: {
type: "object",
required: ["action"],
properties: {
action: {
type: "string",
enum: ["reset", "diff", "version"],
description: "Config action to perform.",
},
scope: {
type: "string",
enum: ["prompts", "workflow", "all"],
description: "Scope for reset action. Default: all.",
},
},
},
async execute(_id: string, params: Record<string, unknown>) {
const action = params.action as string;
const workspacePath = toolCtx.workspaceDir;
if (!workspacePath) throw new Error("No workspace directory available");
switch (action) {
case "reset":
return await handleReset(workspacePath, (params.scope as string) ?? "all");
case "diff":
return await handleDiff(workspacePath);
case "version":
return await handleVersion(workspacePath);
default:
throw new Error(`Unknown config action: ${action}`);
}
},
});
}
// ---------------------------------------------------------------------------
// Handlers
// ---------------------------------------------------------------------------
async function handleReset(workspacePath: string, scope: string) {
const dataDir = path.join(workspacePath, DATA_DIR);
const written: string[] = [];
if (scope === "all") {
const files = await writeAllDefaults(workspacePath, true);
written.push(...files);
} else if (scope === "workflow") {
const workflowPath = path.join(dataDir, "workflow.yaml");
await backupAndWrite(workflowPath, WORKFLOW_YAML_TEMPLATE);
written.push("devclaw/workflow.yaml");
} else if (scope === "prompts") {
const promptsDir = path.join(dataDir, "prompts");
for (const [role, content] of Object.entries(DEFAULT_ROLE_INSTRUCTIONS)) {
if (!content) continue;
const rolePath = path.join(promptsDir, `${role}.md`);
await backupAndWrite(rolePath, content);
written.push(`devclaw/prompts/${role}.md`);
}
} else {
throw new Error(`Unknown scope: ${scope}. Use: prompts, workflow, or all.`);
}
return jsonResult({
success: true,
action: "reset",
scope,
filesWritten: written,
summary: written.length > 0
? `Reset ${written.length} file(s) to package defaults (.bak backups created):\n${written.map(f => ` ${f}`).join("\n")}`
: "No files to reset.",
});
}
async function handleDiff(workspacePath: string) {
const workflowPath = path.join(workspacePath, DATA_DIR, "workflow.yaml");
if (!await fileExists(workflowPath)) {
return jsonResult({
success: true,
action: "diff",
summary: "No workflow.yaml found in workspace — using package defaults.",
});
}
const current = await fs.readFile(workflowPath, "utf-8");
const template = WORKFLOW_YAML_TEMPLATE;
if (current.trim() === template.trim()) {
return jsonResult({
success: true,
action: "diff",
summary: "workflow.yaml matches the package default — no differences.",
});
}
// Simple line-by-line diff
const currentLines = current.split("\n");
const templateLines = template.split("\n");
const diffs: string[] = [];
const maxLen = Math.max(currentLines.length, templateLines.length);
for (let i = 0; i < maxLen; i++) {
const cl = currentLines[i] ?? "";
const tl = templateLines[i] ?? "";
if (cl !== tl) {
if (tl && !cl) diffs.push(`+${i + 1}: ${tl}`);
else if (cl && !tl) diffs.push(`-${i + 1}: ${cl}`);
else {
diffs.push(`-${i + 1}: ${cl}`);
diffs.push(`+${i + 1}: ${tl}`);
}
}
}
return jsonResult({
success: true,
action: "diff",
differences: diffs.length,
summary: `workflow.yaml differs from package default (${diffs.length} line(s)):\n\`\`\`diff\n${diffs.join("\n")}\n\`\`\`\n\nUse \`config({ action: "reset", scope: "workflow" })\` to reset to defaults.`,
});
}
async function handleVersion(workspacePath: string) {
const packageVersion = getCurrentVersion();
const dataDir = path.join(workspacePath, DATA_DIR);
const workspaceVersion = await readVersionFile(dataDir);
const match = workspaceVersion === packageVersion;
return jsonResult({
success: true,
action: "version",
packageVersion,
workspaceVersion: workspaceVersion ?? "(not tracked)",
match,
summary: match
? `DevClaw v${packageVersion} — workspace up to date.`
: workspaceVersion
? `DevClaw v${packageVersion} (workspace tracked: v${workspaceVersion}) — version mismatch.`
: `DevClaw v${packageVersion} — workspace version not yet tracked.`,
});
}