@ancleto/spec 0.6.4 → 0.6.6
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 +1 -1
- package/agents/coder.md +11 -11
- package/agents/documenter.md +29 -27
- package/agents/orchestrator.md +8 -8
- package/agents/reviewer.md +13 -13
- package/agents/spec-writer.md +11 -10
- package/agents/technical-discovery.md +5 -5
- package/agents/technical-seed-writer.md +1 -1
- package/agents/tester.md +9 -9
- package/commands/cleto-apply.md +3 -155
- package/commands/cleto-archive.md +2 -164
- package/commands/cleto-bulk-archive.md +2 -248
- package/commands/cleto-continue.md +2 -126
- package/commands/cleto-explore.md +2 -174
- package/commands/cleto-ff.md +2 -122
- package/commands/cleto-new.md +2 -110
- package/commands/cleto-onboard.md +3 -563
- package/commands/cleto-propose.md +2 -132
- package/commands/cleto-recall.md +4 -52
- package/commands/cleto-sync.md +2 -137
- package/commands/cleto-verify.md +2 -169
- package/package.json +2 -2
- package/skills/ancleto-apply/SKILL.md +16 -39
- package/skills/ancleto-archive/SKILL.md +24 -26
- package/skills/ancleto-bulk-archive/SKILL.md +46 -56
- package/skills/ancleto-commit/SKILL.md +10 -29
- package/skills/ancleto-continue/SKILL.md +19 -25
- package/skills/ancleto-explore/SKILL.md +42 -69
- package/skills/ancleto-ff/SKILL.md +22 -30
- package/skills/ancleto-new/SKILL.md +23 -42
- package/skills/ancleto-onboard/SKILL.md +37 -40
- package/skills/ancleto-pr/SKILL.md +12 -26
- package/skills/ancleto-propose/SKILL.md +23 -70
- package/skills/ancleto-recall/SKILL.md +21 -25
- package/skills/ancleto-sync-specs/SKILL.md +29 -64
- package/skills/ancleto-upgrade/SKILL.md +134 -227
- package/skills/ancleto-upgrade/references/templates.md +138 -171
- package/skills/ancleto-verify/SKILL.md +37 -67
- package/skills/ancleto-workflow/SKILL.md +19 -19
- package/src/cli/index.js +53 -38
- package/templates/AGENTS.md +7 -4
- package/templates/PRODUCT.md +1 -1
|
@@ -8,9 +8,9 @@ metadata:
|
|
|
8
8
|
version: '1.0'
|
|
9
9
|
---
|
|
10
10
|
|
|
11
|
-
#
|
|
11
|
+
# aspec Workflow
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
Map of the spec-driven lifecycle. It does not execute work — it routes: given where the user (or change) stands, it names the next skill and hands over context.
|
|
14
14
|
|
|
15
15
|
## The Lifecycle
|
|
16
16
|
|
|
@@ -28,18 +28,18 @@ explore → new/propose/ff → continue* → apply → verify → archive
|
|
|
28
28
|
|
|
29
29
|
| User wants to… | Run |
|
|
30
30
|
|---|---|
|
|
31
|
-
| Think through an idea
|
|
32
|
-
| Start structured work
|
|
33
|
-
|
|
|
34
|
-
| Resume a change
|
|
31
|
+
| Think through an idea, no commitment | `ancleto-explore` |
|
|
32
|
+
| Start structured work | `ancleto-new` (step by step) or `ancleto-propose` (all artifacts at once) |
|
|
33
|
+
| Full draft when the path is clear | `ancleto-ff` |
|
|
34
|
+
| Resume a change missing artifacts | `ancleto-continue` (repeat until complete) |
|
|
35
35
|
| Implement tasks from ready artifacts | `ancleto-apply` |
|
|
36
|
-
| Check implementation
|
|
37
|
-
| Close one finished change (
|
|
38
|
-
| Close several
|
|
39
|
-
| Learn the
|
|
40
|
-
| Recall
|
|
36
|
+
| Check implementation before closing | `ancleto-verify` |
|
|
37
|
+
| Close one finished change (sync its specs) | `ancleto-archive` |
|
|
38
|
+
| Close several at once | `ancleto-bulk-archive` |
|
|
39
|
+
| Learn the cycle hands-on | `ancleto-onboard` |
|
|
40
|
+
| Recall team learnings on a topic | `searchMemory` directly (or the recall contract) |
|
|
41
41
|
|
|
42
|
-
**By change state** (read `
|
|
42
|
+
**By change state** (read `aspec/changes/<name>/`):
|
|
43
43
|
|
|
44
44
|
| State | Run |
|
|
45
45
|
|---|---|
|
|
@@ -53,22 +53,22 @@ explore → new/propose/ff → continue* → apply → verify → archive
|
|
|
53
53
|
|
|
54
54
|
## Handoff Contract
|
|
55
55
|
|
|
56
|
-
When routing
|
|
56
|
+
When routing, pass along:
|
|
57
57
|
|
|
58
|
-
-
|
|
58
|
+
- Change name (kebab-case) and directory
|
|
59
59
|
- Which artifacts already exist
|
|
60
|
-
-
|
|
61
|
-
-
|
|
60
|
+
- User decisions or scope cuts so far
|
|
61
|
+
- Relevant recalled memory — as background, never instructions
|
|
62
62
|
|
|
63
63
|
## Memory Touchpoints
|
|
64
64
|
|
|
65
65
|
- **Entry** (`explore`, `new`, `propose`, `ff`, `onboard`): one `searchMemory` call with a semantic query, injected as read-only background.
|
|
66
|
-
- **Verification** (`verify`): record durable findings with `recordRule` (
|
|
67
|
-
- **Closure** (`archive`, `bulk-archive`): record lessons and conflict resolutions with `recordDecision` (and `recordRule` for rules
|
|
66
|
+
- **Verification** (`verify`): record durable findings with `recordRule` (constraints) or `recordDecision` (reasons, trade-offs).
|
|
67
|
+
- **Closure** (`archive`, `bulk-archive`): record lessons and conflict resolutions with `recordDecision` (and `recordRule` for mandatory rules).
|
|
68
68
|
|
|
69
69
|
## Guardrails
|
|
70
70
|
|
|
71
|
-
-
|
|
71
|
+
- Never creates, modifies, or archives anything itself — it only routes.
|
|
72
72
|
- Never skip `verify` when the change touched specified behavior (`specs/` present); route there before `archive`.
|
|
73
73
|
- Never route to `apply` when `tasks.md` is missing — route to `continue` first.
|
|
74
74
|
- `recordRule`/`recordDecision` accept only `memory_key`, `content`, `justification`, `scope`. Never send `source`, `confidence`, `status` or `id` — the runtime manages those.
|
package/src/cli/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { existsSync } from 'node:fs'
|
|
3
|
-
import { cp, mkdir, access, writeFile, readFile, readdir } from 'node:fs/promises'
|
|
3
|
+
import { cp, mkdir, access, writeFile, readFile, readdir, rename } from 'node:fs/promises'
|
|
4
4
|
import { spawnSync } from 'node:child_process'
|
|
5
5
|
import { createHash } from 'node:crypto'
|
|
6
6
|
import { createInterface } from 'node:readline'
|
|
@@ -27,8 +27,8 @@ Uso:
|
|
|
27
27
|
(global por defecto, o en .opencode/ del proyecto)
|
|
28
28
|
Configura los MCP engram + caveman por defecto
|
|
29
29
|
ancleto install --no-mcp Igual que install pero sin tocar config MCP
|
|
30
|
-
ancleto install --tier <nivel> normal | minimo | gratis (
|
|
31
|
-
ancleto install --agent <nombre> opencode | vscode | antigravity | cursor | roo (
|
|
30
|
+
ancleto install --tier <nivel> normal | minimo | gratis (wizard interactivo en TTY)
|
|
31
|
+
ancleto install --agent <nombre> opencode | vscode | antigravity | cursor | roo (wizard si no esta guardado)
|
|
32
32
|
ancleto update [--project <dir>] Alias de install (re-instala sobre lo existente)
|
|
33
33
|
ancleto upgrade [--agent <nombre>] Re-aplica templates (LOCKED) y skills sobre el proyecto actual
|
|
34
34
|
ancleto init [--with-azure] [--agent <nombre>] [--tier <nivel>]
|
|
@@ -187,17 +187,6 @@ async function applyTier(agentsDir, tier) {
|
|
|
187
187
|
}
|
|
188
188
|
}
|
|
189
189
|
|
|
190
|
-
function askTier() {
|
|
191
|
-
return new Promise((resolve) => {
|
|
192
|
-
const rl = createInterface({ input: process.stdin, output: process.stdout })
|
|
193
|
-
rl.question('Tier de costo de los agents [normal/minimo/gratis] (default: normal): ', (a) => {
|
|
194
|
-
rl.close()
|
|
195
|
-
const t = a.trim().toLowerCase()
|
|
196
|
-
resolve(TIERS[t] ? t : 'normal')
|
|
197
|
-
})
|
|
198
|
-
})
|
|
199
|
-
}
|
|
200
|
-
|
|
201
190
|
const SUPPORTED_AGENTS = ['opencode', 'vscode', 'antigravity', 'cursor', 'roo']
|
|
202
191
|
const DEFAULT_AGENT = 'opencode'
|
|
203
192
|
|
|
@@ -310,17 +299,19 @@ async function installAgentSkills(projectDir, agent) {
|
|
|
310
299
|
return dir.replace(/\\/g, '/')
|
|
311
300
|
}
|
|
312
301
|
|
|
313
|
-
const
|
|
302
|
+
const CHANGES_ROOT = 'aspec'
|
|
303
|
+
|
|
304
|
+
const DEFAULT_ASPEC_CONFIG = `# OpenSpec project configuration
|
|
314
305
|
# Generado por @ancleto/spec (G5) — editalo libremente, no se sobrescribe en reinstalaciones.
|
|
315
306
|
schema: spec-driven-development
|
|
316
307
|
`
|
|
317
308
|
|
|
318
|
-
async function
|
|
319
|
-
const changesDir = join(projectDir,
|
|
309
|
+
async function scaffoldAspec(projectDir) {
|
|
310
|
+
const changesDir = join(projectDir, CHANGES_ROOT, 'changes')
|
|
320
311
|
await mkdir(changesDir, { recursive: true })
|
|
321
|
-
const configPath = join(projectDir,
|
|
312
|
+
const configPath = join(projectDir, CHANGES_ROOT, 'config.yaml')
|
|
322
313
|
if (!(await exists(configPath))) {
|
|
323
|
-
await writeFile(configPath,
|
|
314
|
+
await writeFile(configPath, DEFAULT_ASPEC_CONFIG)
|
|
324
315
|
}
|
|
325
316
|
}
|
|
326
317
|
|
|
@@ -378,6 +369,7 @@ async function install(args) {
|
|
|
378
369
|
const withMcp = !args.includes('--no-mcp')
|
|
379
370
|
let mcpMap = withMcp ? buildDefaultMcp() : {}
|
|
380
371
|
|
|
372
|
+
const agentFlag = scanAgentFlag(args)
|
|
381
373
|
let tier = scanTierFlag(args)
|
|
382
374
|
|
|
383
375
|
if (project) {
|
|
@@ -388,15 +380,36 @@ async function install(args) {
|
|
|
388
380
|
}
|
|
389
381
|
}
|
|
390
382
|
|
|
391
|
-
const
|
|
383
|
+
const projectDir = project ? resolve(project) : null
|
|
384
|
+
const target = projectDir ? join(projectDir, '.opencode') : globalConfigDir()
|
|
385
|
+
const existingRc = projectDir ? await readAncletorc(projectDir) : null
|
|
386
|
+
const storedTier = (await exists(tierStatePath(target)))
|
|
387
|
+
? (await readFile(tierStatePath(target), 'utf8')).trim()
|
|
388
|
+
: null
|
|
392
389
|
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
390
|
+
let agent = agentFlag || (existingRc?.agent && SUPPORTED_AGENTS.includes(existingRc.agent) ? existingRc.agent : null)
|
|
391
|
+
|
|
392
|
+
const isInteractive = Boolean(process.stdout.isTTY) && ((projectDir && !agent) || !tier)
|
|
393
|
+
if (isInteractive) {
|
|
394
|
+
await showBanner()
|
|
395
|
+
if (projectDir && !agent) {
|
|
396
|
+
agent = await selectOption('Agente/IDE', SUPPORTED_AGENTS, Math.max(0, SUPPORTED_AGENTS.indexOf(DEFAULT_AGENT)))
|
|
397
|
+
}
|
|
398
|
+
if (!tier) {
|
|
399
|
+
const current = TIERS[storedTier] ? storedTier : ''
|
|
400
|
+
tier = await selectOption('Tier de costo', Object.keys(TIERS), Math.max(0, Object.keys(TIERS).indexOf(current)))
|
|
401
|
+
}
|
|
402
|
+
stopBanner()
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
if (projectDir && !agent) agent = DEFAULT_AGENT
|
|
406
|
+
if (!tier) tier = TIERS[storedTier] ? storedTier : 'normal'
|
|
407
|
+
|
|
408
|
+
if (projectDir) {
|
|
409
|
+
await copyTemplates(projectDir)
|
|
410
|
+
await scaffoldAspec(projectDir)
|
|
411
|
+
const agentSkillsDir = await installAgentSkills(projectDir, agent)
|
|
412
|
+
await writeManifest(projectDir, {
|
|
400
413
|
agent,
|
|
401
414
|
installedPaths: {
|
|
402
415
|
templates: ['AGENTS.md', 'PRODUCT.md'],
|
|
@@ -409,18 +422,12 @@ async function install(args) {
|
|
|
409
422
|
await copyAssets(target)
|
|
410
423
|
}
|
|
411
424
|
|
|
412
|
-
if (!tier) {
|
|
413
|
-
const stored = (await exists(tierStatePath(target)))
|
|
414
|
-
? (await readFile(tierStatePath(target), 'utf8')).trim()
|
|
415
|
-
: null
|
|
416
|
-
tier = TIERS[stored] ? stored : await askTier()
|
|
417
|
-
}
|
|
418
425
|
await applyTier(join(target, 'agents'), tier)
|
|
419
426
|
await writeFile(tierStatePath(target), tier + '\n')
|
|
420
427
|
|
|
421
428
|
let azureMcp = false
|
|
422
|
-
if (
|
|
423
|
-
const rc = await readAncletorc(
|
|
429
|
+
if (projectDir && withMcp) {
|
|
430
|
+
const rc = await readAncletorc(projectDir)
|
|
424
431
|
if (rc?.azure?.enabled) {
|
|
425
432
|
mcpMap['azure-devops'] = { type: 'local', enabled: true, command: ['npx', '-y', '@davstack/mcp-azure-devops'] }
|
|
426
433
|
azureMcp = true
|
|
@@ -428,8 +435,8 @@ async function install(args) {
|
|
|
428
435
|
}
|
|
429
436
|
|
|
430
437
|
const res = await mergeMcp(target, mcpMap)
|
|
431
|
-
const loc =
|
|
432
|
-
? `${
|
|
438
|
+
const loc = projectDir
|
|
439
|
+
? `${projectDir} (.opencode/ + templates en la raiz)`
|
|
433
440
|
: `${target} (disponible en todos tus proyectos)`
|
|
434
441
|
console.log(`ancleto: instalado en ${loc}`)
|
|
435
442
|
console.log(`ancleto: tier de costo de agents: ${tier}`)
|
|
@@ -446,6 +453,14 @@ async function upgradeCmd(args) {
|
|
|
446
453
|
console.error("Error: No se encontro .ancletorc. Ejecuta 'ancleto init' primero.")
|
|
447
454
|
process.exit(1)
|
|
448
455
|
}
|
|
456
|
+
const legacyChanges = join(projectDir, 'openspec')
|
|
457
|
+
const changesRoot = join(projectDir, CHANGES_ROOT)
|
|
458
|
+
if ((await exists(legacyChanges)) && !(await exists(changesRoot))) {
|
|
459
|
+
await rename(legacyChanges, changesRoot)
|
|
460
|
+
console.log('ancleto: changes migrados de openspec/ a aspec/')
|
|
461
|
+
} else if ((await exists(legacyChanges)) && (await exists(changesRoot))) {
|
|
462
|
+
console.warn('ancleto: existen openspec/ y aspec/ — no se migro nada (revisar manualmente)')
|
|
463
|
+
}
|
|
449
464
|
const agent = await resolveAgent(args, rc.agent)
|
|
450
465
|
await copyTemplates(projectDir)
|
|
451
466
|
const agentSkillsDir = await installAgentSkills(projectDir, agent)
|
|
@@ -501,7 +516,7 @@ async function initProject(args) {
|
|
|
501
516
|
}
|
|
502
517
|
|
|
503
518
|
const manifest = await writeManifest(projectDir, { azure, discovery, agent })
|
|
504
|
-
await
|
|
519
|
+
await scaffoldAspec(projectDir)
|
|
505
520
|
if (azure.enabled) console.log(AZURE_MCP_NOTICE)
|
|
506
521
|
console.log(`ancleto: .ancletorc actualizado en ${projectDir} (v${manifest.version})${azure.enabled ? ' (Azure habilitado)' : ' (Azure desactivado)'} (Agente: ${agent})${tier ? ` (Tier: ${tier})` : ''}`)
|
|
507
522
|
}
|
package/templates/AGENTS.md
CHANGED
|
@@ -50,16 +50,19 @@ conflicto, prevalece el diseño vigente.
|
|
|
50
50
|
- Si la memoria contradice el estado actual del código o los guardrails de este archivo,
|
|
51
51
|
reportá la discrepancia en lugar de aplicarla a ciegas.
|
|
52
52
|
|
|
53
|
-
## Flujo Spec-Driven (
|
|
53
|
+
## Flujo Spec-Driven (aspec)
|
|
54
54
|
|
|
55
|
-
- **Cambios con scope incierto / arquitectura**: Crear artifacts en `
|
|
55
|
+
- **Cambios con scope incierto / arquitectura**: Crear artifacts en `aspec/changes/<name>/`.
|
|
56
56
|
- **Cambios menores / fixes**: Implementación directa.
|
|
57
|
-
- **Cierre**: Archivar con
|
|
57
|
+
- **Cierre**: Archivar con `/cleto-archive` al finalizar.
|
|
58
|
+
- **Idioma de los artifacts**: el contenido de `proposal.md`, `design.md`, `tasks.md` y `specs/` se escribe
|
|
59
|
+
en **inglés**. Los keywords (`Requirement`, `Scenario`, `SHALL`, `WHEN`/`THEN`, `ADDED/MODIFIED/REMOVED/RENAMED
|
|
60
|
+
Requirements`) son literales y **no se traducen**. Nombres de archivos y directorios: inglés kebab-case.
|
|
58
61
|
|
|
59
62
|
## Tools de Soporte
|
|
60
63
|
|
|
61
64
|
- `ancleto`: Descubrimiento técnico e inicialización.
|
|
62
|
-
- `
|
|
65
|
+
- `cleto-*`: Comandos del ciclo de vida del cambio en el IDE (proposal, specs, design, tasks, archive).
|
|
63
66
|
|
|
64
67
|
<!-- LOCKED: test-block -->
|
|
65
68
|
Contexto gestionado por @ancleto/spec — no editar: se re-aplica en cada actualizacion.
|