shin-engine 1.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/bin/ee.js +174 -0
- package/package.json +20 -0
package/bin/ee.js
ADDED
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
const { execSync, spawn } = require("child_process");
|
|
3
|
+
const { existsSync, mkdirSync } = require("fs");
|
|
4
|
+
const { join } = require("path");
|
|
5
|
+
const path = require("path");
|
|
6
|
+
const https = require("https");
|
|
7
|
+
const http = require("http");
|
|
8
|
+
const os = require("os");
|
|
9
|
+
|
|
10
|
+
const EE_HOME = join(os.homedir(), ".ee");
|
|
11
|
+
const BRAIN_DIR = join(EE_HOME, "engineering-brain");
|
|
12
|
+
const JAR = join(EE_HOME, "ee.jar");
|
|
13
|
+
const PLUGIN_DIR = join(os.homedir(), ".config", "opencode", "plugins", "ee-experience");
|
|
14
|
+
const SKILL_DIR = join(os.homedir(), ".agents", "skills", "engineering-experience-engine");
|
|
15
|
+
const RELEASE_URL = "https://github.com/ragwhoo/Shin/releases/latest/download/ee.zip";
|
|
16
|
+
const PORT = 8080;
|
|
17
|
+
|
|
18
|
+
const cmd = process.argv[2];
|
|
19
|
+
|
|
20
|
+
function log(msg) { console.log(msg); }
|
|
21
|
+
function ok(msg) { console.log(` \u2713 ${msg}`); }
|
|
22
|
+
function step(msg) { console.log(`\n\u2192 ${msg}`); }
|
|
23
|
+
|
|
24
|
+
function sleep(ms) { return new Promise(r => setTimeout(r, ms)); }
|
|
25
|
+
|
|
26
|
+
function download(url, dest) {
|
|
27
|
+
return new Promise((resolve, reject) => {
|
|
28
|
+
const file = require("fs").createWriteStream(dest);
|
|
29
|
+
const proto = url.startsWith("https") ? https : http;
|
|
30
|
+
proto.get(url, res => {
|
|
31
|
+
if (res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
|
|
32
|
+
download(res.headers.location, dest).then(resolve).catch(reject);
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
res.pipe(file);
|
|
36
|
+
file.on("finish", () => { file.close(); resolve(); });
|
|
37
|
+
}).on("error", reject);
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function isRunning() {
|
|
42
|
+
try {
|
|
43
|
+
const res = require("child_process").execSync(
|
|
44
|
+
`powershell -Command "try { $r = Invoke-WebRequest -Uri 'http://localhost:${PORT}/api/v1/concepts' -UseBasicParsing -TimeoutSec 3; Write-Output $r.StatusCode } catch { Write-Output 'down' }"`,
|
|
45
|
+
{ encoding: "utf-8", timeout: 10000 }
|
|
46
|
+
).trim();
|
|
47
|
+
return res === "200";
|
|
48
|
+
} catch { return false; }
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
async function ensureSetup() {
|
|
52
|
+
if (existsSync(JAR)) return true;
|
|
53
|
+
|
|
54
|
+
step("First-time setup");
|
|
55
|
+
|
|
56
|
+
// Check Java
|
|
57
|
+
try {
|
|
58
|
+
const v = execSync("java -version 2>&1", { encoding: "utf-8" });
|
|
59
|
+
if (!v.match(/"(\d+)/) || parseInt(v.match(/"(\d+)/)[1]) < 21) {
|
|
60
|
+
log(" Java 21+ required. Install from: https://adoptium.net/");
|
|
61
|
+
return false;
|
|
62
|
+
}
|
|
63
|
+
ok("Java found");
|
|
64
|
+
} catch {
|
|
65
|
+
log(" Java 21+ required. Install from: https://adoptium.net/");
|
|
66
|
+
return false;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Download EE
|
|
70
|
+
step("Downloading EE (" + RELEASE_URL + ")");
|
|
71
|
+
if (!existsSync(EE_HOME)) mkdirSync(EE_HOME, { recursive: true });
|
|
72
|
+
const zipPath = join(os.tmpdir(), "ee.zip");
|
|
73
|
+
try {
|
|
74
|
+
await download(RELEASE_URL, zipPath);
|
|
75
|
+
ok("Downloaded");
|
|
76
|
+
} catch (e) {
|
|
77
|
+
log(" Download failed: " + e.message);
|
|
78
|
+
return false;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Extract (use PowerShell for Windows zip extraction)
|
|
82
|
+
step("Extracting...");
|
|
83
|
+
try {
|
|
84
|
+
execSync(
|
|
85
|
+
`powershell -Command "Expand-Archive -Path '${zipPath}' -DestinationPath '${EE_HOME}' -Force"`,
|
|
86
|
+
{ encoding: "utf-8", timeout: 30000 }
|
|
87
|
+
);
|
|
88
|
+
require("fs").unlinkSync(zipPath);
|
|
89
|
+
ok("Extracted to " + EE_HOME);
|
|
90
|
+
} catch (e) {
|
|
91
|
+
log(" Extraction failed: " + e.message);
|
|
92
|
+
return false;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// Install plugin
|
|
96
|
+
if (existsSync(join(EE_HOME, "plugin"))) {
|
|
97
|
+
step("Installing OpenCode plugin...");
|
|
98
|
+
if (!existsSync(PLUGIN_DIR)) mkdirSync(PLUGIN_DIR, { recursive: true });
|
|
99
|
+
execSync(`xcopy "${join(EE_HOME, "plugin")}" "${PLUGIN_DIR}" /E /I /Y`, { encoding: "utf-8", timeout: 10000 });
|
|
100
|
+
ok("Plugin installed");
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Install skill
|
|
104
|
+
if (existsSync(join(EE_HOME, "skill"))) {
|
|
105
|
+
step("Installing skill...");
|
|
106
|
+
if (!existsSync(SKILL_DIR)) mkdirSync(SKILL_DIR, { recursive: true });
|
|
107
|
+
execSync(`xcopy "${join(EE_HOME, "skill")}" "${SKILL_DIR}" /E /I /Y`, { encoding: "utf-8", timeout: 10000 });
|
|
108
|
+
ok("Skill installed");
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
return true;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
async function main() {
|
|
115
|
+
if (!cmd || cmd === "start") {
|
|
116
|
+
if (isRunning()) { log("EE is already running on port " + PORT); return; }
|
|
117
|
+
const setup = await ensureSetup();
|
|
118
|
+
if (!setup) { process.exit(1); }
|
|
119
|
+
|
|
120
|
+
step("Starting EE backend...");
|
|
121
|
+
const env = { ...process.env, ENGINEERING_BRAIN_PATH: BRAIN_DIR };
|
|
122
|
+
const child = spawn("java", [
|
|
123
|
+
"-jar", JAR,
|
|
124
|
+
"--experience-engine.brain-path=" + BRAIN_DIR
|
|
125
|
+
], { env, stdio: "ignore", detached: true });
|
|
126
|
+
child.unref();
|
|
127
|
+
|
|
128
|
+
for (let i = 0; i < 30; i++) {
|
|
129
|
+
await sleep(1000);
|
|
130
|
+
if (isRunning()) {
|
|
131
|
+
log("EE ready on port " + PORT);
|
|
132
|
+
log("\nTell your OpenCode agent: 'load the engineering-experience-engine skill'");
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
log("Failed to start within 30s");
|
|
137
|
+
process.exit(1);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
if (cmd === "stop") {
|
|
141
|
+
try {
|
|
142
|
+
execSync(
|
|
143
|
+
`powershell -Command "$conn = Get-NetTCPConnection -LocalPort ${PORT} -ErrorAction SilentlyContinue; if ($conn) { $pids = $conn.OwningProcess; $pids | ForEach-Object { Stop-Process -Id $_ -Force -ErrorAction SilentlyContinue }; Write-Output 'stopped' } else { Write-Output 'not running' }"`,
|
|
144
|
+
{ encoding: "utf-8", timeout: 10000 }
|
|
145
|
+
);
|
|
146
|
+
log("EE stopped");
|
|
147
|
+
} catch { log("EE not running"); }
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
if (cmd === "status") {
|
|
152
|
+
if (isRunning()) {
|
|
153
|
+
log("EE is running on port " + PORT);
|
|
154
|
+
} else {
|
|
155
|
+
log("EE is not running");
|
|
156
|
+
}
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
if (cmd === "--help" || cmd === "-h") {
|
|
161
|
+
log("Usage: ee <command>");
|
|
162
|
+
log("");
|
|
163
|
+
log("Commands:");
|
|
164
|
+
log(" ee start Start the EE backend (first run downloads + installs)");
|
|
165
|
+
log(" ee stop Stop the EE backend");
|
|
166
|
+
log(" ee status Check if EE is running");
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
log("Unknown command: " + cmd);
|
|
171
|
+
log("Run 'ee --help' for usage");
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
main().catch(e => { console.error(e); process.exit(1); });
|
package/package.json
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "shin-engine",
|
|
3
|
+
"version": "1.0.1",
|
|
4
|
+
"description": "Engineering Experience Engine — engineering judgment for AI coding agents",
|
|
5
|
+
"bin": {
|
|
6
|
+
"shin": "bin/ee.js"
|
|
7
|
+
},
|
|
8
|
+
"files": [
|
|
9
|
+
"bin",
|
|
10
|
+
"lib"
|
|
11
|
+
],
|
|
12
|
+
"keywords": [
|
|
13
|
+
"opencode",
|
|
14
|
+
"engineering",
|
|
15
|
+
"experience",
|
|
16
|
+
"knowledge",
|
|
17
|
+
"brain"
|
|
18
|
+
],
|
|
19
|
+
"license": "MIT"
|
|
20
|
+
}
|