Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| e61b352576 | |||
| b7882ef60f | |||
| cd5194c365 | |||
| 71cc64b6d9 | |||
| f254a9e078 | |||
| 3d6a33f8d2 | |||
| d37071dae0 |
Binary file not shown.
@@ -1,68 +0,0 @@
|
||||
# language of the project (csharp, python, rust, java, typescript, go, cpp, or ruby)
|
||||
# * For C, use cpp
|
||||
# * For JavaScript, use typescript
|
||||
# Special requirements:
|
||||
# * csharp: Requires the presence of a .sln file in the project folder.
|
||||
language: typescript
|
||||
|
||||
# whether to use the project's gitignore file to ignore files
|
||||
# Added on 2025-04-07
|
||||
ignore_all_files_in_gitignore: true
|
||||
# list of additional paths to ignore
|
||||
# same syntax as gitignore, so you can use * and **
|
||||
# Was previously called `ignored_dirs`, please update your config if you are using that.
|
||||
# Added (renamed) on 2025-04-07
|
||||
ignored_paths: []
|
||||
|
||||
# whether the project is in read-only mode
|
||||
# If set to true, all editing tools will be disabled and attempts to use them will result in an error
|
||||
# Added on 2025-04-18
|
||||
read_only: false
|
||||
|
||||
|
||||
# list of tool names to exclude. We recommend not excluding any tools, see the readme for more details.
|
||||
# Below is the complete list of tools for convenience.
|
||||
# To make sure you have the latest list of tools, and to view their descriptions,
|
||||
# execute `uv run scripts/print_tool_overview.py`.
|
||||
#
|
||||
# * `activate_project`: Activates a project by name.
|
||||
# * `check_onboarding_performed`: Checks whether project onboarding was already performed.
|
||||
# * `create_text_file`: Creates/overwrites a file in the project directory.
|
||||
# * `delete_lines`: Deletes a range of lines within a file.
|
||||
# * `delete_memory`: Deletes a memory from Serena's project-specific memory store.
|
||||
# * `execute_shell_command`: Executes a shell command.
|
||||
# * `find_referencing_code_snippets`: Finds code snippets in which the symbol at the given location is referenced.
|
||||
# * `find_referencing_symbols`: Finds symbols that reference the symbol at the given location (optionally filtered by type).
|
||||
# * `find_symbol`: Performs a global (or local) search for symbols with/containing a given name/substring (optionally filtered by type).
|
||||
# * `get_current_config`: Prints the current configuration of the agent, including the active and available projects, tools, contexts, and modes.
|
||||
# * `get_symbols_overview`: Gets an overview of the top-level symbols defined in a given file.
|
||||
# * `initial_instructions`: Gets the initial instructions for the current project.
|
||||
# Should only be used in settings where the system prompt cannot be set,
|
||||
# e.g. in clients you have no control over, like Claude Desktop.
|
||||
# * `insert_after_symbol`: Inserts content after the end of the definition of a given symbol.
|
||||
# * `insert_at_line`: Inserts content at a given line in a file.
|
||||
# * `insert_before_symbol`: Inserts content before the beginning of the definition of a given symbol.
|
||||
# * `list_dir`: Lists files and directories in the given directory (optionally with recursion).
|
||||
# * `list_memories`: Lists memories in Serena's project-specific memory store.
|
||||
# * `onboarding`: Performs onboarding (identifying the project structure and essential tasks, e.g. for testing or building).
|
||||
# * `prepare_for_new_conversation`: Provides instructions for preparing for a new conversation (in order to continue with the necessary context).
|
||||
# * `read_file`: Reads a file within the project directory.
|
||||
# * `read_memory`: Reads the memory with the given name from Serena's project-specific memory store.
|
||||
# * `remove_project`: Removes a project from the Serena configuration.
|
||||
# * `replace_lines`: Replaces a range of lines within a file with new content.
|
||||
# * `replace_symbol_body`: Replaces the full definition of a symbol.
|
||||
# * `restart_language_server`: Restarts the language server, may be necessary when edits not through Serena happen.
|
||||
# * `search_for_pattern`: Performs a search for a pattern in the project.
|
||||
# * `summarize_changes`: Provides instructions for summarizing the changes made to the codebase.
|
||||
# * `switch_modes`: Activates modes by providing a list of their names
|
||||
# * `think_about_collected_information`: Thinking tool for pondering the completeness of collected information.
|
||||
# * `think_about_task_adherence`: Thinking tool for determining whether the agent is still on track with the current task.
|
||||
# * `think_about_whether_you_are_done`: Thinking tool for determining whether the task is truly completed.
|
||||
# * `write_memory`: Writes a named memory (for future reference) to Serena's project-specific memory store.
|
||||
excluded_tools: []
|
||||
|
||||
# initial prompt for the project. It will always be given to the LLM upon activating the project
|
||||
# (contrary to the memories, which are loaded on demand).
|
||||
initial_prompt: ""
|
||||
|
||||
project_name: "smartshell"
|
||||
21
changelog.md
21
changelog.md
@@ -1,5 +1,26 @@
|
||||
# Changelog
|
||||
|
||||
## 2026-03-15 - 3.3.8 - fix(repo)
|
||||
remove obsolete Serena project configuration files
|
||||
|
||||
- Deletes the .serena project configuration from the repository
|
||||
- Cleans up editor or tooling metadata that is not part of the package runtime or source code
|
||||
|
||||
## 2026-03-05 - 3.3.7 - fix(smartshell)
|
||||
avoid triple shell nesting, improve WSL path filtering, and use chunked log buffer to reduce memory usage
|
||||
|
||||
- Spawn uses the executor shell binary directly (e.g. /bin/bash) and removes extra bash -c wrapping to avoid triple shell nesting
|
||||
- Only filter out /mnt/c/ and other WSL-specific PATH entries when running under WSL (adds _isWSL detection)
|
||||
- Replace single concatenated Buffer with chunked buffering in ShellLog (lazy concatenation, logLength property) and update checks to use logLength
|
||||
- Remove unused dependency "tree-kill" from package.json
|
||||
|
||||
## 2026-03-04 - 3.3.6 - fix(smartshell)
|
||||
use 'close' event on child processes to ensure exit handling and update dependency versions
|
||||
|
||||
- Replace child_process 'exit' listeners with 'close' in ts/classes.smartshell.ts (two occurrences) to ensure handlers run after stdio streams are closed.
|
||||
- Bump devDependencies: @git.zone/tsbuild ^2.7.3 -> ^4.1.2, @git.zone/tsrun ^1.6.2 -> ^2.0.1, @git.zone/tstest ^2.8.3 -> ^3.2.0, @types/node ^22.19.13 -> ^25.3.3.
|
||||
- Bump dependencies: @push.rocks/smartexit ^2.0.1 -> ^2.0.3, which ^5.0.0 -> ^6.0.1.
|
||||
|
||||
## 2026-03-03 - 3.3.2 - fix(release)
|
||||
add @git.zone/cli release configuration with registries and public access
|
||||
|
||||
|
||||
15
package.json
15
package.json
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@push.rocks/smartshell",
|
||||
"private": false,
|
||||
"version": "3.3.4",
|
||||
"version": "3.3.8",
|
||||
"description": "A library for executing shell commands using promises.",
|
||||
"main": "dist_ts/index.js",
|
||||
"typings": "dist_ts/index.d.ts",
|
||||
@@ -33,18 +33,17 @@
|
||||
},
|
||||
"homepage": "https://code.foss.global/push.rocks/smartshell",
|
||||
"devDependencies": {
|
||||
"@git.zone/tsbuild": "^2.7.3",
|
||||
"@git.zone/tsrun": "^1.6.2",
|
||||
"@git.zone/tstest": "^2.8.3",
|
||||
"@types/node": "^22.19.13"
|
||||
"@git.zone/tsbuild": "^4.1.2",
|
||||
"@git.zone/tsrun": "^2.0.1",
|
||||
"@git.zone/tstest": "^3.2.0",
|
||||
"@types/node": "^25.3.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"@push.rocks/smartdelay": "^3.0.5",
|
||||
"@push.rocks/smartexit": "^2.0.1",
|
||||
"@push.rocks/smartexit": "^2.0.3",
|
||||
"@push.rocks/smartpromise": "^4.2.3",
|
||||
"@types/which": "^3.0.4",
|
||||
"tree-kill": "^1.2.2",
|
||||
"which": "^5.0.0"
|
||||
"which": "^6.0.1"
|
||||
},
|
||||
"files": [
|
||||
"ts/**/*",
|
||||
|
||||
2058
pnpm-lock.yaml
generated
2058
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -3,6 +3,6 @@
|
||||
*/
|
||||
export const commitinfo = {
|
||||
name: '@push.rocks/smartshell',
|
||||
version: '3.3.2',
|
||||
version: '3.3.8',
|
||||
description: 'A library for executing shell commands using promises.'
|
||||
}
|
||||
|
||||
@@ -28,6 +28,17 @@ export class ShellEnv {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Detect if running under Windows Subsystem for Linux
|
||||
*/
|
||||
private static _isWSL(): boolean {
|
||||
return !!(
|
||||
process.env.WSL_DISTRO_NAME ||
|
||||
process.env.WSLENV ||
|
||||
(process.platform === 'linux' && process.env.PATH?.includes('/mnt/c/'))
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* imports path into the shell from env if available and returns it with
|
||||
*/
|
||||
@@ -39,18 +50,16 @@ export class ShellEnv {
|
||||
commandPaths = commandPaths.concat(process.env.SMARTSHELL_PATH.split(':'));
|
||||
}
|
||||
|
||||
// lets filter for unwanted paths
|
||||
// Windows WSL
|
||||
commandPaths = commandPaths.filter((commandPathArg) => {
|
||||
const filterResult =
|
||||
!commandPathArg.startsWith('/mnt/c/') &&
|
||||
!commandPathArg.startsWith('Files/1E') &&
|
||||
!commandPathArg.includes(' ');
|
||||
if (!filterResult) {
|
||||
// console.log(`${commandPathArg} will be filtered!`);
|
||||
}
|
||||
return filterResult;
|
||||
});
|
||||
// Only filter out WSL-specific paths when actually running under WSL
|
||||
if (ShellEnv._isWSL()) {
|
||||
commandPaths = commandPaths.filter((commandPathArg) => {
|
||||
return (
|
||||
!commandPathArg.startsWith('/mnt/c/') &&
|
||||
!commandPathArg.startsWith('Files/1E') &&
|
||||
!commandPathArg.includes(' ')
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
commandResult = `PATH=${commandPaths.join(':')} && ${commandStringArg}`;
|
||||
return commandResult;
|
||||
@@ -89,14 +98,12 @@ export class ShellEnv {
|
||||
}
|
||||
pathString += ` && `;
|
||||
|
||||
switch (this.executor) {
|
||||
case 'bash':
|
||||
commandResult = `bash -c '${pathString}${sourceString}${commandArg}'`;
|
||||
break;
|
||||
case 'sh':
|
||||
commandResult = `${pathString}${sourceString}${commandArg}`;
|
||||
break;
|
||||
}
|
||||
// For both bash and sh executors, build the command string directly.
|
||||
// The shell nesting is handled by spawn() using the appropriate shell binary.
|
||||
// Previously bash executor wrapped in `bash -c '...'` which caused triple
|
||||
// shell nesting (Node spawn sh -> bash -c -> command). Now spawn() uses
|
||||
// shell: '/bin/bash' directly, so we don't need the extra wrapping.
|
||||
commandResult = `${pathString}${sourceString}${commandArg}`;
|
||||
commandResult = this._setPath(commandResult);
|
||||
return commandResult;
|
||||
}
|
||||
|
||||
@@ -5,7 +5,41 @@ import * as plugins from './plugins.js';
|
||||
* making sure the process doesn't run out of memory
|
||||
*/
|
||||
export class ShellLog {
|
||||
public logStore = Buffer.from('');
|
||||
private chunks: Buffer[] = [];
|
||||
private totalLength = 0;
|
||||
|
||||
/**
|
||||
* Get the accumulated log as a single Buffer.
|
||||
* Concatenation happens lazily only when accessed.
|
||||
*/
|
||||
public get logStore(): Buffer {
|
||||
if (this.chunks.length === 0) {
|
||||
return Buffer.alloc(0);
|
||||
}
|
||||
if (this.chunks.length === 1) {
|
||||
return this.chunks[0];
|
||||
}
|
||||
// Flatten chunks into a single buffer
|
||||
const combined = Buffer.concat(this.chunks, this.totalLength);
|
||||
// Replace chunks array with the single combined buffer for future access
|
||||
this.chunks = [combined];
|
||||
return combined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the log store directly (used for truncation).
|
||||
*/
|
||||
public set logStore(value: Buffer) {
|
||||
this.chunks = [value];
|
||||
this.totalLength = value.length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current total length of buffered data without concatenating.
|
||||
*/
|
||||
public get logLength(): number {
|
||||
return this.totalLength;
|
||||
}
|
||||
|
||||
/**
|
||||
* log data to console
|
||||
@@ -22,13 +56,9 @@ export class ShellLog {
|
||||
*/
|
||||
public addToBuffer(dataArg: string | Buffer): void {
|
||||
// make sure we have the data as Buffer
|
||||
const dataBuffer: Buffer = (() => {
|
||||
if (!Buffer.isBuffer(dataArg)) {
|
||||
return Buffer.from(dataArg);
|
||||
}
|
||||
return dataArg;
|
||||
})();
|
||||
this.logStore = Buffer.concat([this.logStore, dataBuffer]);
|
||||
const dataBuffer: Buffer = Buffer.isBuffer(dataArg) ? dataArg : Buffer.from(dataArg);
|
||||
this.chunks.push(dataBuffer);
|
||||
this.totalLength += dataBuffer.length;
|
||||
}
|
||||
|
||||
public logAndAdd(dataArg: string | Buffer): void {
|
||||
|
||||
@@ -119,7 +119,7 @@ export class Smartshell {
|
||||
shell: false, // SECURITY: Never use shell with untrusted input
|
||||
cwd: process.cwd(),
|
||||
env: options.env || process.env,
|
||||
detached: false,
|
||||
detached: true, // Own process group — immune to terminal SIGINT, managed by smartexit
|
||||
signal: options.signal,
|
||||
});
|
||||
|
||||
@@ -182,7 +182,7 @@ export class Smartshell {
|
||||
|
||||
if (!bufferExceeded) {
|
||||
shellLogInstance.addToBuffer(data);
|
||||
if (shellLogInstance.logStore.length > maxBuffer) {
|
||||
if (shellLogInstance.logLength > maxBuffer) {
|
||||
bufferExceeded = true;
|
||||
shellLogInstance.logStore = Buffer.from('[Output truncated - exceeded maxBuffer]');
|
||||
}
|
||||
@@ -203,7 +203,7 @@ export class Smartshell {
|
||||
|
||||
if (!bufferExceeded) {
|
||||
shellLogInstance.addToBuffer(data);
|
||||
if (shellLogInstance.logStore.length > maxBuffer) {
|
||||
if (shellLogInstance.logLength > maxBuffer) {
|
||||
bufferExceeded = true;
|
||||
shellLogInstance.logStore = Buffer.from('[Output truncated - exceeded maxBuffer]');
|
||||
}
|
||||
@@ -249,7 +249,7 @@ export class Smartshell {
|
||||
}
|
||||
};
|
||||
|
||||
execChildProcess.once('exit', handleExit);
|
||||
execChildProcess.once('close', handleExit);
|
||||
execChildProcess.once('error', (error) => {
|
||||
if (timeoutHandle) {
|
||||
clearTimeout(timeoutHandle);
|
||||
@@ -338,11 +338,15 @@ export class Smartshell {
|
||||
return await this._execCommandPty(options, commandToExecute, shellLogInstance);
|
||||
}
|
||||
|
||||
// Use the executor's shell binary directly to avoid triple shell nesting.
|
||||
// Previously: Node spawn(shell:true) → /bin/sh → bash -c → command (3 layers)
|
||||
// Now: Node spawn(shell:bash) → command (1 layer)
|
||||
const shellBinary = this.shellEnv.executor === 'bash' ? '/bin/bash' : true;
|
||||
const execChildProcess = cp.spawn(commandToExecute, [], {
|
||||
shell: true,
|
||||
shell: shellBinary,
|
||||
cwd: process.cwd(),
|
||||
env: options.env || process.env,
|
||||
detached: false,
|
||||
detached: true, // Own process group — immune to terminal SIGINT, managed by smartexit
|
||||
signal: options.signal,
|
||||
});
|
||||
|
||||
@@ -405,7 +409,7 @@ export class Smartshell {
|
||||
|
||||
if (!bufferExceeded) {
|
||||
shellLogInstance.addToBuffer(data);
|
||||
if (shellLogInstance.logStore.length > maxBuffer) {
|
||||
if (shellLogInstance.logLength > maxBuffer) {
|
||||
bufferExceeded = true;
|
||||
shellLogInstance.logStore = Buffer.from('[Output truncated - exceeded maxBuffer]');
|
||||
}
|
||||
@@ -426,7 +430,7 @@ export class Smartshell {
|
||||
|
||||
if (!bufferExceeded) {
|
||||
shellLogInstance.addToBuffer(data);
|
||||
if (shellLogInstance.logStore.length > maxBuffer) {
|
||||
if (shellLogInstance.logLength > maxBuffer) {
|
||||
bufferExceeded = true;
|
||||
shellLogInstance.logStore = Buffer.from('[Output truncated - exceeded maxBuffer]');
|
||||
}
|
||||
@@ -472,7 +476,7 @@ export class Smartshell {
|
||||
}
|
||||
};
|
||||
|
||||
execChildProcess.once('exit', handleExit);
|
||||
execChildProcess.once('close', handleExit);
|
||||
execChildProcess.once('error', (error) => {
|
||||
if (timeoutHandle) {
|
||||
clearTimeout(timeoutHandle);
|
||||
|
||||
Reference in New Issue
Block a user