Compare commits

...
Author SHA1 Message Date
Tabish Bidiwale 8c6edf94a4 refactor: make title extraction case-insensitive for "Change:"
Makes the extractTitle regex case-insensitive to handle variations like
"# change:" or "# CHANGE:" in addition to "# Change:".
2025-10-25 12:45:18 +11:00
Tabish Bidiwale f54d49317b Merge branch 'main' into fix-untitled-change-fallback 2025-10-24 15:53:44 +11:00
Tabish Bidiwale 68d3481c40 fix: use change-id as fallback title instead of "Untitled Change"
Fixes #225 by addressing mismatch between proposal template and title extraction:

- Updated proposal template to include `# Change: [description]` header
- Changed extractTitle fallback from "Untitled Change" to change-id
- Updated all extractTitle call sites to pass changeName parameter

This ensures both new and existing proposals display meaningful titles.
2025-10-24 15:49:58 +11:00
2 changed files with 13 additions and 11 deletions
+11 -11
View File
@@ -28,7 +28,7 @@ export class ChangeCommand {
*/
async show(changeName?: string, options?: { json?: boolean; requirementsOnly?: boolean; deltasOnly?: boolean; noInteractive?: boolean }): Promise<void> {
const changesPath = path.join(process.cwd(), 'openspec', 'changes');
if (!changeName) {
const canPrompt = isInteractive(options?.noInteractive);
const changes = await this.getActiveChanges(changesPath);
@@ -49,25 +49,25 @@ export class ChangeCommand {
return;
}
}
const proposalPath = path.join(changesPath, changeName, 'proposal.md');
try {
await fs.access(proposalPath);
} catch {
throw new Error(`Change "${changeName}" not found at ${proposalPath}`);
}
if (options?.json) {
const jsonOutput = await this.converter.convertChangeToJson(proposalPath);
if (options.requirementsOnly) {
console.error('Flag --requirements-only is deprecated; use --deltas-only instead.');
}
const parsed: Change = JSON.parse(jsonOutput);
const contentForTitle = await fs.readFile(proposalPath, 'utf-8');
const title = this.extractTitle(contentForTitle);
const title = this.extractTitle(contentForTitle, changeName);
const id = parsed.name;
const deltas = parsed.deltas || [];
@@ -124,7 +124,7 @@ export class ChangeCommand {
return {
id: changeName,
title: this.extractTitle(content),
title: this.extractTitle(content, changeName),
deltaCount: change.deltas.length,
taskStatus,
};
@@ -159,7 +159,7 @@ export class ChangeCommand {
const tasksPath = path.join(changesPath, changeName, 'tasks.md');
try {
const content = await fs.readFile(proposalPath, 'utf-8');
const title = this.extractTitle(content);
const title = this.extractTitle(content, changeName);
let taskStatusText = '';
try {
const tasksContent = await fs.readFile(tasksPath, 'utf-8');
@@ -258,9 +258,9 @@ export class ChangeCommand {
}
}
private extractTitle(content: string): string {
const match = content.match(/^#\s+(?:Change:\s+)?(.+)$/m);
return match ? match[1].trim() : 'Untitled Change';
private extractTitle(content: string, changeName: string): string {
const match = content.match(/^#\s+(?:Change:\s+)?(.+)$/im);
return match ? match[1].trim() : changeName;
}
private countTasks(content: string): { total: number; completed: number } {
+2
View File
@@ -160,6 +160,8 @@ New request?
2. **Write proposal.md:**
\`\`\`markdown
# Change: [Brief description of change]
## Why
[1-2 sentences on problem/opportunity]