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
9 changes: 8 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,14 @@ jobs:
- name: built CLI smoke (no model needed)
run: node packages/cli/dist/main.js skills
- name: npm publish preflight (ordered-set check, never publishes)
run: node scripts/publish-preflight.cjs 2>&1 | tee preflight-output.log
# --skip-tests: build/typecheck/test already ran as first-class steps
# above; re-running the whole suite a second time in the same job
# pushed past the preflight's 30min per-command timeout on
# windows-latest (PR#39: standalone suite 496s green, the duplicate
# re-run was killed at exactly 1800s with the zero-not-ok timeout
# signature). publish.yml keeps the full preflight — it is the only
# test gate there.
run: node scripts/publish-preflight.cjs --skip-tests 2>&1 | tee preflight-output.log
- name: test failure excerpts (flaky forensics)
if: failure()
shell: pwsh
Expand Down
375 changes: 375 additions & 0 deletions packages/agent/src/__tests__/forge.test.ts

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions packages/agent/src/capability.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@ const RISK_RULES: Array<{ match: RegExp; risk: CapabilityRisk; permissions: stri
{ match: /^(harmony_emulator_(create|start|stop|delete))$/, risk: 'high', permissions: ['device.write', 'process.spawn'], network: true, sideEffects: ['device-mutation'] },
{ match: /^(harmony_build|harmony_cjpm_build|harmony_cjpm_test|harmony_lint)$/, risk: 'medium', permissions: ['process.spawn', 'fs.write'], network: true, sideEffects: ['build-artifacts'] },
{ match: /^spawn_agent$/, risk: 'high', permissions: ['agent.spawn'], network: false, sideEffects: ['subagent-run'] },
// git forges (GitHub/AtomGit): reads are low; writes mutate remote VCS state
// and are ALL approval-gated (needsApproval) like their domain-ops siblings.
{ match: /^forge_(repo|pr|issue|release)_(create|merge|upload)$|^forge_raw$/, risk: 'high', permissions: ['net.remote', 'fs.read'], network: true, sideEffects: ['remote-vcs'] },
{ match: /^forge_(repo|pr|issue)_list$|^forge_pr_checks$|^forge_status$/, risk: 'low', permissions: ['net.remote'], network: true, sideEffects: [] },
];

export function manifestFor(tool: Tool): CapabilityManifest {
Expand Down
637 changes: 637 additions & 0 deletions packages/agent/src/forge.ts

Large diffs are not rendered by default.

8 changes: 8 additions & 0 deletions packages/agent/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,12 @@
export { baseTools, readFileTool, writeFileTool, listDirTool, runCommandTool, rememberTool, seeImageTool } from './tools.ts';
export {
forgeTools, forgeDetect, forgeDetectHost, forgeRun, forgeLogin,
quoteArg, parseAuthStatus, parseJsonOut, renderRepos, renderPrs, renderIssues,
repoListArgs, repoCreateArgs, prListArgs, prCreateArgs, prMergeArgs, prChecksArgs,
issueListArgs, issueCreateArgs, releaseCreateArgs, releaseUploadArgs,
FORGE_BINS, installHint, loginHint,
type ForgeHost, type ForgeHostStatus,
} from './forge.ts';
export { manifestFor, capabilityReport, authorize, type CapabilityManifest, type CapabilityRisk, type PolicyMode } from './capability.ts';
export {
checkpointProject, createProject, findProject, interruptProject, listProjects,
Expand Down
7 changes: 7 additions & 0 deletions packages/agent/src/prompt.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,13 @@ export function buildSystemPrompt(opts: {
'',
'HarmonyOS development is your home domain: DevEco Studio toolchain, hvigor builds, ohpm packages, hdc devices, ArkTS/ArkUI, OpenHarmony and Cangjie. When a task touches it, prefer the harmony_* tools and precise toolchain knowledge.',

// ---- Git forge 域(GitHub / AtomGit,2026-10-30 用户特性) ----
'',
'## Git forges (GitHub / AtomGit)',
'Repository operations on GitHub or AtomGit — listing/creating repos, PRs, issues, releases, checking CI — go through the forge_* tools (host: "github" → gh CLI, "atomgit" → ag CLI). Do NOT send the user to the website for anything a forge_* tool can do.',
'Call forge_status first; when a host is not logged in, run forge_login for it (it shows the device code / URL the user must complete in the browser) — do not tell the user to run gh/ag commands by hand.',
'Credentials live in gh/ag\'s own storage and never pass through you: never ask for or store a platform token yourself.',

// ---- 通用工作风格 ----
'',
'Working style: read before writing; prefer small focused commands; verify results; state tradeoffs briefly. For risky operations (deleting, overwriting, publishing) say what will happen first. When a command fails twice with the same error, switch strategy instead of repeating it. For multi-line/quoted logic, write a temp .cjs file and run it with node - never fight cmd.exe quoting with node -e one-liners.',
Expand Down
3 changes: 2 additions & 1 deletion packages/agent/src/runner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ import { pageReadSink, lspObserveSink, observeBuildTools } from './code-wm-recor
import * as readline from 'node:readline/promises';
import { stdin } from 'node:process';
import { baseTools } from './tools.ts';
import { forgeTools } from './forge.ts';
import { buildSystemPrompt } from './prompt.ts';
import { strings } from './i18n.ts';
import { makeSpawnTool, MAX_SPAWN_DEPTH, type SpawnBase } from './spawn.ts';
Expand All @@ -61,7 +62,7 @@ export const spawnBase: { current?: SpawnBase } = {};

export function nativeRegistry(depth: number, opts: { lsp?: boolean; browser?: boolean; extension?: boolean; workspaceRoot?: string } = {}): Registry {
const reg = new Registry();
reg.registerAll(baseTools).registerAll(observeBuildTools(harmonyTools, homeDir())).registerAll(opsTools);
reg.registerAll(baseTools).registerAll(observeBuildTools(harmonyTools, homeDir())).registerAll(opsTools).registerAll(forgeTools);
// W6: LSP Tier-0 code-intelligence tools — registered ONLY when a real
// server is discoverable on PATH; a machine with no language servers
// must not carry dead tools. Discovery is synchronous (memoized PATH
Expand Down
Loading