@kntic/kntic 0.6.0 → 0.7.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 +125 -56
- package/package.json +1 -1
- package/src/commands/init.js +40 -0
- package/src/commands/init.test.js +63 -1
package/README.md
CHANGED
|
@@ -1,93 +1,162 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @kntic/kntic
|
|
2
2
|
|
|
3
|
+
KNTIC CLI — bootstrap and manage KNTIC AI-orchestrated projects.
|
|
3
4
|
|
|
5
|
+
## Requirements
|
|
4
6
|
|
|
5
|
-
|
|
7
|
+
- **Node.js** >= 18.0.0
|
|
8
|
+
- **Docker** (with `docker compose` v2) — required for `kntic start` / `kntic stop`
|
|
9
|
+
- **GNU screen** _(optional)_ — needed for `kntic start --screen`
|
|
10
|
+
- **Git** _(optional)_ — enables auto-detection of `GIT_HOST`, `GIT_REPO_PATH`, and `GITLAB_TOKEN` during `kntic init`
|
|
6
11
|
|
|
7
|
-
|
|
12
|
+
## Installation
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
npm install -g @kntic/kntic
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Preflight Checks
|
|
8
19
|
|
|
9
|
-
|
|
20
|
+
When running `kntic init`, the CLI performs automatic preflight checks before downloading the bootstrap archive. All checks are **warnings only** — they never block execution.
|
|
10
21
|
|
|
11
|
-
|
|
22
|
+
| Check | Condition | Warning |
|
|
23
|
+
|-------|-----------|---------|
|
|
24
|
+
| **Platform** | `process.platform !== "linux"` | `⚠ Non-Linux detected (<platform>) — KNTIC is designed for Linux, other platforms may have issues` |
|
|
25
|
+
| **Docker** | `docker` binary not found in `$PATH` | `⚠ docker not found — required for \`kntic start\`` |
|
|
26
|
+
| **Screen** | `screen` binary not found in `$PATH` | `⚠ screen not found — optional, needed for \`kntic start --screen\`` |
|
|
12
27
|
|
|
13
|
-
|
|
14
|
-
* [Add files using the command line](https://docs.gitlab.com/topics/git/add_files/#add-files-to-a-git-repository) or push an existing Git repository with the following command:
|
|
28
|
+
## Commands
|
|
15
29
|
|
|
30
|
+
### `kntic usage`
|
|
31
|
+
|
|
32
|
+
List all available sub-commands.
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
kntic usage
|
|
16
36
|
```
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
37
|
+
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
### `kntic init`
|
|
41
|
+
|
|
42
|
+
Download and extract the KNTIC bootstrap template into the current directory. Sets up the `.kntic/` directory structure, `kntic.yml`, and `.kntic.env`.
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
kntic init [--quick | --interactive | -i]
|
|
21
46
|
```
|
|
22
47
|
|
|
23
|
-
|
|
48
|
+
| Option | Description |
|
|
49
|
+
|--------|-------------|
|
|
50
|
+
| `--quick` | **Default.** Non-interactive mode. Auto-detects `GIT_HOST` and `GIT_REPO_PATH` from the git origin remote (SSH and HTTPS). Extracts `GITLAB_TOKEN` from HTTPS credentials if available (`glpat-*` tokens). |
|
|
51
|
+
| `--interactive`, `-i` | Walks through all `.kntic.env` values interactively, prompting for each variable. Auto-detected values are pre-filled as defaults. Skips `KNTIC_VERSION` and already-detected `GITLAB_TOKEN`. |
|
|
24
52
|
|
|
25
|
-
|
|
53
|
+
**What it does:**
|
|
26
54
|
|
|
27
|
-
|
|
55
|
+
1. Runs [preflight checks](#preflight-checks)
|
|
56
|
+
2. Fetches version metadata from the bootstrap artifact URL
|
|
57
|
+
3. Downloads and extracts the bootstrap archive (merges `.gitignore` if one already exists)
|
|
58
|
+
4. Appends `KNTIC_VERSION=<version>` to `.kntic.env`
|
|
59
|
+
5. Auto-detects git remote information and fills `GIT_HOST`, `GIT_REPO_PATH`, and `GITLAB_TOKEN`
|
|
60
|
+
6. _(Interactive mode only)_ Prompts for remaining `.kntic.env` values
|
|
28
61
|
|
|
29
|
-
|
|
30
|
-
* [Create a new merge request](https://docs.gitlab.com/user/project/merge_requests/creating_merge_requests/)
|
|
31
|
-
* [Automatically close issues from merge requests](https://docs.gitlab.com/user/project/issues/managing_issues/#closing-issues-automatically)
|
|
32
|
-
* [Enable merge request approvals](https://docs.gitlab.com/user/project/merge_requests/approvals/)
|
|
33
|
-
* [Set auto-merge](https://docs.gitlab.com/user/project/merge_requests/auto_merge/)
|
|
62
|
+
---
|
|
34
63
|
|
|
35
|
-
|
|
64
|
+
### `kntic start`
|
|
36
65
|
|
|
37
|
-
|
|
66
|
+
Build and start KNTIC services via Docker Compose.
|
|
38
67
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
* [Use pull-based deployments for improved Kubernetes management](https://docs.gitlab.com/user/clusters/agent/)
|
|
43
|
-
* [Set up protected environments](https://docs.gitlab.com/ci/environments/protected_environments/)
|
|
68
|
+
```bash
|
|
69
|
+
kntic start [--screen]
|
|
70
|
+
```
|
|
44
71
|
|
|
45
|
-
|
|
72
|
+
| Option | Description |
|
|
73
|
+
|--------|-------------|
|
|
74
|
+
| `--screen` | Wrap the Docker Compose process in a GNU `screen` session. The session name is read from `KNTIC_PRJ_PREFIX` in `.kntic.env`, falling back to the current directory name. Skipped if already inside a screen session or if `screen` is not available. |
|
|
46
75
|
|
|
47
|
-
|
|
76
|
+
Runs:
|
|
77
|
+
```bash
|
|
78
|
+
docker compose -f kntic.yml --env-file .kntic.env up --build
|
|
79
|
+
```
|
|
48
80
|
|
|
49
|
-
|
|
81
|
+
---
|
|
50
82
|
|
|
51
|
-
|
|
83
|
+
### `kntic stop`
|
|
52
84
|
|
|
53
|
-
|
|
85
|
+
Stop KNTIC services via Docker Compose.
|
|
54
86
|
|
|
55
|
-
|
|
56
|
-
|
|
87
|
+
```bash
|
|
88
|
+
kntic stop
|
|
89
|
+
```
|
|
57
90
|
|
|
58
|
-
|
|
59
|
-
|
|
91
|
+
Runs:
|
|
92
|
+
```bash
|
|
93
|
+
docker compose -f kntic.yml --env-file .kntic.env stop
|
|
94
|
+
```
|
|
60
95
|
|
|
61
|
-
|
|
62
|
-
On some READMEs, you may see small images that convey metadata, such as whether or not all the tests are passing for the project. You can use Shields to add some to your README. Many services also have instructions for adding a badge.
|
|
96
|
+
---
|
|
63
97
|
|
|
64
|
-
|
|
65
|
-
Depending on what you are making, it can be a good idea to include screenshots or even a video (you'll frequently see GIFs rather than actual videos). Tools like ttygif can help, but check out Asciinema for a more sophisticated method.
|
|
98
|
+
### `kntic update`
|
|
66
99
|
|
|
67
|
-
|
|
68
|
-
|
|
100
|
+
Download the latest KNTIC bootstrap archive and update managed files.
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
kntic update [--lib-only] [--compose]
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
| Option | Description |
|
|
107
|
+
|--------|-------------|
|
|
108
|
+
| `--lib-only` | Update only `.kntic/lib/` (skip ADRs, hooks, and weights). |
|
|
109
|
+
| `--compose` | Also replace `kntic.yml` from the bootstrap template. Creates a backup at `kntic.yml.bak` before overwriting. |
|
|
110
|
+
|
|
111
|
+
**Default update scope** (without `--lib-only`):
|
|
69
112
|
|
|
70
|
-
|
|
71
|
-
|
|
113
|
+
| Path | Strategy |
|
|
114
|
+
|------|----------|
|
|
115
|
+
| `.kntic/lib/` | **Replaced** — cleared and re-extracted |
|
|
116
|
+
| `.kntic/adrs/` | **Replaced** — cleared and re-extracted |
|
|
117
|
+
| `.kntic/hooks/gia/internal/` | **Updated** — existing files overwritten, new files added, unlisted files preserved |
|
|
118
|
+
| `.kntic/hooks/gia/specific/` | **Bootstrap only** — extracted only if the directory does not already exist (user customizations are never overwritten) |
|
|
119
|
+
| `.kntic/gia/weights.json` | **Replaced** if present in the archive |
|
|
120
|
+
| `.kntic.env` | **Merged** — new variables from the template are appended with their comments; existing values are never overwritten |
|
|
121
|
+
| `KNTIC_VERSION` | Updated in `.kntic.env` to the latest version |
|
|
72
122
|
|
|
73
|
-
##
|
|
74
|
-
Tell people where they can go to for help. It can be any combination of an issue tracker, a chat room, an email address, etc.
|
|
123
|
+
## Environment Variables
|
|
75
124
|
|
|
76
|
-
|
|
77
|
-
If you have ideas for releases in the future, it is a good idea to list them in the README.
|
|
125
|
+
The `.kntic.env` file contains project configuration:
|
|
78
126
|
|
|
79
|
-
|
|
80
|
-
|
|
127
|
+
| Variable | Description |
|
|
128
|
+
|----------|-------------|
|
|
129
|
+
| `ANTHROPIC_API_KEY` | API key for Anthropic (used by the orchestrator engine) |
|
|
130
|
+
| `UID` | Host user ID for container user mapping |
|
|
131
|
+
| `GID` | Host group ID for container user mapping |
|
|
132
|
+
| `GITLAB_TOKEN` | GitLab personal access token (`glpat-*`) |
|
|
133
|
+
| `GIT_HOST` | Git server hostname (auto-detected from origin remote) |
|
|
134
|
+
| `GIT_REPO_PATH` | Repository path on the git server (auto-detected from origin remote) |
|
|
135
|
+
| `KNTIC_VERSION` | Bootstrap version (set automatically by `kntic init` / `kntic update`) |
|
|
136
|
+
| `KNTIC_PRJ_PREFIX` | Project prefix used for screen session naming in `kntic start --screen` |
|
|
81
137
|
|
|
82
|
-
|
|
138
|
+
## Services
|
|
83
139
|
|
|
84
|
-
|
|
140
|
+
Defined in `kntic.yml`:
|
|
85
141
|
|
|
86
|
-
|
|
87
|
-
|
|
142
|
+
| Service | Container | Image | Port |
|
|
143
|
+
|---------|-----------|-------|------|
|
|
144
|
+
| **Dashboard** | `control-dashboard` | `kntic/dashboard:latest` | `8002` |
|
|
145
|
+
| **Orchestrator** | `control-engine` | `nexus.kommune7.wien/kntic/kntic-engine:latest` | — |
|
|
146
|
+
|
|
147
|
+
Both services run as `${UID}:${GID}` (non-root) and use `.kntic.env` for environment configuration.
|
|
148
|
+
|
|
149
|
+
## Testing
|
|
150
|
+
|
|
151
|
+
```bash
|
|
152
|
+
npm test
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
Runs tests using the Node.js built-in test runner:
|
|
156
|
+
```bash
|
|
157
|
+
node --test src/**/*.test.js
|
|
158
|
+
```
|
|
88
159
|
|
|
89
160
|
## License
|
|
90
|
-
For open source projects, say how it is licensed.
|
|
91
161
|
|
|
92
|
-
|
|
93
|
-
If you have run out of energy or time for your project, put a note at the top of the README saying that development has slowed down or stopped completely. Someone may choose to fork your project or volunteer to step in as a maintainer or owner, allowing your project to keep going. You can also make an explicit request for maintainers.
|
|
162
|
+
[MIT](LICENSE)
|
package/package.json
CHANGED
package/src/commands/init.js
CHANGED
|
@@ -270,7 +270,46 @@ function interactiveEnvSetup(envPath) {
|
|
|
270
270
|
});
|
|
271
271
|
}
|
|
272
272
|
|
|
273
|
+
/**
|
|
274
|
+
* Run preflight checks before downloading the bootstrap archive.
|
|
275
|
+
* All checks are warnings only — they never throw or exit.
|
|
276
|
+
* Returns an array of warning strings (empty if all checks pass).
|
|
277
|
+
*/
|
|
278
|
+
function preflightChecks() {
|
|
279
|
+
const warnings = [];
|
|
280
|
+
|
|
281
|
+
// 1. Linux platform check
|
|
282
|
+
if (process.platform !== "linux") {
|
|
283
|
+
warnings.push(
|
|
284
|
+
`⚠ Non-Linux detected (${process.platform}) — KNTIC is designed for Linux, other platforms may have issues`
|
|
285
|
+
);
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
// 2. docker binary check
|
|
289
|
+
try {
|
|
290
|
+
execSync("which docker", { stdio: "ignore" });
|
|
291
|
+
} catch {
|
|
292
|
+
warnings.push("⚠ docker not found — required for `kntic start`");
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
// 3. screen binary check
|
|
296
|
+
try {
|
|
297
|
+
execSync("which screen", { stdio: "ignore" });
|
|
298
|
+
} catch {
|
|
299
|
+
warnings.push("⚠ screen not found — optional, needed for `kntic start --screen`");
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
for (const w of warnings) {
|
|
303
|
+
console.log(w);
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
return warnings;
|
|
307
|
+
}
|
|
308
|
+
|
|
273
309
|
async function init(options = {}) {
|
|
310
|
+
// Run preflight checks before anything else
|
|
311
|
+
preflightChecks();
|
|
312
|
+
|
|
274
313
|
// Resolve current version from the artifact metadata file
|
|
275
314
|
const artifactFilename = await fetchText(BOOTSTRAP_ARTIFACT_URL);
|
|
276
315
|
const version = extractVersion(artifactFilename);
|
|
@@ -317,3 +356,4 @@ module.exports.extractArchive = extractArchive;
|
|
|
317
356
|
module.exports.extractVersion = extractVersion;
|
|
318
357
|
module.exports.parseGitRemote = parseGitRemote;
|
|
319
358
|
module.exports.fillEnvValues = fillEnvValues;
|
|
359
|
+
module.exports.preflightChecks = preflightChecks;
|
|
@@ -7,7 +7,7 @@ const path = require("path");
|
|
|
7
7
|
const os = require("os");
|
|
8
8
|
const { execSync } = require("child_process");
|
|
9
9
|
|
|
10
|
-
const { extractArchive, extractVersion, parseGitRemote, fillEnvValues } = require("./init");
|
|
10
|
+
const { extractArchive, extractVersion, parseGitRemote, fillEnvValues, preflightChecks } = require("./init");
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
13
|
* Helper — create a tar.gz archive in `tmpDir` containing the given files.
|
|
@@ -28,6 +28,68 @@ function createTarball(tmpDir, files) {
|
|
|
28
28
|
return tarball;
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
+
describe("preflightChecks", () => {
|
|
32
|
+
let originalPlatform;
|
|
33
|
+
let logMessages;
|
|
34
|
+
let originalLog;
|
|
35
|
+
|
|
36
|
+
beforeEach(() => {
|
|
37
|
+
originalPlatform = Object.getOwnPropertyDescriptor(process, "platform");
|
|
38
|
+
originalLog = console.log;
|
|
39
|
+
logMessages = [];
|
|
40
|
+
console.log = (...args) => logMessages.push(args.join(" "));
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
afterEach(() => {
|
|
44
|
+
Object.defineProperty(process, "platform", originalPlatform);
|
|
45
|
+
console.log = originalLog;
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it("warns when platform is not linux", () => {
|
|
49
|
+
Object.defineProperty(process, "platform", { value: "darwin", configurable: true });
|
|
50
|
+
const warnings = preflightChecks();
|
|
51
|
+
const platformWarning = warnings.find((w) => w.includes("Non-Linux detected"));
|
|
52
|
+
assert.ok(platformWarning, "should warn about non-linux platform");
|
|
53
|
+
assert.ok(platformWarning.includes("darwin"));
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
it("does not warn about platform on linux", () => {
|
|
57
|
+
Object.defineProperty(process, "platform", { value: "linux", configurable: true });
|
|
58
|
+
const warnings = preflightChecks();
|
|
59
|
+
const platformWarning = warnings.find((w) => w.includes("Non-Linux detected"));
|
|
60
|
+
assert.equal(platformWarning, undefined, "should not warn on linux");
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
it("warns when docker is not found", () => {
|
|
64
|
+
// We test by checking the function handles missing binaries.
|
|
65
|
+
// On the test system docker may or may not exist, so we check the structure.
|
|
66
|
+
const warnings = preflightChecks();
|
|
67
|
+
// Each warning should be a string
|
|
68
|
+
for (const w of warnings) {
|
|
69
|
+
assert.equal(typeof w, "string");
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it("each warning is independent (all checks run)", () => {
|
|
74
|
+
Object.defineProperty(process, "platform", { value: "win32", configurable: true });
|
|
75
|
+
const warnings = preflightChecks();
|
|
76
|
+
// Platform warning must be present regardless of binary check results
|
|
77
|
+
const platformWarning = warnings.find((w) => w.includes("Non-Linux detected"));
|
|
78
|
+
assert.ok(platformWarning, "platform warning must be present");
|
|
79
|
+
assert.ok(platformWarning.includes("win32"));
|
|
80
|
+
// All warnings are logged to console
|
|
81
|
+
assert.equal(logMessages.length, warnings.length, "all warnings must be logged");
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
it("prints warnings to console.log", () => {
|
|
85
|
+
Object.defineProperty(process, "platform", { value: "freebsd", configurable: true });
|
|
86
|
+
const warnings = preflightChecks();
|
|
87
|
+
// At least the platform warning should be logged
|
|
88
|
+
const platformLog = logMessages.find((m) => m.includes("Non-Linux detected"));
|
|
89
|
+
assert.ok(platformLog, "platform warning must be printed via console.log");
|
|
90
|
+
});
|
|
91
|
+
});
|
|
92
|
+
|
|
31
93
|
describe("extractArchive", () => {
|
|
32
94
|
let tmpDir;
|
|
33
95
|
let destDir;
|