@adityaaria/agent-os 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 (129) hide show
  1. package/AGENTS.md +41 -0
  2. package/AGENT_OS_BOOTSTRAP.md +29 -0
  3. package/ENTRYPOINT.md +118 -0
  4. package/LICENSE +21 -0
  5. package/README.md +230 -0
  6. package/adapters/antigravity/README.md +9 -0
  7. package/adapters/antigravity/plugin.json +13 -0
  8. package/adapters/claude/CLAUDE.md +16 -0
  9. package/adapters/claude/README.md +10 -0
  10. package/adapters/codex/README.md +20 -0
  11. package/adapters/cursor/README.md +9 -0
  12. package/adapters/cursor/agent-os.rules.md +18 -0
  13. package/bin/agent-os.mjs +98 -0
  14. package/core/AUTO_PROJECT_DETECTOR.md +99 -0
  15. package/core/COMPLIANCE_GATE.md +19 -0
  16. package/core/FRAMEWORK_AUDITOR.md +35 -0
  17. package/core/KNOWLEDGE_REGENERATOR.md +30 -0
  18. package/core/KNOWLEDGE_VALIDATOR.md +29 -0
  19. package/core/PROJECT_CONTEXT_STANDARD.md +19 -0
  20. package/core/QUALITY_GATES.md +42 -0
  21. package/core/REPORTING_STANDARD.md +47 -0
  22. package/core/SKILL_ORCHESTRATOR.md +63 -0
  23. package/core/TASK_ROUTER.md +33 -0
  24. package/core/TRACEABILITY_STANDARD.md +40 -0
  25. package/package.json +28 -0
  26. package/scripts/install.mjs +690 -0
  27. package/scripts/lib/cli-utils.mjs +148 -0
  28. package/scripts/lib/run-utils.mjs +185 -0
  29. package/scripts/lib/telegram-audit-log.mjs +50 -0
  30. package/scripts/lib/telegram-bot-utils.mjs +413 -0
  31. package/scripts/lib/telegram-utils.mjs +127 -0
  32. package/scripts/orchestrate.mjs +230 -0
  33. package/scripts/quality-static.mjs +19 -0
  34. package/scripts/report-export.mjs +86 -0
  35. package/scripts/report-notify.mjs +169 -0
  36. package/scripts/run-execution.mjs +83 -0
  37. package/scripts/run-list.mjs +44 -0
  38. package/scripts/run-status.mjs +38 -0
  39. package/scripts/run-step.mjs +173 -0
  40. package/scripts/setup-wizard.mjs +116 -0
  41. package/scripts/sync-runtime.mjs +87 -0
  42. package/scripts/telegram-bot.mjs +88 -0
  43. package/scripts/telegram-check.mjs +94 -0
  44. package/scripts/telegram-poll.mjs +176 -0
  45. package/scripts/validate-framework.mjs +290 -0
  46. package/skills/api-contract-audit/SKILL.md +26 -0
  47. package/skills/api-contract-audit/checklist.md +16 -0
  48. package/skills/api-contract-audit/examples.md +3 -0
  49. package/skills/api-contract-audit/report-template.md +39 -0
  50. package/skills/api-contract-audit/workflow.md +15 -0
  51. package/skills/audit/SKILL.md +28 -0
  52. package/skills/audit/checklist.md +18 -0
  53. package/skills/audit/examples.md +26 -0
  54. package/skills/audit/report-template.md +39 -0
  55. package/skills/audit/workflow.md +15 -0
  56. package/skills/bug-fix/SKILL.md +25 -0
  57. package/skills/bug-fix/checklist.md +18 -0
  58. package/skills/bug-fix/examples.md +26 -0
  59. package/skills/bug-fix/report-template.md +39 -0
  60. package/skills/bug-fix/workflow.md +10 -0
  61. package/skills/database-impact-analysis/SKILL.md +27 -0
  62. package/skills/database-impact-analysis/checklist.md +16 -0
  63. package/skills/database-impact-analysis/examples.md +3 -0
  64. package/skills/database-impact-analysis/report-template.md +39 -0
  65. package/skills/database-impact-analysis/workflow.md +14 -0
  66. package/skills/enhancement/SKILL.md +27 -0
  67. package/skills/enhancement/checklist.md +19 -0
  68. package/skills/enhancement/examples.md +26 -0
  69. package/skills/enhancement/report-template.md +39 -0
  70. package/skills/enhancement/workflow.md +10 -0
  71. package/skills/enterprise-certification/SKILL.md +19 -0
  72. package/skills/enterprise-certification/checklist.md +17 -0
  73. package/skills/enterprise-certification/report-template.md +39 -0
  74. package/skills/enterprise-certification/workflow.md +14 -0
  75. package/skills/knowledge-evolution/SKILL.md +23 -0
  76. package/skills/knowledge-evolution/checklist.md +17 -0
  77. package/skills/knowledge-evolution/report-template.md +39 -0
  78. package/skills/knowledge-evolution/workflow.md +14 -0
  79. package/skills/new-page/SKILL.md +26 -0
  80. package/skills/new-page/checklist.md +21 -0
  81. package/skills/new-page/examples.md +26 -0
  82. package/skills/new-page/report-template.md +39 -0
  83. package/skills/new-page/workflow.md +10 -0
  84. package/skills/project-onboarding/SKILL.md +27 -0
  85. package/skills/project-onboarding/checklist.md +18 -0
  86. package/skills/project-onboarding/examples.md +26 -0
  87. package/skills/project-onboarding/report-template.md +39 -0
  88. package/skills/project-onboarding/workflow.md +9 -0
  89. package/skills/release-readiness/SKILL.md +27 -0
  90. package/skills/release-readiness/checklist.md +16 -0
  91. package/skills/release-readiness/examples.md +3 -0
  92. package/skills/release-readiness/report-template.md +39 -0
  93. package/skills/release-readiness/workflow.md +14 -0
  94. package/skills/repository-health/SKILL.md +26 -0
  95. package/skills/repository-health/checklist.md +20 -0
  96. package/skills/repository-health/report-template.md +39 -0
  97. package/skills/repository-health/workflow.md +14 -0
  98. package/skills/selenium-e2e/SKILL.md +83 -0
  99. package/skills/selenium-e2e/checklist.md +29 -0
  100. package/skills/selenium-e2e/examples.md +26 -0
  101. package/skills/selenium-e2e/report-template.md +39 -0
  102. package/skills/selenium-e2e/workflow.md +9 -0
  103. package/skills/self-audit/SKILL.md +19 -0
  104. package/skills/self-audit/checklist.md +14 -0
  105. package/skills/self-audit/report-template.md +39 -0
  106. package/skills/self-audit/workflow.md +11 -0
  107. package/skills/traceability-analysis/SKILL.md +25 -0
  108. package/skills/traceability-analysis/checklist.md +17 -0
  109. package/skills/traceability-analysis/examples.md +3 -0
  110. package/skills/traceability-analysis/report-template.md +39 -0
  111. package/skills/traceability-analysis/workflow.md +17 -0
  112. package/templates/API_RULES.md +20 -0
  113. package/templates/ARCHITECTURE.md +20 -0
  114. package/templates/BUSINESS_FLOW.md +20 -0
  115. package/templates/COMMANDS.md +20 -0
  116. package/templates/DATABASE_RULES.md +20 -0
  117. package/templates/DEPENDENCY_MAP.md +20 -0
  118. package/templates/DEPLOYMENT_RULES.md +20 -0
  119. package/templates/DOMAIN_MODEL.md +20 -0
  120. package/templates/FE_BE_TRACEABILITY.md +20 -0
  121. package/templates/KNOWN_LIMITATIONS.md +20 -0
  122. package/templates/MODULE_CATALOG.md +20 -0
  123. package/templates/PROJECT_PROFILE.md +20 -0
  124. package/templates/RISK_REGISTER.md +20 -0
  125. package/templates/SECURITY_RULES.md +20 -0
  126. package/templates/TECHNICAL_DEBT.md +20 -0
  127. package/templates/TECH_STACK.md +20 -0
  128. package/templates/TESTING_RULES.md +47 -0
  129. package/templates/UI_UX_RULES.md +20 -0
@@ -0,0 +1,173 @@
1
+ #!/usr/bin/env node
2
+ import { spawnSync } from 'node:child_process';
3
+ import fs from 'node:fs';
4
+ import path from 'node:path';
5
+ import { fileURLToPath } from 'node:url';
6
+ import { deriveRunStatus, renderReport } from './lib/run-utils.mjs';
7
+ import { createCli, fail, failAll, showHelp, wantsHelp } from './lib/cli-utils.mjs';
8
+
9
+ if (wantsHelp(process.argv)) {
10
+ showHelp({
11
+ command: 'run-step',
12
+ description: 'Update step state inside a run directory',
13
+ usage: ['agent-os run-step --run-dir <dir> --step <number> --status <status> [options]'],
14
+ required: [
15
+ { flag: '--run-dir <dir>', desc: 'Path to the run directory' },
16
+ { flag: '--step <number>', desc: 'Step number to update' },
17
+ { flag: '--status <status>', desc: 'New status: pending|running|passed|failed|blocked' }
18
+ ],
19
+ options: [
20
+ { flag: '--notify-channel-config <file>', desc: 'Channel config for lifecycle notifications' },
21
+ { flag: '--notify-channel <name>', desc: 'Channel name for notifications' },
22
+ { flag: '--notify-on <events>', desc: 'Comma-separated lifecycle events to notify on' },
23
+ { flag: '--dry-run-notify', desc: 'Write notification payload to file' },
24
+ { flag: '--send-notify', desc: 'Send notification via configured channel' }
25
+ ],
26
+ examples: [
27
+ 'agent-os run-step --run-dir .agent-os/runs/my-run --step 1 --status running',
28
+ 'agent-os run-step --run-dir .agent-os/runs/my-run --step 1 --status failed --notify-channel-config .agent-os/project/report-channels.json --notify-channel telegram --notify-on failed,blocked --dry-run-notify'
29
+ ]
30
+ });
31
+ }
32
+
33
+ const __filename = fileURLToPath(import.meta.url);
34
+ const repoRoot = path.resolve(path.dirname(__filename), '..');
35
+
36
+ const { hasFlag, takeOption } = createCli(process.argv.slice(2));
37
+ const allowedStatuses = new Set(['pending', 'running', 'passed', 'failed', 'blocked']);
38
+
39
+ const runDir = takeOption('--run-dir');
40
+ const stepNumber = Number(takeOption('--step'));
41
+ const status = takeOption('--status');
42
+ const notifyChannelConfig = takeOption('--notify-channel-config');
43
+ const notifyChannel = takeOption('--notify-channel');
44
+ const notifyOn = takeOption('--notify-on');
45
+ const dryRunNotify = hasFlag('--dry-run-notify');
46
+ const sendNotify = hasFlag('--send-notify');
47
+
48
+ const errors = [];
49
+ if (!runDir) errors.push('Missing --run-dir <dir>.');
50
+ if (!Number.isInteger(stepNumber)) errors.push('Missing or invalid --step <number>.');
51
+ if (!status) errors.push('Missing --status <pending|running|passed|failed|blocked>.');
52
+ if (status && !allowedStatuses.has(status)) errors.push(`Invalid status: ${status}. Expected: pending|running|passed|failed|blocked.`);
53
+ failAll(errors, 'agent-os run-step --run-dir .agent-os/runs/my-run --step 1 --status running', 'run-step');
54
+
55
+ if ((dryRunNotify && sendNotify) || ((notifyChannelConfig || notifyChannel || notifyOn) && !dryRunNotify && !sendNotify)) {
56
+ fail('Use either --dry-run-notify or --send-notify for lifecycle notifications.');
57
+ }
58
+
59
+ if ((dryRunNotify || sendNotify) && (!notifyChannelConfig || !notifyChannel || !notifyOn)) {
60
+ fail('Lifecycle notifications require --notify-channel-config, --notify-channel, and --notify-on.');
61
+ }
62
+
63
+ const absoluteRunDir = path.resolve(process.cwd(), runDir);
64
+ const planPath = path.join(absoluteRunDir, 'plan.json');
65
+ const stepsPath = path.join(absoluteRunDir, 'steps.json');
66
+ const reportPath = path.join(absoluteRunDir, 'master-report.md');
67
+
68
+ if (!fs.existsSync(planPath) || !fs.existsSync(stepsPath)) {
69
+ fail(`Run directory is missing plan.json or steps.json: ${absoluteRunDir}`);
70
+ }
71
+
72
+ const plan = JSON.parse(fs.readFileSync(planPath, 'utf8'));
73
+ const steps = JSON.parse(fs.readFileSync(stepsPath, 'utf8'));
74
+ const targetStep = steps.find((step) => step.step === stepNumber);
75
+
76
+ if (!targetStep) {
77
+ fail(`Step not found: ${stepNumber}`);
78
+ }
79
+
80
+ if (targetStep.status === 'blocked' && status !== 'blocked') {
81
+ fail(`Step ${stepNumber} is blocked`);
82
+ }
83
+
84
+ if (status === 'running' || status === 'passed') {
85
+ const incompleteDependencies = targetStep.dependsOn.filter((dependencySkill) => {
86
+ const dependencyStep = steps.find((step) => step.skill === dependencySkill);
87
+ return !dependencyStep || dependencyStep.status !== 'passed';
88
+ });
89
+
90
+ if (incompleteDependencies.length > 0) {
91
+ fail(`Dependencies must pass before step ${stepNumber} can run: ${incompleteDependencies.join(', ')}`);
92
+ }
93
+ }
94
+
95
+ targetStep.status = status;
96
+ if (status === 'failed') {
97
+ const blockedSkills = new Set([targetStep.skill]);
98
+ let changed = true;
99
+
100
+ while (changed) {
101
+ changed = false;
102
+ for (const step of steps) {
103
+ if (step.status === 'failed') {
104
+ blockedSkills.add(step.skill);
105
+ }
106
+ if (step.step !== targetStep.step && step.dependsOn.some((skill) => blockedSkills.has(skill))) {
107
+ if (step.status !== 'blocked') {
108
+ step.status = 'blocked';
109
+ changed = true;
110
+ }
111
+ blockedSkills.add(step.skill);
112
+ }
113
+ }
114
+ }
115
+ }
116
+ plan.executionPlan = plan.executionPlan.map((step) => (
117
+ steps.find((updatedStep) => updatedStep.step === step.step) ?? step
118
+ ));
119
+
120
+ fs.writeFileSync(stepsPath, JSON.stringify(steps, null, 2) + '\n');
121
+ fs.writeFileSync(planPath, JSON.stringify(plan, null, 2) + '\n');
122
+ fs.writeFileSync(reportPath, renderReport(plan));
123
+
124
+ const notification = maybeNotifyLifecycle();
125
+
126
+ function maybeNotifyLifecycle() {
127
+ if (!dryRunNotify && !sendNotify) return null;
128
+
129
+ const notifyEvents = new Set(notifyOn.split(',').map((event) => event.trim()).filter(Boolean));
130
+ const runStatus = deriveRunStatus(steps);
131
+ const actualEvents = new Set([status, runStatus]);
132
+ if (steps.some((step) => step.status === 'blocked')) actualEvents.add('blocked');
133
+ if (steps.some((step) => step.status === 'failed')) actualEvents.add('failed');
134
+
135
+ const matchedEvent = [status, runStatus, 'failed', 'blocked', 'complete']
136
+ .find((event) => actualEvents.has(event) && notifyEvents.has(event));
137
+
138
+ if (!matchedEvent) {
139
+ return { skipped: true, reason: 'No matching lifecycle event.' };
140
+ }
141
+
142
+ const notifyArgs = [
143
+ path.join(repoRoot, 'scripts', 'report-notify.mjs'),
144
+ '--run-dir',
145
+ absoluteRunDir,
146
+ '--channel-config',
147
+ notifyChannelConfig,
148
+ '--channel',
149
+ notifyChannel,
150
+ '--event',
151
+ matchedEvent,
152
+ dryRunNotify ? '--dry-run' : '--send'
153
+ ];
154
+
155
+ const result = spawnSync(process.execPath, notifyArgs, {
156
+ cwd: repoRoot,
157
+ encoding: 'utf8',
158
+ env: process.env
159
+ });
160
+
161
+ if (result.status !== 0) {
162
+ if (result.stdout) process.stdout.write(result.stdout);
163
+ fail(result.stderr || `Lifecycle notification failed for channel: ${notifyChannel}`);
164
+ }
165
+
166
+ if (result.stdout) process.stdout.write(result.stdout);
167
+ return { channel: notifyChannel, mode: dryRunNotify ? 'dry-run' : 'send' };
168
+ }
169
+
170
+ if (notification?.channel) {
171
+ console.log(`Notification: ${notification.channel} (${notification.mode})`);
172
+ }
173
+ console.log(JSON.stringify({ runDir: absoluteRunDir, step: stepNumber, status }, null, 2));
@@ -0,0 +1,116 @@
1
+ #!/usr/bin/env node
2
+ import fs from 'node:fs';
3
+ import path from 'node:path';
4
+ import { spawnSync } from 'node:child_process';
5
+ import { fileURLToPath } from 'node:url';
6
+ import { wantsHelp, showHelp } from './lib/cli-utils.mjs';
7
+
8
+ if (wantsHelp(process.argv)) {
9
+ showHelp({
10
+ command: 'setup',
11
+ description: 'Quick setup wizard for new Agent OS installations',
12
+ usage: ['agent-os setup'],
13
+ examples: [
14
+ 'agent-os setup',
15
+ 'cd /path/to/your/project && agent-os setup'
16
+ ]
17
+ });
18
+ }
19
+
20
+ const __filename = fileURLToPath(import.meta.url);
21
+ const repoRoot = path.resolve(path.dirname(__filename), '..');
22
+
23
+ function log(message) {
24
+ console.log(` ${message}`);
25
+ }
26
+
27
+ function findWorkspaceFile(startDir, maxLevels = 2) {
28
+ let dir = startDir;
29
+ for (let i = 0; i <= maxLevels; i++) {
30
+ const entries = fs.readdirSync(dir).filter((f) => f.endsWith('.code-workspace'));
31
+ if (entries.length > 0) {
32
+ return path.join(dir, entries[0]);
33
+ }
34
+ const parent = path.dirname(dir);
35
+ if (parent === dir) break;
36
+ dir = parent;
37
+ }
38
+ return null;
39
+ }
40
+
41
+ function detectProjectType(dir) {
42
+ const manifests = [
43
+ { file: 'package.json', type: 'Node.js' },
44
+ { file: 'requirements.txt', type: 'Python' },
45
+ { file: 'Cargo.toml', type: 'Rust' },
46
+ { file: 'go.mod', type: 'Go' },
47
+ { file: 'pom.xml', type: 'Java (Maven)' },
48
+ { file: 'composer.json', type: 'PHP (Composer)' },
49
+ { file: 'Gemfile', type: 'Ruby' },
50
+ { file: 'pubspec.yaml', type: 'Dart/Flutter' }
51
+ ];
52
+
53
+ for (const manifest of manifests) {
54
+ if (fs.existsSync(path.join(dir, manifest.file))) {
55
+ return manifest.type;
56
+ }
57
+ }
58
+ return 'Unknown';
59
+ }
60
+
61
+ function generateWorkspaceFile(dir) {
62
+ const workspaceContent = JSON.stringify({
63
+ folders: [{ path: '.' }]
64
+ }, null, 2) + '\n';
65
+
66
+ const dirName = path.basename(dir);
67
+ const workspacePath = path.join(dir, `${dirName}.code-workspace`);
68
+ fs.writeFileSync(workspacePath, workspaceContent);
69
+ return workspacePath;
70
+ }
71
+
72
+ console.log('\n=== Agent OS Setup Wizard ===\n');
73
+
74
+ const cwd = process.cwd();
75
+
76
+ // Step 1: Find or create workspace file
77
+ let workspacePath = findWorkspaceFile(cwd);
78
+
79
+ if (workspacePath) {
80
+ log(`✓ Found existing workspace file: ${workspacePath}`);
81
+ } else {
82
+ const projectType = detectProjectType(cwd);
83
+ log(`Detected project type: ${projectType}`);
84
+ workspacePath = generateWorkspaceFile(cwd);
85
+ log(`✓ Generated .code-workspace for ${projectType} project`);
86
+ }
87
+
88
+ // Step 2: Run installer
89
+ log('');
90
+ log('Running Agent OS installer...');
91
+ log('');
92
+
93
+ const installerPath = path.join(repoRoot, 'scripts', 'install.mjs');
94
+ const result = spawnSync(process.execPath, [
95
+ installerPath,
96
+ '--workspace-file', workspacePath,
97
+ '--all',
98
+ '--backup'
99
+ ], {
100
+ cwd: repoRoot,
101
+ stdio: 'inherit'
102
+ });
103
+
104
+ if (result.status !== 0) {
105
+ console.error('\n✗ ERROR [setup]: Installation failed.');
106
+ console.error(' → Suggestion: Check the output above for details and try running the installer manually.');
107
+ process.exit(1);
108
+ }
109
+
110
+ // Step 3: Print summary
111
+ console.log('\n=== Setup Complete ===\n');
112
+ log('✓ Agent OS installed successfully');
113
+ log('');
114
+ log('Next step: Open your IDE and trigger:');
115
+ log(' ANALYZE PROJECT: We are using [your framework] frontend and [your framework] backend.');
116
+ log('');
@@ -0,0 +1,87 @@
1
+ #!/usr/bin/env node
2
+ import fs from 'node:fs';
3
+ import path from 'node:path';
4
+ import { fileURLToPath } from 'node:url';
5
+ import { wantsHelp, showHelp } from './lib/cli-utils.mjs';
6
+
7
+ if (wantsHelp(process.argv)) {
8
+ showHelp({
9
+ command: 'sync',
10
+ description: 'Sync framework source into the local .agent-os runtime',
11
+ usage: ['agent-os sync [--target <dir>]'],
12
+ options: [
13
+ { flag: '--target <dir>', desc: 'Target directory', default: '.' }
14
+ ],
15
+ examples: [
16
+ 'agent-os sync',
17
+ 'agent-os sync --target /path/to/project'
18
+ ]
19
+ });
20
+ }
21
+
22
+ const __filename = fileURLToPath(import.meta.url);
23
+ const repoRoot = path.resolve(path.dirname(__filename), '..');
24
+ const args = process.argv.slice(2);
25
+
26
+ function getArg(name, fallback = null) {
27
+ const index = args.indexOf(name);
28
+ if (index === -1) return fallback;
29
+ return args[index + 1] ?? fallback;
30
+ }
31
+
32
+ const targetRoot = path.resolve(process.cwd(), getArg('--target', '.'));
33
+ const runtimeRoot = path.join(targetRoot, '.agent-os');
34
+
35
+ function copyRecursive(source, destination) {
36
+ if (!fs.existsSync(source)) return;
37
+
38
+ const stat = fs.statSync(source);
39
+ if (stat.isDirectory()) {
40
+ fs.mkdirSync(destination, { recursive: true });
41
+ for (const entry of fs.readdirSync(source)) {
42
+ copyRecursive(path.join(source, entry), path.join(destination, entry));
43
+ }
44
+ return;
45
+ }
46
+
47
+ fs.mkdirSync(path.dirname(destination), { recursive: true });
48
+ fs.copyFileSync(source, destination);
49
+ }
50
+
51
+ for (const file of ['AGENTS.md', 'ENTRYPOINT.md', 'AGENT_OS_BOOTSTRAP.md', 'plugin.json']) {
52
+ copyRecursive(path.join(repoRoot, file), path.join(runtimeRoot, file));
53
+ }
54
+
55
+ for (const directory of ['core', 'skills', 'templates', 'hooks']) {
56
+ copyRecursive(path.join(repoRoot, directory), path.join(runtimeRoot, directory));
57
+ }
58
+
59
+ copyRecursive(path.join(repoRoot, 'validators'), path.join(runtimeRoot, 'validators'));
60
+
61
+ const projectRoot = path.join(runtimeRoot, 'project');
62
+ fs.mkdirSync(projectRoot, { recursive: true });
63
+
64
+ for (const file of ['PROJECT_PROFILE.md', 'TECH_STACK.md', 'ARCHITECTURE.md']) {
65
+ const destination = path.join(projectRoot, file);
66
+ if (!fs.existsSync(destination)) {
67
+ copyRecursive(path.join(repoRoot, 'templates', file), destination);
68
+ }
69
+ }
70
+
71
+ const workspaceMap = path.join(projectRoot, 'WORKSPACE_MAP.md');
72
+ if (!fs.existsSync(workspaceMap)) {
73
+ fs.writeFileSync(workspaceMap, [
74
+ '# Agent OS Workspace Map',
75
+ '',
76
+ '- **Workspace Name**: local-runtime',
77
+ '- **Workspace File**: N/A. Reason: Runtime synced in repository mode.',
78
+ '- **Install Root**: N/A. Reason: Generated by sync-runtime.',
79
+ '',
80
+ '## Detected Folders',
81
+ '',
82
+ 'N/A. Reason: Runtime sync does not inspect external workspace folders.',
83
+ ''
84
+ ].join('\n'));
85
+ }
86
+
87
+ console.log(`Runtime synced: ${runtimeRoot}`);
@@ -0,0 +1,88 @@
1
+ #!/usr/bin/env node
2
+ import { assertEnabledChannel, loadReportChannel } from './lib/run-utils.mjs';
3
+ import {
4
+ createTelegramAccessControl,
5
+ processTelegramUpdates,
6
+ readTelegramUpdatesFromFile,
7
+ writeTelegramBotOutput
8
+ } from './lib/telegram-bot-utils.mjs';
9
+ import { createCli, fail, failAll, resolveDefault, showHelp, wantsHelp } from './lib/cli-utils.mjs';
10
+
11
+ const { hasFlag, takeOption } = createCli(process.argv.slice(2));
12
+
13
+ if (wantsHelp(process.argv)) {
14
+ showHelp({
15
+ command: 'telegram-bot',
16
+ description: 'Process Telegram bot updates from a file (dry-run)',
17
+ usage: [
18
+ 'agent-os telegram-bot --updates-file <file> --dry-run [options]'
19
+ ],
20
+ required: [
21
+ { flag: '--updates-file <file>', desc: 'Path to Telegram updates JSON file' },
22
+ { flag: '--allowed-chat-id <ids>', desc: 'Comma-separated allowed Telegram chat IDs' },
23
+ { flag: '--dry-run', desc: 'Write responses to file (no API calls)' }
24
+ ],
25
+ options: [
26
+ { flag: '--channel-config <file>', desc: 'Channel config file', default: '.agent-os/project/report-channels.json' },
27
+ { flag: '--channel <name>', desc: 'Channel name', default: 'telegram' },
28
+ { flag: '--run-root <dir>', desc: 'Run directory root', default: '.agent-os/runs' },
29
+ { flag: '--output <file>', desc: 'Override output path' }
30
+ ],
31
+ examples: [
32
+ 'agent-os telegram-bot --updates-file updates.json --allowed-chat-id 12345 --dry-run',
33
+ 'agent-os telegram-bot --updates-file updates.json --allowed-chat-id 12345 --dry-run --run-root .agent-os/runs'
34
+ ]
35
+ });
36
+ }
37
+
38
+ const updatesFile = takeOption('--updates-file');
39
+ const channelConfig = takeOption('--channel-config') ?? resolveDefault(null, '.agent-os/project/report-channels.json');
40
+ const channelName = takeOption('--channel') ?? 'telegram';
41
+ const allowedChatId = takeOption('--allowed-chat-id');
42
+ const runRoot = takeOption('--run-root') ?? resolveDefault(null, '.agent-os/runs');
43
+ const outputOverride = takeOption('--output');
44
+ const dryRun = hasFlag('--dry-run');
45
+
46
+ if (!dryRun) {
47
+ fail('telegram-bot currently supports --dry-run only.');
48
+ }
49
+
50
+ const errors = [];
51
+ if (!updatesFile) errors.push('Missing --updates-file <file>.');
52
+ if (!channelConfig) errors.push('Missing --channel-config <file> (no default found at .agent-os/project/report-channels.json).');
53
+ if (!allowedChatId) errors.push('Missing --allowed-chat-id <id[,id]> (required, no default).');
54
+ if (!runRoot) errors.push('Missing --run-root <dir> (no default found at .agent-os/runs).');
55
+ failAll(errors, 'agent-os telegram-bot --updates-file updates.json --allowed-chat-id 12345 --dry-run', 'telegram-bot');
56
+
57
+ let channelContext;
58
+ try {
59
+ channelContext = loadReportChannel(channelConfig, channelName);
60
+ assertEnabledChannel(channelContext);
61
+ } catch (err) {
62
+ fail(err.message);
63
+ }
64
+
65
+ if (channelContext.channel.type !== 'telegram') {
66
+ fail(`Report channel is not a telegram channel: ${channelName}`);
67
+ }
68
+
69
+ const updates = readTelegramUpdatesFromFile(updatesFile);
70
+ const responses = processTelegramUpdates({
71
+ updates,
72
+ accessControl: createTelegramAccessControl({
73
+ allowedChatId,
74
+ roles: channelContext.channel.roles
75
+ }),
76
+ runRoot,
77
+ channelConfig: channelContext.channel,
78
+ auditRunRoot: runRoot
79
+ });
80
+ const outputPath = writeTelegramBotOutput({
81
+ channelContext,
82
+ channelName,
83
+ responses,
84
+ outputOverride,
85
+ mode: 'dry-run'
86
+ });
87
+
88
+ console.log(`Dry-run output: ${outputPath}`);
@@ -0,0 +1,94 @@
1
+ #!/usr/bin/env node
2
+ import fs from 'node:fs';
3
+ import path from 'node:path';
4
+ import { assertEnabledChannel, loadReportChannel } from './lib/run-utils.mjs';
5
+ import { createTelegramMessage, sendTelegramRequest, telegramEnv } from './lib/telegram-utils.mjs';
6
+ import { createCli, fail, failAll, resolveDefault, showHelp, wantsHelp } from './lib/cli-utils.mjs';
7
+
8
+ const { hasFlag, takeOption } = createCli(process.argv.slice(2));
9
+
10
+ if (wantsHelp(process.argv)) {
11
+ showHelp({
12
+ command: 'telegram-test',
13
+ description: 'Test Telegram bot connectivity',
14
+ usage: [
15
+ 'agent-os telegram-test --dry-run [options]',
16
+ 'agent-os telegram-test --send [options]'
17
+ ],
18
+ required: [
19
+ { flag: '--send | --dry-run', desc: 'Send a test message or write payload to file' }
20
+ ],
21
+ options: [
22
+ { flag: '--channel-config <file>', desc: 'Channel config file', default: '.agent-os/project/report-channels.json' },
23
+ { flag: '--channel <name>', desc: 'Channel name', default: 'telegram' },
24
+ { flag: '--output <file>', desc: 'Override output path' }
25
+ ],
26
+ examples: [
27
+ 'agent-os telegram-test --dry-run',
28
+ 'agent-os telegram-test --send'
29
+ ]
30
+ });
31
+ }
32
+
33
+ const channelConfig = takeOption('--channel-config') ?? resolveDefault(null, '.agent-os/project/report-channels.json');
34
+ const channelName = takeOption('--channel') ?? 'telegram';
35
+ const outputOverride = takeOption('--output');
36
+ const dryRun = hasFlag('--dry-run');
37
+ const send = hasFlag('--send');
38
+
39
+ const errors = [];
40
+ if ((dryRun && send) || (!dryRun && !send)) errors.push('Use either --dry-run or --send (exactly one required).');
41
+ if (!channelConfig) errors.push('Missing --channel-config <file> (no default found at .agent-os/project/report-channels.json).');
42
+ failAll(errors, 'agent-os telegram-test --dry-run', 'telegram-test');
43
+
44
+ let channelContext;
45
+ try {
46
+ channelContext = loadReportChannel(channelConfig, channelName);
47
+ assertEnabledChannel(channelContext);
48
+ } catch (err) {
49
+ fail(err.message);
50
+ }
51
+
52
+ const channel = channelContext.channel;
53
+ if (channel.type !== 'telegram') {
54
+ fail(`Report channel is not a telegram channel: ${channelName}`);
55
+ }
56
+
57
+ const { botTokenEnv, chatIdEnv, botToken, chatId } = telegramEnv(channel);
58
+ const message = createTelegramMessage({ test: true, channelName });
59
+
60
+ if (send) {
61
+ if (!botToken) {
62
+ fail(`Missing required environment variable: ${botTokenEnv}`);
63
+ }
64
+ if (!chatId) {
65
+ fail(`Missing required environment variable: ${chatIdEnv}`);
66
+ }
67
+
68
+ try {
69
+ await sendTelegramRequest({ channel, channelContext, message, botToken, chatId });
70
+ } catch (err) {
71
+ fail(err.message);
72
+ }
73
+
74
+ console.log(`Channel: ${channelName}`);
75
+ console.log(`Sent telegram test: ${channelName}`);
76
+ process.exit(0);
77
+ }
78
+
79
+ const payload = {
80
+ channel: channelName,
81
+ mode: 'dry-run',
82
+ transport: 'telegram',
83
+ botTokenEnv,
84
+ chatIdEnv,
85
+ chatId,
86
+ message
87
+ };
88
+
89
+ const outputPath = path.resolve(channelContext.outputBaseDir, outputOverride ?? channel.testOutput ?? channel.output ?? 'reports/telegram-test.json');
90
+
91
+ fs.mkdirSync(path.dirname(outputPath), { recursive: true });
92
+ fs.writeFileSync(outputPath, `${JSON.stringify(payload, null, 2)}\n`);
93
+ console.log(`Channel: ${channelName}`);
94
+ console.log(`Telegram test payload: ${outputPath}`);