lightspec 0.1.1 → 0.2.2

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 (59) hide show
  1. package/README.md +41 -65
  2. package/dist/cli/index.js +12 -24
  3. package/dist/commands/feedback.js +2 -2
  4. package/dist/core/config.d.ts +1 -0
  5. package/dist/core/config.js +1 -0
  6. package/dist/core/configurators/slash/amazon-q.js +6 -2
  7. package/dist/core/configurators/slash/antigravity.js +4 -2
  8. package/dist/core/configurators/slash/auggie.js +8 -1
  9. package/dist/core/configurators/slash/base.d.ts +14 -3
  10. package/dist/core/configurators/slash/base.js +160 -16
  11. package/dist/core/configurators/slash/claude.js +8 -1
  12. package/dist/core/configurators/slash/cline.js +4 -2
  13. package/dist/core/configurators/slash/codebuddy.js +8 -1
  14. package/dist/core/configurators/slash/codex.d.ts +0 -8
  15. package/dist/core/configurators/slash/codex.js +0 -103
  16. package/dist/core/configurators/slash/continue.js +8 -1
  17. package/dist/core/configurators/slash/costrict.js +4 -0
  18. package/dist/core/configurators/slash/crush.js +8 -1
  19. package/dist/core/configurators/slash/cursor.js +8 -1
  20. package/dist/core/configurators/slash/factory.js +8 -1
  21. package/dist/core/configurators/slash/gemini.js +4 -2
  22. package/dist/core/configurators/slash/github-copilot.js +6 -2
  23. package/dist/core/configurators/slash/iflow.js +8 -1
  24. package/dist/core/configurators/slash/kilocode.js +2 -1
  25. package/dist/core/configurators/slash/mistral-vibe.d.ts +6 -0
  26. package/dist/core/configurators/slash/mistral-vibe.js +6 -0
  27. package/dist/core/configurators/slash/opencode.js +4 -0
  28. package/dist/core/configurators/slash/qoder.js +8 -1
  29. package/dist/core/configurators/slash/qwen.js +4 -2
  30. package/dist/core/configurators/slash/registry.d.ts +2 -1
  31. package/dist/core/configurators/slash/registry.js +8 -0
  32. package/dist/core/configurators/slash/roocode.js +4 -2
  33. package/dist/core/configurators/slash/toml-base.d.ts +0 -6
  34. package/dist/core/configurators/slash/toml-base.js +0 -49
  35. package/dist/core/configurators/slash/windsurf.js +4 -2
  36. package/dist/core/init.d.ts +6 -0
  37. package/dist/core/init.js +49 -22
  38. package/dist/core/templates/agents-template.d.ts +1 -1
  39. package/dist/core/templates/agents-template.js +4 -7
  40. package/dist/core/templates/apply-template.d.ts +3 -0
  41. package/dist/core/templates/apply-template.js +21 -0
  42. package/dist/core/templates/archive-template.d.ts +3 -0
  43. package/dist/core/templates/archive-template.js +29 -0
  44. package/dist/core/templates/context-check-template.d.ts +3 -0
  45. package/dist/core/templates/context-check-template.js +128 -0
  46. package/dist/core/templates/index.d.ts +1 -0
  47. package/dist/core/templates/index.js +4 -6
  48. package/dist/core/templates/proposal-template.d.ts +3 -0
  49. package/dist/core/templates/proposal-template.js +30 -0
  50. package/dist/core/templates/skill-common-template.d.ts +2 -0
  51. package/dist/core/templates/skill-common-template.js +5 -0
  52. package/dist/core/templates/slash-command-templates.d.ts +3 -1
  53. package/dist/core/templates/slash-command-templates.js +17 -43
  54. package/dist/core/update.js +5 -5
  55. package/dist/telemetry/index.d.ts +2 -3
  56. package/dist/telemetry/index.js +2 -3
  57. package/dist/types/index.d.ts +8 -0
  58. package/dist/types/index.js +5 -0
  59. package/package.json +19 -22
package/README.md CHANGED
@@ -1,9 +1,7 @@
1
1
  <p align="center">
2
2
  <a href="https://github.com/augmenter-dev/lightspec">
3
3
  <picture>
4
- <source srcset="assets/lightspec_pixel_dark.svg" media="(prefers-color-scheme: dark)">
5
- <source srcset="assets/lightspec_pixel_light.svg" media="(prefers-color-scheme: light)">
6
- <img src="assets/lightspec_pixel_light.svg" alt="LightSpec logo" height="64">
4
+ <img src="assets/augmenter-lightspec.svg" alt="LightSpec logo" height="156">
7
5
  </picture>
8
6
  </a>
9
7
 
@@ -19,16 +17,12 @@
19
17
  </p>
20
18
 
21
19
  <p align="center">
22
- <img src="assets/lightspec_dashboard.png" alt="LightSpec dashboard preview" width="90%">
23
- </p>
24
-
25
- <p align="center">
26
- Follow <a href="https://x.com/0xTab">@0xTab on X</a> for updates · Join the <a href="https://discord.gg/YctCnvvshC">LightSpec Discord</a> for help and questions.
20
+ <img src="assets/openspec_dashboard.png" alt="LightSpec dashboard preview" width="90%">
27
21
  </p>
28
22
 
29
23
  # LightSpec
30
24
 
31
- A fork of [LightSpec](https://github.com/Fission-AI/LightSpec) v0.23.0
25
+ A fork of [OpenSpec](https://github.com/Fission-AI/OpenSpec), focused on simplicity and skill-based agents.
32
26
 
33
27
  LightSpec aligns humans and AI coding assistants with spec-driven development so you agree on what to build before any code is written. **No API keys required.**
34
28
 
@@ -40,13 +34,14 @@ Key outcomes:
40
34
  - Human and AI stakeholders agree on specs before work begins.
41
35
  - Structured change folders (proposals, tasks, and spec updates) keep scope explicit and auditable.
42
36
  - Shared visibility into what's proposed, active, or archived.
43
- - Works with the AI tools you already use: custom slash commands where supported, context rules everywhere else.
37
+ - Works with the AI tools you already use via [agent skills](https://agentskills.io/).
44
38
 
45
39
  ## How LightSpec compares (at a glance)
46
40
 
47
41
  - **Lightweight**: simple workflow, no API keys, minimal setup.
48
42
  - **Brownfield-first**: works great beyond 0→1. LightSpec separates the source of truth from proposals: `lightspec/specs/` (current truth) and `lightspec/changes/` (proposed updates). This keeps diffs explicit and manageable across features.
49
43
  - **Change tracking**: proposals, tasks, and spec deltas live together; archiving merges the approved updates back into specs.
44
+ - **Compared to OpenSpec**: LightSpec is a streamlined alternative to OpenSpec, focused on simplicity and ease of adoption. It has fewer commands and a more opinionated workflow, which can reduce cognitive overhead for teams new to spec-driven development.
50
45
  - **Compared to spec-kit & Kiro**: those shine for brand-new features (0→1). LightSpec also excels when modifying existing behavior (1→n), especially when updates span multiple specs.
51
46
 
52
47
  See the full comparison in [How LightSpec Compares](#how-lightspec-compares).
@@ -87,49 +82,29 @@ See the full comparison in [How LightSpec Compares](#how-lightspec-compares).
87
82
 
88
83
  ### Supported AI Tools
89
84
 
90
- <details>
91
- <summary><strong>Native Slash Commands</strong> (click to expand)</summary>
92
-
93
- These tools have built-in LightSpec commands. Select the LightSpec integration when prompted.
94
-
95
- | Tool | Commands |
96
- |------|----------|
97
- | **Amazon Q Developer** | `@lightspec-proposal`, `@lightspec-apply`, `@lightspec-archive` (`.amazonq/prompts/`) |
98
- | **Antigravity** | `/lightspec-proposal`, `/lightspec-apply`, `/lightspec-archive` (`.agent/workflows/`) |
99
- | **Auggie (Augment CLI)** | `/lightspec-proposal`, `/lightspec-apply`, `/lightspec-archive` (`.augment/commands/`) |
100
- | **Claude Code** | `/lightspec:proposal`, `/lightspec:apply`, `/lightspec:archive` |
101
- | **Cline** | Workflows in `.clinerules/workflows/` directory (`.clinerules/workflows/lightspec-*.md`) |
102
- | **CodeBuddy Code (CLI)** | `/lightspec:proposal`, `/lightspec:apply`, `/lightspec:archive` (`.codebuddy/commands/`) — see [docs](https://www.codebuddy.ai/cli) |
103
- | **Codex** | `/lightspec-proposal`, `/lightspec-apply`, `/lightspec-archive` (global: `~/.codex/prompts`, auto-installed) |
104
- | **Continue** | `/lightspec-proposal`, `/lightspec-apply`, `/lightspec-archive` (`.continue/prompts/`) |
105
- | **CoStrict** | `/lightspec-proposal`, `/lightspec-apply`, `/lightspec-archive` (`.cospec/lightspec/commands/`) — see [docs](https://costrict.ai)|
106
- | **Crush** | `/lightspec-proposal`, `/lightspec-apply`, `/lightspec-archive` (`.crush/commands/lightspec/`) |
107
- | **Cursor** | `/lightspec-proposal`, `/lightspec-apply`, `/lightspec-archive` |
108
- | **Factory Droid** | `/lightspec-proposal`, `/lightspec-apply`, `/lightspec-archive` (`.factory/commands/`) |
109
- | **Gemini CLI** | `/lightspec:proposal`, `/lightspec:apply`, `/lightspec:archive` (`.gemini/commands/lightspec/`) |
110
- | **GitHub Copilot** | `/lightspec-proposal`, `/lightspec-apply`, `/lightspec-archive` (`.github/prompts/`) |
111
- | **iFlow (iflow-cli)** | `/lightspec-proposal`, `/lightspec-apply`, `/lightspec-archive` (`.iflow/commands/`) |
112
- | **Kilo Code** | `/lightspec-proposal.md`, `/lightspec-apply.md`, `/lightspec-archive.md` (`.kilocode/workflows/`) |
113
- | **OpenCode** | `/lightspec-proposal`, `/lightspec-apply`, `/lightspec-archive` |
114
- | **Qoder (CLI)** | `/lightspec:proposal`, `/lightspec:apply`, `/lightspec:archive` (`.qoder/commands/lightspec/`) — see [docs](https://qoder.com/cli) |
115
- | **Qwen Code** | `/lightspec-proposal`, `/lightspec-apply`, `/lightspec-archive` (`.qwen/commands/`) |
116
- | **RooCode** | `/lightspec-proposal`, `/lightspec-apply`, `/lightspec-archive` (`.roo/commands/`) |
117
- | **Windsurf** | `/lightspec-proposal`, `/lightspec-apply`, `/lightspec-archive` (`.windsurf/workflows/`) |
118
-
119
- Kilo Code discovers team workflows automatically. Save the generated files under `.kilocode/workflows/` and trigger them from the command palette with `/lightspec-proposal.md`, `/lightspec-apply.md`, or `/lightspec-archive.md`.
120
-
121
- </details>
122
-
123
- <details>
124
- <summary><strong>AGENTS.md Compatible</strong> (click to expand)</summary>
125
-
126
- These tools automatically read workflow instructions from `lightspec/AGENTS.md`. Ask them to follow the LightSpec workflow if they need a reminder. Learn more about the [AGENTS.md convention](https://agents.md/).
127
-
128
- | Tools |
129
- |-------|
130
- | Amp • Jules • Others |
131
-
132
- </details>
85
+ - Amazon Q Developer
86
+ - Antigravity
87
+ - Auggie (Augment CLI)
88
+ - Claude Code
89
+ - Cline
90
+ - Codex
91
+ - CodeBuddy Code (CLI)
92
+ - Continue (VS Code / JetBrains / CLI)
93
+ - CoStrict
94
+ - Crush
95
+ - Cursor
96
+ - Factory Droid
97
+ - Gemini CLI
98
+ - GitHub Copilot
99
+ - iFlow
100
+ - Kilo Code
101
+ - Mistral Vibe
102
+ - OpenCode
103
+ - Qoder (CLI)
104
+ - Qwen Code
105
+ - RooCode
106
+ - Windsurf
107
+ - Any AGENTS.md-compatible assistant (via Universal `AGENTS.md`)
133
108
 
134
109
  ### Install & Initialize
135
110
 
@@ -196,29 +171,29 @@ lightspec init
196
171
 
197
172
  **What happens during initialization:**
198
173
  - You'll be prompted to pick any natively supported AI tools (Claude Code, CodeBuddy, Cursor, OpenCode, Qoder,etc.); other assistants always rely on the shared `AGENTS.md` stub
199
- - LightSpec automatically configures slash commands for the tools you choose and always writes a managed `AGENTS.md` hand-off at the project root
174
+ - LightSpec automatically configures skills for the tools you choose and always writes a managed `AGENTS.md` hand-off at the project root
200
175
  - A new `lightspec/` directory structure is created in your project
201
176
 
202
177
  **After setup:**
203
178
  - Primary AI tools can trigger `/lightspec` workflows without additional configuration
204
179
  - Run `lightspec list` to verify the setup and view any active changes
205
- - If your coding assistant doesn't surface the new slash commands right away, restart it. Slash commands are loaded at startup,
206
- so a fresh launch ensures they appear
180
+ - If your coding assistant doesn't surface the new skills right away, restart it. Skills are loaded at startup, so a fresh launch ensures they appear
181
+ - Depending on your AI tool, you'll need to invoke the lightspec skills with either slash commands (e.g. `/lightspec:proposal`) or dollar commands (e.g. `$lightspec-proposal`) to create change proposals, apply changes, or archive completed work
207
182
 
208
183
  ### Optional: Populate Project Context
209
184
 
210
- After `lightspec init` completes, you'll receive a suggested prompt to help populate your project context:
185
+ After `lightspec init` completes, you'll receive a suggested command to validate and populate your project context:
211
186
 
212
187
  ```text
213
- Populate your project context:
214
- "Please read lightspec/project.md and help me fill it out with details about my project, tech stack, and conventions"
188
+ Validate and populate your project context:
189
+ "/lightspec:context-check"
215
190
  ```
216
191
 
217
- Use `lightspec/project.md` to define project-level conventions, standards, architectural patterns, and other guidelines that should be followed across all changes.
192
+ Use the `/lightspec:context-check` skill to validate that your agent instruction file (CLAUDE.md or AGENTS.md) contains adequate project context. The skill will check for required properties like Purpose, Tech Stack, Architecture Patterns, and more. If anything is missing, it can help you explore the codebase and populate the missing information.
218
193
 
219
194
  ### Create Your First Change
220
195
 
221
- Here's a real example showing the complete LightSpec workflow. This works with any AI tool. Those with native slash commands will recognize the shortcuts automatically.
196
+ Here's a real example showing the complete LightSpec workflow. This works with any AI tool.
222
197
 
223
198
  #### 1. Draft the Proposal
224
199
  Start by asking your AI to create a change proposal:
@@ -281,8 +256,6 @@ Or run the command yourself in terminal:
281
256
  $ lightspec archive add-profile-filters --yes # Archive the completed change without prompts
282
257
  ```
283
258
 
284
- **Note:** Tools with native slash commands (Claude Code, CodeBuddy, Cursor, Codex, Qoder, RooCode) can use the shortcuts shown. All other tools work with natural language requests to "create an LightSpec proposal", "apply the LightSpec change", or "archive the change".
285
-
286
259
  ## Command Reference
287
260
 
288
261
  ```bash
@@ -379,6 +352,9 @@ Deltas are "patches" that show how specs change:
379
352
 
380
353
  ## How LightSpec Compares
381
354
 
355
+ ### vs. OpenSpec
356
+ OpenSpec has evolved into a more mature yet complex tool with a rich feature set. LightSpec focuses on simplicity and ease of adoption, especially for teams new to spec-driven development. LightSpec's minimalist approach has the additional benefit of reducing the number of skills and commands needed, and reducing the risk of involuntary skill activation from AI assistants.
357
+
382
358
  ### vs. spec-kit
383
359
  LightSpec’s two-folder model (`lightspec/specs/` for the current truth, `lightspec/changes/` for proposed updates) keeps state and diffs separate. This scales when you modify existing features or touch multiple specs. spec-kit is strong for greenfield/0→1 but provides less structure for cross-spec updates and evolving features.
384
360
 
@@ -404,7 +380,7 @@ Run `lightspec update` whenever someone switches tools so your agents pick up th
404
380
  npm install -g lightspec@latest
405
381
  ```
406
382
  2. **Refresh agent instructions**
407
- - Run `lightspec update` inside each project to regenerate AI guidance and ensure the latest slash commands are active.
383
+ - Run `lightspec update` inside each project to regenerate AI guidance and ensure the latest skills are active.
408
384
 
409
385
  ## Contributing
410
386
 
@@ -424,7 +400,7 @@ See [MAINTAINERS.md](MAINTAINERS.md) for the list of core maintainers and adviso
424
400
  ## Agent Skills
425
401
 
426
402
  LightSpec includes 3 Claude Code skills for the core development workflow:
427
- - `lightspec-new` - Create a new change
403
+ - `lightspec-proposal` - Create a new change
428
404
  - `lightspec-apply` - Get apply instructions for implementation
429
405
  - `lightspec-archive` - Archive a completed change
430
406
 
package/dist/cli/index.js CHANGED
@@ -19,23 +19,6 @@ import { maybeShowTelemetryNotice, trackCommand, shutdown } from '../telemetry/i
19
19
  const program = new Command();
20
20
  const require = createRequire(import.meta.url);
21
21
  const { version } = require('../../package.json');
22
- /**
23
- * Get the full command path for nested commands.
24
- * For example: 'change show' -> 'change:show'
25
- */
26
- function getCommandPath(command) {
27
- const names = [];
28
- let current = command;
29
- while (current) {
30
- const name = current.name();
31
- // Skip the root 'lightspec' command
32
- if (name && name !== 'lightspec') {
33
- names.unshift(name);
34
- }
35
- current = current.parent;
36
- }
37
- return names.join(':') || 'lightspec';
38
- }
39
22
  program
40
23
  .name('lightspec')
41
24
  .description('AI-native system for spec-driven development')
@@ -43,19 +26,15 @@ program
43
26
  // Global options
44
27
  program.option('--no-color', 'Disable color output');
45
28
  // Apply global flags and telemetry before any command runs
46
- // Note: preAction receives (thisCommand, actionCommand) where:
47
- // - thisCommand: the command where hook was added (root program)
48
- // - actionCommand: the command actually being executed (subcommand)
49
- program.hook('preAction', async (thisCommand, actionCommand) => {
29
+ program.hook('preAction', async (thisCommand) => {
50
30
  const opts = thisCommand.opts();
51
31
  if (opts.color === false) {
52
32
  process.env.NO_COLOR = '1';
53
33
  }
54
34
  // Show first-run telemetry notice (if not seen)
55
35
  await maybeShowTelemetryNotice();
56
- // Track command execution (use actionCommand to get the actual subcommand)
57
- const commandPath = getCommandPath(actionCommand);
58
- await trackCommand(commandPath, version);
36
+ // Track command execution
37
+ await trackCommand();
59
38
  });
60
39
  // Shutdown telemetry after command completes
61
40
  program.hook('postAction', async () => {
@@ -63,10 +42,12 @@ program.hook('postAction', async () => {
63
42
  });
64
43
  const availableToolIds = AI_TOOLS.filter((tool) => tool.available).map((tool) => tool.value);
65
44
  const toolsOptionDescription = `Configure AI tools non-interactively. Use "all", "none", or a comma-separated list of: ${availableToolIds.join(', ')}`;
45
+ const skillLocationOptionDescription = 'Install generated skills in "project" or "home" location (defaults to interactive selection).';
66
46
  program
67
47
  .command('init [path]')
68
48
  .description('Initialize LightSpec in your project')
69
49
  .option('--tools <tools>', toolsOptionDescription)
50
+ .option('--skills-location <location>', skillLocationOptionDescription)
70
51
  .action(async (targetPath = '.', options) => {
71
52
  try {
72
53
  // Validate that the path is a valid directory
@@ -90,8 +71,15 @@ program
90
71
  }
91
72
  }
92
73
  const { InitCommand } = await import('../core/init.js');
74
+ const skillLocation = options?.skillsLocation === 'project' || options?.skillsLocation === 'home'
75
+ ? options.skillsLocation
76
+ : undefined;
77
+ if (options?.skillsLocation && !skillLocation) {
78
+ throw new Error('Invalid --skills-location value. Use "project" or "home".');
79
+ }
93
80
  const initCommand = new InitCommand({
94
81
  tools: options?.tools,
82
+ skillLocation,
95
83
  });
96
84
  await initCommand.execute(targetPath);
97
85
  }
@@ -87,7 +87,7 @@ function formatBody(bodyText) {
87
87
  * Generate a pre-filled GitHub issue URL for manual submission
88
88
  */
89
89
  function generateManualSubmissionUrl(title, body) {
90
- const repo = 'Fission-AI/LightSpec';
90
+ const repo = 'augmenter-dev/LightSpec';
91
91
  const encodedTitle = encodeURIComponent(title);
92
92
  const encodedBody = encodeURIComponent(body);
93
93
  const encodedLabels = encodeURIComponent('feedback');
@@ -114,7 +114,7 @@ function submitViaGhCli(title, body) {
114
114
  'issue',
115
115
  'create',
116
116
  '--repo',
117
- 'Fission-AI/LightSpec',
117
+ 'augmenter-dev/LightSpec',
118
118
  '--title',
119
119
  title,
120
120
  '--body',
@@ -5,6 +5,7 @@ export declare const LIGHTSPEC_MARKERS: {
5
5
  };
6
6
  export interface LightSpecConfig {
7
7
  aiTools: string[];
8
+ skillLocation: 'project' | 'home';
8
9
  }
9
10
  export interface AIToolOption {
10
11
  name: string;
@@ -20,6 +20,7 @@ export const AI_TOOLS = [
20
20
  { name: 'GitHub Copilot', value: 'github-copilot', available: true, successLabel: 'GitHub Copilot' },
21
21
  { name: 'iFlow', value: 'iflow', available: true, successLabel: 'iFlow' },
22
22
  { name: 'Kilo Code', value: 'kilocode', available: true, successLabel: 'Kilo Code' },
23
+ { name: 'Mistral Vibe', value: 'mistral-vibe', available: true, successLabel: 'Mistral Vibe' },
23
24
  { name: 'OpenCode', value: 'opencode', available: true, successLabel: 'OpenCode' },
24
25
  { name: 'Qoder (CLI)', value: 'qoder', available: true, successLabel: 'Qoder' },
25
26
  { name: 'Qwen Code', value: 'qwen', available: true, successLabel: 'Qwen Code' },
@@ -2,7 +2,8 @@ import { SlashCommandConfigurator } from './base.js';
2
2
  const FILE_PATHS = {
3
3
  proposal: '.amazonq/prompts/lightspec-proposal.md',
4
4
  apply: '.amazonq/prompts/lightspec-apply.md',
5
- archive: '.amazonq/prompts/lightspec-archive.md'
5
+ archive: '.amazonq/prompts/lightspec-archive.md',
6
+ 'context-check': '.aws/amazonq/commands/lightspec-context-check.md'
6
7
  };
7
8
  const FRONTMATTER = {
8
9
  proposal: `---
@@ -31,7 +32,10 @@ The user wants to archive the following deployed change. Use the lightspec instr
31
32
 
32
33
  <ChangeId>
33
34
  $ARGUMENTS
34
- </ChangeId>`
35
+ </ChangeId>`,
36
+ 'context-check': `---
37
+ description: Validate project context in agent instruction files and help populate missing information.
38
+ ---`
35
39
  };
36
40
  export class AmazonQSlashCommandConfigurator extends SlashCommandConfigurator {
37
41
  toolId = 'amazon-q';
@@ -2,12 +2,14 @@ import { SlashCommandConfigurator } from './base.js';
2
2
  const FILE_PATHS = {
3
3
  proposal: '.agent/workflows/lightspec-proposal.md',
4
4
  apply: '.agent/workflows/lightspec-apply.md',
5
- archive: '.agent/workflows/lightspec-archive.md'
5
+ archive: '.agent/workflows/lightspec-archive.md',
6
+ 'context-check': '.antigravity/commands/lightspec-context-check.md'
6
7
  };
7
8
  const DESCRIPTIONS = {
8
9
  proposal: 'Scaffold a new LightSpec change and validate strictly.',
9
10
  apply: 'Implement an approved LightSpec change and keep tasks in sync.',
10
- archive: 'Archive a deployed LightSpec change and update specs.'
11
+ archive: 'Archive a deployed LightSpec change and update specs.',
12
+ 'context-check': 'Validate project context in agent instruction files and help populate missing information.'
11
13
  };
12
14
  export class AntigravitySlashCommandConfigurator extends SlashCommandConfigurator {
13
15
  toolId = 'antigravity';
@@ -2,7 +2,8 @@ import { SlashCommandConfigurator } from './base.js';
2
2
  const FILE_PATHS = {
3
3
  proposal: '.augment/commands/lightspec-proposal.md',
4
4
  apply: '.augment/commands/lightspec-apply.md',
5
- archive: '.augment/commands/lightspec-archive.md'
5
+ archive: '.augment/commands/lightspec-archive.md',
6
+ 'context-check': '.auggie/commands/lightspec-context-check.md'
6
7
  };
7
8
  const FRONTMATTER = {
8
9
  proposal: `---
@@ -16,6 +17,12 @@ argument-hint: change-id
16
17
  archive: `---
17
18
  description: Archive a deployed LightSpec change and update specs.
18
19
  argument-hint: change-id
20
+ ---`,
21
+ 'context-check': `---
22
+ name: LightSpec: Context Check
23
+ description: Validate project context in agent instruction files and help populate missing information.
24
+ category: LightSpec
25
+ tags: [lightspec, context, validation]
19
26
  ---`
20
27
  };
21
28
  export class AuggieSlashCommandConfigurator extends SlashCommandConfigurator {
@@ -2,18 +2,29 @@ import { SlashCommandId } from '../../templates/index.js';
2
2
  export interface SlashCommandTarget {
3
3
  id: SlashCommandId;
4
4
  path: string;
5
- kind: 'slash';
5
+ kind: 'skill';
6
6
  }
7
+ export type SkillInstallLocation = 'project' | 'home';
7
8
  export declare abstract class SlashCommandConfigurator {
8
9
  abstract readonly toolId: string;
9
10
  abstract readonly isAvailable: boolean;
11
+ private installLocation;
12
+ setInstallLocation(location: SkillInstallLocation): void;
10
13
  getTargets(): SlashCommandTarget[];
11
14
  generateAll(projectPath: string, _lightspecDir: string): Promise<string[]>;
12
15
  updateExisting(projectPath: string, _lightspecDir: string): Promise<string[]>;
13
- protected abstract getRelativePath(id: SlashCommandId): string;
14
- protected abstract getFrontmatter(id: SlashCommandId): string | undefined;
15
16
  protected getBody(id: SlashCommandId): string;
16
17
  resolveAbsolutePath(projectPath: string, id: SlashCommandId): string;
18
+ private getRelativeSkillPath;
19
+ private getToolRoot;
20
+ private getHomeRootPath;
21
+ private getSkillName;
22
+ private buildSkillFile;
17
23
  protected updateBody(filePath: string, body: string): Promise<void>;
24
+ private cleanupLegacyArtifacts;
25
+ private relativeToToolRoot;
26
+ private removeLegacyLightSpecFiles;
27
+ private walkAndRemove;
28
+ private isLegacyLightSpecFile;
18
29
  }
19
30
  //# sourceMappingURL=base.d.ts.map
@@ -1,57 +1,151 @@
1
+ import os from 'os';
2
+ import path from 'path';
3
+ import { promises as fs } from 'fs';
1
4
  import { FileSystemUtils } from '../../../utils/file-system.js';
2
5
  import { TemplateManager } from '../../templates/index.js';
3
6
  import { LIGHTSPEC_MARKERS } from '../../config.js';
4
- const ALL_COMMANDS = ['proposal', 'apply', 'archive'];
7
+ const ALL_COMMANDS = ['proposal', 'apply', 'archive', 'context-check'];
8
+ const TOOL_SKILL_ROOTS = {
9
+ 'amazon-q': '.amazonq',
10
+ antigravity: '.antigravity',
11
+ auggie: '.auggie',
12
+ claude: '.claude',
13
+ cline: '.cline',
14
+ codex: '.codex',
15
+ codebuddy: '.codebuddy',
16
+ continue: '.continue',
17
+ costrict: '.cospec/lightspec',
18
+ crush: '.crush',
19
+ cursor: '.cursor',
20
+ factory: '.factory',
21
+ gemini: '.gemini',
22
+ 'github-copilot': '.github/copilot',
23
+ iflow: '.iflow',
24
+ kilocode: '.kilocode',
25
+ 'mistral-vibe': '.vibe',
26
+ opencode: '.opencode',
27
+ qoder: '.qoder',
28
+ qwen: '.qwen',
29
+ roocode: '.roocode',
30
+ windsurf: '.windsurf',
31
+ };
32
+ const TOOL_LEGACY_DIRS = {
33
+ 'amazon-q': ['.amazonq/prompts', '.aws/amazonq/commands'],
34
+ antigravity: ['.antigravity/commands', '.agent/workflows'],
35
+ auggie: ['.augment/commands', '.auggie/commands'],
36
+ claude: ['.claude/commands'],
37
+ cline: ['.cline/commands', '.clinerules/workflows'],
38
+ codex: ['.codex/prompts'],
39
+ codebuddy: ['.codebuddy/commands'],
40
+ continue: ['.continue/prompts', '.continue/commands'],
41
+ costrict: ['.cospec/lightspec/commands'],
42
+ crush: ['.crush/commands'],
43
+ cursor: ['.cursor/commands'],
44
+ factory: ['.factory/commands'],
45
+ gemini: ['.gemini/commands'],
46
+ 'github-copilot': ['.github/prompts', '.github/copilot/prompts'],
47
+ iflow: ['.iflow/commands'],
48
+ kilocode: ['.kilocode/commands', '.kilocode/workflows'],
49
+ 'mistral-vibe': ['.vibe/commands', '.vibe/workflows', '.vibe/prompts'],
50
+ opencode: ['.opencode/command', '.opencode/commands'],
51
+ qoder: ['.qoder/commands', '.qoder/prompts'],
52
+ qwen: ['.qwen/commands', '.qwen/prompts'],
53
+ roocode: ['.roo/commands', '.roocode/commands'],
54
+ windsurf: ['.windsurf/workflows', '.windsurf/commands'],
55
+ };
5
56
  export class SlashCommandConfigurator {
57
+ installLocation = 'project';
58
+ setInstallLocation(location) {
59
+ this.installLocation = location;
60
+ }
6
61
  getTargets() {
7
62
  return ALL_COMMANDS.map((id) => ({
8
63
  id,
9
- path: this.getRelativePath(id),
10
- kind: 'slash'
64
+ path: this.getRelativeSkillPath(id),
65
+ kind: 'skill',
11
66
  }));
12
67
  }
13
68
  async generateAll(projectPath, _lightspecDir) {
14
69
  const createdOrUpdated = [];
15
70
  for (const target of this.getTargets()) {
16
71
  const body = this.getBody(target.id);
17
- const filePath = FileSystemUtils.joinPath(projectPath, target.path);
72
+ const filePath = this.resolveAbsolutePath(projectPath, target.id);
18
73
  if (await FileSystemUtils.fileExists(filePath)) {
19
74
  await this.updateBody(filePath, body);
20
75
  }
21
76
  else {
22
- const frontmatter = this.getFrontmatter(target.id);
23
- const sections = [];
24
- if (frontmatter) {
25
- sections.push(frontmatter.trim());
26
- }
27
- sections.push(`${LIGHTSPEC_MARKERS.start}\n${body}\n${LIGHTSPEC_MARKERS.end}`);
28
- const content = sections.join('\n') + '\n';
77
+ const frontmatter = TemplateManager.getSlashCommandFrontmatter(target.id).trim();
78
+ const content = this.buildSkillFile(frontmatter, body);
29
79
  await FileSystemUtils.writeFile(filePath, content);
30
80
  }
31
81
  createdOrUpdated.push(target.path);
32
82
  }
83
+ await this.cleanupLegacyArtifacts(projectPath);
33
84
  return createdOrUpdated;
34
85
  }
35
86
  async updateExisting(projectPath, _lightspecDir) {
36
87
  const updated = [];
37
88
  for (const target of this.getTargets()) {
38
- const filePath = FileSystemUtils.joinPath(projectPath, target.path);
89
+ const filePath = this.resolveAbsolutePath(projectPath, target.id);
39
90
  if (await FileSystemUtils.fileExists(filePath)) {
40
91
  const body = this.getBody(target.id);
41
92
  await this.updateBody(filePath, body);
42
93
  updated.push(target.path);
43
94
  }
44
95
  }
96
+ await this.cleanupLegacyArtifacts(projectPath);
45
97
  return updated;
46
98
  }
47
99
  getBody(id) {
48
100
  return TemplateManager.getSlashCommandBody(id).trim();
49
101
  }
50
- // Resolve absolute path for a given slash command target. Subclasses may override
51
- // to redirect to tool-specific locations (e.g., global directories).
52
102
  resolveAbsolutePath(projectPath, id) {
53
- const rel = this.getRelativePath(id);
54
- return FileSystemUtils.joinPath(projectPath, rel);
103
+ const relativePath = this.getRelativeSkillPath(id);
104
+ if (this.installLocation === 'project') {
105
+ return FileSystemUtils.joinPath(projectPath, relativePath);
106
+ }
107
+ const homeRoot = this.getHomeRootPath();
108
+ const rootPrefix = this.getToolRoot();
109
+ const normalizedRelativePath = FileSystemUtils.toPosixPath(relativePath);
110
+ if (!normalizedRelativePath.startsWith(`${rootPrefix}/`)) {
111
+ throw new Error(`Skill path '${relativePath}' does not match expected root '${rootPrefix}' for ${this.toolId}`);
112
+ }
113
+ const relativeUnderRoot = normalizedRelativePath.slice(rootPrefix.length + 1);
114
+ return FileSystemUtils.joinPath(homeRoot, relativeUnderRoot);
115
+ }
116
+ getRelativeSkillPath(id) {
117
+ const root = this.getToolRoot();
118
+ const skillName = this.getSkillName(id);
119
+ return `${root}/skills/${skillName}/SKILL.md`;
120
+ }
121
+ getToolRoot() {
122
+ const root = TOOL_SKILL_ROOTS[this.toolId];
123
+ if (!root) {
124
+ throw new Error(`No skill root directory configured for tool '${this.toolId}'`);
125
+ }
126
+ return root;
127
+ }
128
+ getHomeRootPath() {
129
+ if (this.toolId === 'codex') {
130
+ const codexHome = process.env.CODEX_HOME?.trim();
131
+ return codexHome && codexHome.length > 0
132
+ ? codexHome
133
+ : FileSystemUtils.joinPath(os.homedir(), '.codex');
134
+ }
135
+ const toolRoot = this.getToolRoot();
136
+ const trimmed = toolRoot.startsWith('./') ? toolRoot.slice(2) : toolRoot;
137
+ return path.join(os.homedir(), trimmed);
138
+ }
139
+ getSkillName(id) {
140
+ return `lightspec-${id}`;
141
+ }
142
+ buildSkillFile(frontmatter, body) {
143
+ const sections = [];
144
+ if (frontmatter) {
145
+ sections.push(frontmatter);
146
+ }
147
+ sections.push(`${LIGHTSPEC_MARKERS.start}\n${body}\n${LIGHTSPEC_MARKERS.end}`);
148
+ return `${sections.join('\n\n')}\n`;
55
149
  }
56
150
  async updateBody(filePath, body) {
57
151
  const content = await FileSystemUtils.readFile(filePath);
@@ -65,5 +159,55 @@ export class SlashCommandConfigurator {
65
159
  const updatedContent = `${before}\n${body}\n${after}`;
66
160
  await FileSystemUtils.writeFile(filePath, updatedContent);
67
161
  }
162
+ async cleanupLegacyArtifacts(projectPath) {
163
+ const legacyDirs = TOOL_LEGACY_DIRS[this.toolId] ?? [];
164
+ for (const legacyDir of legacyDirs) {
165
+ const absoluteDir = this.installLocation === 'project'
166
+ ? FileSystemUtils.joinPath(projectPath, legacyDir)
167
+ : FileSystemUtils.joinPath(this.getHomeRootPath(), this.relativeToToolRoot(legacyDir));
168
+ await this.removeLegacyLightSpecFiles(absoluteDir);
169
+ }
170
+ }
171
+ relativeToToolRoot(relativePath) {
172
+ const rootPrefix = this.getToolRoot();
173
+ const normalized = FileSystemUtils.toPosixPath(relativePath);
174
+ if (normalized.startsWith(`${rootPrefix}/`)) {
175
+ return normalized.slice(rootPrefix.length + 1);
176
+ }
177
+ if (normalized === rootPrefix) {
178
+ return '';
179
+ }
180
+ return normalized.startsWith('./') ? normalized.slice(2) : normalized;
181
+ }
182
+ async removeLegacyLightSpecFiles(dirPath) {
183
+ if (!(await FileSystemUtils.directoryExists(dirPath))) {
184
+ return;
185
+ }
186
+ await this.walkAndRemove(dirPath);
187
+ }
188
+ async walkAndRemove(currentPath) {
189
+ const entries = await fs.readdir(currentPath, { withFileTypes: true });
190
+ for (const entry of entries) {
191
+ const entryPath = path.join(currentPath, entry.name);
192
+ if (entry.isDirectory()) {
193
+ await this.walkAndRemove(entryPath);
194
+ continue;
195
+ }
196
+ if (this.isLegacyLightSpecFile(entryPath)) {
197
+ await fs.unlink(entryPath);
198
+ }
199
+ }
200
+ }
201
+ isLegacyLightSpecFile(filePath) {
202
+ const normalized = FileSystemUtils.toPosixPath(filePath).toLowerCase();
203
+ return (normalized.includes('/lightspec-proposal') ||
204
+ normalized.includes('/lightspec-apply') ||
205
+ normalized.includes('/lightspec-archive') ||
206
+ normalized.includes('/lightspec-context-check') ||
207
+ normalized.includes('/lightspec/proposal') ||
208
+ normalized.includes('/lightspec/apply') ||
209
+ normalized.includes('/lightspec/archive') ||
210
+ normalized.includes('/lightspec/context-check'));
211
+ }
68
212
  }
69
213
  //# sourceMappingURL=base.js.map
@@ -2,7 +2,8 @@ import { SlashCommandConfigurator } from './base.js';
2
2
  const FILE_PATHS = {
3
3
  proposal: '.claude/commands/lightspec/proposal.md',
4
4
  apply: '.claude/commands/lightspec/apply.md',
5
- archive: '.claude/commands/lightspec/archive.md'
5
+ archive: '.claude/commands/lightspec/archive.md',
6
+ 'context-check': '.claude/commands/lightspec/context-check.md'
6
7
  };
7
8
  const FRONTMATTER = {
8
9
  proposal: `---
@@ -22,6 +23,12 @@ name: LightSpec: Archive
22
23
  description: Archive a deployed LightSpec change and update specs.
23
24
  category: LightSpec
24
25
  tags: [lightspec, archive]
26
+ ---`,
27
+ 'context-check': `---
28
+ name: LightSpec: Context Check
29
+ description: Validate project context in agent instruction files and help populate missing information.
30
+ category: LightSpec
31
+ tags: [lightspec, context, validation]
25
32
  ---`
26
33
  };
27
34
  export class ClaudeSlashCommandConfigurator extends SlashCommandConfigurator {