@sandgarden/miranda 0.1.86 → 0.1.88

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/collector/stub.sh CHANGED
@@ -44,15 +44,15 @@
44
44
  # now downloads the same release from "sandgardenhq/plugins" with the
45
45
  # "miranda-collector" prefix (#813). They stay placeholders rather than
46
46
  # literals so the release host can move without editing this file.
47
- BUILD_VERSION="dfe686cb61ce"
48
- RELEASE_TAG="collector-dfe686cb61ce"
47
+ BUILD_VERSION="d9f84d746201"
48
+ RELEASE_TAG="collector-d9f84d746201"
49
49
  RELEASE_REPO="sandgardenhq/plugins"
50
50
  ASSET_PREFIX="miranda-collector"
51
- CHECKSUM_DARWIN_ARM64="58b322fa26b3f2b7d2d6796d7f041b03916c78bc3fe34298157d812524697adb"
52
- CHECKSUM_DARWIN_X64="2de39f78f59bf4fe2482e75ccd182d30e2881400a1d3d662bcb376e54a24e1af"
53
- CHECKSUM_LINUX_X64="ee4f23586c6a96b86c3a1bcc98b37568c8de3e459cd1c1616096cbe3d8c30d37"
54
- CHECKSUM_LINUX_ARM64="aa93bc71849dd295817d907f20865bdda1560145b46fbcc850b6b67293bc1a62"
55
- CHECKSUM_WINDOWS_X64="e4024913af817202bc83e07fb3527ce6fb8ad75527c593e64c49118c11fa63ba"
51
+ CHECKSUM_DARWIN_ARM64="a7bcd2b373d1862764c693f9a806549ab8dac83c5e51e4c14d65d842b83d4214"
52
+ CHECKSUM_DARWIN_X64="4a94c252717e69ec1113a9b17c7478949229b9b402f5ab62f4bba2d4977048ca"
53
+ CHECKSUM_LINUX_X64="453fcff80977921ef6b2ab799f88339c23e4d6ad744410d8dfc2caab239814c5"
54
+ CHECKSUM_LINUX_ARM64="0eaa33f9cf12ae3cf5d41ecd2d1516499bd25958b360c24d07f54ed383616f26"
55
+ CHECKSUM_WINDOWS_X64="b5a7034625929c1517bca0923396e10bc6a97564a946a6a4e678e7cd5d359d85"
56
56
 
57
57
  # A download lock older than this is a downloader that died mid-run: take it
58
58
  # over (mirrors the collector's sweep-lock staleness cutoff).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sandgarden/miranda",
3
- "version": "0.1.86",
3
+ "version": "0.1.88",
4
4
  "type": "module",
5
5
  "description": "Miranda OpenCode plugin — token-usage tracking skill + the gloria.dev MCP server's scoped /miranda tools.",
6
6
  "main": "index.js",
@@ -22,7 +22,7 @@ gets collected. Two files carry that:
22
22
  Then, once those are wired, enrol this machine so the collector daemon the
23
23
  Miranda plugin installs actually starts reporting usage, and make sure this
24
24
  repository is a Miranda project — the collector attributes a session to a
25
- project only when the session's `origin` remote matches one, so an
25
+ project only when the session's `origin` remote matches one of a project's repositories, so an
26
26
  unregistered repository's spend all lands in **Unassigned**.
27
27
 
28
28
  Never modify the user's files without showing them exactly what will change
@@ -312,16 +312,19 @@ spend stops landing in Unassigned. Use the `miranda` MCP tools only.
312
312
 
313
313
  Change nothing else — not even letter case.
314
314
 
315
- 3. **Look for a match.** Call `list_projects`, normalize each project's
316
- `repoUrl` the same way (older rows can carry a trailing `/` or `.git`), and
317
- compare exactly, case-sensitive — ingest matches the same way, so a
318
- case-only difference is not a match. If a project matches, name the
319
- existing project — its name and slug (all of them, if more than one
320
- matches) — say spend from this repository is already attributed to it, and
321
- register nothing.
315
+ 3. **Look for a match.** A project owns a set of repositories. Call
316
+ `list_projects`, normalize every URL in each project's `repoUrls` (the
317
+ project's repositories, its primary first) the same way (older rows can
318
+ carry a trailing `/` or `.git`), and compare exactly, case-sensitive —
319
+ ingest matches the same way, so a case-only difference is not a match.
320
+ Compare against all of them, not just the primary `repoUrl`: a repository
321
+ attached to a project is that project's as much as its primary is. If a
322
+ project matches, name the existing project — its name and slug (all of
323
+ them, if more than one matches) — say spend from this repository is
324
+ already attributed to it, and register nothing.
322
325
 
323
326
  4. **Register when nothing matches.** Call `register_project` with
324
- `{ "project": { "slug": …, "name": …, "repoUrl": <normalized URL> } }`:
327
+ `{ "project": { "slug": …, "name": …, "repoUrls": [<normalized URL>] } }`:
325
328
  - `name` — the repository name: the last path segment of the normalized
326
329
  URL (`https://github.com/sandgardenhq/gloria.dev` → `gloria.dev`);
327
330
  - `slug` — that name lowercased, every run of characters outside `a-z0-9`
@@ -329,13 +332,23 @@ spend stops landing in Unassigned. Use the `miranda` MCP tools only.
329
332
 
330
333
  `register_project` **upserts by slug**: sending a slug some other project
331
334
  already has overwrites that project's name and repository. So if
332
- `list_projects` already has the slug — whatever its `repoUrl`, set or not —
335
+ `list_projects` already has the slug — whatever its `repoUrls`, set or not —
333
336
  or the slug came out empty, use `<owner>-<repo>` built the same way
334
337
  (`sandgardenhq-gloria-dev`). `<owner>` is the path segment before the
335
338
  repository name (for `https://gitlab.com/group/sub/repo`, `sub`). If that is
336
339
  taken too, append a number to it — `<owner>-<repo>-2`, `-3`, … — until it
337
340
  is free. Slugs can't be changed later, so never guess past this rule.
338
341
 
342
+ If the result's `status` is `"held_by"`, another project already holds
343
+ this repository (it was attached after your `list_projects` call, or the
344
+ comparison missed it): nothing was created. Name that project from
345
+ `heldBy` (its slug and name), say this repository's spend is already
346
+ attributed to it, and register nothing more.
347
+
348
+ Registering never attaches: you can't tell from the code whether two
349
+ repositories are one product, and attaching moves spend between projects.
350
+ Never call `attach_project_repository` yourself.
351
+
339
352
  5. **Report what happened** in step 7, from the result's `project` and
340
353
  `backfill`:
341
354
  - the `slug` it registered under (and the name, when it isn't the same as
@@ -353,6 +366,18 @@ spend stops landing in Unassigned. Use the `miranda` MCP tools only.
353
366
  - in every case, spend from more than 80 days before today stays in
354
367
  Unassigned.
355
368
 
369
+ 6. **Say how to attach instead.** After registering, tell the user that if
370
+ this repository is part of an existing product rather than a product of
371
+ its own, an org admin (`settings:manage`) can attach it to that product's
372
+ project: on the new project's page in Miranda, **Merge into…** that
373
+ project, which attaches this repository to it and moves the spend that
374
+ just moved here along with it. (`attach_project_repository` attaches a
375
+ repository no active project holds, so it won't take this one while the
376
+ new project does — it's how an admin adds a repository that isn't
377
+ registered yet.) Name the org's other projects from `list_projects` (name
378
+ and slug; the first ten and how many more, if there are more than ten) so
379
+ they can pick one. Skip this when the org had no other project.
380
+
356
381
  If `list_projects` or `register_project` fails with a transient error (a
357
382
  dropped connection, a timeout), retry it once. If it still fails, or the
358
383
  `miranda` MCP server isn't connected, or the call returns an error — say so,
@@ -13,7 +13,8 @@ Miranda is available two ways, and both apply to you:
13
13
  `get_my_effectiveness`, `get_my_workflow`, `get_my_tracking_status`,
14
14
  `assign_my_session_work_item`, the managers-only `get_team_effectiveness`,
15
15
  plus the shared
16
- `get_info`/`list_projects`/`register_project`/`put_project_github_info` —
16
+ `get_info`/`list_projects`/`register_project`/`put_project_github_info` and
17
+ the admin-only `attach_project_repository`/`detach_project_repository` —
17
18
  the same rows the
18
19
  `gloria` server exposes, since Miranda and gloria share one database).
19
20
  - **The usage-collector hooks** installed with the Miranda plugin (Claude
@@ -124,10 +125,12 @@ session's cost isn't available yet rather than estimating it.
124
125
  ## When this repository isn't a project
125
126
 
126
127
  Miranda attributes a session to a project only when the session's `origin`
127
- remote matches a registered project's `repoUrl`; everything else lands in
128
- **Unassigned**, where no per-project or per-issue figure can see it. So when a
128
+ remote matches one of a registered project's repositories (a project can own
129
+ several; `list_projects` lists them as `repoUrls`, the primary first);
130
+ everything else lands in **Unassigned**, where no per-project or per-issue
131
+ figure can see it. So when a
129
132
  Miranda tool result shows this repository isn't a project — `list_projects`
130
- has no project whose `repoUrl` matches this repository's `origin`, or
133
+ has no project with this repository's `origin` among its `repoUrls`, or
131
134
  `get_my_tracking_status` or `get_my_spend` puts this repository's sessions in
132
135
  Unassigned — register it, **without asking**. Registering is part of tracking
133
136
  this repository, which the team has already adopted.
@@ -137,11 +140,15 @@ project" step: read `origin` with
137
140
  `git remote get-url origin | sed -E 's#://[^/@]*@#://#'` (the `sed` keeps a
138
141
  token embedded in the remote out of the transcript), normalize it (SSH forms
139
142
  to `https://`, no userinfo, no `.git` or trailing `/`), compare it against
140
- every project's `repoUrl` normalized the same way, and when nothing matches, call `register_project` with the repository's name
143
+ every URL in every project's `repoUrls` normalized the same way, and when nothing matches, call `register_project` with the repository's name
141
144
  and a slug no existing project has. Then tell the user the slug and what the
142
145
  result's `backfill` moved out of Unassigned (sessions, dollars, earliest
143
- session). Spend older than 80 days stays in Unassigned. No `origin` remote
144
- means nothing to match — say so and move on.
146
+ session), and that if this repository belongs to an existing product an
147
+ admin can make it part of that project instead (the skill's step says how,
148
+ naming the org's projects). Never call `attach_project_repository` yourself:
149
+ whether two repositories are one product is the team's call. Spend older
150
+ than 80 days stays in Unassigned. No `origin` remote means nothing to match
151
+ — say so and move on.
145
152
 
146
153
  ## Setting up usage tracking
147
154
 
@@ -157,17 +164,28 @@ this end to end.
157
164
  ## MCP tools reference
158
165
 
159
166
  All tools run as the authenticated user against their active organization.
160
- Reads need `inventory:read` (any member); writes need `inventory:write`.
167
+ Reads need `inventory:read` (any member); writes need `inventory:write` unless noted below.
161
168
 
162
169
  - `get_info` — org id/name/slug. Cheap; call it first when you need org
163
170
  context or to check the `miranda` MCP server is reachable.
164
- - `list_projects` — every project in the org, with its `repoUrl`. How you
165
- tell whether this repository is a project yet.
171
+ - `list_projects` — every project in the org, with `repoUrls` (every
172
+ repository it owns, the primary first) and `repoUrl` (the primary). How you
173
+ tell whether this repository is a project yet: it is when its `origin`
174
+ matches any of a project's `repoUrls`.
166
175
  - `register_project`, `put_project_github_info` — register this repo as a
167
- gloria.dev/Miranda project (the same rows either server writes). Upserts by
168
- slug, so never send a slug another project already has. When it gives a
169
- project a `repoUrl`, its `backfill` reports the repository's earlier
170
- Unassigned spend that moved onto it.
176
+ gloria.dev/Miranda project (the same rows either server writes). Takes
177
+ `repoUrls`, the first the primary. Upserts by slug, so never send a slug
178
+ another project already has; on an existing project it only adds
179
+ repositories. Creating refuses a repository another project holds — status
180
+ `"held_by"` with `heldBy` naming it. For each repository it attaches, the
181
+ earlier Unassigned spend moves onto the project; `backfill` reports the
182
+ total.
183
+ - `attach_project_repository`, `detach_project_repository` — add a repository
184
+ to an existing project, or take one off (`projectSlug`, `repoUrl`). Require
185
+ `settings:manage` (an org admin); call them only when the user asks.
186
+ Attaching moves the repository's last 80 days of Unassigned spend onto the
187
+ project and reports it as `backfill`; detaching leaves attributed spend
188
+ where it is.
171
189
  - `tag_session_work_item` — declare the GitHub issue this session is working
172
190
  on (bare issue number, `gh:482`, or a full issue URL). Session-local — it
173
191
  writes only to this session's own transcript, never the database — the