@mmerterden/multi-agent-pipeline 16.25.0 → 16.27.0

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 (47) hide show
  1. package/CHANGELOG.md +77 -0
  2. package/README.md +1 -1
  3. package/README.tr.md +1 -1
  4. package/install/templates/claude-hooks.json +32 -1
  5. package/package.json +1 -1
  6. package/pipeline/commands/multi-agent/help/SKILL.md +2 -0
  7. package/pipeline/commands/multi-agent/refactor/SKILL.md +23 -1
  8. package/pipeline/commands/multi-agent/search/SKILL.md +28 -0
  9. package/pipeline/commands/multi-agent/setup/SKILL.md +18 -44
  10. package/pipeline/commands/multi-agent/status/SKILL.md +9 -0
  11. package/pipeline/lib/credential-inventory.sh +142 -18
  12. package/pipeline/lib/fetch-crashlytics.sh +123 -28
  13. package/pipeline/multi-agent-refs/features/url-enrichment.md +1 -1
  14. package/pipeline/multi-agent-refs/features/visual-evidence.md +5 -0
  15. package/pipeline/multi-agent-refs/keychain.md +65 -20
  16. package/pipeline/multi-agent-refs/knowledge.md +27 -0
  17. package/pipeline/multi-agent-refs/phases/operations.md +7 -1
  18. package/pipeline/multi-agent-refs/phases/phase-1-analysis.md +3 -1
  19. package/pipeline/multi-agent-refs/phases/phase-4-review.md +1 -8
  20. package/pipeline/multi-agent-refs/phases/phase-7-report.md +11 -21
  21. package/pipeline/multi-agent-refs/picker-contract.md +1 -1
  22. package/pipeline/multi-agent-refs/refactor/observations.md +81 -0
  23. package/pipeline/multi-agent-refs/setup/firebase.md +151 -0
  24. package/pipeline/schemas/learnings-ledger.schema.json +5 -0
  25. package/pipeline/schemas/prefs.schema.json +31 -3
  26. package/pipeline/schemas/skill-observation.schema.json +73 -0
  27. package/pipeline/scripts/capture-flush.sh +158 -0
  28. package/pipeline/scripts/capture-resume.sh +87 -0
  29. package/pipeline/scripts/crush-json.mjs +283 -0
  30. package/pipeline/scripts/firebase-app-discovery.sh +114 -0
  31. package/pipeline/scripts/keychain-save.sh +5 -8
  32. package/pipeline/scripts/keychain.py +76 -14
  33. package/pipeline/scripts/learn-from-transcripts.mjs +625 -0
  34. package/pipeline/scripts/learning-curve.mjs +22 -4
  35. package/pipeline/scripts/learnings-ledger.mjs +86 -12
  36. package/pipeline/scripts/note-session.sh +187 -0
  37. package/pipeline/scripts/observations.mjs +347 -0
  38. package/pipeline/scripts/offload-ref.sh +45 -2
  39. package/pipeline/scripts/pre-commit-check.sh +31 -1
  40. package/pipeline/scripts/scan-agent-config.sh +12 -3
  41. package/pipeline/scripts/skill-siblings.mjs +187 -0
  42. package/pipeline/scripts/triage-memory.mjs +73 -9
  43. package/pipeline/skills/.skill-manifest.json +1 -1
  44. package/pipeline/skills/shared/core/multi-agent-refactor/SKILL.md +23 -1
  45. package/pipeline/skills/shared/core/multi-agent-search/SKILL.md +28 -0
  46. package/pipeline/skills/shared/core/multi-agent-setup/SKILL.md +37 -6
  47. package/pipeline/skills/shared/core/multi-agent-status/SKILL.md +9 -0
@@ -129,6 +129,64 @@ def _ensure_backend() -> str:
129
129
  return info["platform"]
130
130
 
131
131
 
132
+ def _decode_security_password(stderr: bytes) -> Optional[str]:
133
+ """Pull the secret out of `security ... -g` stderr and decode it.
134
+
135
+ `-w` cannot be used to read a secret this store may hold. It prints any value
136
+ containing a byte outside plain printable ASCII as BARE HEX with no marker,
137
+ which is indistinguishable from a secret that is itself hex - and 40-character
138
+ hex API tokens are exactly what lives here. Guessing from the shape would
139
+ corrupt them. `-g` marks the encoded form with an `0x` prefix, so the decision
140
+ is read off the output instead of inferred:
141
+
142
+ multi-line value -> password: 0x6C696E65310A6C696E6532 "line1\\012line2"
143
+ hex-looking value -> password: "deadbeefcafe1234"
144
+
145
+ Two shapes, measured rather than assumed:
146
+ - `0x<HEX>` present -> the hex is authoritative; a quoted trailer may follow
147
+ or may be absent entirely (an all-non-ASCII value prints hex only).
148
+ - no `0x` -> the value sits between the first and last double quote. Inner
149
+ quotes are NOT escaped by `security` (`he"llo` prints as `"he"llo"`), so
150
+ first-to-last is the correct span and unescaping would be wrong.
151
+
152
+ The secret is parsed in-process and returned to the caller. It is never
153
+ logged, never placed in an exception message, and never passed as an argv
154
+ element.
155
+ """
156
+ for raw in stderr.split(b"\n"):
157
+ if not raw.startswith(b"password: "):
158
+ continue
159
+ body = raw[len(b"password: "):].rstrip(b"\r")
160
+ if body.startswith(b"0x"):
161
+ hex_digits = body[2:].split(b" ", 1)[0]
162
+ try:
163
+ return bytes.fromhex(hex_digits.decode("ascii")).decode("utf-8", errors="replace")
164
+ except ValueError:
165
+ return None
166
+ first = body.find(b'"')
167
+ last = body.rfind(b'"')
168
+ if first != -1 and last > first:
169
+ return body[first + 1:last].decode("utf-8", errors="replace")
170
+ return body.decode("utf-8", errors="replace")
171
+ return None
172
+
173
+
174
+ def _macos_find(args: list[str]) -> Optional[str]:
175
+ """One `find-generic-password -g` lookup; None when the item is absent.
176
+
177
+ Trailing newlines are stripped, which is what the `-w` path did before and
178
+ what every consumer already assumes: a newline on the end of a token is an
179
+ artifact of pasting it, and it breaks an HTTP header the moment it survives.
180
+ A value whose trailing newline is meaningful is not something this store has
181
+ ever been able to return.
182
+ """
183
+ rc, _, err = _run(["/usr/bin/security", "find-generic-password", *args, "-g"])
184
+ if rc != 0:
185
+ return None
186
+ value = _decode_security_password(err)
187
+ return None if value is None else value.rstrip("\n")
188
+
189
+
132
190
  def get(label: str, account: Optional[str] = None) -> Optional[str]:
133
191
  """Lookup by label first, then by service name. Two coexisting conventions:
134
192
  - User's manually-added tokens (e.g. mmerterden_Vercel_Access_Token) use -l (label).
@@ -137,19 +195,16 @@ def get(label: str, account: Optional[str] = None) -> Optional[str]:
137
195
  pf = _ensure_backend()
138
196
  acct = _account_default(account)
139
197
  if pf == "macos":
140
- rc, out, _ = _run(["/usr/bin/security", "find-generic-password", "-a", acct, "-l", label, "-w"])
141
- if rc == 0:
142
- return out.decode("utf-8", errors="replace").rstrip("\n")
143
- rc, out, _ = _run(["/usr/bin/security", "find-generic-password", "-a", acct, "-s", label, "-w"])
144
- if rc == 0:
145
- return out.decode("utf-8", errors="replace").rstrip("\n")
146
- # account-agnostic last resort - some entries were written without -a
147
- rc, out, _ = _run(["/usr/bin/security", "find-generic-password", "-l", label, "-w"])
148
- if rc == 0:
149
- return out.decode("utf-8", errors="replace").rstrip("\n")
150
- rc, out, _ = _run(["/usr/bin/security", "find-generic-password", "-s", label, "-w"])
151
- if rc == 0:
152
- return out.decode("utf-8", errors="replace").rstrip("\n")
198
+ for args in (
199
+ ["-a", acct, "-l", label],
200
+ ["-a", acct, "-s", label],
201
+ # account-agnostic last resort - some entries were written without -a
202
+ ["-l", label],
203
+ ["-s", label],
204
+ ):
205
+ value = _macos_find(args)
206
+ if value is not None:
207
+ return value
153
208
  return None
154
209
  # linux / secret-tool - store under both `label` and `service` schema attrs
155
210
  rc, out, _ = _run(["secret-tool", "lookup", "label", label, "account", acct])
@@ -173,9 +228,16 @@ def set_(label: str, value: str, account: Optional[str] = None) -> None:
173
228
  def quote(text: str) -> str:
174
229
  return text.replace("\\", "\\\\").replace('"', '\\"')
175
230
 
231
+ # The secret goes in as `-X <hex>`, not `-w "<text>"`. `security -i`
232
+ # tokenizes ONE command per line, so a newline inside the value ends the
233
+ # command and the remainder is read as further commands - which is how a
234
+ # service-account JSON turned into "security: unknown command" instead of
235
+ # a stored credential. Hex is single-line by construction and byte-exact,
236
+ # and it stays on stdin, so the argv exposure this branch exists to avoid
237
+ # is still avoided.
176
238
  command = (
177
239
  f'add-generic-password -U -a "{quote(acct)}" -l "{quote(label)}" '
178
- f'-s "{quote(label)}" -w "{quote(value)}"\n'
240
+ f'-s "{quote(label)}" -X {value.encode("utf-8").hex()}\n'
179
241
  )
180
242
  rc, _, err = _run(["/usr/bin/security", "-i"], input_value=command.encode("utf-8"))
181
243
  if rc != 0: