@kaisers-io/refs 0.12.0 → 0.13.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/CHANGELOG.md +117 -1
- package/dist/refs.mjs +60 -52
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -5,7 +5,123 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
-
## [
|
|
8
|
+
## [0.13.0] - 2026-09-12
|
|
9
|
+
|
|
10
|
+
### Upgrading
|
|
11
|
+
|
|
12
|
+
**`refs add <url> --description "…"` now refuses a source with workspace members.** It holds one
|
|
13
|
+
description, about the repository, and none for a package — and a package's description is no
|
|
14
|
+
longer read out of its manifest, so there is nothing for it to fall back on. Such a source exits
|
|
15
|
+
`3`, listing every package the proposal will need a description for and printing the two-phase
|
|
16
|
+
commands to run instead, with the source quoted into them. The shortcut still finalizes a
|
|
17
|
+
repository with no detected packages, and one whose only detected package is the root at `.`.
|
|
18
|
+
|
|
19
|
+
If a script calls the one-shot against a monorepo, it will start failing. The two-phase flow is the
|
|
20
|
+
replacement and always was the documented agent path.
|
|
21
|
+
|
|
22
|
+
**Package descriptions already in `config.toml` are left exactly as they are.** A description
|
|
23
|
+
imported from a manifest before this release stays; nothing distinguishes it from one someone
|
|
24
|
+
wrote, so nothing rewrites it. To review them: `refs show <ref> --packages --json`, and
|
|
25
|
+
`refs edit <ref> --package <name> description "…"` to replace one.
|
|
26
|
+
|
|
27
|
+
**A ref key containing an unpaired surrogate is now rejected.** No configuration written by refs
|
|
28
|
+
can contain one — TOML has no escape for it — so this only affects a hand-built key.
|
|
29
|
+
|
|
30
|
+
### Fixed
|
|
31
|
+
|
|
32
|
+
- **`refs add` says when it could not fully inspect a repository's declared workspaces.** A
|
|
33
|
+
repository declaring `packages/**` — an ordinary spelling the pattern classifier cannot expand —
|
|
34
|
+
produced a proposal missing every member, with no indication why. That is not self-explanatory:
|
|
35
|
+
a thin `packages` record is also what an ordinary repository produces, and a detected root makes
|
|
36
|
+
even a memberless result look complete. The configuration was then built without those packages,
|
|
37
|
+
and the drift probe returns immediately for a ref that configures none, so nothing downstream
|
|
38
|
+
could recover it either. Both `--dry-run` and the `--description` one-shot now carry a warning
|
|
39
|
+
naming what stopped the scan — an unexpandable pattern, an unreadable manifest, a candidate that
|
|
40
|
+
could not be inspected — alongside any clone warning rather than instead of it.
|
|
41
|
+
|
|
42
|
+
- **A drift probe that could not finish looking says so, instead of reporting `ok`.** The pass that
|
|
43
|
+
finds packages a checkout declares and the configuration does not have stands down whenever
|
|
44
|
+
workspace detection may have missed something — an unreadable manifest, a pattern the classifier
|
|
45
|
+
cannot expand. That conservatism is right: a second declaration of the same name could be behind
|
|
46
|
+
the obstacle, and naming one path from a partial view would prescribe something registration
|
|
47
|
+
might not do. What was wrong is that it then said nothing at all, so `refs doctor` answered
|
|
48
|
+
`config-drift: ok — every configured package path resolves` while an unregistered package sat in
|
|
49
|
+
the checkout. One malformed `package.json` anywhere in a monorepo was enough, permanently.
|
|
50
|
+
|
|
51
|
+
Both discovery passes now report the obstacle. `structure` gains `discovery_incomplete`, naming
|
|
52
|
+
what stopped them (`packages/c: manifest_unreadable`), and its `status` is `unknown` rather than
|
|
53
|
+
`ok`. The findings about configured entries are unaffected — that half never needed the scan to
|
|
54
|
+
begin.
|
|
55
|
+
|
|
56
|
+
- **A ref whose key carries `@`, a space or any non-ASCII character can be locked, and therefore
|
|
57
|
+
synced.** `zRefKey` admits every character but `/`, `\`, `%` and `:`; the lock alphabet is far
|
|
58
|
+
narrower, and the derived lock name was passed through unchanged. Such a ref could be added and
|
|
59
|
+
read but never locked — so `refs sync`, `refs remove`, `refs resolve`'s package verification and
|
|
60
|
+
`refs doctor`'s drift check all failed for it, with a message naming the lock name rather than
|
|
61
|
+
the ref. A name the lock alphabet will not accept now falls back to the same bounded digest form
|
|
62
|
+
a too-long name already used. Keys the alphabet does admit keep their readable name unchanged.
|
|
63
|
+
Reachable through a self-hosted url or a hand-edited `config.toml`; no forge allows these
|
|
64
|
+
characters in a repository path.
|
|
65
|
+
- **A ref key carrying an unpaired surrogate is rejected.** Such a character is a valid JavaScript
|
|
66
|
+
string and has no UTF-8 encoding, so everything that writes the key out — the checkout directory,
|
|
67
|
+
the digest a lock name can fall back to — silently substituted U+FFFD for it. Keys ending
|
|
68
|
+
U+D800, U+D801 and U+FFFD therefore shared one directory and one lock name, and two unrelated
|
|
69
|
+
refs would have serialized against each other. No real config could carry one: TOML has no
|
|
70
|
+
escape for an unpaired surrogate.
|
|
71
|
+
- **A failed sync no longer persists an unbounded amount of remote output into `state.json`.**
|
|
72
|
+
`last_error` holds the failure's message, most often git's own output — which quotes what git was
|
|
73
|
+
working on, ref names among them, chosen by the tracked repository. No persistence-specific limit
|
|
74
|
+
applied: an upstream with 120 conflicting tags produced a 47,816-character error, all of it
|
|
75
|
+
written into a file refs reads back on every command. A newly recorded message now keeps 1500
|
|
76
|
+
characters from the start and 500 from the end, with an exact count of what was dropped in
|
|
77
|
+
between — so both the command that failed and git's own closing hint survive. The result of the
|
|
78
|
+
run that produced the failure still carries the message as it arrived.
|
|
79
|
+
|
|
80
|
+
### Changed
|
|
81
|
+
|
|
82
|
+
- **Dependencies refreshed.** `zod` 4.5.2 → 4.6.2 — the only production dependency, and bundled
|
|
83
|
+
into the published CLI. The development toolchain moved too: `vitest` 4 → 5, `tsdown` 0.22 →
|
|
84
|
+
0.23 (rolldown 1.2.8), `oxlint`, `oxfmt` and `vite`. No behaviour depends on any of it.
|
|
85
|
+
- **A package's description is never read out of a manifest.** Workspace detection carried each
|
|
86
|
+
package's `description` from its `package.json`, and `refs add <source> --description "…"`
|
|
87
|
+
persisted it into `config.toml` — text written by whoever owns the upstream repository, crossing
|
|
88
|
+
into a file refs reads back as its own configuration and replays on every `refs list` and
|
|
89
|
+
`refs show`. Detection now carries a package's name and path and nothing else, which is what
|
|
90
|
+
`refs sync`'s `unregistered` finding has done since 0.12.0. Every description in a config entry
|
|
91
|
+
is written by someone who read the source.
|
|
92
|
+
|
|
93
|
+
The one-shot `refs add <source> --description "…"` therefore no longer registers workspace
|
|
94
|
+
members: it has one description, about the repository, and none for a package. It still
|
|
95
|
+
finalizes a source with no detected packages, or one whose only detected package is the
|
|
96
|
+
repository root at `.` — and there the text now wins over whatever the root manifest said about
|
|
97
|
+
itself. For anything else it exits `3`, naming every package and printing the two-phase commands
|
|
98
|
+
to run instead, with the source it was given quoted into them, and listing every package the
|
|
99
|
+
proposal will need a description for — the repository root included, which is not the set the
|
|
100
|
+
refusal is about but is the set `refs add --proposal` requires.
|
|
101
|
+
|
|
102
|
+
A refusal registers no ref. The checkout it cloned stays, and is now recorded as a pending add —
|
|
103
|
+
so for the next 24 hours `doctor` reports it as one instead of offering to delete a checkout the
|
|
104
|
+
printed recovery is about to reuse, and the clone mode actually used survives into the finalize.
|
|
105
|
+
That mode cannot be recovered from the checkout afterwards: git records
|
|
106
|
+
`remote.origin.promisor` and `partialclonefilter` from the requested filter, whether or not the
|
|
107
|
+
server honoured it.
|
|
108
|
+
|
|
109
|
+
Existing entries are untouched. A description imported from a manifest before this release stays
|
|
110
|
+
exactly as it is — nothing distinguishes it from one written by hand, so nothing rewrites it. To
|
|
111
|
+
review them: `refs show <ref> --packages --json`, and `refs edit <ref> --package <name>
|
|
112
|
+
description "…"` to replace one.
|
|
113
|
+
|
|
114
|
+
### Security
|
|
115
|
+
|
|
116
|
+
- `last_error` is documented as what it is — a failure message, usually git's own output, quoting
|
|
117
|
+
ref names the tracked repository chose — and `skills/refs/COMMANDS.md` now tells agents to read
|
|
118
|
+
it as untrusted evidence rather than as something refs vouches for.
|
|
119
|
+
- `SECURITY.md` claimed refs "never reads checkout content as configuration", which was not true of
|
|
120
|
+
manifest descriptions and is still not true of package names, paths, the default branch, or a
|
|
121
|
+
`tag_format` derived from real tags. The claim is now stated as what the code enforces: refs never
|
|
122
|
+
imports a description out of a manifest. The values that do still cross are named, along with the
|
|
123
|
+
fact that being structural does not make them safe to read — a package name is whatever the
|
|
124
|
+
manifest declares.
|
|
9
125
|
|
|
10
126
|
## [0.12.0] - 2026-09-08
|
|
11
127
|
|