@kiroku-solutions/kiroku-ai 0.1.3 → 0.2.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 +59 -33
- package/bin/kiroku-ai.mjs +231 -3
- package/package.json +3 -4
- package/src/cli.mjs +0 -420
- package/src/config.mjs +0 -83
- package/src/github.mjs +0 -75
- package/src/integrations.mjs +0 -104
- package/src/local-source.mjs +0 -46
- package/src/lockfile.mjs +0 -47
- package/src/prompts.mjs +0 -232
- package/src/resolver.mjs +0 -141
- package/src/scaffold.mjs +0 -130
- package/src/source.mjs +0 -19
- package/templates/kiroku-ai-sync.yml +0 -108
package/README.md
CHANGED
|
@@ -3,8 +3,23 @@
|
|
|
3
3
|
Interactive CLI that bootstraps **Kiroku AI Standards** ("AI Context as Code")
|
|
4
4
|
into any project. It asks a few questions about your stack, downloads only the
|
|
5
5
|
relevant skill files from the central [`Kiroku-Solutions/kiroku-ai-standards`](https://github.com/Kiroku-Solutions/kiroku-ai-standards)
|
|
6
|
-
repository into
|
|
7
|
-
GitHub Action.
|
|
6
|
+
repository into the folders your chosen AI tool expects, and wires up the
|
|
7
|
+
two-way sync GitHub Action.
|
|
8
|
+
|
|
9
|
+
## Private-by-design: the npm package is just a loader
|
|
10
|
+
|
|
11
|
+
The package published to npm contains **only a bootstrap loader** (`bin/`), not
|
|
12
|
+
the CLI source. On first run the loader:
|
|
13
|
+
|
|
14
|
+
1. Asks for a GitHub token **first** (env `KIROKU_AI_TOKEN`/`GH_TOKEN`, or
|
|
15
|
+
`~/.npmrc`, or an interactive prompt — saved to `~/.npmrc`).
|
|
16
|
+
2. Downloads the real CLI (`CLI/src/**`, `CLI/templates/**`, `CLI/package.json`)
|
|
17
|
+
from the private SSOT repo via the GitHub API.
|
|
18
|
+
3. Caches it under `~/.kiroku-ai/runtime/<ref>/` and runs `npm install` once.
|
|
19
|
+
4. Runs the CLI. Subsequent runs reuse the cache (no token needed) — pass
|
|
20
|
+
`KIROKU_AI_REFRESH=1` to force a re-download.
|
|
21
|
+
|
|
22
|
+
The CLI code and skills therefore never live on npm.
|
|
8
23
|
|
|
9
24
|
## Usage
|
|
10
25
|
|
|
@@ -30,27 +45,31 @@ pnpm --dir CLI start init
|
|
|
30
45
|
|
|
31
46
|
| Command | Description |
|
|
32
47
|
| ---------------- | --------------------------------------------------------------------------- |
|
|
33
|
-
| `init` | Guided stack builder; downloads skills
|
|
48
|
+
| `init` | Guided stack builder; downloads skills into the engine's folders. |
|
|
34
49
|
| `update` | Re-resolves the recorded stack, re-downloads, and prunes removed skills. |
|
|
35
|
-
| `env [tool]` | Switches the AI tool and
|
|
50
|
+
| `env [tool]` | Switches the AI tool: **migrates the folders** and regenerates its pointer. |
|
|
36
51
|
|
|
37
52
|
### AI environments
|
|
38
53
|
|
|
39
|
-
During `init` you pick the AI tool the project targets. The CLI
|
|
40
|
-
|
|
54
|
+
During `init` you pick the AI tool the project targets. The CLI installs into
|
|
55
|
+
**that tool's native folder convention** and generates the matching "pointer"
|
|
56
|
+
file so the tool auto-loads its skills. Tools without their own convention
|
|
57
|
+
(Cursor, Copilot, Windsurf) use the generic `.agents/` + `skills/` fallback.
|
|
41
58
|
|
|
42
|
-
| Tool |
|
|
43
|
-
| ------------- | ---------------------------------- |
|
|
44
|
-
| Claude Code | `CLAUDE.md` |
|
|
45
|
-
| Cursor | `.cursor/rules/kiroku-ai.mdc` |
|
|
46
|
-
| GitHub Copilot| `.github/copilot-instructions.md` |
|
|
47
|
-
| OpenCode | `AGENTS.md` |
|
|
48
|
-
| Antigravity | `
|
|
49
|
-
| Windsurf | `.windsurf/rules/kiroku-ai.md` |
|
|
59
|
+
| Tool | Pointer file | Agents folder | Skills folder |
|
|
60
|
+
| ------------- | ---------------------------------- | ------------------ | ------------------- |
|
|
61
|
+
| Claude Code | `CLAUDE.md` | `.claude/agents/` | `.claude/skills/` |
|
|
62
|
+
| Cursor | `.cursor/rules/kiroku-ai.mdc` | `.agents/` | `skills/` |
|
|
63
|
+
| GitHub Copilot| `.github/copilot-instructions.md` | `.agents/` | `skills/` |
|
|
64
|
+
| OpenCode | `AGENTS.md` | `.opencode/agents/`| `.opencode/skills/` |
|
|
65
|
+
| Antigravity | `GEMINI.md` | `.agent/agents/` | `.agent/skills/` |
|
|
66
|
+
| Windsurf | `.windsurf/rules/kiroku-ai.md` | `.agents/` | `skills/` |
|
|
50
67
|
|
|
51
68
|
Pointer files carry a `kiroku-ai:managed` marker — the CLI never overwrites or
|
|
52
69
|
deletes a hand-written file you already had. Switch tools anytime with
|
|
53
|
-
`kiroku-ai env
|
|
70
|
+
`kiroku-ai env <tool>`; the installed skill/agent folders are moved to the new
|
|
71
|
+
tool's convention automatically. The lockfile always lives in the engine-stable
|
|
72
|
+
`.kiroku-ai/` folder.
|
|
54
73
|
|
|
55
74
|
### Options
|
|
56
75
|
|
|
@@ -70,20 +89,27 @@ deletes a hand-written file you already had. Switch tools anytime with
|
|
|
70
89
|
| `KIROKU_AI_ORG` | Override the GitHub org (default: `Kiroku-Solutions`). |
|
|
71
90
|
| `KIROKU_AI_REPO` | Override the repo (default: `kiroku-ai-standards`). |
|
|
72
91
|
| `KIROKU_AI_REF` | Override the default git ref. |
|
|
73
|
-
| `KIROKU_AI_TOKEN` | Read token — **required
|
|
92
|
+
| `KIROKU_AI_TOKEN` | Read token — **required to fetch the private CLI + skills**. |
|
|
93
|
+
| `KIROKU_AI_REFRESH`| Force the loader to re-download the cached CLI runtime. |
|
|
74
94
|
| `KIROKU_AI_SOURCE` | Local path to an SSOT checkout — reads from disk instead of GitHub (for testing). |
|
|
75
95
|
|
|
76
96
|
## What it generates
|
|
77
97
|
|
|
98
|
+
Folder names depend on the chosen AI tool (see the table above). Example for
|
|
99
|
+
**Claude Code**:
|
|
100
|
+
|
|
78
101
|
```text
|
|
79
102
|
your-project/
|
|
80
|
-
├── .
|
|
81
|
-
│ ├──
|
|
82
|
-
│ ├──
|
|
83
|
-
│ ├──
|
|
84
|
-
│ ├──
|
|
85
|
-
│ └──
|
|
86
|
-
└──
|
|
103
|
+
├── .claude/
|
|
104
|
+
│ ├── skills/
|
|
105
|
+
│ │ ├── global/ frontend/ backend/ infra-and-db/ # downloaded skills (do not edit)
|
|
106
|
+
│ │ ├── proposals/ # propose skills company-wide
|
|
107
|
+
│ │ ├── local/ # project-only AI rules (never synced)
|
|
108
|
+
│ │ └── README.md
|
|
109
|
+
│ └── agents/ # agent personas (Security Auditor, …)
|
|
110
|
+
├── CLAUDE.md # pointer file (auto-loaded by the tool)
|
|
111
|
+
├── .kiroku-ai/lockfile.json # engine-stable; prevents duplicate installs
|
|
112
|
+
└── .github/workflows/kiroku-ai-sync.yml # two-way sync action
|
|
87
113
|
```
|
|
88
114
|
|
|
89
115
|
## How the stack maps to skills
|
|
@@ -101,18 +127,18 @@ The compatibility matrix lives in [`src/resolver.mjs`](./src/resolver.mjs). In s
|
|
|
101
127
|
## Architecture
|
|
102
128
|
|
|
103
129
|
```text
|
|
104
|
-
bin/kiroku-ai.mjs #
|
|
105
|
-
src/
|
|
106
|
-
cli.mjs # arg parsing + init orchestration
|
|
107
|
-
config.mjs # org/repo/ref constants (+ env overrides)
|
|
130
|
+
bin/kiroku-ai.mjs # PUBLISHED loader: token-first, fetches src/ from private GitHub, caches, runs
|
|
131
|
+
src/ # NOT published to npm — fetched at runtime from the SSOT repo
|
|
132
|
+
cli.mjs # arg parsing + init/update/env orchestration
|
|
133
|
+
config.mjs # org/repo/ref constants, CONFIG_DIR, token resolution (+ env overrides)
|
|
108
134
|
prompts.mjs # @clack/prompts guided flow & compatibility constraints
|
|
109
135
|
resolver.mjs # stack -> skill selectors -> repo blob paths
|
|
110
136
|
source.mjs # dispatch: GitHub or local filesystem (KIROKU_AI_SOURCE)
|
|
111
137
|
github.mjs # tree listing + raw file download (public/private)
|
|
112
138
|
local-source.mjs # read skills/agents from a local SSOT checkout (testing)
|
|
113
|
-
scaffold.mjs # write
|
|
114
|
-
integrations.mjs #
|
|
115
|
-
lockfile.mjs # read/write/guard lockfile.json
|
|
139
|
+
scaffold.mjs # write engine folders, governance, inject workflow, prune, migrateLayout
|
|
140
|
+
integrations.mjs # per-engine pointer + agentsDir/skillsDir (CLAUDE.md, AGENTS.md, GEMINI.md, ...)
|
|
141
|
+
lockfile.mjs # read/write/guard .kiroku-ai/lockfile.json
|
|
116
142
|
templates/
|
|
117
143
|
kiroku-ai-sync.yml # workflow injected into consumer projects
|
|
118
144
|
test/ # node:test suite (run: pnpm test)
|
|
@@ -140,9 +166,9 @@ mkdir /tmp/demo-app && cd /tmp/demo-app
|
|
|
140
166
|
KIROKU_AI_SOURCE=/path/to/kiroku-ai-standards \
|
|
141
167
|
node /path/to/kiroku-ai-standards/CLI/bin/kiroku-ai.mjs init
|
|
142
168
|
|
|
143
|
-
# 3. Inspect what came down
|
|
144
|
-
ls -R .
|
|
145
|
-
cat .ai
|
|
169
|
+
# 3. Inspect what came down (folder depends on the AI tool you picked)
|
|
170
|
+
ls -R .claude .opencode .agent .agents skills 2>/dev/null
|
|
171
|
+
cat .kiroku-ai/lockfile.json
|
|
146
172
|
```
|
|
147
173
|
|
|
148
174
|
PowerShell:
|
package/bin/kiroku-ai.mjs
CHANGED
|
@@ -1,8 +1,236 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
2
|
+
/**
|
|
3
|
+
* kiroku-ai — bootstrap loader.
|
|
4
|
+
*
|
|
5
|
+
* The published npm package contains ONLY this file (plus package.json). The
|
|
6
|
+
* real CLI implementation is never shipped to npm; instead this loader:
|
|
7
|
+
*
|
|
8
|
+
* 1. Asks for a GitHub token FIRST (env → ~/.npmrc → interactive prompt),
|
|
9
|
+
* saving it to ~/.npmrc for next time.
|
|
10
|
+
* 2. Downloads the CLI source (`CLI/src/**`, `CLI/templates/**`,
|
|
11
|
+
* `CLI/package.json`) from the private SSOT repo via the GitHub API.
|
|
12
|
+
* 3. Caches it under ~/.kiroku-ai/runtime/<ref>/ and runs `npm install` once
|
|
13
|
+
* so its dependencies (@clack/prompts, picocolors) resolve.
|
|
14
|
+
* 4. Dynamically imports the cached `src/cli.mjs` and runs it.
|
|
15
|
+
*
|
|
16
|
+
* When run from a full checkout of this repo (dev/CI), the sibling `../src`
|
|
17
|
+
* exists and is used directly — no token or network required.
|
|
18
|
+
*/
|
|
3
19
|
|
|
4
|
-
|
|
5
|
-
|
|
20
|
+
import { spawn } from 'node:child_process';
|
|
21
|
+
import { existsSync } from 'node:fs';
|
|
22
|
+
import { mkdir, readFile, writeFile, appendFile } from 'node:fs/promises';
|
|
23
|
+
import { createInterface } from 'node:readline';
|
|
24
|
+
import os from 'node:os';
|
|
25
|
+
import { dirname, join, resolve } from 'node:path';
|
|
26
|
+
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
27
|
+
|
|
28
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
29
|
+
|
|
30
|
+
const ORG = process.env.KIROKU_AI_ORG ?? 'Kiroku-Solutions';
|
|
31
|
+
const REPO = process.env.KIROKU_AI_REPO ?? 'kiroku-ai-standards';
|
|
32
|
+
const REF = process.env.KIROKU_AI_REF ?? 'main';
|
|
33
|
+
const REFRESH = !!process.env.KIROKU_AI_REFRESH;
|
|
34
|
+
|
|
35
|
+
const API = 'https://api.github.com';
|
|
36
|
+
const CACHE_ROOT = join(os.homedir(), '.kiroku-ai', 'runtime');
|
|
37
|
+
// Subtree of the SSOT repo that holds the CLI, mapped onto the cache root.
|
|
38
|
+
const SUBTREE = 'CLI/';
|
|
39
|
+
|
|
40
|
+
/** Run the inner CLI from a given src directory. */
|
|
41
|
+
async function runFrom(srcDir, argv) {
|
|
42
|
+
const mod = await import(pathToFileURL(join(srcDir, 'cli.mjs')).href);
|
|
43
|
+
return mod.run(argv);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// ---------------------------------------------------------------------------
|
|
47
|
+
// Token handling (token first)
|
|
48
|
+
// ---------------------------------------------------------------------------
|
|
49
|
+
|
|
50
|
+
function tokenFromNpmrc(content) {
|
|
51
|
+
const match = content.match(
|
|
52
|
+
/\/\/(?:npm\.pkg\.github\.com|api\.github\.com)\/:_authToken=([^\s]+)/,
|
|
53
|
+
);
|
|
54
|
+
return match?.[1] ?? null;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
async function resolveToken() {
|
|
58
|
+
if (process.env.KIROKU_AI_TOKEN) return process.env.KIROKU_AI_TOKEN;
|
|
59
|
+
if (process.env.GH_TOKEN) return process.env.GH_TOKEN;
|
|
60
|
+
for (const rc of [join(process.cwd(), '.npmrc'), join(os.homedir(), '.npmrc')]) {
|
|
61
|
+
try {
|
|
62
|
+
if (existsSync(rc)) {
|
|
63
|
+
const t = tokenFromNpmrc(await readFile(rc, 'utf8'));
|
|
64
|
+
if (t) return t;
|
|
65
|
+
}
|
|
66
|
+
} catch {
|
|
67
|
+
/* ignore unreadable .npmrc */
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/** Prompt for a secret without echoing it to the terminal. */
|
|
74
|
+
function promptHidden(question) {
|
|
75
|
+
return new Promise((res) => {
|
|
76
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout, terminal: true });
|
|
77
|
+
const onData = (char) => {
|
|
78
|
+
// Re-clear the line so typed characters never render.
|
|
79
|
+
const s = char.toString('utf8');
|
|
80
|
+
if (s === '\n' || s === '\r' || s === '') return;
|
|
81
|
+
process.stdout.clearLine?.(0);
|
|
82
|
+
process.stdout.cursorTo?.(0);
|
|
83
|
+
process.stdout.write(question);
|
|
84
|
+
};
|
|
85
|
+
process.stdin.on('data', onData);
|
|
86
|
+
rl.question(question, (answer) => {
|
|
87
|
+
process.stdin.off('data', onData);
|
|
88
|
+
process.stdout.write('\n');
|
|
89
|
+
rl.close();
|
|
90
|
+
res(answer.trim());
|
|
91
|
+
});
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
async function saveTokenToNpmrc(token) {
|
|
96
|
+
try {
|
|
97
|
+
await appendFile(join(os.homedir(), '.npmrc'), `\n//api.github.com/:_authToken=${token}\n`, 'utf8');
|
|
98
|
+
} catch {
|
|
99
|
+
/* non-fatal: token still used for this run via env */
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
async function ensureToken() {
|
|
104
|
+
let token = await resolveToken();
|
|
105
|
+
if (token) return token;
|
|
106
|
+
|
|
107
|
+
process.stdout.write(
|
|
108
|
+
'\nKiroku AI Standards — authentication required to fetch the private CLI.\n' +
|
|
109
|
+
'Enter a GitHub token with read access to the SSOT repo (input hidden):\n',
|
|
110
|
+
);
|
|
111
|
+
token = await promptHidden('Token: ');
|
|
112
|
+
if (!token) {
|
|
113
|
+
console.error('No token provided. Aborting.');
|
|
114
|
+
process.exit(1);
|
|
115
|
+
}
|
|
116
|
+
await saveTokenToNpmrc(token);
|
|
117
|
+
process.stdout.write('Token saved to ~/.npmrc.\n');
|
|
118
|
+
return token;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// ---------------------------------------------------------------------------
|
|
122
|
+
// GitHub download
|
|
123
|
+
// ---------------------------------------------------------------------------
|
|
124
|
+
|
|
125
|
+
function ghHeaders(token, accept) {
|
|
126
|
+
return {
|
|
127
|
+
Accept: accept,
|
|
128
|
+
'User-Agent': 'kiroku-ai-loader',
|
|
129
|
+
'X-GitHub-Api-Version': '2022-11-28',
|
|
130
|
+
Authorization: `Bearer ${token}`,
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
async function ghJson(url, token, accept = 'application/vnd.github+json') {
|
|
135
|
+
const res = await fetch(url, { headers: ghHeaders(token, accept) });
|
|
136
|
+
if (!res.ok) {
|
|
137
|
+
const hint =
|
|
138
|
+
res.status === 401 || res.status === 403
|
|
139
|
+
? ' (bad/expired token or rate-limited)'
|
|
140
|
+
: res.status === 404
|
|
141
|
+
? ' (repo/ref not found or token lacks access)'
|
|
142
|
+
: '';
|
|
143
|
+
throw new Error(`GitHub ${res.status} ${res.statusText}${hint} — ${url}`);
|
|
144
|
+
}
|
|
145
|
+
return res;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
async function fetchTree(token) {
|
|
149
|
+
const url = `${API}/repos/${ORG}/${REPO}/git/trees/${encodeURIComponent(REF)}?recursive=1`;
|
|
150
|
+
const data = await (await ghJson(url, token)).json();
|
|
151
|
+
if (data.truncated) throw new Error('GitHub tree truncated; cannot bootstrap CLI reliably.');
|
|
152
|
+
return data;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
async function downloadBlob(repoPath, token) {
|
|
156
|
+
const url = `${API}/repos/${ORG}/${REPO}/contents/${repoPath
|
|
157
|
+
.split('/')
|
|
158
|
+
.map(encodeURIComponent)
|
|
159
|
+
.join('/')}?ref=${encodeURIComponent(REF)}`;
|
|
160
|
+
const res = await ghJson(url, token, 'application/vnd.github.raw+json');
|
|
161
|
+
return res.text();
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/** Map a repo path under CLI/ to its path inside the runtime cache dir. */
|
|
165
|
+
function cacheRelPath(repoPath) {
|
|
166
|
+
return repoPath.slice(SUBTREE.length);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
async function downloadCli(runtimeDir, token) {
|
|
170
|
+
const tree = await fetchTree(token);
|
|
171
|
+
const wanted = (tree.tree ?? []).filter(
|
|
172
|
+
(n) =>
|
|
173
|
+
n.type === 'blob' &&
|
|
174
|
+
(n.path.startsWith(`${SUBTREE}src/`) ||
|
|
175
|
+
n.path.startsWith(`${SUBTREE}templates/`) ||
|
|
176
|
+
n.path === `${SUBTREE}package.json`),
|
|
177
|
+
);
|
|
178
|
+
if (wanted.length === 0) {
|
|
179
|
+
throw new Error(`No CLI files found under ${SUBTREE} at ${ORG}/${REPO}@${REF}.`);
|
|
180
|
+
}
|
|
181
|
+
for (const node of wanted) {
|
|
182
|
+
const content = await downloadBlob(node.path, token);
|
|
183
|
+
const dest = join(runtimeDir, cacheRelPath(node.path));
|
|
184
|
+
await mkdir(dirname(dest), { recursive: true });
|
|
185
|
+
await writeFile(dest, content, 'utf8');
|
|
186
|
+
}
|
|
187
|
+
await writeFile(join(runtimeDir, '.kiroku-meta.json'), JSON.stringify({ sha: tree.sha, ref: REF }) + '\n', 'utf8');
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
function npmInstall(cwd) {
|
|
191
|
+
return new Promise((res, rej) => {
|
|
192
|
+
const npm = process.platform === 'win32' ? 'npm.cmd' : 'npm';
|
|
193
|
+
const child = spawn(
|
|
194
|
+
npm,
|
|
195
|
+
['install', '--omit=dev', '--no-audit', '--no-fund', '--loglevel=error'],
|
|
196
|
+
{ cwd, stdio: 'inherit', shell: process.platform === 'win32' },
|
|
197
|
+
);
|
|
198
|
+
child.on('error', rej);
|
|
199
|
+
child.on('close', (code) => (code === 0 ? res() : rej(new Error(`npm install exited ${code}`))));
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// ---------------------------------------------------------------------------
|
|
204
|
+
// Main
|
|
205
|
+
// ---------------------------------------------------------------------------
|
|
206
|
+
|
|
207
|
+
async function main(argv) {
|
|
208
|
+
// Dev / full-checkout fast path: run the real source directly, no network.
|
|
209
|
+
const localSrc = resolve(__dirname, '..', 'src');
|
|
210
|
+
if (existsSync(join(localSrc, 'cli.mjs'))) {
|
|
211
|
+
return runFrom(localSrc, argv);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
const runtimeDir = join(CACHE_ROOT, REF.replace(/[^a-zA-Z0-9._-]/g, '_'));
|
|
215
|
+
const cachedCli = join(runtimeDir, 'src', 'cli.mjs');
|
|
216
|
+
const cacheValid = existsSync(cachedCli) && existsSync(join(runtimeDir, 'node_modules')) && !REFRESH;
|
|
217
|
+
|
|
218
|
+
if (!cacheValid) {
|
|
219
|
+
const token = await ensureToken();
|
|
220
|
+
process.env.KIROKU_AI_TOKEN = token; // hand off to the inner CLI
|
|
221
|
+
|
|
222
|
+
process.stdout.write(`Fetching kiroku-ai CLI from ${ORG}/${REPO}@${REF}…\n`);
|
|
223
|
+
await mkdir(runtimeDir, { recursive: true });
|
|
224
|
+
await downloadCli(runtimeDir, token);
|
|
225
|
+
|
|
226
|
+
process.stdout.write('Installing CLI dependencies (first run only)…\n');
|
|
227
|
+
await npmInstall(runtimeDir);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
return runFrom(join(runtimeDir, 'src'), argv);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
main(process.argv.slice(2)).catch((err) => {
|
|
6
234
|
console.error(err?.stack || String(err));
|
|
7
235
|
process.exit(1);
|
|
8
236
|
});
|
package/package.json
CHANGED
|
@@ -1,15 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kiroku-solutions/kiroku-ai",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Interactive CLI to bootstrap Kiroku AI Standards (AI Context as Code) into any project.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"kiroku-ai": "./bin/kiroku-ai.mjs"
|
|
8
8
|
},
|
|
9
|
+
"comment:files": "Only the bootstrap loader is published to npm. The real CLI (src/, templates/) is fetched from the private SSOT repo at runtime; deps below are needed by that fetched runtime.",
|
|
9
10
|
"files": [
|
|
10
|
-
"bin"
|
|
11
|
-
"src",
|
|
12
|
-
"templates"
|
|
11
|
+
"bin"
|
|
13
12
|
],
|
|
14
13
|
"scripts": {
|
|
15
14
|
"start": "node ./bin/kiroku-ai.mjs",
|