-
Notifications
You must be signed in to change notification settings - Fork 9
Expand file tree
/
Copy path.facts
More file actions
887 lines (809 loc) · 47.8 KB
/
Copy path.facts
File metadata and controls
887 lines (809 loc) · 47.8 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
- label: facts configuration
id: .config
agent: grok
# facts
- a CLI for fact-driven development with coding agents
- works with fact sheets — flat lists of validatable atomic facts
- extreme simplicity in everything — code, format, UX
- there are no useless comments in this codebase
- codebase is DRY
## format
### file
- label: project root is the directory containing the nearest parent .git
command: grep -q "\.git" src/project.rs
tags: [implemented]
- label: fact sheets are *.facts files in the project root
command: grep -q "\.facts" src/project.rs
- label: .facts is the default/main fact sheet
command: grep -q "\"\.facts\"" src/project.rs
- naming is semantic (e.g. cli.facts for CLI-specific facts)
- every line is a heading, a fact, or blank — no other content
### parsing
- headings (# prefixed lines) are extracted first as structure
- each section body is then parsed as YAML
- label: the format is valid Markdown and valid YAML per-section
command: facts lint
### sections
- heading depth maps to hierarchy
- label: sections are addressable by path (e.g. distribution/npm)
command: grep -q "section.*path\|section_path\|--section" src/list.rs
- the CLI treats sections as first-class citizens
- label: sections are created implicitly when a fact is added to them
command: grep -q 'ensure_section_path' src/add.rs
- label: sections are removed when their last fact is deleted
command: grep -q 'cleanup_empty_sections' src/remove.rs
### facts
- label: each fact is a YAML sequence item (- prefixed)
command: grep -q "^- " .facts
- a fact can be a plain string or a mapping
- label: mapping keys: id (optional), label (required), command (optional), tags (optional)
command: grep -q 'known_keys.*=.*\["label", "command", "id", "tags"\]' src/lint.rs
- label: no other keys are allowed
command: grep -q "invalid.*key\|unknown.*key\|Invalid key" src/lint.rs
- plain string facts are equivalent to a mapping with only a label
- each fact is fully atomic and independent
### tags
- tags are freeform tokens for filtering and categorisation @word
- inline syntax: - some fact @mvp @core
- mapping syntax: tags: [mvp, core]
- both can coexist on a single fact — parsed as the union
- tags are stripped from the label before ID hashing and display
- label: write normalises tags to one location: inline for plain strings, tags key for mappings
command: grep -q "tags.join" src/writer.rs && grep -q "@{tag}" src/writer.rs
- label: lint flags mixed inline and mapping tags on the same fact
command: grep -q "mixed.*tag\|inline.*mapping.*tag\|Mixed" src/lint.rs
- label: draft, spec, and implemented are lifecycle tags that drive agent workflow
tags: [implemented]
- label: is informational — check still runs commands for tagged facts
command: ! grep -q "skip.*implemented\|filter.*implemented" src/check.rs
tags: [implemented]
- label: tag names cannot conflict with filter operators (and, or, not)
command: grep -q "conflicts with filter operator" src/add.rs
### lifecycle
- three well-known tags drive the agent workflow: @draft @spec @implemented
- the draft tag marks rough ideas that need refinement and atomization
- the spec tag marks precise and actionable facts, ready to implement
- the implemented tag marks facts that are true and backed by code
- untagged facts are ground truth — verified against the codebase
- the lifecycle is a pipeline: draft to spec to implemented
- facts-discover classifies facts by checking them against the codebase
- each agent skill owns one lifecycle transition
### identity
- every fact has a short ID derived from a hash of its label (tags stripped)
- label: IDs are 3 characters by default, extended only on collision
command: grep -q "encode_base36.*3)" src/id.rs
- IDs are computed, not stored in the file
- an explicit id key in a mapping overrides the generated ID
- IDs are stable as long as the label does not change
### validation
- facts with a command are validated programmatically by the CLI
- facts without a command are validated by the agent against the codebase
- label: commands run via $SHELL, falling back to sh if unset
command: grep -q "SHELL.*sh" src/check.rs
- label: working directory is the project root
command: grep -q "current_dir(project_root\|root)" src/check.rs
- exit 0 means the fact holds, non-zero means it fails
- label: all commands run on every check — no caching
command: ! grep -rq "cache\|Cache\|cached" src/check.rs
- label: commands run sequentially
command: ! grep -q "async\|tokio\|thread::spawn\|rayon" src/check.rs
- no timeout by default, configurable per-command via --timeout flag
- fact authors should write fast, idempotent check commands
- manual facts do not affect the exit code
- label: project is a Cargo project
command: test -f Cargo.toml
## cli
- label: the CLI is named facts
command: facts --version 2>&1 | head -1 | grep -q facts
- label: written in Rust
command: grep -q "edition" Cargo.toml
- label: distributed as a single compact binary
command: grep -q "\[\[bin\]\]\|name.*=.*\"facts\"" Cargo.toml
### subcommands
- label: bare facts (no subcommand) defaults to list
command: grep -q "Option<Command>" src/main.rs
- label: init — scaffold a .facts file, detects well-known framework/runtime combos
command: grep -q "Init" src/main.rs && test -f src/init.rs
- label: check — run all command-facts, report pass/fail/manual
command: grep -q "Check" src/main.rs && test -f src/check.rs
- label: list — show facts in file order, purely structural
command: grep -q "List" src/main.rs && test -f src/list.rs
- label: add — append a fact to a file and section
command: grep -q "Add" src/main.rs && test -f src/add.rs
- label: remove — remove a fact by ID, outputs what was removed
command: grep -q "Remove" src/main.rs && test -f src/remove.rs
- label: edit — modify a fact by ID, preserves explicit id
command: grep -q "Edit" src/main.rs && test -f src/edit.rs
- label: lint — validate that fact sheets are parseable
command: grep -q "Lint" src/main.rs && test -f src/lint.rs
- label: uninit — reverse of init, removes .facts file and agent skills
command: grep -q "Uninit" src/main.rs && test -f src/uninit.rs
### aliases
- the CLI provides five high-impact short aliases that expand before clap parsing and forward all remaining arguments
- label: ll expands to list --light — the most frequently used skim view for agents
command: grep -q "ll = list --light" src/main.rs
- ls expands to list
- rm expands to remove
- label: at <ID>... <TAG> expands to edit <ID>... --add-tag <TAG> — supports multiple IDs and arbitrary extra options after the tag
command: facts at --help 2>&1 | grep -q 'edit'
- rt <ID>... <TAG> expands to edit <ID>... --remove-tag <TAG>
- aliases are documented in the built-in `facts --help`, README, and skills/facts/SKILL.md
- label: using the short forms is recommended for agent workflows because lifecycle tagging (at/rt) and frequent listing (ll) are the highest-friction commands
### init
- creates a fact sheet in the project root (name optional, defaults to .facts)
- label: detects well-known framework/runtime combos (e.g. Cargo.toml, package.json)
command: grep -q "Cargo.toml\|package.json" src/init.rs
- scaffolds initial facts based on detected stack
- label: installs four agent skills to .agents/skills/ (facts, facts-discover, facts-implement, facts-refine)
command: grep -q "facts.*facts-discover.*facts-implement.*facts-refine\|SKILLS" src/init.rs
- label: creates .claude/skills/ symlinks for Claude Code integration
command: grep -q "claude.*skills\|\.claude" src/init.rs
- label: init is idempotent — safe to run multiple times
command: grep -q 'up to date\|skip' src/init.rs
- label: init adds a fact-driven development section to CLAUDE.md or AGENTS.md
command: grep -q 'install_agent_docs\|AGENT_MD_FILES' src/init.rs
- label: init creates AGENTS.md if no agent markdown file exists
command: grep -q 'AGENT_MD_FILES.last' src/init.rs
- init appends to existing files, writes to both if both exist
- label: the agent docs section uses HTML comment markers for surgical removal
command: grep -q 'facts:start.*facts:end\|SECTION_START\|SECTION_END' src/init.rs
### check
- label: lints all files first — aborts early on lint errors
command: grep -q 'lint::lint_content' src/check.rs && grep -q 'fix lint errors first' src/check.rs
- label: output is streamed — each result printed immediately as its command finishes
command: grep -q 'print_result' src/check.rs && grep -q 'out.flush()' src/check.rs
- each fact line shows ID and full path (file > section > label)
- label: file prefix omitted for .facts (default), shown for other files
command: grep -q 'filename == ".facts"' src/model.rs && grep -q 'display_name' src/check.rs
- passed facts show the command inline
- failed facts show command, exit code, and stderr
- manual facts are listed without detail
- label: summary shows unified totals (command + agent combined) with optional breakdown
command: grep -q 'total_passed' src/check.rs && grep -q 'total_failed' src/check.rs && grep -q 'breakdown_parts' src/check.rs
tags: [implemented]
- label: when manual facts exist, the footer tells the agent to verify each one
command: grep -q "verify each against the code" src/check.rs
- exit 0 if all command-facts pass, non-zero if any fail
- label: --tags flag accepts a boolean filter expression: "mvp and not blocked"
command: facts check --help 2>&1 | grep -q '\-\-tags'
- label: --timeout flag sets a per-command timeout
command: facts check --help 2>&1 | grep -q '\-\-timeout'
- label: --quiet flag (-q) suppresses all output, exit code only
command: cargo run -- check --help 2>&1 | grep -q -- '--quiet' && grep -q 'quiet' src/check.rs
tags: [implemented]
- label: --verbose flag (-v) shows agent prompts and raw output
command: cargo run -- check --help 2>&1 | grep -q -- '--verbose' && grep -q 'verbose' src/check.rs
tags: [implemented]
- label: filter expressions support and, or, not, and parentheses
command: grep -q "and\|or\|not\|parse_expr" src/tags.rs
### list
- label: same format as check but without pass/fail status markers
command: ! grep -q "CheckStatus" src/list.rs
- shows facts in file order
- each fact line shows ID and full path (file > section > label)
- label: file prefix omitted for .facts (default), shown for other files
command: grep -q 'filename == ".facts"' src/model.rs && grep -q 'display_name' src/list.rs
- label: supports filtering: --file, --section, --has-command, --manual
command: grep -q "file_filter" src/list.rs && grep -q "has_command" src/list.rs && grep -q "manual" src/list.rs
- label: --tags flag accepts a boolean filter expression
command: facts list --help 2>&1 | grep -q '\-\-tags'
- label: default target is all *.facts files
command: grep -q "discover_fact_files" src/project.rs
- label: --light flag shows markdown-like output with headings and bullets
command: facts list --light --help 2>&1 | grep -q '\-\-light'
tags: [implemented]
- label: light mode shows section headings in gray
command: cargo test list_light_shows_markdown_headings -- --nocapture 2>&1 | grep -q "test result: ok"
tags: [implemented]
- label: light mode shows IDs in gray parentheses after the bullet
command: cargo test list_light_shows_ids_in_parens -- --nocapture 2>&1 | grep -q "test result: ok"
tags: [implemented]
- label: light mode hides commands — light is for skimming, not detail
command: cargo test list_light_hides_commands -- --nocapture 2>&1 | grep -q "test result: ok"
tags: [implemented]
- label: light mode composes with existing filters
command: cargo test list_light_composes_with_filters -- --nocapture 2>&1 | grep -q "test result: ok"
tags: [implemented]
- label: light mode shows tags inline in different colors
command: cargo test list_light_shows_tags -- --nocapture 2>&1 | grep -q "test result: ok"
tags: [implemented]
### add
- label: defaults to .facts at the top level
command: grep -q "unwrap_or.*\".facts\"" src/add.rs
- label: --section flag targets a specific section (created if needed)
command: facts add --help 2>&1 | grep -q '\-\-section'
- label: --file flag targets a specific *.facts file (created if needed)
command: facts add --help 2>&1 | grep -q '\-\-file'
- label: --command flag adds a validation command
command: facts add --help 2>&1 | grep -q '\-\-command'
- label: --id flag sets an explicit ID
command: grep -q "\-\-id\|explicit_id" src/add.rs
- label: --tags flag adds tags
command: facts add --help 2>&1 | grep -q '\-\-tags'
- appends to the end of the target section
- file order is canonical
### remove
- label: takes an ID as argument
command: facts remove --help 2>&1 | grep -q '<ID>'
- label: outputs the removed fact
command: grep -q 'println.*removed_label' src/remove.rs
- label: silent — no confirmation prompt
command: ! grep -q "confirm\|prompt\|stdin\|read_line" src/remove.rs
### edit
- label: takes an ID as argument
command: facts edit --help 2>&1 | grep -q 'IDS'
- label: --label, --command, --id, --tags flags modify respective fields
command: facts edit --help 2>&1 | grep -q '\-\-label' && facts edit --help 2>&1 | grep -q '\-\-new-id'
- label: --add-tag appends tags without removing existing ones
command: grep -q "add.tag\|add_tag" src/edit.rs
- label: --remove-tag removes specific tags
command: grep -q "remove.tag\|remove_tag" src/edit.rs
- label: --tags conflicts with --add-tag and --remove-tag
command: grep -q "conflicts_with\|conflict" src/main.rs
- label: --remove-command strips the validation command, converting the fact to manual
command: grep -q "remove_command" src/edit.rs && cargo run -- edit --help 2>&1 | grep -q "\-\-remove-command"
tags: [implemented]
- preserves explicit id when changing other fields
- when a plain string fact gains a command or id, it becomes a mapping
- tags migrate from inline to tags key when fact becomes a mapping
### lint
- label: validates that fact sheets are parseable
command: facts lint --help 2>&1 | grep -q 'Validate'
- reports clear error messages for structural issues
- label: flags mixed inline and mapping tags on the same fact
command: grep -q "mixed\|inline.*mapping\|Mixed" src/lint.rs
- label: does not run any validation commands
command: ! grep -q "run_command\|execute.*command" src/lint.rs
- label: warns on duplicate explicit IDs across files
command: grep -q "duplicate.*id\|Duplicate" src/lint.rs
- label: warns on bare tags, CRLF endings, empty labels, unknown continuations, duplicate keys, and double-@ tags
command: grep -q "CRLF\|bare.*tag\|empty.*label\|double.*@\|@@\|continuation\|duplicate.*key" src/lint.rs
### errors
- every error state has a clear, descriptive error message
- invalid operations exit non-zero
### output
- label: polished terminal output with color and alignment
command: test -f src/color.rs && grep -q "green\|red\|yellow\|bold" src/color.rs
- label: green for pass, red for fail, yellow for manual
command: grep -q "green.*pass\|red.*fail\|yellow.*manual" src/check.rs
- label: colors disabled when output is not a TTY
command: grep -q "is_terminal\|IsTerminal" src/color.rs
- write back is byte-for-byte deterministic
- parse then write with no changes produces identical output
### uninit
- label: requires --force to delete a non-empty .facts file
command: grep -q "force\|--force" src/uninit.rs
- label: removes agent skills from .agents/skills/ and .claude/skills/
command: grep -q "agents.*skills\|claude.*skills" src/uninit.rs
- preserves named .facts files (only removes .facts)
- uninit is idempotent — safe to run multiple times
- label: uninit removes the facts section from CLAUDE.md and AGENTS.md
command: grep -q 'remove_agent_docs' src/uninit.rs
- label: uninit deletes the file if the facts section was the only content
command: grep -q 'remove_file.*path\|was only facts' src/init.rs
## architecture
- label: minimal dependencies (core 2 + TUI ratatui/crossterm + utils)
id: swd
command: test $(sed -n "/^\[dependencies\]/,/^\[/p" Cargo.toml | grep -c "^[a-z]") -le 6
tags: [implemented]
- label: clap for CLI argument parsing
command: grep -q '^clap' Cargo.toml
- label: anyhow for error handling
command: grep -q '^anyhow' Cargo.toml
- label: integration tests for all user-facing features
command: test -f tests/cli.rs
- label: advisory file locking for write operations via .facts.lock
command: test -f src/lock.rs && grep -q "facts.lock" src/lock.rs
- label: inline unit tests in source modules alongside implementation
command: grep -rq "#\[cfg(test)\]" src/
## platforms
- label: linux amd64 and arm64
command: grep -q "x86_64-unknown-linux-gnu" .github/workflows/release.yml && grep -q "aarch64-unknown-linux-gnu" .github/workflows/release.yml
- label: macOS amd64 and arm64
command: grep -q "x86_64-apple-darwin" .github/workflows/release.yml && grep -q "aarch64-apple-darwin" .github/workflows/release.yml
- label: windows amd64
command: grep -q "x86_64-pc-windows-msvc" .github/workflows/release.yml
## releasing
- bump version in Cargo.toml, commit, push, then tag and push the tag
- label: tags follow vX.Y.Z format (e.g. v0.2.0)
command: grep -q "v\*" .github/workflows/release.yml
- label: pushing a v* tag triggers the GitHub Actions release workflow
command: grep -q "v\*" .github/workflows/release.yml
- label: the workflow builds binaries for all 5 platform targets in parallel
command: test $(grep -c "target:" .github/workflows/release.yml) -ge 5
- label: binaries are packaged as tar.gz (unix) and zip (windows)
command: grep -q "tar.gz\|tar czf" .github/workflows/release.yml && grep -q "zip\|7z" .github/workflows/release.yml
- label: a GitHub release is created automatically with generated release notes
command: grep -q "create-release\|softprops.*action-gh-release\|gh release" .github/workflows/release.yml
- release artifacts are uploaded to the GitHub release
## distribution
### npm
- label: published as an npm package wrapping the prebuilt binary
command: test -f npm/package.json && grep -q "facts" npm/package.json
### pypi
- label: published as a PyPI package wrapping the prebuilt binary
command: test -f pypi/pyproject.toml && grep -q "facts" pypi/pyproject.toml
### install.sh
- label: downloads the binary from GitHub releases
command: test -f install.sh
### local
- label: local install via cargo install --path . copies the binary to ~/.local/bin/
command: test -f ~/.local/bin/facts
## skills
- label: all skills are agent-agnostic (no Claude Code-specific instructions)
command: ! grep -rq "Claude Code" skills/*/SKILL.md
- label: all installable via skills.sh
command: test -f skills.sh
- label: skill names use facts- prefix to avoid collision
command: ls skills/ | grep -q "^facts"
- label: source is in ./skills/
command: test -d skills
### facts
- teaches agents how to install and use facts
- label: source is in ./skills/facts/
command: test -f skills/facts/SKILL.md
### facts-discover
- label: scans the codebase and classifies every fact by lifecycle stage
command: grep -q "classif\|lifecycle" skills/facts-discover/SKILL.md
- label: uses subagents to scan different areas of the codebase in parallel
command: grep -q "subagent\|parallel" skills/facts-discover/SKILL.md
- label: auto-tags: vague → @draft, precise but unbuilt → @spec, code-backed → @implemented
command: grep -q "@draft" skills/facts-discover/SKILL.md && grep -q "@spec" skills/facts-discover/SKILL.md && grep -q "@implemented" skills/facts-discover/SKILL.md
- adds missing facts about the codebase as untagged ground truth
- reports what changed at the end
- label: source is in ./skills/facts-discover/
command: test -f skills/facts-discover/SKILL.md
### facts-implement
- label: operates on facts — implements them in code, then tags
command: grep -q "@spec" skills/facts-implement/SKILL.md
tags: [implemented]
- agent decides order and priority based on its understanding of the codebase
- label: uses subagents to parallelize independent work
command: grep -q "subagent\|parallel" skills/facts-implement/SKILL.md
- label: validation commands are the tests — no separate test step
command: grep -q "validation.*command.*test\|command.*test\|no separate test" skills/facts-implement/SKILL.md
- label: marks completed facts with tag using --add-tag
command: grep -q "@implemented\|--add-tag" skills/facts-implement/SKILL.md
tags: [implemented]
- reports remaining work and blockers if it cannot complete everything
- label: source is in ./skills/facts-implement/
command: test -f skills/facts-implement/SKILL.md
### facts-refine
- label: operates on facts — refines them into precise facts with the user
command: grep -q "@draft" skills/facts-refine/SKILL.md && grep -q "@spec" skills/facts-refine/SKILL.md
tags: [implemented]
- interactive — proposes changes, user decides
- every change discussed with user before applying
- label: identifies vague, compound, contradictory, and gapped facts
command: grep -q "Vague\|Compound\|Contradict\|Gap" skills/facts-refine/SKILL.md
- label: source is in ./skills/facts-refine/
command: test -f skills/facts-refine/SKILL.md
# cli
## get
- label: get — look up a single fact by ID, display label, section, file, command, and tags
command: grep -q "Get" src/main.rs && test -f src/get.rs
tags: [implemented]
- label: get exits non-zero when the ID is not found
command: grep -q "no fact found with ID" src/get.rs
tags: [implemented]
- label: get shows section path for facts in sections
command: grep -q "section_path.join" src/get.rs
tags: [implemented]
- label: get shows tags when present
command: grep -q "tags:" src/get.rs && grep -q "tag_list" src/get.rs
tags: [implemented]
- label: get shows command when present
command: grep -q "command:" src/get.rs && grep -q "fact.command" src/get.rs
tags: [implemented]
- label: get shows file path
command: grep -q "file:" src/get.rs && grep -q "sheet.filename" src/get.rs
tags: [implemented]
- label: get shows 'verification: manual (no command)' for facts without a command
command: grep -q "verification: manual" src/get.rs
tags: [implemented]
## subcommands
- label: move — relocate a fact by ID to a different section or file
command: grep -q "Move" src/main.rs && test -f src/move_fact.rs
tags: [implemented]
- label: get — look up a single fact by ID
command: grep -q "Get" src/main.rs && test -f src/get.rs
tags: [implemented]
- label: skills subcommand exists
command: cargo run -- skills --help 2>/dev/null
tags: [implemented]
- update — self-update the facts binary to the latest release @implemented
## move
- label: moves a fact to a different section within the same file
command: grep -q "move.*section\|section.*move\|target_section" src/move_fact.rs
tags: [implemented]
- label: moves a fact to a different file
command: grep -q "target_file\|file.*move\|cross.file" src/move_fact.rs
tags: [implemented]
- label: creates target section if it does not exist
command: grep -q "ensure_section_path\|add_to_section\|section.*creat" src/move_fact.rs
tags: [implemented]
- label: cleans up empty sections after move
command: grep -q "cleanup_empty_sections\|empty.*section" src/move_fact.rs
tags: [implemented]
- label: retains all fact properties (label, command, tags, explicit ID)
command: grep -q "explicit_id\|\.tags\|\.command\|\.label" src/move_fact.rs
tags: [implemented]
- label: requires at least one of --section or --file
command: grep -q "at least one\|requires.*section.*file\|must provide" src/move_fact.rs
tags: [implemented]
- label: move deletes the source .facts file if the last fact is moved out leaving it empty
command: grep -q "std::fs::remove_file" src/move_fact.rs
tags: [implemented]
## add
- label: rejects absolute paths and .. traversal for --file, allows subdirectory paths
command: grep -q 'must not contain' src/add.rs && grep -q 'resolve_file_arg' src/add.rs
- label: section depth cannot exceed 6 levels
command: grep -q 'parts.len() > 6' src/add.rs
### output
- label: add prints the new fact's assigned ID to stdout
command: grep -q 'println.*id' src/main.rs
tags: [implemented]
- label: the printed ID matches what list shows for that fact
command: grep -q "println.*id" src/add.rs || grep -q "println.*{id}" src/main.rs
tags: [implemented]
- label: add with --id prints the explicit ID
command: grep -q "explicit_id\|explicit.*id\|opts\.id" src/add.rs && grep -q "println.*{id}" src/main.rs
tags: [implemented]
## list/check output
### tags display
- label: list appends lifecycle tags (dimmed) to each fact line
command: grep -q "fact\.tags\|tag.*dim\|dim.*tag" src/list.rs
tags: [implemented]
- label: check appends lifecycle tags (dimmed) to each fact line
command: grep -q "fact\.tags\|tag.*dim\|dim.*tag" src/check.rs
tags: [implemented]
- label: tags are shown unconditionally — no flag needed
command: ! grep -q "show.tags\|display.tags\|--show-tags" src/list.rs
tags: [implemented]
## fmt
- label: fmt subcommand parses, validates (lint), and normalizes all .facts files
command: grep -q "Fmt" src/main.rs && test -f src/fmt.rs
tags: [implemented]
- label: fmt validates before writing — aborts on lint errors
command: grep -q "lint\|validate" src/fmt.rs
tags: [implemented]
- label: fmt writes back deterministic output (parse + write)
command: grep -q "writer::write\|write(" src/fmt.rs
tags: [implemented]
## edit
- label: batch edit cannot use --label or --new-id with multiple IDs
command: grep -q 'cannot be used with multiple IDs' src/edit.rs
- label: edit prints the IDs of the edited facts (the ones provided by the user) on success
command: grep -q "for id in &opts.target_ids" src/edit.rs
tags: [implemented]
- label: edit is not silent on success — it prints the target IDs of modified facts
command: grep -q "for id in &opts.target_ids" src/edit.rs
tags: [implemented]
### batch
- label: edit accepts multiple IDs as positional arguments
command: grep -q "ids.*Vec\|num_args" src/main.rs
tags: [implemented]
- label: batch edit applies the same operation to all targeted facts
command: grep -q "for.*id\|ids\.iter\|for.*target" src/edit.rs
tags: [implemented]
- label: batch edit uses fail-all-before-writing semantics
command: grep -q "resolve.*all\|validate.*before\|all_locations\|resolved" src/edit.rs
tags: [implemented]
- label: batch edit groups mutations by file for cross-file operations
command: grep -q "group.*file\|by_file\|sheet_idx" src/edit.rs
tags: [implemented]
## list
- label: section filter is case-insensitive contiguous sub-path matching
command: grep -q 'eq_ignore_ascii_case' src/list.rs
- label: --search flag accepts a boolean expression matched case-insensitively against section path, label, and tags combined
command: cargo run -- list --search "update and cli" 2>&1 | grep -q "update"
tags: [implemented]
- label: --search reuses the same boolean expression parser as --tags (and, or, not, parentheses)
command: grep -q "matches_search_expr" src/tags.rs && grep -q "validate_tag_expr" src/list.rs
tags: [implemented]
- label: --depth N limits output to facts within N levels of section nesting from root
command: cargo run -- list --depth 1 2>&1 | grep -q "."
tags: [implemented]
- label: --search and --depth compose with existing filters (--section, --tags, --file, --manual) via AND
command: cargo run -- list --search "init" --section "cli" --depth 2 2>&1 | grep -q "init"
tags: [implemented]
- label: list shows a dim indicator per fact: checkmark for command facts, question mark for manual facts
command: grep -q "cmd_indicator" src/list.rs && grep -q "has_command" src/list.rs
tags: [implemented]
## check
- label: timeout exit code is 124
command: grep -q '(124,' src/check.rs
- label: TTY shows pending fact ID while command runs, replaced by result on completion
command: grep -q 'is_tty' src/check.rs && grep -q '\\r\\x1b' src/check.rs
- label: progress indicator is suppressed in non-TTY environments
command: grep -q 'is_tty' src/check.rs && grep -q 'color::enabled' src/check.rs
- label: --search flag filters check output using the same boolean expression as list --search
command: cargo run -- check --search "update" --tags "implemented" 2>&1 | grep -q "update"
tags: [implemented]
- label: --depth N limits section nesting depth in check output from root
command: cargo run -- check --depth 1 2>&1 | grep -q "."
tags: [implemented]
- manual facts footer in check output uses correct singular/plural grammar ('1 fact has' / 'N facts have') @implemented
- manual facts footer wording is precise ('verify each against the code and state PASS or FAIL with a one-line reason') @implemented
- label: check --id flag filters to specific fact IDs, composable with all other filters
command: cargo run -- check --help 2>&1 | grep -q "\-\-id"
tags: [implemented]
- label: check --section flag filters by section path, case-insensitive sub-path match, consistent with list --section
command: cargo run -- check --help 2>&1 | grep -q "\-\-section"
tags: [implemented]
- label: check --strict flag treats unverified manual facts as failures for non-zero exit code
command: cargo run -- check --help 2>&1 | grep -q "\-\-strict"
tags: [implemented]
### agent
- label: --agent flag accepts a shell command template with %p as the prompt placeholder, or a preset name
command: cargo run -- check --agent "echo %p" --help 2>&1 | grep -q 'agent'
tags: [implemented]
- label: --agent presets include claude, codex, pi, droid, opencode, mi with correct templates
command: grep -q '"claude"' src/agent.rs && grep -q '"codex"' src/agent.rs && grep -q '"pi"' src/agent.rs && grep -q '"droid"' src/agent.rs && grep -q '"opencode"' src/agent.rs && grep -q '"mi"' src/agent.rs
tags: [implemented]
- label: --agent operates only on manual facts remaining after --tags, --search, and other filters
command: grep -q 'manual_facts' src/check.rs && grep -q 'opts.tags_expr' src/check.rs && grep -q 'opts.search_expr' src/check.rs
tags: [implemented]
- label: manual facts are batched into a single agent invocation, default 10 facts per batch
command: grep -q 'batch_size' src/check.rs && grep -q 'step_by(batch_size)' src/check.rs
tags: [implemented]
- label: --batch-size N overrides the default batch size of 10 for --agent invocations
command: cargo run -- check --help 2>&1 | grep -q 'batch-size'
tags: [implemented]
- label: '%p' is shell-escaped and includes fact-verdict XML format instructions, fact IDs with labels and section paths
command: grep -q 'shell_escape' src/agent.rs && grep -q 'fact-verdict' src/agent.rs && grep -q 'build_prompt' src/agent.rs
tags: [implemented]
- label: agent output is parsed for fact-verdict XML tags with PASS and FAIL verdicts
command: grep -q 'find_fact_verdicts' src/agent.rs && grep -q 'fact-verdict' src/agent.rs
tags: [implemented]
- label: parser strips ANSI escape codes and markdown fences before extracting fact-verdict tags
command: grep -q 'strip_ansi_and_fences' src/agent.rs
tags: [implemented]
- label: when a fact ID appears in multiple fact-verdict tags, last match wins
command: grep -q 'verdict_map.insert' src/agent.rs && cargo test test_parse_verdicts_last_wins -- --nocapture 2>&1 | grep -q 'ok'
tags: [implemented]
- label: facts with no matching fact-verdict tag in agent output are reported as unresolved in the summary
command: grep -q 'AgentUnresolved' src/check.rs && grep -q 'unresolved' src/check.rs
tags: [implemented]
- label: agent process failure (non-zero exit or spawn error) means all facts in that batch are unresolved
command: grep -q 'AgentVerdict::Unresolved' src/agent.rs && cargo test test_parse_verdicts_unresolved -- --nocapture 2>&1 | grep -q 'ok'
tags: [implemented]
- label: summary line includes agent-verified counts (passed, failed, unresolved)
command: grep -q 'agent_passed' src/check.rs && grep -q 'agent_failed' src/check.rs && grep -q 'agent_unresolved' src/check.rs
tags: [implemented]
- label: unresolved agent facts cause non-zero exit, same as command failures
command: grep -q 'agent_unresolved > 0' src/check.rs
tags: [implemented]
- label: bare facts check does not invoke an agent; manual facts stay manual even when .config agent is set
command: cargo test check_without_agent_flag_skips_config_agent -- --nocapture 2>&1 | grep -q 'ok'
tags: [implemented]
- label: with --agent passed, bare value uses .config agent then PATH preset auto-detect in order
command: grep -q 'auto_detect_agent' src/agent.rs && cargo test check_agent_config_override -- --nocapture 2>&1 | grep -q 'ok'
tags: [implemented]
- label: explicit --agent value overrides .config agent
command: cargo test check_agent_cli_flag_overrides_config_in_multi_file -- --nocapture 2>&1 | grep -q 'ok'
tags: [implemented]
- label: no-agent flag skips agent verification even when --agent is passed
command: cargo run -- check --no-agent --help 2>&1 | grep -q 'no-agent'
tags: [implemented]
- label: check prints agent announcement before starting agent phase: agent name and manual fact count
command: grep -q 'Verifying.*manual facts with' src/check.rs
tags: [implemented]
- label: check prints per-batch progress on TTY with carriage-return overwrite
command: grep -q 'batch {batch_num}/{total_batches}' src/check.rs
tags: [implemented]
- label: resolve_agent returns the detected agent display name alongside the template
command: grep -q 'pub struct ResolvedAgent' src/agent.rs
tags: [implemented]
- label: check summary shows agent hint when manual facts exist and no agent is configured
command: grep -q "Use --agent to enable agent verification" src/check.rs
tags: [implemented]
## skills
- skills list shows all installed skills with name and description @implemented
- skills show prints the full content of a skill by name @implemented
- skills update re-installs skills from the binary without re-running init @implemented
- bare facts skills defaults to list @implemented
- skills show reads from baked-in skills, works without init @implemented
- skills help text tells agents they can read skills via CLI before installing @implemented
## update
- update detects install method from binary path: npm prefix → npm, pipx/pip path → pipx, else → direct curl download @implemented
- direct update: queries GitHub API for latest version, downloads platform binary via curl, replaces in-place @implemented
- update prints old version → new version on success @implemented
- update is a no-op when already on the latest version @implemented
- update exits non-zero if the download or replacement fails @implemented
- update uses the same platform targets and archive formats as install.sh @implemented
- npm update: runs npm update -g @avcodes/facts @implemented
- pipx update: runs pipx upgrade facts-cli @implemented
- direct update requires curl or wget on PATH @implemented
- label: update runs init after upgrading when run inside a facts project
command: grep -q "init::run" src/update.rs
tags: [implemented]
## init
- label: init replaces the agent docs section between markers when content differs from the binary's embedded version
command: grep -q "existing_section == AGENT_DOCS_SECTION" src/init.rs
tags: [implemented]
- .claude/settings.local.json is gitignored (personal Claude Code sandbox permissions must never be committed) @implemented
## remove
- label: remove deletes the .facts file if removing the last fact leaves it empty (symmetric to empty section cleanup)
command: grep -q "std::fs::remove_file" src/remove.rs
tags: [implemented]
## tui
- label: There is a Ratatui-based TUI, launched via the 'tui' subcommand in the clap CLI
id: kot
tags: [implemented]
- label: TUI supports full facts CRUD operations equivalent to the CLI: list/browse (with live search, tags boolean expr, and section path filters), add, remove, edit (label, command, tags replace, new-id), get, check (live streaming); move supported via dedicated m key opening a move form
id: hj2
tags: [implemented]
- label: TUI uses a custom color theme: dark background, neon green accents for pass and accent elements, purple/violet borders and muted text, cyan highlights, red for errors, clean Unicode box-drawing borders, consistent padding matching the CLI output style
id: qcl
tags: [implemented]
- label: TUI subcommand is defined as 'Tui' variant in the clap Command enum in src/main.rs (launched with no additional args)
id: 5hm
tags: [implemented]
- label: TUI declares 'mod tui;' in src/main.rs and calls tui::run() in the main match arm for Command::Tui
id: udj
tags: [implemented]
- label: TUI implementation depends on ratatui and crossterm crates (added to Cargo.toml [dependencies]) for TUI rendering and event handling
id: uni
tags: [implemented]
- label: TUI main browse screen renders a virtualized table of facts (columns: ID, full section path, truncated label, tags, has-command indicator ✓, current check status icon)
id: npw
tags: [list, implemented]
- label: TUI supports live filters: section path (subpath match), tags boolean expr, search text expr; filter bar updates table instantly on keystroke
id: gva
tags: [implemented]
- label: TUI reuses core modules without duplication: parser, writer, model, project, locate, id, tags (for expr parsing), and check runner logic
id: 828
tags: [implemented]
- label: TUI uses vim-style keybindings (j/k arrows for nav, / focus filter, a add, e edit, d delete, c check, r refresh, ? help, q/esc quit, m move, x check-single, t tree) plus mouse scroll for navigation
id: 6tb
tags: [implemented]
- label: TUI add operation opens a centered modal form with labeled inputs for: label (required, focused first), section, command, tags (comma-separated), explicit id, target file; supports tab navigation, validation, submit on Ctrl-S or last-field Enter
id: nk9
tags: [implemented]
- label: TUI edit supports editing selected fact(s) via form prefilled from current values, including tags (full replace via csv field) and setting new-id (preserves explicit id when present); add-tag/remove-tag semantics available via re-entering adjusted tags list
id: mfb
tags: [implemented]
- label: TUI check mode runs validation in a non-blocking worker thread using std::sync::mpsc, streams PASS/FAIL/? updates into the table rows live (color coded green/red/yellow), shows running summary counts on completion and allows cancelling with C
id: ipp
tags: [implemented]
- label: TUI properly initializes terminal: raw mode via crossterm, alternate screen buffer, optional mouse capture; installs panic hook that restores terminal state before re-panicking
id: pcy
tags: [implemented]
- label: TUI restores terminal state (raw mode off, main screen, cursor visible, mouse disabled) on normal quit and panics (via panic hook); error exits from the event loop may skip cleanup since there is no Drop guard
id: 25x
tags: [implemented]
- label: TUI theme is defined in a dedicated module with custom colors: dark background (Rgb 10,10,18), neon green accent/pass (Rgb 0,255,136), purple borders and muted text, cyan highlights, red for errors, clean borders using box-drawing chars
id: v4f7
tags: [implemented]
- label: TUI supports multiple .facts files: merges all discovered files into a single table view with per-row filename indicators; project root shown in status bar
id: 6ul
tags: [implemented]
- label: TUI has a centered modal help overlay (triggered by ?) showing keybindings table and styled footer; esc or q returns to previous mode
id: 7kx
tags: [implemented]
- label: TUI status bar always visible: project root | active filters summary | fact count (filtered/total) | selected ID | current mode | last action result
id: 9u4
tags: [implemented]
- label: TUI handles very large fact sheets (e.g. this project's .facts) with responsive filtered table rendering and navigation; no full reload on every filter (rebuild only on mutation)
id: qgj
tags: [implemented]
- label: TUI gracefully degrades when no .git found (shows warning banner 'no project root — operating in cwd', still allows TUI on local .facts files or explicit paths if supported)
id: 46g
tags: [implemented]
- label: TUI check and other long ops use background thread + channel so main UI event loop (crossterm poll with 50ms timeout) stays responsive; no async crate used, preserving 'no async' constraint in core
id: 7ct
tags: [implemented]
- label: TUI mutations (add/edit/remove/move) call into or replicate the exact logic of add.rs/edit.rs/remove.rs/move_fact.rs + writer so resulting .facts files, ID assignment, section cleanup, and raw preservation are identical to CLI
id: 7tb
tags: [implemented]
- label: TUI adds ratatui, crossterm, unicode-width, and serde_json dependencies to Cargo.toml (6 total with clap and anyhow); the minimal dependencies architecture fact uses -le 6
id: y12
tags: [implemented]
### check
- TUI check does not invoke an agent by default; manual facts stay ? unless agent verification is enabled in the TUI @implemented
- when TUI agent verification is enabled, resolution matches bare facts check --agent: .config agent then PATH auto-detect @implemented
- TUI shows a spinner on each manual fact while the agent batch is in flight, replacing the ? marker @implemented
- TUI replaces spinner with PASS/FAIL result as each fact-verdict is parsed from agent output @implemented
## config
- facts config subcommand manages the .config fact: get, set, show, ls, rm @implemented
- facts config set <key> <value> creates or updates a key in .config @implemented
- facts config get <key> prints a single config value @implemented
- facts config show prints all config key-value pairs (alias: ls) @implemented
- facts config rm <key> removes a key from .config @implemented
- facts config set creates the .config fact in .facts if it does not exist @implemented
# distribution
## npm
- label: npm package includes a placeholder bin/facts stub so npm creates the global symlink before postinstall downloads the real binary
command: test -f npm/bin/facts && head -1 npm/bin/facts | grep -q node
- label: postinstall script downloads the platform-specific binary from GitHub releases
command: grep -q 'github.com.*releases.*download' npm/install.js
- label: published under the avcodes npm scope
command: grep -q '@avcodes/facts' npm/package.json
## pypi
- label: custom build_py downloads the platform-specific binary from GitHub releases during wheel build
command: grep -q 'github.com.*releases.*download' pypi/setup.py && grep -q 'BuildPyWithBinary' pypi/setup.py
- label: PyPI package name is facts-cli
command: grep -q 'name = "facts-cli"' pypi/pyproject.toml
- label: README recommends pipx (not pip) for Python installs
command: grep -q 'pipx install' README.md && ! grep -q 'pip install' README.md
## install.sh
- label: rejects Windows and suggests npm or pip instead
command: grep -q 'MINGW\|MSYS\|CYGWIN' install.sh && grep -q 'npm\|pip' install.sh
# releasing
- label: Linux targets build on ubuntu-22.04 for glibc 2.35 compatibility with Debian bookworm-based containers
command: grep -A1 'linux-gnu' .github/workflows/release.yml | grep -q 'ubuntu-22.04'
# architecture
- label: skills are embedded in the binary via include_str!() macros
command: grep -q 'include_str!' src/init.rs
- label: dev dependencies: tempfile, assert_cmd, predicates
command: grep -q 'tempfile' Cargo.toml && grep -q 'assert_cmd' Cargo.toml && grep -q 'predicates' Cargo.toml
# format
## file
- label: fact file discovery sorts .facts first, then alphabetically
command: grep -q 'sort\|\.facts.*first\|display_name' src/project.rs
## tags
- label: tag filter expressions using --tags match case-insensitively (for consistency with --search)
command: grep -q "eq_ignore_ascii_case" src/tags.rs
tags: [implemented]
## config
- a fact with explicit id ".config" is reserved as tool configuration, not a project truth claim @implemented
- the .config fact label must be "facts configuration" @implemented
- the .config fact allows arbitrary key-value pairs beyond the standard id/label/command/tags keys @implemented
- dot-prefixed IDs (starting with .) are reserved for tool internals — only .config is defined @implemented
- facts list excludes .config by default — shown only with --all @implemented
- facts check skips the .config fact — it is not a verifiable claim @implemented
- facts get .config shows all configuration key-value pairs @implemented
# skills
## facts
- label: the core skill documents correct install commands (npm: pipx: facts-cli, curl)
command: grep -q '@avcodes/facts' skills/facts/SKILL.md && grep -q 'pipx install' skills/facts/SKILL.md
tags: [avcodes/facts,]
- label: the core skill explicitly warns agents not to use npx facts
command: grep -q 'never.*npx facts' skills/facts/SKILL.md
- label: the facts skill documents agent verification: verdict XML format, presets, resolution chain, and config
command: grep -q "fact-verdict" skills/facts/SKILL.md && grep -q "Resolution chain" skills/facts/SKILL.md && grep -q "config set agent" skills/facts/SKILL.md
tags: [implemented]
# domain
- a FactSheet is a parsed .facts file containing an optional preamble and a list of Sections @implemented
- a Section is a heading-delimited group of Facts; sections nest via heading depth (e.g. cli/check) @implemented
- a Fact is an atomic, validatable truth statement with a label, optional command, optional tags, and an ID @implemented
- a FactSheet contains Sections; Facts belong to exactly one Section @implemented
- check runs the command for each fact that has one and records passed/failed/manual status @implemented
- manual facts (no command) are verified by a human or agent reading the code and stating PASS or FAIL with a one-line reason @implemented
# documentation
## wiki
- label: the facts.wiki directory contains a _Sidebar.md for navigation that links to all major articles
command: test -f ../facts.wiki/_Sidebar.md
tags: [implemented]
- label: Home.md in facts.wiki serves as the landing page and prominently features the terms 'fact-driven development' and 'facts cli' in headings and intro
command: test -f ../facts.wiki/Home.md && grep -q "fact-driven development" ../facts.wiki/Home.md && grep -q "facts cli" ../facts.wiki/Home.md
tags: [implemented]
- label: Fact-Driven-Development.md exists in the wiki and explains the methodology with code examples and comparison to traditional specs
command: test -f ../facts.wiki/Fact-Driven-Development.md
tags: [implemented]
- label: Fluffy-Specs.md (or equivalent) exists and positions facts as the antidote for vague/fluffy specifications used with AI coding agents
command: test -f ../facts.wiki/Fluffy-Specs.md || test -f ../facts.wiki/Antidote-for-Fluffy-Specs.md
tags: [implemented]
- label: Facts-File-Format.md provides a complete, searchable reference to the .facts file format, sections, facts, tags, IDs, and YAML/Markdown syntax
command: test -f ../facts.wiki/Facts-File-Format.md
tags: [implemented]
- label: Facts-Check.md is dedicated to the 'facts check' command, its output (pass/fail/manual), filters, CI usage, and timeout features
command: test -f ../facts.wiki/Facts-Check.md
tags: [implemented]
- label: Facts-Lifecycle.md documents the tags and the agent-driven workflow using facts-discover, facts-refine and facts-implement skills
command: test -f ../facts.wiki/Facts-Lifecycle.md
tags: [implemented]
- label: Facts-with-AI-Agents.md (or Claude) explains integration with Claude Code, Cursor, and other LLM agents, including the bundled skills and init workflow
command: test -f ../facts.wiki/Facts-with-AI-Agents.md || test -f ../facts.wiki/Using-Facts-with-AI-Agents.md
tags: [implemented]
- label: Facts-CLI-Reference.md lists all subcommands, short aliases (ll, at, rt, rm), filters (--tags, --search, --section, --depth), and TUI entrypoint
command: test -f ../facts.wiki/Facts-CLI-Reference.md
tags: [implemented]
- label: Installing-Facts.md (or integrated in home) covers all distribution methods: curl install.sh, npm pipx facts-cli, and cargo
command: test -f ../facts.wiki/Installing-Facts.md || grep -q "Installing Facts" ../facts.wiki/Home.md
tags: [implemented]
## readme
- label: the README includes a nicely formatted link to the GitHub wiki at https://github.com/av/facts/wiki with the index and a few key pages (Fact-Driven Development, File Format, CLI Reference, etc.)
command: grep -q "github.com/av/facts/wiki" README.md
tags: [implemented]