@launchsecure/launch-kit 0.0.27 → 0.0.29
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/dist/beacon/beacon.mjs +1003 -440
- package/dist/beacon/beacon.mjs.map +1 -1
- package/dist/beacon/beacon.umd.js +45 -24
- package/dist/beacon/beacon.umd.js.map +1 -1
- package/dist/beacon/types/capture/events.d.ts +20 -0
- package/dist/beacon/types/capture/events.d.ts.map +1 -0
- package/dist/beacon/types/element.d.ts +1 -0
- package/dist/beacon/types/element.d.ts.map +1 -1
- package/dist/beacon/types/index.d.ts +2 -1
- package/dist/beacon/types/index.d.ts.map +1 -1
- package/dist/beacon/types/monitor/dom.d.ts +13 -0
- package/dist/beacon/types/monitor/dom.d.ts.map +1 -0
- package/dist/beacon/types/monitor/index.d.ts +19 -0
- package/dist/beacon/types/monitor/index.d.ts.map +1 -0
- package/dist/beacon/types/monitor/network.d.ts +12 -0
- package/dist/beacon/types/monitor/network.d.ts.map +1 -0
- package/dist/beacon/types/monitor/transport.d.ts +27 -0
- package/dist/beacon/types/monitor/transport.d.ts.map +1 -0
- package/dist/beacon/types/monitor/types.d.ts +117 -0
- package/dist/beacon/types/monitor/types.d.ts.map +1 -0
- package/dist/beacon/types/types.d.ts +10 -0
- package/dist/beacon/types/types.d.ts.map +1 -1
- package/dist/beacon/types/ui/drawer.d.ts +3 -1
- package/dist/beacon/types/ui/drawer.d.ts.map +1 -1
- package/dist/beacon/types/ui/monitor-panel.d.ts +19 -0
- package/dist/beacon/types/ui/monitor-panel.d.ts.map +1 -0
- package/dist/server/beacon-monitor-entry.js +353 -0
- package/dist/server/chart-serve.js +3 -1
- package/dist/server/cli.js +276 -218
- package/dist/server/course-entry.js +246 -0
- package/dist/server/graph-mcp-entry.js +35 -72
- package/dist/server/init-entry.js +1051 -122
- package/dist/server/orbit-entry.js +187 -24
- package/package.json +5 -3
- package/scaffolds/ls-marketplace/.claude-plugin/marketplace.json +15 -0
- package/scaffolds/ls-marketplace/plugins/kit/.claude-plugin/plugin.json +19 -0
- package/scaffolds/ls-marketplace/plugins/kit/commands/activate-beacon.md +216 -0
- package/scaffolds/ls-marketplace/plugins/kit/commands/activate-statusline.md +46 -0
- package/scaffolds/ls-marketplace/plugins/kit/commands/beacon-array.md +92 -0
- package/scaffolds/ls-marketplace/plugins/kit/commands/beacon-clear.md +68 -0
- package/scaffolds/ls-marketplace/plugins/kit/commands/beacon-pulse.md +80 -0
- package/scaffolds/ls-marketplace/plugins/kit/commands/beacon-scan.md +62 -0
- package/scaffolds/ls-marketplace/plugins/kit/commands/deactivate-statusline.md +34 -0
- package/scaffolds/ls-marketplace/plugins/kit/commands/show-mcp-status.md +109 -0
- package/scaffolds/ls-marketplace/plugins/kit/commands/standup.md +191 -0
- package/scaffolds/recall-hook/scripts/ensure-recall.sh +69 -0
- package/scaffolds/statusline/statusline-mcp.sh +192 -0
- package/scaffolds/statusline/statusline-wrapper.sh +50 -0
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Show the health of launch-kit's daemon-style MCPs (today just launch-recall). Default output is a terse one-line summary per daemon; pass `full` for expanded details — PID, pidfile, last snapshot, debounce, shadow repo size, recent activity. Read-only; never mutates state.
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# /kit:show-mcp-status
|
|
6
|
+
|
|
7
|
+
Reports the liveness and recent activity of launch-kit's long-running MCP daemons. The recall watcher is the only daemon today; this command is structured so additional daemons can be added without changing its interface.
|
|
8
|
+
|
|
9
|
+
Read $ARGUMENTS to decide output verbosity:
|
|
10
|
+
- empty (`/kit:show-mcp-status`) → terse one-liner per daemon. Goal: fits in one or two visual lines.
|
|
11
|
+
- `full` (`/kit:show-mcp-status full`) → expanded report per daemon. Goal: enough detail to diagnose health problems.
|
|
12
|
+
|
|
13
|
+
## Preflight
|
|
14
|
+
|
|
15
|
+
1. Verify the launch-recall MCP is wired by checking whether `mcp__launch-recall__recall_status` (or `mcp__local-launch-recall__recall_status` for dev repos) is callable. If neither is available, say so plainly: "launch-recall MCP not wired in this project — nothing to report" and stop.
|
|
16
|
+
2. Pick whichever recall MCP is available. Prefer the project-level published one (`mcp__launch-recall__*`) unless only the local dev one is wired.
|
|
17
|
+
|
|
18
|
+
## Daemons
|
|
19
|
+
|
|
20
|
+
Today: **launch-recall** (the shadow-git file watcher).
|
|
21
|
+
|
|
22
|
+
Structure your output so adding more daemons later is a copy-paste — one section per daemon, same shape.
|
|
23
|
+
|
|
24
|
+
## Default output (no arguments)
|
|
25
|
+
|
|
26
|
+
Call `mcp__launch-recall__recall_status`. Format as ONE LINE per daemon, then a single summary line. Example:
|
|
27
|
+
|
|
28
|
+
```
|
|
29
|
+
recall ✓ alive pid 12456 last snap 2m ago
|
|
30
|
+
|
|
31
|
+
All daemons healthy.
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Or if dead:
|
|
35
|
+
|
|
36
|
+
```
|
|
37
|
+
recall ✗ dead pidfile missing/stale
|
|
38
|
+
|
|
39
|
+
1 daemon down. Run /kit:show-mcp-status full for details, or restart the watcher (kill any stale pid, then either start a new Claude Code session — the SessionStart hook respawns it — or run `node packages/cli/dist/server/recall-entry.js watch` from this repo).
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Rules for the default view:
|
|
43
|
+
- One line per daemon — name, glyph, status word, key metric.
|
|
44
|
+
- `✓` (green) for alive, `✗` (red) for dead.
|
|
45
|
+
- "X ago" for timestamps — relative time, easier to scan than ISO.
|
|
46
|
+
- Summary line on a fresh line at the end. If anything is wrong, include a one-sentence recovery hint.
|
|
47
|
+
- No JSON, no PID files paths, no debug info — that's for `full`.
|
|
48
|
+
|
|
49
|
+
## Full output (`/kit:show-mcp-status full`)
|
|
50
|
+
|
|
51
|
+
For each daemon, call BOTH `recall_status` AND `recall_report`. Combine into a per-daemon block:
|
|
52
|
+
|
|
53
|
+
```
|
|
54
|
+
recall — launch-recall watcher (shadow-git backup)
|
|
55
|
+
|
|
56
|
+
Status: ✓ alive
|
|
57
|
+
PID: 12456
|
|
58
|
+
Watch tree: /Users/prajyot/Documents/Work/AutomateWithUs/launchsecure-v2
|
|
59
|
+
Shadow repo: /Users/prajyot/Documents/Work/AutomateWithUs/launchsecure-v2/.recall/repo.git
|
|
60
|
+
|
|
61
|
+
Last snapshot: 2026-05-21T12:05:45+05:30 (2m ago)
|
|
62
|
+
Total snaps: 1,247
|
|
63
|
+
Shadow size: 1.4 MB
|
|
64
|
+
Debounce: 3000ms
|
|
65
|
+
Retention: keepLast 5000, maxAgeDays 30
|
|
66
|
+
|
|
67
|
+
Recent activity (last 5 snaps):
|
|
68
|
+
a1b2c3d 2m ago snap 2026-05-21T06:35:45Z
|
|
69
|
+
9f8e7d6 14m ago snap 2026-05-21T06:21:42Z
|
|
70
|
+
2557799 53m ago snap 2026-05-21T05:52:35Z
|
|
71
|
+
8349edb 17h ago snap 2026-05-20T13:36:39Z
|
|
72
|
+
e5a05cf 17h ago snap 2026-05-20T13:32:05Z
|
|
73
|
+
|
|
74
|
+
All daemons healthy.
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
If a daemon is dead, the block should still appear but with status `✗ dead` and as many fields as can be derived from the on-disk shadow repo + pidfile. Include a concrete recovery command at the end of the block (the same SessionStart restart path from the default view, plus the manual command for impatient users).
|
|
78
|
+
|
|
79
|
+
Rules for the full view:
|
|
80
|
+
- Two-column field labels — visually aligned, easier to skim.
|
|
81
|
+
- Times: ISO timestamp + relative ("2m ago") in parens.
|
|
82
|
+
- Sizes: human-readable (KB/MB), not bytes.
|
|
83
|
+
- Last 5 snapshots only — newer first.
|
|
84
|
+
- One blank line between daemon blocks (for when there are more than one).
|
|
85
|
+
- Summary line at the end, same as the default view.
|
|
86
|
+
|
|
87
|
+
## Health classification
|
|
88
|
+
|
|
89
|
+
Use these rules to decide alive vs dead vs degraded:
|
|
90
|
+
|
|
91
|
+
- **alive**: `recall_status.running === true` AND `lastSnapshotAt` is within the last 6 hours (heuristic — for an active repo, snaps happen every few minutes; 6h of silence suggests the watcher is wedged or no files have changed, both worth surfacing).
|
|
92
|
+
- **dead**: `recall_status.running === false` OR `recall_status` errors out.
|
|
93
|
+
- **degraded** (still print as alive but note it): `running === true` BUT `lastSnapshotAt` is older than 6h. Add a single inline note `(no activity in 6h — possibly idle or wedged)`.
|
|
94
|
+
|
|
95
|
+
Don't over-warn — if a repo has genuinely been idle (e.g. you opened it for the first time today after a week), a 6h+ gap is normal. The note is informational, not an error.
|
|
96
|
+
|
|
97
|
+
## Constraints
|
|
98
|
+
|
|
99
|
+
- **Read-only.** This command never mutates state, never restarts watchers, never modifies config. Recovery is a recommendation in the output, not an automatic action.
|
|
100
|
+
- **Fast.** Default view should be sub-second. The MCP tools are cheap; don't add scrubbing or heavy formatting.
|
|
101
|
+
- **Plain text.** No markdown headers, no fenced blocks in the actual output — Claude Code renders them but the human eye scans monospace plain text fastest.
|
|
102
|
+
- **Output what the user asked for.** If they ran the default, don't dump the full report "just in case." If they ran `full`, don't abbreviate.
|
|
103
|
+
|
|
104
|
+
## Notes for the assistant
|
|
105
|
+
|
|
106
|
+
- Use the wired `launch-recall` MCP tools — do not shell out to `node packages/cli/dist/server/recall-entry.js status` even though it works. Going through MCP makes this command portable across projects that have launch-kit init'd via npx vs dev-build.
|
|
107
|
+
- When listing recent snaps, use the `recentSnapshots` array from `recall_report`. It's already sorted newest-first; just truncate.
|
|
108
|
+
- For "X ago" formatting, do the math yourself. Don't fetch any time service.
|
|
109
|
+
- If you find yourself wanting to add features beyond status display (restart the watcher, prune snaps, etc.) — stop. Those belong in separate `/kit:*` commands. This one is a status pane.
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Draft a daily standup from work done since the last push, surface in-progress local work and follow-ups, show the draft, and post to LaunchSecure Comm Hub as a daily_update after explicit confirmation.
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Standup
|
|
6
|
+
|
|
7
|
+
Generates a daily-update comment for the LaunchSecure Comm Hub. Pulls commits since the last push (with fallbacks), surfaces what's still in-flight locally (uncommitted work, explicit follow-ups, in-progress work items), drafts a summary in the project's house style, shows it to the user, and posts only after the user confirms. Never posts without explicit "yes".
|
|
8
|
+
|
|
9
|
+
## Preflight
|
|
10
|
+
|
|
11
|
+
1. Verify `.launch-secure.cred.config` exists at the repo root. If missing, abort and tell the user to run `npx @launchsecure/launch-kit init` first — without the cred file the `launch-secure` MCP cannot authenticate, so we cannot read prior standups or post the new one.
|
|
12
|
+
2. Verify we are inside a git repo (`git rev-parse --git-dir`). If not, abort with a clear message.
|
|
13
|
+
3. Record the current branch (`git rev-parse --abbrev-ref HEAD`) and current git email (`git config user.email`).
|
|
14
|
+
|
|
15
|
+
## Gather
|
|
16
|
+
|
|
17
|
+
Pull data from layered sources, in this order. Default to git + commit messages — they already carry the structure. Only reach for `launch-chart` in the explicit case described in step 3.
|
|
18
|
+
|
|
19
|
+
### 1. Determine the window
|
|
20
|
+
|
|
21
|
+
- **Primary**: `git log --reverse --pretty=format:'%h%x09%an%x09%ae%x09%s%n%b%n%x00' @{push}..HEAD`. The `@{push}` revspec resolves to where this branch was last pushed; the diff is commits ahead of upstream.
|
|
22
|
+
- **Fallback 1**: if the primary returns empty OR `@{push}` errors with `unknown revision`, query the `launch-secure` MCP via `communication_read({ tag: "daily_update", limit: 1 })`. Take the most recent comment's `createdAt`; gather commits since then via `git log --since="<that timestamp>"`.
|
|
23
|
+
- **Fallback 2**: if no prior `daily_update` exists either, use `git log --since="24 hours ago"`.
|
|
24
|
+
|
|
25
|
+
Tell the user which window was used in one short line ("Using commits since last push (12 commits)" or "No commits since last push — falling back to commits since 2026-05-20 14:01 (last standup)").
|
|
26
|
+
|
|
27
|
+
### 2. Collect change context
|
|
28
|
+
|
|
29
|
+
- `git diff --stat @{push}..HEAD` (or whichever window was chosen) — file paths + line counts. Drives theme grouping.
|
|
30
|
+
- `git log --pretty=format:'%h %s' <window>` — short subject lines for quick scan.
|
|
31
|
+
- `git log --pretty=format:'%h%n%B%n---' <window>` — full commit bodies. Needed for step 4 (follow-up detection).
|
|
32
|
+
- Branch names + PR references in commit messages (look for `#<digits>`, `LS-<id>`, branch slugs like `fix/foo-bar`). These are work-item handles.
|
|
33
|
+
|
|
34
|
+
### 3. Group into themes
|
|
35
|
+
|
|
36
|
+
**Default: group by commit-message scope.** Conventional-commit subjects like `feat(launch-kit): …`, `refactor(server): …`, `chore(lint): …` already declare the theme — use the scope as the theme name. Multiple commits with the same scope collapse into one `→ <Scope>` section.
|
|
37
|
+
|
|
38
|
+
**Only escalate to `launch-chart` when ALL of these are true:**
|
|
39
|
+
- Window has ≥20 changed files across ≥5 top-level directories, AND
|
|
40
|
+
- Commit scopes are missing, inconsistent, or too coarse to reveal structure (e.g. everything is `chore: …` or untagged).
|
|
41
|
+
|
|
42
|
+
In that case: call `mcp__launch-chart__detect_project_stack` to verify the chart is wired, then for each changed file call `mcp__launch-chart__read_graph({ search: "<basename>" })` and group by the resolved node's `module` field. Common LS modules: `auth`, `pda`, `pda-guides`, `pda-shell`, `radar`, `chart`, `orbit`, `recall`, `comms`, `board`, `webhooks`, `feedback`, `briefs`, `mcp`.
|
|
43
|
+
|
|
44
|
+
Files that don't resolve in the chart (new files, non-TS, config) bucket under "Misc" or by directory.
|
|
45
|
+
|
|
46
|
+
### 4. Collect in-progress signals (the "what's still in flight" data)
|
|
47
|
+
|
|
48
|
+
This is the data that makes the standup honest about what's still open, not just what shipped. Always run all three:
|
|
49
|
+
|
|
50
|
+
**4a. Uncommitted local work** — `git status --short`. Group by area (e.g. `scripts/`, `.claude/`, untracked dirs). Filter out build artifacts that don't represent intentional work (`.launchsecure/graphs/*.json`, `node_modules`, `dist`, `.next`). What remains is in-flight code or scaffolds — surface it under "Pending / in-progress".
|
|
51
|
+
|
|
52
|
+
**4b. Follow-up flags in commit bodies** — grep the full commit bodies pulled in step 2 for these patterns (case-insensitive): `follow-up`, `followup`, `TODO`, `flagged`, `deferred`, `not patched`, `rolled back`, `revert`, `queued`, `to be solved`, `next pass`. Each hit is a self-declared open item by the author — surface the sentence verbatim or paraphrased under "Pending / in-progress".
|
|
53
|
+
|
|
54
|
+
**4c. In-progress work items assigned to current user** — call `mcp__launch-secure__work_items_list({ assignee_email: "<git email from preflight>", status: "IN_PROGRESS" })`. For each returned item, capture title + id. Don't list more than 5; if more exist, list 5 + "(N more)".
|
|
55
|
+
|
|
56
|
+
If any of 4a/4b/4c return nothing, that section just doesn't appear in the draft. Don't fabricate filler.
|
|
57
|
+
|
|
58
|
+
### 5. Work-item closure linkage
|
|
59
|
+
|
|
60
|
+
For each work-item handle found in commit messages from step 2 (regex `#\d+|LS-\w+`), call `mcp__launch-secure__work_item_get({ id: "<id>" })` (or `work_items_list` with id filter). If any status changed to DONE/COMPLETED, call them out in the closing `----` block as a one-liner.
|
|
61
|
+
|
|
62
|
+
Skip this step if no handles were found in commit messages — don't pull the full work-items list to fish for closures.
|
|
63
|
+
|
|
64
|
+
### 6. Release detection
|
|
65
|
+
|
|
66
|
+
A commit qualifies the post for the `release` tag if ANY of the following are true:
|
|
67
|
+
- `package.json` `version` field changed in `git diff @{push}..HEAD -- package.json`
|
|
68
|
+
- A migration file under `prisma/migrations/` was added or `prisma/schema.prisma` changed
|
|
69
|
+
- A deploy/publish was mentioned in commit subjects (regex: `\b(publish|release|deploy|bump)\b`)
|
|
70
|
+
- A new bin or export was added to a package's `package.json`
|
|
71
|
+
|
|
72
|
+
If any are true, set `addReleaseTag = true`. Otherwise `false`.
|
|
73
|
+
|
|
74
|
+
## Draft
|
|
75
|
+
|
|
76
|
+
Produce the standup in the **exact** house format. This is the format Prajyot uses for human-written daily updates (verified against the May 18, 19, 20, 21 posts in the Comm Hub):
|
|
77
|
+
|
|
78
|
+
```
|
|
79
|
+
Hey @everyone
|
|
80
|
+
|
|
81
|
+
Pushed <N> commits to <branch> today. Highlights:
|
|
82
|
+
|
|
83
|
+
→ <Theme 1>
|
|
84
|
+
- <outcome bullet, not a commit message>
|
|
85
|
+
- <outcome bullet>
|
|
86
|
+
|
|
87
|
+
→ <Theme 2>
|
|
88
|
+
- <outcome bullet>
|
|
89
|
+
|
|
90
|
+
→ <Theme N>
|
|
91
|
+
- <outcome bullet>
|
|
92
|
+
|
|
93
|
+
----
|
|
94
|
+
|
|
95
|
+
Pending / in-progress:
|
|
96
|
+
|
|
97
|
+
→ <Pending area 1>
|
|
98
|
+
- <one-line item — what's open, why, where>
|
|
99
|
+
|
|
100
|
+
→ <Pending area 2>
|
|
101
|
+
- <one-line item>
|
|
102
|
+
|
|
103
|
+
----
|
|
104
|
+
|
|
105
|
+
<one-line PSA or deploy-safety note. Examples: "TS clean, no schema/migration changes, safe to deploy." or "Includes prisma migration <name> — run migrate-with-backup.sh before deploy.">
|
|
106
|
+
|
|
107
|
+
Thanks
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
**Strict format rules:**
|
|
111
|
+
|
|
112
|
+
- `Hey @everyone` — exactly this. Not "Hey team", not "Hi everyone". Single line, blank line after.
|
|
113
|
+
- `Pushed N commits to <branch> today. Highlights:` — replace `<branch>` with the actual branch from preflight. If the window was a fallback (not "since last push"), say "Drafted N commits' worth of work on <branch> today. Highlights:" instead — be honest about the window.
|
|
114
|
+
- `→ <Theme>` — right-arrow + space + theme name. Theme names come from commit-message scope (default) or chart module grouping (escalated case). Capitalize the first letter.
|
|
115
|
+
- `- <bullet>` — plain hyphen + space. Bullets are **outcomes**, not commit messages. Translate "feat(radar): add transcript view" into "Structured transcript view with overlay reply drawer". Drop scope prefixes and verb tense.
|
|
116
|
+
- Blank line between themes.
|
|
117
|
+
- `----` — four hyphens, on its own line, blank line above and below. Used twice: once to open the Pending block, once to close it.
|
|
118
|
+
- **Pending / in-progress block** — omit entirely if steps 4a/4b/4c all returned nothing. If present, populate from those three sources. Each `→` sub-heading names an area (e.g. `→ Feedback widget`, `→ Codex config writer`, `→ Beacon monitor UX`). Bullets are short, factual, name the file or symbol when relevant.
|
|
119
|
+
- Closing block: one short line is fine. Don't pad. Mention work-item closures + any deploy-affecting changes here.
|
|
120
|
+
- `Thanks` — exact word, no comma, no name signature (the post's author is attached automatically).
|
|
121
|
+
|
|
122
|
+
**Constraints:**
|
|
123
|
+
|
|
124
|
+
- Plain text only. No `**bold**`, no `*italic*`, no backticks, no `# headers`, no markdown link syntax. The Comm Hub renders as plain text.
|
|
125
|
+
- Aim for ≤ 400 words total (was 350; bumped to accommodate the Pending block). Standups are skim-able; trim aggressively.
|
|
126
|
+
- Top 3–6 themes is the sweet spot. If you have 20 commits in 12 different modules, collapse the small ones into a "→ Misc" theme.
|
|
127
|
+
- Bullets should be **outcome-shaped**: what landed in the product, not what the diff did. "Multi-select EventTypesPicker on subscription forms" beats "modified components/webhooks/EventTypesPicker.tsx".
|
|
128
|
+
- Pending items are honest snapshots, not commitments. "Drawer fix reverted — to be solved fresh" is fine; "will be fixed by EOD" is not.
|
|
129
|
+
|
|
130
|
+
## Confirm
|
|
131
|
+
|
|
132
|
+
Show the draft to the user verbatim in a code-fenced block (so they see plain-text formatting as-is), then ask **exactly**:
|
|
133
|
+
|
|
134
|
+
> "Post this as a daily_update to LS Comm Hub? Reply `yes` to post, `edit` to revise, or `cancel` to abort."
|
|
135
|
+
|
|
136
|
+
Responses:
|
|
137
|
+
|
|
138
|
+
- **`yes`, `y`, `ok`, `post it`** — proceed to duplicate check + post.
|
|
139
|
+
- **`edit`, `change <thing>`, free-form revisions** — apply the edits, regenerate the draft, re-show, re-ask.
|
|
140
|
+
- **`cancel`, `no`, `nope`** — abort. Don't post. Don't keep partial state.
|
|
141
|
+
|
|
142
|
+
Anything ambiguous → treat as "edit, what would you like changed?".
|
|
143
|
+
|
|
144
|
+
## Duplicate check
|
|
145
|
+
|
|
146
|
+
Before posting, call `mcp__launch-secure__communication_read({ tag: "daily_update", limit: 5 })`. If any returned comment has:
|
|
147
|
+
- `resourceType: "comment"` (not `"daily_update"` — that's bot-only, distinct stream)
|
|
148
|
+
- `author.email` matches the current user's email (from preflight)
|
|
149
|
+
- `createdAt` within the last 12 hours
|
|
150
|
+
|
|
151
|
+
…then a manual daily_update already exists from today. Offer:
|
|
152
|
+
|
|
153
|
+
> "A daily_update already exists from you today (posted at <time>, <preview-50-chars>). Choose: `replace` to update it (communication_update), `append` to post another, `cancel` to stop."
|
|
154
|
+
|
|
155
|
+
- **replace** → call `mcp__launch-secure__communication_update({ id: "<existing-id>", content: <new-draft> })`. Preserve tags.
|
|
156
|
+
- **append** → proceed to post.
|
|
157
|
+
- **cancel** → abort.
|
|
158
|
+
|
|
159
|
+
## Post
|
|
160
|
+
|
|
161
|
+
Call `mcp__launch-secure__communication_write`:
|
|
162
|
+
|
|
163
|
+
```
|
|
164
|
+
{
|
|
165
|
+
resourceType: "comment",
|
|
166
|
+
content: "<the final draft, plain text>",
|
|
167
|
+
tags: addReleaseTag ? ["daily_update", "release"] : ["daily_update"]
|
|
168
|
+
}
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
`org_slug` and `project_slug` are auto-supplied from the cred config via the MCP's headersHelper — do NOT pass them yourself.
|
|
172
|
+
|
|
173
|
+
On success: report the comment ID and a friendly "posted to LS Comm Hub" line. If the response includes a URL field, surface it.
|
|
174
|
+
|
|
175
|
+
On failure: surface the error verbatim. Don't retry automatically — auth or schema errors deserve human attention. The draft is preserved in chat so the user can retry manually.
|
|
176
|
+
|
|
177
|
+
## Idempotency
|
|
178
|
+
|
|
179
|
+
Re-running `/kit:standup`:
|
|
180
|
+
- Always re-pulls the window fresh. No cached state.
|
|
181
|
+
- Duplicate check handles same-day reposts.
|
|
182
|
+
- The draft is never written to disk — only shown in chat. User can copy it manually if they cancel.
|
|
183
|
+
|
|
184
|
+
## Notes for the assistant
|
|
185
|
+
|
|
186
|
+
- **Default to git, not the chart.** Conventional-commit scopes already declare themes; only reach for `launch-chart` when scopes are missing or inconsistent across ≥20 files / ≥5 directories. This was the most common failure of the previous prompt — chart calls produced no different grouping than commit-scope grouping would have.
|
|
187
|
+
- **The Pending / in-progress block is the standup's main job, not the shipped-themes block.** Shipped work is visible in `git log`; what's still open isn't. Always run the three step-4 gathers.
|
|
188
|
+
- **Don't ask clarifying questions before drafting.** Produce a first cut from whatever data step 1–6 returned, then iterate. The draft is cheap; the conversation isn't.
|
|
189
|
+
- **Outcome bullet, not commit message** is the single most important transform. A standup full of "feat(x): add y" reads like a changelog, not a status.
|
|
190
|
+
- **Never assume `release` tag without evidence** (see step 6). False positives confuse downstream consumers.
|
|
191
|
+
- **If a step's tool fails**, note it inline and continue with the remaining sources — don't abort the whole standup over one failed call.
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Ensures the launch-recall watcher is running. Designed for the Claude Code
|
|
3
|
+
# SessionStart hook (.claude/settings.json). Idempotent: silently no-ops if
|
|
4
|
+
# recall isn't configured, logs OK if watcher is already alive, spawns a
|
|
5
|
+
# detached watcher if it isn't.
|
|
6
|
+
#
|
|
7
|
+
# Portable across two layouts:
|
|
8
|
+
# - Dev: project has packages/cli/dist/server/recall-entry.js (e.g.
|
|
9
|
+
# launchsecure-v2 itself) → spawn via `node`.
|
|
10
|
+
# - Customer: project doesn't ship launch-kit → spawn via
|
|
11
|
+
# `npx -y -p @launchsecure/launch-kit launch-recall watch`. First run
|
|
12
|
+
# downloads from npm (slow); subsequent runs use the npx cache.
|
|
13
|
+
#
|
|
14
|
+
# Exits 0 in all cases — recall failing should never block Claude Code startup.
|
|
15
|
+
|
|
16
|
+
set -u
|
|
17
|
+
|
|
18
|
+
REPO_ROOT="${CLAUDE_PROJECT_DIR:-$(pwd)}"
|
|
19
|
+
RECALL_DIR="$REPO_ROOT/.recall"
|
|
20
|
+
PIDFILE="$RECALL_DIR/watch.pid"
|
|
21
|
+
LOCAL_RECALL_BIN="$REPO_ROOT/packages/cli/dist/server/recall-entry.js"
|
|
22
|
+
|
|
23
|
+
# No recall configured here — exit quietly. Hook is safe to ship in any repo;
|
|
24
|
+
# it only acts when there's a shadow-git to protect.
|
|
25
|
+
if [[ ! -d "$RECALL_DIR/repo.git" ]]; then
|
|
26
|
+
exit 0
|
|
27
|
+
fi
|
|
28
|
+
|
|
29
|
+
# Watcher already alive?
|
|
30
|
+
if [[ -f "$PIDFILE" ]]; then
|
|
31
|
+
pid="$(cat "$PIDFILE" 2>/dev/null || true)"
|
|
32
|
+
if [[ -n "${pid:-}" ]] && kill -0 "$pid" 2>/dev/null; then
|
|
33
|
+
echo "[recall] watcher alive (pid $pid)" >&2
|
|
34
|
+
exit 0
|
|
35
|
+
fi
|
|
36
|
+
fi
|
|
37
|
+
|
|
38
|
+
# Pick spawn command. Prefer the local dev build (instant) over npx (slow on
|
|
39
|
+
# first run because of the npm download).
|
|
40
|
+
if [[ -f "$LOCAL_RECALL_BIN" ]]; then
|
|
41
|
+
SPAWN_CMD=(node "$LOCAL_RECALL_BIN" watch)
|
|
42
|
+
via="local dev build"
|
|
43
|
+
else
|
|
44
|
+
SPAWN_CMD=(npx -y -p @launchsecure/launch-kit launch-recall watch)
|
|
45
|
+
via="npx (initial download may take a moment)"
|
|
46
|
+
fi
|
|
47
|
+
|
|
48
|
+
echo "[recall] starting watcher via $via" >&2
|
|
49
|
+
|
|
50
|
+
# Spawn detached. nohup + redirected fds + & keeps the watcher alive past this
|
|
51
|
+
# script's exit. disown removes it from the shell's job table.
|
|
52
|
+
nohup "${SPAWN_CMD[@]}" >/dev/null 2>&1 &
|
|
53
|
+
disown 2>/dev/null || true
|
|
54
|
+
|
|
55
|
+
# Brief check — long enough for the local-dev path, not long enough to block
|
|
56
|
+
# session start when npx is downloading.
|
|
57
|
+
sleep 0.5
|
|
58
|
+
|
|
59
|
+
if [[ -f "$PIDFILE" ]]; then
|
|
60
|
+
newpid="$(cat "$PIDFILE" 2>/dev/null || true)"
|
|
61
|
+
if [[ -n "${newpid:-}" ]] && kill -0 "$newpid" 2>/dev/null; then
|
|
62
|
+
echo "[recall] watcher started (pid $newpid)" >&2
|
|
63
|
+
exit 0
|
|
64
|
+
fi
|
|
65
|
+
fi
|
|
66
|
+
|
|
67
|
+
# Spawn is still in flight (likely npx download). Don't block session start.
|
|
68
|
+
echo "[recall] watcher spawn initiated — check with /ls:show-mcp-status in a moment" >&2
|
|
69
|
+
exit 0
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# launch-kit MCP chip generator.
|
|
3
|
+
# Emits a colored one-line summary of MCP daemon health, one chip per daemon.
|
|
4
|
+
# Composes into any existing statusline via the launch-kit wrapper.
|
|
5
|
+
#
|
|
6
|
+
# Usage:
|
|
7
|
+
# statusline-mcp.sh # all known chips (recall, chart, deck, council)
|
|
8
|
+
# statusline-mcp.sh --show=recall,chart # only the listed chips
|
|
9
|
+
# statusline-mcp.sh --compact # collapse to `mcp <up>/<total>`
|
|
10
|
+
# green when all up, red if any down
|
|
11
|
+
#
|
|
12
|
+
# Project root is inferred from $LK_STATUSLINE_CWD (set by the wrapper) or
|
|
13
|
+
# $PWD by walking up until a `.launchsecure` dir is found. Silent exit when
|
|
14
|
+
# the cwd is not inside a launchsecure project.
|
|
15
|
+
set -u
|
|
16
|
+
|
|
17
|
+
show=""
|
|
18
|
+
compact=0
|
|
19
|
+
for arg in "$@"; do
|
|
20
|
+
case "$arg" in
|
|
21
|
+
--show=*) show="${arg#--show=}" ;;
|
|
22
|
+
--compact) compact=1 ;;
|
|
23
|
+
esac
|
|
24
|
+
done
|
|
25
|
+
[ -z "$show" ] && show="recall,chart,deck,council"
|
|
26
|
+
|
|
27
|
+
GREEN=$'\033[32m'
|
|
28
|
+
ORANGE=$'\033[33m'
|
|
29
|
+
RED=$'\033[31m'
|
|
30
|
+
RESET=$'\033[0m'
|
|
31
|
+
|
|
32
|
+
find_project_root() {
|
|
33
|
+
# `.recall` only exists at the repo root (launch-recall init creates it
|
|
34
|
+
# there). `.launchsecure` can appear in subdirs too (e.g. packages/cli
|
|
35
|
+
# when graphs are generated for a sub-project), so we prefer .recall as
|
|
36
|
+
# the canonical root marker. Fallback: outermost `.launchsecure` ancestor
|
|
37
|
+
# (walk up and keep overwriting → keeps the highest match).
|
|
38
|
+
local d="${1:-$PWD}"
|
|
39
|
+
local recall_root=""
|
|
40
|
+
local outermost_launchsecure=""
|
|
41
|
+
while [ -n "$d" ] && [ "$d" != "/" ]; do
|
|
42
|
+
if [ -z "$recall_root" ] && [ -d "$d/.recall" ]; then
|
|
43
|
+
recall_root="$d"
|
|
44
|
+
fi
|
|
45
|
+
if [ -d "$d/.launchsecure" ]; then
|
|
46
|
+
outermost_launchsecure="$d"
|
|
47
|
+
fi
|
|
48
|
+
d=$(dirname "$d")
|
|
49
|
+
done
|
|
50
|
+
if [ -n "$recall_root" ]; then echo "$recall_root"; return 0; fi
|
|
51
|
+
if [ -n "$outermost_launchsecure" ]; then echo "$outermost_launchsecure"; return 0; fi
|
|
52
|
+
return 1
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
PROJECT_ROOT=$(find_project_root "${LK_STATUSLINE_CWD:-$PWD}") || exit 0
|
|
56
|
+
|
|
57
|
+
iso_to_epoch() {
|
|
58
|
+
# Input is always UTC (ISO timestamps from .freshness.json end in Z).
|
|
59
|
+
# macOS BSD `date` defaults to local TZ, so without TZ=UTC the timestamp
|
|
60
|
+
# gets misread as local time → wildly wrong age (e.g. 6h instead of 55m).
|
|
61
|
+
local ts="$1"
|
|
62
|
+
ts="${ts%%.*}"; ts="${ts%%Z*}"; ts="${ts%%+*}"
|
|
63
|
+
TZ=UTC date -j -f "%Y-%m-%dT%H:%M:%S" "$ts" +%s 2>/dev/null
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
fmt_age() {
|
|
67
|
+
local then="$1"
|
|
68
|
+
local now diff
|
|
69
|
+
now=$(date +%s)
|
|
70
|
+
diff=$((now - then))
|
|
71
|
+
if [ "$diff" -lt 60 ]; then echo "${diff}s"
|
|
72
|
+
elif [ "$diff" -lt 3600 ]; then echo "$((diff/60))m"
|
|
73
|
+
elif [ "$diff" -lt 86400 ]; then echo "$((diff/3600))h"
|
|
74
|
+
else echo "$((diff/86400))d"
|
|
75
|
+
fi
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
pid_from_lockfile() {
|
|
79
|
+
grep -o '"pid"[^,}]*' "$1" 2>/dev/null | head -1 | sed 's/[^0-9]//g'
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
# Each chip_* function sets two globals so the caller can use both the
|
|
83
|
+
# colored display string (verbose mode) and the abstract state (compact mode)
|
|
84
|
+
# without re-running the underlying probe. Must be called as a bare command
|
|
85
|
+
# (no subshell) so the assignments survive.
|
|
86
|
+
_state=""
|
|
87
|
+
_display=""
|
|
88
|
+
|
|
89
|
+
chip_recall() {
|
|
90
|
+
local pidfile="$PROJECT_ROOT/.recall/watch.pid"
|
|
91
|
+
local repo="$PROJECT_ROOT/.recall/repo.git"
|
|
92
|
+
if [ ! -f "$pidfile" ]; then _state="red"; _display="${RED}recall${RESET}"; return; fi
|
|
93
|
+
local pid
|
|
94
|
+
pid=$(cat "$pidfile" 2>/dev/null) || pid=""
|
|
95
|
+
if [ -z "$pid" ] || ! kill -0 "$pid" 2>/dev/null; then
|
|
96
|
+
_state="red"; _display="${RED}recall${RESET}"
|
|
97
|
+
return
|
|
98
|
+
fi
|
|
99
|
+
if [ -d "$repo" ]; then
|
|
100
|
+
local last
|
|
101
|
+
last=$(git --git-dir="$repo" log -1 --format=%ct 2>/dev/null)
|
|
102
|
+
if [ -n "$last" ]; then
|
|
103
|
+
local now diff age
|
|
104
|
+
now=$(date +%s); diff=$((now - last)); age=$(fmt_age "$last")
|
|
105
|
+
if [ "$diff" -gt 21600 ]; then
|
|
106
|
+
_state="orange"; _display="${ORANGE}recall(${age})${RESET}"
|
|
107
|
+
else
|
|
108
|
+
_state="green"; _display="${GREEN}recall(${age})${RESET}"
|
|
109
|
+
fi
|
|
110
|
+
return
|
|
111
|
+
fi
|
|
112
|
+
fi
|
|
113
|
+
_state="green"; _display="${GREEN}recall${RESET}"
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
chip_chart() {
|
|
117
|
+
local freshness="$PROJECT_ROOT/.launchsecure/graphs/.freshness.json"
|
|
118
|
+
if [ ! -f "$freshness" ]; then _state="red"; _display="${RED}chart${RESET}"; return; fi
|
|
119
|
+
local last
|
|
120
|
+
last=$(grep -o '"lastFullRegenAt"[^,}]*' "$freshness" | head -1 | sed 's/.*"\([0-9TZ:.+-]*\)".*/\1/')
|
|
121
|
+
local lock="$PROJECT_ROOT/.launchsecure/launch-chart.lock"
|
|
122
|
+
local color="$ORANGE"; local state="orange"
|
|
123
|
+
if [ -f "$lock" ]; then
|
|
124
|
+
local pid
|
|
125
|
+
pid=$(pid_from_lockfile "$lock")
|
|
126
|
+
if [ -n "$pid" ] && kill -0 "$pid" 2>/dev/null; then
|
|
127
|
+
color="$GREEN"; state="green"
|
|
128
|
+
fi
|
|
129
|
+
fi
|
|
130
|
+
if [ -n "$last" ] && [ "$last" != "null" ]; then
|
|
131
|
+
local epoch age
|
|
132
|
+
epoch=$(iso_to_epoch "$last")
|
|
133
|
+
if [ -n "$epoch" ]; then
|
|
134
|
+
age=$(fmt_age "$epoch")
|
|
135
|
+
_state="$state"; _display="${color}chart(${age})${RESET}"
|
|
136
|
+
return
|
|
137
|
+
fi
|
|
138
|
+
fi
|
|
139
|
+
_state="$state"; _display="${color}chart${RESET}"
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
chip_deck() {
|
|
143
|
+
local lock="$PROJECT_ROOT/.launchsecure/launch-deck.lock"
|
|
144
|
+
if [ -f "$lock" ]; then
|
|
145
|
+
local pid; pid=$(pid_from_lockfile "$lock")
|
|
146
|
+
if [ -n "$pid" ] && kill -0 "$pid" 2>/dev/null; then
|
|
147
|
+
_state="green"; _display="${GREEN}deck${RESET}"; return
|
|
148
|
+
fi
|
|
149
|
+
fi
|
|
150
|
+
_state="orange"; _display="${ORANGE}deck${RESET}"
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
chip_council() {
|
|
154
|
+
local lock="$PROJECT_ROOT/.launchsecure/launch-council.lock"
|
|
155
|
+
if [ -f "$lock" ]; then
|
|
156
|
+
local pid; pid=$(pid_from_lockfile "$lock")
|
|
157
|
+
if [ -n "$pid" ] && kill -0 "$pid" 2>/dev/null; then
|
|
158
|
+
_state="green"; _display="${GREEN}council${RESET}"; return
|
|
159
|
+
fi
|
|
160
|
+
fi
|
|
161
|
+
_state="orange"; _display="${ORANGE}council${RESET}"
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
show_list=$(echo "$show" | tr ',' ' ')
|
|
165
|
+
total=0
|
|
166
|
+
up=0
|
|
167
|
+
out=""
|
|
168
|
+
for d in $show_list; do
|
|
169
|
+
case "$d" in
|
|
170
|
+
recall) chip_recall ;;
|
|
171
|
+
chart) chip_chart ;;
|
|
172
|
+
deck) chip_deck ;;
|
|
173
|
+
council) chip_council ;;
|
|
174
|
+
*) continue ;;
|
|
175
|
+
esac
|
|
176
|
+
total=$((total + 1))
|
|
177
|
+
[ "$_state" = "green" ] && up=$((up + 1))
|
|
178
|
+
if [ "$compact" = "0" ]; then
|
|
179
|
+
if [ -z "$out" ]; then out="$_display"; else out="${out} · ${_display}"; fi
|
|
180
|
+
fi
|
|
181
|
+
done
|
|
182
|
+
|
|
183
|
+
if [ "$compact" = "1" ]; then
|
|
184
|
+
if [ "$total" -eq 0 ]; then exit 0; fi
|
|
185
|
+
if [ "$up" -eq "$total" ]; then
|
|
186
|
+
printf '%smcp %d/%d%s' "$GREEN" "$up" "$total" "$RESET"
|
|
187
|
+
else
|
|
188
|
+
printf '%smcp %d/%d%s' "$RED" "$up" "$total" "$RESET"
|
|
189
|
+
fi
|
|
190
|
+
else
|
|
191
|
+
printf '%s' "$out"
|
|
192
|
+
fi
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# launch-kit statusline wrapper.
|
|
3
|
+
# Runs the user's original statusline command, then appends MCP chip output.
|
|
4
|
+
# Reads Claude Code's statusline JSON from stdin once, fans it out.
|
|
5
|
+
#
|
|
6
|
+
# The original command is stored under `_launchKitStatuslineOriginal.command`
|
|
7
|
+
# in ~/.claude/settings.json (written by `launch-kit statusline activate`).
|
|
8
|
+
# `launch-kit statusline deactivate` restores it to `statusLine.command`.
|
|
9
|
+
set -u
|
|
10
|
+
|
|
11
|
+
input=$(cat)
|
|
12
|
+
settings="$HOME/.claude/settings.json"
|
|
13
|
+
|
|
14
|
+
original_cmd=""
|
|
15
|
+
if [ -f "$settings" ] && command -v jq >/dev/null 2>&1; then
|
|
16
|
+
original_cmd=$(jq -r '._launchKitStatuslineOriginal.command // empty' "$settings" 2>/dev/null || true)
|
|
17
|
+
fi
|
|
18
|
+
|
|
19
|
+
original_output=""
|
|
20
|
+
if [ -n "$original_cmd" ]; then
|
|
21
|
+
original_output=$(printf '%s' "$input" | bash -c "$original_cmd" 2>/dev/null || true)
|
|
22
|
+
fi
|
|
23
|
+
|
|
24
|
+
cwd=""
|
|
25
|
+
if command -v jq >/dev/null 2>&1; then
|
|
26
|
+
cwd=$(printf '%s' "$input" | jq -r '.workspace.current_dir // .cwd // empty' 2>/dev/null || true)
|
|
27
|
+
fi
|
|
28
|
+
|
|
29
|
+
chips=""
|
|
30
|
+
if [ -x "$HOME/.launchsecure/statusline-mcp.sh" ]; then
|
|
31
|
+
# Build args array conditionally; `set -u` + empty `"${args[@]}"` would
|
|
32
|
+
# error on bash 4 (default macOS bash), so test length before expanding.
|
|
33
|
+
args=()
|
|
34
|
+
[ -n "${LK_STATUSLINE_SHOW:-}" ] && args+=("--show=$LK_STATUSLINE_SHOW")
|
|
35
|
+
[ "${LK_STATUSLINE_COMPACT:-0}" = "1" ] && args+=("--compact")
|
|
36
|
+
if [ "${#args[@]}" -eq 0 ]; then
|
|
37
|
+
chips=$(LK_STATUSLINE_CWD="$cwd" "$HOME/.launchsecure/statusline-mcp.sh" 2>/dev/null || true)
|
|
38
|
+
else
|
|
39
|
+
chips=$(LK_STATUSLINE_CWD="$cwd" "$HOME/.launchsecure/statusline-mcp.sh" "${args[@]}" 2>/dev/null || true)
|
|
40
|
+
fi
|
|
41
|
+
fi
|
|
42
|
+
|
|
43
|
+
sep=$'\033[2m | \033[0m'
|
|
44
|
+
if [ -n "$original_output" ] && [ -n "$chips" ]; then
|
|
45
|
+
printf '%s%s%s' "$original_output" "$sep" "$chips"
|
|
46
|
+
elif [ -n "$original_output" ]; then
|
|
47
|
+
printf '%s' "$original_output"
|
|
48
|
+
elif [ -n "$chips" ]; then
|
|
49
|
+
printf '%s' "$chips"
|
|
50
|
+
fi
|