@fission-ai/openspec 1.13.1 → 1.14.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.
Files changed (85) hide show
  1. package/README.md +15 -3
  2. package/dist/cli/index.js +39 -1
  3. package/dist/commands/spec.js +1 -0
  4. package/dist/commands/workflow/instructions.js +66 -18
  5. package/dist/commands/workflow/shared.d.ts +7 -0
  6. package/dist/commands/workflow/status.js +15 -2
  7. package/dist/core/archive.js +258 -78
  8. package/dist/core/artifact-graph/instruction-loader.d.ts +7 -0
  9. package/dist/core/artifact-graph/instruction-loader.js +18 -1
  10. package/dist/core/artifact-graph/outputs.d.ts +1 -1
  11. package/dist/core/artifact-graph/outputs.js +38 -8
  12. package/dist/core/change-metadata/schema.d.ts +3 -0
  13. package/dist/core/change-metadata/schema.js +10 -0
  14. package/dist/core/change-status-policy.d.ts +8 -0
  15. package/dist/core/change-status-policy.js +33 -2
  16. package/dist/core/command-generation/adapters/atomcode.d.ts +24 -0
  17. package/dist/core/command-generation/adapters/atomcode.js +46 -0
  18. package/dist/core/command-generation/adapters/codestudio.d.ts +13 -0
  19. package/dist/core/command-generation/adapters/codestudio.js +27 -0
  20. package/dist/core/command-generation/adapters/continue.js +4 -0
  21. package/dist/core/command-generation/adapters/easycode.d.ts +17 -0
  22. package/dist/core/command-generation/adapters/easycode.js +28 -0
  23. package/dist/core/command-generation/adapters/gemini.js +1 -35
  24. package/dist/core/command-generation/adapters/gigacode.d.ts +17 -0
  25. package/dist/core/command-generation/adapters/gigacode.js +31 -0
  26. package/dist/core/command-generation/adapters/index.d.ts +4 -0
  27. package/dist/core/command-generation/adapters/index.js +4 -0
  28. package/dist/core/command-generation/adapters/kilocode.d.ts +1 -1
  29. package/dist/core/command-generation/adapters/kilocode.js +2 -2
  30. package/dist/core/command-generation/registry.js +8 -0
  31. package/dist/core/command-generation/toml.d.ts +19 -0
  32. package/dist/core/command-generation/toml.js +39 -0
  33. package/dist/core/command-surface.js +1 -1
  34. package/dist/core/completions/command-registry.js +19 -0
  35. package/dist/core/completions/installers/zsh-installer.js +4 -2
  36. package/dist/core/config-prompts.js +6 -6
  37. package/dist/core/config.js +11 -1
  38. package/dist/core/init.js +44 -14
  39. package/dist/core/legacy-cleanup.d.ts +1 -1
  40. package/dist/core/legacy-cleanup.js +40 -11
  41. package/dist/core/list.d.ts +2 -0
  42. package/dist/core/list.js +49 -23
  43. package/dist/core/parsers/markdown-parser.js +5 -0
  44. package/dist/core/parsers/requirement-blocks.d.ts +40 -3
  45. package/dist/core/parsers/requirement-blocks.js +74 -25
  46. package/dist/core/project-config.js +45 -1
  47. package/dist/core/references.js +3 -1
  48. package/dist/core/root-selection.d.ts +6 -0
  49. package/dist/core/root-selection.js +16 -0
  50. package/dist/core/schemas/base.schema.d.ts +3 -0
  51. package/dist/core/schemas/base.schema.js +6 -0
  52. package/dist/core/schemas/change.schema.d.ts +8 -0
  53. package/dist/core/schemas/spec.schema.d.ts +2 -0
  54. package/dist/core/specs-apply.d.ts +20 -0
  55. package/dist/core/specs-apply.js +19 -6
  56. package/dist/core/templates/workflows/apply-change.js +5 -2
  57. package/dist/core/templates/workflows/archive-change.js +56 -20
  58. package/dist/core/templates/workflows/bulk-archive-change.js +34 -16
  59. package/dist/core/templates/workflows/continue-change.js +0 -2
  60. package/dist/core/templates/workflows/explore.js +2 -2
  61. package/dist/core/templates/workflows/ff-change.js +2 -2
  62. package/dist/core/templates/workflows/onboard.js +9 -4
  63. package/dist/core/templates/workflows/update-change.js +21 -9
  64. package/dist/core/templates/workflows/verify-change.js +115 -52
  65. package/dist/core/update.js +20 -7
  66. package/dist/core/validation/constants.d.ts +1 -1
  67. package/dist/core/validation/constants.js +1 -1
  68. package/dist/core/validation/purpose-placeholder.js +24 -9
  69. package/dist/core/validation/validator.js +15 -6
  70. package/dist/core/version-check.d.ts +23 -1
  71. package/dist/core/version-check.js +112 -26
  72. package/dist/core/view.d.ts +2 -0
  73. package/dist/core/view.js +62 -4
  74. package/dist/utils/change-metadata.d.ts +20 -1
  75. package/dist/utils/change-metadata.js +57 -1
  76. package/dist/utils/command-references.d.ts +1 -1
  77. package/dist/utils/command-references.js +2 -2
  78. package/dist/utils/file-system.js +20 -3
  79. package/dist/utils/index.d.ts +1 -1
  80. package/dist/utils/index.js +1 -1
  81. package/dist/utils/line-endings.d.ts +34 -0
  82. package/dist/utils/line-endings.js +46 -0
  83. package/package.json +2 -2
  84. package/schemas/spec-driven/schema.yaml +10 -1
  85. package/schemas/spec-driven/templates/proposal.md +6 -3
@@ -10,14 +10,45 @@ export function summarizePlanningHome(planningHome) {
10
10
  };
11
11
  }
12
12
  export function buildActionContext(input) {
13
+ const scope = editScope(input);
14
+ // Keys stay in the published contract order.
13
15
  return {
14
16
  mode: 'repo-local',
15
17
  sourceOfTruth: 'repo',
16
18
  planningArtifacts: input.artifactIds,
17
19
  linkedContext: [],
18
- allowedEditRoots: [input.projectRoot],
20
+ allowedEditRoots: scope.allowedEditRoots,
19
21
  requiresAffectedAreaSelection: false,
20
- constraints: ['Repo-local change artifacts and implementation edits are scoped to this project.'],
22
+ constraints: scope.constraints,
23
+ };
24
+ }
25
+ /**
26
+ * A store holds planning artifacts only. The CLI does not route tasks to
27
+ * repos, so it names the declaring project on the current path as the edit
28
+ * root and has the agent ask before going anywhere else (#2013).
29
+ */
30
+ function editScope(input) {
31
+ if (!input.store) {
32
+ return {
33
+ allowedEditRoots: [input.projectRoot],
34
+ constraints: ['Repo-local change artifacts and implementation edits are scoped to this project.'],
35
+ };
36
+ }
37
+ const planning = `Change artifacts live in store '${input.store.id}' (${input.projectRoot}).`;
38
+ const { implementationRoot } = input.store;
39
+ if (implementationRoot) {
40
+ return {
41
+ allowedEditRoots: [implementationRoot, input.projectRoot],
42
+ constraints: [
43
+ `${planning} Implementation edits go in ${implementationRoot}, the project on the current path that declares this store; ask the user before editing any other repository.`,
44
+ ],
45
+ };
46
+ }
47
+ return {
48
+ allowedEditRoots: [input.projectRoot],
49
+ constraints: [
50
+ `${planning} OpenSpec could not determine which repository implements this change; ask the user which repository to edit, and make implementation edits there.`,
51
+ ],
21
52
  };
22
53
  }
23
54
  export function resolveNextStep(input) {
@@ -0,0 +1,24 @@
1
+ /**
2
+ * AtomCode Command Adapter
3
+ *
4
+ * Formats project commands for AtomCode.
5
+ * https://github.com/atomgit-atomcode/atomcode#custom-commands
6
+ */
7
+ import type { ToolCommandAdapter } from '../types.js';
8
+ /**
9
+ * AtomCode adapter for command generation.
10
+ * File path: .atomcode/commands/opsx-<id>.md
11
+ * Frontmatter: name, description, args
12
+ *
13
+ * AtomCode's custom-command parser reads name and args literally without YAML
14
+ * unquoting, so these controlled identifiers must stay unquoted.
15
+ * The command name matches the filename.
16
+ *
17
+ * `args` mirrors what the workflow actually accepts. AtomCode executes an
18
+ * `args: none` command straight from the slash menu, while `optional` completes
19
+ * to `/name ` and waits for a second Enter. Advertising arguments a workflow
20
+ * never reads would cost every user that extra keystroke, so only workflows
21
+ * carrying an `**Input**:` contract declare `optional` and receive $ARGUMENTS.
22
+ */
23
+ export declare const atomcodeAdapter: ToolCommandAdapter;
24
+ //# sourceMappingURL=atomcode.d.ts.map
@@ -0,0 +1,46 @@
1
+ /**
2
+ * AtomCode Command Adapter
3
+ *
4
+ * Formats project commands for AtomCode.
5
+ * https://github.com/atomgit-atomcode/atomcode#custom-commands
6
+ */
7
+ import path from 'path';
8
+ import { stringify } from 'yaml';
9
+ /** A workflow declares its invocation input with an `**Input**:` heading. */
10
+ const INPUT_HEADING = /^\*\*Input\*\*:/m;
11
+ /**
12
+ * AtomCode adapter for command generation.
13
+ * File path: .atomcode/commands/opsx-<id>.md
14
+ * Frontmatter: name, description, args
15
+ *
16
+ * AtomCode's custom-command parser reads name and args literally without YAML
17
+ * unquoting, so these controlled identifiers must stay unquoted.
18
+ * The command name matches the filename.
19
+ *
20
+ * `args` mirrors what the workflow actually accepts. AtomCode executes an
21
+ * `args: none` command straight from the slash menu, while `optional` completes
22
+ * to `/name ` and waits for a second Enter. Advertising arguments a workflow
23
+ * never reads would cost every user that extra keystroke, so only workflows
24
+ * carrying an `**Input**:` contract declare `optional` and receive $ARGUMENTS.
25
+ */
26
+ export const atomcodeAdapter = {
27
+ toolId: 'atomcode',
28
+ getFilePath(commandId) {
29
+ return path.join('.atomcode', 'commands', `opsx-${commandId}.md`);
30
+ },
31
+ formatFile(content) {
32
+ // Keep ordinary descriptions plain for the literal custom-command parser,
33
+ // while keeping special values valid YAML for frontmatter consumers.
34
+ const description = stringify({ description: content.description }, { lineWidth: 0, blockQuote: false });
35
+ const acceptsInput = INPUT_HEADING.test(content.body);
36
+ const argumentsBlock = acceptsInput ? '\n**Provided arguments**: $ARGUMENTS\n' : '';
37
+ return `---
38
+ name: opsx-${content.id}
39
+ ${description}args: ${acceptsInput ? 'optional' : 'none'}
40
+ ---
41
+ ${argumentsBlock}
42
+ ${content.body}
43
+ `;
44
+ },
45
+ };
46
+ //# sourceMappingURL=atomcode.js.map
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Code Studio Command Adapter
3
+ *
4
+ * Formats commands for Syncfusion Code Studio following its .prompt.md specification.
5
+ */
6
+ import type { ToolCommandAdapter } from '../types.js';
7
+ /**
8
+ * Code Studio adapter for command generation.
9
+ * File path: .codestudio/prompts/opsx-<id>.prompt.md
10
+ * Frontmatter: description
11
+ */
12
+ export declare const codeStudioAdapter: ToolCommandAdapter;
13
+ //# sourceMappingURL=codestudio.d.ts.map
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Code Studio Command Adapter
3
+ *
4
+ * Formats commands for Syncfusion Code Studio following its .prompt.md specification.
5
+ */
6
+ import path from 'path';
7
+ import { escapeYamlValue } from '../yaml.js';
8
+ /**
9
+ * Code Studio adapter for command generation.
10
+ * File path: .codestudio/prompts/opsx-<id>.prompt.md
11
+ * Frontmatter: description
12
+ */
13
+ export const codeStudioAdapter = {
14
+ toolId: 'codestudio',
15
+ getFilePath(commandId) {
16
+ return path.join('.codestudio', 'prompts', `opsx-${commandId}.prompt.md`);
17
+ },
18
+ formatFile(content) {
19
+ return `---
20
+ description: ${escapeYamlValue(content.description)}
21
+ ---
22
+
23
+ ${content.body}
24
+ `;
25
+ },
26
+ };
27
+ //# sourceMappingURL=codestudio.js.map
@@ -16,12 +16,16 @@ export const continueAdapter = {
16
16
  return path.join('.continue', 'prompts', `opsx-${commandId}.prompt`);
17
17
  },
18
18
  formatFile(content) {
19
+ // Continue injects an invoked prompt into the model context. Smaller local
20
+ // models can otherwise mistake the workflow name for a tool to call (#925).
19
21
  return `---
20
22
  name: ${escapeYamlValue(`opsx-${content.id}`)}
21
23
  description: ${escapeYamlValue(content.description)}
22
24
  invokable: true
23
25
  ---
24
26
 
27
+ This workflow prompt is already active. Follow its instructions directly. Do not call a tool named after this workflow.
28
+
25
29
  ${content.body}
26
30
  `;
27
31
  },
@@ -0,0 +1,17 @@
1
+ /**
2
+ * EasyCode Command Adapter
3
+ *
4
+ * Formats commands for OrionStarAI/EasyCode using its TOML command format.
5
+ * https://github.com/OrionStarAI/EasyCode
6
+ */
7
+ import type { ToolCommandAdapter } from '../types.js';
8
+ /**
9
+ * EasyCode adapter for command generation.
10
+ * File path: .easycode/commands/opsx/<id>.toml
11
+ *
12
+ * Format:
13
+ * description = "<basic-string>" single-line, backslash/quote-safe
14
+ * prompt = """<multiline-string>""" multiline, backslash/triple-quote-safe
15
+ */
16
+ export declare const easycodeAdapter: ToolCommandAdapter;
17
+ //# sourceMappingURL=easycode.d.ts.map
@@ -0,0 +1,28 @@
1
+ /**
2
+ * EasyCode Command Adapter
3
+ *
4
+ * Formats commands for OrionStarAI/EasyCode using its TOML command format.
5
+ * https://github.com/OrionStarAI/EasyCode
6
+ */
7
+ import path from 'path';
8
+ import { escapeTomlBasicString, escapeTomlMultilineBasicString } from '../toml.js';
9
+ /**
10
+ * EasyCode adapter for command generation.
11
+ * File path: .easycode/commands/opsx/<id>.toml
12
+ *
13
+ * Format:
14
+ * description = "<basic-string>" single-line, backslash/quote-safe
15
+ * prompt = """<multiline-string>""" multiline, backslash/triple-quote-safe
16
+ */
17
+ export const easycodeAdapter = {
18
+ toolId: 'easycode',
19
+ getFilePath(commandId) {
20
+ return path.join('.easycode', 'commands', 'opsx', `${commandId}.toml`);
21
+ },
22
+ formatFile(content) {
23
+ const safeDesc = escapeTomlBasicString(content.description);
24
+ const safeBody = escapeTomlMultilineBasicString(content.body);
25
+ return `description = "${safeDesc}"\n\nprompt = """\n${safeBody}\n"""\n`;
26
+ },
27
+ };
28
+ //# sourceMappingURL=easycode.js.map
@@ -4,41 +4,7 @@
4
4
  * Formats commands for Gemini CLI following its TOML specification.
5
5
  */
6
6
  import path from 'path';
7
- /**
8
- * Control characters (C0 except tab/newline/carriage return, plus DEL) are
9
- * invalid inside TOML strings and must be written as escapes.
10
- */
11
- const TOML_CONTROL_CHARS = new RegExp('[\\u0000-\\u0008\\u000b\\u000c\\u000e-\\u001f\\u007f]', 'g');
12
- /**
13
- * TOML basic strings are escape-active: a backslash or double quote in the
14
- * value breaks the file if written raw. Newlines cannot appear in a
15
- * single-line basic string at all, so they are escaped too.
16
- */
17
- function escapeTomlBasicString(value) {
18
- return value
19
- .replace(/\\/g, '\\\\')
20
- .replace(/"/g, '\\"')
21
- .replace(/\n/g, '\\n')
22
- .replace(/\r/g, '\\r')
23
- .replace(/\t/g, '\\t')
24
- .replace(TOML_CONTROL_CHARS, (c) => `\\u${c.charCodeAt(0).toString(16).padStart(4, '0')}`);
25
- }
26
- /**
27
- * Multiline basic strings keep raw newlines and tabs, but backslashes are
28
- * still escape-active, any run of three quotes would end the string, and the
29
- * same control characters are invalid as in single-line basic strings — a
30
- * lone carriage return included (only LF and CRLF may appear raw; CRLF is
31
- * normalized away so the emitted file is single-convention). Escapes are
32
- * introduced after backslash-doubling so they are not re-doubled.
33
- */
34
- function escapeTomlMultilineBasicString(value) {
35
- return value
36
- .replace(/\r\n/g, '\n')
37
- .replace(/\\/g, '\\\\')
38
- .replace(/"""/g, '""\\"')
39
- .replace(/\r/g, '\\r')
40
- .replace(TOML_CONTROL_CHARS, (c) => `\\u${c.charCodeAt(0).toString(16).padStart(4, '0')}`);
41
- }
7
+ import { escapeTomlBasicString, escapeTomlMultilineBasicString } from '../toml.js';
42
8
  /**
43
9
  * Gemini adapter for command generation.
44
10
  * File path: .gemini/commands/opsx/<id>.toml
@@ -0,0 +1,17 @@
1
+ /**
2
+ * GigaCode Command Adapter
3
+ *
4
+ * Formats commands for GigaCode using its Markdown custom command format.
5
+ * Project commands live in `.gigacode/commands/` and accept an optional
6
+ * `description` field in YAML frontmatter.
7
+ *
8
+ * @see https://gitverse.ru/docs/ai/ai-assistant-gigacode/gigacode-cli/commands
9
+ */
10
+ import type { ToolCommandAdapter } from '../types.js';
11
+ /**
12
+ * GigaCode adapter for command generation.
13
+ * File path: .gigacode/commands/opsx-<id>.md
14
+ * Format: Markdown with description frontmatter
15
+ */
16
+ export declare const gigacodeAdapter: ToolCommandAdapter;
17
+ //# sourceMappingURL=gigacode.d.ts.map
@@ -0,0 +1,31 @@
1
+ /**
2
+ * GigaCode Command Adapter
3
+ *
4
+ * Formats commands for GigaCode using its Markdown custom command format.
5
+ * Project commands live in `.gigacode/commands/` and accept an optional
6
+ * `description` field in YAML frontmatter.
7
+ *
8
+ * @see https://gitverse.ru/docs/ai/ai-assistant-gigacode/gigacode-cli/commands
9
+ */
10
+ import path from 'path';
11
+ import { escapeYamlValue } from '../yaml.js';
12
+ /**
13
+ * GigaCode adapter for command generation.
14
+ * File path: .gigacode/commands/opsx-<id>.md
15
+ * Format: Markdown with description frontmatter
16
+ */
17
+ export const gigacodeAdapter = {
18
+ toolId: 'gigacode',
19
+ getFilePath(commandId) {
20
+ return path.join('.gigacode', 'commands', `opsx-${commandId}.md`);
21
+ },
22
+ formatFile(content) {
23
+ return `---
24
+ description: ${escapeYamlValue(content.description)}
25
+ ---
26
+
27
+ ${content.body}
28
+ `;
29
+ },
30
+ };
31
+ //# sourceMappingURL=gigacode.js.map
@@ -5,20 +5,24 @@
5
5
  */
6
6
  export { amazonQAdapter } from './amazon-q.js';
7
7
  export { antigravityAdapter } from './antigravity.js';
8
+ export { atomcodeAdapter } from './atomcode.js';
8
9
  export { auggieAdapter } from './auggie.js';
9
10
  export { bobAdapter } from './bob.js';
10
11
  export { claudeAdapter } from './claude.js';
11
12
  export { clineAdapter } from './cline.js';
12
13
  export { commandCodeAdapter } from './command-code.js';
13
14
  export { codebuddyAdapter } from './codebuddy.js';
15
+ export { codeStudioAdapter } from './codestudio.js';
14
16
  export { continueAdapter } from './continue.js';
15
17
  export { costrictAdapter } from './costrict.js';
16
18
  export { crushAdapter } from './crush.js';
17
19
  export { cursorAdapter } from './cursor.js';
18
20
  export { devinAdapter } from './devin.js';
21
+ export { easycodeAdapter } from './easycode.js';
19
22
  export { factoryAdapter } from './factory.js';
20
23
  export { geminiAdapter } from './gemini.js';
21
24
  export { githubCopilotAdapter } from './github-copilot.js';
25
+ export { gigacodeAdapter } from './gigacode.js';
22
26
  export { iflowAdapter } from './iflow.js';
23
27
  export { junieAdapter } from './junie.js';
24
28
  export { kilocodeAdapter } from './kilocode.js';
@@ -5,20 +5,24 @@
5
5
  */
6
6
  export { amazonQAdapter } from './amazon-q.js';
7
7
  export { antigravityAdapter } from './antigravity.js';
8
+ export { atomcodeAdapter } from './atomcode.js';
8
9
  export { auggieAdapter } from './auggie.js';
9
10
  export { bobAdapter } from './bob.js';
10
11
  export { claudeAdapter } from './claude.js';
11
12
  export { clineAdapter } from './cline.js';
12
13
  export { commandCodeAdapter } from './command-code.js';
13
14
  export { codebuddyAdapter } from './codebuddy.js';
15
+ export { codeStudioAdapter } from './codestudio.js';
14
16
  export { continueAdapter } from './continue.js';
15
17
  export { costrictAdapter } from './costrict.js';
16
18
  export { crushAdapter } from './crush.js';
17
19
  export { cursorAdapter } from './cursor.js';
18
20
  export { devinAdapter } from './devin.js';
21
+ export { easycodeAdapter } from './easycode.js';
19
22
  export { factoryAdapter } from './factory.js';
20
23
  export { geminiAdapter } from './gemini.js';
21
24
  export { githubCopilotAdapter } from './github-copilot.js';
25
+ export { gigacodeAdapter } from './gigacode.js';
22
26
  export { iflowAdapter } from './iflow.js';
23
27
  export { junieAdapter } from './junie.js';
24
28
  export { kilocodeAdapter } from './kilocode.js';
@@ -7,7 +7,7 @@
7
7
  import type { ToolCommandAdapter } from '../types.js';
8
8
  /**
9
9
  * Kilo Code adapter for command generation.
10
- * File path: .kilocode/workflows/opsx-<id>.md
10
+ * File path: .kilo/command/opsx-<id>.md
11
11
  * Format: Plain markdown without frontmatter
12
12
  */
13
13
  export declare const kilocodeAdapter: ToolCommandAdapter;
@@ -7,13 +7,13 @@
7
7
  import path from 'path';
8
8
  /**
9
9
  * Kilo Code adapter for command generation.
10
- * File path: .kilocode/workflows/opsx-<id>.md
10
+ * File path: .kilo/command/opsx-<id>.md
11
11
  * Format: Plain markdown without frontmatter
12
12
  */
13
13
  export const kilocodeAdapter = {
14
14
  toolId: 'kilocode',
15
15
  getFilePath(commandId) {
16
- return path.join('.kilocode', 'workflows', `opsx-${commandId}.md`);
16
+ return path.join('.kilo', 'command', `opsx-${commandId}.md`);
17
17
  },
18
18
  formatFile(content) {
19
19
  return `${content.body}
@@ -6,6 +6,7 @@
6
6
  */
7
7
  import { amazonQAdapter } from './adapters/amazon-q.js';
8
8
  import { antigravityAdapter } from './adapters/antigravity.js';
9
+ import { atomcodeAdapter } from './adapters/atomcode.js';
9
10
  import { auggieAdapter } from './adapters/auggie.js';
10
11
  import { bobAdapter } from './adapters/bob.js';
11
12
  import { claudeAdapter } from './adapters/claude.js';
@@ -13,13 +14,16 @@ import { clineAdapter } from './adapters/cline.js';
13
14
  import { commandCodeAdapter } from './adapters/command-code.js';
14
15
  import { devinAdapter } from './adapters/devin.js';
15
16
  import { codebuddyAdapter } from './adapters/codebuddy.js';
17
+ import { codeStudioAdapter } from './adapters/codestudio.js';
16
18
  import { continueAdapter } from './adapters/continue.js';
17
19
  import { costrictAdapter } from './adapters/costrict.js';
18
20
  import { crushAdapter } from './adapters/crush.js';
19
21
  import { cursorAdapter } from './adapters/cursor.js';
22
+ import { easycodeAdapter } from './adapters/easycode.js';
20
23
  import { factoryAdapter } from './adapters/factory.js';
21
24
  import { geminiAdapter } from './adapters/gemini.js';
22
25
  import { githubCopilotAdapter } from './adapters/github-copilot.js';
26
+ import { gigacodeAdapter } from './adapters/gigacode.js';
23
27
  import { iflowAdapter } from './adapters/iflow.js';
24
28
  import { junieAdapter } from './adapters/junie.js';
25
29
  import { kilocodeAdapter } from './adapters/kilocode.js';
@@ -43,6 +47,7 @@ export class CommandAdapterRegistry {
43
47
  static {
44
48
  CommandAdapterRegistry.register(amazonQAdapter);
45
49
  CommandAdapterRegistry.register(antigravityAdapter);
50
+ CommandAdapterRegistry.register(atomcodeAdapter);
46
51
  CommandAdapterRegistry.register(auggieAdapter);
47
52
  CommandAdapterRegistry.register(bobAdapter);
48
53
  CommandAdapterRegistry.register(claudeAdapter);
@@ -50,13 +55,16 @@ export class CommandAdapterRegistry {
50
55
  CommandAdapterRegistry.register(commandCodeAdapter);
51
56
  CommandAdapterRegistry.register(devinAdapter);
52
57
  CommandAdapterRegistry.register(codebuddyAdapter);
58
+ CommandAdapterRegistry.register(codeStudioAdapter);
53
59
  CommandAdapterRegistry.register(continueAdapter);
54
60
  CommandAdapterRegistry.register(costrictAdapter);
55
61
  CommandAdapterRegistry.register(crushAdapter);
56
62
  CommandAdapterRegistry.register(cursorAdapter);
63
+ CommandAdapterRegistry.register(easycodeAdapter);
57
64
  CommandAdapterRegistry.register(factoryAdapter);
58
65
  CommandAdapterRegistry.register(geminiAdapter);
59
66
  CommandAdapterRegistry.register(githubCopilotAdapter);
67
+ CommandAdapterRegistry.register(gigacodeAdapter);
60
68
  CommandAdapterRegistry.register(iflowAdapter);
61
69
  CommandAdapterRegistry.register(junieAdapter);
62
70
  CommandAdapterRegistry.register(kilocodeAdapter);
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Shared TOML string escaping for command adapters.
3
+ */
4
+ /**
5
+ * TOML basic strings are escape-active: a backslash or double quote in the
6
+ * value breaks the file if written raw. Newlines cannot appear in a
7
+ * single-line basic string at all, so they are escaped too.
8
+ */
9
+ export declare function escapeTomlBasicString(value: string): string;
10
+ /**
11
+ * Multiline basic strings keep raw newlines and tabs, but backslashes are
12
+ * still escape-active, any run of three quotes would end the string, and the
13
+ * same control characters are invalid as in single-line basic strings — a
14
+ * lone carriage return included (only LF and CRLF may appear raw; CRLF is
15
+ * normalized away so the emitted file is single-convention). Escapes are
16
+ * introduced after backslash-doubling so they are not re-doubled.
17
+ */
18
+ export declare function escapeTomlMultilineBasicString(value: string): string;
19
+ //# sourceMappingURL=toml.d.ts.map
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Shared TOML string escaping for command adapters.
3
+ */
4
+ /**
5
+ * Control characters (C0 except tab/newline/carriage return, plus DEL) are
6
+ * invalid inside TOML strings and must be written as escapes.
7
+ */
8
+ const TOML_CONTROL_CHARS = new RegExp('[\\u0000-\\u0008\\u000b\\u000c\\u000e-\\u001f\\u007f]', 'g');
9
+ /**
10
+ * TOML basic strings are escape-active: a backslash or double quote in the
11
+ * value breaks the file if written raw. Newlines cannot appear in a
12
+ * single-line basic string at all, so they are escaped too.
13
+ */
14
+ export function escapeTomlBasicString(value) {
15
+ return value
16
+ .replace(/\\/g, '\\\\')
17
+ .replace(/"/g, '\\"')
18
+ .replace(/\n/g, '\\n')
19
+ .replace(/\r/g, '\\r')
20
+ .replace(/\t/g, '\\t')
21
+ .replace(TOML_CONTROL_CHARS, (c) => `\\u${c.charCodeAt(0).toString(16).padStart(4, '0')}`);
22
+ }
23
+ /**
24
+ * Multiline basic strings keep raw newlines and tabs, but backslashes are
25
+ * still escape-active, any run of three quotes would end the string, and the
26
+ * same control characters are invalid as in single-line basic strings — a
27
+ * lone carriage return included (only LF and CRLF may appear raw; CRLF is
28
+ * normalized away so the emitted file is single-convention). Escapes are
29
+ * introduced after backslash-doubling so they are not re-doubled.
30
+ */
31
+ export function escapeTomlMultilineBasicString(value) {
32
+ return value
33
+ .replace(/\r\n/g, '\n')
34
+ .replace(/\\/g, '\\\\')
35
+ .replace(/"""/g, '""\\"')
36
+ .replace(/\r/g, '\\r')
37
+ .replace(TOML_CONTROL_CHARS, (c) => `\\u${c.charCodeAt(0).toString(16).padStart(4, '0')}`);
38
+ }
39
+ //# sourceMappingURL=toml.js.map
@@ -13,7 +13,7 @@ export function resolveCommandSurfaceCapability(toolId) {
13
13
  if (CommandAdapterRegistry.has(toolId)) {
14
14
  return 'adapter-backed';
15
15
  }
16
- if (toolId === 'codex') {
16
+ if (toolId === 'codex' || toolId === 'warp') {
17
17
  return 'skills-invocable';
18
18
  }
19
19
  return 'none';
@@ -54,6 +54,17 @@ export const COMMAND_REGISTRY = [
54
54
  },
55
55
  ],
56
56
  },
57
+ {
58
+ name: 'version',
59
+ description: 'Report the installed OpenSpec version and update availability',
60
+ flags: [
61
+ COMMON_FLAGS.json,
62
+ {
63
+ name: 'check',
64
+ description: 'Check the registry for a newer version',
65
+ },
66
+ ],
67
+ },
57
68
  {
58
69
  name: 'list',
59
70
  description: 'List items (changes by default, or specs with --specs)',
@@ -66,6 +77,14 @@ export const COMMAND_REGISTRY = [
66
77
  name: 'changes',
67
78
  description: 'List changes explicitly (default)',
68
79
  },
80
+ {
81
+ name: 'archived',
82
+ description: 'Show only archived changes',
83
+ },
84
+ {
85
+ name: 'all',
86
+ description: 'Show both active and archived changes',
87
+ },
69
88
  {
70
89
  name: 'sort',
71
90
  description: 'Sort order: "recent" (default) or "name"',
@@ -194,8 +194,10 @@ export class ZshInstaller {
194
194
  }
195
195
  // Remove lines between markers (inclusive)
196
196
  lines.splice(startIndex, endIndex - startIndex + 1);
197
- // Remove trailing empty lines at the start if the markers were at the top
198
- while (lines.length > 0 && lines[0].trim() === '') {
197
+ // Install puts the block at the top of the file followed by one blank
198
+ // separator line; drop that line too so the file reads as it did before.
199
+ // Everything else, including blank lines the user had at the top, is left as is.
200
+ if (startIndex === 0 && lines.length > 0 && lines[0].trim() === '') {
199
201
  lines.shift();
200
202
  }
201
203
  // Write back
@@ -19,13 +19,13 @@ export function serializeConfig(config) {
19
19
  else {
20
20
  // Context section with comments
21
21
  lines.push('# Project context (optional)');
22
- lines.push('# This is shown to AI when creating artifacts.');
23
- lines.push('# Add your tech stack, conventions, style guides, domain knowledge, etc.');
22
+ lines.push('# Add only constraints that should guide OpenSpec artifacts and workflows.');
23
+ lines.push('# Include constraints an agent cannot infer by reading the code.');
24
+ lines.push('# Keep general project documentation and discoverable codebase facts out.');
24
25
  lines.push('# Example:');
25
26
  lines.push('# context: |');
26
- lines.push('# Tech stack: TypeScript, React, Node.js');
27
- lines.push('# We use conventional commits');
28
- lines.push('# Domain: e-commerce platform');
27
+ lines.push('# Designs and tasks must cover Windows, macOS, and Linux');
28
+ lines.push('# Write all artifacts in Spanish');
29
29
  lines.push('');
30
30
  }
31
31
  // Rules section with comments
@@ -35,7 +35,7 @@ export function serializeConfig(config) {
35
35
  lines.push('# rules:');
36
36
  lines.push('# proposal:');
37
37
  lines.push('# - Keep proposals under 500 words');
38
- lines.push('# - Always include a "Non-goals" section');
38
+ lines.push('# - Always state what is out of scope');
39
39
  lines.push('# tasks:');
40
40
  lines.push('# - Break tasks into chunks of max 2 hours');
41
41
  lines.push('');