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.
Files changed (4) hide show
  1. package/README.md +94 -49
  2. package/bin/killstata +113 -111
  3. package/package.json +30 -3
  4. package/postinstall.mjs +194 -193
package/README.md CHANGED
@@ -1,49 +1,94 @@
1
- # killstata
2
-
3
- killstata is an AI-powered CLI for econometric analysis workflows.
4
-
5
- It is designed for users who need reproducible data import, staged preprocessing, econometric estimation, and paper-ready outputs from the command line.
6
-
7
- ## Install
8
-
9
- ```bash
10
- npm install -g killstata
11
- ```
12
-
13
- For source development:
14
-
15
- ```bash
16
- bun install
17
- ```
18
-
19
- ## Run
20
-
21
- ```bash
22
- killstata
23
- ```
24
-
25
- ## What It Supports
26
-
27
- - Data import from `CSV`, `XLSX`, and `DTA`
28
- - Structured working datasets with tracked stages
29
- - QA, filtering, preprocessing, and rollback workflows
30
- - Econometric methods such as OLS, panel fixed effects, DID-style flows, IV, and PSM-related flows
31
- - Output generation for summaries, regression tables, and deliverables
32
-
33
- ## Key Design
34
-
35
- - Continue from saved artifacts instead of rereading raw files
36
- - Treat preprocessing as tracked stages, not silent overwrites
37
- - Generate outputs from structured result files for better traceability
38
-
39
- ## Main Package Path
40
-
41
- - [packages/killstata](.)
42
-
43
- ## Repository
44
-
45
- - GitHub: `https://github.com/dean-create/KILLSTATA`
46
-
47
- ## License
48
-
49
- MIT
1
+ # killstata
2
+
3
+ [![npm version](https://img.shields.io/npm/v/killstata?label=npm)](https://www.npmjs.com/package/killstata)
4
+ ![Windows first](https://img.shields.io/badge/platform-Windows%20first-0078D4)
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
+ ![KillStata start screen](https://raw.githubusercontent.com/dean-create/KillStata/main/docs/images/killstata-home.png)
36
+
37
+ ![KillStata capability view](https://raw.githubusercontent.com/dean-create/KillStata/main/docs/images/killstata-capabilities.png)
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
- Reinstall the npm package for your platform, or install Bun for source-mode development:
107
- https://bun.sh
108
- `)
109
- process.exit(1)
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-powered CLI assistant for econometric analysis. Supports data preprocessing, OLS regression, IV-2SLS, DID, PSM and more causal inference methods.",
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.0",
38
+ "version": "0.1.2",
12
39
  "optionalDependencies": {
13
- "killstata-windows-x64": "0.1.0"
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 is not installed`)
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(` Run ${CYAN}killstata init${RESET} to set up the Python econometrics environment.`)
191
- console.log(` Run ${CYAN}killstata skills list${RESET} to inspect built-in, default, and custom skills.`)
192
- console.log(` Put custom skills under ${CYAN}${localRoot}${RESET} or project-local ${CYAN}.killstata/skills${RESET}.`)
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("")