juno-code 1.0.24 → 1.0.26
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.
- package/dist/bin/cli.js +14756 -16208
- package/dist/bin/cli.js.map +1 -1
- package/dist/bin/cli.mjs +14685 -16138
- package/dist/bin/cli.mjs.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.mjs +1 -1
- package/dist/templates/scripts/kanban.sh +8 -2
- package/dist/templates/services/__pycache__/claude.cpython-38.pyc +0 -0
- package/dist/templates/services/claude.py +135 -24
- package/package.json +1 -1
package/dist/index.js
CHANGED
package/dist/index.mjs
CHANGED
|
@@ -10,13 +10,19 @@
|
|
|
10
10
|
# Usage: ./.juno_task/scripts/kanban.sh [juno-kanban arguments]
|
|
11
11
|
# Example: ./.juno_task/scripts/kanban.sh list --limit 5
|
|
12
12
|
#
|
|
13
|
+
# Environment Variables:
|
|
14
|
+
# JUNO_DEBUG=true - Show [DEBUG] diagnostic messages
|
|
15
|
+
# JUNO_VERBOSE=true - Show [KANBAN] informational messages
|
|
16
|
+
# (Both default to false for silent operation)
|
|
17
|
+
#
|
|
13
18
|
# Created by: juno-code init command
|
|
14
19
|
# Date: Auto-generated during project initialization
|
|
15
20
|
|
|
16
21
|
set -euo pipefail # Exit on error, undefined variable, or pipe failure
|
|
17
22
|
|
|
18
|
-
# DEBUG OUTPUT: Show that kanban.sh is being executed (only if
|
|
19
|
-
|
|
23
|
+
# DEBUG OUTPUT: Show that kanban.sh is being executed (only if JUNO_DEBUG=true)
|
|
24
|
+
# Note: JUNO_DEBUG is separate from JUNO_VERBOSE for fine-grained control
|
|
25
|
+
if [ "${JUNO_DEBUG:-false}" = "true" ]; then
|
|
20
26
|
echo "[DEBUG] kanban.sh is being executed from: $(pwd)" >&2
|
|
21
27
|
fi
|
|
22
28
|
|
|
Binary file
|
|
@@ -31,6 +31,8 @@ class ClaudeService:
|
|
|
31
31
|
self.additional_args: List[str] = []
|
|
32
32
|
self.message_counter = 0
|
|
33
33
|
self.verbose = False
|
|
34
|
+
# User message truncation: -1 = no truncation, N = truncate to N lines
|
|
35
|
+
self.user_message_truncate = int(os.environ.get("CLAUDE_USER_MESSAGE_PRETTY_TRUNCATE", "4"))
|
|
34
36
|
|
|
35
37
|
def check_claude_installed(self) -> bool:
|
|
36
38
|
"""Check if claude CLI is installed and available"""
|
|
@@ -57,12 +59,13 @@ Examples:
|
|
|
57
59
|
%(prog)s -p "Add tests" -m claude-opus-4-20250514 --tool "Bash Edit"
|
|
58
60
|
|
|
59
61
|
Environment Variables:
|
|
60
|
-
CLAUDE_PROJECT_PATH
|
|
61
|
-
CLAUDE_MODEL
|
|
62
|
-
CLAUDE_AUTO_INSTRUCTION
|
|
63
|
-
CLAUDE_PERMISSION_MODE
|
|
64
|
-
CLAUDE_PRETTY
|
|
65
|
-
CLAUDE_VERBOSE
|
|
62
|
+
CLAUDE_PROJECT_PATH Project path (default: current directory)
|
|
63
|
+
CLAUDE_MODEL Model name (default: claude-sonnet-4-5-20250929)
|
|
64
|
+
CLAUDE_AUTO_INSTRUCTION Auto instruction to prepend to prompt
|
|
65
|
+
CLAUDE_PERMISSION_MODE Permission mode (default: default)
|
|
66
|
+
CLAUDE_PRETTY Pretty print JSON output (default: true)
|
|
67
|
+
CLAUDE_VERBOSE Enable verbose output (default: false)
|
|
68
|
+
CLAUDE_USER_MESSAGE_PRETTY_TRUNCATE Max lines for user messages in pretty mode (default: 4, -1: no truncation)
|
|
66
69
|
"""
|
|
67
70
|
)
|
|
68
71
|
|
|
@@ -214,10 +217,20 @@ Environment Variables:
|
|
|
214
217
|
"""
|
|
215
218
|
Format JSON line for pretty output.
|
|
216
219
|
For type=assistant: show datetime, message content, and counter
|
|
220
|
+
For type=user: show datetime, message content (truncated based on env var), and counter
|
|
217
221
|
For other types: show full message with datetime and counter
|
|
218
222
|
Returns None if line should be skipped
|
|
219
223
|
|
|
220
224
|
IMPORTANT: Always preserve the 'type' field so shell backend can parse events
|
|
225
|
+
|
|
226
|
+
MULTI-LINE HANDLING: When content/result fields contain \\n escape sequences,
|
|
227
|
+
the output shows the JSON metadata on one line, then the actual content/result
|
|
228
|
+
value is printed below with newlines properly rendered (similar to jq -r or @text).
|
|
229
|
+
This keeps JSON structure compact while making multi-line strings readable.
|
|
230
|
+
|
|
231
|
+
USER MESSAGE TRUNCATION: User messages are truncated based on CLAUDE_USER_MESSAGE_PRETTY_TRUNCATE
|
|
232
|
+
environment variable (default: 4 lines, -1: no truncation). When truncated, a [Truncated...]
|
|
233
|
+
indicator is added. This only applies to user messages in pretty mode.
|
|
221
234
|
"""
|
|
222
235
|
try:
|
|
223
236
|
data = json.loads(json_line)
|
|
@@ -226,8 +239,53 @@ Environment Variables:
|
|
|
226
239
|
# Get current datetime in readable format
|
|
227
240
|
now = datetime.now().strftime("%I:%M:%S %p")
|
|
228
241
|
|
|
242
|
+
# For user messages, show simplified output with truncation
|
|
243
|
+
if data.get("type") == "user":
|
|
244
|
+
message = data.get("message", {})
|
|
245
|
+
content_list = message.get("content", [])
|
|
246
|
+
|
|
247
|
+
# Extract text content
|
|
248
|
+
text_content = ""
|
|
249
|
+
for item in content_list:
|
|
250
|
+
if isinstance(item, dict) and item.get("type") == "text":
|
|
251
|
+
text_content = item.get("text", "")
|
|
252
|
+
break
|
|
253
|
+
|
|
254
|
+
# Apply truncation for user messages based on CLAUDE_USER_MESSAGE_PRETTY_TRUNCATE
|
|
255
|
+
# -1 means no truncation, otherwise truncate to N lines
|
|
256
|
+
if self.user_message_truncate != -1:
|
|
257
|
+
lines = text_content.split('\n')
|
|
258
|
+
if len(lines) > self.user_message_truncate:
|
|
259
|
+
# Truncate to N lines and add indicator
|
|
260
|
+
text_content = '\n'.join(lines[:self.user_message_truncate]) + '\n[Truncated...]'
|
|
261
|
+
|
|
262
|
+
# Create simplified output with datetime, content, and counter
|
|
263
|
+
simplified = {
|
|
264
|
+
"type": "user",
|
|
265
|
+
"datetime": now,
|
|
266
|
+
"counter": f"#{self.message_counter}"
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
# Check if content has newlines after potential truncation
|
|
270
|
+
if '\n' in text_content:
|
|
271
|
+
# Multi-line content: print JSON metadata, then raw content
|
|
272
|
+
metadata = {
|
|
273
|
+
"type": "user",
|
|
274
|
+
"datetime": now,
|
|
275
|
+
"counter": f"#{self.message_counter}"
|
|
276
|
+
}
|
|
277
|
+
# Print metadata as compact JSON on first line
|
|
278
|
+
output = json.dumps(metadata, ensure_ascii=False)
|
|
279
|
+
# Then print content label and raw multi-line text
|
|
280
|
+
output += "\ncontent:\n" + text_content
|
|
281
|
+
return output
|
|
282
|
+
else:
|
|
283
|
+
# Single-line content: normal JSON
|
|
284
|
+
simplified["content"] = text_content
|
|
285
|
+
return json.dumps(simplified, ensure_ascii=False)
|
|
286
|
+
|
|
229
287
|
# For assistant messages, show simplified output
|
|
230
|
-
|
|
288
|
+
elif data.get("type") == "assistant":
|
|
231
289
|
message = data.get("message", {})
|
|
232
290
|
content_list = message.get("content", [])
|
|
233
291
|
|
|
@@ -259,20 +317,61 @@ Environment Variables:
|
|
|
259
317
|
# Add either content or tool_use data
|
|
260
318
|
if tool_use_data:
|
|
261
319
|
simplified["tool_use"] = tool_use_data
|
|
320
|
+
# Normal JSON output for tool_use
|
|
321
|
+
return json.dumps(simplified, ensure_ascii=False)
|
|
262
322
|
else:
|
|
263
|
-
# For
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
323
|
+
# For content, check if it has newlines
|
|
324
|
+
if '\n' in text_content:
|
|
325
|
+
# Multi-line content: print JSON metadata, then raw content
|
|
326
|
+
metadata = {
|
|
327
|
+
"type": "assistant",
|
|
328
|
+
"datetime": now,
|
|
329
|
+
"counter": f"#{self.message_counter}"
|
|
330
|
+
}
|
|
331
|
+
# Print metadata as compact JSON on first line
|
|
332
|
+
output = json.dumps(metadata, ensure_ascii=False)
|
|
333
|
+
# Then print content label and raw multi-line text
|
|
334
|
+
output += "\ncontent:\n" + text_content
|
|
335
|
+
return output
|
|
271
336
|
else:
|
|
337
|
+
# Single-line content: normal JSON
|
|
272
338
|
simplified["content"] = text_content
|
|
273
|
-
|
|
274
|
-
return json.dumps(simplified)
|
|
339
|
+
return json.dumps(simplified, ensure_ascii=False)
|
|
275
340
|
else:
|
|
341
|
+
# For other message types, check if there's nested content to flatten
|
|
342
|
+
message = data.get("message", {})
|
|
343
|
+
content_list = message.get("content", [])
|
|
344
|
+
|
|
345
|
+
# Check if this is a message with nested tool_result or similar content
|
|
346
|
+
if content_list and isinstance(content_list, list) and len(content_list) > 0:
|
|
347
|
+
nested_item = content_list[0]
|
|
348
|
+
if isinstance(nested_item, dict) and nested_item.get("type") in ["tool_result"]:
|
|
349
|
+
# Flatten the nested structure by pulling nested fields to top level
|
|
350
|
+
flattened = {
|
|
351
|
+
"datetime": now,
|
|
352
|
+
"counter": f"#{self.message_counter}",
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
# Add tool_use_id if present
|
|
356
|
+
if "tool_use_id" in nested_item:
|
|
357
|
+
flattened["tool_use_id"] = nested_item["tool_use_id"]
|
|
358
|
+
|
|
359
|
+
# Add type from nested item
|
|
360
|
+
flattened["type"] = nested_item["type"]
|
|
361
|
+
|
|
362
|
+
# Handle content field with multiline support
|
|
363
|
+
nested_content = nested_item.get("content", "")
|
|
364
|
+
if isinstance(nested_content, str) and '\n' in nested_content:
|
|
365
|
+
# Multi-line content: separate metadata from content
|
|
366
|
+
# Print metadata as compact JSON
|
|
367
|
+
metadata_json = json.dumps(flattened, ensure_ascii=False)
|
|
368
|
+
# Then print content label and raw multi-line text
|
|
369
|
+
return metadata_json + "\ncontent:\n" + nested_content
|
|
370
|
+
else:
|
|
371
|
+
# Single-line content: normal JSON
|
|
372
|
+
flattened["content"] = nested_content
|
|
373
|
+
return json.dumps(flattened, ensure_ascii=False)
|
|
374
|
+
|
|
276
375
|
# For other message types, show full message with datetime and counter
|
|
277
376
|
# Type field is already present in data, so it's preserved
|
|
278
377
|
output = {
|
|
@@ -280,7 +379,18 @@ Environment Variables:
|
|
|
280
379
|
"counter": f"#{self.message_counter}",
|
|
281
380
|
**data
|
|
282
381
|
}
|
|
283
|
-
|
|
382
|
+
|
|
383
|
+
# Check if 'result' field has multi-line content
|
|
384
|
+
if "result" in output and isinstance(output["result"], str) and '\n' in output["result"]:
|
|
385
|
+
# Multi-line result: separate metadata from content
|
|
386
|
+
result_value = output.pop("result")
|
|
387
|
+
# Print metadata as compact JSON
|
|
388
|
+
metadata_json = json.dumps(output, ensure_ascii=False)
|
|
389
|
+
# Then print result label and raw multi-line text
|
|
390
|
+
return metadata_json + "\nresult:\n" + result_value
|
|
391
|
+
else:
|
|
392
|
+
# Normal JSON output
|
|
393
|
+
return json.dumps(output, ensure_ascii=False)
|
|
284
394
|
|
|
285
395
|
except json.JSONDecodeError:
|
|
286
396
|
# If not valid JSON, return as-is
|
|
@@ -368,12 +478,13 @@ Environment Variables:
|
|
|
368
478
|
)
|
|
369
479
|
print("\nRun 'claude.py --help' for usage information.", file=sys.stderr)
|
|
370
480
|
print("\nAvailable Environment Variables:", file=sys.stderr)
|
|
371
|
-
print(" CLAUDE_PROJECT_PATH
|
|
372
|
-
print(" CLAUDE_MODEL
|
|
373
|
-
print(" CLAUDE_AUTO_INSTRUCTION
|
|
374
|
-
print(" CLAUDE_PERMISSION_MODE
|
|
375
|
-
print(" CLAUDE_PRETTY
|
|
376
|
-
print(" CLAUDE_VERBOSE
|
|
481
|
+
print(" CLAUDE_PROJECT_PATH Project path (default: current directory)", file=sys.stderr)
|
|
482
|
+
print(" CLAUDE_MODEL Model name (default: claude-sonnet-4-5-20250929)", file=sys.stderr)
|
|
483
|
+
print(" CLAUDE_AUTO_INSTRUCTION Auto instruction to prepend to prompt", file=sys.stderr)
|
|
484
|
+
print(" CLAUDE_PERMISSION_MODE Permission mode (default: default)", file=sys.stderr)
|
|
485
|
+
print(" CLAUDE_PRETTY Pretty print JSON output (default: true)", file=sys.stderr)
|
|
486
|
+
print(" CLAUDE_VERBOSE Enable verbose output (default: false)", file=sys.stderr)
|
|
487
|
+
print(" CLAUDE_USER_MESSAGE_PRETTY_TRUNCATE Max lines for user messages in pretty mode (default: 4, -1: no truncation)", file=sys.stderr)
|
|
377
488
|
return 1
|
|
378
489
|
|
|
379
490
|
# Check if claude is installed
|