npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@jayyuen66/dsh-dir-prep-organize

v0.1.2

Published

dsh 插件:对话输入框「整理」按钮(主键直接整理/▾ 以角色视角整理,结合当前目录 + session 模型整理优化草稿)+「模板」下拉(内置精选角色集生成自插件 agents/ 角色库,可按部门分组;设置卡可增删改/一键导入内置全部/导入本地角色 .md)

Readme

@jayyuen66/dsh-dir-prep-organize

English · 简体中文

What it does

  • Adds two controls to the right of the dsh web chat input: a split "Organize" button and a "Template" dropdown.
  • The main Organize key organizes directly, the caret key offers "organize from a role's point of view"; the Template dropdown groups entries by group, and an empty group falls into "General".
  • "Organize" hands the draft plus the top level of the session cwd (file names, sizes, full text of key text files) plus the session's recent real turns to the current default model for a rewrite.
  • On success the draft is replaced; on failure it stays untouched and the button shows an error state for 4 seconds (reason in its title).
  • Picking a template inserts it into the draft: a whole fill when the draft is empty, an append after one blank line otherwise.
  • The default list is 85 curated role templates generated by scripts/gen-defaults.mjs from this package's agents/ role library (22 department directories, 306 role .md files).
  • That table stays host-side only: the client bundle no longer inlines it - the generated file is 640KB and used to push client.js to 711KB.
  • The browser fetches GET /_dsh/dir-prep/default-templates once, lazily - on the first menu open or settings-card visit - and reuses it in-process afterwards.
  • While it has not arrived the menus show a loading row rather than the existing "no templates" hint, and a failure surfaces the host's own reason plus a Retry button. Nothing is fetched at all when the user already has their own templates.
  • The "Prompt templates" card on the settings page covers per-entry add/edit/delete, two-step restore of defaults, one-click import of the whole built-in library, import of a local .md or directory by path, and registration of import allowed directories.
  • Templates and allowed directories are read and written through the host settings with zero LLM calls, and no model tool is registered.

Install

The packages are published to the public npm registry (registry.npmjs.org), so installation needs no credentials:

dsh plugin --profile web add @jayyuen66/dsh-dir-prep-organize
  • Host range >= 0.2.0-rc.2, written to both peerDependencies and engines.dsh. Only peerDependencies is actually enforced: since 0.1.7-rc.1 the host evaluates it when loading plugins and disables an entry that fails (compatibility-preflight.ts → deny()), whereas engines.dsh has no reader at this host version.
  • The @deepseek-ai/dsh-* dependencies stay pinned at 0.2.1-alpha.2, the version this package is built and tested against. The range is a claim about which hosts it runs on; the pin is a fact about which host version it was compiled against — mixing the two is why the publish gate now checks them separately.
  • The client half is dsh.client.platform = web with immediately.
  • The publishing form is one repository per package, repository.url is git+https://github.com/JayYuen666/dsh-dir-prep-organize.git ; issues go to that repository's issue tracker (bugs).
  • Requires Node.js >= 22.12. This is an ESM-only package: use import { apply } from "@jayyuen66/dsh-dir-prep-organize" or require("@jayyuen66/dsh-dir-prep-organize"). There is no default export — import pkg from … fails with does not provide an export named 'default'; take the named exports instead. That Node floor is what makes require() of this ESM entry work at all, which is why engines.node is declared.
  • TypeScript declarations ship as types/host.d.ts, wired into both types and the . export condition. The Config schema value carries an explicit annotation so the emitted file never has to name a package a consumer cannot resolve.
  • The published artifact ships only host.js, client.js, templates.data.mjs, types/, cordis.patch.yml, agents/ and THIRD_PARTY_NOTICES.md (package.json files). LICENSE, README.md and package.json are attached unconditionally by the registry client.
  • The built-in template table lives in its own artifact and is loaded on first use, not at plugin load. It used to be bundled into host.js, so every user paid for 85 templates in memory even without opening the dropdown - host.js is now roughly a tenth of its former size, and the browser already fetched this data lazily anyway.
  • agents/ is not authored here: it comes from the upstream project agency-agents-zh (MIT). Its copyright and licence notice ship in THIRD_PARTY_NOTICES.md, because MIT requires it in every distributed copy.
  • No @deepseek-ai/* goes in dependencies, and that is not a style preference. Consumers of this package are dsh plugins installed into a profile (nodeLinker: hoisted, autoInstallPeers: false), so any host package listed under dependencies is written into that profile as a real directory, coexisting with the copy the host process already loaded. @deepseek-ai/dsh-tools keys its TOOL_RUNTIME_SCHEDULER with a plain Symbol (not Symbol.for), so symbol identity is per module instance: the host's dsh-agent-loop reads a ToolRuntime mounted from the duplicate using its own copy's symbol, gets undefined, and every native tool call throws Cannot read properties of undefined (reading 'prepare') — the whole profile loses its tools. This package's build, typecheck and tests all stay green right up to that point, so test/publish-manifest.test.ts pins the invariant in the manifest instead.
  • The two host packages the shipped host.js imports as values are required (non-optional) peerDependencies: @deepseek-ai/schemastery (the host's fork — only it resolves 0.1.7 .volatile() fields into live references) and @deepseek-ai/dsh-brand (0.2.1-alpha.2). The host provides them at runtime and a profile installs no peers, so no duplicate appears. build-host.mjs externalizes dependencies and peerDependencies, so the bare specifiers in host.js stay exactly as they were — that is the ruling behind brandString: the peer keeps the specifier external and the official function body out of the artifact, while a devDependencies-only entry lets rolldown inline it (test/build-host.test.ts pins both "specifier present" and "function body absent").
  • Every host package the emitted declarations name is declared under peerDependencies, and each one reached only there is optional: @deepseek-ai/cordis (4.0.5-alpha.1), @deepseek-ai/cosmokit (1.8.6-alpha.1, reached only through schemastery's Dict) and @deepseek-ai/dsh-agent-default-model, -client-locale, -client-ui-conversation, -client-ui-renderer, -client-ui-settings, -client-ui-slots, -fs, -llm, -session (all 0.2.1-alpha.2). Declaring them is not ceremony: under pnpm's strict layout a bare specifier in types/host.d.ts that no manifest table names is unresolvable, so the declaration entry simply would not typecheck on the consumer's side while this repository's own typecheck stays green — here both tables carry it. test/publish-manifest.test.ts reads that roster off types/**/*.d.ts rather than off this paragraph, so the two cannot drift apart. @deepseek-ai/dsh is the one peer nothing imports: it names the host, which is what engines.dsh says in words.
  • Every peer is repeated in devDependencies at the same exact pin; that is what lets this repository's own typecheck, build and tests resolve them. The remaining @deepseek-ai/* entries here are type-only in source and leave no specifier in the declarations either, so they stay in devDependencies alone.
  • What is left in dependencies is this package's own runtime code: @jayyuen66/dsh-plugin-shared.

Enabling it in dsh

  • Installing the package contributes the bundle patch layer (dsh.bundle.patch points at ./cordis.patch.yml), which inserts - id: dir-prep-organize with name set to the package name.
  • Removal goes through dsh plugin --profile web remove @jayyuen66/dsh-dir-prep-organize.
  • The host half hard-injects webServer and settings: without settings the card is disabled with a warning, the "Template" dropdown falls back to the built-in curated set, and the five endpoints keep working.
  • The client half registers slot conversation.input.right (organize id dir-prep-organize order 99, templates id dir-prep-organize-templates order 100) and the settings card slot plugins.bundle.config.
    • The card slot key = the bundle package name @jayyuen66/dsh-dir-prep-organize: the host dispatches entryKey: pkg.name (source of truth: dsh.profile.bundles in ~/.dsh/profiles/web/package.json), so a bare entry id never matches and the card renders nowhere.
    • The settings namespace and configForms.get() stay the bare entry id dir-prep-organize - not the same identifier as that slot key.

Tools exposed to the model

| Aspect | Fact | | ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Tool registration | None. The host half only injects webServer and settings and never touches the host tool registry | | Organize call | Host llm.stream: one user text message (request-only input - no id, no source; see the three-evidence note at the top of host.ts) plus a system rule block; the model comes from agentDefaultModel.currentSelection() (provider/model/reasoningEffort) and its absence reports "No model selection is available" | | Role point of view | Optional roleText injected into the "Reference role" part of the system block; it only borrows terminology and tone, it never executes the role's tasks | | Limits | All three live in config and every request takes them through limits: organizeTimeoutMs defaults to 120000 ms (a volatile reference read via .get()), maxSnippetBytes to 262144 (256 KiB) and readConcurrency to 8 (plain values, no .get()); the client request timeout is organizeTimeoutMs + 5000 ms (125000 when the host value is absent; the other three endpoints fetch with the constant FETCH_TIMEOUT_MS of 125000 ms); the POST body cap is the config field importBodyMaxBytes at 8 MiB by default; directory/conversation/role text are never truncated; a finish reason other than stop (including a missing finish chunk) fails and never writes back |

Settings

| Field | Type | Default | Notes | | ------------------- | ------------------------------------------ | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | templates | {id,name,description,text,group,emoji}[] | unset | All-string fields; description/group/emoji default to empty strings; entries with an empty name or body are dropped on save, emoji is cut to 12 characters, a missing or colliding id is deterministically replaced by tpl-<index>. The dropdown falls back to the 85 built-in curated entries when unset. ⚠ under implicit registration "unset" reaches the client as an empty array (schemastery casts a missing array field to []; omitting the default does not yield undefined), so an empty list always means "fall back to built-in" - only a non-empty array whose items are all invalid renders as an empty list | | importAllowRoots | string[] | [] | Extra allowed roots for the import endpoint, registered directory by directory; non-strings and whitespace-only items are dropped and duplicates removed; with nothing registered only the session cwd and the built-in role library are allowed | | organizeTimeoutMs | natural, .volatile() | 120000 | The LLM abort deadline in ms; the host reads it per request via config.organizeTimeoutMs.get(). Being volatile it is projected into this entry's form snapshot, and of the three it is the only one the client half reads (rawOrganizeTimeoutMsOf), used to set the organize fetch timeout to "this value + 5000 ms". ⚠ Clamped to [1, 2147483647]: past 2^31-1 Node's setTimeout prints a TimeoutOverflowWarning and silently uses 1 ms, which turns "raise the timeout" into "fail instantly". ⚠ The card has no control for it: src/client-entry.ts writes only templates and importAllowRoots, so changing it means settings.yaml or the row's config: | | maxSnippetBytes | natural (non-volatile) | 262144 | Per-file cap for the key-file snippet; a larger file, or one whose size is unknown, is skipped whole and counted in skipped.tooLarge. Import candidates use the same cap. Non-volatile means it is not projected into the form snapshot, so there is no card row either - deployment value only (settings.yaml / row config:) | | readConcurrency | natural (non-volatile) | 8 | Bounded read concurrency for the key-file snippets and for directory imports (sub-directory listings, third-level counting, candidate reads). Same as above: no projection, no card control. ⚠ Clamped to [1, 64] before use: 0 or a negative value made the bounded map recurse into itself forever until the host process was killed by the OOM killer, and a huge value simply defeats the fd ceiling this field exists to hold | | importBodyMaxBytes | natural (non-volatile) | 8388608 | Byte cap on the bodies of the two POST routes, organize and import (depth-of-defense against a memory blow-up, not a functional truncation: those routes carry the organized draft and the template products, not the file payloads themselves); over the cap is 413, and guardBody meters the stream and returns early at the crossing, so the worst resident memory is exactly this watermark. Same as above: no projection, no card control | | Restore defaults | — | — | Unsets templates back to the curated set and leaves importAllowRoots untouched; two-step confirm, the second click must come within 4 seconds | | Storage | — | — | Settings namespace dir-prep-organize (0.1.7 implicit registration: the namespace is this entry's id, and the fields projected into the settings form are the .volatile() ones on Config in host.ts - templates, importAllowRoots, organizeTimeoutMs, of which the card writes the first two; maxSnippetBytes and readConcurrency stay plain values; the curated set stays on the host half and is served on demand, not as a host base), persisted in the settings.yaml under <dsh data dir> ($DSH_HOME) |

Public interface

| Route | Method | Input | Success payload | | ------------------------- | ------ | ---------------------------------------------------------------------------------------- | ----------------------------------------------------------------------- | | /_dsh/dir-prep/context | GET | query sessionId | ok/csrf/cwd/truncated/skipped/entries[{name,isDir,sizeBytes,snippet}] | | /_dsh/dir-prep/model | GET | query sessionId | ok/csrf/provider/model/reasoningEffort | | /_dsh/dir-prep/organize | POST | body prompt/sessionId/entriesSummary/optional roleText, header x-dir-prep-csrf | ok/content | | /_dsh/dir-prep/import | POST | body path (exactly empty = whole built-in library)/sessionId, no CSRF | ok/count/truncated/skipped/entries | | /_dsh/dir-prep/default-templates | GET | none, no CSRF | ok/templates[{id,name,description,text,group,emoji}] (the curated built-in set) |

| Case | Response | | ---------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Routing and token | All four are exact-match webServer routes (kind: "exact"); the first statement of every handler body is guardTrust(req, res, { servingNonLoopback }) from shared/lib/trust, which writes a single 403 and returns without reading the body; csrf is a per-apply random token, issued by the two GETs and verified on organize | | Order of the trust checks | Host authority → the sec-fetch-site allowlist (only same-origin/none/absent) → a verbatim origin vs Host comparison; the isCrossOrigin branch inside this package's handlers became unreachable once the gate landed and is deleted (the identical check inside the shared guardBody now sits behind the gate too), so the gate is what rejects cross-site traffic and supplies the sentence | | Host authority is not a trusted local address | 403 with {ok:false,error:"untrusted host authority"}; servingNonLoopback comes from isNonLoopbackBindHost(webServer.host) (shared/lib/trust) - the host takes any local interface address as Config.host and rejects the wildcard address at load, so comparing against "0.0.0.0" would be permanently false; a service that reports no bind address keeps the conservative loopback-only setting | | sec-fetch-site outside the allowlist, or origin mismatch | 403 with {ok:false,error:"cross-origin request rejected"} (opaque origin: null sandbox pages, and a local request that carries origin but no Host, land here too) | | Wrong method | 405 with Allow and a JSON body: {ok:false,error:"GET only"} (organize/import answer "POST only") | | Missing or wrong CSRF | 403 with {ok:false,error:"invalid csrf token"} (the guardBody check, still behind the gate) | | Body over the configured cap (8 MiB by default) | 413 with {ok:false,error:"request body too large"} | | Broken stream or invalid JSON | 400 with {ok:false,error} ("request body unreadable" for a broken stream; invalid JSON uses the dictionary text) | | Business failure | Still 200 with {ok:false,error}; the import endpoint's out-of-bounds and malformed-path branches are the exception - 403 and 400 (see Data and privacy) |

Data and privacy

| Aspect | Fact | | ---------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Network calls | Only target this process's same-origin /_dsh/*; nothing is requested from any external host and no environment variable is read | | Data on disk | The only data written to disk is templates and importAllowRoots in settings | | What one organize call sends | The full draft, the session cwd path, top-level directory entries with the full text of key text files, real user/assistant turns (framework-injected messages and tool results are filtered out), and the optional role body | | Snippet extensions | Only 20 code/document extensions (ts/tsx/js/jsx/mjs/cjs/py/rs/go/java/rb/json/toml/md/sql/graphql/proto/css/scss/less); yaml/env types are not whitelisted | | Sensitive file names | Any file whose name contains credential/secret/id_rsa/.pem/.p12/.pfx/keystore/.env/.key never gets a snippet | | Per-file cap and concurrency | A single file over maxSnippetBytes (default 256 KiB), or with unknown size, is skipped whole (no half summaries), reads run with readConcurrency (default 8); both are config fields and neither has a card control | | Counters and echo | Too-large/unreadable/deeper-level/non-role cases are counted in skipped, shown in the UI, and reflected honestly by truncated | | Import endpoint | Read-only: no model call, no disk writes. Absolute paths are refused by default; only paths inside the session cwd, the package's agents/ and importAllowRoots pass, ./../hidden segments (such as .ssh) are always rejected, and an out-of-bounds path answers 403 echoing the resolved path |

FAQ

| Symptom | What happens | | -------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Nothing changed after pressing "Organize"? | Failures never touch the draft; the button shows an error state for 4 seconds with the reason in its title. Usual causes: no session cwd (sessionId missing or session unknown), no model selection, a finish reason other than stop, the organizeTimeoutMs deadline (120 seconds by default) | | The model reports a context overflow? | Directory/conversation/role text are deliberately not truncated, so the model error is the feedback and you slim the input yourself; a payload above the configured body cap (8 MiB by default) gets 413 from the endpoint instead | | Importing local roles was refused? | Absolute paths are refused by default and the settings card has no session context (its POST sends sessionId as an empty string), so the path must live inside the built-in agents/ or a registered directory and contain no ./../hidden segments; the rejection names the allowed zones and echoes the resolved path | | Fewer entries imported than expected? | Read the four skipped counters: directory imports cover the top level plus one sub-directory level (a third level is only counted as deeper), files over maxSnippetBytes (256 KiB by default) count as tooLarge, files whose name cannot be parsed from the first 20 frontmatter lines count as unnamed, and read failures as unreadable | | Template edits not applied / wrong language? | Edits are staged locally and take effect for newly opened dropdowns after "Save"; the card, the two dock buttons next to the input (their labels and copy now live in the ui-messages.ts dictionary) and the host replies all follow the official locale plugin's preference (the host side reads it per request, no restart), while the agents/ role library and template bodies are user data and are never machine translated |