@rse/ase 0.0.51 → 0.0.52
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/package.json +5 -5
- package/plugin/.claude-plugin/plugin.json +1 -1
- package/plugin/.github/plugin/plugin.json +1 -1
- package/plugin/etc/stx.conf +4 -4
- package/plugin/package.json +1 -1
- package/dst/ase-claude.js +0 -116
- package/dst/ase-decision.js +0 -67
- package/dst/ase-foo.js +0 -21
- package/dst/ase-hello.js +0 -23
- package/dst/ase-helloxx.js +0 -23
- package/dst/ase-launch.js +0 -109
- package/dst/ase-plan.js +0 -143
- package/dst/ase-service-probe.js +0 -38
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.52",
|
|
10
10
|
"license": "GPL-3.0-only",
|
|
11
11
|
"author": {
|
|
12
12
|
"name": "Dr. Ralf S. Engelschall",
|
|
@@ -18,8 +18,8 @@
|
|
|
18
18
|
"devDependencies": {
|
|
19
19
|
"eslint": "9.39.4",
|
|
20
20
|
"@eslint/js": "9.39.4",
|
|
21
|
-
"@typescript-eslint/parser": "8.
|
|
22
|
-
"@typescript-eslint/eslint-plugin": "8.
|
|
21
|
+
"@typescript-eslint/parser": "8.60.0",
|
|
22
|
+
"@typescript-eslint/eslint-plugin": "8.60.0",
|
|
23
23
|
"eslint-plugin-promise": "7.3.0",
|
|
24
24
|
"eslint-plugin-import": "2.32.0",
|
|
25
25
|
"neostandard": "0.13.0",
|
|
@@ -41,9 +41,9 @@
|
|
|
41
41
|
},
|
|
42
42
|
"dependencies": {
|
|
43
43
|
"commander": "14.0.3",
|
|
44
|
-
"@dotenvx/dotenvx": "1.
|
|
44
|
+
"@dotenvx/dotenvx": "1.69.0",
|
|
45
45
|
"yaml": "2.9.0",
|
|
46
|
-
"valibot": "1.4.
|
|
46
|
+
"valibot": "1.4.1",
|
|
47
47
|
"execa": "9.6.1",
|
|
48
48
|
"mkdirp": "3.0.1",
|
|
49
49
|
"@hapi/hapi": "21.4.9",
|
package/plugin/etc/stx.conf
CHANGED
|
@@ -4,19 +4,19 @@
|
|
|
4
4
|
## Licensed under GPL 3.0 <https://spdx.org/licenses/GPL-3.0-only>
|
|
5
5
|
##
|
|
6
6
|
|
|
7
|
-
# lint project
|
|
7
|
+
# [plugin] lint project
|
|
8
8
|
lint
|
|
9
9
|
markdownlint-cli2 --config etc/markdownlint.yaml skills/**/*.md
|
|
10
10
|
|
|
11
|
-
# build project
|
|
11
|
+
# [plugin] build project
|
|
12
12
|
build : lint
|
|
13
13
|
true
|
|
14
14
|
|
|
15
|
-
# remove all generated files
|
|
15
|
+
# [plugin] remove all generated files
|
|
16
16
|
clean
|
|
17
17
|
true
|
|
18
18
|
|
|
19
|
-
# remove all built files
|
|
19
|
+
# [plugin] remove all built files
|
|
20
20
|
distclean: clean
|
|
21
21
|
shx rm -f package-lock.json
|
|
22
22
|
shx rm -rf node_modules
|
package/plugin/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.52",
|
|
10
10
|
"license": "GPL-3.0-only",
|
|
11
11
|
"author": {
|
|
12
12
|
"name": "Dr. Ralf S. Engelschall",
|
package/dst/ase-claude.js
DELETED
|
@@ -1,116 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
** Agentic Software Engineering (ASE)
|
|
3
|
-
** Copyright (c) 2025-2026 Dr. Ralf S. Engelschall <rse@engelschall.com>
|
|
4
|
-
** Licensed under GPL 3.0 <https://spdx.org/licenses/GPL-3.0-only>
|
|
5
|
-
*/
|
|
6
|
-
import { execa, execaSync } from "execa";
|
|
7
|
-
import which from "which";
|
|
8
|
-
import { Config, configSchema, parseScope } from "./ase-config.js";
|
|
9
|
-
/* default statusline arguments (claudeX fallback) used when the
|
|
10
|
-
"agent.statusline" config variable is empty or unset */
|
|
11
|
-
const DEFAULT_STATUSLINE_ARGS = "-w 0 -m 2 '<blue>%u</blue> <red>%p</red> <black>%T</black> %s' '%m %e %t' '%P %c'";
|
|
12
|
-
/* CLI command "ase claude" */
|
|
13
|
-
export default class ClaudeCommand {
|
|
14
|
-
log;
|
|
15
|
-
constructor(log) {
|
|
16
|
-
this.log = log;
|
|
17
|
-
}
|
|
18
|
-
/* ensure a tool is available */
|
|
19
|
-
async ensureTool(tool) {
|
|
20
|
-
return which(tool).catch(() => {
|
|
21
|
-
throw new Error(`mandatory tool "${tool}" not found in $PATH`);
|
|
22
|
-
});
|
|
23
|
-
}
|
|
24
|
-
/* resolve the statusline command-line arguments from the layered
|
|
25
|
-
ASE configuration ("agent.statusline"), falling back to the
|
|
26
|
-
claudeX default if the value is empty or unavailable */
|
|
27
|
-
resolveStatuslineArgs() {
|
|
28
|
-
let args = "";
|
|
29
|
-
try {
|
|
30
|
-
const cfg = new Config("config", configSchema, this.log, parseScope("project"));
|
|
31
|
-
cfg.read("lenient");
|
|
32
|
-
args = String(cfg.get("agent.statusline") ?? "").trim();
|
|
33
|
-
}
|
|
34
|
-
catch (_e) {
|
|
35
|
-
/* cascade unavailable; keep fallback */
|
|
36
|
-
}
|
|
37
|
-
if (args === "")
|
|
38
|
-
args = DEFAULT_STATUSLINE_ARGS;
|
|
39
|
-
return args;
|
|
40
|
-
}
|
|
41
|
-
/* register commands */
|
|
42
|
-
register(program) {
|
|
43
|
-
program
|
|
44
|
-
.command("claude")
|
|
45
|
-
.description("start Claude Code with bootstrapped ASE environment and settings")
|
|
46
|
-
.passThroughOptions()
|
|
47
|
-
.allowUnknownOption()
|
|
48
|
-
.argument("[args...]", "arguments forwarded verbatim to the \"claude\" CLI")
|
|
49
|
-
.action(async (args) => {
|
|
50
|
-
/* ensure Claude Code CLI is available */
|
|
51
|
-
await this.ensureTool("claude");
|
|
52
|
-
/* bootstrap ASE_TERM_WIDTH from terminal columns */
|
|
53
|
-
if (process.env.ASE_TERM_WIDTH === undefined) {
|
|
54
|
-
let width = 0;
|
|
55
|
-
if (process.stdout.isTTY) {
|
|
56
|
-
const cols = process.stdout.columns;
|
|
57
|
-
if (typeof cols === "number" && cols > 0)
|
|
58
|
-
width = cols;
|
|
59
|
-
}
|
|
60
|
-
process.env.ASE_TERM_WIDTH = `${width}`;
|
|
61
|
-
}
|
|
62
|
-
/* bootstrap ASE_TERM_HEIGHT from terminal rows */
|
|
63
|
-
if (process.env.ASE_TERM_HEIGHT === undefined) {
|
|
64
|
-
let height = 0;
|
|
65
|
-
if (process.stdout.isTTY) {
|
|
66
|
-
const rows = process.stdout.rows;
|
|
67
|
-
if (typeof rows === "number" && rows > 0)
|
|
68
|
-
height = rows;
|
|
69
|
-
}
|
|
70
|
-
process.env.ASE_TERM_HEIGHT = `${height}`;
|
|
71
|
-
}
|
|
72
|
-
/* bootstrap ASE_TERM_COLORS from "tput colors" */
|
|
73
|
-
if (process.env.ASE_TERM_COLORS === undefined) {
|
|
74
|
-
let colorMode = "none";
|
|
75
|
-
try {
|
|
76
|
-
const { stdout } = execaSync("tput", ["colors"], { reject: false });
|
|
77
|
-
const n = parseInt(stdout.trim(), 10);
|
|
78
|
-
if (!Number.isNaN(n) && n >= 256)
|
|
79
|
-
colorMode = "ansi256";
|
|
80
|
-
else if (!Number.isNaN(n) && n >= 16)
|
|
81
|
-
colorMode = "ansi16";
|
|
82
|
-
}
|
|
83
|
-
catch (_e) {
|
|
84
|
-
/* ignore */
|
|
85
|
-
}
|
|
86
|
-
process.env.ASE_TERM_COLORS = colorMode;
|
|
87
|
-
}
|
|
88
|
-
/* resolve statusline arguments (config or claudeX fallback) */
|
|
89
|
-
const statuslineArgs = this.resolveStatuslineArgs();
|
|
90
|
-
/* build inline Claude Code settings JSON */
|
|
91
|
-
const settings = {
|
|
92
|
-
env: {
|
|
93
|
-
DISABLE_TELEMETRY: "1",
|
|
94
|
-
DISABLE_AUTOUPDATER: "1",
|
|
95
|
-
DISABLE_BUG_COMMAND: "1",
|
|
96
|
-
DISABLE_ERROR_REPORTING: "1"
|
|
97
|
-
},
|
|
98
|
-
statusLine: {
|
|
99
|
-
type: "command",
|
|
100
|
-
command: `ase statusline ${statuslineArgs}`,
|
|
101
|
-
padding: 0
|
|
102
|
-
}
|
|
103
|
-
};
|
|
104
|
-
const settingsJSON = JSON.stringify(settings);
|
|
105
|
-
/* exec Claude Code with the inline settings prepended
|
|
106
|
-
and any user-supplied arguments forwarded verbatim */
|
|
107
|
-
const result = await execa("claude", ["--settings", settingsJSON, ...args], {
|
|
108
|
-
stdio: "inherit",
|
|
109
|
-
env: process.env,
|
|
110
|
-
reject: false,
|
|
111
|
-
windowsHide: false
|
|
112
|
-
});
|
|
113
|
-
process.exit(result.exitCode ?? 0);
|
|
114
|
-
});
|
|
115
|
-
}
|
|
116
|
-
}
|
package/dst/ase-decision.js
DELETED
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
** Agentic Software Engineering (ASE)
|
|
3
|
-
** Copyright (c) 2025-2026 Dr. Ralf S. Engelschall <rse@engelschall.com>
|
|
4
|
-
** Licensed under GPL 3.0 <https://spdx.org/licenses/GPL-3.0-only>
|
|
5
|
-
*/
|
|
6
|
-
import { z } from "zod";
|
|
7
|
-
/* reusable functionality: weighted multi-criteria decision matrix */
|
|
8
|
-
export class Decision {
|
|
9
|
-
/* compute the per-alternative product-sum (rating) row from a
|
|
10
|
-
weighted decision matrix. Each input row has the shape
|
|
11
|
-
`[weight, eval_1, eval_2, ..., eval_N]`. For each alternative
|
|
12
|
-
column j (1..N), the result is the sum over all rows K of
|
|
13
|
-
`weight_K * eval_K_j`. The output array has length N. */
|
|
14
|
-
static productSum(matrix) {
|
|
15
|
-
if (matrix.length === 0)
|
|
16
|
-
return [];
|
|
17
|
-
const cols = matrix[0].length;
|
|
18
|
-
if (cols < 2)
|
|
19
|
-
throw new Error("each row must contain a weight followed by at least one evaluation column");
|
|
20
|
-
const N = cols - 1;
|
|
21
|
-
const ratings = new Array(N).fill(0);
|
|
22
|
-
for (let i = 0; i < matrix.length; i++) {
|
|
23
|
-
const row = matrix[i];
|
|
24
|
-
if (row.length !== cols)
|
|
25
|
-
throw new Error(`row ${i} has ${row.length} columns, expected ${cols}`);
|
|
26
|
-
const weight = row[0];
|
|
27
|
-
for (let j = 0; j < N; j++)
|
|
28
|
-
ratings[j] += weight * row[j + 1];
|
|
29
|
-
}
|
|
30
|
-
return ratings;
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
/* MCP registration entry point for the decision-matrix tool */
|
|
34
|
-
export class DecisionMCP {
|
|
35
|
-
register(mcp) {
|
|
36
|
-
mcp.registerTool("decision_matrix", {
|
|
37
|
-
title: "ASE decision matrix",
|
|
38
|
-
description: "Compute the per-alternative product-sum (rating) row of a weighted " +
|
|
39
|
-
"multi-criteria decision matrix. The input `matrix` is an array of rows, " +
|
|
40
|
-
"one row per criterion, where each row has the shape " +
|
|
41
|
-
"`[weight, eval_1, eval_2, ..., eval_N]` (i.e. the criterion weight " +
|
|
42
|
-
"followed by N evaluation values, one per alternative). For each " +
|
|
43
|
-
"alternative column j (1..N), the result is the sum over all rows K of " +
|
|
44
|
-
"`weight_K * eval_K_j`. Returns a JSON `text` array of length N with " +
|
|
45
|
-
"the raw, unrounded ratings (one per alternative, in the same column " +
|
|
46
|
-
"order as the input).",
|
|
47
|
-
inputSchema: {
|
|
48
|
-
matrix: z.array(z.array(z.number()))
|
|
49
|
-
.describe("Decision matrix rows: each row is `[weight, eval_1, ..., eval_N]`")
|
|
50
|
-
}
|
|
51
|
-
}, async (args) => {
|
|
52
|
-
try {
|
|
53
|
-
const result = Decision.productSum(args.matrix);
|
|
54
|
-
return {
|
|
55
|
-
content: [{ type: "text", text: JSON.stringify(result) }]
|
|
56
|
-
};
|
|
57
|
-
}
|
|
58
|
-
catch (err) {
|
|
59
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
60
|
-
return {
|
|
61
|
-
isError: true,
|
|
62
|
-
content: [{ type: "text", text: `ERROR: ${message}` }]
|
|
63
|
-
};
|
|
64
|
-
}
|
|
65
|
-
});
|
|
66
|
-
}
|
|
67
|
-
}
|
package/dst/ase-foo.js
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
** Agentic Software Engineering (ASE)
|
|
3
|
-
** Copyright (c) 2025-2026 Dr. Ralf S. Engelschall <rse@engelschall.com>
|
|
4
|
-
** Licensed under GPL 3.0 <https://spdx.org/licenses/GPL-3.0-only>
|
|
5
|
-
*/
|
|
6
|
-
/* command-line handling */
|
|
7
|
-
export default class FooCommand {
|
|
8
|
-
log;
|
|
9
|
-
constructor(log) {
|
|
10
|
-
this.log = log;
|
|
11
|
-
}
|
|
12
|
-
/* register commands */
|
|
13
|
-
register(program) {
|
|
14
|
-
program
|
|
15
|
-
.command("foo")
|
|
16
|
-
.description("Print a nice Hello World message")
|
|
17
|
-
.action(() => {
|
|
18
|
-
process.stdout.write("Hello, World!\n");
|
|
19
|
-
});
|
|
20
|
-
}
|
|
21
|
-
}
|
package/dst/ase-hello.js
DELETED
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
** Agentic Software Engineering (ASE)
|
|
3
|
-
** Copyright (c) 2025-2026 Dr. Ralf S. Engelschall <rse@engelschall.com>
|
|
4
|
-
** Licensed under GPL 3.0 <https://spdx.org/licenses/GPL-3.0-only>
|
|
5
|
-
*/
|
|
6
|
-
import chalk from "chalk";
|
|
7
|
-
/* command-line handling */
|
|
8
|
-
export default class HelloCommand {
|
|
9
|
-
log;
|
|
10
|
-
constructor(log) {
|
|
11
|
-
this.log = log;
|
|
12
|
-
}
|
|
13
|
-
/* register commands */
|
|
14
|
-
register(program) {
|
|
15
|
-
program
|
|
16
|
-
.command("hello")
|
|
17
|
-
.description("Print a nice greeting in red")
|
|
18
|
-
.option("-s, --subject <subject>", "subject to greet", "Hello")
|
|
19
|
-
.action((opts) => {
|
|
20
|
-
process.stdout.write(chalk.red(`${opts.subject} Universe!`) + "\n");
|
|
21
|
-
});
|
|
22
|
-
}
|
|
23
|
-
}
|
package/dst/ase-helloxx.js
DELETED
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
** Agentic Software Engineering (ASE)
|
|
3
|
-
** Copyright (c) 2025-2026 Dr. Ralf S. Engelschall <rse@engelschall.com>
|
|
4
|
-
** Licensed under GPL 3.0 <https://spdx.org/licenses/GPL-3.0-only>
|
|
5
|
-
*/
|
|
6
|
-
import chalk from "chalk";
|
|
7
|
-
/* command-line handling */
|
|
8
|
-
export default class HelloxxCommand {
|
|
9
|
-
log;
|
|
10
|
-
constructor(log) {
|
|
11
|
-
this.log = log;
|
|
12
|
-
}
|
|
13
|
-
/* register commands */
|
|
14
|
-
register(program) {
|
|
15
|
-
program
|
|
16
|
-
.command("helloxx")
|
|
17
|
-
.description("Print a Hello World greeting")
|
|
18
|
-
.action(() => {
|
|
19
|
-
process.stdout.write(chalk.bold.green("Hello, World!") + "\n");
|
|
20
|
-
process.exit(0);
|
|
21
|
-
});
|
|
22
|
-
}
|
|
23
|
-
}
|
package/dst/ase-launch.js
DELETED
|
@@ -1,109 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
** Agentic Software Engineering (ASE)
|
|
3
|
-
** Copyright (c) 2025-2026 Dr. Ralf S. Engelschall <rse@engelschall.com>
|
|
4
|
-
** Licensed under GPL 3.0 <https://spdx.org/licenses/GPL-3.0-only>
|
|
5
|
-
*/
|
|
6
|
-
import { execa, execaSync } from "execa";
|
|
7
|
-
import which from "which";
|
|
8
|
-
import { Config, configSchema, parseScope } from "./ase-config.js";
|
|
9
|
-
/* list of supported tools for "ase launch <tool>" */
|
|
10
|
-
const SUPPORTED_TOOLS = ["claude"];
|
|
11
|
-
/* default statusline arguments (used when "agent.statusline" is empty/unset) */
|
|
12
|
-
const DEFAULT_STATUSLINE_ARGS = "-w 0 -m 2 '<blue>%u</blue> <red>%p</red> <black>%T</black> %s' '%m %e %t' '%P %c'";
|
|
13
|
-
/* CLI command "ase launch" */
|
|
14
|
-
export default class LaunchCommand {
|
|
15
|
-
log;
|
|
16
|
-
constructor(log) {
|
|
17
|
-
this.log = log;
|
|
18
|
-
}
|
|
19
|
-
/* resolve the effective "agent.statusline" from the layered
|
|
20
|
-
configuration cascade (default < user < project) */
|
|
21
|
-
readStatuslineConfig() {
|
|
22
|
-
let statusline = "";
|
|
23
|
-
try {
|
|
24
|
-
const cfg = new Config("config", configSchema, this.log, parseScope(undefined));
|
|
25
|
-
cfg.read("lenient");
|
|
26
|
-
statusline = String(cfg.get("agent.statusline") ?? "").trim();
|
|
27
|
-
}
|
|
28
|
-
catch (_e) {
|
|
29
|
-
/* cascade unavailable; leave default */
|
|
30
|
-
}
|
|
31
|
-
/* reject shell metacharacters to prevent command injection through
|
|
32
|
-
the configured value when later interpolated into a shell command */
|
|
33
|
-
if (statusline !== "" && /[;&|`$\n\r]/.test(statusline))
|
|
34
|
-
throw new Error("invalid \"agent.statusline\" configuration value: " +
|
|
35
|
-
"must not contain shell metacharacters (; & | ` $ newline)");
|
|
36
|
-
return statusline;
|
|
37
|
-
}
|
|
38
|
-
/* populate ASE_TERM_* environment variables (only if unset),
|
|
39
|
-
so downstream tools (e.g. the diagram renderer) get reliable
|
|
40
|
-
terminal sizing/color information regardless of the launched tool */
|
|
41
|
-
populateTermEnv() {
|
|
42
|
-
const tty = process.stdout.isTTY;
|
|
43
|
-
if (process.env.ASE_TERM_WIDTH === undefined) {
|
|
44
|
-
const cols = tty ? process.stdout.columns : 0;
|
|
45
|
-
process.env.ASE_TERM_WIDTH = `${typeof cols === "number" && cols > 0 ? cols : 0}`;
|
|
46
|
-
}
|
|
47
|
-
if (process.env.ASE_TERM_HEIGHT === undefined) {
|
|
48
|
-
const rows = tty ? process.stdout.rows : 0;
|
|
49
|
-
process.env.ASE_TERM_HEIGHT = `${typeof rows === "number" && rows > 0 ? rows : 0}`;
|
|
50
|
-
}
|
|
51
|
-
if (process.env.ASE_TERM_COLORS === undefined) {
|
|
52
|
-
let colorMode = "none";
|
|
53
|
-
try {
|
|
54
|
-
const { stdout } = execaSync("tput", ["colors"], { reject: false });
|
|
55
|
-
const n = parseInt(stdout.trim(), 10);
|
|
56
|
-
if (!Number.isNaN(n) && n >= 256)
|
|
57
|
-
colorMode = "ansi256";
|
|
58
|
-
else if (!Number.isNaN(n) && n >= 16)
|
|
59
|
-
colorMode = "ansi16";
|
|
60
|
-
}
|
|
61
|
-
catch (_e) {
|
|
62
|
-
/* ignore */
|
|
63
|
-
}
|
|
64
|
-
process.env.ASE_TERM_COLORS = colorMode;
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
/* handler for "ase launch <tool> [<options>...]" */
|
|
68
|
-
async run(tool, args) {
|
|
69
|
-
/* populate ASE_TERM_* environment variables (always, regardless of tool) */
|
|
70
|
-
this.populateTermEnv();
|
|
71
|
-
/* dispatch by tool name */
|
|
72
|
-
if (tool === "claude") {
|
|
73
|
-
await which("claude").catch(() => {
|
|
74
|
-
throw new Error("required tool \"claude\" not found in $PATH");
|
|
75
|
-
});
|
|
76
|
-
let statusline = this.readStatuslineConfig();
|
|
77
|
-
if (statusline === "")
|
|
78
|
-
statusline = DEFAULT_STATUSLINE_ARGS;
|
|
79
|
-
const settingsJson = JSON.stringify({
|
|
80
|
-
statusLine: {
|
|
81
|
-
type: "command",
|
|
82
|
-
command: `ase statusline ${statusline}`,
|
|
83
|
-
padding: 0
|
|
84
|
-
}
|
|
85
|
-
});
|
|
86
|
-
const result = await execa("claude", ["--settings", settingsJson, ...args], {
|
|
87
|
-
stdio: "inherit",
|
|
88
|
-
env: process.env,
|
|
89
|
-
reject: false
|
|
90
|
-
});
|
|
91
|
-
return result.exitCode ?? 0;
|
|
92
|
-
}
|
|
93
|
-
else
|
|
94
|
-
throw new Error(`unsupported tool "${tool}" ` +
|
|
95
|
-
`(expected one of: ${SUPPORTED_TOOLS.join(", ")})`);
|
|
96
|
-
}
|
|
97
|
-
/* register commands */
|
|
98
|
-
register(program) {
|
|
99
|
-
program
|
|
100
|
-
.command("launch <tool> [options...]")
|
|
101
|
-
.description("launch a supported tool with bootstrapped environment and settings " +
|
|
102
|
-
`(supported tools: ${SUPPORTED_TOOLS.join(", ")})`)
|
|
103
|
-
.passThroughOptions()
|
|
104
|
-
.allowUnknownOption()
|
|
105
|
-
.action(async (tool, options) => {
|
|
106
|
-
process.exit(await this.run(tool, options));
|
|
107
|
-
});
|
|
108
|
-
}
|
|
109
|
-
}
|
package/dst/ase-plan.js
DELETED
|
@@ -1,143 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
** Agentic Software Engineering (ASE)
|
|
3
|
-
** Copyright (c) 2025-2026 Dr. Ralf S. Engelschall <rse@engelschall.com>
|
|
4
|
-
** Licensed under GPL 3.0 <https://spdx.org/licenses/GPL-3.0-only>
|
|
5
|
-
*/
|
|
6
|
-
import path from "node:path";
|
|
7
|
-
import os from "node:os";
|
|
8
|
-
import fs from "node:fs";
|
|
9
|
-
/* validate the plan id to keep it safe as a filename component */
|
|
10
|
-
const validateId = (id) => {
|
|
11
|
-
if (typeof id !== "string" || id.length === 0)
|
|
12
|
-
throw new Error("plan: id must be a non-empty string");
|
|
13
|
-
if (!/^[A-Za-z0-9-]+$/.test(id))
|
|
14
|
-
throw new Error("plan: id must match [A-Za-z0-9-]+");
|
|
15
|
-
};
|
|
16
|
-
/* resolve the on-disk path for a given plan id */
|
|
17
|
-
const planPath = (id) => {
|
|
18
|
-
validateId(id);
|
|
19
|
-
return path.join(os.homedir(), ".ase", "plan", `${id}.md`);
|
|
20
|
-
};
|
|
21
|
-
/* load a plan; returns empty string if no plan exists */
|
|
22
|
-
export const planLoad = (id) => {
|
|
23
|
-
const file = planPath(id);
|
|
24
|
-
if (!fs.existsSync(file))
|
|
25
|
-
return "";
|
|
26
|
-
return fs.readFileSync(file, "utf8");
|
|
27
|
-
};
|
|
28
|
-
/* save a plan as UTF-8 text under the given id */
|
|
29
|
-
export const planSave = (id, text) => {
|
|
30
|
-
if (typeof text !== "string")
|
|
31
|
-
throw new Error("plan: text must be a string");
|
|
32
|
-
const file = planPath(id);
|
|
33
|
-
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
34
|
-
fs.writeFileSync(file, text, "utf8");
|
|
35
|
-
};
|
|
36
|
-
/* delete a plan by id; returns true if a plan existed and was removed */
|
|
37
|
-
export const planDelete = (id) => {
|
|
38
|
-
const file = planPath(id);
|
|
39
|
-
if (!fs.existsSync(file))
|
|
40
|
-
return false;
|
|
41
|
-
fs.rmSync(file);
|
|
42
|
-
return true;
|
|
43
|
-
};
|
|
44
|
-
/* purge plans whose modification time is older than the given cutoff in
|
|
45
|
-
milliseconds; returns the list of removed plan ids */
|
|
46
|
-
export const planPurge = (maxAgeMs) => {
|
|
47
|
-
const dir = path.join(os.homedir(), ".ase", "plan");
|
|
48
|
-
if (!fs.existsSync(dir))
|
|
49
|
-
return [];
|
|
50
|
-
const cutoff = Date.now() - maxAgeMs;
|
|
51
|
-
const removed = [];
|
|
52
|
-
for (const entry of fs.readdirSync(dir)) {
|
|
53
|
-
if (!entry.endsWith(".md"))
|
|
54
|
-
continue;
|
|
55
|
-
const file = path.join(dir, entry);
|
|
56
|
-
const st = fs.statSync(file);
|
|
57
|
-
if (!st.isFile())
|
|
58
|
-
continue;
|
|
59
|
-
if (st.mtimeMs < cutoff) {
|
|
60
|
-
fs.rmSync(file);
|
|
61
|
-
removed.push(entry.slice(0, -3));
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
return removed;
|
|
65
|
-
};
|
|
66
|
-
/* read all of stdin as a UTF-8 string */
|
|
67
|
-
const readStdin = () => {
|
|
68
|
-
return new Promise((resolve, reject) => {
|
|
69
|
-
const chunks = [];
|
|
70
|
-
process.stdin.on("data", (chunk) => chunks.push(chunk));
|
|
71
|
-
process.stdin.on("end", () => resolve(Buffer.concat(chunks).toString("utf8")));
|
|
72
|
-
process.stdin.on("error", (err) => reject(err));
|
|
73
|
-
});
|
|
74
|
-
};
|
|
75
|
-
/* CLI command "ase plan" */
|
|
76
|
-
export default class PlanCommand {
|
|
77
|
-
log;
|
|
78
|
-
constructor(log) {
|
|
79
|
-
this.log = log;
|
|
80
|
-
}
|
|
81
|
-
/* register commands */
|
|
82
|
-
register(program) {
|
|
83
|
-
/* register CLI top-level command "ase plan" */
|
|
84
|
-
const plan = program
|
|
85
|
-
.command("plan")
|
|
86
|
-
.description("Manage persisted plans under ~/.ase/plan/<id>.md")
|
|
87
|
-
.action(() => {
|
|
88
|
-
plan.outputHelp();
|
|
89
|
-
process.exit(1);
|
|
90
|
-
});
|
|
91
|
-
/* register CLI sub-command "ase plan load" */
|
|
92
|
-
plan
|
|
93
|
-
.command("load")
|
|
94
|
-
.description("Load a plan by id and write it to stdout")
|
|
95
|
-
.argument("<id>", "Plan identifier")
|
|
96
|
-
.action((id) => {
|
|
97
|
-
const text = planLoad(id);
|
|
98
|
-
process.stdout.write(text);
|
|
99
|
-
process.exit(0);
|
|
100
|
-
});
|
|
101
|
-
/* register CLI sub-command "ase plan save" */
|
|
102
|
-
plan
|
|
103
|
-
.command("save")
|
|
104
|
-
.description("Save a plan by id, reading content from stdin")
|
|
105
|
-
.argument("<id>", "Plan identifier")
|
|
106
|
-
.action(async (id) => {
|
|
107
|
-
const text = await readStdin();
|
|
108
|
-
planSave(id, text);
|
|
109
|
-
this.log.write("info", `plan: saved "${id}"`);
|
|
110
|
-
process.exit(0);
|
|
111
|
-
});
|
|
112
|
-
/* register CLI sub-command "ase plan delete" */
|
|
113
|
-
plan
|
|
114
|
-
.command("delete")
|
|
115
|
-
.description("Delete a plan by id")
|
|
116
|
-
.argument("<id>", "Plan identifier")
|
|
117
|
-
.action((id) => {
|
|
118
|
-
const removed = planDelete(id);
|
|
119
|
-
if (removed)
|
|
120
|
-
this.log.write("info", `plan: removed "${id}"`);
|
|
121
|
-
else
|
|
122
|
-
this.log.write("info", `plan: no plan "${id}" to remove`);
|
|
123
|
-
process.exit(removed ? 0 : 1);
|
|
124
|
-
});
|
|
125
|
-
/* register CLI sub-command "ase plan purge" */
|
|
126
|
-
plan
|
|
127
|
-
.command("purge")
|
|
128
|
-
.description("Remove all plans with a modification time older than <days> (default: 31)")
|
|
129
|
-
.argument("[<days>]", "Maximum plan age in days", "31")
|
|
130
|
-
.action((days) => {
|
|
131
|
-
const n = Number.parseInt(days, 10);
|
|
132
|
-
if (!Number.isFinite(n) || n < 0)
|
|
133
|
-
throw new Error("plan: <days> must be a non-negative integer");
|
|
134
|
-
const removed = planPurge(n * 24 * 60 * 60 * 1000);
|
|
135
|
-
if (removed.length === 0)
|
|
136
|
-
this.log.write("info", "plan: no plans to purge");
|
|
137
|
-
else
|
|
138
|
-
for (const id of removed)
|
|
139
|
-
this.log.write("info", `plan: purged "${id}"`);
|
|
140
|
-
process.exit(0);
|
|
141
|
-
});
|
|
142
|
-
}
|
|
143
|
-
}
|
package/dst/ase-service-probe.js
DELETED
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
** Agentic Software Engineering (ASE)
|
|
3
|
-
** Copyright (c) 2025-2026 Dr. Ralf S. Engelschall <rse@engelschall.com>
|
|
4
|
-
** Licensed under GPL 3.0 <https://spdx.org/licenses/GPL-3.0-only>
|
|
5
|
-
*/
|
|
6
|
-
import axios from "axios";
|
|
7
|
-
import * as v from "valibot";
|
|
8
|
-
/* shared service host */
|
|
9
|
-
export const SERVICE_HOST = "127.0.0.1";
|
|
10
|
-
/* schema for ".ase/service.yaml" */
|
|
11
|
-
export const serviceSchema = v.nullish(v.strictObject({
|
|
12
|
-
port: v.optional(v.pipe(v.number(), v.integer(), v.minValue(1024), v.maxValue(65535)))
|
|
13
|
-
}));
|
|
14
|
-
/* distinguish ECONNREFUSED from other Axios transport errors */
|
|
15
|
-
export const isConnRefused = (err) => {
|
|
16
|
-
const e = err;
|
|
17
|
-
return e?.code === "ECONNREFUSED" || e?.cause?.code === "ECONNREFUSED";
|
|
18
|
-
};
|
|
19
|
-
/* probe the service and verify ASE identity banner */
|
|
20
|
-
export const probe = async (port, projectId) => {
|
|
21
|
-
try {
|
|
22
|
-
const r = await axios.request({
|
|
23
|
-
method: "OPTIONS",
|
|
24
|
-
url: `http://${SERVICE_HOST}:${port}/`,
|
|
25
|
-
timeout: 2000,
|
|
26
|
-
validateStatus: () => true
|
|
27
|
-
});
|
|
28
|
-
if (r.status < 200 || r.status >= 300)
|
|
29
|
-
return false;
|
|
30
|
-
const d = r.data;
|
|
31
|
-
return d?.ase === true && d?.projectId === projectId;
|
|
32
|
-
}
|
|
33
|
-
catch (err) {
|
|
34
|
-
if (isConnRefused(err))
|
|
35
|
-
return null;
|
|
36
|
-
throw err;
|
|
37
|
-
}
|
|
38
|
-
};
|