telos-framework 0.9.2 → 0.10.1
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/.claude/commands/telos/init.md +386 -30
- package/lib/installers/platform-adapters.js +216 -0
- package/lib/installers/slash-commands.js +136 -55
- package/lib/sdd/spec-templates.js +82 -46
- package/package.json +1 -1
- package/templates/TELOS_CORE.md +9 -0
- package/templates/commands/init.md +609 -0
- package/templates/commands/quick.md +143 -0
- package/templates/commands/reset.md +96 -0
- package/templates/commands/sdd-context.md +64 -0
- package/templates/commands/sdd-discover.md +59 -0
- package/templates/commands/sdd-generate-tests.md +85 -0
- package/templates/commands/sdd-init.md +46 -0
- package/templates/commands/sdd-validate.md +78 -0
- package/templates/commands/status.md +181 -0
- package/templates/commands/validate.md +190 -0
|
@@ -1,89 +1,170 @@
|
|
|
1
1
|
const fs = require('fs').promises;
|
|
2
2
|
const path = require('path');
|
|
3
|
+
const {
|
|
4
|
+
COMMAND_METADATA,
|
|
5
|
+
getPlatformConfig,
|
|
6
|
+
hasNativeCommands,
|
|
7
|
+
transformForRulesFile
|
|
8
|
+
} = require('./platform-adapters');
|
|
3
9
|
|
|
10
|
+
/**
|
|
11
|
+
* Read centralized command template
|
|
12
|
+
*/
|
|
13
|
+
async function readCommandTemplate(commandName) {
|
|
14
|
+
const templatePath = path.join(__dirname, '../../templates/commands', `${commandName}.md`);
|
|
15
|
+
return fs.readFile(templatePath, 'utf-8');
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Ensure command directory exists
|
|
20
|
+
*/
|
|
4
21
|
async function ensureDirectories(projectRoot, platform) {
|
|
5
|
-
const
|
|
6
|
-
|
|
7
|
-
: path.join(projectRoot, '.opencode', 'command');
|
|
22
|
+
const config = getPlatformConfig(platform);
|
|
23
|
+
if (!config || !config.hasNativeCommands) return null;
|
|
8
24
|
|
|
25
|
+
const commandsDir = path.join(projectRoot, config.commandDir);
|
|
9
26
|
await fs.mkdir(commandsDir, { recursive: true });
|
|
10
27
|
return commandsDir;
|
|
11
28
|
}
|
|
12
29
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
};
|
|
21
|
-
|
|
22
|
-
const description = descriptions[filename] || 'Telos command';
|
|
23
|
-
|
|
24
|
-
if (content.startsWith('---')) {
|
|
25
|
-
return content;
|
|
30
|
+
/**
|
|
31
|
+
* Install slash commands for platforms with native command support
|
|
32
|
+
*/
|
|
33
|
+
async function installNativeCommands(projectRoot, platform) {
|
|
34
|
+
const config = getPlatformConfig(platform);
|
|
35
|
+
if (!config || !config.hasNativeCommands) {
|
|
36
|
+
return [];
|
|
26
37
|
}
|
|
27
|
-
|
|
28
|
-
return `---\ndescription: ${description}\n---\n\n${content}`;
|
|
29
|
-
}
|
|
30
38
|
|
|
31
|
-
async function copyCommandFiles(projectRoot, platform) {
|
|
32
|
-
const sourceDir = path.join(__dirname, '../../.claude/commands/telos');
|
|
33
39
|
const targetDir = await ensureDirectories(projectRoot, platform);
|
|
34
|
-
|
|
35
|
-
const commandFiles = [
|
|
36
|
-
'init.md',
|
|
37
|
-
'quick.md',
|
|
38
|
-
'reset.md',
|
|
39
|
-
'validate.md',
|
|
40
|
-
'status.md'
|
|
41
|
-
];
|
|
42
|
-
|
|
40
|
+
const commandNames = Object.keys(COMMAND_METADATA);
|
|
43
41
|
const results = [];
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
let targetFile = file;
|
|
48
|
-
if (platform === 'opencode') {
|
|
49
|
-
targetFile = file.replace('.md', '').replace(/^/, 'telos-') + '.md';
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
const targetPath = path.join(targetDir, targetFile);
|
|
53
|
-
|
|
42
|
+
|
|
43
|
+
for (const commandName of commandNames) {
|
|
54
44
|
try {
|
|
55
|
-
|
|
45
|
+
// Read centralized template
|
|
46
|
+
const content = await readCommandTemplate(commandName);
|
|
56
47
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
}
|
|
48
|
+
// Transform for platform
|
|
49
|
+
const transformed = config.transform(content, commandName);
|
|
60
50
|
|
|
61
|
-
|
|
51
|
+
// Write to target
|
|
52
|
+
const targetFile = config.fileNaming(commandName);
|
|
53
|
+
const targetPath = path.join(targetDir, targetFile);
|
|
54
|
+
|
|
55
|
+
await fs.writeFile(targetPath, transformed);
|
|
62
56
|
results.push({ file: targetFile, success: true, path: targetPath });
|
|
63
57
|
} catch (error) {
|
|
64
|
-
results.push({ file:
|
|
58
|
+
results.push({ file: commandName, success: false, error: error.message });
|
|
65
59
|
}
|
|
66
60
|
}
|
|
67
|
-
|
|
61
|
+
|
|
68
62
|
return results;
|
|
69
63
|
}
|
|
70
64
|
|
|
65
|
+
/**
|
|
66
|
+
* Generate embedded command instructions for platforms without native commands
|
|
67
|
+
*/
|
|
68
|
+
async function generateEmbeddedCommands() {
|
|
69
|
+
const commands = {};
|
|
70
|
+
const commandNames = Object.keys(COMMAND_METADATA);
|
|
71
|
+
|
|
72
|
+
for (const commandName of commandNames) {
|
|
73
|
+
try {
|
|
74
|
+
commands[commandName] = await readCommandTemplate(commandName);
|
|
75
|
+
} catch (error) {
|
|
76
|
+
console.warn(`Warning: Could not read template for ${commandName}: ${error.message}`);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
return transformForRulesFile(commands);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Embed commands into a config file for platforms without native slash commands
|
|
85
|
+
*/
|
|
86
|
+
async function embedCommandsInConfig(projectRoot, platform) {
|
|
87
|
+
const config = getPlatformConfig(platform);
|
|
88
|
+
if (!config || config.hasNativeCommands) {
|
|
89
|
+
return { success: false, reason: 'platform-has-native-commands' };
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const configPath = path.join(projectRoot, config.configFile);
|
|
93
|
+
const embeddedContent = await generateEmbeddedCommands();
|
|
94
|
+
|
|
95
|
+
try {
|
|
96
|
+
// Check if config file exists
|
|
97
|
+
let existingContent = '';
|
|
98
|
+
try {
|
|
99
|
+
existingContent = await fs.readFile(configPath, 'utf-8');
|
|
100
|
+
} catch (e) {
|
|
101
|
+
// File doesn't exist, will create new
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Check if commands already embedded
|
|
105
|
+
if (existingContent.includes('## Telos Commands')) {
|
|
106
|
+
// Replace existing Telos Commands section
|
|
107
|
+
const beforeTelos = existingContent.split('## Telos Commands')[0];
|
|
108
|
+
const afterTelos = existingContent.split('## Telos Commands')[1];
|
|
109
|
+
|
|
110
|
+
// Find end of Telos section (next ## heading or end of file)
|
|
111
|
+
const nextHeadingMatch = afterTelos?.match(/\n## [^T]/);
|
|
112
|
+
const afterTelosSection = nextHeadingMatch
|
|
113
|
+
? afterTelos.substring(nextHeadingMatch.index)
|
|
114
|
+
: '';
|
|
115
|
+
|
|
116
|
+
const newContent = beforeTelos + embeddedContent + afterTelosSection;
|
|
117
|
+
await fs.writeFile(configPath, newContent);
|
|
118
|
+
return { success: true, updated: true, path: configPath };
|
|
119
|
+
} else {
|
|
120
|
+
// Append to existing content
|
|
121
|
+
const newContent = existingContent + '\n\n' + embeddedContent;
|
|
122
|
+
await fs.writeFile(configPath, newContent);
|
|
123
|
+
return { success: true, created: !existingContent, updated: !!existingContent, path: configPath };
|
|
124
|
+
}
|
|
125
|
+
} catch (error) {
|
|
126
|
+
return { success: false, error: error.message };
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Install slash commands for selected platforms
|
|
132
|
+
* - Platforms with native commands: Creates command files
|
|
133
|
+
* - Platforms without native commands: Embeds in config files
|
|
134
|
+
*/
|
|
71
135
|
async function installSlashCommands(projectRoot, selectedPlatforms = ['claude']) {
|
|
72
136
|
const platforms = Array.isArray(selectedPlatforms) ? selectedPlatforms : [selectedPlatforms];
|
|
73
|
-
|
|
74
|
-
const commandPlatforms = platforms.filter(p => p === 'claude' || p === 'opencode');
|
|
75
|
-
|
|
76
137
|
const allResults = [];
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
138
|
+
|
|
139
|
+
for (const platform of platforms) {
|
|
140
|
+
if (hasNativeCommands(platform)) {
|
|
141
|
+
// Install native command files
|
|
142
|
+
const results = await installNativeCommands(projectRoot, platform);
|
|
143
|
+
allResults.push({ platform, type: 'native', results });
|
|
144
|
+
} else if (getPlatformConfig(platform)) {
|
|
145
|
+
// Embed in config file
|
|
146
|
+
const result = await embedCommandsInConfig(projectRoot, platform);
|
|
147
|
+
allResults.push({ platform, type: 'embedded', results: [result] });
|
|
148
|
+
}
|
|
149
|
+
// Skip unknown platforms silently
|
|
80
150
|
}
|
|
81
|
-
|
|
151
|
+
|
|
82
152
|
return allResults;
|
|
83
153
|
}
|
|
84
154
|
|
|
155
|
+
/**
|
|
156
|
+
* Copy command files (legacy API - kept for backwards compatibility)
|
|
157
|
+
*/
|
|
158
|
+
async function copyCommandFiles(projectRoot, platform) {
|
|
159
|
+
return installNativeCommands(projectRoot, platform);
|
|
160
|
+
}
|
|
161
|
+
|
|
85
162
|
module.exports = {
|
|
86
163
|
ensureDirectories,
|
|
87
164
|
copyCommandFiles,
|
|
88
|
-
installSlashCommands
|
|
165
|
+
installSlashCommands,
|
|
166
|
+
installNativeCommands,
|
|
167
|
+
embedCommandsInConfig,
|
|
168
|
+
generateEmbeddedCommands,
|
|
169
|
+
readCommandTemplate
|
|
89
170
|
};
|
|
@@ -244,43 +244,26 @@ function generateSpec(level, data = {}) {
|
|
|
244
244
|
* @returns {string} TELOS.md content
|
|
245
245
|
*/
|
|
246
246
|
function generateTelosEntry(data = {}) {
|
|
247
|
+
// Generate experiences list if provided
|
|
248
|
+
const experiencesList = data.experiences?.length > 0
|
|
249
|
+
? data.experiences.map(e => `- [${e.title}](specs/L3-experience/${e.file})`).join('\n')
|
|
250
|
+
: '_No experiences defined yet. Run `/telos:init` to add user journeys._';
|
|
251
|
+
|
|
247
252
|
return `# TELOS: ${data.projectName || 'Project Name'}
|
|
248
253
|
|
|
249
254
|
> Purpose-driven development with spec-code traceability
|
|
250
255
|
|
|
251
|
-
## Quick Start
|
|
252
|
-
|
|
253
|
-
\`\`\`bash
|
|
254
|
-
telos validate # Validate all specs and links
|
|
255
|
-
telos context <id> # Load recursive context for AI
|
|
256
|
-
telos coverage # Show spec-test coverage
|
|
257
|
-
telos orphans # Find unlinked code
|
|
258
|
-
\`\`\`
|
|
259
|
-
|
|
260
256
|
## Purpose
|
|
261
257
|
|
|
262
258
|
${data.purpose || '[Your project purpose - see L4:purpose spec]'}
|
|
263
259
|
|
|
264
260
|
See: [Full Purpose Spec](specs/L4-purpose/purpose.md)
|
|
265
261
|
|
|
266
|
-
##
|
|
262
|
+
## User Experiences (L3)
|
|
267
263
|
|
|
268
|
-
|
|
269
|
-
L4:purpose ─────────────────────────────────────────────────────┐
|
|
270
|
-
│ ${data.purposeTitle || 'Project Purpose'} │
|
|
271
|
-
│ │
|
|
272
|
-
├── L3:experience ──────────────────────────────────────────────┤
|
|
273
|
-
│ User journeys, UX requirements, analytics │
|
|
274
|
-
│ │
|
|
275
|
-
├── L2:contract ────────────────────────────────────────────────┤
|
|
276
|
-
│ API contracts, component interfaces │
|
|
277
|
-
│ │
|
|
278
|
-
└── L1:function ────────────────────────────────────────────────┤
|
|
279
|
-
Individual functions with TDD scenarios │
|
|
280
|
-
└────────────────────────────────────────────────────────────────┘
|
|
281
|
-
\`\`\`
|
|
264
|
+
${experiencesList}
|
|
282
265
|
|
|
283
|
-
## Spec
|
|
266
|
+
## Spec Hierarchy
|
|
284
267
|
|
|
285
268
|
| Level | Name | Description | Count |
|
|
286
269
|
|-------|------|-------------|-------|
|
|
@@ -289,43 +272,96 @@ L4:purpose ───────────────────────
|
|
|
289
272
|
| L2 | Contract | APIs + component interfaces | ${data.l2Count || 0} |
|
|
290
273
|
| L1 | Function | Functions with TDD | ${data.l1Count || 0} |
|
|
291
274
|
|
|
292
|
-
|
|
275
|
+
---
|
|
276
|
+
|
|
277
|
+
## 🚨 IMPORTANT: Feature Request Workflow
|
|
278
|
+
|
|
279
|
+
**When the user requests a new feature, you MUST follow this workflow:**
|
|
280
|
+
|
|
281
|
+
### Step 1: Check Impact on Experiences (L3)
|
|
282
|
+
|
|
283
|
+
Ask: "Does this feature affect any existing user journeys, or create a new one?"
|
|
284
|
+
|
|
285
|
+
- If **new journey**: Create a new L3 spec in \`telos/specs/L3-experience/\`
|
|
286
|
+
- If **modifies journey**: Update the relevant L3 spec first
|
|
287
|
+
- If **no journey impact**: Proceed to Step 2
|
|
288
|
+
|
|
289
|
+
### Step 2: Define or Update Contracts (L2)
|
|
290
|
+
|
|
291
|
+
Before writing ANY code, create/update L2 contract specs:
|
|
292
|
+
|
|
293
|
+
- **New API endpoint?** → Create \`telos/specs/L2-contract/api-[name].md\`
|
|
294
|
+
- **New component?** → Create \`telos/specs/L2-contract/component-[name].md\`
|
|
295
|
+
- **Modifying existing?** → Update the relevant L2 spec
|
|
296
|
+
|
|
297
|
+
L2 specs must include:
|
|
298
|
+
- Interface/API signature
|
|
299
|
+
- Input/output contracts
|
|
300
|
+
- Error handling
|
|
301
|
+
- Parent L3 experience reference
|
|
302
|
+
|
|
303
|
+
### Step 3: Define Functions (L1)
|
|
293
304
|
|
|
294
|
-
|
|
295
|
-
${data.linters?.map(l => `- **${l.name}**: \`${l.config}\``).join('\n') || `- **ESLint**: \`.eslintrc.js\`
|
|
296
|
-
- **Prettier**: \`.prettierrc\`
|
|
297
|
-
- **TypeScript**: \`tsconfig.json\``}
|
|
305
|
+
For each function needed to implement the L2 contracts:
|
|
298
306
|
|
|
299
|
-
|
|
307
|
+
1. Create L1 spec in \`telos/specs/L1-function/\`
|
|
308
|
+
2. Include TDD scenarios (given/when/then)
|
|
309
|
+
3. Reference parent L2 contract
|
|
310
|
+
|
|
311
|
+
### Step 4: Generate Tests
|
|
312
|
+
|
|
313
|
+
\`\`\`bash
|
|
314
|
+
telos spec generate-tests L1:function:[spec-id]
|
|
315
|
+
\`\`\`
|
|
316
|
+
|
|
317
|
+
Or manually create tests with \`@telos-test\` annotations.
|
|
318
|
+
|
|
319
|
+
### Step 5: Implement with Annotations
|
|
300
320
|
|
|
301
321
|
\`\`\`${data.primaryLanguage || 'typescript'}
|
|
302
322
|
// @telos L1:function:src/module:functionName
|
|
303
323
|
export function functionName() {
|
|
304
324
|
// implementation
|
|
305
325
|
}
|
|
326
|
+
\`\`\`
|
|
327
|
+
|
|
328
|
+
### Step 6: Validate Before Commit
|
|
306
329
|
|
|
330
|
+
\`\`\`bash
|
|
331
|
+
telos validate
|
|
332
|
+
\`\`\`
|
|
333
|
+
|
|
334
|
+
---
|
|
335
|
+
|
|
336
|
+
## Quick Reference
|
|
337
|
+
|
|
338
|
+
**Commands:**
|
|
339
|
+
- \`/telos:validate\` - Validate specs, code links, and tests
|
|
340
|
+
- \`/telos:status\` - Show current spec counts and health
|
|
341
|
+
- \`/telos:sdd-discover\` - Generate specs from existing code
|
|
342
|
+
- \`/telos:sdd-context <spec-id>\` - Load context for a spec
|
|
343
|
+
- \`/telos:sdd-generate-tests <spec-id>\` - Generate tests from scenarios
|
|
344
|
+
|
|
345
|
+
**Annotation Format:**
|
|
346
|
+
\`\`\`${data.primaryLanguage || 'typescript'}
|
|
347
|
+
// @telos L1:function:src/module:functionName
|
|
307
348
|
// @telos-test L1:function:src/module:functionName
|
|
308
|
-
|
|
309
|
-
// @telos-scenario L1:function:src/module:functionName:success-case
|
|
310
|
-
it('should handle success case', () => {
|
|
311
|
-
// test
|
|
312
|
-
});
|
|
313
|
-
});
|
|
349
|
+
// @telos-scenario L1:function:src/module:functionName:scenario-name
|
|
314
350
|
\`\`\`
|
|
315
351
|
|
|
316
|
-
|
|
352
|
+
**Spec ID Format:** \`L[level]:[type]:[path]:[name]\`
|
|
317
353
|
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
354
|
+
Examples:
|
|
355
|
+
- \`L4:purpose\`
|
|
356
|
+
- \`L3:experience:user-signup-flow\`
|
|
357
|
+
- \`L2:contract:api-auth\`
|
|
358
|
+
- \`L1:function:src/auth/validation:validateToken\`
|
|
359
|
+
|
|
360
|
+
---
|
|
325
361
|
|
|
326
362
|
## Links
|
|
327
363
|
|
|
328
|
-
- [
|
|
364
|
+
- [Telos Documentation](https://github.com/telos-framework/init)
|
|
329
365
|
- [Spec-Driven Development Guide](https://telos-framework.dev/sdd)
|
|
330
366
|
`;
|
|
331
367
|
}
|
package/package.json
CHANGED
package/templates/TELOS_CORE.md
CHANGED
|
@@ -6,6 +6,15 @@ This project uses the **Telos Framework** with **Spec-Driven Development
|
|
|
6
6
|
**CRITICAL**: All code must trace back to specifications. Every function needs a
|
|
7
7
|
`@telos` annotation linking it to a spec.
|
|
8
8
|
|
|
9
|
+
## ⚠️ IMPORTANT: Read telos/TELOS.md First
|
|
10
|
+
|
|
11
|
+
Before making any changes, **always read `telos/TELOS.md`** for:
|
|
12
|
+
|
|
13
|
+
- Project purpose and success metrics
|
|
14
|
+
- User experiences (L3 journeys)
|
|
15
|
+
- Feature request workflow (MUST follow for new features)
|
|
16
|
+
- Current spec counts and health
|
|
17
|
+
|
|
9
18
|
## Spec-Driven Development (SDD)
|
|
10
19
|
|
|
11
20
|
This project enforces a 4-level spec hierarchy:
|