New sessions/session_export tools plus a sessions_block vault map in the system prompt (tai.py, ported to detz); recall/search carry a session footer instead of letting the model grep sealed bytes. write_file refuses new markdown at working/repo roots, and the blanket *.md gitignore is gone so docs stay visible. One LineInput owns fd 0 for every prompt (ask, approvals, shell_ask, secrets), ending phantom-empty answers; Ctrl-C re-prompts (double exits), Ctrl-D exits, Ctrl-Z suspends; mid-turn Ctrl-C announces itself and kills foreground shell children via RunInterrupted. Covered by SessionsTests, repl_keys.py pty checks, agent_diff sessions_block cases.
50 lines
1.9 KiB
C++
50 lines
1.9 KiB
C++
#include "shellio.h"
|
|
#include "ansi.h"
|
|
#include "../repl/line_input.h"
|
|
#include "../util/utf8.h"
|
|
#include <cstdio>
|
|
#include <unistd.h>
|
|
namespace detz::shell {
|
|
namespace {
|
|
// getpass.getpass(prompt): prompt on the tty, no echo, newline after.
|
|
// Both go through the one shared fd-0 reader: a second buffered reader here
|
|
// would steal the REPL's typeahead and poison EOF state.
|
|
std::optional<std::string> GetPass(const std::string& prompt) {
|
|
std::string line;
|
|
auto got = repl::LineInput::shared().read_secret(prompt, line);
|
|
if (got != repl::LineInput::Status::Line) return std::nullopt; // EOFError / KeyboardInterrupt
|
|
return line;
|
|
}
|
|
std::optional<std::string> Input(const std::string& prompt) {
|
|
std::string line;
|
|
auto got = repl::LineInput::shared().read(prompt, line);
|
|
if (got != repl::LineInput::Status::Line) return std::nullopt; // EOFError / KeyboardInterrupt
|
|
return line;
|
|
}
|
|
} // namespace
|
|
|
|
std::unique_ptr<StreamSink> ShellIo::Stream(ShellApp& app, const std::string& command, bool live) {
|
|
if (app.HasStreamFactory()) return app.StreamFactory(app.Redact(command));
|
|
if (live && ::isatty(1)) return std::make_unique<LiveStream>();
|
|
return nullptr;
|
|
}
|
|
|
|
AskFn ShellIo::Ask(ShellApp& app, const std::string& command) {
|
|
if (app.Auto() || !app.Persist() || app.Depth() > 0) return nullptr;
|
|
ShellApp* a = &app;
|
|
auto answered = [a](std::optional<std::string> value) {
|
|
if (value) a->MarkUserSteppedIn();
|
|
return value;
|
|
};
|
|
if (app.HasPrompter()) {
|
|
std::string head = Utf8::Head(command, 60);
|
|
return [a, head, answered](const std::string& text, bool) { return answered(a->Prompt("ask", head + " asks:\n" + text, {})); };
|
|
}
|
|
if (!(::isatty(0) && ::isatty(1))) return nullptr;
|
|
return [answered](const std::string& text, bool secret) -> std::optional<std::string> {
|
|
std::string prompt = Paint("\xc2\xbb " + text + " ", Ansi::kYellow);
|
|
return answered(secret ? GetPass(prompt) : Input(prompt));
|
|
};
|
|
}
|
|
} // namespace detz::shell
|