@rozek/nanoclaw 0.0.5 → 0.0.7

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 (132) hide show
  1. package/container/agent-runner/package-lock.json +1524 -0
  2. package/dist/cli.js +41 -4
  3. package/dist/cli.js.map +1 -1
  4. package/dist/index.d.ts.map +1 -1
  5. package/dist/index.js +34 -0
  6. package/dist/index.js.map +1 -1
  7. package/package.json +7 -1
  8. package/.claude/settings.json +0 -1
  9. package/.claude/skills/add-compact/SKILL.md +0 -135
  10. package/.claude/skills/add-discord/SKILL.md +0 -203
  11. package/.claude/skills/add-gmail/SKILL.md +0 -220
  12. package/.claude/skills/add-image-vision/SKILL.md +0 -94
  13. package/.claude/skills/add-ollama-tool/SKILL.md +0 -153
  14. package/.claude/skills/add-parallel/SKILL.md +0 -290
  15. package/.claude/skills/add-pdf-reader/SKILL.md +0 -104
  16. package/.claude/skills/add-reactions/SKILL.md +0 -117
  17. package/.claude/skills/add-slack/SKILL.md +0 -207
  18. package/.claude/skills/add-telegram/SKILL.md +0 -222
  19. package/.claude/skills/add-telegram-swarm/SKILL.md +0 -384
  20. package/.claude/skills/add-voice-transcription/SKILL.md +0 -148
  21. package/.claude/skills/add-whatsapp/SKILL.md +0 -372
  22. package/.claude/skills/convert-to-apple-container/SKILL.md +0 -175
  23. package/.claude/skills/customize/SKILL.md +0 -110
  24. package/.claude/skills/debug/SKILL.md +0 -349
  25. package/.claude/skills/get-qodo-rules/SKILL.md +0 -122
  26. package/.claude/skills/get-qodo-rules/references/output-format.md +0 -41
  27. package/.claude/skills/get-qodo-rules/references/pagination.md +0 -33
  28. package/.claude/skills/get-qodo-rules/references/repository-scope.md +0 -26
  29. package/.claude/skills/qodo-pr-resolver/SKILL.md +0 -326
  30. package/.claude/skills/qodo-pr-resolver/resources/providers.md +0 -329
  31. package/.claude/skills/setup/SKILL.md +0 -218
  32. package/.claude/skills/update-nanoclaw/SKILL.md +0 -235
  33. package/.claude/skills/update-skills/SKILL.md +0 -130
  34. package/.claude/skills/use-local-whisper/SKILL.md +0 -152
  35. package/.claude/skills/x-integration/SKILL.md +0 -417
  36. package/.claude/skills/x-integration/agent.ts +0 -243
  37. package/.claude/skills/x-integration/host.ts +0 -159
  38. package/.claude/skills/x-integration/lib/browser.ts +0 -148
  39. package/.claude/skills/x-integration/lib/config.ts +0 -62
  40. package/.claude/skills/x-integration/scripts/like.ts +0 -56
  41. package/.claude/skills/x-integration/scripts/post.ts +0 -66
  42. package/.claude/skills/x-integration/scripts/quote.ts +0 -80
  43. package/.claude/skills/x-integration/scripts/reply.ts +0 -74
  44. package/.claude/skills/x-integration/scripts/retweet.ts +0 -62
  45. package/.claude/skills/x-integration/scripts/setup.ts +0 -87
  46. package/.env.example +0 -1
  47. package/.github/CODEOWNERS +0 -10
  48. package/.github/PULL_REQUEST_TEMPLATE.md +0 -14
  49. package/.github/workflows/bump-version.yml +0 -32
  50. package/.github/workflows/ci.yml +0 -25
  51. package/.github/workflows/merge-forward-skills.yml +0 -160
  52. package/.github/workflows/update-tokens.yml +0 -42
  53. package/.husky/pre-commit +0 -1
  54. package/.mcp.json +0 -3
  55. package/.nvmrc +0 -1
  56. package/.prettierrc +0 -3
  57. package/CHANGELOG.md +0 -8
  58. package/CONTRIBUTING.md +0 -23
  59. package/CONTRIBUTORS.md +0 -15
  60. package/NanoClaw_with_Web-Support.md +0 -326
  61. package/README_zh.md +0 -200
  62. package/assets/nanoclaw-favicon.png +0 -0
  63. package/assets/nanoclaw-icon.png +0 -0
  64. package/assets/nanoclaw-logo-dark.png +0 -0
  65. package/assets/nanoclaw-logo.png +0 -0
  66. package/assets/nanoclaw-profile.jpeg +0 -0
  67. package/assets/nanoclaw-sales.png +0 -0
  68. package/assets/social-preview.jpg +0 -0
  69. package/config-examples/mount-allowlist.json +0 -25
  70. package/docs/APPLE-CONTAINER-NETWORKING.md +0 -90
  71. package/docs/DEBUG_CHECKLIST.md +0 -143
  72. package/docs/REQUIREMENTS.md +0 -196
  73. package/docs/SDK_DEEP_DIVE.md +0 -643
  74. package/docs/SECURITY.md +0 -122
  75. package/docs/SPEC.md +0 -785
  76. package/docs/docker-sandboxes.md +0 -359
  77. package/docs/nanoclaw-architecture-final.md +0 -1063
  78. package/docs/nanorepo-architecture.md +0 -168
  79. package/docs/skills-as-branches.md +0 -662
  80. package/groups/global/CLAUDE.md +0 -58
  81. package/groups/main/CLAUDE.md +0 -246
  82. package/launchd/com.nanoclaw.plist +0 -32
  83. package/repo-tokens/README.md +0 -113
  84. package/repo-tokens/action.yml +0 -186
  85. package/repo-tokens/badge.svg +0 -23
  86. package/repo-tokens/examples/green.svg +0 -14
  87. package/repo-tokens/examples/red.svg +0 -14
  88. package/repo-tokens/examples/yellow-green.svg +0 -14
  89. package/repo-tokens/examples/yellow.svg +0 -14
  90. package/scripts/run-migrations.ts +0 -105
  91. package/setup.sh +0 -161
  92. package/src/channels/index.ts +0 -15
  93. package/src/channels/registry.test.ts +0 -42
  94. package/src/channels/registry.ts +0 -32
  95. package/src/channels/web.ts +0 -1931
  96. package/src/cli.ts +0 -254
  97. package/src/config.ts +0 -73
  98. package/src/container-runner.test.ts +0 -210
  99. package/src/container-runner.ts +0 -768
  100. package/src/container-runtime.test.ts +0 -149
  101. package/src/container-runtime.ts +0 -127
  102. package/src/credential-proxy.test.ts +0 -192
  103. package/src/credential-proxy.ts +0 -125
  104. package/src/db.test.ts +0 -484
  105. package/src/db.ts +0 -803
  106. package/src/env.ts +0 -42
  107. package/src/formatting.test.ts +0 -256
  108. package/src/group-folder.test.ts +0 -43
  109. package/src/group-folder.ts +0 -44
  110. package/src/group-queue.test.ts +0 -484
  111. package/src/group-queue.ts +0 -379
  112. package/src/index.ts +0 -854
  113. package/src/ipc-auth.test.ts +0 -679
  114. package/src/ipc.ts +0 -461
  115. package/src/logger.ts +0 -16
  116. package/src/mount-security.ts +0 -419
  117. package/src/remote-control.test.ts +0 -397
  118. package/src/remote-control.ts +0 -224
  119. package/src/router.ts +0 -52
  120. package/src/routing.test.ts +0 -170
  121. package/src/sender-allowlist.test.ts +0 -216
  122. package/src/sender-allowlist.ts +0 -128
  123. package/src/session-commands.test.ts +0 -247
  124. package/src/session-commands.ts +0 -163
  125. package/src/task-scheduler.test.ts +0 -129
  126. package/src/task-scheduler.ts +0 -328
  127. package/src/timezone.test.ts +0 -29
  128. package/src/timezone.ts +0 -16
  129. package/src/types.ts +0 -109
  130. package/tsconfig.json +0 -20
  131. package/vitest.config.ts +0 -7
  132. package/vitest.skills.config.ts +0 -7
@@ -1,66 +0,0 @@
1
- #!/usr/bin/env npx tsx
2
- /**
3
- * X Integration - Post Tweet
4
- * Usage: echo '{"content":"Hello world"}' | npx tsx post.ts
5
- */
6
-
7
- import { getBrowserContext, runScript, validateContent, config, ScriptResult } from '../lib/browser.js';
8
-
9
- interface PostInput {
10
- content: string;
11
- }
12
-
13
- async function postTweet(input: PostInput): Promise<ScriptResult> {
14
- const { content } = input;
15
-
16
- const validationError = validateContent(content, 'Tweet');
17
- if (validationError) return validationError;
18
-
19
- let context = null;
20
- try {
21
- context = await getBrowserContext();
22
- const page = context.pages()[0] || await context.newPage();
23
-
24
- await page.goto('https://x.com/home', { timeout: config.timeouts.navigation, waitUntil: 'domcontentloaded' });
25
- await page.waitForTimeout(config.timeouts.pageLoad);
26
-
27
- // Check if logged in
28
- const isLoggedIn = await page.locator('[data-testid="SideNav_AccountSwitcher_Button"]').isVisible().catch(() => false);
29
- if (!isLoggedIn) {
30
- const onLoginPage = await page.locator('input[autocomplete="username"]').isVisible().catch(() => false);
31
- if (onLoginPage) {
32
- return { success: false, message: 'X login expired. Run /x-integration to re-authenticate.' };
33
- }
34
- }
35
-
36
- // Find and fill tweet input
37
- const tweetInput = page.locator('[data-testid="tweetTextarea_0"]');
38
- await tweetInput.waitFor({ timeout: config.timeouts.elementWait * 2 });
39
- await tweetInput.click();
40
- await page.waitForTimeout(config.timeouts.afterClick / 2);
41
- await tweetInput.fill(content);
42
- await page.waitForTimeout(config.timeouts.afterFill);
43
-
44
- // Click post button
45
- const postButton = page.locator('[data-testid="tweetButtonInline"]');
46
- await postButton.waitFor({ timeout: config.timeouts.elementWait });
47
-
48
- const isDisabled = await postButton.getAttribute('aria-disabled');
49
- if (isDisabled === 'true') {
50
- return { success: false, message: 'Post button disabled. Content may be empty or exceed character limit.' };
51
- }
52
-
53
- await postButton.click();
54
- await page.waitForTimeout(config.timeouts.afterSubmit);
55
-
56
- return {
57
- success: true,
58
- message: `Tweet posted: ${content.slice(0, 50)}${content.length > 50 ? '...' : ''}`
59
- };
60
-
61
- } finally {
62
- if (context) await context.close();
63
- }
64
- }
65
-
66
- runScript<PostInput>(postTweet);
@@ -1,80 +0,0 @@
1
- #!/usr/bin/env npx tsx
2
- /**
3
- * X Integration - Quote Tweet
4
- * Usage: echo '{"tweetUrl":"https://x.com/user/status/123","comment":"My thoughts"}' | npx tsx quote.ts
5
- */
6
-
7
- import { getBrowserContext, navigateToTweet, runScript, validateContent, config, ScriptResult } from '../lib/browser.js';
8
-
9
- interface QuoteInput {
10
- tweetUrl: string;
11
- comment: string;
12
- }
13
-
14
- async function quoteTweet(input: QuoteInput): Promise<ScriptResult> {
15
- const { tweetUrl, comment } = input;
16
-
17
- if (!tweetUrl) {
18
- return { success: false, message: 'Please provide a tweet URL' };
19
- }
20
-
21
- const validationError = validateContent(comment, 'Comment');
22
- if (validationError) return validationError;
23
-
24
- let context = null;
25
- try {
26
- context = await getBrowserContext();
27
- const { page, success, error } = await navigateToTweet(context, tweetUrl);
28
-
29
- if (!success) {
30
- return { success: false, message: error || 'Navigation failed' };
31
- }
32
-
33
- // Click retweet button to open menu
34
- const tweet = page.locator('article[data-testid="tweet"]').first();
35
- const retweetButton = tweet.locator('[data-testid="retweet"]');
36
- await retweetButton.waitFor({ timeout: config.timeouts.elementWait });
37
- await retweetButton.click();
38
- await page.waitForTimeout(config.timeouts.afterClick);
39
-
40
- // Click quote option
41
- const quoteOption = page.getByRole('menuitem').filter({ hasText: /Quote/i });
42
- await quoteOption.waitFor({ timeout: config.timeouts.elementWait });
43
- await quoteOption.click();
44
- await page.waitForTimeout(config.timeouts.afterClick * 1.5);
45
-
46
- // Find dialog with aria-modal="true"
47
- const dialog = page.locator('[role="dialog"][aria-modal="true"]');
48
- await dialog.waitFor({ timeout: config.timeouts.elementWait });
49
-
50
- // Fill comment
51
- const quoteInput = dialog.locator('[data-testid="tweetTextarea_0"]');
52
- await quoteInput.waitFor({ timeout: config.timeouts.elementWait });
53
- await quoteInput.click();
54
- await page.waitForTimeout(config.timeouts.afterClick / 2);
55
- await quoteInput.fill(comment);
56
- await page.waitForTimeout(config.timeouts.afterFill);
57
-
58
- // Click submit button
59
- const submitButton = dialog.locator('[data-testid="tweetButton"]');
60
- await submitButton.waitFor({ timeout: config.timeouts.elementWait });
61
-
62
- const isDisabled = await submitButton.getAttribute('aria-disabled');
63
- if (isDisabled === 'true') {
64
- return { success: false, message: 'Submit button disabled. Content may be empty or exceed character limit.' };
65
- }
66
-
67
- await submitButton.click();
68
- await page.waitForTimeout(config.timeouts.afterSubmit);
69
-
70
- return {
71
- success: true,
72
- message: `Quote tweet posted: ${comment.slice(0, 50)}${comment.length > 50 ? '...' : ''}`
73
- };
74
-
75
- } finally {
76
- if (context) await context.close();
77
- }
78
- }
79
-
80
- runScript<QuoteInput>(quoteTweet);
@@ -1,74 +0,0 @@
1
- #!/usr/bin/env npx tsx
2
- /**
3
- * X Integration - Reply to Tweet
4
- * Usage: echo '{"tweetUrl":"https://x.com/user/status/123","content":"Great post!"}' | npx tsx reply.ts
5
- */
6
-
7
- import { getBrowserContext, navigateToTweet, runScript, validateContent, config, ScriptResult } from '../lib/browser.js';
8
-
9
- interface ReplyInput {
10
- tweetUrl: string;
11
- content: string;
12
- }
13
-
14
- async function replyToTweet(input: ReplyInput): Promise<ScriptResult> {
15
- const { tweetUrl, content } = input;
16
-
17
- if (!tweetUrl) {
18
- return { success: false, message: 'Please provide a tweet URL' };
19
- }
20
-
21
- const validationError = validateContent(content, 'Reply');
22
- if (validationError) return validationError;
23
-
24
- let context = null;
25
- try {
26
- context = await getBrowserContext();
27
- const { page, success, error } = await navigateToTweet(context, tweetUrl);
28
-
29
- if (!success) {
30
- return { success: false, message: error || 'Navigation failed' };
31
- }
32
-
33
- // Click reply button
34
- const tweet = page.locator('article[data-testid="tweet"]').first();
35
- const replyButton = tweet.locator('[data-testid="reply"]');
36
- await replyButton.waitFor({ timeout: config.timeouts.elementWait });
37
- await replyButton.click();
38
- await page.waitForTimeout(config.timeouts.afterClick * 1.5);
39
-
40
- // Find dialog with aria-modal="true" to avoid matching other dialogs
41
- const dialog = page.locator('[role="dialog"][aria-modal="true"]');
42
- await dialog.waitFor({ timeout: config.timeouts.elementWait });
43
-
44
- // Fill reply content
45
- const replyInput = dialog.locator('[data-testid="tweetTextarea_0"]');
46
- await replyInput.waitFor({ timeout: config.timeouts.elementWait });
47
- await replyInput.click();
48
- await page.waitForTimeout(config.timeouts.afterClick / 2);
49
- await replyInput.fill(content);
50
- await page.waitForTimeout(config.timeouts.afterFill);
51
-
52
- // Click submit button
53
- const submitButton = dialog.locator('[data-testid="tweetButton"]');
54
- await submitButton.waitFor({ timeout: config.timeouts.elementWait });
55
-
56
- const isDisabled = await submitButton.getAttribute('aria-disabled');
57
- if (isDisabled === 'true') {
58
- return { success: false, message: 'Submit button disabled. Content may be empty or exceed character limit.' };
59
- }
60
-
61
- await submitButton.click();
62
- await page.waitForTimeout(config.timeouts.afterSubmit);
63
-
64
- return {
65
- success: true,
66
- message: `Reply posted: ${content.slice(0, 50)}${content.length > 50 ? '...' : ''}`
67
- };
68
-
69
- } finally {
70
- if (context) await context.close();
71
- }
72
- }
73
-
74
- runScript<ReplyInput>(replyToTweet);
@@ -1,62 +0,0 @@
1
- #!/usr/bin/env npx tsx
2
- /**
3
- * X Integration - Retweet
4
- * Usage: echo '{"tweetUrl":"https://x.com/user/status/123"}' | npx tsx retweet.ts
5
- */
6
-
7
- import { getBrowserContext, navigateToTweet, runScript, config, ScriptResult } from '../lib/browser.js';
8
-
9
- interface RetweetInput {
10
- tweetUrl: string;
11
- }
12
-
13
- async function retweet(input: RetweetInput): Promise<ScriptResult> {
14
- const { tweetUrl } = input;
15
-
16
- if (!tweetUrl) {
17
- return { success: false, message: 'Please provide a tweet URL' };
18
- }
19
-
20
- let context = null;
21
- try {
22
- context = await getBrowserContext();
23
- const { page, success, error } = await navigateToTweet(context, tweetUrl);
24
-
25
- if (!success) {
26
- return { success: false, message: error || 'Navigation failed' };
27
- }
28
-
29
- const tweet = page.locator('article[data-testid="tweet"]').first();
30
- const unretweetButton = tweet.locator('[data-testid="unretweet"]');
31
- const retweetButton = tweet.locator('[data-testid="retweet"]');
32
-
33
- // Check if already retweeted
34
- const alreadyRetweeted = await unretweetButton.isVisible().catch(() => false);
35
- if (alreadyRetweeted) {
36
- return { success: true, message: 'Tweet already retweeted' };
37
- }
38
-
39
- await retweetButton.waitFor({ timeout: config.timeouts.elementWait });
40
- await retweetButton.click();
41
- await page.waitForTimeout(config.timeouts.afterClick);
42
-
43
- // Click retweet confirm option
44
- const retweetConfirm = page.locator('[data-testid="retweetConfirm"]');
45
- await retweetConfirm.waitFor({ timeout: config.timeouts.elementWait });
46
- await retweetConfirm.click();
47
- await page.waitForTimeout(config.timeouts.afterClick * 2);
48
-
49
- // Verify
50
- const nowRetweeted = await unretweetButton.isVisible().catch(() => false);
51
- if (nowRetweeted) {
52
- return { success: true, message: 'Retweet successful' };
53
- }
54
-
55
- return { success: false, message: 'Retweet action completed but could not verify success' };
56
-
57
- } finally {
58
- if (context) await context.close();
59
- }
60
- }
61
-
62
- runScript<RetweetInput>(retweet);
@@ -1,87 +0,0 @@
1
- #!/usr/bin/env npx tsx
2
- /**
3
- * X Integration - Authentication Setup
4
- * Usage: npx tsx setup.ts
5
- *
6
- * Interactive script - opens browser for manual login
7
- */
8
-
9
- import { chromium } from 'playwright';
10
- import * as readline from 'readline';
11
- import fs from 'fs';
12
- import path from 'path';
13
- import { config, cleanupLockFiles } from '../lib/browser.js';
14
-
15
- async function setup(): Promise<void> {
16
- console.log('=== X (Twitter) Authentication Setup ===\n');
17
- console.log('This will open Chrome for you to log in to X.');
18
- console.log('Your login session will be saved for automated interactions.\n');
19
- console.log(`Chrome path: ${config.chromePath}`);
20
- console.log(`Profile dir: ${config.browserDataDir}\n`);
21
-
22
- // Ensure directories exist
23
- fs.mkdirSync(path.dirname(config.authPath), { recursive: true });
24
- fs.mkdirSync(config.browserDataDir, { recursive: true });
25
-
26
- cleanupLockFiles();
27
-
28
- console.log('Launching browser...\n');
29
-
30
- const context = await chromium.launchPersistentContext(config.browserDataDir, {
31
- executablePath: config.chromePath,
32
- headless: false,
33
- viewport: config.viewport,
34
- args: config.chromeArgs.slice(0, 3), // Use first 3 args for setup (less restrictive)
35
- ignoreDefaultArgs: config.chromeIgnoreDefaultArgs,
36
- });
37
-
38
- const page = context.pages()[0] || await context.newPage();
39
-
40
- // Navigate to login page
41
- await page.goto('https://x.com/login');
42
-
43
- console.log('Please log in to X in the browser window.');
44
- console.log('After you see your home feed, come back here and press Enter.\n');
45
-
46
- // Wait for user to complete login
47
- const rl = readline.createInterface({
48
- input: process.stdin,
49
- output: process.stdout
50
- });
51
-
52
- await new Promise<void>(resolve => {
53
- rl.question('Press Enter when logged in... ', () => {
54
- rl.close();
55
- resolve();
56
- });
57
- });
58
-
59
- // Verify login by navigating to home and checking for account button
60
- console.log('\nVerifying login status...');
61
- await page.goto('https://x.com/home');
62
- await page.waitForTimeout(config.timeouts.pageLoad);
63
-
64
- const isLoggedIn = await page.locator('[data-testid="SideNav_AccountSwitcher_Button"]').isVisible().catch(() => false);
65
-
66
- if (isLoggedIn) {
67
- // Save auth marker
68
- fs.writeFileSync(config.authPath, JSON.stringify({
69
- authenticated: true,
70
- timestamp: new Date().toISOString()
71
- }, null, 2));
72
-
73
- console.log('\n✅ Authentication successful!');
74
- console.log(`Session saved to: ${config.browserDataDir}`);
75
- console.log('\nYou can now use X integration features.');
76
- } else {
77
- console.log('\n❌ Could not verify login status.');
78
- console.log('Please try again and make sure you are logged in to X.');
79
- }
80
-
81
- await context.close();
82
- }
83
-
84
- setup().catch(err => {
85
- console.error('Setup failed:', err.message);
86
- process.exit(1);
87
- });
package/.env.example DELETED
@@ -1 +0,0 @@
1
-
@@ -1,10 +0,0 @@
1
- # Core code - maintainer only
2
- /src/ @gavrielc @gabi-simons
3
- /container/ @gavrielc @gabi-simons
4
- /groups/ @gavrielc @gabi-simons
5
- /launchd/ @gavrielc @gabi-simons
6
- /package.json @gavrielc @gabi-simons
7
- /package-lock.json @gavrielc @gabi-simons
8
-
9
- # Skills - open to contributors
10
- /.claude/skills/
@@ -1,14 +0,0 @@
1
- ## Type of Change
2
-
3
- - [ ] **Skill** - adds a new skill in `.claude/skills/`
4
- - [ ] **Fix** - bug fix or security fix to source code
5
- - [ ] **Simplification** - reduces or simplifies source code
6
-
7
- ## Description
8
-
9
-
10
- ## For Skills
11
-
12
- - [ ] I have not made any changes to source code
13
- - [ ] My skill contains instructions for Claude to follow (not pre-built code)
14
- - [ ] I tested this skill on a fresh clone
@@ -1,32 +0,0 @@
1
- name: Bump version
2
-
3
- on:
4
- push:
5
- branches: [main]
6
- paths: ['src/**', 'container/**']
7
-
8
- jobs:
9
- bump-version:
10
- runs-on: ubuntu-latest
11
- steps:
12
- - uses: actions/create-github-app-token@v1
13
- id: app-token
14
- with:
15
- app-id: ${{ secrets.APP_ID }}
16
- private-key: ${{ secrets.APP_PRIVATE_KEY }}
17
-
18
- - uses: actions/checkout@v4
19
- with:
20
- token: ${{ steps.app-token.outputs.token }}
21
-
22
- - name: Bump patch version
23
- run: |
24
- npm version patch --no-git-tag-version
25
- git add package.json package-lock.json
26
- git diff --cached --quiet && exit 0
27
- git config user.name "github-actions[bot]"
28
- git config user.email "github-actions[bot]@users.noreply.github.com"
29
- VERSION=$(node -p "require('./package.json').version")
30
- git commit -m "chore: bump version to $VERSION"
31
- git pull --rebase
32
- git push
@@ -1,25 +0,0 @@
1
- name: CI
2
-
3
- on:
4
- pull_request:
5
- branches: [main]
6
-
7
- jobs:
8
- ci:
9
- runs-on: ubuntu-latest
10
- steps:
11
- - uses: actions/checkout@v4
12
- - uses: actions/setup-node@v4
13
- with:
14
- node-version: 20
15
- cache: npm
16
- - run: npm ci
17
-
18
- - name: Format check
19
- run: npm run format:check
20
-
21
- - name: Typecheck
22
- run: npx tsc --noEmit
23
-
24
- - name: Tests
25
- run: npx vitest run
@@ -1,160 +0,0 @@
1
- name: Merge-forward skill branches
2
-
3
- on:
4
- push:
5
- branches: [main]
6
-
7
- permissions:
8
- contents: write
9
- issues: write
10
-
11
- jobs:
12
- merge-forward:
13
- if: github.repository == 'qwibitai/nanoclaw'
14
- runs-on: ubuntu-latest
15
- steps:
16
- - uses: actions/checkout@v4
17
- with:
18
- fetch-depth: 0
19
- token: ${{ secrets.GITHUB_TOKEN }}
20
-
21
- - uses: actions/setup-node@v4
22
- with:
23
- node-version: 20
24
- cache: npm
25
-
26
- - name: Configure git
27
- run: |
28
- git config user.name "github-actions[bot]"
29
- git config user.email "github-actions[bot]@users.noreply.github.com"
30
-
31
- - name: Merge main into each skill branch
32
- id: merge
33
- run: |
34
- FAILED=""
35
- SUCCEEDED=""
36
-
37
- # List all remote skill branches
38
- SKILL_BRANCHES=$(git branch -r --list 'origin/skill/*' | sed 's|origin/||' | xargs)
39
-
40
- if [ -z "$SKILL_BRANCHES" ]; then
41
- echo "No skill branches found."
42
- exit 0
43
- fi
44
-
45
- for BRANCH in $SKILL_BRANCHES; do
46
- SKILL_NAME=$(echo "$BRANCH" | sed 's|skill/||')
47
- echo ""
48
- echo "=== Processing $BRANCH ==="
49
-
50
- # Checkout the skill branch
51
- git checkout -B "$BRANCH" "origin/$BRANCH"
52
-
53
- # Attempt merge
54
- if ! git merge main --no-edit; then
55
- echo "::warning::Merge conflict in $BRANCH"
56
- git merge --abort
57
- FAILED="$FAILED $SKILL_NAME"
58
- continue
59
- fi
60
-
61
- # Check if there's anything new to push
62
- if git diff --quiet "origin/$BRANCH"; then
63
- echo "$BRANCH is already up to date with main."
64
- SUCCEEDED="$SUCCEEDED $SKILL_NAME"
65
- continue
66
- fi
67
-
68
- # Install deps and validate
69
- npm ci
70
-
71
- if ! npm run build; then
72
- echo "::warning::Build failed for $BRANCH"
73
- git reset --hard "origin/$BRANCH"
74
- FAILED="$FAILED $SKILL_NAME"
75
- continue
76
- fi
77
-
78
- if ! npm test 2>/dev/null; then
79
- echo "::warning::Tests failed for $BRANCH"
80
- git reset --hard "origin/$BRANCH"
81
- FAILED="$FAILED $SKILL_NAME"
82
- continue
83
- fi
84
-
85
- # Push the updated branch
86
- git push origin "$BRANCH"
87
- SUCCEEDED="$SUCCEEDED $SKILL_NAME"
88
- echo "$BRANCH merged and pushed successfully."
89
- done
90
-
91
- echo ""
92
- echo "=== Results ==="
93
- echo "Succeeded: $SUCCEEDED"
94
- echo "Failed: $FAILED"
95
-
96
- # Export for issue creation
97
- echo "failed=$FAILED" >> "$GITHUB_OUTPUT"
98
- echo "succeeded=$SUCCEEDED" >> "$GITHUB_OUTPUT"
99
-
100
- - name: Open issue for failed merges
101
- if: steps.merge.outputs.failed != ''
102
- uses: actions/github-script@v7
103
- with:
104
- script: |
105
- const failed = '${{ steps.merge.outputs.failed }}'.trim().split(/\s+/);
106
- const sha = context.sha.substring(0, 7);
107
- const body = [
108
- `The merge-forward workflow failed to merge \`main\` (${sha}) into the following skill branches:`,
109
- '',
110
- ...failed.map(s => `- \`skill/${s}\`: merge conflict, build failure, or test failure`),
111
- '',
112
- 'Please resolve manually:',
113
- '```bash',
114
- ...failed.map(s => [
115
- `git checkout skill/${s}`,
116
- `git merge main`,
117
- `# resolve conflicts, then: git push`,
118
- ''
119
- ]).flat(),
120
- '```',
121
- '',
122
- `Triggered by push to main: ${context.sha}`
123
- ].join('\n');
124
-
125
- await github.rest.issues.create({
126
- owner: context.repo.owner,
127
- repo: context.repo.repo,
128
- title: `Merge-forward failed for ${failed.length} skill branch(es) after ${sha}`,
129
- body,
130
- labels: ['skill-maintenance']
131
- });
132
-
133
- - name: Notify channel forks
134
- if: always()
135
- uses: actions/github-script@v7
136
- with:
137
- github-token: ${{ secrets.FORK_DISPATCH_TOKEN || secrets.GITHUB_TOKEN }}
138
- script: |
139
- const forks = [
140
- 'nanoclaw-whatsapp',
141
- 'nanoclaw-telegram',
142
- 'nanoclaw-discord',
143
- 'nanoclaw-slack',
144
- 'nanoclaw-gmail',
145
- 'nanoclaw-docker-sandboxes',
146
- ];
147
- const sha = context.sha.substring(0, 7);
148
- for (const repo of forks) {
149
- try {
150
- await github.rest.repos.createDispatchEvent({
151
- owner: 'qwibitai',
152
- repo,
153
- event_type: 'upstream-main-updated',
154
- client_payload: { sha: context.sha },
155
- });
156
- console.log(`Notified ${repo}`);
157
- } catch (e) {
158
- console.log(`Failed to notify ${repo}: ${e.message}`);
159
- }
160
- }
@@ -1,42 +0,0 @@
1
- name: Update token count
2
-
3
- on:
4
- workflow_dispatch:
5
- push:
6
- branches: [main]
7
- paths: ['src/**', 'container/**', 'launchd/**', 'CLAUDE.md']
8
-
9
- jobs:
10
- update-tokens:
11
- runs-on: ubuntu-latest
12
- steps:
13
- - uses: actions/create-github-app-token@v1
14
- id: app-token
15
- with:
16
- app-id: ${{ secrets.APP_ID }}
17
- private-key: ${{ secrets.APP_PRIVATE_KEY }}
18
-
19
- - uses: actions/checkout@v4
20
- with:
21
- token: ${{ steps.app-token.outputs.token }}
22
-
23
- - uses: actions/setup-python@v5
24
- with:
25
- python-version: '3.12'
26
-
27
- - uses: ./repo-tokens
28
- id: tokens
29
- with:
30
- include: 'src/**/*.ts container/agent-runner/src/**/*.ts container/Dockerfile container/build.sh launchd/com.nanoclaw.plist CLAUDE.md'
31
- exclude: 'src/**/*.test.ts'
32
- badge-path: 'repo-tokens/badge.svg'
33
-
34
- - name: Commit if changed
35
- run: |
36
- git add README.md repo-tokens/badge.svg
37
- git diff --cached --quiet && exit 0
38
- git config user.name "github-actions[bot]"
39
- git config user.email "github-actions[bot]@users.noreply.github.com"
40
- git commit -m "docs: update token count to ${{ steps.tokens.outputs.badge }}"
41
- git pull --rebase
42
- git push
package/.husky/pre-commit DELETED
@@ -1 +0,0 @@
1
- npm run format:fix
package/.mcp.json DELETED
@@ -1,3 +0,0 @@
1
- {
2
- "mcpServers": {}
3
- }
package/.nvmrc DELETED
@@ -1 +0,0 @@
1
- 22
package/.prettierrc DELETED
@@ -1,3 +0,0 @@
1
- {
2
- "singleQuote": true
3
- }