@rse/ase 0.0.27 → 0.0.28
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/dst/ase-diagram.js +184 -115
- package/dst/ase-hook.js +29 -0
- package/dst/ase-mcp.js +1 -1
- package/dst/ase-persona.js +87 -0
- package/dst/ase-service.js +182 -403
- package/dst/ase-task.js +305 -95
- package/dst/ase.js +3 -3
- package/package.json +1 -1
package/dst/ase-task.js
CHANGED
|
@@ -4,96 +4,138 @@
|
|
|
4
4
|
** Licensed under GPL 3.0 <https://spdx.org/licenses/GPL-3.0-only>
|
|
5
5
|
*/
|
|
6
6
|
import path from "node:path";
|
|
7
|
-
import os from "node:os";
|
|
8
7
|
import fs from "node:fs";
|
|
9
8
|
import { execaSync } from "execa";
|
|
10
9
|
import { DateTime } from "luxon";
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
return
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
each entry's `mtime` is set to the `plan.md` modification time formatted
|
|
51
|
-
as "YYYY-MM-DD HH:MM", otherwise it is left undefined */
|
|
52
|
-
export const taskList = (verbose = false) => {
|
|
53
|
-
const dir = path.join(os.homedir(), ".ase", "task");
|
|
54
|
-
if (!fs.existsSync(dir))
|
|
55
|
-
return [];
|
|
56
|
-
const out = [];
|
|
57
|
-
for (const entry of fs.readdirSync(dir)) {
|
|
58
|
-
if (!/^[A-Za-z0-9-]+$/.test(entry))
|
|
59
|
-
continue;
|
|
60
|
-
const file = path.join(dir, entry, "plan.md");
|
|
10
|
+
import { isScalar } from "yaml";
|
|
11
|
+
import { z } from "zod";
|
|
12
|
+
import { Config, configSchema, parseScope } from "./ase-config.js";
|
|
13
|
+
/* reusable functionality: persisted task plans under
|
|
14
|
+
<project>/.ase/task/<id>/plan.md */
|
|
15
|
+
export class Task {
|
|
16
|
+
/* validate the task id to keep it safe as a filename component */
|
|
17
|
+
static validateId(id) {
|
|
18
|
+
if (typeof id !== "string" || id.length === 0)
|
|
19
|
+
throw new Error("task: id must be a non-empty string");
|
|
20
|
+
if (!/^[A-Za-z0-9-]+$/.test(id))
|
|
21
|
+
throw new Error("task: id must match [A-Za-z0-9-]+");
|
|
22
|
+
}
|
|
23
|
+
/* determine the project root (Git top-level if inside a Git
|
|
24
|
+
working tree, otherwise the current working directory) */
|
|
25
|
+
static projectRoot() {
|
|
26
|
+
try {
|
|
27
|
+
const result = execaSync("git", ["rev-parse", "--show-toplevel"], { stderr: "ignore" });
|
|
28
|
+
const top = result.stdout.trim();
|
|
29
|
+
if (top !== "")
|
|
30
|
+
return top;
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
/* not inside a Git working tree */
|
|
34
|
+
}
|
|
35
|
+
return process.cwd();
|
|
36
|
+
}
|
|
37
|
+
/* resolve the on-disk base directory for task storage */
|
|
38
|
+
static baseDir() {
|
|
39
|
+
return path.join(Task.projectRoot(), ".ase", "task");
|
|
40
|
+
}
|
|
41
|
+
/* resolve the on-disk path for a given task id */
|
|
42
|
+
static path(id) {
|
|
43
|
+
Task.validateId(id);
|
|
44
|
+
return path.join(Task.baseDir(), id, "plan.md");
|
|
45
|
+
}
|
|
46
|
+
/* load a task; returns empty string if no task exists */
|
|
47
|
+
static load(id) {
|
|
48
|
+
const file = Task.path(id);
|
|
61
49
|
if (!fs.existsSync(file))
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
if (!st.isFile())
|
|
65
|
-
continue;
|
|
66
|
-
const mtime = verbose ? DateTime.fromJSDate(st.mtime).toFormat("yyyy-LL-dd HH:mm") : undefined;
|
|
67
|
-
out.push({ id: entry, mtime });
|
|
50
|
+
return "";
|
|
51
|
+
return fs.readFileSync(file, "utf8");
|
|
68
52
|
}
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
const sub = path.join(dir, entry);
|
|
84
|
-
const file = path.join(sub, "plan.md");
|
|
53
|
+
/* save a task as UTF-8 text under the given id; the task's home
|
|
54
|
+
directory <project>/.ase/task/<id>/ is owned by ASE and removed
|
|
55
|
+
in full by Task.delete, so callers must not place foreign files there */
|
|
56
|
+
static save(id, text) {
|
|
57
|
+
if (typeof text !== "string")
|
|
58
|
+
throw new Error("task: text must be a string");
|
|
59
|
+
const file = Task.path(id);
|
|
60
|
+
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
61
|
+
fs.writeFileSync(file, text, "utf8");
|
|
62
|
+
}
|
|
63
|
+
/* delete a task by id; removes the entire task home directory
|
|
64
|
+
<project>/.ase/task/<id>/ (owned by ASE); returns true if a task existed */
|
|
65
|
+
static delete(id) {
|
|
66
|
+
const file = Task.path(id);
|
|
85
67
|
if (!fs.existsSync(file))
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
68
|
+
return false;
|
|
69
|
+
fs.rmSync(path.dirname(file), { recursive: true, force: true });
|
|
70
|
+
return true;
|
|
71
|
+
}
|
|
72
|
+
/* list all persisted tasks in lexicographic id order; if verbose is true,
|
|
73
|
+
each entry's `mtime` is set to the `plan.md` modification time formatted
|
|
74
|
+
as "YYYY-MM-DD HH:MM", otherwise it is left undefined */
|
|
75
|
+
static list(verbose = false) {
|
|
76
|
+
const dir = Task.baseDir();
|
|
77
|
+
if (!fs.existsSync(dir))
|
|
78
|
+
return [];
|
|
79
|
+
const out = [];
|
|
80
|
+
for (const entry of fs.readdirSync(dir)) {
|
|
81
|
+
if (!/^[A-Za-z0-9-]+$/.test(entry))
|
|
82
|
+
continue;
|
|
83
|
+
const file = path.join(dir, entry, "plan.md");
|
|
84
|
+
if (!fs.existsSync(file))
|
|
85
|
+
continue;
|
|
86
|
+
const st = fs.statSync(file);
|
|
87
|
+
if (!st.isFile())
|
|
88
|
+
continue;
|
|
89
|
+
const mtime = verbose ? DateTime.fromJSDate(st.mtime).toFormat("yyyy-LL-dd HH:mm") : undefined;
|
|
90
|
+
out.push({ id: entry, mtime });
|
|
93
91
|
}
|
|
92
|
+
out.sort((a, b) => a.id.localeCompare(b.id));
|
|
93
|
+
return out;
|
|
94
94
|
}
|
|
95
|
-
|
|
96
|
-
|
|
95
|
+
/* purge tasks whose modification time is older than the given cutoff in
|
|
96
|
+
milliseconds; returns the list of removed task ids */
|
|
97
|
+
static purge(maxAgeMs) {
|
|
98
|
+
const dir = Task.baseDir();
|
|
99
|
+
if (!fs.existsSync(dir))
|
|
100
|
+
return [];
|
|
101
|
+
const cutoff = Date.now() - maxAgeMs;
|
|
102
|
+
const removed = [];
|
|
103
|
+
for (const entry of fs.readdirSync(dir)) {
|
|
104
|
+
if (!/^[A-Za-z0-9-]+$/.test(entry))
|
|
105
|
+
continue;
|
|
106
|
+
const sub = path.join(dir, entry);
|
|
107
|
+
const file = path.join(sub, "plan.md");
|
|
108
|
+
if (!fs.existsSync(file))
|
|
109
|
+
continue;
|
|
110
|
+
const st = fs.statSync(file);
|
|
111
|
+
if (!st.isFile())
|
|
112
|
+
continue;
|
|
113
|
+
if (st.mtimeMs < cutoff) {
|
|
114
|
+
fs.rmSync(sub, { recursive: true, force: true });
|
|
115
|
+
removed.push(entry);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
return removed;
|
|
119
|
+
}
|
|
120
|
+
/* get the active task id for a given session, or empty string if none */
|
|
121
|
+
static getId(log, session) {
|
|
122
|
+
const scope = parseScope(`session:${session}`);
|
|
123
|
+
const cfg = new Config("config", configSchema, log, scope);
|
|
124
|
+
cfg.read();
|
|
125
|
+
const val = cfg.get("agent.task");
|
|
126
|
+
if (val === undefined)
|
|
127
|
+
return "";
|
|
128
|
+
return String(isScalar(val) ? val.value : val);
|
|
129
|
+
}
|
|
130
|
+
/* set the active task id for a given session */
|
|
131
|
+
static setId(log, session, id) {
|
|
132
|
+
const scope = parseScope(`session:${session}`);
|
|
133
|
+
const cfg = new Config("config", configSchema, log, scope);
|
|
134
|
+
cfg.read();
|
|
135
|
+
cfg.set("agent.task", id);
|
|
136
|
+
cfg.write();
|
|
137
|
+
}
|
|
138
|
+
}
|
|
97
139
|
/* read all of stdin as a UTF-8 string */
|
|
98
140
|
const readStdin = () => {
|
|
99
141
|
return new Promise((resolve, reject) => {
|
|
@@ -114,7 +156,7 @@ export default class TaskCommand {
|
|
|
114
156
|
/* register CLI top-level command "ase task" */
|
|
115
157
|
const task = program
|
|
116
158
|
.command("task")
|
|
117
|
-
.description("Manage persisted tasks under
|
|
159
|
+
.description("Manage persisted tasks under <project>/.ase/task/<id>/plan.md")
|
|
118
160
|
.action(() => {
|
|
119
161
|
task.outputHelp();
|
|
120
162
|
process.exit(1);
|
|
@@ -125,7 +167,7 @@ export default class TaskCommand {
|
|
|
125
167
|
.description("List all persisted task ids, one per line")
|
|
126
168
|
.option("-v, --verbose", "also show the plan.md modification time as (YYYY-MM-DD HH:MM)")
|
|
127
169
|
.action((opts) => {
|
|
128
|
-
const items =
|
|
170
|
+
const items = Task.list(opts.verbose ?? false);
|
|
129
171
|
for (const item of items) {
|
|
130
172
|
if (opts.verbose)
|
|
131
173
|
process.stdout.write(`${item.id}\t(${item.mtime})\n`);
|
|
@@ -140,7 +182,7 @@ export default class TaskCommand {
|
|
|
140
182
|
.description("Load a task by id and write it to stdout")
|
|
141
183
|
.argument("<id>", "Task identifier")
|
|
142
184
|
.action((id) => {
|
|
143
|
-
const text =
|
|
185
|
+
const text = Task.load(id);
|
|
144
186
|
process.stdout.write(text);
|
|
145
187
|
process.exit(0);
|
|
146
188
|
});
|
|
@@ -150,7 +192,7 @@ export default class TaskCommand {
|
|
|
150
192
|
.description("Edit a task by id with $EDITOR")
|
|
151
193
|
.argument("<id>", "Task identifier")
|
|
152
194
|
.action((id) => {
|
|
153
|
-
const file =
|
|
195
|
+
const file = Task.path(id);
|
|
154
196
|
const editor = process.env.EDITOR ?? process.env.VISUAL ?? "vi";
|
|
155
197
|
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
156
198
|
if (!fs.existsSync(file))
|
|
@@ -166,7 +208,7 @@ export default class TaskCommand {
|
|
|
166
208
|
.argument("<id>", "Task identifier")
|
|
167
209
|
.action(async (id) => {
|
|
168
210
|
const text = await readStdin();
|
|
169
|
-
|
|
211
|
+
Task.save(id, text);
|
|
170
212
|
this.log.write("info", `task: saved "${id}"`);
|
|
171
213
|
process.exit(0);
|
|
172
214
|
});
|
|
@@ -176,7 +218,7 @@ export default class TaskCommand {
|
|
|
176
218
|
.description("Delete a task by id")
|
|
177
219
|
.argument("<id>", "Task identifier")
|
|
178
220
|
.action((id) => {
|
|
179
|
-
const removed =
|
|
221
|
+
const removed = Task.delete(id);
|
|
180
222
|
if (removed)
|
|
181
223
|
this.log.write("info", `task: removed "${id}"`);
|
|
182
224
|
else
|
|
@@ -186,13 +228,24 @@ export default class TaskCommand {
|
|
|
186
228
|
/* register CLI sub-command "ase task purge" */
|
|
187
229
|
task
|
|
188
230
|
.command("purge")
|
|
189
|
-
.description("Remove all tasks with a modification time older than <
|
|
190
|
-
|
|
191
|
-
.
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
231
|
+
.description("Remove all tasks with a modification time older than <age> (default: 31d); " +
|
|
232
|
+
"<age> is <number><unit> with unit h (hour), d (day), m (month), y (year)")
|
|
233
|
+
.argument("[<age>]", "Maximum task age as <number><unit>", "31d")
|
|
234
|
+
.action((age) => {
|
|
235
|
+
const m = /^(\d+)([hdmy])$/.exec(age);
|
|
236
|
+
if (m === null)
|
|
237
|
+
throw new Error("task: <age> must match <number><unit> with unit h, d, m, or y");
|
|
238
|
+
const n = Number.parseInt(m[1], 10);
|
|
239
|
+
const unit = m[2];
|
|
240
|
+
const hour = 60 * 60 * 1000;
|
|
241
|
+
const day = 24 * hour;
|
|
242
|
+
const month = 30 * day;
|
|
243
|
+
const year = 365 * day;
|
|
244
|
+
const factor = unit === "h" ? hour :
|
|
245
|
+
unit === "d" ? day :
|
|
246
|
+
unit === "m" ? month :
|
|
247
|
+
year;
|
|
248
|
+
const removed = Task.purge(n * factor);
|
|
196
249
|
if (removed.length === 0)
|
|
197
250
|
this.log.write("info", "task: no tasks to purge");
|
|
198
251
|
else
|
|
@@ -202,3 +255,160 @@ export default class TaskCommand {
|
|
|
202
255
|
});
|
|
203
256
|
}
|
|
204
257
|
}
|
|
258
|
+
/* MCP registration entry point for task tools */
|
|
259
|
+
export class TaskMCP {
|
|
260
|
+
log;
|
|
261
|
+
constructor(log) {
|
|
262
|
+
this.log = log;
|
|
263
|
+
}
|
|
264
|
+
register(mcp) {
|
|
265
|
+
mcp.registerTool("task_list", {
|
|
266
|
+
title: "ASE task list",
|
|
267
|
+
description: "List all persisted tasks. " +
|
|
268
|
+
"Returns a `tasks` array (in lexicographic `id` order) where each item has the " +
|
|
269
|
+
"task `id`. If `verbose` is `true`, each item additionally has an `mtime` field " +
|
|
270
|
+
"(last modification time of the task's `plan.md`, formatted as `YYYY-MM-DD HH:MM`). " +
|
|
271
|
+
"Returns an empty array if no tasks exist.",
|
|
272
|
+
inputSchema: {
|
|
273
|
+
verbose: z.boolean().optional()
|
|
274
|
+
.describe("if true, also include the `mtime` field per task (default: false)")
|
|
275
|
+
},
|
|
276
|
+
outputSchema: {
|
|
277
|
+
tasks: z.array(z.object({
|
|
278
|
+
id: z.string().describe("task identifier"),
|
|
279
|
+
mtime: z.string().optional()
|
|
280
|
+
.describe("plan.md modification time (`YYYY-MM-DD HH:MM`); only present if `verbose` is true")
|
|
281
|
+
})).describe("all persisted tasks in lexicographic id order")
|
|
282
|
+
}
|
|
283
|
+
}, async (args) => {
|
|
284
|
+
try {
|
|
285
|
+
const verbose = args.verbose ?? false;
|
|
286
|
+
const items = Task.list(verbose);
|
|
287
|
+
const tasks = verbose ?
|
|
288
|
+
items.map((item) => ({ id: item.id, mtime: item.mtime })) :
|
|
289
|
+
items.map((item) => ({ id: item.id }));
|
|
290
|
+
const result = { tasks };
|
|
291
|
+
return {
|
|
292
|
+
structuredContent: result,
|
|
293
|
+
content: [{ type: "text", text: JSON.stringify(result) }]
|
|
294
|
+
};
|
|
295
|
+
}
|
|
296
|
+
catch (err) {
|
|
297
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
298
|
+
return {
|
|
299
|
+
isError: true,
|
|
300
|
+
content: [{ type: "text", text: `task_list: ERROR: ${message}` }]
|
|
301
|
+
};
|
|
302
|
+
}
|
|
303
|
+
});
|
|
304
|
+
mcp.registerTool("task_load", {
|
|
305
|
+
title: "ASE task load",
|
|
306
|
+
description: "Load a previously persisted task by `id`. " +
|
|
307
|
+
"Returns the task as `text`; returns an empty string if no task exists for the `id`.",
|
|
308
|
+
inputSchema: {
|
|
309
|
+
id: z.string()
|
|
310
|
+
.describe("task identifier (allowed characters: A-Z, a-z, 0-9, '-')")
|
|
311
|
+
}
|
|
312
|
+
}, async (args) => {
|
|
313
|
+
try {
|
|
314
|
+
const text = Task.load(args.id);
|
|
315
|
+
return {
|
|
316
|
+
content: [{ type: "text", text }]
|
|
317
|
+
};
|
|
318
|
+
}
|
|
319
|
+
catch (err) {
|
|
320
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
321
|
+
return {
|
|
322
|
+
isError: true,
|
|
323
|
+
content: [{ type: "text", text: `task_load: ERROR: ${message}` }]
|
|
324
|
+
};
|
|
325
|
+
}
|
|
326
|
+
});
|
|
327
|
+
mcp.registerTool("task_save", {
|
|
328
|
+
title: "ASE task save",
|
|
329
|
+
description: "Persist a task as `text` under `id`. " +
|
|
330
|
+
"Overwrites any existing task for the same `id`.",
|
|
331
|
+
inputSchema: {
|
|
332
|
+
id: z.string()
|
|
333
|
+
.describe("task identifier (allowed characters: A-Z, a-z, 0-9, '-')"),
|
|
334
|
+
text: z.string()
|
|
335
|
+
.describe("text content of the task")
|
|
336
|
+
}
|
|
337
|
+
}, async (args) => {
|
|
338
|
+
try {
|
|
339
|
+
Task.save(args.id, args.text);
|
|
340
|
+
return {
|
|
341
|
+
content: [{ type: "text", text: `task_save: OK: saved task "${args.id}"` }]
|
|
342
|
+
};
|
|
343
|
+
}
|
|
344
|
+
catch (err) {
|
|
345
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
346
|
+
return {
|
|
347
|
+
isError: true,
|
|
348
|
+
content: [{ type: "text", text: `task_save: ERROR: ${message}` }]
|
|
349
|
+
};
|
|
350
|
+
}
|
|
351
|
+
});
|
|
352
|
+
mcp.registerTool("task_delete", {
|
|
353
|
+
title: "ASE task delete",
|
|
354
|
+
description: "Delete a previously persisted task by `id`. " +
|
|
355
|
+
"Returns a status `text` indicating whether a task existed and was removed.",
|
|
356
|
+
inputSchema: {
|
|
357
|
+
id: z.string()
|
|
358
|
+
.describe("task identifier (allowed characters: A-Z, a-z, 0-9, '-')")
|
|
359
|
+
}
|
|
360
|
+
}, async (args) => {
|
|
361
|
+
try {
|
|
362
|
+
const removed = Task.delete(args.id);
|
|
363
|
+
const msg = removed ?
|
|
364
|
+
`task_delete: OK: removed task "${args.id}"` :
|
|
365
|
+
`task_delete: WARNING: no task "${args.id}" to remove`;
|
|
366
|
+
return {
|
|
367
|
+
content: [{ type: "text", text: msg }]
|
|
368
|
+
};
|
|
369
|
+
}
|
|
370
|
+
catch (err) {
|
|
371
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
372
|
+
return {
|
|
373
|
+
isError: true,
|
|
374
|
+
content: [{ type: "text", text: `task_delete: ERROR: ${message}` }]
|
|
375
|
+
};
|
|
376
|
+
}
|
|
377
|
+
});
|
|
378
|
+
mcp.registerTool("task_id", {
|
|
379
|
+
title: "ASE task id get/set",
|
|
380
|
+
description: "Get or set the active ASE task `id` for a given `session`. " +
|
|
381
|
+
"If `id` is provided, it sets the task id in the given `session`, " +
|
|
382
|
+
"otherwise it returns the current task `id` of the `session`.",
|
|
383
|
+
inputSchema: {
|
|
384
|
+
id: z.string().optional()
|
|
385
|
+
.describe("task identifier to set (allowed characters: A-Z, a-z, 0-9, '-'); " +
|
|
386
|
+
"if omitted, the current task id is returned"),
|
|
387
|
+
session: z.string()
|
|
388
|
+
.describe("session identifier (allowed characters: A-Z, a-z, 0-9, '-')")
|
|
389
|
+
}
|
|
390
|
+
}, async (args) => {
|
|
391
|
+
try {
|
|
392
|
+
if (args.id !== undefined) {
|
|
393
|
+
Task.setId(this.log, args.session, args.id);
|
|
394
|
+
const msg = `task_id: OK: set agent.task to "${args.id}" ` +
|
|
395
|
+
`for session "${args.session}"`;
|
|
396
|
+
return {
|
|
397
|
+
content: [{ type: "text", text: msg }]
|
|
398
|
+
};
|
|
399
|
+
}
|
|
400
|
+
const text = Task.getId(this.log, args.session);
|
|
401
|
+
return {
|
|
402
|
+
content: [{ type: "text", text }]
|
|
403
|
+
};
|
|
404
|
+
}
|
|
405
|
+
catch (err) {
|
|
406
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
407
|
+
return {
|
|
408
|
+
isError: true,
|
|
409
|
+
content: [{ type: "text", text: `task_id: ERROR: ${message}` }]
|
|
410
|
+
};
|
|
411
|
+
}
|
|
412
|
+
});
|
|
413
|
+
}
|
|
414
|
+
}
|
package/dst/ase.js
CHANGED
|
@@ -42,14 +42,14 @@ const main = async () => {
|
|
|
42
42
|
log.logFile(opts.logFile);
|
|
43
43
|
});
|
|
44
44
|
/* register top-level commands */
|
|
45
|
+
new SetupCommand(log).register(program);
|
|
45
46
|
new ConfigCommand(log).register(program);
|
|
46
|
-
new ServiceCommand(log).register(program);
|
|
47
47
|
new MCPCommand(log).register(program);
|
|
48
|
+
new ServiceCommand(log).register(program);
|
|
48
49
|
new HookCommand(log).register(program);
|
|
49
|
-
new DiagramCommand(log).register(program);
|
|
50
|
-
new SetupCommand(log).register(program);
|
|
51
50
|
new StatuslineCommand(log).register(program);
|
|
52
51
|
new TaskCommand(log).register(program);
|
|
52
|
+
new DiagramCommand(log).register(program);
|
|
53
53
|
/* parse program arguments */
|
|
54
54
|
await program.parseAsync(process.argv);
|
|
55
55
|
/* gracefully terminate */
|
package/package.json
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
"homepage": "http://github.com/rse/ase",
|
|
7
7
|
"repository": { "url": "git+https://github.com/rse/ase.git", "type": "git" },
|
|
8
8
|
"bugs": { "url": "http://github.com/rse/ase/issues" },
|
|
9
|
-
"version": "0.0.
|
|
9
|
+
"version": "0.0.28",
|
|
10
10
|
"license": "GPL-3.0-only",
|
|
11
11
|
"author": {
|
|
12
12
|
"name": "Dr. Ralf S. Engelschall",
|