fusion-cli 1.1.0__tar.gz → 1.2.0__tar.gz

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.
Files changed (76) hide show
  1. {fusion_cli-1.1.0 → fusion_cli-1.2.0}/PKG-INFO +1 -1
  2. {fusion_cli-1.1.0 → fusion_cli-1.2.0}/pyproject.toml +1 -1
  3. {fusion_cli-1.1.0 → fusion_cli-1.2.0}/skills/fusion-analyst/SKILL.md +6 -3
  4. {fusion_cli-1.1.0 → fusion_cli-1.2.0}/skills/fusion-analyst/references/fusion-conventions.md +13 -1
  5. {fusion_cli-1.1.0 → fusion_cli-1.2.0}/skills/fusion-intake/SKILL.md +6 -3
  6. {fusion_cli-1.1.0/skills/fusion-librarian → fusion_cli-1.2.0/skills/fusion-intake}/references/fusion-conventions.md +13 -1
  7. {fusion_cli-1.1.0 → fusion_cli-1.2.0}/skills/fusion-librarian/SKILL.md +6 -3
  8. {fusion_cli-1.1.0/skills/fusion-planner → fusion_cli-1.2.0/skills/fusion-librarian}/references/fusion-conventions.md +13 -1
  9. {fusion_cli-1.1.0 → fusion_cli-1.2.0}/skills/fusion-planner/SKILL.md +6 -3
  10. {fusion_cli-1.1.0 → fusion_cli-1.2.0}/skills/fusion-planner/references/create-activity.md +21 -0
  11. {fusion_cli-1.1.0/skills/fusion-intake → fusion_cli-1.2.0/skills/fusion-planner}/references/fusion-conventions.md +13 -1
  12. fusion_cli-1.2.0/src/fusion/__init__.py +8 -0
  13. {fusion_cli-1.1.0 → fusion_cli-1.2.0}/src/fusion/checker.py +29 -2
  14. {fusion_cli-1.1.0 → fusion_cli-1.2.0}/src/fusion/cli.py +55 -8
  15. {fusion_cli-1.1.0 → fusion_cli-1.2.0}/src/fusion/scaffold.py +11 -0
  16. {fusion_cli-1.1.0 → fusion_cli-1.2.0}/src/fusion/views.py +12 -2
  17. {fusion_cli-1.1.0 → fusion_cli-1.2.0}/tests/test_checker_warnings.py +30 -0
  18. {fusion_cli-1.1.0 → fusion_cli-1.2.0}/tests/test_cli.py +42 -2
  19. {fusion_cli-1.1.0 → fusion_cli-1.2.0}/tests/test_hub_bucket.py +2 -2
  20. {fusion_cli-1.1.0 → fusion_cli-1.2.0}/tests/test_ledger.py +1 -1
  21. {fusion_cli-1.1.0 → fusion_cli-1.2.0}/tests/test_roundtrip.py +1 -1
  22. {fusion_cli-1.1.0 → fusion_cli-1.2.0}/tests/test_scaffold.py +10 -0
  23. {fusion_cli-1.1.0 → fusion_cli-1.2.0}/tests/test_views.py +11 -7
  24. {fusion_cli-1.1.0 → fusion_cli-1.2.0}/uv.lock +1 -1
  25. fusion_cli-1.1.0/src/fusion/__init__.py +0 -3
  26. {fusion_cli-1.1.0 → fusion_cli-1.2.0}/.gitignore +0 -0
  27. {fusion_cli-1.1.0 → fusion_cli-1.2.0}/README.md +0 -0
  28. {fusion_cli-1.1.0 → fusion_cli-1.2.0}/hatch_build.py +0 -0
  29. {fusion_cli-1.1.0 → fusion_cli-1.2.0}/skills/fusion-analyst/references/assess.md +0 -0
  30. {fusion_cli-1.1.0 → fusion_cli-1.2.0}/skills/fusion-analyst/references/compare.md +0 -0
  31. {fusion_cli-1.1.0 → fusion_cli-1.2.0}/skills/fusion-analyst/references/export.md +0 -0
  32. {fusion_cli-1.1.0 → fusion_cli-1.2.0}/skills/fusion-analyst/references/report.md +0 -0
  33. {fusion_cli-1.1.0 → fusion_cli-1.2.0}/skills/fusion-analyst/scripts/export.py +0 -0
  34. {fusion_cli-1.1.0 → fusion_cli-1.2.0}/skills/fusion-intake/references/convert.md +0 -0
  35. {fusion_cli-1.1.0 → fusion_cli-1.2.0}/skills/fusion-intake/references/delivery.md +0 -0
  36. {fusion_cli-1.1.0 → fusion_cli-1.2.0}/skills/fusion-intake/references/gate.md +0 -0
  37. {fusion_cli-1.1.0 → fusion_cli-1.2.0}/skills/fusion-intake/scripts/convert.py +0 -0
  38. {fusion_cli-1.1.0 → fusion_cli-1.2.0}/skills/fusion-intake/scripts/gate.py +0 -0
  39. {fusion_cli-1.1.0 → fusion_cli-1.2.0}/skills/fusion-intake/tests/conftest.py +0 -0
  40. {fusion_cli-1.1.0 → fusion_cli-1.2.0}/skills/fusion-intake/tests/make_fixtures.py +0 -0
  41. {fusion_cli-1.1.0 → fusion_cli-1.2.0}/skills/fusion-intake/tests/test_convert.py +0 -0
  42. {fusion_cli-1.1.0 → fusion_cli-1.2.0}/skills/fusion-intake/tests/test_gate.py +0 -0
  43. {fusion_cli-1.1.0 → fusion_cli-1.2.0}/skills/fusion-intake/tests/test_integration.py +0 -0
  44. {fusion_cli-1.1.0 → fusion_cli-1.2.0}/skills/fusion-librarian/references/archive.md +0 -0
  45. {fusion_cli-1.1.0 → fusion_cli-1.2.0}/skills/fusion-librarian/references/create.md +0 -0
  46. {fusion_cli-1.1.0 → fusion_cli-1.2.0}/skills/fusion-librarian/references/cross-reference.md +0 -0
  47. {fusion_cli-1.1.0 → fusion_cli-1.2.0}/skills/fusion-librarian/references/promote.md +0 -0
  48. {fusion_cli-1.1.0 → fusion_cli-1.2.0}/skills/fusion-librarian/references/query.md +0 -0
  49. {fusion_cli-1.1.0 → fusion_cli-1.2.0}/skills/fusion-librarian/references/reflect.md +0 -0
  50. {fusion_cli-1.1.0 → fusion_cli-1.2.0}/skills/fusion-librarian/references/restructure.md +0 -0
  51. {fusion_cli-1.1.0 → fusion_cli-1.2.0}/skills/fusion-librarian/references/tag.md +0 -0
  52. {fusion_cli-1.1.0 → fusion_cli-1.2.0}/skills/fusion-librarian/scripts/link-repair.py +0 -0
  53. {fusion_cli-1.1.0 → fusion_cli-1.2.0}/skills/fusion-librarian/tests/conftest.py +0 -0
  54. {fusion_cli-1.1.0 → fusion_cli-1.2.0}/skills/fusion-librarian/tests/test_link_repair.py +0 -0
  55. {fusion_cli-1.1.0 → fusion_cli-1.2.0}/skills/fusion-planner/references/close.md +0 -0
  56. {fusion_cli-1.1.0 → fusion_cli-1.2.0}/skills/fusion-planner/references/horizon.md +0 -0
  57. {fusion_cli-1.1.0 → fusion_cli-1.2.0}/src/fusion/bucket.py +0 -0
  58. {fusion_cli-1.1.0 → fusion_cli-1.2.0}/src/fusion/document.py +0 -0
  59. {fusion_cli-1.1.0 → fusion_cli-1.2.0}/src/fusion/hub.py +0 -0
  60. {fusion_cli-1.1.0 → fusion_cli-1.2.0}/src/fusion/indexer.py +0 -0
  61. {fusion_cli-1.1.0 → fusion_cli-1.2.0}/src/fusion/ledger.py +0 -0
  62. {fusion_cli-1.1.0 → fusion_cli-1.2.0}/src/fusion/manifest.py +0 -0
  63. {fusion_cli-1.1.0 → fusion_cli-1.2.0}/src/fusion/setup.py +0 -0
  64. {fusion_cli-1.1.0 → fusion_cli-1.2.0}/tests/__init__.py +0 -0
  65. {fusion_cli-1.1.0 → fusion_cli-1.2.0}/tests/conftest.py +0 -0
  66. {fusion_cli-1.1.0 → fusion_cli-1.2.0}/tests/test_build_payload.py +0 -0
  67. {fusion_cli-1.1.0 → fusion_cli-1.2.0}/tests/test_checker_errors.py +0 -0
  68. {fusion_cli-1.1.0 → fusion_cli-1.2.0}/tests/test_cli_encoding.py +0 -0
  69. {fusion_cli-1.1.0 → fusion_cli-1.2.0}/tests/test_cli_setup.py +0 -0
  70. {fusion_cli-1.1.0 → fusion_cli-1.2.0}/tests/test_cli_version.py +0 -0
  71. {fusion_cli-1.1.0 → fusion_cli-1.2.0}/tests/test_document.py +0 -0
  72. {fusion_cli-1.1.0 → fusion_cli-1.2.0}/tests/test_indexer.py +0 -0
  73. {fusion_cli-1.1.0 → fusion_cli-1.2.0}/tests/test_line_endings.py +0 -0
  74. {fusion_cli-1.1.0 → fusion_cli-1.2.0}/tests/test_setup_agents.py +0 -0
  75. {fusion_cli-1.1.0 → fusion_cli-1.2.0}/tests/test_setup_core.py +0 -0
  76. {fusion_cli-1.1.0 → fusion_cli-1.2.0}/tests/test_skill_family.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fusion-cli
3
- Version: 1.1.0
3
+ Version: 1.2.0
4
4
  Summary: The Fusion reference CLI — the notary of the Fusion Convention.
5
5
  Author: Bluewaves Boutique
6
6
  License-Expression: MIT
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "fusion-cli"
3
- version = "1.1.0"
3
+ version = "1.2.0"
4
4
  description = "The Fusion reference CLI — the notary of the Fusion Convention."
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.11"
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: fusion-analyst
3
- description: "The Fusion analyst — deliverables out of the bucket. Four gears: report (multi-section analysis from library + activities — the default), assess (scored assessment with explicit criteria and scale), compare (side-by-side matrix with deltas and red flags), export (CSV/JSON/XLSX data extracts). Everything lands in output/ as a summary-first document, cites every source path in data_sources, and ships signed in the ledger. Use for 'report on', 'analyze and write up', 'brief', 'assess', 'evaluate', 'score', 'compare X and Y', 'export as csv/excel'. For searching without producing a deliverable use fusion-librarian's query; for new documents of record use fusion-librarian's create. Applies only inside a Fusion bucket — a directory tree with BUCKET.md and LEDGER.md at its root; if there is no such bucket in play, this skill does not apply."
3
+ description: "The Fusion analyst — deliverables out of the bucket. Four gears: report (multi-section analysis from library + activities — the default), assess (scored assessment with explicit criteria and scale), compare (side-by-side matrix with deltas and red flags), export (CSV/JSON/XLSX data extracts). Everything lands in output/ as a summary-first document, cites every source path in data_sources, and ships signed in the ledger. Use for 'report on', 'analyze and write up', 'brief', 'assess', 'evaluate', 'score', 'compare X and Y', 'export as csv/excel'. For searching without producing a deliverable use fusion-librarian's query; for new documents of record use fusion-librarian's create. Applies only inside a Fusion bucket — a directory tree with BUCKET.md and LEDGER.md at its root; if there is no such bucket in play, offer to create one with `fusion new` instead of improvising structure."
4
4
  license: MIT
5
5
  compatibility: "Requires the fusion CLI on PATH; uv for the export script (PEP 723: openpyxl)."
6
6
  ---
@@ -13,8 +13,11 @@ document cites the exact paths it was built from, and ships with a
13
13
 
14
14
  Read `references/fusion-conventions.md` once per session; read
15
15
  `BUCKET.md ## Conventions` before acting. No `BUCKET.md` up the tree and
16
- none named? Stop this is not a Fusion bucket, and no Fusion skill
17
- applies (`fusion hub` lists the real ones).
16
+ none named? Then nothing here is a Fusion bucket and no Fusion skill
17
+ applies but offer the door instead of walking away: `fusion new
18
+ <path>` scaffolds a conformant bucket and registers it in the hub
19
+ (`fusion hub` lists the ones that exist). Buckets are life-domains —
20
+ few and bold — never improvised by hand.
18
21
 
19
22
  ## Pick the gear
20
23
 
@@ -107,6 +107,11 @@ shell. The ledger verbs (closed set of eleven): `created`, `converted`,
107
107
  `shipped`, `reflected`, `noted`. Sign with your agent name (`--as claude`,
108
108
  or set `FUSION_ACTOR`).
109
109
 
110
+ On a bucket that lives on more than one machine, git's union driver
111
+ (`.gitattributes`, written at bucket birth) merges parallel `LEDGER.md`
112
+ and `MANIFEST.md` appends. After any git merge: `fusion index`, then
113
+ `fusion check`.
114
+
110
115
  ## The CLI crib
111
116
 
112
117
  | Command | What it does |
@@ -116,6 +121,7 @@ or set `FUSION_ACTOR`).
116
121
  | `fusion log <verb> <object>` | append a signed ledger entry |
117
122
  | `fusion index` | regenerate INDEX files (logs `indexed` when changed) |
118
123
  | `fusion check [path]` | conformance: errors, warnings, honest exit codes |
124
+ | `fusion check --hub` | is every registered bucket where the hub says? |
119
125
  | `fusion status [--since …]` | one bucket at a glance |
120
126
  | `fusion today` | the composed morning across all hub buckets |
121
127
  | `fusion agenda` | dated + active items across buckets |
@@ -127,8 +133,14 @@ green before you call the work done.**
127
133
 
128
134
  ## When you're blocked
129
135
 
136
+ - No bucket in play anywhere: never improvise Fusion structure by hand.
137
+ Offer `fusion new <path> --kind <kind> --description "…"` — six zones,
138
+ BUCKET.md, an opened ledger, a merge-safe `.gitattributes`, registered
139
+ in the hub. Guide the human toward few, bold, life-domain buckets
140
+ (personal, one per company, a studio).
130
141
  - `fusion` not on PATH: stop and tell the human — the install is
131
- `uv tool install ./fusion/cli` from a clone of the Fusion repository.
142
+ `uv tool install fusion-cli` (or, from a clone of the Fusion
143
+ repository, `uv tool install ./fusion/cli`).
132
144
  Never imitate the notary by hand: no register writes while the CLI is
133
145
  missing.
134
146
  - `fusion check` red and you cannot fix it: stop, show the findings
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: fusion-intake
3
- description: "The Fusion intake gate — everything that enters a bucket enters through it, losslessly. Classify what landed in inbox/ (new, updated, duplicate, conflicting), preserve the original in sources/ with its sha256 in the MANIFEST, convert to a faithful summary-first document (xlsx, csv, docx, pptx, pdf, html, images, .eml mail, markdown/text exports), propose type and aurora, sign the ledger, clear the inbox. Use when the user says 'process the inbox', 'intake', 'ingest', 'convert this file', or drops files into a Fusion bucket's inbox/. For placement, curation, or restructuring of what is already inside, use fusion-librarian; for deliverables out of the bucket, use fusion-analyst. Applies only inside a Fusion bucket — a directory tree with BUCKET.md and LEDGER.md at its root; if there is no such bucket in play, this skill does not apply."
3
+ description: "The Fusion intake gate — everything that enters a bucket enters through it, losslessly. Classify what landed in inbox/ (new, updated, duplicate, conflicting), preserve the original in sources/ with its sha256 in the MANIFEST, convert to a faithful summary-first document (xlsx, csv, docx, pptx, pdf, html, images, .eml mail, markdown/text exports), propose type and aurora, sign the ledger, clear the inbox. Use when the user says 'process the inbox', 'intake', 'ingest', 'convert this file', or drops files into a Fusion bucket's inbox/. For placement, curation, or restructuring of what is already inside, use fusion-librarian; for deliverables out of the bucket, use fusion-analyst. Applies only inside a Fusion bucket — a directory tree with BUCKET.md and LEDGER.md at its root; if there is no such bucket in play, offer to create one with `fusion new` instead of improvising structure."
4
4
  license: MIT
5
5
  compatibility: "Requires the fusion CLI on PATH and uv. LibreOffice (soffice on PATH) required for docx/pptx/legacy office/html formats — fails fast when missing, never silently degrades. Script deps via PEP 723 (openpyxl, PyYAML, pymupdf)."
6
6
  ---
@@ -14,8 +14,11 @@ that isn't clean and new.
14
14
  Read `references/fusion-conventions.md` once per session. Then, before
15
15
  touching anything: read the bucket's `BUCKET.md` — `## Conventions` may
16
16
  contain filing rules and standing delegations that bind this whole run.
17
- No `BUCKET.md` up the tree and none named? Stop this is not a Fusion
18
- bucket, and no Fusion skill applies (`fusion hub` lists the real ones).
17
+ No `BUCKET.md` up the tree and none named? Then nothing here is a
18
+ Fusion bucket and no Fusion skill applies but offer the door instead
19
+ of walking away: `fusion new <path>` scaffolds a conformant bucket and
20
+ registers it in the hub (`fusion hub` lists the ones that exist).
21
+ Buckets are life-domains — few and bold — never improvised by hand.
19
22
 
20
23
  ## The pipeline
21
24
 
@@ -107,6 +107,11 @@ shell. The ledger verbs (closed set of eleven): `created`, `converted`,
107
107
  `shipped`, `reflected`, `noted`. Sign with your agent name (`--as claude`,
108
108
  or set `FUSION_ACTOR`).
109
109
 
110
+ On a bucket that lives on more than one machine, git's union driver
111
+ (`.gitattributes`, written at bucket birth) merges parallel `LEDGER.md`
112
+ and `MANIFEST.md` appends. After any git merge: `fusion index`, then
113
+ `fusion check`.
114
+
110
115
  ## The CLI crib
111
116
 
112
117
  | Command | What it does |
@@ -116,6 +121,7 @@ or set `FUSION_ACTOR`).
116
121
  | `fusion log <verb> <object>` | append a signed ledger entry |
117
122
  | `fusion index` | regenerate INDEX files (logs `indexed` when changed) |
118
123
  | `fusion check [path]` | conformance: errors, warnings, honest exit codes |
124
+ | `fusion check --hub` | is every registered bucket where the hub says? |
119
125
  | `fusion status [--since …]` | one bucket at a glance |
120
126
  | `fusion today` | the composed morning across all hub buckets |
121
127
  | `fusion agenda` | dated + active items across buckets |
@@ -127,8 +133,14 @@ green before you call the work done.**
127
133
 
128
134
  ## When you're blocked
129
135
 
136
+ - No bucket in play anywhere: never improvise Fusion structure by hand.
137
+ Offer `fusion new <path> --kind <kind> --description "…"` — six zones,
138
+ BUCKET.md, an opened ledger, a merge-safe `.gitattributes`, registered
139
+ in the hub. Guide the human toward few, bold, life-domain buckets
140
+ (personal, one per company, a studio).
130
141
  - `fusion` not on PATH: stop and tell the human — the install is
131
- `uv tool install ./fusion/cli` from a clone of the Fusion repository.
142
+ `uv tool install fusion-cli` (or, from a clone of the Fusion
143
+ repository, `uv tool install ./fusion/cli`).
132
144
  Never imitate the notary by hand: no register writes while the CLI is
133
145
  missing.
134
146
  - `fusion check` red and you cannot fix it: stop, show the findings
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: fusion-librarian
3
- description: "The Fusion librarian — the accountable owner of a bucket's order. One entry, eight gears: query (natural-language search over the bucket — the default), create (a new conformant document), tag (bulk frontmatter metadata), cross-reference (map what relates to what), promote (workbench → a real zone, validated), archive (out of the way, path + aurora agreeing), restructure (reorganize and sign your reasons), reflect (the reflection ritual: introspect the ledger, propose curation, land what was learned). Use for 'find', 'search', 'where is', 'create a document', 'tag', 'what links to', 'promote', 'archive this', 'reorganize', 'restructure', 'run a reflection'. For files arriving from outside use fusion-intake; for activity planning use fusion-planner; for reports and exports use fusion-analyst. Applies only inside a Fusion bucket — a directory tree with BUCKET.md and LEDGER.md at its root; if there is no such bucket in play, this skill does not apply."
3
+ description: "The Fusion librarian — the accountable owner of a bucket's order. One entry, eight gears: query (natural-language search over the bucket — the default), create (a new conformant document), tag (bulk frontmatter metadata), cross-reference (map what relates to what), promote (workbench → a real zone, validated), archive (out of the way, path + aurora agreeing), restructure (reorganize and sign your reasons), reflect (the reflection ritual: introspect the ledger, propose curation, land what was learned). Use for 'find', 'search', 'where is', 'create a document', 'tag', 'what links to', 'promote', 'archive this', 'reorganize', 'restructure', 'run a reflection'. For files arriving from outside use fusion-intake; for activity planning use fusion-planner; for reports and exports use fusion-analyst. Applies only inside a Fusion bucket — a directory tree with BUCKET.md and LEDGER.md at its root; if there is no such bucket in play, offer to create one with `fusion new` instead of improvising structure."
4
4
  license: MIT
5
5
  compatibility: "Requires the fusion CLI on PATH."
6
6
  ---
@@ -14,8 +14,11 @@ ledger with its reasons.
14
14
  Read `references/fusion-conventions.md` once per session. Before any gear:
15
15
  read the bucket's `BUCKET.md` — `### Rules` bind how this bucket files
16
16
  things; `### Delegations` say what you may do without asking. No
17
- `BUCKET.md` up the tree and none named? Stop this is not a Fusion
18
- bucket, and no Fusion skill applies (`fusion hub` lists the real ones).
17
+ `BUCKET.md` up the tree and none named? Then nothing here is a Fusion
18
+ bucket and no Fusion skill applies but offer the door instead of
19
+ walking away: `fusion new <path>` scaffolds a conformant bucket and
20
+ registers it in the hub (`fusion hub` lists the ones that exist).
21
+ Buckets are life-domains — few and bold — never improvised by hand.
19
22
 
20
23
  ## Pick the gear
21
24
 
@@ -107,6 +107,11 @@ shell. The ledger verbs (closed set of eleven): `created`, `converted`,
107
107
  `shipped`, `reflected`, `noted`. Sign with your agent name (`--as claude`,
108
108
  or set `FUSION_ACTOR`).
109
109
 
110
+ On a bucket that lives on more than one machine, git's union driver
111
+ (`.gitattributes`, written at bucket birth) merges parallel `LEDGER.md`
112
+ and `MANIFEST.md` appends. After any git merge: `fusion index`, then
113
+ `fusion check`.
114
+
110
115
  ## The CLI crib
111
116
 
112
117
  | Command | What it does |
@@ -116,6 +121,7 @@ or set `FUSION_ACTOR`).
116
121
  | `fusion log <verb> <object>` | append a signed ledger entry |
117
122
  | `fusion index` | regenerate INDEX files (logs `indexed` when changed) |
118
123
  | `fusion check [path]` | conformance: errors, warnings, honest exit codes |
124
+ | `fusion check --hub` | is every registered bucket where the hub says? |
119
125
  | `fusion status [--since …]` | one bucket at a glance |
120
126
  | `fusion today` | the composed morning across all hub buckets |
121
127
  | `fusion agenda` | dated + active items across buckets |
@@ -127,8 +133,14 @@ green before you call the work done.**
127
133
 
128
134
  ## When you're blocked
129
135
 
136
+ - No bucket in play anywhere: never improvise Fusion structure by hand.
137
+ Offer `fusion new <path> --kind <kind> --description "…"` — six zones,
138
+ BUCKET.md, an opened ledger, a merge-safe `.gitattributes`, registered
139
+ in the hub. Guide the human toward few, bold, life-domain buckets
140
+ (personal, one per company, a studio).
130
141
  - `fusion` not on PATH: stop and tell the human — the install is
131
- `uv tool install ./fusion/cli` from a clone of the Fusion repository.
142
+ `uv tool install fusion-cli` (or, from a clone of the Fusion
143
+ repository, `uv tool install ./fusion/cli`).
132
144
  Never imitate the notary by hand: no register writes while the CLI is
133
145
  missing.
134
146
  - `fusion check` red and you cannot fix it: stop, show the findings
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: fusion-planner
3
- description: "The Fusion planner — the horizon keeper. The human drives activities; the planner structures them. Three gears: create-activity (a new activity folder with a stateful plan document — works for plans, campaigns, programs, agendas alike), horizon (review what's live: stalled activities, expiring commitments, what fusion today will show tomorrow), close (finish an activity: status done, archived, out of the way). Use for 'new project/activity/campaign', 'plan this', 'what's on my plate', 'review my activities', 'what's stalled', 'agenda', 'close/finish this activity'. For placing knowledge use fusion-librarian; for deliverables use fusion-analyst. Applies only inside a Fusion bucket — a directory tree with BUCKET.md and LEDGER.md at its root; if there is no such bucket in play, this skill does not apply."
3
+ description: "The Fusion planner — the horizon keeper. The human drives activities; the planner structures them. Three gears: create-activity (a new activity folder with a stateful plan document — works for plans, campaigns, programs, agendas alike), horizon (review what's live: stalled activities, expiring commitments, what fusion today will show tomorrow), close (finish an activity: status done, archived, out of the way). Use for 'new project/activity/campaign', 'plan this', 'what's on my plate', 'review my activities', 'what's stalled', 'agenda', 'close/finish this activity'. For placing knowledge use fusion-librarian; for deliverables use fusion-analyst. Applies only inside a Fusion bucket — a directory tree with BUCKET.md and LEDGER.md at its root; if there is no such bucket in play, offer to create one with `fusion new` instead of improvising structure."
4
4
  license: MIT
5
5
  compatibility: "Requires the fusion CLI on PATH."
6
6
  ---
@@ -13,8 +13,11 @@ composes tomorrow morning is this skill's report card.
13
13
 
14
14
  Read `references/fusion-conventions.md` once per session; read the
15
15
  bucket's `BUCKET.md ## Conventions` before acting. No `BUCKET.md` up the
16
- tree and none named? Stop this is not a Fusion bucket, and no Fusion
17
- skill applies (`fusion hub` lists the real ones).
16
+ tree and none named? Then nothing here is a Fusion bucket and no
17
+ Fusion skill applies but offer the door instead of walking away:
18
+ `fusion new <path>` scaffolds a conformant bucket and registers it in
19
+ the hub (`fusion hub` lists the ones that exist). Buckets are
20
+ life-domains — few and bold — never improvised by hand.
18
21
 
19
22
  ## Pick the gear
20
23
 
@@ -36,3 +36,24 @@
36
36
  4. `fusion log created "activities/<slug>/plan.md" --bucket <root>
37
37
  --as <you>` · `fusion index <root>` · `fusion check <root>` green.
38
38
  Then show the human what `fusion today` now includes.
39
+
40
+ ## The external project
41
+
42
+ An activity about building something that is code — an app, a site, a
43
+ tool — keeps the repository OUTSIDE the bucket (Fusion holds knowledge
44
+ and work, never code). Point at it instead:
45
+
46
+ ---
47
+ title: <name>
48
+ type: plan
49
+ aurora: focus
50
+ status: active
51
+ created: <today>
52
+ resource: <path or URL of the repository>
53
+ ---
54
+
55
+ The repo keeps its own history; the activity keeps what the code cannot
56
+ remember — the plan, the decisions, the progress log, links to the
57
+ bucket documents it draws on. The normative example:
58
+ `examples/crazy-ones/activities/band-site/plan.md` in the Fusion
59
+ repository.
@@ -107,6 +107,11 @@ shell. The ledger verbs (closed set of eleven): `created`, `converted`,
107
107
  `shipped`, `reflected`, `noted`. Sign with your agent name (`--as claude`,
108
108
  or set `FUSION_ACTOR`).
109
109
 
110
+ On a bucket that lives on more than one machine, git's union driver
111
+ (`.gitattributes`, written at bucket birth) merges parallel `LEDGER.md`
112
+ and `MANIFEST.md` appends. After any git merge: `fusion index`, then
113
+ `fusion check`.
114
+
110
115
  ## The CLI crib
111
116
 
112
117
  | Command | What it does |
@@ -116,6 +121,7 @@ or set `FUSION_ACTOR`).
116
121
  | `fusion log <verb> <object>` | append a signed ledger entry |
117
122
  | `fusion index` | regenerate INDEX files (logs `indexed` when changed) |
118
123
  | `fusion check [path]` | conformance: errors, warnings, honest exit codes |
124
+ | `fusion check --hub` | is every registered bucket where the hub says? |
119
125
  | `fusion status [--since …]` | one bucket at a glance |
120
126
  | `fusion today` | the composed morning across all hub buckets |
121
127
  | `fusion agenda` | dated + active items across buckets |
@@ -127,8 +133,14 @@ green before you call the work done.**
127
133
 
128
134
  ## When you're blocked
129
135
 
136
+ - No bucket in play anywhere: never improvise Fusion structure by hand.
137
+ Offer `fusion new <path> --kind <kind> --description "…"` — six zones,
138
+ BUCKET.md, an opened ledger, a merge-safe `.gitattributes`, registered
139
+ in the hub. Guide the human toward few, bold, life-domain buckets
140
+ (personal, one per company, a studio).
130
141
  - `fusion` not on PATH: stop and tell the human — the install is
131
- `uv tool install ./fusion/cli` from a clone of the Fusion repository.
142
+ `uv tool install fusion-cli` (or, from a clone of the Fusion
143
+ repository, `uv tool install ./fusion/cli`).
132
144
  Never imitate the notary by hand: no register writes while the CLI is
133
145
  missing.
134
146
  - `fusion check` red and you cannot fix it: stop, show the findings
@@ -0,0 +1,8 @@
1
+ """fusion — the reference CLI of the Fusion Convention."""
2
+
3
+ from importlib.metadata import PackageNotFoundError, version
4
+
5
+ try:
6
+ __version__ = version("fusion-cli")
7
+ except PackageNotFoundError: # running from a checkout, not an install
8
+ __version__ = "0.0.0+dev"
@@ -6,7 +6,7 @@ import time
6
6
  from dataclasses import dataclass
7
7
  from pathlib import Path
8
8
 
9
- from . import indexer, ledger, manifest
9
+ from . import hub, indexer, ledger, manifest
10
10
  from .bucket import DOC_ZONES, REQUIRED_BUCKET_FIELDS, ZONES, load, iter_documents
11
11
  from .document import AURORAS, FILENAME_RE, MAX_STEM
12
12
 
@@ -19,7 +19,7 @@ EXPORT_FILENAME_RE = re.compile(r"^[a-z0-9]+(?:-[a-z0-9]+)*\.[a-z0-9]+$")
19
19
  @dataclass
20
20
  class Finding:
21
21
  level: str # "error" | "warning"
22
- code: str # E1..E8, W1..W5
22
+ code: str # E1..E8, W1..W5 — plus H1..H2 from check_hub (CLI vocabulary, not SPEC)
23
23
  path: str # bucket-relative, "" when bucket-wide
24
24
  message: str
25
25
 
@@ -246,3 +246,30 @@ def _w5_untouched_activities(root: Path) -> list[Finding]:
246
246
  "active activity with no ledger mention across the last "
247
247
  "reflection window"))
248
248
  return findings
249
+
250
+
251
+ def check_hub() -> list[Finding]:
252
+ """The hub's own audit — dangling entries and name drift.
253
+
254
+ H-codes are CLI vocabulary, not SPEC conformance codes: the hub is a
255
+ per-machine register and consumers must tolerate anything in it. All
256
+ findings are warnings; a hub audit never fails.
257
+ """
258
+ findings: list[Finding] = []
259
+ for entry in hub.load():
260
+ root = hub.resolve(entry)
261
+ if not (root / "BUCKET.md").is_file():
262
+ findings.append(Finding(
263
+ "warning", "H1", entry.path,
264
+ f"'{entry.name}' — no bucket at this path; clone it "
265
+ f"there, or `fusion hub remove {entry.name}`",
266
+ ))
267
+ continue
268
+ name = load(root).name
269
+ if name and name != entry.name:
270
+ findings.append(Finding(
271
+ "warning", "H2", entry.path,
272
+ f"hub says '{entry.name}' but BUCKET.md says '{name}' — "
273
+ "re-register: `fusion hub remove` then `fusion hub add`",
274
+ ))
275
+ return findings
@@ -88,11 +88,20 @@ def cmd_hub(args) -> int:
88
88
  f"'{args.name}' retired from the hub. The files live on.")
89
89
  return 0
90
90
  entries = hub.load()
91
- human = "\n".join(
92
- f"- **{e.name}** · {e.kind} · {e.path} {e.description}"
93
- for e in entries
94
- ) or "the hub is empty — `fusion new` a bucket."
95
- _emit([asdict(e) for e in entries], args.json, human)
91
+ flagged = [
92
+ (e, not (hub.resolve(e) / "BUCKET.md").is_file()) for e in entries
93
+ ]
94
+ lines = []
95
+ for e, missing in flagged:
96
+ lines.append(f"- **{e.name}** · {e.kind} · {e.path} — {e.description}")
97
+ if missing:
98
+ lines.append(
99
+ f" ⚠ no bucket at that path — clone it there, "
100
+ f"or `fusion hub remove {e.name}`"
101
+ )
102
+ human = "\n".join(lines) or "the hub is empty — `fusion new` a bucket."
103
+ _emit([{**asdict(e), "missing": missing} for e, missing in flagged],
104
+ args.json, human)
96
105
  return 0
97
106
 
98
107
 
@@ -140,12 +149,36 @@ def cmd_index(args) -> int:
140
149
  return 0
141
150
 
142
151
 
152
+ def _check_hub(args) -> int:
153
+ findings = checker.check_hub()
154
+ entries = hub.load()
155
+ if args.json:
156
+ _emit({
157
+ "hub": str(hub.hub_path()),
158
+ "buckets": len(entries),
159
+ "ok": not findings,
160
+ "warnings": [asdict(f) for f in findings],
161
+ }, True, "")
162
+ else:
163
+ for f in findings:
164
+ print(f" {f.code} · {f.path} — {f.message}")
165
+ n, w = len(entries), len(findings)
166
+ verdict = ("every bucket answers." if not findings
167
+ else "some buckets are not where the hub says.")
168
+ print(f"hub: {n} bucket{'s' if n != 1 else ''} · "
169
+ f"{w} warning{'s' if w != 1 else ''} — {verdict}")
170
+ return 0
171
+
172
+
143
173
  def cmd_check(args) -> int:
174
+ if getattr(args, "hub", False):
175
+ return _check_hub(args)
144
176
  root = _root_from(args)
145
177
  if root is None:
146
178
  given = getattr(args, "path", None)
147
- return _fail(f"no bucket at: {given}" if given else
148
- "not inside a bucket and no path given", args.json)
179
+ if given is None:
180
+ return _check_hub(args) # outside any bucket: audit the hub
181
+ return _fail(f"no bucket at: {given}", args.json)
149
182
  findings = checker.check(root)
150
183
  errors = [f for f in findings if f.level == "error"]
151
184
  warnings = [f for f in findings if f.level == "warning"]
@@ -207,6 +240,12 @@ def _render_items(items) -> list[str]:
207
240
 
208
241
  def cmd_today(args) -> int:
209
242
  t = views.today()
243
+ for m in t["missing"]:
244
+ print(
245
+ f"note: hub bucket '{m['name']}' is not at {m['path']} — "
246
+ f"clone it there, or `fusion hub remove {m['name']}`",
247
+ file=sys.stderr,
248
+ )
210
249
  lines = [f"Today across {len(t['buckets'])} bucket"
211
250
  f"{'s' if len(t['buckets']) != 1 else ''}"]
212
251
  for aurora, items in t["groups"].items():
@@ -220,6 +259,12 @@ def cmd_today(args) -> int:
220
259
 
221
260
  def cmd_agenda(args) -> int:
222
261
  a = views.agenda()
262
+ for m in a["missing"]:
263
+ print(
264
+ f"note: hub bucket '{m['name']}' is not at {m['path']} — "
265
+ f"clone it there, or `fusion hub remove {m['name']}`",
266
+ file=sys.stderr,
267
+ )
223
268
  lines = []
224
269
  if a["dated"]:
225
270
  lines.append("dated:")
@@ -336,8 +381,10 @@ def build_parser() -> argparse.ArgumentParser:
336
381
  flag_as(p); flag_json(p)
337
382
  p.set_defaults(func=cmd_index)
338
383
 
339
- p = sub.add_parser("check", help="audit a bucket against the convention", description="Audit a bucket against the convention (SPEC §11). Exit 1 on errors; warnings never fail the check.")
384
+ p = sub.add_parser("check", help="audit a bucket against the convention", description="Audit a bucket against the convention (SPEC §11). Exit 1 on errors; warnings never fail the check. --hub (or running outside any bucket) audits the hub instead: every registered bucket present and answering to its name.")
340
385
  p.add_argument("path", nargs="?", help="bucket root (default: walk up from the current directory)")
386
+ p.add_argument("--hub", action="store_true",
387
+ help="audit the hub instead: every entry answers at its path")
341
388
  flag_json(p)
342
389
  p.set_defaults(func=cmd_check)
343
390
 
@@ -43,6 +43,14 @@ MANIFEST_HEADER = (
43
43
  "|---|---|---|---|---|\n"
44
44
  )
45
45
 
46
+ GITATTRIBUTES = (
47
+ "# written by fusion new — multi-machine merges stay safe (SPEC §6)\n"
48
+ "* text=auto eol=lf\n"
49
+ "sources/** -text\n"
50
+ "LEDGER.md merge=union\n"
51
+ "sources/MANIFEST.md merge=union\n"
52
+ )
53
+
46
54
 
47
55
  class ScaffoldError(Exception):
48
56
  pass
@@ -85,6 +93,9 @@ def new_bucket(
85
93
  (root / "sources" / "MANIFEST.md").write_text(
86
94
  MANIFEST_HEADER, encoding="utf-8", newline="\n"
87
95
  )
96
+ (root / ".gitattributes").write_text(
97
+ GITATTRIBUTES, encoding="utf-8", newline="\n"
98
+ )
88
99
  ledger.append(root, actor, "created", "BUCKET.md", note="bucket born", at=at)
89
100
  indexer.write_indexes(root, actor=None)
90
101
 
@@ -62,6 +62,16 @@ def _hub_documents():
62
62
  yield name, zone, rel, doc
63
63
 
64
64
 
65
+ def missing_hub_entries() -> list[dict]:
66
+ """Hub entries whose path holds no readable BUCKET.md — a bucket not
67
+ yet cloned to this machine, moved, or gone."""
68
+ return [
69
+ {"name": entry.name, "path": entry.path}
70
+ for entry in hub.load()
71
+ if not (hub.resolve(entry) / "BUCKET.md").is_file()
72
+ ]
73
+
74
+
65
75
  def _item(name: str, zone: str, rel, doc, date=None) -> dict:
66
76
  return {
67
77
  "bucket": name,
@@ -92,7 +102,7 @@ def today() -> dict:
92
102
  for aurora, items in groups.items():
93
103
  if aurora not in ordered:
94
104
  ordered[aurora] = items
95
- return {"buckets": buckets, "groups": ordered}
105
+ return {"buckets": buckets, "groups": ordered, "missing": missing_hub_entries()}
96
106
 
97
107
 
98
108
  def agenda() -> dict:
@@ -108,4 +118,4 @@ def agenda() -> dict:
108
118
  elif zone == "activities" and doc.status == "active":
109
119
  active.append(_item(name, zone, rel, doc))
110
120
  dated.sort(key=lambda i: i["date"])
111
- return {"dated": dated, "active": active}
121
+ return {"dated": dated, "active": active, "missing": missing_hub_entries()}
@@ -225,3 +225,33 @@ def test_w5_directory_mention_suppresses(make_bucket):
225
225
  from fusion import indexer
226
226
  indexer.write_indexes(root)
227
227
  assert not [f for f in warnings(root) if f.code == "W5"]
228
+
229
+
230
+ def test_check_hub_dangling_and_drift(tmp_path, monkeypatch):
231
+ from fusion import checker, hub
232
+ from fusion.scaffold import new_bucket
233
+
234
+ monkeypatch.setenv("FUSION_HUB", str(tmp_path / "hub.md"))
235
+ new_bucket(tmp_path / "here", description="d", actor="test")
236
+ hub.add(hub.HubEntry("ghost", "personal", str(tmp_path / "gone"), "x"))
237
+ drifted, _ = new_bucket(tmp_path / "drift", name="oldname",
238
+ description="d", actor="test")
239
+ text = (drifted / "BUCKET.md").read_text(encoding="utf-8")
240
+ (drifted / "BUCKET.md").write_text(
241
+ text.replace("name: oldname", "name: newname"), encoding="utf-8"
242
+ )
243
+ findings = checker.check_hub()
244
+ assert [(f.code, f.level) for f in findings] == [
245
+ ("H1", "warning"), ("H2", "warning")
246
+ ]
247
+ assert "ghost" in findings[0].message
248
+ assert "newname" in findings[1].message
249
+
250
+
251
+ def test_check_hub_all_present_is_empty(tmp_path, monkeypatch):
252
+ from fusion import checker
253
+ from fusion.scaffold import new_bucket
254
+
255
+ monkeypatch.setenv("FUSION_HUB", str(tmp_path / "hub.md"))
256
+ new_bucket(tmp_path / "here", description="d", actor="test")
257
+ assert checker.check_hub() == []
@@ -89,11 +89,11 @@ def test_index_logs_when_changed(tmp_path, capsys):
89
89
 
90
90
  def test_status_today_agenda_json(fixture_bucket, capsys):
91
91
  assert main(["status", str(fixture_bucket), "--json"]) == 0
92
- assert out_json(capsys)["documents"] == 6
92
+ assert out_json(capsys)["documents"] == 7
93
93
  assert main(["today", "--json"]) == 0
94
94
  assert out_json(capsys)["groups"] == {} # empty hub, empty day
95
95
  assert main(["agenda", "--json"]) == 0
96
- assert out_json(capsys) == {"dated": [], "active": []}
96
+ assert out_json(capsys) == {"dated": [], "active": [], "missing": []}
97
97
 
98
98
 
99
99
  def test_every_command_has_help(capsys):
@@ -223,3 +223,43 @@ def test_parent_listing_has_one_liners(capsys):
223
223
  output = capsys.readouterr().out
224
224
  for cmd, oneliner in hub_sub_help.items():
225
225
  assert oneliner in output, f"Hub subcommand help missing for '{cmd}': '{oneliner}'"
226
+
227
+
228
+ def test_hub_list_flags_missing(tmp_path, capsys, monkeypatch):
229
+ from fusion import hub
230
+
231
+ monkeypatch.setenv("FUSION_HUB", str(tmp_path / "hub.md"))
232
+ hub.add(hub.HubEntry("ghost", "personal", str(tmp_path / "gone"), "x"))
233
+ assert main(["hub"]) == 0
234
+ out = capsys.readouterr().out
235
+ assert "ghost" in out and "fusion hub remove ghost" in out
236
+ assert main(["hub", "--json"]) == 0
237
+ assert out_json(capsys)[0]["missing"] is True
238
+
239
+
240
+ def test_check_hub_mode(tmp_path, capsys, monkeypatch):
241
+ from fusion import hub
242
+
243
+ monkeypatch.setenv("FUSION_HUB", str(tmp_path / "hub.md"))
244
+ hub.add(hub.HubEntry("ghost", "personal", str(tmp_path / "gone"), "x"))
245
+ assert main(["check", "--hub", "--json"]) == 0 # warnings never fail
246
+ payload = out_json(capsys)
247
+ assert payload["ok"] is False
248
+ assert payload["warnings"][0]["code"] == "H1"
249
+ # outside any bucket, path-less check falls back to the hub audit
250
+ monkeypatch.chdir(tmp_path)
251
+ assert main(["check"]) == 0
252
+ assert "H1" in capsys.readouterr().out
253
+
254
+
255
+ def test_today_agenda_note_missing_buckets(tmp_path, capsys, monkeypatch):
256
+ from fusion import hub
257
+
258
+ monkeypatch.setenv("FUSION_HUB", str(tmp_path / "hub.md"))
259
+ hub.add(hub.HubEntry("ghost", "personal", str(tmp_path / "gone"), "x"))
260
+ assert main(["today"]) == 0
261
+ assert "ghost" in capsys.readouterr().err
262
+ assert main(["agenda", "--json"]) == 0
263
+ captured = capsys.readouterr()
264
+ assert "ghost" in captured.err
265
+ assert json.loads(captured.out)["missing"][0]["name"] == "ghost"
@@ -79,10 +79,10 @@ def test_load_bucket(fixture_bucket):
79
79
 
80
80
  def test_iter_documents_fixture(fixture_bucket):
81
81
  docs = list(bucket.iter_documents(fixture_bucket))
82
- assert len(docs) == 6
82
+ assert len(docs) == 7
83
83
  from collections import Counter
84
84
  zone_counts = Counter(zone for zone, _, _ in docs)
85
- assert zone_counts == {"library": 3, "activities": 2, "output": 1}
85
+ assert zone_counts == {"library": 3, "activities": 3, "output": 1}
86
86
  names = {rel.name for _, rel, _ in docs}
87
87
  assert "INDEX.md" not in names
88
88
  # yields (zone, path relative to the zone, Document)
@@ -15,7 +15,7 @@ def test_verbs_are_the_eleven_verbatim():
15
15
 
16
16
  def test_parse_fixture(fixture_bucket):
17
17
  entries = ledger.read(fixture_bucket)
18
- assert len(entries) == 17
18
+ assert len(entries) == 19
19
19
  first = entries[0]
20
20
  assert (first.date, first.time, first.actor, first.verb) == (
21
21
  "2026-07-08", "09:00", "bertrand", "created"
@@ -63,4 +63,4 @@ def test_round_trip(tmp_path, capsys):
63
63
  today = out_json(capsys)
64
64
  assert today["buckets"] == ["scratch"]
65
65
  assert main(["agenda", "--json"]) == 0
66
- assert out_json(capsys) == {"dated": [], "active": []}
66
+ assert out_json(capsys) == {"dated": [], "active": [], "missing": []}
@@ -107,3 +107,13 @@ def test_hub_failure_is_a_warning_not_an_error(tmp_path, monkeypatch):
107
107
  root, warnings = new_bucket(tmp_path / "b", description="d", actor="c")
108
108
  assert root.exists()
109
109
  assert any("hub registration failed" in w for w in warnings)
110
+
111
+
112
+ def test_new_bucket_writes_gitattributes(tmp_path):
113
+ root, _ = new_bucket(tmp_path / "b", description="d", actor="test")
114
+ text = (root / ".gitattributes").read_text(encoding="utf-8")
115
+ assert "LEDGER.md merge=union" in text
116
+ assert "sources/MANIFEST.md merge=union" in text
117
+ assert "* text=auto eol=lf" in text
118
+ assert "sources/** -text" in text
119
+ assert "\r" not in text
@@ -7,17 +7,17 @@ from fusion.ledger import Entry
7
7
  def test_status_fixture(fixture_bucket):
8
8
  s = views.status(fixture_bucket)
9
9
  assert s["bucket"] == "crazy-ones"
10
- assert s["documents"] == 6
11
- assert s["auroras"] == {"archive": 1, "collab": 1, "focus": 1, "library": 3}
12
- assert s["types"] == {"instrument": 2, "plan": 2, "press-kit": 1, "recipe": 1}
13
- assert s["activities"] == {"active": 1, "done": 1}
14
- assert len(s["ledger"]) == 10 # last ten of seventeen
10
+ assert s["documents"] == 7
11
+ assert s["auroras"] == {"archive": 1, "collab": 1, "focus": 2, "library": 3}
12
+ assert s["types"] == {"instrument": 2, "plan": 3, "press-kit": 1, "recipe": 1}
13
+ assert s["activities"] == {"active": 2, "done": 1}
14
+ assert len(s["ledger"]) == 10 # last ten of nineteen
15
15
  assert s["ledger"][-1]["verb"] == "reflected"
16
16
 
17
17
 
18
18
  def test_status_since_date(fixture_bucket):
19
19
  s = views.status(fixture_bucket, since="2026-07-10")
20
- assert len(s["ledger"]) == 5
20
+ assert len(s["ledger"]) == 7
21
21
  assert all(e["date"] == "2026-07-10" for e in s["ledger"])
22
22
 
23
23
 
@@ -106,7 +106,11 @@ def test_today_skips_hub_entries_without_bucket(tmp_path, monkeypatch):
106
106
 
107
107
  monkeypatch.setenv("FUSION_HUB", str(tmp_path / "hub.md"))
108
108
  hub.add(hub.HubEntry("ghost", "personal", str(tmp_path / "gone"), "missing"))
109
- assert views.today()["buckets"] == []
109
+ t = views.today()
110
+ assert t["buckets"] == []
111
+ assert t["missing"] == [
112
+ {"name": "ghost", "path": str(tmp_path / "gone")}
113
+ ]
110
114
 
111
115
 
112
116
  def test_views_exclude_archive_aurora_off_archive_paths(two_bucket_hub):
@@ -13,7 +13,7 @@ wheels = [
13
13
 
14
14
  [[package]]
15
15
  name = "fusion-cli"
16
- version = "1.1.0"
16
+ version = "1.2.0"
17
17
  source = { editable = "." }
18
18
  dependencies = [
19
19
  { name = "pyyaml" },
@@ -1,3 +0,0 @@
1
- """fusion — the reference CLI of the Fusion Convention."""
2
-
3
- __version__ = "1.0.0"
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes