create-harness-vibe-coding 0.8.8 → 0.8.10
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/README-CN.md +8 -2
- package/README.md +22 -3
- package/docs/images/harness-icon.png +0 -0
- package/package.json +3 -1
- package/src/generator.js +36 -0
- package/src/index.js +72 -4
- package/templates/common/.claude/agents/architect.md +31 -31
- package/templates/common/.claude/agents/codebase-explorer.md +45 -0
- package/templates/common/.claude/agents/docs-researcher.md +41 -41
- package/templates/common/.claude/agents/memory-master.md +26 -8
- package/templates/common/.claude/agents/reviewer.md +34 -34
- package/templates/common/.claude/agents/task-scribe.md +70 -0
- package/templates/common/.claude/commands/wf-help.md +9 -5
- package/templates/common/.claude/commands/wf-update.md +24 -0
- package/templates/common/.claude/rules/ecc/common.md +15 -2
- package/templates/common/.claude/settings.json +13 -0
- package/templates/common/.claude/skills/subagent-orchestrator/SKILL.md +10 -6
- package/templates/common/.claude/skills/wf/SKILL.md +15 -8
- package/templates/common/.claude/skills/wf-auto/SKILL.md +3 -2
- package/templates/common/.claude/skills/wf-learn/SKILL.md +9 -2
- package/templates/common/.claude/skills/wf-max/SKILL.md +26 -8
- package/templates/common/.claude/skills/wf-review/SKILL.md +63 -12
- package/templates/common/.claude/skills/wf-update/SKILL.md +15 -2
- package/templates/common/.codex/hooks.json +17 -0
- package/templates/common/.harness-version +93 -48
- package/templates/common/.opencode/agents/architect.md +24 -24
- package/templates/common/.opencode/agents/codebase-explorer.md +45 -0
- package/templates/common/.opencode/agents/docs-researcher.md +34 -34
- package/templates/common/.opencode/agents/memory-master.md +26 -8
- package/templates/common/.opencode/agents/reviewer.md +27 -27
- package/templates/common/.opencode/agents/task-scribe.md +70 -0
- package/templates/common/.opencode/commands/wf-auto-spark.md +15 -0
- package/templates/common/.opencode/commands/wf-auto.md +15 -0
- package/templates/common/.opencode/commands/wf-help.md +9 -5
- package/templates/common/.opencode/commands/wf-learn.md +15 -0
- package/templates/common/.opencode/commands/wf-max.md +15 -0
- package/templates/common/.opencode/commands/wf-readme.md +15 -0
- package/templates/common/.opencode/commands/wf-remove.md +15 -0
- package/templates/common/.opencode/commands/wf-review.md +15 -0
- package/templates/common/.opencode/commands/wf-update.md +24 -0
- package/templates/common/.opencode/commands/wf.md +15 -0
- package/templates/common/.opencode/plugins/harness-wf-status.mjs +126 -0
- package/templates/common/AGENTS.md +2 -29
- package/templates/common/CLAUDE.md +35 -6
- package/templates/common/Harness/ACCEPTANCE_PROTOCOL.md +2 -2
- package/templates/common/{MEMORY.md → Harness/MEMORY.md} +17 -4
- package/templates/common/Harness/MEMORY_PROTOCOL.md +80 -30
- package/templates/common/Harness/README.md +53 -25
- package/templates/common/{SETUP.md → Harness/SETUP.md} +278 -276
- package/templates/common/Harness/TASK_ARCHIVE.md +56 -0
- package/templates/common/Harness/WF-AUTO-ANGLES.md +1 -1
- package/templates/common/Harness/WF-AUTO-SPARK.md +5 -14
- package/templates/common/Harness/WF-AUTO.md +22 -84
- package/templates/common/Harness/WF-KERNEL.md +189 -0
- package/templates/common/Harness/WF-MAX.md +60 -328
- package/templates/common/Harness/WF-STATE.md +83 -0
- package/templates/common/Harness/WF.md +117 -237
- package/templates/common/Harness/agent-workflow.md +2 -2
- package/templates/common/Harness/context-loading.md +3 -3
- package/templates/common/Harness/dispatch.md +43 -35
- package/templates/common/Harness/scripts/archive-tasks.mjs +239 -0
- package/templates/common/{scripts → Harness/scripts}/scan-clean.mjs +29 -1
- package/templates/common/{scripts → Harness/scripts}/validate-harness.mjs +921 -699
- package/templates/common/Harness/scripts/wf-auto-update-prompt.mjs +258 -0
- package/templates/common/{scripts → Harness/scripts}/wf-remove.mjs +1 -0
- package/templates/common/{scripts → Harness/scripts}/wf-update-check.mjs +163 -52
- package/templates/common/Harness/subagents.md +36 -28
- package/templates/common/Harness/tasks/_template/PLAN.md +5 -0
- package/templates/common/Harness/tasks/_template/STATE.json +23 -0
- package/templates/common/README.md +2 -2
- package/templates/common/memory/agent-lessons-patterns.md +9 -8
- package/templates/common/memory/routes.md +43 -0
- package/templates/common/memory/startup-hints.md +32 -0
- package/templates/common/memory/tool-usage-reflections.md +9 -8
- package/templates/common/memory/user-corrections-preferences.md +11 -9
- package/templates/optional/catalog.json +8 -0
- package/templates/optional/skills/browser-e2e/.claude/skills/browser-e2e/SKILL.md +42 -42
- package/templates/optional/skills/browser-e2e/.opencode/commands/wf-browser.md +15 -0
- package/templates/optional/skills/github-pr-review/.claude/skills/github-pr-review/SKILL.md +40 -40
- package/templates/optional/skills/python-backend/.claude/skills/python-backend/SKILL.md +40 -40
- package/templates/optional/skills/ts-react-frontend/.claude/skills/ts-react-frontend/SKILL.md +43 -43
- package/templates/optional/skills/ui-ux-review/.claude/skills/ui-ux-review/SKILL.md +40 -40
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* wf-auto-update-prompt.mjs
|
|
4
|
+
*
|
|
5
|
+
* Lightweight hook helper for Claude Code, Codex, and OpenCode.
|
|
6
|
+
* It checks the installed Harness update plan and emits a prompt reminder only
|
|
7
|
+
* when an update is available. It never applies updates.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs';
|
|
11
|
+
import { dirname, resolve } from 'path';
|
|
12
|
+
import { spawnSync } from 'child_process';
|
|
13
|
+
|
|
14
|
+
const DEFAULT_CHECK_INTERVAL_MS = 6 * 60 * 60 * 1000;
|
|
15
|
+
const DEFAULT_NOTICE_INTERVAL_MS = 60 * 60 * 1000;
|
|
16
|
+
const args = process.argv.slice(2);
|
|
17
|
+
const format = readFlagValue('--format') || 'plain';
|
|
18
|
+
const force = args.includes('--force');
|
|
19
|
+
const modeOnly = args.includes('--mode-only');
|
|
20
|
+
const input = readStdinJson();
|
|
21
|
+
const root = findRoot();
|
|
22
|
+
|
|
23
|
+
function readFlagValue(flagName) {
|
|
24
|
+
const index = args.indexOf(flagName);
|
|
25
|
+
if (index === -1) return null;
|
|
26
|
+
const value = args[index + 1];
|
|
27
|
+
return value && !value.startsWith('--') ? value : null;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function readStdinJson() {
|
|
31
|
+
try {
|
|
32
|
+
if (process.stdin.isTTY) return {};
|
|
33
|
+
const raw = readFileSync(0, 'utf-8').trim();
|
|
34
|
+
return raw ? JSON.parse(raw) : {};
|
|
35
|
+
} catch {
|
|
36
|
+
return {};
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function findRoot() {
|
|
41
|
+
const candidates = [
|
|
42
|
+
process.env.WF_ROOT,
|
|
43
|
+
input.cwd,
|
|
44
|
+
input.workspace,
|
|
45
|
+
process.cwd(),
|
|
46
|
+
].filter(Boolean);
|
|
47
|
+
|
|
48
|
+
for (const candidate of candidates) {
|
|
49
|
+
const found = ascendToHarnessRoot(resolve(candidate));
|
|
50
|
+
if (found) return found;
|
|
51
|
+
}
|
|
52
|
+
return process.cwd();
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function ascendToHarnessRoot(start) {
|
|
56
|
+
let current = start;
|
|
57
|
+
for (;;) {
|
|
58
|
+
if (existsSync(resolve(current, 'Harness', '.harness-version'))) return current;
|
|
59
|
+
const parent = dirname(current);
|
|
60
|
+
if (parent === current) return null;
|
|
61
|
+
current = parent;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function runtimeDir() {
|
|
66
|
+
return resolve(root, 'Harness', '.runtime');
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function cachePath() {
|
|
70
|
+
return resolve(runtimeDir(), 'update-check.json');
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function modePath() {
|
|
74
|
+
return resolve(runtimeDir(), 'current-mode.json');
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function readJsonFile(file, fallback) {
|
|
78
|
+
try {
|
|
79
|
+
return JSON.parse(readFileSync(file, 'utf-8'));
|
|
80
|
+
} catch {
|
|
81
|
+
return fallback;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function writeJsonFile(file, value) {
|
|
86
|
+
mkdirSync(dirname(file), { recursive: true });
|
|
87
|
+
writeFileSync(file, JSON.stringify(value, null, 2) + '\n', 'utf-8');
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function readVersion() {
|
|
91
|
+
return readJsonFile(resolve(root, 'Harness', '.harness-version'), null);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function shouldSkipPrompt() {
|
|
95
|
+
const prompt = String(input.prompt || input.message || '');
|
|
96
|
+
return /\b\/?wf-update\b|\$wf-update\b/.test(prompt);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function intervalFromEnv(name, fallback) {
|
|
100
|
+
const raw = process.env[name];
|
|
101
|
+
if (!raw) return fallback;
|
|
102
|
+
const parsed = Number(raw);
|
|
103
|
+
return Number.isFinite(parsed) && parsed >= 0 ? parsed : fallback;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function runUpdateCheck() {
|
|
107
|
+
const script = resolve(root, 'Harness', 'scripts', 'wf-update-check.mjs');
|
|
108
|
+
if (!existsSync(script)) {
|
|
109
|
+
return { status: 'error', message: 'Harness/scripts/wf-update-check.mjs not found.' };
|
|
110
|
+
}
|
|
111
|
+
const result = spawnSync(process.execPath, [script, '--json'], {
|
|
112
|
+
cwd: root,
|
|
113
|
+
encoding: 'utf-8',
|
|
114
|
+
timeout: intervalFromEnv('WF_UPDATE_CHECK_TIMEOUT_MS', 30000),
|
|
115
|
+
env: process.env,
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
if (result.error) {
|
|
119
|
+
return { status: 'offline', message: result.error.message };
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const stdout = (result.stdout || '').trim();
|
|
123
|
+
if (!stdout) {
|
|
124
|
+
return { status: 'error', message: `Update checker exited with no JSON output (${result.status ?? 'unknown'}).` };
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
try {
|
|
128
|
+
return JSON.parse(stdout);
|
|
129
|
+
} catch {
|
|
130
|
+
return { status: 'error', message: 'Update checker returned invalid JSON.' };
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function isUpdateStatus(status) {
|
|
135
|
+
return status === 'update-available' || status === 'partial-update';
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function buildMessage(update) {
|
|
139
|
+
const from = update.from || update.version || 'unknown';
|
|
140
|
+
const to = update.to || update.remote || 'unknown';
|
|
141
|
+
const parts = [
|
|
142
|
+
`Harness update available: ${from} -> ${to}.`,
|
|
143
|
+
`Before unrelated work, ask the user whether to run /wf-update.`,
|
|
144
|
+
];
|
|
145
|
+
// Only include summary counts; never list individual conflict files in hook output.
|
|
146
|
+
if (update.conflict > 0) {
|
|
147
|
+
parts.push(`${update.conflict} conflict file(s) will need agent/user merge decisions.`);
|
|
148
|
+
}
|
|
149
|
+
if ((update.updated > 0 || update.created > 0) && update.updated !== undefined) {
|
|
150
|
+
const safeCount = (update.updated || 0) + (update.created || 0);
|
|
151
|
+
parts.push(`${safeCount} safe file update(s) available.`);
|
|
152
|
+
}
|
|
153
|
+
// Max 2 lines total, never include plan details
|
|
154
|
+
return parts.slice(0, 2).join(' ');
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
function readModeLabel() {
|
|
158
|
+
const mode = readJsonFile(modePath(), null);
|
|
159
|
+
if (!mode || !mode.active || !mode.mode) return null;
|
|
160
|
+
return `${mode.mode} mode on`;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
function emit(message, payload = {}) {
|
|
164
|
+
if (!message && !payload.modeLabel) return;
|
|
165
|
+
|
|
166
|
+
if (format === 'claude') {
|
|
167
|
+
const context = [message, payload.modeLabel].filter(Boolean).join('\n');
|
|
168
|
+
if (context) console.log(JSON.stringify({ additionalContext: context }));
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
if (format === 'codex') {
|
|
173
|
+
const context = [message, payload.modeLabel].filter(Boolean).join('\n');
|
|
174
|
+
if (context) {
|
|
175
|
+
console.log(JSON.stringify({
|
|
176
|
+
hookSpecificOutput: {
|
|
177
|
+
hookEventName: input.hook_event_name || 'SessionStart',
|
|
178
|
+
additionalContext: context,
|
|
179
|
+
},
|
|
180
|
+
}));
|
|
181
|
+
}
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
if (format === 'json') {
|
|
186
|
+
console.log(JSON.stringify({ message, ...payload }, null, 2));
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
if (message) console.log(message);
|
|
191
|
+
if (payload.modeLabel) console.log(payload.modeLabel);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
function main() {
|
|
195
|
+
if (!existsSync(resolve(root, 'Harness', '.harness-version'))) return;
|
|
196
|
+
const version = readVersion();
|
|
197
|
+
if (version?.autoCheck === false) return;
|
|
198
|
+
|
|
199
|
+
const modeLabel = readModeLabel();
|
|
200
|
+
if (modeOnly) {
|
|
201
|
+
emit('', { modeLabel, status: 'mode' });
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
if (shouldSkipPrompt()) {
|
|
206
|
+
emit('', { modeLabel, status: 'skipped-update-command' });
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
const now = Date.now();
|
|
211
|
+
const cache = readJsonFile(cachePath(), {});
|
|
212
|
+
const checkInterval = intervalFromEnv('WF_UPDATE_CHECK_INTERVAL_MS', DEFAULT_CHECK_INTERVAL_MS);
|
|
213
|
+
const noticeInterval = intervalFromEnv('WF_UPDATE_NOTICE_INTERVAL_MS', DEFAULT_NOTICE_INTERVAL_MS);
|
|
214
|
+
|
|
215
|
+
let update = cache.update;
|
|
216
|
+
const shouldCheck = force
|
|
217
|
+
|| !cache.checkedAt
|
|
218
|
+
|| now - Date.parse(cache.checkedAt) >= checkInterval;
|
|
219
|
+
|
|
220
|
+
if (shouldCheck) {
|
|
221
|
+
update = runUpdateCheck();
|
|
222
|
+
writeJsonFile(cachePath(), {
|
|
223
|
+
checkedAt: new Date(now).toISOString(),
|
|
224
|
+
noticedAt: cache.noticedAt || null,
|
|
225
|
+
update,
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
if (!isUpdateStatus(update?.status)) {
|
|
230
|
+
emit('', { modeLabel, status: update?.status || 'unknown' });
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
const shouldNotice = force
|
|
235
|
+
|| !cache.noticedAt
|
|
236
|
+
|| now - Date.parse(cache.noticedAt) >= noticeInterval
|
|
237
|
+
|| cache.update?.to !== update.to
|
|
238
|
+
|| cache.update?.remote !== update.remote;
|
|
239
|
+
|
|
240
|
+
if (!shouldNotice) {
|
|
241
|
+
emit('', { modeLabel, status: update.status, suppressed: true });
|
|
242
|
+
return;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
writeJsonFile(cachePath(), {
|
|
246
|
+
checkedAt: cache.checkedAt || new Date(now).toISOString(),
|
|
247
|
+
noticedAt: new Date(now).toISOString(),
|
|
248
|
+
update,
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
emit(buildMessage(update), { modeLabel, status: update.status, update });
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
try {
|
|
255
|
+
main();
|
|
256
|
+
} catch {
|
|
257
|
+
// Hooks must fail open. A broken update reminder should never block work.
|
|
258
|
+
}
|
|
@@ -214,6 +214,7 @@ const CLEANUP_DIRS = [
|
|
|
214
214
|
|
|
215
215
|
/** Reject paths that escape ROOT (traversal, absolute, .., etc.). Sync with wf-update-check. */
|
|
216
216
|
function safePath(file) {
|
|
217
|
+
if (/^[A-Za-z]:/.test(file)) return null;
|
|
217
218
|
let normalized = file.replace(/\\/g, '/').replace(/^\/+/, '');
|
|
218
219
|
if (normalized.includes('//')) return null; // double slash bypass
|
|
219
220
|
if (normalized.split('/').some(p => p === '..')) return null;
|