cleargate 0.8.2 → 0.11.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 (122) hide show
  1. package/CHANGELOG.md +210 -0
  2. package/README.md +22 -1
  3. package/dist/MANIFEST.json +276 -31
  4. package/dist/chunk-HZPJ5QX4.js +459 -0
  5. package/dist/chunk-HZPJ5QX4.js.map +1 -0
  6. package/dist/{chunk-OM4FAEA7.js → chunk-Q3BTSXCK.js} +69 -3
  7. package/dist/chunk-Q3BTSXCK.js.map +1 -0
  8. package/dist/cli.cjs +2888 -598
  9. package/dist/cli.cjs.map +1 -1
  10. package/dist/cli.js +2481 -619
  11. package/dist/cli.js.map +1 -1
  12. package/dist/lib/ledger.cjs +120 -0
  13. package/dist/lib/ledger.cjs.map +1 -0
  14. package/dist/lib/ledger.d.cts +64 -0
  15. package/dist/lib/ledger.d.ts +64 -0
  16. package/dist/lib/ledger.js +96 -0
  17. package/dist/lib/ledger.js.map +1 -0
  18. package/dist/lib/lifecycle-reconcile.cjs +497 -0
  19. package/dist/lib/lifecycle-reconcile.cjs.map +1 -0
  20. package/dist/lib/lifecycle-reconcile.d.cts +136 -0
  21. package/dist/lib/lifecycle-reconcile.d.ts +136 -0
  22. package/dist/lib/lifecycle-reconcile.js +20 -0
  23. package/dist/lib/lifecycle-reconcile.js.map +1 -0
  24. package/dist/templates/cleargate-planning/.claude/agents/architect.md +65 -10
  25. package/dist/templates/cleargate-planning/.claude/agents/cleargate-wiki-contradict.md +108 -0
  26. package/dist/templates/cleargate-planning/.claude/agents/cleargate-wiki-ingest.md +49 -3
  27. package/dist/templates/cleargate-planning/.claude/agents/cleargate-wiki-lint.md +6 -1
  28. package/dist/templates/cleargate-planning/.claude/agents/developer.md +51 -2
  29. package/dist/templates/cleargate-planning/.claude/agents/devops.md +249 -0
  30. package/dist/templates/cleargate-planning/.claude/agents/qa.md +91 -1
  31. package/dist/templates/cleargate-planning/.claude/agents/reporter.md +72 -14
  32. package/dist/templates/cleargate-planning/.claude/hooks/pre-commit-surface-gate.sh +21 -0
  33. package/dist/templates/cleargate-planning/.claude/hooks/pre-tool-use-task.sh +148 -0
  34. package/dist/templates/cleargate-planning/.claude/hooks/session-start.sh +6 -0
  35. package/dist/templates/cleargate-planning/.claude/hooks/stamp-and-gate.sh +12 -1
  36. package/dist/templates/cleargate-planning/.claude/hooks/token-ledger.sh +334 -96
  37. package/dist/templates/cleargate-planning/.claude/settings.json +4 -0
  38. package/dist/templates/cleargate-planning/.claude/skills/sprint-execution/SKILL.md +644 -0
  39. package/dist/templates/cleargate-planning/.cleargate/config.example.yml +19 -0
  40. package/dist/templates/cleargate-planning/.cleargate/knowledge/cleargate-enforcement.md +542 -0
  41. package/dist/templates/cleargate-planning/.cleargate/knowledge/cleargate-protocol.md +102 -428
  42. package/dist/templates/cleargate-planning/.cleargate/knowledge/mid-sprint-triage-rubric.md +160 -0
  43. package/dist/templates/cleargate-planning/.cleargate/knowledge/readiness-gates.md +72 -9
  44. package/dist/templates/cleargate-planning/.cleargate/knowledge/sprint-closeout-checklist.md +71 -0
  45. package/dist/templates/cleargate-planning/.cleargate/scripts/assert_story_files.mjs +24 -2
  46. package/dist/templates/cleargate-planning/.cleargate/scripts/close_sprint.mjs +471 -29
  47. package/dist/templates/cleargate-planning/.cleargate/scripts/dedupe_frontmatter.mjs +219 -0
  48. package/dist/templates/cleargate-planning/.cleargate/scripts/gate-checks.json +3 -3
  49. package/dist/templates/cleargate-planning/.cleargate/scripts/init_sprint.mjs +86 -10
  50. package/dist/templates/cleargate-planning/.cleargate/scripts/lib/report-filename.mjs +54 -0
  51. package/dist/templates/cleargate-planning/.cleargate/scripts/prep_doc_refresh.mjs +378 -0
  52. package/dist/templates/cleargate-planning/.cleargate/scripts/prep_qa_context.mjs +888 -0
  53. package/dist/templates/cleargate-planning/.cleargate/scripts/run_script.sh +173 -87
  54. package/dist/templates/cleargate-planning/.cleargate/scripts/sprint_trends.mjs +71 -0
  55. package/dist/templates/cleargate-planning/.cleargate/scripts/suggest_improvements.mjs +483 -13
  56. package/dist/templates/cleargate-planning/.cleargate/scripts/test/test_prep_qa_context.sh +482 -0
  57. package/dist/templates/cleargate-planning/.cleargate/scripts/validate_state.mjs +32 -8
  58. package/dist/templates/cleargate-planning/.cleargate/scripts/write_dispatch.sh +136 -0
  59. package/dist/templates/cleargate-planning/.cleargate/templates/Bug.md +27 -1
  60. package/dist/templates/cleargate-planning/.cleargate/templates/CR.md +35 -1
  61. package/dist/templates/cleargate-planning/.cleargate/templates/Sprint Plan Template.md +48 -14
  62. package/dist/templates/cleargate-planning/.cleargate/templates/epic.md +40 -3
  63. package/dist/templates/cleargate-planning/.cleargate/templates/hotfix.md +53 -0
  64. package/dist/templates/cleargate-planning/.cleargate/templates/initiative.md +98 -29
  65. package/dist/templates/cleargate-planning/.cleargate/templates/proposal.md +17 -4
  66. package/dist/templates/cleargate-planning/.cleargate/templates/sprint_context.md +8 -0
  67. package/dist/templates/cleargate-planning/.cleargate/templates/sprint_report.md +23 -4
  68. package/dist/templates/cleargate-planning/.cleargate/templates/story.md +58 -3
  69. package/dist/templates/cleargate-planning/CLAUDE.md +30 -10
  70. package/dist/templates/cleargate-planning/MANIFEST.json +276 -31
  71. package/dist/{whoami-CX7CXJD5.js → whoami-W4U6DPVG.js} +17 -17
  72. package/dist/whoami-W4U6DPVG.js.map +1 -0
  73. package/package.json +20 -6
  74. package/templates/cleargate-planning/.claude/agents/architect.md +65 -10
  75. package/templates/cleargate-planning/.claude/agents/cleargate-wiki-contradict.md +108 -0
  76. package/templates/cleargate-planning/.claude/agents/cleargate-wiki-ingest.md +49 -3
  77. package/templates/cleargate-planning/.claude/agents/cleargate-wiki-lint.md +6 -1
  78. package/templates/cleargate-planning/.claude/agents/developer.md +51 -2
  79. package/templates/cleargate-planning/.claude/agents/devops.md +249 -0
  80. package/templates/cleargate-planning/.claude/agents/qa.md +91 -1
  81. package/templates/cleargate-planning/.claude/agents/reporter.md +72 -14
  82. package/templates/cleargate-planning/.claude/hooks/pre-commit-surface-gate.sh +21 -0
  83. package/templates/cleargate-planning/.claude/hooks/pre-tool-use-task.sh +148 -0
  84. package/templates/cleargate-planning/.claude/hooks/session-start.sh +6 -0
  85. package/templates/cleargate-planning/.claude/hooks/stamp-and-gate.sh +12 -1
  86. package/templates/cleargate-planning/.claude/hooks/token-ledger.sh +334 -96
  87. package/templates/cleargate-planning/.claude/settings.json +4 -0
  88. package/templates/cleargate-planning/.claude/skills/sprint-execution/SKILL.md +644 -0
  89. package/templates/cleargate-planning/.cleargate/config.example.yml +19 -0
  90. package/templates/cleargate-planning/.cleargate/knowledge/cleargate-enforcement.md +542 -0
  91. package/templates/cleargate-planning/.cleargate/knowledge/cleargate-protocol.md +102 -428
  92. package/templates/cleargate-planning/.cleargate/knowledge/mid-sprint-triage-rubric.md +160 -0
  93. package/templates/cleargate-planning/.cleargate/knowledge/readiness-gates.md +72 -9
  94. package/templates/cleargate-planning/.cleargate/knowledge/sprint-closeout-checklist.md +71 -0
  95. package/templates/cleargate-planning/.cleargate/scripts/assert_story_files.mjs +24 -2
  96. package/templates/cleargate-planning/.cleargate/scripts/close_sprint.mjs +471 -29
  97. package/templates/cleargate-planning/.cleargate/scripts/dedupe_frontmatter.mjs +219 -0
  98. package/templates/cleargate-planning/.cleargate/scripts/gate-checks.json +3 -3
  99. package/templates/cleargate-planning/.cleargate/scripts/init_sprint.mjs +86 -10
  100. package/templates/cleargate-planning/.cleargate/scripts/lib/report-filename.mjs +54 -0
  101. package/templates/cleargate-planning/.cleargate/scripts/prep_doc_refresh.mjs +378 -0
  102. package/templates/cleargate-planning/.cleargate/scripts/prep_qa_context.mjs +888 -0
  103. package/templates/cleargate-planning/.cleargate/scripts/run_script.sh +173 -87
  104. package/templates/cleargate-planning/.cleargate/scripts/sprint_trends.mjs +71 -0
  105. package/templates/cleargate-planning/.cleargate/scripts/suggest_improvements.mjs +483 -13
  106. package/templates/cleargate-planning/.cleargate/scripts/test/test_prep_qa_context.sh +482 -0
  107. package/templates/cleargate-planning/.cleargate/scripts/validate_state.mjs +32 -8
  108. package/templates/cleargate-planning/.cleargate/scripts/write_dispatch.sh +136 -0
  109. package/templates/cleargate-planning/.cleargate/templates/Bug.md +27 -1
  110. package/templates/cleargate-planning/.cleargate/templates/CR.md +35 -1
  111. package/templates/cleargate-planning/.cleargate/templates/Sprint Plan Template.md +48 -14
  112. package/templates/cleargate-planning/.cleargate/templates/epic.md +40 -3
  113. package/templates/cleargate-planning/.cleargate/templates/hotfix.md +53 -0
  114. package/templates/cleargate-planning/.cleargate/templates/initiative.md +98 -29
  115. package/templates/cleargate-planning/.cleargate/templates/sprint_context.md +8 -0
  116. package/templates/cleargate-planning/.cleargate/templates/sprint_report.md +23 -4
  117. package/templates/cleargate-planning/.cleargate/templates/story.md +58 -3
  118. package/templates/cleargate-planning/CLAUDE.md +30 -10
  119. package/templates/cleargate-planning/MANIFEST.json +276 -31
  120. package/dist/chunk-OM4FAEA7.js.map +0 -1
  121. package/dist/whoami-CX7CXJD5.js.map +0 -1
  122. package/templates/cleargate-planning/.cleargate/templates/proposal.md +0 -61
@@ -1,123 +1,209 @@
1
1
  #!/usr/bin/env bash
2
- # run_script.sh — Wrapper that captures stdout/stderr separately and prints a
3
- # structured diagnostic block on non-zero exit.
4
- # Usage: run_script.sh <script-name> [args...]
5
- # Supported extensions: .mjs (runs via node), .sh (runs via bash)
2
+ # run_script.sh — Arbitrary-command wrapper that captures stdout/stderr independently
3
+ # and writes a structured JSON incident file on non-zero exit.
4
+ #
5
+ # Interface: bash run_script.sh <command> [args...]
6
+ # <command> — any executable on PATH (e.g. node, bash, sh, true, false)
7
+ # [args...] — forwarded to the command unchanged
8
+ #
9
+ # Example (node script):
10
+ # bash run_script.sh node .cleargate/scripts/update_state.mjs STORY-01 Done
11
+ # Example (bash script):
12
+ # bash run_script.sh bash .cleargate/scripts/pre_gate_runner.sh qa .worktrees/X sprint/S-01
13
+ #
14
+ # On success (exit 0): stdout+stderr are passed through; no incident file written.
15
+ # On failure (exit ≠ 0): stdout+stderr are passed through AND a JSON incident is
16
+ # written to .cleargate/sprint-runs/<active-sprint>/.script-incidents/<ts>-<hash>.json
17
+ #
18
+ # Self-exemption: if RUN_SCRIPT_ACTIVE=1 is already set, the wrapper is already
19
+ # running — do not nest. Execute the command directly to avoid infinite recursion.
20
+ # This guard implements the self-exempt contract documented in SKILL.md §C.x.
21
+ #
22
+ # Env vars read:
23
+ # ORCHESTRATOR_PROJECT_DIR — project root override (falls back to CLAUDE_PROJECT_DIR,
24
+ # then to git rev-parse --show-toplevel, then script dir ancestor)
25
+ # AGENT_TYPE — populates incident JSON agent_type field (null if empty)
26
+ # WORK_ITEM_ID — populates incident JSON work_item_id field (null if empty)
27
+ # RUN_SCRIPT_ACTIVE — self-exemption guard (set to 1 by this wrapper)
28
+
6
29
  set -euo pipefail
7
30
 
8
- SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
31
+ # ---------------------------------------------------------------------------
32
+ # Self-exemption guard — do not wrap recursively
33
+ # ---------------------------------------------------------------------------
34
+ if [[ "${RUN_SCRIPT_ACTIVE:-}" == "1" ]]; then
35
+ # Already inside a wrapper invocation; pass through directly, no JSON capture
36
+ exec "$@"
37
+ fi
9
38
 
10
39
  # ---------------------------------------------------------------------------
11
40
  # Usage guard
12
41
  # ---------------------------------------------------------------------------
13
42
  if [[ $# -lt 1 ]]; then
14
- echo "Usage: run_script.sh <script-name> [args...]" >&2
43
+ echo "Usage: bash run_script.sh <command> [args...]" >&2
15
44
  exit 2
16
45
  fi
17
46
 
18
- SCRIPT_NAME="$1"
19
- shift
20
- SCRIPT_ARGS=()
21
- if [[ $# -gt 0 ]]; then
22
- SCRIPT_ARGS=("$@")
23
- fi
24
-
25
47
  # ---------------------------------------------------------------------------
26
- # Resolve path script may be an absolute path or relative to SCRIPT_DIR
48
+ # Resolve project root for incident file path
27
49
  # ---------------------------------------------------------------------------
28
- if [[ "$SCRIPT_NAME" == /* ]]; then
29
- SCRIPT_PATH="$SCRIPT_NAME"
30
- else
31
- SCRIPT_PATH="${SCRIPT_DIR}/${SCRIPT_NAME}"
32
- fi
50
+ _resolve_project_root() {
51
+ # Priority: ORCHESTRATOR_PROJECT_DIR → CLAUDE_PROJECT_DIR → git toplevel → script dir ancestor
52
+ if [[ -n "${ORCHESTRATOR_PROJECT_DIR:-}" ]]; then
53
+ echo "$ORCHESTRATOR_PROJECT_DIR"
54
+ return
55
+ fi
56
+ if [[ -n "${CLAUDE_PROJECT_DIR:-}" ]]; then
57
+ echo "$CLAUDE_PROJECT_DIR"
58
+ return
59
+ fi
60
+ if _root=$(git rev-parse --show-toplevel 2>/dev/null); then
61
+ echo "$_root"
62
+ return
63
+ fi
64
+ # Fallback: assume run_script.sh lives in <repo>/.cleargate/scripts/
65
+ echo "$(cd "$(dirname "${BASH_SOURCE[0]}")" && cd ../.. && pwd)"
66
+ }
33
67
 
34
- # ---------------------------------------------------------------------------
35
- # Extension routing
36
- # ---------------------------------------------------------------------------
37
- EXT="${SCRIPT_NAME##*.}"
38
- case "$EXT" in
39
- mjs) RUNNER="node" ;;
40
- sh) RUNNER="bash" ;;
41
- *)
42
- echo "unsupported extension: .${EXT}" >&2
43
- exit 2
44
- ;;
45
- esac
68
+ PROJECT_ROOT="$(_resolve_project_root)"
69
+ SPRINT_RUNS_DIR="${PROJECT_ROOT}/.cleargate/sprint-runs"
70
+ ACTIVE_FILE="${SPRINT_RUNS_DIR}/.active"
46
71
 
47
- # ---------------------------------------------------------------------------
48
- # Check script exists
49
- # ---------------------------------------------------------------------------
50
- if [[ ! -f "$SCRIPT_PATH" ]]; then
51
- echo "run_script.sh: script not found: ${SCRIPT_PATH}" >&2
52
- exit 2
72
+ # Determine sprint incident dir
73
+ if [[ -f "$ACTIVE_FILE" ]]; then
74
+ SPRINT_ID="$(cat "$ACTIVE_FILE" | tr -d '[:space:]')"
75
+ INCIDENTS_DIR="${SPRINT_RUNS_DIR}/${SPRINT_ID}/.script-incidents"
76
+ else
77
+ # No active sprint sentinel → write to _off-sprint bucket
78
+ INCIDENTS_DIR="${SPRINT_RUNS_DIR}/_off-sprint/.script-incidents"
53
79
  fi
54
80
 
55
81
  # ---------------------------------------------------------------------------
56
82
  # Capture stdout + stderr to temp files
57
83
  # ---------------------------------------------------------------------------
58
- STDOUT_FILE="$(mktemp)"
59
- STDERR_FILE="$(mktemp)"
60
- trap 'rm -f "$STDOUT_FILE" "$STDERR_FILE"' EXIT
84
+ STDOUT_TMP="$(mktemp)"
85
+ STDERR_TMP="$(mktemp)"
86
+ trap 'rm -f "$STDOUT_TMP" "$STDERR_TMP"' EXIT
87
+
88
+ # Mark self as active before running the wrapped command
89
+ export RUN_SCRIPT_ACTIVE=1
61
90
 
62
91
  EXIT_CODE=0
63
- if [[ ${#SCRIPT_ARGS[@]} -gt 0 ]]; then
64
- "$RUNNER" "$SCRIPT_PATH" "${SCRIPT_ARGS[@]}" > "$STDOUT_FILE" 2> "$STDERR_FILE" || EXIT_CODE=$?
65
- else
66
- "$RUNNER" "$SCRIPT_PATH" > "$STDOUT_FILE" 2> "$STDERR_FILE" || EXIT_CODE=$?
67
- fi
92
+ "$@" >"$STDOUT_TMP" 2>"$STDERR_TMP" || EXIT_CODE=$?
68
93
 
69
94
  # ---------------------------------------------------------------------------
70
- # On success: pass through and exit 0
95
+ # Always pass through stdout + stderr to the caller
96
+ # ---------------------------------------------------------------------------
97
+ cat "$STDOUT_TMP"
98
+ cat "$STDERR_TMP" >&2
99
+
100
+ # ---------------------------------------------------------------------------
101
+ # On success: nothing more to do
71
102
  # ---------------------------------------------------------------------------
72
103
  if [[ $EXIT_CODE -eq 0 ]]; then
73
- cat "$STDOUT_FILE"
74
- cat "$STDERR_FILE" >&2
75
104
  exit 0
76
105
  fi
77
106
 
78
107
  # ---------------------------------------------------------------------------
79
- # On failure: pass stdout through, then print structured diagnostic to stderr
108
+ # On failure: write structured JSON incident file
80
109
  # ---------------------------------------------------------------------------
81
- cat "$STDOUT_FILE"
82
-
83
- # Root-cause heuristic (6-branch)
84
- STDERR_CONTENT="$(cat "$STDERR_FILE")"
85
- ROOT_CAUSE="unknown error"
86
- SUGGESTED_FIX="check the script output above for details"
87
-
88
- if echo "$STDERR_CONTENT" | grep -q "state\.json not found"; then
89
- ROOT_CAUSE="state.json not found — sprint may not be initialized"
90
- SUGGESTED_FIX="run: node .cleargate/scripts/init_sprint.mjs <sprint-id> --stories <ids>"
91
- elif echo "$STDERR_CONTENT" | grep -qi "ENOENT"; then
92
- ROOT_CAUSE="missing file (ENOENT)"
93
- SUGGESTED_FIX="verify all required files exist at the expected paths"
94
- elif echo "$STDERR_CONTENT" | grep -qi "EACCES"; then
95
- ROOT_CAUSE="permission denied (EACCES)"
96
- SUGGESTED_FIX="chmod 755 the target file or directory"
97
- elif echo "$STDERR_CONTENT" | grep -qi "SyntaxError"; then
98
- ROOT_CAUSE="JavaScript syntax error"
99
- SUGGESTED_FIX="fix the syntax error in the script; run: node --check <file>"
100
- elif echo "$STDERR_CONTENT" | grep -qi "Cannot find module"; then
101
- ROOT_CAUSE="missing module (import resolution failure)"
102
- SUGGESTED_FIX="run npm install in the relevant package directory"
103
- elif echo "$STDERR_CONTENT" | grep -qi "command not found"; then
104
- ROOT_CAUSE="required command not found on PATH"
105
- SUGGESTED_FIX="install the missing tool or add it to PATH"
110
+ MAX_BYTES=4096
111
+ TRUNCATION_SUFFIX="... [truncated]"
112
+
113
+ _truncate_stream() {
114
+ # Byte-correct truncation: use head -c for POSIX byte-count (not bash ${var:0:N}
115
+ # which is char-index wrong for UTF-8 multi-byte input).
116
+ # Trade-off: output may end with a partial multi-byte char boundary; downstream
117
+ # Node.js JSON.stringify escapes the partial sequence, producing valid JSON.
118
+ local file="$1"
119
+ local file_bytes
120
+ file_bytes="$(wc -c < "$file")"
121
+ if [[ $file_bytes -le $MAX_BYTES ]]; then
122
+ # File fits within budget emit as-is (no suffix)
123
+ head -c "$MAX_BYTES" "$file"
124
+ else
125
+ # Truncation occurred emit first MAX_BYTES bytes then TRUNCATION_SUFFIX
126
+ printf '%s' "$(head -c "$MAX_BYTES" "$file")${TRUNCATION_SUFFIX}"
127
+ fi
128
+ }
129
+
130
+ STDOUT_CAPTURED="$(_truncate_stream "$STDOUT_TMP")"
131
+ STDERR_CAPTURED="$(_truncate_stream "$STDERR_TMP")"
132
+
133
+ # Build filename: <ts>-<hash>.json
134
+ TS="$(date -u +%Y-%m-%dT%H:%M:%SZ)"
135
+ TS_FILE="$(date -u +%Y%m%dT%H%M%SZ)"
136
+ # Hash the full command string (first arg + all args)
137
+ HASH="$(printf '%s' "$*" | shasum -a 1 | cut -c1-12)"
138
+ INCIDENT_FILE="${INCIDENTS_DIR}/${TS_FILE}-${HASH}.json"
139
+
140
+ # Ensure incident directory exists
141
+ mkdir -p "$INCIDENTS_DIR"
142
+
143
+ # Collect context
144
+ COMMAND="$1"
145
+ shift
146
+ ARGS_JSON="["
147
+ FIRST=1
148
+ for ARG in "$@"; do
149
+ if [[ $FIRST -eq 1 ]]; then
150
+ FIRST=0
151
+ else
152
+ ARGS_JSON="${ARGS_JSON},"
153
+ fi
154
+ # JSON-escape the arg: replace \ with \\, " with \", newlines with \n
155
+ ARG_ESCAPED="${ARG//\\/\\\\}"
156
+ ARG_ESCAPED="${ARG_ESCAPED//\"/\\\"}"
157
+ ARG_ESCAPED="${ARG_ESCAPED//$'\n'/\\n}"
158
+ ARGS_JSON="${ARGS_JSON}\"${ARG_ESCAPED}\""
159
+ done
160
+ ARGS_JSON="${ARGS_JSON}]"
161
+
162
+ CWD="$(pwd)"
163
+
164
+ # Encode agent_type + work_item_id (null if empty)
165
+ AGENT_TYPE_VAL="${AGENT_TYPE:-}"
166
+ WORK_ITEM_ID_VAL="${WORK_ITEM_ID:-}"
167
+
168
+ if [[ -z "$AGENT_TYPE_VAL" ]]; then
169
+ AGENT_TYPE_JSON="null"
170
+ else
171
+ AGENT_TYPE_JSON="\"${AGENT_TYPE_VAL}\""
106
172
  fi
107
173
 
174
+ if [[ -z "$WORK_ITEM_ID_VAL" ]]; then
175
+ WORK_ITEM_ID_JSON="null"
176
+ else
177
+ WORK_ITEM_ID_JSON="\"${WORK_ITEM_ID_VAL}\""
178
+ fi
179
+
180
+ # JSON-encode captured streams (escape backslash, double-quote, and newlines)
181
+ _json_str() {
182
+ local s="$1"
183
+ s="${s//\\/\\\\}"
184
+ s="${s//\"/\\\"}"
185
+ s="${s//$'\n'/\\n}"
186
+ s="${s//$'\r'/\\r}"
187
+ printf '%s' "$s"
188
+ }
189
+
190
+ STDOUT_JSON="$(_json_str "$STDOUT_CAPTURED")"
191
+ STDERR_JSON="$(_json_str "$STDERR_CAPTURED")"
192
+ COMMAND_JSON="$(_json_str "$COMMAND")"
193
+ CWD_JSON="$(_json_str "$CWD")"
194
+
195
+ cat > "$INCIDENT_FILE" <<JSON
108
196
  {
109
- echo ""
110
- echo "## Script Incident"
111
- echo "Script: ${SCRIPT_NAME}"
112
- echo "Runner: ${RUNNER}"
113
- echo "Exit code: ${EXIT_CODE}"
114
- echo ""
115
- echo "### First 10 lines of stderr:"
116
- echo "$STDERR_CONTENT" | head -10
117
- echo ""
118
- echo "Root cause: ${ROOT_CAUSE}"
119
- echo "Suggested fix: ${SUGGESTED_FIX}"
120
- echo "## End Incident"
121
- } >&2
197
+ "ts": "${TS}",
198
+ "command": "${COMMAND_JSON}",
199
+ "args": ${ARGS_JSON},
200
+ "cwd": "${CWD_JSON}",
201
+ "exit_code": ${EXIT_CODE},
202
+ "stdout": "${STDOUT_JSON}",
203
+ "stderr": "${STDERR_JSON}",
204
+ "agent_type": ${AGENT_TYPE_JSON},
205
+ "work_item_id": ${WORK_ITEM_ID_JSON}
206
+ }
207
+ JSON
122
208
 
123
209
  exit $EXIT_CODE
@@ -0,0 +1,71 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * sprint_trends.mjs — Sprint trends stub (full implementation deferred to CR-027)
4
+ *
5
+ * Usage: node sprint_trends.mjs <sprint-id>
6
+ *
7
+ * Counts sibling Completed sprints and appends a placeholder Trends section
8
+ * to the current sprint's improvement-suggestions.md.
9
+ *
10
+ * Test seams:
11
+ * CLEARGATE_SPRINT_DIR=<path> — override sprint dir resolution
12
+ * CLEARGATE_SPRINT_RUNS_DIR=<path> — override .cleargate/sprint-runs/ root for sibling counting
13
+ */
14
+
15
+ import fs from 'node:fs';
16
+ import path from 'node:path';
17
+ import { fileURLToPath } from 'node:url';
18
+
19
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
20
+ const REPO_ROOT = path.resolve(__dirname, '..', '..');
21
+
22
+ function main() {
23
+ const sprintId = process.argv[2];
24
+ if (!sprintId) {
25
+ process.stderr.write('Usage: node sprint_trends.mjs <sprint-id>\n');
26
+ process.exit(2);
27
+ }
28
+
29
+ const sprintDir = process.env.CLEARGATE_SPRINT_DIR
30
+ ? path.resolve(process.env.CLEARGATE_SPRINT_DIR)
31
+ : path.join(REPO_ROOT, '.cleargate', 'sprint-runs', sprintId);
32
+
33
+ const sprintRunsDir = process.env.CLEARGATE_SPRINT_RUNS_DIR
34
+ ? path.resolve(process.env.CLEARGATE_SPRINT_RUNS_DIR)
35
+ : path.dirname(sprintDir);
36
+
37
+ // Count sibling sprint dirs whose state.json has sprint_status === 'Completed'
38
+ let completedCount = 0;
39
+ if (fs.existsSync(sprintRunsDir)) {
40
+ for (const entry of fs.readdirSync(sprintRunsDir)) {
41
+ if (entry === path.basename(sprintDir)) continue;
42
+ const siblingState = path.join(sprintRunsDir, entry, 'state.json');
43
+ if (!fs.existsSync(siblingState)) continue;
44
+ try {
45
+ const state = JSON.parse(fs.readFileSync(siblingState, 'utf8'));
46
+ if (state.sprint_status === 'Completed') completedCount++;
47
+ } catch { /* skip malformed state.json */ }
48
+ }
49
+ }
50
+
51
+ const suggestionsFile = path.join(sprintDir, 'improvement-suggestions.md');
52
+ const trendsSection = `\n## Trends\n\nTrends: ${completedCount} closed sprints visible — full analysis deferred to CR-027.\n`;
53
+
54
+ if (!fs.existsSync(suggestionsFile)) {
55
+ const header = `# Improvement Suggestions — ${sprintId}\n\n`;
56
+ const tmpFile = `${suggestionsFile}.tmp.${process.pid}`;
57
+ fs.writeFileSync(tmpFile, header + trendsSection, 'utf8');
58
+ fs.renameSync(tmpFile, suggestionsFile);
59
+ } else {
60
+ const existing = fs.readFileSync(suggestionsFile, 'utf8');
61
+ const tmpFile = `${suggestionsFile}.tmp.${process.pid}`;
62
+ fs.writeFileSync(tmpFile, existing.trimEnd() + trendsSection, 'utf8');
63
+ fs.renameSync(tmpFile, suggestionsFile);
64
+ }
65
+
66
+ process.stdout.write(
67
+ `sprint_trends: stub — full implementation deferred to CR-027 (counted ${completedCount} closed sprints).\n`
68
+ );
69
+ }
70
+
71
+ main();