Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
# Change Log

## 17.2.0

* Added `--show-secrets` flag to control display of sensitive values in output
* Added automatic redaction of secrets, API keys, tokens, and passwords in CLI output
* Added `init skill` command for installing Appwrite agent skills for AI coding agents
* Added automatic agent skills detection and installation during project initialization
* Added `OPEN_RUNTIMES_ENTRYPOINT` environment variable to function emulation
* Updated `-j` and `-R` output flag descriptions for clarity
* Fixed project init to gracefully handle override decline instead of exiting

## 17.1.0

* Added `organizations` command group with multiple subcommands
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ Once the installation is complete, you can verify the install using

```sh
$ appwrite -v
17.1.0
17.2.0
```

### Install using prebuilt binaries
Expand Down Expand Up @@ -60,7 +60,7 @@ $ scoop install https://raw.githubusercontent.com/appwrite/sdk-for-cli/master/sc
Once the installation completes, you can verify your install using
```
$ appwrite -v
17.1.0
17.2.0
```

## Getting Started
Expand Down
16 changes: 14 additions & 2 deletions cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,19 +98,31 @@ if (process.argv.includes('-v') || process.argv.includes('--version')) {
.helpOption('-h, --help', 'Display help for command')
.version(version, '-v, --version', 'Output the version number')
.option('-V, --verbose', 'Show complete error log')
.option('-j, --json', 'Output in JSON format')
.option('-R, --raw', 'Output full raw JSON (no filtering)')
.option('-j, --json', 'Output filtered JSON without empty values')
.option('-R, --raw', 'Output full JSON response (secrets still redacted unless --show-secrets is set)')
.option('--show-secrets', 'Display sensitive values like secrets and tokens in output')
.hook('preAction', migrate)
.option('-f,--force', 'Flag to confirm all warnings')
.option('-a,--all', 'Flag to push all resources')
.option('--id [id...]', 'Flag to pass a list of ids for a given action')
.option('--report', 'Enable reporting in case of CLI errors')
.hook('preAction', (_thisCommand, actionCommand) => {
const commandConfig = actionCommand as typeof actionCommand & {
outputFields?: string[];
};
cliConfig.displayFields = Array.isArray(commandConfig.outputFields)
? commandConfig.outputFields
: [];
})
.on('option:json', () => {
cliConfig.json = true;
})
.on('option:raw', () => {
cliConfig.raw = true;
})
.on('option:show-secrets', () => {
cliConfig.showSecrets = true;
})
.on('option:verbose', () => {
cliConfig.verbose = true;
})
Expand Down
4 changes: 2 additions & 2 deletions install.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@
# You can use "View source" of this page to see the full script.

# REPO
$GITHUB_x64_URL = "https://github.com/appwrite/sdk-for-cli/releases/download/17.1.0/appwrite-cli-win-x64.exe"
$GITHUB_arm64_URL = "https://github.com/appwrite/sdk-for-cli/releases/download/17.1.0/appwrite-cli-win-arm64.exe"
$GITHUB_x64_URL = "https://github.com/appwrite/sdk-for-cli/releases/download/17.2.0/appwrite-cli-win-x64.exe"
$GITHUB_arm64_URL = "https://github.com/appwrite/sdk-for-cli/releases/download/17.2.0/appwrite-cli-win-arm64.exe"

$APPWRITE_BINARY_NAME = "appwrite.exe"

Expand Down
2 changes: 1 addition & 1 deletion install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ printSuccess() {
downloadBinary() {
echo "[2/4] Downloading executable for $OS ($ARCH) ..."

GITHUB_LATEST_VERSION="17.1.0"
GITHUB_LATEST_VERSION="17.2.0"
GITHUB_FILE="appwrite-cli-${OS}-${ARCH}"
GITHUB_URL="https://github.com/$GITHUB_REPOSITORY_NAME/releases/download/$GITHUB_LATEST_VERSION/$GITHUB_FILE"

Expand Down
4 changes: 4 additions & 0 deletions lib/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,10 @@ class Client {
return this;
}

getHeaders(): Headers {
return { ...this.headers };
}

async call<T = unknown>(
method: string,
path: string = "",
Expand Down
111 changes: 109 additions & 2 deletions lib/commands/init.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,13 @@ import {
commandDescriptions,
} from "../parser.js";
import { sdkForConsole } from "../sdks.js";
import { isCloud } from "../utils.js";
import {
isCloud,
hasSkillsInstalled,
fetchAvailableSkills,
detectProjectSkills,
placeSkills,
} from "../utils.js";
import { Account, UseCases, AppwriteException } from "@appwrite.io/console";
import { DEFAULT_ENDPOINT, EXECUTABLE_NAME } from "../constants.js";

Expand Down Expand Up @@ -84,6 +90,7 @@ const initResources = async (): Promise<void> => {
const actions: Record<string, InitResourceAction> = {
function: initFunction,
site: initSite,
skill: initSkill,
table: initTable,
bucket: initBucket,
team: initTeam,
Expand Down Expand Up @@ -132,7 +139,8 @@ const initProject = async ({
if (!organizationId && !projectId && !projectName) {
answers = await inquirer.prompt(questionsInitProject);
if (answers.override === false) {
process.exit(1);
log("No changes made. Existing project configuration was kept.");
return;
}
} else {
const selectedOrganization =
Expand Down Expand Up @@ -252,6 +260,30 @@ const initProject = async ({
hint(
`Next you can use '${EXECUTABLE_NAME} init' to create resources in your project, or use '${EXECUTABLE_NAME} pull' and '${EXECUTABLE_NAME} push' to synchronize your project.`,
);

if (!hasSkillsInstalled(localConfig.configDirectoryPath)) {
try {
const skillsCwd = localConfig.configDirectoryPath;
log("Setting up Appwrite agent skills ...");
const { skills, tempDir } = fetchAvailableSkills();
try {
const detected = detectProjectSkills(skillsCwd, skills);
if (detected.length > 0) {
const names = detected.map((s) => s.dirName);
placeSkills(skillsCwd, tempDir, names, [".agents", ".claude"], true);
success(
`Installed ${names.length} agent skill${names.length === 1 ? "" : "s"} based on your project: ${detected.map((s) => s.name).join(", ")}`,
);
}
} finally {
fs.rmSync(tempDir, { recursive: true, force: true });
}
} catch (e) {
const msg = e instanceof Error ? e.message : String(e);
error(`Failed to install agent skills: ${msg}`);
hint(`You can install them later with '${EXECUTABLE_NAME} init skill'.`);
}
}
};

const initBucket = async (): Promise<void> => {
Expand Down Expand Up @@ -368,6 +400,75 @@ const initTopic = async (): Promise<void> => {
);
};

const initSkill = async (): Promise<void> => {
process.chdir(localConfig.configDirectoryPath);
const cwd = process.cwd();

log("Fetching available Appwrite agent skills ...");
const { skills, tempDir } = fetchAvailableSkills();

try {
const { selectedSkills } = await inquirer.prompt([
{
type: "checkbox",
name: "selectedSkills",
message: "Which skills would you like to install?",
choices: skills.map((skill) => ({
name: skill.name,
value: skill.dirName,
checked: false,
})),
validate: (value: string[]) =>
value.length > 0 || "Please select at least one skill.",
},
]);

const { selectedAgents } = await inquirer.prompt([
{
type: "checkbox",
name: "selectedAgents",
message: "Which agent directories would you like to install to?",
choices: [
{ name: ".agents", value: ".agents", checked: true },
{ name: ".claude", value: ".claude", checked: false },
],
validate: (value: string[]) =>
value.length > 0 || "Please select at least one agent directory.",
},
]);

const { installMethod } = await inquirer.prompt([
{
type: "list",
name: "installMethod",
message: "How would you like to install the skills?",
choices: [
{
name: "Symlink (recommended) — single source of truth, easy to update",
value: "symlink",
},
{
name: "Copy — independent copies in each agent directory",
value: "copy",
},
],
},
]);

const useSymlinks = installMethod === "symlink";
placeSkills(cwd, tempDir, selectedSkills, selectedAgents, useSymlinks);

success(
`${selectedSkills.length} skill${selectedSkills.length === 1 ? "" : "s"} installed successfully.`,
);
hint(
`Agent skills are automatically discovered by AI coding agents like Claude Code, Cursor, and GitHub Copilot.`,
);
} finally {
fs.rmSync(tempDir, { recursive: true, force: true });
}
};

const initFunction = async (): Promise<void> => {
process.chdir(localConfig.configDirectoryPath);

Expand Down Expand Up @@ -770,6 +871,12 @@ init
.description("Init a new Appwrite site")
.action(actionRunner(initSite));

init
.command("skill")
.alias("skills")
.description("Install Appwrite agent skills for AI coding agents")
.action(actionRunner(initSkill));

init
.command("bucket")
.alias("buckets")
Expand Down
Loading
Loading