skit-cli 0.0.1__tar.gz → 0.1.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 (42) hide show
  1. {skit_cli-0.0.1 → skit_cli-0.1.0}/PKG-INFO +10 -1
  2. {skit_cli-0.0.1 → skit_cli-0.1.0}/README.md +9 -0
  3. {skit_cli-0.0.1 → skit_cli-0.1.0}/pyproject.toml +1 -1
  4. skit_cli-0.1.0/src/skit/__init__.py +20 -0
  5. {skit_cli-0.0.1 → skit_cli-0.1.0}/src/skit/cli.py +55 -23
  6. {skit_cli-0.0.1 → skit_cli-0.1.0}/src/skit/config.py +22 -2
  7. {skit_cli-0.0.1 → skit_cli-0.1.0}/src/skit/flows.py +12 -0
  8. {skit_cli-0.0.1 → skit_cli-0.1.0}/src/skit/locales/skit.pot +322 -284
  9. skit_cli-0.1.0/src/skit/locales/zh_CN/LC_MESSAGES/skit.mo +0 -0
  10. {skit_cli-0.0.1 → skit_cli-0.1.0}/src/skit/locales/zh_CN/LC_MESSAGES/skit.po +326 -285
  11. skit_cli-0.1.0/src/skit/locales/zh_TW/LC_MESSAGES/skit.mo +0 -0
  12. {skit_cli-0.0.1 → skit_cli-0.1.0}/src/skit/locales/zh_TW/LC_MESSAGES/skit.po +326 -285
  13. {skit_cli-0.0.1 → skit_cli-0.1.0}/src/skit/tui.py +102 -7
  14. {skit_cli-0.0.1 → skit_cli-0.1.0}/src/skit/tui_add.py +98 -6
  15. {skit_cli-0.0.1 → skit_cli-0.1.0}/src/skit/tui_footer.py +34 -0
  16. {skit_cli-0.0.1 → skit_cli-0.1.0}/src/skit/tui_form.py +7 -2
  17. {skit_cli-0.0.1 → skit_cli-0.1.0}/src/skit/tui_prefs.py +29 -4
  18. {skit_cli-0.0.1 → skit_cli-0.1.0}/src/skit/tui_settings.py +5 -1
  19. skit_cli-0.0.1/src/skit/__init__.py +0 -3
  20. skit_cli-0.0.1/src/skit/locales/zh_CN/LC_MESSAGES/skit.mo +0 -0
  21. skit_cli-0.0.1/src/skit/locales/zh_TW/LC_MESSAGES/skit.mo +0 -0
  22. {skit_cli-0.0.1 → skit_cli-0.1.0}/LICENSE +0 -0
  23. {skit_cli-0.0.1 → skit_cli-0.1.0}/src/skit/analyzer.py +0 -0
  24. {skit_cli-0.0.1 → skit_cli-0.1.0}/src/skit/argspec.py +0 -0
  25. {skit_cli-0.0.1 → skit_cli-0.1.0}/src/skit/argstate.py +0 -0
  26. {skit_cli-0.0.1 → skit_cli-0.1.0}/src/skit/atomic.py +0 -0
  27. {skit_cli-0.0.1 → skit_cli-0.1.0}/src/skit/editor.py +0 -0
  28. {skit_cli-0.0.1 → skit_cli-0.1.0}/src/skit/i18n.py +0 -0
  29. {skit_cli-0.0.1 → skit_cli-0.1.0}/src/skit/inlineform.py +0 -0
  30. {skit_cli-0.0.1 → skit_cli-0.1.0}/src/skit/launcher.py +0 -0
  31. {skit_cli-0.0.1 → skit_cli-0.1.0}/src/skit/metawriter.py +0 -0
  32. {skit_cli-0.0.1 → skit_cli-0.1.0}/src/skit/models.py +0 -0
  33. {skit_cli-0.0.1 → skit_cli-0.1.0}/src/skit/paths.py +0 -0
  34. {skit_cli-0.0.1 → skit_cli-0.1.0}/src/skit/pep723.py +0 -0
  35. {skit_cli-0.0.1 → skit_cli-0.1.0}/src/skit/promptform.py +0 -0
  36. {skit_cli-0.0.1 → skit_cli-0.1.0}/src/skit/reconcile.py +0 -0
  37. {skit_cli-0.0.1 → skit_cli-0.1.0}/src/skit/shim.py +0 -0
  38. {skit_cli-0.0.1 → skit_cli-0.1.0}/src/skit/store.py +0 -0
  39. {skit_cli-0.0.1 → skit_cli-0.1.0}/src/skit/theme.py +0 -0
  40. {skit_cli-0.0.1 → skit_cli-0.1.0}/src/skit/tokens.py +0 -0
  41. {skit_cli-0.0.1 → skit_cli-0.1.0}/src/skit/tui_health.py +0 -0
  42. {skit_cli-0.0.1 → skit_cli-0.1.0}/src/skit/uvman.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: skit-cli
3
- Version: 0.0.1
3
+ Version: 0.1.0
4
4
  Summary: Script launcher and parameter manager: pick a script, fill in a form, run — no more opening an IDE just to tweak constants.
5
5
  Keywords: script,launcher,uv,tui,pep723,script-manager
6
6
  Author: Yi-Ting Chiu
@@ -102,6 +102,15 @@ uv tool install git+https://github.com/t41372/skit # latest development
102
102
  uvx --from git+https://github.com/t41372/skit skit --help # try it without installing
103
103
  ```
104
104
 
105
+ ## Update
106
+
107
+ ```bash
108
+ uv tool upgrade skit-cli # update to the latest release — also how you "check": it says up to date if you are
109
+ skit --version # the version you're on
110
+ ```
111
+
112
+ `uv tool upgrade` follows whatever source you installed from: PyPI installs track PyPI releases, `git+…` installs re-fetch the main branch.
113
+
105
114
  ## Uninstall
106
115
 
107
116
  ```bash
@@ -72,6 +72,15 @@ uv tool install git+https://github.com/t41372/skit # latest development
72
72
  uvx --from git+https://github.com/t41372/skit skit --help # try it without installing
73
73
  ```
74
74
 
75
+ ## Update
76
+
77
+ ```bash
78
+ uv tool upgrade skit-cli # update to the latest release — also how you "check": it says up to date if you are
79
+ skit --version # the version you're on
80
+ ```
81
+
82
+ `uv tool upgrade` follows whatever source you installed from: PyPI installs track PyPI releases, `git+…` installs re-fetch the main branch.
83
+
75
84
  ## Uninstall
76
85
 
77
86
  ```bash
@@ -2,7 +2,7 @@
2
2
  # Distribution name only ("skit" is admin-prohibited on PyPI); the import package,
3
3
  # CLI command, and config dirs all stay "skit".
4
4
  name = "skit-cli"
5
- version = "0.0.1"
5
+ version = "0.1.0"
6
6
  description = "Script launcher and parameter manager: pick a script, fill in a form, run — no more opening an IDE just to tweak constants."
7
7
  readme = "README.md"
8
8
  requires-python = ">=3.12"
@@ -0,0 +1,20 @@
1
+ """skit — a launcher and parameter manager for your scripts."""
2
+
3
+ import os
4
+ from importlib.metadata import PackageNotFoundError, version
5
+
6
+ # Textual ≥ 8.2.7 enables the kitty keyboard protocol's "report all keys" mode, which
7
+ # iTerm2 (3.6.x) implements in a way that fights the macOS IME: candidate-selection
8
+ # digits and Enter reach the app as raw key events and the composed CJK text is never
9
+ # delivered at all (iTerm2 issue 12906) — Chinese/Japanese/Korean typing breaks. No
10
+ # skit binding needs the protocol, so opt out before the first textual import
11
+ # (textual.constants reads this at import time). setdefault keeps an explicit user
12
+ # override (=0 re-enables) winning.
13
+ os.environ.setdefault("TEXTUAL_DISABLE_KITTY_KEY", "1")
14
+
15
+ # pyproject.toml is the single source of the version; installed distributions carry it
16
+ # as metadata (a wheel doesn't ship pyproject.toml), so read it from there.
17
+ try:
18
+ __version__ = version("skit-cli")
19
+ except PackageNotFoundError: # a bare checkout on sys.path, no installed dist
20
+ __version__ = "0.0.0+unknown"
@@ -536,16 +536,40 @@ def add(
536
536
  gettext("Can't read %(path)s: %(error)s")
537
537
  % {"path": str(resolved), "error": exc.strerror or str(exc)}
538
538
  ) from exc
539
- entry, summary_deps, summary_managed, summary_secrets = _onboard_python(
540
- Path(path),
541
- text,
542
- name=name,
543
- description=description,
544
- ref=ref,
545
- deps_opt=dep,
546
- python_opt=python,
547
- no_input=no_input,
548
- )
539
+ # Interactive + mini-form style: host the SAME review panel the TUI's
540
+ # `a` opens (flags prefill it). Pipes/CI/--no-input/form=plain keep the
541
+ # line-prompt path — the non-interactive contract is untouched.
542
+ if (
543
+ not no_input
544
+ and _is_interactive()
545
+ and os.environ.get("TERM") != "dumb"
546
+ and config.load_form() == "tui"
547
+ ):
548
+ from .tui_add import run_add_review
549
+
550
+ slug = run_add_review(
551
+ Path(path),
552
+ name=name,
553
+ description=description,
554
+ reference=ref,
555
+ deps=dep,
556
+ requires_python=python or "",
557
+ )
558
+ if slug is None:
559
+ console.print(f"[dim]{gettext('Cancelled — nothing was added.')}[/dim]")
560
+ raise typer.Exit(EXIT_CANCELLED)
561
+ entry = store.resolve(slug)
562
+ else:
563
+ entry, summary_deps, summary_managed, summary_secrets = _onboard_python(
564
+ Path(path),
565
+ text,
566
+ name=name,
567
+ description=description,
568
+ ref=ref,
569
+ deps_opt=dep,
570
+ python_opt=python,
571
+ no_input=no_input,
572
+ )
549
573
  except store.StoreError as exc:
550
574
  raise _fail(str(exc), 1) from exc
551
575
  _print_add_summary(entry, summary_deps, summary_managed, summary_secrets)
@@ -772,13 +796,8 @@ def _collect_values(
772
796
 
773
797
 
774
798
  # How a flows.RunOutcome failure maps to skit's exit-code contract (docker convention).
775
- _FAILURE_EXIT = {
776
- flows.FAIL_BAD_VALUE: EXIT_SKIT,
777
- flows.FAIL_DRIFT: EXIT_SKIT,
778
- flows.FAIL_LAUNCH: EXIT_SKIT,
779
- flows.FAIL_NOT_EXECUTABLE: EXIT_NOT_EXECUTABLE,
780
- flows.FAIL_MISSING: EXIT_NOT_FOUND,
781
- }
799
+ # The numbers live in flows so the TUI's exit-after-run path shares them.
800
+ _FAILURE_EXIT = flows.FAILURE_EXIT_CODES
782
801
 
783
802
 
784
803
  @app.command(
@@ -1461,7 +1480,7 @@ def doctor(
1461
1480
  # config (git-config grammar: bare = list, KEY = read, KEY VALUE = write)
1462
1481
  # --------------------------------------------------------------------------
1463
1482
 
1464
- _CONFIG_KEYS = ("lang", "editor", "mirror", "form")
1483
+ _CONFIG_KEYS = ("lang", "editor", "mirror", "form", "after_run")
1465
1484
 
1466
1485
 
1467
1486
  def _config_value(key: str) -> str:
@@ -1475,7 +1494,9 @@ def _config_value(key: str) -> str:
1475
1494
  if key == "mirror":
1476
1495
  m = config.load_mirror()
1477
1496
  return m.pypi if m.enabled else "off"
1478
- return config.load_form() # "form" — _CONFIG_KEYS guards the key set
1497
+ if key == "form":
1498
+ return config.load_form()
1499
+ return config.load_after_run() # "after_run" — _CONFIG_KEYS guards the key set
1479
1500
 
1480
1501
 
1481
1502
  def _config_set(key: str, value: str) -> None:
@@ -1499,22 +1520,33 @@ def _config_set(key: str, value: str) -> None:
1499
1520
  f"[red]{gettext('Unknown mirror: %(name)s. Choose from: %(names)s') % {'name': escape(value), 'names': choices}}[/red]"
1500
1521
  )
1501
1522
  raise typer.Exit(EXIT_USAGE)
1502
- else: # form
1523
+ elif key == "form":
1503
1524
  if value not in config.FORM_STYLES:
1504
1525
  err_console.print(
1505
1526
  f"[red]{gettext('Unknown form style: %(value)s. Choose from: tui, plain') % {'value': escape(value)}}[/red]"
1506
1527
  )
1507
1528
  raise typer.Exit(EXIT_USAGE)
1508
1529
  config.save_form(value)
1530
+ else: # after_run
1531
+ if value not in config.AFTER_RUN_MODES:
1532
+ err_console.print(
1533
+ f"[red]{gettext('Unknown after-run behavior: %(value)s. Choose from: exit, stay') % {'value': escape(value)}}[/red]"
1534
+ )
1535
+ raise typer.Exit(EXIT_USAGE)
1536
+ config.save_after_run(value)
1509
1537
 
1510
1538
 
1511
1539
  @app.command(
1512
1540
  "config",
1513
- help=gettext("Read or set skit's settings (language, editor, mirror, form style)."),
1514
- epilog=gettext("Examples: skit config · skit config lang zh-TW · skit config form plain"),
1541
+ help=gettext("Read or set skit's settings (language, editor, mirror, form style, after-run)."),
1542
+ epilog=gettext(
1543
+ "Examples: skit config · skit config lang zh-TW · skit config after_run stay"
1544
+ ),
1515
1545
  )
1516
1546
  def config_cmd(
1517
- key: str = typer.Argument(None, help=gettext("Setting name: lang / editor / mirror / form")),
1547
+ key: str = typer.Argument(
1548
+ None, help=gettext("Setting name: lang / editor / mirror / form / after_run")
1549
+ ),
1518
1550
  value: str = typer.Argument(
1519
1551
  None, help=gettext('New value (omit to read; lang also accepts "auto")')
1520
1552
  ),
@@ -136,8 +136,10 @@ FORM_STYLES = ("tui", "plain")
136
136
 
137
137
 
138
138
  def load_form() -> str:
139
- """Interactive-form style: "tui" (inline mini-form, the default) or "plain" (line
140
- prompts). Governs every interactive flow (run form, add panel), not just run."""
139
+ """Interactive-form style: "tui" (inline mini-form / review panel, the default) or
140
+ "plain" (line prompts). Consumed by the CLI's interactive flows `skit run`
141
+ parameter collection and `skit add`'s review panel; the TUI workbench always uses
142
+ its own full screens."""
141
143
  value = load_config().get("form", "") # pragma: no mutate — normalized below
142
144
  return value if value in FORM_STYLES else "tui"
143
145
 
@@ -149,6 +151,24 @@ def save_form(style: str) -> None:
149
151
  save_config(doc)
150
152
 
151
153
 
154
+ AFTER_RUN_MODES = ("exit", "stay")
155
+
156
+
157
+ def load_after_run() -> str:
158
+ """What the TUI does when a launched script finishes: "exit" (the default — skit is
159
+ a launcher, so it quits and passes the script's exit code through) or "stay"
160
+ (banner + Enter returns to the Library, the workbench loop)."""
161
+ value = load_config().get("after_run", "") # pragma: no mutate — normalized below
162
+ return value if value in AFTER_RUN_MODES else "exit"
163
+
164
+
165
+ def save_after_run(mode: str) -> None:
166
+ """Persist the after-run behavior, preserving every other key."""
167
+ doc = _load_config_for_save()
168
+ doc["after_run"] = mode
169
+ save_config(doc)
170
+
171
+
152
172
  @dataclass(frozen=True)
153
173
  class MirrorConfig:
154
174
  enabled: bool = False
@@ -485,6 +485,18 @@ FAIL_MISSING = "missing" # the launch target is gone from disk
485
485
  FAIL_NOT_EXECUTABLE = "not_executable" # an exe exists but isn't +x
486
486
  FAIL_LAUNCH = "launch" # any other launch failure
487
487
 
488
+ # Docker-convention process exit codes when the launch itself failed (the script's own
489
+ # exit code passes through untouched whenever it did run): skit failures are 125, an
490
+ # existing-but-unexecutable target 126, a missing target 127. Shared by `skit run` and
491
+ # the TUI's exit-after-run path so the contract can't fork.
492
+ FAILURE_EXIT_CODES = {
493
+ FAIL_BAD_VALUE: 125,
494
+ FAIL_DRIFT: 125,
495
+ FAIL_LAUNCH: 125,
496
+ FAIL_NOT_EXECUTABLE: 126,
497
+ FAIL_MISSING: 127,
498
+ }
499
+
488
500
 
489
501
  @dataclass
490
502
  class RunOutcome: