@hasna/terminal 1.6.0 → 1.6.2

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.
Files changed (3) hide show
  1. package/dist/ai.js +17 -2
  2. package/package.json +1 -1
  3. package/src/ai.ts +22 -3
package/dist/ai.js CHANGED
@@ -50,6 +50,10 @@ const IRREVERSIBLE_PATTERNS = [
50
50
  const SAFE_OVERRIDES = [
51
51
  /^\s*git\s+(log|show|diff|branch|status|blame|tag|remote|stash\s+list)\b/,
52
52
  /^\s*git\s+log\b/,
53
+ // find -exec with read-only tools is safe
54
+ /\bfind\b.*-exec\s+(wc|cat|head|tail|grep|stat|file|du|ls)\b/,
55
+ // find without -exec is always safe
56
+ /^\s*find\b(?!.*-exec\s+(rm|mv|chmod|chown|sed))/,
53
57
  ];
54
58
  export function isIrreversible(command) {
55
59
  // Safe overrides take priority
@@ -119,10 +123,19 @@ function detectProjectContext() {
119
123
  // Top-level dirs
120
124
  const topLevel = execSync("ls -1", { cwd, encoding: "utf8", timeout: 2000 }).trim();
121
125
  parts.push(`Top-level: ${topLevel.split("\n").join(", ")}`);
126
+ // Detect monorepo (packages/ or workspaces in package.json)
127
+ const isMonorepo = existsSync(join(cwd, "packages")) || existsSync(join(cwd, "apps"));
128
+ if (isMonorepo) {
129
+ const pkgDirs = execSync(`ls -d packages/*/src 2>/dev/null || ls -d apps/*/src 2>/dev/null || echo ""`, { cwd, encoding: "utf8", timeout: 2000 }).trim();
130
+ if (pkgDirs) {
131
+ parts.push(`MONOREPO: Source is in packages/*/src/, NOT src/. Search packages/ not src/.`);
132
+ parts.push(`Package sources:\n${pkgDirs}`);
133
+ }
134
+ }
122
135
  // src/ structure — include FILES so AI knows exact filenames + extensions
123
- for (const srcDir of ["src", "lib", "app"]) {
136
+ for (const srcDir of isMonorepo ? ["packages"] : ["src", "lib", "app"]) {
124
137
  if (existsSync(join(cwd, srcDir))) {
125
- const tree = execSync(`find ${srcDir} -maxdepth 3 -not -path '*/node_modules/*' -not -path '*/dist/*' -not -name '*.test.*' -not -name '*.spec.*' 2>/dev/null | sort | head -60`, { cwd, encoding: "utf8", timeout: 2000 }).trim();
138
+ const tree = execSync(`find ${srcDir} -maxdepth ${isMonorepo ? 4 : 3} -not -path '*/node_modules/*' -not -path '*/dist/*' -not -name '*.test.*' -not -name '*.spec.*' 2>/dev/null | sort | head -80`, { cwd, encoding: "utf8", timeout: 3000 }).trim();
126
139
  if (tree)
127
140
  parts.push(`Files in ${srcDir}/:\n${tree}`);
128
141
  break;
@@ -203,6 +216,8 @@ SEMANTIC MAPPING: When the user references a concept, search the file tree for R
203
216
  - When uncertain: grep -rn "keyword" src/ --include="*.ts" -l (list matching files)
204
217
 
205
218
  ACTION vs CONCEPTUAL: If the prompt starts with "run", "execute", "check", "test", "build", "show output of" — ALWAYS generate an executable command. NEVER read README for action requests. Only read docs for "explain why", "what does X mean", "how was X designed".
219
+
220
+ MONOREPO: If the project context says "MONOREPO", search packages/ or apps/ NOT src/. Use: grep -rn "pattern" packages/ --include="*.ts". For specific packages, use packages/PKGNAME/src/.
206
221
  cwd: ${process.cwd()}
207
222
  shell: zsh / macOS${projectContext}${restrictionBlock}${contextBlock}`;
208
223
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hasna/terminal",
3
- "version": "1.6.0",
3
+ "version": "1.6.2",
4
4
  "description": "Smart terminal wrapper for AI agents and humans — structured output, token compression, MCP server, natural language",
5
5
  "type": "module",
6
6
  "bin": {
package/src/ai.ts CHANGED
@@ -59,6 +59,10 @@ const IRREVERSIBLE_PATTERNS = [
59
59
  const SAFE_OVERRIDES = [
60
60
  /^\s*git\s+(log|show|diff|branch|status|blame|tag|remote|stash\s+list)\b/,
61
61
  /^\s*git\s+log\b/,
62
+ // find -exec with read-only tools is safe
63
+ /\bfind\b.*-exec\s+(wc|cat|head|tail|grep|stat|file|du|ls)\b/,
64
+ // find without -exec is always safe
65
+ /^\s*find\b(?!.*-exec\s+(rm|mv|chmod|chown|sed))/,
62
66
  ];
63
67
 
64
68
  export function isIrreversible(command: string): boolean {
@@ -148,12 +152,25 @@ function detectProjectContext(): string {
148
152
  const topLevel = execSync("ls -1", { cwd, encoding: "utf8", timeout: 2000 }).trim();
149
153
  parts.push(`Top-level: ${topLevel.split("\n").join(", ")}`);
150
154
 
155
+ // Detect monorepo (packages/ or workspaces in package.json)
156
+ const isMonorepo = existsSync(join(cwd, "packages")) || existsSync(join(cwd, "apps"));
157
+ if (isMonorepo) {
158
+ const pkgDirs = execSync(
159
+ `ls -d packages/*/src 2>/dev/null || ls -d apps/*/src 2>/dev/null || echo ""`,
160
+ { cwd, encoding: "utf8", timeout: 2000 }
161
+ ).trim();
162
+ if (pkgDirs) {
163
+ parts.push(`MONOREPO: Source is in packages/*/src/, NOT src/. Search packages/ not src/.`);
164
+ parts.push(`Package sources:\n${pkgDirs}`);
165
+ }
166
+ }
167
+
151
168
  // src/ structure — include FILES so AI knows exact filenames + extensions
152
- for (const srcDir of ["src", "lib", "app"]) {
169
+ for (const srcDir of isMonorepo ? ["packages"] : ["src", "lib", "app"]) {
153
170
  if (existsSync(join(cwd, srcDir))) {
154
171
  const tree = execSync(
155
- `find ${srcDir} -maxdepth 3 -not -path '*/node_modules/*' -not -path '*/dist/*' -not -name '*.test.*' -not -name '*.spec.*' 2>/dev/null | sort | head -60`,
156
- { cwd, encoding: "utf8", timeout: 2000 }
172
+ `find ${srcDir} -maxdepth ${isMonorepo ? 4 : 3} -not -path '*/node_modules/*' -not -path '*/dist/*' -not -name '*.test.*' -not -name '*.spec.*' 2>/dev/null | sort | head -80`,
173
+ { cwd, encoding: "utf8", timeout: 3000 }
157
174
  ).trim();
158
175
  if (tree) parts.push(`Files in ${srcDir}/:\n${tree}`);
159
176
  break;
@@ -238,6 +255,8 @@ SEMANTIC MAPPING: When the user references a concept, search the file tree for R
238
255
  - When uncertain: grep -rn "keyword" src/ --include="*.ts" -l (list matching files)
239
256
 
240
257
  ACTION vs CONCEPTUAL: If the prompt starts with "run", "execute", "check", "test", "build", "show output of" — ALWAYS generate an executable command. NEVER read README for action requests. Only read docs for "explain why", "what does X mean", "how was X designed".
258
+
259
+ MONOREPO: If the project context says "MONOREPO", search packages/ or apps/ NOT src/. Use: grep -rn "pattern" packages/ --include="*.ts". For specific packages, use packages/PKGNAME/src/.
241
260
  cwd: ${process.cwd()}
242
261
  shell: zsh / macOS${projectContext}${restrictionBlock}${contextBlock}`;
243
262
  }