featureparity 0.0.6 → 0.0.7

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.
@@ -0,0 +1,137 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'yaml'
4
+
5
+ module Fp
6
+ # ProjectConfig — the repo-local `.featureparity.yml`, which marks a checkout as
7
+ # tracked by FeatureParity and says which project it belongs to.
8
+ #
9
+ # This is deliberately a different thing from Fp::Config (`~/.config/fp/config.yml`).
10
+ # That one is per-machine and secret-bearing: API keys, profiles, and where each
11
+ # surface lives on *this* laptop. This one is committed to the repo and answers a
12
+ # question the machine can't: "which FeatureParity project is this code?"
13
+ #
14
+ # It exists because enforcement has to work in CI and in an agent's first minute in a
15
+ # fresh clone, where nobody has run `fp setup`. Without it, every workflow file and
16
+ # every agent prompt has to hardcode `--project <slug>`, and the answer drifts. With
17
+ # it, `fp check` in a fresh clone already knows what to check.
18
+ #
19
+ # # .featureparity.yml
20
+ # project: feature-parity
21
+ # enforcement: regression
22
+ # surface: api
23
+ #
24
+ # Discovery walks up from the working directory to the filesystem root, so the file
25
+ # is found from a subdirectory too (like .git or package.json).
26
+ class ProjectConfig
27
+ FILENAME = '.featureparity.yml'
28
+
29
+ # Also accepted when reading, so a repo that picked the dotless or .yaml spelling
30
+ # isn't silently ignored. `fp init` only ever writes FILENAME.
31
+ ALTERNATE_FILENAMES = ['.featureparity.yaml', 'featureparity.yml'].freeze
32
+
33
+ CANDIDATE_FILENAMES = ([FILENAME] + ALTERNATE_FILENAMES).freeze
34
+
35
+ attr_reader :path, :data
36
+
37
+ def initialize(path: nil, data: nil)
38
+ @path = path
39
+ @data = data || {}
40
+ end
41
+
42
+ # Find and load the nearest .featureparity.yml at or above `dir`.
43
+ # Always returns a ProjectConfig — an absent file is an empty one, so callers can
44
+ # treat "no config" and "config without this key" the same way.
45
+ def self.discover(dir = Dir.pwd)
46
+ found = find_file(dir)
47
+ return new unless found
48
+
49
+ new(path: found, data: parse(found))
50
+ end
51
+
52
+ # Walk up from `dir` looking for any candidate filename. Stops at the filesystem
53
+ # root; File.dirname('/') == '/' is the loop's terminator.
54
+ def self.find_file(dir)
55
+ current = File.expand_path(dir)
56
+
57
+ loop do
58
+ CANDIDATE_FILENAMES.each do |name|
59
+ candidate = File.join(current, name)
60
+ return candidate if File.file?(candidate)
61
+ end
62
+
63
+ parent = File.dirname(current)
64
+ return nil if parent == current
65
+
66
+ current = parent
67
+ end
68
+ end
69
+
70
+ # A malformed config must not take the CLI down — warn and behave as if absent,
71
+ # so a bad merge in .featureparity.yml doesn't break every fp command in the repo.
72
+ def self.parse(file)
73
+ loaded = YAML.safe_load_file(file, permitted_classes: [Symbol])
74
+ loaded.is_a?(Hash) ? loaded : {}
75
+ rescue StandardError => e
76
+ warn "Warning: Could not load #{file}: #{e.message}"
77
+ {}
78
+ end
79
+
80
+ # Serialize a config file body. Kept here (rather than in `fp init`) so the reader
81
+ # and the writer of this format live next to each other.
82
+ def self.render(project:, enforcement: nil, surface: nil)
83
+ body = +<<~HEADER
84
+ # FeatureParity — this repo is tracked for feature parity.
85
+ # Committed on purpose: it is how CI and agents know which project this code
86
+ # belongs to without anyone passing --project. See https://featureparity.dev
87
+ ---
88
+ project: #{project}
89
+ HEADER
90
+
91
+ body << "\n# Enforcement level the CI gate applies (off, regression, covered, strict).\n" \
92
+ "# Omit to use whatever the project is configured with server-side.\n" \
93
+ "enforcement: #{enforcement}\n" if enforcement
94
+
95
+ body << "\n# Default surface for this repo, used when --surface is not given.\n" \
96
+ "surface: #{surface}\n" if surface
97
+
98
+ body
99
+ end
100
+
101
+ def exists?
102
+ !path.nil?
103
+ end
104
+
105
+ # Project slug this checkout belongs to.
106
+ def project
107
+ fetch_string('project')
108
+ end
109
+
110
+ # Enforcement level override committed alongside the code. Nil means "use the
111
+ # project's configured level" — the normal case, since the point of storing the
112
+ # level server-side is that it only has to be raised in one place.
113
+ def enforcement
114
+ fetch_string('enforcement')
115
+ end
116
+
117
+ # Default surface for a single-surface repo. A monorepo leaves this out and passes
118
+ # --surface per job instead.
119
+ def surface
120
+ fetch_string('surface')
121
+ end
122
+
123
+ def to_h
124
+ { 'path' => path }.merge(data)
125
+ end
126
+
127
+ private
128
+
129
+ def fetch_string(key)
130
+ value = data[key]
131
+ return nil if value.nil?
132
+
133
+ string = value.to_s.strip
134
+ string.empty? ? nil : string
135
+ end
136
+ end
137
+ end
data/lib/fp/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Fp
4
- VERSION = '0.0.6'
4
+ VERSION = '0.0.7'
5
5
  end
data/lib/fp.rb CHANGED
@@ -2,6 +2,8 @@
2
2
 
3
3
  require_relative 'fp/version'
4
4
  require_relative 'fp/config'
5
+ require_relative 'fp/project_config'
6
+ require_relative 'fp/parity'
5
7
  require_relative 'fp/client'
6
8
  require_relative 'fp/junit'
7
9
  require_relative 'fp/output'
data/skills/fp/SKILL.md CHANGED
@@ -45,10 +45,11 @@ If not set, ask the user for their API key or have them run `fp setup`.
45
45
 
46
46
  ```bash
47
47
  fp list --project stowzilla # All active requirements
48
- fp list --project stowzilla --gaps # Requirements without evidence
48
+ fp list --project stowzilla --gaps # Requirements with a required surface lacking evidence
49
49
  fp show <slug> --project stowzilla # Requirement details
50
50
  fp matrix --project stowzilla # ASCII parity matrix
51
51
  fp matrix --project stowzilla --csv # Export as CSV
52
+ fp check --project stowzilla # Enforce parity (exits 1 on violations)
52
53
  ```
53
54
 
54
55
  ### 2. Add the fp:<slug> marker to tests
@@ -118,16 +119,123 @@ fp propose --project stowzilla \
118
119
 
119
120
  **Agents always create requirements as draft.** A human must activate in the web app.
120
121
 
122
+ ### 6. Propose a change to an existing requirement
123
+
124
+ When a requirement no longer says the right thing, don't silently edit it — propose the
125
+ change and say what prompted it. The requirement drops back to **draft** and a human
126
+ reviews the old-vs-new diff before it counts again.
127
+
128
+ ```bash
129
+ fp propose-change print_container_qr --project stowzilla \
130
+ --why "Labels must survive a freezer" \
131
+ --acceptance "QR scans after 24h at -20C" \
132
+ --reason "Cold-chain rollout changed the requirement" \
133
+ --source-type fizzy \
134
+ --source-url https://app.fizzy.do/6098707/cards/1377
135
+ ```
136
+
137
+ To propose deleting one instead (nothing is destroyed — a human decides):
138
+
139
+ ```bash
140
+ fp propose-change print_container_qr --project stowzilla \
141
+ --removal --reason "Superseded by print_container_label"
142
+ ```
143
+
144
+ Rules:
145
+
146
+ - Always pass `--reason` and, where one exists, `--source-type`/`--source-url`. The whole
147
+ point of a proposal is that the *why* is recorded next to the *what*.
148
+ - Agents cannot change `--slug` (it's the join to every `fp:<slug>` marker) or remove a
149
+ required surface. Both come back as a 403, printed verbatim.
150
+ - Agents cannot approve or reject. Only humans review.
151
+ - One open proposal per requirement — review the current one before proposing another.
152
+
153
+ ### 7. See what changed and why
154
+
155
+ ```bash
156
+ fp history print_container_qr --project stowzilla
157
+ fp history print_container_qr --project stowzilla --pending # just what's awaiting review
158
+ ```
159
+
160
+ Shows the timeline (who changed what, when, and what prompted it), the field-by-field
161
+ diff, and a diff of the **test evidence** — so you can see which tests were written
162
+ against wording that has since moved.
163
+
164
+ ### 8. Verify you didn't break the gate
165
+
166
+ ```bash
167
+ fp check # exits non-zero on violations
168
+ fp check --surface api --json # machine-readable violations
169
+ ```
170
+
171
+ `fp check` is the enforcement gate. It asks the API whether the project violates its
172
+ enforcement level and **exits 1 if it does**, which is what makes it usable as a required
173
+ status check on a pull request. Run it after reporting evidence to confirm your change
174
+ left parity intact.
175
+
176
+ Levels, from most to least forgiving:
177
+
178
+ | Level | Fails the build when |
179
+ |-------|----------------------|
180
+ | `off` | never — report only |
181
+ | `regression` | a test that was passing has gone red |
182
+ | `covered` | any required surface lacks real (non-stub) evidence |
183
+ | `strict` | any required surface isn't green in CI |
184
+
185
+ The level is configured on the project, so don't hardcode one. `--level` overrides it for
186
+ a single run; `--warn-only` prints violations but still exits 0.
187
+
188
+ **A requirement you proposed a change to still counts.** It drops to `draft` until a human
189
+ reviews it, but the gate keeps measuring it — otherwise proposing a change would be a way
190
+ to make a red gate green. What relaxes is only evidence *freshness*: proposing marks the
191
+ existing evidence `suspect`, and `suspect` doesn't fail a build below `strict`. Re-write the
192
+ tests against the new wording and re-report to clear it. `fp check` lists everything
193
+ awaiting review so you can tell that apart from a plain gap.
194
+
195
+ **Gating a branch on its own results.** Plain `fp check` sees only what has been recorded,
196
+ which on a pull request is the default branch's state — it cannot see what your branch
197
+ changed. Pass the run's JUnit to evaluate your results without saving them:
198
+
199
+ ```bash
200
+ fp check --surface api --junit junit.xml
201
+ ```
202
+
203
+ This also catches the one regression a green test suite can't: a test that was deleted,
204
+ renamed, or had its `fp:` marker stripped. Every remaining test passes, so CI is happy,
205
+ while the requirement quietly lost its only proof.
206
+
207
+ ### Repo-local config (`.featureparity.yml`)
208
+
209
+ If a repo has a `.featureparity.yml`, `--project` is optional — `fp` reads the slug from
210
+ it. Check for one before asking the user which project to use:
211
+
212
+ ```bash
213
+ cat .featureparity.yml 2>/dev/null
214
+ ```
215
+
216
+ To onboard a repo that has no such file (writes the config, a CI gate workflow, and a
217
+ FeatureParity section in `AGENTS.md`):
218
+
219
+ ```bash
220
+ fp init --project <slug> --surface <surface>
221
+ ```
222
+
121
223
  ## Quick reference
122
224
 
123
225
  | Task | Command |
124
226
  |------|---------|
227
+ | Onboard a repo | `fp init --project X` |
125
228
  | List requirements | `fp list --project X` |
126
229
  | Show gaps | `fp list --project X --gaps` |
127
230
  | Show details | `fp show <slug> --project X` |
128
231
  | Report evidence | `fp report <slug> --project X --surface Y --file Z --repo A/B` |
129
232
  | Report from JUnit | `fp report --junit file.xml --project X --surface Y --repo A/B` |
130
233
  | Propose requirement | `fp propose --project X --slug Y --name "..."` |
234
+ | Propose a change | `fp propose-change <slug> --project X --why "..." --reason "..."` |
235
+ | Propose a deletion | `fp propose-change <slug> --project X --removal --reason "..."` |
236
+ | View change history | `fp history <slug> --project X` |
237
+ | Enforce parity | `fp check --project X` |
238
+ | Gate on this run | `fp check --project X --surface Y --junit file.xml` |
131
239
  | View matrix | `fp matrix --project X` |
132
240
  | List surfaces | `fp surfaces --project X` |
133
241
  | List projects | `fp projects` |
@@ -49,17 +49,22 @@ List requirements.
49
49
 
50
50
  ```bash
51
51
  fp list --project stowzilla # All active requirements
52
- fp list --project stowzilla --gaps # Requirements without evidence
52
+ fp list --project stowzilla --gaps # Requirements with a required surface lacking evidence
53
53
  fp list --project stowzilla --status draft
54
54
  fp list --project stowzilla --json
55
55
  ```
56
56
 
57
57
  | Flag | Description |
58
58
  |------|-------------|
59
- | `--project` | Project slug (required) |
60
- | `--gaps` | Show only requirements without evidence |
59
+ | `--project` | Project slug (optional if `.featureparity.yml` declares one) |
60
+ | `--gaps` | Show only requirements with an uncovered required surface |
61
61
  | `--status` | Filter by status: active, draft, archived |
62
62
  | `--category` | Filter by category |
63
+ | `--surface` | Restrict to one surface |
64
+
65
+ `--gaps` reports a requirement when one of its required surfaces has no real evidence —
66
+ missing, stubbed, or failing. The table's UNCOVERED column names those surfaces and their
67
+ state.
63
68
 
64
69
  ### fp show
65
70
 
@@ -147,16 +152,190 @@ fp propose --project stowzilla \
147
152
 
148
153
  **Agents always create requirements as draft.** A human must activate in the web app.
149
154
 
155
+ ### fp propose-change
156
+
157
+ Propose a change to a requirement that already exists — or propose deleting it.
158
+ Recorded with what the requirement *was*, a field-by-field diff, and what prompted the
159
+ change. The requirement drops back to **draft** until a human approves or rejects.
160
+
161
+ ```bash
162
+ fp propose-change print_container_qr --project stowzilla \
163
+ --why "Labels must survive a freezer" \
164
+ --acceptance "QR scans after 24h at -20C" \
165
+ --reason "Cold-chain rollout" \
166
+ --source-type fizzy \
167
+ --source-url https://app.fizzy.do/6098707/cards/1377
168
+
169
+ # Propose deletion instead
170
+ fp propose-change print_container_qr --project stowzilla \
171
+ --removal --reason "Superseded by print_container_label"
172
+ ```
173
+
174
+ | Flag | Required | Description |
175
+ |------|----------|-------------|
176
+ | `<slug>` | Yes | Positional: the requirement to change |
177
+ | `--project` | No | Project slug (optional if `.featureparity.yml` declares one) |
178
+ | `--removal` | No | Propose deleting the requirement instead of modifying it |
179
+ | `--name` | No | Proposed title |
180
+ | `--why` | No | Proposed justification |
181
+ | `--acceptance` | No | Proposed acceptance criteria |
182
+ | `--required` | No | Proposed surfaces (comma-separated; agents cannot remove existing ones) |
183
+ | `--category` | No | Proposed category |
184
+ | `--parent` | No | Proposed parent requirement, by slug |
185
+ | `--slug` | No | Proposed new slug — **humans only**; breaks existing `fp:<slug>` markers |
186
+ | `--reason` | No | Why the change is needed |
187
+ | `--source-type` | No | `fizzy`, `discord`, `github_issue`, `jira`, `notion`, `manual`, `ai_chat` |
188
+ | `--source-url` | No | Link to the card/message/issue that prompted it |
189
+ | `--source-context` | No | Free-text context |
190
+
191
+ At least one proposed value (or `--removal`) is required — a bare invocation would knock a
192
+ live requirement back to draft for nothing, so it errors instead.
193
+
194
+ Refusals, all printed verbatim:
195
+
196
+ | Situation | Status |
197
+ |-----------|--------|
198
+ | Agent tries to change `--slug` | 403 |
199
+ | Agent tries to remove a required surface | 403 |
200
+ | Proposed values match the current text | 422 |
201
+ | Requirement already has a proposal awaiting review | 409 |
202
+ | Requirement is archived | 422 |
203
+
204
+ **Agents cannot approve or reject.** Reviewing is a human action in the web app.
205
+
206
+ ### fp history
207
+
208
+ Show a requirement's change history: what changed, who changed it, what prompted it, and
209
+ how the test evidence moved.
210
+
211
+ ```bash
212
+ fp history print_container_qr --project stowzilla
213
+ fp history print_container_qr --project stowzilla --pending # only what awaits review
214
+ fp history print_container_qr --project stowzilla --limit 5 # most recent 5 entries
215
+ fp history print_container_qr --project stowzilla --json
216
+ ```
217
+
218
+ Entries are printed oldest first. Each shows the event (created, edited, approved,
219
+ change proposed, deletion proposed, change approved/rejected, archived, deleted), the
220
+ actor and whether they were a human or an agent, the field diff, the evidence diff, and
221
+ the review decision where one was made. An open proposal is called out at the end.
222
+
223
+ `--project` is optional when the repo has a `.featureparity.yml`.
224
+
150
225
  ### fp matrix
151
226
 
152
- Display the parity matrix.
227
+ Display the parity matrix. Cells show the state of the evidence filed for each
228
+ (requirement, surface) pair.
153
229
 
154
230
  ```bash
155
231
  fp matrix --project stowzilla # ASCII table
156
- fp matrix --project stowzilla --csv # CSV export
232
+ fp matrix --project stowzilla --csv # CSV export (state names, not glyphs)
157
233
  fp matrix --project stowzilla --json # JSON
234
+ fp matrix --project stowzilla --surface api
235
+ ```
236
+
237
+ | Glyph | State | Meaning |
238
+ |-------|-------|---------|
239
+ | `✓` | passing | CI ran the test and it was green |
240
+ | `✗` | failing | CI ran the test and it was red, or the marker vanished |
241
+ | `●` | present | an agent found the `fp:` marker; no CI verdict yet |
242
+ | `○` | stub | reported as a stub / skipped test |
243
+ | `⚠` | suspect | the requirement changed; evidence needs re-verification |
244
+ | `·` | missing | no evidence for this surface |
245
+ | `-` | — | the requirement does not require this surface |
246
+
247
+ A slug suffixed with `✎` has a proposed change awaiting review — it is still measured by
248
+ the gate, and its evidence is `suspect` until someone reviews the change. `--csv` writes
249
+ `<slug> (under review)` instead of the glyph.
250
+
251
+ ### fp check
252
+
253
+ The enforcement gate. Exits **1** when the project violates its enforcement level, so it
254
+ can be a required status check on a pull request.
255
+
256
+ ```bash
257
+ fp check # project from .featureparity.yml
258
+ fp check --project stowzilla --surface api
259
+ fp check --level covered # override the project's level for this run
260
+ fp check --warn-only # print violations, still exit 0
261
+ fp check --json
262
+ fp check --surface api --junit junit.xml # gate on this run's results (preview)
158
263
  ```
159
264
 
265
+ | Flag | Description |
266
+ |------|-------------|
267
+ | `--project` | Project slug (optional if `.featureparity.yml` declares one) |
268
+ | `--surface` | Gate one surface only |
269
+ | `--level` | Override the project's enforcement level |
270
+ | `--junit` | Evaluate this run's JUnit results without saving them (repeatable) |
271
+ | `--warn-only` | Report violations but exit 0 |
272
+
273
+ **Enforcement levels** (the level is configured on the project; don't hardcode one):
274
+
275
+ | Level | A build fails when |
276
+ |-------|--------------------|
277
+ | `off` | never — report only |
278
+ | `regression` | a test that was passing has gone red |
279
+ | `covered` | any required surface lacks real (non-stub) evidence |
280
+ | `strict` | any required surface isn't green in CI |
281
+
282
+ `suspect` only violates at `strict`: it means the requirement's wording moved, not that
283
+ the test broke.
284
+
285
+ **Requirements awaiting review still count.** `fp propose-change` demotes a requirement to
286
+ `draft`, but the gate keeps measuring it — otherwise proposing a change would be a way to
287
+ turn a red gate green. Coverage is still demanded; only evidence freshness relaxes, because
288
+ proposing marks the evidence `suspect`. `fp check` lists them separately from violations:
289
+
290
+ ```
291
+ 1 requirement(s) awaiting review of a proposed change (1 modification):
292
+ SLUG PROPOSED TITLE
293
+ print_qr modification Print QR on container label
294
+ These still count toward the gate — proposing a change is not a way out of it.
295
+ ```
296
+
297
+ `fp matrix` marks the same rows with `✎`, and `fp list` names them even when the default
298
+ `--status active` filter would otherwise hide them.
299
+
300
+ **Preview mode (`--junit`).** Without it, `fp check` sees only what has been recorded —
301
+ on a pull request, that's the default branch's state, not what the branch changed. With
302
+ it, the run's results are applied on top of the recorded grid and then discarded, so a
303
+ branch is judged on its own results without writing evidence that would churn the live
304
+ matrix for everyone.
305
+
306
+ Preview also catches the regression a green suite can't: a test that was deleted,
307
+ renamed, or had its `fp:` marker removed. The remaining tests all pass, so CI is happy,
308
+ while the requirement silently lost its only proof. Pass one `--junit` per surface —
309
+ `--surface` tells the server which cells the run had a chance to cover, and pooling
310
+ surfaces into one call would misattribute vanished tests.
311
+
312
+ ### fp init
313
+
314
+ Onboard a repo onto FeatureParity. Writes three files and makes no API calls (so it works
315
+ before a key is configured).
316
+
317
+ ```bash
318
+ fp init --project stowzilla --surface api
319
+ fp init --project stowzilla --no-workflow # skip the CI workflow
320
+ fp init --project stowzilla --no-agents # skip the AGENTS.md section
321
+ fp init --project stowzilla --force # refresh files written previously
322
+ fp init --project stowzilla --dir path/to/repo
323
+ ```
324
+
325
+ | File | Purpose |
326
+ |------|---------|
327
+ | `.featureparity.yml` | declares the project slug, so `--project` becomes optional |
328
+ | `.github/workflows/feature-parity.yml` | the CI gate |
329
+ | `AGENTS.md` (appended) | instructions telling agents to mark and report their tests |
330
+
331
+ Existing files are skipped unless `--force`. The `AGENTS.md` section is appended between
332
+ marker comments and recognised on re-run, so it is never duplicated.
333
+
334
+ The `AGENTS.md` section tells agents to bind a test to a requirement with `fp:<slug>`, to
335
+ propose a new requirement when none covers the work, and to use `fp propose-change`
336
+ instead of editing or ignoring a requirement that no longer says the right thing — plus
337
+ the guards they would otherwise learn from a 403.
338
+
160
339
  ### fp profile
161
340
 
162
341
  Manage named profiles. Each profile stores an API key and (optionally) the
@@ -222,14 +401,32 @@ fp repos unset customer_android --project stowzilla
222
401
  |------|---------|
223
402
  | 0 | Success |
224
403
  | 1 | General error (invalid flags, missing required args, API error) |
404
+ | 1 | `fp check`: the parity gate failed (violations) |
405
+ | 130 | Interrupted (Ctrl-C) |
406
+
407
+ `fp check` deliberately reuses exit 1 for a failed gate rather than a distinct code: CI
408
+ treats any non-zero as a failure, and a special code would only invite scripts to ignore
409
+ it. Use `--json` to distinguish a gate failure (`ok: true`, `passing: false`) from a
410
+ request failure (`ok: false`).
225
411
 
226
412
  ## Configuration files
227
413
 
228
- - `~/.config/fp/config.yml` — Profiles and global settings
414
+ - `~/.config/fp/config.yml` — Profiles and global settings (per-machine, holds API keys)
415
+ - `.featureparity.yml` — Repo-local, committed. Declares which project a checkout belongs
416
+ to so CI and agents don't need `--project`. Discovered by walking up from the working
417
+ directory. Written by `fp init`.
229
418
  - `FP_API_KEY` environment variable overrides all profiles
230
419
  - `FP_API_URL` environment variable overrides API URL
231
420
  - `FP_PROFILE` environment variable selects a profile
232
421
 
422
+ ### .featureparity.yml
423
+
424
+ ```yaml
425
+ project: stowzilla # required — the project slug
426
+ enforcement: regression # optional — usually omit; the level lives on the project
427
+ surface: api # optional — default surface for a single-surface repo
428
+ ```
429
+
233
430
  ## API URL priority
234
431
 
235
432
  1. `--api-url` flag
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: featureparity
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.6
4
+ version: 0.0.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - Stowzilla
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-09-14 00:00:00.000000000 Z
11
+ date: 2026-09-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: net-http
@@ -95,14 +95,19 @@ files:
95
95
  - lib/fp/client.rb
96
96
  - lib/fp/commands.rb
97
97
  - lib/fp/commands/base.rb
98
+ - lib/fp/commands/check.rb
98
99
  - lib/fp/commands/ci_report.rb
99
100
  - lib/fp/commands/config_cmd.rb
101
+ - lib/fp/commands/export.rb
100
102
  - lib/fp/commands/help.rb
103
+ - lib/fp/commands/history.rb
104
+ - lib/fp/commands/init.rb
101
105
  - lib/fp/commands/list.rb
102
106
  - lib/fp/commands/matrix.rb
103
107
  - lib/fp/commands/profile.rb
104
108
  - lib/fp/commands/projects.rb
105
109
  - lib/fp/commands/propose.rb
110
+ - lib/fp/commands/propose_change.rb
106
111
  - lib/fp/commands/report.rb
107
112
  - lib/fp/commands/repos.rb
108
113
  - lib/fp/commands/setup.rb
@@ -113,6 +118,8 @@ files:
113
118
  - lib/fp/config.rb
114
119
  - lib/fp/junit.rb
115
120
  - lib/fp/output.rb
121
+ - lib/fp/parity.rb
122
+ - lib/fp/project_config.rb
116
123
  - lib/fp/version.rb
117
124
  - skills/fp/SKILL.md
118
125
  - skills/fp/references/cli.md