or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

cli.mddocument-elements.mddocument-io.mdindex.mdtext-processing.md

cli.mddocs/

0

# Command Line Interface

1

2

CLI tools for running panflute as a Pandoc filter with automatic filter discovery and execution capabilities. These tools enable panflute to be used as a standalone filter processor or as part of complex document processing pipelines.

3

4

## Capabilities

5

6

### Basic Filter Execution

7

8

Run panflute as a standard Pandoc filter that processes stdin/stdout.

9

10

```python { .api }

11

def main():

12

"""

13

Entry point for panflute CLI when used as a Pandoc filter.

14

15

Reads JSON from stdin, processes with filters specified in metadata,

16

and writes results to stdout. Filters are specified in document

17

metadata under 'panflute-filters' key.

18

19

Example usage:

20

pandoc document.md --filter panflute -o output.html

21

22

Document metadata can specify filters:

23

---

24

panflute-filters:

25

- emphasis_filter

26

- link_processor

27

panflute-path:

28

- ./filters

29

- ~/.pandoc/filters

30

---

31

"""

32

```

33

34

### Advanced Filter Execution

35

36

Run panflute with command-line arguments for complex filter pipelines.

37

38

```python { .api }

39

def panfl():

40

"""

41

Advanced CLI for panflute with command-line filter specification.

42

43

Supports multiple modes:

44

1. Pandoc filter mode (single filter, no arguments)

45

2. Pipeline mode (multiple filters with --to option)

46

47

Command-line options:

48

- filters: Filter names/paths (positional arguments)

49

- --to/-t: Output format for pipeline mode

50

- --dir/-d: Additional search directories (multiple allowed)

51

- --data-dir: Include default Pandoc data directories

52

- --no-sys-path: Exclude Python sys.path from search

53

54

Example usage:

55

# Pipeline mode

56

pandoc -t json | panfl filter1 filter2 --to html | pandoc -f json

57

58

# Pandoc filter mode

59

pandoc document.md --filter panfl -o output.html

60

61

# Custom search directories

62

panfl --dir ./custom-filters --data-dir filter1 filter2 --to latex

63

"""

64

```

65

66

### Core Processing Function

67

68

Low-level document processing with filter execution.

69

70

```python { .api }

71

def stdio(filters=None,

72

search_dirs=None,

73

data_dir=True,

74

sys_path=True,

75

panfl_=False,

76

input_stream=None,

77

output_stream=None):

78

"""

79

Process document from stdin with automatic filter discovery and execution.

80

81

Parameters:

82

- filters: list of filter names/paths (None = read from metadata)

83

- search_dirs: list of directories to search for filters (None = read from metadata)

84

- data_dir: include Pandoc data directories in search (default: True)

85

- sys_path: include Python sys.path in search (default: True)

86

- panfl_: use panfl behavior vs standard panflute (default: False)

87

- input_stream: input source (default: stdin)

88

- output_stream: output destination (default: stdout)

89

90

The function reads document metadata for configuration:

91

- 'panflute-filters': list/string of filter names

92

- 'panflute-path': list/string of search directories

93

- 'panflute-verbose': enable debug output

94

- 'panflute-echo': display debug message

95

96

Special metadata values:

97

- '--data-dir' in panflute-path: enable data_dir

98

- '--no-sys-path' in panflute-path: disable sys_path

99

100

Example usage in filter:

101

import panflute as pf

102

103

# Custom processing with specific filters

104

pf.stdio(

105

filters=['my_filter', 'cleanup_filter'],

106

search_dirs=['./filters', '~/.pandoc/filters'],

107

input_stream=open('input.json'),

108

output_stream=open('output.json', 'w')

109

)

110

"""

111

```

112

113

### Filter Discovery

114

115

Find and manage filter files in the filesystem.

116

117

```python { .api }

118

def get_filter_dirs(hardcoded=True) -> list:

119

"""

120

Get directories where panflute searches for filters.

121

122

Parameters:

123

- hardcoded: use predefined paths vs querying Pandoc (default: True)

124

125

Returns:

126

list: Directory paths to search for filters

127

128

Default search locations:

129

- Linux/macOS: ~/.local/share/pandoc/filters, ~/.pandoc/filters (older Pandoc)

130

- Windows: %APPDATA%/pandoc/filters

131

132

Example:

133

import panflute as pf

134

135

# Get default filter directories

136

dirs = pf.get_filter_dirs()

137

print("Searching for filters in:", dirs)

138

139

# Query Pandoc directly for current directories

140

current_dirs = pf.get_filter_dirs(hardcoded=False)

141

"""

142

143

```

144

145

## Usage Examples

146

147

### Document Metadata Configuration

148

149

Configure filters through document frontmatter:

150

151

```yaml

152

---

153

title: "My Document"

154

author: "John Doe"

155

156

# Panflute configuration

157

panflute-filters:

158

- emphasis_processor

159

- link_enhancer

160

- bibliography_generator

161

162

panflute-path:

163

- ./my-filters

164

- ~/.local/share/pandoc/filters

165

- --data-dir

166

167

panflute-verbose: true

168

panflute-echo: "Processing with custom filters..."

169

---

170

171

# Document Content

172

173

This document will be processed by the specified filters.

174

```

175

176

### Command Line Usage Patterns

177

178

```bash

179

# Basic filter usage

180

pandoc document.md --filter panflute -o output.html

181

182

# Advanced pipeline with multiple filters

183

pandoc document.md -t json | \

184

panfl filter1.py custom_processor --to json | \

185

pandoc -f json -o output.pdf

186

187

# Using custom search directories

188

panfl --dir ./project-filters --dir ~/.pandoc/filters \

189

emphasis_filter link_processor --to html

190

191

# Filter development and testing

192

echo '{"pandoc-api-version":[1,23],"meta":{},"blocks":[]}' | \

193

panfl --data-dir my_test_filter --to json

194

```

195

196

### Custom Filter Runner

197

198

Create a custom filter execution environment:

199

200

```python

201

import panflute as pf

202

import sys

203

import io

204

205

def run_custom_pipeline():

206

"""Run a custom filter pipeline with error handling."""

207

208

# Define filter sequence

209

filters = [

210

'preprocessing_filter',

211

'content_enhancer',

212

'formatting_cleaner'

213

]

214

215

# Set up search paths

216

search_dirs = [

217

'./filters',

218

'~/.local/share/pandoc/filters',

219

'/usr/local/share/pandoc/filters'

220

]

221

222

try:

223

# Load document

224

doc = pf.load()

225

226

# Add processing metadata

227

doc.processing_start = pf.meta2builtin(doc.get_metadata('date', ''))

228

doc.filter_count = 0

229

230

# Process with filters

231

pf.stdio(

232

filters=filters,

233

search_dirs=search_dirs,

234

data_dir=True,

235

sys_path=True,

236

input_stream=sys.stdin,

237

output_stream=sys.stdout

238

)

239

240

except Exception as e:

241

pf.debug(f"Filter pipeline failed: {e}")

242

sys.exit(1)

243

244

if __name__ == '__main__':

245

run_custom_pipeline()

246

```

247

248

### Filter Discovery and Management

249

250

```python

251

import panflute as pf

252

import os

253

254

def list_available_filters():

255

"""Discover and list all available filters."""

256

257

search_dirs = pf.get_filter_dirs()

258

available_filters = []

259

260

for directory in search_dirs:

261

if os.path.exists(directory):

262

for filename in os.listdir(directory):

263

if filename.endswith('.py'):

264

filter_path = os.path.join(directory, filename)

265

filter_name = filename[:-3] # Remove .py extension

266

267

# Check if it's a valid panflute filter

268

try:

269

with open(filter_path, 'r') as f:

270

content = f.read()

271

if 'def main(' in content and 'panflute' in content:

272

available_filters.append({

273

'name': filter_name,

274

'path': filter_path,

275

'directory': directory

276

})

277

except Exception:

278

continue

279

280

return available_filters

281

282

def validate_filter_environment():

283

"""Check filter environment and dependencies."""

284

285

# Check Pandoc installation

286

try:

287

version_info = pf.run_pandoc(args=['--version'])

288

pf.debug(f"Pandoc version: {version_info.split()[1]}")

289

except Exception as e:

290

pf.debug(f"Pandoc not found: {e}")

291

return False

292

293

# Check filter directories

294

dirs = pf.get_filter_dirs()

295

pf.debug(f"Filter search directories: {dirs}")

296

297

for directory in dirs:

298

if os.path.exists(directory):

299

filter_count = len([f for f in os.listdir(directory) if f.endswith('.py')])

300

pf.debug(f" {directory}: {filter_count} Python files")

301

else:

302

pf.debug(f" {directory}: does not exist")

303

304

return True

305

306

# Usage example

307

if __name__ == '__main__':

308

if validate_filter_environment():

309

filters = list_available_filters()

310

pf.debug(f"Found {len(filters)} available filters:")

311

for filter_info in filters:

312

pf.debug(f" {filter_info['name']} ({filter_info['path']})")

313

```

314

315

### Integration with External Tools

316

317

```python

318

import panflute as pf

319

import subprocess

320

import tempfile

321

import os

322

323

def pandoc_filter_chain():

324

"""Example of complex document processing chain."""

325

326

def preprocess_filter(elem, doc):

327

"""First stage: clean up input."""

328

if isinstance(elem, pf.Str):

329

# Normalize whitespace

330

elem.text = ' '.join(elem.text.split())

331

return elem

332

333

def content_filter(elem, doc):

334

"""Second stage: transform content."""

335

if isinstance(elem, pf.Header) and elem.level == 1:

336

# Add document structure tracking

337

if not hasattr(doc, 'sections'):

338

doc.sections = []

339

doc.sections.append(pf.stringify(elem))

340

return elem

341

342

def postprocess_filter(elem, doc):

343

"""Third stage: final formatting."""

344

if isinstance(elem, pf.Link):

345

# Ensure external links open in new tab

346

if elem.url.startswith('http'):

347

elem.attributes['target'] = '_blank'

348

return elem

349

350

def finalize_doc(doc):

351

"""Add document metadata after processing."""

352

if hasattr(doc, 'sections'):

353

doc.metadata['generated-toc'] = pf.MetaList(

354

*[pf.MetaString(section) for section in doc.sections]

355

)

356

357

# Run the filter chain

358

pf.run_filters(

359

[preprocess_filter, content_filter, postprocess_filter],

360

finalize=finalize_doc

361

)

362

363

if __name__ == '__main__':

364

pandoc_filter_chain()

365

```