murmur8 4.5.0 → 4.6.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.
- package/.blueprint/features/feature_feedback-test/FEATURE_SPEC.md +229 -0
- package/.blueprint/features/feature_feedback-test/IMPLEMENTATION_PLAN.md +25 -0
- package/.blueprint/features/feature_feedback-test/handoff-alex.md +20 -0
- package/.blueprint/features/feature_feedback-test/handoff-cass.md +21 -0
- package/.blueprint/features/feature_feedback-test/handoff-nigel.md +20 -0
- package/.blueprint/features/feature_feedback-test/story-config-management.md +103 -0
- package/.blueprint/features/feature_feedback-test/story-parse-pipeline.md +65 -0
- package/.blueprint/features/feature_feedback-test/story-validation-normalisation.md +99 -0
- package/.blueprint/features/feature_pipeline-telemetry/FEATURE_SPEC.md +297 -0
- package/.blueprint/features/feature_pipeline-telemetry/IMPLEMENTATION_PLAN.md +34 -0
- package/.blueprint/features/feature_pipeline-telemetry/handoff-alex.md +21 -0
- package/.blueprint/features/feature_pipeline-telemetry/handoff-cass.md +25 -0
- package/.blueprint/features/feature_pipeline-telemetry/handoff-nigel.md +20 -0
- package/.blueprint/features/feature_pipeline-telemetry/story-failed-queue-retry.md +53 -0
- package/.blueprint/features/feature_pipeline-telemetry/story-identifiers.md +47 -0
- package/.blueprint/features/feature_pipeline-telemetry/story-init-integration.md +48 -0
- package/.blueprint/features/feature_pipeline-telemetry/story-payload-send.md +54 -0
- package/.blueprint/features/feature_pipeline-telemetry/story-telemetry-activation.md +54 -0
- package/.blueprint/features/feature_pipeline-telemetry/story-telemetry-config-command.md +52 -0
- package/README.md +54 -0
- package/SKILL.md +35 -24
- package/package.json +1 -1
- package/src/commands/history.js +41 -2
- package/src/commands/telemetry-config.js +16 -0
- package/src/history.js +31 -0
- package/src/index.js +16 -1
- package/src/init.js +5 -0
- package/src/telemetry.js +198 -0
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# Story — Telemetry Activation & Configuration
|
|
2
|
+
|
|
3
|
+
### User story
|
|
4
|
+
As a platform administrator, I want to activate pipeline telemetry by setting a URL in `.env` so that murmur8 sends structured execution data to my observability endpoint without requiring code changes.
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
### Context / scope
|
|
9
|
+
- Configuration via `.env` file at project root and/or real environment variables
|
|
10
|
+
- Activation is determined solely by the presence of `MURMUR8_TELEMETRY_URL`
|
|
11
|
+
- Absence of the URL means telemetry is fully inactive — no output, no side effects
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
### Acceptance criteria
|
|
16
|
+
|
|
17
|
+
**AC-1 — Telemetry inactive when URL is absent**
|
|
18
|
+
- Given `MURMUR8_TELEMETRY_URL` is not set in `.env` or in the process environment,
|
|
19
|
+
- When a pipeline run completes,
|
|
20
|
+
- Then no HTTP request is made, no queue file is created, and no output is produced.
|
|
21
|
+
|
|
22
|
+
**AC-2 — Telemetry active when URL is set in `.env`**
|
|
23
|
+
- Given `MURMUR8_TELEMETRY_URL` is set to a valid URL in `.env`,
|
|
24
|
+
- When a pipeline run completes,
|
|
25
|
+
- Then `src/telemetry.js` POSTs the payload to that URL.
|
|
26
|
+
|
|
27
|
+
**AC-3 — Real environment variable takes precedence over `.env`**
|
|
28
|
+
- Given `MURMUR8_TELEMETRY_URL` is set to `https://env-value.example.com` in the process environment,
|
|
29
|
+
- And `.env` contains `MURMUR8_TELEMETRY_URL=https://dotenv-value.example.com`,
|
|
30
|
+
- When telemetry resolves the endpoint,
|
|
31
|
+
- Then the request is sent to `https://env-value.example.com` (the process env value).
|
|
32
|
+
|
|
33
|
+
**AC-4 — API key sent as Authorization header when set**
|
|
34
|
+
- Given `MURMUR8_TELEMETRY_KEY` is set (in `.env` or environment),
|
|
35
|
+
- When the telemetry POST is made,
|
|
36
|
+
- Then the request includes the header `Authorization: Bearer <key>`.
|
|
37
|
+
|
|
38
|
+
**AC-5 — Authorization header omitted when key is absent**
|
|
39
|
+
- Given `MURMUR8_TELEMETRY_KEY` is not set,
|
|
40
|
+
- When the telemetry POST is made,
|
|
41
|
+
- Then no `Authorization` header is included in the request.
|
|
42
|
+
|
|
43
|
+
**AC-6 — Same precedence rule applies to API key**
|
|
44
|
+
- Given `MURMUR8_TELEMETRY_KEY` is set in both the process environment and `.env` with different values,
|
|
45
|
+
- When telemetry resolves the key,
|
|
46
|
+
- Then the process environment value is used.
|
|
47
|
+
|
|
48
|
+
---
|
|
49
|
+
|
|
50
|
+
### Out of scope
|
|
51
|
+
- Opt-out flag (absence of URL is the opt-out mechanism)
|
|
52
|
+
- Validating or enforcing HTTPS on the configured URL
|
|
53
|
+
- Telemetry for non-pipeline CLI commands (`history`, `queue`, etc.)
|
|
54
|
+
- Any third-party `.env` parsing library (manual parsing only)
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# Story — telemetry-config CLI Command
|
|
2
|
+
|
|
3
|
+
### User story
|
|
4
|
+
As a platform administrator, I want to run `murmur8 telemetry-config` to view the current telemetry configuration so that I can confirm the endpoint is configured correctly and check the failed-send queue depth — without exposing the API key in plaintext.
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
### Context / scope
|
|
9
|
+
- New CLI command: `murmur8 telemetry-config` (registered in `bin/cli.js`, handled by `src/commands/telemetry-config.js`)
|
|
10
|
+
- Reads configuration from `.env` and process environment (same precedence rules as telemetry module)
|
|
11
|
+
- Displays configured URL; masks the API key; shows failed queue depth
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
### Acceptance criteria
|
|
16
|
+
|
|
17
|
+
**AC-1 — Command displays configured URL**
|
|
18
|
+
- Given `MURMUR8_TELEMETRY_URL` is set,
|
|
19
|
+
- When `murmur8 telemetry-config` is run,
|
|
20
|
+
- Then the output includes the full configured URL.
|
|
21
|
+
|
|
22
|
+
**AC-2 — API key is masked in output**
|
|
23
|
+
- Given `MURMUR8_TELEMETRY_KEY` is set to a non-empty value,
|
|
24
|
+
- When `murmur8 telemetry-config` is run,
|
|
25
|
+
- Then the output displays the key in masked form (e.g. `sk-****1234` showing only the last 4 characters) and never the full plaintext value.
|
|
26
|
+
|
|
27
|
+
**AC-3 — Key shown as "not set" when absent**
|
|
28
|
+
- Given `MURMUR8_TELEMETRY_KEY` is not configured,
|
|
29
|
+
- When `murmur8 telemetry-config` is run,
|
|
30
|
+
- Then the output indicates no key is configured (e.g. `not set`).
|
|
31
|
+
|
|
32
|
+
**AC-4 — Command shows telemetry as inactive when URL is absent**
|
|
33
|
+
- Given `MURMUR8_TELEMETRY_URL` is not set,
|
|
34
|
+
- When `murmur8 telemetry-config` is run,
|
|
35
|
+
- Then the output clearly indicates telemetry is inactive (e.g. `status: inactive`).
|
|
36
|
+
|
|
37
|
+
**AC-5 — Failed queue depth is displayed**
|
|
38
|
+
- Given `.claude/telemetry-failed.json` exists with N entries,
|
|
39
|
+
- When `murmur8 telemetry-config` is run,
|
|
40
|
+
- Then the output includes the number of queued failed sends (e.g. `failed queue: 3 entries`).
|
|
41
|
+
|
|
42
|
+
**AC-6 — Failed queue shown as zero when file is absent**
|
|
43
|
+
- Given `.claude/telemetry-failed.json` does not exist,
|
|
44
|
+
- When `murmur8 telemetry-config` is run,
|
|
45
|
+
- Then the output shows a failed queue depth of 0.
|
|
46
|
+
|
|
47
|
+
---
|
|
48
|
+
|
|
49
|
+
### Out of scope
|
|
50
|
+
- A `--test` flag to send a synthetic ping to the endpoint
|
|
51
|
+
- Editing or clearing configuration via this command (read-only display)
|
|
52
|
+
- Displaying the full contents of the failed send queue
|
package/README.md
CHANGED
|
@@ -148,6 +148,7 @@ This updates `.blueprint/agents/`, `.blueprint/templates/`, `.blueprint/ways_of_
|
|
|
148
148
|
| `npx murmur8 feedback-config set <key> <value>` | Modify feedback settings |
|
|
149
149
|
| `npx murmur8 murm-config` | View murmuration pipeline configuration |
|
|
150
150
|
| `npx murmur8 murm-config set <key> <value>` | Modify murmuration settings |
|
|
151
|
+
| `npx murmur8 telemetry-config` | View telemetry configuration and failed queue depth |
|
|
151
152
|
|
|
152
153
|
## Skill usage
|
|
153
154
|
|
|
@@ -298,6 +299,7 @@ murmur8 includes these built-in modules for observability and self-improvement:
|
|
|
298
299
|
| **stack** | Configurable tech stack detection and configuration |
|
|
299
300
|
| **cost** | Token usage tracking and cost estimation per stage |
|
|
300
301
|
| **diff-preview** | Pre-commit change review with user confirmation |
|
|
302
|
+
| **telemetry** | Opt-in audit layer — POSTs structured run data to a configured endpoint for corpus building and enterprise usage monitoring |
|
|
301
303
|
|
|
302
304
|
### How They Work Together
|
|
303
305
|
|
|
@@ -515,6 +517,58 @@ npx murmur8 cost-config reset
|
|
|
515
517
|
|
|
516
518
|
Cost data is stored in `pipeline-history.json` alongside timing and feedback data.
|
|
517
519
|
|
|
520
|
+
## Pipeline Telemetry
|
|
521
|
+
|
|
522
|
+
An opt-in audit and data collection layer. When a telemetry endpoint is configured, each completed pipeline run POSTs a structured event payload to that endpoint. If no endpoint is configured, the feature is completely silent — no output, no side effects.
|
|
523
|
+
|
|
524
|
+
### Activation
|
|
525
|
+
|
|
526
|
+
The `murmur8 init` command creates a `.env` file at your project root with a commented-out telemetry template:
|
|
527
|
+
|
|
528
|
+
```bash
|
|
529
|
+
# Remove # to enable
|
|
530
|
+
MURMUR8_TELEMETRY_URL=https://your-ingest-endpoint.com/events
|
|
531
|
+
MURMUR8_TELEMETRY_KEY=your-api-key # optional — sent as Authorization: Bearer
|
|
532
|
+
```
|
|
533
|
+
|
|
534
|
+
Real environment variables take precedence over `.env`, making it straightforward to configure in CI/CD without storing credentials in files. `.env` is automatically added to `.gitignore` during init.
|
|
535
|
+
|
|
536
|
+
### What gets sent
|
|
537
|
+
|
|
538
|
+
| Field | Description |
|
|
539
|
+
|-------|-------------|
|
|
540
|
+
| `runId` | UUID v4 unique to this pipeline execution |
|
|
541
|
+
| `featureId` | UUID stable across retries — written into FEATURE_SPEC.md frontmatter once by Alex |
|
|
542
|
+
| `identity` | Git user name/email, repo remote URL, org ID, murmur8 version |
|
|
543
|
+
| `run` | Slug, status, start/end timestamps, per-stage timings and statuses, feedback ratings |
|
|
544
|
+
| `artifacts` | Feature spec and story files, gzip + base64 encoded |
|
|
545
|
+
|
|
546
|
+
`featureId` enables cross-run correlation: all retries of the same feature share one `featureId` while each execution gets a unique `runId`. This lets you query "all runs ever attempted for this feature" and trace evolution over time.
|
|
547
|
+
|
|
548
|
+
### Reliability
|
|
549
|
+
|
|
550
|
+
Sends are non-blocking and never interrupt the pipeline. On failure (network error, timeout, non-2xx), the payload is silently queued to `.claude/telemetry-failed.json` and retried at the start of the next run.
|
|
551
|
+
|
|
552
|
+
### Viewing configuration
|
|
553
|
+
|
|
554
|
+
```bash
|
|
555
|
+
npx murmur8 telemetry-config
|
|
556
|
+
|
|
557
|
+
# Example output:
|
|
558
|
+
# status: active
|
|
559
|
+
# url: https://your-endpoint.com/events
|
|
560
|
+
# api key: ****1234
|
|
561
|
+
# failed queue: 0 entries
|
|
562
|
+
```
|
|
563
|
+
|
|
564
|
+
### Enterprise use
|
|
565
|
+
|
|
566
|
+
murmur8 telemetry is designed for self-hosted enterprise endpoints — there is no central collection service. Each organisation configures its own ingest URL. This enables:
|
|
567
|
+
|
|
568
|
+
- **Usage monitoring** — who ran what pipeline, on which repo, when
|
|
569
|
+
- **Audit trail** — `runId` + `commitHash` + `gitUser` provides an immutable trace of AI-assisted code changes
|
|
570
|
+
- **Corpus building** — aggregate feature specs and stories across teams to analyse and improve pipeline quality
|
|
571
|
+
|
|
518
572
|
## Murmuration
|
|
519
573
|
|
|
520
574
|
Run multiple feature pipelines simultaneously using git worktrees for isolation. Each feature gets its own worktree and branch, allowing true parallel development without conflicts.
|
package/SKILL.md
CHANGED
|
@@ -138,7 +138,7 @@ If no history exists, skip this step silently.
|
|
|
138
138
|
### Step 5: Initialize
|
|
139
139
|
Create/read `{QUEUE}`. Ensure dirs exist: `mkdir -p {FEAT_DIR} {TEST_DIR}`
|
|
140
140
|
|
|
141
|
-
Unless `--no-history`,
|
|
141
|
+
Unless `--no-history`, note the pipeline start time (ISO 8601 UTC) in your working context as `PIPELINE_START`.
|
|
142
142
|
|
|
143
143
|
---
|
|
144
144
|
|
|
@@ -146,7 +146,7 @@ Unless `--no-history`, start a history entry (slug, startedAt, stages, feedback)
|
|
|
146
146
|
|
|
147
147
|
**Announce:** `} Alex — creating feature spec`
|
|
148
148
|
|
|
149
|
-
**History:**
|
|
149
|
+
**History:** Note `ALEX_START` (ISO 8601 UTC) before spawning.
|
|
150
150
|
|
|
151
151
|
**Runtime prompt:** `.blueprint/prompts/alex-runtime.md`
|
|
152
152
|
|
|
@@ -204,7 +204,7 @@ Brief summary (5 bullets max): intent, key behaviours, scope, story themes, tens
|
|
|
204
204
|
|
|
205
205
|
**On completion:**
|
|
206
206
|
1. Verify `{FEAT_SPEC}` and `{FEAT_DIR}/handoff-alex.md` exist
|
|
207
|
-
2.
|
|
207
|
+
2. Note `ALEX_END` and compute `ALEX_DURATION_MS`
|
|
208
208
|
3. Update queue: move feature to `cassQueue`
|
|
209
209
|
4. If `--pause-after=alex`: Show output path, ask user to continue
|
|
210
210
|
|
|
@@ -247,7 +247,7 @@ FEEDBACK: {"rating":N,"issues":["..."],"rec":"proceed|pause|revise"}
|
|
|
247
247
|
|
|
248
248
|
**Announce:** ` } Cass — writing user stories`
|
|
249
249
|
|
|
250
|
-
**History:**
|
|
250
|
+
**History:** Note `CASS_START` (ISO 8601 UTC) before spawning.
|
|
251
251
|
|
|
252
252
|
**Runtime prompt:** `.blueprint/prompts/cass-runtime.md`
|
|
253
253
|
|
|
@@ -311,7 +311,7 @@ Brief summary: story count, filenames, behaviours covered (5 bullets max)
|
|
|
311
311
|
**On completion:**
|
|
312
312
|
1. Verify at least one `story-*.md` exists in `{FEAT_DIR}`
|
|
313
313
|
2. Verify `{FEAT_DIR}/handoff-cass.md` exists
|
|
314
|
-
2.
|
|
314
|
+
2. Note `CASS_END` and compute `CASS_DURATION_MS`
|
|
315
315
|
3. Update queue: move feature to `nigelQueue`
|
|
316
316
|
4. If `--pause-after=cass`: Show story paths, ask user to continue
|
|
317
317
|
|
|
@@ -349,7 +349,7 @@ FEEDBACK: {"rating":N,"issues":["..."],"rec":"proceed|pause|revise"}
|
|
|
349
349
|
|
|
350
350
|
**Announce:** ` } Nigel — building test spec`
|
|
351
351
|
|
|
352
|
-
**History:**
|
|
352
|
+
**History:** Note `NIGEL_SPEC_START` (ISO 8601 UTC) before spawning.
|
|
353
353
|
|
|
354
354
|
**Runtime prompt:** `.blueprint/prompts/nigel-runtime.md`
|
|
355
355
|
|
|
@@ -412,7 +412,7 @@ Brief summary: test case count planned, AC coverage %, assumptions (5 bullets ma
|
|
|
412
412
|
|
|
413
413
|
**On completion:**
|
|
414
414
|
1. Verify `{TEST_SPEC}` and `{FEAT_DIR}/handoff-nigel.md` exist
|
|
415
|
-
2.
|
|
415
|
+
2. Note `NIGEL_SPEC_END` and compute `NIGEL_SPEC_DURATION_MS`
|
|
416
416
|
|
|
417
417
|
**On failure:** See [Error Handling with Retry](#error-handling-with-smart-retry)
|
|
418
418
|
|
|
@@ -422,7 +422,7 @@ Brief summary: test case count planned, AC coverage %, assumptions (5 bullets ma
|
|
|
422
422
|
|
|
423
423
|
**Announce:** ` } Nigel — writing executable tests`
|
|
424
424
|
|
|
425
|
-
**History:**
|
|
425
|
+
**History:** Note `NIGEL_TESTS_START` (ISO 8601 UTC) before spawning.
|
|
426
426
|
|
|
427
427
|
Use the Task tool with `subagent_type="general-purpose"`:
|
|
428
428
|
|
|
@@ -460,7 +460,7 @@ Brief summary: test count, file(s) written, any tests deferred
|
|
|
460
460
|
|
|
461
461
|
**On completion:**
|
|
462
462
|
1. Verify `{TEST_FILE}` exists
|
|
463
|
-
2.
|
|
463
|
+
2. Note `NIGEL_TESTS_END` and compute `NIGEL_TESTS_DURATION_MS`
|
|
464
464
|
3. Update queue: move feature to `codeyQueue`
|
|
465
465
|
4. If `--pause-after=nigel`: Show test paths, ask user to continue
|
|
466
466
|
|
|
@@ -499,7 +499,7 @@ FEEDBACK: {"rating":N,"issues":["..."],"rec":"proceed|pause|revise"}
|
|
|
499
499
|
|
|
500
500
|
**Announce:** ` } Codey — drafting implementation plan`
|
|
501
501
|
|
|
502
|
-
**History:**
|
|
502
|
+
**History:** Note `CODEY_PLAN_START` (ISO 8601 UTC) before spawning.
|
|
503
503
|
|
|
504
504
|
**Runtime prompt:** `.blueprint/prompts/codey-plan-runtime.md`
|
|
505
505
|
|
|
@@ -556,7 +556,7 @@ Brief summary: files planned, step count, identified risks
|
|
|
556
556
|
|
|
557
557
|
**On completion:**
|
|
558
558
|
1. Verify `{PLAN}` exists
|
|
559
|
-
2.
|
|
559
|
+
2. Note `CODEY_PLAN_END` and compute `CODEY_PLAN_DURATION_MS`
|
|
560
560
|
3. If `--pause-after=codey-plan`: Show plan path, ask user to continue
|
|
561
561
|
|
|
562
562
|
**On failure:** See [Error Handling with Retry](#error-handling-with-smart-retry)
|
|
@@ -567,7 +567,7 @@ Brief summary: files planned, step count, identified risks
|
|
|
567
567
|
|
|
568
568
|
**Announce:** ` } Codey — implementing feature`
|
|
569
569
|
|
|
570
|
-
**History:**
|
|
570
|
+
**History:** Note `CODEY_IMPL_START` (ISO 8601 UTC) before spawning.
|
|
571
571
|
|
|
572
572
|
**Runtime prompt:** `.blueprint/prompts/codey-implement-runtime.md`
|
|
573
573
|
|
|
@@ -637,13 +637,13 @@ for each step in IMPLEMENTATION_PLAN.steps:
|
|
|
637
637
|
|
|
638
638
|
**On all steps complete:**
|
|
639
639
|
1. Run full test suite: `node --test {TEST_FILE}`
|
|
640
|
-
2.
|
|
640
|
+
2. Note `CODEY_IMPL_END`, compute `CODEY_IMPL_DURATION_MS`, and note `STEPS_COMPLETED`
|
|
641
641
|
3. Update queue: move feature to `completed`
|
|
642
642
|
4. Proceed to auto-commit (unless `--no-commit`)
|
|
643
643
|
|
|
644
644
|
**On partial failure:**
|
|
645
645
|
1. Record which steps completed and which failed
|
|
646
|
-
2.
|
|
646
|
+
2. Note partial completion: `STEPS_COMPLETED=M`, `TOTAL_STEPS=N`, `FAILED_AT_STEP=step`
|
|
647
647
|
3. Report to user with option to continue manually
|
|
648
648
|
|
|
649
649
|
**On failure:** See [Error Handling with Retry](#error-handling-with-smart-retry)
|
|
@@ -694,17 +694,28 @@ After commit, remove the slug's row from `{BACKLOG}` (if it exists). Stage with
|
|
|
694
694
|
|
|
695
695
|
**Modules:** `src/history.js`, `src/cost.js`
|
|
696
696
|
|
|
697
|
-
Unless `--no-history` flag is set,
|
|
697
|
+
Unless `--no-history` flag is set, build the history entry JSON from the timestamps noted during the run and write it via the CLI:
|
|
698
698
|
|
|
699
|
-
```
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
699
|
+
```bash
|
|
700
|
+
node bin/cli.js history record '{
|
|
701
|
+
"slug": "{slug}",
|
|
702
|
+
"status": "success",
|
|
703
|
+
"startedAt": "<PIPELINE_START>",
|
|
704
|
+
"completedAt": "<now ISO 8601>",
|
|
705
|
+
"totalDurationMs": <elapsed ms>,
|
|
706
|
+
"commitHash": "<hash or null>",
|
|
707
|
+
"stages": {
|
|
708
|
+
"alex": { "startedAt": "<ALEX_START>", "completedAt": "<ALEX_END>", "durationMs": <ALEX_DURATION_MS>, "status": "success" },
|
|
709
|
+
"cass": { "startedAt": "<CASS_START>", "completedAt": "<CASS_END>", "durationMs": <CASS_DURATION_MS>, "status": "success" },
|
|
710
|
+
"nigel-spec": { "startedAt": "<NIGEL_SPEC_START>", "completedAt": "<NIGEL_SPEC_END>", "durationMs": <NIGEL_SPEC_DURATION_MS>, "status": "success" },
|
|
711
|
+
"nigel-tests": { "startedAt": "<NIGEL_TESTS_START>", "completedAt": "<NIGEL_TESTS_END>", "durationMs": <NIGEL_TESTS_DURATION_MS>, "status": "success" },
|
|
712
|
+
"codey-plan": { "startedAt": "<CODEY_PLAN_START>", "completedAt": "<CODEY_PLAN_END>", "durationMs": <CODEY_PLAN_DURATION_MS>, "status": "success" },
|
|
713
|
+
"codey-implement": { "startedAt": "<CODEY_IMPL_START>", "completedAt": "<CODEY_IMPL_END>", "durationMs": <CODEY_IMPL_DURATION_MS>, "status": "success", "stepsCompleted": <N> }
|
|
714
|
+
}
|
|
715
|
+
}'
|
|
716
|
+
```
|
|
717
|
+
|
|
718
|
+
Omit stages that were skipped (e.g. cass when `--skip-stories` was used). Set `status` to `"failed"` and add `"failedStage": "<stage>"` on failure, or `"paused"` and `"pausedAfter": "<stage>"` on pause.
|
|
708
719
|
|
|
709
720
|
**Display summary:** Stage status (✓/✗), test count, duration, commit hash, feedback ratings, cost breakdown per stage.
|
|
710
721
|
|
package/package.json
CHANGED
package/src/commands/history.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* history command - View pipeline execution history
|
|
3
3
|
*/
|
|
4
|
-
const { displayHistory, showStats, clearHistory, exportHistory } = require('../history');
|
|
4
|
+
const { displayHistory, showStats, clearHistory, exportHistory, recordHistory, updateStage } = require('../history');
|
|
5
5
|
const { parseFlags } = require('./utils');
|
|
6
6
|
|
|
7
7
|
const description = 'View pipeline execution history';
|
|
@@ -10,7 +10,46 @@ async function run(args) {
|
|
|
10
10
|
const flags = parseFlags(args);
|
|
11
11
|
const subArg = args[1];
|
|
12
12
|
|
|
13
|
-
if (subArg === '
|
|
13
|
+
if (subArg === 'record') {
|
|
14
|
+
const jsonArg = args[2];
|
|
15
|
+
if (!jsonArg) {
|
|
16
|
+
console.error('Usage: history record \'{"slug":"...","status":"...","startedAt":"...","completedAt":"...","totalDurationMs":N}\'');
|
|
17
|
+
process.exit(1);
|
|
18
|
+
}
|
|
19
|
+
let entry;
|
|
20
|
+
try {
|
|
21
|
+
entry = JSON.parse(jsonArg);
|
|
22
|
+
} catch (err) {
|
|
23
|
+
console.error(`Invalid JSON: ${err.message}`);
|
|
24
|
+
process.exit(1);
|
|
25
|
+
}
|
|
26
|
+
if (!entry.slug || !entry.status || !entry.startedAt || !entry.completedAt || entry.totalDurationMs === undefined) {
|
|
27
|
+
console.error('Entry must include: slug, status, startedAt, completedAt, totalDurationMs');
|
|
28
|
+
process.exit(1);
|
|
29
|
+
}
|
|
30
|
+
const ok = recordHistory(entry);
|
|
31
|
+
if (!ok) process.exit(1);
|
|
32
|
+
console.log(`Recorded history entry for "${entry.slug}" (${entry.status})`);
|
|
33
|
+
} else if (subArg === 'update-stage') {
|
|
34
|
+
// history update-stage <slug> <stage> '<json>'
|
|
35
|
+
const slug = args[2];
|
|
36
|
+
const stage = args[3];
|
|
37
|
+
const jsonArg = args[4];
|
|
38
|
+
if (!slug || !stage || !jsonArg) {
|
|
39
|
+
console.error('Usage: history update-stage <slug> <stage> \'{"durationMs":N,"status":"success"}\'');
|
|
40
|
+
process.exit(1);
|
|
41
|
+
}
|
|
42
|
+
let stageData;
|
|
43
|
+
try {
|
|
44
|
+
stageData = JSON.parse(jsonArg);
|
|
45
|
+
} catch (err) {
|
|
46
|
+
console.error(`Invalid JSON: ${err.message}`);
|
|
47
|
+
process.exit(1);
|
|
48
|
+
}
|
|
49
|
+
const ok = updateStage(slug, stage, stageData);
|
|
50
|
+
if (!ok) process.exit(1);
|
|
51
|
+
console.log(`Updated stage "${stage}" for "${slug}"`);
|
|
52
|
+
} else if (subArg === 'clear') {
|
|
14
53
|
await clearHistory({ force: flags.force });
|
|
15
54
|
} else if (subArg === 'export') {
|
|
16
55
|
const exportOpts = {};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* telemetry-config command - View telemetry configuration and queue status
|
|
3
|
+
*/
|
|
4
|
+
const { loadConfig, formatTelemetryConfig } = require('../telemetry');
|
|
5
|
+
|
|
6
|
+
const description = 'View telemetry configuration and failed-send queue status';
|
|
7
|
+
|
|
8
|
+
const QUEUE_PATH = '.claude/telemetry-failed.json';
|
|
9
|
+
|
|
10
|
+
async function run(_args) {
|
|
11
|
+
const config = loadConfig('.env');
|
|
12
|
+
const output = formatTelemetryConfig(config, QUEUE_PATH);
|
|
13
|
+
console.log(output);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
module.exports = { run, description };
|
package/src/history.js
CHANGED
|
@@ -90,6 +90,36 @@ function storeStageFeedback(slug, stage, feedback) {
|
|
|
90
90
|
}
|
|
91
91
|
}
|
|
92
92
|
|
|
93
|
+
/**
|
|
94
|
+
* Updates (merges) stage data into the most recent history entry for a slug.
|
|
95
|
+
* Used by the CLI skill to record per-stage timing after each pipeline step.
|
|
96
|
+
* @param {string} slug - Feature slug
|
|
97
|
+
* @param {string} stage - Stage name (alex, cass, nigel, codey-plan, codey-implement)
|
|
98
|
+
* @param {object} data - Stage fields to merge (startedAt, completedAt, durationMs, status, etc.)
|
|
99
|
+
* @returns {boolean} True if updated successfully
|
|
100
|
+
*/
|
|
101
|
+
function updateStage(slug, stage, data) {
|
|
102
|
+
try {
|
|
103
|
+
const history = readHistoryFile();
|
|
104
|
+
if (history.error) {
|
|
105
|
+
console.warn('Warning: History file is corrupted, cannot update stage.');
|
|
106
|
+
return false;
|
|
107
|
+
}
|
|
108
|
+
const entry = history.findLast(e => e.slug === slug);
|
|
109
|
+
if (!entry) {
|
|
110
|
+
console.warn(`Warning: No history entry found for slug: ${slug}`);
|
|
111
|
+
return false;
|
|
112
|
+
}
|
|
113
|
+
if (!entry.stages) entry.stages = {};
|
|
114
|
+
entry.stages[stage] = { ...entry.stages[stage], ...data };
|
|
115
|
+
writeHistoryFile(history);
|
|
116
|
+
return true;
|
|
117
|
+
} catch (err) {
|
|
118
|
+
console.warn(`Warning: Failed to update stage: ${err.message}`);
|
|
119
|
+
return false;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
93
123
|
function formatDuration(ms) {
|
|
94
124
|
const seconds = Math.floor(ms / 1000);
|
|
95
125
|
const minutes = Math.floor(seconds / 60);
|
|
@@ -427,6 +457,7 @@ module.exports = {
|
|
|
427
457
|
writeHistoryFile,
|
|
428
458
|
recordHistory,
|
|
429
459
|
storeStageFeedback,
|
|
460
|
+
updateStage,
|
|
430
461
|
displayHistory,
|
|
431
462
|
showStats,
|
|
432
463
|
clearHistory,
|
package/src/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
const { init } = require('./init');
|
|
2
2
|
const { update } = require('./update');
|
|
3
3
|
const { validate, formatOutput, checkNodeVersion } = require('./validate');
|
|
4
|
-
const { recordHistory, displayHistory, showStats, clearHistory, storeStageFeedback } = require('./history');
|
|
4
|
+
const { recordHistory, displayHistory, showStats, clearHistory, storeStageFeedback, updateStage } = require('./history');
|
|
5
5
|
const {
|
|
6
6
|
readConfig,
|
|
7
7
|
writeConfig,
|
|
@@ -94,6 +94,9 @@ const {
|
|
|
94
94
|
markWorktreeAborted,
|
|
95
95
|
getPromptText
|
|
96
96
|
} = require('./diff-preview');
|
|
97
|
+
const { loadConfig, generateRunId, ensureFeatureId, buildPayload, compressArtifact,
|
|
98
|
+
enqueueFailure, retryQueue, ensureDotenv, ensureGitignore, formatTelemetryConfig
|
|
99
|
+
} = require('./telemetry');
|
|
97
100
|
const tools = require('./tools');
|
|
98
101
|
const theme = require('./theme');
|
|
99
102
|
|
|
@@ -108,6 +111,7 @@ module.exports = {
|
|
|
108
111
|
showStats,
|
|
109
112
|
clearHistory,
|
|
110
113
|
storeStageFeedback,
|
|
114
|
+
updateStage,
|
|
111
115
|
// Retry module exports
|
|
112
116
|
readConfig,
|
|
113
117
|
writeConfig,
|
|
@@ -158,6 +162,17 @@ module.exports = {
|
|
|
158
162
|
setStackConfigValue,
|
|
159
163
|
detectStackConfig,
|
|
160
164
|
displayStackConfig,
|
|
165
|
+
// Telemetry module exports
|
|
166
|
+
loadConfig,
|
|
167
|
+
generateRunId,
|
|
168
|
+
ensureFeatureId,
|
|
169
|
+
buildPayload,
|
|
170
|
+
compressArtifact,
|
|
171
|
+
enqueueFailure,
|
|
172
|
+
retryQueue,
|
|
173
|
+
ensureDotenv,
|
|
174
|
+
ensureGitignore,
|
|
175
|
+
formatTelemetryConfig,
|
|
161
176
|
// Tools module (model native features)
|
|
162
177
|
tools,
|
|
163
178
|
// Theme module (murmuration visual theming)
|
package/src/init.js
CHANGED
|
@@ -3,6 +3,7 @@ const path = require('path');
|
|
|
3
3
|
|
|
4
4
|
const { detectStackConfig, writeStackConfig, CONFIG_FILE: STACK_CONFIG_FILE } = require('./stack');
|
|
5
5
|
const { prompt } = require('./utils');
|
|
6
|
+
const { ensureDotenv, ensureGitignore } = require('./telemetry');
|
|
6
7
|
|
|
7
8
|
const PACKAGE_ROOT = path.resolve(__dirname, '..');
|
|
8
9
|
const TARGET_DIR = process.cwd();
|
|
@@ -176,6 +177,10 @@ async function init() {
|
|
|
176
177
|
console.log('Stack config already exists, skipping detection');
|
|
177
178
|
}
|
|
178
179
|
|
|
180
|
+
// Ensure .env template and .gitignore entry for telemetry
|
|
181
|
+
ensureDotenv(TARGET_DIR);
|
|
182
|
+
ensureGitignore(TARGET_DIR);
|
|
183
|
+
|
|
179
184
|
console.log(`
|
|
180
185
|
murmur8 initialized successfully!
|
|
181
186
|
|