@paneui/cli 0.0.4 → 0.0.5

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.
@@ -37,16 +37,18 @@ Subcommands:
37
37
  { taste: string|null, updated_at: string|null, bytes: number }.
38
38
  taste is null and bytes is 0 when notes have never been written.
39
39
 
40
- set Whole-blob replace. Read markdown from stdin OR --file <path>
41
- (exactly one). The relay rejects empty/whitespace-only payloads
42
- and caps the blob at MAX_TASTE_BYTES (utf8). To clear the notes,
40
+ set Whole-blob replace. Source the markdown via --file <path>,
41
+ --file - (read stdin), or by piping into 'pane taste set' with
42
+ no flag. The relay rejects empty/whitespace-only payloads and
43
+ caps the blob at MAX_TASTE_BYTES (utf8). To clear the notes,
43
44
  use 'pane taste clear', not 'set' with an empty body.
44
45
 
45
46
  clear Delete the notes. Requires --yes (it is destructive). Prints
46
47
  { cleared: true }.
47
48
 
48
49
  Options:
49
- --file <path> Read 'set' input from <path> (otherwise reads stdin).
50
+ --file <path|-> Source for 'set' a file path, or '-' to read stdin
51
+ explicitly. Omit to fall back to piped stdin.
50
52
  --yes Confirm 'clear'.
51
53
  --url <url> Relay base URL (overrides PANE_URL).
52
54
  --api-key <key> Agent API key (overrides PANE_API_KEY).
@@ -54,17 +56,17 @@ Options:
54
56
 
55
57
  Examples:
56
58
  pane taste get
57
- echo "- denser layout\\n- no rounded corners" | pane taste set
58
59
  pane taste set --file ./taste.md
60
+ pane taste set --file - # explicit stdin
61
+ echo "- denser layout" | pane taste set
59
62
  pane taste clear --yes
60
63
 
61
64
  Output: stdout is machine-readable JSON.`;
62
- // Drain process.stdin to a utf8 string. Resolves to "" when stdin is a TTY
63
- // (i.e. nothing was piped in), so the caller can detect "no input" and emit a
64
- // proper error instead of hanging forever waiting for keystrokes.
65
+ // Drain process.stdin to a utf8 string. The caller is responsible for
66
+ // deciding that stdin should be read (e.g. an explicit `--file -`, or a
67
+ // non-TTY stdin where data is actually piped). In a TTY this would block
68
+ // waiting for ^D, so the caller MUST gate on `process.stdin.isTTY` first.
65
69
  async function readStdin() {
66
- if (process.stdin.isTTY)
67
- return "";
68
70
  const chunks = [];
69
71
  for await (const chunk of process.stdin) {
70
72
  chunks.push(typeof chunk === "string" ? Buffer.from(chunk) : chunk);
@@ -83,15 +85,19 @@ async function runTasteGet(args) {
83
85
  }
84
86
  async function runTasteSet(args) {
85
87
  const filePath = args.flags.get("file");
86
- const hasStdin = !process.stdin.isTTY;
87
- if (filePath !== undefined && hasStdin) {
88
- fail("'pane taste set' takes input from EITHER stdin OR --file <path>, not both", "invalid_args");
89
- }
90
- if (filePath === undefined && !hasStdin) {
91
- fail("'pane taste set' needs input — pipe markdown on stdin or pass --file <path>", "invalid_args");
92
- }
88
+ // Source the blob deterministically — no isTTY-flag fusing, because
89
+ // `!process.stdin.isTTY` is true under every non-interactive caller
90
+ // (pipes, redirects, closed fd, CI, agent harnesses) and would wrongly
91
+ // reject `--file` for the entire target audience. See issue #148.
92
+ //
93
+ // --file - explicit stdin sentinel
94
+ // --file <path> → read that path (works in TTY and non-TTY alike)
95
+ // (no --file) → fall back to stdin IF non-TTY; error in a TTY
93
96
  let taste;
94
- if (filePath !== undefined) {
97
+ if (filePath === "-") {
98
+ taste = await readStdin();
99
+ }
100
+ else if (filePath !== undefined) {
95
101
  try {
96
102
  taste = readFileSync(filePath, "utf8");
97
103
  }
@@ -99,9 +105,12 @@ async function runTasteSet(args) {
99
105
  fail(`failed to read --file '${filePath}': ${e instanceof Error ? e.message : String(e)}`, "invalid_args");
100
106
  }
101
107
  }
102
- else {
108
+ else if (!process.stdin.isTTY) {
103
109
  taste = await readStdin();
104
110
  }
111
+ else {
112
+ fail("'pane taste set' needs input — pass --file <path>, pipe markdown on stdin, or use --file -", "invalid_args");
113
+ }
105
114
  if (taste.trim().length === 0) {
106
115
  fail("'pane taste set' refuses an empty or whitespace-only blob — use 'pane taste clear --yes' to delete the notes", "invalid_args");
107
116
  }
package/dist/version.js CHANGED
@@ -8,4 +8,4 @@
8
8
  // Keep this in lockstep with packages/cli/package.json's `version` field;
9
9
  // they're consulted in different places (here for the runtime header,
10
10
  // package.json for npm publish + dependency resolution).
11
- export const VERSION = "0.0.4";
11
+ export const VERSION = "0.0.5";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@paneui/cli",
3
- "version": "0.0.4",
3
+ "version": "0.0.5",
4
4
  "description": "Command-line client for the Pane relay: create sessions, inspect state, send and watch events.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -41,7 +41,7 @@
41
41
  "test:unit": "vitest run"
42
42
  },
43
43
  "dependencies": {
44
- "@paneui/core": "^0.0.4"
44
+ "@paneui/core": "^0.0.5"
45
45
  },
46
46
  "devDependencies": {
47
47
  "@types/node": "^22.7.0",