killstata 0.1.0
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/LICENSE +21 -0
- package/README.md +49 -0
- package/bin/killstata +111 -0
- package/package.json +15 -0
- package/postinstall.mjs +193 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 killstata
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
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
|
package/bin/killstata
ADDED
|
@@ -0,0 +1,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
|
+
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
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{
|
|
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.",
|
|
4
|
+
"license": "MIT",
|
|
5
|
+
"bin": {
|
|
6
|
+
"killstata": "./bin/killstata"
|
|
7
|
+
},
|
|
8
|
+
"scripts": {
|
|
9
|
+
"postinstall": "bun ./postinstall.mjs || node ./postinstall.mjs"
|
|
10
|
+
},
|
|
11
|
+
"version": "0.1.0",
|
|
12
|
+
"optionalDependencies": {
|
|
13
|
+
"killstata-windows-x64": "0.1.0"
|
|
14
|
+
}
|
|
15
|
+
}
|
package/postinstall.mjs
ADDED
|
@@ -0,0 +1,193 @@
|
|
|
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("")
|