Problem
jact extract header outputs JSON by default, which includes metadata (links report, stats, content IDs) alongside the extracted content. When used inside Claude Code skills via the !command`` dynamic injection syntax, the agent receives JSON it must parse to find the actual markdown content. The extracted content is buried in .extractedContentBlocks.<id>.content.
Reproduction Steps
- Create a Claude Code skill with dynamic injection:
## Injected Rules
!`jact extract header /path/to/file.md "Section Name" --scope /path/to/repo`
- Invoke the skill via
/test-inject
- Observe: agent receives full JSON output including
outgoingLinksReport, stats, processedLinks metadata
- The actual markdown content is nested at
.extractedContentBlocks.<hash>.content
Root Cause
jact extract header was designed for programmatic consumption (validation pipelines, CI). The Claude Code skill injection use case (!command``) needs raw extracted content only, with no wrapper metadata.
Expected Behavior
A --format markdown (or --format text) flag that outputs only the extracted markdown content, no JSON wrapper:
# Current (default, becomes --verbose or --format json)
jact extract header file.md "Section" --scope /repo
# → full JSON with metadata
# New
jact extract header file.md "Section" --scope /repo --format markdown
# → raw markdown content only, ready for skill injection
Related
Note
Current JSON output works (model parses the .content field), but it wastes tokens on metadata the model does not need. For a single header extraction, the JSON wrapper is ~350 chars of overhead per extraction. In skills with multiple !jact extract`` calls, this compounds.
Proposed migration:
--format markdown (new default candidate): raw content only
--format json (current behavior): full metadata, links report, stats
--verbose flag: could be an alias for the full JSON output
Not proposing changing the current default in this issue. Start with adding --format markdown as an opt-in flag. Default change can be a separate discussion.
Acceptance Criteria
Definition of Done
Problem
jact extract headeroutputs JSON by default, which includes metadata (links report, stats, content IDs) alongside the extracted content. When used inside Claude Code skills via the!command`` dynamic injection syntax, the agent receives JSON it must parse to find the actual markdown content. The extracted content is buried in.extractedContentBlocks.<id>.content.Reproduction Steps
/test-injectoutgoingLinksReport,stats,processedLinksmetadata.extractedContentBlocks.<hash>.contentRoot Cause
jact extract headerwas designed for programmatic consumption (validation pipelines, CI). The Claude Code skill injection use case (!command``) needs raw extracted content only, with no wrapper metadata.Expected Behavior
A
--format markdown(or--format text) flag that outputs only the extracted markdown content, no JSON wrapper:Related
lbnl-benefits/.claude/skills/test-inject/SKILL.md(proof of concept)!command`` syntax replaces the placeholder with stdout before the skill reaches the modelNote
Current JSON output works (model parses the
.contentfield), but it wastes tokens on metadata the model does not need. For a single header extraction, the JSON wrapper is ~350 chars of overhead per extraction. In skills with multiple!jact extract`` calls, this compounds.Proposed migration:
--format markdown(new default candidate): raw content only--format json(current behavior): full metadata, links report, stats--verboseflag: could be an alias for the full JSON outputNot proposing changing the current default in this issue. Start with adding
--format markdownas an opt-in flag. Default change can be a separate discussion.Acceptance Criteria
jact extract header <file> <heading> --format markdownoutputs only the extracted markdown content---separator--format jsonpreserves current behavior exactlyDefinition of Done