Skip to content

Commit acd8d8b

Browse files
authored
Merge pull request #22 from The-Pocket/feat/cursorrule
Feat/cursorrule
2 parents 8461de0 + 0fd1aed commit acd8d8b

File tree

9 files changed

+2266
-358
lines changed

9 files changed

+2266
-358
lines changed

.cursorrule

Lines changed: 2048 additions & 0 deletions
Large diffs are not rendered by default.

README.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,18 @@ Note: To publish to npm, maintainers need to configure the `NPM_TOKEN` secret in
112112
- Link any related issues
113113
- Answer any questions or feedback during review
114114

115+
### Creating a CursorRule
116+
117+
To create a CursorRule to make AI agents work more effectively on the codebase:
118+
119+
1. Visit [gitingest.com](https://gitingest.com/)
120+
2. Paste the link to the docs folder (e.g., https://github.com/The-Pocket/PocketFlow-Typescript/tree/main/docs) to generate content
121+
3. Remove the following from the generated result:
122+
- All utility function files except for llm
123+
- The design_pattern/multi_agent.md file
124+
- All \_config.yaml and index.md files, except for docs/index.md
125+
4. Save the result as a CursorRule to help AI agents understand the codebase structure better
126+
115127
### Development Setup
116128

117129
```bash

docs/core_abstraction/batch.md

Lines changed: 26 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -23,15 +23,14 @@ A **BatchNode** extends `Node` but changes `prep()` and `exec()`:
2323
### Example: Summarize a Large File
2424

2525
```typescript
26-
// Define shared storage type
2726
type SharedStorage = {
2827
data: string;
2928
summary?: string;
3029
};
3130

3231
class MapSummaries extends BatchNode<SharedStorage> {
3332
async prep(shared: SharedStorage): Promise<string[]> {
34-
// Suppose we have a big file; chunk it
33+
// Chunk content into manageable pieces
3534
const content = shared.data;
3635
const chunks: string[] = [];
3736
const chunkSize = 10000;
@@ -44,27 +43,22 @@ class MapSummaries extends BatchNode<SharedStorage> {
4443
}
4544

4645
async exec(chunk: string): Promise<string> {
47-
// Process each chunk with LLM
4846
const prompt = `Summarize this chunk in 10 words: ${chunk}`;
49-
const summary = await callLlm(prompt);
50-
return summary;
47+
return await callLlm(prompt);
5148
}
5249

5350
async post(
5451
shared: SharedStorage,
55-
prepRes: string[],
56-
execRes: string[]
57-
): Promise<string | undefined> {
58-
// Combine summaries
59-
const combined = execRes.join("\n");
60-
shared.summary = combined;
52+
_: string[],
53+
summaries: string[]
54+
): Promise<string> {
55+
shared.summary = summaries.join("\n");
6156
return "default";
6257
}
6358
}
6459

6560
// Usage
66-
const mapSummaries = new MapSummaries();
67-
const flow = new Flow(mapSummaries);
61+
const flow = new Flow(new MapSummaries());
6862
await flow.run({ data: "very long text content..." });
6963
```
7064

@@ -77,7 +71,6 @@ A **BatchFlow** runs a **Flow** multiple times, each time with different `params
7771
### Example: Summarize Many Files
7872

7973
```typescript
80-
// Define shared storage and parameter types
8174
type SharedStorage = {
8275
files: string[];
8376
};
@@ -92,39 +85,28 @@ class SummarizeAllFiles extends BatchFlow<SharedStorage> {
9285
}
9386
}
9487

95-
// Suppose we have a per-file Flow (e.g., load_file >> summarize >> reduce):
96-
const summarizeFile = new SummarizeFile(loadFile);
97-
98-
// Wrap that flow into a BatchFlow:
88+
// Create a per-file summarization flow
89+
const summarizeFile = new SummarizeFile();
9990
const summarizeAllFiles = new SummarizeAllFiles(summarizeFile);
100-
await summarizeAllFiles.run(shared);
91+
92+
await summarizeAllFiles.run({ files: ["file1.txt", "file2.txt"] });
10193
```
10294

10395
### Under the Hood
10496

105-
1. `prep(shared)` returns a list of param dicts—e.g., `[{filename: "file1.txt"}, {filename: "file2.txt"}, ...]`.
106-
2. The **BatchFlow** loops through each dict. For each one:
107-
- It merges the dict with the BatchFlow's own `params`.
108-
- It calls `flow.run(shared)` using the merged result.
109-
3. This means the sub-Flow is run **repeatedly**, once for every param dict.
97+
1. `prep(shared)` returns a list of param objects—e.g., `[{filename: "file1.txt"}, {filename: "file2.txt"}, ...]`.
98+
2. The **BatchFlow** loops through each object and:
99+
- Merges it with the BatchFlow's own `params`
100+
- Calls `flow.run(shared)` using the merged result
101+
3. This means the sub-Flow runs **repeatedly**, once for every param object.
110102

111103
---
112104

113-
## 3. Nested or Multi-Level Batches
114-
115-
You can nest a **BatchFlow** in another **BatchFlow**. For instance:
116-
117-
- **Outer** batch: returns a list of directory param dicts (e.g., `{"directory": "/pathA"}`, `{"directory": "/pathB"}`, ...).
118-
- **Inner** batch: returning a list of per-file param dicts.
105+
## 3. Nested Batches
119106

120-
At each level, **BatchFlow** merges its own param dict with the parent's. By the time you reach the **innermost** node, the final `params` is the merged result of **all** parents in the chain. This way, a nested structure can keep track of the entire context (e.g., directory + file name) at once.
107+
You can nest BatchFlows to handle hierarchical data processing:
121108

122109
```typescript
123-
// Define shared storage and parameter types
124-
type SharedStorage = {
125-
[key: string]: any;
126-
};
127-
128110
type DirectoryParams = {
129111
directory: string;
130112
};
@@ -136,30 +118,28 @@ type FileParams = DirectoryParams & {
136118
class FileBatchFlow extends BatchFlow<SharedStorage> {
137119
async prep(shared: SharedStorage): Promise<FileParams[]> {
138120
const directory = this._params.directory;
139-
// Get files from the directory
140121
const files = await getFilesInDirectory(directory).filter((f) =>
141122
f.endsWith(".txt")
142123
);
143124

144125
return files.map((filename) => ({
145-
directory, // Pass on the directory from parent
146-
filename, // Add the filename for this batch item
126+
directory, // Pass on directory from parent
127+
filename, // Add filename for this batch item
147128
}));
148129
}
149130
}
150131

151132
class DirectoryBatchFlow extends BatchFlow<SharedStorage> {
152133
async prep(shared: SharedStorage): Promise<DirectoryParams[]> {
153-
const directories = ["/path/to/dirA", "/path/to/dirB"];
154-
return directories.map((directory) => ({
134+
return ["/path/to/dirA", "/path/to/dirB"].map((directory) => ({
155135
directory,
156136
}));
157137
}
158138
}
159139

160-
// MapSummaries will have params like {"directory": "/path/to/dirA", "filename": "file1.txt"}
161-
const mapSummaries = new MapSummaries();
162-
const innerFlow = new FileBatchFlow(mapSummaries);
163-
const outerFlow = new DirectoryBatchFlow(innerFlow);
164-
await outerFlow.run({});
140+
// Process all files in all directories
141+
const processingNode = new ProcessingNode();
142+
const fileFlow = new FileBatchFlow(processingNode);
143+
const dirFlow = new DirectoryBatchFlow(fileFlow);
144+
await dirFlow.run({});
165145
```

docs/core_abstraction/node.md

Lines changed: 25 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -56,62 +56,53 @@ When an exception occurs in `exec()`, the Node automatically retries until:
5656

5757
You can get the current retry times (0-based) from `this.currentRetry`.
5858

59-
```typescript
60-
class RetryNode extends Node {
61-
exec(prepRes: unknown): unknown {
62-
console.log(`Retry ${this.currentRetry} times`);
63-
throw new Error("Failed");
64-
}
65-
}
66-
```
67-
6859
### Graceful Fallback
6960

7061
To **gracefully handle** the exception (after all retries) rather than raising it, override:
7162

7263
```typescript
7364
execFallback(prepRes: unknown, error: Error): unknown {
74-
throw error;
65+
return "There was an error processing your request.";
7566
}
7667
```
7768

78-
By default, it just re-raises exception. But you can return a fallback result instead, which becomes the `execRes` passed to `post()`.
69+
By default, it just re-raises the exception.
7970

8071
### Example: Summarize file
8172

8273
```typescript
83-
class SummarizeFile extends Node {
84-
prep(shared: unknown): string {
85-
return shared["data"];
74+
type SharedStore = {
75+
data: string;
76+
summary?: string;
77+
};
78+
79+
class SummarizeFile extends Node<SharedStore> {
80+
prep(shared: SharedStore): string {
81+
return shared.data;
8682
}
8783

88-
exec(prepRes: string): string {
89-
if (!prepRes) {
90-
return "Empty file content";
91-
}
92-
const prompt = `Summarize this text in 10 words: ${prepRes}`;
93-
const summary = callLlm(prompt); // might fail
94-
return summary;
84+
exec(content: string): string {
85+
if (!content) return "Empty file content";
86+
87+
const prompt = `Summarize this text in 10 words: ${content}`;
88+
return callLlm(prompt);
9589
}
9690

97-
execFallback(prepRes: string, error: Error): string {
98-
// Provide a simple fallback instead of crashing
91+
execFallback(_: string, error: Error): string {
9992
return "There was an error processing your request.";
10093
}
10194

102-
post(shared: unknown, prepRes: string, execRes: string): Action | undefined {
103-
shared["summary"] = execRes;
104-
// Return "default" by not returning anything
105-
return undefined;
95+
post(shared: SharedStore, _: string, summary: string): string | undefined {
96+
shared.summary = summary;
97+
return undefined; // "default" action
10698
}
10799
}
108100

109-
const summarizeNode = new SummarizeFile(3); // maxRetries = 3
110-
111-
// node.run() calls prep->exec->post
112-
// If exec() fails, it retries up to 3 times before calling execFallback()
113-
const actionResult = summarizeNode.run(shared);
101+
// Example usage
102+
const node = new SummarizeFile(3); // maxRetries = 3
103+
const shared: SharedStore = { data: "Long text to summarize..." };
104+
const action = node.run(shared);
114105

115-
console.log("Action returned:", actionResult); // undefined
116-
console.log("Summary stored:", shared["summary"]);
106+
console.log("Action:", action);
107+
console.log("Summary:", shared.summary);
117108
```

0 commit comments

Comments
 (0)