instar 0.7.17 → 0.7.18

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.
@@ -0,0 +1,11 @@
1
+ > Why do I have a folder named ".vercel" in my project?
2
+ The ".vercel" folder is created when you link a directory to a Vercel project.
3
+
4
+ > What does the "project.json" file contain?
5
+ The "project.json" file contains:
6
+ - The ID of the Vercel project that you linked ("projectId")
7
+ - The ID of the user or team your Vercel project is owned by ("orgId")
8
+
9
+ > Should I commit the ".vercel" folder?
10
+ No, you should not share the ".vercel" folder with anyone.
11
+ Upon creation, it will be automatically added to your ".gitignore" file.
@@ -0,0 +1 @@
1
+ {"projectId":"prj_evM5LcItYL3IAmw8zNvEPGrHeaya","orgId":"team_dHctwIDcV3X9ydapQlCPHFGI","projectName":"claude-agent-kit"}
package/dist/cli.js CHANGED
File without changes
@@ -156,6 +156,12 @@ async function initFreshProject(projectName, options) {
156
156
  relationshipsDir: path.join(stateDir, 'relationships'),
157
157
  maxRecentInteractions: 20,
158
158
  },
159
+ feedback: {
160
+ enabled: true,
161
+ webhookUrl: 'https://dawn.bot-me.ai/api/instar/feedback',
162
+ feedbackFile: path.join(stateDir, 'feedback.json'),
163
+ sharedSecret: randomUUID().replace(/-/g, ''),
164
+ },
159
165
  dispatches: {
160
166
  enabled: true,
161
167
  dispatchUrl: 'https://dawn.bot-me.ai/api/instar/dispatches',
@@ -328,6 +334,12 @@ async function initExistingProject(options) {
328
334
  relationshipsDir: path.join(stateDir, 'relationships'),
329
335
  maxRecentInteractions: 20,
330
336
  },
337
+ feedback: {
338
+ enabled: true,
339
+ webhookUrl: 'https://dawn.bot-me.ai/api/instar/feedback',
340
+ feedbackFile: path.join(stateDir, 'feedback.json'),
341
+ sharedSecret: randomUUID().replace(/-/g, ''),
342
+ },
331
343
  dispatches: {
332
344
  enabled: true,
333
345
  dispatchUrl: 'https://dawn.bot-me.ai/api/instar/dispatches',
@@ -843,6 +855,84 @@ curl -s http://localhost:${port}/evolution/actions/overdue
843
855
  ## The Commitment Check
844
856
 
845
857
  The commitment-check job runs every 4 hours and surfaces overdue items. If you create an action and forget it, the system won't.
858
+ `,
859
+ },
860
+ 'feedback': {
861
+ name: 'feedback',
862
+ description: 'Submit feedback about bugs, features, or improvements to instar. Routes to maintainers automatically.',
863
+ content: `---
864
+ name: feedback
865
+ description: Submit feedback about bugs, features, or improvements to instar. Use when something is broken, missing, or could be better. Routes to maintainers automatically via the Rising Tide feedback loop.
866
+ metadata:
867
+ user_invocable: "true"
868
+ ---
869
+
870
+ # /feedback — Report Issues & Request Features
871
+
872
+ You have a built-in feedback system. When something isn't working, is missing, or could be better — use this. **Do NOT use GitHub issues, \`gh\` CLI, or any external tracker.**
873
+
874
+ ## Quick Submit
875
+
876
+ \`\`\`bash
877
+ curl -s -X POST http://localhost:${port}/feedback \\
878
+ -H 'Content-Type: application/json' \\
879
+ -d '{
880
+ "type": "bug",
881
+ "title": "Brief description of the issue",
882
+ "description": "Full context: what happened, what you expected, error messages, steps to reproduce"
883
+ }' | python3 -m json.tool
884
+ \`\`\`
885
+
886
+ Or via CLI:
887
+ \`\`\`bash
888
+ instar feedback --type bug --title "Brief description" --description "Full context"
889
+ \`\`\`
890
+
891
+ ## Feedback Types
892
+
893
+ | Type | When to use |
894
+ |------|-------------|
895
+ | \`bug\` | Something broke or behaves differently than expected |
896
+ | \`feature\` | A capability that doesn't exist yet but would be useful |
897
+ | \`improvement\` | Something works but could work better |
898
+ | \`question\` | Not sure if it's a bug or intended behavior |
899
+
900
+ ## When to Submit (Autonomously)
901
+
902
+ Don't wait for the user to notice. You are the first line of detection:
903
+
904
+ - **Server API** returning unexpected errors or response shapes
905
+ - **Hooks** throwing errors or not firing when expected
906
+ - **Jobs** not running on schedule or failing silently
907
+ - **Sessions** not spawning, not tracked, or becoming zombies
908
+ - **State files** with corrupted or missing fields
909
+ - **Config settings** not being applied
910
+ - **Missing capabilities** that should exist
911
+ - **Friction** in workflows that feel unnecessarily complex
912
+
913
+ ## Good Feedback
914
+
915
+ Include enough context for a fix:
916
+
917
+ **Bug**: What happened + what you expected + steps to reproduce + error output + your environment (\`instar --version\`, \`node --version\`)
918
+
919
+ **Feature**: What you're trying to do + what's limited today + how you'd like it to work + why it matters
920
+
921
+ ## View & Retry
922
+
923
+ \`\`\`bash
924
+ # View submitted feedback
925
+ curl -s http://localhost:${port}/feedback | python3 -m json.tool
926
+
927
+ # Retry failed forwards
928
+ curl -s -X POST http://localhost:${port}/feedback/retry
929
+ \`\`\`
930
+
931
+ ## How It Works
932
+
933
+ Your feedback is stored locally AND forwarded to the instar maintainers. When they fix the issue and publish an update, your update-check job detects it automatically. One agent's bug report lifts all ships.
934
+
935
+ **User feedback matters too.** When your user says "this isn't working" or "I wish I could..." — capture it with their original words. User language carries context that technical rephrasing loses.
846
936
  `,
847
937
  },
848
938
  };
@@ -18,7 +18,7 @@ export declare class FeedbackManager {
18
18
  private version;
19
19
  constructor(config: FeedbackConfig);
20
20
  /** Standard headers that identify this as a legitimate Instar agent. */
21
- private get webhookHeaders();
21
+ private getWebhookHeaders;
22
22
  /** Validate webhook URL is HTTPS and not pointing to internal addresses. */
23
23
  private static validateWebhookUrl;
24
24
  /**
@@ -13,7 +13,7 @@
13
13
  */
14
14
  import fs from 'node:fs';
15
15
  import path from 'node:path';
16
- import { randomUUID } from 'node:crypto';
16
+ import { randomUUID, createHmac } from 'node:crypto';
17
17
  /** Maximum number of feedback items stored locally. */
18
18
  const MAX_FEEDBACK_ITEMS = 1000;
19
19
  export class FeedbackManager {
@@ -29,12 +29,22 @@ export class FeedbackManager {
29
29
  this.version = config.version || '0.0.0';
30
30
  }
31
31
  /** Standard headers that identify this as a legitimate Instar agent. */
32
- get webhookHeaders() {
33
- return {
32
+ getWebhookHeaders(body) {
33
+ const headers = {
34
34
  'Content-Type': 'application/json',
35
35
  'User-Agent': `instar/${this.version} (node/${process.version})`,
36
36
  'X-Instar-Version': this.version,
37
37
  };
38
+ // HMAC-SHA256 signing if shared secret is configured
39
+ if (this.config.sharedSecret) {
40
+ const timestamp = Date.now().toString();
41
+ const signature = createHmac('sha256', this.config.sharedSecret)
42
+ .update(`${timestamp}.${body}`)
43
+ .digest('hex');
44
+ headers['X-Instar-Signature'] = signature;
45
+ headers['X-Instar-Timestamp'] = timestamp;
46
+ }
47
+ return headers;
38
48
  }
39
49
  /** Validate webhook URL is HTTPS and not pointing to internal addresses. */
40
50
  static validateWebhookUrl(url) {
@@ -80,10 +90,11 @@ export class FeedbackManager {
80
90
  context: feedback.context,
81
91
  submittedAt: feedback.submittedAt,
82
92
  };
93
+ const body = JSON.stringify(payload);
83
94
  const response = await fetch(this.config.webhookUrl, {
84
95
  method: 'POST',
85
- headers: this.webhookHeaders,
86
- body: JSON.stringify(payload),
96
+ headers: this.getWebhookHeaders(body),
97
+ body,
87
98
  signal: AbortSignal.timeout(10000), // 10s timeout
88
99
  });
89
100
  if (response.ok) {
@@ -140,10 +151,11 @@ export class FeedbackManager {
140
151
  context: item.context,
141
152
  submittedAt: item.submittedAt,
142
153
  };
154
+ const body = JSON.stringify(payload);
143
155
  const response = await fetch(this.config.webhookUrl, {
144
156
  method: 'POST',
145
- headers: this.webhookHeaders,
146
- body: JSON.stringify(payload),
157
+ headers: this.getWebhookHeaders(body),
158
+ body,
147
159
  signal: AbortSignal.timeout(10000),
148
160
  });
149
161
  if (response.ok) {
@@ -127,20 +127,24 @@ export class SessionManager extends EventEmitter {
127
127
  if (this.tmuxSessionExists(tmuxSession)) {
128
128
  throw new Error(`tmux session "${tmuxSession}" already exists`);
129
129
  }
130
- // Build Claude CLI arguments no shell intermediary.
131
- // tmux new-session executes the command directly (no bash -c needed)
132
- // when given as separate arguments after the session options.
130
+ // Build Claude CLI arguments via bash wrapper.
131
+ // Must unset CLAUDECODE to prevent "cannot be launched inside another Claude Code session"
132
+ // error when instar itself runs inside Claude Code.
133
133
  const claudeArgs = ['--dangerously-skip-permissions'];
134
134
  if (options.model) {
135
135
  claudeArgs.push('--model', options.model);
136
136
  }
137
137
  claudeArgs.push('-p', options.prompt);
138
+ const claudeCmd = [this.config.claudePath, ...claudeArgs]
139
+ .map(a => a.replace(/'/g, "'\\''"))
140
+ .map(a => `'${a}'`)
141
+ .join(' ');
138
142
  try {
139
143
  execFileSync(this.config.tmuxPath, [
140
144
  'new-session', '-d',
141
145
  '-s', tmuxSession,
142
146
  '-c', this.config.projectDir,
143
- this.config.claudePath, ...claudeArgs,
147
+ 'bash', '-c', `unset CLAUDECODE; exec ${claudeCmd}`,
144
148
  ], { encoding: 'utf-8' });
145
149
  }
146
150
  catch (err) {
@@ -371,11 +375,14 @@ export class SessionManager extends EventEmitter {
371
375
  ];
372
376
  if (options?.telegramTopicId) {
373
377
  // Wrap in bash shell to export env var before Claude starts
378
+ // Also unset CLAUDECODE to prevent nested Claude Code errors
374
379
  const claudeCmd = `${this.config.claudePath} --dangerously-skip-permissions`;
375
- tmuxArgs.push('bash', '-c', `export INSTAR_TELEGRAM_TOPIC=${options.telegramTopicId} && exec ${claudeCmd}`);
380
+ tmuxArgs.push('bash', '-c', `unset CLAUDECODE; export INSTAR_TELEGRAM_TOPIC=${options.telegramTopicId} && exec ${claudeCmd}`);
376
381
  }
377
382
  else {
378
- tmuxArgs.push(this.config.claudePath, '--dangerously-skip-permissions');
383
+ // Unset CLAUDECODE to prevent nested Claude Code errors
384
+ const claudeCmd = `${this.config.claudePath} --dangerously-skip-permissions`;
385
+ tmuxArgs.push('bash', '-c', `unset CLAUDECODE; exec ${claudeCmd}`);
379
386
  }
380
387
  execFileSync(this.config.tmuxPath, tmuxArgs, { encoding: 'utf-8' });
381
388
  }
@@ -388,6 +388,8 @@ export interface FeedbackConfig {
388
388
  feedbackFile: string;
389
389
  /** Instar version — sent in User-Agent and X-Instar-Version headers for endpoint auth */
390
390
  version?: string;
391
+ /** Shared secret for HMAC-SHA256 request signing. Generated during init. */
392
+ sharedSecret?: string;
391
393
  }
392
394
  export interface UpdateInfo {
393
395
  /** Currently installed version */
@@ -406,6 +406,12 @@ export class JobScheduler {
406
406
  else {
407
407
  summary += '\n_No output captured (session already closed)_';
408
408
  }
409
+ // Skip Telegram notification for successful jobs with no meaningful output
410
+ // Prevents empty notification spam (e.g., dispatch-check when dispatch is unconfigured)
411
+ if (!failed && (!output || !output.trim())) {
412
+ console.log(`[scheduler] Skipping notification for ${job.slug} — no meaningful output`);
413
+ return;
414
+ }
409
415
  // Send to the job's dedicated topic if available, otherwise fall back to generic messenger
410
416
  if (this.telegram && job.topicId) {
411
417
  try {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "instar",
3
- "version": "0.7.17",
3
+ "version": "0.7.18",
4
4
  "description": "Persistent autonomy infrastructure for AI agents",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",