@hone-ai/cli 1.4.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.
Files changed (60) hide show
  1. package/bin/hone.js +2 -0
  2. package/hone-cli.js +4006 -0
  3. package/lib/README.md +119 -0
  4. package/lib/adversarial-negative-lint.js +149 -0
  5. package/lib/audit.js +156 -0
  6. package/lib/auto-detect.js +213 -0
  7. package/lib/autofix-guardrails.js +124 -0
  8. package/lib/branch-protection.js +256 -0
  9. package/lib/ci-classifier.js +150 -0
  10. package/lib/ci-failures.js +173 -0
  11. package/lib/claude-md-tokens.js +71 -0
  12. package/lib/compliance-check.js +62 -0
  13. package/lib/config-augment.js +133 -0
  14. package/lib/config-update.js +70 -0
  15. package/lib/dependency-audit.js +108 -0
  16. package/lib/derive-domain.js +185 -0
  17. package/lib/doc-registry.js +63 -0
  18. package/lib/doctor-admin-merge.js +185 -0
  19. package/lib/doctor-bind-default.js +118 -0
  20. package/lib/doctor-docs.js +205 -0
  21. package/lib/doctor-placeholders.js +144 -0
  22. package/lib/doctor-skill-staleness.js +122 -0
  23. package/lib/domain-skill-template.md +114 -0
  24. package/lib/editor-detect.js +169 -0
  25. package/lib/fast-track-ratify.js +133 -0
  26. package/lib/git-helpers.js +109 -0
  27. package/lib/hook-templates/pre-commit.sh +54 -0
  28. package/lib/hook-templates/pre-push.sh +72 -0
  29. package/lib/install-hooks.js +205 -0
  30. package/lib/knowledge-graph.js +188 -0
  31. package/lib/learnings-audit.js +254 -0
  32. package/lib/learnings-parse.js +331 -0
  33. package/lib/learnings-sync.js +75 -0
  34. package/lib/mcp-detect.js +154 -0
  35. package/lib/metrics-collect.js +214 -0
  36. package/lib/overlay-merge.js +267 -0
  37. package/lib/performance-analyzer.js +142 -0
  38. package/lib/pipeline-config.js +83 -0
  39. package/lib/pipeline-status.js +207 -0
  40. package/lib/pipeline-validate.js +322 -0
  41. package/lib/platform-detect.js +86 -0
  42. package/lib/platform-discover.js +334 -0
  43. package/lib/publish-learning.js +160 -0
  44. package/lib/python-install.js +84 -0
  45. package/lib/refresh-check.js +67 -0
  46. package/lib/refresh-knowledge.js +360 -0
  47. package/lib/rule-resolver.js +146 -0
  48. package/lib/security-scanner.js +168 -0
  49. package/lib/setup-grounding.js +138 -0
  50. package/lib/skill-assertions.js +276 -0
  51. package/lib/skill-audit-render.js +158 -0
  52. package/lib/skill-audit.js +391 -0
  53. package/lib/stack-detect.js +170 -0
  54. package/lib/stack-paths.js +285 -0
  55. package/lib/story-classifier-extract.js +203 -0
  56. package/lib/story-classifier.js +282 -0
  57. package/lib/sync-overwrite.js +47 -0
  58. package/lib/synthetic-pipeline.js +299 -0
  59. package/lib/validate-metadata.js +175 -0
  60. package/package.json +41 -0
@@ -0,0 +1,175 @@
1
+ 'use strict';
2
+ /**
3
+ * validate-metadata.js — SC-011 (Phase 11 of SC family).
4
+ * Pure helper that validates a `.github/pipeline/<STORY-ID>/metadata.yml`
5
+ * file against the JSON schema at
6
+ * `enterprise-assets/.github/schema/metadata.schema.json` (canonical) or
7
+ * an adopter-supplied override.
8
+ *
9
+ * Implements what SC-010 §10 documented (metadata.yml as wire protocol):
10
+ * 3+ agents read these files; without schema validation, typos like
11
+ * `fixFor` vs `fix_for` silently degrade to no-op behavior.
12
+ *
13
+ * Failure modes (all return structured result; never throw):
14
+ * - file missing → status: 'not-found' finding
15
+ * - malformed YAML → status: 'malformed-yaml' finding
16
+ * - schema file missing/unparseable → status: 'schema-error' finding
17
+ * - schema-validation errors → ERROR findings, one per ajv error
18
+ *
19
+ * Pure-helper-with-injected-IO style (Category B per cli/lib/README.md):
20
+ * - Reads files via fs (could be injected; kept simple for now)
21
+ * - Returns { findings, summary }
22
+ * - Never throws
23
+ *
24
+ * Issue: SC-011 — first JSON schema in the framework. Establishes the
25
+ * convention for any future schema (skill manifests, agent metadata,
26
+ * pipeline-config, etc.) per pipeline-integrity §15.
27
+ */
28
+
29
+ const fs = require('node:fs');
30
+ const path = require('node:path');
31
+
32
+ const KNOWN_SEVERITIES = ['ERROR', 'WARN', 'INFO'];
33
+
34
+ /**
35
+ * Validate a metadata.yml file against a JSON schema.
36
+ *
37
+ * @param {object} args
38
+ * @param {string} args.path path to metadata.yml
39
+ * @param {string} args.schemaPath path to JSON schema file
40
+ * @returns {{
41
+ * findings: Array<{severity: string, code: string, path: string, message: string}>,
42
+ * summary: { total: number, errors: number, warnings: number, info: number },
43
+ * filePath: string,
44
+ * }}
45
+ */
46
+ function validateMetadata({ path: filePath, schemaPath } = {}) {
47
+ const findings = [];
48
+
49
+ if (!filePath || typeof filePath !== 'string') {
50
+ return wrap([{ severity: 'ERROR', code: 'invalid-args', path: '', message: 'path is required' }]);
51
+ }
52
+ if (!schemaPath || typeof schemaPath !== 'string') {
53
+ return wrap([{ severity: 'ERROR', code: 'invalid-args', path: '', message: 'schemaPath is required' }]);
54
+ }
55
+
56
+ if (!fs.existsSync(filePath)) {
57
+ return wrap([{ severity: 'ERROR', code: 'file-not-found', path: filePath, message: `metadata file not found: ${filePath}` }]);
58
+ }
59
+ if (!fs.existsSync(schemaPath)) {
60
+ return wrap([{ severity: 'ERROR', code: 'schema-not-found', path: schemaPath, message: `schema file not found: ${schemaPath}` }]);
61
+ }
62
+
63
+ // Parse the schema
64
+ let schema;
65
+ try {
66
+ schema = JSON.parse(fs.readFileSync(schemaPath, 'utf8'));
67
+ } catch (e) {
68
+ return wrap([{ severity: 'ERROR', code: 'schema-error', path: schemaPath, message: `cannot parse schema: ${e.message}` }]);
69
+ }
70
+
71
+ // Parse the metadata file. Use JSON_SCHEMA to keep ISO timestamps as strings
72
+ // (default schema auto-converts to Date objects, which the JSON Schema
73
+ // type=["string","null"] declarations reject).
74
+ let parsed;
75
+ try {
76
+ const yaml = require('js-yaml');
77
+ parsed = yaml.load(fs.readFileSync(filePath, 'utf8'), { schema: yaml.JSON_SCHEMA });
78
+ } catch (e) {
79
+ return wrap([{ severity: 'ERROR', code: 'malformed-yaml', path: filePath, message: `cannot parse YAML: ${e.message}` }]);
80
+ }
81
+
82
+ // Compile + validate via ajv. Use the 2020-12 entry point because the
83
+ // schema uses Draft 2020-12 (`$schema: .../draft/2020-12/schema`).
84
+ let validate;
85
+ try {
86
+ const Ajv = require('ajv/dist/2020');
87
+ const ajv = new Ajv({ allErrors: true, strict: false });
88
+ validate = ajv.compile(schema);
89
+ } catch (e) {
90
+ return wrap([{ severity: 'ERROR', code: 'schema-error', path: schemaPath, message: `ajv compile failed: ${e.message}` }]);
91
+ }
92
+
93
+ const valid = validate(parsed);
94
+ if (!valid) {
95
+ for (const err of validate.errors || []) {
96
+ findings.push({
97
+ severity: 'ERROR',
98
+ code: err.keyword || 'validation',
99
+ path: err.instancePath || '/',
100
+ message: formatAjvError(err),
101
+ });
102
+ }
103
+ }
104
+
105
+ return wrap(findings, filePath);
106
+ }
107
+
108
+ function formatAjvError(err) {
109
+ const { keyword, instancePath, message, params } = err;
110
+ let detail = message || keyword;
111
+ if (params) {
112
+ if (params.missingProperty) detail = `missing required property '${params.missingProperty}'`;
113
+ else if (params.additionalProperty) detail = `unexpected property '${params.additionalProperty}'`;
114
+ else if (params.allowedValues) detail = `${message}: allowed [${params.allowedValues.join(', ')}]`;
115
+ }
116
+ return `${instancePath || '/'}: ${detail}`;
117
+ }
118
+
119
+ function wrap(findings, filePath) {
120
+ const summary = {
121
+ total: findings.length,
122
+ errors: findings.filter(f => f.severity === 'ERROR').length,
123
+ warnings: findings.filter(f => f.severity === 'WARN').length,
124
+ info: findings.filter(f => f.severity === 'INFO').length,
125
+ };
126
+ return { findings, summary, filePath: filePath || null };
127
+ }
128
+
129
+ /**
130
+ * Validate every metadata.yml under a pipeline directory.
131
+ *
132
+ * @param {object} args
133
+ * @param {string} args.repoRoot
134
+ * @param {string} args.schemaPath
135
+ * @returns {{
136
+ * files: Array<{ filePath, findings, summary }>,
137
+ * summary: { total, errors, warnings, info, fileCount, errorFileCount },
138
+ * }}
139
+ */
140
+ function validateAllMetadata({ repoRoot, schemaPath }) {
141
+ const pipelineDir = path.join(repoRoot, '.github/pipeline');
142
+ if (!fs.existsSync(pipelineDir)) {
143
+ return {
144
+ files: [],
145
+ summary: { total: 0, errors: 0, warnings: 0, info: 0, fileCount: 0, errorFileCount: 0 },
146
+ };
147
+ }
148
+ const stories = fs.readdirSync(pipelineDir).filter(d =>
149
+ fs.statSync(path.join(pipelineDir, d)).isDirectory()
150
+ );
151
+
152
+ const files = [];
153
+ for (const story of stories) {
154
+ const filePath = path.join(pipelineDir, story, 'metadata.yml');
155
+ if (!fs.existsSync(filePath)) continue;
156
+ files.push(validateMetadata({ path: filePath, schemaPath }));
157
+ }
158
+
159
+ const summary = {
160
+ total: files.reduce((s, f) => s + f.summary.total, 0),
161
+ errors: files.reduce((s, f) => s + f.summary.errors, 0),
162
+ warnings: files.reduce((s, f) => s + f.summary.warnings, 0),
163
+ info: files.reduce((s, f) => s + f.summary.info, 0),
164
+ fileCount: files.length,
165
+ errorFileCount: files.filter(f => f.summary.errors > 0).length,
166
+ };
167
+
168
+ return { files, summary };
169
+ }
170
+
171
+ module.exports = {
172
+ validateMetadata,
173
+ validateAllMetadata,
174
+ KNOWN_SEVERITIES,
175
+ };
package/package.json ADDED
@@ -0,0 +1,41 @@
1
+ {
2
+ "name": "@hone-ai/cli",
3
+ "version": "1.4.0",
4
+ "description": "Hone AI — Enterprise SDLC Pipeline CLI",
5
+ "main": "hone-cli.js",
6
+ "bin": {
7
+ "hone": "./bin/hone.js"
8
+ },
9
+ "files": [
10
+ "bin/",
11
+ "hone-cli.js",
12
+ "lib/",
13
+ "!lib/*.test.js"
14
+ ],
15
+ "scripts": {
16
+ "test": "echo \"No tests yet\" && exit 0",
17
+ "link": "npm link"
18
+ },
19
+ "dependencies": {
20
+ "ajv": "^8.20.0",
21
+ "axios": "^1.6.0",
22
+ "commander": "^11.0.0",
23
+ "js-yaml": "^4.1.0"
24
+ },
25
+ "engines": {
26
+ "node": ">=18.0.0"
27
+ },
28
+ "keywords": [
29
+ "hone",
30
+ "ai",
31
+ "sdlc",
32
+ "pipeline",
33
+ "enterprise",
34
+ "developer-tools"
35
+ ],
36
+ "author": "Hone AI",
37
+ "license": "UNLICENSED",
38
+ "publishConfig": {
39
+ "access": "public"
40
+ }
41
+ }