| name | cocosearch-new-feature |
| description | Use when adding new functionality — a new command, endpoint, module, handler, or capability. Guides placement, pattern matching, and integration using CocoSearch. |
Add New Features with CocoSearch
A systematic workflow for adding new code to an existing codebase. This skill uses semantic search to find existing patterns, conventions, and integration points so the new feature fits naturally into the project architecture.
Philosophy: New code should look like it was always there. Search the codebase for existing patterns before writing anything.
Pre-flight Check
- Resolve index name (use the resolved name for all operations):
- Try
cocosearch.yaml for indexName field -- if found, use it
- If no config file, call
list_indexes() and match the current project's directory name against available indexes. The MCP tools auto-derive index names from directory paths (e.g., my-project/ -> my_project), so a match is likely if the repo was indexed without a config file.
- If no match found, the project is genuinely not indexed -- offer to index it. Do NOT abandon CocoSearch tools just because
cocosearch.yaml is missing.
list_indexes() to confirm project is indexed
index_stats(index_name="<resolved-name>") to check freshness
- Stale (>7 days) → warn: "Index is X days old -- I may miss recent patterns. Want me to reindex first?"
- Linked index health (if
cocosearch.yaml has linkedIndexes):
- Check the
warnings array from index_stats() for entries starting with "Linked index"
- If stale/missing: warn user — "Linked index 'X' is stale/missing. Cross-project results may be incomplete. Want me to reindex?"
Step 1: Understand the Feature
Parse the user's request to identify what needs to be built and where it fits.
Extract from the request:
- What: The new capability (e.g., "a CLI command to export stats as CSV")
- Where: The subsystem it belongs to (e.g., CLI layer, API layer, indexer, search)
- Interface: How users/code will interact with it (e.g., CLI flag, function call, MCP tool, API endpoint)
Classify the feature type:
- New command/endpoint: Adds a new entry point (CLI subcommand, MCP tool, API route)
- New module: Adds a new subsystem or service (e.g., new handler, new search strategy)
- Extension: Adds capability to an existing module (e.g., new flag, new option, new format)
- Cross-cutting: Touches multiple subsystems (e.g., new data field flowing from indexer to search to output)
Confirm with user: "I understand you want to add [feature]. It fits as a [type] in the [subsystem] layer. Is that right?"
Step 2: Find Existing Patterns
This is the most important step. Search for how similar things are already built. New code should follow established conventions.
2a. Find the Closest Analog
Every feature has a precedent. Search for the most similar existing implementation.
Semantic search for similar functionality:
search_code(
query="<description of what the feature does>",
use_hybrid_search=True,
smart_context=True,
limit=10
)
Cross-project search: If linkedIndexes is configured in cocosearch.yaml, searches automatically expand to linked indexes. For ad-hoc multi-project pattern discovery, pass index_names=["project1", "project2"].
Query rewrite: If the optional query-rewrite controller is enabled, pass rewrite_query=False when you have already crafted precise terms (exact identifiers, symbol_name/symbol_type filters) to search them verbatim. No effect when the controller is disabled.
Search for the subsystem's existing entry points:
search_code(
query="<subsystem> command handler endpoint",
symbol_type=["function", "class"],
use_hybrid_search=True,
smart_context=True
)
Find the registration/wiring pattern:
How are existing features registered? Search for the glue code:
search_code(
query="register add route subcommand handler",
use_hybrid_search=True,
smart_context=True,
limit=10
)
Present the analog: "The closest existing feature to what you want is [analog] in file:line. Here's how it's structured: [brief description]. I'll use this as the template."
2b. Extract the Convention
From the analog and broader search, identify the project's conventions:
File placement — where does new code go?
search_code(
query="<subsystem-name>",
symbol_type="class",
use_hybrid_search=True,
limit=15
)
Look at the file paths in results. Is there a clear directory structure? (e.g., handlers/*.py, commands/*.py, routes/*.py)
Naming conventions — what do things get called?
search_code(
query="<similar-feature-type>",
symbol_name="*<pattern>*",
use_hybrid_search=True
)
Look for naming patterns: cmd_*, handle_*, test_*, *Handler, *Service.
Interface patterns — how are features exposed?
search_code(
query="<interface-type> definition",
use_hybrid_search=True,
smart_context=True
)
Look for: argument parsing patterns, decorator usage, protocol/interface definitions, configuration patterns.
Testing patterns — how are similar features tested?
search_code(
query="test <analog-feature>",
symbol_name="test_*<analog>*",
symbol_type="function",
use_hybrid_search=True
)
Identify: test file location, fixture usage, mocking patterns, assertion style.
Present conventions:
Conventions for [subsystem]:
- Files go in: src/<module>/<subsystem>/
- Naming: <verb>_<noun> for functions, <Noun><Role> for classes
- Registration: via <mechanism> in <file>
- Tests go in: tests/unit/<subsystem>/test_<feature>.py
- Test pattern: [mock X, call Y, assert Z]
2c. Map Integration Points
Find where the new feature needs to connect to existing code.
Find imports/dependencies the feature will need:
search_code(
query="<analog-feature> import dependency",
use_hybrid_search=True,
smart_context=True
)
Find where the feature needs to be registered/wired:
search_code(
query="<registration-mechanism>",
symbol_name="<registration-function>*",
use_hybrid_search=True,
smart_context=True
)
Find shared utilities or helpers the feature should reuse:
search_code(
query="<operation the feature needs> utility helper",
use_hybrid_search=True,
limit=10
)
Present integration points:
Integration points:
1. Register in: <file>:<line> (add to <mechanism>)
2. Import from: <module> (for <dependency>)
3. Reuse: <utility> from <file> (don't reinvent)
4. Expose via: <interface> in <file>
Step 3: Design the Feature
Using the patterns, conventions, and integration points found, produce a concrete implementation plan.
Plan structure:
Feature: [name]
Analog: [closest existing feature] in [file]
New files:
- src/<path>/<feature>.py — [purpose]
- tests/unit/<path>/test_<feature>.py — [what it tests]
Modified files:
- <file> — [what changes: add import, register command, wire route]
- <file> — [what changes]
Implementation order:
1. Create <new-file> with <core-logic> (modeled on <analog>)
2. Add tests in <test-file> (modeled on <analog-tests>)
3. Wire into <registration-file>
4. Update <config/docs> if needed
Key decisions:
- [Decision 1: e.g., "Use existing FooService rather than creating new one"]
- [Decision 2: e.g., "Follow handler autodiscovery pattern — no manual registration needed"]
Present to user: "Here's the plan based on existing patterns. Ready to implement, or want to adjust?"
Handle adjustments: If user wants changes, update the plan and re-present.
Step 4: Implement with Pattern Matching
For each step in the plan, implement by closely following the analog.
For each file to create/modify:
-
Show the analog code:
search_code(
query="<analog-function>",
symbol_name="<analog-function>",
use_hybrid_search=True,
smart_context=True
)
"Here's the existing pattern I'm following from <analog-file>:"
-
Write the new code modeled on the analog. Match:
- Function signatures and return types
- Error handling patterns
- Logging conventions
- Docstring format
- Import style
-
Show the change and request confirmation:
New file: <path>
Based on: <analog-file>
[code]
Proceed? (yes/no/adjust)
-
After creating the core code, write tests:
- Follow the analog's test pattern exactly
- Same fixture usage, same assertion style
- Cover the same categories: happy path, error cases, edge cases
-
Wire the feature in:
- Add to registration mechanism
- Update imports
- Add to any manifests, configs, or init.py exports
Step 5: Verify Integration
After all code is written, verify the feature integrates correctly.
Run the tests:
[project-specific test command for the new test file]
Check for import/lint issues:
[project-specific lint command]
Verify registration:
If the feature should appear in help text, command lists, or API docs, check that it does.
Present results:
Feature implementation complete!
Created:
- <new-file-1> — [description]
- <new-file-2> — [description]
Modified:
- <file> — [what changed]
Tests: [PASS/FAIL]
Lint: [PASS/FAIL]
Based on patterns from: <analog-feature>
Recommended next steps:
1. Try it out: [example usage command]
2. Run full test suite: [command]
3. Commit: git add <files> && git commit -m "feat: <description>"
If tests fail:
- STOP and show failures
- Search for how the analog handles the same scenario
- Fix and re-run
- Ask user before proceeding if fix is non-obvious
Key Design Principles
Pattern-first development. Always find the analog before writing code. The codebase IS the specification for how new code should look.
Reuse, don't reinvent. Before creating any utility, helper, or abstraction, search for existing ones. Duplicating functionality creates maintenance burden.
Match conventions exactly. If existing functions use snake_case, don't introduce camelCase. Consistency matters more than personal preference.
Test analog first. Find how the closest feature is tested before writing your own tests. Match the coverage level, fixture pattern, and assertion style.
Wire last. Create the core code and tests first. Registration and wiring are the final step.
For common search tips (hybrid search, smart_context, symbol filtering), see skills/README.md.
For installation instructions, see skills/README.md.