@walwal-harness/cli 7.1.39 → 7.1.41

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 (74) hide show
  1. package/HR-Resource/cdo/SKILL.md +9 -1
  2. package/HR-Resource/ceo/SKILL.md +33 -2
  3. package/HR-Resource/coo/SKILL.md +9 -1
  4. package/HR-Resource/cqo/SKILL.md +9 -1
  5. package/HR-Resource/cto/SKILL.md +10 -2
  6. package/HR-Resource/ops/SKILL.md +20 -2
  7. package/HR-Resource/ops-prompt-inspector/SKILL.md +105 -0
  8. package/HR-Resource/ops-prompt-inspector/agents/installation-agent.json +7 -0
  9. package/HR-Resource/ops-prompt-inspector/assets/PromptInspector.tsx +599 -0
  10. package/HR-Resource/ops-prompt-inspector/assets/prompt-inspector-static.js +180 -0
  11. package/HR-Resource/ops-prompt-inspector/references/handler-templates.md +255 -0
  12. package/HR-Resource/ops-prompt-inspector/scripts/discover_apis.py +509 -0
  13. package/HR-Resource/ops-prompt-inspector/scripts/install.sh +32 -0
  14. package/HR-Resource/ops-prompt-inspector/scripts/setup.py +381 -0
  15. package/apps/harness-dashboard/app/api/wake/route.ts +41 -0
  16. package/apps/harness-dashboard/components/Scene.tsx +568 -35
  17. package/apps/harness-dashboard/lib/__tests__/harness-state.test.ts +1 -202
  18. package/apps/harness-dashboard/lib/__tests__/sandbox-e2e.test.ts +490 -0
  19. package/apps/harness-dashboard/lib/harness-state.ts +107 -796
  20. package/apps/harness-dashboard/lib/types.ts +22 -222
  21. package/apps/harness-dashboard/playwright.config.ts +26 -7
  22. package/assets/templates/AGENTS-ko.md.template +1 -1
  23. package/assets/templates/AGENTS.md.template +12 -2
  24. package/assets/templates/HARNESS.md +2 -0
  25. package/assets/templates/company-pipeline-manifest.json +5 -14
  26. package/assets/templates/worker-report.md.template +38 -0
  27. package/bin/init.js +4 -3
  28. package/commands/goal.md +4 -2
  29. package/commands/hot-fix.md +1 -1
  30. package/commands/submission.md +1 -1
  31. package/package.json +1 -1
  32. package/scripts/conductor-tick.sh +12 -0
  33. package/scripts/harness-agenda.sh +105 -0
  34. package/scripts/harness-company-block.sh +89 -0
  35. package/scripts/harness-company-complete.sh +17 -8
  36. package/scripts/harness-company-cycle.sh +54 -0
  37. package/scripts/harness-progress-set.sh +8 -2
  38. package/scripts/harness-stop.sh +76 -0
  39. package/scripts/harness-wake-control.sh +151 -0
  40. package/scripts/harness-wake.sh +5 -0
  41. package/apps/harness-dashboard/components/Drawer.tsx +0 -146
  42. package/apps/harness-dashboard/components/Header.tsx +0 -40
  43. package/apps/harness-dashboard/components/MissionTimeline.tsx +0 -214
  44. package/apps/harness-dashboard/components/OrgTree.tsx +0 -459
  45. package/apps/harness-dashboard/components/drawer/AgentLogTab.tsx +0 -56
  46. package/apps/harness-dashboard/components/drawer/DesignPreviewTab.tsx +0 -160
  47. package/apps/harness-dashboard/components/drawer/GotchasTab.tsx +0 -89
  48. package/apps/harness-dashboard/components/drawer/HypothesisTab.tsx +0 -42
  49. package/apps/harness-dashboard/components/drawer/IncidentsTab.tsx +0 -50
  50. package/apps/harness-dashboard/components/drawer/MissionDocTab.tsx +0 -96
  51. package/apps/harness-dashboard/components/drawer/MissionFlowTab.tsx +0 -576
  52. package/apps/harness-dashboard/components/drawer/OwnerHistoryTab.tsx +0 -160
  53. package/apps/harness-dashboard/components/drawer/RoomMetricsTab.tsx +0 -90
  54. package/apps/harness-dashboard/components/drawer/TracksTab.tsx +0 -47
  55. package/apps/harness-dashboard/components/three/ArchiveBoxes.tsx +0 -95
  56. package/apps/harness-dashboard/components/three/DeptBoard.tsx +0 -175
  57. package/apps/harness-dashboard/components/three/Floor3D.tsx +0 -404
  58. package/apps/harness-dashboard/components/three/GoalCard3D.tsx +0 -123
  59. package/apps/harness-dashboard/components/three/HypothesisCards.tsx +0 -71
  60. package/apps/harness-dashboard/components/three/MeetingWhiteboard.tsx +0 -98
  61. package/apps/harness-dashboard/components/three/Minifig3D.tsx +0 -391
  62. package/apps/harness-dashboard/components/three/OpsSiren.tsx +0 -99
  63. package/apps/harness-dashboard/components/three/Stage3D.tsx +0 -104
  64. package/apps/harness-dashboard/components/three/TrackRibbons.tsx +0 -81
  65. package/apps/harness-dashboard/lib/__tests__/i18n.test.ts +0 -16
  66. package/apps/harness-dashboard/lib/__tests__/iso.test.ts +0 -42
  67. package/apps/harness-dashboard/lib/__tests__/state-mapping.test.ts +0 -183
  68. package/apps/harness-dashboard/lib/agent-roster.ts +0 -42
  69. package/apps/harness-dashboard/lib/furniture.ts +0 -165
  70. package/apps/harness-dashboard/lib/i18n.ts +0 -26
  71. package/apps/harness-dashboard/lib/iso.ts +0 -150
  72. package/apps/harness-dashboard/lib/markdown.tsx +0 -306
  73. package/apps/harness-dashboard/lib/path-planning.ts +0 -72
  74. package/apps/harness-dashboard/lib/state-mapping.ts +0 -146
@@ -0,0 +1,381 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Auto-setup script for Prompt Inspector
4
+
5
+ Automatically detects project type and injects the component.
6
+ Supports: Next.js (App Router / Pages Router)
7
+
8
+ Usage:
9
+ python setup.py <project_path>
10
+ """
11
+
12
+ import os
13
+ import sys
14
+ import re
15
+ import shutil
16
+ from pathlib import Path
17
+
18
+ COMPONENT_IMPORT = "import { PromptInspector } from '@/components/PromptInspector';"
19
+
20
+ def get_version_from_file(file_path: Path) -> str | None:
21
+ """Extract version from PromptInspector.tsx file."""
22
+ if not file_path.exists():
23
+ return None
24
+ content = file_path.read_text(encoding='utf-8')
25
+ match = re.search(r"PROMPT_INSPECTOR_VERSION\s*=\s*['\"]([^'\"]+)['\"]", content)
26
+ return match.group(1) if match else '1.0.0' # Default to 1.0.0 for old versions
27
+ COMPONENT_JSX = """{process.env.NODE_ENV === 'development' && <PromptInspector />}"""
28
+
29
+ def find_project_type(project_path: Path) -> dict:
30
+ """Detect project type and find the appropriate layout file."""
31
+ result = {
32
+ 'type': None,
33
+ 'layout_file': None,
34
+ 'components_dir': None,
35
+ }
36
+
37
+ # Check for static/vanilla apps
38
+ static_index = project_path / 'index.html'
39
+
40
+ # Check for Next.js App Router
41
+ app_layout = project_path / 'app' / 'layout.tsx'
42
+ app_layout_js = project_path / 'app' / 'layout.js'
43
+
44
+ # Check for src directory structure
45
+ src_app_layout = project_path / 'src' / 'app' / 'layout.tsx'
46
+ src_app_layout_js = project_path / 'src' / 'app' / 'layout.js'
47
+
48
+ # Check for Pages Router
49
+ pages_app = project_path / 'pages' / '_app.tsx'
50
+ pages_app_js = project_path / 'pages' / '_app.js'
51
+ src_pages_app = project_path / 'src' / 'pages' / '_app.tsx'
52
+ src_pages_app_js = project_path / 'src' / 'pages' / '_app.js'
53
+
54
+ # Determine layout file
55
+ if app_layout.exists():
56
+ result['type'] = 'nextjs-app'
57
+ result['layout_file'] = app_layout
58
+ result['components_dir'] = project_path / 'components'
59
+ elif app_layout_js.exists():
60
+ result['type'] = 'nextjs-app'
61
+ result['layout_file'] = app_layout_js
62
+ result['components_dir'] = project_path / 'components'
63
+ elif src_app_layout.exists():
64
+ result['type'] = 'nextjs-app'
65
+ result['layout_file'] = src_app_layout
66
+ result['components_dir'] = project_path / 'src' / 'components'
67
+ elif src_app_layout_js.exists():
68
+ result['type'] = 'nextjs-app'
69
+ result['layout_file'] = src_app_layout_js
70
+ result['components_dir'] = project_path / 'src' / 'components'
71
+ elif pages_app.exists():
72
+ result['type'] = 'nextjs-pages'
73
+ result['layout_file'] = pages_app
74
+ result['components_dir'] = project_path / 'components'
75
+ elif pages_app_js.exists():
76
+ result['type'] = 'nextjs-pages'
77
+ result['layout_file'] = pages_app_js
78
+ result['components_dir'] = project_path / 'components'
79
+ elif src_pages_app.exists():
80
+ result['type'] = 'nextjs-pages'
81
+ result['layout_file'] = src_pages_app
82
+ result['components_dir'] = project_path / 'src' / 'components'
83
+ elif src_pages_app_js.exists():
84
+ result['type'] = 'nextjs-pages'
85
+ result['layout_file'] = src_pages_app_js
86
+ result['components_dir'] = project_path / 'src' / 'components'
87
+ elif static_index.exists():
88
+ result['type'] = 'static-html'
89
+ result['layout_file'] = static_index
90
+ result['components_dir'] = project_path / 'public'
91
+
92
+ return result
93
+
94
+
95
+ def compare_versions(v1: str, v2: str) -> int:
96
+ """Compare two version strings. Returns: -1 if v1 < v2, 0 if equal, 1 if v1 > v2."""
97
+ def parse(v):
98
+ return [int(x) for x in v.split('.')]
99
+ p1, p2 = parse(v1), parse(v2)
100
+ for a, b in zip(p1, p2):
101
+ if a < b:
102
+ return -1
103
+ if a > b:
104
+ return 1
105
+ return 0
106
+
107
+
108
+ def copy_component(components_dir: Path, script_dir: Path, force_update: bool = False) -> bool:
109
+ """Copy PromptInspector component to project."""
110
+ components_dir.mkdir(parents=True, exist_ok=True)
111
+
112
+ source = script_dir.parent / 'assets' / 'PromptInspector.tsx'
113
+ dest = components_dir / 'PromptInspector.tsx'
114
+
115
+ if not source.exists():
116
+ print(f" Error: Source component not found at {source}")
117
+ return False
118
+
119
+ source_version = get_version_from_file(source)
120
+ dest_version = get_version_from_file(dest)
121
+
122
+ if dest.exists():
123
+ if dest_version and source_version:
124
+ comparison = compare_versions(source_version, dest_version)
125
+ if comparison > 0:
126
+ # Newer version available
127
+ print(f"\n ╔════════════════════════════════════════════════════════════╗")
128
+ print(f" ║ 🆕 UPDATE AVAILABLE ║")
129
+ print(f" ╠════════════════════════════════════════════════════════════╣")
130
+ print(f" ║ Current version: {dest_version:<10} ║")
131
+ print(f" ║ Latest version: {source_version:<10} ║")
132
+ print(f" ╚════════════════════════════════════════════════════════════╝")
133
+
134
+ if force_update:
135
+ response = 'y'
136
+ else:
137
+ response = input("\n Update to latest version? [Y/n]: ").strip().lower()
138
+
139
+ if response in ('', 'y', 'yes'):
140
+ # Backup existing
141
+ backup = dest.with_suffix('.tsx.backup')
142
+ shutil.copy(dest, backup)
143
+ print(f" 📦 Backup saved to {backup.name}")
144
+
145
+ shutil.copy(source, dest)
146
+ print(f" ✅ Updated to v{source_version}")
147
+ return True
148
+ else:
149
+ print(f" ⏭️ Skipped update (keeping v{dest_version})")
150
+ return True
151
+ elif comparison == 0:
152
+ print(f" ✅ Component is up to date (v{dest_version})")
153
+ return True
154
+ else:
155
+ print(f" ⚠️ Local version (v{dest_version}) is newer than source (v{source_version})")
156
+ return True
157
+ else:
158
+ print(f" Component already exists at {dest}")
159
+ return True
160
+
161
+ shutil.copy(source, dest)
162
+ print(f" ✅ Installed PromptInspector v{source_version}")
163
+ return True
164
+
165
+
166
+ def inject_into_layout(layout_file: Path, project_type: str) -> bool:
167
+ """Inject the component into the layout file."""
168
+ content = layout_file.read_text(encoding='utf-8')
169
+
170
+ # Check if already injected
171
+ if 'PromptInspector' in content:
172
+ print(f" Component already injected in {layout_file}")
173
+ return True
174
+
175
+ lines = content.split('\n')
176
+ new_lines = []
177
+ import_added = False
178
+ component_added = False
179
+
180
+ for i, line in enumerate(lines):
181
+ # Add import after the last import statement
182
+ if not import_added and (line.startswith('import ') or line.startswith('from ')):
183
+ new_lines.append(line)
184
+ # Check if next line is not an import
185
+ if i + 1 < len(lines) and not lines[i + 1].startswith('import ') and not lines[i + 1].startswith('from ') and lines[i + 1].strip() != '':
186
+ new_lines.append(COMPONENT_IMPORT)
187
+ import_added = True
188
+ continue
189
+
190
+ new_lines.append(line)
191
+
192
+ # For App Router: inject before closing </body> or </html>
193
+ if project_type == 'nextjs-app':
194
+ if '</body>' in line and not component_added:
195
+ indent = len(line) - len(line.lstrip())
196
+ new_lines.insert(-1, ' ' * (indent + 2) + COMPONENT_JSX)
197
+ component_added = True
198
+
199
+ # For Pages Router: inject inside Component wrapper
200
+ elif project_type == 'nextjs-pages':
201
+ if '<Component' in line and not component_added:
202
+ # Find the closing of the return statement and inject before it
203
+ pass
204
+
205
+ # If import wasn't added (no imports found), add at top
206
+ if not import_added:
207
+ new_lines.insert(0, COMPONENT_IMPORT)
208
+ new_lines.insert(1, '')
209
+
210
+ # For Pages Router, try different injection point
211
+ if project_type == 'nextjs-pages' and not component_added:
212
+ new_content = '\n'.join(new_lines)
213
+ # Find return statement and inject component
214
+ if 'return (' in new_content or 'return(' in new_content:
215
+ # Add before the closing of the main wrapper
216
+ new_content = new_content.replace(
217
+ '</Component>',
218
+ f'</Component>\n {COMPONENT_JSX}'
219
+ )
220
+ new_lines = new_content.split('\n')
221
+ component_added = True
222
+
223
+ new_content = '\n'.join(new_lines)
224
+
225
+ # Backup original
226
+ backup_file = layout_file.with_suffix(layout_file.suffix + '.backup')
227
+ if not backup_file.exists():
228
+ shutil.copy(layout_file, backup_file)
229
+ print(f" Backup created at {backup_file}")
230
+
231
+ layout_file.write_text(new_content, encoding='utf-8')
232
+ print(f" Injected component into {layout_file}")
233
+
234
+ return True
235
+
236
+
237
+ def copy_static_inspector(public_dir: Path, script_dir: Path, force_update: bool = False) -> bool:
238
+ """Copy the static Prompt Inspector browser script."""
239
+ public_dir.mkdir(parents=True, exist_ok=True)
240
+ source = script_dir.parent / 'assets' / 'prompt-inspector-static.js'
241
+ dest = public_dir / 'prompt-inspector-static.js'
242
+ if not source.exists():
243
+ print(f" Error: Source static inspector not found at {source}")
244
+ return False
245
+ if dest.exists() and not force_update:
246
+ print(f" Static inspector already exists at {dest}")
247
+ return True
248
+ shutil.copy(source, dest)
249
+ print(f" Installed static Prompt Inspector at {dest}")
250
+ return True
251
+
252
+
253
+ def inject_into_static_html(index_file: Path) -> bool:
254
+ """Inject the static inspector script into index.html."""
255
+ content = index_file.read_text(encoding='utf-8')
256
+ marker = 'data-harness-ops-prompt-inspector'
257
+ if marker in content:
258
+ print(f" Static inspector already injected in {index_file}")
259
+ return True
260
+
261
+ script_tag = ' <script src="/public/prompt-inspector-static.js" data-harness-ops-prompt-inspector></script>'
262
+ if '</body>' in content:
263
+ new_content = content.replace('</body>', f'{script_tag}\n</body>')
264
+ else:
265
+ new_content = content + f'\n{script_tag}\n'
266
+
267
+ backup_file = index_file.with_suffix(index_file.suffix + '.backup')
268
+ if not backup_file.exists():
269
+ shutil.copy(index_file, backup_file)
270
+ print(f" Backup created at {backup_file}")
271
+
272
+ index_file.write_text(new_content, encoding='utf-8')
273
+ print(f" Injected static inspector into {index_file}")
274
+ return True
275
+
276
+
277
+ def main():
278
+ if len(sys.argv) < 2:
279
+ print("Usage: python setup.py <project_path> [options]")
280
+ print("\nAutomatically sets up Prompt Inspector in your project.")
281
+ print("\nOptions:")
282
+ print(" --check, -c Check for updates only (no installation)")
283
+ print(" --force, -y Auto-accept updates without prompting")
284
+ return 1
285
+
286
+ # Parse arguments
287
+ args = sys.argv[1:]
288
+ project_path = None
289
+ force_update = False
290
+ check_only = False
291
+
292
+ for arg in args:
293
+ if arg in ('--force', '-y'):
294
+ force_update = True
295
+ elif arg in ('--check', '-c'):
296
+ check_only = True
297
+ elif not project_path:
298
+ project_path = Path(arg).resolve()
299
+
300
+ if not project_path:
301
+ print("Error: Project path is required")
302
+ return 1
303
+
304
+ script_dir = Path(__file__).parent
305
+
306
+ if not project_path.exists():
307
+ print(f"Error: Path '{project_path}' does not exist")
308
+ return 1
309
+
310
+ print(f"Detecting project type in {project_path}...")
311
+
312
+ project_info = find_project_type(project_path)
313
+
314
+ if not project_info['type']:
315
+ print("Could not detect project type.")
316
+ print("Supported: Next.js (App Router / Pages Router), static index.html")
317
+ print("\nManual setup required. Copy PromptInspector.tsx or prompt-inspector-static.js")
318
+ print("and import it in your root layout/app file or index.html.")
319
+ return 1
320
+
321
+ print(f"Detected: {project_info['type']}")
322
+ print(f"Layout file: {project_info['layout_file']}")
323
+ print(f"Components dir: {project_info['components_dir']}")
324
+
325
+ # Check-only mode
326
+ if check_only:
327
+ if project_info['type'] == 'static-html':
328
+ dest = project_info['components_dir'] / 'prompt-inspector-static.js'
329
+ print(f"\n Static install check:")
330
+ print(f" └─ Installed: {'yes' if dest.exists() else 'no'}")
331
+ return 0 if dest.exists() else 1
332
+
333
+ source = script_dir.parent / 'assets' / 'PromptInspector.tsx'
334
+ dest = project_info['components_dir'] / 'PromptInspector.tsx'
335
+
336
+ source_version = get_version_from_file(source)
337
+ dest_version = get_version_from_file(dest)
338
+
339
+ print(f"\n Version Check:")
340
+ print(f" ├─ Installed: {dest_version or 'Not installed'}")
341
+ print(f" └─ Latest: {source_version}")
342
+
343
+ if not dest.exists():
344
+ print("\n ⚠️ PromptInspector is not installed. Run without --check to install.")
345
+ return 1
346
+ elif dest_version and source_version and compare_versions(source_version, dest_version) > 0:
347
+ print("\n 🆕 Update available! Run without --check to update.")
348
+ return 1
349
+ else:
350
+ print("\n ✅ Up to date!")
351
+ return 0
352
+
353
+ print("\nSetting up Prompt Inspector...")
354
+
355
+ if project_info['type'] == 'static-html':
356
+ if not copy_static_inspector(project_info['components_dir'], script_dir, force_update):
357
+ return 1
358
+ if not inject_into_static_html(project_info['layout_file']):
359
+ return 1
360
+ print("\nSetup complete!")
361
+ print("Run your dev server and look for the toolbar in the bottom-right corner.")
362
+ print("The static inspector is intended for development servers only.")
363
+ return 0
364
+
365
+ # Step 1: Copy component
366
+ if not copy_component(project_info['components_dir'], script_dir, force_update):
367
+ return 1
368
+
369
+ # Step 2: Inject into layout
370
+ if not inject_into_layout(project_info['layout_file'], project_info['type']):
371
+ return 1
372
+
373
+ print("\nSetup complete!")
374
+ print("Run your dev server and look for the toolbar in the bottom-right corner.")
375
+ print("The component only loads in development mode.")
376
+
377
+ return 0
378
+
379
+
380
+ if __name__ == '__main__':
381
+ sys.exit(main())
@@ -0,0 +1,41 @@
1
+ import { NextResponse } from "next/server";
2
+ import { execFileSync } from "node:child_process";
3
+ import { existsSync } from "node:fs";
4
+ import path from "node:path";
5
+ import { resolveHarnessRoot } from "@/lib/harness-root";
6
+
7
+ export const dynamic = "force-dynamic";
8
+
9
+ // Per-project hourly-wake launchd control, keyed by HARNESS_BASE_PORT. The
10
+ // dashboard runs locally for the Owner, so it may toggle the Owner's own
11
+ // launchd job. Only the fixed subcommands status|on|off are ever invoked.
12
+ function runControl(action: "status" | "on" | "off") {
13
+ const root = resolveHarnessRoot();
14
+ const script = path.join(root, "scripts", "harness-wake-control.sh");
15
+ if (!existsSync(script)) {
16
+ return { supported: false, enabled: false, error: "harness-wake-control.sh not installed in this project" };
17
+ }
18
+ try {
19
+ const out = execFileSync("bash", [script, action, root], {
20
+ encoding: "utf8",
21
+ timeout: 20000,
22
+ });
23
+ const lastLine = out.trim().split("\n").filter(Boolean).pop() ?? "{}";
24
+ return JSON.parse(lastLine);
25
+ } catch (e) {
26
+ return { supported: false, enabled: false, error: (e as Error).message };
27
+ }
28
+ }
29
+
30
+ export async function GET() {
31
+ return NextResponse.json(runControl("status"));
32
+ }
33
+
34
+ export async function POST(req: Request) {
35
+ const body = (await req.json().catch(() => ({}))) as { action?: string };
36
+ const action = body.action === "on" ? "on" : body.action === "off" ? "off" : null;
37
+ if (!action) {
38
+ return NextResponse.json({ error: "action must be 'on' or 'off'" }, { status: 400 });
39
+ }
40
+ return NextResponse.json(runControl(action));
41
+ }