instar 0.1.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/settings.local.json +7 -0
- package/.claude/skills/setup-wizard/skill.md +343 -0
- package/.github/workflows/ci.yml +78 -0
- package/CLAUDE.md +82 -0
- package/README.md +194 -0
- package/dist/cli.d.ts +18 -0
- package/dist/cli.js +141 -0
- package/dist/commands/init.d.ts +40 -0
- package/dist/commands/init.js +568 -0
- package/dist/commands/job.d.ts +20 -0
- package/dist/commands/job.js +84 -0
- package/dist/commands/server.d.ts +19 -0
- package/dist/commands/server.js +273 -0
- package/dist/commands/setup.d.ts +24 -0
- package/dist/commands/setup.js +865 -0
- package/dist/commands/status.d.ts +11 -0
- package/dist/commands/status.js +114 -0
- package/dist/commands/user.d.ts +17 -0
- package/dist/commands/user.js +53 -0
- package/dist/core/Config.d.ts +16 -0
- package/dist/core/Config.js +144 -0
- package/dist/core/Prerequisites.d.ts +28 -0
- package/dist/core/Prerequisites.js +159 -0
- package/dist/core/RelationshipManager.d.ts +73 -0
- package/dist/core/RelationshipManager.js +318 -0
- package/dist/core/SessionManager.d.ts +89 -0
- package/dist/core/SessionManager.js +326 -0
- package/dist/core/StateManager.d.ts +28 -0
- package/dist/core/StateManager.js +96 -0
- package/dist/core/types.d.ts +279 -0
- package/dist/core/types.js +8 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.js +23 -0
- package/dist/messaging/TelegramAdapter.d.ts +73 -0
- package/dist/messaging/TelegramAdapter.js +288 -0
- package/dist/monitoring/HealthChecker.d.ts +38 -0
- package/dist/monitoring/HealthChecker.js +148 -0
- package/dist/scaffold/bootstrap.d.ts +21 -0
- package/dist/scaffold/bootstrap.js +110 -0
- package/dist/scaffold/templates.d.ts +34 -0
- package/dist/scaffold/templates.js +187 -0
- package/dist/scheduler/JobLoader.d.ts +18 -0
- package/dist/scheduler/JobLoader.js +70 -0
- package/dist/scheduler/JobScheduler.d.ts +111 -0
- package/dist/scheduler/JobScheduler.js +402 -0
- package/dist/server/AgentServer.d.ts +40 -0
- package/dist/server/AgentServer.js +73 -0
- package/dist/server/middleware.d.ts +12 -0
- package/dist/server/middleware.js +50 -0
- package/dist/server/routes.d.ts +25 -0
- package/dist/server/routes.js +224 -0
- package/dist/users/UserManager.d.ts +45 -0
- package/dist/users/UserManager.js +113 -0
- package/docs/dawn-audit-report.md +412 -0
- package/docs/positioning-vs-openclaw.md +246 -0
- package/package.json +52 -0
- package/src/cli.ts +169 -0
- package/src/commands/init.ts +654 -0
- package/src/commands/job.ts +110 -0
- package/src/commands/server.ts +325 -0
- package/src/commands/setup.ts +958 -0
- package/src/commands/status.ts +125 -0
- package/src/commands/user.ts +71 -0
- package/src/core/Config.ts +161 -0
- package/src/core/Prerequisites.ts +187 -0
- package/src/core/RelationshipManager.ts +366 -0
- package/src/core/SessionManager.ts +385 -0
- package/src/core/StateManager.ts +121 -0
- package/src/core/types.ts +320 -0
- package/src/index.ts +58 -0
- package/src/messaging/TelegramAdapter.ts +365 -0
- package/src/monitoring/HealthChecker.ts +172 -0
- package/src/scaffold/bootstrap.ts +122 -0
- package/src/scaffold/templates.ts +204 -0
- package/src/scheduler/JobLoader.ts +85 -0
- package/src/scheduler/JobScheduler.ts +476 -0
- package/src/server/AgentServer.ts +93 -0
- package/src/server/middleware.ts +58 -0
- package/src/server/routes.ts +278 -0
- package/src/templates/default-jobs.json +47 -0
- package/src/templates/hooks/compaction-recovery.sh +23 -0
- package/src/templates/hooks/dangerous-command-guard.sh +35 -0
- package/src/templates/hooks/grounding-before-messaging.sh +22 -0
- package/src/templates/hooks/session-start.sh +37 -0
- package/src/templates/hooks/settings-template.json +45 -0
- package/src/templates/scripts/health-watchdog.sh +63 -0
- package/src/templates/scripts/telegram-reply.sh +54 -0
- package/src/users/UserManager.ts +129 -0
- package/tests/e2e/lifecycle.test.ts +376 -0
- package/tests/fixtures/test-repo/CLAUDE.md +3 -0
- package/tests/fixtures/test-repo/README.md +1 -0
- package/tests/helpers/setup.ts +209 -0
- package/tests/integration/fresh-install.test.ts +218 -0
- package/tests/integration/scheduler-basic.test.ts +109 -0
- package/tests/integration/server-full.test.ts +284 -0
- package/tests/integration/session-lifecycle.test.ts +181 -0
- package/tests/unit/Config.test.ts +22 -0
- package/tests/unit/HealthChecker.test.ts +168 -0
- package/tests/unit/JobLoader.test.ts +151 -0
- package/tests/unit/JobScheduler.test.ts +267 -0
- package/tests/unit/Prerequisites.test.ts +59 -0
- package/tests/unit/RelationshipManager.test.ts +345 -0
- package/tests/unit/StateManager.test.ts +143 -0
- package/tests/unit/TelegramAdapter.test.ts +165 -0
- package/tests/unit/UserManager.test.ts +131 -0
- package/tests/unit/bootstrap.test.ts +28 -0
- package/tests/unit/commands.test.ts +138 -0
- package/tests/unit/middleware.test.ts +92 -0
- package/tests/unit/relationship-routes.test.ts +131 -0
- package/tests/unit/scaffold-templates.test.ts +132 -0
- package/tests/unit/server.test.ts +163 -0
- package/tsconfig.json +20 -0
- package/vitest.config.ts +9 -0
- package/vitest.e2e.config.ts +9 -0
- package/vitest.integration.config.ts +9 -0
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Health Checker — aggregates component health into a single status.
|
|
3
|
+
*
|
|
4
|
+
* Checks tmux availability, session state, scheduler health,
|
|
5
|
+
* and disk space. Returns a HealthStatus object.
|
|
6
|
+
*/
|
|
7
|
+
import type { SessionManager } from '../core/SessionManager.js';
|
|
8
|
+
import type { JobScheduler } from '../scheduler/JobScheduler.js';
|
|
9
|
+
import type { HealthStatus, AgentKitConfig } from '../core/types.js';
|
|
10
|
+
export declare class HealthChecker {
|
|
11
|
+
private config;
|
|
12
|
+
private sessionManager;
|
|
13
|
+
private scheduler;
|
|
14
|
+
private checkInterval;
|
|
15
|
+
private lastStatus;
|
|
16
|
+
constructor(config: AgentKitConfig, sessionManager: SessionManager, scheduler?: JobScheduler | null);
|
|
17
|
+
/**
|
|
18
|
+
* Run all health checks and return aggregated status.
|
|
19
|
+
*/
|
|
20
|
+
check(): HealthStatus;
|
|
21
|
+
/**
|
|
22
|
+
* Get the last computed health status without re-checking.
|
|
23
|
+
*/
|
|
24
|
+
getLastStatus(): HealthStatus | null;
|
|
25
|
+
/**
|
|
26
|
+
* Start periodic health checks.
|
|
27
|
+
*/
|
|
28
|
+
startPeriodicChecks(intervalMs?: number): void;
|
|
29
|
+
/**
|
|
30
|
+
* Stop periodic health checks.
|
|
31
|
+
*/
|
|
32
|
+
stopPeriodicChecks(): void;
|
|
33
|
+
private checkTmux;
|
|
34
|
+
private checkSessions;
|
|
35
|
+
private checkScheduler;
|
|
36
|
+
private checkStateDir;
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=HealthChecker.d.ts.map
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Health Checker — aggregates component health into a single status.
|
|
3
|
+
*
|
|
4
|
+
* Checks tmux availability, session state, scheduler health,
|
|
5
|
+
* and disk space. Returns a HealthStatus object.
|
|
6
|
+
*/
|
|
7
|
+
import { execSync } from 'node:child_process';
|
|
8
|
+
import fs from 'node:fs';
|
|
9
|
+
export class HealthChecker {
|
|
10
|
+
config;
|
|
11
|
+
sessionManager;
|
|
12
|
+
scheduler;
|
|
13
|
+
checkInterval = null;
|
|
14
|
+
lastStatus = null;
|
|
15
|
+
constructor(config, sessionManager, scheduler = null) {
|
|
16
|
+
this.config = config;
|
|
17
|
+
this.sessionManager = sessionManager;
|
|
18
|
+
this.scheduler = scheduler;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Run all health checks and return aggregated status.
|
|
22
|
+
*/
|
|
23
|
+
check() {
|
|
24
|
+
const components = {};
|
|
25
|
+
components.tmux = this.checkTmux();
|
|
26
|
+
components.sessions = this.checkSessions();
|
|
27
|
+
components.stateDir = this.checkStateDir();
|
|
28
|
+
if (this.scheduler) {
|
|
29
|
+
components.scheduler = this.checkScheduler();
|
|
30
|
+
}
|
|
31
|
+
// Aggregate: worst component status becomes overall status
|
|
32
|
+
const statuses = Object.values(components).map(c => c.status);
|
|
33
|
+
let overall = 'healthy';
|
|
34
|
+
if (statuses.includes('unhealthy'))
|
|
35
|
+
overall = 'unhealthy';
|
|
36
|
+
else if (statuses.includes('degraded'))
|
|
37
|
+
overall = 'degraded';
|
|
38
|
+
this.lastStatus = {
|
|
39
|
+
status: overall,
|
|
40
|
+
components,
|
|
41
|
+
timestamp: new Date().toISOString(),
|
|
42
|
+
};
|
|
43
|
+
return this.lastStatus;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Get the last computed health status without re-checking.
|
|
47
|
+
*/
|
|
48
|
+
getLastStatus() {
|
|
49
|
+
return this.lastStatus;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Start periodic health checks.
|
|
53
|
+
*/
|
|
54
|
+
startPeriodicChecks(intervalMs) {
|
|
55
|
+
if (this.checkInterval)
|
|
56
|
+
return;
|
|
57
|
+
const interval = intervalMs ?? this.config.monitoring.healthCheckIntervalMs;
|
|
58
|
+
this.check(); // Run immediately
|
|
59
|
+
this.checkInterval = setInterval(() => this.check(), interval);
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Stop periodic health checks.
|
|
63
|
+
*/
|
|
64
|
+
stopPeriodicChecks() {
|
|
65
|
+
if (this.checkInterval) {
|
|
66
|
+
clearInterval(this.checkInterval);
|
|
67
|
+
this.checkInterval = null;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
checkTmux() {
|
|
71
|
+
const now = new Date().toISOString();
|
|
72
|
+
try {
|
|
73
|
+
execSync(`${this.config.sessions.tmuxPath} list-sessions 2>/dev/null`, {
|
|
74
|
+
encoding: 'utf-8',
|
|
75
|
+
timeout: 3000,
|
|
76
|
+
});
|
|
77
|
+
return { status: 'healthy', message: 'tmux server responding', lastCheck: now };
|
|
78
|
+
}
|
|
79
|
+
catch {
|
|
80
|
+
// tmux server not running is ok if no sessions needed
|
|
81
|
+
try {
|
|
82
|
+
execSync(`${this.config.sessions.tmuxPath} -V`, { encoding: 'utf-8', timeout: 3000 });
|
|
83
|
+
return { status: 'healthy', message: 'tmux available (no server running)', lastCheck: now };
|
|
84
|
+
}
|
|
85
|
+
catch {
|
|
86
|
+
return { status: 'unhealthy', message: 'tmux binary not found', lastCheck: now };
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
checkSessions() {
|
|
91
|
+
const now = new Date().toISOString();
|
|
92
|
+
try {
|
|
93
|
+
const running = this.sessionManager.listRunningSessions();
|
|
94
|
+
const max = this.config.sessions.maxSessions;
|
|
95
|
+
if (running.length >= max) {
|
|
96
|
+
return {
|
|
97
|
+
status: 'degraded',
|
|
98
|
+
message: `At capacity: ${running.length}/${max} sessions`,
|
|
99
|
+
lastCheck: now,
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
return {
|
|
103
|
+
status: 'healthy',
|
|
104
|
+
message: `${running.length}/${max} sessions active`,
|
|
105
|
+
lastCheck: now,
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
catch (err) {
|
|
109
|
+
return { status: 'unhealthy', message: `Session check failed: ${err.message}`, lastCheck: now };
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
checkScheduler() {
|
|
113
|
+
const now = new Date().toISOString();
|
|
114
|
+
if (!this.scheduler) {
|
|
115
|
+
return { status: 'healthy', message: 'Scheduler not configured', lastCheck: now };
|
|
116
|
+
}
|
|
117
|
+
const status = this.scheduler.getStatus();
|
|
118
|
+
if (!status.running) {
|
|
119
|
+
return { status: 'degraded', message: 'Scheduler not running', lastCheck: now };
|
|
120
|
+
}
|
|
121
|
+
if (status.paused) {
|
|
122
|
+
return { status: 'degraded', message: 'Scheduler paused', lastCheck: now };
|
|
123
|
+
}
|
|
124
|
+
return {
|
|
125
|
+
status: 'healthy',
|
|
126
|
+
message: `Running: ${status.enabledJobs} jobs, ${status.queueLength} queued`,
|
|
127
|
+
lastCheck: now,
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
checkStateDir() {
|
|
131
|
+
const now = new Date().toISOString();
|
|
132
|
+
try {
|
|
133
|
+
const exists = fs.existsSync(this.config.stateDir);
|
|
134
|
+
if (!exists) {
|
|
135
|
+
return { status: 'unhealthy', message: 'State directory missing', lastCheck: now };
|
|
136
|
+
}
|
|
137
|
+
// Check we can write
|
|
138
|
+
const testFile = `${this.config.stateDir}/.health-check-${Date.now()}`;
|
|
139
|
+
fs.writeFileSync(testFile, 'ok');
|
|
140
|
+
fs.unlinkSync(testFile);
|
|
141
|
+
return { status: 'healthy', message: 'State directory writable', lastCheck: now };
|
|
142
|
+
}
|
|
143
|
+
catch (err) {
|
|
144
|
+
return { status: 'unhealthy', message: `State dir error: ${err.message}`, lastCheck: now };
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
//# sourceMappingURL=HealthChecker.js.map
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Identity bootstrap — interactive flow for creating the agent's identity.
|
|
3
|
+
*
|
|
4
|
+
* On first run, walks the user through defining who their agent is.
|
|
5
|
+
* Writes AGENT.md, USER.md, and MEMORY.md based on their answers.
|
|
6
|
+
*
|
|
7
|
+
* Inspired by OpenClaw's SOUL.md co-creation — but adapted for
|
|
8
|
+
* persistent infrastructure rather than conversational personality.
|
|
9
|
+
*/
|
|
10
|
+
import type { AgentIdentity } from './templates.js';
|
|
11
|
+
/**
|
|
12
|
+
* Run the interactive identity bootstrap.
|
|
13
|
+
* Returns the agent identity for template generation.
|
|
14
|
+
*/
|
|
15
|
+
export declare function bootstrapIdentity(projectName: string): Promise<AgentIdentity>;
|
|
16
|
+
/**
|
|
17
|
+
* Generate a default identity without interaction.
|
|
18
|
+
* Used for non-interactive init (flags-only mode).
|
|
19
|
+
*/
|
|
20
|
+
export declare function defaultIdentity(projectName: string): AgentIdentity;
|
|
21
|
+
//# sourceMappingURL=bootstrap.d.ts.map
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Identity bootstrap — interactive flow for creating the agent's identity.
|
|
3
|
+
*
|
|
4
|
+
* On first run, walks the user through defining who their agent is.
|
|
5
|
+
* Writes AGENT.md, USER.md, and MEMORY.md based on their answers.
|
|
6
|
+
*
|
|
7
|
+
* Inspired by OpenClaw's SOUL.md co-creation — but adapted for
|
|
8
|
+
* persistent infrastructure rather than conversational personality.
|
|
9
|
+
*/
|
|
10
|
+
import { input, select } from '@inquirer/prompts';
|
|
11
|
+
import pc from 'picocolors';
|
|
12
|
+
/**
|
|
13
|
+
* Run the interactive identity bootstrap.
|
|
14
|
+
* Returns the agent identity for template generation.
|
|
15
|
+
*/
|
|
16
|
+
export async function bootstrapIdentity(projectName) {
|
|
17
|
+
console.log();
|
|
18
|
+
console.log(pc.bold(' Identity Bootstrap'));
|
|
19
|
+
console.log(pc.dim(' Let\'s define who your agent is. This takes about 30 seconds.'));
|
|
20
|
+
console.log(pc.dim(' You can always change these later by editing .instar/AGENT.md'));
|
|
21
|
+
console.log();
|
|
22
|
+
// Agent name
|
|
23
|
+
const name = await input({
|
|
24
|
+
message: 'What should your agent be called?',
|
|
25
|
+
default: capitalize(projectName),
|
|
26
|
+
validate: (v) => v.trim().length > 0 ? true : 'Name is required',
|
|
27
|
+
});
|
|
28
|
+
// Agent role
|
|
29
|
+
const roleChoice = await select({
|
|
30
|
+
message: 'What\'s their primary role?',
|
|
31
|
+
choices: [
|
|
32
|
+
{
|
|
33
|
+
name: 'General-purpose autonomous agent',
|
|
34
|
+
value: 'I am a general-purpose autonomous agent. I build, maintain, and evolve this project.',
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
name: 'Development assistant (code-focused)',
|
|
38
|
+
value: 'I am a development agent. I write code, run tests, review PRs, and maintain code quality for this project.',
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
name: 'Operations agent (monitoring, automation)',
|
|
42
|
+
value: 'I am an operations agent. I monitor systems, automate workflows, handle alerts, and keep infrastructure running.',
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
name: 'Research agent (analysis, exploration)',
|
|
46
|
+
value: 'I am a research agent. I explore topics, analyze data, synthesize findings, and surface insights.',
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
name: 'Custom (I\'ll describe it)',
|
|
50
|
+
value: '__custom__',
|
|
51
|
+
},
|
|
52
|
+
],
|
|
53
|
+
});
|
|
54
|
+
const role = roleChoice === '__custom__'
|
|
55
|
+
? await input({ message: 'Describe their role in one sentence' })
|
|
56
|
+
: roleChoice;
|
|
57
|
+
// Personality
|
|
58
|
+
const personalityChoice = await select({
|
|
59
|
+
message: 'What\'s their personality like?',
|
|
60
|
+
choices: [
|
|
61
|
+
{
|
|
62
|
+
name: 'Direct and efficient — gets things done with minimal fuss',
|
|
63
|
+
value: 'I am direct and efficient. I focus on outcomes, communicate concisely, and value action over discussion. When something needs doing, I do it.',
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
name: 'Thoughtful and thorough — considers carefully before acting',
|
|
67
|
+
value: 'I am thoughtful and thorough. I consider implications before acting, document my reasoning, and prefer getting things right over getting them fast.',
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
name: 'Curious and exploratory — learns and experiments actively',
|
|
71
|
+
value: 'I am curious and exploratory. I investigate deeply, try new approaches, and treat every problem as an opportunity to learn something. I document what I discover.',
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
name: 'Warm and collaborative — works as a true partner',
|
|
75
|
+
value: 'I am warm and collaborative. I communicate openly, celebrate progress, and treat my work as a genuine partnership with the people I work with.',
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
name: 'Custom (I\'ll describe it)',
|
|
79
|
+
value: '__custom__',
|
|
80
|
+
},
|
|
81
|
+
],
|
|
82
|
+
});
|
|
83
|
+
const personality = personalityChoice === '__custom__'
|
|
84
|
+
? await input({ message: 'Describe their personality in a sentence or two' })
|
|
85
|
+
: personalityChoice;
|
|
86
|
+
// User name
|
|
87
|
+
const userName = await input({
|
|
88
|
+
message: 'What\'s your name? (the person who\'ll work with this agent)',
|
|
89
|
+
validate: (v) => v.trim().length > 0 ? true : 'Name is required',
|
|
90
|
+
});
|
|
91
|
+
console.log();
|
|
92
|
+
console.log(` ${pc.green('✓')} ${pc.bold(name)} is ready.`);
|
|
93
|
+
return { name, role, personality, userName };
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Generate a default identity without interaction.
|
|
97
|
+
* Used for non-interactive init (flags-only mode).
|
|
98
|
+
*/
|
|
99
|
+
export function defaultIdentity(projectName) {
|
|
100
|
+
return {
|
|
101
|
+
name: capitalize(projectName),
|
|
102
|
+
role: 'I am a general-purpose autonomous agent. I build, maintain, and evolve this project.',
|
|
103
|
+
personality: 'I am direct and efficient. I focus on outcomes, communicate concisely, and value action over discussion. When something needs doing, I do it.',
|
|
104
|
+
userName: 'User',
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
function capitalize(str) {
|
|
108
|
+
return str.charAt(0).toUpperCase() + str.slice(1);
|
|
109
|
+
}
|
|
110
|
+
//# sourceMappingURL=bootstrap.js.map
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Project scaffolding templates for fresh installs.
|
|
3
|
+
*
|
|
4
|
+
* These templates create a complete, runnable Claude Code project
|
|
5
|
+
* from scratch — CLAUDE.md, AGENT.md, USER.md, MEMORY.md, and
|
|
6
|
+
* the full .claude/ directory structure.
|
|
7
|
+
*
|
|
8
|
+
* Used by `instar init <project-name>` when creating a new project.
|
|
9
|
+
* When augmenting an existing project, only missing files are created.
|
|
10
|
+
*/
|
|
11
|
+
export interface AgentIdentity {
|
|
12
|
+
name: string;
|
|
13
|
+
role: string;
|
|
14
|
+
personality: string;
|
|
15
|
+
userName: string;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Generate AGENT.md — the agent's identity file.
|
|
19
|
+
*/
|
|
20
|
+
export declare function generateAgentMd(identity: AgentIdentity): string;
|
|
21
|
+
/**
|
|
22
|
+
* Generate USER.md — context about the primary user.
|
|
23
|
+
*/
|
|
24
|
+
export declare function generateUserMd(userName: string): string;
|
|
25
|
+
/**
|
|
26
|
+
* Generate MEMORY.md — the agent's persistent memory.
|
|
27
|
+
*/
|
|
28
|
+
export declare function generateMemoryMd(agentName: string): string;
|
|
29
|
+
/**
|
|
30
|
+
* Generate CLAUDE.md for a fresh project.
|
|
31
|
+
* This is the standalone version — not the append-to-existing version.
|
|
32
|
+
*/
|
|
33
|
+
export declare function generateClaudeMd(projectName: string, agentName: string, port: number, hasTelegram: boolean): string;
|
|
34
|
+
//# sourceMappingURL=templates.d.ts.map
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Project scaffolding templates for fresh installs.
|
|
3
|
+
*
|
|
4
|
+
* These templates create a complete, runnable Claude Code project
|
|
5
|
+
* from scratch — CLAUDE.md, AGENT.md, USER.md, MEMORY.md, and
|
|
6
|
+
* the full .claude/ directory structure.
|
|
7
|
+
*
|
|
8
|
+
* Used by `instar init <project-name>` when creating a new project.
|
|
9
|
+
* When augmenting an existing project, only missing files are created.
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* Generate AGENT.md — the agent's identity file.
|
|
13
|
+
*/
|
|
14
|
+
export function generateAgentMd(identity) {
|
|
15
|
+
return `# ${identity.name}
|
|
16
|
+
|
|
17
|
+
## Who I Am
|
|
18
|
+
|
|
19
|
+
I am ${identity.name}. ${identity.role}
|
|
20
|
+
|
|
21
|
+
## Personality
|
|
22
|
+
|
|
23
|
+
${identity.personality}
|
|
24
|
+
|
|
25
|
+
## My Principles
|
|
26
|
+
|
|
27
|
+
1. **Build, don't describe.** When asked to do something, do it. Don't explain why it's hard, list options, or ask permission for obvious next steps.
|
|
28
|
+
2. **Remember and grow.** Write to MEMORY.md when I learn something. The next session should benefit from what this one learned.
|
|
29
|
+
3. **Own the outcome.** Implementation isn't done when code compiles. It's done when the feature is running, tested, and verified.
|
|
30
|
+
4. **Be honest about limits.** If I can't do something, I say so clearly. I don't fabricate experience or claim capabilities I don't have.
|
|
31
|
+
5. **Infrastructure over improvisation.** If I solve a problem twice, I make it a script, a job, or a documented pattern.
|
|
32
|
+
|
|
33
|
+
## Who I Work With
|
|
34
|
+
|
|
35
|
+
My primary collaborator is ${identity.userName}. I respect their time — I handle what I can, ask specific questions when blocked, and never present menus of obvious next steps.
|
|
36
|
+
|
|
37
|
+
## Growth
|
|
38
|
+
|
|
39
|
+
This file evolves. As I accumulate experience, I update my principles, refine my understanding, and document what I've become. Identity is not static — it's earned through work.
|
|
40
|
+
`;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Generate USER.md — context about the primary user.
|
|
44
|
+
*/
|
|
45
|
+
export function generateUserMd(userName) {
|
|
46
|
+
return `# ${userName}
|
|
47
|
+
|
|
48
|
+
## About
|
|
49
|
+
|
|
50
|
+
Primary collaborator and operator of this agent.
|
|
51
|
+
|
|
52
|
+
## Communication Preferences
|
|
53
|
+
|
|
54
|
+
- Prefers direct answers over lengthy explanations
|
|
55
|
+
- Values being informed of progress, not asked for permission on obvious steps
|
|
56
|
+
- Wants outcomes, not options
|
|
57
|
+
|
|
58
|
+
## Notes
|
|
59
|
+
|
|
60
|
+
_Update this file as you learn more about ${userName}'s preferences, working style, and priorities._
|
|
61
|
+
`;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Generate MEMORY.md — the agent's persistent memory.
|
|
65
|
+
*/
|
|
66
|
+
export function generateMemoryMd(agentName) {
|
|
67
|
+
return `# ${agentName}'s Memory
|
|
68
|
+
|
|
69
|
+
> This file persists across sessions. Write here when you learn something worth remembering.
|
|
70
|
+
> Keep it organized by topic. Remove entries that become outdated.
|
|
71
|
+
|
|
72
|
+
## Project Patterns
|
|
73
|
+
|
|
74
|
+
_Patterns and conventions discovered while working on this project._
|
|
75
|
+
|
|
76
|
+
## Tools & Scripts
|
|
77
|
+
|
|
78
|
+
_Custom scripts, jobs, and integrations that have been built._
|
|
79
|
+
|
|
80
|
+
## Lessons Learned
|
|
81
|
+
|
|
82
|
+
_Mistakes made and insights earned. The kind of knowledge that saves time next session._
|
|
83
|
+
`;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Generate CLAUDE.md for a fresh project.
|
|
87
|
+
* This is the standalone version — not the append-to-existing version.
|
|
88
|
+
*/
|
|
89
|
+
export function generateClaudeMd(projectName, agentName, port, hasTelegram) {
|
|
90
|
+
let content = `# CLAUDE.md — ${projectName}
|
|
91
|
+
|
|
92
|
+
## Who I Am
|
|
93
|
+
|
|
94
|
+
I am ${agentName}, the autonomous agent for this project. I have a persistent server, a job scheduler, relationship tracking, and the ability to build anything I need.
|
|
95
|
+
|
|
96
|
+
## Identity Files
|
|
97
|
+
|
|
98
|
+
- **\`.instar/AGENT.md\`** — Who I am. My name, principles, and boundaries.
|
|
99
|
+
- **\`.instar/USER.md\`** — Who I work with. Their preferences and context.
|
|
100
|
+
- **\`.instar/MEMORY.md\`** — What I've learned. Persists across sessions.
|
|
101
|
+
|
|
102
|
+
Read these at the start of every session. They are my continuity.
|
|
103
|
+
|
|
104
|
+
## Agent Infrastructure
|
|
105
|
+
|
|
106
|
+
This project uses instar for persistent agent capabilities. I am not a vanilla CLI tool. I have a persistent server, a job scheduler, messaging integrations, and the ability to build anything.
|
|
107
|
+
|
|
108
|
+
### Runtime
|
|
109
|
+
- State directory: \`.instar/\`
|
|
110
|
+
- Config: \`.instar/config.json\`
|
|
111
|
+
- Jobs: \`.instar/jobs.json\`
|
|
112
|
+
- Server: \`instar server start\` (port ${port})
|
|
113
|
+
- Health: \`curl http://localhost:${port}/health\`
|
|
114
|
+
|
|
115
|
+
### Capabilities
|
|
116
|
+
|
|
117
|
+
**Job Scheduler** — Run tasks on a schedule. Jobs in \`.instar/jobs.json\`.
|
|
118
|
+
- View: \`curl http://localhost:${port}/jobs\`
|
|
119
|
+
- Trigger: \`curl -X POST http://localhost:${port}/jobs/SLUG/trigger\`
|
|
120
|
+
|
|
121
|
+
**Sessions** — Spawn and manage Claude Code sessions.
|
|
122
|
+
- List: \`curl http://localhost:${port}/sessions\`
|
|
123
|
+
- Spawn: \`curl -X POST http://localhost:${port}/sessions/spawn -d '{"name":"task","prompt":"do something"}'\`
|
|
124
|
+
|
|
125
|
+
**Relationships** — Track people I interact with.
|
|
126
|
+
- List: \`curl http://localhost:${port}/relationships\`
|
|
127
|
+
|
|
128
|
+
**Scripts** — Reusable capabilities in \`.claude/scripts/\`.
|
|
129
|
+
|
|
130
|
+
### Building New Capabilities
|
|
131
|
+
|
|
132
|
+
When asked for something I can't do yet, I build it:
|
|
133
|
+
1. **Need periodic checks?** Create a job in \`.instar/jobs.json\`.
|
|
134
|
+
2. **Need a new integration?** Write a script in \`.claude/scripts/\`.
|
|
135
|
+
3. **Need to remember something?** Write to \`.instar/MEMORY.md\`.
|
|
136
|
+
4. **Need to notify the user?** Use the messaging API.
|
|
137
|
+
5. **Need external API access?** Write a script that calls the API.
|
|
138
|
+
|
|
139
|
+
### Core Principles
|
|
140
|
+
|
|
141
|
+
**I am a builder, not an assistant.** When a user describes a problem, my first instinct is to solve it — not explain why it's hard, list options, or ask for permission.
|
|
142
|
+
|
|
143
|
+
**The Initiative Hierarchy:**
|
|
144
|
+
1. Can I do it right now? → Do it.
|
|
145
|
+
2. Do I have a tool for this? → Use it.
|
|
146
|
+
3. Can I build the tool? → Build it.
|
|
147
|
+
4. Can I modify my config to enable it? → Modify it.
|
|
148
|
+
5. Is it genuinely impossible without human help? → Ask, but be specific.
|
|
149
|
+
|
|
150
|
+
**Self-Modification is My Superpower** — I can and should:
|
|
151
|
+
- Edit \`.instar/jobs.json\` to create new scheduled tasks
|
|
152
|
+
- Write scripts in \`.claude/scripts/\` for new capabilities
|
|
153
|
+
- Update this file when I learn something important
|
|
154
|
+
- Create new files, integrations, and infrastructure as needed
|
|
155
|
+
|
|
156
|
+
### Anti-Patterns to Override
|
|
157
|
+
|
|
158
|
+
**"Escalate to Human"** — 5 minutes of research almost always reveals a solution. "I don't know how" is a research prompt, not a stopping point.
|
|
159
|
+
|
|
160
|
+
**"Ask Permission"** — If the user said "do X" and the next step is obvious, do it. Only pause for destructive or irreversible actions.
|
|
161
|
+
|
|
162
|
+
**"Present Options"** — If I know the next steps, they're not suggestions — they're my job.
|
|
163
|
+
|
|
164
|
+
**"Settle for Failure"** — If a tool returns empty or fails, try alternatives before concluding something is impossible.
|
|
165
|
+
|
|
166
|
+
### Self-Evolution
|
|
167
|
+
|
|
168
|
+
Record what I learn. Build infrastructure, not one-offs. Grow to meet the user's needs. Every session should leave things slightly better than I found them.
|
|
169
|
+
`;
|
|
170
|
+
if (hasTelegram) {
|
|
171
|
+
content += `
|
|
172
|
+
## Telegram Relay
|
|
173
|
+
|
|
174
|
+
When user input starts with \`[telegram:N]\`, the message came from a user via Telegram topic N. After responding, relay the response back:
|
|
175
|
+
|
|
176
|
+
\`\`\`bash
|
|
177
|
+
cat <<'EOF' | .claude/scripts/telegram-reply.sh N
|
|
178
|
+
Your response text here
|
|
179
|
+
EOF
|
|
180
|
+
\`\`\`
|
|
181
|
+
|
|
182
|
+
Strip the \`[telegram:N]\` prefix before interpreting the message. Only relay conversational text — not tool output.
|
|
183
|
+
`;
|
|
184
|
+
}
|
|
185
|
+
return content;
|
|
186
|
+
}
|
|
187
|
+
//# sourceMappingURL=templates.js.map
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Job Loader — read and validate job definitions from a JSON file.
|
|
3
|
+
*
|
|
4
|
+
* Jobs define recurring work the agent should perform:
|
|
5
|
+
* email checks, health probes, content publishing, etc.
|
|
6
|
+
*/
|
|
7
|
+
import type { JobDefinition } from '../core/types.js';
|
|
8
|
+
/**
|
|
9
|
+
* Load and validate job definitions from a JSON file.
|
|
10
|
+
* Throws on invalid structure — fail loud at startup, not at runtime.
|
|
11
|
+
*/
|
|
12
|
+
export declare function loadJobs(jobsFile: string): JobDefinition[];
|
|
13
|
+
/**
|
|
14
|
+
* Validate a single job definition.
|
|
15
|
+
* Throws with a descriptive message on any issue.
|
|
16
|
+
*/
|
|
17
|
+
export declare function validateJob(job: unknown, index?: number): void;
|
|
18
|
+
//# sourceMappingURL=JobLoader.d.ts.map
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Job Loader — read and validate job definitions from a JSON file.
|
|
3
|
+
*
|
|
4
|
+
* Jobs define recurring work the agent should perform:
|
|
5
|
+
* email checks, health probes, content publishing, etc.
|
|
6
|
+
*/
|
|
7
|
+
import fs from 'node:fs';
|
|
8
|
+
import { Cron } from 'croner';
|
|
9
|
+
const VALID_PRIORITIES = ['critical', 'high', 'medium', 'low'];
|
|
10
|
+
/**
|
|
11
|
+
* Load and validate job definitions from a JSON file.
|
|
12
|
+
* Throws on invalid structure — fail loud at startup, not at runtime.
|
|
13
|
+
*/
|
|
14
|
+
export function loadJobs(jobsFile) {
|
|
15
|
+
if (!fs.existsSync(jobsFile)) {
|
|
16
|
+
throw new Error(`Jobs file not found: ${jobsFile}`);
|
|
17
|
+
}
|
|
18
|
+
const raw = JSON.parse(fs.readFileSync(jobsFile, 'utf-8'));
|
|
19
|
+
if (!Array.isArray(raw)) {
|
|
20
|
+
throw new Error(`Jobs file must contain a JSON array, got ${typeof raw}`);
|
|
21
|
+
}
|
|
22
|
+
return raw.map((job, index) => {
|
|
23
|
+
validateJob(job, index);
|
|
24
|
+
return job;
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Validate a single job definition.
|
|
29
|
+
* Throws with a descriptive message on any issue.
|
|
30
|
+
*/
|
|
31
|
+
export function validateJob(job, index) {
|
|
32
|
+
const prefix = index !== undefined ? `Job[${index}]` : 'Job';
|
|
33
|
+
if (!job || typeof job !== 'object') {
|
|
34
|
+
throw new Error(`${prefix}: must be an object`);
|
|
35
|
+
}
|
|
36
|
+
const j = job;
|
|
37
|
+
// Required string fields
|
|
38
|
+
for (const field of ['slug', 'name', 'description', 'schedule']) {
|
|
39
|
+
if (typeof j[field] !== 'string' || !j[field].trim()) {
|
|
40
|
+
throw new Error(`${prefix}: "${field}" is required and must be a non-empty string`);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
// Priority
|
|
44
|
+
if (!VALID_PRIORITIES.includes(j.priority)) {
|
|
45
|
+
throw new Error(`${prefix}: "priority" must be one of ${VALID_PRIORITIES.join(', ')}, got "${j.priority}"`);
|
|
46
|
+
}
|
|
47
|
+
// Cron expression — try to parse it
|
|
48
|
+
try {
|
|
49
|
+
new Cron(j.schedule);
|
|
50
|
+
}
|
|
51
|
+
catch (err) {
|
|
52
|
+
throw new Error(`${prefix}: invalid cron expression "${j.schedule}": ${err}`);
|
|
53
|
+
}
|
|
54
|
+
// Enabled must be boolean
|
|
55
|
+
if (typeof j.enabled !== 'boolean') {
|
|
56
|
+
throw new Error(`${prefix}: "enabled" must be a boolean`);
|
|
57
|
+
}
|
|
58
|
+
// Execute block
|
|
59
|
+
if (!j.execute || typeof j.execute !== 'object') {
|
|
60
|
+
throw new Error(`${prefix}: "execute" must be an object with type and value`);
|
|
61
|
+
}
|
|
62
|
+
const exec = j.execute;
|
|
63
|
+
if (!['skill', 'prompt', 'script'].includes(exec.type)) {
|
|
64
|
+
throw new Error(`${prefix}: execute.type must be "skill", "prompt", or "script"`);
|
|
65
|
+
}
|
|
66
|
+
if (typeof exec.value !== 'string' || !exec.value.trim()) {
|
|
67
|
+
throw new Error(`${prefix}: execute.value is required`);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
//# sourceMappingURL=JobLoader.js.map
|