@fission-ai/openspec 1.6.0 → 1.8.0
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/README.md +31 -2
- package/dist/cli/index.js +79 -8
- package/dist/commands/change.d.ts +0 -1
- package/dist/commands/change.js +105 -51
- package/dist/commands/config.js +14 -8
- package/dist/commands/doctor.js +9 -4
- package/dist/commands/feedback.js +74 -23
- package/dist/commands/schema.js +110 -31
- package/dist/commands/show.js +5 -2
- package/dist/commands/spec.js +49 -29
- package/dist/commands/validate.js +20 -5
- package/dist/commands/workflow/index.d.ts +1 -1
- package/dist/commands/workflow/index.js +1 -1
- package/dist/commands/workflow/instructions.d.ts +7 -1
- package/dist/commands/workflow/instructions.js +121 -33
- package/dist/commands/workflow/shared.d.ts +13 -2
- package/dist/commands/workflow/shared.js +34 -4
- package/dist/commands/workflow/status.js +9 -4
- package/dist/commands/workflow/templates.js +15 -5
- package/dist/core/archive.d.ts +14 -1
- package/dist/core/archive.js +1332 -155
- package/dist/core/artifact-graph/graph.d.ts +16 -0
- package/dist/core/artifact-graph/graph.js +30 -6
- package/dist/core/artifact-graph/index.d.ts +1 -1
- package/dist/core/artifact-graph/index.js +1 -1
- package/dist/core/artifact-graph/instruction-loader.d.ts +30 -3
- package/dist/core/artifact-graph/instruction-loader.js +70 -12
- package/dist/core/artifact-graph/outputs.d.ts +1 -0
- package/dist/core/artifact-graph/outputs.js +67 -5
- package/dist/core/artifact-graph/resolver.d.ts +14 -0
- package/dist/core/artifact-graph/resolver.js +69 -15
- package/dist/core/artifact-graph/types.js +21 -3
- package/dist/core/available-tools.d.ts +2 -2
- package/dist/core/available-tools.js +14 -3
- package/dist/core/change-metadata/schema.d.ts +2 -0
- package/dist/core/change-metadata/schema.js +13 -0
- package/dist/core/change-status-policy.d.ts +1 -1
- package/dist/core/change-status-policy.js +2 -2
- package/dist/core/command-generation/adapters/amazon-q.d.ts +4 -0
- package/dist/core/command-generation/adapters/amazon-q.js +7 -1
- package/dist/core/command-generation/adapters/antigravity.js +2 -1
- package/dist/core/command-generation/adapters/auggie.js +2 -1
- package/dist/core/command-generation/adapters/bob.d.ts +5 -1
- package/dist/core/command-generation/adapters/bob.js +6 -5
- package/dist/core/command-generation/adapters/claude.js +1 -8
- package/dist/core/command-generation/adapters/codebuddy.js +3 -2
- package/dist/core/command-generation/adapters/continue.js +3 -2
- package/dist/core/command-generation/adapters/costrict.js +2 -1
- package/dist/core/command-generation/adapters/crush.js +5 -5
- package/dist/core/command-generation/adapters/cursor.js +2 -2
- package/dist/core/command-generation/adapters/devin.d.ts +19 -0
- package/dist/core/command-generation/adapters/devin.js +36 -0
- package/dist/core/command-generation/adapters/factory.js +2 -1
- package/dist/core/command-generation/adapters/gemini.js +37 -2
- package/dist/core/command-generation/adapters/github-copilot.js +2 -1
- package/dist/core/command-generation/adapters/iflow.js +5 -4
- package/dist/core/command-generation/adapters/index.d.ts +2 -2
- package/dist/core/command-generation/adapters/index.js +2 -2
- package/dist/core/command-generation/adapters/junie.js +2 -1
- package/dist/core/command-generation/adapters/kiro.js +2 -1
- package/dist/core/command-generation/adapters/lingma.js +5 -5
- package/dist/core/command-generation/adapters/oh-my-pi.d.ts +2 -2
- package/dist/core/command-generation/adapters/oh-my-pi.js +3 -5
- package/dist/core/command-generation/adapters/opencode.js +3 -5
- package/dist/core/command-generation/adapters/pi.d.ts +2 -2
- package/dist/core/command-generation/adapters/pi.js +3 -6
- package/dist/core/command-generation/adapters/qoder.js +5 -5
- package/dist/core/command-generation/adapters/qwen.d.ts +7 -3
- package/dist/core/command-generation/adapters/qwen.js +12 -7
- package/dist/core/command-generation/adapters/roocode.d.ts +4 -4
- package/dist/core/command-generation/adapters/roocode.js +4 -4
- package/dist/core/command-generation/adapters/trae.js +1 -21
- package/dist/core/command-generation/adapters/zcode.d.ts +16 -0
- package/dist/core/command-generation/adapters/zcode.js +33 -0
- package/dist/core/command-generation/generator.d.ts +8 -0
- package/dist/core/command-generation/generator.js +15 -1
- package/dist/core/command-generation/index.d.ts +1 -1
- package/dist/core/command-generation/index.js +1 -1
- package/dist/core/command-generation/invocation.d.ts +73 -0
- package/dist/core/command-generation/invocation.js +77 -0
- package/dist/core/command-generation/registry.js +4 -4
- package/dist/core/command-generation/types.d.ts +9 -2
- package/dist/core/command-generation/yaml.d.ts +20 -6
- package/dist/core/command-generation/yaml.js +31 -21
- package/dist/core/command-surface.d.ts +15 -0
- package/dist/core/command-surface.js +33 -0
- package/dist/core/completions/command-registry.js +16 -2
- package/dist/core/completions/generators/powershell-generator.js +15 -8
- package/dist/core/completions/installers/zsh-installer.d.ts +9 -0
- package/dist/core/completions/installers/zsh-installer.js +20 -4
- package/dist/core/config-prompts.d.ts +1 -1
- package/dist/core/config-prompts.js +14 -1
- package/dist/core/config-schema.d.ts +10 -1
- package/dist/core/config-schema.js +67 -1
- package/dist/core/config.d.ts +15 -0
- package/dist/core/config.js +45 -5
- package/dist/core/file-state.js +56 -24
- package/dist/core/github-copilot/cloud-agent.d.ts +95 -0
- package/dist/core/github-copilot/cloud-agent.js +497 -0
- package/dist/core/global-config.d.ts +16 -0
- package/dist/core/init.d.ts +58 -0
- package/dist/core/init.js +450 -69
- package/dist/core/legacy-cleanup.d.ts +59 -0
- package/dist/core/legacy-cleanup.js +253 -24
- package/dist/core/list.js +5 -7
- package/dist/core/migration.d.ts +73 -1
- package/dist/core/migration.js +360 -11
- package/dist/core/onboarding-commands.d.ts +30 -0
- package/dist/core/onboarding-commands.js +38 -0
- package/dist/core/parsers/change-parser.d.ts +17 -2
- package/dist/core/parsers/change-parser.js +33 -20
- package/dist/core/parsers/code-fence.d.ts +15 -0
- package/dist/core/parsers/code-fence.js +49 -0
- package/dist/core/parsers/markdown-parser.js +2 -1
- package/dist/core/parsers/requirement-blocks.d.ts +18 -0
- package/dist/core/parsers/requirement-blocks.js +120 -35
- package/dist/core/parsers/requirement-text.d.ts +1 -8
- package/dist/core/parsers/requirement-text.js +4 -42
- package/dist/core/parsers/spec-structure.d.ts +1 -1
- package/dist/core/parsers/spec-structure.js +20 -30
- package/dist/core/profile-sync-drift.d.ts +0 -8
- package/dist/core/profile-sync-drift.js +48 -49
- package/dist/core/project-config.d.ts +28 -6
- package/dist/core/project-config.js +108 -8
- package/dist/core/references.js +42 -4
- package/dist/core/relationship-health.d.ts +8 -0
- package/dist/core/relationship-health.js +13 -0
- package/dist/core/root-selection.d.ts +9 -6
- package/dist/core/root-selection.js +42 -5
- package/dist/core/shared/index.d.ts +1 -0
- package/dist/core/shared/index.js +1 -0
- package/dist/core/shared/skill-content-equivalence.d.ts +7 -0
- package/dist/core/shared/skill-content-equivalence.js +41 -0
- package/dist/core/shared/skill-paths.d.ts +13 -0
- package/dist/core/shared/skill-paths.js +23 -0
- package/dist/core/shared/tool-detection.d.ts +25 -5
- package/dist/core/shared/tool-detection.js +166 -36
- package/dist/core/shared-skill-target.d.ts +16 -0
- package/dist/core/shared-skill-target.js +156 -0
- package/dist/core/specs-apply.d.ts +80 -33
- package/dist/core/specs-apply.js +659 -171
- package/dist/core/store/git.d.ts +13 -0
- package/dist/core/store/git.js +24 -0
- package/dist/core/templates/skill-templates.d.ts +1 -1
- package/dist/core/templates/skill-templates.js +1 -1
- package/dist/core/templates/workflows/apply-change.d.ts +10 -0
- package/dist/core/templates/workflows/apply-change.js +48 -158
- package/dist/core/templates/workflows/archive-change.js +170 -42
- package/dist/core/templates/workflows/bulk-archive-change.js +256 -75
- package/dist/core/templates/workflows/continue-change.js +38 -46
- package/dist/core/templates/workflows/explore.js +54 -18
- package/dist/core/templates/workflows/ff-change.js +46 -30
- package/dist/core/templates/workflows/new-change.js +2 -2
- package/dist/core/templates/workflows/onboard.js +13 -7
- package/dist/core/templates/workflows/propose.js +116 -46
- package/dist/core/templates/workflows/store-selection.d.ts +1 -1
- package/dist/core/templates/workflows/store-selection.js +1 -1
- package/dist/core/templates/workflows/sync-specs.js +260 -32
- package/dist/core/templates/workflows/update-change.js +26 -16
- package/dist/core/templates/workflows/verify-change.js +16 -10
- package/dist/core/update.d.ts +40 -5
- package/dist/core/update.js +389 -93
- package/dist/core/validation/constants.d.ts +4 -1
- package/dist/core/validation/constants.js +4 -1
- package/dist/core/validation/validator.d.ts +22 -8
- package/dist/core/validation/validator.js +222 -50
- package/dist/core/version-check.d.ts +152 -0
- package/dist/core/version-check.js +731 -0
- package/dist/core/view.js +12 -17
- package/dist/prompts/searchable-multi-select.js +1 -1
- package/dist/telemetry/config.d.ts +3 -4
- package/dist/telemetry/index.d.ts +10 -5
- package/dist/telemetry/index.js +75 -41
- package/dist/ui/welcome-screen.d.ts +9 -1
- package/dist/ui/welcome-screen.js +91 -38
- package/dist/utils/change-metadata.d.ts +43 -0
- package/dist/utils/change-metadata.js +123 -7
- package/dist/utils/change-utils.d.ts +10 -4
- package/dist/utils/change-utils.js +20 -13
- package/dist/utils/ci.d.ts +12 -0
- package/dist/utils/ci.js +16 -0
- package/dist/utils/command-references.d.ts +92 -6
- package/dist/utils/command-references.js +180 -7
- package/dist/utils/date.d.ts +8 -0
- package/dist/utils/date.js +13 -0
- package/dist/utils/file-system.d.ts +10 -0
- package/dist/utils/file-system.js +70 -0
- package/dist/utils/index.d.ts +1 -1
- package/dist/utils/index.js +1 -1
- package/dist/utils/interactive.d.ts +23 -0
- package/dist/utils/interactive.js +32 -0
- package/dist/utils/item-discovery.d.ts +19 -0
- package/dist/utils/item-discovery.js +30 -48
- package/dist/utils/shell-detection.d.ts +1 -1
- package/dist/utils/shell-detection.js +64 -11
- package/dist/utils/spec-discovery.d.ts +40 -0
- package/dist/utils/spec-discovery.js +115 -0
- package/dist/utils/task-progress.d.ts +17 -0
- package/dist/utils/task-progress.js +41 -13
- package/package.json +14 -12
- package/schemas/spec-driven/schema.yaml +67 -10
- package/schemas/spec-driven/templates/design.md +2 -2
- package/schemas/spec-driven/templates/proposal.md +10 -4
- package/schemas/spec-driven/templates/spec.md +3 -0
- package/dist/core/command-generation/adapters/codex.d.ts +0 -16
- package/dist/core/command-generation/adapters/codex.js +0 -39
- package/dist/core/command-generation/adapters/windsurf.d.ts +0 -14
- package/dist/core/command-generation/adapters/windsurf.js +0 -38
|
@@ -2,7 +2,7 @@ import * as fs from 'node:fs';
|
|
|
2
2
|
import * as path from 'node:path';
|
|
3
3
|
import * as yaml from 'yaml';
|
|
4
4
|
import { ChangeMetadataSchema } from '../core/change-metadata/index.js';
|
|
5
|
-
import { listSchemas } from '../core/artifact-graph/resolver.js';
|
|
5
|
+
import { listSchemas, resolveSchema } from '../core/artifact-graph/resolver.js';
|
|
6
6
|
import { readProjectConfig } from '../core/project-config.js';
|
|
7
7
|
export const METADATA_FILENAME = '.openspec.yaml';
|
|
8
8
|
/**
|
|
@@ -126,16 +126,132 @@ export function resolveSchemaForChange(changeDir, explicitSchema, projectRootOve
|
|
|
126
126
|
return metadata.schema;
|
|
127
127
|
}
|
|
128
128
|
// 3. Try reading from project config when metadata is absent.
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
return config.schema;
|
|
129
|
+
if (options.projectConfig !== undefined) {
|
|
130
|
+
if (options.projectConfig?.schema) {
|
|
131
|
+
return options.projectConfig.schema;
|
|
133
132
|
}
|
|
134
133
|
}
|
|
135
|
-
|
|
136
|
-
|
|
134
|
+
else {
|
|
135
|
+
try {
|
|
136
|
+
const config = readProjectConfig(projectRoot);
|
|
137
|
+
if (config?.schema) {
|
|
138
|
+
return config.schema;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
catch {
|
|
142
|
+
// If config read fails, fall back to default
|
|
143
|
+
}
|
|
137
144
|
}
|
|
138
145
|
// 4. Default
|
|
139
146
|
return 'spec-driven';
|
|
140
147
|
}
|
|
148
|
+
/**
|
|
149
|
+
* Non-throwing read of the skip_specs marker. The marker only counts when the
|
|
150
|
+
* metadata would load for status/instructions: the file parses under
|
|
151
|
+
* ChangeMetadataSchema, its schema name passes readChangeMetadata's
|
|
152
|
+
* listSchemas membership check, AND the schema itself loads via resolveSchema
|
|
153
|
+
* (a schema.yaml that exists but does not parse fails status just the same).
|
|
154
|
+
* Validate and archive must never honor metadata the rest of the CLI rejects,
|
|
155
|
+
* in either direction. The project root for schema resolution is derived from
|
|
156
|
+
* changeDir exactly like resolveSchemaForChange (changeDir is
|
|
157
|
+
* <root>/openspec/changes/<name> for every root type, including store roots).
|
|
158
|
+
* Missing metadata means "not declared"; a marker that cannot be honored
|
|
159
|
+
* yields invalidReason so callers can say why.
|
|
160
|
+
*/
|
|
161
|
+
export function readSkipSpecsMarker(changeDir) {
|
|
162
|
+
return readBooleanMarker(changeDir, 'skip_specs');
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Non-throwing read of the retire_capabilities marker, with exactly the
|
|
166
|
+
* semantics `readSkipSpecsMarker` documents above.
|
|
167
|
+
*
|
|
168
|
+
* Gates the one archive action that removes a file from `openspec/specs/`: when
|
|
169
|
+
* a change's REMOVED entries take a capability's last requirement, archive
|
|
170
|
+
* deletes the emptied main spec rather than aborting on a spec it cannot write
|
|
171
|
+
* (#1302). Declared rather than inferred because the delete is recoverable only
|
|
172
|
+
* from git, so it is the author's call.
|
|
173
|
+
*/
|
|
174
|
+
export function readRetireCapabilitiesMarker(changeDir) {
|
|
175
|
+
return readBooleanMarker(changeDir, 'retire_capabilities');
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Shared implementation for the boolean change-metadata markers, keyed by field
|
|
179
|
+
* name. One body rather than two, so a marker can never drift into honoring
|
|
180
|
+
* metadata the other rejects - the whole point of the contract described above.
|
|
181
|
+
*/
|
|
182
|
+
function readBooleanMarker(changeDir, key) {
|
|
183
|
+
let raw;
|
|
184
|
+
try {
|
|
185
|
+
raw = fs.readFileSync(path.join(changeDir, METADATA_FILENAME), 'utf-8');
|
|
186
|
+
}
|
|
187
|
+
catch (err) {
|
|
188
|
+
if (err?.code === 'ENOENT') {
|
|
189
|
+
return { declared: false };
|
|
190
|
+
}
|
|
191
|
+
// The file exists but cannot be read (EACCES, EISDIR, ...). Status and
|
|
192
|
+
// instructions reject the change outright here, and whether a marker is
|
|
193
|
+
// set cannot be determined - fail closed rather than let archive treat
|
|
194
|
+
// the change as unmarked while every metadata-reading surface errors.
|
|
195
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
196
|
+
return {
|
|
197
|
+
declared: false,
|
|
198
|
+
invalidReason: `the metadata file cannot be read (${message})`,
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
let parsed;
|
|
202
|
+
try {
|
|
203
|
+
parsed = yaml.parse(raw);
|
|
204
|
+
}
|
|
205
|
+
catch {
|
|
206
|
+
// Anchored so a comment like "# maybe add skip_specs later" does not
|
|
207
|
+
// claim the marker was set.
|
|
208
|
+
const mentioned = new RegExp(`^\\s*(['"]?)${key}\\1\\s*:`, 'm').test(raw);
|
|
209
|
+
return mentioned
|
|
210
|
+
? { declared: false, invalidReason: 'the file is not valid YAML' }
|
|
211
|
+
: { declared: false };
|
|
212
|
+
}
|
|
213
|
+
const result = ChangeMetadataSchema.safeParse(parsed);
|
|
214
|
+
if (result.success) {
|
|
215
|
+
if (result.data[key] !== true) {
|
|
216
|
+
return { declared: false };
|
|
217
|
+
}
|
|
218
|
+
// Schema loading is checked only when the marker is set: a broken schema
|
|
219
|
+
// on an ordinary change is status's problem to report, but honoring a
|
|
220
|
+
// marker that status rejects would let validate/archive pass what the
|
|
221
|
+
// rest of the CLI refuses to load. The membership check mirrors
|
|
222
|
+
// readChangeMetadata (which rejects names like 'spec-driven.yaml' that
|
|
223
|
+
// resolveSchema alone would normalize and accept); resolveSchema then
|
|
224
|
+
// proves the schema actually parses. Any failure fails closed.
|
|
225
|
+
try {
|
|
226
|
+
const projectRoot = path.resolve(changeDir, '../../..');
|
|
227
|
+
if (!listSchemas(projectRoot).includes(result.data.schema)) {
|
|
228
|
+
return {
|
|
229
|
+
declared: false,
|
|
230
|
+
invalidReason: `schema: unknown schema '${result.data.schema}'`,
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
resolveSchema(result.data.schema, projectRoot);
|
|
234
|
+
}
|
|
235
|
+
catch (err) {
|
|
236
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
237
|
+
return { declared: false, invalidReason: message };
|
|
238
|
+
}
|
|
239
|
+
return { declared: true };
|
|
240
|
+
}
|
|
241
|
+
// Key presence, not value: skip_specs: "yes" must surface as unhonorable,
|
|
242
|
+
// not vanish while the zero-delta guidance tells the user to set the very
|
|
243
|
+
// marker they set. An explicit skip_specs: false is the opposite of setting
|
|
244
|
+
// the marker, so it must not drag unrelated metadata problems into
|
|
245
|
+
// validate - the change simply is not marked.
|
|
246
|
+
const markerMentioned = typeof parsed === 'object' &&
|
|
247
|
+
parsed !== null &&
|
|
248
|
+
key in parsed &&
|
|
249
|
+
parsed[key] !== false;
|
|
250
|
+
if (markerMentioned) {
|
|
251
|
+
const first = result.error.issues[0];
|
|
252
|
+
const where = first.path.length > 0 ? `${first.path.join('.')}: ` : '';
|
|
253
|
+
return { declared: false, invalidReason: `${where}${first.message}` };
|
|
254
|
+
}
|
|
255
|
+
return { declared: false };
|
|
256
|
+
}
|
|
141
257
|
//# sourceMappingURL=change-metadata.js.map
|
|
@@ -31,17 +31,23 @@ export interface ValidationResult {
|
|
|
31
31
|
/**
|
|
32
32
|
* Validates that a change name follows kebab-case conventions.
|
|
33
33
|
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
34
|
+
* Uses OpenSpec's shared kebab-id grammar (the same one store ids and change
|
|
35
|
+
* metadata ids use), so a change name may:
|
|
36
|
+
* - Start with a lowercase letter or a digit
|
|
36
37
|
* - Contain only lowercase letters, numbers, and hyphens
|
|
37
|
-
* -
|
|
38
|
-
* -
|
|
38
|
+
* - Not start or end with a hyphen
|
|
39
|
+
* - Not contain consecutive hyphens
|
|
40
|
+
*
|
|
41
|
+
* A leading digit is allowed so ordering conventions like `100-add-feature` or
|
|
42
|
+
* `00001-add-auth` work; archive already treats such prefixes as a supported
|
|
43
|
+
* convention (see ARCHIVE_DATE_PREFIX_PATTERN).
|
|
39
44
|
*
|
|
40
45
|
* @param name - The change name to validate
|
|
41
46
|
* @returns Validation result with `valid: true` or `valid: false` with an error message
|
|
42
47
|
*
|
|
43
48
|
* @example
|
|
44
49
|
* validateChangeName('add-auth') // { valid: true }
|
|
50
|
+
* validateChangeName('100-add-feature') // { valid: true }
|
|
45
51
|
* validateChangeName('Add-Auth') // { valid: false, error: '...' }
|
|
46
52
|
*/
|
|
47
53
|
export declare function validateChangeName(name: string): ValidationResult;
|
|
@@ -1,32 +1,43 @@
|
|
|
1
1
|
import path from 'path';
|
|
2
2
|
import { FileSystemUtils } from './file-system.js';
|
|
3
3
|
import { writeChangeMetadata, validateSchemaName } from './change-metadata.js';
|
|
4
|
+
import { formatLocalDate } from './date.js';
|
|
4
5
|
import { readProjectConfig } from '../core/project-config.js';
|
|
6
|
+
import { isKebabId } from '../core/id.js';
|
|
5
7
|
const DEFAULT_SCHEMA = 'spec-driven';
|
|
6
8
|
/**
|
|
7
9
|
* Validates that a change name follows kebab-case conventions.
|
|
8
10
|
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
+
* Uses OpenSpec's shared kebab-id grammar (the same one store ids and change
|
|
12
|
+
* metadata ids use), so a change name may:
|
|
13
|
+
* - Start with a lowercase letter or a digit
|
|
11
14
|
* - Contain only lowercase letters, numbers, and hyphens
|
|
12
|
-
* -
|
|
13
|
-
* -
|
|
15
|
+
* - Not start or end with a hyphen
|
|
16
|
+
* - Not contain consecutive hyphens
|
|
17
|
+
*
|
|
18
|
+
* A leading digit is allowed so ordering conventions like `100-add-feature` or
|
|
19
|
+
* `00001-add-auth` work; archive already treats such prefixes as a supported
|
|
20
|
+
* convention (see ARCHIVE_DATE_PREFIX_PATTERN).
|
|
14
21
|
*
|
|
15
22
|
* @param name - The change name to validate
|
|
16
23
|
* @returns Validation result with `valid: true` or `valid: false` with an error message
|
|
17
24
|
*
|
|
18
25
|
* @example
|
|
19
26
|
* validateChangeName('add-auth') // { valid: true }
|
|
27
|
+
* validateChangeName('100-add-feature') // { valid: true }
|
|
20
28
|
* validateChangeName('Add-Auth') // { valid: false, error: '...' }
|
|
21
29
|
*/
|
|
22
30
|
export function validateChangeName(name) {
|
|
23
|
-
// Pattern: starts with lowercase letter, followed by lowercase letters/numbers,
|
|
24
|
-
// optionally followed by hyphen + lowercase letters/numbers (repeatable)
|
|
25
|
-
const kebabCasePattern = /^[a-z][a-z0-9]*(-[a-z0-9]+)*$/;
|
|
26
31
|
if (!name) {
|
|
27
32
|
return { valid: false, error: 'Change name cannot be empty' };
|
|
28
33
|
}
|
|
29
|
-
|
|
34
|
+
// Filesystem directory components cap at 255 bytes and archive prepends a
|
|
35
|
+
// date prefix; bounding here turns the failure into a validation message
|
|
36
|
+
// instead of a raw ENAMETOOLONG from mkdir.
|
|
37
|
+
if (name.length > 200) {
|
|
38
|
+
return { valid: false, error: 'Change name is too long (200 characters max)' };
|
|
39
|
+
}
|
|
40
|
+
if (!isKebabId(name)) {
|
|
30
41
|
// Provide specific error messages for common mistakes
|
|
31
42
|
if (/[A-Z]/.test(name)) {
|
|
32
43
|
return { valid: false, error: 'Change name must be lowercase (use kebab-case)' };
|
|
@@ -49,9 +60,6 @@ export function validateChangeName(name) {
|
|
|
49
60
|
if (/[^a-z0-9-]/.test(name)) {
|
|
50
61
|
return { valid: false, error: 'Change name can only contain lowercase letters, numbers, and hyphens' };
|
|
51
62
|
}
|
|
52
|
-
if (/^[0-9]/.test(name)) {
|
|
53
|
-
return { valid: false, error: 'Change name must start with a letter' };
|
|
54
|
-
}
|
|
55
63
|
return { valid: false, error: 'Change name must follow kebab-case convention (e.g., add-auth, refactor-db)' };
|
|
56
64
|
}
|
|
57
65
|
return { valid: true };
|
|
@@ -127,10 +135,9 @@ export async function createChange(projectRoot, name, options = {}) {
|
|
|
127
135
|
await FileSystemUtils.writeFile(configPath, `schema: ${defaultSchema}\n`);
|
|
128
136
|
}
|
|
129
137
|
// Write metadata file with schema and creation date
|
|
130
|
-
const today = new Date().toISOString().split('T')[0];
|
|
131
138
|
writeChangeMetadata(changeDir, {
|
|
132
139
|
schema: schemaName,
|
|
133
|
-
created:
|
|
140
|
+
created: formatLocalDate(),
|
|
134
141
|
...options.metadata,
|
|
135
142
|
}, projectRoot);
|
|
136
143
|
return { schema: schemaName, changeDir };
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CI environment detection shared by telemetry and the version check.
|
|
3
|
+
*
|
|
4
|
+
* Providers set CI to "true", "1", "yes", etc. Only an explicit off-value
|
|
5
|
+
* counts as "not CI", so an unknown value still suppresses outbound requests
|
|
6
|
+
* rather than surprising a build.
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* True when `CI` is set to anything other than an explicit off-value.
|
|
10
|
+
*/
|
|
11
|
+
export declare function isCiEnvironment(env?: NodeJS.ProcessEnv): boolean;
|
|
12
|
+
//# sourceMappingURL=ci.d.ts.map
|
package/dist/utils/ci.js
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CI environment detection shared by telemetry and the version check.
|
|
3
|
+
*
|
|
4
|
+
* Providers set CI to "true", "1", "yes", etc. Only an explicit off-value
|
|
5
|
+
* counts as "not CI", so an unknown value still suppresses outbound requests
|
|
6
|
+
* rather than surprising a build.
|
|
7
|
+
*/
|
|
8
|
+
const CI_DISABLED_VALUES = new Set(['', 'false', '0', 'no', 'off']);
|
|
9
|
+
/**
|
|
10
|
+
* True when `CI` is set to anything other than an explicit off-value.
|
|
11
|
+
*/
|
|
12
|
+
export function isCiEnvironment(env = process.env) {
|
|
13
|
+
const value = env.CI;
|
|
14
|
+
return value !== undefined && !CI_DISABLED_VALUES.has(value.trim().toLowerCase());
|
|
15
|
+
}
|
|
16
|
+
//# sourceMappingURL=ci.js.map
|
|
@@ -3,16 +3,102 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Utilities for transforming command references to tool-specific formats.
|
|
5
5
|
*/
|
|
6
|
+
import type { CommandSurfaceCapability } from '../core/command-surface.js';
|
|
7
|
+
import type { CommandInvocation } from '../core/command-generation/invocation.js';
|
|
6
8
|
/**
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
+
* Rewrites the canonical `/opsx:<command>` references that command bodies and
|
|
10
|
+
* skill templates are authored with into the form one tool actually registers
|
|
11
|
+
* — `/opsx-<command>` for tools that name the command by filename,
|
|
12
|
+
* `@opsx-<command>` for Amazon Q's prompt library.
|
|
13
|
+
*
|
|
14
|
+
* Only known command ids are rewritten, matching how
|
|
15
|
+
* `transformToSkillReferences` leaves unrecognized references alone, so a
|
|
16
|
+
* mistyped or invented `/opsx:<something>` is left as written rather than
|
|
17
|
+
* silently reshaped into a command that does not exist either.
|
|
9
18
|
*
|
|
10
19
|
* @param text - The text containing command references
|
|
11
|
-
* @
|
|
20
|
+
* @param invocation - The tool's invocation, from resolveCommandInvocation()
|
|
21
|
+
* @returns Text with command references spelled the tool's way
|
|
12
22
|
*
|
|
13
23
|
* @example
|
|
14
|
-
*
|
|
15
|
-
*
|
|
24
|
+
* transformCommandInvocations('/opsx:new', { style: 'flat', prefix: '/' }) // '/opsx-new'
|
|
25
|
+
* transformCommandInvocations('/opsx:new', { style: 'flat', prefix: '@' }) // '@opsx-new'
|
|
26
|
+
*/
|
|
27
|
+
export declare function transformCommandInvocations(text: string, invocation: CommandInvocation): string;
|
|
28
|
+
/**
|
|
29
|
+
* Whether a tool references skills by natural language rather than a slash
|
|
30
|
+
* command (see NATURAL_LANGUAGE_SKILL_TOOLS).
|
|
31
|
+
*/
|
|
32
|
+
export declare function usesNaturalLanguageSkillReferences(toolId: string): boolean;
|
|
33
|
+
/**
|
|
34
|
+
* Keeps Codex's `$<name>` spelling first while making its canonical shared
|
|
35
|
+
* `.agents` tree usable by agents that invoke the same skills with `/<name>`.
|
|
36
|
+
*/
|
|
37
|
+
export declare function transformToCodexCompatibleSkillReferences(text: string): string;
|
|
38
|
+
/**
|
|
39
|
+
* Transforms command references to skill references using the default `/`
|
|
40
|
+
* invocation prefix. Converts `/opsx:<command>` patterns to
|
|
41
|
+
* `/openspec-<skill>` so that generated skills do not reference commands
|
|
42
|
+
* that were never generated. Used for channels that are not tied to one
|
|
43
|
+
* tool (e.g. the skills.sh distribution); tool-targeted generation should
|
|
44
|
+
* go through getSkillReferenceTransformer instead.
|
|
45
|
+
*
|
|
46
|
+
* Unknown command references are left unchanged.
|
|
47
|
+
*
|
|
48
|
+
* @param text - The text containing command references
|
|
49
|
+
* @returns Text with command references transformed to skill references
|
|
50
|
+
*
|
|
51
|
+
* @example
|
|
52
|
+
* transformToSkillReferences('/opsx:apply') // returns '/openspec-apply-change'
|
|
53
|
+
* transformToSkillReferences('Use /opsx:archive next') // returns 'Use /openspec-archive-change next'
|
|
54
|
+
*/
|
|
55
|
+
export declare function transformToSkillReferences(text: string): string;
|
|
56
|
+
/**
|
|
57
|
+
* Returns the skill-reference transformer for a specific tool, honoring the
|
|
58
|
+
* tool's documented skill invocation syntax (e.g. Kimi Code's
|
|
59
|
+
* `/skill:openspec-propose`). Tools with no slash surface (e.g. Rovo Dev) get
|
|
60
|
+
* natural-language references ("the openspec-propose skill"); everything else
|
|
61
|
+
* falls back to the default `/openspec-*` form.
|
|
62
|
+
*
|
|
63
|
+
* @param toolId - The AI tool identifier (e.g. 'kimi', 'vibe', 'rovodev')
|
|
64
|
+
* @returns A transformer converting `/opsx:*` references to skill invocations
|
|
65
|
+
*/
|
|
66
|
+
export declare function getSkillReferenceTransformer(toolId: string): (text: string) => string;
|
|
67
|
+
/**
|
|
68
|
+
* Selects the command-reference transformer for a skill generation target.
|
|
69
|
+
*
|
|
70
|
+
* Skill references are used whenever the tool ends up without `/opsx:*`
|
|
71
|
+
* commands — because delivery is skills-only, because the tool has no command
|
|
72
|
+
* surface at all (capability 'none', e.g. Kimi Code or Mistral Vibe), or
|
|
73
|
+
* because the tool invokes skills directly and OpenSpec generates no command
|
|
74
|
+
* files for it (capability 'skills-invocable', i.e. Codex) — so those skills
|
|
75
|
+
* never point at commands that were not generated.
|
|
76
|
+
*
|
|
77
|
+
* When commands are generated, the spelling follows the tool's invocation: a
|
|
78
|
+
* `flat` adapter names the command by filename (`.cursor/commands/opsx-apply.md`
|
|
79
|
+
* → `/opsx-apply`), a `namespaced` adapter puts it in an `opsx/` directory
|
|
80
|
+
* (`.claude/commands/opsx/apply.md` → `/opsx:apply`), and a non-slash prefix
|
|
81
|
+
* wraps it further (`.amazonq/prompts/opsx-apply.md` → `@opsx-apply`). Passing
|
|
82
|
+
* the invocation in keeps this module free of a hand-maintained tool list —
|
|
83
|
+
* the list drifted and left 16 tools advertising commands their palettes never
|
|
84
|
+
* registered (#727, #1307).
|
|
85
|
+
*
|
|
86
|
+
* Devin is the one tool that takes skill references even though its commands
|
|
87
|
+
* are generated: only Devin Desktop reads `.devin/workflows/`, so a workflow
|
|
88
|
+
* reference is dead text for anyone on Devin Local, while the `/openspec-*`
|
|
89
|
+
* skills work on both agents. Under commands-only delivery there are no Devin
|
|
90
|
+
* skills to point at, so it falls through to the invocation rewrite below and
|
|
91
|
+
* gets the `/opsx-<id>` form its workflow filenames register.
|
|
92
|
+
*
|
|
93
|
+
* @param toolId - The AI tool identifier (e.g. 'claude', 'opencode', 'pi')
|
|
94
|
+
* @param delivery - The configured delivery mode
|
|
95
|
+
* @param capability - The tool's command surface capability
|
|
96
|
+
* @param invocation - How the tool's generated commands are invoked, from
|
|
97
|
+
* resolveCommandInvocation(); undefined for tools with no command
|
|
98
|
+
* adapter. Required rather than optional so a caller that forgets it
|
|
99
|
+
* fails to compile instead of silently getting the canonical form.
|
|
100
|
+
* @returns The transformer to pass to generateSkillContent, or undefined when
|
|
101
|
+
* the tool already answers to the canonical `/opsx:<id>`
|
|
16
102
|
*/
|
|
17
|
-
export declare function
|
|
103
|
+
export declare function getTransformerForTool(toolId: string, delivery: 'both' | 'skills' | 'commands', capability: CommandSurfaceCapability, invocation: CommandInvocation | undefined): ((text: string) => string) | undefined;
|
|
18
104
|
//# sourceMappingURL=command-references.d.ts.map
|
|
@@ -3,18 +3,191 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Utilities for transforming command references to tool-specific formats.
|
|
5
5
|
*/
|
|
6
|
+
// Value import of a pure, dependency-free helper: invocation.ts imports only
|
|
7
|
+
// `path` and a type, so this does not close the cycle the note above guards.
|
|
8
|
+
import { formatCommandInvocation, needsInvocationRewrite, } from '../core/command-generation/invocation.js';
|
|
6
9
|
/**
|
|
7
|
-
*
|
|
8
|
-
*
|
|
10
|
+
* Rewrites the canonical `/opsx:<command>` references that command bodies and
|
|
11
|
+
* skill templates are authored with into the form one tool actually registers
|
|
12
|
+
* — `/opsx-<command>` for tools that name the command by filename,
|
|
13
|
+
* `@opsx-<command>` for Amazon Q's prompt library.
|
|
14
|
+
*
|
|
15
|
+
* Only known command ids are rewritten, matching how
|
|
16
|
+
* `transformToSkillReferences` leaves unrecognized references alone, so a
|
|
17
|
+
* mistyped or invented `/opsx:<something>` is left as written rather than
|
|
18
|
+
* silently reshaped into a command that does not exist either.
|
|
19
|
+
*
|
|
20
|
+
* @param text - The text containing command references
|
|
21
|
+
* @param invocation - The tool's invocation, from resolveCommandInvocation()
|
|
22
|
+
* @returns Text with command references spelled the tool's way
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* transformCommandInvocations('/opsx:new', { style: 'flat', prefix: '/' }) // '/opsx-new'
|
|
26
|
+
* transformCommandInvocations('/opsx:new', { style: 'flat', prefix: '@' }) // '@opsx-new'
|
|
27
|
+
*/
|
|
28
|
+
export function transformCommandInvocations(text, invocation) {
|
|
29
|
+
return text.replace(/\/opsx:([a-z-]+)/g, (match, commandId) => commandId in COMMAND_TO_SKILL_NAME
|
|
30
|
+
? formatCommandInvocation(invocation, commandId)
|
|
31
|
+
: match);
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Maps command short names to their skill names.
|
|
35
|
+
* Keep in sync with WORKFLOW_TO_SKILL_DIR, which exists in both
|
|
36
|
+
* src/core/profile-sync-drift.ts (exported) and src/core/init.ts (local copy).
|
|
37
|
+
*/
|
|
38
|
+
const COMMAND_TO_SKILL_NAME = {
|
|
39
|
+
'explore': 'openspec-explore',
|
|
40
|
+
'new': 'openspec-new-change',
|
|
41
|
+
'continue': 'openspec-continue-change',
|
|
42
|
+
'apply': 'openspec-apply-change',
|
|
43
|
+
'update': 'openspec-update-change',
|
|
44
|
+
'ff': 'openspec-ff-change',
|
|
45
|
+
'sync': 'openspec-sync-specs',
|
|
46
|
+
'archive': 'openspec-archive-change',
|
|
47
|
+
'bulk-archive': 'openspec-bulk-archive-change',
|
|
48
|
+
'verify': 'openspec-verify-change',
|
|
49
|
+
'onboard': 'openspec-onboard',
|
|
50
|
+
'propose': 'openspec-propose',
|
|
51
|
+
};
|
|
52
|
+
/**
|
|
53
|
+
* Tools whose skill invocation uses a non-default prefix. The default is `/`
|
|
54
|
+
* (e.g. `/openspec-propose`); Kimi Code invokes skills as `/skill:<name>` and
|
|
55
|
+
* Codex CLI as `$<name>` — a `/<name>` form Codex does not recognize
|
|
56
|
+
* (see docs/supported-tools.md).
|
|
57
|
+
*/
|
|
58
|
+
const SKILL_INVOCATION_PREFIX = {
|
|
59
|
+
kimi: '/skill:',
|
|
60
|
+
codex: '$',
|
|
61
|
+
};
|
|
62
|
+
/**
|
|
63
|
+
* Tools that have no slash-command surface at all: skills are matched
|
|
64
|
+
* automatically or invoked by natural-language prompts, never by typing a
|
|
65
|
+
* `/<name>` command. Rovo Dev CLI is such a tool — `/skills` only manages
|
|
66
|
+
* skills, and any `/openspec-*` form would be a dead command (see
|
|
67
|
+
* docs/supported-tools.md). References for these tools are spelled as prose
|
|
68
|
+
* ("the openspec-propose skill") so generated content never tells the user to
|
|
69
|
+
* type a command their CLI does not register.
|
|
70
|
+
*/
|
|
71
|
+
const NATURAL_LANGUAGE_SKILL_TOOLS = new Set(['rovodev']);
|
|
72
|
+
/**
|
|
73
|
+
* Whether a tool references skills by natural language rather than a slash
|
|
74
|
+
* command (see NATURAL_LANGUAGE_SKILL_TOOLS).
|
|
75
|
+
*/
|
|
76
|
+
export function usesNaturalLanguageSkillReferences(toolId) {
|
|
77
|
+
return NATURAL_LANGUAGE_SKILL_TOOLS.has(toolId);
|
|
78
|
+
}
|
|
79
|
+
function replaceCommandsWithNaturalLanguageSkillReferences(text) {
|
|
80
|
+
return text.replace(/\/opsx:([a-z-]+)/g, (match, commandId) => {
|
|
81
|
+
const skillName = COMMAND_TO_SKILL_NAME[commandId];
|
|
82
|
+
return skillName === undefined ? match : `the ${skillName} skill`;
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
function replaceCommandsWithSkillReferences(text, prefix) {
|
|
86
|
+
return text.replace(/\/opsx:([a-z-]+)/g, (match, commandId) => {
|
|
87
|
+
const skillName = COMMAND_TO_SKILL_NAME[commandId];
|
|
88
|
+
return skillName === undefined ? match : `${prefix}${skillName}`;
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Keeps Codex's `$<name>` spelling first while making its canonical shared
|
|
93
|
+
* `.agents` tree usable by agents that invoke the same skills with `/<name>`.
|
|
94
|
+
*/
|
|
95
|
+
export function transformToCodexCompatibleSkillReferences(text) {
|
|
96
|
+
return text.replace(/\/opsx:([a-z-]+)/g, (match, commandId) => {
|
|
97
|
+
const skillName = COMMAND_TO_SKILL_NAME[commandId];
|
|
98
|
+
return skillName === undefined
|
|
99
|
+
? match
|
|
100
|
+
: `$${skillName} (Codex) or /${skillName} (other agents)`;
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Transforms command references to skill references using the default `/`
|
|
105
|
+
* invocation prefix. Converts `/opsx:<command>` patterns to
|
|
106
|
+
* `/openspec-<skill>` so that generated skills do not reference commands
|
|
107
|
+
* that were never generated. Used for channels that are not tied to one
|
|
108
|
+
* tool (e.g. the skills.sh distribution); tool-targeted generation should
|
|
109
|
+
* go through getSkillReferenceTransformer instead.
|
|
110
|
+
*
|
|
111
|
+
* Unknown command references are left unchanged.
|
|
9
112
|
*
|
|
10
113
|
* @param text - The text containing command references
|
|
11
|
-
* @returns Text with command references transformed to
|
|
114
|
+
* @returns Text with command references transformed to skill references
|
|
12
115
|
*
|
|
13
116
|
* @example
|
|
14
|
-
*
|
|
15
|
-
*
|
|
117
|
+
* transformToSkillReferences('/opsx:apply') // returns '/openspec-apply-change'
|
|
118
|
+
* transformToSkillReferences('Use /opsx:archive next') // returns 'Use /openspec-archive-change next'
|
|
119
|
+
*/
|
|
120
|
+
export function transformToSkillReferences(text) {
|
|
121
|
+
return replaceCommandsWithSkillReferences(text, '/');
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Returns the skill-reference transformer for a specific tool, honoring the
|
|
125
|
+
* tool's documented skill invocation syntax (e.g. Kimi Code's
|
|
126
|
+
* `/skill:openspec-propose`). Tools with no slash surface (e.g. Rovo Dev) get
|
|
127
|
+
* natural-language references ("the openspec-propose skill"); everything else
|
|
128
|
+
* falls back to the default `/openspec-*` form.
|
|
129
|
+
*
|
|
130
|
+
* @param toolId - The AI tool identifier (e.g. 'kimi', 'vibe', 'rovodev')
|
|
131
|
+
* @returns A transformer converting `/opsx:*` references to skill invocations
|
|
132
|
+
*/
|
|
133
|
+
export function getSkillReferenceTransformer(toolId) {
|
|
134
|
+
if (usesNaturalLanguageSkillReferences(toolId)) {
|
|
135
|
+
return replaceCommandsWithNaturalLanguageSkillReferences;
|
|
136
|
+
}
|
|
137
|
+
const prefix = SKILL_INVOCATION_PREFIX[toolId];
|
|
138
|
+
if (prefix === undefined) {
|
|
139
|
+
return transformToSkillReferences;
|
|
140
|
+
}
|
|
141
|
+
return (text) => replaceCommandsWithSkillReferences(text, prefix);
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Selects the command-reference transformer for a skill generation target.
|
|
145
|
+
*
|
|
146
|
+
* Skill references are used whenever the tool ends up without `/opsx:*`
|
|
147
|
+
* commands — because delivery is skills-only, because the tool has no command
|
|
148
|
+
* surface at all (capability 'none', e.g. Kimi Code or Mistral Vibe), or
|
|
149
|
+
* because the tool invokes skills directly and OpenSpec generates no command
|
|
150
|
+
* files for it (capability 'skills-invocable', i.e. Codex) — so those skills
|
|
151
|
+
* never point at commands that were not generated.
|
|
152
|
+
*
|
|
153
|
+
* When commands are generated, the spelling follows the tool's invocation: a
|
|
154
|
+
* `flat` adapter names the command by filename (`.cursor/commands/opsx-apply.md`
|
|
155
|
+
* → `/opsx-apply`), a `namespaced` adapter puts it in an `opsx/` directory
|
|
156
|
+
* (`.claude/commands/opsx/apply.md` → `/opsx:apply`), and a non-slash prefix
|
|
157
|
+
* wraps it further (`.amazonq/prompts/opsx-apply.md` → `@opsx-apply`). Passing
|
|
158
|
+
* the invocation in keeps this module free of a hand-maintained tool list —
|
|
159
|
+
* the list drifted and left 16 tools advertising commands their palettes never
|
|
160
|
+
* registered (#727, #1307).
|
|
161
|
+
*
|
|
162
|
+
* Devin is the one tool that takes skill references even though its commands
|
|
163
|
+
* are generated: only Devin Desktop reads `.devin/workflows/`, so a workflow
|
|
164
|
+
* reference is dead text for anyone on Devin Local, while the `/openspec-*`
|
|
165
|
+
* skills work on both agents. Under commands-only delivery there are no Devin
|
|
166
|
+
* skills to point at, so it falls through to the invocation rewrite below and
|
|
167
|
+
* gets the `/opsx-<id>` form its workflow filenames register.
|
|
168
|
+
*
|
|
169
|
+
* @param toolId - The AI tool identifier (e.g. 'claude', 'opencode', 'pi')
|
|
170
|
+
* @param delivery - The configured delivery mode
|
|
171
|
+
* @param capability - The tool's command surface capability
|
|
172
|
+
* @param invocation - How the tool's generated commands are invoked, from
|
|
173
|
+
* resolveCommandInvocation(); undefined for tools with no command
|
|
174
|
+
* adapter. Required rather than optional so a caller that forgets it
|
|
175
|
+
* fails to compile instead of silently getting the canonical form.
|
|
176
|
+
* @returns The transformer to pass to generateSkillContent, or undefined when
|
|
177
|
+
* the tool already answers to the canonical `/opsx:<id>`
|
|
16
178
|
*/
|
|
17
|
-
export function
|
|
18
|
-
|
|
179
|
+
export function getTransformerForTool(toolId, delivery, capability, invocation) {
|
|
180
|
+
if (delivery === 'skills' || capability !== 'adapter-backed') {
|
|
181
|
+
return toolId === 'codex'
|
|
182
|
+
? transformToCodexCompatibleSkillReferences
|
|
183
|
+
: getSkillReferenceTransformer(toolId);
|
|
184
|
+
}
|
|
185
|
+
if (toolId === 'devin' && delivery === 'both') {
|
|
186
|
+
return getSkillReferenceTransformer(toolId);
|
|
187
|
+
}
|
|
188
|
+
if (invocation !== undefined && needsInvocationRewrite(invocation)) {
|
|
189
|
+
return (text) => transformCommandInvocations(text, invocation);
|
|
190
|
+
}
|
|
191
|
+
return undefined;
|
|
19
192
|
}
|
|
20
193
|
//# sourceMappingURL=command-references.js.map
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Formats a date using the effective local time zone of the Node.js process.
|
|
3
|
+
*
|
|
4
|
+
* The result is locale-independent and suitable for date-only metadata and
|
|
5
|
+
* path prefixes.
|
|
6
|
+
*/
|
|
7
|
+
export declare function formatLocalDate(date?: Date): string;
|
|
8
|
+
//# sourceMappingURL=date.d.ts.map
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Formats a date using the effective local time zone of the Node.js process.
|
|
3
|
+
*
|
|
4
|
+
* The result is locale-independent and suitable for date-only metadata and
|
|
5
|
+
* path prefixes.
|
|
6
|
+
*/
|
|
7
|
+
export function formatLocalDate(date = new Date()) {
|
|
8
|
+
const year = date.getFullYear();
|
|
9
|
+
const month = String(date.getMonth() + 1).padStart(2, '0');
|
|
10
|
+
const day = String(date.getDate()).padStart(2, '0');
|
|
11
|
+
return `${year}-${month}-${day}`;
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=date.js.map
|
|
@@ -9,6 +9,16 @@ export declare class FileSystemUtils {
|
|
|
9
9
|
* Falls back to path.resolve() so callers can still produce a stable absolute path.
|
|
10
10
|
*/
|
|
11
11
|
static canonicalizeExistingPath(targetPath: string): string;
|
|
12
|
+
/**
|
|
13
|
+
* Refuses a target that leaves an allowed directory, including through an
|
|
14
|
+
* existing symlink in either the target or one of its parent directories.
|
|
15
|
+
* Missing suffixes are resolved from their nearest existing ancestor.
|
|
16
|
+
*/
|
|
17
|
+
static assertPathWithin(allowedDirectory: string, targetPath: string): void;
|
|
18
|
+
static resolveProjectArtifactPath(projectPath: string, artifactPath: string): string;
|
|
19
|
+
static assertProjectArtifactPath(projectPath: string, targetPath: string): void;
|
|
20
|
+
private static isPathWithin;
|
|
21
|
+
private static canonicalizePotentialPath;
|
|
12
22
|
private static isWindowsBasePath;
|
|
13
23
|
private static normalizeSegments;
|
|
14
24
|
static joinPath(basePath: string, ...segments: string[]): string;
|