tipalti 0.0.1__py3-none-any.whl
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.
- tipalti/__init__.py +13 -0
- tipalti/__main__.py +10 -0
- tipalti/cli/__init__.py +84 -0
- tipalti/cli/_commands/__init__.py +0 -0
- tipalti/cli/_commands/explain.py +38 -0
- tipalti/cli/_commands/learn.py +82 -0
- tipalti/cli/_commands/whoami.py +29 -0
- tipalti/cli/_errors.py +41 -0
- tipalti/cli/_output.py +53 -0
- tipalti/explain/__init__.py +24 -0
- tipalti/explain/catalog.py +88 -0
- tipalti-0.0.1.dist-info/METADATA +63 -0
- tipalti-0.0.1.dist-info/RECORD +16 -0
- tipalti-0.0.1.dist-info/WHEEL +4 -0
- tipalti-0.0.1.dist-info/entry_points.txt +2 -0
- tipalti-0.0.1.dist-info/licenses/LICENSE +21 -0
tipalti/__init__.py
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"""tipalti — agent-first CLI package."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from importlib.metadata import PackageNotFoundError
|
|
6
|
+
from importlib.metadata import version as _pkg_version
|
|
7
|
+
|
|
8
|
+
try:
|
|
9
|
+
__version__ = _pkg_version("tipalti")
|
|
10
|
+
except PackageNotFoundError: # pragma: no cover
|
|
11
|
+
__version__ = "0.0.0"
|
|
12
|
+
|
|
13
|
+
__all__ = ["__version__"]
|
tipalti/__main__.py
ADDED
tipalti/cli/__init__.py
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
"""Unified CLI entry point for tipalti.
|
|
2
|
+
|
|
3
|
+
Noun-based command groups and globals are registered here. Top-level globals
|
|
4
|
+
(``learn``, ``explain``) live under :mod:`tipalti.cli._commands`; per-noun
|
|
5
|
+
groups follow the same pattern.
|
|
6
|
+
|
|
7
|
+
Error-propagation contract: every handler raises
|
|
8
|
+
:class:`tipalti.cli._errors.AfiError` on failure; :func:`main` catches it
|
|
9
|
+
via :func:`_dispatch` and routes through :mod:`tipalti.cli._output`.
|
|
10
|
+
Unknown exceptions are wrapped so no Python traceback leaks.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from __future__ import annotations
|
|
14
|
+
|
|
15
|
+
import argparse
|
|
16
|
+
import sys
|
|
17
|
+
|
|
18
|
+
from tipalti import __version__
|
|
19
|
+
from tipalti.cli._commands import explain as _explain_cmd
|
|
20
|
+
from tipalti.cli._commands import learn as _learn_cmd
|
|
21
|
+
from tipalti.cli._commands import whoami as _whoami_cmd
|
|
22
|
+
from tipalti.cli._errors import EXIT_USER_ERROR, AfiError
|
|
23
|
+
from tipalti.cli._output import emit_error
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class _ArgumentParser(argparse.ArgumentParser):
|
|
27
|
+
"""ArgumentParser that emits errors via our structured format."""
|
|
28
|
+
|
|
29
|
+
def error(self, message: str) -> None: # type: ignore[override]
|
|
30
|
+
err = AfiError(
|
|
31
|
+
code=EXIT_USER_ERROR,
|
|
32
|
+
message=message,
|
|
33
|
+
remediation=f"run '{self.prog} --help' to see valid arguments",
|
|
34
|
+
)
|
|
35
|
+
emit_error(err, json_mode=False)
|
|
36
|
+
raise SystemExit(err.code)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def _build_parser() -> argparse.ArgumentParser:
|
|
40
|
+
parser = _ArgumentParser(
|
|
41
|
+
prog="tipalti",
|
|
42
|
+
description="tipalti — agent-first CLI.",
|
|
43
|
+
)
|
|
44
|
+
parser.add_argument("--version", action="version", version=f"%(prog)s {__version__}")
|
|
45
|
+
sub = parser.add_subparsers(dest="command")
|
|
46
|
+
|
|
47
|
+
_learn_cmd.register(sub)
|
|
48
|
+
_explain_cmd.register(sub)
|
|
49
|
+
_whoami_cmd.register(sub)
|
|
50
|
+
# Register noun groups here:
|
|
51
|
+
# from tipalti.cli._commands import my_noun as _my_noun_group
|
|
52
|
+
# _my_noun_group.register(sub)
|
|
53
|
+
|
|
54
|
+
return parser
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def _dispatch(args: argparse.Namespace) -> int:
|
|
58
|
+
json_mode = bool(getattr(args, "json", False))
|
|
59
|
+
try:
|
|
60
|
+
return args.func(args)
|
|
61
|
+
except AfiError as err:
|
|
62
|
+
emit_error(err, json_mode=json_mode)
|
|
63
|
+
return err.code
|
|
64
|
+
except Exception as err: # noqa: BLE001 - last-resort
|
|
65
|
+
wrapped = AfiError(
|
|
66
|
+
code=EXIT_USER_ERROR,
|
|
67
|
+
message=f"unexpected: {err.__class__.__name__}: {err}",
|
|
68
|
+
remediation="file a bug",
|
|
69
|
+
)
|
|
70
|
+
emit_error(wrapped, json_mode=json_mode)
|
|
71
|
+
return wrapped.code
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def main(argv: list[str] | None = None) -> int:
|
|
75
|
+
parser = _build_parser()
|
|
76
|
+
args = parser.parse_args(argv)
|
|
77
|
+
if args.command is None:
|
|
78
|
+
parser.print_help()
|
|
79
|
+
return 0
|
|
80
|
+
return _dispatch(args)
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
if __name__ == "__main__":
|
|
84
|
+
sys.exit(main())
|
|
File without changes
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"""``tipalti explain <path>...`` — global markdown catalog lookup (stable-contract).
|
|
2
|
+
|
|
3
|
+
``explain`` is global (not nested under a noun). It takes zero or more path
|
|
4
|
+
tokens and resolves them via the catalog in :mod:`tipalti.explain`.
|
|
5
|
+
Unknown paths raise :class:`AfiError` with a remediation hint.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import argparse
|
|
11
|
+
|
|
12
|
+
from tipalti.cli._output import emit_result
|
|
13
|
+
from tipalti.explain import resolve
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def cmd_explain(args: argparse.Namespace) -> int:
|
|
17
|
+
path = tuple(args.path) if args.path else ()
|
|
18
|
+
markdown = resolve(path)
|
|
19
|
+
json_mode = bool(getattr(args, "json", False))
|
|
20
|
+
if json_mode:
|
|
21
|
+
emit_result({"path": list(path), "markdown": markdown}, json_mode=True)
|
|
22
|
+
else:
|
|
23
|
+
emit_result(markdown, json_mode=False)
|
|
24
|
+
return 0
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def register(sub: argparse._SubParsersAction) -> None:
|
|
28
|
+
p = sub.add_parser(
|
|
29
|
+
"explain",
|
|
30
|
+
help="Print markdown docs for a noun/verb path (e.g. 'tipalti explain cli foo').",
|
|
31
|
+
)
|
|
32
|
+
p.add_argument(
|
|
33
|
+
"path",
|
|
34
|
+
nargs="*",
|
|
35
|
+
help="Command path tokens; empty = root (same as 'tipalti').",
|
|
36
|
+
)
|
|
37
|
+
p.add_argument("--json", action="store_true", help="Emit structured JSON.")
|
|
38
|
+
p.set_defaults(func=cmd_explain)
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
"""``tipalti learn`` — the learnability affordance (shape-adapt)."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import argparse
|
|
6
|
+
|
|
7
|
+
from tipalti import __version__
|
|
8
|
+
from tipalti.cli._output import emit_result
|
|
9
|
+
|
|
10
|
+
_TEXT = """\
|
|
11
|
+
tipalti — CLI for Tipalti Solutions.
|
|
12
|
+
|
|
13
|
+
Purpose
|
|
14
|
+
-------
|
|
15
|
+
tipalti is a command-line interface for working with Tipalti Solutions.
|
|
16
|
+
The v0.0.1 surface ships only the agent-first affordances (`learn`,
|
|
17
|
+
`explain`) and an auth-probe stub (`whoami`); domain verbs that talk to
|
|
18
|
+
the Tipalti API land in subsequent releases.
|
|
19
|
+
|
|
20
|
+
Commands
|
|
21
|
+
--------
|
|
22
|
+
tipalti learn Print this self-teaching prompt. Supports --json.
|
|
23
|
+
tipalti explain <path>... Print markdown docs for any noun/verb path.
|
|
24
|
+
Supports --json.
|
|
25
|
+
tipalti whoami Print the active Tipalti principal. Stub for
|
|
26
|
+
now (always reports "unauthenticated") until
|
|
27
|
+
real Tipalti API auth is wired. Supports --json.
|
|
28
|
+
|
|
29
|
+
Machine-readable output
|
|
30
|
+
-----------------------
|
|
31
|
+
Every command that produces a listing or report supports --json. Errors in
|
|
32
|
+
JSON mode emit {"code", "message", "remediation"} to stderr. Stdout and
|
|
33
|
+
stderr are never mixed.
|
|
34
|
+
|
|
35
|
+
Exit-code policy
|
|
36
|
+
----------------
|
|
37
|
+
0 success
|
|
38
|
+
1 user-input error (bad flag, bad path, missing arg)
|
|
39
|
+
2 environment / setup error
|
|
40
|
+
3+ reserved
|
|
41
|
+
|
|
42
|
+
More detail
|
|
43
|
+
-----------
|
|
44
|
+
tipalti explain tipalti
|
|
45
|
+
"""
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def _as_json_payload() -> dict[str, object]:
|
|
49
|
+
return {
|
|
50
|
+
"tool": "tipalti",
|
|
51
|
+
"version": __version__,
|
|
52
|
+
"purpose": "CLI for Tipalti Solutions.",
|
|
53
|
+
"commands": [
|
|
54
|
+
{"path": ["learn"], "summary": "Self-teaching prompt."},
|
|
55
|
+
{"path": ["explain"], "summary": "Markdown docs by path."},
|
|
56
|
+
{"path": ["whoami"], "summary": "Auth probe (stub in v0.0.1)."},
|
|
57
|
+
],
|
|
58
|
+
"exit_codes": {
|
|
59
|
+
"0": "success",
|
|
60
|
+
"1": "user-input error",
|
|
61
|
+
"2": "environment/setup error",
|
|
62
|
+
},
|
|
63
|
+
"json_support": True,
|
|
64
|
+
"explain_pointer": "tipalti explain <path>",
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def cmd_learn(args: argparse.Namespace) -> int:
|
|
69
|
+
if getattr(args, "json", False):
|
|
70
|
+
emit_result(_as_json_payload(), json_mode=True)
|
|
71
|
+
else:
|
|
72
|
+
emit_result(_TEXT, json_mode=False)
|
|
73
|
+
return 0
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def register(sub: argparse._SubParsersAction) -> None:
|
|
77
|
+
p = sub.add_parser(
|
|
78
|
+
"learn",
|
|
79
|
+
help="Print a structured self-teaching prompt for agent consumers.",
|
|
80
|
+
)
|
|
81
|
+
p.add_argument("--json", action="store_true", help="Emit structured JSON.")
|
|
82
|
+
p.set_defaults(func=cmd_learn)
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"""``tipalti whoami`` — auth probe stub (Tipalti-specific).
|
|
2
|
+
|
|
3
|
+
Returns ``unauthenticated`` until real Tipalti API auth lands. Supports
|
|
4
|
+
``--json``. Exit code is always ``0`` (probe, not gate).
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import argparse
|
|
10
|
+
|
|
11
|
+
from tipalti.cli._output import emit_result
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def cmd_whoami(args: argparse.Namespace) -> int:
|
|
15
|
+
payload: dict[str, object] = {"status": "unauthenticated", "principal": None}
|
|
16
|
+
if getattr(args, "json", False):
|
|
17
|
+
emit_result(payload, json_mode=True)
|
|
18
|
+
else:
|
|
19
|
+
emit_result("unauthenticated", json_mode=False)
|
|
20
|
+
return 0
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def register(sub: argparse._SubParsersAction) -> None:
|
|
24
|
+
p = sub.add_parser(
|
|
25
|
+
"whoami",
|
|
26
|
+
help="Print the active Tipalti principal (stub in v0.0.1).",
|
|
27
|
+
)
|
|
28
|
+
p.add_argument("--json", action="store_true", help="Emit structured JSON.")
|
|
29
|
+
p.set_defaults(func=cmd_whoami)
|
tipalti/cli/_errors.py
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"""AfiError and exit-code policy (stable-contract — copy verbatim).
|
|
2
|
+
|
|
3
|
+
Every failure inside tipalti raises :class:`AfiError`. The CLI entry
|
|
4
|
+
point catches it and exits with :attr:`AfiError.code`. Guarantees:
|
|
5
|
+
|
|
6
|
+
* no Python traceback leaks to stderr;
|
|
7
|
+
* every error has shape ``{code, message, remediation}``;
|
|
8
|
+
* the exit-code policy is centralised.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
from dataclasses import dataclass
|
|
14
|
+
|
|
15
|
+
# Exit-code policy (documented in ``tipalti learn`` output).
|
|
16
|
+
# 0 = success
|
|
17
|
+
# 1 = user-input error (bad flag, bad path, missing arg)
|
|
18
|
+
# 2 = environment / setup error
|
|
19
|
+
# 3+ = reserved
|
|
20
|
+
EXIT_SUCCESS = 0
|
|
21
|
+
EXIT_USER_ERROR = 1
|
|
22
|
+
EXIT_ENV_ERROR = 2
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
@dataclass
|
|
26
|
+
class AfiError(Exception):
|
|
27
|
+
"""Structured error with a remediation hint for agents."""
|
|
28
|
+
|
|
29
|
+
code: int
|
|
30
|
+
message: str
|
|
31
|
+
remediation: str = ""
|
|
32
|
+
|
|
33
|
+
def __post_init__(self) -> None:
|
|
34
|
+
super().__init__(self.message)
|
|
35
|
+
|
|
36
|
+
def to_dict(self) -> dict[str, object]:
|
|
37
|
+
return {
|
|
38
|
+
"code": self.code,
|
|
39
|
+
"message": self.message,
|
|
40
|
+
"remediation": self.remediation,
|
|
41
|
+
}
|
tipalti/cli/_output.py
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"""stdout / stderr helpers with a strict split (stable-contract).
|
|
2
|
+
|
|
3
|
+
Rule: **results go to stdout, diagnostics and errors go to stderr.** Agents
|
|
4
|
+
parsing output can rely on this invariant. JSON mode routes structured
|
|
5
|
+
payloads to the same streams — never mixes them.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import json
|
|
11
|
+
import sys
|
|
12
|
+
from typing import Any, TextIO
|
|
13
|
+
|
|
14
|
+
from tipalti.cli._errors import AfiError
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def emit_result(data: Any, *, json_mode: bool, stream: TextIO | None = None) -> None:
|
|
18
|
+
"""Write a command result to stdout (or ``stream``)."""
|
|
19
|
+
s = stream if stream is not None else sys.stdout
|
|
20
|
+
if json_mode:
|
|
21
|
+
json.dump(data, s, ensure_ascii=False)
|
|
22
|
+
s.write("\n")
|
|
23
|
+
return
|
|
24
|
+
text = data if isinstance(data, str) else str(data)
|
|
25
|
+
s.write(text)
|
|
26
|
+
if not text.endswith("\n"):
|
|
27
|
+
s.write("\n")
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def emit_error(err: AfiError, *, json_mode: bool, stream: TextIO | None = None) -> None:
|
|
31
|
+
"""Write an :class:`AfiError` to stderr.
|
|
32
|
+
|
|
33
|
+
Text mode renders as two lines when a remediation is present::
|
|
34
|
+
|
|
35
|
+
error: <message>
|
|
36
|
+
hint: <remediation>
|
|
37
|
+
|
|
38
|
+
The ``hint:`` prefix is required by the agent-first error rubric.
|
|
39
|
+
"""
|
|
40
|
+
s = stream if stream is not None else sys.stderr
|
|
41
|
+
if json_mode:
|
|
42
|
+
json.dump(err.to_dict(), s, ensure_ascii=False)
|
|
43
|
+
s.write("\n")
|
|
44
|
+
return
|
|
45
|
+
s.write(f"error: {err.message}\n")
|
|
46
|
+
if err.remediation:
|
|
47
|
+
s.write(f"hint: {err.remediation}\n")
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def emit_diagnostic(message: str, *, stream: TextIO | None = None) -> None:
|
|
51
|
+
"""Write a human diagnostic (progress, summary) to stderr."""
|
|
52
|
+
s = stream if stream is not None else sys.stderr
|
|
53
|
+
s.write(message if message.endswith("\n") else message + "\n")
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"""Explain catalog — markdown keyed by command-path tuples (stable-contract).
|
|
2
|
+
|
|
3
|
+
Every noun/verb registered in the CLI should have a catalog entry.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
from tipalti.cli._errors import EXIT_USER_ERROR, AfiError
|
|
9
|
+
from tipalti.explain.catalog import ENTRIES
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def resolve(path: tuple[str, ...]) -> str:
|
|
13
|
+
if path in ENTRIES:
|
|
14
|
+
return ENTRIES[path]
|
|
15
|
+
display = " ".join(path) if path else "<root>"
|
|
16
|
+
raise AfiError(
|
|
17
|
+
code=EXIT_USER_ERROR,
|
|
18
|
+
message=f"no explain entry for: {display}",
|
|
19
|
+
remediation="list known entries with: tipalti explain tipalti",
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def known_paths() -> list[tuple[str, ...]]:
|
|
24
|
+
return list(ENTRIES.keys())
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
"""Markdown catalog for ``tipalti explain <path>``.
|
|
2
|
+
|
|
3
|
+
Each entry is verbatim markdown. Keys are command-path tuples. The empty
|
|
4
|
+
tuple and ``("tipalti",)`` both resolve to the root entry.
|
|
5
|
+
|
|
6
|
+
Keep bodies self-contained: an agent reading one entry should get enough
|
|
7
|
+
context without chaining reads.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
_ROOT = """\
|
|
13
|
+
# tipalti
|
|
14
|
+
|
|
15
|
+
`tipalti` is the command-line interface for Tipalti Solutions. v0.0.1
|
|
16
|
+
ships the agent-first affordances (`learn`, `explain`) and an auth-probe
|
|
17
|
+
stub (`whoami`); domain verbs that exercise the Tipalti API land in later
|
|
18
|
+
releases. The CLI is scaffolded from the AgentCulture sibling pattern.
|
|
19
|
+
|
|
20
|
+
## Verbs
|
|
21
|
+
|
|
22
|
+
- `tipalti learn` — structured self-teaching prompt.
|
|
23
|
+
- `tipalti explain <path>` — markdown docs for any noun/verb.
|
|
24
|
+
- `tipalti whoami` — auth probe stub (returns `unauthenticated` for now).
|
|
25
|
+
|
|
26
|
+
## Exit-code policy
|
|
27
|
+
|
|
28
|
+
- `0` success
|
|
29
|
+
- `1` user-input error
|
|
30
|
+
- `2` environment / setup error
|
|
31
|
+
- `3+` reserved
|
|
32
|
+
|
|
33
|
+
## See also
|
|
34
|
+
|
|
35
|
+
- `tipalti explain learn`
|
|
36
|
+
- `tipalti explain explain`
|
|
37
|
+
- `tipalti explain whoami`
|
|
38
|
+
"""
|
|
39
|
+
|
|
40
|
+
_LEARN = """\
|
|
41
|
+
# tipalti learn
|
|
42
|
+
|
|
43
|
+
Prints a structured self-teaching prompt covering tipalti's purpose,
|
|
44
|
+
command map, exit-code policy, `--json` support, and `explain` pointer.
|
|
45
|
+
|
|
46
|
+
## Usage
|
|
47
|
+
|
|
48
|
+
tipalti learn
|
|
49
|
+
tipalti learn --json
|
|
50
|
+
"""
|
|
51
|
+
|
|
52
|
+
_EXPLAIN = """\
|
|
53
|
+
# tipalti explain <path>
|
|
54
|
+
|
|
55
|
+
Prints markdown documentation for any noun/verb path. Unlike `--help`
|
|
56
|
+
(terse, positional), `explain` is global and addressable by path.
|
|
57
|
+
|
|
58
|
+
## Usage
|
|
59
|
+
|
|
60
|
+
tipalti explain tipalti
|
|
61
|
+
tipalti explain learn
|
|
62
|
+
tipalti explain --json <path>
|
|
63
|
+
"""
|
|
64
|
+
|
|
65
|
+
_WHOAMI = """\
|
|
66
|
+
# tipalti whoami
|
|
67
|
+
|
|
68
|
+
Auth probe. Reports the active Tipalti principal — or
|
|
69
|
+
`unauthenticated` when no credentials are configured.
|
|
70
|
+
|
|
71
|
+
In v0.0.1 this is a stub: it always reports `unauthenticated` until real
|
|
72
|
+
Tipalti API authentication lands. Exit code is always `0` (probe, not
|
|
73
|
+
gate).
|
|
74
|
+
|
|
75
|
+
## Usage
|
|
76
|
+
|
|
77
|
+
tipalti whoami
|
|
78
|
+
tipalti whoami --json
|
|
79
|
+
"""
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
ENTRIES: dict[tuple[str, ...], str] = {
|
|
83
|
+
(): _ROOT,
|
|
84
|
+
("tipalti",): _ROOT,
|
|
85
|
+
("learn",): _LEARN,
|
|
86
|
+
("explain",): _EXPLAIN,
|
|
87
|
+
("whoami",): _WHOAMI,
|
|
88
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: tipalti
|
|
3
|
+
Version: 0.0.1
|
|
4
|
+
Summary: Tipalti — CLI for Tipalti Solutions.
|
|
5
|
+
Project-URL: Homepage, https://github.com/agentculture/tipalti
|
|
6
|
+
Project-URL: Issues, https://github.com/agentculture/tipalti/issues
|
|
7
|
+
Author: AgentCulture
|
|
8
|
+
License-Expression: MIT
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Classifier: Development Status :: 3 - Alpha
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
14
|
+
Classifier: Topic :: Software Development
|
|
15
|
+
Requires-Python: >=3.12
|
|
16
|
+
Description-Content-Type: text/markdown
|
|
17
|
+
|
|
18
|
+
# tipalti
|
|
19
|
+
|
|
20
|
+
CLI for Tipalti Solutions — scaffolded from the AgentCulture sibling pattern.
|
|
21
|
+
|
|
22
|
+
## Install
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
uv tool install tipalti
|
|
26
|
+
# or
|
|
27
|
+
pipx install tipalti
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Quickstart
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
tipalti --version # 0.0.1
|
|
34
|
+
tipalti learn # structured self-teaching prompt for agents
|
|
35
|
+
tipalti learn --json # machine-readable form
|
|
36
|
+
tipalti explain tipalti # root markdown doc
|
|
37
|
+
tipalti explain whoami # docs for the whoami verb
|
|
38
|
+
tipalti whoami # auth probe (v0.0.1: always "unauthenticated")
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Status
|
|
42
|
+
|
|
43
|
+
Pre-1.0. v0.0.1 ships only the agent-first affordances (`learn`, `explain`)
|
|
44
|
+
and an auth-probe stub (`whoami`). Domain verbs that exercise the Tipalti
|
|
45
|
+
API land in later releases.
|
|
46
|
+
|
|
47
|
+
## Development
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
uv sync
|
|
51
|
+
uv run pytest -n auto -v
|
|
52
|
+
uv run tipalti --version
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
See [`CLAUDE.md`](CLAUDE.md) for the full project shape, conventions, and
|
|
56
|
+
publish flow. The repo follows the AgentCulture sibling pattern — see
|
|
57
|
+
[`agentculture/steward`](https://github.com/agentculture/steward) and
|
|
58
|
+
[`agentculture/afi-cli`](https://github.com/agentculture/afi-cli) for the
|
|
59
|
+
canonical templates.
|
|
60
|
+
|
|
61
|
+
## License
|
|
62
|
+
|
|
63
|
+
MIT — see [`LICENSE`](LICENSE).
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
tipalti/__init__.py,sha256=-H-5b8b8V2Rn7I1E3ZlJze5KLw2yr-Nf8r1F7S2h5Ok,337
|
|
2
|
+
tipalti/__main__.py,sha256=Bl8Q62cwGXpU4pEJiFj06w0B6c0hwsFFxxZLP0eYsqE,172
|
|
3
|
+
tipalti/cli/__init__.py,sha256=CEJpNAtRxtgGL1tDHOmE6yzQ4X2ZTeskX5n4mNVhixk,2653
|
|
4
|
+
tipalti/cli/_errors.py,sha256=q7hKpKRQOFJz7fT-gb6xUQFZJvtHfT7kmZoA7tP15zI,1082
|
|
5
|
+
tipalti/cli/_output.py,sha256=k0qJSqklpjLkyaxQUxnIcengu6zV9pkHlkDMHxl9JEw,1707
|
|
6
|
+
tipalti/cli/_commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
7
|
+
tipalti/cli/_commands/explain.py,sha256=9SyHgKlg4mxplWBHFKxVnAP41nT8h4MPKh0-qzoiHJ8,1228
|
|
8
|
+
tipalti/cli/_commands/learn.py,sha256=j3JEi1ZnuhT98ik0Jpk6Z70gZR8piZIhM7gMHpSTCjc,2553
|
|
9
|
+
tipalti/cli/_commands/whoami.py,sha256=yhScSykttCUkJkHOI5-d0EhR_cuin0R3ZPfP--TQJCI,878
|
|
10
|
+
tipalti/explain/__init__.py,sha256=fKs4EXaxOjSxXcu5W5IJWvDj-DGVuY9LPjh3wek_zSU,698
|
|
11
|
+
tipalti/explain/catalog.py,sha256=szniTmRNqGqJBdXMF3-VfYzaVsCEKplTjtKjrRS3BMI,2086
|
|
12
|
+
tipalti-0.0.1.dist-info/METADATA,sha256=m2HKSLYwrJZJr8Trtyzykwfv1PTUpbLYg-o-oX8h0b8,1764
|
|
13
|
+
tipalti-0.0.1.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
|
|
14
|
+
tipalti-0.0.1.dist-info/entry_points.txt,sha256=0m3thyJ9MSyzfW-42hHKXEO6s4etTBgyf4NmSSM5vyg,45
|
|
15
|
+
tipalti-0.0.1.dist-info/licenses/LICENSE,sha256=d-o3g1Varo3ruZBbZUlkdoDVJBoIhutjarID0c0OIyU,1069
|
|
16
|
+
tipalti-0.0.1.dist-info/RECORD,,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 agentculture
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|