skiv 0.0.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/LICENSE.txt +7 -0
- package/README.md +27 -0
- package/bin/________________________skiv.ts +3 -0
- package/bin/skiv.js +3 -0
- package/dist/cjs/IssueService.js +236 -0
- package/dist/cjs/Logger.js +39 -0
- package/dist/cjs/Member.js +83 -0
- package/dist/cjs/cli.js +94 -0
- package/dist/cjs/commands/Command.js +66 -0
- package/dist/cjs/commands/InitCommand.js +27 -0
- package/dist/cjs/commands/IssueCommand.js +39 -0
- package/dist/cjs/commands/RunCommand.js +57 -0
- package/dist/cjs/commands/StartCommand.js +69 -0
- package/dist/cjs/db/index.js +27 -0
- package/dist/cjs/db/migrations.js +36 -0
- package/dist/cjs/db/schema.js +25 -0
- package/dist/cjs/utils.js +32 -0
- package/dist/esm/IssueService.d.ts +65 -0
- package/dist/esm/IssueService.d.ts.map +1 -0
- package/dist/esm/IssueService.js +232 -0
- package/dist/esm/Logger.d.ts +21 -0
- package/dist/esm/Logger.d.ts.map +1 -0
- package/dist/esm/Logger.js +36 -0
- package/dist/esm/Member.d.ts +21 -0
- package/dist/esm/Member.d.ts.map +1 -0
- package/dist/esm/Member.js +77 -0
- package/dist/esm/cli.d.ts +2 -0
- package/dist/esm/cli.d.ts.map +1 -0
- package/dist/esm/cli.js +89 -0
- package/dist/esm/commands/Command.d.ts +19 -0
- package/dist/esm/commands/Command.d.ts.map +1 -0
- package/dist/esm/commands/Command.js +30 -0
- package/dist/esm/commands/InitCommand.d.ts +5 -0
- package/dist/esm/commands/InitCommand.d.ts.map +1 -0
- package/dist/esm/commands/InitCommand.js +21 -0
- package/dist/esm/commands/IssueCommand.d.ts +10 -0
- package/dist/esm/commands/IssueCommand.d.ts.map +1 -0
- package/dist/esm/commands/IssueCommand.js +33 -0
- package/dist/esm/commands/RunCommand.d.ts +5 -0
- package/dist/esm/commands/RunCommand.d.ts.map +1 -0
- package/dist/esm/commands/RunCommand.js +18 -0
- package/dist/esm/commands/StartCommand.d.ts +28 -0
- package/dist/esm/commands/StartCommand.d.ts.map +1 -0
- package/dist/esm/commands/StartCommand.js +63 -0
- package/dist/esm/db/index.d.ts +6 -0
- package/dist/esm/db/index.d.ts.map +1 -0
- package/dist/esm/db/index.js +19 -0
- package/dist/esm/db/migrations.d.ts +3 -0
- package/dist/esm/db/migrations.d.ts.map +1 -0
- package/dist/esm/db/migrations.js +33 -0
- package/dist/esm/db/schema.d.ts +46 -0
- package/dist/esm/db/schema.d.ts.map +1 -0
- package/dist/esm/db/schema.js +20 -0
- package/dist/esm/utils.d.ts +7 -0
- package/dist/esm/utils.d.ts.map +1 -0
- package/dist/esm/utils.js +25 -0
- package/package.json +42 -0
- package/skeleton/config.yaml +6 -0
- package/skeleton/roles/corder/.claude/settings.local.json +13 -0
- package/skeleton/roles/corder/CLAUDE.md +66 -0
- package/skeleton/roles/corder/custom.ts +62 -0
- package/skeleton/roles/reviewer/.claude/settings.local.json +13 -0
- package/skeleton/roles/reviewer/CLAUDE.md +59 -0
- package/skeleton/roles/reviewer/custom.ts +54 -0
- package/skeleton/roles/se/.claude/settings.local.json +13 -0
- package/skeleton/roles/se/CLAUDE.md +41 -0
- package/skeleton/workspaces/.gitkeep +0 -0
package/LICENSE.txt
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
Copyright 2026 masashi.machida
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
4
|
+
|
|
5
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
6
|
+
|
|
7
|
+
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# SKIV
|
|
2
|
+
|
|
3
|
+
## Installation
|
|
4
|
+
|
|
5
|
+
```shell
|
|
6
|
+
npm i -g skiv
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
## Usage
|
|
10
|
+
|
|
11
|
+
### Initialize
|
|
12
|
+
|
|
13
|
+
cd to your project directory
|
|
14
|
+
|
|
15
|
+
```shell
|
|
16
|
+
npx skiv init
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
generate `.skiv` directory
|
|
20
|
+
|
|
21
|
+
edit `.skiv/config.yaml` and `.skiv/roles/*`
|
|
22
|
+
|
|
23
|
+
### Start
|
|
24
|
+
|
|
25
|
+
```shell
|
|
26
|
+
npx skiv start
|
|
27
|
+
```
|
package/bin/skiv.js
ADDED
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.IssueService = void 0;
|
|
4
|
+
const schema_1 = require("./db/schema");
|
|
5
|
+
const utils_1 = require("./utils");
|
|
6
|
+
/**
|
|
7
|
+
* Provides services and operations for managing issues in the system.
|
|
8
|
+
*/
|
|
9
|
+
class IssueService {
|
|
10
|
+
db;
|
|
11
|
+
constructor(db) {
|
|
12
|
+
this.db = db;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Creates a new issue with the given title, priority, and optional description.
|
|
16
|
+
*
|
|
17
|
+
* @param {string} title - The title of the issue to be created. This parameter is required.
|
|
18
|
+
* @param {string} [priority="mid"] - The priority level of the issue. Defaults to "mid" if not provided.
|
|
19
|
+
* @param {string | null} description - A description of the issue. Defaults to null if not provided.
|
|
20
|
+
* @return {Promise<Issue>} A promise that resolves to the created Issue object.
|
|
21
|
+
* @throws {Error} If the title is not provided.
|
|
22
|
+
* @throws {Error} If the provided priority value is invalid.
|
|
23
|
+
* @throws {Error} If the issue creation fails.
|
|
24
|
+
*/
|
|
25
|
+
async create(title, priority = "mid", description = null) {
|
|
26
|
+
if (!title) {
|
|
27
|
+
throw new Error("title required");
|
|
28
|
+
}
|
|
29
|
+
if (!(0, schema_1.isIssuePriority)(priority)) {
|
|
30
|
+
throw new Error(`Invalid priority: ${priority} (valid values: ${schema_1.ISSUE_PRIORITIES.join(", ")})`);
|
|
31
|
+
}
|
|
32
|
+
const row = await this.db
|
|
33
|
+
.insertInto('issues')
|
|
34
|
+
.values({
|
|
35
|
+
status: "open",
|
|
36
|
+
title,
|
|
37
|
+
assignee: null,
|
|
38
|
+
priority,
|
|
39
|
+
description,
|
|
40
|
+
created_at: new Date().toISOString(),
|
|
41
|
+
updated_at: new Date().toISOString()
|
|
42
|
+
})
|
|
43
|
+
.returning('id')
|
|
44
|
+
.executeTakeFirst();
|
|
45
|
+
if (!row) {
|
|
46
|
+
throw new Error("Failed to create issue");
|
|
47
|
+
}
|
|
48
|
+
const issue = await this.findById(row.id);
|
|
49
|
+
if (!issue) {
|
|
50
|
+
throw new Error("Failed to retrieve newly created issue");
|
|
51
|
+
}
|
|
52
|
+
return issue;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Retrieves a list of issues filtered by their status.
|
|
56
|
+
*
|
|
57
|
+
* @param {string | undefined} status - The status to filter issues by. If undefined, retrieves all issues regardless of status.
|
|
58
|
+
* @return {Promise<Partial<Issue>[]>} A promise resolving to a list of issues matching the specified status.
|
|
59
|
+
*/
|
|
60
|
+
async listIssues(status) {
|
|
61
|
+
if (status !== undefined && !(0, schema_1.isIssueStatus)(status)) {
|
|
62
|
+
throw new Error(`Invalid status: ${status}`);
|
|
63
|
+
}
|
|
64
|
+
let query = this.db
|
|
65
|
+
.selectFrom('issues')
|
|
66
|
+
.selectAll();
|
|
67
|
+
if (status) {
|
|
68
|
+
query = query.where('status', '=', status);
|
|
69
|
+
}
|
|
70
|
+
return query
|
|
71
|
+
.orderBy('created_at', 'asc')
|
|
72
|
+
.execute();
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Retrieves the first assigned issue for a given assignee where the issue status is 'in_progress'.
|
|
76
|
+
* If no assigned issue is found, it attempts to assign the next available issue.
|
|
77
|
+
*
|
|
78
|
+
* @param {string} assignee - The username or identifier of the assignee whose issue needs to be fetched.
|
|
79
|
+
* @param {IssueStatus} status - The status of the issue to be fetched. Defaults to 'in_progress'.
|
|
80
|
+
* @return {Promise<Issue | null>} A promise that resolves to the assigned issue if found, or null if no matching issue is found.
|
|
81
|
+
*/
|
|
82
|
+
async getAssignedIssue(assignee, status) {
|
|
83
|
+
const row = await this.db.selectFrom('issues')
|
|
84
|
+
.select(['id'])
|
|
85
|
+
.where('status', '=', status)
|
|
86
|
+
.where('assignee', '=', assignee)
|
|
87
|
+
.orderBy('id', 'asc')
|
|
88
|
+
.executeTakeFirst();
|
|
89
|
+
if (!row)
|
|
90
|
+
return null;
|
|
91
|
+
return this.findById(row.id);
|
|
92
|
+
}
|
|
93
|
+
async getNextIssue(fromStatus, toStatus, assignee) {
|
|
94
|
+
const id = await (0, utils_1.executeImmediate)(this.db, async (trx) => {
|
|
95
|
+
let query = trx
|
|
96
|
+
.selectFrom('issues')
|
|
97
|
+
.selectAll()
|
|
98
|
+
.where('status', '=', fromStatus);
|
|
99
|
+
// .where('assignee', 'is', null)
|
|
100
|
+
query = query.orderBy('created_at', 'asc');
|
|
101
|
+
const nextIssue = await query.executeTakeFirst();
|
|
102
|
+
if (!nextIssue)
|
|
103
|
+
return null;
|
|
104
|
+
await trx.updateTable('issues')
|
|
105
|
+
.set({
|
|
106
|
+
status: toStatus,
|
|
107
|
+
assignee: assignee,
|
|
108
|
+
updated_at: new Date().toISOString()
|
|
109
|
+
})
|
|
110
|
+
.where('id', '=', nextIssue.id)
|
|
111
|
+
.execute();
|
|
112
|
+
return nextIssue.id;
|
|
113
|
+
});
|
|
114
|
+
if (!id)
|
|
115
|
+
return null;
|
|
116
|
+
return this.findById(id);
|
|
117
|
+
}
|
|
118
|
+
// /**
|
|
119
|
+
// * Assigns the next available issue to a specified assignee.
|
|
120
|
+
// * The next issue is determined based on its creation time,
|
|
121
|
+
// * considering only open issues without an assigned user.
|
|
122
|
+
// *
|
|
123
|
+
// * @param {string} assignee - The username or identifier of the person to whom the issue will be assigned.
|
|
124
|
+
// * @return {Promise<Issue | null>} A promise that resolves to the assigned issue object if successful, or null if no issue is available.
|
|
125
|
+
// */
|
|
126
|
+
// public async assignNextIssue(assignee: string): Promise<Issue | null> {
|
|
127
|
+
// const nextIssue = await this.db
|
|
128
|
+
// .selectFrom('issues')
|
|
129
|
+
// .selectAll()
|
|
130
|
+
// .where('status', '=', 'open')
|
|
131
|
+
// .where('assignee', 'is', null)
|
|
132
|
+
// .orderBy('created_at', 'asc')
|
|
133
|
+
// .executeTakeFirst()
|
|
134
|
+
//
|
|
135
|
+
// if (!nextIssue) {
|
|
136
|
+
// return null
|
|
137
|
+
// }
|
|
138
|
+
//
|
|
139
|
+
// return this.assignIssue(nextIssue.id, assignee)
|
|
140
|
+
// }
|
|
141
|
+
/**
|
|
142
|
+
* Adds a comment to the specified issue.
|
|
143
|
+
*
|
|
144
|
+
* @param {number} issueId - The unique identifier of the issue to comment on.
|
|
145
|
+
* @param {string} by - The username or identifier of the person making the comment.
|
|
146
|
+
* @param {string} message - The content of the comment.
|
|
147
|
+
* @return {Promise<Issue>} A promise that resolves to the updated issue object with the new comment included.
|
|
148
|
+
*/
|
|
149
|
+
async comment(issueId, by, message) {
|
|
150
|
+
const issue = await this.findById(issueId);
|
|
151
|
+
if (!issue) {
|
|
152
|
+
throw new Error(`Issue #${issueId} not found`);
|
|
153
|
+
}
|
|
154
|
+
await this.db
|
|
155
|
+
.insertInto('comments')
|
|
156
|
+
.values({
|
|
157
|
+
issue_id: issue.id,
|
|
158
|
+
by,
|
|
159
|
+
message,
|
|
160
|
+
at: new Date().toISOString()
|
|
161
|
+
})
|
|
162
|
+
.execute();
|
|
163
|
+
const returnIssue = await this.findById(issueId);
|
|
164
|
+
if (!returnIssue) {
|
|
165
|
+
throw new Error(`Failed to retrieve issue #${issueId} after commenting`);
|
|
166
|
+
}
|
|
167
|
+
return returnIssue;
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Updates the status of an issue.
|
|
171
|
+
*
|
|
172
|
+
* @param {number} issueId - The ID of the issue to be updated.
|
|
173
|
+
* @param {IssueStatus} status - The new status to assign to the issue.
|
|
174
|
+
* @returns {Promise<Issue>} The updated issue.
|
|
175
|
+
* @throws {Error} If the issue is not found.
|
|
176
|
+
*/
|
|
177
|
+
async updateStatus(issueId, status) {
|
|
178
|
+
const issue = await this.findById(issueId);
|
|
179
|
+
if (!issue) {
|
|
180
|
+
throw new Error(`Issue #${issueId} not found`);
|
|
181
|
+
}
|
|
182
|
+
await this.db
|
|
183
|
+
.updateTable('issues')
|
|
184
|
+
.set({
|
|
185
|
+
status,
|
|
186
|
+
updated_at: new Date().toISOString()
|
|
187
|
+
})
|
|
188
|
+
.where('id', '=', issueId)
|
|
189
|
+
.execute();
|
|
190
|
+
const returnIssue = await this.findById(issueId);
|
|
191
|
+
if (!returnIssue) {
|
|
192
|
+
throw new Error(`Failed to retrieve issue #${issueId} after updating status`);
|
|
193
|
+
}
|
|
194
|
+
return returnIssue;
|
|
195
|
+
}
|
|
196
|
+
async updateAssignee(issueId, assignee) {
|
|
197
|
+
const issue = await this.findById(issueId);
|
|
198
|
+
if (!issue) {
|
|
199
|
+
throw new Error(`Issue #${issueId} not found`);
|
|
200
|
+
}
|
|
201
|
+
await this.db
|
|
202
|
+
.updateTable('issues')
|
|
203
|
+
.set({
|
|
204
|
+
assignee,
|
|
205
|
+
updated_at: new Date().toISOString()
|
|
206
|
+
})
|
|
207
|
+
.where('id', '=', issueId)
|
|
208
|
+
.execute();
|
|
209
|
+
const returnIssue = await this.findById(issueId);
|
|
210
|
+
if (!returnIssue) {
|
|
211
|
+
throw new Error(`Failed to retrieve issue #${issueId} after updating assignee`);
|
|
212
|
+
}
|
|
213
|
+
return returnIssue;
|
|
214
|
+
}
|
|
215
|
+
/**
|
|
216
|
+
* Finds an issue by its unique identifier.
|
|
217
|
+
*
|
|
218
|
+
* @param {number} id - The unique identifier of the issue to retrieve.
|
|
219
|
+
* @return {Promise<Issue | null>} A promise that resolves to the issue with its associated comments if found, or null if no issue is found with the given identifier.
|
|
220
|
+
*/
|
|
221
|
+
async findById(id) {
|
|
222
|
+
const row = await this.db.selectFrom('issues')
|
|
223
|
+
.selectAll()
|
|
224
|
+
.where('id', '=', id)
|
|
225
|
+
.executeTakeFirst();
|
|
226
|
+
if (!row)
|
|
227
|
+
return null;
|
|
228
|
+
const comments = await this.db.selectFrom('comments')
|
|
229
|
+
.selectAll()
|
|
230
|
+
.where('issue_id', '=', id)
|
|
231
|
+
.orderBy('at', 'asc')
|
|
232
|
+
.execute();
|
|
233
|
+
return { ...row, comments };
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
exports.IssueService = IssueService;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const LogLevels = {
|
|
4
|
+
DEBUG: 0,
|
|
5
|
+
INFO: 1,
|
|
6
|
+
WARNING: 2,
|
|
7
|
+
ERROR: 3,
|
|
8
|
+
CRITICAL: 4,
|
|
9
|
+
};
|
|
10
|
+
class Logger {
|
|
11
|
+
level;
|
|
12
|
+
constructor(level) {
|
|
13
|
+
this.level = level;
|
|
14
|
+
}
|
|
15
|
+
log(message, level) {
|
|
16
|
+
if (LogLevels[level] >= LogLevels[this.level]) {
|
|
17
|
+
console.log(message);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
debug(message) {
|
|
21
|
+
this.log(message, "DEBUG");
|
|
22
|
+
}
|
|
23
|
+
info(message) {
|
|
24
|
+
this.log(message, "INFO");
|
|
25
|
+
}
|
|
26
|
+
warn(message) {
|
|
27
|
+
this.log(message, "WARNING");
|
|
28
|
+
}
|
|
29
|
+
error(message) {
|
|
30
|
+
this.log(`\x1b[31m${message}\x1b[0m`, "ERROR");
|
|
31
|
+
}
|
|
32
|
+
critical(message) {
|
|
33
|
+
this.log(`\x1b[32m${message}\x1b[0m`, "CRITICAL");
|
|
34
|
+
}
|
|
35
|
+
static log(message) {
|
|
36
|
+
console.log(message);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
exports.default = Logger;
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const child_process_1 = require("child_process");
|
|
7
|
+
const IssueService_1 = require("./IssueService");
|
|
8
|
+
const Logger_1 = __importDefault(require("./Logger"));
|
|
9
|
+
const utils_1 = require("./utils");
|
|
10
|
+
class Member {
|
|
11
|
+
LOG_LEVEL = "INFO";
|
|
12
|
+
issueService;
|
|
13
|
+
logger;
|
|
14
|
+
NAME;
|
|
15
|
+
WORKSPACE;
|
|
16
|
+
LOOP_INTERVAL_MSEC = 5000;
|
|
17
|
+
MODEL;
|
|
18
|
+
PROMPT;
|
|
19
|
+
constructor(db, name, workspace, model) {
|
|
20
|
+
this.issueService = new IssueService_1.IssueService(db);
|
|
21
|
+
this.logger = new Logger_1.default(this.LOG_LEVEL);
|
|
22
|
+
this.NAME = name;
|
|
23
|
+
this.WORKSPACE = workspace;
|
|
24
|
+
this.MODEL = model;
|
|
25
|
+
this.PROMPT = `あなたの名前は${this.NAME}です。CLAUDE.mdに沿って処理をしてください。`;
|
|
26
|
+
if (!this.NAME) {
|
|
27
|
+
throw new Error('arg 0 name required');
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
async loop() {
|
|
31
|
+
while (true) {
|
|
32
|
+
// clearScreen()
|
|
33
|
+
this.logger.debug("LOOP START");
|
|
34
|
+
const beforeValue = await this.before();
|
|
35
|
+
if (beforeValue) {
|
|
36
|
+
const response = await this.execute();
|
|
37
|
+
await this.after(response);
|
|
38
|
+
}
|
|
39
|
+
this.logger.debug("LOOP END");
|
|
40
|
+
await (0, utils_1.sleep)(this.LOOP_INTERVAL_MSEC);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
async before() {
|
|
44
|
+
return Promise.resolve(true);
|
|
45
|
+
}
|
|
46
|
+
async execute() {
|
|
47
|
+
return this.spawn("claude", [
|
|
48
|
+
"--model", this.MODEL,
|
|
49
|
+
"--permission-mode", "acceptEdits",
|
|
50
|
+
"-p", `"${this.PROMPT}"`
|
|
51
|
+
], {
|
|
52
|
+
stdio: ["inherit", "pipe", "pipe"],
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
async after(response) {
|
|
56
|
+
}
|
|
57
|
+
async spawn(command, args, options = {}) {
|
|
58
|
+
return new Promise((resolve, reject) => {
|
|
59
|
+
Logger_1.default.log(`💻 ${command} ${args.join(' ')}`);
|
|
60
|
+
let stdout = '';
|
|
61
|
+
const child = (0, child_process_1.spawn)(command, args, options);
|
|
62
|
+
child.stdout?.on("data", (chunk) => {
|
|
63
|
+
stdout += chunk.toString();
|
|
64
|
+
this.logger.info(chunk.toString());
|
|
65
|
+
});
|
|
66
|
+
child.stderr?.on("data", (chunk) => {
|
|
67
|
+
this.logger.error(chunk.toString());
|
|
68
|
+
});
|
|
69
|
+
child.on("close", (code) => {
|
|
70
|
+
if (code === 0) {
|
|
71
|
+
resolve(stdout);
|
|
72
|
+
}
|
|
73
|
+
else {
|
|
74
|
+
reject(new Error(`Process exited with code ${code}`));
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
child.on("error", (err) => {
|
|
78
|
+
reject(err);
|
|
79
|
+
});
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
exports.default = Member;
|
package/dist/cjs/cli.js
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const commander_1 = require("commander");
|
|
7
|
+
const RunCommand_1 = __importDefault(require("./commands/RunCommand"));
|
|
8
|
+
const InitCommand_1 = __importDefault(require("./commands/InitCommand"));
|
|
9
|
+
const IssueCommand_1 = __importDefault(require("./commands/IssueCommand"));
|
|
10
|
+
const StartCommand_1 = __importDefault(require("./commands/StartCommand"));
|
|
11
|
+
const schema_1 = require("./db/schema");
|
|
12
|
+
commander_1.program
|
|
13
|
+
.name('skiv')
|
|
14
|
+
.description('Claude Code orchestration tool')
|
|
15
|
+
.version('0.0.1');
|
|
16
|
+
// init
|
|
17
|
+
commander_1.program.command('init')
|
|
18
|
+
.description('Initialize skiv')
|
|
19
|
+
.action(async () => {
|
|
20
|
+
const command = new InitCommand_1.default();
|
|
21
|
+
await command.execute();
|
|
22
|
+
});
|
|
23
|
+
// start
|
|
24
|
+
commander_1.program.command('start')
|
|
25
|
+
.description('run skiv')
|
|
26
|
+
.action(async () => {
|
|
27
|
+
const command = new StartCommand_1.default();
|
|
28
|
+
await command.execute();
|
|
29
|
+
});
|
|
30
|
+
// run
|
|
31
|
+
commander_1.program.command('run')
|
|
32
|
+
.description('dispatch member')
|
|
33
|
+
.argument('<name>', 'member name')
|
|
34
|
+
.argument('[model]', 'model name', 'sonnet')
|
|
35
|
+
.action(async (name, model) => {
|
|
36
|
+
const command = new RunCommand_1.default();
|
|
37
|
+
await command.execute(name, model);
|
|
38
|
+
});
|
|
39
|
+
// issue
|
|
40
|
+
const issue = commander_1.program.command('issue')
|
|
41
|
+
.description('Issue manager');
|
|
42
|
+
// create
|
|
43
|
+
issue.command('create')
|
|
44
|
+
.description('create a new issue')
|
|
45
|
+
.argument('<title>', 'issue title')
|
|
46
|
+
.addArgument(new commander_1.Argument('[priority]', 'issue priority')
|
|
47
|
+
.choices(['low', 'mid', 'high'])
|
|
48
|
+
.default('mid'))
|
|
49
|
+
.argument('[description]', 'spec')
|
|
50
|
+
.action(async (title, priority, description) => {
|
|
51
|
+
const command = new IssueCommand_1.default();
|
|
52
|
+
await command.create(title, priority, description);
|
|
53
|
+
});
|
|
54
|
+
// list
|
|
55
|
+
issue.command('list')
|
|
56
|
+
.description('list issues')
|
|
57
|
+
.addOption(new commander_1.Option('-s, --status <string>', 'filter by status')
|
|
58
|
+
.choices(schema_1.ISSUE_STATUSES))
|
|
59
|
+
.action(async (options) => {
|
|
60
|
+
const command = new IssueCommand_1.default();
|
|
61
|
+
await command.list(options.status);
|
|
62
|
+
});
|
|
63
|
+
// assign
|
|
64
|
+
issue.command('assign')
|
|
65
|
+
.description('assign an issue')
|
|
66
|
+
.argument('<fromStatus>', 'search from status')
|
|
67
|
+
.argument('<toStatus>', 'change to status')
|
|
68
|
+
.argument('<assignee>', 'assignee name')
|
|
69
|
+
.action(async (fromStatus, toStatus, assignee) => {
|
|
70
|
+
const command = new IssueCommand_1.default();
|
|
71
|
+
await command.assign(fromStatus, toStatus, assignee);
|
|
72
|
+
});
|
|
73
|
+
// comment
|
|
74
|
+
issue.command('comment')
|
|
75
|
+
.description('add a comment to an issue')
|
|
76
|
+
.argument('<id>', 'issue id')
|
|
77
|
+
.argument('<by>', 'author name')
|
|
78
|
+
.argument('<message>', 'comment message')
|
|
79
|
+
.action(async (id, by, message) => {
|
|
80
|
+
const command = new IssueCommand_1.default();
|
|
81
|
+
await command.comment(id, by, message);
|
|
82
|
+
});
|
|
83
|
+
// update status
|
|
84
|
+
issue.command('update_status')
|
|
85
|
+
.description('update issue status')
|
|
86
|
+
.argument('<id>', 'issue id')
|
|
87
|
+
.addArgument(new commander_1.Argument('<status>', 'issue status')
|
|
88
|
+
.choices(schema_1.ISSUE_STATUSES))
|
|
89
|
+
.action(async (id, status) => {
|
|
90
|
+
const command = new IssueCommand_1.default();
|
|
91
|
+
await command.updateStatus(id, status);
|
|
92
|
+
});
|
|
93
|
+
// parse
|
|
94
|
+
commander_1.program.parse();
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
const fs_1 = __importStar(require("fs"));
|
|
37
|
+
const path_1 = __importStar(require("path"));
|
|
38
|
+
const js_yaml_1 = require("js-yaml");
|
|
39
|
+
const db_1 = require("../db");
|
|
40
|
+
class Command {
|
|
41
|
+
DIR_NAME = ".skiv";
|
|
42
|
+
CONFIG_FILE_NAME = "config.yaml";
|
|
43
|
+
DB_FILE_NAME = "data.db";
|
|
44
|
+
config;
|
|
45
|
+
db;
|
|
46
|
+
initialize() {
|
|
47
|
+
const rootDir = this.getRootDir();
|
|
48
|
+
const yamlPath = path_1.default.resolve(rootDir, this.CONFIG_FILE_NAME);
|
|
49
|
+
const yaml = fs_1.default.readFileSync(yamlPath, 'utf-8');
|
|
50
|
+
this.config = (0, js_yaml_1.load)(yaml);
|
|
51
|
+
const dbPath = (0, path_1.join)(rootDir, this.DB_FILE_NAME);
|
|
52
|
+
this.db = (0, db_1.createDb)(dbPath);
|
|
53
|
+
}
|
|
54
|
+
getRootDir() {
|
|
55
|
+
let dir = process.cwd();
|
|
56
|
+
while (true) {
|
|
57
|
+
if ((0, fs_1.existsSync)((0, path_1.join)(dir, this.DIR_NAME)))
|
|
58
|
+
return `${dir}/${this.DIR_NAME}`;
|
|
59
|
+
const parent = (0, path_1.dirname)(dir);
|
|
60
|
+
if (parent === dir)
|
|
61
|
+
throw new Error("root not found");
|
|
62
|
+
dir = parent;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
exports.default = Command;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const path_1 = __importDefault(require("path"));
|
|
7
|
+
const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
8
|
+
const Command_1 = __importDefault(require("./Command"));
|
|
9
|
+
class InitCommand extends Command_1.default {
|
|
10
|
+
async execute() {
|
|
11
|
+
const cwd = process.cwd();
|
|
12
|
+
const dest = path_1.default.join(cwd, this.DIR_NAME);
|
|
13
|
+
const src = path_1.default.resolve(__dirname, "../../skeleton");
|
|
14
|
+
if (fs_extra_1.default.existsSync(dest)) {
|
|
15
|
+
console.error(`Error: Directory ${dest} already exists.`);
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
try {
|
|
19
|
+
await fs_extra_1.default.copy(src, dest);
|
|
20
|
+
console.log(`Initialized skiv in ${dest}`);
|
|
21
|
+
}
|
|
22
|
+
catch (err) {
|
|
23
|
+
console.error("Failed to initialize:", err);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
exports.default = InitCommand;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const IssueService_1 = require("../IssueService");
|
|
7
|
+
const Command_1 = __importDefault(require("./Command"));
|
|
8
|
+
class IssueCommand extends Command_1.default {
|
|
9
|
+
async create(title, priority, description) {
|
|
10
|
+
this.initialize();
|
|
11
|
+
const service = new IssueService_1.IssueService(this.db);
|
|
12
|
+
const issue = await service.create(title, priority, description);
|
|
13
|
+
console.log(`issue created: ${issue.id}`);
|
|
14
|
+
}
|
|
15
|
+
async list(status) {
|
|
16
|
+
this.initialize();
|
|
17
|
+
const service = new IssueService_1.IssueService(this.db);
|
|
18
|
+
const issues = await service.listIssues(status);
|
|
19
|
+
console.table(issues);
|
|
20
|
+
}
|
|
21
|
+
async assign(fromStatus, toStatus, assignee) {
|
|
22
|
+
this.initialize();
|
|
23
|
+
const service = new IssueService_1.IssueService(this.db);
|
|
24
|
+
return service.getNextIssue(fromStatus, toStatus, assignee);
|
|
25
|
+
}
|
|
26
|
+
async comment(id, by, message) {
|
|
27
|
+
this.initialize();
|
|
28
|
+
const service = new IssueService_1.IssueService(this.db);
|
|
29
|
+
const issue = await service.comment(id, by, message);
|
|
30
|
+
console.log(`commented: ${issue.id}`);
|
|
31
|
+
}
|
|
32
|
+
async updateStatus(id, status) {
|
|
33
|
+
this.initialize();
|
|
34
|
+
const service = new IssueService_1.IssueService(this.db);
|
|
35
|
+
const issue = await service.updateStatus(id, status);
|
|
36
|
+
console.log(`updated status: ${issue.id} ${issue.status}`);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
exports.default = IssueCommand;
|