@seanmars/tospec 0.19.0-beta.6 → 0.19.0-beta.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +137 -0
- package/assets/dashboard/app.js +3 -3
- package/assets/rules/tospec/decision.md +3 -0
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +205 -36
- package/dist/cli/index.js.map +1 -1
- package/dist/commands/config.d.ts.map +1 -1
- package/dist/commands/config.js +151 -23
- package/dist/commands/config.js.map +1 -1
- package/dist/commands/dashboard.d.ts +34 -9
- package/dist/commands/dashboard.d.ts.map +1 -1
- package/dist/commands/dashboard.js +213 -21
- package/dist/commands/dashboard.js.map +1 -1
- package/dist/commands/decision.d.ts +11 -0
- package/dist/commands/decision.d.ts.map +1 -1
- package/dist/commands/decision.js +182 -20
- package/dist/commands/decision.js.map +1 -1
- package/dist/commands/shared-output.d.ts +12 -0
- package/dist/commands/shared-output.d.ts.map +1 -1
- package/dist/commands/shared-output.js +50 -1
- package/dist/commands/shared-output.js.map +1 -1
- package/dist/commands/show.d.ts +10 -0
- package/dist/commands/show.d.ts.map +1 -1
- package/dist/commands/show.js +23 -4
- package/dist/commands/show.js.map +1 -1
- package/dist/commands/validate.d.ts +41 -1
- package/dist/commands/validate.d.ts.map +1 -1
- package/dist/commands/validate.js +95 -17
- package/dist/commands/validate.js.map +1 -1
- package/dist/commands/workflow/index.d.ts +5 -5
- package/dist/commands/workflow/index.d.ts.map +1 -1
- package/dist/commands/workflow/index.js +5 -5
- package/dist/commands/workflow/index.js.map +1 -1
- package/dist/commands/workflow/instructions.d.ts +23 -0
- package/dist/commands/workflow/instructions.d.ts.map +1 -1
- package/dist/commands/workflow/instructions.js +84 -16
- package/dist/commands/workflow/instructions.js.map +1 -1
- package/dist/commands/workflow/new-change.d.ts +2 -0
- package/dist/commands/workflow/new-change.d.ts.map +1 -1
- package/dist/commands/workflow/new-change.js +32 -4
- package/dist/commands/workflow/new-change.js.map +1 -1
- package/dist/commands/workflow/schemas.d.ts +2 -0
- package/dist/commands/workflow/schemas.d.ts.map +1 -1
- package/dist/commands/workflow/schemas.js +38 -1
- package/dist/commands/workflow/schemas.js.map +1 -1
- package/dist/commands/workflow/shared.d.ts +34 -2
- package/dist/commands/workflow/shared.d.ts.map +1 -1
- package/dist/commands/workflow/shared.js +37 -3
- package/dist/commands/workflow/shared.js.map +1 -1
- package/dist/commands/workflow/status.d.ts +14 -0
- package/dist/commands/workflow/status.d.ts.map +1 -1
- package/dist/commands/workflow/status.js +65 -8
- package/dist/commands/workflow/status.js.map +1 -1
- package/dist/commands/workflow/templates.d.ts +7 -0
- package/dist/commands/workflow/templates.d.ts.map +1 -1
- package/dist/commands/workflow/templates.js +13 -0
- package/dist/commands/workflow/templates.js.map +1 -1
- package/dist/core/archive.d.ts +14 -0
- package/dist/core/archive.d.ts.map +1 -1
- package/dist/core/archive.js +169 -18
- package/dist/core/archive.js.map +1 -1
- package/dist/core/artifact-graph/graph.d.ts +13 -3
- package/dist/core/artifact-graph/graph.d.ts.map +1 -1
- package/dist/core/artifact-graph/graph.js +16 -6
- package/dist/core/artifact-graph/graph.js.map +1 -1
- package/dist/core/artifact-graph/index.d.ts +1 -1
- package/dist/core/artifact-graph/index.d.ts.map +1 -1
- package/dist/core/artifact-graph/index.js +1 -1
- package/dist/core/artifact-graph/index.js.map +1 -1
- package/dist/core/artifact-graph/instruction-loader.d.ts +74 -3
- package/dist/core/artifact-graph/instruction-loader.d.ts.map +1 -1
- package/dist/core/artifact-graph/instruction-loader.js +79 -31
- package/dist/core/artifact-graph/instruction-loader.js.map +1 -1
- package/dist/core/artifact-graph/resolver.d.ts +26 -0
- package/dist/core/artifact-graph/resolver.d.ts.map +1 -1
- package/dist/core/artifact-graph/resolver.js +44 -4
- package/dist/core/artifact-graph/resolver.js.map +1 -1
- package/dist/core/artifact-graph/stub-detection.d.ts +20 -0
- package/dist/core/artifact-graph/stub-detection.d.ts.map +1 -0
- package/dist/core/artifact-graph/stub-detection.js +42 -0
- package/dist/core/artifact-graph/stub-detection.js.map +1 -0
- package/dist/core/change-presenter.d.ts +16 -0
- package/dist/core/change-presenter.d.ts.map +1 -1
- package/dist/core/change-presenter.js +68 -9
- package/dist/core/change-presenter.js.map +1 -1
- package/dist/core/change-status-policy.d.ts +12 -1
- package/dist/core/change-status-policy.d.ts.map +1 -1
- package/dist/core/change-status-policy.js +33 -1
- package/dist/core/change-status-policy.js.map +1 -1
- package/dist/core/codex-residue.js +1 -1
- package/dist/core/config-schema.d.ts +1 -12
- package/dist/core/config-schema.d.ts.map +1 -1
- package/dist/core/config-schema.js +27 -1
- package/dist/core/config-schema.js.map +1 -1
- package/dist/core/config.d.ts +37 -0
- package/dist/core/config.d.ts.map +1 -1
- package/dist/core/config.js +37 -0
- package/dist/core/config.js.map +1 -1
- package/dist/core/dashboard-activity.js +2 -2
- package/dist/core/dashboard-activity.js.map +1 -1
- package/dist/core/dashboard-data.d.ts +5 -0
- package/dist/core/dashboard-data.d.ts.map +1 -1
- package/dist/core/dashboard-data.js +15 -8
- package/dist/core/dashboard-data.js.map +1 -1
- package/dist/core/init.d.ts +22 -0
- package/dist/core/init.d.ts.map +1 -1
- package/dist/core/init.js +107 -45
- package/dist/core/init.js.map +1 -1
- package/dist/core/list.d.ts +1 -1
- package/dist/core/list.d.ts.map +1 -1
- package/dist/core/list.js +68 -17
- package/dist/core/list.js.map +1 -1
- package/dist/core/markdown-render.d.ts +33 -0
- package/dist/core/markdown-render.d.ts.map +1 -0
- package/dist/core/markdown-render.js +103 -0
- package/dist/core/markdown-render.js.map +1 -0
- package/dist/core/migrate.d.ts +20 -6
- package/dist/core/migrate.d.ts.map +1 -1
- package/dist/core/migrate.js +102 -6
- package/dist/core/migrate.js.map +1 -1
- package/dist/core/parsers/change-parser.js +1 -1
- package/dist/core/parsers/change-parser.js.map +1 -1
- package/dist/core/parsers/markdown-parser.d.ts.map +1 -1
- package/dist/core/parsers/markdown-parser.js +16 -0
- package/dist/core/parsers/markdown-parser.js.map +1 -1
- package/dist/core/parsers/requirement-blocks.js +32 -9
- package/dist/core/parsers/requirement-blocks.js.map +1 -1
- package/dist/core/root-selection.d.ts +11 -1
- package/dist/core/root-selection.d.ts.map +1 -1
- package/dist/core/root-selection.js +4 -2
- package/dist/core/root-selection.js.map +1 -1
- package/dist/core/rules.d.ts +6 -1
- package/dist/core/rules.d.ts.map +1 -1
- package/dist/core/rules.js +24 -2
- package/dist/core/rules.js.map +1 -1
- package/dist/core/schemas/base.schema.d.ts +3 -0
- package/dist/core/schemas/base.schema.d.ts.map +1 -1
- package/dist/core/schemas/base.schema.js +22 -0
- package/dist/core/schemas/base.schema.js.map +1 -1
- package/dist/core/schemas/change.schema.d.ts +8 -0
- package/dist/core/schemas/change.schema.d.ts.map +1 -1
- package/dist/core/schemas/spec.schema.d.ts +2 -0
- package/dist/core/schemas/spec.schema.d.ts.map +1 -1
- package/dist/core/shared/index.d.ts +1 -1
- package/dist/core/shared/index.d.ts.map +1 -1
- package/dist/core/shared/index.js +1 -1
- package/dist/core/shared/index.js.map +1 -1
- package/dist/core/shared/rules-generation.d.ts +27 -6
- package/dist/core/shared/rules-generation.d.ts.map +1 -1
- package/dist/core/shared/rules-generation.js +115 -44
- package/dist/core/shared/rules-generation.js.map +1 -1
- package/dist/core/shared/tool-detection.d.ts +19 -0
- package/dist/core/shared/tool-detection.d.ts.map +1 -1
- package/dist/core/shared/tool-detection.js +30 -2
- package/dist/core/shared/tool-detection.js.map +1 -1
- package/dist/core/spec-presenter.d.ts.map +1 -1
- package/dist/core/spec-presenter.js +5 -0
- package/dist/core/spec-presenter.js.map +1 -1
- package/dist/core/specs-apply.d.ts +15 -1
- package/dist/core/specs-apply.d.ts.map +1 -1
- package/dist/core/specs-apply.js +33 -11
- package/dist/core/specs-apply.js.map +1 -1
- package/dist/core/templates/workflows/apply.js +1 -1
- package/dist/core/templates/workflows/apply.js.map +1 -1
- package/dist/core/templates/workflows/archive.d.ts.map +1 -1
- package/dist/core/templates/workflows/archive.js +4 -2
- package/dist/core/templates/workflows/archive.js.map +1 -1
- package/dist/core/templates/workflows/decision.js +1 -1
- package/dist/core/templates/workflows/issue.d.ts.map +1 -1
- package/dist/core/templates/workflows/issue.js +3 -0
- package/dist/core/templates/workflows/issue.js.map +1 -1
- package/dist/core/update.d.ts +26 -0
- package/dist/core/update.d.ts.map +1 -1
- package/dist/core/update.js +111 -23
- package/dist/core/update.js.map +1 -1
- package/dist/core/validation/constants.d.ts +1 -1
- package/dist/core/validation/constants.d.ts.map +1 -1
- package/dist/core/validation/constants.js +6 -1
- package/dist/core/validation/constants.js.map +1 -1
- package/dist/core/validation/section-validator.d.ts.map +1 -1
- package/dist/core/validation/section-validator.js +21 -3
- package/dist/core/validation/section-validator.js.map +1 -1
- package/dist/core/validation/validator.d.ts +17 -4
- package/dist/core/validation/validator.d.ts.map +1 -1
- package/dist/core/validation/validator.js +120 -34
- package/dist/core/validation/validator.js.map +1 -1
- package/dist/utils/change-utils.d.ts +31 -2
- package/dist/utils/change-utils.d.ts.map +1 -1
- package/dist/utils/change-utils.js +127 -37
- package/dist/utils/change-utils.js.map +1 -1
- package/dist/utils/file-system.d.ts +1 -1
- package/dist/utils/file-system.js +1 -1
- package/dist/utils/item-discovery.d.ts +20 -5
- package/dist/utils/item-discovery.d.ts.map +1 -1
- package/dist/utils/item-discovery.js +28 -9
- package/dist/utils/item-discovery.js.map +1 -1
- package/dist/utils/spec-files.d.ts +12 -0
- package/dist/utils/spec-files.d.ts.map +1 -1
- package/dist/utils/spec-files.js +34 -0
- package/dist/utils/spec-files.js.map +1 -1
- package/dist/utils/task-progress.js +1 -1
- package/package.json +1 -1
- package/schemas/decision/templates/decision.md +3 -1
- package/schemas/decision/templates/index.md +2 -2
- package/schemas/issue/schema.yaml +4 -2
- package/schemas/issue/templates/spec.md +20 -2
- package/schemas/sdd/schema.yaml +4 -0
- package/schemas/sdd/templates/spec.md +20 -2
- /package/assets/rules/tospec/{single-sourc-of-truth.md → single-source-of-truth.md} +0 -0
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import path from 'path';
|
|
2
|
+
import { promises as fsPromises } from 'fs';
|
|
2
3
|
import { FileSystemUtils } from './file-system.js';
|
|
3
4
|
import { formatTimestamp, formatLocalDate } from './timestamp.js';
|
|
4
5
|
import { writeChangeMetadata, validateSchemaName } from './change-metadata.js';
|
|
5
6
|
import { readProjectConfig, resolveConfigFileInDir } from '../core/project-config.js';
|
|
6
7
|
import { isKebabId } from '../core/id.js';
|
|
7
8
|
import { resolveSchema, loadTemplate } from '../core/artifact-graph/index.js';
|
|
9
|
+
import { findTicketPath } from './item-discovery.js';
|
|
8
10
|
const DEFAULT_SCHEMA = 'sdd';
|
|
9
11
|
// sdd → requirement, issue → issue; custom schemas leave type unset.
|
|
10
12
|
const SCHEMA_TO_CHANGE_TYPE = {
|
|
@@ -24,7 +26,8 @@ const SCHEMA_TO_CHANGE_TYPE = {
|
|
|
24
26
|
* A leading digit is allowed so ordering conventions like `100-add-feature` or
|
|
25
27
|
* `00001-add-auth` work.
|
|
26
28
|
*
|
|
27
|
-
* @param name - The
|
|
29
|
+
* @param name - The kebab-case identifier to validate
|
|
30
|
+
* @param noun - The user-facing name for the identifier in validation errors
|
|
28
31
|
* @returns Validation result with `valid: true` or `valid: false` with an error message
|
|
29
32
|
*
|
|
30
33
|
* @example
|
|
@@ -32,34 +35,59 @@ const SCHEMA_TO_CHANGE_TYPE = {
|
|
|
32
35
|
* validateChangeName('100-add-feature') // { valid: true }
|
|
33
36
|
* validateChangeName('Add-Auth') // { valid: false, error: '...' }
|
|
34
37
|
*/
|
|
35
|
-
|
|
38
|
+
/**
|
|
39
|
+
* Upper bound on a change name, in characters.
|
|
40
|
+
*
|
|
41
|
+
* Deliberately generous: a kebab-case summary of a change runs well under this,
|
|
42
|
+
* so the limit only catches input that was never a name. See the check in
|
|
43
|
+
* `validateChangeName` for why it exists at all.
|
|
44
|
+
*/
|
|
45
|
+
export const MAX_CHANGE_NAME_LENGTH = 100;
|
|
46
|
+
export function validateChangeName(name, noun = 'Change name') {
|
|
36
47
|
if (!name) {
|
|
37
|
-
return { valid: false, error:
|
|
48
|
+
return { valid: false, error: `${noun} cannot be empty` };
|
|
38
49
|
}
|
|
39
50
|
if (!isKebabId(name)) {
|
|
40
51
|
// Provide specific error messages for common mistakes
|
|
41
52
|
if (/[A-Z]/.test(name)) {
|
|
42
|
-
return { valid: false, error:
|
|
53
|
+
return { valid: false, error: `${noun} must be lowercase (use kebab-case)` };
|
|
43
54
|
}
|
|
44
55
|
if (/\s/.test(name)) {
|
|
45
|
-
return { valid: false, error:
|
|
56
|
+
return { valid: false, error: `${noun} cannot contain spaces (use hyphens instead)` };
|
|
46
57
|
}
|
|
47
58
|
if (/_/.test(name)) {
|
|
48
|
-
return { valid: false, error:
|
|
59
|
+
return { valid: false, error: `${noun} cannot contain underscores (use hyphens instead)` };
|
|
49
60
|
}
|
|
50
61
|
if (name.startsWith('-')) {
|
|
51
|
-
return { valid: false, error:
|
|
62
|
+
return { valid: false, error: `${noun} cannot start with a hyphen` };
|
|
52
63
|
}
|
|
53
64
|
if (name.endsWith('-')) {
|
|
54
|
-
return { valid: false, error:
|
|
65
|
+
return { valid: false, error: `${noun} cannot end with a hyphen` };
|
|
55
66
|
}
|
|
56
67
|
if (/--/.test(name)) {
|
|
57
|
-
return { valid: false, error:
|
|
68
|
+
return { valid: false, error: `${noun} cannot contain consecutive hyphens` };
|
|
58
69
|
}
|
|
59
70
|
if (/[^a-z0-9-]/.test(name)) {
|
|
60
|
-
return { valid: false, error:
|
|
71
|
+
return { valid: false, error: `${noun} can only contain lowercase letters, numbers, and hyphens` };
|
|
61
72
|
}
|
|
62
|
-
return { valid: false, error:
|
|
73
|
+
return { valid: false, error: `${noun} must follow kebab-case convention (e.g., add-auth, refactor-db)` };
|
|
74
|
+
}
|
|
75
|
+
// Checked last so a malformed name still gets the specific message above.
|
|
76
|
+
//
|
|
77
|
+
// The name becomes a directory under tospec/changes/ and, on archive, the
|
|
78
|
+
// longer `<yyyyMMdd_HHmmss>-<name>` under tospec/changes/archive/ — about 38
|
|
79
|
+
// characters of path before the name itself. Without a limit the overflow
|
|
80
|
+
// surfaced as a raw ENOENT from whichever write happened to cross the
|
|
81
|
+
// platform's path limit first, after the change directory was already on disk.
|
|
82
|
+
// No cap can guarantee the write succeeds (the project root's own length is
|
|
83
|
+
// unknown here), so this is a legibility bound, not a correctness one: it
|
|
84
|
+
// rejects the names that cannot plausibly be intended, with a message that
|
|
85
|
+
// names the problem.
|
|
86
|
+
if (name.length > MAX_CHANGE_NAME_LENGTH) {
|
|
87
|
+
return {
|
|
88
|
+
valid: false,
|
|
89
|
+
error: `${noun} is ${name.length} characters; the maximum is ${MAX_CHANGE_NAME_LENGTH}`,
|
|
90
|
+
};
|
|
63
91
|
}
|
|
64
92
|
return { valid: true };
|
|
65
93
|
}
|
|
@@ -85,6 +113,30 @@ export function validateChangeName(name) {
|
|
|
85
113
|
* const result = await createChange('/path/to/project', 'add-auth', { schema: 'my-workflow' })
|
|
86
114
|
* console.log(result.schema) // 'my-workflow'
|
|
87
115
|
*/
|
|
116
|
+
/**
|
|
117
|
+
* Bring a tospec root up to the full layout every command expects.
|
|
118
|
+
*
|
|
119
|
+
* Any command that writes into `tospec/` may be the one that creates it — from
|
|
120
|
+
* an implicit root, or a config-only clone. A command that creates only the one
|
|
121
|
+
* subdirectory it needs leaves a half-root: the directory then resolves as a
|
|
122
|
+
* project for every other command, so `validate --all` and `status --all` start
|
|
123
|
+
* answering "0 items, all fine" about somewhere that was never a project. That
|
|
124
|
+
* vacuous pass is the reason those commands refuse an implicit root in the first
|
|
125
|
+
* place, so creating one by halves hands it back through the side door.
|
|
126
|
+
*
|
|
127
|
+
* Shared rather than inlined because `new change` and `decision new` are both
|
|
128
|
+
* such commands, and only one of them used to do this.
|
|
129
|
+
*/
|
|
130
|
+
export async function completeRootStructure(projectRoot, defaultSchema) {
|
|
131
|
+
const tospecDir = path.join(projectRoot, 'tospec');
|
|
132
|
+
await FileSystemUtils.createDirectory(path.join(tospecDir, 'specs'));
|
|
133
|
+
await FileSystemUtils.createDirectory(path.join(tospecDir, 'changes', 'archive'));
|
|
134
|
+
// The config records the PROJECT default schema, never a one-off override,
|
|
135
|
+
// and is written only when none exists.
|
|
136
|
+
if (!resolveConfigFileInDir(tospecDir)) {
|
|
137
|
+
await FileSystemUtils.writeFile(path.join(tospecDir, 'config.yaml'), `schema: ${defaultSchema}\n`);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
88
140
|
export async function createChange(projectRoot, name, options = {}) {
|
|
89
141
|
// Validate the name first
|
|
90
142
|
const validation = validateChangeName(name);
|
|
@@ -116,32 +168,49 @@ export async function createChange(projectRoot, name, options = {}) {
|
|
|
116
168
|
if (await FileSystemUtils.directoryExists(changeDir)) {
|
|
117
169
|
throw new Error(`Change '${name}' already exists at ${changeDir}`);
|
|
118
170
|
}
|
|
119
|
-
// Creating a change may scaffold or complete the root itself (an
|
|
120
|
-
// implicit root, or a config-only/incomplete clone). Never leave a
|
|
121
|
-
// half-root behind that doctor immediately calls unhealthy: ensure
|
|
122
|
-
// specs/ and changes/archive/ exist, and write a config only when
|
|
123
|
-
// none exists. The config records the PROJECT default schema, never
|
|
124
|
-
// a one-change --schema override.
|
|
125
|
-
const tospecDir = path.join(projectRoot, 'tospec');
|
|
126
171
|
// Create the directory (including parent directories if needed)
|
|
127
172
|
await FileSystemUtils.createDirectory(changeDir);
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
173
|
+
// Everything after the change directory exists is rolled back on failure.
|
|
174
|
+
//
|
|
175
|
+
// Creation is several writes — metadata, then the ticket stub in a different
|
|
176
|
+
// tree — and a failure partway through used to leave the change directory
|
|
177
|
+
// behind with no ticket. `list`, `status --all` and `validate --all` then
|
|
178
|
+
// treated that debris as a real change, so a command that reported failure
|
|
179
|
+
// had in fact added something the project had to be cleaned of by hand.
|
|
180
|
+
//
|
|
181
|
+
// Only the change directory is removed. The sibling scaffolding below
|
|
182
|
+
// (specs/, changes/archive/, config.yaml) completes the root itself and stays
|
|
183
|
+
// valid for every other change, so undoing it would turn one change's failure
|
|
184
|
+
// into damage to the project.
|
|
185
|
+
try {
|
|
186
|
+
// Creating a change may scaffold or complete the root itself; never leave
|
|
187
|
+
// a half-root behind that doctor immediately calls unhealthy.
|
|
188
|
+
await completeRootStructure(projectRoot, defaultSchema);
|
|
189
|
+
// Write metadata file with schema and creation date
|
|
190
|
+
const today = formatLocalDate();
|
|
191
|
+
writeChangeMetadata(changeDir, {
|
|
192
|
+
schema: schemaName,
|
|
193
|
+
type: SCHEMA_TO_CHANGE_TYPE[schemaName],
|
|
194
|
+
created: today,
|
|
195
|
+
...options.metadata,
|
|
196
|
+
}, projectRoot);
|
|
197
|
+
// Generate the ticket stub. The timestamped filename (and its `date` header)
|
|
198
|
+
// are CLI-generated — agents can't reliably reproduce the timestamp.
|
|
199
|
+
//
|
|
200
|
+
// The path comes back so the caller can report it. Nothing else can work it
|
|
201
|
+
// out: the stamp is generated in here and never written anywhere an agent
|
|
202
|
+
// reads, so filling in the ticket's Summary meant globbing a directory for a
|
|
203
|
+
// filename the CLI had just chosen and thrown away.
|
|
204
|
+
const ticketPath = await writeTicketStub(projectRoot, name, schemaName, options.description);
|
|
205
|
+
return { schema: schemaName, changeDir, ...(ticketPath ? { ticketPath } : {}) };
|
|
206
|
+
}
|
|
207
|
+
catch (error) {
|
|
208
|
+
// Best effort: report the original failure, not a cleanup failure. If the
|
|
209
|
+
// rollback itself cannot run, the caller still needs to see what actually
|
|
210
|
+
// went wrong.
|
|
211
|
+
await fsPromises.rm(changeDir, { recursive: true, force: true }).catch(() => { });
|
|
212
|
+
throw error;
|
|
132
213
|
}
|
|
133
|
-
// Write metadata file with schema and creation date
|
|
134
|
-
const today = formatLocalDate();
|
|
135
|
-
writeChangeMetadata(changeDir, {
|
|
136
|
-
schema: schemaName,
|
|
137
|
-
type: SCHEMA_TO_CHANGE_TYPE[schemaName],
|
|
138
|
-
created: today,
|
|
139
|
-
...options.metadata,
|
|
140
|
-
}, projectRoot);
|
|
141
|
-
// Generate the ticket stub. The timestamped filename (and its `date` header)
|
|
142
|
-
// are CLI-generated — agents can't reliably reproduce the timestamp.
|
|
143
|
-
await writeTicketStub(projectRoot, name, schemaName, options.description);
|
|
144
|
-
return { schema: schemaName, changeDir };
|
|
145
214
|
}
|
|
146
215
|
/**
|
|
147
216
|
* Writes a ticket stub into the global `tospec/tickets/<yyyyMMdd_HHmmss>-<name>.md`
|
|
@@ -158,7 +227,7 @@ async function writeTicketStub(projectRoot, name, schemaName, description) {
|
|
|
158
227
|
schema = resolveSchema(schemaName, projectRoot);
|
|
159
228
|
}
|
|
160
229
|
catch {
|
|
161
|
-
return; // schema unresolvable — nothing to scaffold
|
|
230
|
+
return undefined; // schema unresolvable — nothing to scaffold
|
|
162
231
|
}
|
|
163
232
|
// A schema opts into the global ticket index by shipping a ticket.md template.
|
|
164
233
|
let body;
|
|
@@ -166,7 +235,7 @@ async function writeTicketStub(projectRoot, name, schemaName, description) {
|
|
|
166
235
|
body = loadTemplate(schemaName, 'ticket.md', projectRoot);
|
|
167
236
|
}
|
|
168
237
|
catch {
|
|
169
|
-
return; // no ticket template — this schema doesn't use tickets
|
|
238
|
+
return undefined; // no ticket template — this schema doesn't use tickets
|
|
170
239
|
}
|
|
171
240
|
// Fill the `## Summary` placeholder with the provided description; a function
|
|
172
241
|
// replacer keeps `$`-sequences in the text literal. No description → the
|
|
@@ -175,6 +244,17 @@ async function writeTicketStub(projectRoot, name, schemaName, description) {
|
|
|
175
244
|
if (summary) {
|
|
176
245
|
body = body.replace(/(##[ \t]*Summary[ \t]*\r?\n)\[[^\]]*\]/, (_m, head) => head + summary);
|
|
177
246
|
}
|
|
247
|
+
// Round 4 report 6. The ledger is meant to hold one active ticket per change,
|
|
248
|
+
// but nothing checked: abandoning a change with `rm -rf` leaves its ticket
|
|
249
|
+
// behind, and recreating the change under the same name wrote a second. Both
|
|
250
|
+
// then pointed at the same proposal, archive filed the run under the older
|
|
251
|
+
// one, and the newer stayed in the active list forever naming a path that no
|
|
252
|
+
// longer exists — with no command able to remove it. Reusing the existing
|
|
253
|
+
// ticket keeps the index 1:1; its stamp is from the earlier attempt, which is
|
|
254
|
+
// the honest record of when this change name was first opened.
|
|
255
|
+
const existing = await findTicketPath(projectRoot, name);
|
|
256
|
+
if (existing)
|
|
257
|
+
return existing;
|
|
178
258
|
const stamp = formatTimestamp(new Date());
|
|
179
259
|
const ticketPath = path.join(projectRoot, 'tospec', 'tickets', `${stamp}-${name}.md`);
|
|
180
260
|
// ref points at where the change's details live: proposal.md (sdd) or task.md,
|
|
@@ -184,8 +264,18 @@ async function writeTicketStub(projectRoot, name, schemaName, description) {
|
|
|
184
264
|
const ref = hasProposal ? `tospec/changes/${name}/proposal.md` : `tospec/changes/${name}/task.md`;
|
|
185
265
|
// date is quoted so yaml keeps it a string (bare yyyyMMdd_HHmmss would parse
|
|
186
266
|
// as an underscore-separated integer under yaml 1.1).
|
|
187
|
-
|
|
267
|
+
//
|
|
268
|
+
// `type` carries the change type, the same vocabulary as .tospec.yaml and
|
|
269
|
+
// `list --json` (requirement | issue). It used to carry the schema name, so
|
|
270
|
+
// one key meant two different things across two files of the same change —
|
|
271
|
+
// invisible for an issue change, where the two words coincide, and wrong for
|
|
272
|
+
// every sdd one. The dashboard had already absorbed the split as a CSS rule
|
|
273
|
+
// matching `.badge-requirement, .badge-sdd`. The schema name is still worth
|
|
274
|
+
// recording, so it gets its own key rather than borrowing this one.
|
|
275
|
+
const changeType = SCHEMA_TO_CHANGE_TYPE[schemaName] ?? schemaName;
|
|
276
|
+
const header = `---\ntitle: ${name}\ndate: "${stamp}"\ntype: ${changeType}\nschema: ${schemaName}\nchange: ${name}\nref: ${ref}\n---\n\n`;
|
|
188
277
|
await FileSystemUtils.createDirectory(path.join(projectRoot, 'tospec', 'tickets'));
|
|
189
278
|
await FileSystemUtils.writeFile(ticketPath, header + body);
|
|
279
|
+
return ticketPath;
|
|
190
280
|
}
|
|
191
281
|
//# sourceMappingURL=change-utils.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"change-utils.js","sourceRoot":"","sources":["../../src/utils/change-utils.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AACnD,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAClE,OAAO,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAC/E,OAAO,EAAE,iBAAiB,EAAE,sBAAsB,EAAE,MAAM,2BAA2B,CAAC;AACtF,OAAO,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,iCAAiC,CAAC;
|
|
1
|
+
{"version":3,"file":"change-utils.js","sourceRoot":"","sources":["../../src/utils/change-utils.ts"],"names":[],"mappings":"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"}
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
*
|
|
5
5
|
* Shared by `tospec list --sort recent` and the dashboard so both rank changes
|
|
6
6
|
* by the same clock; they used to walk separately and could disagree.
|
|
7
|
-
*
|
|
7
|
+
* Limitation: recursive walk; fine at repo scale (dozens of changes).
|
|
8
8
|
*/
|
|
9
9
|
export declare function getLastModified(dirPath: string): Promise<Date>;
|
|
10
10
|
/**
|
|
@@ -7,7 +7,7 @@ const fs = nodeFs.promises;
|
|
|
7
7
|
*
|
|
8
8
|
* Shared by `tospec list --sort recent` and the dashboard so both rank changes
|
|
9
9
|
* by the same clock; they used to walk separately and could disagree.
|
|
10
|
-
*
|
|
10
|
+
* Limitation: recursive walk; fine at repo scale (dozens of changes).
|
|
11
11
|
*/
|
|
12
12
|
export async function getLastModified(dirPath) {
|
|
13
13
|
let latest = null;
|
|
@@ -1,12 +1,27 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
* (non-recursive), so archived
|
|
5
|
-
*
|
|
2
|
+
* Every ticket in the active `tospec/tickets/` index whose frontmatter `change:`
|
|
3
|
+
* names this change, sorted by filename — which is the `yyyyMMdd_HHmmss` stamp,
|
|
4
|
+
* so oldest first. Scans only the active folder (non-recursive), so archived
|
|
5
|
+
* tickets under `tickets/archive/` are excluded.
|
|
6
6
|
*
|
|
7
|
-
*
|
|
7
|
+
* Plural because the index can hold more than one: a change abandoned with
|
|
8
|
+
* `rm -rf` leaves its ticket behind, and recreating the change under the same
|
|
9
|
+
* name used to add a second (round 4 report 6). Callers that can report the
|
|
10
|
+
* ambiguity should; `findTicketPath` picks for those that cannot.
|
|
11
|
+
*
|
|
12
|
+
* Limitation: linear scan of tickets/; fine at repo scale (one ticket per change).
|
|
8
13
|
* Add an index if a project ever accumulates thousands of active changes.
|
|
9
14
|
*/
|
|
15
|
+
export declare function findTicketPaths(root: string, changeName: string): Promise<string[]>;
|
|
16
|
+
/**
|
|
17
|
+
* The ticket to treat as this change's, when there is more than one.
|
|
18
|
+
*
|
|
19
|
+
* The newest wins: a second ticket exists because the change was recreated, so
|
|
20
|
+
* the later stamp is the one that describes the change on disk. Taking the
|
|
21
|
+
* first `readdir` entry instead meant archive filed the run under a ticket
|
|
22
|
+
* belonging to the abandoned attempt and left the real one stranded in the
|
|
23
|
+
* active list forever (round 4 report 6).
|
|
24
|
+
*/
|
|
10
25
|
export declare function findTicketPath(root: string, changeName: string): Promise<string | undefined>;
|
|
11
26
|
/**
|
|
12
27
|
* Active change directory names under `changesDir`, sorted, excluding `archive`
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"item-discovery.d.ts","sourceRoot":"","sources":["../../src/utils/item-discovery.ts"],"names":[],"mappings":"AAIA;;;;;;;;GAQG;AACH,wBAAsB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,
|
|
1
|
+
{"version":3,"file":"item-discovery.d.ts","sourceRoot":"","sources":["../../src/utils/item-discovery.ts"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;GAaG;AACH,wBAAsB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAoBzF;AAED;;;;;;;;GAQG;AACH,wBAAsB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAGlG;AAED;;;;;GAKG;AACH,wBAAsB,oBAAoB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAUhF;AAED,wBAAsB,kBAAkB,CAAC,IAAI,GAAE,MAAsB,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAExF;AAED,wBAAsB,UAAU,CAAC,IAAI,GAAE,MAAsB,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAmBhF;AAED,wBAAsB,oBAAoB,CAAC,IAAI,GAAE,MAAsB,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAW1F"}
|
|
@@ -2,23 +2,29 @@ import { promises as fs } from 'fs';
|
|
|
2
2
|
import path from 'path';
|
|
3
3
|
import { frontmatterString } from './frontmatter.js';
|
|
4
4
|
/**
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* (non-recursive), so archived
|
|
8
|
-
*
|
|
5
|
+
* Every ticket in the active `tospec/tickets/` index whose frontmatter `change:`
|
|
6
|
+
* names this change, sorted by filename — which is the `yyyyMMdd_HHmmss` stamp,
|
|
7
|
+
* so oldest first. Scans only the active folder (non-recursive), so archived
|
|
8
|
+
* tickets under `tickets/archive/` are excluded.
|
|
9
9
|
*
|
|
10
|
-
*
|
|
10
|
+
* Plural because the index can hold more than one: a change abandoned with
|
|
11
|
+
* `rm -rf` leaves its ticket behind, and recreating the change under the same
|
|
12
|
+
* name used to add a second (round 4 report 6). Callers that can report the
|
|
13
|
+
* ambiguity should; `findTicketPath` picks for those that cannot.
|
|
14
|
+
*
|
|
15
|
+
* Limitation: linear scan of tickets/; fine at repo scale (one ticket per change).
|
|
11
16
|
* Add an index if a project ever accumulates thousands of active changes.
|
|
12
17
|
*/
|
|
13
|
-
export async function
|
|
18
|
+
export async function findTicketPaths(root, changeName) {
|
|
14
19
|
const ticketsPath = path.join(root, 'tospec', 'tickets');
|
|
15
20
|
let entries;
|
|
16
21
|
try {
|
|
17
22
|
entries = await fs.readdir(ticketsPath, { withFileTypes: true });
|
|
18
23
|
}
|
|
19
24
|
catch {
|
|
20
|
-
return
|
|
25
|
+
return [];
|
|
21
26
|
}
|
|
27
|
+
const matches = [];
|
|
22
28
|
for (const entry of entries) {
|
|
23
29
|
if (!entry.isFile() || !entry.name.endsWith('.md'))
|
|
24
30
|
continue;
|
|
@@ -26,13 +32,26 @@ export async function findTicketPath(root, changeName) {
|
|
|
26
32
|
try {
|
|
27
33
|
const content = await fs.readFile(full, 'utf-8');
|
|
28
34
|
if (frontmatterString(content, 'change') === changeName)
|
|
29
|
-
|
|
35
|
+
matches.push(full);
|
|
30
36
|
}
|
|
31
37
|
catch {
|
|
32
38
|
continue;
|
|
33
39
|
}
|
|
34
40
|
}
|
|
35
|
-
return
|
|
41
|
+
return matches.sort((a, b) => path.basename(a).localeCompare(path.basename(b)));
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* The ticket to treat as this change's, when there is more than one.
|
|
45
|
+
*
|
|
46
|
+
* The newest wins: a second ticket exists because the change was recreated, so
|
|
47
|
+
* the later stamp is the one that describes the change on disk. Taking the
|
|
48
|
+
* first `readdir` entry instead meant archive filed the run under a ticket
|
|
49
|
+
* belonging to the abandoned attempt and left the real one stranded in the
|
|
50
|
+
* active list forever (round 4 report 6).
|
|
51
|
+
*/
|
|
52
|
+
export async function findTicketPath(root, changeName) {
|
|
53
|
+
const matches = await findTicketPaths(root, changeName);
|
|
54
|
+
return matches.length === 0 ? undefined : matches[matches.length - 1];
|
|
36
55
|
}
|
|
37
56
|
/**
|
|
38
57
|
* Active change directory names under `changesDir`, sorted, excluding `archive`
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"item-discovery.js","sourceRoot":"","sources":["../../src/utils/item-discovery.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,IAAI,EAAE,EAAE,MAAM,IAAI,CAAC;AACpC,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAErD
|
|
1
|
+
{"version":3,"file":"item-discovery.js","sourceRoot":"","sources":["../../src/utils/item-discovery.ts"],"names":[],"mappings":"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"}
|
|
@@ -16,4 +16,16 @@
|
|
|
16
16
|
* differently on CI than locally).
|
|
17
17
|
*/
|
|
18
18
|
export declare function findSpecFiles(specsDir: string): Promise<string[]>;
|
|
19
|
+
/**
|
|
20
|
+
* Every `*.md` under a change's `specs/` directory, at any depth — unlike
|
|
21
|
+
* {@link findSpecFiles}, basename is not filtered.
|
|
22
|
+
*
|
|
23
|
+
* Exists so the validator can see the same misplacement class it already
|
|
24
|
+
* catches (wrong depth, dot-directory) when the mistake is a wrong *basename*
|
|
25
|
+
* instead: `specs/<capability>/spec.md`'s wrong-basename sibling is invisible
|
|
26
|
+
* to `findSpecFiles`, which drops it before any guard loop runs. The merge
|
|
27
|
+
* path (`findSpecUpdates`) must keep reading only `spec.md` — this walker is
|
|
28
|
+
* for validation to report the file, not for merge to read it.
|
|
29
|
+
*/
|
|
30
|
+
export declare function findAllMarkdownFiles(specsDir: string): Promise<string[]>;
|
|
19
31
|
//# sourceMappingURL=spec-files.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"spec-files.d.ts","sourceRoot":"","sources":["../../src/utils/spec-files.ts"],"names":[],"mappings":"AAGA;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAsB,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAuBvE"}
|
|
1
|
+
{"version":3,"file":"spec-files.d.ts","sourceRoot":"","sources":["../../src/utils/spec-files.ts"],"names":[],"mappings":"AAGA;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAsB,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAuBvE;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,oBAAoB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAsB9E"}
|
package/dist/utils/spec-files.js
CHANGED
|
@@ -41,4 +41,38 @@ export async function findSpecFiles(specsDir) {
|
|
|
41
41
|
await walk(specsDir);
|
|
42
42
|
return results.sort();
|
|
43
43
|
}
|
|
44
|
+
/**
|
|
45
|
+
* Every `*.md` under a change's `specs/` directory, at any depth — unlike
|
|
46
|
+
* {@link findSpecFiles}, basename is not filtered.
|
|
47
|
+
*
|
|
48
|
+
* Exists so the validator can see the same misplacement class it already
|
|
49
|
+
* catches (wrong depth, dot-directory) when the mistake is a wrong *basename*
|
|
50
|
+
* instead: `specs/<capability>/spec.md`'s wrong-basename sibling is invisible
|
|
51
|
+
* to `findSpecFiles`, which drops it before any guard loop runs. The merge
|
|
52
|
+
* path (`findSpecUpdates`) must keep reading only `spec.md` — this walker is
|
|
53
|
+
* for validation to report the file, not for merge to read it.
|
|
54
|
+
*/
|
|
55
|
+
export async function findAllMarkdownFiles(specsDir) {
|
|
56
|
+
const results = [];
|
|
57
|
+
const walk = async (dir) => {
|
|
58
|
+
let entries;
|
|
59
|
+
try {
|
|
60
|
+
entries = await fs.readdir(dir, { withFileTypes: true });
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
for (const entry of entries) {
|
|
66
|
+
const full = path.join(dir, entry.name);
|
|
67
|
+
if (entry.isDirectory()) {
|
|
68
|
+
await walk(full);
|
|
69
|
+
}
|
|
70
|
+
else if (entry.isFile() && entry.name.toLowerCase().endsWith('.md')) {
|
|
71
|
+
results.push(full);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
await walk(specsDir);
|
|
76
|
+
return results.sort();
|
|
77
|
+
}
|
|
44
78
|
//# sourceMappingURL=spec-files.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"spec-files.js","sourceRoot":"","sources":["../../src/utils/spec-files.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,IAAI,EAAE,EAAE,MAAM,SAAS,CAAC;AACzC,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,QAAgB;IAClD,MAAM,OAAO,GAAa,EAAE,CAAC;IAE7B,MAAM,IAAI,GAAG,KAAK,EAAE,GAAW,EAAiB,EAAE;QAChD,IAAI,OAAO,CAAC;QACZ,IAAI,CAAC;YACH,OAAO,GAAG,MAAM,EAAE,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;QAC3D,CAAC;QAAC,MAAM,CAAC;YACP,yEAAyE;YACzE,OAAO;QACT,CAAC;QACD,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;YAC5B,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;YACxC,IAAI,KAAK,CAAC,WAAW,EAAE,EAAE,CAAC;gBACxB,MAAM,IAAI,CAAC,IAAI,CAAC,CAAC;YACnB,CAAC;iBAAM,IAAI,KAAK,CAAC,MAAM,EAAE,IAAI,KAAK,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;gBACtD,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACrB,CAAC;QACH,CAAC;IACH,CAAC,CAAC;IAEF,MAAM,IAAI,CAAC,QAAQ,CAAC,CAAC;IACrB,OAAO,OAAO,CAAC,IAAI,EAAE,CAAC;AACxB,CAAC"}
|
|
1
|
+
{"version":3,"file":"spec-files.js","sourceRoot":"","sources":["../../src/utils/spec-files.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,IAAI,EAAE,EAAE,MAAM,SAAS,CAAC;AACzC,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,QAAgB;IAClD,MAAM,OAAO,GAAa,EAAE,CAAC;IAE7B,MAAM,IAAI,GAAG,KAAK,EAAE,GAAW,EAAiB,EAAE;QAChD,IAAI,OAAO,CAAC;QACZ,IAAI,CAAC;YACH,OAAO,GAAG,MAAM,EAAE,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;QAC3D,CAAC;QAAC,MAAM,CAAC;YACP,yEAAyE;YACzE,OAAO;QACT,CAAC;QACD,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;YAC5B,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;YACxC,IAAI,KAAK,CAAC,WAAW,EAAE,EAAE,CAAC;gBACxB,MAAM,IAAI,CAAC,IAAI,CAAC,CAAC;YACnB,CAAC;iBAAM,IAAI,KAAK,CAAC,MAAM,EAAE,IAAI,KAAK,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;gBACtD,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACrB,CAAC;QACH,CAAC;IACH,CAAC,CAAC;IAEF,MAAM,IAAI,CAAC,QAAQ,CAAC,CAAC;IACrB,OAAO,OAAO,CAAC,IAAI,EAAE,CAAC;AACxB,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,CAAC,KAAK,UAAU,oBAAoB,CAAC,QAAgB;IACzD,MAAM,OAAO,GAAa,EAAE,CAAC;IAE7B,MAAM,IAAI,GAAG,KAAK,EAAE,GAAW,EAAiB,EAAE;QAChD,IAAI,OAAO,CAAC;QACZ,IAAI,CAAC;YACH,OAAO,GAAG,MAAM,EAAE,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;QAC3D,CAAC;QAAC,MAAM,CAAC;YACP,OAAO;QACT,CAAC;QACD,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;YAC5B,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;YACxC,IAAI,KAAK,CAAC,WAAW,EAAE,EAAE,CAAC;gBACxB,MAAM,IAAI,CAAC,IAAI,CAAC,CAAC;YACnB,CAAC;iBAAM,IAAI,KAAK,CAAC,MAAM,EAAE,IAAI,KAAK,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;gBACtE,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACrB,CAAC;QACH,CAAC;IACH,CAAC,CAAC;IAEF,MAAM,IAAI,CAAC,QAAQ,CAAC,CAAC;IACrB,OAAO,OAAO,CAAC,IAAI,EAAE,CAAC;AACxB,CAAC"}
|
|
@@ -8,7 +8,7 @@ import { resolveSchemaForChange } from './change-metadata.js';
|
|
|
8
8
|
* subtask is never invisible to progress; the indent is kept only so the
|
|
9
9
|
* dashboard can render the nesting it found.
|
|
10
10
|
*
|
|
11
|
-
*
|
|
11
|
+
* Limitation: line-based, so a `- [ ]` inside an indented code block in tasks.md
|
|
12
12
|
* miscounts. A real markdown parse is the fix if that ever bites.
|
|
13
13
|
*/
|
|
14
14
|
const TASK_PATTERN = /^([ \t]*)[-*]\s+\[([\sxX])\]\s*(.*)$/;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@seanmars/tospec",
|
|
3
|
-
"version": "0.19.0-beta.
|
|
3
|
+
"version": "0.19.0-beta.8",
|
|
4
4
|
"description": "Spec-driven development CLI for structured requirements and issue workflows",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"homepage": "https://www.npmjs.com/package/@seanmars/tospec",
|
|
@@ -23,7 +23,9 @@
|
|
|
23
23
|
<!-- Follow-up implementation items, phased if useful -->
|
|
24
24
|
|
|
25
25
|
## Related Changes
|
|
26
|
-
<!-- Optional
|
|
26
|
+
<!-- Optional, maintained by hand: nothing writes this section. `tospec new change --decisions <file>`
|
|
27
|
+
records the link on the change's side only. List change names here yourself, add archive paths
|
|
28
|
+
once archived; omit if none yet -->
|
|
27
29
|
|
|
28
30
|
## Decision Process
|
|
29
31
|
<!-- Optional: the key Q&A that shaped this decision; each pair starts with **Q:**, answer in the next paragraph; summarize, don't paste a transcript; omit if there was no interactive discussion -->
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
| timestamp | title | summary | file |
|
|
2
|
-
| ----- | ----- | ----- | ----- |
|
|
1
|
+
| timestamp | status | title | summary | file |
|
|
2
|
+
| ----- | ----- | ----- | ----- | ----- |
|
|
@@ -29,8 +29,10 @@ artifacts:
|
|
|
29
29
|
instruction: |
|
|
30
30
|
Write delta specs only when the fix changes externally observable behavior
|
|
31
31
|
(same grammar as sdd, same layout: specs/<capability>/spec.md, exactly one
|
|
32
|
-
directory level).
|
|
33
|
-
|
|
32
|
+
directory level). REMOVED lists each name as a bullet with Reason and
|
|
33
|
+
Migration fields; RENAMED writes a FROM:/TO: pair, e.g. "- FROM: `<old
|
|
34
|
+
name>`" / "- TO: `<new name>`". If the bug stems from a wrong spec, fix
|
|
35
|
+
that Requirement with MODIFIED.
|
|
34
36
|
apply:
|
|
35
37
|
requires: [task]
|
|
36
38
|
tracks: task.md
|
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
<!-- A NEW capability's delta opens with ## Purpose (one sentence on why this
|
|
2
|
+
capability exists); archive carries it into the newly created main spec. Delete
|
|
3
|
+
this block when the delta targets a capability that already has a main spec —
|
|
4
|
+
edit that spec's Purpose directly instead. -->
|
|
5
|
+
|
|
6
|
+
## Purpose
|
|
7
|
+
[One sentence: why this capability exists]
|
|
8
|
+
|
|
1
9
|
## ADDED Requirements
|
|
2
10
|
|
|
3
11
|
### Requirement: [name]
|
|
@@ -9,5 +17,15 @@
|
|
|
9
17
|
- **AND** [additional outcome]
|
|
10
18
|
|
|
11
19
|
<!-- MODIFIED, REMOVED, and RENAMED Requirements may also be used.
|
|
12
|
-
MODIFIED must contain the full updated requirement block.
|
|
13
|
-
|
|
20
|
+
MODIFIED must contain the full updated requirement block. -->
|
|
21
|
+
|
|
22
|
+
## REMOVED Requirements
|
|
23
|
+
|
|
24
|
+
- `[name]`
|
|
25
|
+
- Reason: [why removed]
|
|
26
|
+
- Migration: [how existing usage migrates]
|
|
27
|
+
|
|
28
|
+
## RENAMED Requirements
|
|
29
|
+
|
|
30
|
+
- FROM: `[old name]`
|
|
31
|
+
- TO: `[new name]`
|
package/schemas/sdd/schema.yaml
CHANGED
|
@@ -39,6 +39,10 @@ artifacts:
|
|
|
39
39
|
body, not only in the heading) /
|
|
40
40
|
#### Scenario: <name> (four #, WHEN/THEN/AND bullets).
|
|
41
41
|
MODIFIED must paste the full updated requirement block.
|
|
42
|
+
REMOVED lists each removed name as a bullet with Reason and Migration
|
|
43
|
+
fields, e.g. "- <name>" / "- Reason: ..." / "- Migration: ...".
|
|
44
|
+
RENAMED writes a FROM:/TO: pair per entry, e.g. "- FROM: `<old name>`" /
|
|
45
|
+
"- TO: `<new name>`".
|
|
42
46
|
A new capability's delta must open with ## Purpose (one sentence on why this
|
|
43
47
|
capability exists); archive carries it into the newly created main spec. Do
|
|
44
48
|
not add Purpose to an existing capability's delta (edit the main spec's
|
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
<!-- A NEW capability's delta opens with ## Purpose (one sentence on why this
|
|
2
|
+
capability exists); archive carries it into the newly created main spec. Delete
|
|
3
|
+
this block when the delta targets a capability that already has a main spec —
|
|
4
|
+
edit that spec's Purpose directly instead. -->
|
|
5
|
+
|
|
6
|
+
## Purpose
|
|
7
|
+
[One sentence: why this capability exists]
|
|
8
|
+
|
|
1
9
|
## ADDED Requirements
|
|
2
10
|
|
|
3
11
|
### Requirement: [name]
|
|
@@ -9,5 +17,15 @@
|
|
|
9
17
|
- **AND** [additional outcome]
|
|
10
18
|
|
|
11
19
|
<!-- MODIFIED, REMOVED, and RENAMED Requirements may also be used.
|
|
12
|
-
MODIFIED must contain the full updated requirement block.
|
|
13
|
-
|
|
20
|
+
MODIFIED must contain the full updated requirement block. -->
|
|
21
|
+
|
|
22
|
+
## REMOVED Requirements
|
|
23
|
+
|
|
24
|
+
- `[name]`
|
|
25
|
+
- Reason: [why removed]
|
|
26
|
+
- Migration: [how existing usage migrates]
|
|
27
|
+
|
|
28
|
+
## RENAMED Requirements
|
|
29
|
+
|
|
30
|
+
- FROM: `[old name]`
|
|
31
|
+
- TO: `[new name]`
|
|
File without changes
|