face-cli 0.7.0__tar.gz → 0.7.1__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 (79) hide show
  1. {face_cli-0.7.0 → face_cli-0.7.1}/CHANGELOG.md +57 -0
  2. {face_cli-0.7.0 → face_cli-0.7.1}/CLAUDE.md +6 -3
  3. {face_cli-0.7.0 → face_cli-0.7.1}/PKG-INFO +2 -2
  4. {face_cli-0.7.0 → face_cli-0.7.1}/face_cli/cli/__init__.py +26 -2
  5. {face_cli-0.7.0 → face_cli-0.7.1}/face_cli/cli/_commands/learn.py +17 -8
  6. {face_cli-0.7.0 → face_cli-0.7.1}/face_cli/cli/_commands/overview.py +9 -1
  7. {face_cli-0.7.0 → face_cli-0.7.1}/face_cli/cli/_commands/whoami.py +2 -2
  8. {face_cli-0.7.0 → face_cli-0.7.1}/face_cli/explain/catalog.py +9 -6
  9. {face_cli-0.7.0 → face_cli-0.7.1}/pyproject.toml +2 -2
  10. {face_cli-0.7.0 → face_cli-0.7.1}/tests/test_cli.py +24 -0
  11. {face_cli-0.7.0 → face_cli-0.7.1}/uv.lock +1 -1
  12. {face_cli-0.7.0 → face_cli-0.7.1}/.claude/skills/agent-config/SKILL.md +0 -0
  13. {face_cli-0.7.0 → face_cli-0.7.1}/.claude/skills/agent-config/data/backend-fingerprints.yaml +0 -0
  14. {face_cli-0.7.0 → face_cli-0.7.1}/.claude/skills/agent-config/scripts/show.sh +0 -0
  15. {face_cli-0.7.0 → face_cli-0.7.1}/.claude/skills/ask-colleague/SKILL.md +0 -0
  16. {face_cli-0.7.0 → face_cli-0.7.1}/.claude/skills/ask-colleague/prompts/explore.md +0 -0
  17. {face_cli-0.7.0 → face_cli-0.7.1}/.claude/skills/ask-colleague/prompts/review.md +0 -0
  18. {face_cli-0.7.0 → face_cli-0.7.1}/.claude/skills/ask-colleague/prompts/write.md +0 -0
  19. {face_cli-0.7.0 → face_cli-0.7.1}/.claude/skills/ask-colleague/scripts/ask-colleague.sh +0 -0
  20. {face_cli-0.7.0 → face_cli-0.7.1}/.claude/skills/assign-to-workforce/SKILL.md +0 -0
  21. {face_cli-0.7.0 → face_cli-0.7.1}/.claude/skills/assign-to-workforce/scripts/assign-to-workforce.sh +0 -0
  22. {face_cli-0.7.0 → face_cli-0.7.1}/.claude/skills/challenge/SKILL.md +0 -0
  23. {face_cli-0.7.0 → face_cli-0.7.1}/.claude/skills/cicd/SKILL.md +0 -0
  24. {face_cli-0.7.0 → face_cli-0.7.1}/.claude/skills/cicd/scripts/_resolve-nick.sh +0 -0
  25. {face_cli-0.7.0 → face_cli-0.7.1}/.claude/skills/cicd/scripts/portability-lint.sh +0 -0
  26. {face_cli-0.7.0 → face_cli-0.7.1}/.claude/skills/cicd/scripts/pr-reply.sh +0 -0
  27. {face_cli-0.7.0 → face_cli-0.7.1}/.claude/skills/cicd/scripts/pr-status.sh +0 -0
  28. {face_cli-0.7.0 → face_cli-0.7.1}/.claude/skills/cicd/scripts/workflow.sh +0 -0
  29. {face_cli-0.7.0 → face_cli-0.7.1}/.claude/skills/communicate/SKILL.md +0 -0
  30. {face_cli-0.7.0 → face_cli-0.7.1}/.claude/skills/communicate/scripts/fetch-issues.sh +0 -0
  31. {face_cli-0.7.0 → face_cli-0.7.1}/.claude/skills/communicate/scripts/mesh-message.sh +0 -0
  32. {face_cli-0.7.0 → face_cli-0.7.1}/.claude/skills/communicate/scripts/post-comment.sh +0 -0
  33. {face_cli-0.7.0 → face_cli-0.7.1}/.claude/skills/communicate/scripts/post-issue.sh +0 -0
  34. {face_cli-0.7.0 → face_cli-0.7.1}/.claude/skills/communicate/scripts/templates/skill-new-brief.md +0 -0
  35. {face_cli-0.7.0 → face_cli-0.7.1}/.claude/skills/communicate/scripts/templates/skill-update-brief.md +0 -0
  36. {face_cli-0.7.0 → face_cli-0.7.1}/.claude/skills/deviate/SKILL.md +0 -0
  37. {face_cli-0.7.0 → face_cli-0.7.1}/.claude/skills/doc-test-alignment/SKILL.md +0 -0
  38. {face_cli-0.7.0 → face_cli-0.7.1}/.claude/skills/doc-test-alignment/scripts/check.sh +0 -0
  39. {face_cli-0.7.0 → face_cli-0.7.1}/.claude/skills/pypi-maintainer/SKILL.md +0 -0
  40. {face_cli-0.7.0 → face_cli-0.7.1}/.claude/skills/pypi-maintainer/scripts/switch-source.sh +0 -0
  41. {face_cli-0.7.0 → face_cli-0.7.1}/.claude/skills/recall/SKILL.md +0 -0
  42. {face_cli-0.7.0 → face_cli-0.7.1}/.claude/skills/recall/scripts/recall.sh +0 -0
  43. {face_cli-0.7.0 → face_cli-0.7.1}/.claude/skills/remember/SKILL.md +0 -0
  44. {face_cli-0.7.0 → face_cli-0.7.1}/.claude/skills/remember/scripts/remember.sh +0 -0
  45. {face_cli-0.7.0 → face_cli-0.7.1}/.claude/skills/run-tests/SKILL.md +0 -0
  46. {face_cli-0.7.0 → face_cli-0.7.1}/.claude/skills/run-tests/scripts/test.sh +0 -0
  47. {face_cli-0.7.0 → face_cli-0.7.1}/.claude/skills/scope/SKILL.md +0 -0
  48. {face_cli-0.7.0 → face_cli-0.7.1}/.claude/skills/sonarclaude/SKILL.md +0 -0
  49. {face_cli-0.7.0 → face_cli-0.7.1}/.claude/skills/sonarclaude/scripts/sonar.sh +0 -0
  50. {face_cli-0.7.0 → face_cli-0.7.1}/.claude/skills/spec-to-plan/SKILL.md +0 -0
  51. {face_cli-0.7.0 → face_cli-0.7.1}/.claude/skills/spec-to-plan/scripts/spec-to-plan.sh +0 -0
  52. {face_cli-0.7.0 → face_cli-0.7.1}/.claude/skills/summarize-delivery/SKILL.md +0 -0
  53. {face_cli-0.7.0 → face_cli-0.7.1}/.claude/skills/think/SKILL.md +0 -0
  54. {face_cli-0.7.0 → face_cli-0.7.1}/.claude/skills/think/scripts/think.sh +0 -0
  55. {face_cli-0.7.0 → face_cli-0.7.1}/.claude/skills/version-bump/SKILL.md +0 -0
  56. {face_cli-0.7.0 → face_cli-0.7.1}/.claude/skills/version-bump/scripts/bump.py +0 -0
  57. {face_cli-0.7.0 → face_cli-0.7.1}/.claude/skills.local.yaml.example +0 -0
  58. {face_cli-0.7.0 → face_cli-0.7.1}/.flake8 +0 -0
  59. {face_cli-0.7.0 → face_cli-0.7.1}/.github/workflows/publish.yml +0 -0
  60. {face_cli-0.7.0 → face_cli-0.7.1}/.github/workflows/tests.yml +0 -0
  61. {face_cli-0.7.0 → face_cli-0.7.1}/.gitignore +0 -0
  62. {face_cli-0.7.0 → face_cli-0.7.1}/.markdownlint-cli2.yaml +0 -0
  63. {face_cli-0.7.0 → face_cli-0.7.1}/AGENTS.colleague.md +0 -0
  64. {face_cli-0.7.0 → face_cli-0.7.1}/LICENSE +0 -0
  65. {face_cli-0.7.0 → face_cli-0.7.1}/README.md +0 -0
  66. {face_cli-0.7.0 → face_cli-0.7.1}/culture.yaml +0 -0
  67. {face_cli-0.7.0 → face_cli-0.7.1}/docs/skill-sources.md +0 -0
  68. {face_cli-0.7.0 → face_cli-0.7.1}/face_cli/__init__.py +0 -0
  69. {face_cli-0.7.0 → face_cli-0.7.1}/face_cli/__main__.py +0 -0
  70. {face_cli-0.7.0 → face_cli-0.7.1}/face_cli/cli/_commands/__init__.py +0 -0
  71. {face_cli-0.7.0 → face_cli-0.7.1}/face_cli/cli/_commands/cli.py +0 -0
  72. {face_cli-0.7.0 → face_cli-0.7.1}/face_cli/cli/_commands/doctor.py +0 -0
  73. {face_cli-0.7.0 → face_cli-0.7.1}/face_cli/cli/_commands/explain.py +0 -0
  74. {face_cli-0.7.0 → face_cli-0.7.1}/face_cli/cli/_errors.py +0 -0
  75. {face_cli-0.7.0 → face_cli-0.7.1}/face_cli/cli/_output.py +0 -0
  76. {face_cli-0.7.0 → face_cli-0.7.1}/face_cli/explain/__init__.py +0 -0
  77. {face_cli-0.7.0 → face_cli-0.7.1}/sonar-project.properties +0 -0
  78. {face_cli-0.7.0 → face_cli-0.7.1}/tests/__init__.py +0 -0
  79. {face_cli-0.7.0 → face_cli-0.7.1}/tests/test_cli_introspection.py +0 -0
@@ -5,6 +5,63 @@ All notable changes to this project will be documented in this file.
5
5
  Format follows [Keep a Changelog](https://keepachangelog.com/). This project
6
6
  adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [0.7.1] - 2026-07-24
9
+
10
+ ### Added
11
+
12
+ - **`--json` is accepted before the verb, not only after it** — `face --json
13
+ whoami` used to fail with `unrecognized arguments: --json` while `face whoami
14
+ --json` worked. Flag-before-verb is a common idiom, and an agent-first CLI
15
+ that rejects it is a trap. The root parser now declares `--json`, and
16
+ `main()` re-asserts it after `parse_args` from the existing raw-argv scan:
17
+ each subparser re-declares `--json` with its own `False` default, which
18
+ silently overwrites a leading flag during sub-parsing. Both orders now
19
+ produce identical output, pinned by a parametrized test.
20
+ - **`face --json` with no verb emits the machine-readable command map** rather
21
+ than an error — the JSON analogue of printing help. It reuses `learn`'s
22
+ payload builder (promoted from `_as_json_payload` to `as_json_payload`), so
23
+ there is one command map, not two.
24
+
25
+ ### Changed
26
+
27
+ - **The CLI's own introspection text now describes this agent** — `face --help`,
28
+ `face learn`, `face explain`, and `face overview` still called the repo "a
29
+ clonable template for AgentCulture mesh agents" and gave clone/rename
30
+ instructions, contradicting the docs 0.7.0 rewrote. Because `learn` and
31
+ `explain` are explicitly the machine-readable teaching surface, that mismatch
32
+ misinformed every agent consuming them, not just human readers. All four now
33
+ state the domain (the expressive output side of a face; recognition belongs to
34
+ the sibling) and the honest status (scaffold — no renderer, no gaze primitive,
35
+ no render verbs yet), and `overview` gained a `Status` section carrying the
36
+ same. The leftover clone/rename framing is also gone from the `whoami` and
37
+ `overview` module docstrings. The `face` / `face-cli` naming split is
38
+ untouched: the console script stays `face` and every help / `learn` /
39
+ `explain` string stays `face-cli`, matching argparse's `prog`.
40
+ - **Package metadata no longer advertises capability that was never requested**
41
+ — `pyproject.toml`'s description promised a "gaze **and expression** surface"
42
+ while `README.md` says "gaze surface" and the build brief records that
43
+ expression was *not* asked for. Published metadata is public-facing (it is
44
+ what PyPI shows), so the conflicting claim was narrowed to match the README.
45
+ This removes the claim rather than settling the scope question, which stays
46
+ open in `CLAUDE.md`; the GitHub repo description, set at provisioning, still
47
+ carries the old wording and should follow whichever way the decision lands.
48
+
49
+ - **Public-facing descriptions no longer read as if the renderer exists** —
50
+ `pyproject.toml`'s description (what PyPI renders, and `publish.yml` pushes a
51
+ release on every merge to `main`) and the root `argparse` description both
52
+ described a working browser-rendered face in the present tense, while
53
+ `learn` / `explain` / `overview` said "scaffold, no renderer". Both now carry
54
+ the same qualifier. This also makes 0.7.1's own claim about `face --help`
55
+ true — it stated the honest status was there before the root description
56
+ actually carried it.
57
+
58
+ The first two items were raised by Qodo's review of
59
+ [#2](https://github.com/agentculture/face-cli/pull/2) (review comments
60
+ `3646920415`, `3646920422`), which merged before the fixes landed — hence a
61
+ follow-up release rather than an amendment. The remaining items came from
62
+ Qodo's review of [#3](https://github.com/agentculture/face-cli/pull/3) (review
63
+ comments `3647009663`, `3647009668`).
64
+
8
65
  ## [0.7.0] - 2026-07-24
9
66
 
10
67
  ### Added
@@ -205,9 +205,12 @@ CHANGELOG entry); do not let them get settled by accident in a commit:
205
205
  to compute it with no human in the loop.
206
206
  - **Whether expression is in scope at all.** The operator asked for direction.
207
207
  Expression is the obvious neighbour and was *not* requested — decide
208
- explicitly rather than drifting into it. (Note the repo description and
209
- `pyproject.toml` already say "gaze and expression surface", which is drift to
210
- resolve one way or the other.)
208
+ explicitly rather than drifting into it. Published metadata no longer
209
+ pre-answers it: `pyproject.toml` used to advertise a "gaze **and expression**
210
+ surface" while nothing of the sort exists, so its description was narrowed to
211
+ match the README. That removes the claim, it does not settle the question —
212
+ the GitHub repo description (set at provisioning) still carries the old
213
+ wording and should follow whichever way the decision lands.
211
214
  - **What `--json` returns for a verb whose real output is pixels.** A
212
215
  screenshot path is one honest answer; the resolved gaze state is another.
213
216
  - **Who runs the render loop** — browser-side animation with the CLI setting
@@ -1,7 +1,7 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: face-cli
3
- Version: 0.7.0
4
- Summary: A simulated face rendered in the browser that appears three-dimensional on a flat 2D screen and can look in any direction — an agent-drivable gaze and expression surface for robots, kiosks, and screens.
3
+ Version: 0.7.1
4
+ Summary: Scaffold for a browser-rendered face that reads three-dimensional on a flat 2D screen and looks in any direction — an agent-drivable gaze surface for robots, kiosks, and screens. The renderer is not implemented yet; the CLI ships introspection verbs only.
5
5
  Project-URL: Homepage, https://github.com/agentculture/face-cli
6
6
  Project-URL: Issues, https://github.com/agentculture/face-cli/issues
7
7
  Author: AgentCulture
@@ -26,7 +26,7 @@ import sys
26
26
 
27
27
  from face_cli import __version__
28
28
  from face_cli.cli._errors import EXIT_USER_ERROR, CliError
29
- from face_cli.cli._output import emit_error
29
+ from face_cli.cli._output import emit_error, emit_result
30
30
 
31
31
  _ISSUES_URL = "https://github.com/agentculture/face-cli/issues"
32
32
 
@@ -71,13 +71,23 @@ def _build_parser() -> argparse.ArgumentParser:
71
71
 
72
72
  parser = _CliArgumentParser(
73
73
  prog="face-cli",
74
- description="face-cli — a clonable template for AgentCulture mesh agents.",
74
+ description="face-cli — a browser-rendered face that reads 3D on a 2D screen "
75
+ "and looks in any direction. Scaffold: no renderer or gaze verbs yet; the "
76
+ "verbs below are the introspection surface.",
75
77
  )
76
78
  parser.add_argument(
77
79
  "--version",
78
80
  action="version",
79
81
  version=f"%(prog)s {__version__}",
80
82
  )
83
+ # Accepted before the verb too (`face --json whoami`), not just after it.
84
+ # argparse gives each subparser its own `json` default, so a leading flag is
85
+ # clobbered on the way down; main() re-asserts it from the raw-argv scan.
86
+ parser.add_argument(
87
+ "--json",
88
+ action="store_true",
89
+ help="Emit structured JSON. Accepted before or after the verb.",
90
+ )
81
91
  # parser_class propagates to every subparser so their .error() routes
82
92
  # through _CliArgumentParser too.
83
93
  sub = parser.add_subparsers(dest="command", parser_class=_CliArgumentParser)
@@ -125,7 +135,21 @@ def main(argv: list[str] | None = None) -> int:
125
135
  parser = _build_parser()
126
136
  args = parser.parse_args(argv)
127
137
 
138
+ # Position must not change meaning: each subparser re-declares `--json` with
139
+ # its own `False` default, which overwrites a leading `--json` during
140
+ # sub-parsing. Re-assert from the raw-argv scan so `face --json whoami` and
141
+ # `face whoami --json` behave identically.
142
+ if _CliArgumentParser._json_hint:
143
+ args.json = True
144
+
128
145
  if args.command is None:
146
+ # No verb: the JSON analogue of printing help is the machine-readable
147
+ # command map, which `learn` already owns.
148
+ if getattr(args, "json", False):
149
+ from face_cli.cli._commands.learn import as_json_payload
150
+
151
+ emit_result(as_json_payload(), json_mode=True)
152
+ return 0
129
153
  parser.print_help()
130
154
  return 0
131
155
 
@@ -12,14 +12,19 @@ from face_cli import __version__
12
12
  from face_cli.cli._output import emit_result
13
13
 
14
14
  _TEXT = """\
15
- face-cli — a clonable template for AgentCulture mesh agents.
15
+ face-cli — a browser-rendered face that reads 3D on a 2D screen and looks
16
+ anywhere.
16
17
 
17
18
  Purpose
18
19
  -------
19
- Scaffold for a new Culture mesh agent: an agent-first CLI (cited from the teken
20
- `python-cli` reference), an identity (culture.yaml + CLAUDE.md), the canonical
21
- guildmaster skill kit under .claude/skills/, and a deploy/CI baseline. Clone it,
22
- rename the package, and edit culture.yaml to mint a new agent.
20
+ The expressive *output* side of a face: a simulated face rendered in a browser
21
+ that appears three-dimensional on a flat screen and can point its gaze in any
22
+ direction, drivable by an agent. Its sibling face-recognition-cli is the
23
+ perceptual *input* side; this tool does no recognition.
24
+
25
+ Status: scaffold. No renderer, no gaze primitive, and no render verbs exist
26
+ yet — the commands below are the introspection surface only. See the build
27
+ brief at https://github.com/agentculture/face-cli/issues/1.
23
28
 
24
29
  Commands
25
30
  --------
@@ -48,11 +53,15 @@ More detail
48
53
  """
49
54
 
50
55
 
51
- def _as_json_payload() -> dict[str, object]:
56
+ def as_json_payload() -> dict[str, object]:
57
+ """Machine-readable command map. Shared with the root `--json` (no verb) path."""
52
58
  return {
53
59
  "tool": "face-cli",
54
60
  "version": __version__,
55
- "purpose": "Clonable scaffold for a new AgentCulture mesh agent.",
61
+ "purpose": (
62
+ "Browser-rendered face that reads 3D on a 2D screen and looks in any "
63
+ "direction. Scaffold: no render verbs yet."
64
+ ),
56
65
  "commands": [
57
66
  {"path": ["whoami"], "summary": "Identity probe from culture.yaml."},
58
67
  {"path": ["learn"], "summary": "Self-teaching prompt."},
@@ -73,7 +82,7 @@ def _as_json_payload() -> dict[str, object]:
73
82
 
74
83
  def cmd_learn(args: argparse.Namespace) -> int:
75
84
  if getattr(args, "json", False):
76
- emit_result(_as_json_payload(), json_mode=True)
85
+ emit_result(as_json_payload(), json_mode=True)
77
86
  else:
78
87
  emit_result(_TEXT, json_mode=False)
79
88
  return 0
@@ -1,7 +1,7 @@
1
1
  """``face-cli overview`` — read-only descriptive snapshot of the agent.
2
2
 
3
3
  Describes the agent to an agent reader: identity (from culture.yaml), the verb
4
- surface, and the sibling-pattern artifacts this template carries. The shared
4
+ surface, and the sibling-pattern artifacts this repo carries. The shared
5
5
  section/render helpers here are reused by the ``cli`` noun's ``overview`` (see
6
6
  :mod:`face_cli.cli._commands.cli`).
7
7
 
@@ -24,6 +24,13 @@ _ARTIFACTS = [
24
24
  "pyproject.toml + .github/workflows/ — buildable, deployable package baseline",
25
25
  ]
26
26
 
27
+ _STATUS = [
28
+ "domain: the expressive output side of a face — browser-rendered, reads 3D "
29
+ "on a flat 2D screen, gaze pointable in any direction",
30
+ "scaffold: no renderer, no gaze primitive, no render verbs on disk yet",
31
+ "build brief: https://github.com/agentculture/face-cli/issues/1",
32
+ ]
33
+
27
34
  _VERBS = [
28
35
  "whoami — identity probe (nick, version, backend, model)",
29
36
  "learn — structured self-teaching prompt",
@@ -46,6 +53,7 @@ def agent_sections() -> list[dict[str, object]]:
46
53
  f"model: {ident['model']}",
47
54
  ],
48
55
  },
56
+ {"title": "Status", "items": list(_STATUS)},
49
57
  {"title": "Verbs", "items": list(_VERBS)},
50
58
  {"title": "Sibling-pattern artifacts", "items": list(_ARTIFACTS)},
51
59
  ]
@@ -4,8 +4,8 @@ Reports the agent's identity as declared in ``culture.yaml``: its nick
4
4
  (``suffix``), the backend it runs on, and the served model (if any) — plus the
5
5
  package version. Read-only; touches nothing but its own ``culture.yaml``.
6
6
 
7
- When you clone this template, rename the package and update ``culture.yaml``
8
- ``whoami`` then reflects your new agent's identity with no code change.
7
+ The identity is data, not code: editing ``culture.yaml`` changes what ``whoami``
8
+ reports with no code change.
9
9
  """
10
10
 
11
11
  from __future__ import annotations
@@ -12,11 +12,14 @@ from __future__ import annotations
12
12
  _ROOT = """\
13
13
  # face-cli
14
14
 
15
- A clonable template for AgentCulture mesh agents. It carries an agent-first CLI
16
- (cited from the teken `python-cli` reference), a mesh identity (`culture.yaml` +
17
- `CLAUDE.md`), the canonical guildmaster skill kit under `.claude/skills/`, and a
18
- buildable/deployable package baseline. Clone it, rename the package, edit
19
- `culture.yaml`, and you have a new agent.
15
+ The expressive *output* side of a face: a simulated face rendered in a browser
16
+ that appears three-dimensional on a flat 2D screen and can look in any
17
+ direction, drivable by an agent. The sibling `face-recognition-cli` is the
18
+ perceptual *input* side this tool does no recognition.
19
+
20
+ **Status: scaffold.** There is no renderer, no gaze primitive, and no render
21
+ verbs on disk yet; what follows is the introspection surface. The build brief
22
+ is <https://github.com/agentculture/face-cli/issues/1>.
20
23
 
21
24
  ## Verbs
22
25
 
@@ -81,7 +84,7 @@ _OVERVIEW = """\
81
84
  # face-cli overview
82
85
 
83
86
  Read-only descriptive snapshot of the agent: identity (from `culture.yaml`), the
84
- verb surface, and the sibling-pattern artifacts the template carries. Accepts an
87
+ verb surface, and the sibling-pattern artifacts this repo carries. Accepts an
85
88
  ignored `target` so a stray path never hard-fails.
86
89
 
87
90
  ## Usage
@@ -1,7 +1,7 @@
1
1
  [project]
2
2
  name = "face-cli"
3
- version = "0.7.0"
4
- description = "A simulated face rendered in the browser that appears three-dimensional on a flat 2D screen and can look in any direction — an agent-drivable gaze and expression surface for robots, kiosks, and screens."
3
+ version = "0.7.1"
4
+ description = "Scaffold for a browser-rendered face that reads three-dimensional on a flat 2D screen and looks in any direction — an agent-drivable gaze surface for robots, kiosks, and screens. The renderer is not implemented yet; the CLI ships introspection verbs only."
5
5
  readme = "README.md"
6
6
  license = "Apache-2.0"
7
7
  requires-python = ">=3.12"
@@ -24,6 +24,30 @@ def test_no_args_prints_help(capsys: pytest.CaptureFixture[str]) -> None:
24
24
  assert "usage: face-cli" in capsys.readouterr().out
25
25
 
26
26
 
27
+ def test_root_json_without_verb_emits_command_map(capsys: pytest.CaptureFixture[str]) -> None:
28
+ """`face --json` (no verb) is the machine-readable analogue of printing help."""
29
+ rc = main(["--json"])
30
+ assert rc == 0
31
+ payload = json.loads(capsys.readouterr().out)
32
+ assert payload["tool"] == "face-cli"
33
+ assert payload["commands"]
34
+
35
+
36
+ @pytest.mark.parametrize(
37
+ "argv",
38
+ [["--json", "whoami"], ["whoami", "--json"]],
39
+ ids=["flag-before-verb", "flag-after-verb"],
40
+ )
41
+ def test_json_flag_position_does_not_change_meaning(
42
+ argv: list[str], capsys: pytest.CaptureFixture[str]
43
+ ) -> None:
44
+ """A leading `--json` must survive sub-parsing (each subparser re-defaults it)."""
45
+ rc = main(argv)
46
+ assert rc == 0
47
+ payload = json.loads(capsys.readouterr().out)
48
+ assert payload["nick"] == "face-cli"
49
+
50
+
27
51
  def test_unknown_command_errors(capsys: pytest.CaptureFixture[str]) -> None:
28
52
  with pytest.raises(SystemExit) as exc:
29
53
  main(["bogus"])
@@ -165,7 +165,7 @@ wheels = [
165
165
 
166
166
  [[package]]
167
167
  name = "face-cli"
168
- version = "0.7.0"
168
+ version = "0.7.1"
169
169
  source = { editable = "." }
170
170
 
171
171
  [package.dev-dependencies]
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes