@hone-ai/cli 1.17.0 → 1.19.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/hone-cli.js +1022 -34
- package/lib/architect-config.js +121 -0
- package/lib/ci-gate-chooser.js +267 -0
- package/lib/doctor-admin-merge.js +3 -2
- package/lib/doctor-architecture.js +213 -0
- package/lib/eval-evidence.js +213 -0
- package/lib/git-env.js +94 -0
- package/lib/git-helpers.js +3 -2
- package/lib/pipeline-config.js +80 -0
- package/lib/pipeline-status.js +12 -1
- package/lib/refresh-knowledge.js +7 -0
- package/lib/release-review-cache.js +162 -0
- package/lib/release-review-config.js +4 -2
- package/lib/schedule-cron.js +141 -0
- package/lib/skill-eval-runner.js +667 -0
- package/lib/stack-paths.js +84 -6
- package/lib/story-classifier-extract.js +2 -1
- package/package.json +3 -1
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
/**
|
|
3
|
+
* schedule-cron.js — HC-054f cron-hour analysis for `hone schedule install`.
|
|
4
|
+
*
|
|
5
|
+
* Adopters who install a workflow with a cron like "0 2 * * *" (2 AM
|
|
6
|
+
* UTC) almost always want Night Shift mode (--overnight) because a
|
|
7
|
+
* 2 AM run is by definition an overnight batch. Pre-HC-054f the CLI
|
|
8
|
+
* never passed --overnight to queue-stories so the scheduled workflow
|
|
9
|
+
* silently ran without auto-approve / longer timeouts / 25-story cap.
|
|
10
|
+
*
|
|
11
|
+
* **Timezone — load-bearing**: GitHub Actions cron runs in **UTC** by
|
|
12
|
+
* default. This helper analyzes the cron's hour field AS UTC. It does
|
|
13
|
+
* NOT translate to the adopter's local timezone. Adopters in non-UTC
|
|
14
|
+
* timezones who want overnight semantics in THEIR LOCAL TIME must
|
|
15
|
+
* write the cron's UTC equivalent (e.g., Tokyo adopter wanting
|
|
16
|
+
* 2 AM JST should write `"0 17 * * *"` because 2 AM JST = 17 UTC the
|
|
17
|
+
* previous day). Pass-1 review HIGH #2 caught the original docstring
|
|
18
|
+
* + roadmap incorrectly described the window as "local time" — this
|
|
19
|
+
* was misleading.
|
|
20
|
+
*
|
|
21
|
+
* HC-054f: when the cron expression's hour (UTC) is a LITERAL single
|
|
22
|
+
* value within the 18:00–05:59 UTC window, default --overnight=true.
|
|
23
|
+
* Ambiguous hour fields (wildcard, range, step) and out-of-window
|
|
24
|
+
* hours (06:00–17:59 UTC) DO NOT auto-default — adopter must
|
|
25
|
+
* explicitly opt in via --overnight yes.
|
|
26
|
+
*
|
|
27
|
+
* Window choice: [18, 23] ∪ [0, 5] UTC. 06:00 UTC itself is NOT
|
|
28
|
+
* overnight — a `"0 6 * * *"` cron is a morning queue, not an
|
|
29
|
+
* overnight one. The 5:59:59 UTC boundary matches "wake before 6 AM
|
|
30
|
+
* UTC" intuition. Adopters in distant timezones (JST, AEST, PST etc.)
|
|
31
|
+
* may need to translate; the CLI surfaces the UTC interpretation in
|
|
32
|
+
* its decision-reporting output so the adopter sees what the helper
|
|
33
|
+
* actually decided.
|
|
34
|
+
*
|
|
35
|
+
* Pure helper. No I/O. Unit-tested at cli/lib/schedule-cron.test.js.
|
|
36
|
+
*/
|
|
37
|
+
|
|
38
|
+
const OVERNIGHT_HOURS = Object.freeze(
|
|
39
|
+
Array.from({ length: 24 }, (_, h) => h).filter(h => h >= 18 || h < 6),
|
|
40
|
+
);
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Analyze a cron expression to determine whether the schedule falls
|
|
44
|
+
* in the overnight window.
|
|
45
|
+
*
|
|
46
|
+
* @param {string} cronExpr — 5-field cron expression
|
|
47
|
+
* @returns {{ overnight: boolean, hour: number|null, reason: string }}
|
|
48
|
+
* overnight: true ONLY when the hour field is a literal single value
|
|
49
|
+
* within OVERNIGHT_HOURS.
|
|
50
|
+
* hour: the parsed hour (0-23) when literal; null for ambiguous.
|
|
51
|
+
* reason: short human-readable explanation for the CLI output.
|
|
52
|
+
*/
|
|
53
|
+
function analyzeCron(cronExpr) {
|
|
54
|
+
if (typeof cronExpr !== 'string') {
|
|
55
|
+
return { overnight: false, hour: null, reason: 'cron must be a string' };
|
|
56
|
+
}
|
|
57
|
+
const fields = cronExpr.trim().split(/\s+/);
|
|
58
|
+
if (fields.length !== 5) {
|
|
59
|
+
return { overnight: false, hour: null, reason: 'cron must have exactly 5 fields' };
|
|
60
|
+
}
|
|
61
|
+
const hourField = fields[1];
|
|
62
|
+
|
|
63
|
+
// Wildcard, range, step, list — all ambiguous (adopter could be
|
|
64
|
+
// running it during the day too). Don't auto-default; require
|
|
65
|
+
// explicit --overnight yes.
|
|
66
|
+
if (!/^\d+$/.test(hourField)) {
|
|
67
|
+
return { overnight: false, hour: null,
|
|
68
|
+
reason: `hour field "${hourField}" is not a literal — pass --overnight yes to enable Night Shift` };
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const hour = parseInt(hourField, 10);
|
|
72
|
+
if (hour < 0 || hour > 23) {
|
|
73
|
+
return { overnight: false, hour: null,
|
|
74
|
+
reason: `hour field "${hourField}" out of range 0-23` };
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (OVERNIGHT_HOURS.includes(hour)) {
|
|
78
|
+
return { overnight: true, hour,
|
|
79
|
+
reason: `hour ${hour} UTC falls in the overnight window (18:00-05:59 UTC)` };
|
|
80
|
+
}
|
|
81
|
+
return { overnight: false, hour,
|
|
82
|
+
reason: `hour ${hour} UTC is daytime (06:00-17:59 UTC); pass --overnight yes to enable Night Shift anyway` };
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Accepted aliases for explicit modes. Lowercased before comparison so
|
|
86
|
+
// `--overnight YES` and `--overnight Yes` both work — pass-1 review
|
|
87
|
+
// HIGH caught that the original case-sensitive implementation silently
|
|
88
|
+
// fell through to auto on uppercase aliases, contradicting adopter
|
|
89
|
+
// intent without any signal.
|
|
90
|
+
const YES_ALIASES = Object.freeze(new Set(['yes', 'true', '1', 'on', 'enable', 'enabled']));
|
|
91
|
+
const NO_ALIASES = Object.freeze(new Set(['no', 'false', '0', 'off', 'disable', 'disabled']));
|
|
92
|
+
const AUTO_ALIASES = Object.freeze(new Set(['auto', 'default', '']));
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Check whether a `--overnight <mode>` value is a recognized mode (any
|
|
96
|
+
* known alias, case-insensitive). Caller uses this to validate the
|
|
97
|
+
* adopter's input UPFRONT and exit non-zero with a helpful message
|
|
98
|
+
* rather than silently falling back to auto on a typo.
|
|
99
|
+
*
|
|
100
|
+
* @param {string} mode
|
|
101
|
+
* @returns {boolean}
|
|
102
|
+
*/
|
|
103
|
+
function isKnownOvernightMode(mode) {
|
|
104
|
+
if (typeof mode !== 'string') return false;
|
|
105
|
+
const m = mode.trim().toLowerCase();
|
|
106
|
+
return YES_ALIASES.has(m) || NO_ALIASES.has(m) || AUTO_ALIASES.has(m);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Resolve the final overnight setting given the adopter's explicit
|
|
111
|
+
* mode (`auto`|`yes`|`no`, case-insensitive, with aliases) and the
|
|
112
|
+
* cron analysis.
|
|
113
|
+
*
|
|
114
|
+
* UNKNOWN modes are treated as `auto` per the historical fallback,
|
|
115
|
+
* BUT the CLI should reject unknown modes upfront via
|
|
116
|
+
* `isKnownOvernightMode` — this fallback exists so library callers
|
|
117
|
+
* that didn't validate get safe behavior.
|
|
118
|
+
*
|
|
119
|
+
* @param {string} mode — value of --overnight (auto|yes|no + aliases)
|
|
120
|
+
* @param {ReturnType<typeof analyzeCron>} analysis
|
|
121
|
+
* @returns {{ overnight: boolean, source: 'explicit-yes'|'explicit-no'|'auto-detect-yes'|'auto-detect-no' }}
|
|
122
|
+
*/
|
|
123
|
+
function resolveOvernight(mode, analysis) {
|
|
124
|
+
const m = typeof mode === 'string' ? mode.trim().toLowerCase() : '';
|
|
125
|
+
if (YES_ALIASES.has(m)) return { overnight: true, source: 'explicit-yes' };
|
|
126
|
+
if (NO_ALIASES.has(m)) return { overnight: false, source: 'explicit-no' };
|
|
127
|
+
return {
|
|
128
|
+
overnight: analysis.overnight,
|
|
129
|
+
source: analysis.overnight ? 'auto-detect-yes' : 'auto-detect-no',
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
module.exports = {
|
|
134
|
+
OVERNIGHT_HOURS,
|
|
135
|
+
YES_ALIASES,
|
|
136
|
+
NO_ALIASES,
|
|
137
|
+
AUTO_ALIASES,
|
|
138
|
+
analyzeCron,
|
|
139
|
+
resolveOvernight,
|
|
140
|
+
isKnownOvernightMode,
|
|
141
|
+
};
|