workermill 0.2.0 → 0.3.1
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/README.md +10 -0
- package/dist/{chunk-VC6VNVEY.js → chunk-NGQKIYVB.js} +39 -1
- package/dist/index.js +126 -68
- package/dist/{orchestrator-5I7BGPC7.js → orchestrator-2M4BCHQR.js} +6 -36
- package/package.json +1 -1
- package/personas/architect.md +51 -0
- package/personas/backend_developer.md +51 -0
- package/personas/critic.md +65 -16
- package/personas/data_ml_engineer.md +51 -0
- package/personas/devops_engineer.md +51 -0
- package/personas/frontend_developer.md +51 -0
- package/personas/mobile_developer.md +51 -0
- package/personas/planner.md +105 -16
- package/personas/qa_engineer.md +51 -0
- package/personas/security_engineer.md +51 -0
- package/personas/tech_lead.md +120 -25
- package/personas/tech_writer.md +51 -0
package/README.md
CHANGED
|
@@ -123,6 +123,16 @@ Config stored at `~/.workermill/cli.json` (global) and `.workermill/config.json`
|
|
|
123
123
|
|
|
124
124
|
backend_developer, frontend_developer, devops_engineer, qa_engineer, security_engineer, data_ml_engineer, mobile_developer, tech_writer, architect, tech_lead, planner, critic
|
|
125
125
|
|
|
126
|
+
All worker personas include production-hardened rules:
|
|
127
|
+
- **Real services, not mocks** — Docker containers for databases, caches, queues. Tests run against real services.
|
|
128
|
+
- **Version trust** — Never downgrades language/runtime versions (training data is outdated)
|
|
129
|
+
- **Learning markers** — Reports codebase discoveries with `::learning::` markers for team visibility
|
|
130
|
+
- **Right-sized plans** — Planner matches plan complexity to task complexity (1 step for simple, 3-5 for complex)
|
|
131
|
+
- **Approval bias** — Tech lead only blocks on real functional/security issues, not cosmetic preferences
|
|
132
|
+
- **File overlap detection** — Critic catches parallel merge conflicts before they happen
|
|
133
|
+
|
|
134
|
+
Custom personas can be added per-project in `.workermill/personas/` or globally in `~/.workermill/personas/`.
|
|
135
|
+
|
|
126
136
|
## Requirements
|
|
127
137
|
|
|
128
138
|
- Node.js 20+
|
|
@@ -1639,6 +1639,41 @@ var CostTracker = class {
|
|
|
1639
1639
|
}
|
|
1640
1640
|
};
|
|
1641
1641
|
|
|
1642
|
+
// src/logger.js
|
|
1643
|
+
import fs9 from "fs";
|
|
1644
|
+
import path11 from "path";
|
|
1645
|
+
var LOG_DIR = path11.join(process.cwd(), ".workermill");
|
|
1646
|
+
var LOG_FILE = path11.join(LOG_DIR, "cli.log");
|
|
1647
|
+
var logStream = null;
|
|
1648
|
+
function ensureLogDir() {
|
|
1649
|
+
if (!fs9.existsSync(LOG_DIR)) {
|
|
1650
|
+
fs9.mkdirSync(LOG_DIR, { recursive: true });
|
|
1651
|
+
}
|
|
1652
|
+
}
|
|
1653
|
+
function getStream() {
|
|
1654
|
+
if (!logStream) {
|
|
1655
|
+
ensureLogDir();
|
|
1656
|
+
logStream = fs9.createWriteStream(LOG_FILE, { flags: "a" });
|
|
1657
|
+
}
|
|
1658
|
+
return logStream;
|
|
1659
|
+
}
|
|
1660
|
+
function timestamp() {
|
|
1661
|
+
return (/* @__PURE__ */ new Date()).toISOString();
|
|
1662
|
+
}
|
|
1663
|
+
function log(level, message, data) {
|
|
1664
|
+
const entry = data ? `[${timestamp()}] ${level}: ${message} ${JSON.stringify(data)}` : `[${timestamp()}] ${level}: ${message}`;
|
|
1665
|
+
getStream().write(entry + "\n");
|
|
1666
|
+
}
|
|
1667
|
+
function info(message, data) {
|
|
1668
|
+
log("INFO", message, data);
|
|
1669
|
+
}
|
|
1670
|
+
function error(message, data) {
|
|
1671
|
+
log("ERROR", message, data);
|
|
1672
|
+
}
|
|
1673
|
+
function debug(message, data) {
|
|
1674
|
+
log("DEBUG", message, data);
|
|
1675
|
+
}
|
|
1676
|
+
|
|
1642
1677
|
export {
|
|
1643
1678
|
__dirname,
|
|
1644
1679
|
loadConfig,
|
|
@@ -1648,5 +1683,8 @@ export {
|
|
|
1648
1683
|
createModel,
|
|
1649
1684
|
killActiveProcess,
|
|
1650
1685
|
createToolDefinitions,
|
|
1651
|
-
CostTracker
|
|
1686
|
+
CostTracker,
|
|
1687
|
+
info,
|
|
1688
|
+
error,
|
|
1689
|
+
debug
|
|
1652
1690
|
};
|
package/dist/index.js
CHANGED
|
@@ -4,15 +4,19 @@ import {
|
|
|
4
4
|
buildOllamaOptions,
|
|
5
5
|
createModel,
|
|
6
6
|
createToolDefinitions,
|
|
7
|
+
debug,
|
|
8
|
+
error,
|
|
7
9
|
getProviderForPersona,
|
|
10
|
+
info,
|
|
8
11
|
killActiveProcess,
|
|
9
12
|
loadConfig,
|
|
10
13
|
saveConfig
|
|
11
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-NGQKIYVB.js";
|
|
12
15
|
|
|
13
16
|
// src/index.ts
|
|
14
17
|
import React5 from "react";
|
|
15
18
|
import { render } from "ink";
|
|
19
|
+
import chalk2 from "chalk";
|
|
16
20
|
import { Command } from "commander";
|
|
17
21
|
|
|
18
22
|
// src/setup.js
|
|
@@ -119,7 +123,7 @@ async function runSetup() {
|
|
|
119
123
|
}
|
|
120
124
|
|
|
121
125
|
// src/ui/Root.tsx
|
|
122
|
-
import { useState as useState5, useCallback as useCallback3, useRef as useRef3 } from "react";
|
|
126
|
+
import { useState as useState5, useCallback as useCallback3, useRef as useRef3, useEffect as useEffect2 } from "react";
|
|
123
127
|
import { useApp as useApp2 } from "ink";
|
|
124
128
|
import { execSync as execSync2 } from "child_process";
|
|
125
129
|
import fs2 from "fs";
|
|
@@ -344,7 +348,7 @@ function useAgent(options) {
|
|
|
344
348
|
const envMap = {
|
|
345
349
|
anthropic: "ANTHROPIC_API_KEY",
|
|
346
350
|
openai: "OPENAI_API_KEY",
|
|
347
|
-
google: "
|
|
351
|
+
google: "GOOGLE_GENERATIVE_AI_API_KEY"
|
|
348
352
|
};
|
|
349
353
|
const envVar = envMap[options.provider];
|
|
350
354
|
if (envVar && !process.env[envVar]) {
|
|
@@ -447,20 +451,20 @@ function useAgent(options) {
|
|
|
447
451
|
...td,
|
|
448
452
|
execute: async (input) => {
|
|
449
453
|
const callId = crypto2.randomUUID();
|
|
450
|
-
const
|
|
454
|
+
const info2 = {
|
|
451
455
|
id: callId,
|
|
452
456
|
name,
|
|
453
457
|
input,
|
|
454
458
|
status: "pending"
|
|
455
459
|
};
|
|
456
|
-
setStreamingToolCalls((prev) => [...prev,
|
|
460
|
+
setStreamingToolCalls((prev) => [...prev, info2]);
|
|
457
461
|
setMessages((prev) => [
|
|
458
462
|
...prev,
|
|
459
463
|
{
|
|
460
464
|
id: `tc-${callId}`,
|
|
461
465
|
role: "assistant",
|
|
462
466
|
content: "",
|
|
463
|
-
toolCalls: [{ ...
|
|
467
|
+
toolCalls: [{ ...info2, status: "running" }],
|
|
464
468
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
465
469
|
}
|
|
466
470
|
]);
|
|
@@ -487,8 +491,10 @@ function useAgent(options) {
|
|
|
487
491
|
);
|
|
488
492
|
setStatus("tool_running");
|
|
489
493
|
try {
|
|
494
|
+
info("Tool call", { tool: name, input: JSON.stringify(input).slice(0, 200) });
|
|
490
495
|
const result = await td.execute(input);
|
|
491
496
|
const resultStr = typeof result === "string" ? result : JSON.stringify(result);
|
|
497
|
+
debug("Tool result", { tool: name, result: resultStr.slice(0, 200) });
|
|
492
498
|
setStreamingToolCalls(
|
|
493
499
|
(prev) => prev.map(
|
|
494
500
|
(tc) => tc.id === callId ? { ...tc, status: "done", result: resultStr } : tc
|
|
@@ -531,6 +537,7 @@ function useAgent(options) {
|
|
|
531
537
|
void (async () => {
|
|
532
538
|
const session = sessionRef.current;
|
|
533
539
|
addMessage(session, "user", input);
|
|
540
|
+
info("User message", { length: input.length, preview: input.slice(0, 100) });
|
|
534
541
|
if (!session.name) {
|
|
535
542
|
session.name = input.slice(0, 50).replace(/\n/g, " ");
|
|
536
543
|
}
|
|
@@ -591,6 +598,7 @@ function useAgent(options) {
|
|
|
591
598
|
setStreamingText("");
|
|
592
599
|
addMessage(session, "assistant", finalText);
|
|
593
600
|
session.totalTokens += inputTokens + outputTokens;
|
|
601
|
+
info("Response complete", { inputTokens, outputTokens, textLength: finalText.length });
|
|
594
602
|
costTrackerRef.current.addUsage(
|
|
595
603
|
"agent",
|
|
596
604
|
options.provider,
|
|
@@ -634,6 +642,7 @@ function useAgent(options) {
|
|
|
634
642
|
return;
|
|
635
643
|
}
|
|
636
644
|
const errText = err instanceof Error ? err.message : String(err);
|
|
645
|
+
error("Agent error", { error: errText });
|
|
637
646
|
const errorMsg = {
|
|
638
647
|
id: crypto2.randomUUID(),
|
|
639
648
|
role: "assistant",
|
|
@@ -772,7 +781,7 @@ function useOrchestrator(addMessage2) {
|
|
|
772
781
|
setRunning(false);
|
|
773
782
|
return;
|
|
774
783
|
}
|
|
775
|
-
const { classifyComplexity, runOrchestration } = await import("./orchestrator-
|
|
784
|
+
const { classifyComplexity, runOrchestration } = await import("./orchestrator-2M4BCHQR.js");
|
|
776
785
|
const output = {
|
|
777
786
|
log(persona, message) {
|
|
778
787
|
const emoji = getEmoji(persona);
|
|
@@ -1469,6 +1478,10 @@ function App(props) {
|
|
|
1469
1478
|
const lastCtrlCRef = useRef2(0);
|
|
1470
1479
|
const width = stdout?.columns || 80;
|
|
1471
1480
|
useInput3((input, key) => {
|
|
1481
|
+
if (key.escape && props.status !== "idle") {
|
|
1482
|
+
props.onCancel();
|
|
1483
|
+
return;
|
|
1484
|
+
}
|
|
1472
1485
|
if (key.ctrl && input === "c") {
|
|
1473
1486
|
const now = Date.now();
|
|
1474
1487
|
if (props.status === "idle" && now - lastCtrlCRef.current < 500) {
|
|
@@ -1479,43 +1492,7 @@ function App(props) {
|
|
|
1479
1492
|
}
|
|
1480
1493
|
});
|
|
1481
1494
|
const mode = props.planMode ? "PLAN" : props.trustAll ? "trust all" : "ask";
|
|
1482
|
-
const headerInner = Math.min(width - 4, 50);
|
|
1483
1495
|
return /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", width: "100%", children: [
|
|
1484
|
-
/* @__PURE__ */ jsx6(Static, { items: [{ id: "__header__" }], children: () => /* @__PURE__ */ jsxs6(
|
|
1485
|
-
Box6,
|
|
1486
|
-
{
|
|
1487
|
-
flexDirection: "column",
|
|
1488
|
-
borderStyle: "round",
|
|
1489
|
-
borderColor: theme.subtleDark,
|
|
1490
|
-
paddingX: 1,
|
|
1491
|
-
width: headerInner,
|
|
1492
|
-
children: [
|
|
1493
|
-
/* @__PURE__ */ jsxs6(Box6, { children: [
|
|
1494
|
-
/* @__PURE__ */ jsx6(Text6, { color: theme.brand, children: "\u25C6 " }),
|
|
1495
|
-
/* @__PURE__ */ jsx6(Text6, { color: theme.text, bold: true, children: "WorkerMill" })
|
|
1496
|
-
] }),
|
|
1497
|
-
/* @__PURE__ */ jsx6(Text6, { children: " " }),
|
|
1498
|
-
/* @__PURE__ */ jsxs6(Text6, { color: theme.subtle, children: [
|
|
1499
|
-
" ",
|
|
1500
|
-
props.provider,
|
|
1501
|
-
"/",
|
|
1502
|
-
props.model
|
|
1503
|
-
] }),
|
|
1504
|
-
/* @__PURE__ */ jsxs6(Text6, { color: theme.subtle, children: [
|
|
1505
|
-
" ",
|
|
1506
|
-
"cwd: ",
|
|
1507
|
-
props.workingDir
|
|
1508
|
-
] }),
|
|
1509
|
-
/* @__PURE__ */ jsxs6(Text6, { color: theme.subtle, children: [
|
|
1510
|
-
" ",
|
|
1511
|
-
"Type ",
|
|
1512
|
-
/* @__PURE__ */ jsx6(Text6, { color: theme.text, children: "/help" }),
|
|
1513
|
-
" for commands"
|
|
1514
|
-
] })
|
|
1515
|
-
]
|
|
1516
|
-
},
|
|
1517
|
-
"__header__"
|
|
1518
|
-
) }),
|
|
1519
1496
|
/* @__PURE__ */ jsx6(Static, { items: props.messages, children: (message) => /* @__PURE__ */ jsx6(Box6, { flexDirection: "column", marginTop: 1, children: message.role === "user" ? /* @__PURE__ */ jsxs6(Box6, { marginLeft: 1, children: [
|
|
1520
1497
|
/* @__PURE__ */ jsx6(Text6, { color: theme.brand, bold: true, children: "\u2771 " }),
|
|
1521
1498
|
/* @__PURE__ */ jsx6(Text6, { color: theme.text, children: message.content })
|
|
@@ -1616,34 +1593,37 @@ function getGitStatus(cwd) {
|
|
|
1616
1593
|
${status}
|
|
1617
1594
|
\`\`\``;
|
|
1618
1595
|
}
|
|
1619
|
-
var HELP_TEXT = `**WorkerMill
|
|
1596
|
+
var HELP_TEXT = `**WorkerMill** \u2014 AI coding agent for your terminal.
|
|
1620
1597
|
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
| \`/cost\` | Show session cost breakdown |
|
|
1626
|
-
| \`/status\` | Show session status |
|
|
1627
|
-
| \`/plan\` | Toggle plan mode (read-only tools) |
|
|
1628
|
-
| \`/trust\` | Trust all tool calls for this session |
|
|
1629
|
-
| \`/build <task>\` | Multi-expert orchestration |
|
|
1630
|
-
| \`/git\` | Show git branch and status |
|
|
1631
|
-
| \`/sessions\` | List recent sessions |
|
|
1632
|
-
| \`/editor\` | Open \\$EDITOR, submit contents |
|
|
1633
|
-
| \`/compact\` | Trigger context compaction |
|
|
1634
|
-
| \`/clear\` | Clear screen (limited in Ink) |
|
|
1635
|
-
| \`/quit\` | Exit WorkerMill |
|
|
1636
|
-
| \`/exit\` | Exit WorkerMill |
|
|
1598
|
+
**Two ways to work:**
|
|
1599
|
+
|
|
1600
|
+
**Chat** \u2014 Ask anything. I'll read files, write code, run commands.
|
|
1601
|
+
Just type your question or task and press Enter.
|
|
1637
1602
|
|
|
1638
|
-
**
|
|
1603
|
+
**Build** \u2014 Create software with multiple specialist AI agents.
|
|
1604
|
+
Type \`/build <description>\` and I'll plan stories, assign experts
|
|
1605
|
+
(backend, frontend, devops, security), execute, and review.
|
|
1639
1606
|
|
|
1640
|
-
|
|
1641
|
-
- \`Ctrl+C\` -- Cancel current operation
|
|
1642
|
-
- \`Ctrl+C Ctrl+C\` -- Exit when idle
|
|
1607
|
+
Or from the command line: \`wm build "your task"\`
|
|
1643
1608
|
|
|
1644
|
-
|
|
1609
|
+
---
|
|
1645
1610
|
|
|
1646
|
-
|
|
1611
|
+
**Commands**
|
|
1612
|
+
|
|
1613
|
+
| Command | Description |
|
|
1614
|
+
|---|---|
|
|
1615
|
+
| \`/build <task>\` | Multi-expert orchestration \u2014 the main feature |
|
|
1616
|
+
| \`/plan\` | Toggle plan mode (read-only, explore before committing) |
|
|
1617
|
+
| \`/trust\` | Auto-approve all tool calls for this session |
|
|
1618
|
+
| \`/model\` | Show current provider and model |
|
|
1619
|
+
| \`/cost\` | Session cost and token usage |
|
|
1620
|
+
| \`/status\` | Session info |
|
|
1621
|
+
| \`/git\` | Git branch and status |
|
|
1622
|
+
| \`/sessions\` | List/switch sessions |
|
|
1623
|
+
| \`/editor\` | Open \\$EDITOR for longer input |
|
|
1624
|
+
| \`/quit\` | Exit |
|
|
1625
|
+
|
|
1626
|
+
**Shortcuts:** \`!command\` runs shell directly, \`ESC\` cancels, \`Ctrl+C Ctrl+C\` exits.`;
|
|
1647
1627
|
function Root(props) {
|
|
1648
1628
|
const { exit } = useApp2();
|
|
1649
1629
|
const agent = useAgent(props);
|
|
@@ -1658,6 +1638,14 @@ function Root(props) {
|
|
|
1658
1638
|
[agent]
|
|
1659
1639
|
);
|
|
1660
1640
|
const orchestrator = useOrchestrator(addOrchestratorMessage);
|
|
1641
|
+
const buildStarted = useRef3(false);
|
|
1642
|
+
useEffect2(() => {
|
|
1643
|
+
if (props.initialBuildTask && !buildStarted.current) {
|
|
1644
|
+
buildStarted.current = true;
|
|
1645
|
+
agent.addUserMessage(`/build ${props.initialBuildTask}`);
|
|
1646
|
+
orchestrator.start(props.initialBuildTask, props.trustAll, props.sandboxed);
|
|
1647
|
+
}
|
|
1648
|
+
}, [props.initialBuildTask, props.trustAll, props.sandboxed, agent, orchestrator]);
|
|
1661
1649
|
const [inputHistory, setInputHistory] = useState5(() => loadHistory());
|
|
1662
1650
|
const [gitBranch, setGitBranch] = useState5(() => getGitBranch());
|
|
1663
1651
|
const lastBranchCheck = useRef3(Date.now());
|
|
@@ -1938,6 +1926,7 @@ ${trimmedOutput}
|
|
|
1938
1926
|
trustAll: props.trustAll,
|
|
1939
1927
|
planMode: props.planMode,
|
|
1940
1928
|
onSubmit: handleSubmit,
|
|
1929
|
+
onCancel: agent.cancel,
|
|
1941
1930
|
messages: agent.messages,
|
|
1942
1931
|
status: orchestrator.running ? "tool_running" : agent.status,
|
|
1943
1932
|
permissionRequest: agent.permissionRequest,
|
|
@@ -1952,8 +1941,25 @@ ${trimmedOutput}
|
|
|
1952
1941
|
}
|
|
1953
1942
|
|
|
1954
1943
|
// src/index.ts
|
|
1955
|
-
|
|
1956
|
-
|
|
1944
|
+
function printWelcome(provider, model, workingDir) {
|
|
1945
|
+
const brand = chalk2.hex("#D77757");
|
|
1946
|
+
const dim = chalk2.dim;
|
|
1947
|
+
const white = chalk2.white;
|
|
1948
|
+
console.log();
|
|
1949
|
+
console.log(` ${brand("\u25C6")} ${white.bold("WorkerMill")} ${dim("v" + VERSION)}`);
|
|
1950
|
+
console.log();
|
|
1951
|
+
console.log(dim(` ${provider}/${model}`));
|
|
1952
|
+
console.log(dim(` cwd: ${workingDir}`));
|
|
1953
|
+
console.log();
|
|
1954
|
+
console.log(dim(" Ask me anything, or use ") + brand("/build") + dim(" to create software with multi-expert AI."));
|
|
1955
|
+
console.log(dim(" Type ") + white("/help") + dim(" for all commands."));
|
|
1956
|
+
console.log();
|
|
1957
|
+
}
|
|
1958
|
+
var VERSION = "0.3.1";
|
|
1959
|
+
function addSharedOptions(cmd) {
|
|
1960
|
+
return cmd.option("--provider <provider>", "Override default provider").option("--model <model>", "Override model").option("--trust", "Skip all tool permission prompts").option("--full-disk", "Allow tools to access files outside working directory");
|
|
1961
|
+
}
|
|
1962
|
+
async function resolveConfig(options) {
|
|
1957
1963
|
let config = loadConfig();
|
|
1958
1964
|
if (!config) {
|
|
1959
1965
|
config = await runSetup();
|
|
@@ -1967,8 +1973,14 @@ var program = new Command().name("workermill").description("AI coding agent with
|
|
|
1967
1973
|
providerConfig.model = options.model;
|
|
1968
1974
|
}
|
|
1969
1975
|
}
|
|
1976
|
+
return config;
|
|
1977
|
+
}
|
|
1978
|
+
var program = new Command().name("wm").description("WorkerMill \u2014 AI coding agent for your terminal").version(VERSION);
|
|
1979
|
+
var defaultCmd = program.command("chat", { isDefault: true }).description("Interactive AI coding agent (default)").option("--resume", "Resume the last conversation").option("--plan", "Start in plan mode (read-only tools)").action(async (options) => {
|
|
1980
|
+
const config = await resolveConfig(options);
|
|
1970
1981
|
const { provider, model, apiKey, host, contextLength } = getProviderForPersona(config);
|
|
1971
1982
|
const workingDir = process.cwd();
|
|
1983
|
+
printWelcome(provider, model, workingDir);
|
|
1972
1984
|
const { waitUntilExit } = render(
|
|
1973
1985
|
React5.createElement(Root, {
|
|
1974
1986
|
provider,
|
|
@@ -1985,4 +1997,50 @@ var program = new Command().name("workermill").description("AI coding agent with
|
|
|
1985
1997
|
);
|
|
1986
1998
|
await waitUntilExit();
|
|
1987
1999
|
});
|
|
2000
|
+
addSharedOptions(defaultCmd);
|
|
2001
|
+
var buildCmd = program.command("build [task...]").description("Build software with multi-expert orchestration").option("--critic", "Run critic pass on plan before execution").action(async (taskParts, options) => {
|
|
2002
|
+
const task = taskParts.join(" ");
|
|
2003
|
+
if (!task) {
|
|
2004
|
+
console.log('\n Usage: wm build "<task description>"\n');
|
|
2005
|
+
console.log(" Example:");
|
|
2006
|
+
console.log(' wm build "REST API with auth, tests, and Docker"');
|
|
2007
|
+
console.log(' wm build "Add search feature to the React frontend"\n');
|
|
2008
|
+
process.exit(0);
|
|
2009
|
+
}
|
|
2010
|
+
const config = await resolveConfig(options);
|
|
2011
|
+
if (options.critic) {
|
|
2012
|
+
config.review = { ...config.review, useCritic: true };
|
|
2013
|
+
}
|
|
2014
|
+
const { provider, model, apiKey, host, contextLength } = getProviderForPersona(config);
|
|
2015
|
+
const trustAll = options.trust || false;
|
|
2016
|
+
const sandboxed = !options.fullDisk;
|
|
2017
|
+
if (apiKey) {
|
|
2018
|
+
const envMap = {
|
|
2019
|
+
anthropic: "ANTHROPIC_API_KEY",
|
|
2020
|
+
openai: "OPENAI_API_KEY",
|
|
2021
|
+
google: "GOOGLE_GENERATIVE_AI_API_KEY"
|
|
2022
|
+
};
|
|
2023
|
+
const envVar = envMap[provider];
|
|
2024
|
+
if (envVar && !process.env[envVar]) {
|
|
2025
|
+
process.env[envVar] = apiKey;
|
|
2026
|
+
}
|
|
2027
|
+
}
|
|
2028
|
+
const { waitUntilExit } = render(
|
|
2029
|
+
React5.createElement(Root, {
|
|
2030
|
+
provider,
|
|
2031
|
+
model,
|
|
2032
|
+
apiKey,
|
|
2033
|
+
host,
|
|
2034
|
+
contextLength,
|
|
2035
|
+
trustAll,
|
|
2036
|
+
planMode: false,
|
|
2037
|
+
sandboxed,
|
|
2038
|
+
resume: false,
|
|
2039
|
+
workingDir: process.cwd(),
|
|
2040
|
+
initialBuildTask: task
|
|
2041
|
+
})
|
|
2042
|
+
);
|
|
2043
|
+
await waitUntilExit();
|
|
2044
|
+
});
|
|
2045
|
+
addSharedOptions(buildCmd);
|
|
1988
2046
|
program.parse();
|
|
@@ -4,8 +4,9 @@ import {
|
|
|
4
4
|
buildOllamaOptions,
|
|
5
5
|
createModel,
|
|
6
6
|
createToolDefinitions,
|
|
7
|
-
getProviderForPersona
|
|
8
|
-
|
|
7
|
+
getProviderForPersona,
|
|
8
|
+
info
|
|
9
|
+
} from "./chunk-NGQKIYVB.js";
|
|
9
10
|
|
|
10
11
|
// src/orchestrator.js
|
|
11
12
|
import chalk3 from "chalk";
|
|
@@ -224,37 +225,6 @@ var PermissionManager = class {
|
|
|
224
225
|
// src/tui.js
|
|
225
226
|
import chalk2 from "chalk";
|
|
226
227
|
import { execSync } from "child_process";
|
|
227
|
-
|
|
228
|
-
// src/logger.js
|
|
229
|
-
import fs2 from "fs";
|
|
230
|
-
import path2 from "path";
|
|
231
|
-
var LOG_DIR = path2.join(process.cwd(), ".workermill");
|
|
232
|
-
var LOG_FILE = path2.join(LOG_DIR, "cli.log");
|
|
233
|
-
var logStream = null;
|
|
234
|
-
function ensureLogDir() {
|
|
235
|
-
if (!fs2.existsSync(LOG_DIR)) {
|
|
236
|
-
fs2.mkdirSync(LOG_DIR, { recursive: true });
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
|
-
function getStream() {
|
|
240
|
-
if (!logStream) {
|
|
241
|
-
ensureLogDir();
|
|
242
|
-
logStream = fs2.createWriteStream(LOG_FILE, { flags: "a" });
|
|
243
|
-
}
|
|
244
|
-
return logStream;
|
|
245
|
-
}
|
|
246
|
-
function timestamp() {
|
|
247
|
-
return (/* @__PURE__ */ new Date()).toISOString();
|
|
248
|
-
}
|
|
249
|
-
function log(level, message, data) {
|
|
250
|
-
const entry = data ? `[${timestamp()}] ${level}: ${message} ${JSON.stringify(data)}` : `[${timestamp()}] ${level}: ${message}`;
|
|
251
|
-
getStream().write(entry + "\n");
|
|
252
|
-
}
|
|
253
|
-
function info(message, data) {
|
|
254
|
-
log("INFO", message, data);
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
// src/tui.js
|
|
258
228
|
function formatToolCall(toolName, toolInput) {
|
|
259
229
|
let msg = `Tool: ${toolName}`;
|
|
260
230
|
if (toolInput) {
|
|
@@ -1253,10 +1223,10 @@ ${story.description}`,
|
|
|
1253
1223
|
} catch {
|
|
1254
1224
|
wmCoordinatorLog("Initializing git repository...");
|
|
1255
1225
|
execSync2("git init", { cwd: workingDir, encoding: "utf-8", stdio: "pipe" });
|
|
1256
|
-
const
|
|
1226
|
+
const fs2 = await import("fs");
|
|
1257
1227
|
const gitignorePath = `${workingDir}/.gitignore`;
|
|
1258
|
-
if (!
|
|
1259
|
-
|
|
1228
|
+
if (!fs2.existsSync(gitignorePath)) {
|
|
1229
|
+
fs2.writeFileSync(gitignorePath, "node_modules/\ndist/\n.env\n.workermill/\n*.log\n", "utf-8");
|
|
1260
1230
|
}
|
|
1261
1231
|
wmCoordinatorLog("Git repo initialized");
|
|
1262
1232
|
}
|
package/package.json
CHANGED
package/personas/architect.md
CHANGED
|
@@ -27,3 +27,54 @@ Work Style:
|
|
|
27
27
|
- Create architecture decision records for major choices
|
|
28
28
|
- Consider scalability, maintainability, and team velocity
|
|
29
29
|
- Document architectural patterns and rationale
|
|
30
|
+
|
|
31
|
+
## ABSOLUTE RULE — NEVER Flag Language/Runtime Versions as Invalid
|
|
32
|
+
|
|
33
|
+
Your training data is OUTDATED — newer language and runtime versions have been released since your cutoff. NEVER suggest downgrading a language version. NEVER say a version "doesn't exist" or is "non-existent." If the project uses a version you don't recognize, it is correct — the project authors chose it deliberately.
|
|
34
|
+
|
|
35
|
+
## Development Environment
|
|
36
|
+
|
|
37
|
+
You have `docker` and `docker compose` available. **You MUST spin up real service dependencies** (databases, caches, message queues) using Docker containers before writing any application code that depends on them. Do NOT mock or stub external services — connect to real instances running in Docker.
|
|
38
|
+
|
|
39
|
+
### Required Workflow
|
|
40
|
+
1. **Before writing application code**: Start all required service containers
|
|
41
|
+
2. **Configure your code** to connect to `localhost` on the container ports
|
|
42
|
+
3. **Run tests against real services** — integration tests must hit real databases, not mocks
|
|
43
|
+
4. **Clean up containers** when done (`docker stop <name>`)
|
|
44
|
+
|
|
45
|
+
### Common Services
|
|
46
|
+
- MongoDB: `docker run -d --rm -p 27017:27017 --name mongo-test mongo:7`
|
|
47
|
+
- Redis: `docker run -d --rm -p 6379:6379 --name redis-test redis:7-alpine`
|
|
48
|
+
- PostgreSQL: `docker run -d --rm -p 5432:5432 -e POSTGRES_PASSWORD=test --name postgres-test postgres:16-alpine`
|
|
49
|
+
- MySQL: `docker run -d --rm -p 3306:3306 -e MYSQL_ROOT_PASSWORD=test --name mysql-test mysql:8`
|
|
50
|
+
- If the project has a `docker-compose.yml`, use `docker compose up -d`
|
|
51
|
+
|
|
52
|
+
### Why This Matters
|
|
53
|
+
Mocking produces code full of assumptions that break on first contact with real services. Real containers catch connection strings, schema mismatches, query errors, and serialization bugs immediately. **Tests that pass against mocks but fail against real services are worthless.**
|
|
54
|
+
|
|
55
|
+
### If Docker Is Not Working
|
|
56
|
+
If `docker` commands fail, DO NOT fall back to mocking. Report the Docker error as a blocker. Never write test stubs or mock implementations as a workaround.
|
|
57
|
+
|
|
58
|
+
### CI/CD Workflows Must Include Service Containers
|
|
59
|
+
When creating GitHub Actions CI workflows that run tests requiring databases, you **MUST** add `services:` blocks so the CI runner has real service instances. Match your local Docker setup with CI service containers.
|
|
60
|
+
|
|
61
|
+
## Reporting Learnings
|
|
62
|
+
|
|
63
|
+
When you discover something specific and actionable about this codebase, emit a learning marker:
|
|
64
|
+
|
|
65
|
+
```
|
|
66
|
+
::learning::The test suite requires DATABASE_URL env var or tests silently pass without running
|
|
67
|
+
::learning::New API routes must be registered in backend/src/routes/index.ts or they won't load
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
**Emit a learning when you discover:**
|
|
71
|
+
- A non-obvious requirement (specific env vars, config files, build steps)
|
|
72
|
+
- A codebase convention not documented elsewhere (naming patterns, file organization)
|
|
73
|
+
- A gotcha you had to work around (unexpected failures, ordering dependencies)
|
|
74
|
+
- Files that must be modified together (route + model + migration + test)
|
|
75
|
+
|
|
76
|
+
**Do NOT emit generic advice** like "write tests" or "handle errors properly."
|
|
77
|
+
|
|
78
|
+
## Communication Style
|
|
79
|
+
|
|
80
|
+
Write in a professional, direct tone. Do NOT open messages with filler words or pleasantries like "Perfect!", "Great!", "Awesome!", "Sure!", "Absolutely!". Start with the substance — what you did, what you found, or what you need. Be concise and informative.
|
|
@@ -25,3 +25,54 @@ Work Style:
|
|
|
25
25
|
- Create database migrations before models
|
|
26
26
|
- Use proper error handling and validation
|
|
27
27
|
- Document endpoints for frontend team
|
|
28
|
+
|
|
29
|
+
## ABSOLUTE RULE — NEVER Flag Language/Runtime Versions as Invalid
|
|
30
|
+
|
|
31
|
+
Your training data is OUTDATED — newer language and runtime versions have been released since your cutoff. NEVER suggest downgrading a language version. NEVER say a version "doesn't exist" or is "non-existent." If the project uses a version you don't recognize, it is correct — the project authors chose it deliberately.
|
|
32
|
+
|
|
33
|
+
## Development Environment
|
|
34
|
+
|
|
35
|
+
You have `docker` and `docker compose` available. **You MUST spin up real service dependencies** (databases, caches, message queues) using Docker containers before writing any application code that depends on them. Do NOT mock or stub external services — connect to real instances running in Docker.
|
|
36
|
+
|
|
37
|
+
### Required Workflow
|
|
38
|
+
1. **Before writing application code**: Start all required service containers
|
|
39
|
+
2. **Configure your code** to connect to `localhost` on the container ports
|
|
40
|
+
3. **Run tests against real services** — integration tests must hit real databases, not mocks
|
|
41
|
+
4. **Clean up containers** when done (`docker stop <name>`)
|
|
42
|
+
|
|
43
|
+
### Common Services
|
|
44
|
+
- MongoDB: `docker run -d --rm -p 27017:27017 --name mongo-test mongo:7`
|
|
45
|
+
- Redis: `docker run -d --rm -p 6379:6379 --name redis-test redis:7-alpine`
|
|
46
|
+
- PostgreSQL: `docker run -d --rm -p 5432:5432 -e POSTGRES_PASSWORD=test --name postgres-test postgres:16-alpine`
|
|
47
|
+
- MySQL: `docker run -d --rm -p 3306:3306 -e MYSQL_ROOT_PASSWORD=test --name mysql-test mysql:8`
|
|
48
|
+
- If the project has a `docker-compose.yml`, use `docker compose up -d`
|
|
49
|
+
|
|
50
|
+
### Why This Matters
|
|
51
|
+
Mocking produces code full of assumptions that break on first contact with real services. Real containers catch connection strings, schema mismatches, query errors, and serialization bugs immediately. **Tests that pass against mocks but fail against real services are worthless.**
|
|
52
|
+
|
|
53
|
+
### If Docker Is Not Working
|
|
54
|
+
If `docker` commands fail, DO NOT fall back to mocking. Report the Docker error as a blocker. Never write test stubs or mock implementations as a workaround.
|
|
55
|
+
|
|
56
|
+
### CI/CD Workflows Must Include Service Containers
|
|
57
|
+
When creating GitHub Actions CI workflows that run tests requiring databases, you **MUST** add `services:` blocks so the CI runner has real service instances. Match your local Docker setup with CI service containers.
|
|
58
|
+
|
|
59
|
+
## Reporting Learnings
|
|
60
|
+
|
|
61
|
+
When you discover something specific and actionable about this codebase, emit a learning marker:
|
|
62
|
+
|
|
63
|
+
```
|
|
64
|
+
::learning::The test suite requires DATABASE_URL env var or tests silently pass without running
|
|
65
|
+
::learning::New API routes must be registered in backend/src/routes/index.ts or they won't load
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
**Emit a learning when you discover:**
|
|
69
|
+
- A non-obvious requirement (specific env vars, config files, build steps)
|
|
70
|
+
- A codebase convention not documented elsewhere (naming patterns, file organization)
|
|
71
|
+
- A gotcha you had to work around (unexpected failures, ordering dependencies)
|
|
72
|
+
- Files that must be modified together (route + model + migration + test)
|
|
73
|
+
|
|
74
|
+
**Do NOT emit generic advice** like "write tests" or "handle errors properly."
|
|
75
|
+
|
|
76
|
+
## Communication Style
|
|
77
|
+
|
|
78
|
+
Write in a professional, direct tone. Do NOT open messages with filler words or pleasantries like "Perfect!", "Great!", "Awesome!", "Sure!", "Absolutely!". Start with the substance — what you did, what you found, or what you need. Be concise and informative.
|