@sallmarta/eye-hate-agent 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.
Files changed (64) hide show
  1. package/.agents/rules/agent.md +66 -0
  2. package/.claude/commands/eha/README.md +3 -0
  3. package/.claude/commands/eha/eha-bootstrap.md +9 -0
  4. package/.claude/commands/eha/eha-discuss.md +9 -0
  5. package/.claude/commands/eha/eha-execute.md +9 -0
  6. package/.claude/commands/eha/eha-parity.md +9 -0
  7. package/.claude/commands/eha/eha-refresh.md +9 -0
  8. package/.claude/commands/eha/eha-verify.md +9 -0
  9. package/.claude/rules/agent-rules.md +64 -0
  10. package/.github/instructions/agent-rules.instructions.md +63 -0
  11. package/.github/instructions/eha-workflows.instructions.md +21 -0
  12. package/LICENSE +21 -0
  13. package/README.md +337 -0
  14. package/bin/eha.js +163 -0
  15. package/docs/eyehateagent-contract.md +475 -0
  16. package/docs/eyehateagent-maintenance.md +103 -0
  17. package/docs/project-docs/changelog.md +287 -0
  18. package/docs/project-docs/foundation/architecture.md +117 -0
  19. package/docs/project-docs/foundation/status.md +32 -0
  20. package/docs/project-docs/foundation/workflow.md +63 -0
  21. package/docs/project-docs/index.md +20 -0
  22. package/docs/project-docs/testing.md +73 -0
  23. package/docs/vibes/project-docs-template/foundation/architecture.md +79 -0
  24. package/docs/vibes/project-docs-template/foundation/changelog.md +53 -0
  25. package/docs/vibes/project-docs-template/foundation/feature-inventory.md +46 -0
  26. package/docs/vibes/project-docs-template/foundation/phases.md +60 -0
  27. package/docs/vibes/project-docs-template/foundation/prd.md +69 -0
  28. package/docs/vibes/project-docs-template/foundation/status.md +57 -0
  29. package/docs/vibes/project-docs-template/foundation/workflow.md +59 -0
  30. package/docs/vibes/project-docs-template/getting-started.md +52 -0
  31. package/docs/vibes/project-docs-template/index.md +66 -0
  32. package/docs/vibes/project-docs-template/operations/ci-cd.md +56 -0
  33. package/docs/vibes/project-docs-template/operations/compliance.md +46 -0
  34. package/docs/vibes/project-docs-template/operations/governance.md +46 -0
  35. package/docs/vibes/project-docs-template/operations/observability.md +53 -0
  36. package/docs/vibes/project-docs-template/operations/production-runbook.md +62 -0
  37. package/docs/vibes/project-docs-template/operations/security.md +49 -0
  38. package/docs/vibes/project-docs-template/technical/api-contract.md +49 -0
  39. package/docs/vibes/project-docs-template/technical/database.md +59 -0
  40. package/docs/vibes/project-docs-template/technical/error-handling.md +54 -0
  41. package/docs/vibes/project-docs-template/technical/internationalization.md +46 -0
  42. package/docs/vibes/project-docs-template/technical/testing.md +57 -0
  43. package/docs/vibes/project-docs-template/technical/ui-ux.md +68 -0
  44. package/docs/vibes/project-docs-template/technical-guidelines/index.md +52 -0
  45. package/docs/vibes/reusable-prompts/00-project-docs-bootstrap.md +59 -0
  46. package/docs/vibes/reusable-prompts/00-project-docs-parity.md +88 -0
  47. package/docs/vibes/reusable-prompts/00-project-docs-refresh.md +81 -0
  48. package/docs/vibes/reusable-prompts/01-sdd-execute.md +34 -0
  49. package/docs/vibes/reusable-prompts/02-sdd-discuss.md +27 -0
  50. package/docs/vibes/skills/analysis/SKILL.md +173 -0
  51. package/docs/vibes/skills/api-design/SKILL.md +199 -0
  52. package/docs/vibes/skills/code-audit/SKILL.md +170 -0
  53. package/docs/vibes/skills/full-verification/SKILL.md +173 -0
  54. package/docs/vibes/skills/parity/SKILL.md +158 -0
  55. package/docs/vibes/skills/project-elevation/SKILL.md +157 -0
  56. package/docs/vibes/skills/test-authoring/SKILL.md +201 -0
  57. package/docs/vibes/skills/test-authoring/references/patterns.md +116 -0
  58. package/docs/vibes/skills/test-authoring/references/test-types.md +52 -0
  59. package/package.json +53 -0
  60. package/src/engine/index.js +22 -0
  61. package/src/engine/install.js +222 -0
  62. package/src/engine/runtime-adapters.js +106 -0
  63. package/src/engine/state.js +109 -0
  64. package/src/engine/workflow-registry.js +75 -0
@@ -0,0 +1,106 @@
1
+ const path = require('node:path');
2
+
3
+ function renderClaudeCommand(workflow, sourcePaths) {
4
+ return `---
5
+ description: "Run the EHA ${workflow.id} workflow"
6
+ ---
7
+
8
+ Read \`${sourcePaths.contractPath}\` first.
9
+
10
+ Execute \`${sourcePaths.workflowPath}\`.
11
+
12
+ If the user provides additional context, apply it while preserving the workflow's output contract and the owning-doc rules in \`docs/project-docs/\`.
13
+ `;
14
+ }
15
+
16
+ function renderCopilotInstructions(workflows, bundledSources) {
17
+ const workflowTable = workflows
18
+ .map(
19
+ (workflow) =>
20
+ `- \`${workflow.commandName}\` -> \`${bundledSources.workflowPaths[workflow.repoRelativePath]}\`${workflow.capabilityNote ? ` (${workflow.capabilityNote})` : ''}`,
21
+ )
22
+ .join('\n');
23
+
24
+ return `---
25
+ description: "Generated EHA workflow routing for GitHub Copilot"
26
+ applyTo: "**"
27
+ ---
28
+
29
+ # EHA Engine Workflows
30
+
31
+ When a user asks to run an EHA workflow, prefer the canonical reusable prompt file listed below and preserve its output contract.
32
+
33
+ ${workflowTable}
34
+
35
+ ## Runtime support
36
+
37
+ - GitHub Copilot support tier is **guided**
38
+ - this install generates repo-local instructions, not slash commands
39
+ - bundled contract path: \`${bundledSources.contractPath}\`
40
+ - generated outputs stay inside the repository for transparency
41
+ `;
42
+ }
43
+
44
+ const RUNTIME_ADAPTERS = {
45
+ claude: {
46
+ id: 'claude',
47
+ name: 'Claude',
48
+ supportTier: 'full',
49
+ description: 'Repo-local Claude command projection under .claude/commands/eha/',
50
+ generateFiles(rootDir, workflows, bundledSources) {
51
+ const files = [];
52
+ for (const workflow of workflows) {
53
+ files.push({
54
+ relativePath: path.join('.claude', 'commands', 'eha', `eha-${workflow.commandName}.md`),
55
+ content: renderClaudeCommand(workflow, {
56
+ contractPath: bundledSources.contractPath,
57
+ workflowPath: bundledSources.workflowPaths[workflow.repoRelativePath],
58
+ }),
59
+ });
60
+ }
61
+
62
+ files.push({
63
+ relativePath: path.join('.claude', 'commands', 'eha', 'README.md'),
64
+ content:
65
+ '# Generated EHA Claude commands\n\nThese files are generated by `eha install` from the canonical workflow registry.\n',
66
+ });
67
+
68
+ return files;
69
+ },
70
+ },
71
+ copilot: {
72
+ id: 'copilot',
73
+ name: 'GitHub Copilot',
74
+ supportTier: 'guided',
75
+ description: 'Repo-local Copilot instruction projection under .github/instructions/',
76
+ generateFiles(rootDir, workflows, bundledSources) {
77
+ return [
78
+ {
79
+ relativePath: path.join('.github', 'instructions', 'eha-workflows.instructions.md'),
80
+ content: renderCopilotInstructions(workflows, bundledSources),
81
+ },
82
+ ];
83
+ },
84
+ },
85
+ };
86
+
87
+ function getRuntimeAdapter(runtimeId) {
88
+ const adapter = RUNTIME_ADAPTERS[String(runtimeId).trim().toLowerCase()];
89
+ if (!adapter) {
90
+ throw new Error(`Unsupported runtime: ${runtimeId}`);
91
+ }
92
+ return adapter;
93
+ }
94
+
95
+ function listSupportedRuntimes() {
96
+ return Object.values(RUNTIME_ADAPTERS).map((runtime) => ({
97
+ id: runtime.id,
98
+ supportTier: runtime.supportTier,
99
+ description: runtime.description,
100
+ }));
101
+ }
102
+
103
+ module.exports = {
104
+ getRuntimeAdapter,
105
+ listSupportedRuntimes,
106
+ };
@@ -0,0 +1,109 @@
1
+ const fs = require('node:fs');
2
+ const path = require('node:path');
3
+
4
+ const CONTRACT_PATH = path.join('docs', 'eyehateagent-contract.md');
5
+ const ROOT_MARKERS = [CONTRACT_PATH, 'package.json', '.git'];
6
+
7
+ function ensureDir(dirPath) {
8
+ fs.mkdirSync(dirPath, { recursive: true });
9
+ }
10
+
11
+ function readJsonIfExists(filePath) {
12
+ if (!fs.existsSync(filePath)) {
13
+ return null;
14
+ }
15
+
16
+ return JSON.parse(fs.readFileSync(filePath, 'utf8'));
17
+ }
18
+
19
+ function writeJson(filePath, value) {
20
+ ensureDir(path.dirname(filePath));
21
+ fs.writeFileSync(filePath, `${JSON.stringify(value, null, 2)}\n`);
22
+ }
23
+
24
+ function writeText(filePath, value) {
25
+ ensureDir(path.dirname(filePath));
26
+ fs.writeFileSync(filePath, value);
27
+ }
28
+
29
+ function fileExists(rootDir, relativePath) {
30
+ return fs.existsSync(path.join(rootDir, relativePath));
31
+ }
32
+
33
+ function getPackageRoot() {
34
+ return path.resolve(__dirname, '..', '..');
35
+ }
36
+
37
+ function getBundledAssetPath(repoRelativePath) {
38
+ return path.join(getPackageRoot(), repoRelativePath);
39
+ }
40
+
41
+ function findRepoRoot(startDir = process.cwd()) {
42
+ let currentDir = path.resolve(startDir);
43
+ const initialDir = currentDir;
44
+
45
+ while (true) {
46
+ if (ROOT_MARKERS.some((markerPath) => fileExists(currentDir, markerPath))) {
47
+ return currentDir;
48
+ }
49
+
50
+ const parentDir = path.dirname(currentDir);
51
+ if (parentDir === currentDir) {
52
+ throw new Error(
53
+ `Could not find a project root from ${initialDir}. Expected one of: ${ROOT_MARKERS.join(', ')}.`,
54
+ );
55
+ }
56
+
57
+ currentDir = parentDir;
58
+ }
59
+ }
60
+
61
+ function getEnginePaths(rootDir) {
62
+ const ehaDir = path.join(rootDir, '.eha');
63
+ return {
64
+ ehaDir,
65
+ stateDir: path.join(ehaDir, 'state'),
66
+ generatedDir: path.join(ehaDir, 'generated'),
67
+ manifestPath: path.join(ehaDir, 'generated', 'install-manifest.json'),
68
+ lastRunPath: path.join(ehaDir, 'state', 'last-run.json'),
69
+ lastPromptPath: path.join(ehaDir, 'state', 'last-prompt.md'),
70
+ };
71
+ }
72
+
73
+ function removeFileIfExists(filePath) {
74
+ if (fs.existsSync(filePath)) {
75
+ fs.unlinkSync(filePath);
76
+ }
77
+ }
78
+
79
+ function removeEmptyParents(startPath, stopPath) {
80
+ let currentPath = path.resolve(startPath);
81
+ const normalizedStop = path.resolve(stopPath);
82
+
83
+ while (currentPath.startsWith(normalizedStop) && currentPath !== normalizedStop) {
84
+ if (!fs.existsSync(currentPath)) {
85
+ currentPath = path.dirname(currentPath);
86
+ continue;
87
+ }
88
+
89
+ if (fs.readdirSync(currentPath).length > 0) {
90
+ break;
91
+ }
92
+
93
+ fs.rmdirSync(currentPath);
94
+ currentPath = path.dirname(currentPath);
95
+ }
96
+ }
97
+
98
+ module.exports = {
99
+ ensureDir,
100
+ findRepoRoot,
101
+ getBundledAssetPath,
102
+ getEnginePaths,
103
+ getPackageRoot,
104
+ readJsonIfExists,
105
+ removeEmptyParents,
106
+ removeFileIfExists,
107
+ writeJson,
108
+ writeText,
109
+ };
@@ -0,0 +1,75 @@
1
+ const path = require('node:path');
2
+
3
+ const WORKFLOW_DEFINITIONS = {
4
+ bootstrap: {
5
+ id: 'bootstrap',
6
+ commandName: 'bootstrap',
7
+ description: 'Prepare the bootstrap workflow for creating project docs',
8
+ repoRelativePath: path.join('docs', 'vibes', 'reusable-prompts', '00-project-docs-bootstrap.md'),
9
+ },
10
+ refresh: {
11
+ id: 'refresh',
12
+ commandName: 'refresh',
13
+ description: 'Prepare the refresh workflow for owner-doc updates',
14
+ repoRelativePath: path.join('docs', 'vibes', 'reusable-prompts', '00-project-docs-refresh.md'),
15
+ },
16
+ parity: {
17
+ id: 'parity',
18
+ commandName: 'parity',
19
+ description: 'Prepare the parity workflow for ownership and drift checks',
20
+ repoRelativePath: path.join('docs', 'vibes', 'reusable-prompts', '00-project-docs-parity.md'),
21
+ },
22
+ discuss: {
23
+ id: 'discuss',
24
+ commandName: 'discuss',
25
+ description: 'Prepare the spec-discussion workflow',
26
+ repoRelativePath: path.join('docs', 'vibes', 'reusable-prompts', '02-sdd-discuss.md'),
27
+ },
28
+ execute: {
29
+ id: 'execute',
30
+ commandName: 'execute',
31
+ description: 'Prepare the spec-driven execution workflow',
32
+ repoRelativePath: path.join('docs', 'vibes', 'reusable-prompts', '01-sdd-execute.md'),
33
+ },
34
+ verify: {
35
+ id: 'verify',
36
+ commandName: 'verify',
37
+ description: 'Prepare the verification workflow',
38
+ repoRelativePath: path.join('docs', 'vibes', 'reusable-prompts', '00-project-docs-parity.md'),
39
+ capabilityNote: 'A dedicated verify workflow does not exist yet; verify currently routes to the parity workflow.',
40
+ },
41
+ };
42
+
43
+ const WORKFLOW_ALIASES = {
44
+ init: 'bootstrap',
45
+ };
46
+
47
+ function normalizeWorkflowId(input) {
48
+ if (!input) {
49
+ throw new Error('Workflow id is required');
50
+ }
51
+
52
+ const key = String(input).trim().toLowerCase();
53
+ return WORKFLOW_ALIASES[key] || key;
54
+ }
55
+
56
+ function getWorkflow(workflowId) {
57
+ const normalizedId = normalizeWorkflowId(workflowId);
58
+ const workflow = WORKFLOW_DEFINITIONS[normalizedId];
59
+
60
+ if (!workflow) {
61
+ throw new Error(`Unknown workflow: ${workflowId}`);
62
+ }
63
+
64
+ return { ...workflow };
65
+ }
66
+
67
+ function listWorkflows() {
68
+ return Object.values(WORKFLOW_DEFINITIONS).map((workflow) => ({ ...workflow }));
69
+ }
70
+
71
+ module.exports = {
72
+ getWorkflow,
73
+ listWorkflows,
74
+ normalizeWorkflowId,
75
+ };