Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 15 additions & 15 deletions openspec/changes/add-agent-schema-selection/tasks.md
Original file line number Diff line number Diff line change
@@ -1,32 +1,32 @@
## Prerequisites

- [ ] 0.1 Implement `add-per-change-schema-metadata` change first
- [x] 0.1 Implement `add-per-change-schema-metadata` change first

## 1. Schema Discovery

- [ ] 1.1 Add CLI command or helper to list schemas with descriptions (for agent use)
- [ ] 1.2 Ensure `openspec templates --schema <name>` returns artifact list for any schema
- [x] 1.1 Add CLI command or helper to list schemas with descriptions (for agent use)
- [x] 1.2 Ensure `openspec templates --schema <name>` returns artifact list for any schema

## 2. Update New Change Skill

- [ ] 2.1 Add schema selection prompt using AskUserQuestion tool
- [ ] 2.2 Present available schemas with descriptions (spec-driven, tdd, etc.)
- [ ] 2.3 Pass selected schema to `openspec new change --schema <name>`
- [ ] 2.4 Update output to show which schema/workflow was selected
- [x] 2.1 Add schema selection prompt using AskUserQuestion tool
- [x] 2.2 Present available schemas with descriptions (spec-driven, tdd, etc.)
- [x] 2.3 Pass selected schema to `openspec new change --schema <name>`
- [x] 2.4 Update output to show which schema/workflow was selected

## 3. Update Continue Change Skill

- [ ] 3.1 Remove hardcoded artifact references (proposal, specs, design, tasks)
- [ ] 3.2 Read artifact list dynamically from `openspec status --json`
- [ ] 3.3 Adjust artifact creation guidelines to be schema-agnostic
- [ ] 3.4 Handle schema-specific artifact types (e.g., TDD's `tests` artifact)
- [x] 3.1 Remove hardcoded artifact references (proposal, specs, design, tasks)
- [x] 3.2 Read artifact list dynamically from `openspec status --json`
- [x] 3.3 Adjust artifact creation guidelines to be schema-agnostic
- [x] 3.4 Handle schema-specific artifact types (e.g., TDD's `tests` artifact)

## 4. Update Apply Change Skill

- [ ] 4.1 Make task detection work with different schema structures
- [ ] 4.2 Adjust context file reading for schema-specific artifacts
- [x] 4.1 Make task detection work with different schema structures
- [x] 4.2 Adjust context file reading for schema-specific artifacts

## 5. Documentation

- [ ] 5.1 Add schema descriptions to help text or skill instructions
- [ ] 5.2 Document when to use each schema (TDD for bug fixes, spec-driven for features, etc.)
- [x] 5.1 Add schema descriptions to help text or skill instructions
- [x] 5.2 Document when to use each schema (TDD for bug fixes, spec-driven for features, etc.)
45 changes: 45 additions & 0 deletions src/commands/artifact-workflow.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,13 @@ import {
formatChangeStatus,
generateInstructions,
listSchemas,
listSchemasWithInfo,
getSchemaDir,
resolveSchema,
ArtifactGraph,
type ChangeStatus,
type ArtifactInstructions,
type SchemaInfo,
} from '../core/artifact-graph/index.js';
import { createChange, validateChangeName } from '../utils/change-utils.js';
import { getNewChangeSkillTemplate, getContinueChangeSkillTemplate, getApplyChangeSkillTemplate, getOpsxNewCommandTemplate, getOpsxContinueCommandTemplate, getOpsxApplyCommandTemplate } from '../core/templates/skill-templates.js';
Expand Down Expand Up @@ -820,6 +822,34 @@ ${template.content}
}
}

// -----------------------------------------------------------------------------
// Schemas Command
// -----------------------------------------------------------------------------

interface SchemasOptions {
json?: boolean;
}

async function schemasCommand(options: SchemasOptions): Promise<void> {
const schemas = listSchemasWithInfo();

if (options.json) {
console.log(JSON.stringify(schemas, null, 2));
return;
}

console.log('Available schemas:');
console.log();

for (const schema of schemas) {
const sourceLabel = schema.source === 'user' ? chalk.dim(' (user override)') : '';
console.log(` ${chalk.bold(schema.name)}${sourceLabel}`);
console.log(` ${schema.description}`);
console.log(` Artifacts: ${schema.artifacts.join(' → ')}`);
console.log();
}
}

// -----------------------------------------------------------------------------
// Command Registration
// -----------------------------------------------------------------------------
Expand Down Expand Up @@ -884,6 +914,21 @@ export function registerArtifactWorkflowCommands(program: Command): void {
}
});

// Schemas command
program
.command('schemas')
.description('[Experimental] List available workflow schemas with descriptions')
.option('--json', 'Output as JSON (for agent use)')
.action(async (options: SchemasOptions) => {
try {
await schemasCommand(options);
} catch (error) {
console.log();
ora().fail(`Error: ${(error as Error).message}`);
process.exit(1);
}
});

// New command group with change subcommand
const newCmd = program.command('new').description('[Experimental] Create new items');

Expand Down
2 changes: 2 additions & 0 deletions src/core/artifact-graph/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,12 @@ export { detectCompleted } from './state.js';
export {
resolveSchema,
listSchemas,
listSchemasWithInfo,
getSchemaDir,
getPackageSchemasDir,
getUserSchemasDir,
SchemaLoadError,
type SchemaInfo,
} from './resolver.js';

// Instruction loading
Expand Down
68 changes: 68 additions & 0 deletions src/core/artifact-graph/resolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -156,3 +156,71 @@ export function listSchemas(): string[] {

return Array.from(schemas).sort();
}

/**
* Schema info with metadata (name, description, artifacts).
*/
export interface SchemaInfo {
name: string;
description: string;
artifacts: string[];
source: 'package' | 'user';
}

/**
* Lists all available schemas with their descriptions and artifact lists.
* Useful for agent skills to present schema selection to users.
*/
export function listSchemasWithInfo(): SchemaInfo[] {
const schemas: SchemaInfo[] = [];
const seenNames = new Set<string>();

// Add user override schemas first (they take precedence)
const userDir = getUserSchemasDir();
if (fs.existsSync(userDir)) {
for (const entry of fs.readdirSync(userDir, { withFileTypes: true })) {
if (entry.isDirectory()) {
const schemaPath = path.join(userDir, entry.name, 'schema.yaml');
if (fs.existsSync(schemaPath)) {
try {
const schema = parseSchema(fs.readFileSync(schemaPath, 'utf-8'));
schemas.push({
name: entry.name,
description: schema.description || '',
artifacts: schema.artifacts.map((a) => a.id),
source: 'user',
});
seenNames.add(entry.name);
} catch {
// Skip invalid schemas
}
}
}
}
}

// Add package built-in schemas (if not overridden)
const packageDir = getPackageSchemasDir();
if (fs.existsSync(packageDir)) {
for (const entry of fs.readdirSync(packageDir, { withFileTypes: true })) {
if (entry.isDirectory() && !seenNames.has(entry.name)) {
const schemaPath = path.join(packageDir, entry.name, 'schema.yaml');
if (fs.existsSync(schemaPath)) {
try {
const schema = parseSchema(fs.readFileSync(schemaPath, 'utf-8'));
schemas.push({
name: entry.name,
description: schema.description || '',
artifacts: schema.artifacts.map((a) => a.id),
source: 'package',
});
} catch {
// Skip invalid schemas
}
}
}
}
}

return schemas.sort((a, b) => a.name.localeCompare(b.name));
}
Loading
Loading