@michaelmusyoka/eng-os-kit 1.0.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 +104 -0
- package/bin/eng-os.mjs +222 -0
- package/lib/targets.mjs +49 -0
- package/package.json +14 -0
- package/rules/engineering-contract.md +51 -0
- package/scripts/capture-evidence.sh +38 -0
- package/scripts/placeholder-audit.sh +51 -0
- package/scripts/validate-registry.mjs +71 -0
- package/skills/api-database-contract/SKILL.md +38 -0
- package/skills/code-review/SKILL.md +39 -0
- package/skills/engineering-contract/SKILL.md +40 -0
- package/skills/engineering-contract/references/definition-of-done.md +47 -0
- package/skills/implementation-prompt/SKILL.md +38 -0
- package/skills/incident-response/SKILL.md +34 -0
- package/skills/release-gate/SKILL.md +30 -0
- package/skills/repo-inspection/SKILL.md +41 -0
- package/skills/security-review/SKILL.md +43 -0
- package/skills/security-review/references/prompt-injection.md +18 -0
- package/skills/signature-dark-ui/SKILL.md +72 -0
- package/skills/signature-dark-ui/references/components.md +449 -0
- package/skills/signature-dark-ui/references/layout-and-motion.md +1246 -0
- package/skills/test-strategy/SKILL.md +36 -0
- package/skills/traceability-audit/SKILL.md +46 -0
- package/skills/verification-evidence/SKILL.md +30 -0
- package/state/decision-log.md +6 -0
- package/state/feature-registry.json +18 -0
- package/state/feature-registry.schema.json +29 -0
- package/state/known-issues.md +6 -0
- package/templates/adr.md +19 -0
- package/templates/feature-record.md +40 -0
- package/templates/implementation-prompt.md +49 -0
- package/templates/incident-report.md +29 -0
- package/templates/verification-record.md +45 -0
package/README.md
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
# eng-os-kit
|
|
2
|
+
|
|
3
|
+
An engineering operating system for AI coding agents, installable with npm. Rules that stay in context, standards that load on demand as Agent Skills, and scripts that actually fail a build.
|
|
4
|
+
|
|
5
|
+
Built for **Kilo Code**, and installs just as well into Claude Code, Roo Code, Cursor, or any agent implementing the Agent Skills standard.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
Per project (recommended):
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npx eng-os-kit init
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Every project on this machine:
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
npx eng-os-kit init --global
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Other agents:
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
npx eng-os-kit init --agent claude
|
|
25
|
+
npx eng-os-kit init --agent all
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Then **reload your editor** — Kilo Code only reliably picks up new `SKILL.md` files on reload.
|
|
29
|
+
|
|
30
|
+
## What lands in your project
|
|
31
|
+
|
|
32
|
+
```text
|
|
33
|
+
.kilocode/
|
|
34
|
+
├── rules/00-engineering-contract.md # ~50 lines, always in context
|
|
35
|
+
├── skills/ # generic skills, loaded on demand
|
|
36
|
+
├── skills-architect/ # planning skills, Architect mode only
|
|
37
|
+
└── skills-code/ # code + UI skills, Code mode only
|
|
38
|
+
.agent/
|
|
39
|
+
├── state/feature-registry.json # machine-validated project state
|
|
40
|
+
├── state/known-issues.md
|
|
41
|
+
├── state/decision-log.md
|
|
42
|
+
├── templates/ # prompt, verification, ADR, incident, feature
|
|
43
|
+
├── scripts/ # the enforcement layer
|
|
44
|
+
├── prompts/ plans/ audits/ verification/ reports/
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Commands
|
|
48
|
+
|
|
49
|
+
| Command | Does |
|
|
50
|
+
|---|---|
|
|
51
|
+
| `npx eng-os-kit init` | install rules, skills and `.agent/` scaffolding |
|
|
52
|
+
| `npx eng-os-kit add security-review test-strategy` | install a subset |
|
|
53
|
+
| `npx eng-os-kit list` | every skill and its trigger description |
|
|
54
|
+
| `npx eng-os-kit check` | run the enforcement scripts (CI-safe, exits non-zero) |
|
|
55
|
+
| `npx eng-os-kit doctor` | what is installed where |
|
|
56
|
+
|
|
57
|
+
Flags: `--agent kilocode|claude|roo|cursor|codex|all`, `--global`, `--skills a,b`, `--link` (symlink so `npm update` propagates), `--force`, `--cwd <path>`.
|
|
58
|
+
|
|
59
|
+
## Skills
|
|
60
|
+
|
|
61
|
+
| Skill | Mode | Triggers on |
|
|
62
|
+
|---|---|---|
|
|
63
|
+
| `engineering-contract` | all | starting non-trivial work, "what's the process" |
|
|
64
|
+
| `repo-inspection` | architect | unfamiliar codebase, before any edit |
|
|
65
|
+
| `implementation-prompt` | architect | auth/money/migrations/integrations, >3 files |
|
|
66
|
+
| `api-database-contract` | all | any endpoint, schema, migration, idempotency |
|
|
67
|
+
| `security-review` | all | auth, permissions, uploads, payments, webhooks |
|
|
68
|
+
| `test-strategy` | all | writing tests, "is this tested", bug found |
|
|
69
|
+
| `traceability-audit` | all | "is this done", before release |
|
|
70
|
+
| `verification-evidence` | all | before claiming anything passed |
|
|
71
|
+
| `release-gate` | all | before any production deploy |
|
|
72
|
+
| `code-review` | code | reviewing a diff, after generating code |
|
|
73
|
+
| `incident-response` | all | production broken, writing a postmortem |
|
|
74
|
+
| `signature-dark-ui` | code | any UI work |
|
|
75
|
+
|
|
76
|
+
## The enforcement layer
|
|
77
|
+
|
|
78
|
+
Documentation that nothing checks is decoration. Three scripts do the checking:
|
|
79
|
+
|
|
80
|
+
- **`placeholder-audit.sh`** — fails on committed secrets, `TODO`/`FIXME`/`placeholder`/`not implemented`, mocks in production paths; warns on dead `href="#"` links and debug output. Tests, docs and `.agent/` are excluded.
|
|
81
|
+
- **`validate-registry.mjs`** — validates `feature-registry.json` and enforces the rule that matters: a feature cannot be `VERIFIED` or `PRODUCTION_READY` with an empty `evidence` array, or with evidence paths that do not exist.
|
|
82
|
+
- **`capture-evidence.sh`** — runs a command and writes command + exit code + commit SHA + output to `.agent/verification/<feature-id>/`, so evidence is a file rather than a claim.
|
|
83
|
+
|
|
84
|
+
Copy `.github/workflows/eng-os.yml` into your project and make the deploy job `needs: eng-os`. A pipeline that deploys on push without a passing gate makes every gate unreachable.
|
|
85
|
+
|
|
86
|
+
## Design
|
|
87
|
+
|
|
88
|
+
- **Always in context**: one ~50-line rules file. Precedence, six hard rules, statuses, report format.
|
|
89
|
+
- **On demand**: everything else as a skill. The agent sees only name + description (~100 tokens each) until a task matches.
|
|
90
|
+
- **Machine-checked**: the claims that agents get wrong — "tests pass", "it's complete" — are validated by scripts, not trust.
|
|
91
|
+
|
|
92
|
+
## Customising
|
|
93
|
+
|
|
94
|
+
Fork it. Edit `rules/engineering-contract.md` and the `skills/` folders, bump the version, then `npm publish` under your own scope, or install straight from git:
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
npx github:<you>/eng-os-kit init
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
Use `--link` during authoring so edits in the package show up in your project immediately.
|
|
101
|
+
|
|
102
|
+
## License
|
|
103
|
+
|
|
104
|
+
MIT.
|
package/bin/eng-os.mjs
ADDED
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import os from "node:os";
|
|
5
|
+
import { fileURLToPath } from "node:url";
|
|
6
|
+
import { spawnSync } from "node:child_process";
|
|
7
|
+
import { TARGETS, DEFAULT_TARGET, MODE_SUFFIX_SUPPORT } from "../lib/targets.mjs";
|
|
8
|
+
|
|
9
|
+
const PKG_ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
|
|
10
|
+
const pkg = JSON.parse(fs.readFileSync(path.join(PKG_ROOT, "package.json"), "utf8"));
|
|
11
|
+
|
|
12
|
+
const argv = process.argv.slice(2);
|
|
13
|
+
const cmd = argv.find((a) => !a.startsWith("-")) || "help";
|
|
14
|
+
const flag = (name) => argv.includes(`--${name}`);
|
|
15
|
+
const opt = (name, fallback) => {
|
|
16
|
+
const i = argv.findIndex((a) => a === `--${name}` || a.startsWith(`--${name}=`));
|
|
17
|
+
if (i === -1) return fallback;
|
|
18
|
+
const a = argv[i];
|
|
19
|
+
return a.includes("=") ? a.slice(a.indexOf("=") + 1) : argv[i + 1] ?? fallback;
|
|
20
|
+
};
|
|
21
|
+
const positionals = argv.filter((a) => !a.startsWith("-")).slice(1);
|
|
22
|
+
|
|
23
|
+
const c = { r: "\x1b[31m", g: "\x1b[32m", y: "\x1b[33m", d: "\x1b[2m", b: "\x1b[1m", x: "\x1b[0m" };
|
|
24
|
+
process.stdout.on("error", (e) => { if (e.code === "EPIPE") process.exit(0); });
|
|
25
|
+
const say = (m) => process.stdout.write(m + "\n");
|
|
26
|
+
const ok = (m) => say(`${c.g}✓${c.x} ${m}`);
|
|
27
|
+
const warn = (m) => say(`${c.y}!${c.x} ${m}`);
|
|
28
|
+
const die = (m) => { say(`${c.r}✗${c.x} ${m}`); process.exit(1); };
|
|
29
|
+
|
|
30
|
+
const SKILLS_DIR = path.join(PKG_ROOT, "skills");
|
|
31
|
+
const allSkills = () =>
|
|
32
|
+
fs.readdirSync(SKILLS_DIR, { withFileTypes: true })
|
|
33
|
+
.filter((d) => d.isDirectory() && fs.existsSync(path.join(SKILLS_DIR, d.name, "SKILL.md")))
|
|
34
|
+
.map((d) => d.name)
|
|
35
|
+
.sort();
|
|
36
|
+
|
|
37
|
+
function frontmatter(skill) {
|
|
38
|
+
const body = fs.readFileSync(path.join(SKILLS_DIR, skill, "SKILL.md"), "utf8");
|
|
39
|
+
const m = body.match(/^---\n([\s\S]*?)\n---/);
|
|
40
|
+
const out = {};
|
|
41
|
+
if (m) for (const line of m[1].split("\n")) {
|
|
42
|
+
const i = line.indexOf(":");
|
|
43
|
+
if (i > 0) out[line.slice(0, i).trim()] = line.slice(i + 1).trim();
|
|
44
|
+
}
|
|
45
|
+
return out;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function copyDir(from, to, { link = false, force = false }) {
|
|
49
|
+
fs.mkdirSync(path.dirname(to), { recursive: true });
|
|
50
|
+
if (fs.existsSync(to)) {
|
|
51
|
+
if (!force) return "skipped";
|
|
52
|
+
fs.rmSync(to, { recursive: true, force: true });
|
|
53
|
+
}
|
|
54
|
+
if (link) { fs.symlinkSync(from, to, "dir"); return "linked"; }
|
|
55
|
+
fs.cpSync(from, to, { recursive: true });
|
|
56
|
+
return "installed";
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function copyFile(from, to, { force = false }) {
|
|
60
|
+
fs.mkdirSync(path.dirname(to), { recursive: true });
|
|
61
|
+
if (fs.existsSync(to) && !force) return "skipped";
|
|
62
|
+
fs.copyFileSync(from, to);
|
|
63
|
+
return fs.existsSync(to) ? "installed" : "failed";
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function resolveTarget(name) {
|
|
67
|
+
const t = TARGETS[name];
|
|
68
|
+
if (!t) die(`Unknown --agent "${name}". Choose one of: ${Object.keys(TARGETS).join(", ")}`);
|
|
69
|
+
return t;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function base() {
|
|
73
|
+
return flag("global") ? os.homedir() : path.resolve(opt("cwd", process.cwd()));
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function selected() {
|
|
77
|
+
const req = opt("skills", positionals.length ? positionals.join(",") : null);
|
|
78
|
+
if (!req || req === "all") return allSkills();
|
|
79
|
+
const want = req.split(",").map((s) => s.trim()).filter(Boolean);
|
|
80
|
+
const known = allSkills();
|
|
81
|
+
const bad = want.filter((w) => !known.includes(w));
|
|
82
|
+
if (bad.length) die(`Unknown skill(s): ${bad.join(", ")}\nRun: eng-os list`);
|
|
83
|
+
return want;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function installSkills(target, agentName, root, opts) {
|
|
87
|
+
const results = [];
|
|
88
|
+
for (const skill of selected()) {
|
|
89
|
+
const fm = frontmatter(skill);
|
|
90
|
+
const mode = fm.mode && MODE_SUFFIX_SUPPORT.has(agentName) ? `-${fm.mode}` : "";
|
|
91
|
+
const dest = path.join(root, `${target.projectSkills}${mode}`, skill);
|
|
92
|
+
results.push([skill, copyDir(path.join(SKILLS_DIR, skill), dest, opts)]);
|
|
93
|
+
}
|
|
94
|
+
return results;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function cmdInit() {
|
|
98
|
+
const agentName = opt("agent", DEFAULT_TARGET);
|
|
99
|
+
const agents = agentName === "all" ? Object.keys(TARGETS) : [agentName];
|
|
100
|
+
const root = base();
|
|
101
|
+
const opts = { force: flag("force"), link: flag("link") };
|
|
102
|
+
|
|
103
|
+
say(`${c.b}eng-os-kit ${pkg.version}${c.x} → ${root}\n`);
|
|
104
|
+
|
|
105
|
+
for (const name of agents) {
|
|
106
|
+
const target = resolveTarget(name);
|
|
107
|
+
say(`${c.b}${target.label}${c.x}`);
|
|
108
|
+
for (const [skill, status] of installSkills(target, name, root, opts)) {
|
|
109
|
+
status === "skipped"
|
|
110
|
+
? warn(`${skill} ${c.d}(already present, use --force)${c.x}`)
|
|
111
|
+
: ok(`${skill} ${c.d}${status}${c.x}`);
|
|
112
|
+
}
|
|
113
|
+
const rulesDest = path.join(root, target.projectRules, target.rulesFile);
|
|
114
|
+
const st = copyFile(path.join(PKG_ROOT, "rules", "engineering-contract.md"), rulesDest, opts);
|
|
115
|
+
st === "skipped" ? warn(`${target.rulesFile} ${c.d}(already present)${c.x}`) : ok(`${path.relative(root, rulesDest)}`);
|
|
116
|
+
say("");
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
if (flag("global")) { ok("Global install done. Reload your editor to pick up new skills."); return; }
|
|
120
|
+
|
|
121
|
+
for (const dir of [".agent/state", ".agent/prompts", ".agent/plans", ".agent/audits", ".agent/verification", ".agent/reports", ".agent/templates"]) {
|
|
122
|
+
fs.mkdirSync(path.join(root, dir), { recursive: true });
|
|
123
|
+
}
|
|
124
|
+
for (const f of fs.readdirSync(path.join(PKG_ROOT, "templates"))) {
|
|
125
|
+
copyFile(path.join(PKG_ROOT, "templates", f), path.join(root, ".agent/templates", f), opts);
|
|
126
|
+
}
|
|
127
|
+
for (const f of fs.readdirSync(path.join(PKG_ROOT, "state"))) {
|
|
128
|
+
copyFile(path.join(PKG_ROOT, "state", f), path.join(root, ".agent/state", f), opts);
|
|
129
|
+
}
|
|
130
|
+
fs.mkdirSync(path.join(root, ".agent/scripts"), { recursive: true });
|
|
131
|
+
for (const f of fs.readdirSync(path.join(PKG_ROOT, "scripts"))) {
|
|
132
|
+
const to = path.join(root, ".agent/scripts", f);
|
|
133
|
+
copyFile(path.join(PKG_ROOT, "scripts", f), to, opts);
|
|
134
|
+
if (fs.existsSync(to)) fs.chmodSync(to, 0o755);
|
|
135
|
+
}
|
|
136
|
+
ok(".agent/ state, templates and scripts ready");
|
|
137
|
+
say(`\n${c.d}Next:${c.x} reload your editor, then ask the agent: "read the engineering contract and run discovery".`);
|
|
138
|
+
say(`${c.d}Enforce:${c.x} npx eng-os check`);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function cmdList() {
|
|
142
|
+
say(`${c.b}Skills in eng-os-kit ${pkg.version}${c.x}\n`);
|
|
143
|
+
for (const s of allSkills()) {
|
|
144
|
+
const fm = frontmatter(s);
|
|
145
|
+
say(`${c.b}${s}${c.x}${fm.mode ? c.d + " [mode: " + fm.mode + "]" + c.x : ""}`);
|
|
146
|
+
say(` ${c.d}${fm.description || ""}${c.x}\n`);
|
|
147
|
+
}
|
|
148
|
+
say(`${c.d}Install all: npx eng-os-kit init${c.x}`);
|
|
149
|
+
say(`${c.d}Install some: npx eng-os-kit add security-review test-strategy${c.x}`);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function cmdAdd() {
|
|
153
|
+
if (!positionals.length && !opt("skills", null)) die("Name at least one skill. Run: eng-os list");
|
|
154
|
+
cmdInit();
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
function cmdCheck() {
|
|
158
|
+
const root = base();
|
|
159
|
+
let failed = 0;
|
|
160
|
+
const run = (label, file, args = []) => {
|
|
161
|
+
const local = path.join(root, ".agent/scripts", file);
|
|
162
|
+
const script = fs.existsSync(local) ? local : path.join(PKG_ROOT, "scripts", file);
|
|
163
|
+
say(`\n${c.b}› ${label}${c.x}`);
|
|
164
|
+
const bin = file.endsWith(".mjs") ? process.execPath : "bash";
|
|
165
|
+
const r = spawnSync(bin, [script, ...args], { stdio: "inherit", cwd: root });
|
|
166
|
+
if (r.status !== 0) failed++;
|
|
167
|
+
};
|
|
168
|
+
run("Placeholder / dead-path audit", "placeholder-audit.sh");
|
|
169
|
+
run("Feature registry validation", "validate-registry.mjs");
|
|
170
|
+
if (failed) die(`${failed} check(s) failed. Nothing is PRODUCTION_READY until these pass.`);
|
|
171
|
+
say(`\n${c.g}All eng-os checks passed.${c.x}`);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
function cmdDoctor() {
|
|
175
|
+
const root = base();
|
|
176
|
+
const agentName = opt("agent", DEFAULT_TARGET);
|
|
177
|
+
const target = resolveTarget(agentName);
|
|
178
|
+
say(`${c.b}eng-os doctor${c.x} ${c.d}(${target.label} @ ${root})${c.x}\n`);
|
|
179
|
+
const check = (label, p) => fs.existsSync(path.join(root, p)) ? ok(`${label} ${c.d}${p}${c.x}`) : warn(`${label} missing ${c.d}${p}${c.x}`);
|
|
180
|
+
check("skills", target.projectSkills);
|
|
181
|
+
check("rules", path.join(target.projectRules, target.rulesFile));
|
|
182
|
+
check("state", ".agent/state/feature-registry.json");
|
|
183
|
+
check("scripts", ".agent/scripts/placeholder-audit.sh");
|
|
184
|
+
const skillsParent = path.dirname(path.join(root, target.projectSkills));
|
|
185
|
+
const leaf = path.basename(target.projectSkills);
|
|
186
|
+
const installed = fs.existsSync(skillsParent)
|
|
187
|
+
? fs.readdirSync(skillsParent, { withFileTypes: true })
|
|
188
|
+
.filter((d) => d.isDirectory() && d.name.startsWith(leaf))
|
|
189
|
+
.flatMap((d) => fs.readdirSync(path.join(skillsParent, d.name)))
|
|
190
|
+
: [];
|
|
191
|
+
const missing = allSkills().filter((s) => !installed.includes(s));
|
|
192
|
+
if (missing.length) warn(`not installed: ${missing.join(", ")}`);
|
|
193
|
+
say(`\n${c.d}Reload the editor after any skill change — Kilo Code only reliably picks up SKILL.md changes on reload.${c.x}`);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
function cmdHelp() {
|
|
197
|
+
say(`${c.b}eng-os-kit${c.x} ${pkg.version} — engineering operating system for AI coding agents
|
|
198
|
+
|
|
199
|
+
${c.b}Usage${c.x}
|
|
200
|
+
npx eng-os-kit init [options] install rules + all skills + .agent scaffolding
|
|
201
|
+
npx eng-os-kit add <skill...> install specific skills only
|
|
202
|
+
npx eng-os-kit list list bundled skills and their triggers
|
|
203
|
+
npx eng-os-kit check run the enforcement scripts (CI-safe)
|
|
204
|
+
npx eng-os-kit doctor show what is installed where
|
|
205
|
+
|
|
206
|
+
${c.b}Options${c.x}
|
|
207
|
+
--agent <name> kilocode (default) | claude | roo | cursor | codex | all
|
|
208
|
+
--global install to your home directory instead of this project
|
|
209
|
+
--skills a,b comma separated subset
|
|
210
|
+
--link symlink skills instead of copying (keeps them updatable)
|
|
211
|
+
--force overwrite existing files
|
|
212
|
+
--cwd <path> target project directory
|
|
213
|
+
|
|
214
|
+
${c.b}Examples${c.x}
|
|
215
|
+
npx eng-os-kit init # Kilo Code, this project
|
|
216
|
+
npx eng-os-kit init --agent kilocode --global # every project on this machine
|
|
217
|
+
npx eng-os-kit init --link # stay in sync with npm updates
|
|
218
|
+
npx eng-os-kit add security-review --agent roo
|
|
219
|
+
`);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
({ init: cmdInit, add: cmdAdd, list: cmdList, check: cmdCheck, doctor: cmdDoctor, help: cmdHelp }[cmd] || cmdHelp)();
|
package/lib/targets.mjs
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
// Where each agent expects rules and skills to live.
|
|
2
|
+
// Kilo Code: https://kilo.ai/docs/features/skills (.kilocode/skills, .kilocode/rules)
|
|
3
|
+
export const TARGETS = {
|
|
4
|
+
kilocode: {
|
|
5
|
+
label: "Kilo Code",
|
|
6
|
+
projectSkills: ".kilocode/skills",
|
|
7
|
+
globalSkills: ".kilocode/skills",
|
|
8
|
+
projectRules: ".kilocode/rules",
|
|
9
|
+
rulesFile: "00-engineering-contract.md",
|
|
10
|
+
globalBase: ".kilocode"
|
|
11
|
+
},
|
|
12
|
+
claude: {
|
|
13
|
+
label: "Claude Code",
|
|
14
|
+
projectSkills: ".claude/skills",
|
|
15
|
+
globalSkills: ".claude/skills",
|
|
16
|
+
projectRules: ".",
|
|
17
|
+
rulesFile: "CLAUDE.md",
|
|
18
|
+
globalBase: ".claude"
|
|
19
|
+
},
|
|
20
|
+
roo: {
|
|
21
|
+
label: "Roo Code",
|
|
22
|
+
projectSkills: ".roo/skills",
|
|
23
|
+
globalSkills: ".roo/skills",
|
|
24
|
+
projectRules: ".roo/rules",
|
|
25
|
+
rulesFile: "00-engineering-contract.md",
|
|
26
|
+
globalBase: ".roo"
|
|
27
|
+
},
|
|
28
|
+
cursor: {
|
|
29
|
+
label: "Cursor",
|
|
30
|
+
projectSkills: ".cursor/skills",
|
|
31
|
+
globalSkills: ".cursor/skills",
|
|
32
|
+
projectRules: ".",
|
|
33
|
+
rulesFile: "AGENTS.md",
|
|
34
|
+
globalBase: ".cursor"
|
|
35
|
+
},
|
|
36
|
+
codex: {
|
|
37
|
+
label: "Codex / generic Agent Skills",
|
|
38
|
+
projectSkills: ".agents/skills",
|
|
39
|
+
globalSkills: ".agents/skills",
|
|
40
|
+
projectRules: ".",
|
|
41
|
+
rulesFile: "AGENTS.md",
|
|
42
|
+
globalBase: ".agents"
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
export const DEFAULT_TARGET = "kilocode";
|
|
47
|
+
|
|
48
|
+
// Mode-scoped skills for agents that support skills-<mode> (Kilo Code, Roo).
|
|
49
|
+
export const MODE_SUFFIX_SUPPORT = new Set(["kilocode", "roo"]);
|
package/package.json
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@michaelmusyoka/eng-os-kit",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Production engineering operating system for AI coding agents: rules, Agent Skills, templates and enforcement scripts. Installs into Kilo Code, Claude Code, Roo, Cursor or any Agent Skills compatible agent.",
|
|
5
|
+
"keywords": ["kilocode", "kilo-code", "agent-skills", "skill", "claude-code", "roo-code", "ai-agent", "engineering-standards"],
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"engines": { "node": ">=18" },
|
|
9
|
+
"bin": { "eng-os": "bin/eng-os.mjs" },
|
|
10
|
+
"files": ["bin", "lib", "rules", "skills", "templates", "scripts", "state", "README.md"],
|
|
11
|
+
"scripts": {
|
|
12
|
+
"selftest": "node bin/eng-os.mjs list && node bin/eng-os.mjs --help"
|
|
13
|
+
}
|
|
14
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# Engineering Contract
|
|
2
|
+
|
|
3
|
+
Always in context. Everything else loads on demand as a skill.
|
|
4
|
+
|
|
5
|
+
## Precedence
|
|
6
|
+
1. Explicit user instruction
|
|
7
|
+
2. This contract
|
|
8
|
+
3. Approved implementation prompt in `.agent/prompts/`
|
|
9
|
+
4. Repository architecture docs and conventions
|
|
10
|
+
5. Installed framework/package documentation
|
|
11
|
+
6. General judgement
|
|
12
|
+
|
|
13
|
+
Surface material conflicts. Do not resolve them silently.
|
|
14
|
+
|
|
15
|
+
## Six rules that never bend
|
|
16
|
+
1. **Inspect before editing.** Read the real files, schema, routes and tests. Do not assume structure.
|
|
17
|
+
2. **Plan before production code.** Anything touching auth, money, migrations, external integrations, or more than three files needs an approved prompt first (skill: `implementation-prompt`). Smaller changes may proceed directly.
|
|
18
|
+
3. **Never claim a check you did not run.** No "should work". Evidence or nothing (skill: `verification-evidence`).
|
|
19
|
+
4. **Never fake production behaviour.** No mocks, stubbed success, hardcoded metrics or UI-only state in a production path.
|
|
20
|
+
5. **Never weaken a test, type check, lint rule or security control to make something pass.** Fix the cause.
|
|
21
|
+
6. **Never move a secret into client code, and never trust client-side validation for security.**
|
|
22
|
+
|
|
23
|
+
## Statuses
|
|
24
|
+
`PLANNED → IN_PROGRESS → IMPLEMENTED → VERIFIED → PRODUCTION_READY`, plus `BLOCKED`.
|
|
25
|
+
|
|
26
|
+
`IMPLEMENTED` means code exists. It never means done. Only `VERIFIED` (evidence recorded) and `PRODUCTION_READY` (release gate passed) do.
|
|
27
|
+
|
|
28
|
+
Track every feature in `.agent/state/feature-registry.json`. Conversation memory is not project state.
|
|
29
|
+
|
|
30
|
+
## Blocked
|
|
31
|
+
When a dependency, credential or decision is missing, stop and report: blocker, impact, what resolution is needed, what you could still verify. Never invent a dependency response.
|
|
32
|
+
|
|
33
|
+
## Vertical slices
|
|
34
|
+
UI → route/API → authentication → authorization → validation → service → persistence → integration → events → UI state. A UI-only or API-only implementation of a user-facing requirement is incomplete.
|
|
35
|
+
|
|
36
|
+
## Final report format
|
|
37
|
+
```
|
|
38
|
+
### What I did
|
|
39
|
+
- short bullets
|
|
40
|
+
|
|
41
|
+
### Test
|
|
42
|
+
1. exact command or manual step
|
|
43
|
+
2. actual result
|
|
44
|
+
|
|
45
|
+
### Needs your attention
|
|
46
|
+
- decision, blocker, or None
|
|
47
|
+
```
|
|
48
|
+
Keep it short. Detail belongs in `.agent/prompts/`, `.agent/verification/` and `.agent/audits/`.
|
|
49
|
+
|
|
50
|
+
## Skills available
|
|
51
|
+
`repo-inspection`, `implementation-prompt`, `api-database-contract`, `security-review`, `test-strategy`, `traceability-audit`, `verification-evidence`, `release-gate`, `code-review`, `incident-response`, `signature-dark-ui`. Load the one that fits; do not work from memory of these standards.
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Runs a command and records real output as evidence.
|
|
3
|
+
# Usage: .agent/scripts/capture-evidence.sh F-001 "npm test -- --run"
|
|
4
|
+
set -uo pipefail
|
|
5
|
+
|
|
6
|
+
FEATURE="${1:?usage: capture-evidence.sh <feature-id> \"<command>\"}"
|
|
7
|
+
CMD="${2:?usage: capture-evidence.sh <feature-id> \"<command>\"}"
|
|
8
|
+
|
|
9
|
+
DIR=".agent/verification/${FEATURE}"
|
|
10
|
+
mkdir -p "$DIR"
|
|
11
|
+
STAMP="$(date -u +%Y%m%dT%H%M%SZ)"
|
|
12
|
+
SLUG="$(printf '%s' "$CMD" | tr -cs 'a-zA-Z0-9' '-' | cut -c1-40 | sed 's/-$//')"
|
|
13
|
+
OUT="${DIR}/${STAMP}-${SLUG}.log"
|
|
14
|
+
SHA="$(git rev-parse HEAD 2>/dev/null || echo 'not-a-git-repo')"
|
|
15
|
+
|
|
16
|
+
{
|
|
17
|
+
echo "feature: $FEATURE"
|
|
18
|
+
echo "command: $CMD"
|
|
19
|
+
echo "commit: $SHA"
|
|
20
|
+
echo "timestamp: $STAMP"
|
|
21
|
+
echo "host: $(uname -sm)"
|
|
22
|
+
echo "---"
|
|
23
|
+
} > "$OUT"
|
|
24
|
+
|
|
25
|
+
set +e
|
|
26
|
+
bash -lc "$CMD" 2>&1 | tail -400 >> "$OUT"
|
|
27
|
+
CODE="${PIPESTATUS[0]}"
|
|
28
|
+
set -e
|
|
29
|
+
|
|
30
|
+
{ echo "---"; echo "exit_code: $CODE"; } >> "$OUT"
|
|
31
|
+
|
|
32
|
+
if [ "$CODE" -eq 0 ]; then
|
|
33
|
+
echo "✓ PASS exit 0 → $OUT"
|
|
34
|
+
else
|
|
35
|
+
echo "✗ FAIL exit $CODE → $OUT"
|
|
36
|
+
fi
|
|
37
|
+
echo "Add this path to the feature's \"evidence\" array in .agent/state/feature-registry.json"
|
|
38
|
+
exit "$CODE"
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Fails when unfinished production behaviour is committed.
|
|
3
|
+
# Usage: .agent/scripts/placeholder-audit.sh [path] (default: repo root)
|
|
4
|
+
set -uo pipefail
|
|
5
|
+
|
|
6
|
+
ROOT="${1:-.}"
|
|
7
|
+
STRICT_PATTERN='TODO|FIXME|XXX|HACK|placeholder|coming soon|not implemented|NotImplemented|dummy|FAKE_|hardcoded'
|
|
8
|
+
MOCK_PATTERN='mockResolvedValue|jest\.mock|MSW_ENABLED|process\.env\.MOCK|useMockData|__mocks__/'
|
|
9
|
+
DEADLINK_PATTERN='href=["'"'"']#["'"'"']|href=["'"'"']["'"'"']|to=["'"'"']#["'"'"']'
|
|
10
|
+
DEBUG_PATTERN='console\.log|debugger;|print\(.*DEBUG'
|
|
11
|
+
SECRET_PATTERN='(AKIA[0-9A-Z]{16})|(sk_live_[0-9a-zA-Z]{10,})|(-----BEGIN (RSA |EC |OPENSSH )?PRIVATE KEY-----)|(ghp_[0-9a-zA-Z]{20,})'
|
|
12
|
+
|
|
13
|
+
EXCLUDES=(--exclude-dir=node_modules --exclude-dir=.git --exclude-dir=dist --exclude-dir=build
|
|
14
|
+
--exclude-dir=.next --exclude-dir=coverage --exclude-dir=vendor --exclude-dir=.venv
|
|
15
|
+
--exclude-dir=__tests__ --exclude-dir=tests --exclude-dir=test --exclude-dir=e2e
|
|
16
|
+
--exclude-dir=.agent --exclude-dir=.kilocode --exclude-dir=.claude
|
|
17
|
+
--exclude=*.test.* --exclude=*.spec.* --exclude=*.md --exclude=*.lock --exclude=*.map
|
|
18
|
+
--exclude=placeholder-audit.sh --exclude=validate-registry.mjs --exclude=capture-evidence.sh)
|
|
19
|
+
|
|
20
|
+
fail=0
|
|
21
|
+
scan() {
|
|
22
|
+
local label="$1" pattern="$2" severity="$3"
|
|
23
|
+
local hits
|
|
24
|
+
hits="$(grep -rInE "$pattern" "$ROOT" "${EXCLUDES[@]}" 2>/dev/null || true)"
|
|
25
|
+
if [ -n "$hits" ]; then
|
|
26
|
+
local n; n="$(printf '%s\n' "$hits" | wc -l | tr -d ' ')"
|
|
27
|
+
echo "[$severity] $label — $n occurrence(s)"
|
|
28
|
+
printf '%s\n' "$hits" | head -40 | sed 's/^/ /'
|
|
29
|
+
[ "$n" -gt 40 ] && echo " ... $((n - 40)) more"
|
|
30
|
+
[ "$severity" = "FAIL" ] && fail=1
|
|
31
|
+
echo
|
|
32
|
+
else
|
|
33
|
+
echo "[ok] $label"
|
|
34
|
+
fi
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
echo "Placeholder / dead-path audit — $(date -u +%Y-%m-%dT%H:%M:%SZ)"
|
|
38
|
+
echo "Scanning: $ROOT (production paths; tests and docs excluded)"
|
|
39
|
+
echo
|
|
40
|
+
scan "committed secrets" "$SECRET_PATTERN" "FAIL"
|
|
41
|
+
scan "unfinished markers" "$STRICT_PATTERN" "FAIL"
|
|
42
|
+
scan "mocks in production paths" "$MOCK_PATTERN" "FAIL"
|
|
43
|
+
scan "dead links / empty hrefs" "$DEADLINK_PATTERN" "WARN"
|
|
44
|
+
scan "debug output" "$DEBUG_PATTERN" "WARN"
|
|
45
|
+
|
|
46
|
+
echo
|
|
47
|
+
if [ "$fail" -ne 0 ]; then
|
|
48
|
+
echo "RESULT: FAIL — resolve each FAIL occurrence, or document it in .agent/state/known-issues.md and exclude it deliberately."
|
|
49
|
+
exit 1
|
|
50
|
+
fi
|
|
51
|
+
echo "RESULT: PASS"
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Validates .agent/state/feature-registry.json: schema-ish checks plus the rules
|
|
3
|
+
// that actually catch false completion claims. No dependencies.
|
|
4
|
+
import fs from "node:fs";
|
|
5
|
+
import path from "node:path";
|
|
6
|
+
|
|
7
|
+
const root = process.argv[2] || process.cwd();
|
|
8
|
+
const file = path.join(root, ".agent/state/feature-registry.json");
|
|
9
|
+
const STATUSES = ["PLANNED", "IN_PROGRESS", "IMPLEMENTED", "VERIFIED", "PRODUCTION_READY", "BLOCKED"];
|
|
10
|
+
const NEEDS_EVIDENCE = ["VERIFIED", "PRODUCTION_READY"];
|
|
11
|
+
|
|
12
|
+
if (!fs.existsSync(file)) {
|
|
13
|
+
console.error(`✗ missing ${path.relative(root, file)} — run: npx eng-os-kit init`);
|
|
14
|
+
process.exit(1);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
let data;
|
|
18
|
+
try { data = JSON.parse(fs.readFileSync(file, "utf8")); }
|
|
19
|
+
catch (e) { console.error(`✗ invalid JSON: ${e.message}`); process.exit(1); }
|
|
20
|
+
|
|
21
|
+
const errors = [];
|
|
22
|
+
const warnings = [];
|
|
23
|
+
const features = data.features;
|
|
24
|
+
|
|
25
|
+
if (!Array.isArray(features)) {
|
|
26
|
+
console.error("✗ top-level \"features\" must be an array");
|
|
27
|
+
process.exit(1);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const seen = new Set();
|
|
31
|
+
for (const [i, f] of features.entries()) {
|
|
32
|
+
const at = f?.id || `features[${i}]`;
|
|
33
|
+
const need = (k, test, msg) => { if (!test) errors.push(`${at}: ${msg || `missing or invalid "${k}"`}`); };
|
|
34
|
+
|
|
35
|
+
need("id", typeof f.id === "string" && /^F-\d{3,}$/.test(f.id), `"id" must look like F-001`);
|
|
36
|
+
if (seen.has(f.id)) errors.push(`${at}: duplicate id`);
|
|
37
|
+
seen.add(f.id);
|
|
38
|
+
need("title", typeof f.title === "string" && f.title.length > 2);
|
|
39
|
+
need("criticality", ["critical", "standard"].includes(f.criticality), `"criticality" must be "critical" or "standard"`);
|
|
40
|
+
need("status", STATUSES.includes(f.status), `"status" must be one of ${STATUSES.join(", ")}`);
|
|
41
|
+
need("requirements", Array.isArray(f.requirements) && f.requirements.length > 0, `needs at least one requirement id`);
|
|
42
|
+
need("acceptanceCriteria", Array.isArray(f.acceptanceCriteria) && f.acceptanceCriteria.length > 0, `needs at least one acceptance criterion`);
|
|
43
|
+
need("tests", Array.isArray(f.tests));
|
|
44
|
+
need("evidence", Array.isArray(f.evidence));
|
|
45
|
+
|
|
46
|
+
if (NEEDS_EVIDENCE.includes(f.status)) {
|
|
47
|
+
if (!f.evidence?.length) errors.push(`${at}: status ${f.status} with no evidence — evidence is required, not optional`);
|
|
48
|
+
if (!f.tests?.length) errors.push(`${at}: status ${f.status} with no tests listed`);
|
|
49
|
+
for (const e of f.evidence || []) {
|
|
50
|
+
const p = path.join(root, e);
|
|
51
|
+
if (!e.startsWith("http") && !fs.existsSync(p)) errors.push(`${at}: evidence path does not exist: ${e}`);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
if (f.status === "PRODUCTION_READY" && f.criticality === "critical" && !(f.security || []).length) {
|
|
55
|
+
errors.push(`${at}: critical feature marked PRODUCTION_READY with no security checks recorded`);
|
|
56
|
+
}
|
|
57
|
+
if (f.status === "BLOCKED" && !f.notes) warnings.push(`${at}: BLOCKED without notes explaining the blocker`);
|
|
58
|
+
if (f.criticality === "critical" && f.status === "IMPLEMENTED") warnings.push(`${at}: critical feature IMPLEMENTED but not VERIFIED`);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const count = (s) => features.filter((f) => f.status === s).length;
|
|
62
|
+
console.log(`Feature registry: ${features.length} feature(s)`);
|
|
63
|
+
console.log(STATUSES.map((s) => ` ${s}: ${count(s)}`).join("\n"));
|
|
64
|
+
if (warnings.length) { console.log("\nWarnings:"); warnings.forEach((w) => console.log(` ! ${w}`)); }
|
|
65
|
+
if (errors.length) {
|
|
66
|
+
console.error("\nErrors:");
|
|
67
|
+
errors.forEach((e) => console.error(` ✗ ${e}`));
|
|
68
|
+
console.error(`\nRESULT: FAIL (${errors.length} error(s))`);
|
|
69
|
+
process.exit(1);
|
|
70
|
+
}
|
|
71
|
+
console.log("\nRESULT: PASS");
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: api-database-contract
|
|
3
|
+
description: Standards for designing endpoints, error models, database entities, constraints, indexes, idempotency and migrations. Use this whenever adding or changing an API route, server action, database table, column, or migration, when defining error responses, and when a state-changing operation could be retried. Load before writing any schema or endpoint.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# API and Database Contracts
|
|
7
|
+
|
|
8
|
+
## Every endpoint defines
|
|
9
|
+
method, path, purpose, authentication, permission, request schema, validation, response schema, error cases, rate limit, timeout, idempotency behaviour, pagination/filtering/sorting, audit behaviour.
|
|
10
|
+
|
|
11
|
+
## Error model
|
|
12
|
+
| Code | Meaning |
|
|
13
|
+
|---|---|
|
|
14
|
+
| 400 | malformed request |
|
|
15
|
+
| 401 | unauthenticated |
|
|
16
|
+
| 403 | authenticated but not permitted |
|
|
17
|
+
| 404 | missing, or hidden for authorization reasons |
|
|
18
|
+
| 409 | conflict / concurrent update |
|
|
19
|
+
| 422 | semantically invalid |
|
|
20
|
+
| 429 | rate limited |
|
|
21
|
+
| 500 | internal failure |
|
|
22
|
+
| 503 | dependency unavailable |
|
|
23
|
+
|
|
24
|
+
Never leak stack traces, SQL, secrets, internal paths or provider payloads. Return a stable error `code` string alongside the message so clients can branch without string matching.
|
|
25
|
+
|
|
26
|
+
## Every entity defines
|
|
27
|
+
ownership, primary key, relationships, nullability, unique constraints, indexes supporting its real queries, audit fields, retention, deletion behaviour (hard vs soft, and what cascades).
|
|
28
|
+
|
|
29
|
+
Use database constraints for critical integrity rules. Application-level validation must not be the only protection — a unique index is the only thing that actually prevents a duplicate under concurrency.
|
|
30
|
+
|
|
31
|
+
## Idempotency
|
|
32
|
+
Any state-changing operation that an external system, a retry, or an impatient user can repeat needs a duplicate-safety story. Options: idempotency key with stored response, natural unique constraint, or conditional update on a version column. Choose one explicitly and test:
|
|
33
|
+
- same request + same key → one logical operation, same response
|
|
34
|
+
- different key → separate operation where permitted
|
|
35
|
+
- concurrent identical requests → one winner, no partial state
|
|
36
|
+
|
|
37
|
+
## Migrations
|
|
38
|
+
Versioned, reproducible from empty, tested, reviewed, safe for existing data. Before a production migration assess: lock duration, downtime, backfill volume, rollback path, and whether old and new application versions can both run against the intermediate schema. Assume no migration is safely reversible until you have reversed it.
|