okstra 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/README.md +36 -0
- package/bin/okstra +62 -0
- package/package.json +30 -0
- package/runtime/.gitkeep +0 -0
- package/runtime/BUILD.json +5 -0
- package/runtime/agents/SKILL.md +243 -0
- package/runtime/agents/TODO.md +168 -0
- package/runtime/agents/workers/claude-worker.md +106 -0
- package/runtime/agents/workers/codex-worker.md +179 -0
- package/runtime/agents/workers/gemini-worker.md +179 -0
- package/runtime/agents/workers/report-writer-worker.md +116 -0
- package/runtime/bin/okstra-central.sh +152 -0
- package/runtime/bin/okstra-codex-exec.sh +53 -0
- package/runtime/bin/okstra-error-log.py +295 -0
- package/runtime/bin/okstra-gemini-exec.sh +55 -0
- package/runtime/bin/okstra-token-usage.py +46 -0
- package/runtime/bin/okstra.sh +162 -0
- package/runtime/prompts/launch.template.md +52 -0
- package/runtime/prompts/profiles/error-analysis.md +43 -0
- package/runtime/prompts/profiles/final-verification.md +37 -0
- package/runtime/prompts/profiles/implementation-planning.md +85 -0
- package/runtime/prompts/profiles/implementation.md +71 -0
- package/runtime/prompts/profiles/requirements-discovery.md +43 -0
- package/runtime/python/lib/okstra/cli.sh +227 -0
- package/runtime/python/lib/okstra/globals.sh +157 -0
- package/runtime/python/lib/okstra/interactive.sh +411 -0
- package/runtime/python/lib/okstra/project-resolver.sh +57 -0
- package/runtime/python/lib/okstra/usage.sh +98 -0
- package/runtime/python/lib/okstra-ctl/cmd-batch.sh +59 -0
- package/runtime/python/lib/okstra-ctl/cmd-list.sh +35 -0
- package/runtime/python/lib/okstra-ctl/cmd-open.sh +36 -0
- package/runtime/python/lib/okstra-ctl/cmd-projects.sh +26 -0
- package/runtime/python/lib/okstra-ctl/cmd-reconcile.sh +27 -0
- package/runtime/python/lib/okstra-ctl/cmd-reindex.sh +38 -0
- package/runtime/python/lib/okstra-ctl/cmd-rerun.sh +326 -0
- package/runtime/python/lib/okstra-ctl/cmd-show.sh +27 -0
- package/runtime/python/lib/okstra-ctl/cmd-tail.sh +76 -0
- package/runtime/python/lib/okstra-ctl/main.sh +41 -0
- package/runtime/python/lib/okstra-ctl/prepare.sh +29 -0
- package/runtime/python/lib/okstra-ctl/usage.sh +23 -0
- package/runtime/python/okstra_ctl/__init__.py +125 -0
- package/runtime/python/okstra_ctl/backfill.py +253 -0
- package/runtime/python/okstra_ctl/batch.py +62 -0
- package/runtime/python/okstra_ctl/ids.py +84 -0
- package/runtime/python/okstra_ctl/index.py +216 -0
- package/runtime/python/okstra_ctl/invocation.py +49 -0
- package/runtime/python/okstra_ctl/jsonl.py +84 -0
- package/runtime/python/okstra_ctl/listing.py +156 -0
- package/runtime/python/okstra_ctl/locks.py +42 -0
- package/runtime/python/okstra_ctl/material.py +62 -0
- package/runtime/python/okstra_ctl/models.py +63 -0
- package/runtime/python/okstra_ctl/path_resolve.py +40 -0
- package/runtime/python/okstra_ctl/paths.py +251 -0
- package/runtime/python/okstra_ctl/project_meta.py +51 -0
- package/runtime/python/okstra_ctl/reconcile.py +166 -0
- package/runtime/python/okstra_ctl/render.py +1065 -0
- package/runtime/python/okstra_ctl/resolver.py +54 -0
- package/runtime/python/okstra_ctl/run.py +674 -0
- package/runtime/python/okstra_ctl/run_context.py +166 -0
- package/runtime/python/okstra_ctl/seeding.py +97 -0
- package/runtime/python/okstra_ctl/sequence.py +53 -0
- package/runtime/python/okstra_ctl/session.py +33 -0
- package/runtime/python/okstra_ctl/tmux.py +27 -0
- package/runtime/python/okstra_ctl/workers.py +64 -0
- package/runtime/python/okstra_ctl/workflow.py +182 -0
- package/runtime/python/okstra_project/__init__.py +41 -0
- package/runtime/python/okstra_project/resolver.py +126 -0
- package/runtime/python/okstra_project/state.py +170 -0
- package/runtime/python/okstra_token_usage/__init__.py +26 -0
- package/runtime/python/okstra_token_usage/blocks.py +62 -0
- package/runtime/python/okstra_token_usage/claude.py +97 -0
- package/runtime/python/okstra_token_usage/cli.py +84 -0
- package/runtime/python/okstra_token_usage/codex.py +80 -0
- package/runtime/python/okstra_token_usage/collect.py +161 -0
- package/runtime/python/okstra_token_usage/gemini.py +77 -0
- package/runtime/python/okstra_token_usage/jsonl_io.py +18 -0
- package/runtime/python/okstra_token_usage/paths.py +22 -0
- package/runtime/python/okstra_token_usage/pricing.py +71 -0
- package/runtime/python/okstra_token_usage/report.py +64 -0
- package/runtime/templates/prd/brief.template.md +273 -0
- package/runtime/templates/project-docs/task-index.template.md +65 -0
- package/runtime/templates/reports/error-analysis-input.template.md +80 -0
- package/runtime/templates/reports/final-report.template.md +167 -0
- package/runtime/templates/reports/final-verification-input.template.md +67 -0
- package/runtime/templates/reports/implementation-input.template.md +81 -0
- package/runtime/templates/reports/implementation-planning-input.template.md +93 -0
- package/runtime/templates/reports/quick-input.template.md +64 -0
- package/runtime/templates/reports/schedule.template.md +168 -0
- package/runtime/templates/reports/settings.template.json +101 -0
- package/runtime/templates/reports/task-brief.template.md +165 -0
- package/runtime/validators/lib/common.sh +44 -0
- package/runtime/validators/lib/fixtures.sh +322 -0
- package/runtime/validators/lib/paths.sh +44 -0
- package/runtime/validators/lib/runners.sh +140 -0
- package/runtime/validators/lib/summary.sh +15 -0
- package/runtime/validators/lib/validate-assets.sh +44 -0
- package/runtime/validators/lib/validate-prompt-metadata.sh +267 -0
- package/runtime/validators/lib/validate-tasks.sh +335 -0
- package/runtime/validators/validate-run.py +568 -0
- package/runtime/validators/validate-schedule.py +665 -0
- package/runtime/validators/validate-workflow.sh +190 -0
- package/src/doctor.mjs +127 -0
- package/src/install.mjs +355 -0
- package/src/paths.mjs +132 -0
- package/src/uninstall.mjs +122 -0
- package/src/version.mjs +20 -0
package/src/paths.mjs
ADDED
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import { homedir } from "node:os";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { getPackageRoot, getPackageVersion } from "./version.mjs";
|
|
4
|
+
|
|
5
|
+
const USAGE = `okstra paths — print runtime paths
|
|
6
|
+
|
|
7
|
+
Usage:
|
|
8
|
+
okstra paths Print all paths as JSON
|
|
9
|
+
okstra paths --json Same as the default
|
|
10
|
+
okstra paths --field <key> Print one field as a plain string
|
|
11
|
+
okstra paths --shell Print shell 'export' statements for the
|
|
12
|
+
standard OKSTRA_* env vars (sourced by skills)
|
|
13
|
+
|
|
14
|
+
Fields:
|
|
15
|
+
agents Absolute path to agents/. Default: <workspace>/agents.
|
|
16
|
+
workspace Directory containing prompts/, templates/, validators/,
|
|
17
|
+
agents/. Equals packages/okstra/runtime in copy mode,
|
|
18
|
+
or the dev-link repo path in dev mode.
|
|
19
|
+
pythonpath $HOME/.okstra/lib/python
|
|
20
|
+
bin $HOME/.okstra/bin
|
|
21
|
+
home $HOME/.okstra
|
|
22
|
+
version Installed okstra version (from ~/.okstra/version stamp)
|
|
23
|
+
package Currently invoked package version
|
|
24
|
+
dev-link Repo path recorded by 'okstra install --link <repo>'
|
|
25
|
+
(empty string when not in dev mode).
|
|
26
|
+
|
|
27
|
+
Shell mode exports (variable name -> field):
|
|
28
|
+
OKSTRA_WORKSPACE workspace
|
|
29
|
+
OKSTRA_AGENTS_DIR agents
|
|
30
|
+
OKSTRA_PYTHONPATH pythonpath
|
|
31
|
+
OKSTRA_BIN bin
|
|
32
|
+
OKSTRA_HOME home
|
|
33
|
+
`;
|
|
34
|
+
|
|
35
|
+
const VALID_FIELDS = new Set([
|
|
36
|
+
"agents",
|
|
37
|
+
"workspace",
|
|
38
|
+
"pythonpath",
|
|
39
|
+
"bin",
|
|
40
|
+
"home",
|
|
41
|
+
"version",
|
|
42
|
+
"package",
|
|
43
|
+
"dev-link",
|
|
44
|
+
]);
|
|
45
|
+
|
|
46
|
+
const SHELL_EXPORTS = [
|
|
47
|
+
["OKSTRA_WORKSPACE", "workspace"],
|
|
48
|
+
["OKSTRA_AGENTS_DIR", "agents"],
|
|
49
|
+
["OKSTRA_PYTHONPATH", "pythonpath"],
|
|
50
|
+
["OKSTRA_BIN", "bin"],
|
|
51
|
+
["OKSTRA_HOME", "home"],
|
|
52
|
+
];
|
|
53
|
+
|
|
54
|
+
export async function resolvePaths() {
|
|
55
|
+
const home = join(homedir(), ".okstra");
|
|
56
|
+
const pkgRoot = getPackageRoot();
|
|
57
|
+
const devLink = await readSimpleFile(join(home, "dev-link"));
|
|
58
|
+
const runtimeRoot = devLink || join(pkgRoot, "runtime");
|
|
59
|
+
return {
|
|
60
|
+
agents: join(runtimeRoot, "agents"),
|
|
61
|
+
workspace: runtimeRoot,
|
|
62
|
+
pythonpath: join(home, "lib", "python"),
|
|
63
|
+
bin: join(home, "bin"),
|
|
64
|
+
home,
|
|
65
|
+
package: await getPackageVersion(),
|
|
66
|
+
version: await readSimpleFile(join(home, "version")),
|
|
67
|
+
"dev-link": devLink || "",
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
async function readSimpleFile(path) {
|
|
72
|
+
try {
|
|
73
|
+
const { readFile } = await import("node:fs/promises");
|
|
74
|
+
return (await readFile(path, "utf8")).trim();
|
|
75
|
+
} catch {
|
|
76
|
+
return "";
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export async function run(args) {
|
|
81
|
+
if (args.includes("--help") || args.includes("-h")) {
|
|
82
|
+
process.stdout.write(USAGE);
|
|
83
|
+
return 0;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
let field = null;
|
|
87
|
+
let mode = "json";
|
|
88
|
+
for (let i = 0; i < args.length; i++) {
|
|
89
|
+
const a = args[i];
|
|
90
|
+
if (a === "--json") {
|
|
91
|
+
mode = "json";
|
|
92
|
+
} else if (a === "--shell") {
|
|
93
|
+
mode = "shell";
|
|
94
|
+
} else if (a === "--field") {
|
|
95
|
+
field = args[i + 1];
|
|
96
|
+
i++;
|
|
97
|
+
if (!field) {
|
|
98
|
+
process.stderr.write("error: --field requires a name\n");
|
|
99
|
+
return 2;
|
|
100
|
+
}
|
|
101
|
+
if (!VALID_FIELDS.has(field)) {
|
|
102
|
+
process.stderr.write(`error: unknown field '${field}'. Valid: ${[...VALID_FIELDS].join(", ")}\n`);
|
|
103
|
+
return 2;
|
|
104
|
+
}
|
|
105
|
+
mode = "field";
|
|
106
|
+
} else {
|
|
107
|
+
process.stderr.write(`error: unknown argument '${a}'\n\n${USAGE}`);
|
|
108
|
+
return 2;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const paths = await resolvePaths();
|
|
113
|
+
|
|
114
|
+
if (mode === "field") {
|
|
115
|
+
process.stdout.write(`${paths[field]}\n`);
|
|
116
|
+
return 0;
|
|
117
|
+
}
|
|
118
|
+
if (mode === "shell") {
|
|
119
|
+
for (const [varName, key] of SHELL_EXPORTS) {
|
|
120
|
+
process.stdout.write(`export ${varName}=${shellQuote(paths[key])}\n`);
|
|
121
|
+
}
|
|
122
|
+
return 0;
|
|
123
|
+
}
|
|
124
|
+
process.stdout.write(`${JSON.stringify(paths, null, 2)}\n`);
|
|
125
|
+
return 0;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function shellQuote(value) {
|
|
129
|
+
if (value === "" || value == null) return "''";
|
|
130
|
+
if (/^[A-Za-z0-9_\/.\-+:@%=]+$/.test(value)) return value;
|
|
131
|
+
return `'${String(value).replace(/'/g, "'\\''")}'`;
|
|
132
|
+
}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import { promises as fs } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { resolvePaths } from "./paths.mjs";
|
|
4
|
+
|
|
5
|
+
const BIN_ENTRYPOINTS = [
|
|
6
|
+
"okstra.sh",
|
|
7
|
+
"okstra-codex-exec.sh",
|
|
8
|
+
"okstra-gemini-exec.sh",
|
|
9
|
+
"okstra-central.sh",
|
|
10
|
+
"okstra-token-usage.py",
|
|
11
|
+
"okstra-error-log.py",
|
|
12
|
+
];
|
|
13
|
+
|
|
14
|
+
const USAGE = `okstra uninstall — remove installed runtime from ~/.okstra
|
|
15
|
+
|
|
16
|
+
Usage:
|
|
17
|
+
okstra uninstall Remove lib/, bin/{known entries}, version, dev-link
|
|
18
|
+
Preserves user data: recent.jsonl, active.jsonl,
|
|
19
|
+
projects/, archive/, state.json, .locks/
|
|
20
|
+
okstra uninstall --purge Remove the entire ~/.okstra directory (DESTRUCTIVE)
|
|
21
|
+
Requires -y or an interactive confirmation
|
|
22
|
+
okstra uninstall --dry-run Print the plan without touching disk
|
|
23
|
+
okstra uninstall -y Skip confirmation prompt for --purge
|
|
24
|
+
`;
|
|
25
|
+
|
|
26
|
+
async function pathExists(p) {
|
|
27
|
+
try {
|
|
28
|
+
await fs.lstat(p);
|
|
29
|
+
return true;
|
|
30
|
+
} catch {
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
async function removePath(p, opts) {
|
|
36
|
+
const { dryRun = false, quiet = false } = opts ?? {};
|
|
37
|
+
if (!(await pathExists(p))) {
|
|
38
|
+
if (!quiet) process.stdout.write(` not present: ${p}\n`);
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
41
|
+
if (dryRun) {
|
|
42
|
+
process.stdout.write(`[dry-run] rm ${p}\n`);
|
|
43
|
+
return true;
|
|
44
|
+
}
|
|
45
|
+
await fs.rm(p, { recursive: true, force: true });
|
|
46
|
+
if (!quiet) process.stdout.write(` removed: ${p}\n`);
|
|
47
|
+
return true;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
async function promptConfirm(question) {
|
|
51
|
+
if (!process.stdin.isTTY) return false;
|
|
52
|
+
process.stdout.write(`${question} [y/N] `);
|
|
53
|
+
return await new Promise((resolveP) => {
|
|
54
|
+
process.stdin.setEncoding("utf8");
|
|
55
|
+
process.stdin.once("data", (chunk) => {
|
|
56
|
+
const answer = chunk.toString().trim().toLowerCase();
|
|
57
|
+
resolveP(answer === "y" || answer === "yes");
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export async function runUninstall(args) {
|
|
63
|
+
if (args.includes("--help") || args.includes("-h")) {
|
|
64
|
+
process.stdout.write(USAGE);
|
|
65
|
+
return 0;
|
|
66
|
+
}
|
|
67
|
+
const opts = {
|
|
68
|
+
dryRun: args.includes("--dry-run"),
|
|
69
|
+
quiet: args.includes("-q") || args.includes("--quiet"),
|
|
70
|
+
purge: args.includes("--purge"),
|
|
71
|
+
yes: args.includes("-y") || args.includes("--yes"),
|
|
72
|
+
};
|
|
73
|
+
const paths = await resolvePaths();
|
|
74
|
+
|
|
75
|
+
if (!(await pathExists(paths.home))) {
|
|
76
|
+
if (!opts.quiet) process.stdout.write(`okstra not installed (no ${paths.home})\n`);
|
|
77
|
+
return 0;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (opts.purge) {
|
|
81
|
+
if (!opts.yes && !opts.dryRun) {
|
|
82
|
+
const ok = await promptConfirm(`purge entire ${paths.home}? user data will be lost.`);
|
|
83
|
+
if (!ok) {
|
|
84
|
+
process.stdout.write("aborted.\n");
|
|
85
|
+
return 1;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
if (!opts.quiet) process.stdout.write(`purging ${paths.home}\n`);
|
|
89
|
+
await removePath(paths.home, opts);
|
|
90
|
+
return 0;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
if (!opts.quiet) {
|
|
94
|
+
process.stdout.write(`uninstalling okstra runtime\n`);
|
|
95
|
+
process.stdout.write(` home: ${paths.home}\n`);
|
|
96
|
+
}
|
|
97
|
+
await removePath(paths.pythonpath, opts);
|
|
98
|
+
for (const name of BIN_ENTRYPOINTS) {
|
|
99
|
+
await removePath(join(paths.bin, name), opts);
|
|
100
|
+
}
|
|
101
|
+
// Clean now-empty parents (best-effort).
|
|
102
|
+
for (const dir of [join(paths.home, "lib"), paths.bin]) {
|
|
103
|
+
if (await pathExists(dir)) {
|
|
104
|
+
try {
|
|
105
|
+
const entries = await fs.readdir(dir);
|
|
106
|
+
if (entries.length === 0) {
|
|
107
|
+
if (!opts.dryRun) await fs.rmdir(dir);
|
|
108
|
+
if (!opts.quiet) process.stdout.write(` removed empty: ${dir}\n`);
|
|
109
|
+
}
|
|
110
|
+
} catch {
|
|
111
|
+
/* ignore */
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
await removePath(join(paths.home, "version"), opts);
|
|
116
|
+
await removePath(join(paths.home, "dev-link"), opts);
|
|
117
|
+
|
|
118
|
+
if (!opts.quiet) {
|
|
119
|
+
process.stdout.write("done. user data preserved (recent.jsonl, projects/, archive/, ...).\n");
|
|
120
|
+
}
|
|
121
|
+
return 0;
|
|
122
|
+
}
|
package/src/version.mjs
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
2
|
+
import { fileURLToPath } from "node:url";
|
|
3
|
+
import { dirname, join } from "node:path";
|
|
4
|
+
|
|
5
|
+
const here = dirname(fileURLToPath(import.meta.url));
|
|
6
|
+
const packageJsonPath = join(here, "..", "package.json");
|
|
7
|
+
|
|
8
|
+
let cachedVersion = null;
|
|
9
|
+
|
|
10
|
+
export async function getPackageVersion() {
|
|
11
|
+
if (cachedVersion) return cachedVersion;
|
|
12
|
+
const raw = await readFile(packageJsonPath, "utf8");
|
|
13
|
+
const { version } = JSON.parse(raw);
|
|
14
|
+
cachedVersion = version;
|
|
15
|
+
return version;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function getPackageRoot() {
|
|
19
|
+
return join(here, "..");
|
|
20
|
+
}
|