@tplog/pi-zendy 0.2.17 → 0.3.0

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.
@@ -1,162 +0,0 @@
1
- /**
2
- * Source-clone cleanup extension (deterministic, code-level defense).
3
- *
4
- * dify-enterprise / dify-enterprise-frontend are PRIVATE repositories.
5
- * Relying on skill-level ("please remember to clean up") guidance is
6
- * prompt-level defense — an AI can forget. This extension enforces
7
- * cleanup in code:
8
- *
9
- * - on session_start:
10
- * · create /tmp/zendy-session-<pid>-<ts>/ (mode 0700)
11
- * · export its path via process.env.ZENDY_SRC_DIR so bash
12
- * subprocesses spawned by pi (and the source-check skill)
13
- * clone into it
14
- * · sweep orphan session dirs whose owning pid is dead
15
- *
16
- * - on session_shutdown AND on process exit / SIGINT / SIGTERM /
17
- * uncaughtException:
18
- * · rm -rf this session's dir (idempotent, best-effort)
19
- *
20
- * - /cleanup-src slash command inside pi: wipe all dify-* and
21
- * orphan zendy-session-* dirs on demand.
22
- *
23
- * Caveats (do not oversell):
24
- * - kill -9 / OOM / power loss bypass handlers → rely on the
25
- * next startup's orphan sweep.
26
- * - rm -rf is not secure erase. For at-rest confidentiality,
27
- * disk encryption (FileVault/LUKS) is required.
28
- */
29
-
30
- import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
31
- import { mkdirSync, rmSync, readdirSync } from "node:fs";
32
- import { join } from "node:path";
33
-
34
- const BASE_DIR = "/tmp";
35
- const SESSION_PREFIX = "zendy-session-";
36
- const WIPE_PREFIXES = ["dify-", SESSION_PREFIX];
37
-
38
- function parseSessionDir(name: string): { pid: number } | null {
39
- const m = /^zendy-session-(\d+)-(\d+)$/.exec(name);
40
- return m ? { pid: parseInt(m[1]!, 10) } : null;
41
- }
42
-
43
- function isAlive(pid: number): boolean {
44
- try {
45
- process.kill(pid, 0);
46
- return true;
47
- } catch (e) {
48
- return (e as NodeJS.ErrnoException).code === "EPERM";
49
- }
50
- }
51
-
52
- function safeRmrf(path: string): boolean {
53
- try {
54
- rmSync(path, { recursive: true, force: true });
55
- return true;
56
- } catch {
57
- return false;
58
- }
59
- }
60
-
61
- function sweepOrphans(base: string, exclude: string): string[] {
62
- let entries;
63
- try {
64
- entries = readdirSync(base, { withFileTypes: true });
65
- } catch {
66
- return [];
67
- }
68
- const removed: string[] = [];
69
- for (const e of entries) {
70
- if (!e.isDirectory()) continue;
71
- const info = parseSessionDir(e.name);
72
- if (!info) continue;
73
- const full = join(base, e.name);
74
- if (full === exclude) continue;
75
- if (isAlive(info.pid)) continue;
76
- if (safeRmrf(full)) removed.push(full);
77
- }
78
- return removed;
79
- }
80
-
81
- export default function (pi: ExtensionAPI) {
82
- const ts = Date.now();
83
- const sessionDir = join(BASE_DIR, `${SESSION_PREFIX}${process.pid}-${ts}`);
84
- let cleanedUp = false;
85
-
86
- function ensureDir(): void {
87
- try {
88
- mkdirSync(sessionDir, { recursive: true, mode: 0o700 });
89
- } catch {
90
- // non-fatal; clone commands will also create subpaths with mkdir -p
91
- }
92
- }
93
-
94
- function cleanupSession(): void {
95
- if (cleanedUp) return;
96
- cleanedUp = true;
97
- safeRmrf(sessionDir);
98
- }
99
-
100
- // Belt-and-suspenders: if session_shutdown doesn't fire (some exit paths
101
- // skip event dispatch), rely on node's process signals. All idempotent.
102
- process.on("exit", cleanupSession);
103
- process.on("SIGINT", () => {
104
- cleanupSession();
105
- process.exit(130);
106
- });
107
- process.on("SIGTERM", () => {
108
- cleanupSession();
109
- process.exit(143);
110
- });
111
- process.on("uncaughtException", (err) => {
112
- cleanupSession();
113
- // Preserve original Node behavior of non-zero exit + stderr trace.
114
- // eslint-disable-next-line no-console
115
- console.error(err);
116
- process.exit(1);
117
- });
118
-
119
- pi.on("session_start", async (_event, ctx) => {
120
- ensureDir();
121
- process.env["ZENDY_SRC_DIR"] = sessionDir;
122
- const removed = sweepOrphans(BASE_DIR, sessionDir);
123
- if (ctx.hasUI && removed.length > 0) {
124
- ctx.ui.notify(
125
- `[source-cleanup] swept ${removed.length} orphan session dir(s)`,
126
- "info",
127
- );
128
- }
129
- });
130
-
131
- pi.on("session_shutdown", async () => {
132
- cleanupSession();
133
- });
134
-
135
- pi.registerCommand("cleanup-src", {
136
- description:
137
- "Wipe all Dify source clones (/tmp/dify-*) and orphan zendy session dirs",
138
- handler: async (_args, ctx) => {
139
- let entries;
140
- try {
141
- entries = readdirSync(BASE_DIR, { withFileTypes: true });
142
- } catch {
143
- ctx.ui.notify(`[source-cleanup] cannot read ${BASE_DIR}`, "error");
144
- return;
145
- }
146
- const removed: string[] = [];
147
- for (const e of entries) {
148
- if (!e.isDirectory()) continue;
149
- if (!WIPE_PREFIXES.some((p) => e.name.startsWith(p))) continue;
150
- const full = join(BASE_DIR, e.name);
151
- if (full === sessionDir) continue; // never nuke our own live session
152
- if (safeRmrf(full)) removed.push(full);
153
- }
154
- ctx.ui.notify(
155
- removed.length === 0
156
- ? `[source-cleanup] nothing to remove`
157
- : `[source-cleanup] removed ${removed.length} dir(s):\n${removed.join("\n")}`,
158
- "info",
159
- );
160
- },
161
- });
162
- }
@@ -1,82 +0,0 @@
1
- /**
2
- * Status slash-command extension.
3
- *
4
- * Registers `/status` inside pi. On invocation it shells out to
5
- * `zendy preflight --json` and renders the result as a single notification.
6
- *
7
- * Why subprocess instead of duplicating check logic here? The canonical
8
- * checks live in `src/preflight.ts` (pi/zcli/github), and the npm-published
9
- * `zendy` binary is in PATH (pi was launched by it). One source of truth,
10
- * trivial extension, ~50ms cold start per invocation — fine for an
11
- * interactive on-demand command.
12
- */
13
-
14
- import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
15
- import { execFile } from "node:child_process";
16
-
17
- interface CheckResult {
18
- name: string;
19
- label: string;
20
- level: "fatal" | "core" | "enhanced";
21
- status: "ok" | "missing" | "auth_error";
22
- hint: string;
23
- }
24
-
25
- interface PreflightReport {
26
- results: CheckResult[];
27
- hasFatal: boolean;
28
- hasCore: boolean;
29
- hasEnhanced: boolean;
30
- }
31
-
32
- function runZendyPreflight(): Promise<
33
- { ok: true; report: PreflightReport } | { ok: false; error: string }
34
- > {
35
- return new Promise((resolve) => {
36
- execFile(
37
- "zendy",
38
- ["preflight", "--json"],
39
- { timeout: 15000, encoding: "utf-8" },
40
- (err, stdout, stderr) => {
41
- if (err && (err as NodeJS.ErrnoException).code === "ENOENT") {
42
- resolve({ ok: false, error: "`zendy` not found in PATH" });
43
- return;
44
- }
45
- if (err) {
46
- const msg = stderr.trim() || (err as Error).message;
47
- resolve({ ok: false, error: msg });
48
- return;
49
- }
50
- try {
51
- const report = JSON.parse(stdout.trim()) as PreflightReport;
52
- resolve({ ok: true, report });
53
- } catch {
54
- resolve({ ok: false, error: `could not parse preflight JSON: ${stdout.slice(0, 200)}` });
55
- }
56
- },
57
- );
58
- });
59
- }
60
-
61
- function formatLine(r: CheckResult): string {
62
- if (r.status === "ok") return ` ✓ ${r.label}`;
63
- const firstHintLine = r.hint.split("\n")[0] ?? "";
64
- return ` ✗ ${r.label}${firstHintLine ? ` — ${firstHintLine}` : ""}`;
65
- }
66
-
67
- export default function (pi: ExtensionAPI) {
68
- pi.registerCommand("status", {
69
- description: "Show pi / Zendesk / GitHub connection status",
70
- handler: async (_args, ctx) => {
71
- const result = await runZendyPreflight();
72
- if (!result.ok) {
73
- ctx.ui.notify(`[status] could not run preflight: ${result.error}`, "error");
74
- return;
75
- }
76
- const { report } = result;
77
- const body = report.results.map(formatLine).join("\n");
78
- const hasIssue = report.hasFatal || report.hasCore || report.hasEnhanced;
79
- ctx.ui.notify(`zendy status:\n${body}`, hasIssue ? "error" : "info");
80
- },
81
- });
82
- }
@@ -1,24 +0,0 @@
1
- import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
2
- import { readFileSync } from "node:fs";
3
- import { dirname, join } from "node:path";
4
- import { fileURLToPath } from "node:url";
5
-
6
- const __dirname = dirname(fileURLToPath(import.meta.url));
7
- const AGENTS_PATH = join(__dirname, "..", "agents.md");
8
-
9
- let cachedContext: string | undefined;
10
-
11
- function loadZendyContext(): string {
12
- if (cachedContext === undefined) {
13
- cachedContext = readFileSync(AGENTS_PATH, "utf-8");
14
- }
15
- return cachedContext;
16
- }
17
-
18
- export default function (pi: ExtensionAPI) {
19
- pi.on("before_agent_start", async (event) => {
20
- return {
21
- systemPrompt: `${event.systemPrompt}\n\n${loadZendyContext()}`,
22
- };
23
- });
24
- }
@@ -1,146 +0,0 @@
1
- ---
2
- name: helm-watchdog
3
- description: "Query Dify Helm chart metadata — values.yaml, container images, validation results, and version info. Use when analyzing Helm-level configuration for a specific Dify Enterprise version."
4
- ---
5
-
6
- # Dify Helm Watchdog Skill
7
-
8
- Query the dify-helm-watchdog **HTTP API** to inspect Helm chart data for any cached Dify version.
9
-
10
- **IMPORTANT: This is a REST API, not a CLI tool. There is no `helm-watchdog` binary. Use `curl` to call the endpoints below.**
11
-
12
- ## Quick Reference — Copy-Paste Commands
13
-
14
- ```bash
15
- # Get values.yaml for a version
16
- curl -s "https://dify-helm-watchdog.vercel.app/api/v1/versions/3.8.0/values"
17
-
18
- # Get version metadata (chart version → app version mapping)
19
- curl -s "https://dify-helm-watchdog.vercel.app/api/v1/versions/3.8.0" | jq .
20
-
21
- # List all container images with their paths and tags
22
- curl -s "https://dify-helm-watchdog.vercel.app/api/v1/versions/3.8.0/images" | jq '.[] | {path, tag}'
23
-
24
- # Find a specific component's image (e.g., plugin-connector)
25
- curl -s "https://dify-helm-watchdog.vercel.app/api/v1/versions/3.8.0/images" | jq '.[] | select(.path | test("plugin-connector"))'
26
-
27
- # Check image validation status
28
- curl -s "https://dify-helm-watchdog.vercel.app/api/v1/versions/3.8.0/images?validate=true" | jq '.[] | {path, tag, status}'
29
-
30
- # Get latest version number
31
- curl -s "https://dify-helm-watchdog.vercel.app/api/v1/versions/latest?versionOnly=true"
32
- ```
33
-
34
- ### Images API response structure
35
-
36
- The `/images` endpoint returns a JSON array. Each element has these fields:
37
- ```json
38
- {
39
- "path": "langgenius/dify-api", // image repository path
40
- "tag": "7a1f0e32...", // image tag
41
- "registry": "docker.io", // registry (may be absent)
42
- "valuesPath": "api.image" // location in values.yaml
43
- }
44
- ```
45
-
46
- Key: the field is `.path`, not `.name` or `.repository`.
47
-
48
- ## When to Use
49
-
50
- - Looking up `values.yaml` for a specific Dify chart version
51
- - Checking what container images a chart version uses
52
- - Finding the latest chart version
53
- - Validating chart image availability
54
- - Answering customer questions about Helm configuration, default values, or image tags
55
-
56
- ## When NOT to Use
57
-
58
- - Modifying chart values or deploying — this is read-only
59
- - Anything outside the Helm chart layer (application code, runtime behavior)
60
-
61
- ## Base URL
62
-
63
- ```
64
- https://dify-helm-watchdog.vercel.app/api/v1
65
- ```
66
-
67
- ## API Endpoints
68
-
69
- ### List cached versions
70
-
71
- ```bash
72
- curl -s "https://dify-helm-watchdog.vercel.app/api/v1/versions" | jq .
73
- ```
74
-
75
- ### Get latest version
76
-
77
- ```bash
78
- curl -s "https://dify-helm-watchdog.vercel.app/api/v1/versions/latest"
79
- # Plain text version number only:
80
- curl -s "https://dify-helm-watchdog.vercel.app/api/v1/versions/latest?versionOnly=true"
81
- ```
82
-
83
- ### Get version metadata
84
-
85
- ```bash
86
- curl -s "https://dify-helm-watchdog.vercel.app/api/v1/versions/3.8.0" | jq .
87
- ```
88
-
89
- Returns chart version, app version, creation date, and asset URLs.
90
-
91
- ### Get values.yaml
92
-
93
- ```bash
94
- curl -s "https://dify-helm-watchdog.vercel.app/api/v1/versions/3.8.0/values"
95
- ```
96
-
97
- Returns the full `values.yaml` for the requested chart version. This is the most commonly used endpoint — use it to check default configuration, environment variables, image tags, and feature flags.
98
-
99
- ### Get container images
100
-
101
- ```bash
102
- # JSON output
103
- curl -s "https://dify-helm-watchdog.vercel.app/api/v1/versions/3.8.0/images" | jq .
104
- # With validation status
105
- curl -s "https://dify-helm-watchdog.vercel.app/api/v1/versions/3.8.0/images?validate=true" | jq .
106
- ```
107
-
108
- Lists all container image references from the chart's values. Useful for answering questions about image tags (`latest` vs pinned), base images, and vulnerability scoping.
109
-
110
- ### Get validation results
111
-
112
- ```bash
113
- # All validations
114
- curl -s "https://dify-helm-watchdog.vercel.app/api/v1/versions/3.8.0/validation" | jq .
115
- # Only missing images
116
- curl -s "https://dify-helm-watchdog.vercel.app/api/v1/versions/3.8.0/validation?status=MISSING" | jq .
117
- ```
118
-
119
- ### Inspect cache metadata
120
-
121
- ```bash
122
- curl -s "https://dify-helm-watchdog.vercel.app/api/v1/cache" | jq .
123
- ```
124
-
125
- ## Practical Guidance
126
-
127
- 1. **Start with version metadata** to confirm the chart version exists and get the app version mapping (e.g., chart 3.8.0 → app 1.12.0).
128
- 2. **Use values.yaml** to answer configuration questions — most customer inquiries about Helm settings can be resolved here.
129
- 3. **Use images** endpoint when the question is about container images, tags, or vulnerability scope.
130
- 4. When a customer reports an issue on a specific version, always pull the values.yaml for **that exact version** — defaults change between releases.
131
- 5. Output is JSON unless otherwise noted (values endpoint returns raw YAML).
132
-
133
- ## Version Mapping
134
-
135
- Chart versions map to Dify app versions. Common mappings:
136
- - Chart 3.8.0 → App 1.12.0
137
- - Chart 3.7.5 → App 1.11.4
138
-
139
- Use the version metadata endpoint to confirm the mapping for any version.
140
-
141
- ## Notes for the Agent
142
-
143
- - All endpoints are public and read-only, no authentication needed for GET requests.
144
- - Use `curl -s` and pipe to `jq` for readable output.
145
- - The values.yaml can be large — if you only need a specific section, pipe through `grep` or use `yq` if available.
146
- - When comparing versions, pull values for both and diff them.
@@ -1,143 +0,0 @@
1
- ---
2
- name: source-check
3
- description: "Clone and analyze Dify source code to investigate customer issues. Knows the repo map: enterprise backend/frontend, open-source core, plugin daemon, sandbox. Human-triggered only."
4
- ---
5
-
6
- # Dify Source Analysis Skill
7
-
8
- Clone the correct Dify repository and analyze source code to investigate customer issues.
9
-
10
- ## When to Use
11
-
12
- - The user explicitly asks to look at the source code
13
- - A question cannot be answered from Helm chart values alone
14
-
15
- ## When NOT to Use
16
-
17
- - Do NOT proactively decide to clone source code — wait for the user to ask
18
- - Do NOT use for questions answerable from values.yaml or documentation
19
-
20
- ## Repository Map
21
-
22
- **First, determine which repo to clone based on the problem area:**
23
-
24
- **Clone destination:** always clone under `"$ZENDY_SRC_DIR"` (set by the `source-cleanup`
25
- extension at session start). The extension will wipe the whole directory when the
26
- session ends, so anything inside is guaranteed to be cleaned up — even if you forget.
27
- If for some reason `$ZENDY_SRC_DIR` is not set, fall back to `/tmp` but tell the user.
28
-
29
- | Problem area | Repo | Access | Language | Clone command |
30
- |---|---|---|---|---|
31
- | SSO, token, session, SCIM, license, audit, branding, MFA, password policy | `langgenius/dify-enterprise` | private | Go | `git clone --depth 1 --branch <tag> git@github.com:langgenius/dify-enterprise.git "$ZENDY_SRC_DIR/dify-enterprise-<tag>"` |
32
- | Enterprise Dashboard UI, admin console frontend | `langgenius/dify-enterprise-frontend` | private | TypeScript | `git clone --depth 1 --branch <tag> git@github.com:langgenius/dify-enterprise-frontend.git "$ZENDY_SRC_DIR/dify-enterprise-frontend-<tag>"` |
33
- | API, workflow, knowledge base, model provider, RAG, chat | `langgenius/dify` | public | Python | `git clone --depth 1 --branch <tag> git@github.com:langgenius/dify.git "$ZENDY_SRC_DIR/dify-<tag>"` |
34
- | Plugin runtime, plugin execution | `langgenius/dify-plugin-daemon` | public | Go/Python | `git clone --depth 1 --branch <tag> git@github.com:langgenius/dify-plugin-daemon.git "$ZENDY_SRC_DIR/dify-plugin-daemon-<tag>"` |
35
- | Code sandbox execution | `langgenius/dify-sandbox` | public | Go | `git clone --depth 1 --branch <tag> git@github.com:langgenius/dify-sandbox.git "$ZENDY_SRC_DIR/dify-sandbox-<tag>"` |
36
-
37
- Enterprise repos also contain these components (all in dify-enterprise):
38
- - **gateway** — enterprise API gateway (`cmd/gateway/`, `pkg/gateway/`)
39
- - **plugin-connector** — plugin pod orchestration on K8s (`cmd/plugin-connector/`, `pkg/connector/`)
40
- - **plugin-manager** — enterprise plugin management (`cmd/plugin-manager/`, `pkg/plugin-manager/`)
41
- - **plugin-crd** — K8s CRD controller for plugins (`cmd/plugin-crd/`, `pkg/plugincrd/`)
42
- - **audit** — audit logging (`cmd/audit/`, `pkg/audit/`)
43
-
44
- ## Version Mapping
45
-
46
- Customer's Dify version → correct git tag:
47
-
48
- 1. Get chart version from Zendesk ticket fields (e.g., `3.8.0`)
49
- 2. Use helm-watchdog to get the image tags from values.yaml:
50
- - `enterprise.image.tag` → tag for `dify-enterprise` and `dify-enterprise-frontend` (e.g., `0.15.0`)
51
- - `api.image.tag` → tag for `dify` (usually a commit hash, e.g., `7a1f0e32...`)
52
- - `pluginDaemon.image.tag` → tag for `dify-plugin-daemon`
53
- - `sandbox.image.tag` → tag for `dify-sandbox`
54
-
55
- ## dify-enterprise Directory Structure (Go)
56
-
57
- ```
58
- cmd/ # Entry points (one per component)
59
- ├── enterprise/ # Main enterprise backend
60
- ├── gateway/ # API gateway
61
- ├── audit/ # Audit service
62
- ├── plugin-connector/ # Plugin K8s orchestrator
63
- ├── plugin-manager/ # Plugin manager
64
- ├── plugin-crd/ # K8s CRD controller
65
- └── plugin-shader/ # Plugin image builder
66
-
67
- pkg/
68
- ├── enterprise/
69
- │ └── service/ # Core business logic
70
- │ ├── console-sso.go # Console SSO login (SAML/OIDC/OAuth2)
71
- │ ├── web-sso.go # WebApp SSO login
72
- │ ├── dashboard-sso.go # Dashboard SSO
73
- │ ├── cookie.go # Cookie/session management
74
- │ ├── ssoconfig.go # SSO configuration
75
- │ ├── member.go # User/member management
76
- │ ├── license.go # License validation
77
- │ ├── mfa.go # Multi-factor auth
78
- │ ├── password-policy.go # Password policy
79
- │ ├── scim.go # SCIM provisioning
80
- │ ├── scim-users.go # SCIM user sync
81
- │ ├── scim-groups.go # SCIM group sync
82
- │ ├── branding.go # Custom branding
83
- │ ├── workspace.go # Workspace management
84
- │ ├── admin-*.go # Admin operations
85
- │ └── webapp.go # WebApp config
86
- ├── sysconf/
87
- │ ├── keys.go # Configuration key definitions
88
- │ └── values.go # Default values
89
- ├── auth/ # Authentication
90
- ├── connector/ # Plugin connector logic
91
- ├── gateway/ # Gateway logic
92
- ├── plugin/ # Plugin management
93
- ├── plugin-manager/ # Plugin manager logic
94
- ├── plugincrd/ # K8s CRD logic
95
- ├── audit/ # Audit logic
96
- ├── kube/ # Kubernetes utilities
97
- └── data/ # Data layer
98
-
99
- ts/ # TypeScript code
100
- ├── connector/ # Plugin connector frontend
101
- ├── enterprise_client/ # Enterprise API client
102
- └── dify/ # Dify integrations
103
- ```
104
-
105
- ## dify (open-source) Directory Structure (Python)
106
-
107
- ```
108
- api/ # Backend API (Flask)
109
- ├── controllers/ # HTTP handlers
110
- ├── core/
111
- │ ├── workflow/ # Workflow engine
112
- │ ├── rag/ # RAG pipeline
113
- │ ├── model_runtime/ # Model provider integrations
114
- │ ├── app/ # App orchestration
115
- │ └── tools/ # Built-in tools
116
- ├── services/ # Business logic
117
- ├── models/ # Database models
118
- └── configs/ # Configuration
119
-
120
- web/ # Frontend (Next.js / TypeScript)
121
- ├── app/ # Pages
122
- ├── components/ # UI components
123
- └── service/ # API client
124
- ```
125
-
126
- ## Analysis Approach
127
-
128
- 1. **grep first** — find relevant files by keyword (feature name, config key, error message)
129
- 2. **Read identified files** — understand the logic
130
- 3. **Trace the flow** — API endpoint → handler → service → data layer
131
- 4. **Compare** with the customer's reported behavior
132
- 5. **Conclude** — bug, misconfiguration, or expected behavior
133
-
134
- ## Rules
135
-
136
- - Always shallow clone (`--depth 1`) for speed
137
- - State confidence level: "confirmed from code" vs "inferred from structure"
138
- - Read-only — never modify source code
139
- - Clone into `"$ZENDY_SRC_DIR"`. The `source-cleanup` extension wipes this directory
140
- on session shutdown, so manual `rm -rf` after analysis is no longer required.
141
- If the user asks for immediate cleanup mid-session, use the `/cleanup-src` slash
142
- command (wipes `/tmp/dify-*` and orphan session dirs).
143
- - If clone fails (permission denied), tell the user to configure SSH keys
@@ -1,37 +0,0 @@
1
- ---
2
- name: zendesk-cli
3
- description: "Zendesk ticket lookup via the zcli CLI. Provides ticket metadata, comment threads, and search — all as JSON."
4
- ---
5
-
6
- # zendesk-cli
7
-
8
- `zcli` is a Zendesk CLI that outputs JSON. Use it to fetch tickets, comments, and search results.
9
-
10
- ## Quick start
11
-
12
- Discover all commands and options:
13
-
14
- ```bash
15
- zcli --help
16
- zcli <command> --help
17
- ```
18
-
19
- ## Install (if missing)
20
-
21
- ```bash
22
- npm install -g @tplog/zendesk-cli
23
- zcli configure
24
- ```
25
-
26
- ## Key behaviors the agent must know
27
-
28
- - **`zcli <email>` is assignee lookup, not requester.** This is the most common mistake.
29
- - **`zcli follower <email>`** is a separate command — don't try to filter assignee results manually.
30
- - **Output is already JSON.** No `--json` flag needed. Summarize directly from stdout.
31
- - **When summarizing a ticket, always parallel-call both:**
32
- ```bash
33
- zcli <id> # ticket metadata
34
- zcli comments <id> # comment thread
35
- ```
36
- Ticket metadata alone is not enough for a complete summary.
37
- - **Errors are also JSON on stdout** with non-zero exit code. Check stdout, not just stderr.