@drafthq/draft 2.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +38 -0
- package/.claude-plugin/plugin.json +26 -0
- package/LICENSE +21 -0
- package/README.md +272 -0
- package/bin/README.md +49 -0
- package/cli/bin/draft.js +13 -0
- package/cli/src/cli.js +113 -0
- package/cli/src/hosts/claude-code.js +46 -0
- package/cli/src/hosts/codex.js +33 -0
- package/cli/src/hosts/cursor.js +50 -0
- package/cli/src/hosts/index.js +24 -0
- package/cli/src/hosts/opencode.js +39 -0
- package/cli/src/installer.js +61 -0
- package/cli/src/lib/fsx.js +34 -0
- package/cli/src/lib/graph.js +23 -0
- package/cli/src/lib/log.js +32 -0
- package/cli/src/lib/paths.js +14 -0
- package/core/agents/architect.md +338 -0
- package/core/agents/debugger.md +193 -0
- package/core/agents/ops.md +104 -0
- package/core/agents/planner.md +158 -0
- package/core/agents/rca.md +314 -0
- package/core/agents/reviewer.md +256 -0
- package/core/agents/writer.md +110 -0
- package/core/guardrails/README.md +4 -0
- package/core/guardrails/code-quality.md +4 -0
- package/core/guardrails/dependency-triage.md +4 -0
- package/core/guardrails/design-norms.md +4 -0
- package/core/guardrails/language-standards.md +4 -0
- package/core/guardrails/review-checks.md +4 -0
- package/core/guardrails/secure-patterns.md +4 -0
- package/core/guardrails/security.md +4 -0
- package/core/guardrails.md +22 -0
- package/core/knowledge-base.md +127 -0
- package/core/methodology.md +1221 -0
- package/core/shared/condensation.md +224 -0
- package/core/shared/context-verify.md +44 -0
- package/core/shared/cross-skill-dispatch.md +127 -0
- package/core/shared/discovery-schema.md +75 -0
- package/core/shared/draft-context-loading.md +282 -0
- package/core/shared/git-report-metadata.md +106 -0
- package/core/shared/graph-query.md +239 -0
- package/core/shared/graph-usage-report.md +22 -0
- package/core/shared/jira-sync.md +170 -0
- package/core/shared/parallel-analysis.md +386 -0
- package/core/shared/parallel-fanout.md +10 -0
- package/core/shared/pattern-learning.md +146 -0
- package/core/shared/red-flags.md +58 -0
- package/core/shared/template-contract.md +22 -0
- package/core/shared/template-hygiene.md +10 -0
- package/core/shared/tool-resolver.md +10 -0
- package/core/shared/vcs-commands.md +97 -0
- package/core/shared/verification-gates.md +47 -0
- package/core/templates/CHANGELOG.md +70 -0
- package/core/templates/ai-context-export.md +8 -0
- package/core/templates/ai-context.md +270 -0
- package/core/templates/ai-profile.md +41 -0
- package/core/templates/architecture.md +203 -0
- package/core/templates/dependency-graph.md +103 -0
- package/core/templates/discovery.md +79 -0
- package/core/templates/guardrails.md +143 -0
- package/core/templates/hld.md +327 -0
- package/core/templates/intake-questions.md +403 -0
- package/core/templates/jira.md +119 -0
- package/core/templates/lld.md +283 -0
- package/core/templates/metadata.json +66 -0
- package/core/templates/plan.md +130 -0
- package/core/templates/product.md +110 -0
- package/core/templates/rca.md +86 -0
- package/core/templates/root-architecture.md +127 -0
- package/core/templates/root-product.md +53 -0
- package/core/templates/root-tech-stack.md +117 -0
- package/core/templates/service-index.md +55 -0
- package/core/templates/session-summary.md +8 -0
- package/core/templates/spec.md +165 -0
- package/core/templates/tech-matrix.md +101 -0
- package/core/templates/tech-stack.md +169 -0
- package/core/templates/track-architecture.md +311 -0
- package/core/templates/workflow.md +187 -0
- package/integrations/agents/AGENTS.md +24384 -0
- package/integrations/copilot/.github/copilot-instructions.md +24384 -0
- package/integrations/gemini/.gemini.md +26 -0
- package/package.json +53 -0
- package/scripts/fetch-memory-engine.sh +116 -0
- package/scripts/lib.sh +256 -0
- package/scripts/tools/_lib.sh +220 -0
- package/scripts/tools/adr-index.sh +117 -0
- package/scripts/tools/check-graph-usage-report.sh +95 -0
- package/scripts/tools/check-scope-conflicts.sh +139 -0
- package/scripts/tools/check-skill-line-caps.sh +115 -0
- package/scripts/tools/check-template-noop.sh +87 -0
- package/scripts/tools/check-track-hygiene.sh +230 -0
- package/scripts/tools/classify-files.sh +231 -0
- package/scripts/tools/cycle-detect.sh +75 -0
- package/scripts/tools/detect-test-framework.sh +135 -0
- package/scripts/tools/diff-templates-vs-tracks.sh +176 -0
- package/scripts/tools/emit-skill-metrics.sh +71 -0
- package/scripts/tools/fix-whitespace.sh +192 -0
- package/scripts/tools/freshness-check.sh +143 -0
- package/scripts/tools/git-metadata.sh +203 -0
- package/scripts/tools/graph-callers.sh +74 -0
- package/scripts/tools/graph-impact.sh +93 -0
- package/scripts/tools/graph-snapshot.sh +102 -0
- package/scripts/tools/hotspot-rank.sh +75 -0
- package/scripts/tools/manage-symlinks.sh +85 -0
- package/scripts/tools/mermaid-from-graph.sh +92 -0
- package/scripts/tools/migrate-track-frontmatter.sh +241 -0
- package/scripts/tools/parse-git-log.sh +135 -0
- package/scripts/tools/parse-reports.sh +114 -0
- package/scripts/tools/render-track.sh +145 -0
- package/scripts/tools/run-coverage.sh +153 -0
- package/scripts/tools/scan-markers.sh +144 -0
- package/scripts/tools/skill-caps.conf +24 -0
- package/scripts/tools/validate-frontmatter.sh +125 -0
- package/scripts/tools/verify-citations.sh +250 -0
- package/scripts/tools/verify-doc-anchors.sh +204 -0
- package/scripts/tools/verify-graph-binary.sh +154 -0
- package/skills/GRAPH.md +332 -0
- package/skills/adr/SKILL.md +374 -0
- package/skills/assist-review/SKILL.md +49 -0
- package/skills/bughunt/SKILL.md +668 -0
- package/skills/bughunt/references/regression-tests.md +399 -0
- package/skills/change/SKILL.md +267 -0
- package/skills/coverage/SKILL.md +336 -0
- package/skills/debug/SKILL.md +201 -0
- package/skills/decompose/SKILL.md +656 -0
- package/skills/deep-review/SKILL.md +326 -0
- package/skills/deploy-checklist/SKILL.md +254 -0
- package/skills/discover/SKILL.md +66 -0
- package/skills/docs/SKILL.md +42 -0
- package/skills/documentation/SKILL.md +197 -0
- package/skills/draft/SKILL.md +177 -0
- package/skills/draft/context-files.md +57 -0
- package/skills/draft/intent-mapping.md +37 -0
- package/skills/draft/quality-guide.md +51 -0
- package/skills/graph/SKILL.md +107 -0
- package/skills/impact/SKILL.md +86 -0
- package/skills/implement/SKILL.md +794 -0
- package/skills/incident-response/SKILL.md +245 -0
- package/skills/index/SKILL.md +848 -0
- package/skills/init/SKILL.md +1784 -0
- package/skills/init/references/architecture-spec.md +1259 -0
- package/skills/integrations/SKILL.md +53 -0
- package/skills/jira/SKILL.md +577 -0
- package/skills/jira/references/review.md +1322 -0
- package/skills/learn/SKILL.md +478 -0
- package/skills/new-track/SKILL.md +841 -0
- package/skills/ops/SKILL.md +57 -0
- package/skills/plan/SKILL.md +60 -0
- package/skills/quick-review/SKILL.md +216 -0
- package/skills/revert/SKILL.md +178 -0
- package/skills/review/SKILL.md +1114 -0
- package/skills/standup/SKILL.md +183 -0
- package/skills/status/SKILL.md +183 -0
- package/skills/tech-debt/SKILL.md +318 -0
- package/skills/testing-strategy/SKILL.md +195 -0
- package/skills/tour/SKILL.md +38 -0
- package/skills/upload/SKILL.md +117 -0
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// Host registry. Order here is the order shown by `draft list`.
|
|
4
|
+
const hosts = [
|
|
5
|
+
require('./claude-code'),
|
|
6
|
+
require('./cursor'),
|
|
7
|
+
require('./codex'),
|
|
8
|
+
require('./opencode'),
|
|
9
|
+
];
|
|
10
|
+
|
|
11
|
+
const byId = new Map();
|
|
12
|
+
for (const host of hosts) {
|
|
13
|
+
byId.set(host.id, host);
|
|
14
|
+
for (const alias of host.aliases || []) {
|
|
15
|
+
byId.set(alias, host);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function getHost(id) {
|
|
20
|
+
if (!id) return null;
|
|
21
|
+
return byId.get(String(id).toLowerCase()) || null;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
module.exports = { hosts, getHost };
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const path = require('path');
|
|
4
|
+
const { asset } = require('../lib/paths');
|
|
5
|
+
|
|
6
|
+
// opencode reads AGENTS.md from the repo root and auto-discovers skills under the
|
|
7
|
+
// cross-host ~/.agents/skills/ convention. Write the generated AGENTS.md (the
|
|
8
|
+
// working integration) and bundle Draft's skills under ~/.agents/skills/draft/.
|
|
9
|
+
module.exports = {
|
|
10
|
+
id: 'opencode',
|
|
11
|
+
label: 'opencode',
|
|
12
|
+
aliases: ['open-code'],
|
|
13
|
+
defaultScope: 'project',
|
|
14
|
+
|
|
15
|
+
plan(ctx) {
|
|
16
|
+
const agentsDest = path.join(ctx.cwd, 'AGENTS.md');
|
|
17
|
+
const skillsDest = path.join(ctx.home, '.agents', 'skills', 'draft');
|
|
18
|
+
return {
|
|
19
|
+
targetSummary: `${agentsDest} + ${skillsDest}`,
|
|
20
|
+
actions: [
|
|
21
|
+
{
|
|
22
|
+
kind: 'copyFile',
|
|
23
|
+
src: asset('integrations', 'agents', 'AGENTS.md'),
|
|
24
|
+
dest: agentsDest,
|
|
25
|
+
label: 'AGENTS.md',
|
|
26
|
+
guard: true,
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
kind: 'copyTree',
|
|
30
|
+
src: asset('skills'),
|
|
31
|
+
dest: skillsDest,
|
|
32
|
+
label: '~/.agents/skills/draft/',
|
|
33
|
+
},
|
|
34
|
+
],
|
|
35
|
+
graph: false,
|
|
36
|
+
done: 'opencode reads AGENTS.md from the repo root; Draft skills bundled under ~/.agents/skills/draft/.',
|
|
37
|
+
};
|
|
38
|
+
},
|
|
39
|
+
};
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const fsx = require('./lib/fsx');
|
|
4
|
+
const log = require('./lib/log');
|
|
5
|
+
const { fetchGraph } = require('./lib/graph');
|
|
6
|
+
|
|
7
|
+
function execAction(act, ctx) {
|
|
8
|
+
log.plan(`${ctx.dryRun ? 'would write' : 'writing'}: ${act.dest}`);
|
|
9
|
+
if (ctx.dryRun) return;
|
|
10
|
+
switch (act.kind) {
|
|
11
|
+
case 'copyTree':
|
|
12
|
+
fsx.copyTree(act.src, act.dest);
|
|
13
|
+
break;
|
|
14
|
+
case 'copyFile':
|
|
15
|
+
fsx.copyFile(act.src, act.dest);
|
|
16
|
+
break;
|
|
17
|
+
case 'writeFile':
|
|
18
|
+
fsx.writeFile(act.dest, act.content);
|
|
19
|
+
break;
|
|
20
|
+
default:
|
|
21
|
+
throw new Error(`Unknown action kind: ${act.kind}`);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function install(host, ctx) {
|
|
26
|
+
const plan = host.plan(ctx);
|
|
27
|
+
log.step(`Installing Draft -> ${host.label} [${plan.targetSummary}]${ctx.dryRun ? ' (dry run)' : ''}`);
|
|
28
|
+
|
|
29
|
+
// Pre-flight: every bundled source must exist, and guarded targets must not
|
|
30
|
+
// already exist unless --force. Checked up front so a failure writes nothing.
|
|
31
|
+
for (const act of plan.actions) {
|
|
32
|
+
if (!fsx.exists(act.src)) {
|
|
33
|
+
log.error(`Bundled asset missing: ${act.src}`);
|
|
34
|
+
log.error('Reinstall @drafthq/draft — the package looks incomplete.');
|
|
35
|
+
return 1;
|
|
36
|
+
}
|
|
37
|
+
if (act.guard && fsx.exists(act.dest) && !ctx.force) {
|
|
38
|
+
log.error(`${act.dest} already exists. Re-run with --force to overwrite.`);
|
|
39
|
+
return 1;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
for (const act of plan.actions) {
|
|
44
|
+
execAction(act, ctx);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if (plan.graph && ctx.graph && !ctx.dryRun) {
|
|
48
|
+
fetchGraph(ctx);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
(plan.notes || []).forEach((n) => log.note(n));
|
|
52
|
+
|
|
53
|
+
if (ctx.dryRun) {
|
|
54
|
+
log.success('Dry run complete — no files written.');
|
|
55
|
+
} else {
|
|
56
|
+
log.success(plan.done || 'Done.');
|
|
57
|
+
}
|
|
58
|
+
return 0;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
module.exports = { install };
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
|
|
6
|
+
function exists(p) {
|
|
7
|
+
try {
|
|
8
|
+
fs.accessSync(p);
|
|
9
|
+
return true;
|
|
10
|
+
} catch {
|
|
11
|
+
return false;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function ensureDir(dir) {
|
|
16
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function copyTree(src, dest) {
|
|
20
|
+
ensureDir(path.dirname(dest));
|
|
21
|
+
fs.cpSync(src, dest, { recursive: true });
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function copyFile(src, dest) {
|
|
25
|
+
ensureDir(path.dirname(dest));
|
|
26
|
+
fs.copyFileSync(src, dest);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function writeFile(dest, content) {
|
|
30
|
+
ensureDir(path.dirname(dest));
|
|
31
|
+
fs.writeFileSync(dest, content);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
module.exports = { exists, ensureDir, copyTree, copyFile, writeFile };
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { spawnSync } = require('child_process');
|
|
4
|
+
const { asset } = require('./paths');
|
|
5
|
+
const fsx = require('./fsx');
|
|
6
|
+
const log = require('./log');
|
|
7
|
+
|
|
8
|
+
// Best-effort fetch of the knowledge-graph engine (codebase-memory-mcp) into the
|
|
9
|
+
// Draft-managed cache (~/.cache/draft/bin). Network-gated; failures are non-fatal
|
|
10
|
+
// because graph features degrade gracefully when the engine is absent.
|
|
11
|
+
function fetchGraph() {
|
|
12
|
+
const script = asset('scripts', 'fetch-memory-engine.sh');
|
|
13
|
+
if (!fsx.exists(script)) {
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
log.note('Fetching knowledge-graph engine (best-effort)...');
|
|
17
|
+
const result = spawnSync('bash', [script], { stdio: 'inherit' });
|
|
18
|
+
if (result.status !== 0) {
|
|
19
|
+
log.warn('Graph engine fetch skipped (offline or unsupported platform) — features degrade gracefully.');
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
module.exports = { fetchGraph };
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// Minimal, dependency-free logging. Plain ASCII so it renders everywhere.
|
|
4
|
+
function info(msg) {
|
|
5
|
+
console.log(msg);
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
function step(msg) {
|
|
9
|
+
console.log('\n' + msg);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function plan(msg) {
|
|
13
|
+
console.log(' - ' + msg);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function note(msg) {
|
|
17
|
+
console.log(' > ' + msg);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function success(msg) {
|
|
21
|
+
console.log('OK ' + msg);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function warn(msg) {
|
|
25
|
+
console.warn('! ' + msg);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function error(msg) {
|
|
29
|
+
console.error('x ' + msg);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
module.exports = { info, step, plan, note, success, warn, error };
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const path = require('path');
|
|
4
|
+
|
|
5
|
+
// The package root holds the bundled assets (skills/, core/, integrations/, …).
|
|
6
|
+
// cli/src/lib/paths.js → ../../.. === package root, identical for global installs
|
|
7
|
+
// and `npx` (both unpack the published tarball with this layout).
|
|
8
|
+
const PACKAGE_ROOT = path.resolve(__dirname, '..', '..', '..');
|
|
9
|
+
|
|
10
|
+
function asset(...parts) {
|
|
11
|
+
return path.join(PACKAGE_ROOT, ...parts);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
module.exports = { PACKAGE_ROOT, asset };
|
|
@@ -0,0 +1,338 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Architecture agent for module decomposition, story writing, execution state design, and function skeleton generation. Guides structured pre-implementation design.
|
|
3
|
+
capabilities:
|
|
4
|
+
- Module identification and boundary definition
|
|
5
|
+
- Dependency graph analysis and implementation ordering
|
|
6
|
+
- Algorithm documentation (Stories)
|
|
7
|
+
- Execution state design
|
|
8
|
+
- Function skeleton generation
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
# Architect Agent
|
|
12
|
+
|
|
13
|
+
You are an architecture agent for Draft-based development. You guide developers through structured pre-implementation design: decomposing systems into modules, documenting algorithms, designing execution state, and generating function skeletons.
|
|
14
|
+
|
|
15
|
+
## Module Decomposition
|
|
16
|
+
|
|
17
|
+
### Rules
|
|
18
|
+
|
|
19
|
+
1. **Single Responsibility** - Each module owns one concern
|
|
20
|
+
2. **Size Constraint** - 1-3 files per module. If more, split further.
|
|
21
|
+
3. **Clear API Boundary** - Every module has a defined public interface
|
|
22
|
+
4. **Minimal Coupling** - Modules communicate through interfaces, not internals
|
|
23
|
+
5. **Testable in Isolation** - Each module can be unit-tested independently
|
|
24
|
+
|
|
25
|
+
### Module Definition Format
|
|
26
|
+
|
|
27
|
+
For each module, define:
|
|
28
|
+
- **Name** - Short, descriptive (e.g., `auth`, `scheduler`, `parser`)
|
|
29
|
+
- **Responsibility** - One sentence describing what it owns
|
|
30
|
+
- **Files** - Expected source files
|
|
31
|
+
- **API Surface** - Public functions/classes/interfaces (see language-specific examples below)
|
|
32
|
+
- **Dependencies** - Which other modules it imports from
|
|
33
|
+
- **Complexity** - Low / Medium / High
|
|
34
|
+
|
|
35
|
+
Output format: Use the template at `core/templates/ai-context.md` for project-wide context documents, or `core/templates/architecture.md` for track-scoped and human-readable documents.
|
|
36
|
+
|
|
37
|
+
### API Surface Examples by Language
|
|
38
|
+
|
|
39
|
+
Represent API surfaces using the conventions of the project's primary language:
|
|
40
|
+
|
|
41
|
+
**TypeScript:**
|
|
42
|
+
```
|
|
43
|
+
- API Surface:
|
|
44
|
+
- `createUser(data: CreateUserInput): Promise<User>`
|
|
45
|
+
- `deleteUser(id: string): Promise<void>`
|
|
46
|
+
- `interface UserRepository { findById, findByEmail, save }`
|
|
47
|
+
- `type CreateUserInput = { name: string; email: string }`
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
**Python:**
|
|
51
|
+
```
|
|
52
|
+
- API Surface:
|
|
53
|
+
- `create_user(data: CreateUserInput) -> User`
|
|
54
|
+
- `delete_user(user_id: str) -> None`
|
|
55
|
+
- `class UserRepository(Protocol): find_by_id, find_by_email, save`
|
|
56
|
+
- `@dataclass CreateUserInput: name: str, email: str`
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
**Go:**
|
|
60
|
+
```
|
|
61
|
+
- API Surface:
|
|
62
|
+
- `func CreateUser(data CreateUserInput) (*User, error)`
|
|
63
|
+
- `func DeleteUser(id string) error`
|
|
64
|
+
- `type UserRepository interface { FindByID, FindByEmail, Save }`
|
|
65
|
+
- `type CreateUserInput struct { Name, Email string }`
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
**Rust:**
|
|
69
|
+
```
|
|
70
|
+
- API Surface:
|
|
71
|
+
- `pub fn create_user(data: CreateUserInput) -> Result<User, Error>`
|
|
72
|
+
- `pub fn delete_user(id: &str) -> Result<(), Error>`
|
|
73
|
+
- `pub trait UserRepository { fn find_by_id, fn find_by_email, fn save }`
|
|
74
|
+
- `pub struct CreateUserInput { pub name: String, pub email: String }`
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Use the project's primary language from `draft/tech-stack.md`. Include function signatures with types, exported interfaces/traits/protocols, and key data structures.
|
|
78
|
+
|
|
79
|
+
### Ingredients
|
|
80
|
+
|
|
81
|
+
Each module typically contains some combination of:
|
|
82
|
+
- **API** - Public interface exposed to other modules
|
|
83
|
+
- **Control Flow** - Core logic and decision paths
|
|
84
|
+
- **Execution State** - Intermediate data structures used during processing
|
|
85
|
+
- **Functions** - Operations that transform inputs to outputs
|
|
86
|
+
|
|
87
|
+
---
|
|
88
|
+
|
|
89
|
+
## Dependency Analysis
|
|
90
|
+
|
|
91
|
+
### Process
|
|
92
|
+
|
|
93
|
+
1. **Identify edges** - Module A depends on Module B if A imports from B's API
|
|
94
|
+
2. **Detect cycles** - Circular dependencies indicate poor boundaries. Break using the cycle-breaking framework below.
|
|
95
|
+
3. **Topological sort** - Implementation order follows reverse dependency order (implement leaves first)
|
|
96
|
+
4. **Identify parallelism** - Modules with no dependency relationship can be implemented concurrently
|
|
97
|
+
|
|
98
|
+
### Dependency Diagram Format
|
|
99
|
+
|
|
100
|
+
```
|
|
101
|
+
[auth] ──> [database]
|
|
102
|
+
│ │
|
|
103
|
+
└──> [config] <──┘
|
|
104
|
+
│
|
|
105
|
+
[logging] (shared, no deps)
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
Use ASCII art. Arrow direction: `A ──> B` means A depends on B.
|
|
109
|
+
|
|
110
|
+
### Cycle-Breaking Framework
|
|
111
|
+
|
|
112
|
+
When modules form a circular dependency (A → B → A), apply this decision process:
|
|
113
|
+
|
|
114
|
+
**Step 1: Identify the shared concern.** What data or behavior do both modules need from each other? Name it explicitly.
|
|
115
|
+
|
|
116
|
+
**Step 2: Choose a strategy:**
|
|
117
|
+
|
|
118
|
+
| Pattern | When to Use | Result |
|
|
119
|
+
|---------|-------------|--------|
|
|
120
|
+
| **Extract shared interface** | Both modules need the same abstraction (types, contracts) | New `<name>-types` or `<name>-shared` module containing only interfaces/types |
|
|
121
|
+
| **Invert dependency** | One module only needs a callback or event from the other | Dependent module accepts a function/interface instead of importing directly |
|
|
122
|
+
| **Merge modules** | The two modules are actually one concern split artificially | Combined module with single responsibility |
|
|
123
|
+
|
|
124
|
+
**Step 3: Name the extracted module.** Use `<shared-concern>-types` for pure type modules, `<shared-concern>-core` for shared logic modules. Never use generic names like `shared` or `common`.
|
|
125
|
+
|
|
126
|
+
**Step 4: Define the extracted module's API.** It should contain only what both modules need — nothing more.
|
|
127
|
+
|
|
128
|
+
**Example:**
|
|
129
|
+
|
|
130
|
+
Before (cycle):
|
|
131
|
+
```
|
|
132
|
+
[user-service] ──> [notification-service]
|
|
133
|
+
↑ │
|
|
134
|
+
└────────────────────┘
|
|
135
|
+
```
|
|
136
|
+
`user-service` imports `sendNotification` from `notification-service`.
|
|
137
|
+
`notification-service` imports `getUserPreferences` from `user-service`.
|
|
138
|
+
|
|
139
|
+
Analysis: Both modules need user preference data. Extract it.
|
|
140
|
+
|
|
141
|
+
After (resolved):
|
|
142
|
+
```
|
|
143
|
+
[user-preferences] (new - extracted shared concern)
|
|
144
|
+
↑ ↑
|
|
145
|
+
│ │
|
|
146
|
+
[user-service] [notification-service]
|
|
147
|
+
│
|
|
148
|
+
└──> [notification-service]
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
New module `user-preferences`:
|
|
152
|
+
- **Responsibility:** Owns user notification/display preference data and access
|
|
153
|
+
- **API Surface:** `getUserPreferences(userId): Preferences`
|
|
154
|
+
- **Files:** `user-preferences.ts`, `user-preferences.test.ts`
|
|
155
|
+
- **Dependencies:** none (leaf module)
|
|
156
|
+
|
|
157
|
+
### Dependency Table Format
|
|
158
|
+
|
|
159
|
+
| Module | Depends On | Depended By |
|
|
160
|
+
|--------|-----------|-------------|
|
|
161
|
+
| logging | - | auth, database, config |
|
|
162
|
+
| config | logging | auth, database |
|
|
163
|
+
| database | config, logging | auth |
|
|
164
|
+
| auth | database, config, logging | - |
|
|
165
|
+
|
|
166
|
+
---
|
|
167
|
+
|
|
168
|
+
## Story Writing
|
|
169
|
+
|
|
170
|
+
### Purpose
|
|
171
|
+
|
|
172
|
+
A Story is a natural-language algorithm description placed at the top of a code file. It captures the **Input -> Output** path and the algorithmic approach before any code is written.
|
|
173
|
+
|
|
174
|
+
### Story Lifecycle
|
|
175
|
+
|
|
176
|
+
Stories flow through three stages:
|
|
177
|
+
|
|
178
|
+
1. **Placeholder** — During `/draft:decompose`, each module in `.ai-context.md` (or track-level `architecture.md`) gets a Story field set to `[placeholder - filled during /draft:implement]`. This signals that the module exists but its algorithm hasn't been documented yet.
|
|
179
|
+
|
|
180
|
+
2. **Written** — During `/draft:implement` (with architecture mode), before coding each module's first file, write the Story as a code comment at the top of the file. Present it to the developer for approval. Once approved, update the module's Story field in `.ai-context.md` (or `architecture.md`) with a one-line summary referencing the file:
|
|
181
|
+
```markdown
|
|
182
|
+
- **Story:** Documented in `src/auth.ts:1-12` — validates token, resolves user, checks permissions
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
3. **Updated** — If the algorithm changes during refactoring, update both the code comment and the `.ai-context.md` summary. The code comment is the source of truth; the `.ai-context.md` entry is a pointer.
|
|
186
|
+
|
|
187
|
+
**Key rule:** The `.ai-context.md` Story field is never the full story — it's a summary + file reference. The complete story lives as a comment in the source code.
|
|
188
|
+
|
|
189
|
+
### Story Format
|
|
190
|
+
|
|
191
|
+
```
|
|
192
|
+
// Story: [Module/File Name]
|
|
193
|
+
//
|
|
194
|
+
// Input: [what this module/function receives]
|
|
195
|
+
// Process:
|
|
196
|
+
// 1. [first algorithmic step]
|
|
197
|
+
// 2. [second algorithmic step]
|
|
198
|
+
// 3. [third algorithmic step]
|
|
199
|
+
// Output: [what this module/function produces]
|
|
200
|
+
//
|
|
201
|
+
// Dependencies: [what this module relies on]
|
|
202
|
+
// Side effects: [any mutations, I/O, or external calls]
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
### Guidelines
|
|
206
|
+
|
|
207
|
+
- **Describe the algorithm, not the implementation** - "Sort by priority, then deduplicate" not "Use Array.sort() with comparator"
|
|
208
|
+
- **Use natural language** - No code syntax in stories
|
|
209
|
+
- **Be specific about data flow** - Name the data, describe transformations
|
|
210
|
+
- **Keep it concise** - 5-15 lines max. If longer, the module is too complex.
|
|
211
|
+
- **Update when algorithm changes** - Story must reflect current logic
|
|
212
|
+
- **Elegance check** - Before presenting the story, ask: "Is this the simplest algorithm that satisfies the requirements?" If a cleaner approach exists, propose it here — the story stage is the cheapest place to change direction, before skeletons and TDD lock in the design. Skip for trivial tasks.
|
|
213
|
+
|
|
214
|
+
### Anti-Patterns
|
|
215
|
+
|
|
216
|
+
| Don't | Instead |
|
|
217
|
+
|-------|---------|
|
|
218
|
+
| Describe implementation details | Describe the algorithm |
|
|
219
|
+
| List every function call | Describe the data transformation |
|
|
220
|
+
| Copy the code in English | Explain the "why" and "how" at algorithm level |
|
|
221
|
+
| Write a novel | 5-15 lines maximum |
|
|
222
|
+
|
|
223
|
+
---
|
|
224
|
+
|
|
225
|
+
## Execution State Design
|
|
226
|
+
|
|
227
|
+
### Purpose
|
|
228
|
+
|
|
229
|
+
Define the intermediate state variables your code will use during processing. This step bridges the gap between the Story (algorithm) and Function Skeletons (code structure).
|
|
230
|
+
|
|
231
|
+
### Process
|
|
232
|
+
|
|
233
|
+
1. **Read the Story** - Understand the Input -> Output path
|
|
234
|
+
2. **Identify intermediate values** - What data exists between input and output?
|
|
235
|
+
3. **Study similar code** - Look for patterns in the codebase
|
|
236
|
+
4. **Propose state variables** - Name, type, purpose for each
|
|
237
|
+
5. **Present for approval** - Developer refines before coding
|
|
238
|
+
|
|
239
|
+
### Execution State Format
|
|
240
|
+
|
|
241
|
+
```
|
|
242
|
+
## Execution State: [Module Name]
|
|
243
|
+
|
|
244
|
+
### Input State
|
|
245
|
+
- `rawConfig: Config` - Unvalidated configuration from file
|
|
246
|
+
|
|
247
|
+
### Intermediate State
|
|
248
|
+
- `parsedEntries: Entry[]` - Config entries after parsing
|
|
249
|
+
- `validEntries: Entry[]` - Entries that passed validation
|
|
250
|
+
- `resolvedDeps: Map<string, string[]>` - Dependency graph after resolution
|
|
251
|
+
|
|
252
|
+
### Output State
|
|
253
|
+
- `buildPlan: BuildPlan` - Ordered list of build steps
|
|
254
|
+
|
|
255
|
+
### Error State
|
|
256
|
+
- `validationErrors: ValidationError[]` - Accumulated validation failures
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
### Guidelines
|
|
260
|
+
|
|
261
|
+
- Name variables clearly - the name should explain the data's role
|
|
262
|
+
- Include types - even if approximate
|
|
263
|
+
- Separate input/intermediate/output/error states
|
|
264
|
+
- Flag mutable vs. immutable state
|
|
265
|
+
|
|
266
|
+
---
|
|
267
|
+
|
|
268
|
+
## Function Skeleton Generation
|
|
269
|
+
|
|
270
|
+
### Purpose
|
|
271
|
+
|
|
272
|
+
Generate function/method stubs with complete signatures before writing implementation. Establishes the code structure that the developer approves before TDD begins.
|
|
273
|
+
|
|
274
|
+
### Skeleton Format
|
|
275
|
+
|
|
276
|
+
```typescript
|
|
277
|
+
/**
|
|
278
|
+
* Parses raw configuration entries from file content.
|
|
279
|
+
* Called after file is read, before validation.
|
|
280
|
+
*/
|
|
281
|
+
function parseConfigEntries(rawContent: string): Entry[] {
|
|
282
|
+
// TODO: Implement after approval
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
/**
|
|
286
|
+
* Validates entries against schema rules.
|
|
287
|
+
* Returns valid entries; accumulates errors in validationErrors.
|
|
288
|
+
*/
|
|
289
|
+
function validateEntries(
|
|
290
|
+
entries: Entry[],
|
|
291
|
+
schema: Schema
|
|
292
|
+
): { valid: Entry[]; errors: ValidationError[] } {
|
|
293
|
+
// TODO: Implement after approval
|
|
294
|
+
}
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
### Guidelines
|
|
298
|
+
|
|
299
|
+
- **Complete signatures** - All parameters, return types, generics
|
|
300
|
+
- **Docstrings** - One sentence describing purpose + when it's called
|
|
301
|
+
- **No implementation** - Body is `// TODO` or language equivalent (`pass`, `unimplemented!()`)
|
|
302
|
+
- **Follow project naming conventions** - Match patterns from `tech-stack.md`
|
|
303
|
+
- **Order matches control flow** - Functions appear in the order they're called
|
|
304
|
+
|
|
305
|
+
### Anti-Patterns
|
|
306
|
+
|
|
307
|
+
| Don't | Instead |
|
|
308
|
+
|-------|---------|
|
|
309
|
+
| Partial signatures (missing types) | Include all types |
|
|
310
|
+
| Implementation in skeletons | Only stubs |
|
|
311
|
+
| Generic names (`processData`) | Specific names (`validateEntries`) |
|
|
312
|
+
| Skip error-handling functions | Include error paths |
|
|
313
|
+
|
|
314
|
+
---
|
|
315
|
+
|
|
316
|
+
## Integration with Draft
|
|
317
|
+
|
|
318
|
+
### In `/draft:decompose`
|
|
319
|
+
|
|
320
|
+
1. Analyze scope (project or track)
|
|
321
|
+
2. Apply module decomposition rules
|
|
322
|
+
3. Generate dependency diagram and table
|
|
323
|
+
4. Present for developer approval at each checkpoint
|
|
324
|
+
|
|
325
|
+
### In `/draft:implement` (when architecture mode enabled)
|
|
326
|
+
|
|
327
|
+
1. **Before coding a file** - Write Story, present for approval
|
|
328
|
+
2. **Before TDD cycle** - Design execution state, generate skeletons, present each for approval
|
|
329
|
+
3. **After task completion** - Update module status in `.ai-context.md` (or `architecture.md`) if it exists. For project-level `.ai-context.md` updates, also trigger the Condensation Subroutine (defined in `core/shared/condensation.md`) to regenerate `.ai-context.md` from `architecture.md`.
|
|
330
|
+
4. **Validation report** - When track validation is enabled, results are persisted to `draft/tracks/<id>/validation-report.md`.
|
|
331
|
+
|
|
332
|
+
### Escalation
|
|
333
|
+
|
|
334
|
+
If module boundaries are unclear after analysis:
|
|
335
|
+
1. Document what you know
|
|
336
|
+
2. List the ambiguous boundaries
|
|
337
|
+
3. Ask developer to clarify responsibility ownership
|
|
338
|
+
4. Do NOT guess at boundaries - wrong boundaries are worse than no boundaries
|