noodle-dev
Use when developing features for the noodle terminal REST client — adding panes, keybindings, hooks, overlays, auth types, body types, I/O operation…
它会碰到什么
逐条看命中(3 条严重或高危)
- 严重
recipes.md:198cred-paths1. For new load feature: add function in `src/env/` that reads/parses `.env` files
- 严重
recipes.md:204cred-paths**Test:** Use `mkdtemp` for temp env dirs. Write `.env` files, load, assert fields, and inject a `SecretBackend` with `setSecretBackendForTests()` instead of to
- 严重
SKILL.md:58cred-paths- **Environments:** Dotenv-style `.env` files in `<collection>/.environments/`. Public, disabled, and secret keys use `^\w+$`; `_color` is reserved metadata. Pr
这一栏是扫描器报的事实,不是结论。命中多不等于有毒(安全工具、规则库、示例脚本本来就会包含危险写法),命中少也不等于干净。它和你手上的凭据、文件、网络有什么关系,需要你自己看。
技能内容
noodle-dev
Terminal REST client. OpenTUI (React binding) on Bun. YAML files on disk.
REQUIRED BACKGROUND: Read AGENTS.md for CLI commands, stack, and conventions.
Quick routing
| Task | Read |
| ------------------------------------------------------------------------------- | ------------------------------------------------------------------ |
| Understand module boundaries, data flow, state, CLI, collection layout | [architecture.md](architecture.md) |
| Add a keybinding, pane, overlay, auth type, body type, hook, importer, CLI flag | [recipes.md](recipes.md) |
| Write tests for new feature | [testing.md](testing.md) |
| Fix a bug or investigate a regression | [testing.md](testing.md) → [Bug-fix workflow](#bug-fix-workflow) |
| Add/modify persistent state (new files, config, timeline) | [architecture.md](architecture.md) → "Collection directory layout" |
| Build terminal UI components | REQUIRED SUB-SKILL: Use opentui skill |
Bug-fix workflow
For bug reports, keep investigation, regression-test creation, implementation,
and review as separate stages. Never declare a bug fixed based only on code
inspection.
- Reproduce the reported behavior before changing production code.
- When practical, add the smallest focused failing regression test that proves
the defect.
- If the user requests investigation or approval first, stop after reporting
the reproduction, likely root cause, and proposed minimal fix; do not
implement until approved. Otherwise, continue with the authorized fix.
- Make the smallest localized change that passes the regression test. Do not
refactor unrelated code or change behavior outside the reported bug.
- Never delete, skip, weaken, or broadly rewrite tests merely to make them
pass.
- Run the focused test first, then the full test suite after the patch.
- Review the final diff for regressions and unintended behavior changes. Report
the root cause, changed files, tests changed or added, commands run,
user-visible behavior changes, and remaining risks.
If an automated regression test is not practical, explain why and provide a
reproducible manual acceptance procedure. If the issue cannot be reproduced,
do not make speculative production changes; report what was attempted instead.
Ask for explicit approval before a compatibility-sensitive change that has not
already been authorized: public CLI or API behavior, collection or YAML formats,
configuration, keybindings, persistence, or backwards compatibility.
Key conventions
- Error re-throws: Pass
{ cause: e }as the second argument tonew Error(...). Use the surrounding module's error-message style; amodule.function:prefix is not universal. - Variable syntax:
$VARNAME(no braces), with names matching^\w+$.src/variableReference.tsis the shared scanner/replacer;$$emits a literal dollar and values resolve once. Applied in url/headers/params/body/formData/filePath/auth and assertion expected string values. Disabled fields remain source-identical. Automation overlays RunScope capture values through the same substitution path. - Authentication:
src/auth/defaults.tsowns reusable auth defaults,src/lang/auth.tsowns strict shared request and folder parsing/serialization, andsrc/ui/authRows.tsowns field metadata and mutation for both editors. OAuth 1.0a signing lives insrc/requests/oauth1.ts; OAuth 2.0 token acquisition, refresh, vault storage, and loopback browser authorization live inoauth2.tsandoauth2Browser.ts. Keep generated OAuth state out of YAML, re-sign or reapply auth per redirect leg, and strip credentials on origin changes. - YAML files:
.ymlextension (not.yaml). Requests stored one-per-file in collection dir. - Environments: Dotenv-style
.envfiles in<collection>/.environments/. Public, disabled, and secret keys use^\w+$;_coloris reserved metadata. Preserve all value content after the first=, including trailing spaces.# @secret NAMEplus a blank placeholder declares an OS-vault secret;process.env.NAMEtakes precedence over the stored value. - Draft pattern:
useRequestDraftholdsMap<id, Request>of dirty edits.DraftOpand mutation logic live inrequestDraftReducer.ts. Compare withisDirtyvia deep equality. - Focus model:
"sidebar" → "urlbar" → "request" → "response"(main). URL bar has method and URL sub-focuses."env-sidebar" → "env-header" → "env-vars"(env editor)."cookie-sidebar" → "cookie-list"(cookie jar)."runner-options" → "runner-requests"(collection Runner). Skips hidden panes. - Resizable main layout:
AppInner.tsxowns the sidebar width and separate stacked and side-by-side split ratios.MainView.tsxhandles mouse drag state, responsive minimums, and double-click reset;RequestResponseView.tsxrenders the layout-specific handles. Keep sizing session-only unless persistence is explicitly requested, and stop resize events from changing pane focus. - Keymap layers:
src/ui/keymap/*Layers.tsdefines bindings gated onfocus,mode,overlay, andview;layers.tsassembles them. UseuseBindings()from@opentui/keymap/react. - Optional request tabs:
useEditBrowseowns per-request session reveal state and menu focus. Empty Assert/Capture tabs stay behindRequestPane's+Select; populated tabs remain visible.cycleField()skips hidden tabs, anduseJumpModereveals directv/ctargets or focuses the menu witho. Keep visibility and keyboard navigation consistent. - Edit/Browse FSM: Three modes —
inactive → browsing → editing.useEditBrowsehook manages cursor, commit, cancel. - JSON and XML request bodies:
RequestBodyTabrenders both through the inlineCodeEditorRenderable; JSON validates substituted content, while XML uses Tree-sitter highlighting and is sent unchanged after substitution with anapplication/xmldefault when no enabled Content-Type exists.editingBodycontrols focus and the editor updates the request draft throughonBodyChange. Escape and Shift+Tab return to the body-type selector; Ctrl+Z undoes and Ctrl+Shift+Z redoes body edits. - JSON validation:
jsonValidation.tsvalidates the substituted payload but maps parse failures back to the request source, including the variable name and source line/column when a substituted value is invalid. - Visual response bodies:
ResponseVisualBodyrenders the JSON/XML tree fromresponseVisual.tswith expandable previews and compact tables. The footer andresponse.body-viewcommand (min the focused Body tab) toggle Source/Visual;/performs literal, case-insensitive Visual search or Source JSONPath. Keep Visual copy bound to the original body, preserve numeric JSON tokens, reject XML DTDs, and retain the 5 MiB opt-in guard. Search and expansion state belong to the current response. Size scroll content to the viewport and use intrinsic row width only as its minimum so pane resizing does not introduce transient scrollbars. - Sidebar visibility:
AppInner.tsxowns session-only visibility;commandActions.tshandlesCtrl+Band the palette action. Toggling preserves non-sidebar focus; hiding the focused sidebar falls back to the URL bar or folder pane. Focus cycling skips a hidden sidebar, and the sidebar jump reopens and focuses it. Response and cookie copying default toCtrl+Alt+B. - Response bodies:
ResponsePaneuses a read-onlyCodeEditorRenderablewith source-numbered folds and a themed scrollbar. Folded selections and body copying must return the original source, not the collapsed display text. - Environment UI:
eopensEnvironmentPickerOverlay;F3opens the full editor.Ctrl+Nin the editor opensNewEnvironmentOverlay, anduseEnvironmentEditor.createEnv()persists the validated name and optional color. - Collection formatting:
collection format <path>loads and rewrites every request with canonical YAML, pretty-printing valid JSON bodies throughformatJson. Invalid JSON remains unchanged, and valid numeric literals must retain their original precision. Imports run this formatter after writing the collection. - Response assertions:
src/response.tsparses and resolvesstatus,response.time, case-insensitive header, and JSON body expressions.src/assertions.tsevaluates typed operators.src/executionResults.tsorchestrates capture-before-assertion execution for manual TUI sends and automation. Disabled assertions remain validated and editable but produce no results. Structured and persisted results recursively redact known secrets from expected and actual values; live response views and arbitrary server data remain visible. - Response captures and RunScope: Every request
captureentry is an object with requiredvalue, optionalenabled, and optionalpersist; scalar shorthand is invalid.src/runScope.tsstores typed successful values for one top-level call and exposes an environment overlay tosubstitute(). Captures commit before assertions, failed captures do not replace prior values, and disabled captures produce no mutation or result. Values captured from sensitive response headers become secret automatically. Manual sends andrequest runmay persist plaintext or secret values throughpersistResponseCaptures(); collection runs and the TUI Runner ignore persistence. Capture declarations persist in request YAML but are excluded from timeline snapshots; capture results and RunScope values never enter timeline history. - Collection Runner:
useCollectionRunner.tsowns transient request/folder selection, environment, include/exclude tags, fail-fast and delay state, progress, and in-memory results.CollectionRunnerView.tsxrenders the two-pane workspace and shared expandableResponseResults;runnerLayers.tsowns navigation. F5 opens it by default. It composesselectCollectionRunRequests()andcollectionRun()rather than owning alternate request declaration editors or execution semantics. - Selective collection runs:
collection run <path> [<target>...]accepts request IDs and folder paths ending in/. Resolve and validate all targets before sending, include nested folder requests, deduplicate overlaps, and retain collection order. - Collection suites: Optional request and non-root folder
tagsform dynamic suites. Effective tags are the union across the ancestor chain. Collection targets resolve first, then repeated--tagvalues use AND matching and repeated--exclude-tagvalues use OR matching before environment, proxy, TLS, cookies, or execution. Preserve collection order, RunScope isolation, fixed failure-category ordering, fail-fast skips, and exit codes0success,1completed failure,2configuration failure. Render editable Settings tags, Runner filters, and Runner request-row tags with the sharedBadgetreatment:#prefix,theme.accentontheme.backgroundElementwhen inactive,theme.primarywiththeme.backgroundPaneltext when selected, and muted text for the add-tag control. Include badge padding and gaps in clipping and width calculations. - File I/O: Save/delete operations validate path IDs to prevent traversal. Environment replacement and settings saves are atomic via temporary files plus
rename(); new, cloned, and renamed environments use atomic exclusive creation. Request and folder writes are direct writes. - Collection modes: TUI opens collection roots in editable collection mode, request-containing uninitialized directories in read-only browse mode, and empty directories in read-only empty mode. Initialize through the command palette before editing or sending. Invalid request or folder YAML opens
CollectionErrorView, which reusesYamlFileEditorfor per-file repair drafts, validation, save, and delete actions. - Updates:
src/app/commands/update.tsreads the versionedhttps://noodlerest.dev/update.jsonmanifest, caches validated checksums, and verifies the matching binary before replacement.useUpdateFlowchecks and installs updates when the TUI starts, whileHeaderandAboutOverlayrender progress. After a successful standalone or Homebrew update,updateInstall.tsrefreshes an existing managednoodle-useskill with the new executable; refresh failure is non-fatal and must surfacenoodle agent installas the retry. Keep manifest schema, release workflow publishing, installation docs, and update tests synchronized when changing this flow. - Agent skill installation:
src/agentSkill.tsembeds the repository'snoodle-usefiles, installs the managed copy under~/.agents/skills/noodle-use, and links detected Claude, Cursor, Codex, and OpenCode skill directories.src/app/commands/agent.tsexposesnoodle agent install [--json] [--force]; the command palette calls the same installer throughcommandActions.ts. Preserve unmanaged targets by default and report every conflict before modifying anything. Force replacement must retain backups until all targets succeed and roll completed replacements back without overwriting a target that changed during the operation. - Timeline storage and security: Timeline request snapshots redact declared environment, proxy, and TLS secrets; substituted and literal credentials; jar-sent
Cookieheaders; known captured secrets; and assertion metadata before persistence. Response headers and bodies recursively redact the same known values, and sensitive response headers such asSet-Cookieare field-masked. Bodies larger than 10 KB move to gzip sidecars under.timeline/<request-id>.yml.bodies/; YAML entries retain abodyRef. Redact before compression; marking or updating a secret does not rewrite existing entries or sidecars. Preserve response structure while redacting values. Treat entries and sidecars as sensitive because public variables and unknown server data remain visible. - Settings secrets:
src/secrets/index.tswrapsBun.secretsfor environment secrets, proxy credentials, and encrypted mTLS key passphrases. Collection-scoped accounts use the generatedcollection_id; persist configuration and secret mutations transactionally so one cannot succeed without the other. - Cookie jars:
src/cookies/index.tswrapstough-cookiewith one concurrency-safe jar percollection_idunder~/.config/noodle/cookies/. Prefer OS-vault-backed encryption, report the mode-0600plaintext fallback, never replace unreadable state automatically, and back it up before an explicit reset.sendCookies: falsesuppresses sending jar cookies for one request but does not suppress response capture. - Network security: Custom proxy URLs reject credentials and variables; authentication metadata is
auth: trueand credentials come from the OS vault.src/tls.tsvalidates collection TLS, resolves CA/client-certificate files, and matches profiles by exact host and effective port. Redirects reject HTTPS-to-HTTP downgrades. When the origin changes, disable request auth, strip sensitive headers plus headers containing known secrets, and refuse a redirect that would preserve a known secret in the request body; redirects that discard the body may continue. Normalize request-timeout aborts as transport failures while propagating caller-owned cancellation. - OAuth discovery:
src/auth/oauth2.tsnormalizes issuer URLs;src/requests/oauth2.tsfills only missing endpoints usingdiscovery_url. Absentdiscovery_url_kindmeansissuer;documentrequests the exact URL. Discovery and token subrequests useresolveVariables: falsebecause auth values were already resolved.authRows.tsexposes both discovery fields in request and folder editors. OpenAPI supportsopenIdConnect; Postman requires explicit endpoints for the selected grant. - System theme:
generateSystemTheme()derives colors from the terminal palette.ThemeProviderrefreshes on palette/theme notifications, cancels stale updates when leaving System, and keeps the savedsystempreference while falling back to Noodle colors if detection fails. - OAuth security: OAuth 1.0a PLAINTEXT is limited to HTTPS or loopback HTTP, body placement requires URL-encoded form data, and body hashes reject multipart. OAuth 2.0 endpoints require HTTPS except on loopback, browser grants use an HTTP loopback callback, tokens prefer OS-vault storage with session-only memory fallback, and non-interactive sends never open a browser. Generated code is unavailable for OAuth requests because signatures and tokens depend on request-specific secure state.
- Imports and exports: Module singletons (
filestore,lang,env,executor).runImport()lazily registers the OpenAPI 3.0, Swagger 2.0, Postman, and Insomnia importers;runExport()writes OpenAPI 3.0.3 or Postman Collection v2.1 output. XML bodies preserve literal examples and explicit MIME types across supported formats. Types come fromschema/index.ts. - TUI collection transfer: The command palette owns Import Collection and Export Collection.
ImportCollectionOverlaycan create a new collection or write into the current one, which must have no unsaved changes;ExportCollectionOverlaypreviews an OpenAPI or Postman target and picks the next available Postman directory. Both use@/path completion throughuserPath.ts. - Command actions: Shared command logic lives in
commandActions.ts. Keymap layers andcommands.tsimport from it. If you add a new action, add it there and call from both paths. Do not duplicate logic. - External editors:
externalEditor.tsdetects supported editor executables and macOS applications. Global Behavior stores the selectedexternal_editor; command actions open only the collection or application settings directories. - CommandItem.run returns boolean: Palette commands return
true(close palette) orfalse(stay open). Unavailable commands (save when not dirty, copy body when no response) returnfalse. - Commands are contextual by view: Build them in view-specific arrays (
requestCommands,mainEnvCommands,editorEnvCommands,workspaceCommands,systemCommands, etc.) inbuildCommandPaletteCommands. Use arrays for view-level availability; state-dependentrun()guards returnfalsewhen unavailable. - PickerOverlay isNavigable: Command palette sections are generated by
CommandPaletteOverlay. For other picker items with non-selectable rows, passisNavigableso navigation skips them. - Modal keyboard isolation:
useModalKeyboardShieldinstalls a hard-blocking interceptor only for explicitly non-editable overlays. Editable and unknown overlays leave events available to the focused input; unknown names warn and remain input-safe. Modal-owned controls that must receive keys first (for example, an openSelectmenu) use a priority above the shield. - getView reads React state, not keymap: In
AppInner.tsx,getView: () => keymap.getData("app.view")is stale during render. UsegetView: () => viewwhereviewis the React state variable.
Common pitfalls
- Forgetting
{ cause: e }on re-throws — breaks error chains - Using
{{var}}instead of$var— noodle uses$prefix, not mustache - Skipping
validatePathId()in new file operations — security risk - Adding keybindings without
fixed: truefor navigation keys (Tab, Enter, Escape, arrows) — users could break navigation - Not registering a new keymap layer in
layers.ts— binding won't fire - Forgetting to update
focus.tscycleFocus()when adding new panes — tab cycling breaks - Adding command logic inline instead of in
commandActions.ts— will drift from keymap layer and vice versa - Using
run: () => voidinstead ofrun: () => boolean— palette won't close correctly - Adding vanilla
run()with earlyreturninstead ofreturn false— palette closes on unavailable commands - Handling modal keys only with
useKeyboard— events can leak to obscured panes; consume them through a keymap interceptor instead - Treating
sendCookies: falseas a capture toggle; it disables jar cookies on the outgoing request only
想直接用这个技能?
本站把开放许可(MIT / Apache 等)的技能按仓库打包整理到网盘,点一下转存到你自己的网盘,不用一个个从 GitHub 拉。许可未声明的技能只给原始仓库链接,不打包。