@prism-d1/cli 1.8.0 → 1.9.1

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 (36) hide show
  1. package/README.md +48 -0
  2. package/dist/assets/github-workflows/prism-eval-gate-kiro.yml +77 -38
  3. package/dist/assets/gitlab-workflows/prism-eval-gate.yml +55 -29
  4. package/dist/assets/kirocrew-app/README.md +234 -0
  5. package/dist/assets/kirocrew-app/app.json +96 -0
  6. package/dist/assets/kirocrew-app/assets/icon.png +0 -0
  7. package/dist/assets/kirocrew-app/assets/screenshots/page-dark.png +0 -0
  8. package/dist/assets/kirocrew-app/assets/screenshots/page-light.png +0 -0
  9. package/dist/assets/kirocrew-app/assets/screenshots/report-dark.png +0 -0
  10. package/dist/assets/kirocrew-app/assets/screenshots/report-light.png +0 -0
  11. package/dist/assets/kirocrew-app/scripts/digest.py +263 -0
  12. package/dist/assets/kirocrew-app/scripts/page_model.py +298 -0
  13. package/dist/assets/kirocrew-app/scripts/page_routes.py +241 -0
  14. package/dist/assets/kirocrew-app/scripts/prism_cli_path.py +131 -0
  15. package/dist/assets/kirocrew-app/scripts/prism_mcp.py +28 -0
  16. package/dist/assets/kirocrew-app/skills/prism-fix-findings/SKILL.md +62 -0
  17. package/dist/assets/kirocrew-app/ui/index.mjs +269 -0
  18. package/dist/src/commands/_top/version.d.ts +9 -0
  19. package/dist/src/commands/_top/version.d.ts.map +1 -1
  20. package/dist/src/commands/_top/version.js +22 -1
  21. package/dist/src/commands/_top/version.js.map +1 -1
  22. package/dist/src/commands/enable/kirocrew.d.ts +82 -0
  23. package/dist/src/commands/enable/kirocrew.d.ts.map +1 -0
  24. package/dist/src/commands/enable/kirocrew.js +376 -0
  25. package/dist/src/commands/enable/kirocrew.js.map +1 -0
  26. package/dist/src/dashboard/setup.d.ts +6 -1
  27. package/dist/src/dashboard/setup.d.ts.map +1 -1
  28. package/dist/src/dashboard/setup.js +17 -0
  29. package/dist/src/dashboard/setup.js.map +1 -1
  30. package/dist/src/index.js +1 -1
  31. package/dist/src/index.js.map +1 -1
  32. package/dist/src/utils/kirocrew.d.ts +35 -0
  33. package/dist/src/utils/kirocrew.d.ts.map +1 -0
  34. package/dist/src/utils/kirocrew.js +65 -0
  35. package/dist/src/utils/kirocrew.js.map +1 -0
  36. package/package.json +4 -2
package/README.md ADDED
@@ -0,0 +1,48 @@
1
+ # prism-cli
2
+
3
+ The command line for [PRISM D1 Velocity](https://github.com/aws-samples/sample-prism-d1-velocity): measure, secure and review AI-assisted development from your own machine. Codewatch git hooks, a local dashboard, an MCP server for AI agents, AWS Security Agent scans, read-only AWS account checks, and the setup helpers for a team's PRISM deployment.
4
+
5
+ > Sample project, not production-ready. See [SECURITY.md](https://github.com/aws-samples/sample-prism-d1-velocity/blob/main/SECURITY.md).
6
+
7
+ ## Install
8
+
9
+ Node.js 22 or later:
10
+
11
+ ```bash
12
+ npm install -g @prism-d1/cli
13
+ prism-cli version # installed version, newer release on npm, other copies on PATH
14
+ ```
15
+
16
+ ## Quick start
17
+
18
+ ```bash
19
+ prism-cli enable codewatch # in a repo: secret scan on commit, AI review on push (report only)
20
+ prism-cli dashboard # local page on 127.0.0.1: results, AI usage, setup checklist
21
+ prism-cli aws check # read-only AWS account checks; then prism-cli aws recommend
22
+ ```
23
+
24
+ Send AI usage to your team's PRISM with `prism-cli enable codeburn --url <OtelCollectorUrl>` (the URL comes from your platform team). Every command takes `--help`.
25
+
26
+ ## Documentation
27
+
28
+ - [Using PRISM locally](https://github.com/aws-samples/sample-prism-d1-velocity/blob/main/USER_GUIDE.md#using-prism-locally): what each feature does and where it shows up (VS Code family editors, Kiro Crew, Claude Code)
29
+ - [Command reference](https://github.com/aws-samples/sample-prism-d1-velocity/blob/main/docs/CLI-REFERENCE.md): every command and flag
30
+ - [prism-cli Overview](https://github.com/aws-samples/sample-prism-d1-velocity/blob/main/docs/PRISM-CLI-OVERVIEW.md): what each command touches in AWS, its cost and its safety defaults
31
+ - [User Guide](https://github.com/aws-samples/sample-prism-d1-velocity/blob/main/USER_GUIDE.md): deploying PRISM, CI workflows and eval gates
32
+
33
+ ## Develop
34
+
35
+ From `cli/` in a clone:
36
+
37
+ ```bash
38
+ npm ci
39
+ npm run build # tsc -> dist/, plus the bundled assets
40
+ npm test # type-checks the tests, then the node:test suite
41
+ npm run docs # regenerate docs/CLI-REFERENCE.md after changing a command or flag
42
+ ```
43
+
44
+ Releases are published to npm by `.github/workflows/publish.yml` when a `v*` tag is pushed.
45
+
46
+ ## License
47
+
48
+ MIT
@@ -282,49 +282,85 @@ jobs:
282
282
  # on long content. Use a minimal CLI arg prompt and let kiro read files itself.
283
283
  git diff "${BASE_SHA}..${HEAD_SHA}" -- ${FILES} > /tmp/pr-diff.patch
284
284
 
285
- REVIEW_OUTPUT=$(kiro-cli chat --no-interactive \
286
- --trust-all-tools \
287
- --model auto \
288
- "Review /tmp/pr-diff.patch for code quality. Read it, then output ONLY JSON: {\"findings\":[{\"file\":\"path\",\"line\":1,\"severity\":\"high|medium|low\",\"category\":\"str\",\"message\":\"str\"}],\"score\":0.0,\"summary\":\"str\"}") || true
289
-
290
- # Strip ANSI escapes, then extract the LAST JSON object that parses and
291
- # has a numeric score and a findings array. kiro-cli writes reasoning
292
- # notes first, and they quote code with braces, so a greedy
293
- # first-brace-to-last-brace match is not the verdict.
294
- CLEANED=$(echo "${REVIEW_OUTPUT}" | sed 's/\x1b\[[0-9;?]*[a-zA-Z]//g' | tr -d '\r')
295
- REVIEW_JSON=$(printf '%s' "${CLEANED}" | python3 -c 'if True:
296
- import json, math, sys
297
- text = sys.stdin.read()
298
- dec = json.JSONDecoder(strict=False)
299
- pos = text.rfind("{")
300
- while pos != -1:
301
- try:
302
- obj = dec.raw_decode(text, pos)[0]
303
- except ValueError:
304
- obj = None
305
- score = obj.get("score") if isinstance(obj, dict) else None
306
- if (isinstance(score, (int, float)) and not isinstance(score, bool)
307
- and math.isfinite(score) and isinstance(obj.get("findings"), list)):
308
- print(json.dumps(obj))
309
- sys.exit(0)
310
- pos = text.rfind("{", 0, pos)
311
- sys.exit(1)
312
- ' || true)
313
-
314
- if [ -z "${REVIEW_JSON}" ] || ! echo "${REVIEW_JSON}" | jq -e '.score' > /dev/null 2>&1; then
315
- # FAIL CLOSED: a gate that can't evaluate must not pass
316
- echo "::group::Debug: raw kiro-cli output (first 2000 chars)"
285
+ # One retry, only when the reply cannot be parsed. Attempt 2 re-runs the
286
+ # same review (same model, same diff file) with one sentence added that
287
+ # asks for a single JSON object. It does not hand the bad reply back
288
+ # for "repair": a broken reply is often truncated, and repairing it
289
+ # would make the model invent the missing findings. Each attempt reads
290
+ # the diff, so any accepted verdict comes from a real review. Nothing
291
+ # is repaired by regex; two unparseable replies still fail closed.
292
+ REVIEW_PROMPT="Review /tmp/pr-diff.patch for code quality. Read it, then output ONLY JSON: {\"findings\":[{\"file\":\"path\",\"line\":1,\"severity\":\"high|medium|low\",\"category\":\"str\",\"message\":\"str\"}],\"score\":0.0,\"summary\":\"str\"}"
293
+ RETRY_NOTE="Your previous reply was not valid JSON. Return exactly one JSON object matching that schema and nothing else: no prose, no code fences."
294
+ # Per-attempt cap. The retry gets the same budget as the first attempt.
295
+ REVIEW_TIMEOUT_SECS=600
296
+ REVIEW_JSON=""
297
+ REVIEW_ATTEMPT=""
298
+ for ATTEMPT in 1 2; do
299
+ PROMPT="${REVIEW_PROMPT}"
300
+ if [ "${ATTEMPT}" = "2" ]; then PROMPT="${REVIEW_PROMPT} ${RETRY_NOTE}"; fi
301
+ REVIEW_OUTPUT=$(timeout "${REVIEW_TIMEOUT_SECS}" kiro-cli chat --no-interactive \
302
+ --trust-all-tools \
303
+ --model auto \
304
+ "${PROMPT}") || true
305
+
306
+ # Strip ANSI escapes, then extract the LAST JSON object that parses and
307
+ # has a numeric score and a findings array. kiro-cli writes reasoning
308
+ # notes first, and they quote code with braces, so a greedy
309
+ # first-brace-to-last-brace match is not the verdict. Same rule as
310
+ # extractReviewJson in cli/src/codewatch/reviewers/types.ts; the copy
311
+ # in each eval-gate workflow is kept identical by cli/test/eval-gate-extract.test.ts.
312
+ CLEANED=$(echo "${REVIEW_OUTPUT}" | sed 's/\x1b\[[0-9;?]*[a-zA-Z]//g' | tr -d '\r')
313
+ REVIEW_JSON=$(printf '%s' "${CLEANED}" | python3 -c 'if True:
314
+ import json, math, sys
315
+ text = sys.stdin.read()
316
+ dec = json.JSONDecoder(strict=False)
317
+ pos = text.rfind("{")
318
+ while pos != -1:
319
+ try:
320
+ obj = dec.raw_decode(text, pos)[0]
321
+ except ValueError:
322
+ obj = None
323
+ score = obj.get("score") if isinstance(obj, dict) else None
324
+ if (isinstance(score, (int, float)) and not isinstance(score, bool)
325
+ and math.isfinite(score) and isinstance(obj.get("findings"), list)):
326
+ print(json.dumps(obj))
327
+ sys.exit(0)
328
+ pos = text.rfind("{", 0, pos)
329
+ sys.exit(1)
330
+ ' || true)
331
+
332
+ if [ -n "${REVIEW_JSON}" ] && echo "${REVIEW_JSON}" | jq -e '.score' > /dev/null 2>&1; then
333
+ REVIEW_ATTEMPT="${ATTEMPT}"
334
+ break
335
+ fi
336
+ REVIEW_JSON=""
337
+
338
+ # The reply is untrusted model output. Workflow commands are paused
339
+ # while it is printed, so a "::" line in it is shown, not executed.
340
+ STOP_TOKEN=$(od -An -N16 -tx1 /dev/urandom | tr -d ' \n')
341
+ echo "::group::Debug: attempt ${ATTEMPT} raw kiro-cli output (first 2000 chars)"
342
+ echo "::stop-commands::${STOP_TOKEN}"
317
343
  echo "${REVIEW_OUTPUT:-<empty>}" | head -c 2000
318
344
  echo ""
319
- echo "::endgroup::"
320
- echo "::group::Debug: cleaned output"
345
+ echo "--- cleaned ---"
321
346
  echo "${CLEANED:-<empty>}" | head -c 2000
322
347
  echo ""
348
+ echo "::${STOP_TOKEN}::"
323
349
  echo "::endgroup::"
324
- echo "::error::Could not parse kiro-cli review output -- failing closed"
350
+ if [ "${ATTEMPT}" = "1" ]; then
351
+ echo "::warning::Attempt 1: could not parse kiro-cli review output -- retrying the review once"
352
+ fi
353
+ done
354
+
355
+ if [ -z "${REVIEW_JSON}" ]; then
356
+ # FAIL CLOSED: a gate that can't evaluate must not pass
357
+ echo "::error::Could not parse kiro-cli review output on either attempt (both unparseable) -- failing closed"
325
358
  echo "result=ERROR" >> "$GITHUB_OUTPUT"
359
+ echo "Eval gate review: both attempts returned unparseable output -- failed closed." >> "$GITHUB_STEP_SUMMARY"
326
360
  exit 0
327
361
  fi
362
+ echo "Review verdict parsed on attempt ${REVIEW_ATTEMPT} of 2"
363
+ echo "attempt=${REVIEW_ATTEMPT}" >> "$GITHUB_OUTPUT"
328
364
 
329
365
  SCORE=$(echo "${REVIEW_JSON}" | jq -r '.score')
330
366
  FINDINGS=$(echo "${REVIEW_JSON}" | jq '.findings | length')
@@ -343,10 +379,13 @@ jobs:
343
379
  # Determine pass/fail (awk instead of bc -- no extra package needed)
344
380
  PASS=$(awk -v s="${SCORE}" 'BEGIN { print (s >= 0.82) ? "true" : "false" }')
345
381
  if [ "${HIGH}" -gt 0 ] || [ "${PASS}" != "true" ]; then
346
- echo "result=FAIL" >> "$GITHUB_OUTPUT"
382
+ RESULT=FAIL
347
383
  else
348
- echo "result=PASS" >> "$GITHUB_OUTPUT"
384
+ RESULT=PASS
349
385
  fi
386
+ echo "result=${RESULT}" >> "$GITHUB_OUTPUT"
387
+ # Only numbers and fixed words here; no model text reaches the summary.
388
+ echo "Eval gate review: ${RESULT} (score ${SCORE}, ${HIGH} high), verdict parsed on attempt ${REVIEW_ATTEMPT} of 2." >> "$GITHUB_STEP_SUMMARY"
350
389
 
351
390
  - name: Post PR comment
352
391
  if: always()
@@ -372,7 +411,7 @@ jobs:
372
411
  if (result === 'SKIPPED') {
373
412
  body = '⚠️ **PRISM Eval Gate** -- Review skipped (no source files changed or no API key on fork PRs).';
374
413
  } else if (result === 'ERROR') {
375
- body = '❌ **PRISM Eval Gate** -- Review output could not be parsed. Failing closed; re-run the workflow or review manually.';
414
+ body = '❌ **PRISM Eval Gate** -- Review output could not be parsed on either attempt (the review is retried once). Failing closed; re-run the workflow or review manually.';
376
415
  } else {
377
416
  const score = process.env.REVIEW_SCORE || '?';
378
417
  const findings = process.env.REVIEW_FINDINGS || '0';
@@ -217,41 +217,67 @@ prism-eval-gate:
217
217
  # into HTTP headers, so let it read the patch off disk itself.
218
218
  git diff "${BASE_SHA}..${HEAD_SHA}" -- ${FILES} > /tmp/mr-diff.patch
219
219
 
220
- set +e
221
- REVIEW_OUTPUT=$(kiro-cli chat --no-interactive --trust-all-tools --model auto \
222
- "Review /tmp/mr-diff.patch for code quality. Read it, then output ONLY JSON: {\"findings\":[{\"file\":\"path\",\"line\":1,\"severity\":\"high|medium|low\",\"category\":\"str\",\"message\":\"str\"}],\"score\":0.0,\"summary\":\"str\"}")
223
- set -e
220
+ # One retry, only when the reply cannot be parsed. Attempt 2 re-runs
221
+ # the same review (same model, same diff file) with one sentence
222
+ # added that asks for a single JSON object. It does not hand the bad
223
+ # reply back for "repair": a broken reply is often truncated, and
224
+ # repairing it would make the model invent the missing findings.
225
+ # Nothing is repaired by regex; two unparseable replies fail closed.
226
+ REVIEW_PROMPT="Review /tmp/mr-diff.patch for code quality. Read it, then output ONLY JSON: {\"findings\":[{\"file\":\"path\",\"line\":1,\"severity\":\"high|medium|low\",\"category\":\"str\",\"message\":\"str\"}],\"score\":0.0,\"summary\":\"str\"}"
227
+ RETRY_NOTE="Your previous reply was not valid JSON. Return exactly one JSON object matching that schema and nothing else: no prose, no code fences."
228
+ # Per-attempt cap. The retry gets the same budget as the first attempt.
229
+ REVIEW_TIMEOUT_SECS=600
230
+ REVIEW_JSON=""
231
+ REVIEW_ATTEMPT=""
232
+ for ATTEMPT in 1 2; do
233
+ PROMPT="${REVIEW_PROMPT}"
234
+ if [ "${ATTEMPT}" = "2" ]; then PROMPT="${REVIEW_PROMPT} ${RETRY_NOTE}"; fi
235
+ set +e
236
+ REVIEW_OUTPUT=$(timeout "${REVIEW_TIMEOUT_SECS}" kiro-cli chat --no-interactive --trust-all-tools --model auto "${PROMPT}")
237
+ set -e
224
238
 
225
- # Extract the LAST JSON object that parses and has a numeric score
226
- # and a findings array. kiro-cli writes reasoning notes first, and
227
- # they quote code with braces, so a greedy first-brace-to-last-brace
228
- # match is not the verdict.
229
- CLEANED=$(printf '%s' "${REVIEW_OUTPUT}" | sed 's/\x1b\[[0-9;?]*[a-zA-Z]//g' | tr -d '\r')
230
- REVIEW_JSON=$(printf '%s' "${CLEANED}" | python3 -c 'if True:
231
- import json, math, sys
232
- text = sys.stdin.read()
233
- dec = json.JSONDecoder(strict=False)
234
- pos = text.rfind("{")
235
- while pos != -1:
236
- try:
237
- obj = dec.raw_decode(text, pos)[0]
238
- except ValueError:
239
- obj = None
240
- score = obj.get("score") if isinstance(obj, dict) else None
241
- if (isinstance(score, (int, float)) and not isinstance(score, bool)
242
- and math.isfinite(score) and isinstance(obj.get("findings"), list)):
243
- print(json.dumps(obj))
244
- sys.exit(0)
245
- pos = text.rfind("{", 0, pos)
246
- sys.exit(1)
247
- ' || true)
239
+ # Extract the LAST JSON object that parses and has a numeric score
240
+ # and a findings array. kiro-cli writes reasoning notes first, and
241
+ # they quote code with braces, so a greedy first-brace-to-last-brace
242
+ # match is not the verdict.
243
+ CLEANED=$(printf '%s' "${REVIEW_OUTPUT}" | sed 's/\x1b\[[0-9;?]*[a-zA-Z]//g' | tr -d '\r')
244
+ REVIEW_JSON=$(printf '%s' "${CLEANED}" | python3 -c 'if True:
245
+ import json, math, sys
246
+ text = sys.stdin.read()
247
+ dec = json.JSONDecoder(strict=False)
248
+ pos = text.rfind("{")
249
+ while pos != -1:
250
+ try:
251
+ obj = dec.raw_decode(text, pos)[0]
252
+ except ValueError:
253
+ obj = None
254
+ score = obj.get("score") if isinstance(obj, dict) else None
255
+ if (isinstance(score, (int, float)) and not isinstance(score, bool)
256
+ and math.isfinite(score) and isinstance(obj.get("findings"), list)):
257
+ print(json.dumps(obj))
258
+ sys.exit(0)
259
+ pos = text.rfind("{", 0, pos)
260
+ sys.exit(1)
261
+ ' || true)
248
262
 
249
- if [ -z "${REVIEW_JSON}" ] || ! printf '%s' "${REVIEW_JSON}" | jq -e '.score' >/dev/null 2>&1; then
250
- echo "❌ Could not parse kiro-cli review output — failing closed."
263
+ if [ -n "${REVIEW_JSON}" ] && printf '%s' "${REVIEW_JSON}" | jq -e '.score' >/dev/null 2>&1; then
264
+ REVIEW_ATTEMPT="${ATTEMPT}"
265
+ break
266
+ fi
267
+ REVIEW_JSON=""
268
+ echo "⚠ Attempt ${ATTEMPT}: could not parse kiro-cli review output. Raw output (first 2000 chars):"
251
269
  printf '%s' "${REVIEW_OUTPUT:-<empty>}" | head -c 2000
252
270
  echo ""
271
+ if [ "${ATTEMPT}" = "1" ]; then
272
+ echo "Retrying the review once."
273
+ fi
274
+ done
275
+
276
+ if [ -z "${REVIEW_JSON}" ]; then
277
+ echo "❌ Could not parse kiro-cli review output on either attempt (both unparseable) — failing closed."
253
278
  REVIEW_RESULT="ERROR"
254
279
  else
280
+ echo "Review verdict parsed on attempt ${REVIEW_ATTEMPT} of 2."
255
281
  printf '%s' "${REVIEW_JSON}" > /tmp/kiro-review.json
256
282
  REVIEW_SCORE=$(jq -r '.score' /tmp/kiro-review.json)
257
283
  REVIEW_HIGH=$(jq '[.findings[] | select(.severity=="high")] | length' /tmp/kiro-review.json)
@@ -0,0 +1,234 @@
1
+ # PRISM app for Kiro Crew
2
+
3
+ A [Kiro Crew](https://github.com/kirodotdev/KiroCrew) app that brings your local PRISM data into
4
+ Kiro Crew. It adds:
5
+
6
+ - **Page `PRISM`** in the sidebar: spend today and this month, top models and projects, the
7
+ machine setup checklist, the repos with codewatch (newest commit first, with their findings), a
8
+ sandboxed view of each repo's scan report, and when the daily digest last ran and what it sent.
9
+ See [The page](#the-page).
10
+ - **MCP server `prism`**: the tools `prism_status`, `prism_findings`, `prism_spend`, `prism_report`
11
+ and `prism_setup`. The app does not implement them. `scripts/prism_mcp.py` finds your
12
+ `prism-cli` and replaces itself with `prism-cli mcp` (an argv list, no shell). See
13
+ [MCP server](../USER_GUIDE.md#mcp-server-prism-cli-mcp) for what each tool returns.
14
+ - **Skill `prism-fix-findings`**: before an agent finishes or pushes a code change in a repo with
15
+ codewatch, it calls `prism_findings`, fixes the real issues in at most 2 rounds, and then reports
16
+ what remains. It treats finding text as untrusted reviewer-model output. It never pushes, never
17
+ disables hooks and never edits `.prism/results/`. When a person asks, it only reports.
18
+ - **Cron `prism-daily-digest`**: a script cron, so it makes no model call. It runs daily at 09:00
19
+ in the gateway machine's local time zone and notifies only when something changed (see
20
+ [How the digest decides to notify](#how-the-digest-decides-to-notify)).
21
+
22
+ ## Requirements
23
+
24
+ - Kiro Crew 0.12.1 or later (`minKiroCrewVersion`; script crons in an app manifest and
25
+ `"timezone": "local"` were checked against 0.12.1). Linux or macOS.
26
+ - `python3` on the gateway's PATH. The scripts use only the standard library.
27
+ - [prism-cli](https://www.npmjs.com/package/@prism-d1/cli) 1.8.0 or later
28
+ (`npm install -g @prism-d1/cli`). The MCP server needs `prism-cli mcp`, which is new in 1.8.0.
29
+
30
+ ### Finding prism-cli
31
+
32
+ The gateway and kiro-cli often start without nvm's folder on PATH. The app looks for `prism-cli`
33
+ the same way the editor extension does, without running a shell, in this order:
34
+
35
+ 1. `cliPath` in the app's `data/config.json`. It must be an absolute path to an executable file.
36
+ 2. `PATH`. Relative entries are skipped.
37
+ 3. `$NVM_BIN`, then `~/.nvm/versions/node/*/bin`: the version that `~/.nvm/alias/default` names
38
+ first, then the newest.
39
+ 4. Volta (`$VOLTA_HOME/bin` or `~/.volta/bin`), `~/.local/bin`, `~/.npm-global/bin`, then
40
+ `$NPM_CONFIG_PREFIX/bin`.
41
+
42
+ Before it runs `prism-cli`, the app puts that folder first on `PATH`, so the CLI's
43
+ `#!/usr/bin/env node` line finds the `node` installed next to it.
44
+
45
+ ## Install
46
+
47
+ With prism-cli 1.9.0 or later, which bundles this app:
48
+
49
+ ```bash
50
+ npm i -g @prism-d1/cli && prism-cli enable kirocrew
51
+ ```
52
+
53
+ What the command does, its `--status`, `--dry-run` and `--uninstall` options and the two steps
54
+ that stay with you (trust the app, approve its API routes) are in the User Guide's
55
+ [Kiro Crew app](../USER_GUIDE.md#kiro-crew-app) section; every flag is in the
56
+ [command reference](../docs/CLI-REFERENCE.md#prism-cli-enable-kirocrew). It updates an app that
57
+ was installed from another folder, such as a clone, too. It never installs Kiro Crew.
58
+
59
+ ### Install from a folder
60
+
61
+ From a local folder instead (a clone, for development):
62
+
63
+ ```bash
64
+ git clone https://github.com/aws-samples/sample-prism-d1-velocity.git
65
+ kirocrew app install "$PWD/sample-prism-d1-velocity/kirocrew-app"
66
+ kirocrew app enable prism
67
+ ```
68
+
69
+ Kiro Crew copies the folder into its apps directory (`~/.kiro/crew/apps/prism/`). Later edits to
70
+ the folder do nothing until you update the app. The app's data stays in
71
+ `~/.kiro/crew/apps/prism/data/` across updates.
72
+
73
+ Kiro Crew registers nothing from a third-party app until you trust it. If you enable the app from
74
+ the dashboard's App Store, it asks you to trust this one app. `kirocrew app enable` refuses with
75
+ `app_execution_denied` until you have trusted the app, either there or under
76
+ **Settings → Security → Third-party apps**.
77
+
78
+ What you are trusting. After you enable it, the app can:
79
+
80
+ - run `python3 scripts/prism_mcp.py` as an MCP server in your agent sessions. That process runs
81
+ your `prism-cli mcp`, which reads the dashboard's local data under your home directory
82
+ (codewatch results in your repos, the codeburn usage cache and setup checks). It makes no
83
+ network or AWS calls. Kiro Crew asks you to approve each tool call, because the app
84
+ auto-approves none.
85
+ - run `scripts/digest.py` once a day without a model. It runs `prism-cli dashboard --json`, which
86
+ scans git repos up to 3 levels under your home directory. It writes only
87
+ `digest-state.json` in the app's data folder, and it sends a Kiro Crew notification when
88
+ something changed.
89
+ - give your agents the `prism-fix-findings` skill.
90
+ - run `scripts/page_routes.py` inside the gateway process (`backend.hooks.routes`). These are the
91
+ page's two read-only routes. While the page is open, they run `prism-cli dashboard --json`,
92
+ `prism-cli --version`, `git log -1` in up to 30 repos, and `prism-cli codewatch report`. Each
93
+ report is written to `data/reports/` in the app's data folder and deleted as soon as it is read.
94
+ Any file older than a day that is left over is removed on the next report request. Like every
95
+ in-gateway app code, these routes run with the gateway's privileges.
96
+
97
+ The manifest declares `cron`, `storage` and `api: ["/api/apps/prism/*"]` (the page may call only
98
+ this app's own routes), and sets `network: false`. The app opens no port and runs no backend
99
+ process. It has no install hooks, no `spawn` and no session control.
100
+
101
+ ### Updating from v0.1
102
+
103
+ ```bash
104
+ kirocrew app update prism --source <path-to>/sample-prism-d1-velocity/kirocrew-app
105
+ ```
106
+
107
+ v0.2 adds the `api` permission for the page. Per the App Kit, an update that adds an `api` entry
108
+ keeps the app enabled but withholds the new entry until you approve it on the app's detail page
109
+ (**Apps → PRISM**). Until you approve it, the page shows an API error. If the page answers 404 after
110
+ the update, the gateway has not wired the new routes yet: disable and enable the app once, or
111
+ restart the gateway.
112
+
113
+ ## Settings
114
+
115
+ The app reads `~/.kiro/crew/apps/prism/data/config.json`, which is optional:
116
+
117
+ ```json
118
+ { "spendThreshold": 25, "cliPath": "/absolute/path/to/prism-cli" }
119
+ ```
120
+
121
+ | Key | Default | Meaning |
122
+ |-----|---------|---------|
123
+ | `spendThreshold` | off | Notify once a day when today's AI spend (codeburn, in its currency) is above this number. Remove the key, or set it to `null` or `0`, to turn it off. |
124
+ | `cliPath` | not set | Use this `prism-cli` instead of searching for one. |
125
+
126
+ To change the schedule, edit `prism-daily-digest` on the dashboard's **Schedule** page. Your edit
127
+ is kept across app updates unless a later version of the app changes the schedule itself.
128
+
129
+ ## The page
130
+
131
+ ![PRISM page in Kiro Crew with demo data: spend cards, top models and projects, four repos with codewatch, one expanded with its findings](../assets/images/prism-cli/kirocrew-app-page-light.png)
132
+
133
+ *The PRISM page, rendered with made-up demo data.*
134
+
135
+ The **PRISM** sidebar page shows the same data as the editor extension's machine mode:
136
+
137
+ - spend today and this month (calls, sessions), sync mode, prism-cli version, and when the data
138
+ and the usage cache were taken;
139
+ - top models and top projects this month (project folder names only);
140
+ - the machine setup checklist, with fix commands you can copy (the page never runs them);
141
+ - the repos with codewatch, newest last commit first (`git log -1` in at most 30 repos, 4 at a
142
+ time; a repo whose commit is unknown sorts by its last review). Each repo shows a badge from its
143
+ latest review (red `N high`, amber `N med`, muted `N low`, green `clean`, or `no review`; the row's
144
+ left border uses the same color), whether its hooks are on, its last commit, the latest review's
145
+ counts and up to 20 findings, plus an "N more" line. Times on the page are relative ("3 h ago",
146
+ with the full local time as a tooltip) and update while the page is open;
147
+ - **Open report**: the scan report of the review covering the repo's last commit, or the latest
148
+ review when none covers it. The page says which. You can also download the report;
149
+ - **Last digest**: when the digest cron last ran and the last message it sent;
150
+ - **Refresh**, plus an automatic refresh every 5 minutes while the page is visible.
151
+
152
+ How it talks to its backend. The page is `ui/index.mjs`, an ES module that the dashboard loads.
153
+ It calls `GET /api/apps/prism/data` and `GET /api/apps/prism/report?repoIndex=N` through the App
154
+ SDK's `useAppApi()`, which allows only the paths in `permissions.api`. The gateway serves those
155
+ routes from `scripts/page_routes.py` inside its own process. Its token middleware authenticates
156
+ every request (a dashboard session, or an app token scoped to `permissions.api`; loopback is not
157
+ exempt). Each handler then also requires the dashboard owner (`ctx.require_owner`), or this app's
158
+ own token, and refuses everything else.
159
+
160
+ Security:
161
+
162
+ - The page sends only indexes. The backend resolves an index against its own last result and
163
+ refuses any value that is not a plain number inside that list.
164
+ - Recorded text (repo names, commit subjects, finding messages written by a reviewer model) is
165
+ rendered only as React text, never as HTML.
166
+ - The report HTML is untrusted. It is shown in an `<iframe sandbox="">` (no scripts, an opaque
167
+ origin, no forms or popups), and the backend adds a CSP that blocks script and every remote load.
168
+ - Usage comes from the dashboard's 5-minute cache. When nothing is cached for today, the page does
169
+ not start a codeburn export. It says so and offers a **Load now** button that does. When the cache
170
+ is stale, the backend returns as soon as `dashboard --json` prints and lets the CLI's background
171
+ refresh finish on its own.
172
+
173
+ ## How the digest decides to notify
174
+
175
+ Each run reads `prism-cli dashboard --json` (with no `--root`, so it scans your whole home
176
+ directory) and compares it with the previous run's `data/digest-state.json`. It sends one message
177
+ that lists the changes below, then saves the new state. If delivery fails, the state is not saved,
178
+ so the next run reports the same changes again:
179
+
180
+ - **New high findings.** For each repo, the high findings in the latest codewatch review of each
181
+ branch that were not there last time. A finding is identified by branch, file and rule, so the
182
+ same issue reviewed again on a later push is not counted as new. The message gives the repo name
183
+ (never its path), the count, and the review's short commit and branch. It also includes the first
184
+ finding's text, cut to 120 characters on one line. That text comes from a reviewer model and is
185
+ only displayed, never acted on.
186
+ - **Spend.** Today's spend is above `spendThreshold`. This is sent at most once per day.
187
+ - **Setup regressions.** A machine setup item, or a repo's codewatch setup, went from `ok` to
188
+ `warn` or `missing`.
189
+
190
+ When nothing changed, the run is silent. The first run, and any run that finds the state file
191
+ missing or unreadable, saves a baseline and sends nothing. If `prism-cli` cannot be found or fails,
192
+ the run ends in an error, which the Schedule page shows, and the saved state is left as it was.
193
+
194
+ To see what a run would send without Kiro Crew:
195
+
196
+ ```bash
197
+ python3 kirocrew-app/scripts/digest.py --state-dir "$(mktemp -d)" # prints the message, or why it was quiet
198
+ ```
199
+
200
+ `--state-dir DIR` is required and keeps a manual run away from the cron's own state. `--payload FILE`
201
+ reads a saved `dashboard --json` output instead of running `prism-cli`.
202
+
203
+ ## Uninstall
204
+
205
+ ```bash
206
+ prism-cli enable kirocrew --uninstall # kirocrew app uninstall prism, then removes ~/.prism/kirocrew-app
207
+ ```
208
+
209
+ Or with Kiro Crew directly:
210
+
211
+ ```bash
212
+ kirocrew app disable prism
213
+ kirocrew app uninstall prism
214
+ ```
215
+
216
+ Disabling the app removes its cron job, MCP server and skill. By default, uninstalling keeps
217
+ `~/.kiro/crew/apps/prism/data/`. Delete that folder yourself if you want the state gone.
218
+
219
+ ## Development
220
+
221
+ ```bash
222
+ python3 -m unittest discover -s kirocrew-app/tests
223
+ node --test kirocrew-app/tests/page.test.mjs
224
+ ```
225
+
226
+ The tests cover the digest's diff logic, the `prism-cli` lookup, the page model (sorting, caps,
227
+ index checks, report arguments and fallback, digest state) and the page's rendering with fixture
228
+ data. They also check `app.json` against the
229
+ [App Kit manifest reference](https://github.com/kirodotdev/KiroCrew/blob/main/docs/app-kit/manifest-reference.md).
230
+ When Kiro Crew is installed, they also run Kiro Crew's own manifest validator. CI runs them in
231
+ `.github/workflows/kirocrew-app.yml`.
232
+
233
+ Not in v0.2: Windows support (`os.execve` on an npm `.cmd` shim needs a shell), and delivery to a
234
+ channel other than Kiro Crew's default notification.
@@ -0,0 +1,96 @@
1
+ {
2
+ "name": "prism",
3
+ "version": "0.2.3",
4
+ "displayName": "PRISM",
5
+ "description": "Codewatch findings, AI spend and setup status from your local prism-cli for Kiro Crew agents, and a daily digest that speaks only when something changed.",
6
+ "author": "aws-samples",
7
+ "license": "MIT",
8
+ "minKiroCrewVersion": "0.12.1",
9
+ "iconPath": "assets/icon.png",
10
+ "screenshots": ["assets/screenshots/page-light.png", "assets/screenshots/report-light.png"],
11
+ "screenshotsDark": ["assets/screenshots/page-dark.png", "assets/screenshots/report-dark.png"],
12
+ "tags": ["code-review", "developer-tools", "code-quality"],
13
+ "highlights": [
14
+ "PRISM page: spend, setup checklist, repos with codewatch newest commit first, their findings and a sandboxed scan report, all read-only",
15
+ "The prism MCP server: prism_status, prism_findings, prism_spend, prism_report and prism_setup, served by your own prism-cli mcp",
16
+ "Finds prism-cli installed with nvm or Volta even when Kiro Crew's PATH does not include it",
17
+ "prism-fix-findings skill: an agent checks the latest codewatch review before it finishes, fixes real issues in at most 2 rounds and reports the rest",
18
+ "prism-daily-digest: a script cron with no model call that notifies only on a new high finding, spend over your threshold, or a setup item that broke"
19
+ ],
20
+ "useCases": [
21
+ "When you start the day, open the PRISM page to see which repos got new codewatch findings since their last commit and open the scan report of the one that needs attention",
22
+ "When an agent finishes a code change in a repo with codewatch, the prism-fix-findings skill has it call prism_findings and fix the real high and medium findings before it reports done",
23
+ "When you want to know what codewatch flagged across your repos, ask a chat to call prism_findings or prism_status instead of opening the dashboard",
24
+ "When you want a heads-up without watching a page, let prism-daily-digest run at 09:00 and tell you only about new high findings, a setup regression, or spend over your threshold"
25
+ ],
26
+ "configuration": [
27
+ "Install with prism-cli 1.9.0 or later: npm install -g @prism-d1/cli && prism-cli enable kirocrew (or install this folder; see the README)",
28
+ "Trust the app when you enable it; Kiro Crew registers nothing from a third-party app until you do",
29
+ "Optional: set spendThreshold and cliPath in the app's data/config.json (see the README)"
30
+ ],
31
+ "links": [
32
+ { "label": "README", "url": "https://github.com/aws-samples/sample-prism-d1-velocity/blob/main/kirocrew-app/README.md" },
33
+ { "label": "PRISM user guide", "url": "https://github.com/aws-samples/sample-prism-d1-velocity/blob/main/USER_GUIDE.md" }
34
+ ],
35
+ "skills": ["skills/prism-fix-findings"],
36
+ "ui": {
37
+ "entry": "index.mjs",
38
+ "pages": [
39
+ { "route": "/apps/prism", "label": "PRISM", "icon": "ScanSearch" }
40
+ ]
41
+ },
42
+ "backend": {
43
+ "hooks": {
44
+ "routes": "scripts.page_routes:register_routes"
45
+ }
46
+ },
47
+ "mcpServers": {
48
+ "prism": {
49
+ "command": "python3",
50
+ "args": ["scripts/prism_mcp.py"]
51
+ }
52
+ },
53
+ "crons": [
54
+ {
55
+ "name": "prism-daily-digest",
56
+ "cron_expr": "0 9 * * *",
57
+ "timezone": "local",
58
+ "strict_schedule": true,
59
+ "script": "scripts/digest.py:run",
60
+ "message": "PRISM daily digest (script, no model call)",
61
+ "persistent_session": false,
62
+ "timeout": 120,
63
+ "timeout_secs": 130
64
+ }
65
+ ],
66
+ "permissions": {
67
+ "api": ["/api/apps/prism/*"],
68
+ "cron": true,
69
+ "storage": true,
70
+ "network": false
71
+ },
72
+ "dependencies": {
73
+ "commands": ["python3"],
74
+ "optionalCommands": ["prism-cli"]
75
+ },
76
+ "platform": {
77
+ "os": ["macos", "linux"]
78
+ },
79
+ "setup": {
80
+ "configSchema": {
81
+ "type": "object",
82
+ "additionalProperties": false,
83
+ "properties": {
84
+ "spendThreshold": {
85
+ "type": ["number", "null"],
86
+ "exclusiveMinimum": 0,
87
+ "description": "Notify once a day when today's AI spend (codeburn, in its currency) is above this. Unset or null: off."
88
+ },
89
+ "cliPath": {
90
+ "type": ["string", "null"],
91
+ "description": "Absolute path to prism-cli, when it is not found on PATH or in the nvm, Volta, ~/.local/bin or npm prefix folders."
92
+ }
93
+ }
94
+ }
95
+ }
96
+ }