carto-md 1.0.18 → 1.0.19
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 +10 -11
- package/package.json +1 -1
- package/src/detector/files.js +2 -2
- package/src/extractors/imports.js +32 -0
- package/src/extractors/languages/r.js +19 -0
package/README.md
CHANGED
|
@@ -135,14 +135,13 @@ npx carto-md init
|
|
|
135
135
|
# 1. Go to your project
|
|
136
136
|
cd your-project
|
|
137
137
|
|
|
138
|
-
# 2.
|
|
138
|
+
# 2. Run once — like git init
|
|
139
139
|
carto init
|
|
140
|
-
|
|
141
|
-
# 3. Keep it live while you work
|
|
142
|
-
carto watch
|
|
143
140
|
```
|
|
144
141
|
|
|
145
|
-
|
|
142
|
+
That's it. Carto installs a git hook. Every `git commit` syncs AGENTS.md automatically — no watching, no manual runs, nothing to remember.
|
|
143
|
+
|
|
144
|
+
Want live updates on every file save too? Run `carto watch` in a background terminal.
|
|
146
145
|
|
|
147
146
|
---
|
|
148
147
|
|
|
@@ -150,17 +149,17 @@ Leave `carto watch` running in a background terminal. Every file save updates AG
|
|
|
150
149
|
|
|
151
150
|
| Command | What it does |
|
|
152
151
|
|---------|-------------|
|
|
153
|
-
| `carto init` | Detect stack, generate AGENTS.md, install git hook |
|
|
154
|
-
| `carto watch` |
|
|
155
|
-
| `carto sync` | One-time refresh
|
|
152
|
+
| `carto init` | Detect stack, generate AGENTS.md, install git hook — auto-syncs on every commit |
|
|
153
|
+
| `carto watch` | Live updates on every file save — optional, for between commits |
|
|
154
|
+
| `carto sync` | One-time manual refresh |
|
|
156
155
|
| `carto impact <file>` | Show blast radius before touching a file |
|
|
157
156
|
| `carto remove` | Remove AGENTS.md and .carto/ from this project |
|
|
158
157
|
| `carto --version` | Show version |
|
|
159
158
|
|
|
160
159
|
**When to use each:**
|
|
161
|
-
- `init` — once
|
|
162
|
-
- `watch` —
|
|
163
|
-
- `sync` — skipped watch and need a fresh snapshot
|
|
160
|
+
- `init` — once per project, sets everything up
|
|
161
|
+
- `watch` — optional, if you want updates between commits
|
|
162
|
+
- `sync` — if you skipped watch and need a fresh snapshot
|
|
164
163
|
- `impact` — before editing anything critical
|
|
165
164
|
|
|
166
165
|
---
|
package/package.json
CHANGED
package/src/detector/files.js
CHANGED
|
@@ -64,8 +64,8 @@ function discoverForFramework(projectRoot, framework, ignoreFn) {
|
|
|
64
64
|
if (['plumber', 'shiny', 'r-generic'].includes(framework)) {
|
|
65
65
|
const rFiles = findFilesRecursive(projectRoot, ['.r'], R_IGNORE, ignoreFn)
|
|
66
66
|
.filter(f => {
|
|
67
|
-
const
|
|
68
|
-
return !
|
|
67
|
+
const lbase = path.basename(f).toLowerCase();
|
|
68
|
+
return !lbase.startsWith('test_') && !lbase.startsWith('test-') && !lbase.endsWith('_test.r');
|
|
69
69
|
});
|
|
70
70
|
|
|
71
71
|
if (rFiles.length <= MAX_FILES_TOTAL) {
|
|
@@ -20,6 +20,10 @@ const fs = require('fs');
|
|
|
20
20
|
* from app.module import X (local package — resolved if file exists)
|
|
21
21
|
* import .module (relative)
|
|
22
22
|
*
|
|
23
|
+
* R patterns:
|
|
24
|
+
* library(pkg) / require(pkg) (package name recorded as-is)
|
|
25
|
+
* source("./file.R") (resolved if file exists)
|
|
26
|
+
*
|
|
23
27
|
* Only includes paths that resolve to actual files in the project.
|
|
24
28
|
* Skips: node_modules, non-code files, anything that doesn't resolve.
|
|
25
29
|
*/
|
|
@@ -33,6 +37,8 @@ function extractImports(content, filePath, projectRoot) {
|
|
|
33
37
|
rawImports = extractJSImports(content);
|
|
34
38
|
} else if (ext === '.py') {
|
|
35
39
|
rawImports = extractPythonImports(content, filePath, projectRoot);
|
|
40
|
+
} else if (ext === '.r') {
|
|
41
|
+
return extractRImports(content, filePath, projectRoot);
|
|
36
42
|
}
|
|
37
43
|
|
|
38
44
|
// Resolve and deduplicate
|
|
@@ -72,6 +78,32 @@ function extractJSImports(content) {
|
|
|
72
78
|
return imports;
|
|
73
79
|
}
|
|
74
80
|
|
|
81
|
+
/**
|
|
82
|
+
* Extract imports from R content.
|
|
83
|
+
* library(pkg) / require(pkg) → package name recorded directly.
|
|
84
|
+
* source("./file.R") → resolved relative path if the file exists.
|
|
85
|
+
*/
|
|
86
|
+
function extractRImports(content, filePath, projectRoot) {
|
|
87
|
+
const results = new Set();
|
|
88
|
+
const fileDir = path.dirname(filePath);
|
|
89
|
+
|
|
90
|
+
const pkgRe = /(?:library|require)\s*\(\s*["']?(\w[\w.]+)["']?\s*\)/g;
|
|
91
|
+
let m;
|
|
92
|
+
while ((m = pkgRe.exec(content)) !== null) {
|
|
93
|
+
results.add(m[1]);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const sourceRe = /source\s*\(\s*["']([^"']+)["']\s*\)/g;
|
|
97
|
+
while ((m = sourceRe.exec(content)) !== null) {
|
|
98
|
+
const abs = path.resolve(fileDir, m[1]);
|
|
99
|
+
if (fs.existsSync(abs)) {
|
|
100
|
+
results.add(path.relative(projectRoot, abs));
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return [...results].sort();
|
|
105
|
+
}
|
|
106
|
+
|
|
75
107
|
/**
|
|
76
108
|
* Extract import paths from Python content. Relative imports only.
|
|
77
109
|
*/
|
|
@@ -201,6 +201,25 @@ function extractModels(content) {
|
|
|
201
201
|
models.push({ className, fields });
|
|
202
202
|
}
|
|
203
203
|
|
|
204
|
+
const s7Re = /^(\w+)\s*<-\s*(?:S7::)?new_class\s*\(\s*(?:name\s*=\s*)?["'](\w+)["']/gm;
|
|
205
|
+
while ((m = s7Re.exec(collapsed)) !== null) {
|
|
206
|
+
const className = m[2];
|
|
207
|
+
const propsIdx = collapsed.indexOf('properties', m.index);
|
|
208
|
+
if (propsIdx === -1 || propsIdx > m.index + 400) continue;
|
|
209
|
+
const listIdx = collapsed.indexOf('list(', propsIdx);
|
|
210
|
+
if (listIdx === -1) continue;
|
|
211
|
+
const openPos = listIdx + 4;
|
|
212
|
+
const closePos = findBalancedEnd(collapsed, openPos);
|
|
213
|
+
const propsContent = collapsed.slice(openPos + 1, closePos);
|
|
214
|
+
const fields = [];
|
|
215
|
+
const propRe = /(\w+)\s*=\s*(?:S7::)?class_(\w+)/g;
|
|
216
|
+
let pm;
|
|
217
|
+
while ((pm = propRe.exec(propsContent)) !== null) {
|
|
218
|
+
fields.push({ name: pm[1], type: pm[2] });
|
|
219
|
+
}
|
|
220
|
+
models.push({ className, fields });
|
|
221
|
+
}
|
|
222
|
+
|
|
204
223
|
return models;
|
|
205
224
|
}
|
|
206
225
|
|