feat: add skills and workspace files

- Add complete skill set including feishu, github, ci-gen, etc.
- Add workspace documentation (AGENTS.md, SOUL.md, etc.)
- Add memory system and project context
This commit is contained in:
OpenClaw Agent
2026-02-06 19:14:24 +08:00
parent f26d4d8f7f
commit 7e0acef4f9
118 changed files with 12293 additions and 0 deletions
+60
View File
@@ -0,0 +1,60 @@
#!/usr/bin/env node
import { Command } from "commander";
import ora from "ora";
import * as fs from "fs";
import * as path from "path";
import { scanProject, generateWorkflow } from "./index";
const program = new Command();
program
.name("ai-ci")
.description("Generates CI/CD workflows from project analysis")
.version("1.0.0")
.option("-p, --preview", "Preview without writing")
.option("--provider <provider>", "CI provider", "github")
.option("--deploy <target>", "Deploy target (vercel, netlify, aws, docker)")
.option("-d, --dir <path>", "Project directory", ".")
.option("-o, --output <path>", "Output path")
.action(async (opts) => {
const spinner = ora("Scanning project...").start();
try {
const dir = path.resolve(opts.dir);
const info = await scanProject(dir);
if (info.files.length === 0) {
spinner.warn("No project files found. Are you in the right directory?");
process.exit(1);
}
spinner.text = `Found ${info.files.length} config files. Generating workflow...`;
const workflow = await generateWorkflow(info, opts.provider, opts.deploy);
const defaultOutput = opts.provider === "github"
? ".github/workflows/ci.yml"
: "ci-workflow.yml";
const outPath = path.resolve(opts.output || defaultOutput);
if (opts.preview) {
spinner.stop();
console.log(`\n--- Generated ${opts.provider} workflow ---\n`);
console.log(workflow);
console.log("\n-----------------------------------------\n");
} else {
const outDir = path.dirname(outPath);
if (!fs.existsSync(outDir)) {
fs.mkdirSync(outDir, { recursive: true });
}
fs.writeFileSync(outPath, workflow + "\n");
spinner.succeed(`Workflow written to ${outPath}`);
}
} catch (err: any) {
spinner.fail(err.message);
process.exit(1);
}
});
program.parse();
+65
View File
@@ -0,0 +1,65 @@
import OpenAI from "openai";
import { glob } from "glob";
import * as fs from "fs";
import * as path from "path";
const CONFIG_FILES = [
"package.json", "tsconfig.json", "next.config.js", "next.config.mjs",
"vite.config.ts", "nuxt.config.ts", "Cargo.toml", "go.mod",
"pyproject.toml", "requirements.txt", "Gemfile", "build.gradle", "pom.xml",
"jest.config.js", "jest.config.ts", "vitest.config.ts", ".eslintrc.js",
".eslintrc.json", "prettier.config.js", "Makefile",
"docker-compose.yml", "Dockerfile", "vercel.json", "netlify.toml",
"fly.toml", "railway.json",
];
export async function scanProject(dir: string): Promise<{ files: string[]; contents: Record<string, string> }> {
const found: string[] = [];
const contents: Record<string, string> = {};
for (const f of CONFIG_FILES) {
const full = path.join(dir, f);
if (fs.existsSync(full)) {
found.push(f);
const stat = fs.statSync(full);
if (stat.size < 5000) {
contents[f] = fs.readFileSync(full, "utf-8");
}
}
}
return { files: found, contents };
}
export async function generateWorkflow(
projectInfo: { files: string[]; contents: Record<string, string> },
provider: string,
deployTarget?: string
): Promise<string> {
if (!process.env.OPENAI_API_KEY) {
throw new Error("Missing OPENAI_API_KEY environment variable. Set it with: export OPENAI_API_KEY=sk-...");
}
const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });
const deployInstruction = deployTarget
? `Include a deployment step for ${deployTarget}. Use the standard ${deployTarget} GitHub Action or deployment method.`
: "Don't include deployment steps, just CI (lint, test, build).";
const response = await openai.chat.completions.create({
model: "gpt-4o-mini",
messages: [
{
role: "system",
content: `You are a CI/CD expert. Generate a ${provider === "github" ? "GitHub Actions" : provider} workflow YAML file. Output ONLY the YAML contents. No markdown code fences. Include proper caching, matrix testing if appropriate, and best practices. ${deployInstruction}`
},
{
role: "user",
content: `Project files: ${projectInfo.files.join(", ")}\n\nKey file contents:\n${Object.entries(projectInfo.contents).map(([k, v]) => `--- ${k} ---\n${v}`).join("\n\n")}`
}
],
temperature: 0.3,
});
return response.choices[0]?.message?.content?.trim() || "";
}