vibe-coding-master 0.7.42 → 0.7.44

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 (50) hide show
  1. package/README.md +49 -17
  2. package/dist/backend/api/artifact-routes.js +3 -0
  3. package/dist/backend/api/harness-routes.js +16 -0
  4. package/dist/backend/api/task-routes.js +32 -2
  5. package/dist/backend/api/workflow-control-routes.js +7 -26
  6. package/dist/backend/cli/install-vcm-harness.js +61 -6
  7. package/dist/backend/role-tool-policy.js +1 -1
  8. package/dist/backend/server.js +14 -5
  9. package/dist/backend/services/artifact-service.js +10 -32
  10. package/dist/backend/services/auto-memory-service.js +642 -14
  11. package/dist/backend/services/claude-hook-service.js +183 -6
  12. package/dist/backend/services/gate-review-service.js +173 -74
  13. package/dist/backend/services/harness-feedback-service.js +180 -80
  14. package/dist/backend/services/harness-service.js +65 -9
  15. package/dist/backend/services/memory-review-paths.js +13 -0
  16. package/dist/backend/services/role-stall-detector-service.js +322 -0
  17. package/dist/backend/services/round-service.js +25 -0
  18. package/dist/backend/services/runtime-coordinator-service.js +12 -1
  19. package/dist/backend/services/session-service.js +70 -3
  20. package/dist/backend/services/status-service.js +1 -0
  21. package/dist/backend/services/translation-worker-service.js +19 -4
  22. package/dist/backend/services/workflow-control-service.js +524 -204
  23. package/dist/backend/templates/handoff.js +46 -5
  24. package/dist/backend/templates/harness/architect-agent.js +12 -6
  25. package/dist/backend/templates/harness/architect-scaffold-worker-agent.js +1 -1
  26. package/dist/backend/templates/harness/check-scaffold-ledger.js +234 -10
  27. package/dist/backend/templates/harness/claude-root.js +3 -2
  28. package/dist/backend/templates/harness/coder-agent.js +12 -5
  29. package/dist/backend/templates/harness/gate-review.js +150 -57
  30. package/dist/backend/templates/harness/harness-engineer-agent.js +38 -13
  31. package/dist/backend/templates/harness/project-manager-agent.js +18 -12
  32. package/dist/backend/templates/harness/resolve-durable-doc-assignment.js +60 -0
  33. package/dist/backend/templates/harness/tester-agent.js +13 -0
  34. package/dist/backend/templates/harness/vcm-ask-user-skill.js +82 -0
  35. package/dist/backend/templates/harness/vcm-code-navigation-skill.js +7 -6
  36. package/dist/backend/templates/harness/vcm-task-state-skill.js +2 -2
  37. package/dist/backend/templates/harness/vcm-workflow-review-skill.js +7 -9
  38. package/dist/shared/types/role-stall.js +1 -0
  39. package/dist/shared/types/workflow.js +15 -0
  40. package/dist/shared/validation/artifact-check.js +3 -3
  41. package/dist/shared/validation/artifact-contract.js +1 -1
  42. package/dist/shared/validation/artifact-registry.js +17 -1
  43. package/dist-frontend/assets/{index-C_XHGNBD.css → index-B0d4Z6ny.css} +1 -1
  44. package/dist-frontend/assets/index-BvCmrFlN.js +97 -0
  45. package/dist-frontend/index.html +2 -2
  46. package/package.json +1 -1
  47. package/scripts/claude-plugins/vcm-lsp-bridge/.claude-plugin/plugin.json +21 -6
  48. package/scripts/harness-tools/vcm-artifact +1 -2
  49. package/scripts/harness-tools/vcm-bash-guard +204 -14
  50. package/dist-frontend/assets/index-Bocc2DWF.js +0 -97
@@ -4,8 +4,8 @@
4
4
  <meta charset="UTF-8" />
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
6
  <title>VibeCodingMaster</title>
7
- <script type="module" crossorigin src="/assets/index-Bocc2DWF.js"></script>
8
- <link rel="stylesheet" crossorigin href="/assets/index-C_XHGNBD.css">
7
+ <script type="module" crossorigin src="/assets/index-BvCmrFlN.js"></script>
8
+ <link rel="stylesheet" crossorigin href="/assets/index-B0d4Z6ny.css">
9
9
  </head>
10
10
  <body>
11
11
  <div id="root"></div>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vibe-coding-master",
3
- "version": "0.7.42",
3
+ "version": "0.7.44",
4
4
  "description": "Local GUI session cockpit for Claude Code role sessions.",
5
5
  "type": "module",
6
6
  "files": [
@@ -11,7 +11,12 @@
11
11
  "extensionToLanguage": {
12
12
  ".rs": "rust"
13
13
  },
14
- "startupTimeout": 180000
14
+ "initializationOptions": {
15
+ "checkOnSave": false
16
+ },
17
+ "startupTimeout": 180000,
18
+ "restartOnCrash": true,
19
+ "maxRestarts": 3
15
20
  },
16
21
  "typescript": {
17
22
  "command": "typescript-language-server",
@@ -27,7 +32,9 @@
27
32
  ".cts": "typescript",
28
33
  ".mjs": "javascript",
29
34
  ".cjs": "javascript"
30
- }
35
+ },
36
+ "restartOnCrash": true,
37
+ "maxRestarts": 3
31
38
  },
32
39
  "pyright": {
33
40
  "command": "pyright-langserver",
@@ -37,13 +44,17 @@
37
44
  "extensionToLanguage": {
38
45
  ".py": "python",
39
46
  ".pyi": "python"
40
- }
47
+ },
48
+ "restartOnCrash": true,
49
+ "maxRestarts": 3
41
50
  },
42
51
  "gopls": {
43
52
  "command": "gopls",
44
53
  "extensionToLanguage": {
45
54
  ".go": "go"
46
- }
55
+ },
56
+ "restartOnCrash": true,
57
+ "maxRestarts": 3
47
58
  },
48
59
  "clangd": {
49
60
  "command": "clangd",
@@ -60,14 +71,18 @@
60
71
  ".hxx": "cpp",
61
72
  ".C": "cpp",
62
73
  ".H": "cpp"
63
- }
74
+ },
75
+ "restartOnCrash": true,
76
+ "maxRestarts": 3
64
77
  },
65
78
  "jdtls": {
66
79
  "command": "jdtls",
67
80
  "extensionToLanguage": {
68
81
  ".java": "java"
69
82
  },
70
- "startupTimeout": 120000
83
+ "startupTimeout": 120000,
84
+ "restartOnCrash": true,
85
+ "maxRestarts": 3
71
86
  }
72
87
  }
73
88
  }
@@ -20,6 +20,7 @@ KINDS = (
20
20
  "architect-debug",
21
21
  "architecture-diagnosis",
22
22
  "test-report",
23
+ "docs-update-report",
23
24
  "docs-sync-report",
24
25
  "workflow-progress",
25
26
  "final-acceptance",
@@ -28,7 +29,6 @@ KINDS = (
28
29
  "gate-review-report",
29
30
  "memory-proposal",
30
31
  "harness-feedback",
31
- "retrospective-report",
32
32
  )
33
33
  DYNAMIC_KINDS = {
34
34
  "route-message",
@@ -36,7 +36,6 @@ DYNAMIC_KINDS = {
36
36
  "gate-review-report",
37
37
  "memory-proposal",
38
38
  "harness-feedback",
39
- "retrospective-report",
40
39
  }
41
40
 
42
41
 
@@ -11,6 +11,7 @@ supervised foreground process group.
11
11
  """
12
12
  import json
13
13
  import re
14
+ import shlex
14
15
  import sys
15
16
 
16
17
  SKILL_HINT = (
@@ -32,6 +33,14 @@ PROTECTED_TOOL_INVOCATION = re.compile(
32
33
  r"(?:(?:[A-Za-z_][A-Za-z0-9_]*=[^\s;&|()]+)\s+)*"
33
34
  r"(?:[^\s;&|()]*/)?(?:run-long-check|watch-job)(?=\s|$)"
34
35
  )
36
+ HEREDOC_START = re.compile(
37
+ r"<<(?!<)(-?)\s*(?:(['\"])([^'\"]+)\2|([^\s;&|()<>]+))"
38
+ )
39
+ EXECUTABLE_HEREDOC_RECEIVER = re.compile(
40
+ r"(?:^|[;&|()])\s*"
41
+ r"(?:(?:env\s+)?(?:[A-Za-z_][A-Za-z0-9_]*=[^\s;&|()]+\s+)*)"
42
+ r"(?:[^\s;&|()]*/)?(?:sh|bash|zsh|dash|ksh|python\d*(?:\.\d+)?|node)(?=\s|$)"
43
+ )
35
44
  SHELL_CONTROL_OPERATOR = re.compile(r"(?:\|&?|&&|\|\||;|\n|\(|\)|`)")
36
45
  RUN_LONG_CHECK_SHELL_STRING = re.compile(
37
46
  r"(?:^|[;&|()\n])\s*"
@@ -47,14 +56,26 @@ MANAGED_ARTIFACT_PATH = re.compile(
47
56
  r"gate-reviews[/\\]requests[/\\].*\.report\.md|"
48
57
  r"memory-review[/\\].*\.md|"
49
58
  r"coder-workers[/\\]reports[/\\].*\.md|"
50
- r"harness-feedback[/\\](?:pending|task-retrospectives)[/\\].*\.md"
59
+ r"harness-feedback[/\\]pending[/\\].*\.md"
51
60
  r")"
52
61
  )
53
- SHELL_WRITE = re.compile(
54
- r"(?:>>?|\btee\b|\brm\b|\bmv\b|\bcp\b|"
55
- r"\bsed\b[^\n]*\s-i\b|\bperl\b[^\n]*\s-i\b|"
56
- r"\bpython\d*\b|\bnode\b)"
62
+ SHELL_SEPARATOR_TOKEN = re.compile(r"^[;&|()]+$")
63
+ SHELL_ASSIGNMENT = re.compile(r"^[A-Za-z_][A-Za-z0-9_]*=")
64
+ PYTHON_PATH_MUTATION = re.compile(
65
+ r"\bPath\s*\(\s*(['\"])(?P<path>[^'\"]+)\1\s*\)\s*\.\s*"
66
+ r"(?:write_text|write_bytes|unlink|rmdir|touch|rename|replace)\s*\("
67
+ )
68
+ PYTHON_OPEN = re.compile(
69
+ r"\bopen\s*\(\s*(['\"])(?P<path>[^'\"]+)\1\s*,\s*"
70
+ r"(['\"])(?P<mode>[^'\"]+)\3"
57
71
  )
72
+ NODE_PATH_MUTATION = re.compile(
73
+ r"\b(?:writeFile|writeFileSync|appendFile|appendFileSync|unlink|unlinkSync|"
74
+ r"rm|rmSync|rmdir|rmdirSync|createWriteStream)\s*\(\s*"
75
+ r"(['\"])(?P<path>[^'\"]+)\1"
76
+ )
77
+
78
+
58
79
  def strip_quoted(command: str) -> str:
59
80
  return QUOTED.sub(" ", command)
60
81
 
@@ -85,6 +106,179 @@ def strip_escaped_characters(command: str) -> str:
85
106
  return "".join(cleaned)
86
107
 
87
108
 
109
+ def strip_heredoc_bodies(command: str) -> str:
110
+ """Remove heredoc data while preserving executable lines and separators."""
111
+ pending: list[tuple[str, bool, bool]] = []
112
+ executable = []
113
+ for line in command.splitlines(keepends=True):
114
+ if pending:
115
+ delimiter, strip_tabs, preserve_body = pending[0]
116
+ candidate = line.rstrip("\r\n")
117
+ if strip_tabs:
118
+ candidate = candidate.lstrip("\t")
119
+ if candidate == delimiter:
120
+ pending.pop(0)
121
+ elif preserve_body:
122
+ executable.append(line)
123
+ elif line.endswith(("\n", "\r")):
124
+ executable.append("\n")
125
+ continue
126
+
127
+ executable.append(line)
128
+ preserve_body = bool(EXECUTABLE_HEREDOC_RECEIVER.search(line))
129
+ for match in HEREDOC_START.finditer(line):
130
+ delimiter = match.group(3) or match.group(4)
131
+ pending.append((delimiter, bool(match.group(1)), preserve_body))
132
+ return "".join(executable)
133
+
134
+
135
+ def shell_segments(command: str) -> list[list[str]]:
136
+ normalized = strip_heredoc_bodies(command).replace("\\\n", " ").replace("\n", " ; ")
137
+ try:
138
+ lexer = shlex.shlex(normalized, posix=True, punctuation_chars=";&|()<>")
139
+ lexer.whitespace_split = True
140
+ lexer.commenters = ""
141
+ tokens = list(lexer)
142
+ except ValueError:
143
+ return []
144
+
145
+ segments: list[list[str]] = []
146
+ current: list[str] = []
147
+ for token in tokens:
148
+ if SHELL_SEPARATOR_TOKEN.fullmatch(token):
149
+ if current:
150
+ segments.append(current)
151
+ current = []
152
+ continue
153
+ current.append(token)
154
+ if current:
155
+ segments.append(current)
156
+ return segments
157
+
158
+
159
+ def is_managed_artifact_path(value: str) -> bool:
160
+ return bool(MANAGED_ARTIFACT_PATH.search(value))
161
+
162
+
163
+ def command_index(segment: list[str]):
164
+ index = 0
165
+ while index < len(segment) and SHELL_ASSIGNMENT.match(segment[index]):
166
+ index += 1
167
+ if index < len(segment) and segment[index].rsplit("/", 1)[-1] == "env":
168
+ index += 1
169
+ while index < len(segment) and (
170
+ segment[index].startswith("-") or SHELL_ASSIGNMENT.match(segment[index])
171
+ ):
172
+ index += 1
173
+ return index if index < len(segment) else None
174
+
175
+
176
+ def redirection_details(segment: list[str]) -> tuple[list[str], set[int]]:
177
+ targets: list[str] = []
178
+ consumed: set[int] = set()
179
+ for index, token in enumerate(segment):
180
+ if ">" not in token or not all(character in "<>&|" for character in token):
181
+ continue
182
+ consumed.add(index)
183
+ if index > 0 and segment[index - 1].isdigit():
184
+ consumed.add(index - 1)
185
+ if index + 1 >= len(segment):
186
+ continue
187
+ consumed.add(index + 1)
188
+ target = segment[index + 1]
189
+ if token in (">&", "<&") and (target.isdigit() or target == "-"):
190
+ continue
191
+ targets.append(target)
192
+ return targets, consumed
193
+
194
+
195
+ def command_operands(segment: list[str], start: int, consumed: set[int]) -> list[str]:
196
+ operands = []
197
+ options_done = False
198
+ for index in range(start, len(segment)):
199
+ if index in consumed:
200
+ continue
201
+ token = segment[index]
202
+ if token == "--":
203
+ options_done = True
204
+ continue
205
+ if not options_done and token.startswith("-"):
206
+ continue
207
+ operands.append(token)
208
+ return operands
209
+
210
+
211
+ def explicit_interpreter_mutation(command_names: set[str], source: str) -> bool:
212
+ if any(re.fullmatch(r"python\d*(?:\.\d+)?", name) for name in command_names):
213
+ for match in PYTHON_PATH_MUTATION.finditer(source):
214
+ if is_managed_artifact_path(match.group("path")):
215
+ return True
216
+ for match in PYTHON_OPEN.finditer(source):
217
+ if is_managed_artifact_path(match.group("path")) and any(
218
+ flag in match.group("mode") for flag in "wax+"
219
+ ):
220
+ return True
221
+ if "node" in command_names:
222
+ return any(
223
+ is_managed_artifact_path(match.group("path"))
224
+ for match in NODE_PATH_MUTATION.finditer(source)
225
+ )
226
+ return False
227
+
228
+
229
+ def mutates_managed_artifact(command: str) -> bool:
230
+ executable_command = strip_heredoc_bodies(command)
231
+ segments = shell_segments(command)
232
+ command_names = {
233
+ segment[index].rsplit("/", 1)[-1]
234
+ for segment in segments
235
+ if (index := command_index(segment)) is not None
236
+ }
237
+ if explicit_interpreter_mutation(command_names, executable_command):
238
+ return True
239
+
240
+ for segment in segments:
241
+ redirects, consumed = redirection_details(segment)
242
+ if any(is_managed_artifact_path(target) for target in redirects):
243
+ return True
244
+
245
+ executable_index = command_index(segment)
246
+ if executable_index is None:
247
+ continue
248
+ command_name = segment[executable_index].rsplit("/", 1)[-1]
249
+ argument_start = executable_index + 1
250
+ arguments = segment[argument_start:]
251
+ operands = command_operands(segment, argument_start, consumed)
252
+
253
+ target_directory = None
254
+ for index, argument in enumerate(arguments):
255
+ if argument in ("-t", "--target-directory") and index + 1 < len(arguments):
256
+ target_directory = arguments[index + 1]
257
+ elif argument.startswith("--target-directory="):
258
+ target_directory = argument.split("=", 1)[1]
259
+
260
+ if command_name == "cp":
261
+ destination = target_directory or (operands[-1] if len(operands) >= 2 else None)
262
+ if destination and is_managed_artifact_path(destination):
263
+ return True
264
+ elif command_name == "mv":
265
+ if target_directory and is_managed_artifact_path(target_directory):
266
+ return True
267
+ if any(is_managed_artifact_path(operand) for operand in operands):
268
+ return True
269
+ elif command_name in ("rm", "tee"):
270
+ if any(is_managed_artifact_path(operand) for operand in operands):
271
+ return True
272
+ elif command_name in ("sed", "perl"):
273
+ in_place = any(
274
+ argument.startswith("-") and "i" in argument[1:]
275
+ for argument in arguments
276
+ )
277
+ if in_place and any(is_managed_artifact_path(argument) for argument in arguments):
278
+ return True
279
+ return False
280
+
281
+
88
282
  def unquoted_ampersand(command_without_quotes: str) -> bool:
89
283
  cleaned = re.sub(r"\d*>&\d*", " ", command_without_quotes)
90
284
  cleaned = re.sub(r"<&\d*", " ", cleaned)
@@ -98,7 +292,8 @@ def protected_tool_invoked(command_without_quotes: str) -> bool:
98
292
 
99
293
  def scan_shell_command(command: str, depth: int = 0) -> list[str]:
100
294
  reasons = []
101
- stripped = strip_escaped_characters(strip_quoted(command))
295
+ executable_command = strip_heredoc_bodies(command)
296
+ stripped = strip_escaped_characters(strip_quoted(executable_command))
102
297
  if re.search(r"(?:^|[\s;&|(])(?:nohup|setsid)(?:\s|$)", stripped):
103
298
  reasons.append("nohup/setsid detach is forbidden")
104
299
  if re.search(r"(?:^|[\s;&|(])disown(?:\s|$)", stripped):
@@ -113,12 +308,12 @@ def scan_shell_command(command: str, depth: int = 0) -> list[str]:
113
308
 
114
309
  # `sh -c '...'` quoted payloads are shell code, not plain strings.
115
310
  if depth < MAX_NESTED_SHELL_DEPTH and SHELL_DASH_C.search(stripped):
116
- for segment in quoted_segments(command):
311
+ for segment in quoted_segments(executable_command):
117
312
  if protected_tool_invoked(strip_escaped_characters(strip_quoted(segment))):
118
313
  reasons.append("run-long-check and watch-job must not be invoked through a shell command string")
119
314
  reasons.extend(scan_shell_command(segment, depth + 1))
120
315
  if depth < MAX_NESTED_SHELL_DEPTH:
121
- for substitution in double_quoted_command_substitutions(command):
316
+ for substitution in double_quoted_command_substitutions(executable_command):
122
317
  nested_stripped = strip_escaped_characters(strip_quoted(substitution))
123
318
  if protected_tool_invoked(nested_stripped):
124
319
  reasons.append("run-long-check and watch-job must not be invoked through command substitution")
@@ -135,12 +330,7 @@ def guard_reasons(tool_input: dict) -> list[str]:
135
330
  command = command if isinstance(command, str) else ""
136
331
 
137
332
  reasons.extend(scan_shell_command(command))
138
- unquoted_path_command = command.replace('"', "").replace("'", "")
139
- if (
140
- MANAGED_ARTIFACT_PATH.search(unquoted_path_command)
141
- and "vcm-artifact" not in command
142
- and SHELL_WRITE.search(strip_quoted(command))
143
- ):
333
+ if mutates_managed_artifact(command):
144
334
  reasons.append("direct Bash writes to VCM-managed Markdown are forbidden")
145
335
  return list(dict.fromkeys(reasons))
146
336