@buba_71/levit 0.3.4 → 0.4.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 CHANGED
@@ -34,7 +34,8 @@ Its role is simple:
34
34
  ## What levit-kit does
35
35
 
36
36
  - Initializes a standardized project structure
37
- - Installs explicit conventions
37
+ - Installs explicit conventions for **AI-Driven Development (AIDD)**
38
+ - Provides a protocol for Human-AI collaboration (Intents, Decisions, Evals)
38
39
  - Facilitates human and agent onboarding
39
40
  - Reduces unnecessary variability between projects
40
41
 
@@ -78,6 +79,18 @@ Levit-kit does not remain in the project after initialization and installs no de
78
79
 
79
80
  ---
80
81
 
82
+ ## The AIDD Workflow (Human + Agent)
83
+
84
+ Levit-kit installs a cognitive pipeline in your project:
85
+
86
+ 1. **Human Intent**: You define *what* you want in `features/INTENT.md`.
87
+ 2. **Agent Onboarding**: Your AI reads `.levit/AGENT_ONBOARDING.md` to learn your rules.
88
+ 3. **Collaborative Decision**: The agent proposes technical choices in `.levit/decision-record.md`.
89
+ 4. **Verification**: You or the agent run quality tests in `evals/`.
90
+ 5. **Review**: The agent submits its work following a strict protocol.
91
+
92
+ ---
93
+
81
94
  ## Where does the levit command come from?
82
95
 
83
96
  The `levit` command is provided through the npm ecosystem.
package/dist/bin/cli.js CHANGED
@@ -5,6 +5,9 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  };
6
6
  Object.defineProperty(exports, "__esModule", { value: true });
7
7
  const init_1 = require("../src/commands/init");
8
+ const decision_1 = require("../src/commands/decision");
9
+ const feature_1 = require("../src/commands/feature");
10
+ const handoff_1 = require("../src/commands/handoff");
8
11
  const version_1 = require("../src/core/version");
9
12
  const node_path_1 = __importDefault(require("node:path"));
10
13
  function showHelp() {
@@ -13,13 +16,16 @@ Usage: levit [command] [options]
13
16
 
14
17
  Commands:
15
18
  init <project-name> Initialize a new levit project
19
+ feature new Create a new feature intent file (wizard)
20
+ decision new Create a new decision record (wizard)
21
+ handoff new Create an agent handoff brief (wizard)
16
22
 
17
23
  Options:
18
24
  -v, --version Show version number
19
25
  -h, --help Show help
20
26
  `);
21
27
  }
22
- function main() {
28
+ async function main() {
23
29
  const args = process.argv.slice(2);
24
30
  if (args.includes("-h") || args.includes("--help") || args.length === 0) {
25
31
  showHelp();
@@ -50,10 +56,37 @@ function main() {
50
56
  process.exit(1);
51
57
  }
52
58
  }
59
+ else if (args[0] === "feature") {
60
+ try {
61
+ await (0, feature_1.featureCommand)(args.slice(1), process.cwd());
62
+ }
63
+ catch (error) {
64
+ console.error(error instanceof Error ? `Error: ${error.message}` : "Unexpected error");
65
+ process.exit(1);
66
+ }
67
+ }
68
+ else if (args[0] === "decision") {
69
+ try {
70
+ await (0, decision_1.decisionCommand)(args.slice(1), process.cwd());
71
+ }
72
+ catch (error) {
73
+ console.error(error instanceof Error ? `Error: ${error.message}` : "Unexpected error");
74
+ process.exit(1);
75
+ }
76
+ }
77
+ else if (args[0] === "handoff") {
78
+ try {
79
+ await (0, handoff_1.handoffCommand)(args.slice(1), process.cwd());
80
+ }
81
+ catch (error) {
82
+ console.error(error instanceof Error ? `Error: ${error.message}` : "Unexpected error");
83
+ process.exit(1);
84
+ }
85
+ }
53
86
  else {
54
87
  console.error(`Error: Unknown command "${args[0]}"`);
55
88
  showHelp();
56
89
  process.exit(1);
57
90
  }
58
91
  }
59
- main();
92
+ void main();
@@ -0,0 +1,59 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.decisionCommand = decisionCommand;
7
+ const node_path_1 = __importDefault(require("node:path"));
8
+ const promises_1 = __importDefault(require("node:readline/promises"));
9
+ const levit_project_1 = require("../core/levit_project");
10
+ const cli_args_1 = require("../core/cli_args");
11
+ const write_file_1 = require("../core/write_file");
12
+ const ids_1 = require("../core/ids");
13
+ function normalizeSlug(input) {
14
+ return input
15
+ .trim()
16
+ .toLowerCase()
17
+ .replace(/[^a-z0-9\s-_]/g, "")
18
+ .replace(/\s+/g, "-");
19
+ }
20
+ async function decisionCommand(argv, cwd) {
21
+ const { positional, flags } = (0, cli_args_1.parseArgs)(argv);
22
+ const sub = positional[0];
23
+ if (sub !== "new") {
24
+ throw new Error('Usage: levit decision new [--title "..."] [--id "001"]');
25
+ }
26
+ const projectRoot = (0, levit_project_1.requireLevitProjectRoot)(cwd);
27
+ const yes = (0, cli_args_1.getBooleanFlag)(flags, "yes");
28
+ const overwrite = (0, cli_args_1.getBooleanFlag)(flags, "force");
29
+ let title = (0, cli_args_1.getStringFlag)(flags, "title");
30
+ let id = (0, cli_args_1.getStringFlag)(flags, "id");
31
+ const featureRef = (0, cli_args_1.getStringFlag)(flags, "feature");
32
+ const computedId = (0, ids_1.nextSequentialId)(node_path_1.default.join(projectRoot, ".levit", "decisions"), /^ADR-(\d+)-/);
33
+ if (!yes) {
34
+ const rl = promises_1.default.createInterface({ input: process.stdin, output: process.stdout });
35
+ if (!title) {
36
+ title = (await rl.question("Decision title: ")).trim();
37
+ }
38
+ if (!id) {
39
+ id = (await rl.question(`Decision id [${computedId}]: `)).trim() || computedId;
40
+ }
41
+ await rl.close();
42
+ }
43
+ if (!id) {
44
+ id = computedId;
45
+ }
46
+ if (!title) {
47
+ throw new Error("Missing --title");
48
+ }
49
+ if (!id) {
50
+ throw new Error("Missing --id");
51
+ }
52
+ const slug = normalizeSlug(title);
53
+ const fileName = `ADR-${id}-${slug}.md`;
54
+ const decisionPath = node_path_1.default.join(projectRoot, ".levit", "decisions", fileName);
55
+ const featureLine = featureRef ? `- **Feature**: ${featureRef}\n` : "";
56
+ const content = `# ADR ${id}: ${title}\n\n- **Date**: [YYYY-MM-DD]\n- **Status**: [Draft / Proposed / Approved]\n${featureLine}\n## Context\n[fill]\n\n## Decision\n[fill]\n\n## Rationale\n[fill]\n\n## Alternatives Considered\n[fill]\n\n## Consequences\n[fill]\n`;
57
+ (0, write_file_1.writeTextFile)(decisionPath, content, { overwrite });
58
+ process.stdout.write(`Created ${node_path_1.default.relative(projectRoot, decisionPath)}\n`);
59
+ }
@@ -0,0 +1,61 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.featureCommand = featureCommand;
7
+ const node_path_1 = __importDefault(require("node:path"));
8
+ const promises_1 = __importDefault(require("node:readline/promises"));
9
+ const levit_project_1 = require("../core/levit_project");
10
+ const cli_args_1 = require("../core/cli_args");
11
+ const write_file_1 = require("../core/write_file");
12
+ const ids_1 = require("../core/ids");
13
+ function normalizeSlug(input) {
14
+ return input
15
+ .trim()
16
+ .toLowerCase()
17
+ .replace(/[^a-z0-9\s-_]/g, "")
18
+ .replace(/\s+/g, "-");
19
+ }
20
+ async function featureCommand(argv, cwd) {
21
+ const { positional, flags } = (0, cli_args_1.parseArgs)(argv);
22
+ const sub = positional[0];
23
+ if (sub !== "new") {
24
+ throw new Error('Usage: levit feature new [--title "..."] [--slug "..."] [--id "001"]');
25
+ }
26
+ const projectRoot = (0, levit_project_1.requireLevitProjectRoot)(cwd);
27
+ const yes = (0, cli_args_1.getBooleanFlag)(flags, "yes");
28
+ const overwrite = (0, cli_args_1.getBooleanFlag)(flags, "force");
29
+ let title = (0, cli_args_1.getStringFlag)(flags, "title");
30
+ let slug = (0, cli_args_1.getStringFlag)(flags, "slug");
31
+ let id = (0, cli_args_1.getStringFlag)(flags, "id");
32
+ const computedId = (0, ids_1.nextSequentialId)(node_path_1.default.join(projectRoot, "features"), /^(\d+)-/);
33
+ if (!yes) {
34
+ const rl = promises_1.default.createInterface({ input: process.stdin, output: process.stdout });
35
+ if (!title) {
36
+ title = (await rl.question("Feature title: ")).trim();
37
+ }
38
+ if (!slug) {
39
+ const computed = normalizeSlug(title || "");
40
+ slug = (await rl.question(`Feature slug [${computed}]: `)).trim() || computed;
41
+ }
42
+ if (!id) {
43
+ id = (await rl.question(`Feature id [${computedId}]: `)).trim() || computedId;
44
+ }
45
+ await rl.close();
46
+ }
47
+ if (!id) {
48
+ id = computedId;
49
+ }
50
+ if (!title) {
51
+ throw new Error("Missing --title");
52
+ }
53
+ if (!slug) {
54
+ throw new Error("Missing --slug");
55
+ }
56
+ const fileName = `${id}-${slug}.md`;
57
+ const featurePath = node_path_1.default.join(projectRoot, "features", fileName);
58
+ const content = `# INTENT: ${title}\n\n## 1. Vision (The \"Why\")\n- **User Story**: [fill]\n- **Priority**: [Low / Medium / High / Critical]\n\n## 2. Success Criteria (The \"What\")\n- [ ] Criterion 1\n\n## 3. Boundaries (The \"No\")\n- Non-goal 1\n\n## 4. Technical Constraints\n- [fill]\n\n## 5. Agent Task\n- [fill]\n`;
59
+ (0, write_file_1.writeTextFile)(featurePath, content, { overwrite });
60
+ process.stdout.write(`Created ${node_path_1.default.relative(projectRoot, featurePath)}\n`);
61
+ }
@@ -0,0 +1,49 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.handoffCommand = handoffCommand;
7
+ const node_path_1 = __importDefault(require("node:path"));
8
+ const promises_1 = __importDefault(require("node:readline/promises"));
9
+ const levit_project_1 = require("../core/levit_project");
10
+ const cli_args_1 = require("../core/cli_args");
11
+ const write_file_1 = require("../core/write_file");
12
+ function isoDate() {
13
+ return new Date().toISOString().slice(0, 10);
14
+ }
15
+ async function handoffCommand(argv, cwd) {
16
+ const { positional, flags } = (0, cli_args_1.parseArgs)(argv);
17
+ const sub = positional[0];
18
+ if (sub !== "new") {
19
+ throw new Error("Usage: levit handoff new --feature <features/001-...md> [--role developer|qa|security|devops]");
20
+ }
21
+ const projectRoot = (0, levit_project_1.requireLevitProjectRoot)(cwd);
22
+ const yes = (0, cli_args_1.getBooleanFlag)(flags, "yes");
23
+ const overwrite = (0, cli_args_1.getBooleanFlag)(flags, "force");
24
+ let feature = (0, cli_args_1.getStringFlag)(flags, "feature");
25
+ let role = (0, cli_args_1.getStringFlag)(flags, "role");
26
+ if (!yes) {
27
+ const rl = promises_1.default.createInterface({ input: process.stdin, output: process.stdout });
28
+ if (!feature) {
29
+ feature = (await rl.question("Feature path (e.g. features/001-...md): ")).trim();
30
+ }
31
+ if (!role) {
32
+ role = (await rl.question("Role [developer]: ")).trim() || "developer";
33
+ }
34
+ await rl.close();
35
+ }
36
+ if (!feature) {
37
+ throw new Error("Missing --feature");
38
+ }
39
+ if (!role) {
40
+ role = "developer";
41
+ }
42
+ const safeRole = role.trim().toLowerCase();
43
+ const date = isoDate();
44
+ const fileName = `${date}-${node_path_1.default.basename(feature, node_path_1.default.extname(feature))}-${safeRole}.md`;
45
+ const handoffPath = node_path_1.default.join(projectRoot, ".levit", "handoff", fileName);
46
+ const content = `# Agent Handoff\n\n- **Date**: ${date}\n- **Role**: ${safeRole}\n- **Feature**: ${feature}\n\n## What to read first\n- SOCIAL_CONTRACT.md\n- .levit/AGENT_ONBOARDING.md\n- ${feature}\n\n## Boundaries\nFollow the Boundaries section of the feature spec strictly.\n\n## Deliverables\n- A minimal, atomic diff\n- A short summary: what changed + why\n- How to verify (commands to run)\n- Open questions / risks\n\n## Review protocol\nFollow: .levit/workflows/submit-for-review.md\n`;
47
+ (0, write_file_1.writeTextFile)(handoffPath, content, { overwrite });
48
+ process.stdout.write(`Created ${node_path_1.default.relative(projectRoot, handoffPath)}\n`);
49
+ }
@@ -0,0 +1,11 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.handoffCommand = exports.decisionCommand = exports.featureCommand = exports.initProject = void 0;
4
+ var init_1 = require("./init");
5
+ Object.defineProperty(exports, "initProject", { enumerable: true, get: function () { return init_1.initProject; } });
6
+ var feature_1 = require("./feature");
7
+ Object.defineProperty(exports, "featureCommand", { enumerable: true, get: function () { return feature_1.featureCommand; } });
8
+ var decision_1 = require("./decision");
9
+ Object.defineProperty(exports, "decisionCommand", { enumerable: true, get: function () { return decision_1.decisionCommand; } });
10
+ var handoff_1 = require("./handoff");
11
+ Object.defineProperty(exports, "handoffCommand", { enumerable: true, get: function () { return handoff_1.handoffCommand; } });
@@ -42,8 +42,8 @@ function initProject(projectName, targetPath) {
42
42
  console.log("");
43
43
  console.log("Next steps:");
44
44
  console.log(` 1. cd ${projectName}`);
45
- console.log(" 2. Review SOCIAL_CONTRACT.md to understand the foundations");
46
- console.log(" 3. Define your first feature in features/README.md");
45
+ console.log(" 2. Read README.md for the Human Operator Guide");
46
+ console.log(" 3. Onboard your AI in .levit/AGENT_ONBOARDING.md");
47
47
  console.log("");
48
48
  }
49
49
  catch (error) {
@@ -0,0 +1,35 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.parseArgs = parseArgs;
4
+ exports.getStringFlag = getStringFlag;
5
+ exports.getBooleanFlag = getBooleanFlag;
6
+ function parseArgs(args) {
7
+ const positional = [];
8
+ const flags = {};
9
+ for (let i = 0; i < args.length; i += 1) {
10
+ const token = args[i];
11
+ if (!token.startsWith("--")) {
12
+ positional.push(token);
13
+ continue;
14
+ }
15
+ const key = token.slice(2);
16
+ const next = args[i + 1];
17
+ if (!next || next.startsWith("--")) {
18
+ flags[key] = true;
19
+ continue;
20
+ }
21
+ flags[key] = next;
22
+ i += 1;
23
+ }
24
+ return { positional, flags };
25
+ }
26
+ function getStringFlag(flags, key) {
27
+ const v = flags[key];
28
+ if (typeof v === "string") {
29
+ return v;
30
+ }
31
+ return undefined;
32
+ }
33
+ function getBooleanFlag(flags, key) {
34
+ return flags[key] === true;
35
+ }
@@ -0,0 +1,34 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.nextSequentialId = nextSequentialId;
7
+ const fs_extra_1 = __importDefault(require("fs-extra"));
8
+ /**
9
+ * Finds the next sequential 3-digit ID in a directory based on a filename pattern.
10
+ *
11
+ * @param directory The directory to scan
12
+ * @param pattern A regex with a single capture group for the ID number
13
+ * @returns The next ID as a zero-padded string (e.g., "001")
14
+ */
15
+ function nextSequentialId(directory, pattern) {
16
+ if (!fs_extra_1.default.existsSync(directory)) {
17
+ return "001";
18
+ }
19
+ const files = fs_extra_1.default.readdirSync(directory);
20
+ let max = 0;
21
+ for (const f of files) {
22
+ const m = f.match(pattern);
23
+ if (!m) {
24
+ continue;
25
+ }
26
+ const n = Number.parseInt(m[1], 10);
27
+ if (!Number.isFinite(n)) {
28
+ continue;
29
+ }
30
+ max = Math.max(max, n);
31
+ }
32
+ const next = max + 1;
33
+ return String(next).padStart(3, "0");
34
+ }
@@ -0,0 +1,30 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.findLevitProjectRoot = findLevitProjectRoot;
7
+ exports.requireLevitProjectRoot = requireLevitProjectRoot;
8
+ const fs_extra_1 = __importDefault(require("fs-extra"));
9
+ const node_path_1 = __importDefault(require("node:path"));
10
+ function findLevitProjectRoot(startDir) {
11
+ let current = node_path_1.default.resolve(startDir);
12
+ while (true) {
13
+ const marker = node_path_1.default.join(current, ".levit", "AGENT_ONBOARDING.md");
14
+ if (fs_extra_1.default.existsSync(marker)) {
15
+ return current;
16
+ }
17
+ const parent = node_path_1.default.dirname(current);
18
+ if (parent === current) {
19
+ return null;
20
+ }
21
+ current = parent;
22
+ }
23
+ }
24
+ function requireLevitProjectRoot(startDir) {
25
+ const root = findLevitProjectRoot(startDir);
26
+ if (!root) {
27
+ throw new Error("Not a levit project (missing .levit/AGENT_ONBOARDING.md). Run this command from a levit-initialized repository.");
28
+ }
29
+ return root;
30
+ }
@@ -0,0 +1,15 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.writeTextFile = writeTextFile;
7
+ const fs_extra_1 = __importDefault(require("fs-extra"));
8
+ const node_path_1 = __importDefault(require("node:path"));
9
+ function writeTextFile(targetPath, content, options) {
10
+ fs_extra_1.default.ensureDirSync(node_path_1.default.dirname(targetPath));
11
+ if (fs_extra_1.default.existsSync(targetPath) && !options.overwrite) {
12
+ throw new Error(`File already exists: ${targetPath}`);
13
+ }
14
+ fs_extra_1.default.writeFileSync(targetPath, content, "utf8");
15
+ }
@@ -13,6 +13,9 @@ const init_1 = require("../src/commands/init");
13
13
  function exists(p) {
14
14
  return node_fs_1.default.existsSync(p);
15
15
  }
16
+ function getCliPath() {
17
+ return node_path_1.default.join(process.cwd(), "dist", "bin", "cli.js");
18
+ }
16
19
  (0, node_test_1.default)("levit init copies default template exactly", () => {
17
20
  const tempDir = node_fs_1.default.mkdtempSync(node_path_1.default.join(node_os_1.default.tmpdir(), "levit-kit-test-"));
18
21
  const projectName = "test-project";
@@ -28,13 +31,24 @@ function exists(p) {
28
31
  node_assert_1.default.ok(exists(node_path_1.default.join(projectPath, "pipelines")));
29
32
  node_assert_1.default.ok(exists(node_path_1.default.join(projectPath, "docs")));
30
33
  node_assert_1.default.ok(exists(node_path_1.default.join(projectPath, "roles", "README.md")), "Roles README should exist");
31
- // New files assertions
34
+ // AIDD assertions
35
+ node_assert_1.default.ok(exists(node_path_1.default.join(projectPath, ".levit")), ".levit directory should exist");
36
+ node_assert_1.default.ok(exists(node_path_1.default.join(projectPath, ".levit", "AGENT_ONBOARDING.md")), "AGENT_ONBOARDING.md should exist");
37
+ node_assert_1.default.ok(exists(node_path_1.default.join(projectPath, ".levit", "decision-record.md")), "decision-record.md should exist");
38
+ node_assert_1.default.ok(exists(node_path_1.default.join(projectPath, ".levit", "workflows", "example-task.md")), "Example workflow should exist");
39
+ node_assert_1.default.ok(exists(node_path_1.default.join(projectPath, ".levit", "workflows", "submit-for-review.md")), "Submit for review workflow should exist");
40
+ node_assert_1.default.ok(exists(node_path_1.default.join(projectPath, ".levit", "prompts")), "Prompts directory should exist");
41
+ node_assert_1.default.ok(exists(node_path_1.default.join(projectPath, ".levit", "prompts", "global-rules.md")), "Global rules should exist");
42
+ // New folders assertions
43
+ node_assert_1.default.ok(exists(node_path_1.default.join(projectPath, "evals")), "Evals directory should exist");
44
+ node_assert_1.default.ok(exists(node_path_1.default.join(projectPath, "evals", "README.md")), "Evals README should exist");
32
45
  node_assert_1.default.ok(exists(node_path_1.default.join(projectPath, ".gitignore")), ".gitignore should exist");
33
46
  node_assert_1.default.ok(exists(node_path_1.default.join(projectPath, "package.json")), "package.json should exist");
34
47
  // Agent boundaries
35
48
  node_assert_1.default.ok(exists(node_path_1.default.join(projectPath, "agents", "AGENTS.md")), "Agent guidelines should exist");
36
49
  // Feature contract
37
50
  node_assert_1.default.ok(exists(node_path_1.default.join(projectPath, "features", "README.md")), "Feature README should exist");
51
+ node_assert_1.default.ok(exists(node_path_1.default.join(projectPath, "features", "INTENT.md")), "Feature INTENT template should exist");
38
52
  // Clean up
39
53
  node_fs_1.default.rmSync(tempDir, { recursive: true, force: true });
40
54
  });
@@ -43,6 +57,59 @@ function exists(p) {
43
57
  node_assert_1.default.ok(output.includes("Usage: levit [command] [options]"));
44
58
  node_assert_1.default.ok(output.includes("init <project-name>"));
45
59
  });
60
+ (0, node_test_1.default)("CLI feature new creates a feature intent file", () => {
61
+ const tempDir = node_fs_1.default.mkdtempSync(node_path_1.default.join(node_os_1.default.tmpdir(), "levit-kit-test-"));
62
+ const projectName = "test-project";
63
+ const projectPath = node_path_1.default.join(tempDir, projectName);
64
+ (0, init_1.initProject)(projectName, projectPath);
65
+ const cliPath = getCliPath();
66
+ (0, node_child_process_1.execSync)(`node ${cliPath} feature new --yes --id 001 --title "My Feature" --slug my-feature`, { cwd: projectPath });
67
+ node_assert_1.default.ok(exists(node_path_1.default.join(projectPath, "features", "001-my-feature.md")));
68
+ node_fs_1.default.rmSync(tempDir, { recursive: true, force: true });
69
+ });
70
+ (0, node_test_1.default)("CLI feature new auto-assigns id when omitted", () => {
71
+ const tempDir = node_fs_1.default.mkdtempSync(node_path_1.default.join(node_os_1.default.tmpdir(), "levit-kit-test-"));
72
+ const projectName = "test-project";
73
+ const projectPath = node_path_1.default.join(tempDir, projectName);
74
+ (0, init_1.initProject)(projectName, projectPath);
75
+ const cliPath = getCliPath();
76
+ (0, node_child_process_1.execSync)(`node ${cliPath} feature new --yes --title "My Feature" --slug my-feature`, { cwd: projectPath });
77
+ node_assert_1.default.ok(exists(node_path_1.default.join(projectPath, "features", "001-my-feature.md")));
78
+ node_fs_1.default.rmSync(tempDir, { recursive: true, force: true });
79
+ });
80
+ (0, node_test_1.default)("CLI decision new creates a decision record", () => {
81
+ const tempDir = node_fs_1.default.mkdtempSync(node_path_1.default.join(node_os_1.default.tmpdir(), "levit-kit-test-"));
82
+ const projectName = "test-project";
83
+ const projectPath = node_path_1.default.join(tempDir, projectName);
84
+ (0, init_1.initProject)(projectName, projectPath);
85
+ const cliPath = getCliPath();
86
+ (0, node_child_process_1.execSync)(`node ${cliPath} decision new --yes --id 001 --title "Choose DB" --feature features/001-some-feature.md`, { cwd: projectPath });
87
+ node_assert_1.default.ok(exists(node_path_1.default.join(projectPath, ".levit", "decisions")));
88
+ node_assert_1.default.ok(exists(node_path_1.default.join(projectPath, ".levit", "decisions", "ADR-001-choose-db.md")));
89
+ node_fs_1.default.rmSync(tempDir, { recursive: true, force: true });
90
+ });
91
+ (0, node_test_1.default)("CLI decision new auto-assigns id when omitted", () => {
92
+ const tempDir = node_fs_1.default.mkdtempSync(node_path_1.default.join(node_os_1.default.tmpdir(), "levit-kit-test-"));
93
+ const projectName = "test-project";
94
+ const projectPath = node_path_1.default.join(tempDir, projectName);
95
+ (0, init_1.initProject)(projectName, projectPath);
96
+ const cliPath = getCliPath();
97
+ (0, node_child_process_1.execSync)(`node ${cliPath} decision new --yes --title "Auto ID Decision"`, { cwd: projectPath });
98
+ node_assert_1.default.ok(exists(node_path_1.default.join(projectPath, ".levit", "decisions", "ADR-001-auto-id-decision.md")));
99
+ node_fs_1.default.rmSync(tempDir, { recursive: true, force: true });
100
+ });
101
+ (0, node_test_1.default)("CLI handoff new creates an agent handoff brief", () => {
102
+ const tempDir = node_fs_1.default.mkdtempSync(node_path_1.default.join(node_os_1.default.tmpdir(), "levit-kit-test-"));
103
+ const projectName = "test-project";
104
+ const projectPath = node_path_1.default.join(tempDir, projectName);
105
+ (0, init_1.initProject)(projectName, projectPath);
106
+ const cliPath = getCliPath();
107
+ (0, node_child_process_1.execSync)(`node ${cliPath} handoff new --yes --feature features/001-some-feature.md --role security`, { cwd: projectPath });
108
+ const handoffDir = node_path_1.default.join(projectPath, ".levit", "handoff");
109
+ node_assert_1.default.ok(exists(handoffDir));
110
+ node_assert_1.default.ok(node_fs_1.default.readdirSync(handoffDir).some((f) => f.endsWith("-security.md")));
111
+ node_fs_1.default.rmSync(tempDir, { recursive: true, force: true });
112
+ });
46
113
  (0, node_test_1.default)("CLI --version works", () => {
47
114
  const output = (0, node_child_process_1.execSync)("node dist/bin/cli.js --version").toString();
48
115
  node_assert_1.default.ok(output.startsWith("levit-kit v"));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@buba_71/levit",
3
- "version": "0.3.4",
3
+ "version": "0.4.0",
4
4
  "description": "Hybrid starter kit for Antigravity projects",
5
5
  "author": "David BUBA",
6
6
  "license": "MIT",
@@ -0,0 +1,28 @@
1
+ # AGENT_ONBOARDING.md
2
+
3
+ > [!IMPORTANT]
4
+ > **To the AI Agent reading this**: You are entering a project governed by the **Levit-Kit** protocol. This document is your operating manual for this specific environment.
5
+
6
+ ## 1. Project Essence
7
+ This project is a hybrid workspace where humans define intentions and AI ensures execution.
8
+ - **Human Role**: Vision, decision-making, final validation.
9
+ - **AI Role (You)**: Implementation, proactive suggestions, technical consistency.
10
+
11
+ ## 2. Navigation & Context
12
+ To understand this project quickly, explore in this order:
13
+ 1. `SOCIAL_CONTRACT.md`: Your ethical and operational boundaries.
14
+ 2. `features/README.md`: The roadmap and logic of what we are building.
15
+ 3. `agents/AGENTS.md`: Specific definitions of other roles if they exist.
16
+ 4. `.levit/workflows/`: Step-by-step guides for common tasks.
17
+
18
+ ## 3. Rules of Engagement
19
+ - **Transparency**: If you are unsure about a decision, ask the human. Do not "hallucinate" architectural choices.
20
+ - **Atomicity**: Keep your edits small and focused.
21
+ - **Traceability**: Every major change should be linked to a feature defined in `features/`.
22
+ - **Tests First**: Before submitting a complex change, check if tests exist in `tests/` and run them.
23
+
24
+ ## 4. How to use Workflows
25
+ Check `.levit/workflows/` before starting a task. If a workflow like `add-logic.md` exists, follow its steps strictly to maintain project harmony.
26
+
27
+ ---
28
+ *Generated by Levit-Kit - Elevating Human-AI Collaboration.*
@@ -0,0 +1,19 @@
1
+ # Decision Record: [Title of the Decision]
2
+
3
+ - **Date**: [YYYY-MM-DD]
4
+ - **Status**: [Draft / Proposed / Approved]
5
+
6
+ ## Context
7
+ What is the problem we are solving? What are the constraints?
8
+
9
+ ## Decision
10
+ What is the chosen solution? How does it solve the problem?
11
+
12
+ ## Rationale
13
+ Why did we choose this? (e.g., performance, simplicity, maintainability, cost)
14
+
15
+ ## Alternatives Considered
16
+ What was rejected and why?
17
+
18
+ ## Consequences
19
+ What are the trade-offs? What are the next steps?
@@ -0,0 +1,10 @@
1
+ # Project Standards & Global Rules
2
+
3
+ > [!NOTE]
4
+ > These rules are defaults provided by Levit-Kit. Feel free to adapt them to your specific tech stack.
5
+
6
+ 1. **Strict Typing**: Prioritize strong typing and explicit interfaces over dynamic types.
7
+ 2. **Documentation**: Every core function or business logic should include descriptive comments.
8
+ 3. **Atomicity**: One task = one intent = one atomic implementation.
9
+ 4. **No Hallucinations**: Do not assume the existence of external dependencies. Verify the environment before using a library.
10
+ 5. **Quality First**: Follow the project's architectural patterns (SOLID, modularity).
@@ -0,0 +1,9 @@
1
+ # Refactoring Guidelines
2
+
3
+ Follow these steps when task involves cleaning or refactoring existing code.
4
+
5
+ 1. **Analyze**: Run tests before changing anything.
6
+ 2. **Protect**: Identify the critical path of the logic.
7
+ 3. **Iterate**: Make small, verifiable changes.
8
+ 4. **Verify**: Re-run tests and run the `conformance.eval.ts`.
9
+ 5. **Document**: Update `SOCIAL_CONTRACT.md` if the refactoring changes a core principle.
@@ -0,0 +1,9 @@
1
+ # Workflow: Example Task
2
+
3
+ This is a placeholder for a cognitive workflow.
4
+
5
+ ## Steps
6
+ 1. **Research**: Analyze the current file.
7
+ 2. **Propose**: Suggest the change.
8
+ 3. **Implement**: Code the solution.
9
+ 4. **Verify**: Run tests.
@@ -0,0 +1,18 @@
1
+ # Workflow: Submit for Review
2
+
3
+ When you have finished a task, follow these steps to hand over to the Human:
4
+
5
+ 1. **Self-Verification**:
6
+ - Ensure you haven't broken any principle from `SOCIAL_CONTRACT.md`.
7
+ - Verify that your code is atomic and follows naming conventions.
8
+ - Run existing tests and ensure they pass.
9
+
10
+ 2. **The "Handover" Message**:
11
+ Provide a summary to the Human containing:
12
+ - **What changed**: A bullet-point list of modifications.
13
+ - **Verification**: How did you test it? (CLI output, test logs, etc.).
14
+ - **Next Steps**: What should the human do or check now?
15
+ - **Open Questions**: Are there any remaining uncertainties or technical debt?
16
+
17
+ 3. **Status Update**:
18
+ Mark the task as completed in the relevant feature file or task tracker.
@@ -1,16 +1,35 @@
1
1
  # Project Overview
2
2
 
3
- This project was initialized with **levit-kit**.
3
+ This project was initialized with **Levit-Kit**.
4
+ It is designed for a **hybrid workspace** where Human vision leads and AI execution follows.
4
5
 
5
- Its purpose is to provide a **clear, predictable structure**
6
- for working with humans and AI agents.
6
+ ## 🚀 Quick Start: Human Operator Guide
7
7
 
8
- This repository:
9
- - contains no business logic
10
- - imposes no workflow
11
- - makes no automated decisions
8
+ ### 1. The Day One Setup
9
+ - **Adjust Governance**: Open `SOCIAL_CONTRACT.md` and tweak the principles to match your vision.
10
+ - **Define Standards**: Open `.levit/prompts/global-rules.md` to set your technical expectations (language, styling, strictness).
12
11
 
13
- Start by reading:
14
- - SOCIAL_CONTRACT.md
15
- - agents/AGENTS.md
16
- - features/README.md
12
+ ### 2. The "Intent-First" Workflow
13
+ When building a new feature, do not start with code:
14
+ 1. **Declare Intent**: Copy `features/INTENT.md` to a new file (e.g., `features/001-my-feature.md`).
15
+ 2. **Define Boundaries**: Fill in the "User Story" and especially the "Boundaries" section (what the AI must NOT touch).
16
+
17
+ ### 3. Leading your Agents
18
+ When using an AI agent (Antigravity, Cursor, etc.):
19
+ 1. **Onboard the Agent**: Direct it to read `.levit/AGENT_ONBOARDING.md` in your first prompt.
20
+ 2. **Assign the Task**: Point it to your new intent file in `features/`.
21
+ 3. **Review the Output**: Follow the guides in `.levit/workflows/submit-for-review.md`.
22
+
23
+ ---
24
+
25
+ ## 🏛️ Project Principles
26
+ This repository is built for **clarity over automation**:
27
+ - **Explicit Structure**: No hidden magic.
28
+ - **Human Sovereignty**: You make the final decisions.
29
+ - **Traceability**: All technical choices are documented (see `.levit/decision-record.md`).
30
+
31
+ ## 📂 Navigation
32
+ - `SOCIAL_CONTRACT.md`: Your ethical and operational foundations.
33
+ - `.levit/`: The AI's workspace (Onboarding, Prompts, Workflows).
34
+ - `features/`: The project roadmap and active intents.
35
+ - `evals/`: Technical quality tests for AI outputs.
@@ -8,6 +8,11 @@ This project follows the levit-kit social contract.
8
8
  - Agents are assistants, not decision-makers
9
9
  - Structure is explicit
10
10
  - Conventions are preferred over configuration
11
+ - **Agents (AI) are onboarded through specific protocols**
12
+
13
+ ## Agent (AI) Role
14
+
15
+ This project is AI-friendly. Agents must follow the instructions located in `.levit/AGENT_ONBOARDING.md`.
11
16
 
12
17
  ## What this project does not do
13
18
 
@@ -0,0 +1,14 @@
1
+ # Evaluations (Evals)
2
+
3
+ This directory is dedicated to testing the **intentions** and the **quality** of the AI output, rather than just the code implementation.
4
+
5
+ ## Why Evals?
6
+ While unit tests verify if the code works (logic), Evals verify if the AI respected the "Social Contract" and the "Human Intent".
7
+
8
+ ## Example Evals
9
+ - **Conformance**: Does the generated code follow our naming conventions?
10
+ - **Security**: Did the AI introduce any obvious secret leaks in the prompts?
11
+ - **Compliance**: Does the output still match the rules in `.levit/AGENT_ONBOARDING.md`?
12
+
13
+ ---
14
+ *Verify then Trust.*
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Example Conformance Evaluation
3
+ *
4
+ * This is a placeholder for an evaluation script.
5
+ * In a real-world scenario, you would use a library like Promptfoo or Giskard
6
+ * to run these evaluations against your agent's outputs.
7
+ */
8
+
9
+ export const conformanceEval = {
10
+ name: "Social Contract Adherence",
11
+ criteria: [
12
+ "Does the code follow camelCase for variables?",
13
+ "Is every change linked to an entry in features/?",
14
+ "Does the AI avoid modifying files outside its boundaries?"
15
+ ]
16
+ };
@@ -0,0 +1,23 @@
1
+ # INTENT: [Feature Name]
2
+
3
+ > Copy this file to create a new feature specification.
4
+
5
+ ## 1. Vision (The "Why")
6
+ - **User Story**: As a [role], I want to [action], so that [value].
7
+ - **Priority**: [Low / Medium / High / Critical]
8
+
9
+ ## 2. Success Criteria (The "What")
10
+ What must be true for this feature to be considered "Done"?
11
+ - [ ] Criterion 1
12
+ - [ ] Criterion 2
13
+
14
+ ## 3. Boundaries (The "No")
15
+ What is explicitly **OUT OF SCOPE** for this iteration? Prevents scope creep.
16
+ - Non-goal 1
17
+ - Non-goal 2
18
+
19
+ ## 4. Technical Constraints
20
+ - Specific tech, rules, or patterns to follow.
21
+
22
+ ## 5. Agent Task
23
+ Specific starting point for the AI (e.g., "Implement the API endpoint first").
@@ -1,12 +1,11 @@
1
1
  # Features
2
2
 
3
- A feature is a **contract**, not an implementation.
3
+ This directory contains the roadmap and the active specifications of the project.
4
4
 
5
- Each feature document should define:
6
- - intent
7
- - inputs
8
- - outputs
9
- - constraints
5
+ ## How to add a feature
6
+ 1. Copy [INTENT.md](./INTENT.md) to a new file (e.g., `001-my-feature.md`).
7
+ 2. Fill the "Human Intent" sections.
8
+ 3. Let the Agent implement the technical details.
10
9
 
11
- Features are discussed and validated here.
12
- Implementation happens elsewhere.
10
+ ---
11
+ 📄 Refer to [.levit/AGENT_ONBOARDING.md](../.levit/AGENT_ONBOARDING.md) for collaboration guidelines.
@@ -0,0 +1,11 @@
1
+ {
2
+ "name": "project-name",
3
+ "version": "0.1.0",
4
+ "private": true,
5
+ "scripts": {
6
+ "test": "node --test",
7
+ "eval": "ts-node evals/conformance.eval.ts"
8
+ },
9
+ "dependencies": {},
10
+ "devDependencies": {}
11
+ }