happyskills 0.9.0 → 0.9.1
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/CHANGELOG.md +5 -0
- package/package.json +1 -1
- package/src/commands/publish.js +35 -30
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/).
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.9.1] - 2026-03-10
|
|
11
|
+
|
|
12
|
+
### Changed
|
|
13
|
+
- Change `publish` to run full skill validation (SKILL.md + skill.json + cross-file rules) before publishing, blocking on errors with structured `VALIDATION_FAILED` output in JSON mode — replaces the previous ad-hoc manifest validation and frontmatter warnings
|
|
14
|
+
|
|
10
15
|
## [0.9.0] - 2026-03-10
|
|
11
16
|
|
|
12
17
|
### Added
|
package/package.json
CHANGED
package/src/commands/publish.js
CHANGED
|
@@ -1,9 +1,7 @@
|
|
|
1
|
-
const fs = require('fs')
|
|
2
1
|
const path = require('path')
|
|
3
2
|
const { error: { catch_errors, wrap_errors: e } } = require('puffy-core')
|
|
4
3
|
const { read_manifest } = require('../manifest/reader')
|
|
5
4
|
const { write_manifest } = require('../manifest/writer')
|
|
6
|
-
const { validate_manifest } = require('../manifest/validator')
|
|
7
5
|
const { require_token } = require('../auth/token_store')
|
|
8
6
|
const repos_api = require('../api/repos')
|
|
9
7
|
const workspaces_api = require('../api/workspaces')
|
|
@@ -15,11 +13,13 @@ const { find_project_root } = require('../config/paths')
|
|
|
15
13
|
const { read_lock, get_all_locked_skills } = require('../lock/reader')
|
|
16
14
|
const { write_lock, update_lock_skills } = require('../lock/writer')
|
|
17
15
|
const { hash_directory } = require('../lock/integrity')
|
|
18
|
-
const {
|
|
16
|
+
const { validate_skill_md } = require('../validation/skill_md_rules')
|
|
17
|
+
const { validate_skill_json } = require('../validation/skill_json_rules')
|
|
18
|
+
const { validate_cross } = require('../validation/cross_rules')
|
|
19
19
|
const { create_spinner } = require('../ui/spinner')
|
|
20
20
|
const { print_help, print_success, print_error, print_warn, print_hint, print_json, code } = require('../ui/output')
|
|
21
21
|
const { exit_with_error, UsageError, CliError } = require('../utils/errors')
|
|
22
|
-
const { EXIT_CODES
|
|
22
|
+
const { EXIT_CODES } = require('../constants')
|
|
23
23
|
|
|
24
24
|
const HELP_TEXT = `Usage: happyskills publish <skill-name> [options]
|
|
25
25
|
|
|
@@ -85,34 +85,39 @@ const run = (args) => catch_errors('Publish failed', async () => {
|
|
|
85
85
|
if (write_err) throw e('Failed to update manifest version', write_err)
|
|
86
86
|
}
|
|
87
87
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
const
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
88
|
+
// Run full validation — block on errors, show warnings
|
|
89
|
+
const dir_name = path.basename(dir)
|
|
90
|
+
const [md_err, md_data] = await validate_skill_md(dir, dir_name)
|
|
91
|
+
if (md_err) throw md_err
|
|
92
|
+
const [json_err, json_data] = await validate_skill_json(dir)
|
|
93
|
+
if (json_err) throw json_err
|
|
94
|
+
const [cross_err, cross_results] = await validate_cross(dir, md_data.frontmatter, json_data.manifest, md_data.content)
|
|
95
|
+
if (cross_err) throw cross_err
|
|
96
|
+
|
|
97
|
+
const all_results = [...md_data.results, ...json_data.results, ...cross_results]
|
|
98
|
+
const validation_errors = all_results.filter(r => r.severity === 'error')
|
|
99
|
+
const validation_warnings = all_results.filter(r => r.severity === 'warning')
|
|
100
|
+
|
|
101
|
+
if (validation_errors.length > 0) {
|
|
102
|
+
const { is_json_mode } = require('../state')
|
|
103
|
+
if (is_json_mode()) {
|
|
104
|
+
const structured_errors = validation_errors.map(({ severity, ...rest }) => rest)
|
|
105
|
+
print_json({
|
|
106
|
+
error: {
|
|
107
|
+
code: 'VALIDATION_FAILED',
|
|
108
|
+
message: `Skill failed validation with ${validation_errors.length} error(s). Fix these issues and try again.`,
|
|
109
|
+
exit_code: EXIT_CODES.ERROR,
|
|
110
|
+
validation_errors: structured_errors
|
|
111
|
+
}
|
|
112
|
+
})
|
|
113
|
+
return process.exit(EXIT_CODES.ERROR)
|
|
107
114
|
}
|
|
108
|
-
|
|
109
|
-
|
|
115
|
+
const error_msgs = validation_errors.map(r => `${r.file || 'General'}${r.field ? ` (${r.field})` : ''}: ${r.message}`)
|
|
116
|
+
throw new CliError(`Skill failed validation with ${validation_errors.length} error(s):\n ${error_msgs.join('\n ')}\n\nRun \`happyskills validate ${skill_name}\` for full details.`, EXIT_CODES.ERROR)
|
|
110
117
|
}
|
|
111
118
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
print_warn(warning)
|
|
115
|
-
}
|
|
119
|
+
for (const w of validation_warnings) {
|
|
120
|
+
print_warn(`${w.file || 'General'}${w.field ? ` (${w.field})` : ''}: ${w.message}`)
|
|
116
121
|
}
|
|
117
122
|
|
|
118
123
|
const spinner = create_spinner('Preparing to publish...')
|
|
@@ -199,7 +204,7 @@ const run = (args) => catch_errors('Publish failed', async () => {
|
|
|
199
204
|
commit: push_data?.commit || null,
|
|
200
205
|
bumped_from
|
|
201
206
|
}
|
|
202
|
-
if (
|
|
207
|
+
if (validation_warnings.length > 0) json_data.warnings = validation_warnings.map(w => w.message)
|
|
203
208
|
print_json({ data: json_data })
|
|
204
209
|
return
|
|
205
210
|
}
|