@ghostwater/soulforge 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 (55) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +115 -0
  3. package/dist/cli/cli.d.ts +2 -0
  4. package/dist/cli/cli.js +483 -0
  5. package/dist/cli/cli.js.map +1 -0
  6. package/dist/daemon/daemon-entry.d.ts +2 -0
  7. package/dist/daemon/daemon-entry.js +8 -0
  8. package/dist/daemon/daemon-entry.js.map +1 -0
  9. package/dist/daemon/daemon.d.ts +9 -0
  10. package/dist/daemon/daemon.js +88 -0
  11. package/dist/daemon/daemon.js.map +1 -0
  12. package/dist/daemon/runner.d.ts +3 -0
  13. package/dist/daemon/runner.js +310 -0
  14. package/dist/daemon/runner.js.map +1 -0
  15. package/dist/db/database.d.ts +95 -0
  16. package/dist/db/database.js +277 -0
  17. package/dist/db/database.js.map +1 -0
  18. package/dist/executors/claude-code.d.ts +9 -0
  19. package/dist/executors/claude-code.js +86 -0
  20. package/dist/executors/claude-code.js.map +1 -0
  21. package/dist/executors/codex.d.ts +9 -0
  22. package/dist/executors/codex.js +66 -0
  23. package/dist/executors/codex.js.map +1 -0
  24. package/dist/executors/openclaw.d.ts +10 -0
  25. package/dist/executors/openclaw.js +19 -0
  26. package/dist/executors/openclaw.js.map +1 -0
  27. package/dist/executors/registry.d.ts +4 -0
  28. package/dist/executors/registry.js +20 -0
  29. package/dist/executors/registry.js.map +1 -0
  30. package/dist/executors/self.d.ts +10 -0
  31. package/dist/executors/self.js +25 -0
  32. package/dist/executors/self.js.map +1 -0
  33. package/dist/executors/types.d.ts +41 -0
  34. package/dist/executors/types.js +2 -0
  35. package/dist/executors/types.js.map +1 -0
  36. package/dist/index.d.ts +6 -0
  37. package/dist/index.js +7 -0
  38. package/dist/index.js.map +1 -0
  39. package/dist/lib/logger.d.ts +7 -0
  40. package/dist/lib/logger.js +56 -0
  41. package/dist/lib/logger.js.map +1 -0
  42. package/dist/lib/worktree.d.ts +30 -0
  43. package/dist/lib/worktree.js +131 -0
  44. package/dist/lib/worktree.js.map +1 -0
  45. package/dist/workflow/parser.d.ts +2 -0
  46. package/dist/workflow/parser.js +95 -0
  47. package/dist/workflow/parser.js.map +1 -0
  48. package/dist/workflow/template.d.ts +14 -0
  49. package/dist/workflow/template.js +71 -0
  50. package/dist/workflow/template.js.map +1 -0
  51. package/dist/workflow/types.d.ts +65 -0
  52. package/dist/workflow/types.js +2 -0
  53. package/dist/workflow/types.js.map +1 -0
  54. package/package.json +46 -0
  55. package/workflows/feature-dev/workflow.yml +169 -0
package/dist/index.js ADDED
@@ -0,0 +1,7 @@
1
+ export * from "./db/database.js";
2
+ export * from "./workflow/types.js";
3
+ export * from "./workflow/parser.js";
4
+ export * from "./workflow/template.js";
5
+ export * from "./executors/types.js";
6
+ export * from "./executors/registry.js";
7
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,kBAAkB,CAAC;AACjC,cAAc,qBAAqB,CAAC;AACpC,cAAc,sBAAsB,CAAC;AACrC,cAAc,wBAAwB,CAAC;AACvC,cAAc,sBAAsB,CAAC;AACrC,cAAc,yBAAyB,CAAC"}
@@ -0,0 +1,7 @@
1
+ export declare const logger: {
2
+ info: (msg: string, meta?: Record<string, any>) => void;
3
+ warn: (msg: string, meta?: Record<string, any>) => void;
4
+ error: (msg: string, meta?: Record<string, any>) => void;
5
+ debug: (msg: string, meta?: Record<string, any>) => void;
6
+ };
7
+ export declare function readRecentLogs(lines?: number): string[];
@@ -0,0 +1,56 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import os from "node:os";
4
+ const LOG_DIR = path.join(os.homedir(), ".soulforge");
5
+ const LOG_PATH = path.join(LOG_DIR, "daemon.log");
6
+ const MAX_LOG_SIZE = 5 * 1024 * 1024; // 5MB
7
+ function ensureLogDir() {
8
+ fs.mkdirSync(LOG_DIR, { recursive: true });
9
+ }
10
+ function rotateIfNeeded() {
11
+ try {
12
+ const stat = fs.statSync(LOG_PATH);
13
+ if (stat.size > MAX_LOG_SIZE) {
14
+ const backup = LOG_PATH + ".1";
15
+ try {
16
+ fs.unlinkSync(backup);
17
+ }
18
+ catch { }
19
+ fs.renameSync(LOG_PATH, backup);
20
+ }
21
+ }
22
+ catch {
23
+ // File doesn't exist yet
24
+ }
25
+ }
26
+ function formatEntry(level, message, meta) {
27
+ const ts = new Date().toISOString();
28
+ const metaStr = meta ? " " + JSON.stringify(meta) : "";
29
+ return `[${ts}] ${level.toUpperCase()} ${message}${metaStr}\n`;
30
+ }
31
+ function write(level, message, meta) {
32
+ ensureLogDir();
33
+ rotateIfNeeded();
34
+ const entry = formatEntry(level, message, meta);
35
+ fs.appendFileSync(LOG_PATH, entry);
36
+ if (level === "error") {
37
+ process.stderr.write(entry);
38
+ }
39
+ }
40
+ export const logger = {
41
+ info: (msg, meta) => write("info", msg, meta),
42
+ warn: (msg, meta) => write("warn", msg, meta),
43
+ error: (msg, meta) => write("error", msg, meta),
44
+ debug: (msg, meta) => write("debug", msg, meta),
45
+ };
46
+ export function readRecentLogs(lines = 50) {
47
+ try {
48
+ const content = fs.readFileSync(LOG_PATH, "utf-8");
49
+ const allLines = content.split("\n").filter((l) => l.trim());
50
+ return allLines.slice(-lines);
51
+ }
52
+ catch {
53
+ return [];
54
+ }
55
+ }
56
+ //# sourceMappingURL=logger.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logger.js","sourceRoot":"","sources":["../../src/lib/logger.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,30 @@
1
+ export interface WorktreeResult {
2
+ worktreePath: string;
3
+ branch: string;
4
+ }
5
+ export interface GitRepoInfo {
6
+ type: 'bare' | 'standard' | 'worktree' | 'not-git';
7
+ repoRoot: string | null;
8
+ }
9
+ /**
10
+ * Validate whether a path is a git repository and determine its type.
11
+ * - 'bare': Uses bare+worktree layout (.bare/ exists in parent)
12
+ * - 'standard': Regular .git directory
13
+ * - 'worktree': Inside an existing worktree
14
+ * - 'not-git': Not a git repository
15
+ */
16
+ export declare function validateGitRepo(repoPath: string): GitRepoInfo;
17
+ /**
18
+ * Check if a branch already exists in the repository.
19
+ */
20
+ export declare function branchExists(repoPath: string, branchName: string): boolean;
21
+ /**
22
+ * Create a worktree for the given repository with a new branch.
23
+ *
24
+ * @param repoInfo - Validated git repo info from validateGitRepo()
25
+ * @param repoPath - Original path provided by user
26
+ * @param branch - Branch name to create
27
+ * @returns WorktreeResult with the path to the created worktree
28
+ */
29
+ export declare function createWorktree(repoPath: string, branch: string, repoInfo?: GitRepoInfo): WorktreeResult;
30
+ export declare function removeWorktree(repoPath: string, worktreePath: string): void;
@@ -0,0 +1,131 @@
1
+ import { execSync } from 'node:child_process';
2
+ import path from 'node:path';
3
+ import fs from 'node:fs';
4
+ /**
5
+ * Validate whether a path is a git repository and determine its type.
6
+ * - 'bare': Uses bare+worktree layout (.bare/ exists in parent)
7
+ * - 'standard': Regular .git directory
8
+ * - 'worktree': Inside an existing worktree
9
+ * - 'not-git': Not a git repository
10
+ */
11
+ export function validateGitRepo(repoPath) {
12
+ const resolvedPath = path.resolve(repoPath);
13
+ // Check if .bare/ exists in parent (bare+worktree layout)
14
+ const parentDir = path.dirname(resolvedPath);
15
+ const bareDir = path.join(parentDir, '.bare');
16
+ if (fs.existsSync(bareDir) && fs.statSync(bareDir).isDirectory()) {
17
+ return { type: 'bare', repoRoot: parentDir };
18
+ }
19
+ // Check if we're inside a worktree by looking for .git file (not directory)
20
+ const gitPath = path.join(resolvedPath, '.git');
21
+ if (fs.existsSync(gitPath)) {
22
+ const stat = fs.statSync(gitPath);
23
+ if (stat.isFile()) {
24
+ // .git is a file, meaning this is a worktree - resolve to parent repo
25
+ try {
26
+ const superproject = execSync('git rev-parse --show-superproject-working-tree', {
27
+ cwd: resolvedPath,
28
+ encoding: 'utf-8',
29
+ }).trim();
30
+ if (superproject) {
31
+ // Recursively validate the parent repo
32
+ return validateGitRepo(superproject);
33
+ }
34
+ }
35
+ catch {
36
+ // Fall through to standard check
37
+ }
38
+ return { type: 'worktree', repoRoot: resolvedPath };
39
+ }
40
+ else if (stat.isDirectory()) {
41
+ return { type: 'standard', repoRoot: resolvedPath };
42
+ }
43
+ }
44
+ // Check if we're somewhere inside a git repo
45
+ try {
46
+ const gitRoot = execSync('git rev-parse --show-toplevel', {
47
+ cwd: resolvedPath,
48
+ encoding: 'utf-8',
49
+ }).trim();
50
+ if (gitRoot) {
51
+ // Check if this git root has a bare parent
52
+ const gitRootParent = path.dirname(gitRoot);
53
+ const gitRootBareDir = path.join(gitRootParent, '.bare');
54
+ if (fs.existsSync(gitRootBareDir) && fs.statSync(gitRootBareDir).isDirectory()) {
55
+ return { type: 'bare', repoRoot: gitRootParent };
56
+ }
57
+ return { type: 'standard', repoRoot: gitRoot };
58
+ }
59
+ }
60
+ catch {
61
+ // Not a git repo
62
+ }
63
+ return { type: 'not-git', repoRoot: null };
64
+ }
65
+ /**
66
+ * Check if a branch already exists in the repository.
67
+ */
68
+ export function branchExists(repoPath, branchName) {
69
+ try {
70
+ const result = execSync(`git branch --list "${branchName}"`, {
71
+ cwd: repoPath,
72
+ encoding: 'utf-8',
73
+ }).trim();
74
+ return result.length > 0;
75
+ }
76
+ catch {
77
+ return false;
78
+ }
79
+ }
80
+ /**
81
+ * Create a worktree for the given repository with a new branch.
82
+ *
83
+ * @param repoInfo - Validated git repo info from validateGitRepo()
84
+ * @param repoPath - Original path provided by user
85
+ * @param branch - Branch name to create
86
+ * @returns WorktreeResult with the path to the created worktree
87
+ */
88
+ export function createWorktree(repoPath, branch, repoInfo) {
89
+ // If no repoInfo provided, validate the repo
90
+ const info = repoInfo ?? validateGitRepo(repoPath);
91
+ if (info.type === 'not-git') {
92
+ throw new Error(`Cannot create worktree: ${repoPath} is not a git repository. Use --workdir instead.`);
93
+ }
94
+ const repoRoot = info.repoRoot;
95
+ // Check if branch already exists
96
+ if (branchExists(repoRoot, branch)) {
97
+ throw new Error(`Branch '${branch}' already exists. Use --branch to specify a different name, or --workdir to work in an existing directory.`);
98
+ }
99
+ // Determine worktree placement based on repo type
100
+ let worktreesDir;
101
+ let gitDir;
102
+ let workingDir;
103
+ if (info.type === 'bare') {
104
+ // Bare layout: worktrees go in <parent>/worktrees/
105
+ worktreesDir = path.join(repoRoot, 'worktrees');
106
+ gitDir = path.join(repoRoot, '.bare');
107
+ workingDir = repoRoot;
108
+ }
109
+ else {
110
+ // Standard or worktree: worktrees go in <repo>/worktrees/
111
+ worktreesDir = path.join(repoRoot, 'worktrees');
112
+ gitDir = path.join(repoRoot, '.git');
113
+ workingDir = repoRoot;
114
+ }
115
+ const worktreePath = path.join(worktreesDir, branch.replace(/\//g, '-'));
116
+ fs.mkdirSync(worktreesDir, { recursive: true });
117
+ // Get current HEAD to branch from
118
+ const head = execSync('git rev-parse HEAD', { cwd: workingDir }).toString().trim();
119
+ // Create worktree with new branch
120
+ execSync(`git worktree add "${worktreePath}" -b "${branch}" ${head}`, { cwd: workingDir, env: { ...process.env, GIT_DIR: gitDir } });
121
+ return { worktreePath, branch };
122
+ }
123
+ export function removeWorktree(repoPath, worktreePath) {
124
+ try {
125
+ execSync(`git worktree remove "${worktreePath}" --force`, { cwd: repoPath });
126
+ }
127
+ catch {
128
+ // Best effort cleanup
129
+ }
130
+ }
131
+ //# sourceMappingURL=worktree.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"worktree.js","sourceRoot":"","sources":["../../src/lib/worktree.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import type { WorkflowSpec } from "./types.js";
2
+ export declare function loadWorkflowSpec(workflowPath: string): Promise<WorkflowSpec>;
@@ -0,0 +1,95 @@
1
+ import fs from "node:fs/promises";
2
+ import path from "node:path";
3
+ import YAML from "yaml";
4
+ export async function loadWorkflowSpec(workflowPath) {
5
+ // workflowPath can be a directory (containing workflow.yml) or a file
6
+ let filePath;
7
+ const stat = await fs.stat(workflowPath);
8
+ if (stat.isDirectory()) {
9
+ filePath = path.join(workflowPath, "workflow.yml");
10
+ }
11
+ else {
12
+ filePath = workflowPath;
13
+ }
14
+ const raw = await fs.readFile(filePath, "utf-8");
15
+ const parsed = YAML.parse(raw);
16
+ if (!parsed?.id) {
17
+ throw new Error(`workflow.yml missing 'id' in ${filePath}`);
18
+ }
19
+ if (!Array.isArray(parsed.steps) || parsed.steps.length === 0) {
20
+ throw new Error(`workflow.yml missing 'steps' in ${filePath}`);
21
+ }
22
+ // Parse loop configs from raw YAML
23
+ for (const step of parsed.steps) {
24
+ const rawStep = step;
25
+ if (rawStep.type)
26
+ step.type = rawStep.type;
27
+ if (rawStep.loop)
28
+ step.loop = parseLoopConfig(rawStep.loop);
29
+ }
30
+ // Apply defaults
31
+ if (parsed.defaults) {
32
+ for (const step of parsed.steps) {
33
+ if (!step.executor && parsed.defaults.executor)
34
+ step.executor = parsed.defaults.executor;
35
+ if (!step.model && parsed.defaults.model)
36
+ step.model = parsed.defaults.model;
37
+ if (!step.timeout && parsed.defaults.timeout)
38
+ step.timeout = parsed.defaults.timeout;
39
+ if (step.max_retries === undefined && parsed.defaults.max_retries !== undefined) {
40
+ step.max_retries = parsed.defaults.max_retries;
41
+ }
42
+ if (!step.workdir && parsed.defaults.workdir)
43
+ step.workdir = parsed.defaults.workdir;
44
+ }
45
+ }
46
+ // Default executor to openclaw for backward compat
47
+ for (const step of parsed.steps) {
48
+ if (!step.executor)
49
+ step.executor = "openclaw";
50
+ }
51
+ validateSteps(parsed.steps);
52
+ return parsed;
53
+ }
54
+ function parseLoopConfig(raw) {
55
+ return {
56
+ over: raw.over,
57
+ completion: raw.completion,
58
+ freshSession: raw.fresh_session ?? raw.freshSession,
59
+ verifyEach: raw.verify_each ?? raw.verifyEach,
60
+ verifyStep: raw.verify_step ?? raw.verifyStep,
61
+ };
62
+ }
63
+ function validateSteps(steps) {
64
+ const ids = new Set();
65
+ for (const step of steps) {
66
+ if (!step.id?.trim()) {
67
+ throw new Error("Step missing 'id'");
68
+ }
69
+ if (ids.has(step.id)) {
70
+ throw new Error(`Duplicate step id "${step.id}"`);
71
+ }
72
+ ids.add(step.id);
73
+ if (!step.input?.trim() && step.executor !== "self") {
74
+ throw new Error(`Step "${step.id}" missing 'input'`);
75
+ }
76
+ // Validate loop config
77
+ if (step.type === "loop") {
78
+ if (!step.loop) {
79
+ throw new Error(`Step "${step.id}" has type=loop but no loop config`);
80
+ }
81
+ if (step.loop.over !== "stories") {
82
+ throw new Error(`Step "${step.id}" loop.over must be "stories"`);
83
+ }
84
+ if (step.loop.verifyEach && step.loop.verifyStep && !ids.has(step.loop.verifyStep)) {
85
+ // verifyStep might come later in the steps array — defer this check
86
+ // Actually it usually comes after, so we just skip validation here
87
+ }
88
+ }
89
+ // openclaw executor requires agent
90
+ if (step.executor === "openclaw" && !step.agent) {
91
+ throw new Error(`Step "${step.id}" uses openclaw executor but has no 'agent'`);
92
+ }
93
+ }
94
+ }
95
+ //# sourceMappingURL=parser.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parser.js","sourceRoot":"","sources":["../../src/workflow/parser.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Resolve {{key}} placeholders in a template against a context object.
3
+ * Case-insensitive key lookup.
4
+ */
5
+ export declare function resolveTemplate(template: string, context: Record<string, string>): string;
6
+ /**
7
+ * Extract KEY: value pairs from output text.
8
+ * Keys must be uppercase with optional underscores.
9
+ */
10
+ export declare function extractContext(output: string): Record<string, string>;
11
+ /**
12
+ * Parse STORIES_JSON from step output.
13
+ */
14
+ export declare function parseStoriesJson(output: string): any[] | null;
@@ -0,0 +1,71 @@
1
+ /**
2
+ * Resolve {{key}} placeholders in a template against a context object.
3
+ * Case-insensitive key lookup.
4
+ */
5
+ export function resolveTemplate(template, context) {
6
+ return template.replace(/\{\{(\w+(?:\.\w+)*)\}\}/g, (_match, key) => {
7
+ if (key in context)
8
+ return context[key];
9
+ const lower = key.toLowerCase();
10
+ if (lower in context)
11
+ return context[lower];
12
+ // Try uppercase
13
+ const upper = key.toUpperCase();
14
+ if (upper in context)
15
+ return context[upper];
16
+ return `[missing: ${key}]`;
17
+ });
18
+ }
19
+ /**
20
+ * Extract KEY: value pairs from output text.
21
+ * Keys must be uppercase with optional underscores.
22
+ */
23
+ export function extractContext(output) {
24
+ const context = {};
25
+ for (const line of output.split("\n")) {
26
+ const match = line.match(/^([A-Z][A-Z0-9_]+):\s*(.+)$/);
27
+ if (match && !match[1].startsWith("STORIES_JSON")) {
28
+ context[match[1].toLowerCase()] = match[2].trim();
29
+ }
30
+ }
31
+ return context;
32
+ }
33
+ /**
34
+ * Parse STORIES_JSON from step output.
35
+ */
36
+ export function parseStoriesJson(output) {
37
+ const lines = output.split("\n");
38
+ const startIdx = lines.findIndex((l) => l.startsWith("STORIES_JSON:"));
39
+ if (startIdx === -1)
40
+ return null;
41
+ const firstLine = lines[startIdx].slice("STORIES_JSON:".length).trim();
42
+ const jsonLines = [firstLine];
43
+ for (let i = startIdx + 1; i < lines.length; i++) {
44
+ if (/^[A-Z_]+:\s/.test(lines[i]))
45
+ break;
46
+ jsonLines.push(lines[i]);
47
+ }
48
+ const jsonText = jsonLines.join("\n").trim();
49
+ const stories = JSON.parse(jsonText);
50
+ if (!Array.isArray(stories)) {
51
+ throw new Error("STORIES_JSON must be an array");
52
+ }
53
+ if (stories.length > 30) {
54
+ throw new Error(`STORIES_JSON has ${stories.length} stories, max is 30`);
55
+ }
56
+ // Validate each story
57
+ const seenIds = new Set();
58
+ for (let i = 0; i < stories.length; i++) {
59
+ const s = stories[i];
60
+ const ac = s.acceptanceCriteria ?? s.acceptance_criteria;
61
+ if (!s.id || !s.title || !s.description || !Array.isArray(ac) || ac.length === 0) {
62
+ throw new Error(`Story at index ${i} missing required fields (id, title, description, acceptanceCriteria)`);
63
+ }
64
+ if (seenIds.has(s.id)) {
65
+ throw new Error(`Duplicate story id "${s.id}"`);
66
+ }
67
+ seenIds.add(s.id);
68
+ }
69
+ return stories;
70
+ }
71
+ //# sourceMappingURL=template.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"template.js","sourceRoot":"","sources":["../../src/workflow/template.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,65 @@
1
+ export type ExecutorType = "openclaw" | "claude-code" | "codex" | "self";
2
+ export type LoopConfig = {
3
+ over: "stories";
4
+ completion: "all_done";
5
+ freshSession?: boolean;
6
+ verifyEach?: boolean;
7
+ verifyStep?: string;
8
+ };
9
+ export type StepFailure = {
10
+ retry_step?: string;
11
+ max_retries?: number;
12
+ escalate_to?: string;
13
+ on_exhausted?: {
14
+ escalate_to?: string;
15
+ };
16
+ };
17
+ export type WorkflowStep = {
18
+ id: string;
19
+ executor?: ExecutorType;
20
+ agent?: string;
21
+ model?: string;
22
+ workdir?: string;
23
+ type?: "single" | "loop";
24
+ loop?: LoopConfig;
25
+ input: string;
26
+ expects?: string;
27
+ timeout?: number;
28
+ max_retries?: number;
29
+ on_fail?: StepFailure;
30
+ };
31
+ export type AgentRole = "analysis" | "coding" | "verification" | "testing" | "pr" | "scanning";
32
+ export type WorkflowAgent = {
33
+ id: string;
34
+ name?: string;
35
+ description?: string;
36
+ role?: AgentRole;
37
+ workspace: {
38
+ baseDir: string;
39
+ files: Record<string, string>;
40
+ skills?: string[];
41
+ };
42
+ };
43
+ export type WorkflowDefaults = {
44
+ executor?: ExecutorType;
45
+ model?: string;
46
+ timeout?: number;
47
+ max_retries?: number;
48
+ workdir?: string;
49
+ };
50
+ export type WorkflowSpec = {
51
+ id: string;
52
+ name?: string;
53
+ version?: number;
54
+ description?: string;
55
+ defaults?: WorkflowDefaults;
56
+ context?: Record<string, string>;
57
+ agents?: WorkflowAgent[];
58
+ steps: WorkflowStep[];
59
+ };
60
+ export interface CallbackConfig {
61
+ url: string;
62
+ method?: string;
63
+ headers?: Record<string, string>;
64
+ bodyTemplate?: Record<string, any>;
65
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/workflow/types.ts"],"names":[],"mappings":""}
package/package.json ADDED
@@ -0,0 +1,46 @@
1
+ {
2
+ "name": "@ghostwater/soulforge",
3
+ "version": "0.1.0",
4
+ "description": "Pluggable executor workflow engine for AI coding agents. Dispatch workflow steps to Claude Code, Codex CLI, or human review checkpoints.",
5
+ "type": "module",
6
+ "engines": {
7
+ "node": ">=22"
8
+ },
9
+ "bin": {
10
+ "soulforge": "dist/cli/cli.js"
11
+ },
12
+ "scripts": {
13
+ "build": "tsc -p tsconfig.json && chmod +x dist/cli/cli.js",
14
+ "dev": "tsc -p tsconfig.json --watch",
15
+ "start": "node dist/cli/cli.js",
16
+ "prepublishOnly": "npm run build"
17
+ },
18
+ "keywords": [
19
+ "workflow",
20
+ "ai",
21
+ "coding-agent",
22
+ "claude-code",
23
+ "codex",
24
+ "executor",
25
+ "daemon",
26
+ "cli"
27
+ ],
28
+ "author": "Ghostwater Studio",
29
+ "license": "MIT",
30
+ "repository": {
31
+ "type": "git",
32
+ "url": "https://github.com/ghostwater-ai/soulforge.git"
33
+ },
34
+ "homepage": "https://github.com/ghostwater-ai/soulforge",
35
+ "files": [
36
+ "dist",
37
+ "workflows",
38
+ "README.md",
39
+ "LICENSE"
40
+ ],
41
+ "dependencies": {
42
+ "yaml": "^2.4.5",
43
+ "typescript": "^5.9.3",
44
+ "@types/node": "^22.0.0"
45
+ }
46
+ }