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.
Files changed (82) hide show
  1. package/README-CN.md +8 -2
  2. package/README.md +22 -3
  3. package/docs/images/harness-icon.png +0 -0
  4. package/package.json +3 -1
  5. package/src/generator.js +36 -0
  6. package/src/index.js +72 -4
  7. package/templates/common/.claude/agents/architect.md +31 -31
  8. package/templates/common/.claude/agents/codebase-explorer.md +45 -0
  9. package/templates/common/.claude/agents/docs-researcher.md +41 -41
  10. package/templates/common/.claude/agents/memory-master.md +26 -8
  11. package/templates/common/.claude/agents/reviewer.md +34 -34
  12. package/templates/common/.claude/agents/task-scribe.md +70 -0
  13. package/templates/common/.claude/commands/wf-help.md +9 -5
  14. package/templates/common/.claude/commands/wf-update.md +24 -0
  15. package/templates/common/.claude/rules/ecc/common.md +15 -2
  16. package/templates/common/.claude/settings.json +13 -0
  17. package/templates/common/.claude/skills/subagent-orchestrator/SKILL.md +10 -6
  18. package/templates/common/.claude/skills/wf/SKILL.md +15 -8
  19. package/templates/common/.claude/skills/wf-auto/SKILL.md +3 -2
  20. package/templates/common/.claude/skills/wf-learn/SKILL.md +9 -2
  21. package/templates/common/.claude/skills/wf-max/SKILL.md +26 -8
  22. package/templates/common/.claude/skills/wf-review/SKILL.md +63 -12
  23. package/templates/common/.claude/skills/wf-update/SKILL.md +15 -2
  24. package/templates/common/.codex/hooks.json +17 -0
  25. package/templates/common/.harness-version +93 -48
  26. package/templates/common/.opencode/agents/architect.md +24 -24
  27. package/templates/common/.opencode/agents/codebase-explorer.md +45 -0
  28. package/templates/common/.opencode/agents/docs-researcher.md +34 -34
  29. package/templates/common/.opencode/agents/memory-master.md +26 -8
  30. package/templates/common/.opencode/agents/reviewer.md +27 -27
  31. package/templates/common/.opencode/agents/task-scribe.md +70 -0
  32. package/templates/common/.opencode/commands/wf-auto-spark.md +15 -0
  33. package/templates/common/.opencode/commands/wf-auto.md +15 -0
  34. package/templates/common/.opencode/commands/wf-help.md +9 -5
  35. package/templates/common/.opencode/commands/wf-learn.md +15 -0
  36. package/templates/common/.opencode/commands/wf-max.md +15 -0
  37. package/templates/common/.opencode/commands/wf-readme.md +15 -0
  38. package/templates/common/.opencode/commands/wf-remove.md +15 -0
  39. package/templates/common/.opencode/commands/wf-review.md +15 -0
  40. package/templates/common/.opencode/commands/wf-update.md +24 -0
  41. package/templates/common/.opencode/commands/wf.md +15 -0
  42. package/templates/common/.opencode/plugins/harness-wf-status.mjs +126 -0
  43. package/templates/common/AGENTS.md +2 -29
  44. package/templates/common/CLAUDE.md +35 -6
  45. package/templates/common/Harness/ACCEPTANCE_PROTOCOL.md +2 -2
  46. package/templates/common/{MEMORY.md → Harness/MEMORY.md} +17 -4
  47. package/templates/common/Harness/MEMORY_PROTOCOL.md +80 -30
  48. package/templates/common/Harness/README.md +53 -25
  49. package/templates/common/{SETUP.md → Harness/SETUP.md} +278 -276
  50. package/templates/common/Harness/TASK_ARCHIVE.md +56 -0
  51. package/templates/common/Harness/WF-AUTO-ANGLES.md +1 -1
  52. package/templates/common/Harness/WF-AUTO-SPARK.md +5 -14
  53. package/templates/common/Harness/WF-AUTO.md +22 -84
  54. package/templates/common/Harness/WF-KERNEL.md +189 -0
  55. package/templates/common/Harness/WF-MAX.md +60 -328
  56. package/templates/common/Harness/WF-STATE.md +83 -0
  57. package/templates/common/Harness/WF.md +117 -237
  58. package/templates/common/Harness/agent-workflow.md +2 -2
  59. package/templates/common/Harness/context-loading.md +3 -3
  60. package/templates/common/Harness/dispatch.md +43 -35
  61. package/templates/common/Harness/scripts/archive-tasks.mjs +239 -0
  62. package/templates/common/{scripts → Harness/scripts}/scan-clean.mjs +29 -1
  63. package/templates/common/{scripts → Harness/scripts}/validate-harness.mjs +921 -699
  64. package/templates/common/Harness/scripts/wf-auto-update-prompt.mjs +258 -0
  65. package/templates/common/{scripts → Harness/scripts}/wf-remove.mjs +1 -0
  66. package/templates/common/{scripts → Harness/scripts}/wf-update-check.mjs +163 -52
  67. package/templates/common/Harness/subagents.md +36 -28
  68. package/templates/common/Harness/tasks/_template/PLAN.md +5 -0
  69. package/templates/common/Harness/tasks/_template/STATE.json +23 -0
  70. package/templates/common/README.md +2 -2
  71. package/templates/common/memory/agent-lessons-patterns.md +9 -8
  72. package/templates/common/memory/routes.md +43 -0
  73. package/templates/common/memory/startup-hints.md +32 -0
  74. package/templates/common/memory/tool-usage-reflections.md +9 -8
  75. package/templates/common/memory/user-corrections-preferences.md +11 -9
  76. package/templates/optional/catalog.json +8 -0
  77. package/templates/optional/skills/browser-e2e/.claude/skills/browser-e2e/SKILL.md +42 -42
  78. package/templates/optional/skills/browser-e2e/.opencode/commands/wf-browser.md +15 -0
  79. package/templates/optional/skills/github-pr-review/.claude/skills/github-pr-review/SKILL.md +40 -40
  80. package/templates/optional/skills/python-backend/.claude/skills/python-backend/SKILL.md +40 -40
  81. package/templates/optional/skills/ts-react-frontend/.claude/skills/ts-react-frontend/SKILL.md +43 -43
  82. 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;