@karthikrajkumar.kannan/get-things-done 1.0.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/LICENSE +21 -0
- package/README.md +237 -0
- package/agents/backward/gtd-accuracy-verifier.md +198 -0
- package/agents/backward/gtd-api-doc-writer.md +130 -0
- package/agents/backward/gtd-api-extractor.md +128 -0
- package/agents/backward/gtd-architecture-analyzer.md +144 -0
- package/agents/backward/gtd-capacity-writer.md +123 -0
- package/agents/backward/gtd-codebase-mapper.md +274 -0
- package/agents/backward/gtd-completeness-auditor.md +129 -0
- package/agents/backward/gtd-data-flow-tracer.md +104 -0
- package/agents/backward/gtd-dependency-analyzer.md +98 -0
- package/agents/backward/gtd-diagram-generator.md +152 -0
- package/agents/backward/gtd-hld-writer.md +123 -0
- package/agents/backward/gtd-lld-writer.md +126 -0
- package/agents/backward/gtd-pattern-detector.md +111 -0
- package/agents/backward/gtd-performance-profiler.md +93 -0
- package/agents/backward/gtd-runbook-writer.md +126 -0
- package/agents/backward/gtd-security-scanner.md +106 -0
- package/agents/backward/gtd-sysdesign-writer.md +137 -0
- package/agents/backward/gtd-tdd-writer.md +125 -0
- package/agents/forward/gtd-code-reviewer.md +130 -0
- package/agents/forward/gtd-debugger.md +133 -0
- package/agents/forward/gtd-deployer.md +110 -0
- package/agents/forward/gtd-executor.md +110 -0
- package/agents/forward/gtd-phase-researcher.md +114 -0
- package/agents/forward/gtd-plan-checker.md +132 -0
- package/agents/forward/gtd-planner.md +136 -0
- package/agents/forward/gtd-project-researcher.md +106 -0
- package/agents/forward/gtd-research-synthesizer.md +99 -0
- package/agents/forward/gtd-roadmapper.md +126 -0
- package/agents/forward/gtd-test-runner.md +119 -0
- package/agents/forward/gtd-verifier.md +115 -0
- package/agents/sync/gtd-alignment-auditor.md +222 -0
- package/agents/sync/gtd-drift-detector.md +222 -0
- package/agents/sync/gtd-reconciliation-planner.md +194 -0
- package/bin/gtd-tools.cjs +89 -0
- package/bin/install.js +164 -0
- package/commands/gtd/backward/analyze.md +42 -0
- package/commands/gtd/backward/create-all.md +32 -0
- package/commands/gtd/backward/create-api-docs.md +33 -0
- package/commands/gtd/backward/create-capacity.md +33 -0
- package/commands/gtd/backward/create-hld.md +33 -0
- package/commands/gtd/backward/create-lld.md +33 -0
- package/commands/gtd/backward/create-runbook.md +33 -0
- package/commands/gtd/backward/create-sysdesign.md +33 -0
- package/commands/gtd/backward/create-tdd.md +33 -0
- package/commands/gtd/backward/diff.md +22 -0
- package/commands/gtd/backward/doc-status.md +24 -0
- package/commands/gtd/backward/review-docs.md +22 -0
- package/commands/gtd/backward/scan.md +32 -0
- package/commands/gtd/backward/update-docs.md +30 -0
- package/commands/gtd/backward/verify-docs.md +28 -0
- package/commands/gtd/forward/add-phase.md +28 -0
- package/commands/gtd/forward/autonomous.md +28 -0
- package/commands/gtd/forward/code-review.md +28 -0
- package/commands/gtd/forward/complete-milestone.md +28 -0
- package/commands/gtd/forward/debug.md +28 -0
- package/commands/gtd/forward/discuss-phase.md +29 -0
- package/commands/gtd/forward/execute-phase.md +28 -0
- package/commands/gtd/forward/fast.md +28 -0
- package/commands/gtd/forward/new-milestone.md +28 -0
- package/commands/gtd/forward/new-project.md +29 -0
- package/commands/gtd/forward/next.md +28 -0
- package/commands/gtd/forward/plan-phase.md +29 -0
- package/commands/gtd/forward/progress.md +28 -0
- package/commands/gtd/forward/quick.md +28 -0
- package/commands/gtd/forward/ship.md +28 -0
- package/commands/gtd/forward/verify-work.md +28 -0
- package/commands/gtd/sync/audit.md +27 -0
- package/commands/gtd/sync/drift.md +27 -0
- package/commands/gtd/sync/reconcile.md +27 -0
- package/commands/gtd/sync/sync.md +27 -0
- package/commands/gtd/utility/health.md +53 -0
- package/commands/gtd/utility/help.md +61 -0
- package/commands/gtd/utility/map-codebase.md +27 -0
- package/commands/gtd/utility/settings.md +65 -0
- package/commands/gtd/utility/status.md +57 -0
- package/contexts/analysis.md +26 -0
- package/contexts/execution.md +35 -0
- package/contexts/planning.md +33 -0
- package/contexts/research.md +26 -0
- package/contexts/review.md +27 -0
- package/contexts/writing.md +29 -0
- package/hooks/gtd-check-update.js +37 -0
- package/hooks/gtd-context-monitor.js +32 -0
- package/hooks/gtd-prompt-guard.js +35 -0
- package/hooks/gtd-statusline.js +32 -0
- package/lib/agent-skills.cjs +130 -0
- package/lib/analysis.cjs +242 -0
- package/lib/config.cjs +255 -0
- package/lib/deploy.cjs +222 -0
- package/lib/diff-engine.cjs +245 -0
- package/lib/docs.cjs +243 -0
- package/lib/drift-engine.cjs +202 -0
- package/lib/file-ops.cjs +106 -0
- package/lib/frontmatter.cjs +100 -0
- package/lib/git.cjs +137 -0
- package/lib/init.cjs +370 -0
- package/lib/installer-core.cjs +197 -0
- package/lib/installers/augment.cjs +62 -0
- package/lib/installers/claude.cjs +89 -0
- package/lib/installers/cline.cjs +96 -0
- package/lib/installers/codex.cjs +63 -0
- package/lib/installers/copilot.cjs +62 -0
- package/lib/installers/cursor.cjs +62 -0
- package/lib/installers/gemini.cjs +62 -0
- package/lib/installers/opencode.cjs +62 -0
- package/lib/installers/windsurf.cjs +62 -0
- package/lib/phase.cjs +206 -0
- package/lib/roadmap.cjs +156 -0
- package/lib/scale-adapter.cjs +192 -0
- package/lib/security.cjs +243 -0
- package/lib/state.cjs +320 -0
- package/lib/template.cjs +218 -0
- package/lib/test-runner.cjs +202 -0
- package/package.json +76 -0
- package/references/agent-contracts.md +157 -0
- package/references/analysis-patterns.md +138 -0
- package/references/context-budget.md +148 -0
- package/references/diagram-conventions.md +88 -0
- package/references/document-standards.md +60 -0
- package/references/framework-signatures.md +609 -0
- package/references/gate-prompts.md +239 -0
- package/references/language-analyzers.md +227 -0
- package/references/planning-config.md +125 -0
- package/references/questioning.md +142 -0
- package/references/verification-patterns.md +67 -0
- package/templates/backward/api-docs/standard.md +42 -0
- package/templates/backward/capacity/standard.md +50 -0
- package/templates/backward/formats/compliance-guide.md +45 -0
- package/templates/backward/hld/standard.md +62 -0
- package/templates/backward/lld/standard.md +63 -0
- package/templates/backward/runbook/standard.md +50 -0
- package/templates/backward/system-design/standard.md +64 -0
- package/templates/backward/tdd/compliance.md +146 -0
- package/templates/backward/tdd/enterprise.md +134 -0
- package/templates/backward/tdd/standard.md +88 -0
- package/templates/backward/tdd/startup.md +51 -0
- package/templates/forward/context.md +65 -0
- package/templates/forward/phase-prompt.md +109 -0
- package/templates/forward/project.md +71 -0
- package/templates/forward/requirements.md +74 -0
- package/templates/forward/research/ARCHITECTURE.md +118 -0
- package/templates/forward/research/FEATURES.md +95 -0
- package/templates/forward/research/PITFALLS.md +106 -0
- package/templates/forward/research/STACK.md +80 -0
- package/templates/forward/research/SUMMARY.md +86 -0
- package/templates/forward/roadmap.md +72 -0
- package/workflows/backward/analyze-codebase.md +123 -0
- package/workflows/backward/create-all.md +53 -0
- package/workflows/backward/generate-document.md +182 -0
- package/workflows/backward/incremental-update.md +71 -0
- package/workflows/backward/review-document.md +102 -0
- package/workflows/backward/scan-codebase.md +111 -0
- package/workflows/backward/verify-document.md +79 -0
- package/workflows/forward/add-phase.md +29 -0
- package/workflows/forward/autonomous.md +62 -0
- package/workflows/forward/code-review.md +78 -0
- package/workflows/forward/complete-milestone.md +45 -0
- package/workflows/forward/debug.md +78 -0
- package/workflows/forward/deploy-local.md +51 -0
- package/workflows/forward/discuss-phase.md +89 -0
- package/workflows/forward/execute-phase.md +138 -0
- package/workflows/forward/fast.md +64 -0
- package/workflows/forward/new-milestone.md +61 -0
- package/workflows/forward/new-project.md +126 -0
- package/workflows/forward/next.md +49 -0
- package/workflows/forward/plan-phase.md +100 -0
- package/workflows/forward/progress.md +37 -0
- package/workflows/forward/quick.md +65 -0
- package/workflows/forward/ship.md +40 -0
- package/workflows/forward/test-phase.md +47 -0
- package/workflows/forward/verify-work.md +52 -0
- package/workflows/sync/audit.md +110 -0
- package/workflows/sync/detect-drift.md +122 -0
- package/workflows/sync/reconcile.md +113 -0
- package/workflows/sync/sync.md +150 -0
package/lib/template.cjs
ADDED
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GTD Template Engine — Variable substitution, conditionals, file includes.
|
|
3
|
+
*
|
|
4
|
+
* Template syntax:
|
|
5
|
+
* {{variable_name}} — Replaced with context value
|
|
6
|
+
* {{#section_name}} ... {{/section_name}} — Conditional section (included if truthy)
|
|
7
|
+
* {{^section_name}} ... {{/section_name}} — Inverted section (included if falsy)
|
|
8
|
+
* {{@file:path}} — Inline file content
|
|
9
|
+
*
|
|
10
|
+
* @module lib/template
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
'use strict';
|
|
14
|
+
|
|
15
|
+
const fs = require('fs');
|
|
16
|
+
const path = require('path');
|
|
17
|
+
|
|
18
|
+
// Resolve templates directory relative to package root
|
|
19
|
+
const TEMPLATES_DIR = path.resolve(__dirname, '..', 'templates');
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Fill a template with variables.
|
|
23
|
+
*
|
|
24
|
+
* @param {string} templateContent - Raw template string
|
|
25
|
+
* @param {object} variables - Key-value pairs for substitution
|
|
26
|
+
* @returns {string} Filled template
|
|
27
|
+
*/
|
|
28
|
+
function fill(templateContent, variables) {
|
|
29
|
+
let result = templateContent;
|
|
30
|
+
|
|
31
|
+
// 1. Process conditional sections: {{#flag}} ... {{/flag}}
|
|
32
|
+
result = result.replace(
|
|
33
|
+
/\{\{#(\w+)\}\}([\s\S]*?)\{\{\/\1\}\}/g,
|
|
34
|
+
(_match, key, content) => {
|
|
35
|
+
return variables[key] ? content : '';
|
|
36
|
+
},
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
// 2. Process inverted sections: {{^flag}} ... {{/flag}}
|
|
40
|
+
result = result.replace(
|
|
41
|
+
/\{\{\^(\w+)\}\}([\s\S]*?)\{\{\/\1\}\}/g,
|
|
42
|
+
(_match, key, content) => {
|
|
43
|
+
return !variables[key] ? content : '';
|
|
44
|
+
},
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
// 3. Process file includes: {{@file:path}}
|
|
48
|
+
result = result.replace(/\{\{@file:(.*?)\}\}/g, (_match, filePath) => {
|
|
49
|
+
try {
|
|
50
|
+
const resolved = path.isAbsolute(filePath)
|
|
51
|
+
? filePath
|
|
52
|
+
: path.resolve(process.cwd(), filePath);
|
|
53
|
+
return fs.readFileSync(resolved, 'utf8');
|
|
54
|
+
} catch (_) {
|
|
55
|
+
return `<!-- File not found: ${filePath} -->`;
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
// 4. Process variable substitution: {{variable_name}}
|
|
60
|
+
result = result.replace(/\{\{(\w[\w.]*)\}\}/g, (_match, key) => {
|
|
61
|
+
const value = getNestedValue(variables, key);
|
|
62
|
+
if (value === undefined || value === null) return _match; // Preserve unresolved
|
|
63
|
+
return String(value);
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
return result;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Load and fill a template by type and format.
|
|
71
|
+
*
|
|
72
|
+
* @param {string} type - Template type ('tdd', 'hld', 'lld', etc.)
|
|
73
|
+
* @param {string} [format='standard'] - Format variant ('standard', 'enterprise', 'startup', 'compliance')
|
|
74
|
+
* @param {object} variables - Variables for substitution
|
|
75
|
+
* @returns {string} Filled template
|
|
76
|
+
*/
|
|
77
|
+
function loadAndFill(type, format, variables) {
|
|
78
|
+
const templatePath = resolveTemplate(type, format);
|
|
79
|
+
const content = fs.readFileSync(templatePath, 'utf8');
|
|
80
|
+
return fill(content, variables);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Resolve a template file path by type and format.
|
|
85
|
+
* Resolution chain: format-specific → standard → default
|
|
86
|
+
*
|
|
87
|
+
* @param {string} type - Template type
|
|
88
|
+
* @param {string} [format='standard'] - Format variant
|
|
89
|
+
* @returns {string} Absolute path to template file
|
|
90
|
+
* @throws {Error} If no template found
|
|
91
|
+
*/
|
|
92
|
+
function resolveTemplate(type, format) {
|
|
93
|
+
const formatName = format || 'standard';
|
|
94
|
+
|
|
95
|
+
// Check backward templates first, then forward
|
|
96
|
+
const candidates = [
|
|
97
|
+
path.join(TEMPLATES_DIR, 'backward', type, `${formatName}.md`),
|
|
98
|
+
path.join(TEMPLATES_DIR, 'backward', type, 'standard.md'),
|
|
99
|
+
path.join(TEMPLATES_DIR, 'backward', type, 'default.md'),
|
|
100
|
+
path.join(TEMPLATES_DIR, 'forward', type, `${formatName}.md`),
|
|
101
|
+
path.join(TEMPLATES_DIR, 'forward', type, 'standard.md'),
|
|
102
|
+
path.join(TEMPLATES_DIR, type, `${formatName}.md`),
|
|
103
|
+
path.join(TEMPLATES_DIR, type, 'standard.md'),
|
|
104
|
+
];
|
|
105
|
+
|
|
106
|
+
for (const candidate of candidates) {
|
|
107
|
+
if (fs.existsSync(candidate)) return candidate;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
throw new Error(
|
|
111
|
+
`Template not found for type="${type}", format="${formatName}". ` +
|
|
112
|
+
`Searched: ${candidates.map((c) => path.relative(TEMPLATES_DIR, c)).join(', ')}`,
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* List all available template types and their formats.
|
|
118
|
+
*
|
|
119
|
+
* @returns {Array<{type: string, formats: string[]}>}
|
|
120
|
+
*/
|
|
121
|
+
function listTemplates() {
|
|
122
|
+
const result = [];
|
|
123
|
+
|
|
124
|
+
for (const subdir of ['backward', 'forward']) {
|
|
125
|
+
const dir = path.join(TEMPLATES_DIR, subdir);
|
|
126
|
+
if (!fs.existsSync(dir)) continue;
|
|
127
|
+
|
|
128
|
+
for (const typeDir of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
129
|
+
if (!typeDir.isDirectory()) continue;
|
|
130
|
+
|
|
131
|
+
const typePath = path.join(dir, typeDir.name);
|
|
132
|
+
const formats = fs.readdirSync(typePath)
|
|
133
|
+
.filter((f) => f.endsWith('.md'))
|
|
134
|
+
.map((f) => f.replace('.md', ''));
|
|
135
|
+
|
|
136
|
+
if (formats.length > 0) {
|
|
137
|
+
result.push({ type: typeDir.name, category: subdir, formats });
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
return result;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Validate a template for syntax errors.
|
|
147
|
+
*
|
|
148
|
+
* @param {string} content - Template content
|
|
149
|
+
* @returns {{ valid: boolean, errors: string[] }}
|
|
150
|
+
*/
|
|
151
|
+
function validateTemplate(content) {
|
|
152
|
+
const errors = [];
|
|
153
|
+
|
|
154
|
+
// Check for unclosed conditional sections
|
|
155
|
+
const opens = content.match(/\{\{#(\w+)\}\}/g) || [];
|
|
156
|
+
const closes = content.match(/\{\{\/(\w+)\}\}/g) || [];
|
|
157
|
+
|
|
158
|
+
const openNames = opens.map((m) => m.match(/\{\{#(\w+)\}\}/)[1]);
|
|
159
|
+
const closeNames = closes.map((m) => m.match(/\{\{\/(\w+)\}\}/)[1]);
|
|
160
|
+
|
|
161
|
+
for (const name of openNames) {
|
|
162
|
+
if (!closeNames.includes(name)) {
|
|
163
|
+
errors.push(`Unclosed section: {{#${name}}} has no matching {{/${name}}}`);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
for (const name of closeNames) {
|
|
168
|
+
if (!openNames.includes(name)) {
|
|
169
|
+
errors.push(`Orphaned close: {{/${name}}} has no matching {{#${name}}}`);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
return { valid: errors.length === 0, errors };
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// --- Utility ---
|
|
177
|
+
|
|
178
|
+
function getNestedValue(obj, dotPath) {
|
|
179
|
+
const keys = dotPath.split('.');
|
|
180
|
+
let current = obj;
|
|
181
|
+
for (const key of keys) {
|
|
182
|
+
if (current === null || current === undefined || typeof current !== 'object') {
|
|
183
|
+
return undefined;
|
|
184
|
+
}
|
|
185
|
+
current = current[key];
|
|
186
|
+
}
|
|
187
|
+
return current;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// CLI handler
|
|
191
|
+
function run(args) {
|
|
192
|
+
const subcommand = args[0];
|
|
193
|
+
|
|
194
|
+
if (subcommand === 'list') {
|
|
195
|
+
process.stdout.write(JSON.stringify(listTemplates(), null, 2));
|
|
196
|
+
} else if (subcommand === 'resolve' && args[1]) {
|
|
197
|
+
try {
|
|
198
|
+
const resolved = resolveTemplate(args[1], args[2]);
|
|
199
|
+
process.stdout.write(resolved);
|
|
200
|
+
} catch (err) {
|
|
201
|
+
process.stderr.write(err.message + '\n');
|
|
202
|
+
process.exit(1);
|
|
203
|
+
}
|
|
204
|
+
} else {
|
|
205
|
+
process.stderr.write('Usage: gtd-tools.cjs template <list|resolve> [type] [format]\n');
|
|
206
|
+
process.exit(1);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
module.exports = {
|
|
211
|
+
fill,
|
|
212
|
+
loadAndFill,
|
|
213
|
+
resolveTemplate,
|
|
214
|
+
listTemplates,
|
|
215
|
+
validateTemplate,
|
|
216
|
+
TEMPLATES_DIR,
|
|
217
|
+
run,
|
|
218
|
+
};
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GTD Test Runner Module — Test framework detection, discovery, and execution.
|
|
3
|
+
* @module lib/test-runner
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
'use strict';
|
|
7
|
+
|
|
8
|
+
const fs = require('fs');
|
|
9
|
+
const path = require('path');
|
|
10
|
+
const { fileExists } = require('./file-ops.cjs');
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Test framework detection rules.
|
|
14
|
+
*/
|
|
15
|
+
const TEST_FRAMEWORKS = [
|
|
16
|
+
{
|
|
17
|
+
name: 'vitest',
|
|
18
|
+
detect: (root) => {
|
|
19
|
+
const pkg = loadPkg(root);
|
|
20
|
+
return (pkg?.devDependencies?.vitest || pkg?.dependencies?.vitest) ? true : false;
|
|
21
|
+
},
|
|
22
|
+
runCmd: 'npx vitest run',
|
|
23
|
+
coverageCmd: 'npx vitest run --coverage',
|
|
24
|
+
filePattern: '**/*.test.{ts,js,tsx,jsx}',
|
|
25
|
+
configFiles: ['vitest.config.ts', 'vitest.config.js'],
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
name: 'jest',
|
|
29
|
+
detect: (root) => {
|
|
30
|
+
const pkg = loadPkg(root);
|
|
31
|
+
return (pkg?.devDependencies?.jest || pkg?.dependencies?.jest) ? true : false;
|
|
32
|
+
},
|
|
33
|
+
runCmd: 'npx jest',
|
|
34
|
+
coverageCmd: 'npx jest --coverage',
|
|
35
|
+
filePattern: '**/*.test.{ts,js,tsx,jsx}',
|
|
36
|
+
configFiles: ['jest.config.ts', 'jest.config.js'],
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
name: 'pytest',
|
|
40
|
+
detect: (root) =>
|
|
41
|
+
fileExists(path.join(root, 'pytest.ini')) ||
|
|
42
|
+
fileExists(path.join(root, 'pyproject.toml')) ||
|
|
43
|
+
fileExists(path.join(root, 'conftest.py')) ||
|
|
44
|
+
fs.existsSync(path.join(root, 'tests')) && hasFiles(path.join(root, 'tests'), 'test_*.py'),
|
|
45
|
+
runCmd: 'pytest',
|
|
46
|
+
coverageCmd: 'pytest --cov',
|
|
47
|
+
filePattern: '**/test_*.py',
|
|
48
|
+
configFiles: ['pytest.ini', 'pyproject.toml', 'setup.cfg'],
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
name: 'go-test',
|
|
52
|
+
detect: (root) =>
|
|
53
|
+
fileExists(path.join(root, 'go.mod')) &&
|
|
54
|
+
hasFiles(root, '*_test.go'),
|
|
55
|
+
runCmd: 'go test ./...',
|
|
56
|
+
coverageCmd: 'go test ./... -cover',
|
|
57
|
+
filePattern: '**/*_test.go',
|
|
58
|
+
configFiles: [],
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
name: 'cargo-test',
|
|
62
|
+
detect: (root) => fileExists(path.join(root, 'Cargo.toml')),
|
|
63
|
+
runCmd: 'cargo test',
|
|
64
|
+
coverageCmd: 'cargo tarpaulin',
|
|
65
|
+
filePattern: '**/tests/**/*.rs',
|
|
66
|
+
configFiles: ['Cargo.toml'],
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
name: 'rspec',
|
|
70
|
+
detect: (root) =>
|
|
71
|
+
fileExists(path.join(root, 'Gemfile')) &&
|
|
72
|
+
fs.existsSync(path.join(root, 'spec')),
|
|
73
|
+
runCmd: 'bundle exec rspec',
|
|
74
|
+
coverageCmd: 'bundle exec rspec',
|
|
75
|
+
filePattern: 'spec/**/*_spec.rb',
|
|
76
|
+
configFiles: ['.rspec', 'spec/spec_helper.rb'],
|
|
77
|
+
},
|
|
78
|
+
];
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Detect the test framework for a project.
|
|
82
|
+
* @param {string} projectRoot - Project root directory
|
|
83
|
+
* @returns {{ name: string, runCmd: string, coverageCmd: string, filePattern: string }|null}
|
|
84
|
+
*/
|
|
85
|
+
function detectTestFramework(projectRoot) {
|
|
86
|
+
for (const fw of TEST_FRAMEWORKS) {
|
|
87
|
+
if (fw.detect(projectRoot)) {
|
|
88
|
+
return {
|
|
89
|
+
name: fw.name,
|
|
90
|
+
runCmd: fw.runCmd,
|
|
91
|
+
coverageCmd: fw.coverageCmd,
|
|
92
|
+
filePattern: fw.filePattern,
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
return null;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Count test files in a project.
|
|
101
|
+
* @param {string} projectRoot - Project root directory
|
|
102
|
+
* @param {string} [pattern] - Glob pattern for test files
|
|
103
|
+
* @returns {number}
|
|
104
|
+
*/
|
|
105
|
+
function countTestFiles(projectRoot, pattern) {
|
|
106
|
+
const fw = detectTestFramework(projectRoot);
|
|
107
|
+
if (!fw && !pattern) return 0;
|
|
108
|
+
|
|
109
|
+
const searchPattern = pattern || fw.filePattern;
|
|
110
|
+
let count = 0;
|
|
111
|
+
|
|
112
|
+
function walk(dir) {
|
|
113
|
+
if (!fs.existsSync(dir)) return;
|
|
114
|
+
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
115
|
+
if (entry.name === 'node_modules' || entry.name === '.git' || entry.name === 'dist') continue;
|
|
116
|
+
const full = path.join(dir, entry.name);
|
|
117
|
+
if (entry.isDirectory()) {
|
|
118
|
+
walk(full);
|
|
119
|
+
} else if (matchesTestPattern(entry.name, searchPattern)) {
|
|
120
|
+
count++;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
walk(projectRoot);
|
|
126
|
+
return count;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Get test infrastructure summary.
|
|
131
|
+
* @param {string} projectRoot - Project root directory
|
|
132
|
+
* @returns {{ framework: string|null, testFiles: number, hasConfig: boolean, runCmd: string|null }}
|
|
133
|
+
*/
|
|
134
|
+
function getTestSummary(projectRoot) {
|
|
135
|
+
const fw = detectTestFramework(projectRoot);
|
|
136
|
+
if (!fw) {
|
|
137
|
+
return { framework: null, testFiles: 0, hasConfig: false, runCmd: null };
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const testFiles = countTestFiles(projectRoot, fw.filePattern);
|
|
141
|
+
const hasConfig = fw.configFiles
|
|
142
|
+
? fw.configFiles.some((cf) => fileExists(path.join(projectRoot, cf)))
|
|
143
|
+
: false;
|
|
144
|
+
|
|
145
|
+
return {
|
|
146
|
+
framework: fw.name,
|
|
147
|
+
testFiles,
|
|
148
|
+
hasConfig,
|
|
149
|
+
runCmd: fw.runCmd,
|
|
150
|
+
coverageCmd: fw.coverageCmd,
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// --- Helpers ---
|
|
155
|
+
|
|
156
|
+
function loadPkg(dir) {
|
|
157
|
+
try {
|
|
158
|
+
return JSON.parse(fs.readFileSync(path.join(dir, 'package.json'), 'utf8'));
|
|
159
|
+
} catch (_) {
|
|
160
|
+
return null;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
function hasFiles(dir, pattern) {
|
|
165
|
+
try {
|
|
166
|
+
return fs.readdirSync(dir).some((f) => matchesTestPattern(f, pattern));
|
|
167
|
+
} catch (_) {
|
|
168
|
+
return false;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
function matchesTestPattern(filename, pattern) {
|
|
173
|
+
// Simple pattern matching: *.test.js, test_*.py, *_test.go
|
|
174
|
+
if (pattern.includes('test_*.py')) return filename.startsWith('test_') && filename.endsWith('.py');
|
|
175
|
+
if (pattern.includes('*_test.go')) return filename.endsWith('_test.go');
|
|
176
|
+
if (pattern.includes('*_spec.rb')) return filename.endsWith('_spec.rb');
|
|
177
|
+
if (pattern.includes('*.test.')) return filename.includes('.test.');
|
|
178
|
+
return false;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// CLI handler
|
|
182
|
+
function run(args) {
|
|
183
|
+
const projectRoot = process.cwd();
|
|
184
|
+
const subcommand = args[0] || 'detect';
|
|
185
|
+
|
|
186
|
+
if (subcommand === 'detect') {
|
|
187
|
+
process.stdout.write(JSON.stringify(getTestSummary(projectRoot), null, 2));
|
|
188
|
+
} else if (subcommand === 'count') {
|
|
189
|
+
process.stdout.write(JSON.stringify(countTestFiles(projectRoot)));
|
|
190
|
+
} else {
|
|
191
|
+
process.stderr.write('Usage: gtd-tools.cjs test <detect|count>\n');
|
|
192
|
+
process.exit(1);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
module.exports = {
|
|
197
|
+
TEST_FRAMEWORKS,
|
|
198
|
+
detectTestFramework,
|
|
199
|
+
countTestFiles,
|
|
200
|
+
getTestSummary,
|
|
201
|
+
run,
|
|
202
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@karthikrajkumar.kannan/get-things-done",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Bidirectional spec-driven agentic framework — Forward (idea to code to deploy), Backward (code to docs), Sync (drift detection and reconciliation). The first framework that goes both ways.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"ai",
|
|
7
|
+
"agentic",
|
|
8
|
+
"spec-driven",
|
|
9
|
+
"documentation",
|
|
10
|
+
"code-generation",
|
|
11
|
+
"claude",
|
|
12
|
+
"claude-code",
|
|
13
|
+
"llm",
|
|
14
|
+
"technical-design",
|
|
15
|
+
"reverse-engineering",
|
|
16
|
+
"drift-detection",
|
|
17
|
+
"bidirectional",
|
|
18
|
+
"gemini",
|
|
19
|
+
"copilot",
|
|
20
|
+
"cursor",
|
|
21
|
+
"windsurf",
|
|
22
|
+
"codex"
|
|
23
|
+
],
|
|
24
|
+
"author": "GTD Contributors",
|
|
25
|
+
"license": "MIT",
|
|
26
|
+
"repository": {
|
|
27
|
+
"type": "git",
|
|
28
|
+
"url": "https://github.com/get-things-done/get-things-done"
|
|
29
|
+
},
|
|
30
|
+
"bin": {
|
|
31
|
+
"get-things-done": "./bin/install.js"
|
|
32
|
+
},
|
|
33
|
+
"main": "./bin/gtd-tools.cjs",
|
|
34
|
+
"files": [
|
|
35
|
+
"bin/",
|
|
36
|
+
"lib/",
|
|
37
|
+
"agents/",
|
|
38
|
+
"commands/",
|
|
39
|
+
"workflows/",
|
|
40
|
+
"references/",
|
|
41
|
+
"templates/",
|
|
42
|
+
"contexts/",
|
|
43
|
+
"hooks/"
|
|
44
|
+
],
|
|
45
|
+
"engines": {
|
|
46
|
+
"node": ">=20.0.0"
|
|
47
|
+
},
|
|
48
|
+
"scripts": {
|
|
49
|
+
"test": "vitest run",
|
|
50
|
+
"test:watch": "vitest watch",
|
|
51
|
+
"test:coverage": "vitest run --coverage",
|
|
52
|
+
"lint": "eslint .",
|
|
53
|
+
"lint:fix": "eslint . --fix",
|
|
54
|
+
"format": "prettier --write .",
|
|
55
|
+
"format:check": "prettier --check .",
|
|
56
|
+
"quality": "npm run lint && npm run format:check && npm run test",
|
|
57
|
+
"prepare": "husky"
|
|
58
|
+
},
|
|
59
|
+
"devDependencies": {
|
|
60
|
+
"vitest": "^3.1.0",
|
|
61
|
+
"@vitest/coverage-v8": "^3.1.0",
|
|
62
|
+
"eslint": "^9.0.0",
|
|
63
|
+
"prettier": "^3.5.0",
|
|
64
|
+
"husky": "^9.1.0",
|
|
65
|
+
"lint-staged": "^15.4.0"
|
|
66
|
+
},
|
|
67
|
+
"lint-staged": {
|
|
68
|
+
"*.{js,cjs,ts}": [
|
|
69
|
+
"eslint --fix",
|
|
70
|
+
"prettier --write"
|
|
71
|
+
],
|
|
72
|
+
"*.{md,json,yml,yaml}": [
|
|
73
|
+
"prettier --write"
|
|
74
|
+
]
|
|
75
|
+
}
|
|
76
|
+
}
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
# Agent Contracts
|
|
2
|
+
|
|
3
|
+
> Formal interface definitions between orchestrators and agents in the forward pipeline.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Contract Overview
|
|
8
|
+
|
|
9
|
+
Every agent interaction follows this lifecycle:
|
|
10
|
+
|
|
11
|
+
```
|
|
12
|
+
Orchestrator --> [Spawn] --> Agent
|
|
13
|
+
Orchestrator --> [Context Bundle] --> Agent
|
|
14
|
+
Agent --> [File Artifacts] --> Orchestrator
|
|
15
|
+
Agent --> [Status Signal] --> Orchestrator
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## Spawn Protocol
|
|
21
|
+
|
|
22
|
+
### How Orchestrators Spawn Agents
|
|
23
|
+
|
|
24
|
+
| Field | Required | Description |
|
|
25
|
+
|-------|----------|-------------|
|
|
26
|
+
| `agent_type` | Yes | One of: `research`, `planning`, `execution`, `gate` |
|
|
27
|
+
| `agent_id` | Yes | Unique identifier, format: `{{agent_type}}-{{sequence}}` |
|
|
28
|
+
| `task_description` | Yes | One-line description of what the agent must do |
|
|
29
|
+
| `context_files` | Yes | List of file paths the agent must read before starting |
|
|
30
|
+
| `output_files` | Yes | List of file paths the agent must produce |
|
|
31
|
+
| `timeout_ms` | No | Override default timeout (see defaults below) |
|
|
32
|
+
| `max_retries` | No | Override default retry count |
|
|
33
|
+
|
|
34
|
+
### Example Spawn
|
|
35
|
+
|
|
36
|
+
```json
|
|
37
|
+
{
|
|
38
|
+
"agent_type": "research",
|
|
39
|
+
"agent_id": "research-1",
|
|
40
|
+
"task_description": "Analyze technology stack options for a React + Node.js SaaS app",
|
|
41
|
+
"context_files": [
|
|
42
|
+
"PROJECT.md",
|
|
43
|
+
"REQUIREMENTS.md"
|
|
44
|
+
],
|
|
45
|
+
"output_files": [
|
|
46
|
+
"research/STACK.md"
|
|
47
|
+
],
|
|
48
|
+
"timeout_ms": 120000,
|
|
49
|
+
"max_retries": 1
|
|
50
|
+
}
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
---
|
|
54
|
+
|
|
55
|
+
## Context Bundle
|
|
56
|
+
|
|
57
|
+
What the agent receives when spawned.
|
|
58
|
+
|
|
59
|
+
| Agent Type | Always Receives | Conditionally Receives |
|
|
60
|
+
|-----------|----------------|----------------------|
|
|
61
|
+
| Research | PROJECT.md, REQUIREMENTS.md | Prior research outputs (if re-running) |
|
|
62
|
+
| Planning | PROJECT.md, REQUIREMENTS.md, research/* | User feedback (if revision) |
|
|
63
|
+
| Execution | PROJECT.md, phase CONTEXT.md, phase PROMPT.md | Prior phase artifacts |
|
|
64
|
+
| Gate | Phase output files, REQUIREMENTS.md | ROADMAP.md (for cross-phase gates) |
|
|
65
|
+
|
|
66
|
+
### Context Size Rules
|
|
67
|
+
|
|
68
|
+
- Total context bundle must fit within the agent's allocated token budget
|
|
69
|
+
- If context exceeds budget, orchestrator must summarize or truncate
|
|
70
|
+
- Priority order for truncation: research files > context files > requirements > project
|
|
71
|
+
|
|
72
|
+
---
|
|
73
|
+
|
|
74
|
+
## Result Protocol
|
|
75
|
+
|
|
76
|
+
### How Agents Return Results
|
|
77
|
+
|
|
78
|
+
Agents communicate results through file artifacts, not return values.
|
|
79
|
+
|
|
80
|
+
| Signal | Mechanism | Description |
|
|
81
|
+
|--------|-----------|-------------|
|
|
82
|
+
| Success | All `output_files` exist and are non-empty | Agent completed its task |
|
|
83
|
+
| Partial | Some `output_files` exist | Agent completed partially |
|
|
84
|
+
| Failure | No `output_files` created OR error marker file | Agent could not complete |
|
|
85
|
+
| Escalation | `ESCALATION.md` file created | Agent needs human decision |
|
|
86
|
+
|
|
87
|
+
### Result File Requirements
|
|
88
|
+
|
|
89
|
+
- Every output file must have a YAML-style header with `agent_id` and `timestamp`
|
|
90
|
+
- Files must be valid Markdown
|
|
91
|
+
- Files must not exceed 500 lines (split into multiple files if needed)
|
|
92
|
+
- Agent must not modify files outside its `output_files` list
|
|
93
|
+
|
|
94
|
+
---
|
|
95
|
+
|
|
96
|
+
## Timeout Handling
|
|
97
|
+
|
|
98
|
+
| Agent Type | Default Timeout | Max Timeout | On Timeout |
|
|
99
|
+
|-----------|----------------|-------------|------------|
|
|
100
|
+
| Research | 120s | 300s | Retry once, then mark partial |
|
|
101
|
+
| Planning | 180s | 300s | Retry once, then escalate |
|
|
102
|
+
| Execution | 300s | 600s | Retry once, then pause pipeline |
|
|
103
|
+
| Gate | 60s | 120s | Auto-fail the gate |
|
|
104
|
+
|
|
105
|
+
### Timeout Behavior
|
|
106
|
+
|
|
107
|
+
1. Orchestrator sends a soft cancellation signal at `timeout - 30s`
|
|
108
|
+
2. Agent should wrap up and write partial results
|
|
109
|
+
3. At timeout, orchestrator force-terminates the agent
|
|
110
|
+
4. Partial output files (if any) are preserved
|
|
111
|
+
5. Retry uses the same context bundle plus partial output as additional context
|
|
112
|
+
|
|
113
|
+
---
|
|
114
|
+
|
|
115
|
+
## Retry Policy
|
|
116
|
+
|
|
117
|
+
| Condition | Retry | Max Retries | Backoff |
|
|
118
|
+
|-----------|-------|-------------|---------|
|
|
119
|
+
| Timeout | Yes | 1 | None (immediate) |
|
|
120
|
+
| Output file missing | Yes | 1 | None |
|
|
121
|
+
| Output file empty | Yes | 1 | None |
|
|
122
|
+
| Agent error (crash) | Yes | 2 | 5s between retries |
|
|
123
|
+
| Escalation signal | No | - | Route to user |
|
|
124
|
+
| Gate failure | No | - | Route to revision |
|
|
125
|
+
|
|
126
|
+
### Retry Context
|
|
127
|
+
|
|
128
|
+
On retry, the agent receives the original context bundle plus:
|
|
129
|
+
- `_retry_reason`: Why the previous attempt failed
|
|
130
|
+
- `_partial_output`: Any files the previous attempt produced
|
|
131
|
+
- `_attempt_number`: Current attempt (1-indexed)
|
|
132
|
+
|
|
133
|
+
---
|
|
134
|
+
|
|
135
|
+
## Inter-Agent Communication
|
|
136
|
+
|
|
137
|
+
Agents do NOT communicate directly. All coordination flows through the orchestrator.
|
|
138
|
+
|
|
139
|
+
| Allowed | Not Allowed |
|
|
140
|
+
|---------|-------------|
|
|
141
|
+
| Agent writes to its output files | Agent reads another agent's in-progress files |
|
|
142
|
+
| Agent reads files listed in context_files | Agent spawns sub-agents |
|
|
143
|
+
| Agent creates ESCALATION.md | Agent modifies orchestrator state |
|
|
144
|
+
| Agent logs to its designated log path | Agent writes to shared directories |
|
|
145
|
+
|
|
146
|
+
---
|
|
147
|
+
|
|
148
|
+
## Escalation Protocol
|
|
149
|
+
|
|
150
|
+
When an agent encounters a decision it cannot make:
|
|
151
|
+
|
|
152
|
+
1. Create `ESCALATION.md` in the phase directory
|
|
153
|
+
2. Include: question, options, recommendation, impact of each option
|
|
154
|
+
3. Set status to `escalated`
|
|
155
|
+
4. Orchestrator pauses the pipeline and presents to user
|
|
156
|
+
5. User decision is recorded in CONTEXT.md
|
|
157
|
+
6. Agent is re-spawned with the decision in context
|