@cardor/agent-harness-kit 0.18.0 → 1.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.
@@ -0,0 +1,112 @@
1
+ type Provider = 'claude-code' | 'opencode';
2
+ interface ProjectConfig {
3
+ name: string;
4
+ description: string;
5
+ docsPath: string;
6
+ agentsMd?: string | null;
7
+ }
8
+ interface AgentConfig {
9
+ instructionsPath: string | null;
10
+ context?: string;
11
+ allowedPaths?: string[];
12
+ writablePaths?: string[];
13
+ }
14
+ interface CustomAgentConfig {
15
+ name: string;
16
+ instructionsPath: string;
17
+ }
18
+ interface AgentsConfig {
19
+ lead: AgentConfig;
20
+ explorer: AgentConfig;
21
+ builder: AgentConfig;
22
+ reviewer: AgentConfig;
23
+ custom?: CustomAgentConfig[];
24
+ }
25
+ type TasksAdapter = 'local' | 'jira' | 'linear' | 'mcp';
26
+ interface ActionSections {
27
+ toolsUsed: boolean;
28
+ filesModified: boolean;
29
+ result: boolean;
30
+ blockers: boolean;
31
+ nextSteps: boolean;
32
+ }
33
+ interface SQLiteConfig {
34
+ type: 'sqlite';
35
+ /** Path to the .db file, relative to cwd */
36
+ path: string;
37
+ }
38
+ interface RemoteDBConfig {
39
+ type: 'postgres' | 'mysql';
40
+ /** Full connection URL — postgres://user:pass@host:5432/db or mysql://... */
41
+ connectionString: string;
42
+ }
43
+ type DatabaseConfig = SQLiteConfig | RemoteDBConfig;
44
+ interface StorageConfig {
45
+ /** Directory for local harness files: current.md, feature_list.json, scripts */
46
+ dir: string;
47
+ tasks: {
48
+ adapter: TasksAdapter;
49
+ [key: string]: unknown;
50
+ };
51
+ sections: ActionSections;
52
+ markdownFallback: {
53
+ enabled: boolean;
54
+ path: string;
55
+ };
56
+ }
57
+ interface HealthConfig {
58
+ scriptPath: string;
59
+ required: boolean;
60
+ }
61
+ interface ToolsConfig {
62
+ mcp: {
63
+ enabled: boolean;
64
+ port: number;
65
+ };
66
+ scripts: {
67
+ enabled: boolean;
68
+ outputDir: string;
69
+ };
70
+ }
71
+ interface HarnessConfig {
72
+ project: ProjectConfig;
73
+ provider: Provider;
74
+ agents: AgentsConfig;
75
+ storage: StorageConfig;
76
+ database: DatabaseConfig;
77
+ health: HealthConfig;
78
+ tools: ToolsConfig;
79
+ }
80
+ type TaskStatus = 'pending' | 'in_progress' | 'done' | 'blocked';
81
+ interface TaskRow {
82
+ id: number;
83
+ slug: string;
84
+ title: string;
85
+ description: string | null;
86
+ status: TaskStatus;
87
+ assigned_to: string | null;
88
+ created_at: string;
89
+ started_at: string | null;
90
+ completed_at: string | null;
91
+ }
92
+ type AgentName = 'lead' | 'explorer' | 'builder' | 'reviewer' | `custom:${string}`;
93
+ type ActionStatus = 'in_progress' | 'completed' | 'blocked';
94
+ interface ActionRow {
95
+ id: string;
96
+ task_id: number;
97
+ agent: AgentName;
98
+ status: ActionStatus;
99
+ created_at: string;
100
+ completed_at: string | null;
101
+ summary: string | null;
102
+ }
103
+ interface TaskSeed {
104
+ slug: string;
105
+ title: string;
106
+ description?: string;
107
+ acceptance?: string[];
108
+ }
109
+
110
+ declare function defineHarness(config: HarnessConfig): HarnessConfig;
111
+
112
+ export { type ActionRow, type ActionSections, type ActionStatus, type AgentConfig, type AgentName, type AgentsConfig, type CustomAgentConfig, type HarnessConfig, type HealthConfig, type ProjectConfig, type Provider, type StorageConfig, type TaskRow, type TaskSeed, type TaskStatus, type TasksAdapter, type ToolsConfig, defineHarness };
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  defineHarness
3
- } from "./chunk-LQ7SDMK6.js";
3
+ } from "./chunk-X7FOJOZB.js";
4
4
  export {
5
5
  defineHarness
6
6
  };
@@ -0,0 +1,161 @@
1
+ // src/core/drivers/mysql.ts
2
+ import mysql from "mysql2/promise";
3
+ var SCHEMA = `
4
+ CREATE TABLE IF NOT EXISTS tasks (
5
+ id INT AUTO_INCREMENT PRIMARY KEY,
6
+ slug VARCHAR(255) NOT NULL UNIQUE,
7
+ title VARCHAR(500) NOT NULL,
8
+ description TEXT,
9
+ status VARCHAR(20) NOT NULL DEFAULT 'pending'
10
+ CHECK(status IN ('pending','in_progress','done','blocked')),
11
+ assigned_to VARCHAR(255),
12
+ created_at VARCHAR(30) NOT NULL,
13
+ started_at VARCHAR(30),
14
+ completed_at VARCHAR(30)
15
+ );
16
+
17
+ CREATE TABLE IF NOT EXISTS task_acceptance (
18
+ id INT AUTO_INCREMENT PRIMARY KEY,
19
+ task_id INT NOT NULL,
20
+ criterion TEXT NOT NULL,
21
+ met INT NOT NULL DEFAULT 0,
22
+ FOREIGN KEY (task_id) REFERENCES tasks(id) ON DELETE CASCADE
23
+ );
24
+
25
+ CREATE TABLE IF NOT EXISTS actions (
26
+ id VARCHAR(36) PRIMARY KEY,
27
+ task_id INT NOT NULL,
28
+ agent VARCHAR(100) NOT NULL,
29
+ status VARCHAR(20) NOT NULL DEFAULT 'in_progress'
30
+ CHECK(status IN ('in_progress','completed','blocked')),
31
+ created_at VARCHAR(30) NOT NULL,
32
+ completed_at VARCHAR(30),
33
+ summary TEXT,
34
+ FOREIGN KEY (task_id) REFERENCES tasks(id) ON DELETE CASCADE
35
+ );
36
+
37
+ CREATE TABLE IF NOT EXISTS action_sections (
38
+ id INT AUTO_INCREMENT PRIMARY KEY,
39
+ action_id VARCHAR(36) NOT NULL,
40
+ section_type VARCHAR(100) NOT NULL,
41
+ content TEXT NOT NULL,
42
+ created_at VARCHAR(30) NOT NULL,
43
+ FOREIGN KEY (action_id) REFERENCES actions(id) ON DELETE CASCADE
44
+ );
45
+
46
+ CREATE TABLE IF NOT EXISTS action_files (
47
+ id INT AUTO_INCREMENT PRIMARY KEY,
48
+ action_id VARCHAR(36) NOT NULL,
49
+ file_path VARCHAR(1000) NOT NULL,
50
+ operation VARCHAR(20) NOT NULL
51
+ CHECK(operation IN ('read','created','modified','deleted')),
52
+ notes TEXT,
53
+ FOREIGN KEY (action_id) REFERENCES actions(id) ON DELETE CASCADE
54
+ );
55
+
56
+ CREATE TABLE IF NOT EXISTS action_tools (
57
+ id INT AUTO_INCREMENT PRIMARY KEY,
58
+ action_id VARCHAR(36) NOT NULL,
59
+ tool_name VARCHAR(255) NOT NULL,
60
+ args_json TEXT,
61
+ result_summary TEXT,
62
+ called_at VARCHAR(30) NOT NULL,
63
+ FOREIGN KEY (action_id) REFERENCES actions(id) ON DELETE CASCADE
64
+ );
65
+
66
+ CREATE INDEX IF NOT EXISTS idx_tasks_status ON tasks(status);
67
+ CREATE INDEX IF NOT EXISTS idx_actions_task_id ON actions(task_id);
68
+ CREATE INDEX IF NOT EXISTS idx_actions_agent ON actions(agent);
69
+ CREATE INDEX IF NOT EXISTS idx_actions_status ON actions(status);
70
+ CREATE INDEX IF NOT EXISTS idx_action_files_path ON action_files(file_path(255));
71
+ CREATE INDEX IF NOT EXISTS idx_action_tools_name ON action_tools(tool_name);
72
+ `;
73
+ var MySQLDriver = class {
74
+ pool;
75
+ constructor(config) {
76
+ this.pool = mysql.createPool(config.connectionString);
77
+ }
78
+ async ensureSchema() {
79
+ const statements = SCHEMA.split(";").map((s) => s.trim()).filter(Boolean);
80
+ const conn = await this.pool.getConnection();
81
+ try {
82
+ for (const stmt of statements) {
83
+ await conn.execute(stmt);
84
+ }
85
+ } finally {
86
+ conn.release();
87
+ }
88
+ }
89
+ async query(sql, params = []) {
90
+ const [rows] = await this.pool.execute(sql, params);
91
+ return rows;
92
+ }
93
+ async queryOne(sql, params = []) {
94
+ const rows = await this.query(sql, params);
95
+ return rows[0] ?? null;
96
+ }
97
+ async insert(sql, params = []) {
98
+ const [result] = await this.pool.execute(sql, params);
99
+ return result.insertId;
100
+ }
101
+ async exec(sql, params = []) {
102
+ const [result] = await this.pool.execute(sql, params);
103
+ return result.affectedRows;
104
+ }
105
+ async execRaw(sql) {
106
+ await this.pool.query(sql);
107
+ }
108
+ async transaction(fn) {
109
+ const conn = await this.pool.getConnection();
110
+ await conn.beginTransaction();
111
+ try {
112
+ const txDriver = new MySQLTxDriver(conn);
113
+ const result = await fn(txDriver);
114
+ await conn.commit();
115
+ return result;
116
+ } catch (err) {
117
+ await conn.rollback();
118
+ throw err;
119
+ } finally {
120
+ conn.release();
121
+ }
122
+ }
123
+ async close() {
124
+ await this.pool.end();
125
+ }
126
+ };
127
+ var MySQLTxDriver = class {
128
+ constructor(conn) {
129
+ this.conn = conn;
130
+ }
131
+ conn;
132
+ async query(sql, params = []) {
133
+ const [rows] = await this.conn.execute(sql, params);
134
+ return rows;
135
+ }
136
+ async queryOne(sql, params = []) {
137
+ return (await this.query(sql, params))[0] ?? null;
138
+ }
139
+ async insert(sql, params = []) {
140
+ const [result] = await this.conn.execute(sql, params);
141
+ return result.insertId;
142
+ }
143
+ async exec(sql, params = []) {
144
+ const [result] = await this.conn.execute(sql, params);
145
+ return result.affectedRows;
146
+ }
147
+ async execRaw(sql) {
148
+ await this.conn.query(sql);
149
+ }
150
+ async transaction(fn) {
151
+ return fn(this);
152
+ }
153
+ async ensureSchema() {
154
+ }
155
+ async close() {
156
+ }
157
+ };
158
+ export {
159
+ MySQLDriver
160
+ };
161
+ //# sourceMappingURL=mysql-IMDWH2CU.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/core/drivers/mysql.ts"],"sourcesContent":["import mysql, { type ExecuteValues } from 'mysql2/promise'\n\nimport type { DBDriver } from './types'\nimport type { RemoteDBConfig } from '@/types'\n\nconst SCHEMA = `\nCREATE TABLE IF NOT EXISTS tasks (\n id INT AUTO_INCREMENT PRIMARY KEY,\n slug VARCHAR(255) NOT NULL UNIQUE,\n title VARCHAR(500) NOT NULL,\n description TEXT,\n status VARCHAR(20) NOT NULL DEFAULT 'pending'\n CHECK(status IN ('pending','in_progress','done','blocked')),\n assigned_to VARCHAR(255),\n created_at VARCHAR(30) NOT NULL,\n started_at VARCHAR(30),\n completed_at VARCHAR(30)\n);\n\nCREATE TABLE IF NOT EXISTS task_acceptance (\n id INT AUTO_INCREMENT PRIMARY KEY,\n task_id INT NOT NULL,\n criterion TEXT NOT NULL,\n met INT NOT NULL DEFAULT 0,\n FOREIGN KEY (task_id) REFERENCES tasks(id) ON DELETE CASCADE\n);\n\nCREATE TABLE IF NOT EXISTS actions (\n id VARCHAR(36) PRIMARY KEY,\n task_id INT NOT NULL,\n agent VARCHAR(100) NOT NULL,\n status VARCHAR(20) NOT NULL DEFAULT 'in_progress'\n CHECK(status IN ('in_progress','completed','blocked')),\n created_at VARCHAR(30) NOT NULL,\n completed_at VARCHAR(30),\n summary TEXT,\n FOREIGN KEY (task_id) REFERENCES tasks(id) ON DELETE CASCADE\n);\n\nCREATE TABLE IF NOT EXISTS action_sections (\n id INT AUTO_INCREMENT PRIMARY KEY,\n action_id VARCHAR(36) NOT NULL,\n section_type VARCHAR(100) NOT NULL,\n content TEXT NOT NULL,\n created_at VARCHAR(30) NOT NULL,\n FOREIGN KEY (action_id) REFERENCES actions(id) ON DELETE CASCADE\n);\n\nCREATE TABLE IF NOT EXISTS action_files (\n id INT AUTO_INCREMENT PRIMARY KEY,\n action_id VARCHAR(36) NOT NULL,\n file_path VARCHAR(1000) NOT NULL,\n operation VARCHAR(20) NOT NULL\n CHECK(operation IN ('read','created','modified','deleted')),\n notes TEXT,\n FOREIGN KEY (action_id) REFERENCES actions(id) ON DELETE CASCADE\n);\n\nCREATE TABLE IF NOT EXISTS action_tools (\n id INT AUTO_INCREMENT PRIMARY KEY,\n action_id VARCHAR(36) NOT NULL,\n tool_name VARCHAR(255) NOT NULL,\n args_json TEXT,\n result_summary TEXT,\n called_at VARCHAR(30) NOT NULL,\n FOREIGN KEY (action_id) REFERENCES actions(id) ON DELETE CASCADE\n);\n\nCREATE INDEX IF NOT EXISTS idx_tasks_status ON tasks(status);\nCREATE INDEX IF NOT EXISTS idx_actions_task_id ON actions(task_id);\nCREATE INDEX IF NOT EXISTS idx_actions_agent ON actions(agent);\nCREATE INDEX IF NOT EXISTS idx_actions_status ON actions(status);\nCREATE INDEX IF NOT EXISTS idx_action_files_path ON action_files(file_path(255));\nCREATE INDEX IF NOT EXISTS idx_action_tools_name ON action_tools(tool_name);\n`\n\ntype MySQLPool = mysql.Pool\n\nexport class MySQLDriver implements DBDriver {\n private pool: MySQLPool\n\n constructor(config: RemoteDBConfig) {\n this.pool = mysql.createPool(config.connectionString)\n }\n\n async ensureSchema(): Promise<void> {\n // Run each statement individually since mysql2 doesn't support multi-statement by default\n const statements = SCHEMA.split(';')\n .map((s) => s.trim())\n .filter(Boolean)\n\n const conn = await this.pool.getConnection()\n try {\n for (const stmt of statements) {\n await conn.execute(stmt)\n }\n } finally {\n conn.release()\n }\n }\n\n async query<T>(sql: string, params: unknown[] = []): Promise<T[]> {\n const [rows] = await this.pool.execute(sql, params as ExecuteValues)\n return rows as unknown as T[]\n }\n\n async queryOne<T>(sql: string, params: unknown[] = []): Promise<T | null> {\n const rows = await this.query<T>(sql, params)\n return (rows as T[])[0] ?? null\n }\n\n async insert(sql: string, params: unknown[] = []): Promise<number> {\n const [result] = await this.pool.execute(sql, params as ExecuteValues)\n return (result as mysql.ResultSetHeader).insertId\n }\n\n async exec(sql: string, params: unknown[] = []): Promise<number> {\n const [result] = await this.pool.execute(sql, params as ExecuteValues)\n return (result as mysql.ResultSetHeader).affectedRows\n }\n\n async execRaw(sql: string): Promise<void> {\n await this.pool.query(sql)\n }\n\n async transaction<T>(fn: (tx: DBDriver) => Promise<T>): Promise<T> {\n const conn = await this.pool.getConnection()\n await conn.beginTransaction()\n try {\n const txDriver = new MySQLTxDriver(conn)\n const result = await fn(txDriver)\n await conn.commit()\n return result\n } catch (err) {\n await conn.rollback()\n throw err\n } finally {\n conn.release()\n }\n }\n\n async close(): Promise<void> {\n await this.pool.end()\n }\n}\n\nclass MySQLTxDriver implements DBDriver {\n constructor(private conn: mysql.PoolConnection) { }\n\n async query<T>(sql: string, params: unknown[] = []): Promise<T[]> {\n const [rows] = await this.conn.execute(sql, params as ExecuteValues)\n return rows as unknown as T[]\n }\n\n async queryOne<T>(sql: string, params: unknown[] = []): Promise<T | null> {\n return (await this.query<T>(sql, params))[0] ?? null\n }\n\n async insert(sql: string, params: unknown[] = []): Promise<number> {\n const [result] = await this.conn.execute(sql, params as ExecuteValues)\n return (result as mysql.ResultSetHeader).insertId\n }\n\n async exec(sql: string, params: unknown[] = []): Promise<number> {\n const [result] = await this.conn.execute(sql, params as ExecuteValues)\n return (result as mysql.ResultSetHeader).affectedRows\n }\n\n async execRaw(sql: string): Promise<void> {\n await this.conn.query(sql)\n }\n\n async transaction<T>(fn: (tx: DBDriver) => Promise<T>): Promise<T> {\n return fn(this)\n }\n\n async ensureSchema(): Promise<void> { }\n async close(): Promise<void> { }\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,148 @@
1
+ // src/core/drivers/postgres.ts
2
+ import postgres from "postgres";
3
+ var SCHEMA = `
4
+ CREATE TABLE IF NOT EXISTS tasks (
5
+ id SERIAL PRIMARY KEY,
6
+ slug TEXT NOT NULL UNIQUE,
7
+ title TEXT NOT NULL,
8
+ description TEXT,
9
+ status TEXT NOT NULL DEFAULT 'pending'
10
+ CHECK(status IN ('pending','in_progress','done','blocked')),
11
+ assigned_to TEXT,
12
+ created_at TEXT NOT NULL,
13
+ started_at TEXT,
14
+ completed_at TEXT
15
+ );
16
+
17
+ CREATE TABLE IF NOT EXISTS task_acceptance (
18
+ id SERIAL PRIMARY KEY,
19
+ task_id INTEGER NOT NULL REFERENCES tasks(id) ON DELETE CASCADE,
20
+ criterion TEXT NOT NULL,
21
+ met INTEGER NOT NULL DEFAULT 0
22
+ );
23
+
24
+ CREATE TABLE IF NOT EXISTS actions (
25
+ id TEXT PRIMARY KEY,
26
+ task_id INTEGER NOT NULL REFERENCES tasks(id) ON DELETE CASCADE,
27
+ agent TEXT NOT NULL
28
+ CHECK(agent IN ('lead','explorer','builder','reviewer') OR agent LIKE 'custom:%'),
29
+ status TEXT NOT NULL DEFAULT 'in_progress'
30
+ CHECK(status IN ('in_progress','completed','blocked')),
31
+ created_at TEXT NOT NULL,
32
+ completed_at TEXT,
33
+ summary TEXT
34
+ );
35
+
36
+ CREATE TABLE IF NOT EXISTS action_sections (
37
+ id SERIAL PRIMARY KEY,
38
+ action_id TEXT NOT NULL REFERENCES actions(id) ON DELETE CASCADE,
39
+ section_type TEXT NOT NULL,
40
+ content TEXT NOT NULL,
41
+ created_at TEXT NOT NULL
42
+ );
43
+
44
+ CREATE TABLE IF NOT EXISTS action_files (
45
+ id SERIAL PRIMARY KEY,
46
+ action_id TEXT NOT NULL REFERENCES actions(id) ON DELETE CASCADE,
47
+ file_path TEXT NOT NULL,
48
+ operation TEXT NOT NULL
49
+ CHECK(operation IN ('read','created','modified','deleted')),
50
+ notes TEXT
51
+ );
52
+
53
+ CREATE TABLE IF NOT EXISTS action_tools (
54
+ id SERIAL PRIMARY KEY,
55
+ action_id TEXT NOT NULL REFERENCES actions(id) ON DELETE CASCADE,
56
+ tool_name TEXT NOT NULL,
57
+ args_json TEXT,
58
+ result_summary TEXT,
59
+ called_at TEXT NOT NULL
60
+ );
61
+
62
+ CREATE INDEX IF NOT EXISTS idx_tasks_status ON tasks(status);
63
+ CREATE INDEX IF NOT EXISTS idx_actions_task_id ON actions(task_id);
64
+ CREATE INDEX IF NOT EXISTS idx_actions_agent ON actions(agent);
65
+ CREATE INDEX IF NOT EXISTS idx_actions_status ON actions(status);
66
+ CREATE INDEX IF NOT EXISTS idx_action_files_path ON action_files(file_path);
67
+ CREATE INDEX IF NOT EXISTS idx_action_tools_name ON action_tools(tool_name);
68
+ `;
69
+ function toPositional(sql) {
70
+ let i = 0;
71
+ return sql.replace(/\?/g, () => `$${++i}`);
72
+ }
73
+ var PostgresDriver = class {
74
+ sql;
75
+ constructor(config) {
76
+ this.sql = postgres(config.connectionString);
77
+ }
78
+ async ensureSchema() {
79
+ await this.sql.unsafe(SCHEMA);
80
+ }
81
+ async query(sql, params = []) {
82
+ const rows = await this.sql.unsafe(toPositional(sql), params);
83
+ return rows;
84
+ }
85
+ async queryOne(sql, params = []) {
86
+ const rows = await this.query(sql, params);
87
+ return rows[0] ?? null;
88
+ }
89
+ async insert(sql, params = []) {
90
+ const withReturning = toPositional(sql.trimEnd()) + " RETURNING id";
91
+ const rows = await this.sql.unsafe(withReturning, params);
92
+ return rows[0].id;
93
+ }
94
+ async exec(sql, params = []) {
95
+ const result = await this.sql.unsafe(toPositional(sql), params);
96
+ return result.count;
97
+ }
98
+ async execRaw(sql) {
99
+ await this.sql.unsafe(sql);
100
+ }
101
+ async transaction(fn) {
102
+ let result;
103
+ await this.sql.begin(async (txSql) => {
104
+ const txDriver = new PostgresTxDriver(txSql);
105
+ result = await fn(txDriver);
106
+ });
107
+ return result;
108
+ }
109
+ async close() {
110
+ await this.sql.end();
111
+ }
112
+ };
113
+ var PostgresTxDriver = class {
114
+ constructor(sql) {
115
+ this.sql = sql;
116
+ }
117
+ sql;
118
+ async query(sql, params = []) {
119
+ const rows = await this.sql.unsafe(toPositional(sql), params);
120
+ return rows;
121
+ }
122
+ async queryOne(sql, params = []) {
123
+ return (await this.query(sql, params))[0] ?? null;
124
+ }
125
+ async insert(sql, params = []) {
126
+ const withReturning = toPositional(sql.trimEnd()) + " RETURNING id";
127
+ const rows = await this.sql.unsafe(withReturning, params);
128
+ return rows[0].id;
129
+ }
130
+ async exec(sql, params = []) {
131
+ const result = await this.sql.unsafe(toPositional(sql), params);
132
+ return result.count;
133
+ }
134
+ async execRaw(sql) {
135
+ await this.sql.unsafe(sql);
136
+ }
137
+ async transaction(fn) {
138
+ return fn(this);
139
+ }
140
+ async ensureSchema() {
141
+ }
142
+ async close() {
143
+ }
144
+ };
145
+ export {
146
+ PostgresDriver
147
+ };
148
+ //# sourceMappingURL=postgres-TYINLEAT.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/core/drivers/postgres.ts"],"sourcesContent":["import postgres from 'postgres'\n\nimport type { DBDriver } from './types'\nimport type { RemoteDBConfig } from '@/types'\n\nconst SCHEMA = `\nCREATE TABLE IF NOT EXISTS tasks (\n id SERIAL PRIMARY KEY,\n slug TEXT NOT NULL UNIQUE,\n title TEXT NOT NULL,\n description TEXT,\n status TEXT NOT NULL DEFAULT 'pending'\n CHECK(status IN ('pending','in_progress','done','blocked')),\n assigned_to TEXT,\n created_at TEXT NOT NULL,\n started_at TEXT,\n completed_at TEXT\n);\n\nCREATE TABLE IF NOT EXISTS task_acceptance (\n id SERIAL PRIMARY KEY,\n task_id INTEGER NOT NULL REFERENCES tasks(id) ON DELETE CASCADE,\n criterion TEXT NOT NULL,\n met INTEGER NOT NULL DEFAULT 0\n);\n\nCREATE TABLE IF NOT EXISTS actions (\n id TEXT PRIMARY KEY,\n task_id INTEGER NOT NULL REFERENCES tasks(id) ON DELETE CASCADE,\n agent TEXT NOT NULL\n CHECK(agent IN ('lead','explorer','builder','reviewer') OR agent LIKE 'custom:%'),\n status TEXT NOT NULL DEFAULT 'in_progress'\n CHECK(status IN ('in_progress','completed','blocked')),\n created_at TEXT NOT NULL,\n completed_at TEXT,\n summary TEXT\n);\n\nCREATE TABLE IF NOT EXISTS action_sections (\n id SERIAL PRIMARY KEY,\n action_id TEXT NOT NULL REFERENCES actions(id) ON DELETE CASCADE,\n section_type TEXT NOT NULL,\n content TEXT NOT NULL,\n created_at TEXT NOT NULL\n);\n\nCREATE TABLE IF NOT EXISTS action_files (\n id SERIAL PRIMARY KEY,\n action_id TEXT NOT NULL REFERENCES actions(id) ON DELETE CASCADE,\n file_path TEXT NOT NULL,\n operation TEXT NOT NULL\n CHECK(operation IN ('read','created','modified','deleted')),\n notes TEXT\n);\n\nCREATE TABLE IF NOT EXISTS action_tools (\n id SERIAL PRIMARY KEY,\n action_id TEXT NOT NULL REFERENCES actions(id) ON DELETE CASCADE,\n tool_name TEXT NOT NULL,\n args_json TEXT,\n result_summary TEXT,\n called_at TEXT NOT NULL\n);\n\nCREATE INDEX IF NOT EXISTS idx_tasks_status ON tasks(status);\nCREATE INDEX IF NOT EXISTS idx_actions_task_id ON actions(task_id);\nCREATE INDEX IF NOT EXISTS idx_actions_agent ON actions(agent);\nCREATE INDEX IF NOT EXISTS idx_actions_status ON actions(status);\nCREATE INDEX IF NOT EXISTS idx_action_files_path ON action_files(file_path);\nCREATE INDEX IF NOT EXISTS idx_action_tools_name ON action_tools(tool_name);\n`\n\n// Convert SQLite-style ? placeholders to Postgres $1, $2, ...\nfunction toPositional(sql: string): string {\n let i = 0\n return sql.replace(/\\?/g, () => `$${++i}`)\n}\n\nexport class PostgresDriver implements DBDriver {\n private sql: postgres.Sql\n\n constructor(config: RemoteDBConfig) {\n this.sql = postgres(config.connectionString)\n }\n\n async ensureSchema(): Promise<void> {\n await this.sql.unsafe(SCHEMA)\n }\n\n async query<T>(sql: string, params: unknown[] = []): Promise<T[]> {\n const rows = await this.sql.unsafe(toPositional(sql), params as postgres.ParameterOrJSON<never>[])\n return rows as unknown as T[]\n }\n\n async queryOne<T>(sql: string, params: unknown[] = []): Promise<T | null> {\n const rows = await this.query<T>(sql, params)\n return rows[0] ?? null\n }\n\n async insert(sql: string, params: unknown[] = []): Promise<number> {\n const withReturning = toPositional(sql.trimEnd()) + ' RETURNING id'\n const rows = await this.sql.unsafe(withReturning, params as postgres.ParameterOrJSON<never>[])\n return (rows[0] as unknown as { id: number }).id\n }\n\n async exec(sql: string, params: unknown[] = []): Promise<number> {\n const result = await this.sql.unsafe(toPositional(sql), params as postgres.ParameterOrJSON<never>[])\n return result.count\n }\n\n async execRaw(sql: string): Promise<void> {\n await this.sql.unsafe(sql)\n }\n\n async transaction<T>(fn: (tx: DBDriver) => Promise<T>): Promise<T> {\n let result!: T\n await this.sql.begin(async (txSql) => {\n const txDriver = new PostgresTxDriver(txSql)\n result = await fn(txDriver)\n })\n return result\n }\n\n async close(): Promise<void> {\n await this.sql.end()\n }\n}\n\nclass PostgresTxDriver implements DBDriver {\n constructor(private sql: postgres.TransactionSql) { }\n\n async query<T>(sql: string, params: unknown[] = []): Promise<T[]> {\n const rows = await this.sql.unsafe(toPositional(sql), params as postgres.ParameterOrJSON<never>[])\n return rows as unknown as T[]\n }\n\n async queryOne<T>(sql: string, params: unknown[] = []): Promise<T | null> {\n return (await this.query<T>(sql, params))[0] ?? null\n }\n\n async insert(sql: string, params: unknown[] = []): Promise<number> {\n const withReturning = toPositional(sql.trimEnd()) + ' RETURNING id'\n const rows = await this.sql.unsafe(withReturning, params as postgres.ParameterOrJSON<never>[])\n return (rows[0] as unknown as { id: number }).id\n }\n\n async exec(sql: string, params: unknown[] = []): Promise<number> {\n const result = await this.sql.unsafe(toPositional(sql), params as postgres.ParameterOrJSON<never>[])\n return result.count\n }\n\n async execRaw(sql: string): Promise<void> {\n await this.sql.unsafe(sql)\n }\n\n async transaction<T>(fn: (tx: DBDriver) => Promise<T>): Promise<T> {\n return fn(this)\n }\n\n async ensureSchema(): Promise<void> { }\n async close(): Promise<void> { }\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,143 @@
1
+ // src/core/drivers/sqlite.ts
2
+ import { existsSync, mkdirSync, rmSync, statSync } from "fs";
3
+ import { dirname } from "path";
4
+
5
+ // src/core/sqlite-adapter.ts
6
+ import { createRequire } from "module";
7
+ var _require = createRequire(import.meta.url);
8
+ var isBun = "bun" in process.versions;
9
+ function openSQLite(path) {
10
+ if (isBun) {
11
+ const { Database } = _require("bun:sqlite");
12
+ return new Database(path);
13
+ }
14
+ const { DatabaseSync } = _require("node:sqlite");
15
+ return new DatabaseSync(path);
16
+ }
17
+ function lastInsertId(db) {
18
+ const row = db.prepare("SELECT last_insert_rowid() AS id").get();
19
+ return row.id;
20
+ }
21
+
22
+ // src/core/drivers/sqlite.ts
23
+ var SCHEMA = `
24
+ CREATE TABLE IF NOT EXISTS tasks (
25
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
26
+ slug TEXT NOT NULL UNIQUE,
27
+ title TEXT NOT NULL,
28
+ description TEXT,
29
+ status TEXT NOT NULL DEFAULT 'pending'
30
+ CHECK(status IN ('pending','in_progress','done','blocked')),
31
+ assigned_to TEXT,
32
+ created_at TEXT NOT NULL,
33
+ started_at TEXT,
34
+ completed_at TEXT
35
+ );
36
+
37
+ CREATE TABLE IF NOT EXISTS task_acceptance (
38
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
39
+ task_id INTEGER NOT NULL REFERENCES tasks(id) ON DELETE CASCADE,
40
+ criterion TEXT NOT NULL,
41
+ met INTEGER NOT NULL DEFAULT 0
42
+ );
43
+
44
+ CREATE TABLE IF NOT EXISTS actions (
45
+ id TEXT PRIMARY KEY,
46
+ task_id INTEGER NOT NULL REFERENCES tasks(id) ON DELETE CASCADE,
47
+ agent TEXT NOT NULL
48
+ CHECK(agent IN ('lead','explorer','builder','reviewer') OR agent LIKE 'custom:%'),
49
+ status TEXT NOT NULL DEFAULT 'in_progress'
50
+ CHECK(status IN ('in_progress','completed','blocked')),
51
+ created_at TEXT NOT NULL,
52
+ completed_at TEXT,
53
+ summary TEXT
54
+ );
55
+
56
+ CREATE TABLE IF NOT EXISTS action_sections (
57
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
58
+ action_id TEXT NOT NULL REFERENCES actions(id) ON DELETE CASCADE,
59
+ section_type TEXT NOT NULL,
60
+ content TEXT NOT NULL,
61
+ created_at TEXT NOT NULL
62
+ );
63
+
64
+ CREATE TABLE IF NOT EXISTS action_files (
65
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
66
+ action_id TEXT NOT NULL REFERENCES actions(id) ON DELETE CASCADE,
67
+ file_path TEXT NOT NULL,
68
+ operation TEXT NOT NULL
69
+ CHECK(operation IN ('read','created','modified','deleted')),
70
+ notes TEXT
71
+ );
72
+
73
+ CREATE TABLE IF NOT EXISTS action_tools (
74
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
75
+ action_id TEXT NOT NULL REFERENCES actions(id) ON DELETE CASCADE,
76
+ tool_name TEXT NOT NULL,
77
+ args_json TEXT,
78
+ result_summary TEXT,
79
+ called_at TEXT NOT NULL
80
+ );
81
+
82
+ CREATE INDEX IF NOT EXISTS idx_tasks_status ON tasks(status);
83
+ CREATE INDEX IF NOT EXISTS idx_actions_task_id ON actions(task_id);
84
+ CREATE INDEX IF NOT EXISTS idx_actions_agent ON actions(agent);
85
+ CREATE INDEX IF NOT EXISTS idx_actions_status ON actions(status);
86
+ CREATE INDEX IF NOT EXISTS idx_action_files_path ON action_files(file_path);
87
+ CREATE INDEX IF NOT EXISTS idx_action_tools_name ON action_tools(tool_name);
88
+ `;
89
+ var SQLiteDriver = class {
90
+ db;
91
+ constructor(dbPath) {
92
+ mkdirSync(dirname(dbPath), { recursive: true });
93
+ if (existsSync(dbPath)) {
94
+ const shm = `${dbPath}-shm`;
95
+ const wal = `${dbPath}-wal`;
96
+ if (existsSync(shm) && existsSync(wal) && statSync(wal).size === 0) {
97
+ rmSync(shm, { force: true });
98
+ rmSync(wal, { force: true });
99
+ }
100
+ }
101
+ this.db = openSQLite(dbPath);
102
+ this.db.exec("PRAGMA journal_mode = WAL");
103
+ this.db.exec("PRAGMA foreign_keys = ON");
104
+ }
105
+ async ensureSchema() {
106
+ this.db.exec(SCHEMA);
107
+ }
108
+ async query(sql, params = []) {
109
+ return this.db.prepare(sql).all(...params);
110
+ }
111
+ async queryOne(sql, params = []) {
112
+ return this.db.prepare(sql).get(...params) ?? null;
113
+ }
114
+ async insert(sql, params = []) {
115
+ this.db.prepare(sql).run(...params);
116
+ return lastInsertId(this.db);
117
+ }
118
+ async exec(sql, params = []) {
119
+ const result = this.db.prepare(sql).run(...params);
120
+ return result.changes ?? 0;
121
+ }
122
+ async execRaw(sql) {
123
+ this.db.exec(sql);
124
+ }
125
+ async transaction(fn) {
126
+ this.db.exec("BEGIN IMMEDIATE");
127
+ try {
128
+ const result = await fn(this);
129
+ this.db.exec("COMMIT");
130
+ return result;
131
+ } catch (err) {
132
+ this.db.exec("ROLLBACK");
133
+ throw err;
134
+ }
135
+ }
136
+ async close() {
137
+ this.db.close();
138
+ }
139
+ };
140
+ export {
141
+ SQLiteDriver
142
+ };
143
+ //# sourceMappingURL=sqlite-5R6LB3RX.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/core/drivers/sqlite.ts","../src/core/sqlite-adapter.ts"],"sourcesContent":["import { existsSync, mkdirSync, rmSync, statSync } from 'node:fs'\nimport { dirname } from 'node:path'\n\nimport { lastInsertId, openSQLite, type SQLiteDB } from '../sqlite-adapter'\n\nimport type { DBDriver } from './types'\n\nconst SCHEMA = `\nCREATE TABLE IF NOT EXISTS tasks (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n slug TEXT NOT NULL UNIQUE,\n title TEXT NOT NULL,\n description TEXT,\n status TEXT NOT NULL DEFAULT 'pending'\n CHECK(status IN ('pending','in_progress','done','blocked')),\n assigned_to TEXT,\n created_at TEXT NOT NULL,\n started_at TEXT,\n completed_at TEXT\n);\n\nCREATE TABLE IF NOT EXISTS task_acceptance (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n task_id INTEGER NOT NULL REFERENCES tasks(id) ON DELETE CASCADE,\n criterion TEXT NOT NULL,\n met INTEGER NOT NULL DEFAULT 0\n);\n\nCREATE TABLE IF NOT EXISTS actions (\n id TEXT PRIMARY KEY,\n task_id INTEGER NOT NULL REFERENCES tasks(id) ON DELETE CASCADE,\n agent TEXT NOT NULL\n CHECK(agent IN ('lead','explorer','builder','reviewer') OR agent LIKE 'custom:%'),\n status TEXT NOT NULL DEFAULT 'in_progress'\n CHECK(status IN ('in_progress','completed','blocked')),\n created_at TEXT NOT NULL,\n completed_at TEXT,\n summary TEXT\n);\n\nCREATE TABLE IF NOT EXISTS action_sections (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n action_id TEXT NOT NULL REFERENCES actions(id) ON DELETE CASCADE,\n section_type TEXT NOT NULL,\n content TEXT NOT NULL,\n created_at TEXT NOT NULL\n);\n\nCREATE TABLE IF NOT EXISTS action_files (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n action_id TEXT NOT NULL REFERENCES actions(id) ON DELETE CASCADE,\n file_path TEXT NOT NULL,\n operation TEXT NOT NULL\n CHECK(operation IN ('read','created','modified','deleted')),\n notes TEXT\n);\n\nCREATE TABLE IF NOT EXISTS action_tools (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n action_id TEXT NOT NULL REFERENCES actions(id) ON DELETE CASCADE,\n tool_name TEXT NOT NULL,\n args_json TEXT,\n result_summary TEXT,\n called_at TEXT NOT NULL\n);\n\nCREATE INDEX IF NOT EXISTS idx_tasks_status ON tasks(status);\nCREATE INDEX IF NOT EXISTS idx_actions_task_id ON actions(task_id);\nCREATE INDEX IF NOT EXISTS idx_actions_agent ON actions(agent);\nCREATE INDEX IF NOT EXISTS idx_actions_status ON actions(status);\nCREATE INDEX IF NOT EXISTS idx_action_files_path ON action_files(file_path);\nCREATE INDEX IF NOT EXISTS idx_action_tools_name ON action_tools(tool_name);\n`\n\nexport class SQLiteDriver implements DBDriver {\n private db: SQLiteDB\n\n constructor(dbPath: string) {\n mkdirSync(dirname(dbPath), { recursive: true })\n // Remove stale WAL/SHM files left by a crashed session — they cause SQLITE_IOERR.\n // A 0-byte WAL alongside a non-empty SHM means the last checkpoint never completed.\n if (existsSync(dbPath)) {\n const shm = `${dbPath}-shm`\n const wal = `${dbPath}-wal`\n if (existsSync(shm) && existsSync(wal) && statSync(wal).size === 0) {\n rmSync(shm, { force: true })\n rmSync(wal, { force: true })\n }\n }\n this.db = openSQLite(dbPath)\n this.db.exec('PRAGMA journal_mode = WAL')\n this.db.exec('PRAGMA foreign_keys = ON')\n }\n\n async ensureSchema(): Promise<void> {\n this.db.exec(SCHEMA)\n }\n\n async query<T>(sql: string, params: unknown[] = []): Promise<T[]> {\n return this.db.prepare(sql).all(...params) as unknown as T[]\n }\n\n async queryOne<T>(sql: string, params: unknown[] = []): Promise<T | null> {\n return (this.db.prepare(sql).get(...params) as unknown as T) ?? null\n }\n\n async insert(sql: string, params: unknown[] = []): Promise<number> {\n this.db.prepare(sql).run(...params)\n return lastInsertId(this.db)\n }\n\n async exec(sql: string, params: unknown[] = []): Promise<number> {\n const result = this.db.prepare(sql).run(...params)\n return (result as { changes?: number }).changes ?? 0\n }\n\n async execRaw(sql: string): Promise<void> {\n this.db.exec(sql)\n }\n\n async transaction<T>(fn: (tx: DBDriver) => Promise<T>): Promise<T> {\n this.db.exec('BEGIN IMMEDIATE')\n try {\n const result = await fn(this)\n this.db.exec('COMMIT')\n return result\n } catch (err) {\n this.db.exec('ROLLBACK')\n throw err\n }\n }\n\n async close(): Promise<void> {\n this.db.close()\n }\n}\n","import { createRequire } from 'node:module'\n\nconst _require = createRequire(import.meta.url)\nconst isBun = 'bun' in process.versions\n\n// ─── Shared interface ─────────────────────────────────────────────────────────\n// Both node:sqlite (DatabaseSync) and bun:sqlite (Database) expose this surface.\n\nexport type SQLRow = Record<string, unknown>\n\nexport interface SQLStatement {\n run(...args: unknown[]): unknown\n get(...args: unknown[]): SQLRow | undefined\n all(...args: unknown[]): SQLRow[]\n}\n\nexport interface SQLiteDB {\n exec(sql: string): void\n prepare(sql: string): SQLStatement\n close(): void\n}\n\n// ─── Factory ─────────────────────────────────────────────────────────────────\n\nexport function openSQLite(path: string): SQLiteDB {\n if (isBun) {\n const { Database } = _require('bun:sqlite') as {\n Database: new (path: string) => unknown\n }\n return new Database(path) as unknown as SQLiteDB\n }\n\n const { DatabaseSync } = _require('node:sqlite') as {\n DatabaseSync: new (path: string) => unknown\n }\n return new DatabaseSync(path) as unknown as SQLiteDB\n}\n\n// ─── last_insert_rowid() helper ───────────────────────────────────────────────\n// Both bun:sqlite and node:sqlite have different return types for stmt.run().\n// Reading last_insert_rowid() directly avoids the inconsistency.\n\nexport function lastInsertId(db: SQLiteDB): number {\n const row = db.prepare('SELECT last_insert_rowid() AS id').get() as { id: number }\n return row.id\n}\n"],"mappings":";AAAA,SAAS,YAAY,WAAW,QAAQ,gBAAgB;AACxD,SAAS,eAAe;;;ACDxB,SAAS,qBAAqB;AAE9B,IAAM,WAAW,cAAc,YAAY,GAAG;AAC9C,IAAM,QAAQ,SAAS,QAAQ;AAqBxB,SAAS,WAAW,MAAwB;AACjD,MAAI,OAAO;AACT,UAAM,EAAE,SAAS,IAAI,SAAS,YAAY;AAG1C,WAAO,IAAI,SAAS,IAAI;AAAA,EAC1B;AAEA,QAAM,EAAE,aAAa,IAAI,SAAS,aAAa;AAG/C,SAAO,IAAI,aAAa,IAAI;AAC9B;AAMO,SAAS,aAAa,IAAsB;AACjD,QAAM,MAAM,GAAG,QAAQ,kCAAkC,EAAE,IAAI;AAC/D,SAAO,IAAI;AACb;;;ADtCA,IAAM,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAmER,IAAM,eAAN,MAAuC;AAAA,EACpC;AAAA,EAER,YAAY,QAAgB;AAC1B,cAAU,QAAQ,MAAM,GAAG,EAAE,WAAW,KAAK,CAAC;AAG9C,QAAI,WAAW,MAAM,GAAG;AACtB,YAAM,MAAM,GAAG,MAAM;AACrB,YAAM,MAAM,GAAG,MAAM;AACrB,UAAI,WAAW,GAAG,KAAK,WAAW,GAAG,KAAK,SAAS,GAAG,EAAE,SAAS,GAAG;AAClE,eAAO,KAAK,EAAE,OAAO,KAAK,CAAC;AAC3B,eAAO,KAAK,EAAE,OAAO,KAAK,CAAC;AAAA,MAC7B;AAAA,IACF;AACA,SAAK,KAAK,WAAW,MAAM;AAC3B,SAAK,GAAG,KAAK,2BAA2B;AACxC,SAAK,GAAG,KAAK,0BAA0B;AAAA,EACzC;AAAA,EAEA,MAAM,eAA8B;AAClC,SAAK,GAAG,KAAK,MAAM;AAAA,EACrB;AAAA,EAEA,MAAM,MAAS,KAAa,SAAoB,CAAC,GAAiB;AAChE,WAAO,KAAK,GAAG,QAAQ,GAAG,EAAE,IAAI,GAAG,MAAM;AAAA,EAC3C;AAAA,EAEA,MAAM,SAAY,KAAa,SAAoB,CAAC,GAAsB;AACxE,WAAQ,KAAK,GAAG,QAAQ,GAAG,EAAE,IAAI,GAAG,MAAM,KAAsB;AAAA,EAClE;AAAA,EAEA,MAAM,OAAO,KAAa,SAAoB,CAAC,GAAoB;AACjE,SAAK,GAAG,QAAQ,GAAG,EAAE,IAAI,GAAG,MAAM;AAClC,WAAO,aAAa,KAAK,EAAE;AAAA,EAC7B;AAAA,EAEA,MAAM,KAAK,KAAa,SAAoB,CAAC,GAAoB;AAC/D,UAAM,SAAS,KAAK,GAAG,QAAQ,GAAG,EAAE,IAAI,GAAG,MAAM;AACjD,WAAQ,OAAgC,WAAW;AAAA,EACrD;AAAA,EAEA,MAAM,QAAQ,KAA4B;AACxC,SAAK,GAAG,KAAK,GAAG;AAAA,EAClB;AAAA,EAEA,MAAM,YAAe,IAA8C;AACjE,SAAK,GAAG,KAAK,iBAAiB;AAC9B,QAAI;AACF,YAAM,SAAS,MAAM,GAAG,IAAI;AAC5B,WAAK,GAAG,KAAK,QAAQ;AACrB,aAAO;AAAA,IACT,SAAS,KAAK;AACZ,WAAK,GAAG,KAAK,UAAU;AACvB,YAAM;AAAA,IACR;AAAA,EACF;AAAA,EAEA,MAAM,QAAuB;AAC3B,SAAK,GAAG,MAAM;AAAA,EAChB;AACF;","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cardor/agent-harness-kit",
3
- "version": "0.18.0",
3
+ "version": "1.1.0",
4
4
  "description": "CLI scaffolding for multi-agent harness systems",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -44,10 +44,14 @@
44
44
  "@modelcontextprotocol/sdk": "^1.10.2",
45
45
  "cli-table3": "^0.6.5",
46
46
  "commander": "^12.1.0",
47
+ "drizzle-orm": "^0.45.2",
47
48
  "hono": "^4.6.0",
48
49
  "jiti": "^2.4.2",
50
+ "mysql2": "^3.22.3",
49
51
  "open": "^10.1.0",
50
52
  "picocolors": "^1.1.1",
53
+ "postgres": "^3.4.9",
54
+ "valibot": "^1.4.0",
51
55
  "ws": "^8.18.0"
52
56
  },
53
57
  "devDependencies": {
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/core/config.ts"],"sourcesContent":["import { existsSync } from 'node:fs'\nimport { join } from 'node:path'\nimport { createJiti } from 'jiti'\n\nimport type { HarnessConfig } from '@/types'\n\nconst CONFIG_NAMES = [\n 'agent-harness-kit.config.ts',\n 'agent-harness-kit.config',\n 'agent-harness-kit.config.mjs',\n]\n\nexport function findConfigFile(cwd: string): string | null {\n for (const name of CONFIG_NAMES) {\n const candidate = join(cwd, name)\n if (existsSync(candidate)) return candidate\n }\n return null\n}\n\nexport async function loadConfig(cwd: string): Promise<HarnessConfig> {\n const configPath = findConfigFile(cwd)\n if (!configPath) {\n throw new Error('No agent-harness-kit.config found. Run: ahk init')\n }\n\n const jiti = createJiti(import.meta.url)\n const mod = await jiti.import(configPath) as { default?: HarnessConfig } | HarnessConfig\n const config = (mod as { default?: HarnessConfig }).default ?? (mod as HarnessConfig)\n\n if (!config || typeof config !== 'object') {\n throw new Error(`agent-harness-kit.config must export a default HarnessConfig object.`)\n }\n\n return applyDefaults(config as HarnessConfig)\n}\n\nexport function defineHarness(config: HarnessConfig): HarnessConfig {\n return config\n}\n\nfunction applyDefaults(config: HarnessConfig): HarnessConfig {\n const c = config as Partial<HarnessConfig>\n return {\n ...config,\n provider: c.provider ?? 'claude-code',\n project: {\n docsPath: './docs',\n agentsMd: './AGENTS.md',\n ...c.project,\n } as HarnessConfig['project'],\n agents: {\n lead: { instructionsPath: null },\n explorer: { instructionsPath: null },\n builder: { instructionsPath: null },\n reviewer: { instructionsPath: null },\n custom: [],\n ...c.agents,\n } as HarnessConfig['agents'],\n storage: {\n dir: '.harness',\n dbPath: '.harness/harness.db',\n tasks: { adapter: 'local' as const },\n sections: {\n toolsUsed: true,\n filesModified: true,\n result: true,\n blockers: true,\n nextSteps: false,\n },\n markdownFallback: { enabled: true, path: '.harness/current.md' },\n ...c.storage,\n } as HarnessConfig['storage'],\n health: {\n scriptPath: './health.sh',\n required: true,\n ...c.health,\n },\n tools: {\n mcp: { enabled: true, port: 3742 },\n scripts: { enabled: true, outputDir: './.harness/scripts' },\n ...c.tools,\n } as HarnessConfig['tools'],\n }\n}\n"],"mappings":";AAAA,SAAS,kBAAkB;AAC3B,SAAS,YAAY;AACrB,SAAS,kBAAkB;AAI3B,IAAM,eAAe;AAAA,EACnB;AAAA,EACA;AAAA,EACA;AACF;AAEO,SAAS,eAAe,KAA4B;AACzD,aAAW,QAAQ,cAAc;AAC/B,UAAM,YAAY,KAAK,KAAK,IAAI;AAChC,QAAI,WAAW,SAAS,EAAG,QAAO;AAAA,EACpC;AACA,SAAO;AACT;AAEA,eAAsB,WAAW,KAAqC;AACpE,QAAM,aAAa,eAAe,GAAG;AACrC,MAAI,CAAC,YAAY;AACf,UAAM,IAAI,MAAM,kDAAkD;AAAA,EACpE;AAEA,QAAM,OAAO,WAAW,YAAY,GAAG;AACvC,QAAM,MAAM,MAAM,KAAK,OAAO,UAAU;AACxC,QAAM,SAAU,IAAoC,WAAY;AAEhE,MAAI,CAAC,UAAU,OAAO,WAAW,UAAU;AACzC,UAAM,IAAI,MAAM,sEAAsE;AAAA,EACxF;AAEA,SAAO,cAAc,MAAuB;AAC9C;AAEO,SAAS,cAAc,QAAsC;AAClE,SAAO;AACT;AAEA,SAAS,cAAc,QAAsC;AAC3D,QAAM,IAAI;AACV,SAAO;AAAA,IACL,GAAG;AAAA,IACH,UAAU,EAAE,YAAY;AAAA,IACxB,SAAS;AAAA,MACP,UAAU;AAAA,MACV,UAAU;AAAA,MACV,GAAG,EAAE;AAAA,IACP;AAAA,IACA,QAAQ;AAAA,MACN,MAAM,EAAE,kBAAkB,KAAK;AAAA,MAC/B,UAAU,EAAE,kBAAkB,KAAK;AAAA,MACnC,SAAS,EAAE,kBAAkB,KAAK;AAAA,MAClC,UAAU,EAAE,kBAAkB,KAAK;AAAA,MACnC,QAAQ,CAAC;AAAA,MACT,GAAG,EAAE;AAAA,IACP;AAAA,IACA,SAAS;AAAA,MACP,KAAK;AAAA,MACL,QAAQ;AAAA,MACR,OAAO,EAAE,SAAS,QAAiB;AAAA,MACnC,UAAU;AAAA,QACR,WAAW;AAAA,QACX,eAAe;AAAA,QACf,QAAQ;AAAA,QACR,UAAU;AAAA,QACV,WAAW;AAAA,MACb;AAAA,MACA,kBAAkB,EAAE,SAAS,MAAM,MAAM,sBAAsB;AAAA,MAC/D,GAAG,EAAE;AAAA,IACP;AAAA,IACA,QAAQ;AAAA,MACN,YAAY;AAAA,MACZ,UAAU;AAAA,MACV,GAAG,EAAE;AAAA,IACP;AAAA,IACA,OAAO;AAAA,MACL,KAAK,EAAE,SAAS,MAAM,MAAM,KAAK;AAAA,MACjC,SAAS,EAAE,SAAS,MAAM,WAAW,qBAAqB;AAAA,MAC1D,GAAG,EAAE;AAAA,IACP;AAAA,EACF;AACF;","names":[]}