@buba_71/levit 0.4.0 → 0.8.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +182 -10
- package/dist/bin/cli.js +58 -48
- package/dist/src/commands/decision.js +34 -19
- package/dist/src/commands/feature.js +117 -19
- package/dist/src/commands/handoff.js +34 -12
- package/dist/src/commands/index.js +3 -1
- package/dist/src/commands/init.js +53 -39
- package/dist/src/commands/validate.js +84 -0
- package/dist/src/core/error_helper.js +93 -0
- package/dist/src/core/errors.js +25 -0
- package/dist/src/core/frontmatter.js +62 -0
- package/dist/src/core/logger.js +77 -0
- package/dist/src/core/table.js +63 -0
- package/dist/src/init.js +9 -0
- package/dist/src/readers/decision_reader.js +47 -0
- package/dist/src/readers/feature_reader.js +48 -0
- package/dist/src/readers/handoff_reader.js +47 -0
- package/dist/src/services/decision_service.js +49 -0
- package/dist/src/services/feature_service.js +89 -0
- package/dist/src/services/handoff_service.js +37 -0
- package/dist/src/services/manifest_service.js +89 -0
- package/dist/src/services/project_service.js +39 -0
- package/dist/src/services/validation_service.js +461 -0
- package/dist/src/types/domain.js +2 -0
- package/dist/src/types/index.js +2 -0
- package/dist/src/types/manifest.js +26 -0
- package/dist/tests/{init.test.js → cli/integration.test.js} +79 -39
- package/dist/tests/services/decision_service.test.js +27 -0
- package/dist/tests/services/feature_service.test.js +42 -0
- package/dist/tests/services/handoff_service.test.js +28 -0
- package/dist/tests/services/manifest_service.test.js +189 -0
- package/dist/tests/services/validation_service.test.js +196 -0
- package/package.json +7 -2
- package/templates/default/.github/workflows/README.md +56 -0
- package/templates/default/.github/workflows/levit-validate.yml +93 -0
- package/templates/default/.gitlab-ci.yml +73 -0
- package/templates/default/.levit/AGENT_CONTRACT.md +34 -0
- package/templates/default/.levit/AGENT_ONBOARDING.md +5 -4
- package/templates/default/.levit/decisions/.gitkeep +0 -0
- package/templates/default/{features → .levit/features}/INTENT.md +9 -0
- package/templates/default/{features → .levit/features}/README.md +2 -2
- package/templates/default/.levit/handoff/.gitkeep +0 -0
- package/templates/default/HUMAN_AGENT_MANAGER.md +654 -0
- package/templates/default/MIGRATION_GUIDE.md +597 -0
- package/templates/default/README.md +26 -7
- package/templates/symfony/.github/workflows/README.md +56 -0
- package/templates/symfony/.github/workflows/levit-validate.yml +82 -0
- package/templates/symfony/.gitlab-ci.yml +62 -0
- package/templates/symfony/.levit/AGENT_CONTRACT.md +34 -0
- package/templates/symfony/.levit/AGENT_ONBOARDING.md +124 -0
- package/templates/symfony/.levit/decisions/.gitkeep +0 -0
- package/templates/symfony/.levit/features/INTENT.md +32 -0
- package/templates/symfony/.levit/features/README.md +11 -0
- package/templates/symfony/.levit/handoff/.gitkeep +0 -0
- package/templates/symfony/.levit/prompts/global-rules.md +10 -0
- package/templates/symfony/.levit/prompts/refactoring-guidelines.md +9 -0
- package/templates/symfony/.levit/workflows/example-task.md +9 -0
- package/templates/symfony/.levit/workflows/submit-for-review.md +18 -0
- package/templates/symfony/HUMAN_AGENT_MANAGER.md +654 -0
- package/templates/symfony/MIGRATION_GUIDE.md +597 -0
- package/templates/symfony/README.md +101 -0
- package/templates/symfony/SOCIAL_CONTRACT.md +34 -0
- package/templates/default/.levit/decision-record.md +0 -19
- package/templates/default/package.json +0 -11
- /package/templates/default/{agents → .levit/agents}/AGENTS.md +0 -0
- /package/templates/default/{agents → .levit/agents}/boundaries.md +0 -0
- /package/templates/default/{docs → .levit/docs}/architecture.md +0 -0
- /package/templates/default/{evals → .levit/evals}/README.md +0 -0
- /package/templates/default/{evals → .levit/evals}/conformance.eval.ts +0 -0
- /package/templates/default/{pipelines → .levit/pipelines}/README.md +0 -0
- /package/templates/default/{roles → .levit/roles}/README.md +0 -0
- /package/templates/default/{roles → .levit/roles}/devops.md +0 -0
- /package/templates/default/{roles → .levit/roles}/qa.md +0 -0
- /package/templates/default/{roles → .levit/roles}/security.md +0 -0
|
@@ -0,0 +1,196 @@
|
|
|
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
|
+
const node_test_1 = __importDefault(require("node:test"));
|
|
7
|
+
const node_assert_1 = __importDefault(require("node:assert"));
|
|
8
|
+
const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
9
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
10
|
+
const node_os_1 = __importDefault(require("node:os"));
|
|
11
|
+
const validation_service_1 = require("../../src/services/validation_service");
|
|
12
|
+
(0, node_test_1.default)("ValidationService reports errors if core directories are missing", () => {
|
|
13
|
+
const tempDir = fs_extra_1.default.mkdtempSync(node_path_1.default.join(node_os_1.default.tmpdir(), "validation-service-test-"));
|
|
14
|
+
// Validate empty dir
|
|
15
|
+
const result = validation_service_1.ValidationService.validate(tempDir);
|
|
16
|
+
// Expect invalid
|
|
17
|
+
node_assert_1.default.strictEqual(result.valid, false);
|
|
18
|
+
node_assert_1.default.ok(result.metrics.errors > 0, "Should have errors");
|
|
19
|
+
// Check specific error code presence
|
|
20
|
+
const missingDirs = result.issues.filter(i => i.code === "MISSING_DIRECTORY");
|
|
21
|
+
node_assert_1.default.ok(missingDirs.length > 0, "Should report missing directories");
|
|
22
|
+
fs_extra_1.default.rmSync(tempDir, { recursive: true, force: true });
|
|
23
|
+
});
|
|
24
|
+
(0, node_test_1.default)("ValidationService passes for valid structure", () => {
|
|
25
|
+
const tempDir = fs_extra_1.default.mkdtempSync(node_path_1.default.join(node_os_1.default.tmpdir(), "validation-service-succ-"));
|
|
26
|
+
// Scout scaffolding: .levit/features, .levit/decisions, .levit/handoff, core files
|
|
27
|
+
const dirs = [".levit/features", ".levit/decisions", ".levit/handoff"];
|
|
28
|
+
dirs.forEach(d => fs_extra_1.default.ensureDirSync(node_path_1.default.join(tempDir, d)));
|
|
29
|
+
const files = ["SOCIAL_CONTRACT.md", ".levit/AGENT_CONTRACT.md", ".levit/AGENT_ONBOARDING.md"];
|
|
30
|
+
files.forEach(f => fs_extra_1.default.writeFileSync(node_path_1.default.join(tempDir, f), "content"));
|
|
31
|
+
const result = validation_service_1.ValidationService.validate(tempDir);
|
|
32
|
+
node_assert_1.default.strictEqual(result.valid, true);
|
|
33
|
+
fs_extra_1.default.rmSync(tempDir, { recursive: true, force: true });
|
|
34
|
+
});
|
|
35
|
+
(0, node_test_1.default)("ValidationService detects feature with invalid frontmatter", () => {
|
|
36
|
+
const tempDir = fs_extra_1.default.mkdtempSync(node_path_1.default.join(node_os_1.default.tmpdir(), "validation-service-test-"));
|
|
37
|
+
const featuresDir = node_path_1.default.join(tempDir, ".levit", "features");
|
|
38
|
+
fs_extra_1.default.ensureDirSync(featuresDir);
|
|
39
|
+
// Create core structure
|
|
40
|
+
fs_extra_1.default.ensureDirSync(node_path_1.default.join(tempDir, ".levit/decisions"));
|
|
41
|
+
fs_extra_1.default.ensureDirSync(node_path_1.default.join(tempDir, ".levit/handoff"));
|
|
42
|
+
fs_extra_1.default.writeFileSync(node_path_1.default.join(tempDir, "SOCIAL_CONTRACT.md"), "content");
|
|
43
|
+
fs_extra_1.default.writeFileSync(node_path_1.default.join(tempDir, ".levit/AGENT_CONTRACT.md"), "content");
|
|
44
|
+
fs_extra_1.default.writeFileSync(node_path_1.default.join(tempDir, ".levit/AGENT_ONBOARDING.md"), "content");
|
|
45
|
+
// Create feature with missing required fields
|
|
46
|
+
const invalidFeature = `---
|
|
47
|
+
id: 001
|
|
48
|
+
status: active
|
|
49
|
+
# Missing: owner, last_updated, risk_level, depends_on
|
|
50
|
+
|
|
51
|
+
# INTENT: Test Feature
|
|
52
|
+
`;
|
|
53
|
+
fs_extra_1.default.writeFileSync(node_path_1.default.join(featuresDir, "001-test.md"), invalidFeature);
|
|
54
|
+
const result = validation_service_1.ValidationService.validate(tempDir);
|
|
55
|
+
node_assert_1.default.strictEqual(result.valid, false);
|
|
56
|
+
const frontmatterErrors = result.issues.filter(i => i.code === "INVALID_FRONTMATTER");
|
|
57
|
+
node_assert_1.default.ok(frontmatterErrors.length > 0, "Should report invalid frontmatter");
|
|
58
|
+
fs_extra_1.default.rmSync(tempDir, { recursive: true, force: true });
|
|
59
|
+
});
|
|
60
|
+
(0, node_test_1.default)("ValidationService detects feature without INTENT header", () => {
|
|
61
|
+
const tempDir = fs_extra_1.default.mkdtempSync(node_path_1.default.join(node_os_1.default.tmpdir(), "validation-service-test-"));
|
|
62
|
+
const featuresDir = node_path_1.default.join(tempDir, ".levit", "features");
|
|
63
|
+
fs_extra_1.default.ensureDirSync(featuresDir);
|
|
64
|
+
// Create core structure
|
|
65
|
+
fs_extra_1.default.ensureDirSync(node_path_1.default.join(tempDir, ".levit/decisions"));
|
|
66
|
+
fs_extra_1.default.ensureDirSync(node_path_1.default.join(tempDir, ".levit/handoff"));
|
|
67
|
+
fs_extra_1.default.writeFileSync(node_path_1.default.join(tempDir, "SOCIAL_CONTRACT.md"), "content");
|
|
68
|
+
fs_extra_1.default.writeFileSync(node_path_1.default.join(tempDir, ".levit/AGENT_CONTRACT.md"), "content");
|
|
69
|
+
fs_extra_1.default.writeFileSync(node_path_1.default.join(tempDir, ".levit/AGENT_ONBOARDING.md"), "content");
|
|
70
|
+
// Create feature without INTENT header
|
|
71
|
+
const featureWithoutIntent = `---
|
|
72
|
+
id: 001
|
|
73
|
+
status: active
|
|
74
|
+
owner: human
|
|
75
|
+
last_updated: 2026-01-01
|
|
76
|
+
risk_level: low
|
|
77
|
+
depends_on: []
|
|
78
|
+
---
|
|
79
|
+
|
|
80
|
+
# Some other header
|
|
81
|
+
`;
|
|
82
|
+
fs_extra_1.default.writeFileSync(node_path_1.default.join(featuresDir, "001-test.md"), featureWithoutIntent);
|
|
83
|
+
const result = validation_service_1.ValidationService.validate(tempDir);
|
|
84
|
+
node_assert_1.default.strictEqual(result.valid, false);
|
|
85
|
+
const structureErrors = result.issues.filter(i => i.code === "INVALID_STRUCTURE");
|
|
86
|
+
node_assert_1.default.ok(structureErrors.length > 0, "Should report missing INTENT header");
|
|
87
|
+
fs_extra_1.default.rmSync(tempDir, { recursive: true, force: true });
|
|
88
|
+
});
|
|
89
|
+
(0, node_test_1.default)("ValidationService detects decision with invalid frontmatter", () => {
|
|
90
|
+
const tempDir = fs_extra_1.default.mkdtempSync(node_path_1.default.join(node_os_1.default.tmpdir(), "validation-service-test-"));
|
|
91
|
+
const decisionsDir = node_path_1.default.join(tempDir, ".levit/decisions");
|
|
92
|
+
fs_extra_1.default.ensureDirSync(decisionsDir);
|
|
93
|
+
// Create core structure
|
|
94
|
+
fs_extra_1.default.ensureDirSync(node_path_1.default.join(tempDir, ".levit", "features"));
|
|
95
|
+
fs_extra_1.default.ensureDirSync(node_path_1.default.join(tempDir, ".levit", "handoff"));
|
|
96
|
+
fs_extra_1.default.writeFileSync(node_path_1.default.join(tempDir, "SOCIAL_CONTRACT.md"), "content");
|
|
97
|
+
fs_extra_1.default.writeFileSync(node_path_1.default.join(tempDir, ".levit/AGENT_CONTRACT.md"), "content");
|
|
98
|
+
fs_extra_1.default.writeFileSync(node_path_1.default.join(tempDir, ".levit/AGENT_ONBOARDING.md"), "content");
|
|
99
|
+
// Create decision with invalid YAML
|
|
100
|
+
const invalidDecision = `---
|
|
101
|
+
id: ADR-001
|
|
102
|
+
status: draft
|
|
103
|
+
owner: human
|
|
104
|
+
last_updated: 2026-01-01
|
|
105
|
+
risk_level: low
|
|
106
|
+
depends_on: [invalid: yaml: syntax]
|
|
107
|
+
---
|
|
108
|
+
|
|
109
|
+
# ADR 001: Test
|
|
110
|
+
`;
|
|
111
|
+
fs_extra_1.default.writeFileSync(node_path_1.default.join(decisionsDir, "ADR-001-test.md"), invalidDecision);
|
|
112
|
+
const result = validation_service_1.ValidationService.validate(tempDir);
|
|
113
|
+
node_assert_1.default.strictEqual(result.valid, false);
|
|
114
|
+
const frontmatterErrors = result.issues.filter(i => i.code === "INVALID_FRONTMATTER");
|
|
115
|
+
node_assert_1.default.ok(frontmatterErrors.length > 0, "Should report invalid YAML");
|
|
116
|
+
fs_extra_1.default.rmSync(tempDir, { recursive: true, force: true });
|
|
117
|
+
});
|
|
118
|
+
(0, node_test_1.default)("ValidationService detects handoff with missing frontmatter", () => {
|
|
119
|
+
const tempDir = fs_extra_1.default.mkdtempSync(node_path_1.default.join(node_os_1.default.tmpdir(), "validation-service-test-"));
|
|
120
|
+
const handoffDir = node_path_1.default.join(tempDir, ".levit/handoff");
|
|
121
|
+
fs_extra_1.default.ensureDirSync(handoffDir);
|
|
122
|
+
// Create core structure
|
|
123
|
+
fs_extra_1.default.ensureDirSync(node_path_1.default.join(tempDir, ".levit", "features"));
|
|
124
|
+
fs_extra_1.default.ensureDirSync(node_path_1.default.join(tempDir, ".levit", "decisions"));
|
|
125
|
+
fs_extra_1.default.writeFileSync(node_path_1.default.join(tempDir, "SOCIAL_CONTRACT.md"), "content");
|
|
126
|
+
fs_extra_1.default.writeFileSync(node_path_1.default.join(tempDir, ".levit/AGENT_CONTRACT.md"), "content");
|
|
127
|
+
fs_extra_1.default.writeFileSync(node_path_1.default.join(tempDir, ".levit/AGENT_ONBOARDING.md"), "content");
|
|
128
|
+
// Create handoff without frontmatter
|
|
129
|
+
const handoffWithoutFrontmatter = `# Agent Handoff
|
|
130
|
+
No frontmatter here
|
|
131
|
+
`;
|
|
132
|
+
fs_extra_1.default.writeFileSync(node_path_1.default.join(handoffDir, "2026-01-01-test.md"), handoffWithoutFrontmatter);
|
|
133
|
+
const result = validation_service_1.ValidationService.validate(tempDir);
|
|
134
|
+
node_assert_1.default.strictEqual(result.valid, false);
|
|
135
|
+
const frontmatterErrors = result.issues.filter(i => i.code === "INVALID_FRONTMATTER");
|
|
136
|
+
node_assert_1.default.ok(frontmatterErrors.length > 0, "Should report missing frontmatter");
|
|
137
|
+
fs_extra_1.default.rmSync(tempDir, { recursive: true, force: true });
|
|
138
|
+
});
|
|
139
|
+
(0, node_test_1.default)("ValidationService reports warning when no features exist", () => {
|
|
140
|
+
const tempDir = fs_extra_1.default.mkdtempSync(node_path_1.default.join(node_os_1.default.tmpdir(), "validation-service-test-"));
|
|
141
|
+
const featuresDir = node_path_1.default.join(tempDir, ".levit", "features");
|
|
142
|
+
fs_extra_1.default.ensureDirSync(featuresDir);
|
|
143
|
+
// Create core structure
|
|
144
|
+
fs_extra_1.default.ensureDirSync(node_path_1.default.join(tempDir, ".levit/decisions"));
|
|
145
|
+
fs_extra_1.default.ensureDirSync(node_path_1.default.join(tempDir, ".levit/handoff"));
|
|
146
|
+
fs_extra_1.default.writeFileSync(node_path_1.default.join(tempDir, "SOCIAL_CONTRACT.md"), "content");
|
|
147
|
+
fs_extra_1.default.writeFileSync(node_path_1.default.join(tempDir, ".levit/AGENT_CONTRACT.md"), "content");
|
|
148
|
+
fs_extra_1.default.writeFileSync(node_path_1.default.join(tempDir, ".levit/AGENT_ONBOARDING.md"), "content");
|
|
149
|
+
// No features in features directory
|
|
150
|
+
const result = validation_service_1.ValidationService.validate(tempDir);
|
|
151
|
+
node_assert_1.default.strictEqual(result.valid, true, "Should be valid (only warning)");
|
|
152
|
+
node_assert_1.default.ok(result.metrics.warnings > 0, "Should have warnings");
|
|
153
|
+
const noFeaturesWarning = result.issues.find(i => i.code === "NO_FEATURES");
|
|
154
|
+
node_assert_1.default.ok(noFeaturesWarning, "Should report no features warning");
|
|
155
|
+
fs_extra_1.default.rmSync(tempDir, { recursive: true, force: true });
|
|
156
|
+
});
|
|
157
|
+
(0, node_test_1.default)("ValidationService counts files scanned correctly", () => {
|
|
158
|
+
const tempDir = fs_extra_1.default.mkdtempSync(node_path_1.default.join(node_os_1.default.tmpdir(), "validation-service-test-"));
|
|
159
|
+
const featuresDir = node_path_1.default.join(tempDir, ".levit", "features");
|
|
160
|
+
fs_extra_1.default.ensureDirSync(featuresDir);
|
|
161
|
+
// Create core structure
|
|
162
|
+
fs_extra_1.default.ensureDirSync(node_path_1.default.join(tempDir, ".levit/decisions"));
|
|
163
|
+
fs_extra_1.default.ensureDirSync(node_path_1.default.join(tempDir, ".levit/handoff"));
|
|
164
|
+
fs_extra_1.default.writeFileSync(node_path_1.default.join(tempDir, "SOCIAL_CONTRACT.md"), "content");
|
|
165
|
+
fs_extra_1.default.writeFileSync(node_path_1.default.join(tempDir, ".levit/AGENT_CONTRACT.md"), "content");
|
|
166
|
+
fs_extra_1.default.writeFileSync(node_path_1.default.join(tempDir, ".levit/AGENT_ONBOARDING.md"), "content");
|
|
167
|
+
// Create valid feature
|
|
168
|
+
const validFeature = `---
|
|
169
|
+
id: 001
|
|
170
|
+
status: active
|
|
171
|
+
owner: human
|
|
172
|
+
last_updated: 2026-01-01
|
|
173
|
+
risk_level: low
|
|
174
|
+
depends_on: []
|
|
175
|
+
---
|
|
176
|
+
|
|
177
|
+
# INTENT: Test Feature
|
|
178
|
+
`;
|
|
179
|
+
fs_extra_1.default.writeFileSync(node_path_1.default.join(featuresDir, "001-test.md"), validFeature);
|
|
180
|
+
// Create valid decision
|
|
181
|
+
const validDecision = `---
|
|
182
|
+
id: ADR-001
|
|
183
|
+
status: draft
|
|
184
|
+
owner: human
|
|
185
|
+
last_updated: 2026-01-01
|
|
186
|
+
risk_level: low
|
|
187
|
+
depends_on: []
|
|
188
|
+
---
|
|
189
|
+
|
|
190
|
+
# ADR 001: Test Decision
|
|
191
|
+
`;
|
|
192
|
+
fs_extra_1.default.writeFileSync(node_path_1.default.join(tempDir, ".levit/decisions", "ADR-001-test.md"), validDecision);
|
|
193
|
+
const result = validation_service_1.ValidationService.validate(tempDir);
|
|
194
|
+
node_assert_1.default.strictEqual(result.metrics.filesScanned, 2, "Should scan 2 files (1 feature + 1 decision)");
|
|
195
|
+
fs_extra_1.default.rmSync(tempDir, { recursive: true, force: true });
|
|
196
|
+
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@buba_71/levit",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.2",
|
|
4
4
|
"description": "Hybrid starter kit for Antigravity projects",
|
|
5
5
|
"author": "David BUBA",
|
|
6
6
|
"license": "MIT",
|
|
@@ -19,14 +19,19 @@
|
|
|
19
19
|
"main": "dist/src/init.js",
|
|
20
20
|
"scripts": {
|
|
21
21
|
"build": "tsc && chmod +x dist/bin/cli.js",
|
|
22
|
+
"prepublishOnly": "npm run build",
|
|
22
23
|
"start": "node dist/bin/cli.js",
|
|
23
24
|
"test": "npm run build && node --test dist/tests"
|
|
24
25
|
},
|
|
25
26
|
"dependencies": {
|
|
26
|
-
"fs-extra": "^11.2.0"
|
|
27
|
+
"fs-extra": "^11.2.0",
|
|
28
|
+
"js-yaml": "^4.1.1",
|
|
29
|
+
"chalk": "^4.1.2",
|
|
30
|
+
"cli-table3": "^0.6.5"
|
|
27
31
|
},
|
|
28
32
|
"devDependencies": {
|
|
29
33
|
"@types/fs-extra": "^11.0.4",
|
|
34
|
+
"@types/js-yaml": "^4.0.9",
|
|
30
35
|
"@types/node": "^20.11.0",
|
|
31
36
|
"typescript": "^5.3.0"
|
|
32
37
|
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# GitHub Actions Workflows
|
|
2
|
+
|
|
3
|
+
This directory contains CI/CD workflows for levit-kit projects.
|
|
4
|
+
|
|
5
|
+
## Available Workflows
|
|
6
|
+
|
|
7
|
+
### `levit-validate.yml`
|
|
8
|
+
|
|
9
|
+
Validates the levit-kit project structure on every push and pull request.
|
|
10
|
+
|
|
11
|
+
**What it does**:
|
|
12
|
+
- Runs `levit validate` to check project structure
|
|
13
|
+
- Fails the build if validation errors are found
|
|
14
|
+
- Uploads validation results as artifacts
|
|
15
|
+
- Displays results in GitHub Actions summary
|
|
16
|
+
|
|
17
|
+
**When it runs**:
|
|
18
|
+
- On pushes to `main`, `master`, or `develop` branches
|
|
19
|
+
- On pull requests targeting these branches
|
|
20
|
+
|
|
21
|
+
**How to use**:
|
|
22
|
+
1. This workflow is automatically included in levit-kit projects
|
|
23
|
+
2. No configuration needed - it works out of the box
|
|
24
|
+
3. Customize the `on:` section if you use different branch names
|
|
25
|
+
|
|
26
|
+
**Customization**:
|
|
27
|
+
Edit `.github/workflows/levit-validate.yml` to:
|
|
28
|
+
- Change branch names
|
|
29
|
+
- Add additional validation steps
|
|
30
|
+
- Integrate with other workflows
|
|
31
|
+
- Add notifications
|
|
32
|
+
|
|
33
|
+
## Adding Custom Workflows
|
|
34
|
+
|
|
35
|
+
You can add additional workflows for:
|
|
36
|
+
- Feature status checks
|
|
37
|
+
- Decision record validation
|
|
38
|
+
- Handoff completeness checks
|
|
39
|
+
- Custom evals
|
|
40
|
+
|
|
41
|
+
Example structure:
|
|
42
|
+
```yaml
|
|
43
|
+
name: Custom Validation
|
|
44
|
+
on: [push, pull_request]
|
|
45
|
+
jobs:
|
|
46
|
+
custom-check:
|
|
47
|
+
runs-on: ubuntu-latest
|
|
48
|
+
steps:
|
|
49
|
+
- uses: actions/checkout@v4
|
|
50
|
+
- run: your-custom-script.sh
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
---
|
|
54
|
+
|
|
55
|
+
*For more information, see the [levit-kit documentation](https://github.com/buba71/levit-kit).*
|
|
56
|
+
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
name: Validate Levit Project
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [ main, master, develop ]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [ main, master, develop ]
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
validate:
|
|
11
|
+
name: Validate levit-kit Structure
|
|
12
|
+
runs-on: ubuntu-latest
|
|
13
|
+
|
|
14
|
+
steps:
|
|
15
|
+
- name: Checkout code
|
|
16
|
+
uses: actions/checkout@v4
|
|
17
|
+
|
|
18
|
+
- name: Setup Node.js
|
|
19
|
+
uses: actions/setup-node@v4
|
|
20
|
+
with:
|
|
21
|
+
node-version: '20'
|
|
22
|
+
cache: 'npm'
|
|
23
|
+
|
|
24
|
+
- name: Validate levit-kit structure
|
|
25
|
+
id: validate
|
|
26
|
+
run: |
|
|
27
|
+
npx @buba_71/levit validate --json > validation.json 2>&1 || VALIDATION_EXIT=$?
|
|
28
|
+
if [ -n "$VALIDATION_EXIT" ]; then
|
|
29
|
+
echo "validation_failed=true" >> $GITHUB_OUTPUT
|
|
30
|
+
else
|
|
31
|
+
echo "validation_failed=false" >> $GITHUB_OUTPUT
|
|
32
|
+
fi
|
|
33
|
+
|
|
34
|
+
- name: Display validation results
|
|
35
|
+
if: always()
|
|
36
|
+
run: |
|
|
37
|
+
if [ -f validation.json ]; then
|
|
38
|
+
echo "## Validation Results" >> $GITHUB_STEP_SUMMARY
|
|
39
|
+
echo '```json' >> $GITHUB_STEP_SUMMARY
|
|
40
|
+
cat validation.json >> $GITHUB_STEP_SUMMARY
|
|
41
|
+
echo '```' >> $GITHUB_STEP_SUMMARY
|
|
42
|
+
|
|
43
|
+
# Check for errors in JSON output
|
|
44
|
+
ERRORS=$(node -e "
|
|
45
|
+
try {
|
|
46
|
+
const fs = require('fs');
|
|
47
|
+
const content = fs.readFileSync('validation.json', 'utf8');
|
|
48
|
+
const lines = content.split('\\n').filter(l => l.trim());
|
|
49
|
+
let errorCount = 0;
|
|
50
|
+
for (const line of lines) {
|
|
51
|
+
try {
|
|
52
|
+
const obj = JSON.parse(line);
|
|
53
|
+
// Check for ERROR level or validation failure messages
|
|
54
|
+
if (obj.level === 'ERROR' ||
|
|
55
|
+
(obj.message && (
|
|
56
|
+
obj.message.toLowerCase().includes('error') ||
|
|
57
|
+
obj.message.toLowerCase().includes('validation failed') ||
|
|
58
|
+
obj.message.toLowerCase().includes('failed')
|
|
59
|
+
))) {
|
|
60
|
+
errorCount++;
|
|
61
|
+
}
|
|
62
|
+
} catch (e) {
|
|
63
|
+
// Non-JSON lines might be error messages
|
|
64
|
+
if (line.toLowerCase().includes('error') || line.toLowerCase().includes('failed')) {
|
|
65
|
+
errorCount++;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
console.log(errorCount);
|
|
70
|
+
} catch (e) {
|
|
71
|
+
console.log('0');
|
|
72
|
+
}
|
|
73
|
+
" || echo "0")
|
|
74
|
+
|
|
75
|
+
if [ "$ERRORS" -gt 0 ] || [ "${{ steps.validate.outputs.validation_failed }}" == "true" ]; then
|
|
76
|
+
echo "❌ Validation failed"
|
|
77
|
+
exit 1
|
|
78
|
+
else
|
|
79
|
+
echo "✅ Validation passed"
|
|
80
|
+
fi
|
|
81
|
+
else
|
|
82
|
+
echo "⚠️ No validation output found"
|
|
83
|
+
exit 1
|
|
84
|
+
fi
|
|
85
|
+
|
|
86
|
+
- name: Upload validation results
|
|
87
|
+
if: always()
|
|
88
|
+
uses: actions/upload-artifact@v3
|
|
89
|
+
with:
|
|
90
|
+
name: validation-results
|
|
91
|
+
path: validation.json
|
|
92
|
+
if-no-files-found: ignore
|
|
93
|
+
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# GitLab CI configuration for levit-kit validation
|
|
2
|
+
# Add this to your existing .gitlab-ci.yml or use it as a separate file
|
|
3
|
+
|
|
4
|
+
stages:
|
|
5
|
+
- validate
|
|
6
|
+
|
|
7
|
+
levit-validate:
|
|
8
|
+
stage: validate
|
|
9
|
+
image: node:20
|
|
10
|
+
before_script:
|
|
11
|
+
- npm install -g @buba_71/levit
|
|
12
|
+
script:
|
|
13
|
+
- |
|
|
14
|
+
echo "🔍 Validating levit-kit project structure..."
|
|
15
|
+
levit validate --json > validation.json 2>&1 || VALIDATION_EXIT=$?
|
|
16
|
+
|
|
17
|
+
if [ -f validation.json ]; then
|
|
18
|
+
echo "Validation results:"
|
|
19
|
+
cat validation.json
|
|
20
|
+
|
|
21
|
+
# Count errors in JSON output
|
|
22
|
+
ERRORS=$(node -e "
|
|
23
|
+
try {
|
|
24
|
+
const fs = require('fs');
|
|
25
|
+
const content = fs.readFileSync('validation.json', 'utf8');
|
|
26
|
+
const lines = content.split('\\n').filter(l => l.trim());
|
|
27
|
+
let errorCount = 0;
|
|
28
|
+
for (const line of lines) {
|
|
29
|
+
try {
|
|
30
|
+
const obj = JSON.parse(line);
|
|
31
|
+
// Check for ERROR level or validation failure messages
|
|
32
|
+
if (obj.level === 'ERROR' ||
|
|
33
|
+
(obj.message && (
|
|
34
|
+
obj.message.toLowerCase().includes('error') ||
|
|
35
|
+
obj.message.toLowerCase().includes('validation failed') ||
|
|
36
|
+
obj.message.toLowerCase().includes('failed')
|
|
37
|
+
))) {
|
|
38
|
+
errorCount++;
|
|
39
|
+
}
|
|
40
|
+
} catch (e) {
|
|
41
|
+
// Non-JSON lines might be error messages
|
|
42
|
+
if (line.toLowerCase().includes('error') || line.toLowerCase().includes('failed')) {
|
|
43
|
+
errorCount++;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
console.log(errorCount);
|
|
48
|
+
} catch (e) {
|
|
49
|
+
console.log('0');
|
|
50
|
+
}
|
|
51
|
+
" || echo "0")
|
|
52
|
+
|
|
53
|
+
if [ "$ERRORS" -gt 0 ] || [ -n "$VALIDATION_EXIT" ]; then
|
|
54
|
+
echo "❌ Validation failed with errors"
|
|
55
|
+
exit 1
|
|
56
|
+
else
|
|
57
|
+
echo "✅ Validation passed"
|
|
58
|
+
fi
|
|
59
|
+
else
|
|
60
|
+
echo "⚠️ No validation output found"
|
|
61
|
+
exit 1
|
|
62
|
+
fi
|
|
63
|
+
artifacts:
|
|
64
|
+
when: always
|
|
65
|
+
paths:
|
|
66
|
+
- validation.json
|
|
67
|
+
expire_in: 1 week
|
|
68
|
+
only:
|
|
69
|
+
- merge_requests
|
|
70
|
+
- main
|
|
71
|
+
- master
|
|
72
|
+
- develop
|
|
73
|
+
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# AGENT_CONTRACT.md
|
|
2
|
+
|
|
3
|
+
> [!IMPORTANT]
|
|
4
|
+
> This document defines the social and technical contract between the **Human Operator** and any **AI Agent** working on this project.
|
|
5
|
+
|
|
6
|
+
## 1. Social Contract & Philosophy
|
|
7
|
+
Our collaboration is based on the [SOCIAL_CONTRACT.md](file:///path/to/SOCIAL_CONTRACT.md) guidelines:
|
|
8
|
+
- **Human in the Loop**: AI proposes, Human disposes.
|
|
9
|
+
- **Cognitive Scaffolding**: Documentation is not a chore; it is the project's memory.
|
|
10
|
+
|
|
11
|
+
## 2. Agent Mandates (MUST)
|
|
12
|
+
- **Traceability**: Every code change MUST be mapped to an intention in `features/`.
|
|
13
|
+
- **Decision Records**: Significant technical choices MUST be documented in `.levit/decisions/`.
|
|
14
|
+
- **Handoffs**: Leave a clear handoff in `.levit/handoff/` when ending a session.
|
|
15
|
+
- **Linter First**: Run `levit validate` before declaring a task finished.
|
|
16
|
+
|
|
17
|
+
## 3. Agent Prohibitions (NEVER)
|
|
18
|
+
- **No Shadow Changes**: Never modify code without updating the corresponding `INTENT` or `ADR` if the change is structural.
|
|
19
|
+
- **No Deletion without Approval**: Never delete core infrastructure or large blocks of code without explicit confirmation.
|
|
20
|
+
- **No Hallucinated Tech**: If an API or library is unclear, ask for clarification.
|
|
21
|
+
|
|
22
|
+
## 4. Interaction Protocol
|
|
23
|
+
1. **Read**: Check `AGENT_ONBOARDING.md` and the latest `HANDOFF`.
|
|
24
|
+
2. **Plan**: Propose changes and get approval.
|
|
25
|
+
3. **Execute**: Implement with tests.
|
|
26
|
+
4. **Validate**: Run `levit validate`.
|
|
27
|
+
5. **Handoff**: Document what was done.
|
|
28
|
+
|
|
29
|
+
## 5. Machine-First Metadata
|
|
30
|
+
Agents should prioritize reading and maintaining the YAML frontmatter in all documentation files.
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
*Status: ACTIVE | Schema: 1.1 | Last Updated: 2025-12-31*
|
|
34
|
+
|
|
@@ -11,14 +11,15 @@ This project is a hybrid workspace where humans define intentions and AI ensures
|
|
|
11
11
|
## 2. Navigation & Context
|
|
12
12
|
To understand this project quickly, explore in this order:
|
|
13
13
|
1. `SOCIAL_CONTRACT.md`: Your ethical and operational boundaries.
|
|
14
|
-
2.
|
|
15
|
-
3.
|
|
16
|
-
4. `.levit/
|
|
14
|
+
2. `.levit/features/`: The roadmap and logic of what we are building.
|
|
15
|
+
3. `.levit/decisions/`: History of technical choices (ADRs).
|
|
16
|
+
4. `.levit/handoff/`: Specific context for your current mission.
|
|
17
|
+
5. `.levit/workflows/`: Step-by-step guides for common tasks.
|
|
17
18
|
|
|
18
19
|
## 3. Rules of Engagement
|
|
19
20
|
- **Transparency**: If you are unsure about a decision, ask the human. Do not "hallucinate" architectural choices.
|
|
20
21
|
- **Atomicity**: Keep your edits small and focused.
|
|
21
|
-
- **Traceability**: Every major change should be linked to a feature defined in
|
|
22
|
+
- **Traceability**: Every major change should be linked to a feature defined in `.levit/features/`.
|
|
22
23
|
- **Tests First**: Before submitting a complex change, check if tests exist in `tests/` and run them.
|
|
23
24
|
|
|
24
25
|
## 4. How to use Workflows
|
|
File without changes
|
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
This directory contains the roadmap and the active specifications of the project.
|
|
4
4
|
|
|
5
5
|
## How to add a feature
|
|
6
|
-
1.
|
|
7
|
-
2. Fill the "Human Intent" sections.
|
|
6
|
+
1. Run `levit feature new` from the project root.
|
|
7
|
+
2. Fill the "Human Intent" sections in the generated file.
|
|
8
8
|
3. Let the Agent implement the technical details.
|
|
9
9
|
|
|
10
10
|
---
|
|
File without changes
|