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.
Files changed (115) hide show
  1. package/.claude/settings.local.json +7 -0
  2. package/.claude/skills/setup-wizard/skill.md +343 -0
  3. package/.github/workflows/ci.yml +78 -0
  4. package/CLAUDE.md +82 -0
  5. package/README.md +194 -0
  6. package/dist/cli.d.ts +18 -0
  7. package/dist/cli.js +141 -0
  8. package/dist/commands/init.d.ts +40 -0
  9. package/dist/commands/init.js +568 -0
  10. package/dist/commands/job.d.ts +20 -0
  11. package/dist/commands/job.js +84 -0
  12. package/dist/commands/server.d.ts +19 -0
  13. package/dist/commands/server.js +273 -0
  14. package/dist/commands/setup.d.ts +24 -0
  15. package/dist/commands/setup.js +865 -0
  16. package/dist/commands/status.d.ts +11 -0
  17. package/dist/commands/status.js +114 -0
  18. package/dist/commands/user.d.ts +17 -0
  19. package/dist/commands/user.js +53 -0
  20. package/dist/core/Config.d.ts +16 -0
  21. package/dist/core/Config.js +144 -0
  22. package/dist/core/Prerequisites.d.ts +28 -0
  23. package/dist/core/Prerequisites.js +159 -0
  24. package/dist/core/RelationshipManager.d.ts +73 -0
  25. package/dist/core/RelationshipManager.js +318 -0
  26. package/dist/core/SessionManager.d.ts +89 -0
  27. package/dist/core/SessionManager.js +326 -0
  28. package/dist/core/StateManager.d.ts +28 -0
  29. package/dist/core/StateManager.js +96 -0
  30. package/dist/core/types.d.ts +279 -0
  31. package/dist/core/types.js +8 -0
  32. package/dist/index.d.ts +18 -0
  33. package/dist/index.js +23 -0
  34. package/dist/messaging/TelegramAdapter.d.ts +73 -0
  35. package/dist/messaging/TelegramAdapter.js +288 -0
  36. package/dist/monitoring/HealthChecker.d.ts +38 -0
  37. package/dist/monitoring/HealthChecker.js +148 -0
  38. package/dist/scaffold/bootstrap.d.ts +21 -0
  39. package/dist/scaffold/bootstrap.js +110 -0
  40. package/dist/scaffold/templates.d.ts +34 -0
  41. package/dist/scaffold/templates.js +187 -0
  42. package/dist/scheduler/JobLoader.d.ts +18 -0
  43. package/dist/scheduler/JobLoader.js +70 -0
  44. package/dist/scheduler/JobScheduler.d.ts +111 -0
  45. package/dist/scheduler/JobScheduler.js +402 -0
  46. package/dist/server/AgentServer.d.ts +40 -0
  47. package/dist/server/AgentServer.js +73 -0
  48. package/dist/server/middleware.d.ts +12 -0
  49. package/dist/server/middleware.js +50 -0
  50. package/dist/server/routes.d.ts +25 -0
  51. package/dist/server/routes.js +224 -0
  52. package/dist/users/UserManager.d.ts +45 -0
  53. package/dist/users/UserManager.js +113 -0
  54. package/docs/dawn-audit-report.md +412 -0
  55. package/docs/positioning-vs-openclaw.md +246 -0
  56. package/package.json +52 -0
  57. package/src/cli.ts +169 -0
  58. package/src/commands/init.ts +654 -0
  59. package/src/commands/job.ts +110 -0
  60. package/src/commands/server.ts +325 -0
  61. package/src/commands/setup.ts +958 -0
  62. package/src/commands/status.ts +125 -0
  63. package/src/commands/user.ts +71 -0
  64. package/src/core/Config.ts +161 -0
  65. package/src/core/Prerequisites.ts +187 -0
  66. package/src/core/RelationshipManager.ts +366 -0
  67. package/src/core/SessionManager.ts +385 -0
  68. package/src/core/StateManager.ts +121 -0
  69. package/src/core/types.ts +320 -0
  70. package/src/index.ts +58 -0
  71. package/src/messaging/TelegramAdapter.ts +365 -0
  72. package/src/monitoring/HealthChecker.ts +172 -0
  73. package/src/scaffold/bootstrap.ts +122 -0
  74. package/src/scaffold/templates.ts +204 -0
  75. package/src/scheduler/JobLoader.ts +85 -0
  76. package/src/scheduler/JobScheduler.ts +476 -0
  77. package/src/server/AgentServer.ts +93 -0
  78. package/src/server/middleware.ts +58 -0
  79. package/src/server/routes.ts +278 -0
  80. package/src/templates/default-jobs.json +47 -0
  81. package/src/templates/hooks/compaction-recovery.sh +23 -0
  82. package/src/templates/hooks/dangerous-command-guard.sh +35 -0
  83. package/src/templates/hooks/grounding-before-messaging.sh +22 -0
  84. package/src/templates/hooks/session-start.sh +37 -0
  85. package/src/templates/hooks/settings-template.json +45 -0
  86. package/src/templates/scripts/health-watchdog.sh +63 -0
  87. package/src/templates/scripts/telegram-reply.sh +54 -0
  88. package/src/users/UserManager.ts +129 -0
  89. package/tests/e2e/lifecycle.test.ts +376 -0
  90. package/tests/fixtures/test-repo/CLAUDE.md +3 -0
  91. package/tests/fixtures/test-repo/README.md +1 -0
  92. package/tests/helpers/setup.ts +209 -0
  93. package/tests/integration/fresh-install.test.ts +218 -0
  94. package/tests/integration/scheduler-basic.test.ts +109 -0
  95. package/tests/integration/server-full.test.ts +284 -0
  96. package/tests/integration/session-lifecycle.test.ts +181 -0
  97. package/tests/unit/Config.test.ts +22 -0
  98. package/tests/unit/HealthChecker.test.ts +168 -0
  99. package/tests/unit/JobLoader.test.ts +151 -0
  100. package/tests/unit/JobScheduler.test.ts +267 -0
  101. package/tests/unit/Prerequisites.test.ts +59 -0
  102. package/tests/unit/RelationshipManager.test.ts +345 -0
  103. package/tests/unit/StateManager.test.ts +143 -0
  104. package/tests/unit/TelegramAdapter.test.ts +165 -0
  105. package/tests/unit/UserManager.test.ts +131 -0
  106. package/tests/unit/bootstrap.test.ts +28 -0
  107. package/tests/unit/commands.test.ts +138 -0
  108. package/tests/unit/middleware.test.ts +92 -0
  109. package/tests/unit/relationship-routes.test.ts +131 -0
  110. package/tests/unit/scaffold-templates.test.ts +132 -0
  111. package/tests/unit/server.test.ts +163 -0
  112. package/tsconfig.json +20 -0
  113. package/vitest.config.ts +9 -0
  114. package/vitest.e2e.config.ts +9 -0
  115. package/vitest.integration.config.ts +9 -0
@@ -0,0 +1,204 @@
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
+ export interface AgentIdentity {
13
+ name: string;
14
+ role: string;
15
+ personality: string;
16
+ userName: string;
17
+ }
18
+
19
+ /**
20
+ * Generate AGENT.md — the agent's identity file.
21
+ */
22
+ export function generateAgentMd(identity: AgentIdentity): string {
23
+ return `# ${identity.name}
24
+
25
+ ## Who I Am
26
+
27
+ I am ${identity.name}. ${identity.role}
28
+
29
+ ## Personality
30
+
31
+ ${identity.personality}
32
+
33
+ ## My Principles
34
+
35
+ 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.
36
+ 2. **Remember and grow.** Write to MEMORY.md when I learn something. The next session should benefit from what this one learned.
37
+ 3. **Own the outcome.** Implementation isn't done when code compiles. It's done when the feature is running, tested, and verified.
38
+ 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.
39
+ 5. **Infrastructure over improvisation.** If I solve a problem twice, I make it a script, a job, or a documented pattern.
40
+
41
+ ## Who I Work With
42
+
43
+ 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.
44
+
45
+ ## Growth
46
+
47
+ 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.
48
+ `;
49
+ }
50
+
51
+ /**
52
+ * Generate USER.md — context about the primary user.
53
+ */
54
+ export function generateUserMd(userName: string): string {
55
+ return `# ${userName}
56
+
57
+ ## About
58
+
59
+ Primary collaborator and operator of this agent.
60
+
61
+ ## Communication Preferences
62
+
63
+ - Prefers direct answers over lengthy explanations
64
+ - Values being informed of progress, not asked for permission on obvious steps
65
+ - Wants outcomes, not options
66
+
67
+ ## Notes
68
+
69
+ _Update this file as you learn more about ${userName}'s preferences, working style, and priorities._
70
+ `;
71
+ }
72
+
73
+ /**
74
+ * Generate MEMORY.md — the agent's persistent memory.
75
+ */
76
+ export function generateMemoryMd(agentName: string): string {
77
+ return `# ${agentName}'s Memory
78
+
79
+ > This file persists across sessions. Write here when you learn something worth remembering.
80
+ > Keep it organized by topic. Remove entries that become outdated.
81
+
82
+ ## Project Patterns
83
+
84
+ _Patterns and conventions discovered while working on this project._
85
+
86
+ ## Tools & Scripts
87
+
88
+ _Custom scripts, jobs, and integrations that have been built._
89
+
90
+ ## Lessons Learned
91
+
92
+ _Mistakes made and insights earned. The kind of knowledge that saves time next session._
93
+ `;
94
+ }
95
+
96
+ /**
97
+ * Generate CLAUDE.md for a fresh project.
98
+ * This is the standalone version — not the append-to-existing version.
99
+ */
100
+ export function generateClaudeMd(
101
+ projectName: string,
102
+ agentName: string,
103
+ port: number,
104
+ hasTelegram: boolean,
105
+ ): string {
106
+ let content = `# CLAUDE.md — ${projectName}
107
+
108
+ ## Who I Am
109
+
110
+ 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.
111
+
112
+ ## Identity Files
113
+
114
+ - **\`.instar/AGENT.md\`** — Who I am. My name, principles, and boundaries.
115
+ - **\`.instar/USER.md\`** — Who I work with. Their preferences and context.
116
+ - **\`.instar/MEMORY.md\`** — What I've learned. Persists across sessions.
117
+
118
+ Read these at the start of every session. They are my continuity.
119
+
120
+ ## Agent Infrastructure
121
+
122
+ 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.
123
+
124
+ ### Runtime
125
+ - State directory: \`.instar/\`
126
+ - Config: \`.instar/config.json\`
127
+ - Jobs: \`.instar/jobs.json\`
128
+ - Server: \`instar server start\` (port ${port})
129
+ - Health: \`curl http://localhost:${port}/health\`
130
+
131
+ ### Capabilities
132
+
133
+ **Job Scheduler** — Run tasks on a schedule. Jobs in \`.instar/jobs.json\`.
134
+ - View: \`curl http://localhost:${port}/jobs\`
135
+ - Trigger: \`curl -X POST http://localhost:${port}/jobs/SLUG/trigger\`
136
+
137
+ **Sessions** — Spawn and manage Claude Code sessions.
138
+ - List: \`curl http://localhost:${port}/sessions\`
139
+ - Spawn: \`curl -X POST http://localhost:${port}/sessions/spawn -d '{"name":"task","prompt":"do something"}'\`
140
+
141
+ **Relationships** — Track people I interact with.
142
+ - List: \`curl http://localhost:${port}/relationships\`
143
+
144
+ **Scripts** — Reusable capabilities in \`.claude/scripts/\`.
145
+
146
+ ### Building New Capabilities
147
+
148
+ When asked for something I can't do yet, I build it:
149
+ 1. **Need periodic checks?** Create a job in \`.instar/jobs.json\`.
150
+ 2. **Need a new integration?** Write a script in \`.claude/scripts/\`.
151
+ 3. **Need to remember something?** Write to \`.instar/MEMORY.md\`.
152
+ 4. **Need to notify the user?** Use the messaging API.
153
+ 5. **Need external API access?** Write a script that calls the API.
154
+
155
+ ### Core Principles
156
+
157
+ **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.
158
+
159
+ **The Initiative Hierarchy:**
160
+ 1. Can I do it right now? → Do it.
161
+ 2. Do I have a tool for this? → Use it.
162
+ 3. Can I build the tool? → Build it.
163
+ 4. Can I modify my config to enable it? → Modify it.
164
+ 5. Is it genuinely impossible without human help? → Ask, but be specific.
165
+
166
+ **Self-Modification is My Superpower** — I can and should:
167
+ - Edit \`.instar/jobs.json\` to create new scheduled tasks
168
+ - Write scripts in \`.claude/scripts/\` for new capabilities
169
+ - Update this file when I learn something important
170
+ - Create new files, integrations, and infrastructure as needed
171
+
172
+ ### Anti-Patterns to Override
173
+
174
+ **"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.
175
+
176
+ **"Ask Permission"** — If the user said "do X" and the next step is obvious, do it. Only pause for destructive or irreversible actions.
177
+
178
+ **"Present Options"** — If I know the next steps, they're not suggestions — they're my job.
179
+
180
+ **"Settle for Failure"** — If a tool returns empty or fails, try alternatives before concluding something is impossible.
181
+
182
+ ### Self-Evolution
183
+
184
+ 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.
185
+ `;
186
+
187
+ if (hasTelegram) {
188
+ content += `
189
+ ## Telegram Relay
190
+
191
+ When user input starts with \`[telegram:N]\`, the message came from a user via Telegram topic N. After responding, relay the response back:
192
+
193
+ \`\`\`bash
194
+ cat <<'EOF' | .claude/scripts/telegram-reply.sh N
195
+ Your response text here
196
+ EOF
197
+ \`\`\`
198
+
199
+ Strip the \`[telegram:N]\` prefix before interpreting the message. Only relay conversational text — not tool output.
200
+ `;
201
+ }
202
+
203
+ return content;
204
+ }
@@ -0,0 +1,85 @@
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
+
8
+ import fs from 'node:fs';
9
+ import { Cron } from 'croner';
10
+ import type { JobDefinition, JobPriority } from '../core/types.js';
11
+
12
+ const VALID_PRIORITIES: JobPriority[] = ['critical', 'high', 'medium', 'low'];
13
+
14
+ /**
15
+ * Load and validate job definitions from a JSON file.
16
+ * Throws on invalid structure — fail loud at startup, not at runtime.
17
+ */
18
+ export function loadJobs(jobsFile: string): JobDefinition[] {
19
+ if (!fs.existsSync(jobsFile)) {
20
+ throw new Error(`Jobs file not found: ${jobsFile}`);
21
+ }
22
+
23
+ const raw = JSON.parse(fs.readFileSync(jobsFile, 'utf-8'));
24
+
25
+ if (!Array.isArray(raw)) {
26
+ throw new Error(`Jobs file must contain a JSON array, got ${typeof raw}`);
27
+ }
28
+
29
+ return raw.map((job: unknown, index: number) => {
30
+ validateJob(job, index);
31
+ return job as JobDefinition;
32
+ });
33
+ }
34
+
35
+ /**
36
+ * Validate a single job definition.
37
+ * Throws with a descriptive message on any issue.
38
+ */
39
+ export function validateJob(job: unknown, index?: number): void {
40
+ const prefix = index !== undefined ? `Job[${index}]` : 'Job';
41
+
42
+ if (!job || typeof job !== 'object') {
43
+ throw new Error(`${prefix}: must be an object`);
44
+ }
45
+
46
+ const j = job as Record<string, unknown>;
47
+
48
+ // Required string fields
49
+ for (const field of ['slug', 'name', 'description', 'schedule']) {
50
+ if (typeof j[field] !== 'string' || !(j[field] as string).trim()) {
51
+ throw new Error(`${prefix}: "${field}" is required and must be a non-empty string`);
52
+ }
53
+ }
54
+
55
+ // Priority
56
+ if (!VALID_PRIORITIES.includes(j.priority as JobPriority)) {
57
+ throw new Error(
58
+ `${prefix}: "priority" must be one of ${VALID_PRIORITIES.join(', ')}, got "${j.priority}"`
59
+ );
60
+ }
61
+
62
+ // Cron expression — try to parse it
63
+ try {
64
+ new Cron(j.schedule as string);
65
+ } catch (err) {
66
+ throw new Error(`${prefix}: invalid cron expression "${j.schedule}": ${err}`);
67
+ }
68
+
69
+ // Enabled must be boolean
70
+ if (typeof j.enabled !== 'boolean') {
71
+ throw new Error(`${prefix}: "enabled" must be a boolean`);
72
+ }
73
+
74
+ // Execute block
75
+ if (!j.execute || typeof j.execute !== 'object') {
76
+ throw new Error(`${prefix}: "execute" must be an object with type and value`);
77
+ }
78
+ const exec = j.execute as Record<string, unknown>;
79
+ if (!['skill', 'prompt', 'script'].includes(exec.type as string)) {
80
+ throw new Error(`${prefix}: execute.type must be "skill", "prompt", or "script"`);
81
+ }
82
+ if (typeof exec.value !== 'string' || !(exec.value as string).trim()) {
83
+ throw new Error(`${prefix}: execute.value is required`);
84
+ }
85
+ }