@brunobrise/xfeat 1.2.0 → 1.4.0

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.
@@ -2,9 +2,9 @@ name: CI
2
2
 
3
3
  on:
4
4
  push:
5
- branches: [ "main" ]
5
+ branches: ["main"]
6
6
  pull_request:
7
- branches: [ "main" ]
7
+ branches: ["main"]
8
8
 
9
9
  jobs:
10
10
  build:
@@ -15,13 +15,13 @@ jobs:
15
15
  node-version: [18.x, 20.x]
16
16
 
17
17
  steps:
18
- - uses: actions/checkout@v4
19
- - name: Use Node.js ${{ matrix.node-version }}
20
- uses: actions/setup-node@v4
21
- with:
22
- node-version: ${{ matrix.node-version }}
23
- cache: 'npm'
24
- - run: npm ci
25
- - run: npm run lint
26
- - run: npm test
27
- - run: npm audit --audit-level=critical
18
+ - uses: actions/checkout@v4
19
+ - name: Use Node.js ${{ matrix.node-version }}
20
+ uses: actions/setup-node@v4
21
+ with:
22
+ node-version: ${{ matrix.node-version }}
23
+ cache: "npm"
24
+ - run: npm ci
25
+ - run: npm run lint
26
+ - run: npm test
27
+ - run: npm audit --audit-level=critical
@@ -1 +1 @@
1
- {}
1
+ {}
package/README.md CHANGED
@@ -11,11 +11,15 @@
11
11
  - **Agentic Source Code Reading:** Rather than guessing based off function identifiers, the AI utilizes a specialized `view_file` tool to selectively dive into the raw source code wherever AST context is insufficient.
12
12
  - **Automated Mermaid Diagrams:** Visually maps out how files interact at macro and global architecture levels.
13
13
  - **Structured Markdown Deliverables:** Produces a neat, hierarchical `FEATURES.md` report encompassing everything from the executive summary to granular file logic.
14
- - **Smart Directory Traversal:** Adheres to your local `.gitignore` rules to avoid processing build artifacts and generic dependencies.
14
+ - **Smart Directory Traversal:** Adheres to your local `.gitignore` and optional custom `.xfeatignore` rules to avoid processing build artifacts and generic dependencies.
15
15
 
16
16
  ## How It Works
17
17
 
18
- The engine executes in an expanding 3-stage pipeline:
18
+ The engine executes in an expanding 4-stage pipeline:
19
+
20
+ 0. **AI Pre-filtering (Stage 0):**
21
+ - Interactively requests permission to AI-filter the target files.
22
+ - Cleans the file list by intelligently removing trivial boilerplate, config files, and UI assets dynamically, saving time and tokens.
19
23
 
20
24
  1. **Micro Analysis (File-Level):**
21
25
  - Identifies granular structural signatures across source files.
@@ -94,6 +98,7 @@ The foundational Tree-sitter AST parser natively understands:
94
98
  - TypeScript (`.ts`, `.tsx`)
95
99
  - Python (`.py`)
96
100
  - Rust (`.rs`)
101
+ - Go (`.go`)
97
102
 
98
103
  ## License
99
104
 
package/index.js CHANGED
@@ -51,6 +51,17 @@ async function getIgnores(targetDir) {
51
51
  } catch (err) {
52
52
  // No .gitignore found, proceed with defaults
53
53
  }
54
+
55
+ try {
56
+ const xfeatignoreContent = await fs.readFile(
57
+ path.join(targetDir, ".xfeatignore"),
58
+ "utf8",
59
+ );
60
+ ig.add(xfeatignoreContent);
61
+ } catch (err) {
62
+ // No .xfeatignore found, silently continue
63
+ }
64
+
54
65
  return ig;
55
66
  }
56
67
 
@@ -154,7 +165,8 @@ async function extractStructure(filePath) {
154
165
  const isClass =
155
166
  type.includes("class") ||
156
167
  type.includes("struct") ||
157
- type.includes("interface");
168
+ type.includes("interface") ||
169
+ type === "type_spec";
158
170
  const isFunction =
159
171
  type.includes("function") ||
160
172
  type.includes("method") ||
@@ -169,8 +181,14 @@ async function extractStructure(filePath) {
169
181
  if (isClass) {
170
182
  const nameNode =
171
183
  node.childForFieldName("name") ||
172
- node.children.find((c) => c.type === "identifier");
173
- if (nameNode) structure.classes.push(nameNode.text);
184
+ node.children.find((c) => c.type === "identifier") ||
185
+ node.children.find((c) => c.type === "type_identifier");
186
+ if (nameNode) {
187
+ structure.classes.push(nameNode.text);
188
+ if (ext === ".go" && /^[A-Z]/.test(nameNode.text)) {
189
+ structure.exports.push(nameNode.text);
190
+ }
191
+ }
174
192
  }
175
193
 
176
194
  if (isFunction) {
@@ -179,6 +197,9 @@ async function extractStructure(filePath) {
179
197
  node.children.find((c) => c.type === "identifier");
180
198
  if (nameNode && !nameNode.text.startsWith("__")) {
181
199
  structure.functions.push(nameNode.text);
200
+ if (ext === ".go" && /^[A-Z]/.test(nameNode.text)) {
201
+ structure.exports.push(nameNode.text);
202
+ }
182
203
  }
183
204
  }
184
205
 
package/index.test.js CHANGED
@@ -53,6 +53,17 @@ def py_function():
53
53
  `,
54
54
  );
55
55
 
56
+ await fs.writeFile(
57
+ path.join(testDir, "sample.go"),
58
+ `
59
+ package main
60
+ import "fmt"
61
+ type MyGoStruct struct {}
62
+ func (m *MyGoStruct) GoMethod() {}
63
+ func GoFunction() {}
64
+ `,
65
+ );
66
+
56
67
  await fs.writeFile(path.join(testDir, "unsupported.txt"), "Hello world");
57
68
 
58
69
  // Initialize TreeSitter before testing extraction
@@ -132,6 +143,21 @@ def py_function():
132
143
  expect(structure.functions).toContain("rs_function");
133
144
  });
134
145
 
146
+ it("should extract AST structure for Go files", async () => {
147
+ const goFilePath = path.join(testDir, "sample.go");
148
+ const structure = await extractStructure(goFilePath);
149
+
150
+ expect(structure).not.toBeNull();
151
+ expect(structure.file).toBe(goFilePath);
152
+ expect(structure.classes).toContain("MyGoStruct");
153
+ expect(structure.functions).toContain("GoMethod");
154
+ expect(structure.functions).toContain("GoFunction");
155
+ expect(structure.exports).toContain("MyGoStruct");
156
+ expect(structure.exports).toContain("GoMethod");
157
+ expect(structure.exports).toContain("GoFunction");
158
+ expect(structure.imports).toContain('"fmt"');
159
+ });
160
+
135
161
  it("should return null for unsupported file extensions", async () => {
136
162
  const txtFilePath = path.join(testDir, "unsupported.txt");
137
163
  const structure = await extractStructure(txtFilePath);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@brunobrise/xfeat",
3
- "version": "1.2.0",
3
+ "version": "1.4.0",
4
4
  "description": "Automated AI-driven CLI for codebase analysis and feature extraction.",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -42,6 +42,7 @@
42
42
  "map-stream": "^0.0.7",
43
43
  "tree-sitter-bash": "^0.25.1",
44
44
  "tree-sitter-css": "^0.25.0",
45
+ "tree-sitter-go": "^0.25.0",
45
46
  "tree-sitter-html": "^0.23.2",
46
47
  "tree-sitter-javascript": "^0.25.0",
47
48
  "tree-sitter-json": "^0.24.8",