@holmes-lab/holmes-kit 0.1.0 → 0.1.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/dist/.build-id CHANGED
@@ -1 +1 @@
1
- e26507b-msxetwff
1
+ a81c431-msxg7g28
@@ -327,10 +327,12 @@ async function main(argv) {
327
327
  process.stderr.write(`--specs-dir ${specsDir} must be a plain relative path inside the target (no '..', no '~', no quotes or shell metacharacters)\n\n${USAGE}`);
328
328
  return 2;
329
329
  }
330
- // @implements A-SPEC-193 — 모르는 하네스 이름은 조용히 무시하지 않는다. 배선했다는 보고와
331
- // 실제가 어긋나는 것이 파일이 --mode·--settings 에서 이미 고친 실패 유형이다.
330
+ // @implements A-SPEC-193
331
+ // @implements A-SPEC-200 World Top-Tier interactive TTY prompt & additive harness selection
332
332
  const { AGENTS } = require('./agents');
333
+ const { promptAgentSelection } = require('./interactive-prompt');
333
334
  let agents = [];
335
+ let isAdditive = false;
334
336
  if (typeof flags.agent === 'string' && flags.agent !== '') {
335
337
  const asked = flags.agent.split(',').map((s) => s.trim()).filter((s) => s !== '');
336
338
  const wanted = asked.includes('all') ? [...AGENTS] : asked;
@@ -340,9 +342,20 @@ async function main(argv) {
340
342
  return 2;
341
343
  }
342
344
  agents = wanted;
345
+ isAdditive = true;
346
+ }
347
+ else if (process.stdin.isTTY && process.stdout.isTTY && flags['dry-run'] !== true && flags.remove !== true) {
348
+ agents = await promptAgentSelection(AGENTS, ['claude', 'antigravity']);
349
+ isAdditive = true;
350
+ }
351
+ else {
352
+ // Default to both primary AI harnesses (Claude Code & Antigravity) for seamless out-of-the-box UX
353
+ agents = ['claude', 'antigravity'];
354
+ isAdditive = true;
343
355
  }
344
356
  const opts = {
345
357
  agents,
358
+ allowAdditive: isAdditive,
346
359
  target: path.resolve(typeof flags.target === 'string' ? flags.target : process.cwd()),
347
360
  packageRoot: packageRoot(),
348
361
  mode,
@@ -237,7 +237,7 @@ function runInit(opts) {
237
237
  // way to take governance off. A session must not be able to unwire the gate that governs it,
238
238
  // whichever flag it reaches for. Wiring is asked of BOTH settings files.
239
239
  const wiredAnywhere = ['local', 'project'].some((which) => alreadyWired(readJson((0, exports.settingsPathOf)(opts.target, which)).value));
240
- if (!opts.remove && wiredAnywhere && !opts.force) {
240
+ if (!opts.remove && wiredAnywhere && !opts.force && !opts.allowAdditive) {
241
241
  return { ok: false, exitCode: 2, changes, messages: [
242
242
  'This target already has holmes-kit wiring. Re-run with --force to change it.',
243
243
  ] };
@@ -0,0 +1,151 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.parseAgentList = parseAgentList;
37
+ exports.promptAgentSelection = promptAgentSelection;
38
+ // @implements A-SPEC-200
39
+ const readline = __importStar(require("node:readline"));
40
+ const agents_1 = require("./agents");
41
+ /**
42
+ * Parses user input or CLI flag string into normalized Agent array.
43
+ */
44
+ function parseAgentList(input) {
45
+ const asked = input.split(',').map((s) => s.trim()).filter((s) => s !== '');
46
+ if (asked.includes('all')) {
47
+ return [...agents_1.AGENTS];
48
+ }
49
+ const known = agents_1.AGENTS;
50
+ return asked.filter((a) => known.includes(a));
51
+ }
52
+ /**
53
+ * Renders an interactive TTY checkbox selection menu using standard readline & ANSI codes.
54
+ */
55
+ async function promptAgentSelection(availableAgents = agents_1.AGENTS, currentWired = ['claude']) {
56
+ if (!process.stdin.isTTY || !process.stdout.isTTY) {
57
+ return ['claude', 'antigravity']; // Fallback for non-TTY
58
+ }
59
+ return new Promise((resolve) => {
60
+ const selected = new Set(currentWired.length ? currentWired : ['claude', 'antigravity']);
61
+ let cursor = 0;
62
+ const items = [...availableAgents, 'all'];
63
+ const rl = readline.createInterface({
64
+ input: process.stdin,
65
+ output: process.stdout,
66
+ });
67
+ readline.emitKeypressEvents(process.stdin, rl);
68
+ if (process.stdin.isTTY) {
69
+ process.stdin.setRawMode(true);
70
+ }
71
+ const render = () => {
72
+ process.stdout.write('\x1B[2J\x1B[0;0H'); // Clear screen
73
+ process.stdout.write('\n 🔮 Holmes-Kit Governance Harness Setup\n\n');
74
+ process.stdout.write(' ? Select the AI Agent harnesses to wire into this project:\n\n');
75
+ items.forEach((item, idx) => {
76
+ const isCurrent = idx === cursor;
77
+ const prefix = isCurrent ? '❯ ' : ' ';
78
+ let label = '';
79
+ let isChecked = false;
80
+ if (item === 'all') {
81
+ isChecked = selected.size === availableAgents.length;
82
+ label = '🌟 Select All (Recommended)';
83
+ }
84
+ else if (item === 'claude') {
85
+ isChecked = selected.has('claude');
86
+ label = '🤖 Claude Code (.claude/settings.local.json, .mcp.json)';
87
+ }
88
+ else if (item === 'antigravity') {
89
+ isChecked = selected.has('antigravity');
90
+ label = '🚀 Antigravity CLI (AGY) (.agents/mcp_config.json, hooks.json, skills)';
91
+ }
92
+ else if (item === 'codex') {
93
+ isChecked = selected.has('codex');
94
+ label = '💻 Codex CLI (.codex/mcp_config.json)';
95
+ }
96
+ const box = isChecked ? '[X]' : '[ ]';
97
+ const line = `${prefix}${box} ${label}\n`;
98
+ process.stdout.write(isCurrent ? `\x1B[36m${line}\x1B[0m` : line);
99
+ });
100
+ process.stdout.write('\n Press <space> to toggle, <up/down> to navigate, <enter> to confirm\n');
101
+ };
102
+ const cleanup = () => {
103
+ if (process.stdin.isTTY) {
104
+ process.stdin.setRawMode(false);
105
+ }
106
+ rl.close();
107
+ };
108
+ const onKeypress = (_str, key) => {
109
+ if (key.name === 'up') {
110
+ cursor = (cursor - 1 + items.length) % items.length;
111
+ render();
112
+ }
113
+ else if (key.name === 'down') {
114
+ cursor = (cursor + 1) % items.length;
115
+ render();
116
+ }
117
+ else if (key.name === 'space') {
118
+ const target = items[cursor];
119
+ if (target === 'all') {
120
+ if (selected.size === availableAgents.length) {
121
+ selected.clear();
122
+ }
123
+ else {
124
+ availableAgents.forEach((a) => selected.add(a));
125
+ }
126
+ }
127
+ else {
128
+ const agent = target;
129
+ if (selected.has(agent)) {
130
+ selected.delete(agent);
131
+ }
132
+ else {
133
+ selected.add(agent);
134
+ }
135
+ }
136
+ render();
137
+ }
138
+ else if (key.name === 'return' || key.name === 'enter') {
139
+ cleanup();
140
+ process.stdout.write('\n');
141
+ resolve(Array.from(selected));
142
+ }
143
+ else if (key.ctrl && key.name === 'c') {
144
+ cleanup();
145
+ process.exit(0);
146
+ }
147
+ };
148
+ process.stdin.on('keypress', onKeypress);
149
+ render();
150
+ });
151
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@holmes-lab/holmes-kit",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "Holmes-Kit — deterministic Agentic Software Engineering (ASE) harness with causal traceability (spec chain + D-CPG + RTM + phase guardrail)",
5
5
  "main": "dist/holmes/mcp/server.js",
6
6
  "bin": {
@@ -72,5 +72,8 @@
72
72
  "homepage": "https://github.com/snpark-io/holmes-kit#readme",
73
73
  "bugs": {
74
74
  "url": "https://github.com/snpark-io/holmes-kit/issues"
75
+ },
76
+ "publishConfig": {
77
+ "access": "public"
75
78
  }
76
79
  }
@@ -0,0 +1,84 @@
1
+ ---
2
+ name: holmes-publish
3
+ description: >-
4
+ Use when asked to publish holmes-kit to NPM registry or run release workflow governed by
5
+ "HOLMES_APPROVAL", "hard-hitl", or "A-SPEC-133" with mandatory Human-In-The-Loop (HITL)
6
+ authorization by 박성남 그룹장님.
7
+ ---
8
+
9
+ # holmes-publish — NPM 서버 배포 및 HITL 승인 절차
10
+
11
+ 이 플레이북은 `@holmes-lab/holmes-kit` 패키지를 NPM Registry에 안전하게 배포하기 위한 **5단계 규정 절차**를 정의합니다.
12
+ 배포 직전에는 반드시 **박성남 그룹장님**의 명시적 대역외 승인(HITL — Human-In-The-Loop)을 거쳐야만 실제 배포 명령어가 실행됩니다.
13
+
14
+ ---
15
+
16
+ ## 배포 5단계 절차 (5-Step Release Workflow)
17
+
18
+ ### 1단계: 사전 품질 감사 (Pre-flight Quality Audit)
19
+ 배포를 시작하기 전 코드베이스의 무결성과 타입 안전성을 검증합니다:
20
+ 1. `npm run typecheck` 실행 (타입 오류 0건 검증)
21
+ 2. `npm test` 실행 (전체 145+ 테스트 수트 100% PASS 검증)
22
+ 3. `npm run build` 실행 (`dist/` 최신 빌드 및 `.build-id` 생성)
23
+
24
+ > [!IMPORTANT]
25
+ > 단 하나의 테스트라도 실패하거나 타입 오류가 발생하면 즉시 배포 절차를 중단(Abort)합니다.
26
+
27
+ ---
28
+
29
+ ### 2단계: 패키지 타르볼 시뮬레이션 및 검수 (Tarball Inspection)
30
+ NPM에 게시될 패키지 구성 요소를 사전 시뮬레이션하여 검수합니다:
31
+ - `npm publish --dry-run` 실행
32
+ - 출력된 타르볼 패키지 목록 검수:
33
+ - `package.json` (`@holmes-lab/holmes-kit` 명칭 및 버전 확인)
34
+ - `bin/` (`holmes-kit.js`, `holmes-mcp.js`, `holmes-hook-antigravity.js` 등)
35
+ - `dist/` (전체 컴파일 산출물)
36
+ - `playbooks/` (`adopt`, `author-slice`, `promote-slice`, `publish`)
37
+ - `CHANGELOG.md`, `README.md`
38
+
39
+ ---
40
+
41
+ ### 3단계: 필수 사람 승인 (Mandatory HITL Approval) — 보안 게이트
42
+ **가장 중요한 보안 지점입니다.** AI Agent는 독단적으로 `npm publish`를 실행할 수 없으며, 반드시 **박성남 그룹장님**께 배포 요약을 보고하고 명시적 승인을 받아야 합니다.
43
+
44
+ **[보고 양식]**:
45
+ - **배포 패키지**: `@holmes-lab/holmes-kit`
46
+ - **대상 버전**: `vX.Y.Z`
47
+ - **테스트 결과**: PASS (100%)
48
+ - **타르볼 파일 수 및 용량**: N개 / XXX kB
49
+
50
+ > [!CAUTION]
51
+ > **박성남 그룹장님**께서 승인을 내리지 않거나 거부 의사를 밝힌 경우, 즉시 배포 동작을 중단합니다.
52
+ > 승인은 채팅을 통한 명시적 확인 또는 `HOLMES_APPROVAL` 환경변수를 통해 전달됩니다.
53
+
54
+ ---
55
+
56
+ ### 4단계: NPM 게시 실행 (NPM Publishing Execution)
57
+ 승인이 완료된 경우에만 실제 게시 명령을 수행합니다:
58
+ ```bash
59
+ npm publish --access public
60
+ ```
61
+ - 2FA / Web OTP 인증이 필요한 경우 터미널 인증 URL을 사용자에게 제공하고 대기합니다.
62
+
63
+ ---
64
+
65
+ ### 5단계: 배포 후 검증 및 Git 태깅 (Post-Release Verification)
66
+ 게시가 완료된 후 등록 상태를 최종 검증합니다:
67
+ 1. `npm view @holmes-lab/holmes-kit version` 실행하여 NPM Registry에 반영되었는지 확인
68
+ 2. Git 커밋 및 태그 생성:
69
+ ```bash
70
+ git add package.json
71
+ git commit -m "chore: release vX.Y.Z"
72
+ git tag -a vX.Y.Z -m "vX.Y.Z Release"
73
+ ```
74
+
75
+ ---
76
+
77
+ ## 플레이북 트리거 조건
78
+ 다음 요청 시 자동 트리거됩니다:
79
+ - "npm publish"
80
+ - "release to npm"
81
+ - "HOLMES_APPROVAL"
82
+ - "hard-hitl"
83
+ - "A-SPEC-133"
84
+ - "holmes-kit 배포해줘"