ralph-cli-sandboxed 0.6.6 → 0.7.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.
- package/LICENSE +191 -0
- package/README.md +1 -1
- package/dist/commands/ask.d.ts +6 -0
- package/dist/commands/ask.js +140 -0
- package/dist/commands/branch.js +8 -4
- package/dist/commands/chat.js +11 -8
- package/dist/commands/docker.js +19 -4
- package/dist/commands/fix-config.js +0 -41
- package/dist/commands/help.js +10 -0
- package/dist/commands/run.js +9 -9
- package/dist/index.js +2 -0
- package/dist/providers/telegram.js +1 -1
- package/dist/responders/claude-code-responder.js +1 -0
- package/dist/responders/cli-responder.js +1 -0
- package/dist/responders/llm-responder.js +1 -1
- package/dist/templates/macos-scripts.js +18 -18
- package/dist/tui/components/JsonSnippetEditor.js +7 -7
- package/dist/tui/components/KeyValueEditor.js +5 -1
- package/dist/tui/components/LLMProvidersEditor.js +7 -9
- package/dist/tui/components/Preview.js +1 -1
- package/dist/tui/components/SectionNav.js +18 -2
- package/dist/utils/chat-client.js +1 -0
- package/dist/utils/config.d.ts +1 -0
- package/dist/utils/config.js +3 -1
- package/dist/utils/config.test.d.ts +1 -0
- package/dist/utils/config.test.js +424 -0
- package/dist/utils/notification.js +1 -1
- package/dist/utils/prd-validator.js +16 -4
- package/dist/utils/prd-validator.test.d.ts +1 -0
- package/dist/utils/prd-validator.test.js +1095 -0
- package/dist/utils/responder.js +4 -1
- package/dist/utils/stream-json.test.d.ts +1 -0
- package/dist/utils/stream-json.test.js +1007 -0
- package/docs/DOCKER.md +14 -0
- package/docs/PRD-GENERATOR.md +15 -0
- package/package.json +16 -13
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* LLM Responder - Sends messages to LLM providers and returns responses.
|
|
3
3
|
* Used by chat clients to respond to messages matched by the responder matcher.
|
|
4
4
|
*/
|
|
5
|
-
import { getLLMProviders, loadConfig
|
|
5
|
+
import { getLLMProviders, loadConfig } from "../utils/config.js";
|
|
6
6
|
import { createLLMClient } from "../utils/llm-client.js";
|
|
7
7
|
import { createResponderLog } from "../utils/responder-logger.js";
|
|
8
8
|
import { basename, resolve } from "path";
|
|
@@ -48,8 +48,8 @@ PROJECT_NAME="\${1:-${projectName}}"
|
|
|
48
48
|
SCRIPT_DIR="$(cd "$(dirname "\${BASH_SOURCE[0]}")" && pwd)"
|
|
49
49
|
PROJECT_ROOT="$(dirname "$SCRIPT_DIR")"
|
|
50
50
|
|
|
51
|
-
echo "Generating Xcode project:
|
|
52
|
-
echo "Project root:
|
|
51
|
+
echo "Generating Xcode project: $PROJECT_NAME"
|
|
52
|
+
echo "Project root: $PROJECT_ROOT"
|
|
53
53
|
echo ""
|
|
54
54
|
|
|
55
55
|
cd "$PROJECT_ROOT"
|
|
@@ -62,7 +62,7 @@ if [ ! -f "Package.swift" ]; then
|
|
|
62
62
|
fi
|
|
63
63
|
|
|
64
64
|
# Create Supporting Files directory for Info.plist and entitlements
|
|
65
|
-
SUPPORT_DIR="$PROJECT_ROOT/Sources
|
|
65
|
+
SUPPORT_DIR="$PROJECT_ROOT/Sources/$PROJECT_NAME/Supporting Files"
|
|
66
66
|
mkdir -p "$SUPPORT_DIR"
|
|
67
67
|
|
|
68
68
|
# Generate Info.plist if it doesn't exist
|
|
@@ -105,7 +105,7 @@ PLIST_EOF
|
|
|
105
105
|
fi
|
|
106
106
|
|
|
107
107
|
# Generate entitlements file if it doesn't exist
|
|
108
|
-
ENTITLEMENTS="$SUPPORT_DIR
|
|
108
|
+
ENTITLEMENTS="$SUPPORT_DIR/$PROJECT_NAME.entitlements"
|
|
109
109
|
if [ ! -f "$ENTITLEMENTS" ]; then
|
|
110
110
|
echo "Creating entitlements file..."
|
|
111
111
|
cat > "$ENTITLEMENTS" << 'ENTITLEMENTS_EOF'
|
|
@@ -135,7 +135,7 @@ if command -v xcodegen &> /dev/null; then
|
|
|
135
135
|
if [ ! -f "$PROJECT_YML" ]; then
|
|
136
136
|
echo "Creating project.yml for xcodegen..."
|
|
137
137
|
cat > "$PROJECT_YML" << YAML_EOF
|
|
138
|
-
name:
|
|
138
|
+
name: $PROJECT_NAME
|
|
139
139
|
options:
|
|
140
140
|
bundleIdPrefix: com.example
|
|
141
141
|
deploymentTarget:
|
|
@@ -144,18 +144,18 @@ options:
|
|
|
144
144
|
generateEmptyDirectories: true
|
|
145
145
|
|
|
146
146
|
targets:
|
|
147
|
-
|
|
147
|
+
$PROJECT_NAME:
|
|
148
148
|
type: application
|
|
149
149
|
platform: macOS
|
|
150
150
|
sources:
|
|
151
|
-
- path: Sources
|
|
151
|
+
- path: Sources/$PROJECT_NAME
|
|
152
152
|
excludes:
|
|
153
153
|
- "**/*.entitlements"
|
|
154
154
|
settings:
|
|
155
155
|
base:
|
|
156
|
-
PRODUCT_BUNDLE_IDENTIFIER: com.example
|
|
157
|
-
INFOPLIST_FILE: Sources
|
|
158
|
-
CODE_SIGN_ENTITLEMENTS: Sources
|
|
156
|
+
PRODUCT_BUNDLE_IDENTIFIER: com.example.$PROJECT_NAME
|
|
157
|
+
INFOPLIST_FILE: Sources/$PROJECT_NAME/Supporting Files/Info.plist
|
|
158
|
+
CODE_SIGN_ENTITLEMENTS: Sources/$PROJECT_NAME/Supporting Files/$PROJECT_NAME.entitlements
|
|
159
159
|
MACOSX_DEPLOYMENT_TARGET: "13.0"
|
|
160
160
|
SWIFT_VERSION: "5.9"
|
|
161
161
|
DEVELOPMENT_TEAM: ""
|
|
@@ -191,7 +191,7 @@ fi
|
|
|
191
191
|
|
|
192
192
|
echo ""
|
|
193
193
|
echo "Next steps:"
|
|
194
|
-
echo " 1. Open
|
|
194
|
+
echo " 1. Open $PROJECT_NAME.xcodeproj in Xcode"
|
|
195
195
|
echo " 2. Configure your Team ID for code signing"
|
|
196
196
|
echo " 3. Build and run (Cmd+R)"
|
|
197
197
|
echo ""
|
|
@@ -389,9 +389,9 @@ app_identifier "${bundleId}"
|
|
|
389
389
|
* @param projectName - Name of the Xcode project/app
|
|
390
390
|
*/
|
|
391
391
|
export function generateFastlaneReadmeSection(projectName = "App") {
|
|
392
|
-
return `## Fastlane Deployment
|
|
392
|
+
return `## Fastlane Deployment for ${projectName}
|
|
393
393
|
|
|
394
|
-
This project includes [Fastlane](https://fastlane.tools/) configuration for automated builds and deployments.
|
|
394
|
+
This project includes [Fastlane](https://fastlane.tools/) configuration for automated builds and deployments of ${projectName}.
|
|
395
395
|
|
|
396
396
|
### Setup
|
|
397
397
|
|
|
@@ -418,9 +418,9 @@ Run Fastlane commands from the \`scripts/\` directory:
|
|
|
418
418
|
|
|
419
419
|
| Lane | Description |
|
|
420
420
|
|------|-------------|
|
|
421
|
-
| \`fastlane tests\` | Run all unit and UI tests |
|
|
422
|
-
| \`fastlane beta\` | Build and upload to TestFlight |
|
|
423
|
-
| \`fastlane release\` | Build and submit to App Store |
|
|
421
|
+
| \`fastlane tests\` | Run all ${projectName} unit and UI tests |
|
|
422
|
+
| \`fastlane beta\` | Build ${projectName} and upload to TestFlight |
|
|
423
|
+
| \`fastlane release\` | Build ${projectName} and submit to App Store |
|
|
424
424
|
|
|
425
425
|
### Using with Ralph
|
|
426
426
|
|
|
@@ -438,8 +438,8 @@ ralph action fastlane_release
|
|
|
438
438
|
|
|
439
439
|
### Customization
|
|
440
440
|
|
|
441
|
-
- **Fastfile** (\`scripts/fastlane/Fastfile\`): Add custom lanes for your workflow
|
|
442
|
-
- **Appfile** (\`scripts/fastlane/Appfile\`): Configure
|
|
441
|
+
- **Fastfile** (\`scripts/fastlane/Fastfile\`): Add custom lanes for your ${projectName} workflow
|
|
442
|
+
- **Appfile** (\`scripts/fastlane/Appfile\`): Configure ${projectName} bundle ID and team settings
|
|
443
443
|
|
|
444
444
|
For more information, see the [Fastlane documentation](https://docs.fastlane.tools/).
|
|
445
445
|
`;
|
|
@@ -161,16 +161,16 @@ function truncateJsonString(str, maxLen) {
|
|
|
161
161
|
/**
|
|
162
162
|
* Simple syntax highlighting for JSON preview.
|
|
163
163
|
*/
|
|
164
|
-
function highlightJson(json, maxLines, maxLineWidth = 60) {
|
|
164
|
+
function highlightJson(json, maxLines, maxLineWidth = 60, scrollOffset = 0) {
|
|
165
165
|
const lines = json.split("\n");
|
|
166
|
-
const displayLines = lines.slice(
|
|
167
|
-
const hasMore = lines.length > maxLines;
|
|
166
|
+
const displayLines = lines.slice(scrollOffset, scrollOffset + maxLines);
|
|
167
|
+
const hasMore = lines.length > scrollOffset + maxLines;
|
|
168
168
|
const elements = [];
|
|
169
169
|
// Calculate max string length based on available width (account for line number, indentation)
|
|
170
170
|
const maxStringLen = Math.max(20, Math.min(50, maxLineWidth - 15));
|
|
171
171
|
for (let i = 0; i < displayLines.length; i++) {
|
|
172
172
|
const line = displayLines[i];
|
|
173
|
-
const lineNum = String(i + 1).padStart(3, " ");
|
|
173
|
+
const lineNum = String(scrollOffset + i + 1).padStart(3, " ");
|
|
174
174
|
// Simple tokenization for highlighting
|
|
175
175
|
const tokens = [];
|
|
176
176
|
let remaining = line;
|
|
@@ -230,7 +230,7 @@ function highlightJson(json, maxLines, maxLineWidth = 60) {
|
|
|
230
230
|
elements.push(_jsxs(Box, { children: [_jsxs(Text, { dimColor: true, children: [lineNum, " "] }), tokens] }, i));
|
|
231
231
|
}
|
|
232
232
|
if (hasMore) {
|
|
233
|
-
elements.push(_jsx(Box, { children: _jsxs(Text, { dimColor: true, children: [" ... (", lines.length - maxLines, " more lines)"] }) }, "more"));
|
|
233
|
+
elements.push(_jsx(Box, { children: _jsxs(Text, { dimColor: true, children: [" ... (", lines.length - scrollOffset - maxLines, " more lines)"] }) }, "more"));
|
|
234
234
|
}
|
|
235
235
|
return elements;
|
|
236
236
|
}
|
|
@@ -246,8 +246,8 @@ export function JsonSnippetEditor({ label, value, onConfirm, onCancel, isFocused
|
|
|
246
246
|
const [editText, setEditText] = useState(initialJson);
|
|
247
247
|
const [parseError, setParseError] = useState(null);
|
|
248
248
|
const [warnings, setWarnings] = useState([]);
|
|
249
|
-
const [scrollOffset, setScrollOffset] = useState(0);
|
|
250
249
|
const [copied, setCopied] = useState(false);
|
|
250
|
+
const [scrollOffset, setScrollOffset] = useState(0);
|
|
251
251
|
// Validate JSON as user types
|
|
252
252
|
useEffect(() => {
|
|
253
253
|
const result = parseJsonWithLineInfo(editText);
|
|
@@ -375,7 +375,7 @@ export function JsonSnippetEditor({ label, value, onConfirm, onCancel, isFocused
|
|
|
375
375
|
// Render view mode with syntax-highlighted preview
|
|
376
376
|
// Account for border (2) and padding (2) when calculating preview width
|
|
377
377
|
const previewWidth = Math.max(40, maxWidth - 4);
|
|
378
|
-
const previewLines = highlightJson(editText, previewMaxLines, previewWidth);
|
|
378
|
+
const previewLines = highlightJson(editText, previewMaxLines, previewWidth, scrollOffset);
|
|
379
379
|
return (_jsxs(Box, { flexDirection: "column", borderStyle: "single", borderColor: "cyan", paddingX: 1, children: [_jsxs(Box, { marginBottom: 1, justifyContent: "space-between", children: [_jsxs(Text, { bold: true, color: "cyan", children: ["Edit as JSON: ", label] }), copied && _jsx(Text, { color: "green", children: " Copied!" })] }), _jsx(Box, { flexDirection: "column", marginBottom: 1, children: previewLines }), _jsx(Box, { marginBottom: 1, flexDirection: "column", children: parseError ? (_jsx(Box, { children: _jsxs(Text, { color: "red", bold: true, children: ["Error:", " ", parseError.line && parseError.column
|
|
380
380
|
? `Line ${parseError.line}:${parseError.column} - `
|
|
381
381
|
: "", parseError.message] }) })) : warningCount > 0 ? (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Text, { color: "yellow", bold: true, children: [warningCount, " warning", warningCount > 1 ? "s" : "", ":"] }), warnings.slice(0, 3).map((w, i) => (_jsxs(Text, { color: "yellow", dimColor: true, children: [" ", "- ", w] }, i))), warningCount > 3 && _jsxs(Text, { dimColor: true, children: [" ... and ", warningCount - 3, " more"] })] })) : (_jsx(Text, { color: "green", children: "Valid JSON" })) }), _jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsx(Text, { dimColor: true, children: "e/Enter: edit | s: save | c: copy | f: format" }), _jsx(Text, { dimColor: true, children: "j/k: scroll | PgUp/Dn: page | Esc: cancel" })] })] }));
|
|
@@ -72,7 +72,11 @@ export const PROVIDER_HINTS = {
|
|
|
72
72
|
],
|
|
73
73
|
openai: [
|
|
74
74
|
{ key: "type", description: "Provider type (openai)", required: true },
|
|
75
|
-
{
|
|
75
|
+
{
|
|
76
|
+
key: "model",
|
|
77
|
+
description: "Model name (e.g., gpt-4o, gpt-4-turbo, gpt-3.5-turbo)",
|
|
78
|
+
required: true,
|
|
79
|
+
},
|
|
76
80
|
{ key: "apiKey", description: "API key (defaults to OPENAI_API_KEY env var)" },
|
|
77
81
|
{ key: "baseUrl", description: "Custom API base URL (for OpenAI-compatible services)" },
|
|
78
82
|
],
|
|
@@ -252,19 +252,13 @@ export function LLMProvidersEditor({ label, providers, onConfirm, onCancel, isFo
|
|
|
252
252
|
}, { isActive: isFocused && mode === "select-type" });
|
|
253
253
|
// Handle keyboard input for text editing modes
|
|
254
254
|
useInput((_input, key) => {
|
|
255
|
-
if (!isFocused ||
|
|
256
|
-
mode === "list" ||
|
|
257
|
-
mode === "select-type" ||
|
|
258
|
-
mode === "edit-provider")
|
|
255
|
+
if (!isFocused || mode === "list" || mode === "select-type" || mode === "edit-provider")
|
|
259
256
|
return;
|
|
260
257
|
if (key.escape) {
|
|
261
258
|
handleCancel();
|
|
262
259
|
}
|
|
263
260
|
}, {
|
|
264
|
-
isActive: isFocused &&
|
|
265
|
-
mode !== "list" &&
|
|
266
|
-
mode !== "select-type" &&
|
|
267
|
-
mode !== "edit-provider",
|
|
261
|
+
isActive: isFocused && mode !== "list" && mode !== "select-type" && mode !== "edit-provider",
|
|
268
262
|
});
|
|
269
263
|
// Handle keyboard input for edit-provider mode (viewing a provider)
|
|
270
264
|
useInput((input, key) => {
|
|
@@ -315,7 +309,11 @@ export function LLMProvidersEditor({ label, providers, onConfirm, onCancel, isFo
|
|
|
315
309
|
if (mode === "select-type") {
|
|
316
310
|
return (_jsxs(Box, { flexDirection: "column", borderStyle: "single", borderColor: "cyan", paddingX: 1, children: [_jsxs(Box, { marginBottom: 1, children: [_jsx(Text, { bold: true, color: "cyan", children: "Select Provider Type" }), editingProvider && _jsxs(Text, { dimColor: true, children: [" for \"", editingProvider.name, "\""] })] }), PROVIDER_TYPES.map((type, index) => {
|
|
317
311
|
const isHighlighted = index === typeIndex;
|
|
318
|
-
return (_jsxs(Box, { children: [_jsx(Text, { color: isHighlighted ? "cyan" : undefined, children: isHighlighted ? "▸ " : " " }), _jsx(Text, { bold: isHighlighted, color: isHighlighted ? "cyan" : undefined, inverse: isHighlighted, children: type }), _jsxs(Text, { dimColor: true, children: ["
|
|
312
|
+
return (_jsxs(Box, { children: [_jsx(Text, { color: isHighlighted ? "cyan" : undefined, children: isHighlighted ? "▸ " : " " }), _jsx(Text, { bold: isHighlighted, color: isHighlighted ? "cyan" : undefined, inverse: isHighlighted, children: type }), _jsxs(Text, { dimColor: true, children: [" ", "-", " ", type === "anthropic"
|
|
313
|
+
? "Claude models"
|
|
314
|
+
: type === "openai"
|
|
315
|
+
? "GPT models"
|
|
316
|
+
: "Local models"] })] }, type));
|
|
319
317
|
}), _jsx(Box, { marginTop: 1, children: _jsx(Text, { dimColor: true, children: "j/k: navigate | Enter: select | Esc: cancel" }) })] }));
|
|
320
318
|
}
|
|
321
319
|
// Render name input mode
|
|
@@ -89,7 +89,7 @@ function highlightJson(jsonString) {
|
|
|
89
89
|
continue;
|
|
90
90
|
}
|
|
91
91
|
// Match brackets
|
|
92
|
-
const bracketMatch = remaining.match(/^[
|
|
92
|
+
const bracketMatch = remaining.match(/^[[\]{}]/);
|
|
93
93
|
if (bracketMatch) {
|
|
94
94
|
tokens.push({ type: "bracket", value: bracketMatch[0] });
|
|
95
95
|
remaining = remaining.slice(1);
|
|
@@ -16,7 +16,16 @@ export const CONFIG_SECTIONS = [
|
|
|
16
16
|
id: "cli",
|
|
17
17
|
label: "CLI",
|
|
18
18
|
icon: "⌨",
|
|
19
|
-
fields: [
|
|
19
|
+
fields: [
|
|
20
|
+
"cliProvider",
|
|
21
|
+
"cli.command",
|
|
22
|
+
"cli.args",
|
|
23
|
+
"cli.model",
|
|
24
|
+
"cli.yoloArgs",
|
|
25
|
+
"cli.promptArgs",
|
|
26
|
+
"cli.modelArgs",
|
|
27
|
+
"cli.fileArgs",
|
|
28
|
+
],
|
|
20
29
|
},
|
|
21
30
|
{
|
|
22
31
|
id: "docker",
|
|
@@ -52,7 +61,14 @@ export const CONFIG_SECTIONS = [
|
|
|
52
61
|
id: "chat",
|
|
53
62
|
label: "Chat",
|
|
54
63
|
icon: "💬",
|
|
55
|
-
fields: [
|
|
64
|
+
fields: [
|
|
65
|
+
"chat.enabled",
|
|
66
|
+
"chat.provider",
|
|
67
|
+
"chat.telegram",
|
|
68
|
+
"chat.slack",
|
|
69
|
+
"chat.discord",
|
|
70
|
+
"chat.responders",
|
|
71
|
+
],
|
|
56
72
|
},
|
|
57
73
|
{
|
|
58
74
|
id: "notifications",
|
|
@@ -84,6 +84,7 @@ export function escapeHtml(text) {
|
|
|
84
84
|
*/
|
|
85
85
|
export function stripAnsiCodes(text) {
|
|
86
86
|
// Match ANSI escape sequences: ESC[...m (SGR), ESC[...K (EL), etc.
|
|
87
|
+
// eslint-disable-next-line no-control-regex
|
|
87
88
|
return text.replace(/\x1B\[[0-9;]*[mKJHfsu]/g, "");
|
|
88
89
|
}
|
|
89
90
|
/**
|
package/dist/utils/config.d.ts
CHANGED
package/dist/utils/config.js
CHANGED
|
@@ -134,7 +134,9 @@ export function getProjectName() {
|
|
|
134
134
|
catch {
|
|
135
135
|
// Config not available, fall back to directory name
|
|
136
136
|
}
|
|
137
|
-
return basename(process.cwd())
|
|
137
|
+
return basename(process.cwd())
|
|
138
|
+
.toLowerCase()
|
|
139
|
+
.replace(/[^a-z0-9-]/g, "-");
|
|
138
140
|
}
|
|
139
141
|
export function loadPrompt() {
|
|
140
142
|
const promptPath = join(getRalphDir(), PROMPT_FILE);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|