Skip to content

Add Ruby as a supported output language#104

Merged
kalil0321 merged 5 commits into
kalil0321:mainfrom
jarsh921:add-ruby-output-language
Jul 22, 2026
Merged

Add Ruby as a supported output language#104
kalil0321 merged 5 commits into
kalil0321:mainfrom
jarsh921:add-ruby-output-language

Conversation

@jarsh921

@jarsh921 jarsh921 commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

Follows the exact same pattern as python/javascript/typescript:

  • New prompt partial (prompts/partials/_language_ruby.md): uses net/http and json - both part of Ruby's standard library, so no gem/Bundler dependency is needed at all, no project/dependency file of any kind. Calls out that net/http has no built-in cookie jar (unlike some other languages' HTTP clients), so cookie-based auth needs manual Set-Cookie capture and replay. Same auth-hardcoding/ refresh guidance as the other languages otherwise.
  • base_engineer.py: added "ruby" to _OUTPUT_LANGUAGE_EXTENSIONS (.rb) and _get_language_name() ("Ruby"). _get_run_command() uses the full path rather than a bare relative "ruby api_client.rb" - the same working-directory fix already applied to Go/Java/C#/PHP, applied proactively here too since ruby has the identical shape (the agent's cwd is scripts_dir.parent.parent, not scripts_dir where the script is actually saved).
  • cli.py: added Ruby to the interactive output_language config picker.
  • Updated the three docstrings/comments that listed the language set (config.py, engineer.py, prompts/init.py) and README.md's output language line.
  • Tests: mirrored the existing per-language test pattern in test_base_engineer.py (extension, run command, prompt content) and test_prompts.py (partial loading) for Ruby.

Checked the partial for the literal-brace str.format_map() bug caught in the Java PR - no literal {/} anywhere in the new file.

Full test suite passes (49/49, run 3x) except the same pre-existing flaky test noted on the other language PRs
(test_existing_client_language_falls_back_to_newest_file - a mtime-resolution race condition, unrelated to this change). ruff check is clean on every file this touches (8 pre-existing lint errors elsewhere on main, none in the touched lines).


Summary by cubic

Adds Ruby as a supported output language for generated API clients. Uses stdlib net/http and json, runs via a shell-quoted absolute-path ruby command, and adds per-origin connection reuse plus a cookie jar with domain/path/secure and expiration/deletion handling.

  • New Features

    • Ruby prompt partial with per-origin connection reuse and a cookie jar that enforces domain/path/secure scope, Expires/Max-Age, and deletion; includes token refresh guidance.
    • BaseEngineer: .rb extension, language name, and ruby <absolute path to api_client.rb> using shlex.quote.
    • CLI adds Ruby; README/CHANGELOG and language docs/comments updated; tests cover extension mapping, prompt/partial loading, metacharacter-safe command, and absolute-path resolution.
  • Bug Fixes

    • Ruby run command quotes and resolves the script path to prevent command substitution and cwd-related path errors.

Written for commit 916f68d. Summary will update on new commits.

Review in cubic

Greptile Summary

This PR adds Ruby as a supported generated-client language. The main changes are:

  • Ruby prompt guidance based on net/http and json.
  • Ruby extension, display-name, and run-command support.
  • Ruby selection in CLI settings.
  • Documentation and tests for the new language.

Confidence Score: 4/5

The remaining cookie behavior should be fixed before merging.

  • Connection reuse is now separated by origin.
  • Ruby execution uses a quoted absolute path.
  • Cookie expiration and deletion can leave stale session state in generated clients.

src/reverse_api/prompts/partials/_language_ruby.md

Security Review

The cookie guidance can retain and replay credentials after a server expires or deletes them.

Important Files Changed

Filename Overview
src/reverse_api/prompts/partials/_language_ruby.md Adds Ruby generation guidance and scopes connections per origin, but cookie expiration and deletion remain unhandled.
src/reverse_api/base_engineer.py Adds Ruby filename, display-name, and shell-quoted absolute run-command support.
src/reverse_api/cli.py Adds Ruby to the interactive output-language choices.
tests/test_base_engineer.py Adds Ruby coverage for extensions, prompts, path resolution, and shell quoting.
tests/test_prompts.py Adds coverage for loading the Ruby prompt partial.
Prompt To Fix All With AI
Fix the following 1 code review issue. Work through them one at a time, proposing concise fixes.

---

### Issue 1 of 1
src/reverse_api/prompts/partials/_language_ruby.md:11
**Handle cookie expiration too**

This instruction still omits `Expires` and `Max-Age`. When a refresh or logout response expires a cookie with `Max-Age=0`, generated code can retain and replay the old session value instead of deleting it. This can leave the script authenticated as the wrong session or cause later requests to fail. Require the cookie store to process expiration and deletion, with entries keyed by name, domain, and path.

```suggestion
- `net/http` has no built-in cookie jar, unlike some other languages' HTTP clients — if the API uses cookies, store each `Set-Cookie` header by name, domain, and path; enforce its secure, `Expires`, and `Max-Age` attributes; remove expired or deleted entries; and send back only cookies matching the request's origin and path in the `Cookie` header (a multi-origin trace can otherwise leak a session cookie to the wrong host or send an invalid header)
```

Reviews (2): Last reviewed commit: "Resolve scripts_dir to an absolute path ..." | Re-trigger Greptile

Greptile also left 1 inline comment on this PR.

Comment thread src/reverse_api/prompts/partials/_language_ruby.md Outdated
Comment thread src/reverse_api/prompts/partials/_language_ruby.md Outdated

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All reported issues were addressed across 10 files

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread src/reverse_api/prompts/partials/_language_ruby.md Outdated
Comment thread src/reverse_api/prompts/partials/_language_ruby.md Outdated
@kind-agent

kind-agent Bot commented Jul 21, 2026

Copy link
Copy Markdown

⚠️ Error — The test run failed unexpectedly.

Snapshot kind-sandbox is inactive

This is likely a transient issue. You can re-trigger a run from the dashboard.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All reported issues were addressed across 3 files (changes from recent commits).

Tip: Review your code locally with the cubic CLI to iterate faster.

Re-trigger cubic

Comment thread src/reverse_api/base_engineer.py Outdated
@kalil0321
kalil0321 force-pushed the add-ruby-output-language branch from 8f19b95 to cdef5f0 Compare July 22, 2026 11:12
@kalil0321

Copy link
Copy Markdown
Owner

@greptile review

Joshua Dunbar added 4 commits July 22, 2026 13:15
Follows the exact same pattern as python/javascript/typescript:

- New prompt partial (prompts/partials/_language_ruby.md): uses
  net/http and json - both part of Ruby's standard library, so no
  gem/Bundler dependency is needed at all, no project/dependency file
  of any kind. Calls out that net/http has no built-in cookie jar
  (unlike some other languages' HTTP clients), so cookie-based auth
  needs manual Set-Cookie capture and replay. Same auth-hardcoding/
  refresh guidance as the other languages otherwise.
- base_engineer.py: added "ruby" to _OUTPUT_LANGUAGE_EXTENSIONS (.rb)
  and _get_language_name() ("Ruby"). _get_run_command() uses the full
  path rather than a bare relative "ruby api_client.rb" - the same
  working-directory fix already applied to Go/Java/C#/PHP, applied
  proactively here too since ruby <relative-path> has the identical
  shape (the agent's cwd is scripts_dir.parent.parent, not scripts_dir
  where the script is actually saved).
- cli.py: added Ruby to the interactive `output_language` config
  picker.
- Updated the three docstrings/comments that listed the language set
  (config.py, engineer.py, prompts/__init__.py) and README.md's output
  language line.
- Tests: mirrored the existing per-language test pattern in
  test_base_engineer.py (extension, run command, prompt content) and
  test_prompts.py (partial loading) for Ruby.

Checked the partial for the literal-brace str.format_map() bug caught
in the Java PR - no literal `{`/`}` anywhere in the new file.

Full test suite passes (49/49, run 3x) except the same pre-existing
flaky test noted on the other language PRs
(test_existing_client_language_falls_back_to_newest_file - a
mtime-resolution race condition, unrelated to this change). ruff check
is clean on every file this touches (8 pre-existing lint errors
elsewhere on main, none in the touched lines).
Manually double-quoting the path (f'ruby "{path}"') does not stop
$()/backtick command substitution inside a POSIX shell double-quoted
string. Use shlex.quote() instead, matching the fix already applied to
the Go/Java/C#/PHP run commands after bot review flagged the same
issue on the PHP PR.
Both review bots flagged the same two issues: "reuse one persistent
connection" doesn't account for captured traffic spanning multiple
hosts (Net::HTTP.start is bound to a single host:port), and replaying
every captured cookie as one Cookie header ignores Set-Cookie's
domain/path/secure scoping, which can leak a session cookie to the
wrong origin. Reworded both bullets to scope per origin instead.
cubic flagged that a relative --output-dir breaks this command: scripts_dir
stays relative to the original cwd, but once the agent's cwd moves to
scripts_dir.parent.parent (see analyze_and_generate), embedding that same
relative string re-interprets it from the new location and points at a
doubly-nested, nonexistent path. Resolve scripts_dir before interpolating
it, same fix as just applied to the C# run command.
@kalil0321
kalil0321 force-pushed the add-ruby-output-language branch from cdef5f0 to 2978715 Compare July 22, 2026 11:15
**Authentication & credentials:**
- Hardcode all cookies, tokens, session IDs, and auth headers found in the traffic directly in the script
- The user should be able to run the script immediately with zero configuration — no env vars, no config files, no `bundle install`
- `net/http` has no built-in cookie jar, unlike some other languages' HTTP clients — if the API uses cookies, capture each `Set-Cookie` header's name/value and its domain, path, and secure attributes, and send back only the cookies matching a given request's origin and path in the `Cookie` header, rather than replaying every captured cookie on every request (a multi-origin trace can otherwise leak a session cookie to the wrong host or send an invalid header)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 security Handle cookie expiration too

This instruction still omits Expires and Max-Age. When a refresh or logout response expires a cookie with Max-Age=0, generated code can retain and replay the old session value instead of deleting it. This can leave the script authenticated as the wrong session or cause later requests to fail. Require the cookie store to process expiration and deletion, with entries keyed by name, domain, and path.

Suggested change
- `net/http` has no built-in cookie jar, unlike some other languages' HTTP clients — if the API uses cookies, capture each `Set-Cookie` header's name/value and its domain, path, and secure attributes, and send back only the cookies matching a given request's origin and path in the `Cookie` header, rather than replaying every captured cookie on every request (a multi-origin trace can otherwise leak a session cookie to the wrong host or send an invalid header)
- `net/http` has no built-in cookie jar, unlike some other languages' HTTP clients — if the API uses cookies, store each `Set-Cookie` header by name, domain, and path; enforce its secure, `Expires`, and `Max-Age` attributes; remove expired or deleted entries; and send back only cookies matching the request's origin and path in the `Cookie` header (a multi-origin trace can otherwise leak a session cookie to the wrong host or send an invalid header)
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/reverse_api/prompts/partials/_language_ruby.md
Line: 11

Comment:
**Handle cookie expiration too**

This instruction still omits `Expires` and `Max-Age`. When a refresh or logout response expires a cookie with `Max-Age=0`, generated code can retain and replay the old session value instead of deleting it. This can leave the script authenticated as the wrong session or cause later requests to fail. Require the cookie store to process expiration and deletion, with entries keyed by name, domain, and path.

```suggestion
- `net/http` has no built-in cookie jar, unlike some other languages' HTTP clients — if the API uses cookies, store each `Set-Cookie` header by name, domain, and path; enforce its secure, `Expires`, and `Max-Age` attributes; remove expired or deleted entries; and send back only cookies matching the request's origin and path in the `Cookie` header (a multi-origin trace can otherwise leak a session cookie to the wrong host or send an invalid header)
```

How can I resolve this? If you propose a fix, please make it concise.

A Max-Age=0 or Expires deletion from a refresh/logout response must
evict the stored entry, or the generated script keeps replaying a dead
session value. Key entries by name, domain, and path.
@kalil0321
kalil0321 merged commit b0c0e8e into kalil0321:main Jul 22, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants