@hone-ai/cli 1.18.0 → 1.20.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.
@@ -235,10 +235,72 @@ function parsePlatformConfigPaths(yamlText) {
235
235
  return out;
236
236
  }
237
237
 
238
+ /**
239
+ * Parse adopter-discovered `platform.metadata_types.config[].path` entries
240
+ * from a `.pipeline-config.yml` text. HC-018: closes the classifier loop
241
+ * by feeding setup-time-discovered CONFIG metadata locations into
242
+ * `classifyPathsForRepo` so adopter platforms (Salesforce, NetSuite, dbt,
243
+ * Terraform, ...) classify as CONFIG without needing to be hardcoded
244
+ * into STACK_PATH_TABLE.
245
+ *
246
+ * The YAML shape emitted by cli/lib/config-augment.js is:
247
+ * platform:
248
+ * metadata_types:
249
+ * config:
250
+ * - { type: customObjects, path: "force-app/main/default/objects/", count: 18 }
251
+ *
252
+ * Some discover paths are comma-joined multi-paths
253
+ * (e.g. "force-app/main/default/classes, force-app/main/default/triggers")
254
+ * — those are split into individual entries.
255
+ *
256
+ * Returned values are raw filesystem paths (NOT regex). The caller
257
+ * anchors + escapes them before unioning into `extraConfigPaths`.
258
+ *
259
+ * @param {string} yamlText
260
+ * @returns {string[]} raw path strings discovered under metadata_types.config[]
261
+ */
262
+ function parsePlatformMetadataTypesConfigPaths(yamlText) {
263
+ if (typeof yamlText !== 'string' || yamlText.length === 0) return [];
264
+ const blockMatch = yamlText.match(/^platform:\s*\n((?:[ \t]+.*\n?)*)/m);
265
+ if (!blockMatch) return [];
266
+ const block = blockMatch[1];
267
+ // metadata_types: <body> — body lines indented strictly more than the key
268
+ const mtMatch = block.match(/^([ \t]*)metadata_types:\s*\n((?:\1[ \t]+.*\n?)*)/m);
269
+ if (!mtMatch) return [];
270
+ const mtBody = mtMatch[2];
271
+ // config: <body> under metadata_types — same indent-bounded scan
272
+ const cfgMatch = mtBody.match(/^([ \t]*)config:\s*\n((?:\1[ \t]+.*\n?)*)/m);
273
+ if (!cfgMatch) return [];
274
+ const cfgBody = cfgMatch[2];
275
+ // Match each inline-mapping item: `- { ..., path: "VALUE", ... }`
276
+ // (also accepts single-quoted and bare-token paths)
277
+ const out = [];
278
+ const re = /-\s*\{[^}]*\bpath:\s*(?:"([^"]+)"|'([^']+)'|([^,}\s]+))/g;
279
+ let m;
280
+ while ((m = re.exec(cfgBody)) !== null) {
281
+ const raw = (m[1] || m[2] || m[3] || '').trim();
282
+ if (!raw) continue;
283
+ // Discover code sometimes joins multiple paths with ", " — split them.
284
+ for (const piece of raw.split(/\s*,\s*/)) {
285
+ const v = piece.trim();
286
+ if (v) out.push(v);
287
+ }
288
+ }
289
+ return out;
290
+ }
291
+
292
+ /**
293
+ * Escape a literal filesystem path so it can be used as a RegExp pattern.
294
+ */
295
+ function escapeRegexLiteral(s) {
296
+ return String(s).replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
297
+ }
298
+
238
299
  /**
239
300
  * Higher-level classifier composing platform fingerprint detection +
240
- * adopter-supplied `.pipeline-config.yml platform.config_paths` + the
241
- * pure `classifyPathsForStack` core.
301
+ * adopter-supplied `.pipeline-config.yml platform.config_paths` +
302
+ * setup-discovered `platform.metadata_types.config[].path` (HC-018) +
303
+ * the pure `classifyPathsForStack` core.
242
304
  *
243
305
  * Pure-helper-with-injected-I/O shape (same as compliance-check.js):
244
306
  * caller wraps `fs.existsSync` and `fs.readFileSync` so the helper can
@@ -249,7 +311,12 @@ function parsePlatformConfigPaths(yamlText) {
249
311
  * @param {string} opts.repoRoot - absolute path to repo root
250
312
  * @param {(relativePath: string) => boolean} [opts.fileExists] - filesystem check
251
313
  * @param {(relativePath: string) => string|null} [opts.readFile] - file reader
252
- * @returns {{ category: string, detectedPlatforms: string[], configPathsUsed: string[] }}
314
+ * @returns {{
315
+ * category: string,
316
+ * detectedPlatforms: string[],
317
+ * configPathsUsed: string[],
318
+ * metadataConfigPathsUsed: string[]
319
+ * }}
253
320
  */
254
321
  function classifyPathsForRepo(opts = {}) {
255
322
  const { paths, repoRoot, fileExists, readFile } = opts;
@@ -261,19 +328,28 @@ function classifyPathsForRepo(opts = {}) {
261
328
  detectedPlatforms = detectPlatforms({ repoRoot, fileExists });
262
329
  } catch { /* defensive — if the module is unavailable, treat as no platforms */ }
263
330
 
264
- // Read adopter-supplied platform.config_paths from .pipeline-config.yml (best-effort)
331
+ // Read adopter-supplied platform.config_paths + discovered metadata_types
332
+ // from .pipeline-config.yml (best-effort)
265
333
  let configPathsUsed = [];
334
+ let metadataConfigPathsUsed = [];
266
335
  try {
267
336
  if (typeof readFile === 'function') {
268
337
  const yamlText = readFile('.pipeline-config.yml');
269
338
  if (typeof yamlText === 'string' && yamlText.length > 0) {
270
339
  configPathsUsed = parsePlatformConfigPaths(yamlText);
340
+ metadataConfigPathsUsed = parsePlatformMetadataTypesConfigPaths(yamlText);
271
341
  }
272
342
  }
273
343
  } catch { /* defensive — bad YAML / missing file is a no-op */ }
274
344
 
275
- const category = classifyPathsForStack(paths, { extraConfigPaths: configPathsUsed });
276
- return { category, detectedPlatforms, configPathsUsed };
345
+ // metadata_types paths are raw filesystem prefixes — anchor + escape
346
+ // so a discovered `force-app/main/default/objects/` is matched as a
347
+ // path prefix, not as a free-form regex.
348
+ const metadataPatterns = metadataConfigPathsUsed.map(p => '^' + escapeRegexLiteral(p));
349
+ const extraConfigPaths = configPathsUsed.concat(metadataPatterns);
350
+
351
+ const category = classifyPathsForStack(paths, { extraConfigPaths });
352
+ return { category, detectedPlatforms, configPathsUsed, metadataConfigPathsUsed };
277
353
  }
278
354
 
279
355
  module.exports = {
@@ -282,4 +358,6 @@ module.exports = {
282
358
  classifyPathsForStack,
283
359
  classifyPathsForRepo,
284
360
  parsePlatformConfigPaths,
361
+ parsePlatformMetadataTypesConfigPaths,
362
+ escapeRegexLiteral,
285
363
  };
@@ -19,6 +19,7 @@
19
19
  */
20
20
 
21
21
  const { execSync } = require('node:child_process');
22
+ const { gitEnv } = require('./git-env');
22
23
  const fs = require('node:fs');
23
24
  const path = require('node:path');
24
25
 
@@ -181,7 +182,7 @@ function extractRecentlyModifiedOverlap(repoRoot, touchedFiles, windowDays) {
181
182
  try {
182
183
  const out = execSync(
183
184
  `git log --since="${w} days ago" --format=%H -- ${JSON.stringify(rel)}`,
184
- { cwd: repoRoot, encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] }
185
+ { cwd: repoRoot, env: gitEnv(), encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] }
185
186
  ).trim();
186
187
  if (out) return true;
187
188
  } catch {
@@ -0,0 +1,78 @@
1
+ 'use strict';
2
+ /**
3
+ * verify-patch.js — HC-019n-followup-24 (pipeline-recovery condition 6).
4
+ *
5
+ * Condition 5 proved step_4's diff APPLIES. Condition 6 runs the adopter's test
6
+ * command against the applied diff and reports pass/fail — the first ground
7
+ * truth in the pipeline that the generated code does not BREAK the suite.
8
+ *
9
+ * The command runs inside a throwaway git WORKTREE checked out from HEAD, so the
10
+ * adopter's real tree (including uncommitted work) is never touched. This module
11
+ * is the pure half — resolving the command and interpreting an exit code into a
12
+ * verdict. The worktree lifecycle and process exec live in the CLI command,
13
+ * calling verdictFromRun() with the result, so this stays I/O-free and
14
+ * unit-testable.
15
+ *
16
+ * See .github/pipeline/HC-019n-followup-24/architect.md. NOTE the honest limit:
17
+ * a passing suite means "does not regress", NOT "the story is correctly
18
+ * implemented" — the existing tests may not cover the new behaviour.
19
+ */
20
+
21
+ /** Exit codes, matching the check-patch convention so scripts can branch. */
22
+ const EXIT = {
23
+ PASS: 0, // tests passed on the applied diff
24
+ NO_DIFF: 1, // no diff / fetch failure (condition-5 precondition unmet)
25
+ DOES_NOT_APPLY: 2, // diff did not apply
26
+ TESTS_FAILED: 3, // diff applied, tests failed
27
+ TIMEOUT: 4, // the command exceeded --timeout
28
+ };
29
+
30
+ /**
31
+ * Resolve which command to run.
32
+ *
33
+ * Priority: an explicit --command wins; else the adopter's configured
34
+ * ci.local_command; else the documented default `make ci`. Never an empty
35
+ * string (an empty command would "succeed" vacuously and report a false pass).
36
+ *
37
+ * @param {string|undefined} explicit --command flag
38
+ * @param {string|undefined} configured ci_local_command from run config
39
+ * @returns {string}
40
+ */
41
+ function resolveCommand(explicit, configured) {
42
+ const pick = (explicit && explicit.trim())
43
+ || (configured && configured.trim())
44
+ || 'make ci';
45
+ return pick;
46
+ }
47
+
48
+ /**
49
+ * Interpret a completed command run into a condition-6 verdict.
50
+ *
51
+ * @param {{ code: number|null, timedOut: boolean, stdout?: string, stderr?: string }} run
52
+ * @returns {{ verdict: 'pass'|'fail'|'timeout', exitCode: number, detail: string }}
53
+ */
54
+ function verdictFromRun(run) {
55
+ if (run.timedOut) {
56
+ return { verdict: 'timeout', exitCode: EXIT.TIMEOUT, detail: 'test command exceeded the timeout' };
57
+ }
58
+ if (run.code === 0) {
59
+ return { verdict: 'pass', exitCode: EXIT.PASS, detail: 'tests passed on the applied diff' };
60
+ }
61
+ return {
62
+ verdict: 'fail',
63
+ exitCode: EXIT.TESTS_FAILED,
64
+ detail: `tests failed (exit ${run.code})` + (lastMeaningfulLine(run) ? `: ${lastMeaningfulLine(run)}` : ''),
65
+ };
66
+ }
67
+
68
+ /** The most useful one-liner from a failed run's output, for the report. */
69
+ function lastMeaningfulLine({ stdout = '', stderr = '' }) {
70
+ const combined = `${stdout}\n${stderr}`.split('\n')
71
+ .map(l => l.trim())
72
+ .filter(Boolean);
73
+ // Prefer a line that names a failure; else the last non-empty line.
74
+ const failLine = combined.reverse().find(l => /fail|error|✗|not ok|assert/i.test(l));
75
+ return (failLine || combined[0] || '').slice(0, 200);
76
+ }
77
+
78
+ module.exports = { EXIT, resolveCommand, verdictFromRun, lastMeaningfulLine };
@@ -0,0 +1,141 @@
1
+ 'use strict';
2
+ /**
3
+ * verify-pr.js — HC-019n-followup-32 (pipeline-standards G4 §2, G3 functional half).
4
+ *
5
+ * The POST-emit-pr verification the recovery deferred. `check-patch` (cond 5) →
6
+ * `verify-patch` (cond 6, runs the tests) → `emit-pr` (cond 7, opens the PR) →
7
+ * **`verify-pr`** (this): once a real PR exists, run the real skill audit and the
8
+ * real CI gate against it — the two artifacts (step-5b-skill-audit.md,
9
+ * step-5c-ci.md) the server DAG can only MODEL, never execute (see
10
+ * HC-019n-followup-31: those steps are serverExecutable:false).
11
+ *
12
+ * This module is the PURE half — CI-gate mode resolution, `gh pr checks`
13
+ * interpretation, and the verdict — I/O-free and unit-testable. The gh/make-ci
14
+ * execution + artifact writes live in the CLI command.
15
+ *
16
+ * Unlike the code-reviewer prompt's step_5c (which DIAGNOSES + auto-fixes CI in a
17
+ * loop), verify-pr only VERIFIES and REPORTS a verdict — a CLI command states the
18
+ * truth; the operator (or a later closed loop) acts on it. NOTE the honest limit:
19
+ * a green CI gate means the checks passed, not that the change is correct.
20
+ */
21
+
22
+ /** Exit codes, extending the check/verify-patch convention. */
23
+ const EXIT = {
24
+ PASS: 0, // all required CI gates green (skill audit is informational)
25
+ NO_PR: 1, // could not resolve a PR / branch to verify
26
+ GATE_RED: 2, // a required CI gate is failing
27
+ GATE_PENDING: 3, // checks did not reach a terminal state within the timeout
28
+ ERROR: 4, // gh / command execution error
29
+ };
30
+
31
+ const GATE_MODES = ['github', 'local', 'both', 'none'];
32
+
33
+ /**
34
+ * Resolve the CI-gate mode. Precedence: explicit --mode > config ci.gate >
35
+ * default 'github' (backward compat, matching the step_5c prompt contract).
36
+ */
37
+ function resolveGateMode(explicit, configGate) {
38
+ const norm = (v) => {
39
+ const s = String(v == null ? '' : v).toLowerCase().trim();
40
+ return GATE_MODES.includes(s) ? s : null;
41
+ };
42
+ return norm(explicit) || norm(configGate) || 'github';
43
+ }
44
+
45
+ /**
46
+ * Interpret `gh pr checks <PR> --json name,state,bucket` output (an array).
47
+ * `bucket` is gh's own classification: pass | fail | pending | skipping | cancel.
48
+ *
49
+ * @param {Array<{name?:string, bucket?:string, state?:string, link?:string}>} rows
50
+ * @returns {{ total:number, passed:number, failed:number, pending:number,
51
+ * skipped:number, failures:string[], allTerminal:boolean, allGreen:boolean }}
52
+ */
53
+ function interpretGhChecks(rows) {
54
+ const list = Array.isArray(rows) ? rows : [];
55
+ let passed = 0, failed = 0, pending = 0, skipped = 0;
56
+ const failures = [];
57
+ for (const r of list) {
58
+ const bucket = String(r.bucket || r.state || '').toLowerCase();
59
+ if (bucket === 'pass') passed++;
60
+ else if (bucket === 'fail' || bucket === 'cancel') { failed++; failures.push(r.name || '(unnamed check)'); }
61
+ else if (bucket === 'skipping' || bucket === 'skipped') skipped++;
62
+ else pending++; // pending / queued / in_progress / unknown
63
+ }
64
+ return {
65
+ total: list.length,
66
+ passed, failed, pending, skipped, failures,
67
+ allTerminal: pending === 0,
68
+ // Green = at least one check, none failed, none still pending.
69
+ allGreen: list.length > 0 && failed === 0 && pending === 0,
70
+ };
71
+ }
72
+
73
+ /**
74
+ * Compute the overall CI-gate verdict from the per-mode results.
75
+ *
76
+ * @param {{ mode:string, gh?:object|null, local?:{code:number,timedOut:boolean}|null }} o
77
+ * @returns {{ verdict:'pass'|'fail'|'pending'|'disabled', exitCode:number, detail:string }}
78
+ */
79
+ function ciGateVerdict({ mode, gh, local }) {
80
+ if (mode === 'none') {
81
+ return { verdict: 'disabled', exitCode: EXIT.PASS, detail: 'ci.gate=none — CI verification explicitly disabled (audit row only)' };
82
+ }
83
+ const parts = [];
84
+ let anyFail = false, anyPending = false;
85
+
86
+ if (mode === 'github' || mode === 'both') {
87
+ if (!gh) { anyFail = true; parts.push('github: could not read checks'); }
88
+ else if (gh.failed > 0) { anyFail = true; parts.push(`github: ${gh.failed}/${gh.total} failing (${gh.failures.join(', ')})`); }
89
+ else if (!gh.allTerminal) { anyPending = true; parts.push(`github: ${gh.pending} check(s) still pending`); }
90
+ else if (gh.total === 0) { anyPending = true; parts.push('github: no checks reported yet'); }
91
+ else parts.push(`github: ${gh.passed}/${gh.total} green`);
92
+ }
93
+ if (mode === 'local' || mode === 'both') {
94
+ if (!local) { anyFail = true; parts.push('local: command did not run'); }
95
+ else if (local.timedOut) { anyPending = true; parts.push('local: command timed out'); }
96
+ else if (local.code !== 0) { anyFail = true; parts.push(`local: \`make ci\` exit ${local.code}`); }
97
+ else parts.push('local: passed');
98
+ }
99
+
100
+ if (anyFail) return { verdict: 'fail', exitCode: EXIT.GATE_RED, detail: parts.join(' | ') };
101
+ if (anyPending) return { verdict: 'pending', exitCode: EXIT.GATE_PENDING, detail: parts.join(' | ') };
102
+ return { verdict: 'pass', exitCode: EXIT.PASS, detail: parts.join(' | ') };
103
+ }
104
+
105
+ /**
106
+ * Render the step-5c-ci.md gate matrix with a truthful provenance column.
107
+ * @returns {string} markdown
108
+ */
109
+ function renderStep5cArtifact({ storyId, mode, prRef, gh, local, verdict }) {
110
+ const lines = [];
111
+ lines.push(`# Step 5c — CI Gate Verification (${storyId || 'unknown-story'})`);
112
+ lines.push('');
113
+ lines.push(`- **PR / branch:** ${prRef || '(unknown)'}`);
114
+ lines.push(`- **CI gate mode:** ${mode}`);
115
+ lines.push(`- **Verdict:** ${verdict.verdict.toUpperCase()} — ${verdict.detail}`);
116
+ lines.push(`- **Produced by:** \`hone verify-pr\` (real execution — NOT an LLM description; HC-019n-followup-32)`);
117
+ lines.push('');
118
+ lines.push('| Check | Result | Provenance |');
119
+ lines.push('|---|---|---|');
120
+ if (mode === 'none') {
121
+ lines.push('| (CI verification) | DISABLED | ci.gate=none |');
122
+ }
123
+ if ((mode === 'github' || mode === 'both') && gh) {
124
+ if (gh.total === 0) lines.push('| (github checks) | none reported | github |');
125
+ else {
126
+ lines.push(`| github checks | ${gh.passed} pass / ${gh.failed} fail / ${gh.pending} pending / ${gh.skipped} skip | github |`);
127
+ for (const f of gh.failures) lines.push(`| ❌ ${f} | FAIL | github |`);
128
+ }
129
+ }
130
+ if ((mode === 'local' || mode === 'both')) {
131
+ if (!local) lines.push('| local `make ci` | did not run | local |');
132
+ else lines.push(`| local \`make ci\` | ${local.timedOut ? 'TIMEOUT' : local.code === 0 ? 'PASS' : `FAIL (exit ${local.code})`} | ${local.code === 0 ? 'OPERATOR-VERIFIED-LOCAL' : 'local'} |`);
133
+ }
134
+ lines.push('');
135
+ lines.push('> Honest limit: a green CI gate means the checks passed, not that the change is correct.');
136
+ return lines.join('\n') + '\n';
137
+ }
138
+
139
+ module.exports = {
140
+ EXIT, GATE_MODES, resolveGateMode, interpretGhChecks, ciGateVerdict, renderStep5cArtifact,
141
+ };
package/mcp-server.js ADDED
@@ -0,0 +1,67 @@
1
+ 'use strict';
2
+ /**
3
+ * mcp-server.js — HC-COMM-012 (A1 P1): the Hone MCP server (stdio).
4
+ *
5
+ * A THIN bridge: it exposes the pipeline tool handlers (lib/mcp-tools.js, which shell
6
+ * out to the `hone` CLI) over the Model Context Protocol so Claude Code (and later
7
+ * any MCP editor) can drive the server-mode pipeline. It contains NO pipeline logic,
8
+ * NO prompts, NO agent chains — the moat stays server-side (architect memo HC-COMM-012).
9
+ *
10
+ * stdout is the MCP protocol channel — NEVER write logs to it; use stderr.
11
+ */
12
+ const { Server } = require('@modelcontextprotocol/sdk/server/index.js');
13
+ const { StdioServerTransport } = require('@modelcontextprotocol/sdk/server/stdio.js');
14
+ const { ListToolsRequestSchema, CallToolRequestSchema } = require('@modelcontextprotocol/sdk/types.js');
15
+ const { TOOLS, readHoneConfig } = require('./lib/mcp-tools');
16
+
17
+ function buildServer() {
18
+ let version = '0.0.0';
19
+ try { version = require('./package.json').version; } catch { /* keep default */ }
20
+ const server = new Server({ name: 'hone', version }, { capabilities: { tools: {} } });
21
+
22
+ server.setRequestHandler(ListToolsRequestSchema, async () => ({
23
+ tools: TOOLS.map((t) => ({
24
+ name: t.name,
25
+ description: t.description,
26
+ inputSchema: t.inputSchema,
27
+ annotations: { readOnlyHint: !!t.readOnly },
28
+ })),
29
+ }));
30
+
31
+ server.setRequestHandler(CallToolRequestSchema, async (req) => {
32
+ const tool = TOOLS.find((t) => t.name === req.params.name);
33
+ if (!tool) {
34
+ return { content: [{ type: 'text', text: `Unknown tool: ${req.params.name}` }], isError: true };
35
+ }
36
+ try {
37
+ const result = await tool.handler(req.params.arguments || {});
38
+ return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }], isError: !!result.isError };
39
+ } catch (e) {
40
+ return { content: [{ type: 'text', text: `Tool error: ${e && e.message ? e.message : String(e)}` }], isError: true };
41
+ }
42
+ });
43
+
44
+ return server;
45
+ }
46
+
47
+ async function start() {
48
+ const server = buildServer();
49
+ const transport = new StdioServerTransport();
50
+ await server.connect(transport);
51
+ // Preflight (stderr only — stdout is the protocol channel). Editor-agnostic: this
52
+ // is the same signal in Claude Code, Cursor, Windsurf, or any MCP client.
53
+ const cfg = readHoneConfig();
54
+ process.stderr.write(
55
+ `hone MCP server ready — api=${cfg.apiUrl}, token=${cfg.hasToken ? 'present' : 'MISSING (run `hone init --token <t>`)'}, ${TOOLS.length} tools. ` +
56
+ `Run hone_doctor if calls fail.\n`,
57
+ );
58
+ }
59
+
60
+ if (require.main === module) {
61
+ start().catch((e) => {
62
+ process.stderr.write(`hone MCP server failed to start: ${e && e.message ? e.message : String(e)}\n`);
63
+ process.exit(1);
64
+ });
65
+ }
66
+
67
+ module.exports = { buildServer, start };
package/package.json CHANGED
@@ -1,17 +1,19 @@
1
1
  {
2
2
  "name": "@hone-ai/cli",
3
- "version": "1.18.0",
3
+ "version": "1.20.0",
4
4
  "description": "Hone AI — Enterprise SDLC Pipeline CLI",
5
5
  "main": "hone-cli.js",
6
6
  "bin": {
7
- "hone": "./bin/hone.js"
7
+ "hone": "./bin/hone.js",
8
+ "hone-mcp": "./bin/hone-mcp.js"
8
9
  },
9
10
  "files": [
10
11
  "bin/",
11
12
  "hone-cli.js",
12
13
  "lib/",
13
14
  "!lib/*.test.js",
14
- "schema/"
15
+ "schema/",
16
+ "mcp-server.js"
15
17
  ],
16
18
  "scripts": {
17
19
  "test": "echo \"No tests yet\" && exit 0",
@@ -21,10 +23,11 @@
21
23
  "postinstall": "echo '\\n Hone AI CLI installed successfully.\\n Next: run `hone init --token <YOUR_TOKEN>` to configure.\\n Docs: https://github.com/subbareddyvani/hone-server\\n'"
22
24
  },
23
25
  "dependencies": {
26
+ "@modelcontextprotocol/sdk": "^1.30.0",
24
27
  "ajv": "^8.20.0",
25
- "axios": "^1.6.0",
28
+ "axios": "^1.20.0",
26
29
  "commander": "^11.0.0",
27
- "js-yaml": "^4.1.0"
30
+ "js-yaml": "^4.3.2"
28
31
  },
29
32
  "engines": {
30
33
  "node": ">=18.0.0"