@leejungkiin/awkit 1.6.4 → 1.6.5

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/bin/awk.js CHANGED
@@ -38,7 +38,9 @@ const HOME = process.env.HOME || process.env.USERPROFILE;
38
38
  const { generateClineRules, generateClineWorkflows, generateClineSkills } = require('./cline-generators');
39
39
  const { generateCodexAgentsMd, generateCodexSkills, generateCodexAgents } = require('./codex-generators');
40
40
  const { generateClaudeRules, generateClaudeSkills } = require('./claude-generators');
41
+ const { generateCursorRules, generateCursorSkills } = require('./cursor-generators');
41
42
  const { cmdGate } = require('../scripts/automation-gate');
43
+ const { cmdObsidian } = require('../scripts/obsidian-sync');
42
44
 
43
45
  // ─── Platform Definitions ──────────────────────────────────────────────────
44
46
 
@@ -85,6 +87,24 @@ const PLATFORMS = {
85
87
  dirs: {
86
88
  skills: '.claude/skills',
87
89
  },
90
+ },
91
+ qwen: {
92
+ name: 'Qwen Code',
93
+ globalRoot: path.join(HOME, '.qwen'),
94
+ rulesFile: null,
95
+ versionFile: path.join(HOME, '.qwen', 'awk_version'),
96
+ dirs: {
97
+ skills: 'skills',
98
+ },
99
+ },
100
+ cursor: {
101
+ name: 'Cursor AI',
102
+ globalRoot: process.cwd(), // Local to project
103
+ rulesFile: '.cursor/rules/antigravity-rules.mdc',
104
+ versionFile: '.cursor/awk_version',
105
+ dirs: {
106
+ skills: '.cursor/rules',
107
+ },
88
108
  }
89
109
  };
90
110
 
@@ -516,6 +536,8 @@ function cmdInstall(args = []) {
516
536
  if (args.includes('--cline') || args.includes('cline')) selectedPlatforms.push('cline');
517
537
  if (args.includes('--codex') || args.includes('-x')) selectedPlatforms.push('codex');
518
538
  if (args.includes('--claude-code') || args.includes('--claude') || args.includes('-c') || args.includes('claude')) selectedPlatforms.push('claude');
539
+ if (args.includes('--qwen') || args.includes('--qwen-code') || args.includes('-q') || args.includes('qwen')) selectedPlatforms.push('qwen');
540
+ if (args.includes('--cursor') || args.includes('cursor')) selectedPlatforms.push('cursor');
519
541
 
520
542
  const pIdx = args.indexOf('--platform');
521
543
  let legacyArg = null;
@@ -534,25 +556,29 @@ function cmdInstall(args = []) {
534
556
  if (isUpdate) {
535
557
  selectedPlatforms = [getActivePlatform()];
536
558
  } else {
537
- const platformOrder = ['antigravity', 'cline', 'codex', 'claude'];
559
+ const platformOrder = ['antigravity', 'cline', 'codex', 'claude', 'qwen', 'cursor'];
538
560
  const defaultPlatform = getActivePlatform();
539
561
  const defaultChoice = String(Math.max(platformOrder.indexOf(defaultPlatform), 0) + 1);
540
- log(`${C.cyan}Select platforms to install (e.g., type "1,2", "all", or "1,2,3,4"):${C.reset}`);
562
+ log(`${C.cyan}Select platforms to install (e.g., type "1,2", "all", or "1-6"):${C.reset}`);
541
563
  log(` 1. Gemini Code Assist (antigravity)`);
542
564
  log(` 2. Cline (VS Code)`);
543
565
  log(` 3. Codex CLI (codex)`);
544
566
  log(` 4. Claude Code (.claude/)`);
545
- log(` 5. All of the above`);
567
+ log(` 5. Qwen Code (~/.qwen/)`);
568
+ log(` 6. Cursor AI (.cursor/rules/)`);
569
+ log(` 7. All of the above`);
546
570
  log(`${C.gray}Press Enter to install only the active platform: ${PLATFORMS[defaultPlatform].name}.${C.reset}`);
547
571
  const choice = promptChoice('Choice', defaultChoice).trim().toLowerCase();
548
572
 
549
- if (choice === '5' || choice === 'all') {
573
+ if (choice === '7' || choice === 'all') {
550
574
  selectedPlatforms = Object.keys(PLATFORMS);
551
575
  } else {
552
576
  if (choice.includes('1')) selectedPlatforms.push('antigravity');
553
577
  if (choice.includes('2')) selectedPlatforms.push('cline');
554
578
  if (choice.includes('3')) selectedPlatforms.push('codex');
555
579
  if (choice.includes('4')) selectedPlatforms.push('claude');
580
+ if (choice.includes('5')) selectedPlatforms.push('qwen');
581
+ if (choice.includes('6')) selectedPlatforms.push('cursor');
556
582
  }
557
583
  }
558
584
  }
@@ -614,6 +640,10 @@ function cmdInstall(args = []) {
614
640
  const claudeTemplateSrc = path.join(AWK_ROOT, 'core', 'CLAUDE.md');
615
641
  const claudeRulesDest = path.join(target, plat.rulesFile);
616
642
  generateClaudeRules(claudeTemplateSrc, claudeRulesDest);
643
+ } else if (platform === 'cursor') {
644
+ info('Generating Cursor AI .mdc rules...');
645
+ const cursorRulesDest = path.join(target, plat.rulesFile);
646
+ generateCursorRules(path.join(AWK_ROOT, 'core', 'GEMINI.md'), cursorRulesDest);
617
647
  }
618
648
 
619
649
  // 3. Backup and install workflows
@@ -668,7 +698,11 @@ function cmdInstall(args = []) {
668
698
  const agentsDest = path.join(target, plat.dirs.agents);
669
699
  generateCodexAgents(skillsSrc, agentsDest, coreSkills);
670
700
  } else if (platform === 'claude') {
671
- generateClaudeSkills(skillsSrc, skillsDest, coreSkills);
701
+ generateClaudeSkills(skillsSrc, skillsDest, coreSkills, 'Claude Code');
702
+ } else if (platform === 'qwen') {
703
+ generateClaudeSkills(skillsSrc, skillsDest, coreSkills, 'Qwen Code'); // Shares the same format with Claude
704
+ } else if (platform === 'cursor') {
705
+ generateCursorSkills(skillsSrc, skillsDest, coreSkills);
672
706
  } else {
673
707
  const skillCount = copySelectedSkillDirs(skillsSrc, skillsDest, coreSkills);
674
708
  ok(`${skillCount} core skill files installed`);
@@ -2298,6 +2332,25 @@ function buildProjectIdentity(projectName, projectType, cwd, date) {
2298
2332
  list: 'Your List Name',
2299
2333
  card: 'Your Card Name',
2300
2334
  },
2335
+ automation: {
2336
+ autoQA: true,
2337
+ maxSelfCorrectionLoops: 3,
2338
+ telegram: {
2339
+ enabled: true,
2340
+ chatId: "",
2341
+ topicId: "",
2342
+ triggers: { git_push: true, task_complete: false, deploy: true }
2343
+ },
2344
+ trello: {
2345
+ enabled: true,
2346
+ autoSync: true,
2347
+ triggers: { task_complete: true, milestone: true, blocked: true }
2348
+ },
2349
+ git: {
2350
+ autoCommit: true,
2351
+ autoPush: true
2352
+ }
2353
+ },
2301
2354
  projectStage: 'development',
2302
2355
  codingStandards: cfg.codingStandards,
2303
2356
  projectGoals: [],
@@ -2505,6 +2558,8 @@ async function cmdInit(forceFlag = false) {
2505
2558
  const currentIdentity = JSON.parse(fs.readFileSync(identityPath, 'utf8'));
2506
2559
  if (!currentIdentity.automation) {
2507
2560
  currentIdentity.automation = {
2561
+ autoQA: true,
2562
+ maxSelfCorrectionLoops: 3,
2508
2563
  telegram: {
2509
2564
  enabled: true,
2510
2565
  chatId: "",
@@ -2523,6 +2578,12 @@ async function cmdInit(forceFlag = false) {
2523
2578
  };
2524
2579
  fs.writeFileSync(identityPath, JSON.stringify(currentIdentity, null, 2) + '\n');
2525
2580
  ok('Added Automation config placeholder to .project-identity');
2581
+ } else if (currentIdentity.automation.autoQA === undefined) {
2582
+ // Update existing automation block with new QA fields
2583
+ currentIdentity.automation.autoQA = true;
2584
+ currentIdentity.automation.maxSelfCorrectionLoops = 3;
2585
+ fs.writeFileSync(identityPath, JSON.stringify(currentIdentity, null, 2) + '\n');
2586
+ ok('Updated Automation config with autoQA defaults in .project-identity');
2526
2587
  }
2527
2588
  } catch (_) { /* ignore */ }
2528
2589
 
@@ -3867,6 +3928,10 @@ const [, , command, ...args] = process.argv;
3867
3928
  case 'gate':
3868
3929
  cmdGate(args);
3869
3930
  break;
3931
+ case 'obsidian':
3932
+ case 'obs':
3933
+ cmdObsidian(args);
3934
+ break;
3870
3935
  case 'admin':
3871
3936
  cmdAdmin();
3872
3937
  break;
@@ -25,7 +25,7 @@ function generateClaudeRules(sourcePath, destPath) {
25
25
  * Preserves full directory structure: <skill-name>/SKILL.md + scripts/ + templates/
26
26
  * Injects YAML frontmatter if missing.
27
27
  */
28
- function generateClaudeSkills(srcDir, destDir, selectedSkills = null) {
28
+ function generateClaudeSkills(srcDir, destDir, selectedSkills = null, platformName = 'Claude Code') {
29
29
  if (!fs.existsSync(srcDir)) return;
30
30
 
31
31
  fs.mkdirSync(destDir, { recursive: true });
@@ -51,7 +51,7 @@ function generateClaudeSkills(srcDir, destDir, selectedSkills = null) {
51
51
 
52
52
  count++;
53
53
  }
54
- console.log(`✅ Generated ${count} Claude Code skills in ${destDir}`);
54
+ console.log(`✅ Generated ${count} ${platformName} skills in ${destDir}`);
55
55
  }
56
56
 
57
57
  /**
@@ -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.5",
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}`);