killstata 0.1.0 → 0.1.2
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/README.md +94 -49
- package/bin/killstata +113 -111
- package/package.json +30 -3
- package/postinstall.mjs +194 -193
package/README.md
CHANGED
|
@@ -1,49 +1,94 @@
|
|
|
1
|
-
# killstata
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
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
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
1
|
+
# killstata
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/killstata)
|
|
4
|
+

|
|
5
|
+
|
|
6
|
+
killstata is an AI-native CLI for econometric analysis workflows.
|
|
7
|
+
|
|
8
|
+
It is designed for users who need reproducible data import, staged preprocessing, econometric estimation, and paper-ready outputs from the command line.
|
|
9
|
+
|
|
10
|
+
## Install
|
|
11
|
+
|
|
12
|
+
Recommended for Windows users:
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
npm i -g killstata@latest
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
For source development:
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
bun install
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Quick Start
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
killstata
|
|
28
|
+
killstata --version
|
|
29
|
+
killstata init
|
|
30
|
+
killstata skills list
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Screenshots
|
|
34
|
+
|
|
35
|
+

|
|
36
|
+
|
|
37
|
+

|
|
38
|
+
|
|
39
|
+
## Common Prompt Examples
|
|
40
|
+
|
|
41
|
+
- `Import this Excel file and show me the schema.`
|
|
42
|
+
- `Run QA on the current dataset and tell me if panel keys are duplicated.`
|
|
43
|
+
- `Use the current panel stage and run a fixed-effects regression with clustered SE.`
|
|
44
|
+
- `Export a three-line table and a short result summary.`
|
|
45
|
+
|
|
46
|
+
## What It Supports
|
|
47
|
+
|
|
48
|
+
- Data import from `CSV`, `XLSX`, and `DTA`
|
|
49
|
+
- Structured working datasets with tracked stages
|
|
50
|
+
- QA, filtering, preprocessing, and rollback workflows
|
|
51
|
+
- Econometric methods such as OLS, panel fixed effects, DID-style flows, IV, and PSM-related flows
|
|
52
|
+
- Output generation for summaries, regression tables, and deliverables
|
|
53
|
+
|
|
54
|
+
## Output Layout
|
|
55
|
+
|
|
56
|
+
Typical artifact layout:
|
|
57
|
+
|
|
58
|
+
```text
|
|
59
|
+
.killstata/
|
|
60
|
+
datasets/
|
|
61
|
+
<datasetId>/
|
|
62
|
+
manifest.json
|
|
63
|
+
stages/
|
|
64
|
+
inspection/
|
|
65
|
+
meta/
|
|
66
|
+
audit/
|
|
67
|
+
reports/
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## Install Troubleshooting
|
|
71
|
+
|
|
72
|
+
If installation succeeds but the CLI still does not start, retry the Windows-first install path:
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
npm i -g killstata@latest
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
If you are developing from source on a platform without a bundled native binary, install Bun:
|
|
79
|
+
|
|
80
|
+
- https://bun.sh
|
|
81
|
+
|
|
82
|
+
## Key Design
|
|
83
|
+
|
|
84
|
+
- Continue from saved artifacts instead of rereading raw files
|
|
85
|
+
- Treat preprocessing as tracked stages, not silent overwrites
|
|
86
|
+
- Generate outputs from structured result files for better traceability
|
|
87
|
+
|
|
88
|
+
## Repository
|
|
89
|
+
|
|
90
|
+
- GitHub: `https://github.com/dean-create/KillStata`
|
|
91
|
+
|
|
92
|
+
## License
|
|
93
|
+
|
|
94
|
+
MIT
|
package/bin/killstata
CHANGED
|
@@ -1,111 +1,113 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
import { execSync, spawn } from "child_process"
|
|
4
|
-
import fs from "fs"
|
|
5
|
-
import os from "os"
|
|
6
|
-
import path from "path"
|
|
7
|
-
import { fileURLToPath } from "url"
|
|
8
|
-
|
|
9
|
-
const __filename = fileURLToPath(import.meta.url)
|
|
10
|
-
const __dirname = path.dirname(__filename)
|
|
11
|
-
|
|
12
|
-
const PACKAGE_ROOT = path.resolve(__dirname, "..")
|
|
13
|
-
const PACKAGE_NAME = "killstata"
|
|
14
|
-
|
|
15
|
-
function commandExists(cmd) {
|
|
16
|
-
try {
|
|
17
|
-
const check = os.platform() === "win32" ? "where" : "which"
|
|
18
|
-
execSync(`${check} ${cmd}`, { stdio: "ignore" })
|
|
19
|
-
return true
|
|
20
|
-
} catch {
|
|
21
|
-
return false
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
function launch(runtime, entryFile) {
|
|
26
|
-
const args = entryFile ? [entryFile, ...process.argv.slice(2)] : [...process.argv.slice(2)]
|
|
27
|
-
|
|
28
|
-
if (runtime === "bun" && entryFile.endsWith(".ts")) {
|
|
29
|
-
args.unshift("run", "--conditions=browser")
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
const child = spawn(runtime, args, {
|
|
33
|
-
stdio: "inherit",
|
|
34
|
-
cwd: PACKAGE_ROOT,
|
|
35
|
-
env: {
|
|
36
|
-
...process.env,
|
|
37
|
-
KILLSTATA: "1",
|
|
38
|
-
},
|
|
39
|
-
})
|
|
40
|
-
|
|
41
|
-
child.on("exit", (code) => {
|
|
42
|
-
process.exit(code ?? 0)
|
|
43
|
-
})
|
|
44
|
-
|
|
45
|
-
child.on("error", (err) => {
|
|
46
|
-
console.error(`Failed to launch killstata: ${err.message}`)
|
|
47
|
-
process.exit(1)
|
|
48
|
-
})
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
function installedBinaryCandidates() {
|
|
52
|
-
const platform = os.platform() === "win32" ? "windows" : os.platform()
|
|
53
|
-
const arch = os.arch() === "x64" || os.arch() === "arm64" ? os.arch() : "x64"
|
|
54
|
-
const names = [`${PACKAGE_NAME}-${platform}-${arch}`]
|
|
55
|
-
const nodeModulesRoots = [path.join(PACKAGE_ROOT, "node_modules"), path.resolve(PACKAGE_ROOT, "..")]
|
|
56
|
-
|
|
57
|
-
if (platform === "linux") {
|
|
58
|
-
names.push(
|
|
59
|
-
`${PACKAGE_NAME}-${platform}-${arch}-musl`,
|
|
60
|
-
`${PACKAGE_NAME}-${platform}-${arch}-baseline`,
|
|
61
|
-
`${PACKAGE_NAME}-${platform}-${arch}-baseline-musl`,
|
|
62
|
-
)
|
|
63
|
-
} else if (arch === "x64") {
|
|
64
|
-
names.push(`${PACKAGE_NAME}-${platform}-${arch}-baseline`)
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
return nodeModulesRoots.flatMap((root) =>
|
|
68
|
-
names.flatMap((name) => {
|
|
69
|
-
const base = path.join(root, name, "bin")
|
|
70
|
-
return [path.join(base, `${PACKAGE_NAME}.exe`), path.join(base, PACKAGE_NAME)]
|
|
71
|
-
}),
|
|
72
|
-
)
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
if (process.env.KILLSTATA_BIN_PATH) {
|
|
76
|
-
launch(process.env.KILLSTATA_BIN_PATH, "")
|
|
77
|
-
} else {
|
|
78
|
-
let launched = false
|
|
79
|
-
|
|
80
|
-
for (const binaryPath of installedBinaryCandidates()) {
|
|
81
|
-
if (!fs.existsSync(binaryPath)) continue
|
|
82
|
-
launch(binaryPath, "")
|
|
83
|
-
launched = true
|
|
84
|
-
break
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
if (!launched) {
|
|
88
|
-
const tsEntry = path.join(PACKAGE_ROOT, "src", "index.ts")
|
|
89
|
-
if (commandExists("bun") && fs.existsSync(tsEntry)) {
|
|
90
|
-
launch("bun", tsEntry)
|
|
91
|
-
launched = true
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
if (!launched) {
|
|
96
|
-
const jsEntry = path.join(PACKAGE_ROOT, "dist", "index.js")
|
|
97
|
-
if (fs.existsSync(jsEntry)) {
|
|
98
|
-
launch("node", jsEntry)
|
|
99
|
-
launched = true
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
if (!launched) {
|
|
104
|
-
console.error(`
|
|
105
|
-
killstata could not find a bundled native binary, a local dist build, or a Bun runtime.
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { execSync, spawn } from "child_process"
|
|
4
|
+
import fs from "fs"
|
|
5
|
+
import os from "os"
|
|
6
|
+
import path from "path"
|
|
7
|
+
import { fileURLToPath } from "url"
|
|
8
|
+
|
|
9
|
+
const __filename = fileURLToPath(import.meta.url)
|
|
10
|
+
const __dirname = path.dirname(__filename)
|
|
11
|
+
|
|
12
|
+
const PACKAGE_ROOT = path.resolve(__dirname, "..")
|
|
13
|
+
const PACKAGE_NAME = "killstata"
|
|
14
|
+
|
|
15
|
+
function commandExists(cmd) {
|
|
16
|
+
try {
|
|
17
|
+
const check = os.platform() === "win32" ? "where" : "which"
|
|
18
|
+
execSync(`${check} ${cmd}`, { stdio: "ignore" })
|
|
19
|
+
return true
|
|
20
|
+
} catch {
|
|
21
|
+
return false
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function launch(runtime, entryFile) {
|
|
26
|
+
const args = entryFile ? [entryFile, ...process.argv.slice(2)] : [...process.argv.slice(2)]
|
|
27
|
+
|
|
28
|
+
if (runtime === "bun" && entryFile.endsWith(".ts")) {
|
|
29
|
+
args.unshift("run", "--conditions=browser")
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const child = spawn(runtime, args, {
|
|
33
|
+
stdio: "inherit",
|
|
34
|
+
cwd: PACKAGE_ROOT,
|
|
35
|
+
env: {
|
|
36
|
+
...process.env,
|
|
37
|
+
KILLSTATA: "1",
|
|
38
|
+
},
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
child.on("exit", (code) => {
|
|
42
|
+
process.exit(code ?? 0)
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
child.on("error", (err) => {
|
|
46
|
+
console.error(`Failed to launch killstata: ${err.message}`)
|
|
47
|
+
process.exit(1)
|
|
48
|
+
})
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function installedBinaryCandidates() {
|
|
52
|
+
const platform = os.platform() === "win32" ? "windows" : os.platform()
|
|
53
|
+
const arch = os.arch() === "x64" || os.arch() === "arm64" ? os.arch() : "x64"
|
|
54
|
+
const names = [`${PACKAGE_NAME}-${platform}-${arch}`]
|
|
55
|
+
const nodeModulesRoots = [path.join(PACKAGE_ROOT, "node_modules"), path.resolve(PACKAGE_ROOT, "..")]
|
|
56
|
+
|
|
57
|
+
if (platform === "linux") {
|
|
58
|
+
names.push(
|
|
59
|
+
`${PACKAGE_NAME}-${platform}-${arch}-musl`,
|
|
60
|
+
`${PACKAGE_NAME}-${platform}-${arch}-baseline`,
|
|
61
|
+
`${PACKAGE_NAME}-${platform}-${arch}-baseline-musl`,
|
|
62
|
+
)
|
|
63
|
+
} else if (arch === "x64") {
|
|
64
|
+
names.push(`${PACKAGE_NAME}-${platform}-${arch}-baseline`)
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return nodeModulesRoots.flatMap((root) =>
|
|
68
|
+
names.flatMap((name) => {
|
|
69
|
+
const base = path.join(root, name, "bin")
|
|
70
|
+
return [path.join(base, `${PACKAGE_NAME}.exe`), path.join(base, PACKAGE_NAME)]
|
|
71
|
+
}),
|
|
72
|
+
)
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if (process.env.KILLSTATA_BIN_PATH) {
|
|
76
|
+
launch(process.env.KILLSTATA_BIN_PATH, "")
|
|
77
|
+
} else {
|
|
78
|
+
let launched = false
|
|
79
|
+
|
|
80
|
+
for (const binaryPath of installedBinaryCandidates()) {
|
|
81
|
+
if (!fs.existsSync(binaryPath)) continue
|
|
82
|
+
launch(binaryPath, "")
|
|
83
|
+
launched = true
|
|
84
|
+
break
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
if (!launched) {
|
|
88
|
+
const tsEntry = path.join(PACKAGE_ROOT, "src", "index.ts")
|
|
89
|
+
if (commandExists("bun") && fs.existsSync(tsEntry)) {
|
|
90
|
+
launch("bun", tsEntry)
|
|
91
|
+
launched = true
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (!launched) {
|
|
96
|
+
const jsEntry = path.join(PACKAGE_ROOT, "dist", "index.js")
|
|
97
|
+
if (fs.existsSync(jsEntry)) {
|
|
98
|
+
launch("node", jsEntry)
|
|
99
|
+
launched = true
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
if (!launched) {
|
|
104
|
+
console.error(`
|
|
105
|
+
killstata could not find a bundled native binary, a local dist build, or a Bun runtime.
|
|
106
|
+
Windows users should retry:
|
|
107
|
+
npm i -g killstata@latest
|
|
108
|
+
For source-mode development on unsupported platforms, install Bun:
|
|
109
|
+
https://bun.sh
|
|
110
|
+
`)
|
|
111
|
+
process.exit(1)
|
|
112
|
+
}
|
|
113
|
+
}
|
package/package.json
CHANGED
|
@@ -1,15 +1,42 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "killstata",
|
|
3
|
-
"description": "AI-
|
|
3
|
+
"description": "AI-native econometrics CLI with staged data import, regression workflows, and paper-ready outputs.",
|
|
4
4
|
"license": "MIT",
|
|
5
|
+
"author": "killstata",
|
|
6
|
+
"homepage": "https://github.com/dean-create/KillStata#readme",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "https://github.com/dean-create/KillStata.git",
|
|
10
|
+
"directory": "packages/killstata"
|
|
11
|
+
},
|
|
12
|
+
"keywords": [
|
|
13
|
+
"econometrics",
|
|
14
|
+
"econometrics-cli",
|
|
15
|
+
"stata",
|
|
16
|
+
"stata-alternative",
|
|
17
|
+
"regression",
|
|
18
|
+
"causal-inference",
|
|
19
|
+
"empirical-research",
|
|
20
|
+
"data-analysis",
|
|
21
|
+
"panel-data",
|
|
22
|
+
"panel-regression",
|
|
23
|
+
"DID",
|
|
24
|
+
"difference-in-differences",
|
|
25
|
+
"PSM",
|
|
26
|
+
"IV",
|
|
27
|
+
"iv-2sls",
|
|
28
|
+
"cli",
|
|
29
|
+
"windows-cli",
|
|
30
|
+
"ai-agent"
|
|
31
|
+
],
|
|
5
32
|
"bin": {
|
|
6
33
|
"killstata": "./bin/killstata"
|
|
7
34
|
},
|
|
8
35
|
"scripts": {
|
|
9
36
|
"postinstall": "bun ./postinstall.mjs || node ./postinstall.mjs"
|
|
10
37
|
},
|
|
11
|
-
"version": "0.1.
|
|
38
|
+
"version": "0.1.2",
|
|
12
39
|
"optionalDependencies": {
|
|
13
|
-
"killstata-windows-x64": "0.1.
|
|
40
|
+
"killstata-windows-x64": "0.1.2"
|
|
14
41
|
}
|
|
15
42
|
}
|
package/postinstall.mjs
CHANGED
|
@@ -1,193 +1,194 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
import { execSync } from "child_process"
|
|
4
|
-
import fs from "fs"
|
|
5
|
-
import os from "os"
|
|
6
|
-
import path from "path"
|
|
7
|
-
|
|
8
|
-
const BOLD = "\x1b[1m"
|
|
9
|
-
const GREEN = "\x1b[32m"
|
|
10
|
-
const YELLOW = "\x1b[33m"
|
|
11
|
-
const CYAN = "\x1b[36m"
|
|
12
|
-
const RESET = "\x1b[0m"
|
|
13
|
-
|
|
14
|
-
function commandExists(cmd) {
|
|
15
|
-
try {
|
|
16
|
-
const check = os.platform() === "win32" ? "where" : "which"
|
|
17
|
-
execSync(`${check} ${cmd}`, { stdio: "ignore" })
|
|
18
|
-
return true
|
|
19
|
-
} catch {
|
|
20
|
-
return false
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
function getVersion(cmd, flag = "--version") {
|
|
25
|
-
try {
|
|
26
|
-
return execSync(`${cmd} ${flag}`, { encoding: "utf-8" }).trim()
|
|
27
|
-
} catch {
|
|
28
|
-
return null
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
function ensureDir(dir) {
|
|
33
|
-
fs.mkdirSync(dir, { recursive: true })
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
function candidateBinaryPackages() {
|
|
37
|
-
const platform = os.platform() === "win32" ? "windows" : os.platform()
|
|
38
|
-
const arch = os.arch() === "x64" || os.arch() === "arm64" ? os.arch() : "x64"
|
|
39
|
-
const names = [`killstata-${platform}-${arch}`]
|
|
40
|
-
|
|
41
|
-
if (platform === "linux") {
|
|
42
|
-
names.push(`killstata-${platform}-${arch}-musl`, `killstata-${platform}-${arch}-baseline`, `killstata-${platform}-${arch}-baseline-musl`)
|
|
43
|
-
} else if (arch === "x64") {
|
|
44
|
-
names.push(`killstata-${platform}-${arch}-baseline`)
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
return names
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
function hasInstalledNativeBinary() {
|
|
51
|
-
const nodeModulesDirs = [
|
|
52
|
-
path.resolve(import.meta.dirname, "../node_modules"),
|
|
53
|
-
path.resolve(import.meta.dirname, "../../node_modules"),
|
|
54
|
-
]
|
|
55
|
-
|
|
56
|
-
return candidateBinaryPackages().some((name) => nodeModulesDirs.some((dir) => fs.existsSync(path.join(dir, name))))
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
function readPackageInfo() {
|
|
60
|
-
const pkgPath = path.resolve(import.meta.dirname, "../package.json")
|
|
61
|
-
try {
|
|
62
|
-
return JSON.parse(fs.readFileSync(pkgPath, "utf-8"))
|
|
63
|
-
} catch {
|
|
64
|
-
return { name: "killstata", version: "0.0.0" }
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
function advertisedNativeBinary() {
|
|
69
|
-
const pkg = readPackageInfo()
|
|
70
|
-
const optionalDeps = Object.keys(pkg.optionalDependencies ?? {})
|
|
71
|
-
return candidateBinaryPackages().some((name) => optionalDeps.includes(name))
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
function listManagedDefaultSkills(root) {
|
|
75
|
-
if (!fs.existsSync(root)) return []
|
|
76
|
-
return fs
|
|
77
|
-
.readdirSync(root, { withFileTypes: true })
|
|
78
|
-
.filter((entry) => entry.isDirectory() && fs.existsSync(path.join(root, entry.name, "SKILL.md")))
|
|
79
|
-
.map((entry) => entry.name)
|
|
80
|
-
.sort()
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
function copyDirectory(source, destination) {
|
|
84
|
-
fs.rmSync(destination, { recursive: true, force: true })
|
|
85
|
-
fs.mkdirSync(path.dirname(destination), { recursive: true })
|
|
86
|
-
fs.cpSync(source, destination, { recursive: true, force: true })
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
function syncManagedDefaultSkills(sourceRoot, destinationRoot, manifestPath, version) {
|
|
90
|
-
ensureDir(destinationRoot)
|
|
91
|
-
|
|
92
|
-
const bundled = listManagedDefaultSkills(sourceRoot)
|
|
93
|
-
let previous = {}
|
|
94
|
-
if (fs.existsSync(manifestPath)) {
|
|
95
|
-
try {
|
|
96
|
-
previous = JSON.parse(fs.readFileSync(manifestPath, "utf-8") || "{}")
|
|
97
|
-
} catch {
|
|
98
|
-
previous = {}
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
const previousSkills = Array.isArray(previous.skills) ? previous.skills : []
|
|
102
|
-
|
|
103
|
-
for (const skillName of previousSkills) {
|
|
104
|
-
if (bundled.includes(skillName)) continue
|
|
105
|
-
fs.rmSync(path.join(destinationRoot, skillName), { recursive: true, force: true })
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
for (const skillName of bundled) {
|
|
109
|
-
copyDirectory(path.join(sourceRoot, skillName), path.join(destinationRoot, skillName))
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
fs.writeFileSync(
|
|
113
|
-
manifestPath,
|
|
114
|
-
JSON.stringify(
|
|
115
|
-
{
|
|
116
|
-
managedBy: "killstata-postinstall",
|
|
117
|
-
version,
|
|
118
|
-
generatedAt: new Date().toISOString(),
|
|
119
|
-
skills: bundled,
|
|
120
|
-
},
|
|
121
|
-
null,
|
|
122
|
-
2,
|
|
123
|
-
),
|
|
124
|
-
"utf-8",
|
|
125
|
-
)
|
|
126
|
-
|
|
127
|
-
return bundled
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
const homeDir = process.env.KILLSTATA_TEST_HOME || os.homedir()
|
|
131
|
-
const killstataRoot = path.join(homeDir, ".killstata")
|
|
132
|
-
const skillRoot = path.join(killstataRoot, "skills")
|
|
133
|
-
const defaultRoot = path.join(skillRoot, "default")
|
|
134
|
-
const importedRoot = path.join(skillRoot, "imported")
|
|
135
|
-
const localRoot = path.join(skillRoot, "local")
|
|
136
|
-
const cacheRoot = path.join(skillRoot, "cache")
|
|
137
|
-
const managedManifestPath = path.join(defaultRoot, ".managed.json")
|
|
138
|
-
const builtinRoot = path.resolve(import.meta.dirname, "../skills/builtin")
|
|
139
|
-
const bundledDefaultRoot = path.resolve(import.meta.dirname, "../skills/default")
|
|
140
|
-
const pkg = readPackageInfo()
|
|
141
|
-
|
|
142
|
-
ensureDir(killstataRoot)
|
|
143
|
-
ensureDir(skillRoot)
|
|
144
|
-
ensureDir(defaultRoot)
|
|
145
|
-
ensureDir(importedRoot)
|
|
146
|
-
ensureDir(localRoot)
|
|
147
|
-
ensureDir(cacheRoot)
|
|
148
|
-
|
|
149
|
-
const syncedSkills = syncManagedDefaultSkills(bundledDefaultRoot, defaultRoot, managedManifestPath, pkg.version)
|
|
150
|
-
|
|
151
|
-
console.log("")
|
|
152
|
-
console.log(`${BOLD}${CYAN}killstata${RESET} - AI-powered Econometrics Agent`)
|
|
153
|
-
console.log("")
|
|
154
|
-
|
|
155
|
-
const hasNativeBinary = hasInstalledNativeBinary()
|
|
156
|
-
const hasAdvertisedNativeBinary = advertisedNativeBinary()
|
|
157
|
-
if (hasNativeBinary) {
|
|
158
|
-
console.log(` ${GREEN}[OK]${RESET} Native package installed for ${os.platform()}/${os.arch()}`)
|
|
159
|
-
} else if (hasAdvertisedNativeBinary) {
|
|
160
|
-
console.log(` ${YELLOW}[WARN]${RESET} Native package for ${os.platform()}/${os.arch()} was expected but
|
|
161
|
-
} else {
|
|
162
|
-
console.log(` ${YELLOW}[WARN]${RESET} This release does not bundle a native package for ${os.platform()}/${os.arch()}`)
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
const hasBun = commandExists("bun")
|
|
166
|
-
if (hasBun) {
|
|
167
|
-
console.log(` ${GREEN}[OK]${RESET} Bun runtime: ${getVersion("bun")}`)
|
|
168
|
-
} else if (hasNativeBinary) {
|
|
169
|
-
console.log(` ${GREEN}[OK]${RESET} Bun runtime not required for this install`)
|
|
170
|
-
} else {
|
|
171
|
-
console.log(` ${YELLOW}[WARN]${RESET} Bun runtime not found; this install will not run without Bun or a native binary`)
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
const pythonCmd = os.platform() === "win32" ? "python" : "python3"
|
|
175
|
-
if (commandExists(pythonCmd)) {
|
|
176
|
-
console.log(` ${GREEN}[OK]${RESET} Python: ${getVersion(pythonCmd)}`)
|
|
177
|
-
} else {
|
|
178
|
-
console.log(` ${YELLOW}[WARN]${RESET} Python not found`)
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
console.log(` ${GREEN}[OK]${RESET} Skills root: ${skillRoot}`)
|
|
182
|
-
console.log(` ${GREEN}[OK]${RESET} Default skills: ${defaultRoot} (${syncedSkills.length} managed)`)
|
|
183
|
-
console.log(` ${GREEN}[OK]${RESET} Built-in skills: ${builtinRoot}`)
|
|
184
|
-
console.log(` ${GREEN}[OK]${RESET} Imported skills: ${importedRoot}`)
|
|
185
|
-
console.log(` ${GREEN}[OK]${RESET} Local skills: ${localRoot}`)
|
|
186
|
-
console.log(` ${GREEN}[OK]${RESET} Managed default manifest: ${managedManifestPath}`)
|
|
187
|
-
|
|
188
|
-
console.log("")
|
|
189
|
-
console.log(`${BOLD}Next steps${RESET}`)
|
|
190
|
-
console.log(`
|
|
191
|
-
console.log(` Run ${CYAN}killstata
|
|
192
|
-
console.log(`
|
|
193
|
-
console.log(
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { execSync } from "child_process"
|
|
4
|
+
import fs from "fs"
|
|
5
|
+
import os from "os"
|
|
6
|
+
import path from "path"
|
|
7
|
+
|
|
8
|
+
const BOLD = "\x1b[1m"
|
|
9
|
+
const GREEN = "\x1b[32m"
|
|
10
|
+
const YELLOW = "\x1b[33m"
|
|
11
|
+
const CYAN = "\x1b[36m"
|
|
12
|
+
const RESET = "\x1b[0m"
|
|
13
|
+
|
|
14
|
+
function commandExists(cmd) {
|
|
15
|
+
try {
|
|
16
|
+
const check = os.platform() === "win32" ? "where" : "which"
|
|
17
|
+
execSync(`${check} ${cmd}`, { stdio: "ignore" })
|
|
18
|
+
return true
|
|
19
|
+
} catch {
|
|
20
|
+
return false
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function getVersion(cmd, flag = "--version") {
|
|
25
|
+
try {
|
|
26
|
+
return execSync(`${cmd} ${flag}`, { encoding: "utf-8" }).trim()
|
|
27
|
+
} catch {
|
|
28
|
+
return null
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function ensureDir(dir) {
|
|
33
|
+
fs.mkdirSync(dir, { recursive: true })
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function candidateBinaryPackages() {
|
|
37
|
+
const platform = os.platform() === "win32" ? "windows" : os.platform()
|
|
38
|
+
const arch = os.arch() === "x64" || os.arch() === "arm64" ? os.arch() : "x64"
|
|
39
|
+
const names = [`killstata-${platform}-${arch}`]
|
|
40
|
+
|
|
41
|
+
if (platform === "linux") {
|
|
42
|
+
names.push(`killstata-${platform}-${arch}-musl`, `killstata-${platform}-${arch}-baseline`, `killstata-${platform}-${arch}-baseline-musl`)
|
|
43
|
+
} else if (arch === "x64") {
|
|
44
|
+
names.push(`killstata-${platform}-${arch}-baseline`)
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return names
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function hasInstalledNativeBinary() {
|
|
51
|
+
const nodeModulesDirs = [
|
|
52
|
+
path.resolve(import.meta.dirname, "../node_modules"),
|
|
53
|
+
path.resolve(import.meta.dirname, "../../node_modules"),
|
|
54
|
+
]
|
|
55
|
+
|
|
56
|
+
return candidateBinaryPackages().some((name) => nodeModulesDirs.some((dir) => fs.existsSync(path.join(dir, name))))
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function readPackageInfo() {
|
|
60
|
+
const pkgPath = path.resolve(import.meta.dirname, "../package.json")
|
|
61
|
+
try {
|
|
62
|
+
return JSON.parse(fs.readFileSync(pkgPath, "utf-8"))
|
|
63
|
+
} catch {
|
|
64
|
+
return { name: "killstata", version: "0.0.0" }
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function advertisedNativeBinary() {
|
|
69
|
+
const pkg = readPackageInfo()
|
|
70
|
+
const optionalDeps = Object.keys(pkg.optionalDependencies ?? {})
|
|
71
|
+
return candidateBinaryPackages().some((name) => optionalDeps.includes(name))
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function listManagedDefaultSkills(root) {
|
|
75
|
+
if (!fs.existsSync(root)) return []
|
|
76
|
+
return fs
|
|
77
|
+
.readdirSync(root, { withFileTypes: true })
|
|
78
|
+
.filter((entry) => entry.isDirectory() && fs.existsSync(path.join(root, entry.name, "SKILL.md")))
|
|
79
|
+
.map((entry) => entry.name)
|
|
80
|
+
.sort()
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function copyDirectory(source, destination) {
|
|
84
|
+
fs.rmSync(destination, { recursive: true, force: true })
|
|
85
|
+
fs.mkdirSync(path.dirname(destination), { recursive: true })
|
|
86
|
+
fs.cpSync(source, destination, { recursive: true, force: true })
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function syncManagedDefaultSkills(sourceRoot, destinationRoot, manifestPath, version) {
|
|
90
|
+
ensureDir(destinationRoot)
|
|
91
|
+
|
|
92
|
+
const bundled = listManagedDefaultSkills(sourceRoot)
|
|
93
|
+
let previous = {}
|
|
94
|
+
if (fs.existsSync(manifestPath)) {
|
|
95
|
+
try {
|
|
96
|
+
previous = JSON.parse(fs.readFileSync(manifestPath, "utf-8") || "{}")
|
|
97
|
+
} catch {
|
|
98
|
+
previous = {}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
const previousSkills = Array.isArray(previous.skills) ? previous.skills : []
|
|
102
|
+
|
|
103
|
+
for (const skillName of previousSkills) {
|
|
104
|
+
if (bundled.includes(skillName)) continue
|
|
105
|
+
fs.rmSync(path.join(destinationRoot, skillName), { recursive: true, force: true })
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
for (const skillName of bundled) {
|
|
109
|
+
copyDirectory(path.join(sourceRoot, skillName), path.join(destinationRoot, skillName))
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
fs.writeFileSync(
|
|
113
|
+
manifestPath,
|
|
114
|
+
JSON.stringify(
|
|
115
|
+
{
|
|
116
|
+
managedBy: "killstata-postinstall",
|
|
117
|
+
version,
|
|
118
|
+
generatedAt: new Date().toISOString(),
|
|
119
|
+
skills: bundled,
|
|
120
|
+
},
|
|
121
|
+
null,
|
|
122
|
+
2,
|
|
123
|
+
),
|
|
124
|
+
"utf-8",
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
return bundled
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const homeDir = process.env.KILLSTATA_TEST_HOME || os.homedir()
|
|
131
|
+
const killstataRoot = path.join(homeDir, ".killstata")
|
|
132
|
+
const skillRoot = path.join(killstataRoot, "skills")
|
|
133
|
+
const defaultRoot = path.join(skillRoot, "default")
|
|
134
|
+
const importedRoot = path.join(skillRoot, "imported")
|
|
135
|
+
const localRoot = path.join(skillRoot, "local")
|
|
136
|
+
const cacheRoot = path.join(skillRoot, "cache")
|
|
137
|
+
const managedManifestPath = path.join(defaultRoot, ".managed.json")
|
|
138
|
+
const builtinRoot = path.resolve(import.meta.dirname, "../skills/builtin")
|
|
139
|
+
const bundledDefaultRoot = path.resolve(import.meta.dirname, "../skills/default")
|
|
140
|
+
const pkg = readPackageInfo()
|
|
141
|
+
|
|
142
|
+
ensureDir(killstataRoot)
|
|
143
|
+
ensureDir(skillRoot)
|
|
144
|
+
ensureDir(defaultRoot)
|
|
145
|
+
ensureDir(importedRoot)
|
|
146
|
+
ensureDir(localRoot)
|
|
147
|
+
ensureDir(cacheRoot)
|
|
148
|
+
|
|
149
|
+
const syncedSkills = syncManagedDefaultSkills(bundledDefaultRoot, defaultRoot, managedManifestPath, pkg.version)
|
|
150
|
+
|
|
151
|
+
console.log("")
|
|
152
|
+
console.log(`${BOLD}${CYAN}killstata${RESET} - AI-powered Econometrics Agent`)
|
|
153
|
+
console.log("")
|
|
154
|
+
|
|
155
|
+
const hasNativeBinary = hasInstalledNativeBinary()
|
|
156
|
+
const hasAdvertisedNativeBinary = advertisedNativeBinary()
|
|
157
|
+
if (hasNativeBinary) {
|
|
158
|
+
console.log(` ${GREEN}[OK]${RESET} Native package installed for ${os.platform()}/${os.arch()}`)
|
|
159
|
+
} else if (hasAdvertisedNativeBinary) {
|
|
160
|
+
console.log(` ${YELLOW}[WARN]${RESET} Native package for ${os.platform()}/${os.arch()} was expected but was not found after install`)
|
|
161
|
+
} else {
|
|
162
|
+
console.log(` ${YELLOW}[WARN]${RESET} This release does not currently bundle a native package for ${os.platform()}/${os.arch()}`)
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
const hasBun = commandExists("bun")
|
|
166
|
+
if (hasBun) {
|
|
167
|
+
console.log(` ${GREEN}[OK]${RESET} Bun runtime: ${getVersion("bun")}`)
|
|
168
|
+
} else if (hasNativeBinary) {
|
|
169
|
+
console.log(` ${GREEN}[OK]${RESET} Bun runtime not required for this install`)
|
|
170
|
+
} else {
|
|
171
|
+
console.log(` ${YELLOW}[WARN]${RESET} Bun runtime not found; this install will not run without Bun or a bundled native binary`)
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
const pythonCmd = os.platform() === "win32" ? "python" : "python3"
|
|
175
|
+
if (commandExists(pythonCmd)) {
|
|
176
|
+
console.log(` ${GREEN}[OK]${RESET} Python: ${getVersion(pythonCmd)}`)
|
|
177
|
+
} else {
|
|
178
|
+
console.log(` ${YELLOW}[WARN]${RESET} Python not found`)
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
console.log(` ${GREEN}[OK]${RESET} Skills root: ${skillRoot}`)
|
|
182
|
+
console.log(` ${GREEN}[OK]${RESET} Default skills: ${defaultRoot} (${syncedSkills.length} managed)`)
|
|
183
|
+
console.log(` ${GREEN}[OK]${RESET} Built-in skills: ${builtinRoot}`)
|
|
184
|
+
console.log(` ${GREEN}[OK]${RESET} Imported skills: ${importedRoot}`)
|
|
185
|
+
console.log(` ${GREEN}[OK]${RESET} Local skills: ${localRoot}`)
|
|
186
|
+
console.log(` ${GREEN}[OK]${RESET} Managed default manifest: ${managedManifestPath}`)
|
|
187
|
+
|
|
188
|
+
console.log("")
|
|
189
|
+
console.log(`${BOLD}Next steps${RESET}`)
|
|
190
|
+
console.log(` Windows users: reinstall with ${CYAN}npm i -g killstata@latest${RESET} if the native binary was not installed correctly.`)
|
|
191
|
+
console.log(` Run ${CYAN}killstata init${RESET} to set up the Python econometrics environment.`)
|
|
192
|
+
console.log(` Run ${CYAN}killstata skills list${RESET} to inspect built-in, default, and custom skills.`)
|
|
193
|
+
console.log(` Put custom skills under ${CYAN}${localRoot}${RESET} or project-local ${CYAN}.killstata/skills${RESET}.`)
|
|
194
|
+
console.log("")
|