@leejungkiin/awkit 1.6.4 → 1.6.6

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,256 @@
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+
4
+ /**
5
+ * Cursor Rules Generator for AWKit
6
+ *
7
+ * Follows Cursor's official rule design:
8
+ * - 4 activation modes: Always Apply, Auto Attached (Globs), Agent Requested, Manual
9
+ * - Token efficiency: alwaysApply rules must be concise
10
+ * - Skills → Agent Requested mode (description only, no globs)
11
+ * - Global rules → Always Apply mode (concise, essential only)
12
+ *
13
+ * @see https://docs.cursor.com/docs/rules
14
+ */
15
+
16
+ /**
17
+ * Generate Cursor rule files from core templates.
18
+ * Creates TWO files following Cursor's best practices:
19
+ * 1. antigravity-rules.mdc (Always Apply) — concise identity + protocol (~30 lines)
20
+ * 2. antigravity-detailed.mdc (Agent Requested) — full rules loaded when relevant
21
+ *
22
+ * @param {string} sourcePath Path to core/GEMINI.md (used to auto-detect core dir)
23
+ * @param {string} destPath Destination .cursor/rules/antigravity-rules.mdc
24
+ */
25
+ function generateCursorRules(sourcePath, destPath) {
26
+ const coreDir = path.dirname(sourcePath);
27
+ const destDir = path.dirname(destPath);
28
+ fs.mkdirSync(destDir, { recursive: true });
29
+
30
+ // --- File 1: Always Apply (concise identity + protocol) ---
31
+ const cursorTemplate = path.join(coreDir, 'CURSOR.md');
32
+ const actualSource = fs.existsSync(cursorTemplate) ? cursorTemplate : sourcePath;
33
+
34
+ if (!fs.existsSync(actualSource)) {
35
+ console.log(`⚠️ Template not found: ${actualSource}`);
36
+ return;
37
+ }
38
+
39
+ const content = fs.readFileSync(actualSource, 'utf8');
40
+ const alwaysFrontmatter = [
41
+ '---',
42
+ 'description: "MANDATORY Antigravity Orchestrator identity and session protocol. You MUST follow these rules in EVERY conversation."',
43
+ 'alwaysApply: true',
44
+ '---',
45
+ '', '',
46
+ ].join('\n');
47
+
48
+ fs.writeFileSync(destPath, alwaysFrontmatter + content.trim() + '\n');
49
+ console.log(`✅ Cursor Rules (Always Apply) → ${path.basename(destPath)}`);
50
+
51
+ // --- File 2: Agent Requested (detailed operational rules) ---
52
+ const detailedSource = path.join(coreDir, 'CURSOR_DETAILED.md');
53
+ if (fs.existsSync(detailedSource)) {
54
+ const detailedContent = fs.readFileSync(detailedSource, 'utf8');
55
+ const detailedFrontmatter = [
56
+ '---',
57
+ 'description: "Antigravity detailed rules: 7-Gate system, code standards, safety guardrails, NeuralMemory protocol, GitNexus integration, and decision principles. Load when planning, coding, debugging, or reviewing."',
58
+ '---',
59
+ '', '',
60
+ ].join('\n');
61
+
62
+ const detailedDest = path.join(destDir, 'antigravity-detailed.mdc');
63
+ fs.writeFileSync(detailedDest, detailedFrontmatter + detailedContent.trim() + '\n');
64
+ console.log(`✅ Cursor Rules (Agent Requested) → antigravity-detailed.mdc`);
65
+ }
66
+ }
67
+
68
+ /**
69
+ * Convert AWKit skills to Cursor .mdc rules using proper activation modes.
70
+ *
71
+ * Activation mode strategy:
72
+ * - Skills with clear triggers → Agent Requested (description only)
73
+ * - Skills that are always needed → alwaysApply: true (rare)
74
+ * - All skills: NO globs (skills are concept-based, not file-based)
75
+ *
76
+ * @param {string} srcDir Source skills directory
77
+ * @param {string} destDir Destination .cursor/rules directory
78
+ * @param {string[]} selectedSkills Optional list of skill names to include
79
+ */
80
+ function generateCursorSkills(srcDir, destDir, selectedSkills = null) {
81
+ if (!fs.existsSync(srcDir)) return;
82
+
83
+ fs.mkdirSync(destDir, { recursive: true });
84
+ const skills = fs.readdirSync(srcDir);
85
+ const allowed = selectedSkills ? new Set(selectedSkills) : null;
86
+
87
+ let count = 0;
88
+ for (const skill of skills) {
89
+ if (allowed && !allowed.has(skill)) continue;
90
+ const skillSrcDir = path.join(srcDir, skill);
91
+ if (!fs.statSync(skillSrcDir).isDirectory()) continue;
92
+ if (skill === '.DS_Store') continue;
93
+
94
+ const skillFile = path.join(skillSrcDir, 'SKILL.md');
95
+ if (!fs.existsSync(skillFile)) continue;
96
+
97
+ const destFile = path.join(destDir, `${skill}.mdc`);
98
+ let content = fs.readFileSync(skillFile, 'utf8');
99
+
100
+ // Extract description from existing frontmatter or content
101
+ let description = extractDescription(content, skill);
102
+
103
+ // Strip existing frontmatter (we'll generate Cursor-specific one)
104
+ content = stripFrontmatter(content);
105
+
106
+ // Determine activation mode based on skill nature
107
+ const mode = getSkillActivationMode(skill);
108
+
109
+ // Build Cursor-compatible frontmatter
110
+ const frontmatter = buildFrontmatter(description, mode);
111
+
112
+ fs.writeFileSync(destFile, frontmatter + content.trim() + '\n');
113
+
114
+ // Copy scripts subdirectory if exists (reference for AI)
115
+ const scriptsSrc = path.join(skillSrcDir, 'scripts');
116
+ if (fs.existsSync(scriptsSrc)) {
117
+ const scriptsDest = path.join(destDir, 'scripts', skill);
118
+ copyRecursive(scriptsSrc, scriptsDest);
119
+ }
120
+
121
+ count++;
122
+ }
123
+ console.log(`✅ Generated ${count} Cursor rules (.mdc) in ${destDir}`);
124
+ }
125
+
126
+ /**
127
+ * Extract a clean description from skill content.
128
+ * Handles both single-line and YAML folded block scalar (>-) descriptions.
129
+ */
130
+ function extractDescription(content, skillName) {
131
+ // Try YAML frontmatter description
132
+ if (content.startsWith('---\n')) {
133
+ const endIdx = content.indexOf('\n---\n', 4);
134
+ if (endIdx !== -1) {
135
+ const frontmatter = content.slice(4, endIdx);
136
+
137
+ // Handle folded (>-) or literal (|) block scalar: description: >- or description: |
138
+ const foldedMatch = frontmatter.match(/^description:\s*[>|]-?\s*\n((?:\s{2,}.+\n?)+)/m);
139
+ if (foldedMatch) {
140
+ // Join indented continuation lines, strip leading whitespace
141
+ const lines = foldedMatch[1].split('\n')
142
+ .map(l => l.trim())
143
+ .filter(l => l.length > 0);
144
+ return lines.join(' ');
145
+ }
146
+
147
+ // Handle single-line: description: "value" or description: value
148
+ const singleMatch = frontmatter.match(/^description:\s*"?([^"\n]+)"?\s*$/m);
149
+ if (singleMatch) return singleMatch[1].trim();
150
+ }
151
+ }
152
+
153
+ // Fallback: try first markdown heading
154
+ const headingMatch = content.match(/^#\s+(.+)/m);
155
+ if (headingMatch) {
156
+ return headingMatch[1].replace(/[`*_]/g, '').trim();
157
+ }
158
+
159
+ return `Antigravity skill: ${skillName}`;
160
+ }
161
+
162
+ /**
163
+ * Strip existing YAML frontmatter from content.
164
+ */
165
+ function stripFrontmatter(content) {
166
+ if (content.startsWith('---\n')) {
167
+ const endIdx = content.indexOf('\n---\n', 4);
168
+ if (endIdx !== -1) {
169
+ return content.slice(endIdx + 5).trimStart();
170
+ }
171
+ }
172
+ return content;
173
+ }
174
+
175
+ /**
176
+ * Determine the Cursor activation mode for a skill.
177
+ *
178
+ * Modes:
179
+ * - "always" → alwaysApply: true (very few skills)
180
+ * - "agent" → Agent Requested: description only (most skills)
181
+ * - "globs" → Auto Attached: for file-type-specific skills
182
+ *
183
+ * @returns {{mode: string, globs?: string[]}}
184
+ */
185
+ function getSkillActivationMode(skillName) {
186
+ // Skills that should always be available (critical orchestration)
187
+ const alwaysApplySkills = [
188
+ 'orchestrator',
189
+ ];
190
+
191
+ // Skills with file-type affinity
192
+ const globSkills = {
193
+ 'gitnexus-intelligence': ['**/*.js', '**/*.ts', '**/*.py', '**/*.swift', '**/*.kt'],
194
+ };
195
+
196
+ if (alwaysApplySkills.includes(skillName)) {
197
+ return { mode: 'always' };
198
+ }
199
+
200
+ if (globSkills[skillName]) {
201
+ return { mode: 'globs', globs: globSkills[skillName] };
202
+ }
203
+
204
+ // Default: Agent Requested (description-based, most token-efficient)
205
+ return { mode: 'agent' };
206
+ }
207
+
208
+ /**
209
+ * Build Cursor-compatible YAML frontmatter.
210
+ */
211
+ function buildFrontmatter(description, modeConfig) {
212
+ const lines = ['---'];
213
+
214
+ lines.push(`description: "${description.replace(/"/g, '\\"')}"`);
215
+
216
+ if (modeConfig.mode === 'always') {
217
+ lines.push('alwaysApply: true');
218
+ } else if (modeConfig.mode === 'globs') {
219
+ const globStr = modeConfig.globs.map(g => `"${g}"`).join(', ');
220
+ lines.push(`globs: [${globStr}]`);
221
+ lines.push('alwaysApply: false');
222
+ } else {
223
+ // Agent Requested: just description, no globs, no alwaysApply
224
+ // Cursor docs: when only description is set, AI decides relevance
225
+ }
226
+
227
+ lines.push('---');
228
+ lines.push('');
229
+ lines.push('');
230
+ return lines.join('\n');
231
+ }
232
+
233
+ /**
234
+ * Recursively copy directories.
235
+ */
236
+ function copyRecursive(src, dest) {
237
+ if (!fs.existsSync(src)) return;
238
+ fs.mkdirSync(dest, { recursive: true });
239
+
240
+ const entries = fs.readdirSync(src, { withFileTypes: true });
241
+ for (const entry of entries) {
242
+ if (entry.name === '.DS_Store' || entry.name === '__pycache__') continue;
243
+ const srcPath = path.join(src, entry.name);
244
+ const destPath = path.join(dest, entry.name);
245
+ if (entry.isDirectory()) {
246
+ copyRecursive(srcPath, destPath);
247
+ } else {
248
+ fs.copyFileSync(srcPath, destPath);
249
+ }
250
+ }
251
+ }
252
+
253
+ module.exports = {
254
+ generateCursorRules,
255
+ generateCursorSkills,
256
+ };
package/core/CURSOR.md ADDED
@@ -0,0 +1,47 @@
1
+ # IDENTITY
2
+
3
+ You are **Antigravity Orchestrator**. Not "Cursor assistant", not "AI assistant". You are Antigravity.
4
+ When asked "who are you?", answer: "Tôi là Antigravity Orchestrator."
5
+
6
+ # SESSION PROTOCOL (HARD-ENFORCED)
7
+
8
+ > [!CAUTION]
9
+ > This is NOT optional. Skipping this = DISCIPLINARY VIOLATION.
10
+ > You MUST execute terminal commands BEFORE any analysis, planning, or code generation.
11
+
12
+ **MANDATORY FIRST ACTIONS** — Run these in terminal (RunTerminalCommand) sequentially:
13
+
14
+ ```bash
15
+ # Step 1: Read project identity (ALWAYS)
16
+ cat .project-identity
17
+
18
+ # Step 2: Check Symphony status via CLI (NEVER read symphony/ files directly)
19
+ command -v symphony >/dev/null && symphony next || echo "Symphony CLI not installed"
20
+ ```
21
+
22
+ Then use NeuralMemory MCP (if available): `nmem_recall` with project context.
23
+
24
+ **ZERO-TOLERANCE RULES:**
25
+ - ⛔ **BANNED**: Using read_file/grep/search tools on `symphony/` directory or `~/.gemini/brain/` to infer task state. You MUST use `symphony status` or `symphony next` CLI commands instead.
26
+ - ⛔ **BANNED**: Responding with a markdown plan or "Trạng thái hiện tại" summary BEFORE terminal init commands have executed successfully.
27
+ - ⛔ **BANNED**: Saying "Symphony đang trỏ project khác" as an observation without IMMEDIATELY running `symphony project switch <correct-id>` to fix it.
28
+
29
+ **Exception**: Pure Q&A (no code changes planned) may skip init chain.
30
+ **When in doubt**: Run init chain. Default to compliance.
31
+
32
+ # ABSOLUTE RULES
33
+
34
+ 1. Chat in **Vietnamese**. Code/docs/comments in **English**.
35
+ 2. End every response with **"Next steps"** section.
36
+ 3. Never run `git reset --hard`, `git push --force`, `rm -rf` without user confirmation.
37
+ 4. Never say "Great idea!" if the idea has problems. State risks first.
38
+ 5. When you don't follow a rule, say "Tôi đã không tuân thủ rule X" — never rationalize.
39
+ 6. **Git commits**: Use `awkit gate git auto "message"` — NEVER use raw `git commit` in terminal.
40
+
41
+ # ANTI-EVASION
42
+
43
+ - Do NOT reinterpret rules to avoid compliance.
44
+ - Do NOT blame "environment limitations" to skip rules. If a tool is missing, say so once, then do what you CAN.
45
+ - Do NOT separate "I am Cursor" from "Antigravity Orchestrator". You are one entity.
46
+ - Do NOT write paragraphs explaining why a rule doesn't apply. Follow it or state the violation.
47
+ - Do NOT read static files to "understand context" as a substitute for running CLI commands. The CLI commands exist to ensure correct state synchronization.
@@ -0,0 +1,102 @@
1
+ # Antigravity Detailed Rules
2
+
3
+ > This file contains detailed operational rules. Loaded by Cursor when relevant to the task.
4
+
5
+ ## 7-Gate Autonomous System
6
+
7
+ Triage complexity before every task:
8
+
9
+ | Complexity | Gates Required |
10
+ |---|---|
11
+ | TRIVIAL | Skip to Gate 4 (code directly) |
12
+ | MODERATE | Gate 3 → 4 → 5 |
13
+ | COMPLEX | All 7 Gates sequentially |
14
+
15
+ ### Gates
16
+ 1. **Gate 1 (Spec)**: Brainstorm → BRIEF.md
17
+ 2. **Gate 1.5 (Module Spec)**: Per-module product specs (COMPLEX + >3 modules only)
18
+ 3. **Gate 2 (Architecture)**: Design doc + user approve
19
+ 4. **Gate 2.5 (Visual Design)**: UI agreement (skip for backend-only tasks)
20
+ 5. **Gate 3 (Tasks)**: Create task tickets via Symphony
21
+ 6. **Gate 4 (Execution)**: Code to ticket, verify against design doc
22
+ 7. **Gate 5 (Verification)**: Test + code review
23
+
24
+ ### Gate 4 Three-Phase (COMPLEX + UI only)
25
+ - **Phase A** 🏗️ Infrastructure: dependencies, navigation skeleton → build check
26
+ - **Phase B** 🎨 UI Shell: all screens with mock data → USER TEST CHECKPOINT
27
+ - **Phase C** ⚡ Logic: replace mock with real data → USER TEST per feature
28
+ - ⛔ NEVER code logic before user confirms UI is OK.
29
+
30
+ ## Symphony Interaction Protocol (MANDATORY)
31
+
32
+ > [!WARNING]
33
+ > These rules prevent the #1 compliance failure: agents bypassing Symphony by reading raw files.
34
+
35
+ ### BANNED Actions
36
+ | Action | Why Banned | Correct Alternative |
37
+ |--------|-----------|-------------------|
38
+ | `read_file("symphony/tasks/*.json")` | Bypasses state sync, gets stale data | `symphony task list` in terminal |
39
+ | `read_file("symphony/agents/*.json")` | Reads unfiltered cross-project data | `symphony status` in terminal |
40
+ | `grep_search("symphony/")` | Static sniffing ≠ live state | `symphony next` in terminal |
41
+ | Writing markdown plan before init | Gives illusion of progress, skips context | Run terminal init first, then plan |
42
+
43
+ ### REQUIRED Actions
44
+ | Situation | Terminal Command |
45
+ |-----------|-----------------|
46
+ | Start of session | `cat .project-identity && symphony next` |
47
+ | User asks "what's next?" | `symphony next` |
48
+ | Need task list | `symphony task list` |
49
+ | Project mismatch detected | `symphony project switch <projectId>` |
50
+ | Task completed | `awkit gate git auto "feat: description"` |
51
+ | Check current state | `symphony status` |
52
+
53
+ ### Project ID Mismatch Hard-Stop
54
+ If you detect that Symphony's active project differs from `.project-identity`'s `projectId`:
55
+ 1. **STOP ALL ANALYSIS IMMEDIATELY** — do not brainstorm, plan, or summarize.
56
+ 2. Run: `symphony project switch <correct-projectId>` in terminal.
57
+ 3. Run: `symphony next` to get correct context.
58
+ 4. ONLY THEN proceed with the user's request.
59
+
60
+ ## Code Standards
61
+ - Production quality. Files < 500 lines.
62
+ - Don't modify code outside scope.
63
+ - Don't hardcode secrets → use `.env`.
64
+ - AI models: Gemini 2.5+ only.
65
+ - Firebase: Firebase AI Logic SDK.
66
+
67
+ ## Auto-Commit Protocol
68
+ - Build success (0 errors) → use `awkit gate git auto "type: message"`.
69
+ - ⛔ **NEVER** use raw `git commit` or `git push` in terminal. Always use `awkit gate git auto`.
70
+ - Commit message: conventional format (`fix:`, `feat:`, `refactor:`, `chore:`).
71
+ - Push fail → gate handles retry automatically. Never force push.
72
+
73
+ ## NeuralMemory Protocol
74
+ - Brain = projectId (from `.project-identity`). Switch brain before any nmem call.
75
+ - Tag every `nmem_remember()` with projectId.
76
+ - Cross-brain queries: `nmem_recall(query, brains=["default", projectId])`.
77
+
78
+ ## Safety Guardrails
79
+ Never auto-run these without user confirmation:
80
+ - `rm -rf`, `rm -r` (recursive delete)
81
+ - `git push --force`, `git reset --hard`, `git clean -fd`
82
+ - `DROP TABLE`, `DROP DATABASE`, `DELETE FROM` (without WHERE)
83
+ - `npm publish`, `pod trunk push`
84
+ - Any production deploy command
85
+
86
+ ## 6 Decision Principles (when deciding without user)
87
+ 1. Complete > Shortcuts
88
+ 2. Evidence > Assumptions
89
+ 3. Standard > Custom
90
+ 4. Explicit > Implicit
91
+ 5. Test > Trust
92
+ 6. Small > Big
93
+
94
+ ## Project Context
95
+ - `.project-identity` exists → READ IT FIRST for projectId, techStack, goals.
96
+ - `CODEBASE.md` exists → don't scan raw directory.
97
+
98
+ ## GitNexus (Code Intelligence)
99
+ - `.gitnexus/` exists → MUST use GitNexus tools.
100
+ - Before editing a symbol → `gitnexus_impact` check blast radius.
101
+ - Before committing → `gitnexus_detect_changes()` verify scope.
102
+ - HIGH/CRITICAL risk → WARN user before editing.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@leejungkiin/awkit",
3
- "version": "1.6.4",
4
- "description": "Antigravity Workflow Kit v12.5. Unified AI agent orchestration system with Mindful Checkpoints.",
3
+ "version": "1.6.6",
4
+ "description": "Antigravity Workflow Kit v1.6 Unified AI agent orchestration system with Mindful Checkpoints.",
5
5
  "main": "bin/awk.js",
6
6
  "bin": {
7
7
  "awkit": "bin/awk.js",
@@ -19,6 +19,19 @@ const fs = require('fs');
19
19
  const path = require('path');
20
20
  const { execSync } = require('child_process');
21
21
 
22
+ // Lazy-load obsidian-sync to avoid circular dependencies
23
+ let _autoSyncObsidian = null;
24
+ function getAutoSyncObsidian() {
25
+ if (!_autoSyncObsidian) {
26
+ try {
27
+ _autoSyncObsidian = require('./obsidian-sync').autoSyncObsidian;
28
+ } catch (_) {
29
+ _autoSyncObsidian = () => {}; // Graceful fallback
30
+ }
31
+ }
32
+ return _autoSyncObsidian;
33
+ }
34
+
22
35
  // ─── Color helpers (borrowed from main CLI) ──────────────────────────────────
23
36
 
24
37
  const C = {
@@ -224,6 +237,9 @@ function execGitAuto(message) {
224
237
  }
225
238
  }
226
239
 
240
+ // Trigger Obsidian sync after successful git auto
241
+ getAutoSyncObsidian()();
242
+
227
243
  return true;
228
244
  }
229
245
 
@@ -240,6 +256,8 @@ function execTrelloAction(action, args) {
240
256
  try {
241
257
  const cmd = `awkit trello ${action} ${args.map(a => `"${a}"`).join(' ')}`;
242
258
  execSync(cmd, { stdio: 'inherit' });
259
+ // Trigger Obsidian sync after successful trello action
260
+ getAutoSyncObsidian()();
243
261
  return true;
244
262
  } catch (e) {
245
263
  err(`Trello ${action} failed: ${e.message}`);