pointfree-docs 0.1.0 → 0.2.1
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/README.md +67 -7
- package/dist/cli.js +89 -2
- package/dist/commands/get.d.ts +2 -0
- package/dist/commands/get.js +59 -2
- package/dist/commands/init.d.ts +2 -0
- package/dist/commands/init.js +78 -13
- package/dist/commands/list.d.ts +1 -0
- package/dist/commands/list.js +59 -19
- package/dist/commands/search.d.ts +1 -0
- package/dist/commands/search.js +27 -4
- package/dist/commands/stats.js +32 -5
- package/dist/commands/update.d.ts +2 -0
- package/dist/commands/update.js +33 -7
- package/dist/config.d.ts +32 -1
- package/dist/config.js +34 -1
- package/dist/lib/format.d.ts +12 -0
- package/dist/lib/format.js +30 -0
- package/dist/lib/index.d.ts +14 -2
- package/dist/lib/index.js +162 -25
- package/dist/lib/repos.d.ts +32 -0
- package/dist/lib/repos.js +133 -1
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -2,6 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
A CLI tool for searching Point-Free library documentation locally. Uses sparse git checkout and SQLite FTS5 for fast, offline full-text search. Built for use with AI coding assistants like Claude Code.
|
|
4
4
|
|
|
5
|
+
## Demo
|
|
6
|
+
|
|
7
|
+
See pf-docs in action: install, search, and real-world usage with Claude Code.
|
|
8
|
+
|
|
9
|
+
https://github.com/user-attachments/assets/ed6c6340-2d29-4515-89ca-62f65cfa4755
|
|
10
|
+
|
|
5
11
|
## Quick Start
|
|
6
12
|
|
|
7
13
|
```bash
|
|
@@ -31,7 +37,7 @@ pf-docs list tca
|
|
|
31
37
|
|
|
32
38
|
### `pf-docs list --available`
|
|
33
39
|
|
|
34
|
-
Show all libraries available to download.
|
|
40
|
+
Show all libraries available to download, plus additional sources (examples, episodes).
|
|
35
41
|
|
|
36
42
|
### `pf-docs init`
|
|
37
43
|
|
|
@@ -40,6 +46,9 @@ Download and index documentation. Only fetches `Documentation.docc` folders via
|
|
|
40
46
|
```bash
|
|
41
47
|
pf-docs init --libs tca dependencies navigation
|
|
42
48
|
pf-docs init --all
|
|
49
|
+
pf-docs init --examples # TCA CaseStudies, SyncUps, etc.
|
|
50
|
+
pf-docs init --episodes # 350+ Point-Free episode code samples
|
|
51
|
+
pf-docs init --all --examples --episodes # Everything
|
|
43
52
|
```
|
|
44
53
|
|
|
45
54
|
### `pf-docs update`
|
|
@@ -47,8 +56,10 @@ pf-docs init --all
|
|
|
47
56
|
Pull latest changes and re-index.
|
|
48
57
|
|
|
49
58
|
```bash
|
|
50
|
-
pf-docs update
|
|
51
|
-
pf-docs update --libs tca
|
|
59
|
+
pf-docs update # All initialized libraries
|
|
60
|
+
pf-docs update --libs tca # Specific libraries
|
|
61
|
+
pf-docs update --examples # Update examples
|
|
62
|
+
pf-docs update --episodes # Update episodes
|
|
52
63
|
```
|
|
53
64
|
|
|
54
65
|
### `pf-docs search <query>`
|
|
@@ -59,6 +70,12 @@ Full-text search across all indexed docs.
|
|
|
59
70
|
pf-docs search "testing effects"
|
|
60
71
|
pf-docs search "navigation" --lib tca
|
|
61
72
|
pf-docs search "Store" --limit 5
|
|
73
|
+
|
|
74
|
+
# Search by source type
|
|
75
|
+
pf-docs search "TestStore" --source=docs # Default: docs only
|
|
76
|
+
pf-docs search "TestStore" --source=examples # TCA examples only
|
|
77
|
+
pf-docs search "TestStore" --source=episodes # Episode code only
|
|
78
|
+
pf-docs search "TestStore" --source=all # Everything, labeled
|
|
62
79
|
```
|
|
63
80
|
|
|
64
81
|
### `pf-docs get <path>`
|
|
@@ -68,6 +85,11 @@ Fetch a specific article as clean markdown.
|
|
|
68
85
|
```bash
|
|
69
86
|
pf-docs get tca/Articles/TestingTCA
|
|
70
87
|
pf-docs get dependencies/Articles/QuickStart --raw
|
|
88
|
+
|
|
89
|
+
# Code files show preview by default (50 lines)
|
|
90
|
+
pf-docs get examples/CaseStudies/03-Effects-Basics.swift
|
|
91
|
+
pf-docs get examples/CaseStudies/03-Effects-Basics.swift --raw # Full content
|
|
92
|
+
pf-docs get episodes/0156-testable-state/Main.swift --lines=100 # More lines
|
|
71
93
|
```
|
|
72
94
|
|
|
73
95
|
### `pf-docs list [lib]`
|
|
@@ -75,13 +97,15 @@ pf-docs get dependencies/Articles/QuickStart --raw
|
|
|
75
97
|
List indexed documentation.
|
|
76
98
|
|
|
77
99
|
```bash
|
|
78
|
-
pf-docs list
|
|
79
|
-
pf-docs list tca --tree
|
|
100
|
+
pf-docs list # All indexed docs
|
|
101
|
+
pf-docs list tca --tree # Tree view for one library
|
|
102
|
+
pf-docs list --source=examples # Only examples
|
|
103
|
+
pf-docs list --source=all # All sources
|
|
80
104
|
```
|
|
81
105
|
|
|
82
106
|
### `pf-docs stats`
|
|
83
107
|
|
|
84
|
-
Show indexing statistics.
|
|
108
|
+
Show indexing statistics by source and library.
|
|
85
109
|
|
|
86
110
|
All commands support `--json` for programmatic output.
|
|
87
111
|
|
|
@@ -104,12 +128,48 @@ All commands support `--json` for programmatic output.
|
|
|
104
128
|
|
|
105
129
|
Run `pf-docs list --available` to see this list in your terminal.
|
|
106
130
|
|
|
131
|
+
## Additional Sources
|
|
132
|
+
|
|
133
|
+
| Source | Description | Flag |
|
|
134
|
+
|--------|-------------|------|
|
|
135
|
+
| `examples` | TCA CaseStudies, SyncUps, Todos, VoiceMemos, etc. | `--examples` |
|
|
136
|
+
| `episodes` | 350+ Point-Free episode code samples | `--episodes` |
|
|
137
|
+
|
|
138
|
+
### Source Types for Search
|
|
139
|
+
|
|
140
|
+
When searching, you can filter by source type:
|
|
141
|
+
|
|
142
|
+
- `docs` (default) — Library documentation (DocC articles)
|
|
143
|
+
- `examples` — TCA example apps and case studies
|
|
144
|
+
- `episodes` — Point-Free episode code samples
|
|
145
|
+
- `all` — Everything, with labels showing the source
|
|
146
|
+
|
|
147
|
+
```bash
|
|
148
|
+
# Search docs for concepts
|
|
149
|
+
pf-docs search "testing effects"
|
|
150
|
+
|
|
151
|
+
# Search examples for real implementations
|
|
152
|
+
pf-docs search "TestStore" --source=examples
|
|
153
|
+
|
|
154
|
+
# Search everything
|
|
155
|
+
pf-docs search "dependency injection" --source=all
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
Results with `--source=all` are labeled:
|
|
159
|
+
```
|
|
160
|
+
[DOC] tca/Articles/Testing (0.94)
|
|
161
|
+
[EXAMPLE] CaseStudies/03-Effects-Basics (0.82)
|
|
162
|
+
[EPISODE] Ep156: Testable State (0.71)
|
|
163
|
+
```
|
|
164
|
+
|
|
107
165
|
## Usage with Claude Code
|
|
108
166
|
|
|
109
167
|
Add the key commands to your project's `CLAUDE.md`:
|
|
110
168
|
|
|
111
169
|
```markdown
|
|
112
|
-
Use `pf-docs search "<query>"` to search Point-Free docs
|
|
170
|
+
Use `pf-docs search "<query>"` to search Point-Free docs.
|
|
171
|
+
Use `pf-docs search "<query>" --source=examples` to find real code examples.
|
|
172
|
+
Use `pf-docs get <path>` to read an article or code file.
|
|
113
173
|
```
|
|
114
174
|
|
|
115
175
|
## Adding Libraries
|
package/dist/cli.js
CHANGED
|
@@ -14,31 +14,99 @@ import { statsCommand } from "./commands/stats.js";
|
|
|
14
14
|
const program = new Command();
|
|
15
15
|
program
|
|
16
16
|
.name("pf-docs")
|
|
17
|
-
.description(
|
|
18
|
-
|
|
17
|
+
.description(`CLI tool for searching Point-Free library documentation.
|
|
18
|
+
|
|
19
|
+
Indexes DocC articles, code examples, and episode samples from 12 Point-Free
|
|
20
|
+
open-source libraries (TCA, Dependencies, Navigation, Sharing, etc.).
|
|
21
|
+
|
|
22
|
+
Typical workflow:
|
|
23
|
+
1. pf-docs init --libs tca dependencies # clone & index selected libs
|
|
24
|
+
2. pf-docs search "testing reducer" # full-text search across docs
|
|
25
|
+
3. pf-docs get tca/Articles/Testing # retrieve a specific article
|
|
26
|
+
|
|
27
|
+
Path format: <library>/<Articles|Tutorials|Extensions>/<Name>
|
|
28
|
+
e.g. tca/Articles/TestingTCA, sharing/Articles/SharingState
|
|
29
|
+
|
|
30
|
+
All commands support --json for machine-readable output.`)
|
|
31
|
+
.version("0.2.1");
|
|
19
32
|
program
|
|
20
33
|
.command("init")
|
|
21
34
|
.description("Initialize and download documentation for specified libraries")
|
|
22
35
|
.option("-l, --libs <libs...>", "Libraries to download (e.g., tca dependencies)")
|
|
23
36
|
.option("-a, --all", "Download all available libraries")
|
|
37
|
+
.option("-e, --examples", "Download TCA examples (CaseStudies, SyncUps, etc.)")
|
|
38
|
+
.option("-p, --episodes", "Download Point-Free episode code samples (350+)")
|
|
39
|
+
.addHelpText("after", `
|
|
40
|
+
Examples:
|
|
41
|
+
$ pf-docs init --libs tca dependencies # just TCA and Dependencies
|
|
42
|
+
$ pf-docs init --all # all 12 libraries
|
|
43
|
+
$ pf-docs init --libs tca --examples # TCA docs + example apps
|
|
44
|
+
$ pf-docs init --all --examples --episodes # everything
|
|
45
|
+
|
|
46
|
+
Available library short names:
|
|
47
|
+
tca, dependencies, navigation, perception, sharing,
|
|
48
|
+
identified-collections, case-paths, custom-dump,
|
|
49
|
+
concurrency-extras, clocks, snapshot-testing, issue-reporting
|
|
50
|
+
|
|
51
|
+
Uses sparse git checkout — only documentation folders are cloned.
|
|
52
|
+
Re-running init on already-cloned libraries is safe (skips existing).`)
|
|
24
53
|
.action(initCommand);
|
|
25
54
|
program
|
|
26
55
|
.command("update")
|
|
27
56
|
.description("Update documentation from remote repositories")
|
|
28
57
|
.option("-l, --libs <libs...>", "Specific libraries to update")
|
|
58
|
+
.option("-e, --examples", "Update examples")
|
|
59
|
+
.option("-p, --episodes", "Update episodes")
|
|
60
|
+
.addHelpText("after", `
|
|
61
|
+
Examples:
|
|
62
|
+
$ pf-docs update # update all cloned libraries
|
|
63
|
+
$ pf-docs update --libs tca # update only TCA
|
|
64
|
+
$ pf-docs update --examples --episodes # update examples and episodes
|
|
65
|
+
|
|
66
|
+
Runs git pull on cloned repos and rebuilds the search index.
|
|
67
|
+
Only updates libraries that were previously initialized.`)
|
|
29
68
|
.action(updateCommand);
|
|
30
69
|
program
|
|
31
70
|
.command("search <query>")
|
|
32
71
|
.description("Search across all indexed documentation")
|
|
33
72
|
.option("-l, --lib <lib>", "Limit search to specific library")
|
|
34
73
|
.option("-n, --limit <n>", "Max results to return", "10")
|
|
74
|
+
.option("-s, --source <source>", "Source type: docs, examples, episodes, all (default: docs)")
|
|
35
75
|
.option("-j, --json", "Output results as JSON")
|
|
76
|
+
.addHelpText("after", `
|
|
77
|
+
Examples:
|
|
78
|
+
$ pf-docs search "testing reducer" # search docs (default)
|
|
79
|
+
$ pf-docs search "testing" --lib tca # search within TCA only
|
|
80
|
+
$ pf-docs search "dependency" --limit 5 # top 5 results
|
|
81
|
+
$ pf-docs search "SyncUps" --source examples # search example apps
|
|
82
|
+
$ pf-docs search "parser" --source all # search everything
|
|
83
|
+
$ pf-docs search "testing" --json # JSON output
|
|
84
|
+
|
|
85
|
+
By default, searches docs only. Use --source all to include examples and episodes.
|
|
86
|
+
Results include path (usable with 'get'), title, snippet, and relevance score.
|
|
87
|
+
Uses SQLite FTS5 with BM25 ranking.`)
|
|
36
88
|
.action(searchCommand);
|
|
37
89
|
program
|
|
38
90
|
.command("get <path>")
|
|
39
91
|
.description("Get a specific documentation article (e.g., tca/Testing)")
|
|
40
92
|
.option("-j, --json", "Output as JSON")
|
|
41
93
|
.option("-r, --raw", "Output raw content without header")
|
|
94
|
+
.option("-p, --preview", "Preview mode (first 50 lines for code files)")
|
|
95
|
+
.option("--lines <n>", "Number of lines to show in preview mode", "50")
|
|
96
|
+
.addHelpText("after", `
|
|
97
|
+
Examples:
|
|
98
|
+
$ pf-docs get tca/Articles/Testing # full article as markdown
|
|
99
|
+
$ pf-docs get sharing/Articles/SharingState # article from Sharing lib
|
|
100
|
+
$ pf-docs get tca/Articles/Testing --raw # no header, just content
|
|
101
|
+
$ pf-docs get tca/Articles/Testing --json # JSON with metadata
|
|
102
|
+
$ pf-docs get examples/CaseStudies/01-Basics-BindingBasics --preview
|
|
103
|
+
$ pf-docs get episodes/0290-CrossPlatformPt1 --preview --lines 100
|
|
104
|
+
|
|
105
|
+
Path format for docs: <library>/Articles/<ArticleName>
|
|
106
|
+
Path format for examples: examples/<AppName>/<FilePath>
|
|
107
|
+
Path format for episodes: episodes/<EpisodeName>/<FilePath>
|
|
108
|
+
Use 'pf-docs list <lib>' or search results to discover valid paths.
|
|
109
|
+
Code files (Swift) default to preview mode; use --raw for full content.`)
|
|
42
110
|
.action(getCommand);
|
|
43
111
|
program
|
|
44
112
|
.command("list [lib]")
|
|
@@ -46,10 +114,29 @@ program
|
|
|
46
114
|
.option("-t, --tree", "Show as tree structure")
|
|
47
115
|
.option("-j, --json", "Output as JSON")
|
|
48
116
|
.option("-a, --available", "Show all libraries available to download")
|
|
117
|
+
.option("-s, --source <source>", "Filter by source: docs, examples, episodes, all")
|
|
118
|
+
.addHelpText("after", `
|
|
119
|
+
Examples:
|
|
120
|
+
$ pf-docs list # list all indexed docs
|
|
121
|
+
$ pf-docs list tca # list TCA articles only
|
|
122
|
+
$ pf-docs list tca --tree # tree view of TCA docs
|
|
123
|
+
$ pf-docs list --source examples # list example app files
|
|
124
|
+
$ pf-docs list --source all # list everything
|
|
125
|
+
$ pf-docs list --available # show all downloadable libraries
|
|
126
|
+
$ pf-docs list --json # JSON output
|
|
127
|
+
|
|
128
|
+
Without arguments, lists all indexed documentation articles.
|
|
129
|
+
Paths in the output can be passed directly to 'pf-docs get'.`)
|
|
49
130
|
.action(listCommand);
|
|
50
131
|
program
|
|
51
132
|
.command("stats")
|
|
52
133
|
.description("Show indexing statistics")
|
|
53
134
|
.option("-j, --json", "Output as JSON")
|
|
135
|
+
.addHelpText("after", `
|
|
136
|
+
Examples:
|
|
137
|
+
$ pf-docs stats # summary table
|
|
138
|
+
$ pf-docs stats --json # JSON output
|
|
139
|
+
|
|
140
|
+
Shows per-library document counts, total indexed articles, and index size.`)
|
|
54
141
|
.action(statsCommand);
|
|
55
142
|
program.parse();
|
package/dist/commands/get.d.ts
CHANGED
package/dist/commands/get.js
CHANGED
|
@@ -4,6 +4,29 @@
|
|
|
4
4
|
import chalk from "chalk";
|
|
5
5
|
import { getDoc, withIndex } from "../lib/index.js";
|
|
6
6
|
import { formatDocForOutput } from "../lib/markdown.js";
|
|
7
|
+
import { getSourceLabel } from "../lib/format.js";
|
|
8
|
+
const DEFAULT_PREVIEW_LINES = 50;
|
|
9
|
+
/**
|
|
10
|
+
* Truncate content to a specified number of lines
|
|
11
|
+
*/
|
|
12
|
+
function truncateContent(content, maxLines) {
|
|
13
|
+
const lines = content.split("\n");
|
|
14
|
+
const totalLines = lines.length;
|
|
15
|
+
if (lines.length <= maxLines) {
|
|
16
|
+
return { content, truncated: false, totalLines };
|
|
17
|
+
}
|
|
18
|
+
return {
|
|
19
|
+
content: lines.slice(0, maxLines).join("\n"),
|
|
20
|
+
truncated: true,
|
|
21
|
+
totalLines,
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Check if a document is code (not documentation) based on its source
|
|
26
|
+
*/
|
|
27
|
+
function isCodeFile(doc) {
|
|
28
|
+
return doc.source === "examples" || doc.source === "episodes";
|
|
29
|
+
}
|
|
7
30
|
export function getCommand(path, options = {}) {
|
|
8
31
|
const doc = withIndex(() => getDoc(path));
|
|
9
32
|
if (!doc) {
|
|
@@ -16,8 +39,28 @@ export function getCommand(path, options = {}) {
|
|
|
16
39
|
console.log(chalk.gray(`Or search: pf-docs search "<query>"`));
|
|
17
40
|
return;
|
|
18
41
|
}
|
|
42
|
+
// Determine preview lines
|
|
43
|
+
const previewLines = options.lines
|
|
44
|
+
? parseInt(options.lines, 10)
|
|
45
|
+
: DEFAULT_PREVIEW_LINES;
|
|
46
|
+
// Apply preview mode for code files (examples/episodes)
|
|
47
|
+
const shouldPreview = options.preview || (isCodeFile(doc) && !options.raw);
|
|
48
|
+
let outputContent = doc.content;
|
|
49
|
+
let truncationInfo = "";
|
|
50
|
+
if (shouldPreview && !options.raw) {
|
|
51
|
+
const { content, truncated, totalLines } = truncateContent(doc.content, previewLines);
|
|
52
|
+
outputContent = content;
|
|
53
|
+
if (truncated) {
|
|
54
|
+
truncationInfo = `\n${chalk.gray(`... truncated (showing ${previewLines} of ${totalLines} lines)`)}\n${chalk.gray(`Use --raw for full content, or --lines=<n> for more`)}`;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
19
57
|
if (options.json) {
|
|
20
|
-
|
|
58
|
+
const result = { ...doc, content: outputContent };
|
|
59
|
+
if (truncationInfo) {
|
|
60
|
+
result.truncated = true;
|
|
61
|
+
result.previewLines = previewLines;
|
|
62
|
+
}
|
|
63
|
+
console.log(JSON.stringify(result, null, 2));
|
|
21
64
|
return;
|
|
22
65
|
}
|
|
23
66
|
// Raw output (just content, no header)
|
|
@@ -25,6 +68,20 @@ export function getCommand(path, options = {}) {
|
|
|
25
68
|
console.log(doc.content);
|
|
26
69
|
return;
|
|
27
70
|
}
|
|
71
|
+
// For code files, show with syntax hint
|
|
72
|
+
if (isCodeFile(doc)) {
|
|
73
|
+
console.log(`\n${getSourceLabel(doc.source)} ${chalk.bold(doc.title)}`);
|
|
74
|
+
console.log(chalk.gray(`Path: ${doc.path}`));
|
|
75
|
+
console.log(chalk.gray("─".repeat(60)));
|
|
76
|
+
console.log(outputContent);
|
|
77
|
+
if (truncationInfo) {
|
|
78
|
+
console.log(truncationInfo);
|
|
79
|
+
}
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
28
82
|
// Output the formatted document (clean markdown)
|
|
29
|
-
console.log(formatDocForOutput(doc));
|
|
83
|
+
console.log(formatDocForOutput({ ...doc, content: outputContent }));
|
|
84
|
+
if (truncationInfo) {
|
|
85
|
+
console.log(truncationInfo);
|
|
86
|
+
}
|
|
30
87
|
}
|
package/dist/commands/init.d.ts
CHANGED
package/dist/commands/init.js
CHANGED
|
@@ -3,10 +3,12 @@
|
|
|
3
3
|
*/
|
|
4
4
|
import chalk from "chalk";
|
|
5
5
|
import { LIBRARIES, getLibrary, LIBRARY_NAMES } from "../config.js";
|
|
6
|
-
import { cloneLibrary } from "../lib/repos.js";
|
|
7
|
-
import { indexLibrary, openIndex, closeIndex } from "../lib/index.js";
|
|
6
|
+
import { cloneLibrary, cloneExamples, cloneEpisodes } from "../lib/repos.js";
|
|
7
|
+
import { indexLibrary, indexExamples, indexEpisodes, openIndex, closeIndex } from "../lib/index.js";
|
|
8
8
|
export async function initCommand(options) {
|
|
9
9
|
console.log(chalk.bold("\n📚 Initializing Point-Free Documentation\n"));
|
|
10
|
+
const includeExamples = options.examples || false;
|
|
11
|
+
const includeEpisodes = options.episodes || false;
|
|
10
12
|
// Determine which libraries to download
|
|
11
13
|
let librariesToInit = LIBRARIES;
|
|
12
14
|
if (options.libs && !options.all) {
|
|
@@ -22,28 +24,67 @@ export async function initCommand(options) {
|
|
|
22
24
|
}
|
|
23
25
|
}
|
|
24
26
|
}
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
console.log(
|
|
28
|
-
console.log(
|
|
27
|
+
// If no libs specified and not --all, and no examples/episodes, show usage
|
|
28
|
+
if (librariesToInit.length === 0 && !includeExamples && !includeEpisodes) {
|
|
29
|
+
console.log(chalk.red("No valid libraries or sources specified."));
|
|
30
|
+
console.log(`\nUsage:`);
|
|
31
|
+
console.log(` pf-docs init --libs tca dependencies navigation`);
|
|
32
|
+
console.log(` pf-docs init --all`);
|
|
33
|
+
console.log(` pf-docs init --examples`);
|
|
34
|
+
console.log(` pf-docs init --episodes`);
|
|
35
|
+
console.log(` pf-docs init --all --examples --episodes`);
|
|
29
36
|
console.log(`\nAvailable libraries: ${LIBRARY_NAMES.join(", ")}`);
|
|
30
37
|
return;
|
|
31
38
|
}
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
39
|
+
// Show what we're going to initialize
|
|
40
|
+
const sources = [];
|
|
41
|
+
if (librariesToInit.length > 0) {
|
|
42
|
+
sources.push(`Docs: ${librariesToInit.map((l) => l.shortName).join(", ")}`);
|
|
43
|
+
}
|
|
44
|
+
if (includeExamples) {
|
|
45
|
+
sources.push("Examples (TCA CaseStudies, SyncUps, etc.)");
|
|
46
|
+
}
|
|
47
|
+
if (includeEpisodes) {
|
|
48
|
+
sources.push("Episodes (350+ Point-Free episode code samples)");
|
|
49
|
+
}
|
|
50
|
+
console.log(chalk.blue(`Initializing: \n ${sources.join("\n ")}\n`));
|
|
51
|
+
// Clone library documentation repositories
|
|
52
|
+
if (librariesToInit.length > 0) {
|
|
53
|
+
console.log(chalk.bold("Cloning documentation repositories..."));
|
|
54
|
+
for (const lib of librariesToInit) {
|
|
55
|
+
try {
|
|
56
|
+
await cloneLibrary(lib);
|
|
57
|
+
}
|
|
58
|
+
catch (error) {
|
|
59
|
+
console.error(chalk.red(` ✗ Failed to clone ${lib.shortName}:`), error);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
// Clone examples if requested
|
|
64
|
+
if (includeExamples) {
|
|
65
|
+
console.log(chalk.bold("\nCloning TCA examples..."));
|
|
66
|
+
try {
|
|
67
|
+
await cloneExamples();
|
|
68
|
+
}
|
|
69
|
+
catch (error) {
|
|
70
|
+
console.error(chalk.red(` ✗ Failed to clone examples:`), error);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
// Clone episodes if requested
|
|
74
|
+
if (includeEpisodes) {
|
|
75
|
+
console.log(chalk.bold("\nCloning episode code samples..."));
|
|
36
76
|
try {
|
|
37
|
-
await
|
|
77
|
+
await cloneEpisodes();
|
|
38
78
|
}
|
|
39
79
|
catch (error) {
|
|
40
|
-
console.error(chalk.red(` ✗ Failed to clone
|
|
80
|
+
console.error(chalk.red(` ✗ Failed to clone episodes:`), error);
|
|
41
81
|
}
|
|
42
82
|
}
|
|
43
83
|
// Build search index
|
|
44
84
|
console.log(chalk.bold("\nBuilding search index..."));
|
|
45
85
|
openIndex();
|
|
46
86
|
let totalIndexed = 0;
|
|
87
|
+
// Index library docs
|
|
47
88
|
for (const lib of librariesToInit) {
|
|
48
89
|
try {
|
|
49
90
|
const count = indexLibrary(lib);
|
|
@@ -54,10 +95,34 @@ export async function initCommand(options) {
|
|
|
54
95
|
console.error(chalk.red(` ✗ Failed to index ${lib.shortName}:`), error);
|
|
55
96
|
}
|
|
56
97
|
}
|
|
98
|
+
// Index examples
|
|
99
|
+
if (includeExamples) {
|
|
100
|
+
try {
|
|
101
|
+
const count = indexExamples();
|
|
102
|
+
console.log(` ✓ Indexed examples: ${count} files`);
|
|
103
|
+
totalIndexed += count;
|
|
104
|
+
}
|
|
105
|
+
catch (error) {
|
|
106
|
+
console.error(chalk.red(` ✗ Failed to index examples:`), error);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
// Index episodes
|
|
110
|
+
if (includeEpisodes) {
|
|
111
|
+
try {
|
|
112
|
+
const count = indexEpisodes();
|
|
113
|
+
console.log(` ✓ Indexed episodes: ${count} files`);
|
|
114
|
+
totalIndexed += count;
|
|
115
|
+
}
|
|
116
|
+
catch (error) {
|
|
117
|
+
console.error(chalk.red(` ✗ Failed to index episodes:`), error);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
57
120
|
closeIndex();
|
|
58
|
-
console.log(chalk.green(`\n✅ Done! Indexed ${totalIndexed}
|
|
121
|
+
console.log(chalk.green(`\n✅ Done! Indexed ${totalIndexed} items.\n`));
|
|
59
122
|
console.log(`Try it out:`);
|
|
60
123
|
console.log(chalk.gray(` pf-docs search "testing async effects"`));
|
|
124
|
+
console.log(chalk.gray(` pf-docs search "Store" --source=examples`));
|
|
125
|
+
console.log(chalk.gray(` pf-docs search "dependency" --source=all`));
|
|
61
126
|
console.log(chalk.gray(` pf-docs list tca`));
|
|
62
127
|
console.log(chalk.gray(` pf-docs get tca/Testing`));
|
|
63
128
|
}
|
package/dist/commands/list.d.ts
CHANGED
package/dist/commands/list.js
CHANGED
|
@@ -3,14 +3,19 @@
|
|
|
3
3
|
*/
|
|
4
4
|
import chalk from "chalk";
|
|
5
5
|
import { listDocs, getStats, withIndex } from "../lib/index.js";
|
|
6
|
-
import { getLibrary, LIBRARIES, LIBRARY_NAMES } from "../config.js";
|
|
6
|
+
import { getLibrary, LIBRARIES, LIBRARY_NAMES, SOURCE_TYPES, EXAMPLES_CONFIG, EPISODES_CONFIG } from "../config.js";
|
|
7
|
+
import { getSourceLabel } from "../lib/format.js";
|
|
7
8
|
export function listCommand(lib, options) {
|
|
8
9
|
if (options.available) {
|
|
9
10
|
if (options.json) {
|
|
10
11
|
const libs = LIBRARIES.map(({ shortName, name, repo, description }) => ({
|
|
11
12
|
shortName, name, repo, description,
|
|
12
13
|
}));
|
|
13
|
-
|
|
14
|
+
const extras = [
|
|
15
|
+
{ shortName: "examples", name: EXAMPLES_CONFIG.name, repo: EXAMPLES_CONFIG.repo, description: EXAMPLES_CONFIG.description },
|
|
16
|
+
{ shortName: "episodes", name: EPISODES_CONFIG.name, repo: EPISODES_CONFIG.repo, description: EPISODES_CONFIG.description },
|
|
17
|
+
];
|
|
18
|
+
console.log(JSON.stringify({ libraries: libs, extras }, null, 2));
|
|
14
19
|
return;
|
|
15
20
|
}
|
|
16
21
|
console.log(chalk.bold(`\n📦 Available Libraries\n`));
|
|
@@ -20,10 +25,27 @@ export function listCommand(lib, options) {
|
|
|
20
25
|
}
|
|
21
26
|
console.log(chalk.gray(`Total: ${LIBRARIES.length} libraries`));
|
|
22
27
|
console.log(chalk.gray(`\nTo download: pf-docs init --libs <name> [<name>...]`));
|
|
28
|
+
console.log(chalk.bold(`\n📦 Additional Sources\n`));
|
|
29
|
+
console.log(` ${chalk.magenta("examples".padEnd(24))} ${EXAMPLES_CONFIG.description}`);
|
|
30
|
+
console.log(chalk.gray(` ${"".padEnd(24)} --examples flag\n`));
|
|
31
|
+
console.log(` ${chalk.yellow("episodes".padEnd(24))} ${EPISODES_CONFIG.description}`);
|
|
32
|
+
console.log(chalk.gray(` ${"".padEnd(24)} --episodes flag\n`));
|
|
23
33
|
return;
|
|
24
34
|
}
|
|
35
|
+
// Validate source option
|
|
36
|
+
let source;
|
|
37
|
+
if (options.source) {
|
|
38
|
+
if (options.source === "all" || SOURCE_TYPES.includes(options.source)) {
|
|
39
|
+
source = options.source;
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
console.log(chalk.red(`Invalid source: ${options.source}`));
|
|
43
|
+
console.log(chalk.gray(`Valid sources: ${SOURCE_TYPES.join(", ")}, all`));
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
25
47
|
const { docs, stats } = withIndex(() => ({
|
|
26
|
-
docs: listDocs(lib),
|
|
48
|
+
docs: listDocs(lib, source),
|
|
27
49
|
stats: getStats(),
|
|
28
50
|
}));
|
|
29
51
|
// JSON output for programmatic use
|
|
@@ -45,6 +67,11 @@ export function listCommand(lib, options) {
|
|
|
45
67
|
console.log(chalk.bold(`\n📄 Available Documentation\n`));
|
|
46
68
|
// Show stats summary
|
|
47
69
|
if (!lib) {
|
|
70
|
+
console.log(chalk.gray(`Indexed by source:`));
|
|
71
|
+
for (const [sourceName, count] of Object.entries(stats.bySource)) {
|
|
72
|
+
console.log(chalk.gray(` ${getSourceLabel(sourceName)}: ${count} items`));
|
|
73
|
+
}
|
|
74
|
+
console.log();
|
|
48
75
|
console.log(chalk.gray(`Indexed libraries:`));
|
|
49
76
|
for (const [libName, count] of Object.entries(stats.byLibrary)) {
|
|
50
77
|
const libConfig = getLibrary(libName);
|
|
@@ -54,33 +81,46 @@ export function listCommand(lib, options) {
|
|
|
54
81
|
console.log();
|
|
55
82
|
}
|
|
56
83
|
if (options.tree) {
|
|
57
|
-
// Group by
|
|
58
|
-
const
|
|
84
|
+
// Group by source and library, show as tree
|
|
85
|
+
const bySource = new Map();
|
|
59
86
|
for (const doc of docs) {
|
|
60
|
-
if (!
|
|
61
|
-
|
|
87
|
+
if (!bySource.has(doc.source)) {
|
|
88
|
+
bySource.set(doc.source, new Map());
|
|
89
|
+
}
|
|
90
|
+
const sourceMap = bySource.get(doc.source);
|
|
91
|
+
if (!sourceMap.has(doc.library)) {
|
|
92
|
+
sourceMap.set(doc.library, []);
|
|
62
93
|
}
|
|
63
|
-
|
|
94
|
+
sourceMap.get(doc.library).push(doc);
|
|
64
95
|
}
|
|
65
|
-
for (const [
|
|
66
|
-
console.log(
|
|
67
|
-
for (
|
|
68
|
-
|
|
69
|
-
const
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
96
|
+
for (const [sourceName, byLibrary] of bySource) {
|
|
97
|
+
console.log(getSourceLabel(sourceName));
|
|
98
|
+
for (const [library, libraryDocs] of byLibrary) {
|
|
99
|
+
console.log(chalk.blue(` ${library}/`));
|
|
100
|
+
const maxToShow = 10;
|
|
101
|
+
for (let i = 0; i < Math.min(libraryDocs.length, maxToShow); i++) {
|
|
102
|
+
const doc = libraryDocs[i];
|
|
103
|
+
const relativePath = doc.path.replace(`${library}/`, "").replace(`${sourceName}/`, "");
|
|
104
|
+
const isLast = i === Math.min(libraryDocs.length, maxToShow) - 1;
|
|
105
|
+
const prefix = isLast ? "└── " : "├── ";
|
|
106
|
+
console.log(chalk.gray(` ${prefix}${relativePath}`));
|
|
107
|
+
}
|
|
108
|
+
if (libraryDocs.length > maxToShow) {
|
|
109
|
+
console.log(chalk.gray(` ... and ${libraryDocs.length - maxToShow} more`));
|
|
110
|
+
}
|
|
73
111
|
}
|
|
74
112
|
console.log();
|
|
75
113
|
}
|
|
76
114
|
}
|
|
77
115
|
else {
|
|
78
|
-
// Simple list
|
|
116
|
+
// Simple list with source labels
|
|
117
|
+
const showSourceLabel = !source || source === "all";
|
|
79
118
|
for (const doc of docs) {
|
|
80
|
-
|
|
119
|
+
const label = showSourceLabel ? `${getSourceLabel(doc.source)} ` : "";
|
|
120
|
+
console.log(`${label}${chalk.blue(doc.path)}`);
|
|
81
121
|
console.log(chalk.gray(` ${doc.title}`));
|
|
82
122
|
}
|
|
83
123
|
}
|
|
84
|
-
console.log(chalk.gray(`\nTotal: ${docs.length}
|
|
124
|
+
console.log(chalk.gray(`\nTotal: ${docs.length} items`));
|
|
85
125
|
console.log(chalk.gray(`\nTo view a document: pf-docs get <path>`));
|
|
86
126
|
}
|