بنقرة واحدة
scaffold-extractor
Generates boilerplate for a new Woods extractor and its spec
التثبيت باستخدام Codex أو Claude انسخ هذا Prompt والصقه في Codex أو Claude أو مساعد آخر ليراجع صفحة Skill ويثبّتها لك.
القائمة
Generates boilerplate for a new Woods extractor and its spec
التثبيت باستخدام Codex أو Claude انسخ هذا Prompt والصقه في Codex أو Claude أو مساعد آخر ليراجع صفحة Skill ويثبّتها لك.
استنادا إلى تصنيف SOC المهني
Systematic troubleshooting for Woods — diagnose extraction, MCP, embedding, and storage issues
Generate correct .mcp.json configuration for Woods in any environment
Guide through Woods initial setup — install, configure, extract, verify, and connect MCP servers
Setup, operation, and debugging of the Unblocked integration — collection creation, incremental sync, the sync manifest, escape hatches, live API quirks, and smoke testing. Use when working on lib/woods/unblocked/, the woods:unblocked_sync rake task, sync CI wiring, or when a sync misbehaves (everything re-pushes, deletes refused, budget exhausted, 400s from the API).
Woods backlog workflow — picking items, implementing with TDD, marking resolved, and adding new work. Use when the user asks "what's next?", references backlog items, opens a session without a clear task, or when closing out work to record what shipped.
Design rules and reference patterns for building MCP servers in Woods
| name | scaffold-extractor |
| description | Generates boilerplate for a new Woods extractor and its spec |
| argument-hint | [extractor-name] |
| allowed-tools | Read, Write, Glob, Grep |
Generate a new extractor and its RSpec spec following project conventions.
/scaffold-extractor [name] where [name] is the extractor type (e.g., query_object, graphql_type).
If $ARGUMENTS is empty, ask the user what to name the extractor.
.claude/rules/extractors.md for the interface contract.lib/woods/extractors/*_extractor.rb to ensure the name isn't taken.lib/woods/extractors/$ARGUMENTS_extractor.rb using the template below.spec/extractors/$ARGUMENTS_extractor_spec.rb using the spec template below.extractor.rb, add to CLAUDE.md if needed).Scanning route helpers? If the extractor will scan templates, views, mailers, or any source that uses
_path/_urlhelpers, alsorequire_relative 'route_helper_resolver', includeRouteHelperResolverbelowSharedDependencyScanner, callbuild_route_helper_mapininitialize, and callscan_navigation_dependencies(source)/scan_form_dependencies(source)insideextract_dependencies. Leaving theincludewithout the call is dead wiring — see.claude/rules/extractors.md.
# frozen_string_literal: true
require_relative 'shared_utility_methods'
require_relative 'shared_dependency_scanner'
# RouteHelperResolver wiring is a three-step commitment — if you uncomment
# the require below you MUST also uncomment the include AND the
# build_route_helper_map call in initialize AND call scan_navigation_dependencies
# (and/or scan_form_dependencies) inside extract_dependencies. Any one of
# the three missing turns the others into dead code. See
# .claude/rules/extractors.md for the canonical navigation-edge pattern.
# require_relative 'route_helper_resolver' # uncomment if scanning nav/form helpers
module Woods
module Extractors
# {Name}Extractor extracts {description} from Rails applications.
#
# @example
# extractor = {Name}Extractor.new
# units = extractor.extract_all
#
class {Name}Extractor
include SharedUtilityMethods
include SharedDependencyScanner
# include RouteHelperResolver # uncomment if scanning nav/form helpers — REMEMBER build_route_helper_map + scan_navigation_dependencies
APP_DIRECTORIES = %w[app].freeze
def initialize
@directories = APP_DIRECTORIES.map { |d| Rails.root.join(d) }.select(&:directory?)
# build_route_helper_map # REQUIRED if you included RouteHelperResolver — do not skip
end
# Extract all {type} units.
#
# @return [Array<ExtractedUnit>] extracted units
def extract_all
return [] if @directories.empty?
units = []
@directories.each do |dir|
Dir.glob(dir.join('**/*.rb')).sort.each do |file_path|
result = extract_{type}_file(file_path)
units << result if result
end
end
units
end
# Extract a single {type} from a file.
#
# @param file_path [String] absolute path to the Ruby file
# @return [ExtractedUnit, nil] extracted unit or nil if not a {type}
def extract_{type}_file(file_path)
source = File.read(file_path)
relative = relative_path(file_path)
# TODO: implement extraction logic
nil
end
private
# TODO: add private helper methods
end
end
end
# frozen_string_literal: true
require 'spec_helper'
require 'set'
require 'tmpdir'
require 'fileutils'
require 'active_support/core_ext/object/blank'
require 'woods/model_name_cache'
require 'woods/extractors/shared_utility_methods'
require 'woods/extractors/shared_dependency_scanner'
require 'woods/extractors/{type}_extractor'
RSpec.describe Woods::Extractors::{Name}Extractor do
include_context 'extractor setup'
describe '#initialize' do
it 'handles missing app directory gracefully' do
extractor = described_class.new
expect(extractor.extract_all).to eq([])
end
end
describe '#extract_all' do
it 'returns empty array for files without {type} patterns' do
create_file('app/models/user.rb', <<~RUBY)
class User < ApplicationRecord
end
RUBY
units = described_class.new.extract_all
expect(units).to eq([])
end
# TODO: add happy path and edge case tests
end
end
{Name} = PascalCase of the argument (e.g., query_object -> QueryObject){type} = snake_case of the argument (e.g., query_object){description} = ask the user for a one-line description, or infer from the nameextractor.rb — that requires understanding the dispatch type (CLASS_BASED vs FILE_BASED). Tell the user to do this manually.