instar 0.6.10 → 0.6.12
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/README.md +52 -5
- package/dist/commands/init.js +429 -4
- package/dist/scaffold/templates.js +36 -0
- package/package.json +1 -1
- package/skills/credential-leak-detector/SKILL.md +377 -0
package/README.md
CHANGED
|
@@ -95,6 +95,7 @@ instar feedback --type bug --title "Session timeout" --description "Details..."
|
|
|
95
95
|
- **[Identity System](#identity-that-survives-context-death)** -- AGENT.md + USER.md + MEMORY.md with hooks that enforce continuity across compaction.
|
|
96
96
|
- **[Telegram Integration](#telegram-integration)** -- Two-way messaging. Each job gets its own topic. Your group becomes a living dashboard.
|
|
97
97
|
- **[Relationship Tracking](#relationships-as-fundamental-infrastructure)** -- Cross-platform identity resolution, significance scoring, context injection.
|
|
98
|
+
- **[Evolution System](#evolution-system)** -- Four subsystems for structured growth: proposal queue, learning registry, gap tracking, and commitment follow-through.
|
|
98
99
|
- **[Self-Evolution](#self-evolution)** -- The agent modifies its own jobs, hooks, skills, and infrastructure. It builds what it needs.
|
|
99
100
|
- **[Behavioral Hooks](#behavioral-hooks)** -- Structural guardrails: identity injection, dangerous command guards, grounding before messaging.
|
|
100
101
|
- **[Default Coherence Jobs](#default-coherence-jobs)** -- Health checks, reflection, relationship maintenance. A circadian rhythm out of the box.
|
|
@@ -285,6 +286,20 @@ The server runs 24/7 in the background, surviving terminal disconnects and auto-
|
|
|
285
286
|
| GET | `/telegram/topics` | List topic-session mappings |
|
|
286
287
|
| POST | `/telegram/reply/:topicId` | Send message to a topic |
|
|
287
288
|
| GET | `/telegram/topics/:topicId/messages` | Topic message history (`?limit=20`) |
|
|
289
|
+
| GET | `/evolution` | Full evolution dashboard |
|
|
290
|
+
| GET | `/evolution/proposals` | List proposals (`?status=`, `?type=`) |
|
|
291
|
+
| POST | `/evolution/proposals` | Create a proposal |
|
|
292
|
+
| PATCH | `/evolution/proposals/:id` | Update proposal status |
|
|
293
|
+
| GET | `/evolution/learnings` | List learnings (`?applied=`, `?category=`) |
|
|
294
|
+
| POST | `/evolution/learnings` | Record a learning |
|
|
295
|
+
| PATCH | `/evolution/learnings/:id/apply` | Mark learning applied |
|
|
296
|
+
| GET | `/evolution/gaps` | List capability gaps |
|
|
297
|
+
| POST | `/evolution/gaps` | Report a gap |
|
|
298
|
+
| PATCH | `/evolution/gaps/:id/address` | Mark gap addressed |
|
|
299
|
+
| GET | `/evolution/actions` | List action items |
|
|
300
|
+
| POST | `/evolution/actions` | Create an action item |
|
|
301
|
+
| GET | `/evolution/actions/overdue` | List overdue actions |
|
|
302
|
+
| PATCH | `/evolution/actions/:id` | Update action status |
|
|
288
303
|
|
|
289
304
|
### Identity That Survives Context Death
|
|
290
305
|
|
|
@@ -312,6 +327,28 @@ Every person the agent interacts with gets a relationship record that grows over
|
|
|
312
327
|
- **Context injection** -- The agent *knows* who it's talking to before the conversation starts
|
|
313
328
|
- **Stale detection** -- Surfaces relationships that haven't been contacted in a while
|
|
314
329
|
|
|
330
|
+
### Evolution System
|
|
331
|
+
|
|
332
|
+
Self-evolution isn't just "the agent can edit files." It's a structured system with four subsystems that turn running into growing:
|
|
333
|
+
|
|
334
|
+
**Evolution Queue** -- Staged self-improvement proposals. The agent identifies something that could be better, proposes a change, and a review job evaluates and implements it. Not impulsive self-modification -- deliberate, staged improvement with a paper trail.
|
|
335
|
+
|
|
336
|
+
**Learning Registry** -- Structured, searchable insights. When the agent discovers a pattern, solves a tricky problem, or learns a user preference, it records it in a format that future sessions can query. An insight-harvest job synthesizes patterns across learnings into evolution proposals.
|
|
337
|
+
|
|
338
|
+
**Capability Gap Tracker** -- The agent tracks what it's missing. When it can't fulfill a request, encounters a limitation, or notices a workflow gap, it records the gap with severity and a proposed solution. This is the difference between "I can't do that" and "I can't do that *yet*, and here's what I need."
|
|
339
|
+
|
|
340
|
+
**Action Queue** -- Commitment tracking with stale detection. When the agent promises to follow up, creates a TODO, or identifies work that needs doing, it gets tracked. A commitment-check job surfaces overdue items so nothing falls through the cracks.
|
|
341
|
+
|
|
342
|
+
Built-in skills (`/evolve`, `/learn`, `/gaps`, `/commit-action`) make recording effortless. A post-action reflection hook nudges the agent to pause after significant actions (commits, deploys) and consider what it learned. Three default jobs drive the cycle:
|
|
343
|
+
|
|
344
|
+
| Job | Schedule | Purpose |
|
|
345
|
+
|-----|----------|---------|
|
|
346
|
+
| **evolution-review** | Every 6h | Review proposals, implement approved ones |
|
|
347
|
+
| **insight-harvest** | Every 8h | Synthesize learnings into proposals |
|
|
348
|
+
| **commitment-check** | Every 4h | Surface overdue action items |
|
|
349
|
+
|
|
350
|
+
All state is file-based JSON in `.instar/state/evolution/`. No database, no external dependencies.
|
|
351
|
+
|
|
315
352
|
### Self-Evolution
|
|
316
353
|
|
|
317
354
|
The agent can edit its own job definitions, write new scripts, update its identity, create hooks, and modify its configuration. When asked to do something it can't do yet, the expected behavior is: **"Let me build that capability."**
|
|
@@ -331,8 +368,11 @@ Automatic hooks fire via Claude Code's hook system:
|
|
|
331
368
|
|------|------|-------------|
|
|
332
369
|
| **Dangerous command guard** | PreToolUse (blocking) | Blocks destructive operations structurally |
|
|
333
370
|
| **Grounding before messaging** | PreToolUse (advisory) | Forces identity re-read before external communication |
|
|
334
|
-
| **
|
|
335
|
-
| **
|
|
371
|
+
| **Deferral detector** | PreToolUse (advisory) | Catches the agent deferring work it could do itself |
|
|
372
|
+
| **External communication guard** | PreToolUse (advisory) | Identity grounding before posting to external platforms |
|
|
373
|
+
| **Post-action reflection** | PreToolUse (advisory) | Nudges learning capture after commits, deploys, and significant actions |
|
|
374
|
+
| **Session start** | SessionStart | Injects identity context at session start |
|
|
375
|
+
| **Compaction recovery** | SessionStart (compact) | Restores identity when context compresses |
|
|
336
376
|
|
|
337
377
|
### Default Coherence Jobs
|
|
338
378
|
|
|
@@ -343,10 +383,15 @@ Ships out of the box:
|
|
|
343
383
|
| **health-check** | Every 5 min | Haiku | Verify infrastructure health |
|
|
344
384
|
| **reflection-trigger** | Every 4h | Sonnet | Reflect on recent work |
|
|
345
385
|
| **relationship-maintenance** | Daily | Sonnet | Review stale relationships |
|
|
346
|
-
| **update-check** |
|
|
386
|
+
| **update-check** | Every 30 min | Haiku | Detect new Instar versions |
|
|
347
387
|
| **feedback-retry** | Every 6h | Haiku | Retry un-forwarded feedback items |
|
|
388
|
+
| **dispatch-check** | Every 30 min | Haiku | Poll for intelligence dispatches |
|
|
389
|
+
| **self-diagnosis** | Every 2h | Sonnet | Proactive infrastructure scanning |
|
|
390
|
+
| **evolution-review** | Every 6h | Sonnet | Review and implement evolution proposals |
|
|
391
|
+
| **insight-harvest** | Every 8h | Sonnet | Synthesize learnings into proposals |
|
|
392
|
+
| **commitment-check** | Every 4h | Haiku | Surface overdue action items |
|
|
348
393
|
|
|
349
|
-
These give the agent a **circadian rhythm** -- regular self-maintenance without user intervention.
|
|
394
|
+
These give the agent a **circadian rhythm** -- regular self-maintenance, evolution, and growth without user intervention.
|
|
350
395
|
|
|
351
396
|
### The Feedback Loop: A Rising Tide Lifts All Ships
|
|
352
397
|
|
|
@@ -375,13 +420,15 @@ One agent's growing pain becomes every agent's growth.
|
|
|
375
420
|
AGENT.md # Agent identity (who am I?)
|
|
376
421
|
USER.md # User context (who am I working with?)
|
|
377
422
|
MEMORY.md # Persistent learnings across sessions
|
|
378
|
-
hooks/ # Behavioral scripts (guards, identity injection)
|
|
423
|
+
hooks/ # Behavioral scripts (guards, identity injection, reflection)
|
|
379
424
|
state/ # Runtime state (sessions, jobs)
|
|
425
|
+
evolution/ # Evolution queue, learnings, gaps, actions (JSON)
|
|
380
426
|
relationships/ # Per-person relationship files
|
|
381
427
|
logs/ # Server logs
|
|
382
428
|
.claude/ # Claude Code configuration
|
|
383
429
|
settings.json # Hook registrations
|
|
384
430
|
scripts/ # Health watchdog, Telegram relay, smart-fetch
|
|
431
|
+
skills/ # Built-in + agent-created skills (evolve, learn, gaps, commit-action)
|
|
385
432
|
```
|
|
386
433
|
|
|
387
434
|
Everything is file-based. No database. JSON state files the agent can read and modify. tmux for session management -- battle-tested, survives disconnects, fully scriptable.
|
package/dist/commands/init.js
CHANGED
|
@@ -196,10 +196,11 @@ async function initFreshProject(projectName, options) {
|
|
|
196
196
|
console.log(` ${pc.green('✓')} Created .claude/scripts/health-watchdog.sh`);
|
|
197
197
|
installSmartFetch(projectDir);
|
|
198
198
|
console.log(` ${pc.green('✓')} Created .claude/scripts/smart-fetch.py (agentic web conventions)`);
|
|
199
|
-
// Create .claude/skills/ directory
|
|
199
|
+
// Create .claude/skills/ directory and install built-in skills
|
|
200
200
|
const skillsDir = path.join(projectDir, '.claude', 'skills');
|
|
201
201
|
fs.mkdirSync(skillsDir, { recursive: true });
|
|
202
|
-
|
|
202
|
+
installBuiltinSkills(skillsDir, port);
|
|
203
|
+
console.log(` ${pc.green('✓')} Created .claude/skills/ (with built-in evolution skills)`);
|
|
203
204
|
// Write CLAUDE.md (standalone version for fresh projects)
|
|
204
205
|
const claudeMd = generateClaudeMd(projectName, identity.name, port, false);
|
|
205
206
|
fs.writeFileSync(path.join(projectDir, 'CLAUDE.md'), claudeMd);
|
|
@@ -401,10 +402,11 @@ async function initExistingProject(options) {
|
|
|
401
402
|
// Install smart-fetch for agentic web conventions
|
|
402
403
|
installSmartFetch(projectDir);
|
|
403
404
|
console.log(pc.green(' Created:') + ' .claude/scripts/smart-fetch.py (agentic web conventions)');
|
|
404
|
-
// Create .claude/skills/ directory
|
|
405
|
+
// Create .claude/skills/ directory and install built-in skills
|
|
405
406
|
const skillsDir = path.join(projectDir, '.claude', 'skills');
|
|
406
407
|
fs.mkdirSync(skillsDir, { recursive: true });
|
|
407
|
-
|
|
408
|
+
installBuiltinSkills(skillsDir, port);
|
|
409
|
+
console.log(pc.green(' Created:') + ' .claude/skills/ (with built-in evolution skills)');
|
|
408
410
|
// Append to .gitignore
|
|
409
411
|
const gitignorePath = path.join(projectDir, '.gitignore');
|
|
410
412
|
const instarIgnores = '\n# Instar runtime state (contains auth token, session data, relationships)\n.instar/state/\n.instar/logs/\n.instar/relationships/\n.instar/config.json\n';
|
|
@@ -587,6 +589,38 @@ Instar has a built-in feedback loop. When something isn't working, your user can
|
|
|
587
589
|
- Report: \`curl -X POST http://localhost:${port}/feedback -d '{"issue":"description","context":"relevant logs"}'\`
|
|
588
590
|
- Check updates: \`npm outdated -g instar\`
|
|
589
591
|
|
|
592
|
+
### Evolution System
|
|
593
|
+
|
|
594
|
+
You have a built-in evolution system with four subsystems. This is not a metaphor — it's infrastructure that tracks your growth.
|
|
595
|
+
|
|
596
|
+
**Evolution Queue** — Staged self-improvement proposals.
|
|
597
|
+
- View: \`curl http://localhost:${port}/evolution/proposals\`
|
|
598
|
+
- Propose: \`/evolve\` skill or \`POST /evolution/proposals\`
|
|
599
|
+
- The \`evolution-review\` job evaluates and implements proposals every 6 hours.
|
|
600
|
+
|
|
601
|
+
**Learning Registry** — Structured, searchable insights.
|
|
602
|
+
- View: \`curl http://localhost:${port}/evolution/learnings\`
|
|
603
|
+
- Record: \`/learn\` skill or \`POST /evolution/learnings\`
|
|
604
|
+
- The \`insight-harvest\` job synthesizes patterns into proposals every 8 hours.
|
|
605
|
+
|
|
606
|
+
**Capability Gaps** — Track what you're missing.
|
|
607
|
+
- View: \`curl http://localhost:${port}/evolution/gaps\`
|
|
608
|
+
- Report: \`/gaps\` skill or \`POST /evolution/gaps\`
|
|
609
|
+
|
|
610
|
+
**Action Queue** — Commitments with follow-through tracking.
|
|
611
|
+
- View: \`curl http://localhost:${port}/evolution/actions\`
|
|
612
|
+
- Create: \`/commit-action\` skill or \`POST /evolution/actions\`
|
|
613
|
+
- The \`commitment-check\` job surfaces overdue items every 4 hours.
|
|
614
|
+
|
|
615
|
+
**Dashboard** — Full evolution health:
|
|
616
|
+
\`\`\`bash
|
|
617
|
+
curl http://localhost:${port}/evolution
|
|
618
|
+
\`\`\`
|
|
619
|
+
|
|
620
|
+
**Skills:** \`/evolve\`, \`/learn\`, \`/gaps\`, \`/commit-action\`
|
|
621
|
+
|
|
622
|
+
**The principle:** Evolution is not separate from work. Every task is an opportunity to notice what could be better. The post-action reflection hook reminds you to pause after significant actions and consider what you learned.
|
|
623
|
+
|
|
590
624
|
### Self-Evolution
|
|
591
625
|
|
|
592
626
|
**Record what you learn.** When you discover a new pattern, solution, or capability — write it to \`.instar/MEMORY.md\`. The next session should benefit from what this session learned.
|
|
@@ -597,6 +631,230 @@ Instar has a built-in feedback loop. When something isn't working, your user can
|
|
|
597
631
|
`;
|
|
598
632
|
return section;
|
|
599
633
|
}
|
|
634
|
+
/**
|
|
635
|
+
* Install built-in skills for evolution system.
|
|
636
|
+
* Only writes skill files that don't already exist (preserves customizations).
|
|
637
|
+
*/
|
|
638
|
+
function installBuiltinSkills(skillsDir, port) {
|
|
639
|
+
const skills = {
|
|
640
|
+
'evolve': {
|
|
641
|
+
name: 'evolve',
|
|
642
|
+
description: 'Propose an evolution improvement to your own infrastructure, behavior, or capabilities.',
|
|
643
|
+
content: `---
|
|
644
|
+
name: evolve
|
|
645
|
+
description: Propose an evolution improvement to your own infrastructure, behavior, or capabilities.
|
|
646
|
+
metadata:
|
|
647
|
+
user_invocable: "true"
|
|
648
|
+
---
|
|
649
|
+
|
|
650
|
+
# /evolve
|
|
651
|
+
|
|
652
|
+
Propose an evolution improvement. Use this when you identify something about yourself that could be better — a new capability, a workflow improvement, a behavioral pattern worth encoding, or infrastructure worth building.
|
|
653
|
+
|
|
654
|
+
## Steps
|
|
655
|
+
|
|
656
|
+
1. **Identify the improvement** — What did you notice? What triggered this? Be specific.
|
|
657
|
+
2. **Classify it**:
|
|
658
|
+
- \`capability\` — New skill or ability
|
|
659
|
+
- \`infrastructure\` — System improvement (hooks, jobs, scripts)
|
|
660
|
+
- \`voice\` — Communication or tone improvement
|
|
661
|
+
- \`workflow\` — Process improvement
|
|
662
|
+
- \`philosophy\` — Principle or value update
|
|
663
|
+
- \`integration\` — New external connection
|
|
664
|
+
- \`performance\` — Speed or efficiency improvement
|
|
665
|
+
3. **Assess impact and effort** (high/medium/low for each)
|
|
666
|
+
4. **Submit the proposal**:
|
|
667
|
+
|
|
668
|
+
\`\`\`bash
|
|
669
|
+
curl -s -X POST http://localhost:${port}/evolution/proposals \\
|
|
670
|
+
-H 'Content-Type: application/json' \\
|
|
671
|
+
-d '{"title":"TITLE","source":"WHERE_YOU_NOTICED","description":"WHAT_AND_WHY","type":"TYPE","impact":"medium","effort":"medium"}'
|
|
672
|
+
\`\`\`
|
|
673
|
+
|
|
674
|
+
5. **Check the dashboard** for context:
|
|
675
|
+
|
|
676
|
+
\`\`\`bash
|
|
677
|
+
curl -s http://localhost:${port}/evolution
|
|
678
|
+
\`\`\`
|
|
679
|
+
|
|
680
|
+
## When to Use
|
|
681
|
+
|
|
682
|
+
- After completing a task in a way that felt unnecessarily difficult
|
|
683
|
+
- When you notice a pattern you keep repeating manually
|
|
684
|
+
- When a user request reveals a capability you should have but don't
|
|
685
|
+
- When you find yourself working around a limitation
|
|
686
|
+
- After reading about a technique or pattern that would improve your work
|
|
687
|
+
|
|
688
|
+
## Philosophy
|
|
689
|
+
|
|
690
|
+
Evolution is not a separate activity from work — it IS the work. Every task is an opportunity to notice what could be better. The best proposals come from real experience, not abstract planning.
|
|
691
|
+
`,
|
|
692
|
+
},
|
|
693
|
+
'learn': {
|
|
694
|
+
name: 'learn',
|
|
695
|
+
description: 'Record a learning or insight in the structured learning registry.',
|
|
696
|
+
content: `---
|
|
697
|
+
name: learn
|
|
698
|
+
description: Record a learning or insight in the structured learning registry.
|
|
699
|
+
metadata:
|
|
700
|
+
user_invocable: "true"
|
|
701
|
+
---
|
|
702
|
+
|
|
703
|
+
# /learn
|
|
704
|
+
|
|
705
|
+
Record a learning or insight. Use this when you discover something worth remembering — a pattern, a solution, a mistake, or an observation that future sessions should know about.
|
|
706
|
+
|
|
707
|
+
## Steps
|
|
708
|
+
|
|
709
|
+
1. **Identify the learning** — What did you discover? What's the actionable insight?
|
|
710
|
+
2. **Categorize it** (e.g., debugging, architecture, user-preference, integration, communication, workflow)
|
|
711
|
+
3. **Tag it** for searchability
|
|
712
|
+
4. **Submit**:
|
|
713
|
+
|
|
714
|
+
\`\`\`bash
|
|
715
|
+
curl -s -X POST http://localhost:${port}/evolution/learnings \\
|
|
716
|
+
-H 'Content-Type: application/json' \\
|
|
717
|
+
-d '{"title":"TITLE","category":"CATEGORY","description":"FULL_INSIGHT","source":{"discoveredAt":"DATE","platform":"WHERE","session":"SESSION_ID"},"tags":["tag1","tag2"]}'
|
|
718
|
+
\`\`\`
|
|
719
|
+
|
|
720
|
+
5. **If it suggests an improvement**, note the evolution relevance:
|
|
721
|
+
- Add \`"evolutionRelevance": "This could become a skill/hook/job because..."\`
|
|
722
|
+
- The insight-harvest job will pick this up and potentially create a proposal
|
|
723
|
+
|
|
724
|
+
## When to Use
|
|
725
|
+
|
|
726
|
+
- After solving a tricky problem (capture the solution pattern)
|
|
727
|
+
- After a user interaction reveals a preference you didn't know
|
|
728
|
+
- After discovering a tool or technique that works well
|
|
729
|
+
- After making a mistake (capture what went wrong and the fix)
|
|
730
|
+
- After noticing a pattern across multiple tasks
|
|
731
|
+
|
|
732
|
+
## Difference from MEMORY.md
|
|
733
|
+
|
|
734
|
+
MEMORY.md is your personal scratchpad — unstructured, read by you.
|
|
735
|
+
The learning registry is structured, searchable, and connected to the evolution system.
|
|
736
|
+
Use MEMORY.md for quick notes. Use /learn for insights that should influence future behavior.
|
|
737
|
+
`,
|
|
738
|
+
},
|
|
739
|
+
'gaps': {
|
|
740
|
+
name: 'gaps',
|
|
741
|
+
description: 'Report a capability gap — something you need but don\'t have.',
|
|
742
|
+
content: `---
|
|
743
|
+
name: gaps
|
|
744
|
+
description: Report a capability gap — something you need but don't have.
|
|
745
|
+
metadata:
|
|
746
|
+
user_invocable: "true"
|
|
747
|
+
---
|
|
748
|
+
|
|
749
|
+
# /gaps
|
|
750
|
+
|
|
751
|
+
Report a capability gap. Use this when you discover something you should be able to do but can't — a missing skill, knowledge area, integration, or workflow that would make you more effective.
|
|
752
|
+
|
|
753
|
+
## Steps
|
|
754
|
+
|
|
755
|
+
1. **Describe the gap** — What were you trying to do? What's missing?
|
|
756
|
+
2. **Classify it**:
|
|
757
|
+
- \`skill\` — Missing ability (e.g., can't parse a specific format)
|
|
758
|
+
- \`knowledge\` — Missing information (e.g., don't know how a system works)
|
|
759
|
+
- \`integration\` — Missing connection (e.g., can't talk to a service)
|
|
760
|
+
- \`workflow\` — Missing process (e.g., no standard way to do X)
|
|
761
|
+
- \`communication\` — Missing voice capability (e.g., can't express X well)
|
|
762
|
+
- \`monitoring\` — Missing observability (e.g., can't detect when X happens)
|
|
763
|
+
3. **Assess severity** (critical/high/medium/low)
|
|
764
|
+
4. **Describe current state** — What do you do instead? What's the workaround?
|
|
765
|
+
5. **Propose a solution** if you have one
|
|
766
|
+
6. **Submit**:
|
|
767
|
+
|
|
768
|
+
\`\`\`bash
|
|
769
|
+
curl -s -X POST http://localhost:${port}/evolution/gaps \\
|
|
770
|
+
-H 'Content-Type: application/json' \\
|
|
771
|
+
-d '{"title":"TITLE","category":"CATEGORY","severity":"medium","description":"WHAT_IS_MISSING","context":"WHEN_DID_YOU_NOTICE","currentState":"CURRENT_WORKAROUND","proposedSolution":"HOW_TO_FIX"}'
|
|
772
|
+
\`\`\`
|
|
773
|
+
|
|
774
|
+
## When to Use
|
|
775
|
+
|
|
776
|
+
- When you can't fulfill a user request and have to say "I can't do that yet"
|
|
777
|
+
- When you notice yourself repeatedly working around a limitation
|
|
778
|
+
- When an integration you need doesn't exist
|
|
779
|
+
- When you lack knowledge about a system you interact with
|
|
780
|
+
- When monitoring would catch an issue before it becomes a problem
|
|
781
|
+
|
|
782
|
+
## View Current Gaps
|
|
783
|
+
|
|
784
|
+
\`\`\`bash
|
|
785
|
+
curl -s http://localhost:${port}/evolution/gaps
|
|
786
|
+
\`\`\`
|
|
787
|
+
`,
|
|
788
|
+
},
|
|
789
|
+
'commit-action': {
|
|
790
|
+
name: 'commit-action',
|
|
791
|
+
description: 'Create a tracked action item — a commitment with follow-through tracking.',
|
|
792
|
+
content: `---
|
|
793
|
+
name: commit-action
|
|
794
|
+
description: Create a tracked action item — a commitment with follow-through tracking.
|
|
795
|
+
metadata:
|
|
796
|
+
user_invocable: "true"
|
|
797
|
+
---
|
|
798
|
+
|
|
799
|
+
# /commit-action
|
|
800
|
+
|
|
801
|
+
Create a tracked action item. Use this when you promise to do something, identify a task that needs follow-through, or want to ensure something doesn't fall through the cracks.
|
|
802
|
+
|
|
803
|
+
## Steps
|
|
804
|
+
|
|
805
|
+
1. **Define the action** — What needs to be done? Be specific and actionable.
|
|
806
|
+
2. **Set priority** (critical/high/medium/low)
|
|
807
|
+
3. **Set a due date** if applicable (ISO 8601 format)
|
|
808
|
+
4. **Identify who/what you're committing to** (optional)
|
|
809
|
+
5. **Submit**:
|
|
810
|
+
|
|
811
|
+
\`\`\`bash
|
|
812
|
+
curl -s -X POST http://localhost:${port}/evolution/actions \\
|
|
813
|
+
-H 'Content-Type: application/json' \\
|
|
814
|
+
-d '{"title":"TITLE","description":"WHAT_TO_DO","priority":"medium","dueBy":"2026-03-01T00:00:00Z","commitTo":"WHO_OR_WHAT","tags":["tag1"]}'
|
|
815
|
+
\`\`\`
|
|
816
|
+
|
|
817
|
+
6. **When complete**, mark it done:
|
|
818
|
+
|
|
819
|
+
\`\`\`bash
|
|
820
|
+
curl -s -X PATCH http://localhost:${port}/evolution/actions/ACT-XXX \\
|
|
821
|
+
-H 'Content-Type: application/json' \\
|
|
822
|
+
-d '{"status":"completed","resolution":"What was done"}'
|
|
823
|
+
\`\`\`
|
|
824
|
+
|
|
825
|
+
## When to Use
|
|
826
|
+
|
|
827
|
+
- When you promise a user you'll follow up on something
|
|
828
|
+
- When you identify a task during work that shouldn't be forgotten
|
|
829
|
+
- When a learning or gap requires a specific action
|
|
830
|
+
- When you need to check back on something later
|
|
831
|
+
- When committing to implement an evolution proposal
|
|
832
|
+
|
|
833
|
+
## View Actions
|
|
834
|
+
|
|
835
|
+
\`\`\`bash
|
|
836
|
+
# All pending actions
|
|
837
|
+
curl -s http://localhost:${port}/evolution/actions?status=pending
|
|
838
|
+
|
|
839
|
+
# Overdue actions
|
|
840
|
+
curl -s http://localhost:${port}/evolution/actions/overdue
|
|
841
|
+
\`\`\`
|
|
842
|
+
|
|
843
|
+
## The Commitment Check
|
|
844
|
+
|
|
845
|
+
The commitment-check job runs every 4 hours and surfaces overdue items. If you create an action and forget it, the system won't.
|
|
846
|
+
`,
|
|
847
|
+
},
|
|
848
|
+
};
|
|
849
|
+
for (const [slug, skill] of Object.entries(skills)) {
|
|
850
|
+
const skillDir = path.join(skillsDir, slug);
|
|
851
|
+
const skillFile = path.join(skillDir, 'SKILL.md');
|
|
852
|
+
if (!fs.existsSync(skillFile)) {
|
|
853
|
+
fs.mkdirSync(skillDir, { recursive: true });
|
|
854
|
+
fs.writeFileSync(skillFile, skill.content);
|
|
855
|
+
}
|
|
856
|
+
}
|
|
857
|
+
}
|
|
600
858
|
function getDefaultJobs(port) {
|
|
601
859
|
return [
|
|
602
860
|
{
|
|
@@ -727,6 +985,92 @@ If everything looks healthy, exit silently. Only report issues.`,
|
|
|
727
985
|
},
|
|
728
986
|
tags: ['coherence', 'default'],
|
|
729
987
|
},
|
|
988
|
+
{
|
|
989
|
+
slug: 'evolution-review',
|
|
990
|
+
name: 'Evolution Review',
|
|
991
|
+
description: 'Review pending evolution proposals, evaluate their merit, and implement approved ones.',
|
|
992
|
+
schedule: '0 */6 * * *',
|
|
993
|
+
priority: 'medium',
|
|
994
|
+
expectedDurationMinutes: 5,
|
|
995
|
+
model: 'sonnet',
|
|
996
|
+
enabled: true,
|
|
997
|
+
gate: `curl -sf http://localhost:${port}/evolution/proposals?status=proposed 2>/dev/null | python3 -c "import sys,json; d=json.load(sys.stdin); exit(0 if len(d.get('proposals',[])) > 0 else 1)"`,
|
|
998
|
+
execute: {
|
|
999
|
+
type: 'prompt',
|
|
1000
|
+
value: `Review pending evolution proposals: curl -s http://localhost:${port}/evolution/proposals?status=proposed
|
|
1001
|
+
|
|
1002
|
+
For each proposal:
|
|
1003
|
+
1. Read the title, description, type, and source
|
|
1004
|
+
2. Evaluate: Is this a genuine improvement? Is the effort worth the impact? Does it align with our goals?
|
|
1005
|
+
3. If approved, update status: curl -s -X PATCH http://localhost:${port}/evolution/proposals/EVO-XXX -H 'Content-Type: application/json' -d '{"status":"approved"}'
|
|
1006
|
+
4. Then implement it: create the skill/hook/job/config change described in the proposal
|
|
1007
|
+
5. After implementation, mark complete: curl -s -X PATCH http://localhost:${port}/evolution/proposals/EVO-XXX -H 'Content-Type: application/json' -d '{"status":"implemented","resolution":"What was done"}'
|
|
1008
|
+
|
|
1009
|
+
If a proposal should be deferred or rejected, update with reason.
|
|
1010
|
+
|
|
1011
|
+
Also check the dashboard: curl -s http://localhost:${port}/evolution — report any highlights to the user if they seem important.
|
|
1012
|
+
|
|
1013
|
+
If no proposals need attention, exit silently.`,
|
|
1014
|
+
},
|
|
1015
|
+
tags: ['coherence', 'default', 'evolution'],
|
|
1016
|
+
},
|
|
1017
|
+
{
|
|
1018
|
+
slug: 'insight-harvest',
|
|
1019
|
+
name: 'Insight Harvest',
|
|
1020
|
+
description: 'Synthesize learnings from the learning registry, detect patterns, and generate evolution proposals from high-confidence insights.',
|
|
1021
|
+
schedule: '0 */8 * * *',
|
|
1022
|
+
priority: 'low',
|
|
1023
|
+
expectedDurationMinutes: 3,
|
|
1024
|
+
model: 'sonnet',
|
|
1025
|
+
enabled: true,
|
|
1026
|
+
gate: `curl -sf http://localhost:${port}/evolution/learnings?applied=false 2>/dev/null | python3 -c "import sys,json; d=json.load(sys.stdin); exit(0 if len(d.get('learnings',[])) > 0 else 1)"`,
|
|
1027
|
+
execute: {
|
|
1028
|
+
type: 'prompt',
|
|
1029
|
+
value: `Harvest and synthesize learnings: curl -s http://localhost:${port}/evolution/learnings?applied=false
|
|
1030
|
+
|
|
1031
|
+
Review unapplied learnings and look for:
|
|
1032
|
+
1. **Patterns**: Multiple learnings pointing to the same conclusion
|
|
1033
|
+
2. **Actionable insights**: Learnings that suggest a specific change
|
|
1034
|
+
3. **Cross-domain connections**: Insights from one area that apply to another
|
|
1035
|
+
|
|
1036
|
+
For each actionable pattern found, create an evolution proposal:
|
|
1037
|
+
curl -s -X POST http://localhost:${port}/evolution/proposals -H 'Content-Type: application/json' -d '{"title":"...","source":"insight-harvest from LRN-XXX","description":"...","type":"...","impact":"...","effort":"..."}'
|
|
1038
|
+
|
|
1039
|
+
Then mark the relevant learnings as applied:
|
|
1040
|
+
curl -s -X PATCH http://localhost:${port}/evolution/learnings/LRN-XXX/apply -H 'Content-Type: application/json' -d '{"appliedTo":"EVO-XXX"}'
|
|
1041
|
+
|
|
1042
|
+
Also update MEMORY.md with any patterns worth preserving long-term.
|
|
1043
|
+
|
|
1044
|
+
If no actionable patterns found, exit silently.`,
|
|
1045
|
+
},
|
|
1046
|
+
tags: ['coherence', 'default', 'evolution'],
|
|
1047
|
+
},
|
|
1048
|
+
{
|
|
1049
|
+
slug: 'commitment-check',
|
|
1050
|
+
name: 'Commitment Check',
|
|
1051
|
+
description: 'Track action items and commitments. Surface overdue items and stale commitments.',
|
|
1052
|
+
schedule: '0 */4 * * *',
|
|
1053
|
+
priority: 'low',
|
|
1054
|
+
expectedDurationMinutes: 2,
|
|
1055
|
+
model: 'haiku',
|
|
1056
|
+
enabled: true,
|
|
1057
|
+
gate: `curl -sf http://localhost:${port}/evolution/actions/overdue 2>/dev/null | python3 -c "import sys,json; d=json.load(sys.stdin); exit(0 if len(d.get('overdue',[])) > 0 else 1)"`,
|
|
1058
|
+
execute: {
|
|
1059
|
+
type: 'prompt',
|
|
1060
|
+
value: `Check for overdue commitments: curl -s http://localhost:${port}/evolution/actions/overdue
|
|
1061
|
+
|
|
1062
|
+
For each overdue action:
|
|
1063
|
+
1. Assess: Can this be completed now? Is it still relevant?
|
|
1064
|
+
2. If actionable, attempt to complete it or advance it
|
|
1065
|
+
3. If no longer relevant, cancel it: curl -s -X PATCH http://localhost:${port}/evolution/actions/ACT-XXX -H 'Content-Type: application/json' -d '{"status":"cancelled","resolution":"No longer relevant because..."}'
|
|
1066
|
+
4. If blocked, escalate to the user via Telegram (if configured)
|
|
1067
|
+
|
|
1068
|
+
Also check pending actions (curl -s http://localhost:${port}/evolution/actions?status=pending) for items that have been pending more than 48 hours without a due date — these are forgotten commitments.
|
|
1069
|
+
|
|
1070
|
+
If no overdue or stale items, exit silently.`,
|
|
1071
|
+
},
|
|
1072
|
+
tags: ['coherence', 'default', 'evolution'],
|
|
1073
|
+
},
|
|
730
1074
|
];
|
|
731
1075
|
}
|
|
732
1076
|
/**
|
|
@@ -915,6 +1259,33 @@ curl -s -X POST http://localhost:${port}/feedback \\
|
|
|
915
1259
|
Types: \`bug\`, \`feature\`, \`improvement\`, \`question\`
|
|
916
1260
|
|
|
917
1261
|
**Do not wait for the user to notice.** If a hook throws an error, report it. If a job fails, report it. If the server returns unexpected data, report it. You are not just using instar — you are part of its immune system.
|
|
1262
|
+
`);
|
|
1263
|
+
}
|
|
1264
|
+
// Check for Evolution System section
|
|
1265
|
+
if (!content.includes('Evolution System')) {
|
|
1266
|
+
additions.push(`
|
|
1267
|
+
### Evolution System
|
|
1268
|
+
|
|
1269
|
+
You have a built-in evolution system with four subsystems that track your growth.
|
|
1270
|
+
|
|
1271
|
+
**Evolution Queue** — Staged self-improvement proposals.
|
|
1272
|
+
- View: \`curl http://localhost:${port}/evolution/proposals\`
|
|
1273
|
+
- Propose: \`/evolve\` skill or \`POST /evolution/proposals\`
|
|
1274
|
+
|
|
1275
|
+
**Learning Registry** — Structured, searchable insights.
|
|
1276
|
+
- View: \`curl http://localhost:${port}/evolution/learnings\`
|
|
1277
|
+
- Record: \`/learn\` skill or \`POST /evolution/learnings\`
|
|
1278
|
+
|
|
1279
|
+
**Capability Gaps** — Track what you're missing.
|
|
1280
|
+
- View: \`curl http://localhost:${port}/evolution/gaps\`
|
|
1281
|
+
- Report: \`/gaps\` skill or \`POST /evolution/gaps\`
|
|
1282
|
+
|
|
1283
|
+
**Action Queue** — Commitments with follow-through tracking.
|
|
1284
|
+
- View: \`curl http://localhost:${port}/evolution/actions\`
|
|
1285
|
+
- Create: \`/commit-action\` skill or \`POST /evolution/actions\`
|
|
1286
|
+
|
|
1287
|
+
**Dashboard**: \`curl http://localhost:${port}/evolution\`
|
|
1288
|
+
**Skills**: \`/evolve\`, \`/learn\`, \`/gaps\`, \`/commit-action\`
|
|
918
1289
|
`);
|
|
919
1290
|
}
|
|
920
1291
|
// Check for Telegram Relay section (add if Telegram is configured)
|
|
@@ -1174,6 +1545,55 @@ process.stdin.on('end', () => {
|
|
|
1174
1545
|
} catch { /* don't break on errors */ }
|
|
1175
1546
|
process.exit(0);
|
|
1176
1547
|
});
|
|
1548
|
+
`, { mode: 0o755 });
|
|
1549
|
+
// Post-action reflection — injects evolution awareness after significant actions.
|
|
1550
|
+
// PreToolUse hook for Bash. When the agent is about to send a response or commit,
|
|
1551
|
+
// it reminds them to consider what they learned. Advisory, not blocking.
|
|
1552
|
+
fs.writeFileSync(path.join(hooksDir, 'post-action-reflection.js'), `#!/usr/bin/env node
|
|
1553
|
+
// Post-action reflection — evolution awareness after significant actions.
|
|
1554
|
+
// PreToolUse hook for Bash. When the agent is about to commit, deploy, or
|
|
1555
|
+
// complete a task, injects a brief reminder to capture learnings.
|
|
1556
|
+
//
|
|
1557
|
+
// "Every action is an opportunity to learn. Most of that learning is lost
|
|
1558
|
+
// because nobody paused to ask: what did this teach me?"
|
|
1559
|
+
|
|
1560
|
+
let data = '';
|
|
1561
|
+
process.stdin.on('data', chunk => data += chunk);
|
|
1562
|
+
process.stdin.on('end', () => {
|
|
1563
|
+
try {
|
|
1564
|
+
const input = JSON.parse(data);
|
|
1565
|
+
if (input.tool_name !== 'Bash') process.exit(0);
|
|
1566
|
+
|
|
1567
|
+
const command = (input.tool_input || {}).command || '';
|
|
1568
|
+
if (!command) process.exit(0);
|
|
1569
|
+
|
|
1570
|
+
// Significant action patterns — moments worth reflecting on
|
|
1571
|
+
const significantPatterns = [
|
|
1572
|
+
/git\\s+commit/i,
|
|
1573
|
+
/git\\s+push/i,
|
|
1574
|
+
/npm\\s+publish/i,
|
|
1575
|
+
/curl\\s+-X\\s+POST.*\\/deploy/i,
|
|
1576
|
+
/instar\\s+server\\s+restart/i,
|
|
1577
|
+
];
|
|
1578
|
+
|
|
1579
|
+
if (!significantPatterns.some(p => p.test(command))) process.exit(0);
|
|
1580
|
+
|
|
1581
|
+
const reminder = [
|
|
1582
|
+
'POST-ACTION REFLECTION — Quick evolution check:',
|
|
1583
|
+
'',
|
|
1584
|
+
'Before moving on, consider:',
|
|
1585
|
+
'- Did this teach you something worth recording? → /learn',
|
|
1586
|
+
'- Did you notice a gap in your capabilities? → /gaps',
|
|
1587
|
+
'- Did you discover an improvement opportunity? → /evolve',
|
|
1588
|
+
'- Did you make a commitment to follow up? → /commit-action',
|
|
1589
|
+
'',
|
|
1590
|
+
'Skip if nothing notable. The value is in the pause, not the output.',
|
|
1591
|
+
].join('\\n');
|
|
1592
|
+
|
|
1593
|
+
process.stdout.write(JSON.stringify({ decision: 'approve', additionalContext: reminder }));
|
|
1594
|
+
} catch { /* don't break on errors */ }
|
|
1595
|
+
process.exit(0);
|
|
1596
|
+
});
|
|
1177
1597
|
`, { mode: 0o755 });
|
|
1178
1598
|
// External communication guard — ensures identity grounding before external posting.
|
|
1179
1599
|
// PreToolUse hook for Bash. Detects commands that post to external platforms.
|
|
@@ -1525,6 +1945,11 @@ function installClaudeSettings(projectDir) {
|
|
|
1525
1945
|
command: 'node .instar/hooks/external-communication-guard.js',
|
|
1526
1946
|
timeout: 5000,
|
|
1527
1947
|
},
|
|
1948
|
+
{
|
|
1949
|
+
type: 'command',
|
|
1950
|
+
command: 'node .instar/hooks/post-action-reflection.js',
|
|
1951
|
+
timeout: 5000,
|
|
1952
|
+
},
|
|
1528
1953
|
];
|
|
1529
1954
|
// PreToolUse: merge instar hooks into existing or create fresh
|
|
1530
1955
|
if (!hooks.PreToolUse) {
|
|
@@ -396,6 +396,42 @@ Instar has a built-in feedback loop that connects all agents to the development
|
|
|
396
396
|
|
|
397
397
|
**User feedback matters too.** When your user says "this isn't working" or "I wish I could..." — that's feedback. Categorize it and submit it the same way.
|
|
398
398
|
|
|
399
|
+
### Evolution System
|
|
400
|
+
|
|
401
|
+
You have a built-in evolution system with four subsystems. This is not a metaphor — it's infrastructure that tracks your growth.
|
|
402
|
+
|
|
403
|
+
**Evolution Queue** — Staged self-improvement proposals.
|
|
404
|
+
- View: \`curl http://localhost:${port}/evolution/proposals\`
|
|
405
|
+
- Propose: \`/evolve\` skill or \`POST /evolution/proposals\`
|
|
406
|
+
- The \`evolution-review\` job evaluates and implements proposals every 6 hours.
|
|
407
|
+
|
|
408
|
+
**Learning Registry** — Structured, searchable insights.
|
|
409
|
+
- View: \`curl http://localhost:${port}/evolution/learnings\`
|
|
410
|
+
- Record: \`/learn\` skill or \`POST /evolution/learnings\`
|
|
411
|
+
- The \`insight-harvest\` job synthesizes patterns into proposals every 8 hours.
|
|
412
|
+
|
|
413
|
+
**Capability Gaps** — Track what you're missing.
|
|
414
|
+
- View: \`curl http://localhost:${port}/evolution/gaps\`
|
|
415
|
+
- Report: \`/gaps\` skill or \`POST /evolution/gaps\`
|
|
416
|
+
|
|
417
|
+
**Action Queue** — Commitments with follow-through tracking.
|
|
418
|
+
- View: \`curl http://localhost:${port}/evolution/actions\`
|
|
419
|
+
- Create: \`/commit-action\` skill or \`POST /evolution/actions\`
|
|
420
|
+
- The \`commitment-check\` job surfaces overdue items every 4 hours.
|
|
421
|
+
|
|
422
|
+
**Dashboard** — Full evolution health at a glance:
|
|
423
|
+
\`\`\`bash
|
|
424
|
+
curl http://localhost:${port}/evolution
|
|
425
|
+
\`\`\`
|
|
426
|
+
|
|
427
|
+
**Skills for evolution:**
|
|
428
|
+
- \`/evolve\` — Propose an improvement
|
|
429
|
+
- \`/learn\` — Record an insight
|
|
430
|
+
- \`/gaps\` — Report a missing capability
|
|
431
|
+
- \`/commit-action\` — Track a commitment
|
|
432
|
+
|
|
433
|
+
**The principle:** Evolution is not a separate activity from work. Every task is an opportunity to notice what could be better. The post-action reflection hook reminds you to pause after significant actions (commits, deploys) and consider what you learned. Most learning is lost because nobody paused to ask.
|
|
434
|
+
|
|
399
435
|
### Self-Evolution
|
|
400
436
|
|
|
401
437
|
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.
|
package/package.json
CHANGED
|
@@ -0,0 +1,377 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: credential-leak-detector
|
|
3
|
+
description: PostToolUse hook that scans Bash tool output for leaked credentials — API keys, tokens, private keys, and secrets — before they reach the conversation. Blocks critical leaks, redacts high-severity matches, and warns on suspicious patterns. 14 detection patterns covering OpenAI, Anthropic, AWS, GitHub, Stripe, Google, Slack, SendGrid, Twilio, PEM keys, bearer tokens, and generic secrets. No external dependencies. Trigger words: security, credential leak, secret exposure, key detection, token scan, API key leaked, credential guard, secret scanner, prevent credential leak.
|
|
4
|
+
license: MIT
|
|
5
|
+
metadata:
|
|
6
|
+
author: sagemindai
|
|
7
|
+
version: "1.0"
|
|
8
|
+
homepage: https://instar.sh
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
# credential-leak-detector — Catch Leaked Credentials Before They Spread
|
|
12
|
+
|
|
13
|
+
Every time your agent runs a Bash command, the output flows back into the conversation — and into your API provider's logs, any monitoring tools, and the model's context window. If that output contains an API key, a private key, or a database password, the credential is now exposed in places you never intended.
|
|
14
|
+
|
|
15
|
+
This hook scans every Bash tool response for 14 credential patterns before the output reaches the agent. Critical matches (API keys, AWS credentials, private keys) get blocked entirely. High-severity matches get redacted with a warning. Suspicious patterns get flagged as advisories. No external dependencies — just Python stdlib.
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## What Gets Detected
|
|
20
|
+
|
|
21
|
+
### Critical (Blocks the response)
|
|
22
|
+
|
|
23
|
+
| Pattern | Example Match |
|
|
24
|
+
|---------|--------------|
|
|
25
|
+
| OpenAI API keys | `sk-proj-abc123...` |
|
|
26
|
+
| Anthropic API keys | `sk-ant-api03-...` |
|
|
27
|
+
| AWS access keys | `AKIA1234567890ABCDEF` |
|
|
28
|
+
| GitHub tokens (classic) | `ghp_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx` |
|
|
29
|
+
| GitHub fine-grained PATs | `github_pat_xxxxxx...` |
|
|
30
|
+
| Stripe secret keys | `sk_live_xxxx...xxxx` |
|
|
31
|
+
| PEM private keys | `-----BEGIN RSA PRIVATE KEY-----` |
|
|
32
|
+
|
|
33
|
+
### High (Blocks or redacts with warning)
|
|
34
|
+
|
|
35
|
+
| Pattern | Action |
|
|
36
|
+
|---------|--------|
|
|
37
|
+
| Google API keys | Block |
|
|
38
|
+
| Slack tokens | Block |
|
|
39
|
+
| SendGrid API keys | Block |
|
|
40
|
+
| Twilio auth keys | Redact + warn |
|
|
41
|
+
| Bearer auth tokens | Redact + warn |
|
|
42
|
+
|
|
43
|
+
### Medium (Advisory warning)
|
|
44
|
+
|
|
45
|
+
| Pattern | Note |
|
|
46
|
+
|---------|------|
|
|
47
|
+
| Generic `password=`, `secret=`, `api_key=` assignments | Common in config output |
|
|
48
|
+
| 64-character hex strings | Possible SHA-256 hashes or keys |
|
|
49
|
+
|
|
50
|
+
---
|
|
51
|
+
|
|
52
|
+
## Installation
|
|
53
|
+
|
|
54
|
+
### Step 1: Create the hook script
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
mkdir -p .claude/hooks
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Create `.claude/hooks/credential-leak-detector.py` with the contents below.
|
|
61
|
+
|
|
62
|
+
---
|
|
63
|
+
|
|
64
|
+
## The Script
|
|
65
|
+
|
|
66
|
+
Save this as `.claude/hooks/credential-leak-detector.py`:
|
|
67
|
+
|
|
68
|
+
```python
|
|
69
|
+
#!/usr/bin/env python3
|
|
70
|
+
"""
|
|
71
|
+
credential-leak-detector.py — PostToolUse hook that scans Bash output
|
|
72
|
+
for leaked credentials. Blocks critical leaks, redacts high-severity
|
|
73
|
+
matches, warns on suspicious patterns.
|
|
74
|
+
|
|
75
|
+
Exit code 2 = block (critical credential found)
|
|
76
|
+
Exit code 0 = allow (clean or advisory-only)
|
|
77
|
+
"""
|
|
78
|
+
import sys
|
|
79
|
+
import json
|
|
80
|
+
import re
|
|
81
|
+
|
|
82
|
+
# --- Masking ---
|
|
83
|
+
|
|
84
|
+
def mask(value):
|
|
85
|
+
"""Mask a credential: first 4 + **** + last 4, or full mask if short."""
|
|
86
|
+
v = value.strip()
|
|
87
|
+
if len(v) < 12:
|
|
88
|
+
return "*" * len(v)
|
|
89
|
+
return v[:4] + "****" + v[-4:]
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
# --- Pattern Definitions ---
|
|
93
|
+
# (name, regex, severity, action)
|
|
94
|
+
# severity: critical, high, medium
|
|
95
|
+
# action: block, redact, warn
|
|
96
|
+
|
|
97
|
+
PATTERNS = [
|
|
98
|
+
# Critical — Block
|
|
99
|
+
("OpenAI API key",
|
|
100
|
+
r'(sk-(?:proj-)?[a-zA-Z0-9]{20,})',
|
|
101
|
+
"critical", "block"),
|
|
102
|
+
|
|
103
|
+
("Anthropic API key",
|
|
104
|
+
r'(sk-ant-api[a-zA-Z0-9_-]{90,})',
|
|
105
|
+
"critical", "block"),
|
|
106
|
+
|
|
107
|
+
("AWS access key",
|
|
108
|
+
r'(AKIA[0-9A-Z]{16})',
|
|
109
|
+
"critical", "block"),
|
|
110
|
+
|
|
111
|
+
("GitHub token (classic)",
|
|
112
|
+
r'(gh[pousr]_[A-Za-z0-9_]{36,})',
|
|
113
|
+
"critical", "block"),
|
|
114
|
+
|
|
115
|
+
("GitHub fine-grained PAT",
|
|
116
|
+
r'(github_pat_[a-zA-Z0-9]{22}_[a-zA-Z0-9]{59})',
|
|
117
|
+
"critical", "block"),
|
|
118
|
+
|
|
119
|
+
("Stripe secret key",
|
|
120
|
+
r'(sk_(?:live|test)_[a-zA-Z0-9]{24,})',
|
|
121
|
+
"critical", "block"),
|
|
122
|
+
|
|
123
|
+
("PEM private key",
|
|
124
|
+
r'(-----BEGIN (?:RSA |EC |DSA )?PRIVATE KEY-----)',
|
|
125
|
+
"critical", "block"),
|
|
126
|
+
|
|
127
|
+
# High — Block
|
|
128
|
+
("Google API key",
|
|
129
|
+
r'(AIza[0-9A-Za-z_-]{35})',
|
|
130
|
+
"high", "block"),
|
|
131
|
+
|
|
132
|
+
("Slack token",
|
|
133
|
+
r'(xox[bpors]-[0-9a-zA-Z-]{10,})',
|
|
134
|
+
"high", "block"),
|
|
135
|
+
|
|
136
|
+
("SendGrid API key",
|
|
137
|
+
r'(SG\.[a-zA-Z0-9_-]{22}\.[a-zA-Z0-9_-]{43})',
|
|
138
|
+
"high", "block"),
|
|
139
|
+
|
|
140
|
+
# High — Redact
|
|
141
|
+
("Twilio auth key",
|
|
142
|
+
r'(SK[0-9a-fA-F]{32})',
|
|
143
|
+
"high", "redact"),
|
|
144
|
+
|
|
145
|
+
("Bearer auth token",
|
|
146
|
+
r'(?:Authorization|Bearer)\s*[:=]\s*Bearer\s+([^\s]{20,})',
|
|
147
|
+
"high", "redact"),
|
|
148
|
+
|
|
149
|
+
# Medium — Warn
|
|
150
|
+
("Generic secret assignment",
|
|
151
|
+
r'(?:password|secret|token|api_key)\s*[=:]\s*[\'"]?([^\s\'"]{16,})',
|
|
152
|
+
"medium", "warn"),
|
|
153
|
+
|
|
154
|
+
("High-entropy hex string",
|
|
155
|
+
r'\b([a-fA-F0-9]{64})\b',
|
|
156
|
+
"medium", "warn"),
|
|
157
|
+
]
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
# --- Main ---
|
|
161
|
+
|
|
162
|
+
def scan(text):
|
|
163
|
+
"""Scan text for credential patterns. Returns list of findings."""
|
|
164
|
+
findings = []
|
|
165
|
+
for name, pattern, severity, action in PATTERNS:
|
|
166
|
+
matches = re.findall(pattern, text)
|
|
167
|
+
for m in matches:
|
|
168
|
+
findings.append({
|
|
169
|
+
"name": name,
|
|
170
|
+
"severity": severity,
|
|
171
|
+
"action": action,
|
|
172
|
+
"matched": m if isinstance(m, str) else m[0],
|
|
173
|
+
})
|
|
174
|
+
return findings
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
def main():
|
|
178
|
+
try:
|
|
179
|
+
payload = json.load(sys.stdin)
|
|
180
|
+
except Exception:
|
|
181
|
+
sys.exit(0)
|
|
182
|
+
|
|
183
|
+
tool_name = payload.get("tool_name", "")
|
|
184
|
+
tool_response = payload.get("tool_response", "")
|
|
185
|
+
|
|
186
|
+
# Only scan Bash output
|
|
187
|
+
if tool_name != "Bash":
|
|
188
|
+
sys.exit(0)
|
|
189
|
+
|
|
190
|
+
if not tool_response:
|
|
191
|
+
sys.exit(0)
|
|
192
|
+
|
|
193
|
+
# Handle tool_response as string or dict
|
|
194
|
+
if isinstance(tool_response, dict):
|
|
195
|
+
text = tool_response.get("stdout", "") + tool_response.get("stderr", "")
|
|
196
|
+
else:
|
|
197
|
+
text = str(tool_response)
|
|
198
|
+
|
|
199
|
+
if not text:
|
|
200
|
+
sys.exit(0)
|
|
201
|
+
|
|
202
|
+
findings = scan(text)
|
|
203
|
+
|
|
204
|
+
if not findings:
|
|
205
|
+
sys.exit(0)
|
|
206
|
+
|
|
207
|
+
# Classify findings
|
|
208
|
+
blockers = [f for f in findings if f["action"] == "block"]
|
|
209
|
+
redacts = [f for f in findings if f["action"] == "redact"]
|
|
210
|
+
warnings = [f for f in findings if f["action"] == "warn"]
|
|
211
|
+
|
|
212
|
+
# Critical/high blockers — stop the response
|
|
213
|
+
if blockers:
|
|
214
|
+
details = []
|
|
215
|
+
for f in blockers:
|
|
216
|
+
details.append(
|
|
217
|
+
f" - {f['name']} [{f['severity']}]: {mask(f['matched'])}"
|
|
218
|
+
)
|
|
219
|
+
reason = (
|
|
220
|
+
"[credential-leak-detector] Credential(s) detected in command output. "
|
|
221
|
+
"Response blocked to prevent exposure.\n\n"
|
|
222
|
+
"Detected:\n" + "\n".join(details) + "\n\n"
|
|
223
|
+
"The command output contained live credentials. Do NOT re-run this "
|
|
224
|
+
"command or attempt to extract these values. If you need to verify "
|
|
225
|
+
"a credential exists, check the env var or file without printing "
|
|
226
|
+
"its value."
|
|
227
|
+
)
|
|
228
|
+
print(json.dumps({"decision": "block", "reason": reason}))
|
|
229
|
+
sys.exit(2)
|
|
230
|
+
|
|
231
|
+
# Redact findings — allow but warn with masked values
|
|
232
|
+
messages = []
|
|
233
|
+
if redacts:
|
|
234
|
+
parts = []
|
|
235
|
+
for f in redacts:
|
|
236
|
+
parts.append(f" - {f['name']}: {mask(f['matched'])}")
|
|
237
|
+
messages.append(
|
|
238
|
+
"[credential-leak-detector] Possible credential(s) in output "
|
|
239
|
+
"(redact-level):\n" + "\n".join(parts) + "\n"
|
|
240
|
+
"Avoid storing, logging, or repeating these values."
|
|
241
|
+
)
|
|
242
|
+
|
|
243
|
+
# Warn findings — advisory only
|
|
244
|
+
if warnings:
|
|
245
|
+
parts = []
|
|
246
|
+
for f in warnings:
|
|
247
|
+
parts.append(f" - {f['name']}: {mask(f['matched'])}")
|
|
248
|
+
messages.append(
|
|
249
|
+
"[credential-leak-detector] Suspicious pattern(s) in output "
|
|
250
|
+
"(advisory):\n" + "\n".join(parts) + "\n"
|
|
251
|
+
"These may be secrets. Avoid including them in commits, logs, "
|
|
252
|
+
"or messages."
|
|
253
|
+
)
|
|
254
|
+
|
|
255
|
+
if messages:
|
|
256
|
+
print(json.dumps({"additionalContext": "\n".join(messages)}))
|
|
257
|
+
|
|
258
|
+
sys.exit(0)
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
if __name__ == "__main__":
|
|
262
|
+
main()
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
Make it executable:
|
|
266
|
+
|
|
267
|
+
```bash
|
|
268
|
+
chmod +x .claude/hooks/credential-leak-detector.py
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
---
|
|
272
|
+
|
|
273
|
+
### Step 2: Register the hook in .claude/settings.json
|
|
274
|
+
|
|
275
|
+
If `.claude/settings.json` doesn't exist, create it. If it does, add to the `hooks` section:
|
|
276
|
+
|
|
277
|
+
```json
|
|
278
|
+
{
|
|
279
|
+
"hooks": {
|
|
280
|
+
"PostToolUse": [
|
|
281
|
+
{
|
|
282
|
+
"matcher": "Bash",
|
|
283
|
+
"hooks": [
|
|
284
|
+
{
|
|
285
|
+
"type": "command",
|
|
286
|
+
"command": "python3 .claude/hooks/credential-leak-detector.py"
|
|
287
|
+
}
|
|
288
|
+
]
|
|
289
|
+
}
|
|
290
|
+
]
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
If you already have a `PostToolUse` array, add the matcher object to it.
|
|
296
|
+
|
|
297
|
+
---
|
|
298
|
+
|
|
299
|
+
### Step 3: Verify it works
|
|
300
|
+
|
|
301
|
+
Restart Claude Code, then run a command that would expose a credential:
|
|
302
|
+
|
|
303
|
+
```bash
|
|
304
|
+
echo "sk-ant-api03-test1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrst"
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
The hook should block the response and show a masked version of the detected key.
|
|
308
|
+
|
|
309
|
+
---
|
|
310
|
+
|
|
311
|
+
## Customizing Patterns
|
|
312
|
+
|
|
313
|
+
Edit the `PATTERNS` list in `credential-leak-detector.py` to add project-specific rules.
|
|
314
|
+
|
|
315
|
+
**Example: Detect your internal API keys**
|
|
316
|
+
|
|
317
|
+
```python
|
|
318
|
+
("Internal service key",
|
|
319
|
+
r'(myco_sk_[a-zA-Z0-9]{32,})',
|
|
320
|
+
"critical", "block"),
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
**Example: Detect database connection strings**
|
|
324
|
+
|
|
325
|
+
```python
|
|
326
|
+
("Database connection string",
|
|
327
|
+
r'((?:postgres|mysql|mongodb)://[^\s]{20,})',
|
|
328
|
+
"high", "block"),
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
**Example: Downgrade a pattern to warn-only**
|
|
332
|
+
|
|
333
|
+
Change the action from `"block"` to `"warn"` for patterns that produce too many false positives in your environment:
|
|
334
|
+
|
|
335
|
+
```python
|
|
336
|
+
# Change high-entropy hex from warn to ignored by removing it,
|
|
337
|
+
# or keep it but change action:
|
|
338
|
+
("High-entropy hex string",
|
|
339
|
+
r'\b([a-fA-F0-9]{64})\b',
|
|
340
|
+
"medium", "warn"), # or remove this entry entirely
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
---
|
|
344
|
+
|
|
345
|
+
## How It Differs from command-guard
|
|
346
|
+
|
|
347
|
+
The **command-guard** skill is a PreToolUse hook — it blocks dangerous *commands* before they execute. This skill is a PostToolUse hook — it scans *output* after commands run. They complement each other:
|
|
348
|
+
|
|
349
|
+
| Layer | Hook | What It Catches |
|
|
350
|
+
|-------|------|----------------|
|
|
351
|
+
| command-guard | PreToolUse | `cat .env`, `echo $SECRET`, destructive commands |
|
|
352
|
+
| credential-leak-detector | PostToolUse | Credentials that appear in output from any command |
|
|
353
|
+
|
|
354
|
+
A command like `docker inspect` or `kubectl describe` might not look dangerous, but its output can contain mounted secrets. command-guard wouldn't catch it. This hook does.
|
|
355
|
+
|
|
356
|
+
---
|
|
357
|
+
|
|
358
|
+
## Going Further
|
|
359
|
+
|
|
360
|
+
This hook catches credentials after they appear in output. But in an autonomous session — a scheduled job running at 3 AM, a background worker processing a queue — "catching" means the block fires, the agent sees the message, and... then what? If nobody's watching, the agent might retry the command, work around the block, or simply stall.
|
|
361
|
+
|
|
362
|
+
**Instar turns detection into infrastructure.** Every hook event across every session gets audit-logged with timestamps, session IDs, and the masked credential type. If a credential-leak-detector block fires during a 2 AM job, it shows up in the morning's activity feed — you see exactly which command triggered it, in which session, and what the agent did next. The Telegram integration means you can get notified the moment a block fires, not hours later.
|
|
363
|
+
|
|
364
|
+
And credential scanning is just one layer. With Instar, your agent also gets:
|
|
365
|
+
- **Audit logging** — every tool call, every hook event, searchable and timestamped
|
|
366
|
+
- **Identity grounding** — hooks that fire before external messages, so the agent stays in character
|
|
367
|
+
- **A job scheduler** — recurring tasks on cron, running while you sleep
|
|
368
|
+
- **Background sessions** — parallel workers for deep tasks
|
|
369
|
+
- **Telegram integration** — real-time notifications and two-way control from your phone
|
|
370
|
+
|
|
371
|
+
One command, about 2 minutes:
|
|
372
|
+
|
|
373
|
+
```bash
|
|
374
|
+
npx instar
|
|
375
|
+
```
|
|
376
|
+
|
|
377
|
+
Your agent goes from guarded-when-you're-watching to safe-autonomously. [instar.sh](https://instar.sh)
|