Token导航 LogoToken导航

Agent Skill · 研究检索

nullables(nullables 搜索)

nullables 用于查找、检索和筛选相关信息,适合在 Codex、Claude、Cursor、Gemini CLI 中需要根据关键词、任务场景或来源线索快速定位候选结果时使用。可结合来源仓库、安装命令和原始 README 继续核验具体用法。安装前建议确认权限范围、维护状态,以及是否会触发联网、命令执行或文件读写。

nullables 是研究检索类 Agent Skill,可用宿主为 Codex、Claude、Cursor、Gemini CLI,通过GitHub 仓库获取,权限为需要联网;许可证信息还不明确,第三方扫描已通过,按安装量在研究检索类的 73,675 个 Skill 里排第 60,976。

总安装 360周安装 15GitHub Stars 公开资料未说明下载 120核验 2026-05-01

安装说明

本站只整理中文说明,不托管安装包,也不代用户安装。
GitHub
许可证
unknown

安装方式

通过对话安装

复制提示词发给支持本地命令或 Skills 的 AI 助手,先确认命令和权限,再让它执行。

请帮我安装这个 Agent Skill:nullables(nullables 搜索)
来源仓库:https://github.com/danielbush/skills
仓库路径:skills/nullables
安装命令:
npx skills add https://github.com/danielbush/skills --skill nullables
安装前请先检查当前环境是否支持对应 CLI,并向我确认将要执行的命令、安装目录、联网范围和文件读写权限;确认后再执行。

命令行安装

复制命令到本机终端执行。该命令会通过 npx skills 从第三方来源获取 Skill;本站只展示命令,不托管安装包,也不自动执行。

skills.shnpx skills
npx skills add https://github.com/danielbush/skills --skill nullables

这个 Skill 是做什么的

研究检索类 Skill 主要解决「找资料、核对事实、把来源整理清楚」的问题,通常会让助手先检索再总结。

nullables 用于查找、检索和筛选相关信息,适合在主流 Agent 宿主中快速定位候选结果。适用于围绕可空类型(nullable types)相关技术内容进行信息检索。通过 npx skills add 命令安装,需结合原始 README 确认具体用法。

安装前建议确认权限范围、维护状态及是否触发联网或文件读写操作。可结合来源仓库进一步核验功能细节和使用限制。

来源仓库内路径:skills/nullables

上手步骤

4 步
  1. 1

    确认自己的宿主工具

    公开资料标注支持 Codex、Claude、Cursor、Gemini CLI;先确认自己用的是其中之一,再按对应工具的 Skills 目录放置。

  2. 2

    拿到安装命令

    可以直接复制的命令是 npx skills add https://github.com/danielbush/skills --skill nullables;命令里的占位符换成自己的路径或密钥再执行。

  3. 3

    安装后重启宿主

    命令执行完要完全退出宿主工具再打开,让新装的 Skill 被重新扫描到,否则列表里可能看不到。

  4. 4

    按最小权限试用

    它需要需要联网,先在测试目录或测试账号里跑,确认没问题再放到正式项目。

常见问题

6 条

nullables 怎么安装?

在终端执行 npx skills add https://github.com/danielbush/skills --skill nullables。装完完全退出宿主工具再打开,让它重新扫描本地 Skill。

nullables 支持哪些 AI 工具?

公开资料标注支持 Codex、Claude、Cursor、Gemini CLI。其他工具只要能读取本地 Skill 目录,一般也能手动放进去,但要自己确认触发方式是否一致。

nullables 需要什么权限?

权限级别是需要联网。该 Skill 可能需要联网访问来源站点、仓库或外部 API;具体网络访问范围需要结合源码和 README 复核。

nullables 是免费的吗?

公开资料没有标注明确许可证,收费和商用限制都无法确认。正式使用前请到来源仓库核对授权说明。

nullables 用的人多吗?

公开数据是累计安装 360 次,近一周安装 15 次,下载 120 次。这些数字来自第三方目录统计,只能当作热度参考,不代表质量或安全性。

nullables 安全吗?

第三方扫描结果为通过,但这只覆盖已知的静态检查项,不能替代自己读一遍源码和安装命令。

值得选的地方和要先想清楚的地方

值得选的地方

  • 同时适配 4 种工具,换客户端不用重新找替代品。
  • 第三方扫描已通过,常见的高风险行为没有命中。

先想清楚的地方

  • 许可证没有明确标注,能不能商用、能不能再分发都不确定。
  • 第三方目录整理,版本更新和行为细节以来源仓库为准。

适不适合你

更适合

  • 已经在用 Codex,想直接扩展它能力的人。
  • 需要处理研究检索类任务、又不想每次都手写提示词的人。

不太适合

  • 需要明确授权才能用到公司项目里的场景。
  • 需要厂商级技术支持和 SLA 的生产系统。

研究检索类 Skill 对比

6 个同类
Skill总安装GitHub 星权限许可证扫描
find-skills

寻找技能 · Codex、Claude

31,199,81316,486操作浏览器MIT有提醒
self-improving-agent

自我改进 Agent · OpenClaw

16,464,0023,396接触敏感数据MIT-0通过
soultrace

灵魂追踪 · Codex、Claude

7,588,94412需要联网未明确有提醒
self-improving

自我完善 · OpenClaw

6,829,2841,046需要联网MIT-0通过
microsoft-foundry

微软代工厂 · Codex、Claude

6,614,144764调用外部服务未明确有提醒
nullables

nullables 搜索 · 本页

360—需要联网未明确通过

按安装量取同分类前几名;数字来自第三方目录统计,只作热度参考,不代表质量结论。

SKILL.md 原文

Nullables and A-Frame Architecture

Overview

This skill guides implementation of James Shore's Nullables pattern and A-Frame architecture. This approach enables fast, reliable testing without mocks through:

  • A-Frame Architecture: Separation of Logic, Infrastructure, and Application layers
  • Nullables Pattern: Production code with an "off switch" for testing
  • Static Factory Methods: .create() for production, .createNull() for tests
  • State-Based Testing: Narrow, sociable tests that verify outputs instead of interactions

When to Use This Pattern

Apply this pattern when:

  • If the code uses a dependency injection framework (eg effect-ts) then use it to perform injections; also inform me where this happens as we might need to think about where the boundary lies between a 3rd party injection approach and the use of Nullables (.create / .createNull).
  • Writing new code
  • Refactoring existing code to eliminate mocks and improve test reliability
  • Writing tests for code that uses nullable patterns such as classes that use static factory method .create.

Core Principles

1. Three-Layer Architecture

Organize code into distinct layers:

  • Logic: Pure business logic and value objects (no external dependencies)
  • Infrastructure: Code interfacing with external systems (network, filesystem, databases)
  • Application: High-level orchestration coordinating logic and infrastructure

- application code is effectively infrastructure code because it contains instances of instrastructure that talk to the outside world

2. Dual Factory Methods

Every application and infrastructure class implements:

class ServiceClass {
  static create(config) {
    // Production instance - calls .create() on dependencies
    const dependency = Dependency.create();
    return new ServiceClass(dependency);
  }

  static createNull(config = {}) {
    // Test instance - calls .createNull() on dependencies
    const dependency = Dependency.createNull();
    return new ServiceClass(dependency);
  }

  constructor(dependency) {
    // Constructor receives instantiated dependencies
  }
}

3. Testing Without Mocks

  • Unit tests: Use .createNull() - fast, sociable, no external calls to the outside world
  • Integration tests: Use .create() sparingly - verify live infrastructure
  • Configure responses via factory: Service.createNull({response: data})
  • Track outputs: service.trackSentEmails()
  • Verify state and outputs, not interaction patterns

4. Value Objects

  • Immutable by default - represent data without behavior
  • Mutable value objects allowed - use methods for in-memory transformations only
  • Infrastructure operations (network, I/O) should take/return value objects, not be methods on them

Nullable algorithm for writing and testing code

We will use names like Foo, foo, Bar, Client etc for the purposes of describing this algorithm, but more descriptive names should be used in the code. Most code should be classes except potentially for logic layer code. Foo, Bar, Client are classes; foo is an instance of Foo etc.

Suppose Foo is the class under test or being created.

  • If Foo is application layer or infrastructure layer

- Foo should be a class - Foo.create and injects dependencies into the constructor of Foo; - Foo.createNull injects nulled versions of the any infrastructure dependencies for unit tests; "nulled" ensures any infrastructure code is "nulled" and returns a configured response rather than perform an I/O operation (network, disk etc); "nulled" instance never interacts with the outside world. - always get instances of foo by calling Foo.create in production code - when testing Foo, instantiate it directly foo = new Foo(...) and pass in nulled versions of dependencies: eg foo = new Foo(Bar.createNull(...)). - when Foo is a dependency in a unit test, use Foo.createNull. - Foo.create should set valid production defaults as much as possible reducing the need to pass parameters to Foo.create - an instance foo created by Foo.createNull should execute in exactly the same way, line for line, as one created using Foo.create.

  • If Foo is logic layer code:

- if it's stateless, pure functions should be fine; in this case Foo becomes foo (upper case is for classes) - if it involves non-I/O non-network in-memory manipulation, a class may be suitable - use Foo.create - no need to create Foo.createNull since there should be no I/O or network operations

  • If Val is a value object (immutable or mutable)

- Val should be a class - we should have a static Val.create but we don't need a Val.createNull so some of the procedures below may not apply; this is because value objects shouldn't be doing I/O operations so we don't need nulled versions - it may not make sense to set defaults for all parameters when creating value objects; so Val.create may end up requiring the consumer to specify many or all parameters and should be typed accordingly - use a static Val.createTestInstance to create test values; this is to indicate that the value create is a test value; it's fine for .createTestInstance to set defaults to make tests less verbose

The following is the algorithm for refactoring and creating new code. It doesn't need to be followed precisely, it is meant to convery the end result.

  • Find the code (eg a class Foo) that you think is the most important to write or test

- we want to be able to test Foo using narrow, sociable unit tests without mocks... - suppose Bar represents any another class that an instance of Foo needs to perform its actions - we have 2 scenarios - (1) instantiate Bar straight away: ensure Foo.create calls Bar.create and passes the instance to Foos constructor - (2) delayed instantiation of Bar: we pass in a factory createBar that returns an instance of Bar and let the instance of Foo create Bar at a later time; we might do this if Bar is only created based on some condition/event at a later time. - if we are passing multiple infrastructure dependencies we can use a "create" object that we pass in to the constructor: {Bar: (...) => Bar.create(...), Baz:...}; foo can then call create.Bar(...) to get a Bar instance at a later time. - Foo.createNull can then pass in a "create" object with nulled factories so that when foo calls create.Bar(...) it gets a Bar instance created by Bar.createNull - NOTE: We wrap all create functions eg Bar.create / Bar.createNull in a function because when injecting delayed nullables (Bar.createNull), the .createNull may take non-production parameters to configure it. - Factories in the create object can accept runtime parameters from the instance. This keeps decision logic in the factory (inside Foo.create) rather than in the instance. For example, if foo needs to select between implementations based on runtime state, the factory should accept a parameter for that: {Bar: ({useFallback}) => useFallback? FallbackBar.create(): Bar.create()}. The instance just calls create.Bar({useFallback: this.flag}) — it doesn't know about the implementations. - If you see Bar.create within an instance of Foo refactor to inject it using either (1) or (2) - if you had to create Bar.create, repeat this process but with Bar in place of Foo - keep recursing as required

  • Now repeat the above but for Foo.createNull (if applicable)

- this means Foo.createNull will either (1) or (2) like Foo.create.

  • Code that directly makes I/O calls to access an external service or resource such as the network (eg fetch) or disk etc is infrastrucure layer code and should be wrapped up in a class with .create and .createNull making it into a client we can instantiate;

- call this Client (here) but give it an appropriately descriptive class name eg HttpClient, DiskClient etc - Client sole purpose is to interface with the outside world but have minimal business logic - we also call Client an "infrastructure wrapper" - Client should make an effort to provide data to the rest of the application in the form the application needs and no more - Client.createNull should instantiate a nulled version - Client.createNull mimics I/O calls (eg mimics calls to fetch) - by default it should return a default "happy-path" response - it should take parameters (use a param object) that can configure the responses ("configurable response parameters") - "configurable response parameters" should configure for possible responses or outcomes we might have to test for; as a human reading the tests I should be able to easily see at a glance what outcome is being configured for in the nulled object; let the implementation within .createNull worry about the details - in some situations an "embedded stub" might be appropriate that stubs out a built-in I/O or network operation; the embedded stub should be configued within Client.createNull. - if Client is not using a framework like effect-ts to manage errors, then use neverthrow and use an object with a .type field to discriminate the error at compile time; if there is an underlying error from a throw or a rejection, assign it to .cause

  • value objects (instances of Val) are often returned by Client's
  • If Foo is infrastructure or application layer code and its methods contain I/O or network operations, replace these operations with a client and inject an instance of Client via Foo.create as per the above algorithm.

- Where possible make Foo.create generate a default production-ready instance of Client without the need to specifying anything to Foo.create.

  • If the code makes use of a 3rd party library to perform I/O eg tanstack-query / react-query / effect.runPromise etc, then...

- determine if this library provides a test client and use it for the tests - think about how it should be incorporated into the .create/.createNull architecture and let me know

  • Now go back to Foo

- write narrow sociable unit tests against instances of Foo using Foo.createNull

  • For insfrastructure wrapper code (Client),

- we should test Client.createNull - in a separate folder designated for live integration tests we should also test Client.create - these shouldn't be too numerous, we're testing against a live service to test the actual non-nulled code and also that we get back the responses we expect - these live integration tests should be run in a separate command to the unit tests above

  • If there are standalone functions that perform I/O or network operations (foo), favour re-writing foo as a class Client and treating it as a client / infrastructure wrapper and implement .createNull accordingly.
  • Sometimes we want to know that changes to the outside world occurred in tests, maybe also in a certain order.

- this allows us to keep tests state-based and outcome-focused rather than relying on mocks and testing internal interactions - Usually this happens in a Client - Client should implement an EventEmitter or a similar mechanism using .emit and .on/.off; when some interaction with the outside world (X) occurs, it should emit an event for X - Client should additionally implement a trackX function where X represents the outcome we're tracking - .trackX should return a tracking object (let's call it tracker which is an instance of Tracker) - Tracker should have access to the event emitter in the instance of Client - Tracker can add itself as a listener for X using the event listener and log the events usually in sequential order - tracker.stop() should get Tracker to remove itself - tracker.clear() should clear the previously logged data - tracker.data should be a getter to access the data - in some cases, tracking may be useful to the application, in which this event emitter should be exposed publicly; we can still implement a Tracker using the above so tests can track the outcomes

End of algorithm.

Implementing a New Feature

  1. Identify the layer:

- Pure computation? → Logic layer - External system interaction? → Infrastructure layer - Coordinating workflow? → Application layer

  1. Create the class with factory methods:

- Add static create() with reasonable defaults - Add static createNull() for infrastructure and application classes - Use constructor for dependency injection

  1. Implement dependencies through factories:

- .create() calls .create() on dependencies - .createNull() calls .createNull() on dependencies - Pass instantiated dependencies to constructor

  1. Write tests:

- Use .createNull() for unit tests - Configure responses via factory: Service.createNull({response: data}) - Track outputs: service.trackOutput() - Verify state and return values

  1. Add integration tests (sparingly):

- Use .create() for a few narrow integration tests - Verify infrastructure wrappers correctly mimic third-party behavior

Refactoring Existing Code

  1. Separate concerns:

- Extract logic into pure functions/classes - Wrap external dependencies in infrastructure classes - Create application layer to orchestrate

  1. Add factory methods:

- Replace direct instantiation with .create() - Add .createNull() to infrastructure and application classes - Update constructors to receive dependencies

  1. Replace mocks with nullables:

- Remove mock setup - Use .createNull() instances - Configure responses on nullable infrastructure

  1. Refactor tests to be state-based:

- Remove interaction assertions (verify/toHaveBeenCalled) - Add state and output assertions - Use event emitters for observable state changes

Implementation Patterns

Pattern: Infrastructure Wrapper

Wrap third-party code. Use an embedded stub that mimics the third-party library:

class HttpClient {
  static create() {
    return new HttpClient(realHttp); // Real HTTP library
  }

  static createNull() {
    return new HttpClient(new StubbedHttp()); // Embedded stub
  }

  constructor(http) {
    this._http = http;
  }

  async request(options) {
    return await this._http.request(options);
  }
}

// Embedded stub - mimics third-party library interface
class StubbedHttp {
  async request(options) {
    return { status: 200, body: {} };
  }
}

Pattern: Logic Sandwich

Application code: Read → Process → Write

async transfer(fromId, toId, amount) {
  // Read from infrastructure
  const accounts = await this._repo.getAccounts(fromId, toId);

  // Process with logic
  const result = this._logic.transfer(accounts, amount);

  // Write through infrastructure
  await this._repo.saveAccounts(result);
}

Pattern: Consumer-Defined Interfaces

When a class depends on infrastructure, define the simplest interface the consumer needs rather than accepting a concrete class. The consumer owns the contract; implementations adapt to it.

// Consumer defines what it needs — just updateBindings, not the full store API
interface EditorStore {
  updateBindings(data: SerializableData): ResultAsync<string, StoreError>;
}

class Editor {
  static create(ctl, values: { store: EditorStore; /* ... */ }) {
    return new Editor(ctl, values.store);
  }
  constructor(private ctl, private store: EditorStore) {}
}

// Multiple implementations satisfy the same interface
class LocalService implements EditorStore { /* persists to IDB */ }
class TestService implements EditorStore { /* always fails */ }

This keeps infrastructure errors out of the consumer. Implementations map their internal errors to domain errors the consumer expects (see error mapping below).

Pattern: Error Mapping at Boundaries

When implementing a consumer-defined interface, map infrastructure errors to domain errors using .mapErr(). The consumer should never see IDBStoreError, FetchError, etc. — only the error types declared in the interface.

class StoreError extends Error {
  readonly _tag = 'StoreError' as const;
  constructor(message: string, options?: ErrorOptions) {
    super(message, options);
  }
}

class LocalService implements EditorStore {
  updateBindings = (data: SerializableData) =>
    this.db.updateBindings(data)
      .mapErr((err) => new StoreError(err.message, { cause: err }));
}

Use a _tag discriminant on error classes so consumers can exhaustively match when the interface has multiple error types in a union.

Pattern: Event Emitters for State

Expose state changes for testing:

class Account extends EventEmitter {
  deposit(amount) {
    this._balance += amount;
    this.emit('balance-changed', { balance: this._balance });
  }
}

// Test by observing events
account.on('balance-changed', (event) => events.push(event));

Detailed Reference

For comprehensive implementation details, see nullables-guide.md:

  • Complete A-Frame architecture details
  • Nullables pattern implementation
  • Value objects (immutable and mutable)
  • Testing strategies and examples
  • Infrastructure wrappers and embedded stubs
  • Configurable responses and output tracking
  • Event emitters for state-based tests
  • Implementation checklist

平台分布

Codex

33.09%
按下载量换算40

Claude

30.42%
按下载量换算37

Cursor

19.41%
按下载量换算23

Gemini CLI

8.73%
按下载量换算10

安全审计

Gen Agent Trust Hub

通过

Socket

通过

Snyk

通过

权限和风险

需要联网

该 Skill 可能需要联网访问来源站点、仓库或外部 API;具体网络访问范围需要结合源码和 README 复核。

安装前确认

本站仅展示第三方公开信息,不托管安装包,不提供自动安装或运行环境。安装前应自行审查源码、依赖和命令行为。目前只有一个公开来源,建议同时核对源仓库。

继续浏览同类 Skills