@walwal-harness/cli 7.1.38 → 7.1.40
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/HR-Resource/cto/SKILL.md +8 -0
- package/HR-Resource/ops/SKILL.md +18 -0
- package/HR-Resource/ops-prompt-inspector/SKILL.md +100 -0
- package/HR-Resource/ops-prompt-inspector/agents/installation-agent.json +7 -0
- package/HR-Resource/ops-prompt-inspector/assets/PromptInspector.tsx +599 -0
- package/HR-Resource/ops-prompt-inspector/references/handler-templates.md +255 -0
- package/HR-Resource/ops-prompt-inspector/scripts/discover_apis.py +509 -0
- package/HR-Resource/ops-prompt-inspector/scripts/install.sh +32 -0
- package/HR-Resource/ops-prompt-inspector/scripts/setup.py +318 -0
- package/apps/harness-dashboard/package-lock.json +43 -42
- package/assets/templates/AGENTS-ko.md.template +15 -2
- package/assets/templates/AGENTS.md.template +17 -2
- package/bin/init.js +2 -2
- package/package.json +1 -1
|
@@ -0,0 +1,318 @@
|
|
|
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 Next.js App Router
|
|
38
|
+
app_layout = project_path / 'app' / 'layout.tsx'
|
|
39
|
+
app_layout_js = project_path / 'app' / 'layout.js'
|
|
40
|
+
|
|
41
|
+
# Check for src directory structure
|
|
42
|
+
src_app_layout = project_path / 'src' / 'app' / 'layout.tsx'
|
|
43
|
+
src_app_layout_js = project_path / 'src' / 'app' / 'layout.js'
|
|
44
|
+
|
|
45
|
+
# Check for Pages Router
|
|
46
|
+
pages_app = project_path / 'pages' / '_app.tsx'
|
|
47
|
+
pages_app_js = project_path / 'pages' / '_app.js'
|
|
48
|
+
src_pages_app = project_path / 'src' / 'pages' / '_app.tsx'
|
|
49
|
+
src_pages_app_js = project_path / 'src' / 'pages' / '_app.js'
|
|
50
|
+
|
|
51
|
+
# Determine layout file
|
|
52
|
+
if app_layout.exists():
|
|
53
|
+
result['type'] = 'nextjs-app'
|
|
54
|
+
result['layout_file'] = app_layout
|
|
55
|
+
result['components_dir'] = project_path / 'components'
|
|
56
|
+
elif app_layout_js.exists():
|
|
57
|
+
result['type'] = 'nextjs-app'
|
|
58
|
+
result['layout_file'] = app_layout_js
|
|
59
|
+
result['components_dir'] = project_path / 'components'
|
|
60
|
+
elif src_app_layout.exists():
|
|
61
|
+
result['type'] = 'nextjs-app'
|
|
62
|
+
result['layout_file'] = src_app_layout
|
|
63
|
+
result['components_dir'] = project_path / 'src' / 'components'
|
|
64
|
+
elif src_app_layout_js.exists():
|
|
65
|
+
result['type'] = 'nextjs-app'
|
|
66
|
+
result['layout_file'] = src_app_layout_js
|
|
67
|
+
result['components_dir'] = project_path / 'src' / 'components'
|
|
68
|
+
elif pages_app.exists():
|
|
69
|
+
result['type'] = 'nextjs-pages'
|
|
70
|
+
result['layout_file'] = pages_app
|
|
71
|
+
result['components_dir'] = project_path / 'components'
|
|
72
|
+
elif pages_app_js.exists():
|
|
73
|
+
result['type'] = 'nextjs-pages'
|
|
74
|
+
result['layout_file'] = pages_app_js
|
|
75
|
+
result['components_dir'] = project_path / 'components'
|
|
76
|
+
elif src_pages_app.exists():
|
|
77
|
+
result['type'] = 'nextjs-pages'
|
|
78
|
+
result['layout_file'] = src_pages_app
|
|
79
|
+
result['components_dir'] = project_path / 'src' / 'components'
|
|
80
|
+
elif src_pages_app_js.exists():
|
|
81
|
+
result['type'] = 'nextjs-pages'
|
|
82
|
+
result['layout_file'] = src_pages_app_js
|
|
83
|
+
result['components_dir'] = project_path / 'src' / 'components'
|
|
84
|
+
|
|
85
|
+
return result
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def compare_versions(v1: str, v2: str) -> int:
|
|
89
|
+
"""Compare two version strings. Returns: -1 if v1 < v2, 0 if equal, 1 if v1 > v2."""
|
|
90
|
+
def parse(v):
|
|
91
|
+
return [int(x) for x in v.split('.')]
|
|
92
|
+
p1, p2 = parse(v1), parse(v2)
|
|
93
|
+
for a, b in zip(p1, p2):
|
|
94
|
+
if a < b:
|
|
95
|
+
return -1
|
|
96
|
+
if a > b:
|
|
97
|
+
return 1
|
|
98
|
+
return 0
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def copy_component(components_dir: Path, script_dir: Path, force_update: bool = False) -> bool:
|
|
102
|
+
"""Copy PromptInspector component to project."""
|
|
103
|
+
components_dir.mkdir(parents=True, exist_ok=True)
|
|
104
|
+
|
|
105
|
+
source = script_dir.parent / 'assets' / 'PromptInspector.tsx'
|
|
106
|
+
dest = components_dir / 'PromptInspector.tsx'
|
|
107
|
+
|
|
108
|
+
if not source.exists():
|
|
109
|
+
print(f" Error: Source component not found at {source}")
|
|
110
|
+
return False
|
|
111
|
+
|
|
112
|
+
source_version = get_version_from_file(source)
|
|
113
|
+
dest_version = get_version_from_file(dest)
|
|
114
|
+
|
|
115
|
+
if dest.exists():
|
|
116
|
+
if dest_version and source_version:
|
|
117
|
+
comparison = compare_versions(source_version, dest_version)
|
|
118
|
+
if comparison > 0:
|
|
119
|
+
# Newer version available
|
|
120
|
+
print(f"\n ╔════════════════════════════════════════════════════════════╗")
|
|
121
|
+
print(f" ║ 🆕 UPDATE AVAILABLE ║")
|
|
122
|
+
print(f" ╠════════════════════════════════════════════════════════════╣")
|
|
123
|
+
print(f" ║ Current version: {dest_version:<10} ║")
|
|
124
|
+
print(f" ║ Latest version: {source_version:<10} ║")
|
|
125
|
+
print(f" ╚════════════════════════════════════════════════════════════╝")
|
|
126
|
+
|
|
127
|
+
if force_update:
|
|
128
|
+
response = 'y'
|
|
129
|
+
else:
|
|
130
|
+
response = input("\n Update to latest version? [Y/n]: ").strip().lower()
|
|
131
|
+
|
|
132
|
+
if response in ('', 'y', 'yes'):
|
|
133
|
+
# Backup existing
|
|
134
|
+
backup = dest.with_suffix('.tsx.backup')
|
|
135
|
+
shutil.copy(dest, backup)
|
|
136
|
+
print(f" 📦 Backup saved to {backup.name}")
|
|
137
|
+
|
|
138
|
+
shutil.copy(source, dest)
|
|
139
|
+
print(f" ✅ Updated to v{source_version}")
|
|
140
|
+
return True
|
|
141
|
+
else:
|
|
142
|
+
print(f" ⏭️ Skipped update (keeping v{dest_version})")
|
|
143
|
+
return True
|
|
144
|
+
elif comparison == 0:
|
|
145
|
+
print(f" ✅ Component is up to date (v{dest_version})")
|
|
146
|
+
return True
|
|
147
|
+
else:
|
|
148
|
+
print(f" ⚠️ Local version (v{dest_version}) is newer than source (v{source_version})")
|
|
149
|
+
return True
|
|
150
|
+
else:
|
|
151
|
+
print(f" Component already exists at {dest}")
|
|
152
|
+
return True
|
|
153
|
+
|
|
154
|
+
shutil.copy(source, dest)
|
|
155
|
+
print(f" ✅ Installed PromptInspector v{source_version}")
|
|
156
|
+
return True
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
def inject_into_layout(layout_file: Path, project_type: str) -> bool:
|
|
160
|
+
"""Inject the component into the layout file."""
|
|
161
|
+
content = layout_file.read_text(encoding='utf-8')
|
|
162
|
+
|
|
163
|
+
# Check if already injected
|
|
164
|
+
if 'PromptInspector' in content:
|
|
165
|
+
print(f" Component already injected in {layout_file}")
|
|
166
|
+
return True
|
|
167
|
+
|
|
168
|
+
lines = content.split('\n')
|
|
169
|
+
new_lines = []
|
|
170
|
+
import_added = False
|
|
171
|
+
component_added = False
|
|
172
|
+
|
|
173
|
+
for i, line in enumerate(lines):
|
|
174
|
+
# Add import after the last import statement
|
|
175
|
+
if not import_added and (line.startswith('import ') or line.startswith('from ')):
|
|
176
|
+
new_lines.append(line)
|
|
177
|
+
# Check if next line is not an import
|
|
178
|
+
if i + 1 < len(lines) and not lines[i + 1].startswith('import ') and not lines[i + 1].startswith('from ') and lines[i + 1].strip() != '':
|
|
179
|
+
new_lines.append(COMPONENT_IMPORT)
|
|
180
|
+
import_added = True
|
|
181
|
+
continue
|
|
182
|
+
|
|
183
|
+
new_lines.append(line)
|
|
184
|
+
|
|
185
|
+
# For App Router: inject before closing </body> or </html>
|
|
186
|
+
if project_type == 'nextjs-app':
|
|
187
|
+
if '</body>' in line and not component_added:
|
|
188
|
+
indent = len(line) - len(line.lstrip())
|
|
189
|
+
new_lines.insert(-1, ' ' * (indent + 2) + COMPONENT_JSX)
|
|
190
|
+
component_added = True
|
|
191
|
+
|
|
192
|
+
# For Pages Router: inject inside Component wrapper
|
|
193
|
+
elif project_type == 'nextjs-pages':
|
|
194
|
+
if '<Component' in line and not component_added:
|
|
195
|
+
# Find the closing of the return statement and inject before it
|
|
196
|
+
pass
|
|
197
|
+
|
|
198
|
+
# If import wasn't added (no imports found), add at top
|
|
199
|
+
if not import_added:
|
|
200
|
+
new_lines.insert(0, COMPONENT_IMPORT)
|
|
201
|
+
new_lines.insert(1, '')
|
|
202
|
+
|
|
203
|
+
# For Pages Router, try different injection point
|
|
204
|
+
if project_type == 'nextjs-pages' and not component_added:
|
|
205
|
+
new_content = '\n'.join(new_lines)
|
|
206
|
+
# Find return statement and inject component
|
|
207
|
+
if 'return (' in new_content or 'return(' in new_content:
|
|
208
|
+
# Add before the closing of the main wrapper
|
|
209
|
+
new_content = new_content.replace(
|
|
210
|
+
'</Component>',
|
|
211
|
+
f'</Component>\n {COMPONENT_JSX}'
|
|
212
|
+
)
|
|
213
|
+
new_lines = new_content.split('\n')
|
|
214
|
+
component_added = True
|
|
215
|
+
|
|
216
|
+
new_content = '\n'.join(new_lines)
|
|
217
|
+
|
|
218
|
+
# Backup original
|
|
219
|
+
backup_file = layout_file.with_suffix(layout_file.suffix + '.backup')
|
|
220
|
+
if not backup_file.exists():
|
|
221
|
+
shutil.copy(layout_file, backup_file)
|
|
222
|
+
print(f" Backup created at {backup_file}")
|
|
223
|
+
|
|
224
|
+
layout_file.write_text(new_content, encoding='utf-8')
|
|
225
|
+
print(f" Injected component into {layout_file}")
|
|
226
|
+
|
|
227
|
+
return True
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
def main():
|
|
231
|
+
if len(sys.argv) < 2:
|
|
232
|
+
print("Usage: python setup.py <project_path> [options]")
|
|
233
|
+
print("\nAutomatically sets up Prompt Inspector in your project.")
|
|
234
|
+
print("\nOptions:")
|
|
235
|
+
print(" --check, -c Check for updates only (no installation)")
|
|
236
|
+
print(" --force, -y Auto-accept updates without prompting")
|
|
237
|
+
return 1
|
|
238
|
+
|
|
239
|
+
# Parse arguments
|
|
240
|
+
args = sys.argv[1:]
|
|
241
|
+
project_path = None
|
|
242
|
+
force_update = False
|
|
243
|
+
check_only = False
|
|
244
|
+
|
|
245
|
+
for arg in args:
|
|
246
|
+
if arg in ('--force', '-y'):
|
|
247
|
+
force_update = True
|
|
248
|
+
elif arg in ('--check', '-c'):
|
|
249
|
+
check_only = True
|
|
250
|
+
elif not project_path:
|
|
251
|
+
project_path = Path(arg).resolve()
|
|
252
|
+
|
|
253
|
+
if not project_path:
|
|
254
|
+
print("Error: Project path is required")
|
|
255
|
+
return 1
|
|
256
|
+
|
|
257
|
+
script_dir = Path(__file__).parent
|
|
258
|
+
|
|
259
|
+
if not project_path.exists():
|
|
260
|
+
print(f"Error: Path '{project_path}' does not exist")
|
|
261
|
+
return 1
|
|
262
|
+
|
|
263
|
+
print(f"Detecting project type in {project_path}...")
|
|
264
|
+
|
|
265
|
+
project_info = find_project_type(project_path)
|
|
266
|
+
|
|
267
|
+
if not project_info['type']:
|
|
268
|
+
print("Could not detect project type.")
|
|
269
|
+
print("Supported: Next.js (App Router / Pages Router)")
|
|
270
|
+
print("\nManual setup required. Copy PromptInspector.tsx to your components folder")
|
|
271
|
+
print("and import it in your root layout/app file.")
|
|
272
|
+
return 1
|
|
273
|
+
|
|
274
|
+
print(f"Detected: {project_info['type']}")
|
|
275
|
+
print(f"Layout file: {project_info['layout_file']}")
|
|
276
|
+
print(f"Components dir: {project_info['components_dir']}")
|
|
277
|
+
|
|
278
|
+
# Check-only mode
|
|
279
|
+
if check_only:
|
|
280
|
+
source = script_dir.parent / 'assets' / 'PromptInspector.tsx'
|
|
281
|
+
dest = project_info['components_dir'] / 'PromptInspector.tsx'
|
|
282
|
+
|
|
283
|
+
source_version = get_version_from_file(source)
|
|
284
|
+
dest_version = get_version_from_file(dest)
|
|
285
|
+
|
|
286
|
+
print(f"\n Version Check:")
|
|
287
|
+
print(f" ├─ Installed: {dest_version or 'Not installed'}")
|
|
288
|
+
print(f" └─ Latest: {source_version}")
|
|
289
|
+
|
|
290
|
+
if not dest.exists():
|
|
291
|
+
print("\n ⚠️ PromptInspector is not installed. Run without --check to install.")
|
|
292
|
+
return 1
|
|
293
|
+
elif dest_version and source_version and compare_versions(source_version, dest_version) > 0:
|
|
294
|
+
print("\n 🆕 Update available! Run without --check to update.")
|
|
295
|
+
return 1
|
|
296
|
+
else:
|
|
297
|
+
print("\n ✅ Up to date!")
|
|
298
|
+
return 0
|
|
299
|
+
|
|
300
|
+
print("\nSetting up Prompt Inspector...")
|
|
301
|
+
|
|
302
|
+
# Step 1: Copy component
|
|
303
|
+
if not copy_component(project_info['components_dir'], script_dir, force_update):
|
|
304
|
+
return 1
|
|
305
|
+
|
|
306
|
+
# Step 2: Inject into layout
|
|
307
|
+
if not inject_into_layout(project_info['layout_file'], project_info['type']):
|
|
308
|
+
return 1
|
|
309
|
+
|
|
310
|
+
print("\nSetup complete!")
|
|
311
|
+
print("Run your dev server and look for the toolbar in the bottom-right corner.")
|
|
312
|
+
print("The component only loads in development mode.")
|
|
313
|
+
|
|
314
|
+
return 0
|
|
315
|
+
|
|
316
|
+
|
|
317
|
+
if __name__ == '__main__':
|
|
318
|
+
sys.exit(main())
|
|
@@ -985,15 +985,15 @@
|
|
|
985
985
|
}
|
|
986
986
|
},
|
|
987
987
|
"node_modules/@next/env": {
|
|
988
|
-
"version": "15.5.
|
|
989
|
-
"resolved": "https://registry.npmjs.org/@next/env/-/env-15.5.
|
|
990
|
-
"integrity": "sha512-
|
|
988
|
+
"version": "15.5.16",
|
|
989
|
+
"resolved": "https://registry.npmjs.org/@next/env/-/env-15.5.16.tgz",
|
|
990
|
+
"integrity": "sha512-9QMKolCl+JnJtaRAQSXy4RQrhgfe8W7/G1+Hl3QSB/HZY7zQMzTwPDdTRwwio8BS96ps1MHpHhbS8qxoNV3JIQ==",
|
|
991
991
|
"license": "MIT"
|
|
992
992
|
},
|
|
993
993
|
"node_modules/@next/swc-darwin-arm64": {
|
|
994
|
-
"version": "15.5.
|
|
995
|
-
"resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-15.5.
|
|
996
|
-
"integrity": "sha512-
|
|
994
|
+
"version": "15.5.16",
|
|
995
|
+
"resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-15.5.16.tgz",
|
|
996
|
+
"integrity": "sha512-wzdER4JZj+31vNkhaZ1Ght3IsNI8DMwj7VqadfIOqJB5sh8FiOqNSopYADQn6mgEPomzDd/DHqBcfo2fmVMYtg==",
|
|
997
997
|
"cpu": [
|
|
998
998
|
"arm64"
|
|
999
999
|
],
|
|
@@ -1007,9 +1007,9 @@
|
|
|
1007
1007
|
}
|
|
1008
1008
|
},
|
|
1009
1009
|
"node_modules/@next/swc-darwin-x64": {
|
|
1010
|
-
"version": "15.5.
|
|
1011
|
-
"resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-15.5.
|
|
1012
|
-
"integrity": "sha512-
|
|
1010
|
+
"version": "15.5.16",
|
|
1011
|
+
"resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-15.5.16.tgz",
|
|
1012
|
+
"integrity": "sha512-PPTo+cvcanxkuDEuDyZGk28ntmu0WjfkxqlG7hw9Mhsiribs4x1C6h2Culn0cJKqsne1gFjjZRK3ax7WYlSxgg==",
|
|
1013
1013
|
"cpu": [
|
|
1014
1014
|
"x64"
|
|
1015
1015
|
],
|
|
@@ -1023,9 +1023,9 @@
|
|
|
1023
1023
|
}
|
|
1024
1024
|
},
|
|
1025
1025
|
"node_modules/@next/swc-linux-arm64-gnu": {
|
|
1026
|
-
"version": "15.5.
|
|
1027
|
-
"resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-15.5.
|
|
1028
|
-
"integrity": "sha512-
|
|
1026
|
+
"version": "15.5.16",
|
|
1027
|
+
"resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-15.5.16.tgz",
|
|
1028
|
+
"integrity": "sha512-Jl0IL9P7S8uNl5oI1TqrQmfmLp7OqjWM58000pVnUVIsHrvPP6m9QDW/uNWYUbmd+8IYvc6MTeZKICstBMBpew==",
|
|
1029
1029
|
"cpu": [
|
|
1030
1030
|
"arm64"
|
|
1031
1031
|
],
|
|
@@ -1039,9 +1039,9 @@
|
|
|
1039
1039
|
}
|
|
1040
1040
|
},
|
|
1041
1041
|
"node_modules/@next/swc-linux-arm64-musl": {
|
|
1042
|
-
"version": "15.5.
|
|
1043
|
-
"resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-15.5.
|
|
1044
|
-
"integrity": "sha512-
|
|
1042
|
+
"version": "15.5.16",
|
|
1043
|
+
"resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-15.5.16.tgz",
|
|
1044
|
+
"integrity": "sha512-Zf0BIqv/o5uOWfyRkzgGhyV2Tky7HLt0bG+w7XWdaU1JpyX0tltM3TrSfa/Y9c597SJG4CzN47+u2InhgZZ4vg==",
|
|
1045
1045
|
"cpu": [
|
|
1046
1046
|
"arm64"
|
|
1047
1047
|
],
|
|
@@ -1055,9 +1055,9 @@
|
|
|
1055
1055
|
}
|
|
1056
1056
|
},
|
|
1057
1057
|
"node_modules/@next/swc-linux-x64-gnu": {
|
|
1058
|
-
"version": "15.5.
|
|
1059
|
-
"resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-15.5.
|
|
1060
|
-
"integrity": "sha512-
|
|
1058
|
+
"version": "15.5.16",
|
|
1059
|
+
"resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-15.5.16.tgz",
|
|
1060
|
+
"integrity": "sha512-HCDDU1TRLeUDV180QQTWrs5Oa4lIcI7XH9nF0UVUVmYLN/boZ6LqyFtm3814gc1fv+lOVyKaw5B6bVC9BpXTSQ==",
|
|
1061
1061
|
"cpu": [
|
|
1062
1062
|
"x64"
|
|
1063
1063
|
],
|
|
@@ -1071,9 +1071,9 @@
|
|
|
1071
1071
|
}
|
|
1072
1072
|
},
|
|
1073
1073
|
"node_modules/@next/swc-linux-x64-musl": {
|
|
1074
|
-
"version": "15.5.
|
|
1075
|
-
"resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-15.5.
|
|
1076
|
-
"integrity": "sha512-
|
|
1074
|
+
"version": "15.5.16",
|
|
1075
|
+
"resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-15.5.16.tgz",
|
|
1076
|
+
"integrity": "sha512-kvXUY1dn5wxKuMkXxQRUbPjEnKxW1PR9uKOm0zpIpj3574+cFfaePhYFmBVtrOuwt+w34OdDzNaJr5Iixf+HBQ==",
|
|
1077
1077
|
"cpu": [
|
|
1078
1078
|
"x64"
|
|
1079
1079
|
],
|
|
@@ -1087,9 +1087,9 @@
|
|
|
1087
1087
|
}
|
|
1088
1088
|
},
|
|
1089
1089
|
"node_modules/@next/swc-win32-arm64-msvc": {
|
|
1090
|
-
"version": "15.5.
|
|
1091
|
-
"resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-15.5.
|
|
1092
|
-
"integrity": "sha512-
|
|
1090
|
+
"version": "15.5.16",
|
|
1091
|
+
"resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-15.5.16.tgz",
|
|
1092
|
+
"integrity": "sha512-zpOQuF+eyENMXRjglp2hZCIrUjTdO37suEBnDn1mX4PXSuetXZDMLpjKOh4dYSw3SiDTnOoOUwBl5i5Elr6nnQ==",
|
|
1093
1093
|
"cpu": [
|
|
1094
1094
|
"arm64"
|
|
1095
1095
|
],
|
|
@@ -1103,9 +1103,9 @@
|
|
|
1103
1103
|
}
|
|
1104
1104
|
},
|
|
1105
1105
|
"node_modules/@next/swc-win32-x64-msvc": {
|
|
1106
|
-
"version": "15.5.
|
|
1107
|
-
"resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-15.5.
|
|
1108
|
-
"integrity": "sha512-
|
|
1106
|
+
"version": "15.5.16",
|
|
1107
|
+
"resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-15.5.16.tgz",
|
|
1108
|
+
"integrity": "sha512-LnwKYpiSmIzXlTq76hMeeIzZoDcFwu848p6H+QBkGFJIbZphgzNUPdHruJcHM/bFnaFeco0l1Frie5I27VKglA==",
|
|
1109
1109
|
"cpu": [
|
|
1110
1110
|
"x64"
|
|
1111
1111
|
],
|
|
@@ -1687,9 +1687,9 @@
|
|
|
1687
1687
|
"license": "MIT"
|
|
1688
1688
|
},
|
|
1689
1689
|
"node_modules/@types/node": {
|
|
1690
|
-
"version": "22.19.
|
|
1691
|
-
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.
|
|
1692
|
-
"integrity": "sha512-
|
|
1690
|
+
"version": "22.19.17",
|
|
1691
|
+
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.17.tgz",
|
|
1692
|
+
"integrity": "sha512-wGdMcf+vPYM6jikpS/qhg6WiqSV/OhG+jeeHT/KlVqxYfD40iYJf9/AE1uQxVWFvU7MipKRkRv8NSHiCGgPr8Q==",
|
|
1693
1693
|
"dev": true,
|
|
1694
1694
|
"license": "MIT",
|
|
1695
1695
|
"dependencies": {
|
|
@@ -2660,6 +2660,7 @@
|
|
|
2660
2660
|
"version": "2.3.2",
|
|
2661
2661
|
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
|
|
2662
2662
|
"integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
|
|
2663
|
+
"dev": true,
|
|
2663
2664
|
"hasInstallScript": true,
|
|
2664
2665
|
"license": "MIT",
|
|
2665
2666
|
"optional": true,
|
|
@@ -3947,12 +3948,12 @@
|
|
|
3947
3948
|
}
|
|
3948
3949
|
},
|
|
3949
3950
|
"node_modules/next": {
|
|
3950
|
-
"version": "15.5.
|
|
3951
|
-
"resolved": "https://registry.npmjs.org/next/-/next-15.5.
|
|
3952
|
-
"integrity": "sha512-
|
|
3951
|
+
"version": "15.5.16",
|
|
3952
|
+
"resolved": "https://registry.npmjs.org/next/-/next-15.5.16.tgz",
|
|
3953
|
+
"integrity": "sha512-aZExBk/V6JCu3NCFc90twdj9L/M3y0+ukeQwUAZbOiqRhAX+h2oMEa0NZFhcpj6HYRYjVS3V2/3xvyOpNnmw7A==",
|
|
3953
3954
|
"license": "MIT",
|
|
3954
3955
|
"dependencies": {
|
|
3955
|
-
"@next/env": "15.5.
|
|
3956
|
+
"@next/env": "15.5.16",
|
|
3956
3957
|
"@swc/helpers": "0.5.15",
|
|
3957
3958
|
"caniuse-lite": "^1.0.30001579",
|
|
3958
3959
|
"postcss": "8.4.31",
|
|
@@ -3965,14 +3966,14 @@
|
|
|
3965
3966
|
"node": "^18.18.0 || ^19.8.0 || >= 20.0.0"
|
|
3966
3967
|
},
|
|
3967
3968
|
"optionalDependencies": {
|
|
3968
|
-
"@next/swc-darwin-arm64": "15.5.
|
|
3969
|
-
"@next/swc-darwin-x64": "15.5.
|
|
3970
|
-
"@next/swc-linux-arm64-gnu": "15.5.
|
|
3971
|
-
"@next/swc-linux-arm64-musl": "15.5.
|
|
3972
|
-
"@next/swc-linux-x64-gnu": "15.5.
|
|
3973
|
-
"@next/swc-linux-x64-musl": "15.5.
|
|
3974
|
-
"@next/swc-win32-arm64-msvc": "15.5.
|
|
3975
|
-
"@next/swc-win32-x64-msvc": "15.5.
|
|
3969
|
+
"@next/swc-darwin-arm64": "15.5.16",
|
|
3970
|
+
"@next/swc-darwin-x64": "15.5.16",
|
|
3971
|
+
"@next/swc-linux-arm64-gnu": "15.5.16",
|
|
3972
|
+
"@next/swc-linux-arm64-musl": "15.5.16",
|
|
3973
|
+
"@next/swc-linux-x64-gnu": "15.5.16",
|
|
3974
|
+
"@next/swc-linux-x64-musl": "15.5.16",
|
|
3975
|
+
"@next/swc-win32-arm64-msvc": "15.5.16",
|
|
3976
|
+
"@next/swc-win32-x64-msvc": "15.5.16",
|
|
3976
3977
|
"sharp": "^0.34.3"
|
|
3977
3978
|
},
|
|
3978
3979
|
"peerDependencies": {
|
|
@@ -172,7 +172,19 @@ Owner는 최종 검수자다. Tester, QA worker, debugger, 배포 확인 담당
|
|
|
172
172
|
|
|
173
173
|
`.harness/`는 미션 상태 저장소다. 빌드 산출물이 아니다. 삭제하지 않는다.
|
|
174
174
|
|
|
175
|
-
## 10.
|
|
175
|
+
## 10. 브라우저 자동화
|
|
176
|
+
|
|
177
|
+
CEO, CXX, hired worker의 모든 Playwright 사용은 실제로 보이는 브라우저 창으로 실행한다.
|
|
178
|
+
|
|
179
|
+
- Playwright launch/config 코드에는 `headless: false`를 사용한다.
|
|
180
|
+
- Playwright test 실행은 headed mode(`--headed`, `PWDEBUG=1`, 또는 동등한 설정)로 수행한다.
|
|
181
|
+
- 설치된 Chrome을 사용할 수 있으면 `channel: 'chrome'`을 우선한다.
|
|
182
|
+
- Owner가 명시적으로 예외를 승인하지 않는 한 headless Playwright 실행을 추가, 권장, 의존하지 않는다.
|
|
183
|
+
- CTO와 OPS는 Playwright, browser automation, E2E, visual check, browser-based monitoring이 포함될 수 있는 모든 worker brief에 `headless: false` 요구사항을 명시적으로 주입한다.
|
|
184
|
+
- 브라우저 동작을 사람이 관찰해야 할 때는 적절한 `slowMo` 또는 동등한 단계 지연을 둔다.
|
|
185
|
+
- CQO evaluator evidence에는 Playwright/browser 실행이 headed 또는 visible이었다는 사실을 기록한다.
|
|
186
|
+
|
|
187
|
+
## 11. 금지 규칙
|
|
176
188
|
|
|
177
189
|
1. **`{mission}/cto.md` 없이 소스 코드 편집 금지** — CTO의 범위 승인이 있어야 소스 파일을 수정할 수 있다.
|
|
178
190
|
2. **CXX 사칭 금지** — 현재 모델이 CEO/CTO/CQO를 인라인으로 대행하지 않는다. 설치된 harness skill을 fresh session에서 사용한다.
|
|
@@ -184,10 +196,11 @@ Owner는 최종 검수자다. Tester, QA worker, debugger, 배포 확인 담당
|
|
|
184
196
|
8. **OPS는 CQO 검수 중 관제한다** — 실행 가능한 제품에서 CQO PASS는 OPS가 검수 runtime을 관제했거나 OPS watch가 불필요한 이유를 기록했을 때만 유효하다. 열린 OPS incident, runtime mapping 누락, 필수 로그 누락, service down, health mismatch, 검수 대상 service 미관제는 Owner acceptance를 막는다.
|
|
185
197
|
9. **Production incident는 회사 이벤트다** — 오픈 후 사용자 영향 OPS signal은 CEO를 통해 CTO/CQO/OPS로 라우팅된다. CTO는 복구, CQO는 회귀 검증, OPS는 증거와 close criteria를 책임진다.
|
|
186
198
|
10. **Implementation Notes는 같은 보고서 하단에 작성** — `ceo.md`, 모든 `{cxx}.md`, 모든 worker report는 하나의 English `## Implementation Notes` 섹션으로 끝나야 한다. 별도 sidecar notes 파일을 만들지 않는다. 결정/증거를 만든 동일한 role 또는 worker report 하단에 `Design Decisions`, `Deviations`, `Tradeoffs`, `Open Questions`를 작성하고 빈 항목은 `None`을 사용한다.
|
|
199
|
+
11. **Playwright는 visible이 기본값이다** — 모든 Playwright/browser automation은 headed/visible mode(`headless: false` 또는 동등한 설정)를 사용한다. Headless Playwright는 mission에 기록된 Owner의 명시적 승인이 필요하다.
|
|
187
200
|
|
|
188
201
|
---
|
|
189
202
|
|
|
190
|
-
##
|
|
203
|
+
## 12. 프로젝트 컨텍스트
|
|
191
204
|
|
|
192
205
|
- **기술 스택**: {{TECH_STACK}}
|
|
193
206
|
- **프로젝트 구조**: {{PROJECT_STRUCTURE}}
|
|
@@ -141,7 +141,21 @@ Note: A docmeta skip decision on harness documents (ceo.md, cto.md, cqo.md, work
|
|
|
141
141
|
|
|
142
142
|
---
|
|
143
143
|
|
|
144
|
-
## 7.
|
|
144
|
+
## 7. Browser Automation
|
|
145
|
+
|
|
146
|
+
All Playwright usage by CEO, CXX, and hired workers must run with a visible real browser window.
|
|
147
|
+
|
|
148
|
+
- Use `headless: false` in Playwright launch/config code.
|
|
149
|
+
- Use headed mode for Playwright test runs (`--headed`, `PWDEBUG=1`, or equivalent config).
|
|
150
|
+
- Prefer `channel: 'chrome'` when the installed Chrome browser is available.
|
|
151
|
+
- Do not add, recommend, or rely on headless Playwright execution unless the Owner explicitly approves an exception.
|
|
152
|
+
- CTO and OPS must explicitly inject the `headless: false` requirement into every worker brief that may involve Playwright, browser automation, E2E, visual checks, or browser-based monitoring.
|
|
153
|
+
- When browser actions should be observable, add a reasonable `slowMo` value or equivalent step pacing.
|
|
154
|
+
- CQO evaluator evidence must note that the Playwright/browser run was headed or visible.
|
|
155
|
+
|
|
156
|
+
---
|
|
157
|
+
|
|
158
|
+
## 8. Hard Rules
|
|
145
159
|
|
|
146
160
|
1. **No source edit without `{mission}/cto.md`** — CTO scope sign-off is required before any source file is modified.
|
|
147
161
|
2. **No CXX impersonation** — The active model must not act as CEO/CTO/CQO inline. Use installed harness skills in fresh sessions.
|
|
@@ -160,10 +174,11 @@ Note: A docmeta skip decision on harness documents (ceo.md, cto.md, cqo.md, work
|
|
|
160
174
|
15. **Owner is final acceptance only** — Owner is not a tester or QA substitute. CEO/CXX must not report "done, please check" until worker-backed verification proves the goal can be completed. Use unit tests, E2E, Playwright, test accounts, seeded data, build/run checks, logs, and CQO evidence before requesting Owner acceptance.
|
|
161
175
|
16. **OPS watches CQO verification** — For runnable products, CQO PASS is invalid unless OPS has monitored the verification runtime or documented why OPS watch is not applicable. Open OPS incidents, missing runtime mapping, required log gaps, service down, health mismatch, or an unmonitored tested service block Owner acceptance.
|
|
162
176
|
17. **Production incidents are company events** — After launch, user-impacting OPS signals route through CEO to CTO/CQO/OPS. CTO owns recovery, CQO owns regression confirmation, and OPS owns evidence plus close criteria.
|
|
177
|
+
18. **Playwright is visible by default** — All Playwright/browser automation must use headed/visible mode (`headless: false` or equivalent). Headless Playwright requires explicit Owner approval recorded in the mission.
|
|
163
178
|
|
|
164
179
|
---
|
|
165
180
|
|
|
166
|
-
##
|
|
181
|
+
## 9. Project Context
|
|
167
182
|
|
|
168
183
|
- **Tech Stack**: {{TECH_STACK}}
|
|
169
184
|
- **Structure**: {{PROJECT_STRUCTURE}}
|
package/bin/init.js
CHANGED
|
@@ -912,7 +912,7 @@ function installSkills() {
|
|
|
912
912
|
log(` Installed: ${destName}`);
|
|
913
913
|
};
|
|
914
914
|
|
|
915
|
-
const coreHrSkills = ['ceo', 'coo', 'cdo', 'cto', 'cqo', 'ops', 'hiring', 'resource-manager', 'brick-office'];
|
|
915
|
+
const coreHrSkills = ['ceo', 'coo', 'cdo', 'cto', 'cqo', 'ops', 'hiring', 'resource-manager', 'brick-office', 'ops-prompt-inspector'];
|
|
916
916
|
if (fs.existsSync(hrSrc)) {
|
|
917
917
|
for (const skill of coreHrSkills) {
|
|
918
918
|
const src = path.join(hrSrc, skill);
|
|
@@ -1552,7 +1552,7 @@ function detectMigrationNeeded() {
|
|
|
1552
1552
|
bundleVersionStale: null, // { current, installed }
|
|
1553
1553
|
};
|
|
1554
1554
|
|
|
1555
|
-
const coreSkills = ['ceo', 'coo', 'cdo', 'cto', 'cqo', 'ops', 'hiring', 'resource-manager', 'brick-office'];
|
|
1555
|
+
const coreSkills = ['ceo', 'coo', 'cdo', 'cto', 'cqo', 'ops', 'hiring', 'resource-manager', 'brick-office', 'ops-prompt-inspector'];
|
|
1556
1556
|
for (const skill of coreSkills) {
|
|
1557
1557
|
const srcPath = path.join(PKG_ROOT, 'HR-Resource', skill, 'SKILL.md');
|
|
1558
1558
|
if (!fs.existsSync(srcPath)) continue;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@walwal-harness/cli",
|
|
3
|
-
"version": "7.1.
|
|
3
|
+
"version": "7.1.40",
|
|
4
4
|
"description": "Company-style AI agent harness for Claude and Codex. Installs commands, CXX agents, skills, HR-Resource hiring pool, and project-local .harness runtime state.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"walwal-harness": "bin/init.js"
|