pocketsync 0.10.0__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.
- pocketsync/__init__.py +13 -0
- pocketsync/__main__.py +10 -0
- pocketsync/cli/__init__.py +136 -0
- pocketsync/cli/_commands/__init__.py +1 -0
- pocketsync/cli/_commands/cli.py +43 -0
- pocketsync/cli/_commands/doctor.py +194 -0
- pocketsync/cli/_commands/explain.py +38 -0
- pocketsync/cli/_commands/learn.py +88 -0
- pocketsync/cli/_commands/overview.py +112 -0
- pocketsync/cli/_commands/whoami.py +106 -0
- pocketsync/cli/_errors.py +42 -0
- pocketsync/cli/_output.py +53 -0
- pocketsync/explain/__init__.py +24 -0
- pocketsync/explain/catalog.py +135 -0
- pocketsync-0.10.0.dist-info/METADATA +157 -0
- pocketsync-0.10.0.dist-info/RECORD +19 -0
- pocketsync-0.10.0.dist-info/WHEEL +4 -0
- pocketsync-0.10.0.dist-info/entry_points.txt +2 -0
- pocketsync-0.10.0.dist-info/licenses/LICENSE +201 -0
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
"""``pocketsync whoami`` — the smallest identity probe.
|
|
2
|
+
|
|
3
|
+
Reports the agent's identity as declared in ``culture.yaml``: its nick
|
|
4
|
+
(``suffix``), the backend it runs on, and the served model (if any) — plus the
|
|
5
|
+
package version. Read-only; touches nothing but its own ``culture.yaml``.
|
|
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.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
import argparse
|
|
14
|
+
from pathlib import Path
|
|
15
|
+
|
|
16
|
+
from pocketsync import __version__
|
|
17
|
+
from pocketsync.cli._output import emit_result
|
|
18
|
+
|
|
19
|
+
_FALLBACK_NICK = "pocketsync"
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def find_culture_yaml() -> Path | None:
|
|
23
|
+
"""Locate this agent's own ``culture.yaml`` by walking up from this module.
|
|
24
|
+
|
|
25
|
+
The identity must be the agent's own, not whatever ``culture.yaml`` happens
|
|
26
|
+
to sit in the caller's current working directory. In an editable / source
|
|
27
|
+
install, walking up from ``__file__`` finds the repo root; in a wheel
|
|
28
|
+
install no ``culture.yaml`` ships alongside the package and the caller falls
|
|
29
|
+
back to the literal defaults.
|
|
30
|
+
"""
|
|
31
|
+
here = Path(__file__).resolve()
|
|
32
|
+
for parent in here.parents:
|
|
33
|
+
candidate = parent / "culture.yaml"
|
|
34
|
+
if candidate.is_file():
|
|
35
|
+
return candidate
|
|
36
|
+
return None
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def read_agent_fields() -> dict[str, str]:
|
|
40
|
+
"""Return ``suffix``/``backend``/``model`` from the first agent block.
|
|
41
|
+
|
|
42
|
+
Parsed without a YAML dependency to keep the runtime deps empty. Reads
|
|
43
|
+
top-level ``key: value`` lines within the first agent entry; anything
|
|
44
|
+
fancier than the documented shape falls back to the defaults below.
|
|
45
|
+
"""
|
|
46
|
+
fields = {"nick": _FALLBACK_NICK, "backend": "unknown", "model": "unknown"}
|
|
47
|
+
cfg = find_culture_yaml()
|
|
48
|
+
if cfg is None:
|
|
49
|
+
return fields
|
|
50
|
+
try:
|
|
51
|
+
text = cfg.read_text(encoding="utf-8")
|
|
52
|
+
except OSError:
|
|
53
|
+
return fields
|
|
54
|
+
seen_agent = False
|
|
55
|
+
for line in text.splitlines():
|
|
56
|
+
stripped = line.strip()
|
|
57
|
+
if stripped.startswith(("- suffix:", "suffix:")):
|
|
58
|
+
if seen_agent: # second agent block — stop at the first
|
|
59
|
+
break
|
|
60
|
+
seen_agent = True
|
|
61
|
+
fields["nick"] = _scalar(stripped, "suffix")
|
|
62
|
+
elif seen_agent and stripped.startswith("backend:"):
|
|
63
|
+
fields["backend"] = _scalar(stripped, "backend")
|
|
64
|
+
elif seen_agent and stripped.startswith("model:"):
|
|
65
|
+
fields["model"] = _scalar(stripped, "model")
|
|
66
|
+
return fields
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def _scalar(line: str, key: str) -> str:
|
|
70
|
+
"""Extract the scalar after ``key:`` from a ``culture.yaml`` line."""
|
|
71
|
+
_, _, value = line.partition(f"{key}:")
|
|
72
|
+
return value.strip().strip("'\"") or "unknown"
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def report() -> dict[str, object]:
|
|
76
|
+
fields = read_agent_fields()
|
|
77
|
+
return {
|
|
78
|
+
"nick": fields["nick"],
|
|
79
|
+
"version": __version__,
|
|
80
|
+
"backend": fields["backend"],
|
|
81
|
+
"model": fields["model"],
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def cmd_whoami(args: argparse.Namespace) -> None:
|
|
86
|
+
identity = report()
|
|
87
|
+
json_mode = bool(getattr(args, "json", False))
|
|
88
|
+
if json_mode:
|
|
89
|
+
emit_result(identity, json_mode=True)
|
|
90
|
+
return
|
|
91
|
+
text = (
|
|
92
|
+
f"nick: {identity['nick']}\n"
|
|
93
|
+
f"version: {identity['version']}\n"
|
|
94
|
+
f"backend: {identity['backend']}\n"
|
|
95
|
+
f"model: {identity['model']}"
|
|
96
|
+
)
|
|
97
|
+
emit_result(text, json_mode=False)
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def register(sub: argparse._SubParsersAction) -> None:
|
|
101
|
+
p = sub.add_parser(
|
|
102
|
+
"whoami",
|
|
103
|
+
help="Report this agent's nick, version, backend, and served model.",
|
|
104
|
+
)
|
|
105
|
+
p.add_argument("--json", action="store_true", help="Emit structured JSON.")
|
|
106
|
+
p.set_defaults(func=cmd_whoami)
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"""CliError and exit-code policy (stable-contract).
|
|
2
|
+
|
|
3
|
+
Every failure inside pocketsync raises :class:`CliError`. The
|
|
4
|
+
top-level ``main()`` catches it, formats via :mod:`pocketsync.cli._output`,
|
|
5
|
+
and exits with :attr:`CliError.code`. This guarantees:
|
|
6
|
+
|
|
7
|
+
* no Python traceback leaks to stderr (the agent-first error contract);
|
|
8
|
+
* every error has a structured shape ``{code, message, remediation}``;
|
|
9
|
+
* the exit-code policy is centralised in one place.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
|
|
14
|
+
from dataclasses import dataclass
|
|
15
|
+
|
|
16
|
+
# Exit-code policy. Documented in ``pocketsync learn`` output.
|
|
17
|
+
# 0 = success
|
|
18
|
+
# 1 = user-input error (bad flag, missing required arg, unknown path)
|
|
19
|
+
# 2 = environment / setup error (tool not installed, file unreadable)
|
|
20
|
+
# 3+ = reserved for future categorisation
|
|
21
|
+
EXIT_SUCCESS = 0
|
|
22
|
+
EXIT_USER_ERROR = 1
|
|
23
|
+
EXIT_ENV_ERROR = 2
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
@dataclass
|
|
27
|
+
class CliError(Exception):
|
|
28
|
+
"""Structured error raised within the CLI; carries a remediation hint for agents."""
|
|
29
|
+
|
|
30
|
+
code: int
|
|
31
|
+
message: str
|
|
32
|
+
remediation: str = ""
|
|
33
|
+
|
|
34
|
+
def __post_init__(self) -> None:
|
|
35
|
+
super().__init__(self.message)
|
|
36
|
+
|
|
37
|
+
def to_dict(self) -> dict[str, object]:
|
|
38
|
+
return {
|
|
39
|
+
"code": self.code,
|
|
40
|
+
"message": self.message,
|
|
41
|
+
"remediation": self.remediation,
|
|
42
|
+
}
|
|
@@ -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 pocketsync.cli._errors import CliError
|
|
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: CliError, *, json_mode: bool, stream: TextIO | None = None) -> None:
|
|
31
|
+
"""Write a :class:`CliError` 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 pocketsync.cli._errors import EXIT_USER_ERROR, CliError
|
|
9
|
+
from pocketsync.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 CliError(
|
|
17
|
+
code=EXIT_USER_ERROR,
|
|
18
|
+
message=f"no explain entry for: {display}",
|
|
19
|
+
remediation="list entries with: pocketsync explain pocketsync",
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def known_paths() -> list[tuple[str, ...]]:
|
|
24
|
+
return list(ENTRIES.keys())
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
"""Markdown catalog for ``pocketsync explain <path>``.
|
|
2
|
+
|
|
3
|
+
Each entry is verbatim markdown. Keys are command-path tuples. The empty tuple
|
|
4
|
+
and ``("pocketsync",)`` 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
|
+
# pocketsync
|
|
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.
|
|
20
|
+
|
|
21
|
+
## Verbs
|
|
22
|
+
|
|
23
|
+
- `pocketsync whoami` — identity probe from `culture.yaml`.
|
|
24
|
+
- `pocketsync learn` — structured self-teaching prompt.
|
|
25
|
+
- `pocketsync explain <path>` — markdown docs for any noun/verb.
|
|
26
|
+
- `pocketsync overview` — descriptive snapshot of the agent.
|
|
27
|
+
- `pocketsync doctor` — check the agent-identity invariants.
|
|
28
|
+
- `pocketsync cli overview` — describe the CLI surface.
|
|
29
|
+
|
|
30
|
+
## Exit-code policy
|
|
31
|
+
|
|
32
|
+
- `0` success
|
|
33
|
+
- `1` user-input error
|
|
34
|
+
- `2` environment / setup error
|
|
35
|
+
- `3+` reserved
|
|
36
|
+
|
|
37
|
+
## See also
|
|
38
|
+
|
|
39
|
+
- `pocketsync explain whoami`
|
|
40
|
+
- `pocketsync explain doctor`
|
|
41
|
+
"""
|
|
42
|
+
|
|
43
|
+
_WHOAMI = """\
|
|
44
|
+
# pocketsync whoami
|
|
45
|
+
|
|
46
|
+
Reports the agent's identity from `culture.yaml`: nick (`suffix`), backend,
|
|
47
|
+
served model, and the package version. Read-only.
|
|
48
|
+
|
|
49
|
+
## Usage
|
|
50
|
+
|
|
51
|
+
pocketsync whoami
|
|
52
|
+
pocketsync whoami --json
|
|
53
|
+
"""
|
|
54
|
+
|
|
55
|
+
_LEARN = """\
|
|
56
|
+
# pocketsync learn
|
|
57
|
+
|
|
58
|
+
Prints a structured self-teaching prompt covering purpose, command map,
|
|
59
|
+
exit-code policy, `--json` support, and the `explain` pointer.
|
|
60
|
+
|
|
61
|
+
## Usage
|
|
62
|
+
|
|
63
|
+
pocketsync learn
|
|
64
|
+
pocketsync learn --json
|
|
65
|
+
"""
|
|
66
|
+
|
|
67
|
+
_EXPLAIN = """\
|
|
68
|
+
# pocketsync explain <path>
|
|
69
|
+
|
|
70
|
+
Prints markdown documentation for any noun/verb path. Unlike `--help` (terse,
|
|
71
|
+
positional), `explain` is global and addressable by path.
|
|
72
|
+
|
|
73
|
+
## Usage
|
|
74
|
+
|
|
75
|
+
pocketsync explain pocketsync
|
|
76
|
+
pocketsync explain whoami
|
|
77
|
+
pocketsync explain --json <path>
|
|
78
|
+
"""
|
|
79
|
+
|
|
80
|
+
_OVERVIEW = """\
|
|
81
|
+
# pocketsync overview
|
|
82
|
+
|
|
83
|
+
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
|
|
85
|
+
ignored `target` so a stray path never hard-fails.
|
|
86
|
+
|
|
87
|
+
## Usage
|
|
88
|
+
|
|
89
|
+
pocketsync overview
|
|
90
|
+
pocketsync overview --json
|
|
91
|
+
"""
|
|
92
|
+
|
|
93
|
+
_DOCTOR = """\
|
|
94
|
+
# pocketsync doctor
|
|
95
|
+
|
|
96
|
+
Checks the agent-identity invariants `steward doctor` verifies:
|
|
97
|
+
prompt-file-present and backend-consistency (`claude` → `CLAUDE.md`), plus a
|
|
98
|
+
skills-present check. Exits 1 when unhealthy.
|
|
99
|
+
|
|
100
|
+
prompt-file-present requires the *resident* prompt the declared backend
|
|
101
|
+
actually reads. Other harness prompt files recognized under the same backend
|
|
102
|
+
name (`AGENTS.override.md`, `.pi/SYSTEM.md`, `QWEN.md`) belong to
|
|
103
|
+
interactively available harnesses the mesh daemon never loads; they are
|
|
104
|
+
reported by the informational harness-prompts check and never substituted.
|
|
105
|
+
|
|
106
|
+
## Usage
|
|
107
|
+
|
|
108
|
+
pocketsync doctor
|
|
109
|
+
pocketsync doctor --json
|
|
110
|
+
"""
|
|
111
|
+
|
|
112
|
+
_CLI = """\
|
|
113
|
+
# pocketsync cli
|
|
114
|
+
|
|
115
|
+
Noun group for CLI-surface introspection. `cli overview` describes the CLI
|
|
116
|
+
itself (distinct from the global `overview`, which describes the agent).
|
|
117
|
+
|
|
118
|
+
## Usage
|
|
119
|
+
|
|
120
|
+
pocketsync cli overview
|
|
121
|
+
pocketsync cli overview --json
|
|
122
|
+
"""
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
ENTRIES: dict[tuple[str, ...], str] = {
|
|
126
|
+
(): _ROOT,
|
|
127
|
+
("pocketsync",): _ROOT,
|
|
128
|
+
("whoami",): _WHOAMI,
|
|
129
|
+
("learn",): _LEARN,
|
|
130
|
+
("explain",): _EXPLAIN,
|
|
131
|
+
("overview",): _OVERVIEW,
|
|
132
|
+
("doctor",): _DOCTOR,
|
|
133
|
+
("cli",): _CLI,
|
|
134
|
+
("cli", "overview"): _CLI,
|
|
135
|
+
}
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: pocketsync
|
|
3
|
+
Version: 0.10.0
|
|
4
|
+
Summary: Sync Pocket voice-recorder recordings, transcripts (STT) and summaries from the Pocket cloud to local disk or S3 - a durable landing zone that culture-rules workflows fan out to anywhere.
|
|
5
|
+
Project-URL: Homepage, https://github.com/agentculture/pocketsync
|
|
6
|
+
Project-URL: Issues, https://github.com/agentculture/pocketsync/issues
|
|
7
|
+
Author: AgentCulture
|
|
8
|
+
License-Expression: Apache-2.0
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Classifier: Development Status :: 3 - Alpha
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: License :: OSI Approved :: Apache Software 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
|
+
# pocketsync
|
|
19
|
+
|
|
20
|
+
Sync Pocket voice-recorder recordings, transcripts (STT) and summaries from the Pocket cloud to local disk or S3 - a durable landing zone that culture-rules workflows fan out to anywhere.
|
|
21
|
+
|
|
22
|
+
## What you get
|
|
23
|
+
|
|
24
|
+
- **An agent-first CLI** cited from [teken](https://github.com/agentculture/teken)
|
|
25
|
+
(`afi-cli`) — the runtime package has no third-party dependencies.
|
|
26
|
+
- **A mesh identity** — `culture.yaml` (`suffix` + `backend`) and the matching
|
|
27
|
+
resident prompt file (`CLAUDE.md`, since this template runs
|
|
28
|
+
`backend: claude`). The mesh resident is one of **two separate
|
|
29
|
+
selections** over this clone — see
|
|
30
|
+
[Two selections, not one](#two-selections-not-one) below.
|
|
31
|
+
- **Four harness prompt files**, one per agent harness, each read by exactly
|
|
32
|
+
one of them (see [Prompt files by harness](#prompt-files-by-harness) below).
|
|
33
|
+
All four harnesses are usable interactively regardless of which one
|
|
34
|
+
`culture.yaml` names as the mesh resident.
|
|
35
|
+
- **The canonical guildmaster skill kit** (11 skills) under `.claude/skills/`,
|
|
36
|
+
vendored cite-don't-import. See [`docs/skill-sources.md`](docs/skill-sources.md).
|
|
37
|
+
- **A build + deploy baseline** — pytest, lint, the agent-first rubric gate, and
|
|
38
|
+
PyPI Trusted Publishing wired into GitHub Actions.
|
|
39
|
+
|
|
40
|
+
## Prompt files by harness
|
|
41
|
+
|
|
42
|
+
Four harnesses, four root files, no shared base — each file is read by
|
|
43
|
+
exactly one harness:
|
|
44
|
+
|
|
45
|
+
| Harness | File(s) |
|
|
46
|
+
|---------|---------|
|
|
47
|
+
| Claude Code | [`CLAUDE.md`](CLAUDE.md) |
|
|
48
|
+
| Pi / associate | [`AGENTS.override.md`](AGENTS.override.md) + [`.pi/SYSTEM.md`](.pi/SYSTEM.md) |
|
|
49
|
+
| colleague | [`AGENTS.colleague.md`](AGENTS.colleague.md) |
|
|
50
|
+
| Qwen Code | [`QWEN.md`](QWEN.md) |
|
|
51
|
+
|
|
52
|
+
**Claude Code** — `CLAUDE.md` is the fullest write-up of the repo's
|
|
53
|
+
conventions; read it first.
|
|
54
|
+
|
|
55
|
+
**Pi / associate** — `AGENTS.override.md` replaces this directory's
|
|
56
|
+
`AGENTS.md`/`CLAUDE.md` in Pi's context layer, so Pi does not inherit
|
|
57
|
+
`CLAUDE.md`. `.pi/SYSTEM.md` replaces Pi's default system prompt with the
|
|
58
|
+
non-coding `associate` identity (read/find/summarize only).
|
|
59
|
+
|
|
60
|
+
**colleague** — colleague's prompt cascade is `AGENTS.md` →
|
|
61
|
+
`AGENTS.colleague.md` → `AGENTS.colleague.<model>.md`. This repo ships only
|
|
62
|
+
the middle layer: there is no `AGENTS.md` (a shared base across harnesses was
|
|
63
|
+
considered and rejected) and no per-model override file.
|
|
64
|
+
|
|
65
|
+
**Qwen Code** — Qwen Code reads `QWEN.md` and `AGENTS.md`; since there is no
|
|
66
|
+
`AGENTS.md`, `QWEN.md` is its sole source of guidance.
|
|
67
|
+
|
|
68
|
+
There is intentionally **no `AGENTS.md`** at the root — each harness gets an
|
|
69
|
+
unrelated file rather than cascading from a shared base.
|
|
70
|
+
|
|
71
|
+
## Two selections, not one
|
|
72
|
+
|
|
73
|
+
It is tempting to read "switch harness" as one decision. It is actually two,
|
|
74
|
+
and this template exists partly to keep them separate:
|
|
75
|
+
|
|
76
|
+
1. **The interactive harness** — which binary you run (`claude`, `pi`,
|
|
77
|
+
`colleague`, `qwen`). `cd` into the clone and run any of them; all four
|
|
78
|
+
are live simultaneously, and none of them requires editing a file or
|
|
79
|
+
flipping a switch. A harness can be force-selected for one invocation
|
|
80
|
+
(e.g. a CI smoke check) without ever touching `culture.yaml` — see
|
|
81
|
+
[`docs/automation-contract.md`](docs/automation-contract.md).
|
|
82
|
+
2. **The mesh resident** — the single `backend` `culture.yaml` declares,
|
|
83
|
+
which is what the Culture daemon starts and what `steward doctor`
|
|
84
|
+
checks. `guild harness use <name>` changes only this.
|
|
85
|
+
|
|
86
|
+
`culture.yaml`'s `backend` affects (2) only. It never affects which harness
|
|
87
|
+
you can invoke interactively in (1). See
|
|
88
|
+
[`docs/harness-selection.md`](docs/harness-selection.md) for the full
|
|
89
|
+
writeup, including who reads this config and why existing siblings are not
|
|
90
|
+
retrofitted by this arc.
|
|
91
|
+
|
|
92
|
+
## Quickstart
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
uv sync
|
|
96
|
+
uv run pytest -n auto # run the test suite
|
|
97
|
+
uv run pocketsync whoami # identity from culture.yaml
|
|
98
|
+
uv run pocketsync learn # self-teaching prompt (add --json)
|
|
99
|
+
uv run teken cli doctor . --strict # the agent-first rubric gate CI runs
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
## CLI
|
|
103
|
+
|
|
104
|
+
| Verb | What it does |
|
|
105
|
+
|------|--------------|
|
|
106
|
+
| `whoami` | Report this agent's nick, version, backend, and model from `culture.yaml`. |
|
|
107
|
+
| `learn` | Print a structured self-teaching prompt. |
|
|
108
|
+
| `explain <path>` | Markdown docs for any noun/verb path. |
|
|
109
|
+
| `overview` | Read-only descriptive snapshot of the agent. |
|
|
110
|
+
| `doctor` | Check the agent-identity invariants (prompt-file-present, backend-consistency). |
|
|
111
|
+
| `cli overview` | Describe the CLI surface itself. |
|
|
112
|
+
|
|
113
|
+
Every command supports `--json`. Results go to stdout, errors/diagnostics to
|
|
114
|
+
stderr (never mixed). Exit codes: `0` success, `1` user error, `2` environment
|
|
115
|
+
error, `3+` reserved.
|
|
116
|
+
|
|
117
|
+
## Make it your own
|
|
118
|
+
|
|
119
|
+
1. Rename the package `pocketsync/` and the `pocketsync`
|
|
120
|
+
CLI/dist name throughout `pyproject.toml`, the package, `tests/`,
|
|
121
|
+
`sonar-project.properties`, and this `README.md`. The name is hard-coded in
|
|
122
|
+
~100 places, so list every occurrence first — see the `git grep` discovery
|
|
123
|
+
command in [`CLAUDE.md`](CLAUDE.md), the authoritative rename procedure.
|
|
124
|
+
2. Edit `culture.yaml` with your `suffix` and `backend`, and adjust its
|
|
125
|
+
`gate` section (below) to your project's setup and test commands.
|
|
126
|
+
3. Rewrite `CLAUDE.md` for your agent and run `/init`. Rewrite the other three
|
|
127
|
+
harness files (`AGENTS.override.md` + `.pi/SYSTEM.md`, `AGENTS.colleague.md`,
|
|
128
|
+
`QWEN.md`) too if your agent uses those harnesses — don't let them drift out
|
|
129
|
+
of sync with `CLAUDE.md`.
|
|
130
|
+
4. Re-vendor only the skills you need from guildmaster (see
|
|
131
|
+
[`docs/skill-sources.md`](docs/skill-sources.md)).
|
|
132
|
+
|
|
133
|
+
See [`CLAUDE.md`](CLAUDE.md) for the full conventions (version-bump-every-PR,
|
|
134
|
+
the `cicd` PR lane, deploy setup).
|
|
135
|
+
|
|
136
|
+
## The `gate` section
|
|
137
|
+
|
|
138
|
+
`culture.yaml` may carry a top-level `gate` section that declares how to set
|
|
139
|
+
up and test this repo, so tooling (for example a PR-fixer that re-runs the
|
|
140
|
+
checks before pushing) needs no per-repo knowledge:
|
|
141
|
+
|
|
142
|
+
```yaml
|
|
143
|
+
gate:
|
|
144
|
+
setup:
|
|
145
|
+
- [uv, sync]
|
|
146
|
+
test:
|
|
147
|
+
- [uv, run, pytest, -n, auto]
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
`setup` and `test` are each a list of commands, and each command is an argv
|
|
151
|
+
list (never a shell string), run in order from the repo root. The values are
|
|
152
|
+
configuration only; no tool supplies a default. Culture's loader ignores
|
|
153
|
+
unknown top-level keys, so the section is safe alongside `agents`.
|
|
154
|
+
|
|
155
|
+
## License
|
|
156
|
+
|
|
157
|
+
Apache 2.0 — see [`LICENSE`](LICENSE).
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
pocketsync/__init__.py,sha256=NYpSctaj8iph2EHMoZ0ugFQPLBqSxMdOm1f7pMEJNfw,402
|
|
2
|
+
pocketsync/__main__.py,sha256=yz078z6gJ4Tzm5IRPztoFfpLhTMJQeSIuhwa6A6sxDU,178
|
|
3
|
+
pocketsync/cli/__init__.py,sha256=MXQPXQOh2kPCzalYVNdcVKhsPElLonr6n5O-0uAfApo,4982
|
|
4
|
+
pocketsync/cli/_errors.py,sha256=z7XNf3wWDqb46ylPkDcg2z-LsYG79_EJ14o01ZgMO0c,1301
|
|
5
|
+
pocketsync/cli/_output.py,sha256=Zdi9g_CcoioC0uxC7bbzi8cJnL0m6dl8bMSz0fZv1VI,1709
|
|
6
|
+
pocketsync/cli/_commands/__init__.py,sha256=DqpfVkhBo7cj6eWOpSlwx1ag0nTbrYJkV8pnPgl2gyU,70
|
|
7
|
+
pocketsync/cli/_commands/cli.py,sha256=4t_M8Y-DTTw-cnMhfpkaqnHeJpnYCiIoXSsnetZQg74,1677
|
|
8
|
+
pocketsync/cli/_commands/doctor.py,sha256=0IOePf4PLmZQ0CNGai-3ZfpLM6wOR_rju38zxD_QwCs,7856
|
|
9
|
+
pocketsync/cli/_commands/explain.py,sha256=nmwS10b3Fu9J49nTDIqTH_16CAot-QczihepTwJZAOM,1227
|
|
10
|
+
pocketsync/cli/_commands/learn.py,sha256=NwcjMOOvXxk-WERbuxkYCVmEH9h94vfv82CQIBmsNsk,2999
|
|
11
|
+
pocketsync/cli/_commands/overview.py,sha256=joS7fQMQCD9P8ZZCZd_BX3YXIquHPKYORidiQ2JXypc,3961
|
|
12
|
+
pocketsync/cli/_commands/whoami.py,sha256=Xg9aZd9k4N0ayFlrQmOsrMLLs5DPhi9lo0NCdJeJgEE,3650
|
|
13
|
+
pocketsync/explain/__init__.py,sha256=sdFX0sX4tAOC9zjD81mOtokbdFtU0QXas_PlWZAbJFg,704
|
|
14
|
+
pocketsync/explain/catalog.py,sha256=kjkadO5QGAJgRzfXkBkCFrrzJgRfP_2GQ1-Zz2tDfUM,3559
|
|
15
|
+
pocketsync-0.10.0.dist-info/METADATA,sha256=DX6o_WIj-8wTyY93xrQEzLdo_2pfWLar43pJckAv1p8,7059
|
|
16
|
+
pocketsync-0.10.0.dist-info/WHEEL,sha256=W3fkpkm7-wf9vBI5Z-7s0eWkeM-spu78I8Neb98DeEg,87
|
|
17
|
+
pocketsync-0.10.0.dist-info/entry_points.txt,sha256=YdBMQkHq7Rx7SC2nzdIYq6a8cNFRsv28DTQLTr4PGvk,51
|
|
18
|
+
pocketsync-0.10.0.dist-info/licenses/LICENSE,sha256=UTsio4-AMxNDoMcES3iMDOVbdrnTy1rQD7Be7zWjqcA,11340
|
|
19
|
+
pocketsync-0.10.0.dist-info/RECORD,,
|