argos-harness 0.1.0
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/LICENSE +21 -0
- package/README.md +21 -0
- package/assets/agents/auditor.md +140 -0
- package/assets/agents/commit-pr-pilot.md +154 -0
- package/assets/agents/explorer.md +93 -0
- package/assets/agents/implementer.md +117 -0
- package/assets/agents/leader.md +149 -0
- package/assets/agents/researcher.md +89 -0
- package/assets/agents/review-readability.md +87 -0
- package/assets/agents/review-reliability.md +100 -0
- package/assets/agents/review-resilience.md +87 -0
- package/assets/agents/review-risk.md +87 -0
- package/assets/agents/reviewer.md +167 -0
- package/assets/agents/ticket-audit.md +129 -0
- package/assets/hooks/argos-guard-destructive.sh +127 -0
- package/assets/hooks/argos-quality-gate.sh +129 -0
- package/assets/managed/aterrizaje.md +24 -0
- package/assets/managed/formato-respuesta.md +21 -0
- package/assets/managed/identidad.md +48 -0
- package/assets/managed/operaciones-seguras.md +14 -0
- package/assets/managed/orquestacion.md +169 -0
- package/assets/output-styles/argos.md +71 -0
- package/assets/skills/ai-sdk-5/SKILL.md +230 -0
- package/assets/skills/angular/SKILL.md +19 -0
- package/assets/skills/angular/references/architecture.md +137 -0
- package/assets/skills/angular/references/core.md +197 -0
- package/assets/skills/angular/references/forms.md +115 -0
- package/assets/skills/angular/references/performance.md +124 -0
- package/assets/skills/apollo-client/SKILL.md +61 -0
- package/assets/skills/app-blueprint/SKILL.md +45 -0
- package/assets/skills/app-blueprint/assets/module-template.md +48 -0
- package/assets/skills/app-blueprint/assets/system-template.md +38 -0
- package/assets/skills/app-blueprint/references/workflow.md +101 -0
- package/assets/skills/app-builder/SKILL.md +43 -0
- package/assets/skills/app-builder/phases/0-product.md +58 -0
- package/assets/skills/app-builder/phases/1-scaffold.md +33 -0
- package/assets/skills/app-builder/phases/10-store.md +40 -0
- package/assets/skills/app-builder/phases/2-data.md +34 -0
- package/assets/skills/app-builder/phases/3-domain.md +33 -0
- package/assets/skills/app-builder/phases/4-ui-nav.md +32 -0
- package/assets/skills/app-builder/phases/5-identity.md +30 -0
- package/assets/skills/app-builder/phases/6-polish.md +34 -0
- package/assets/skills/app-builder/phases/7-brand.md +31 -0
- package/assets/skills/app-builder/phases/8-web.md +30 -0
- package/assets/skills/app-builder/phases/9-docs.md +31 -0
- package/assets/skills/app-ia/SKILL.md +54 -0
- package/assets/skills/astro/SKILL.md +39 -0
- package/assets/skills/axios/SKILL.md +61 -0
- package/assets/skills/branch-pr/SKILL.md +200 -0
- package/assets/skills/bullmq/SKILL.md +55 -0
- package/assets/skills/chained-pr/SKILL.md +48 -0
- package/assets/skills/chained-pr/references/chaining-details.md +99 -0
- package/assets/skills/cognitive-doc-design/SKILL.md +81 -0
- package/assets/skills/comment-writer/SKILL.md +74 -0
- package/assets/skills/dashboard-ia/SKILL.md +54 -0
- package/assets/skills/django-drf/SKILL.md +180 -0
- package/assets/skills/go-testing/SKILL.md +47 -0
- package/assets/skills/go-testing/references/examples.md +89 -0
- package/assets/skills/issue-creation/SKILL.md +223 -0
- package/assets/skills/jira-epic/SKILL.md +306 -0
- package/assets/skills/jira-task/SKILL.md +382 -0
- package/assets/skills/judgment-day/SKILL.md +52 -0
- package/assets/skills/judgment-day/references/prompts-and-formats.md +98 -0
- package/assets/skills/lightsail-deploy/SKILL.md +44 -0
- package/assets/skills/lightsail-deploy/references/runbook.md +102 -0
- package/assets/skills/loop-back-debug/SKILL.md +102 -0
- package/assets/skills/mantine-form/SKILL.md +57 -0
- package/assets/skills/mongoose/SKILL.md +66 -0
- package/assets/skills/nextjs-15/SKILL.md +144 -0
- package/assets/skills/not-boring-mobile/SKILL.md +43 -0
- package/assets/skills/not-boring-mobile/references/not-boring-playbook.md +65 -0
- package/assets/skills/playwright/SKILL.md +315 -0
- package/assets/skills/pr-comments/SKILL.md +93 -0
- package/assets/skills/pr-create/SKILL.md +64 -0
- package/assets/skills/promo-video/SKILL.md +52 -0
- package/assets/skills/promo-video/assets/package.template.json +22 -0
- package/assets/skills/promo-video/assets/promo.template.tsx +469 -0
- package/assets/skills/promo-video/assets/theme.template.ts +27 -0
- package/assets/skills/promo-video/references/pipeline.md +119 -0
- package/assets/skills/promo-video-web/SKILL.md +51 -0
- package/assets/skills/promo-video-web/assets/browser-promo.template.tsx +385 -0
- package/assets/skills/promo-video-web/assets/capture.template.ts +70 -0
- package/assets/skills/promo-video-web/assets/package.template.json +26 -0
- package/assets/skills/promo-video-web/references/pipeline.md +84 -0
- package/assets/skills/pytest/SKILL.md +180 -0
- package/assets/skills/react-19/SKILL.md +118 -0
- package/assets/skills/react-hook-form/SKILL.md +59 -0
- package/assets/skills/react-router/SKILL.md +60 -0
- package/assets/skills/redux-toolkit/SKILL.md +60 -0
- package/assets/skills/review-diff/SKILL.md +101 -0
- package/assets/skills/ship-docs/SKILL.md +45 -0
- package/assets/skills/ship-docs/references/ship-docs-playbook.md +31 -0
- package/assets/skills/skill-creator/SKILL.md +97 -0
- package/assets/skills/skill-creator/assets/SKILL-TEMPLATE.md +68 -0
- package/assets/skills/skill-creator/references/skill-style-guide.md +79 -0
- package/assets/skills/skill-improver/SKILL.md +50 -0
- package/assets/skills/skill-improver/references/skill-style-guide.md +79 -0
- package/assets/skills/socketio/SKILL.md +58 -0
- package/assets/skills/spec-bootstrap/SKILL.md +62 -0
- package/assets/skills/store-ship/SKILL.md +52 -0
- package/assets/skills/store-ship/assets/android-supply.template.md +22 -0
- package/assets/skills/store-ship/assets/eas.template.json +32 -0
- package/assets/skills/store-ship/assets/maestro-flow.template.yaml +27 -0
- package/assets/skills/store-ship/assets/store.config.template.json +28 -0
- package/assets/skills/store-ship/references/pipeline.md +198 -0
- package/assets/skills/stripe/SKILL.md +83 -0
- package/assets/skills/tailwind-4/SKILL.md +193 -0
- package/assets/skills/tamagui/SKILL.md +60 -0
- package/assets/skills/tanstack-query/SKILL.md +58 -0
- package/assets/skills/ticket-intake/SKILL.md +55 -0
- package/assets/skills/typescript/SKILL.md +134 -0
- package/assets/skills/verify-before-done/SKILL.md +111 -0
- package/assets/skills/webapp-rebuilder/SKILL.md +48 -0
- package/assets/skills/webapp-rebuilder/assets/charter-template.md +46 -0
- package/assets/skills/webapp-rebuilder/references/workflow.md +47 -0
- package/assets/skills/winston-logging/SKILL.md +61 -0
- package/assets/skills/work-unit-commits/SKILL.md +84 -0
- package/assets/skills/zod-4/SKILL.md +210 -0
- package/assets/skills/zustand-5/SKILL.md +216 -0
- package/bin/argos.js +6 -0
- package/dist/commands/adopt.d.ts +35 -0
- package/dist/commands/adopt.d.ts.map +1 -0
- package/dist/commands/adopt.js +347 -0
- package/dist/commands/adopt.js.map +1 -0
- package/dist/commands/doctor.d.ts +20 -0
- package/dist/commands/doctor.d.ts.map +1 -0
- package/dist/commands/doctor.js +478 -0
- package/dist/commands/doctor.js.map +1 -0
- package/dist/commands/init.d.ts +32 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +358 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/remove.d.ts +62 -0
- package/dist/commands/remove.d.ts.map +1 -0
- package/dist/commands/remove.js +487 -0
- package/dist/commands/remove.js.map +1 -0
- package/dist/commands/workspace.d.ts +73 -0
- package/dist/commands/workspace.d.ts.map +1 -0
- package/dist/commands/workspace.js +354 -0
- package/dist/commands/workspace.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +24 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/assets.d.ts +29 -0
- package/dist/lib/assets.d.ts.map +1 -0
- package/dist/lib/assets.js +57 -0
- package/dist/lib/assets.js.map +1 -0
- package/dist/lib/atomic-write.d.ts +17 -0
- package/dist/lib/atomic-write.d.ts.map +1 -0
- package/dist/lib/atomic-write.js +41 -0
- package/dist/lib/atomic-write.js.map +1 -0
- package/dist/lib/backup.d.ts +11 -0
- package/dist/lib/backup.d.ts.map +1 -0
- package/dist/lib/backup.js +42 -0
- package/dist/lib/backup.js.map +1 -0
- package/dist/lib/config.d.ts +36 -0
- package/dist/lib/config.d.ts.map +1 -0
- package/dist/lib/config.js +52 -0
- package/dist/lib/config.js.map +1 -0
- package/dist/lib/detect.d.ts +58 -0
- package/dist/lib/detect.d.ts.map +1 -0
- package/dist/lib/detect.js +330 -0
- package/dist/lib/detect.js.map +1 -0
- package/dist/lib/ficha.d.ts +10 -0
- package/dist/lib/ficha.d.ts.map +1 -0
- package/dist/lib/ficha.js +38 -0
- package/dist/lib/ficha.js.map +1 -0
- package/dist/lib/git.d.ts +27 -0
- package/dist/lib/git.d.ts.map +1 -0
- package/dist/lib/git.js +79 -0
- package/dist/lib/git.js.map +1 -0
- package/dist/lib/managed-files.d.ts +35 -0
- package/dist/lib/managed-files.d.ts.map +1 -0
- package/dist/lib/managed-files.js +97 -0
- package/dist/lib/managed-files.js.map +1 -0
- package/dist/lib/markers.d.ts +64 -0
- package/dist/lib/markers.d.ts.map +1 -0
- package/dist/lib/markers.js +157 -0
- package/dist/lib/markers.js.map +1 -0
- package/dist/lib/navori-import.d.ts +42 -0
- package/dist/lib/navori-import.d.ts.map +1 -0
- package/dist/lib/navori-import.js +65 -0
- package/dist/lib/navori-import.js.map +1 -0
- package/dist/lib/openclaw-agents.d.ts +53 -0
- package/dist/lib/openclaw-agents.d.ts.map +1 -0
- package/dist/lib/openclaw-agents.js +118 -0
- package/dist/lib/openclaw-agents.js.map +1 -0
- package/dist/lib/package-root.d.ts +11 -0
- package/dist/lib/package-root.d.ts.map +1 -0
- package/dist/lib/package-root.js +23 -0
- package/dist/lib/package-root.js.map +1 -0
- package/dist/lib/paths.d.ts +11 -0
- package/dist/lib/paths.d.ts.map +1 -0
- package/dist/lib/paths.js +16 -0
- package/dist/lib/paths.js.map +1 -0
- package/dist/lib/settings-merge.d.ts +125 -0
- package/dist/lib/settings-merge.d.ts.map +1 -0
- package/dist/lib/settings-merge.js +373 -0
- package/dist/lib/settings-merge.js.map +1 -0
- package/dist/lib/version.d.ts +3 -0
- package/dist/lib/version.d.ts.map +1 -0
- package/dist/lib/version.js +13 -0
- package/dist/lib/version.js.map +1 -0
- package/dist/lib/which.d.ts +8 -0
- package/dist/lib/which.d.ts.map +1 -0
- package/dist/lib/which.js +30 -0
- package/dist/lib/which.js.map +1 -0
- package/dist/lib/workspaces.d.ts +133 -0
- package/dist/lib/workspaces.d.ts.map +1 -0
- package/dist/lib/workspaces.js +241 -0
- package/dist/lib/workspaces.js.map +1 -0
- package/dist/lib/zod-messages.d.ts +4 -0
- package/dist/lib/zod-messages.d.ts.map +1 -0
- package/dist/lib/zod-messages.js +28 -0
- package/dist/lib/zod-messages.js.map +1 -0
- package/package.json +44 -0
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OpenClaw fixes one cwd per "agent". `argos workspace agents <name>` needs
|
|
3
|
+
* one OpenClaw agent per repo registered in an Argos workspace so that
|
|
4
|
+
* Claude Code sessions anchor inside each repo and use the global motor from
|
|
5
|
+
* that repo's own working directory. This module holds the pure per-repo
|
|
6
|
+
* planning/classification logic so `commands/workspace.ts` stays a thin
|
|
7
|
+
* formatter — mirroring how `commands/adopt.ts` exports `runAdopt` for the
|
|
8
|
+
* same reason. Ported from the proven design in navori-harness (F2 spec's
|
|
9
|
+
* "hereda de navori-harness" list).
|
|
10
|
+
*/
|
|
11
|
+
export declare const OPENCLAW_BINARY = "openclaw";
|
|
12
|
+
/** OpenClaw agent name for a repo: optional prefix + repo name, no other transform. */
|
|
13
|
+
export declare function buildAgentName(repoName: string, prefix?: string): string;
|
|
14
|
+
export declare function buildOpenclawAddArgs(agentName: string, repoPath: string): string[];
|
|
15
|
+
/** Human-readable form of the command, for preview output only (never parsed back). */
|
|
16
|
+
export declare function formatOpenclawAddCommand(agentName: string, repoPath: string): string;
|
|
17
|
+
export type OpenclawAddOutcome = "created" | "exists" | "error";
|
|
18
|
+
export interface OpenclawAddResult {
|
|
19
|
+
outcome: OpenclawAddOutcome;
|
|
20
|
+
detail: string;
|
|
21
|
+
}
|
|
22
|
+
/** Classify a finished `openclaw agents add` invocation into created/exists/error. */
|
|
23
|
+
export declare function classifyOpenclawAddResult(status: number | null, stdout: string, stderr: string): OpenclawAddResult;
|
|
24
|
+
/** Runs `openclaw agents add ...` for real. Injectable so tests never shell out. */
|
|
25
|
+
export type OpenclawRunner = (agentName: string, repoPath: string) => OpenclawAddResult;
|
|
26
|
+
export declare const runOpenclawAgentAdd: OpenclawRunner;
|
|
27
|
+
export type WorkspaceAgentStatus = "created" | "would-create" | "exists" | "missing" | "error";
|
|
28
|
+
export interface WorkspaceAgentRow {
|
|
29
|
+
name: string;
|
|
30
|
+
status: WorkspaceAgentStatus;
|
|
31
|
+
detail: string;
|
|
32
|
+
}
|
|
33
|
+
export interface PlanWorkspaceAgentsOptions {
|
|
34
|
+
/** true = preview only, never calls the runner. */
|
|
35
|
+
preview: boolean;
|
|
36
|
+
prefix?: string;
|
|
37
|
+
/** Injectable for tests; defaults to runOpenclawAgentAdd. */
|
|
38
|
+
runner?: OpenclawRunner;
|
|
39
|
+
/** Injectable for tests; defaults to node:fs existsSync. */
|
|
40
|
+
pathExists?: (path: string) => boolean;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Build the per-repo plan for `workspace agents`: one OpenClaw agent per
|
|
44
|
+
* registered repo. Preview mode never shells out — it only formats the
|
|
45
|
+
* command that would run. Apply mode calls the runner per repo and keeps
|
|
46
|
+
* going on a per-repo failure (e.g. the agent already exists), since one
|
|
47
|
+
* bad repo must not abort the rest of the workspace.
|
|
48
|
+
*/
|
|
49
|
+
export declare function planWorkspaceAgents(repos: Array<{
|
|
50
|
+
name: string;
|
|
51
|
+
path: string;
|
|
52
|
+
}>, options: PlanWorkspaceAgentsOptions): WorkspaceAgentRow[];
|
|
53
|
+
//# sourceMappingURL=openclaw-agents.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"openclaw-agents.d.ts","sourceRoot":"","sources":["../../src/lib/openclaw-agents.ts"],"names":[],"mappings":"AAGA;;;;;;;;;GASG;AAEH,eAAO,MAAM,eAAe,aAAa,CAAC;AAE1C,uFAAuF;AACvF,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,SAAK,GAAG,MAAM,CAEpE;AAED,wBAAgB,oBAAoB,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,EAAE,CAElF;AAED,uFAAuF;AACvF,wBAAgB,wBAAwB,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAEpF;AAED,MAAM,MAAM,kBAAkB,GAAG,SAAS,GAAG,QAAQ,GAAG,OAAO,CAAC;AAEhE,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,kBAAkB,CAAC;IAC5B,MAAM,EAAE,MAAM,CAAC;CAChB;AAWD,sFAAsF;AACtF,wBAAgB,yBAAyB,CACvC,MAAM,EAAE,MAAM,GAAG,IAAI,EACrB,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,GACb,iBAAiB,CAYnB;AAED,oFAAoF;AACpF,MAAM,MAAM,cAAc,GAAG,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,KAAK,iBAAiB,CAAC;AAQxF,eAAO,MAAM,mBAAmB,EAAE,cAmBjC,CAAC;AAEF,MAAM,MAAM,oBAAoB,GAAG,SAAS,GAAG,cAAc,GAAG,QAAQ,GAAG,SAAS,GAAG,OAAO,CAAC;AAE/F,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,oBAAoB,CAAC;IAC7B,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,0BAA0B;IACzC,mDAAmD;IACnD,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,6DAA6D;IAC7D,MAAM,CAAC,EAAE,cAAc,CAAC;IACxB,4DAA4D;IAC5D,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC;CACxC;AAED;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC,EAC5C,OAAO,EAAE,0BAA0B,GAClC,iBAAiB,EAAE,CAiBrB"}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
import { spawnSync } from "node:child_process";
|
|
3
|
+
/**
|
|
4
|
+
* OpenClaw fixes one cwd per "agent". `argos workspace agents <name>` needs
|
|
5
|
+
* one OpenClaw agent per repo registered in an Argos workspace so that
|
|
6
|
+
* Claude Code sessions anchor inside each repo and use the global motor from
|
|
7
|
+
* that repo's own working directory. This module holds the pure per-repo
|
|
8
|
+
* planning/classification logic so `commands/workspace.ts` stays a thin
|
|
9
|
+
* formatter — mirroring how `commands/adopt.ts` exports `runAdopt` for the
|
|
10
|
+
* same reason. Ported from the proven design in navori-harness (F2 spec's
|
|
11
|
+
* "hereda de navori-harness" list).
|
|
12
|
+
*/ export const OPENCLAW_BINARY = "openclaw";
|
|
13
|
+
/** OpenClaw agent name for a repo: optional prefix + repo name, no other transform. */ export function buildAgentName(repoName, prefix = "") {
|
|
14
|
+
return `${prefix}${repoName}`;
|
|
15
|
+
}
|
|
16
|
+
export function buildOpenclawAddArgs(agentName, repoPath) {
|
|
17
|
+
return [
|
|
18
|
+
"agents",
|
|
19
|
+
"add",
|
|
20
|
+
agentName,
|
|
21
|
+
"--workspace",
|
|
22
|
+
repoPath,
|
|
23
|
+
"--non-interactive"
|
|
24
|
+
];
|
|
25
|
+
}
|
|
26
|
+
/** Human-readable form of the command, for preview output only (never parsed back). */ export function formatOpenclawAddCommand(agentName, repoPath) {
|
|
27
|
+
return `${OPENCLAW_BINARY} ${buildOpenclawAddArgs(agentName, repoPath).join(" ")}`;
|
|
28
|
+
}
|
|
29
|
+
// openclaw's exact wording for "this agent name is already registered" isn't
|
|
30
|
+
// part of any published contract, so this is a best-effort classifier over
|
|
31
|
+
// common phrasings. Widen it here if a real openclaw error message doesn't match.
|
|
32
|
+
// Deliberately does NOT match a bare "duplicate" — that also fires on unrelated
|
|
33
|
+
// errors like "duplicate key violation in database", which must surface as a
|
|
34
|
+
// real failure instead of being swallowed as a benign "already exists".
|
|
35
|
+
const DUPLICATE_PATTERN = /\bagent\b[^\n]{0,80}\balready (?:exists|registered)\b|\bname\b[^\n]{0,80}\balready in use\b|\balready exists\b|\balready registered\b/i;
|
|
36
|
+
/** Classify a finished `openclaw agents add` invocation into created/exists/error. */ export function classifyOpenclawAddResult(status, stdout, stderr) {
|
|
37
|
+
if (status === 0) {
|
|
38
|
+
return {
|
|
39
|
+
outcome: "created",
|
|
40
|
+
detail: stdout.trim()
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
const combined = `${stderr}\n${stdout}`;
|
|
44
|
+
if (DUPLICATE_PATTERN.test(combined)) {
|
|
45
|
+
return {
|
|
46
|
+
outcome: "exists",
|
|
47
|
+
detail: stderr.trim() || stdout.trim() || "agent already exists"
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
return {
|
|
51
|
+
outcome: "error",
|
|
52
|
+
detail: stderr.trim() || stdout.trim() || `openclaw exited with status ${status}`
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
// A non-interactive "register an agent" call should never legitimately run
|
|
56
|
+
// for minutes, but stays bounded so a hang (e.g. openclaw waiting on a stuck
|
|
57
|
+
// interactive prompt) can't freeze the whole per-repo agent-add loop forever
|
|
58
|
+
// — a real risk when this runs unattended from cron on the VPS.
|
|
59
|
+
const OPENCLAW_ADD_TIMEOUT_MS = 120_000; // 120s, per spec
|
|
60
|
+
export const runOpenclawAgentAdd = (agentName, repoPath)=>{
|
|
61
|
+
const result = spawnSync(OPENCLAW_BINARY, buildOpenclawAddArgs(agentName, repoPath), {
|
|
62
|
+
encoding: "utf-8",
|
|
63
|
+
stdio: [
|
|
64
|
+
"ignore",
|
|
65
|
+
"pipe",
|
|
66
|
+
"pipe"
|
|
67
|
+
],
|
|
68
|
+
timeout: OPENCLAW_ADD_TIMEOUT_MS
|
|
69
|
+
});
|
|
70
|
+
if (result.error) {
|
|
71
|
+
// spawnSync sets result.error with this code when the timeout fires.
|
|
72
|
+
if (result.error.code === "ETIMEDOUT") {
|
|
73
|
+
return {
|
|
74
|
+
outcome: "error",
|
|
75
|
+
detail: `Timed out adding agent '${agentName}' after ${OPENCLAW_ADD_TIMEOUT_MS / 1000}s. ` + `openclaw may be waiting for interactive input — check for a stuck prompt.`
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
return {
|
|
79
|
+
outcome: "error",
|
|
80
|
+
detail: result.error.message
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
return classifyOpenclawAddResult(result.status, result.stdout ?? "", result.stderr ?? "");
|
|
84
|
+
};
|
|
85
|
+
/**
|
|
86
|
+
* Build the per-repo plan for `workspace agents`: one OpenClaw agent per
|
|
87
|
+
* registered repo. Preview mode never shells out — it only formats the
|
|
88
|
+
* command that would run. Apply mode calls the runner per repo and keeps
|
|
89
|
+
* going on a per-repo failure (e.g. the agent already exists), since one
|
|
90
|
+
* bad repo must not abort the rest of the workspace.
|
|
91
|
+
*/ export function planWorkspaceAgents(repos, options) {
|
|
92
|
+
const { preview, prefix = "", runner = runOpenclawAgentAdd, pathExists = existsSync } = options;
|
|
93
|
+
return repos.map((repo)=>{
|
|
94
|
+
const agentName = buildAgentName(repo.name, prefix);
|
|
95
|
+
if (!pathExists(repo.path)) {
|
|
96
|
+
return {
|
|
97
|
+
name: repo.name,
|
|
98
|
+
status: "missing",
|
|
99
|
+
detail: repo.path
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
if (preview) {
|
|
103
|
+
return {
|
|
104
|
+
name: repo.name,
|
|
105
|
+
status: "would-create",
|
|
106
|
+
detail: formatOpenclawAddCommand(agentName, repo.path)
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
const result = runner(agentName, repo.path);
|
|
110
|
+
return {
|
|
111
|
+
name: repo.name,
|
|
112
|
+
status: result.outcome,
|
|
113
|
+
detail: result.detail
|
|
114
|
+
};
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
//# sourceMappingURL=openclaw-agents.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/lib/openclaw-agents.ts"],"sourcesContent":["import { existsSync } from \"node:fs\";\nimport { spawnSync } from \"node:child_process\";\n\n/**\n * OpenClaw fixes one cwd per \"agent\". `argos workspace agents <name>` needs\n * one OpenClaw agent per repo registered in an Argos workspace so that\n * Claude Code sessions anchor inside each repo and use the global motor from\n * that repo's own working directory. This module holds the pure per-repo\n * planning/classification logic so `commands/workspace.ts` stays a thin\n * formatter — mirroring how `commands/adopt.ts` exports `runAdopt` for the\n * same reason. Ported from the proven design in navori-harness (F2 spec's\n * \"hereda de navori-harness\" list).\n */\n\nexport const OPENCLAW_BINARY = \"openclaw\";\n\n/** OpenClaw agent name for a repo: optional prefix + repo name, no other transform. */\nexport function buildAgentName(repoName: string, prefix = \"\"): string {\n return `${prefix}${repoName}`;\n}\n\nexport function buildOpenclawAddArgs(agentName: string, repoPath: string): string[] {\n return [\"agents\", \"add\", agentName, \"--workspace\", repoPath, \"--non-interactive\"];\n}\n\n/** Human-readable form of the command, for preview output only (never parsed back). */\nexport function formatOpenclawAddCommand(agentName: string, repoPath: string): string {\n return `${OPENCLAW_BINARY} ${buildOpenclawAddArgs(agentName, repoPath).join(\" \")}`;\n}\n\nexport type OpenclawAddOutcome = \"created\" | \"exists\" | \"error\";\n\nexport interface OpenclawAddResult {\n outcome: OpenclawAddOutcome;\n detail: string;\n}\n\n// openclaw's exact wording for \"this agent name is already registered\" isn't\n// part of any published contract, so this is a best-effort classifier over\n// common phrasings. Widen it here if a real openclaw error message doesn't match.\n// Deliberately does NOT match a bare \"duplicate\" — that also fires on unrelated\n// errors like \"duplicate key violation in database\", which must surface as a\n// real failure instead of being swallowed as a benign \"already exists\".\nconst DUPLICATE_PATTERN =\n /\\bagent\\b[^\\n]{0,80}\\balready (?:exists|registered)\\b|\\bname\\b[^\\n]{0,80}\\balready in use\\b|\\balready exists\\b|\\balready registered\\b/i;\n\n/** Classify a finished `openclaw agents add` invocation into created/exists/error. */\nexport function classifyOpenclawAddResult(\n status: number | null,\n stdout: string,\n stderr: string,\n): OpenclawAddResult {\n if (status === 0) {\n return { outcome: \"created\", detail: stdout.trim() };\n }\n const combined = `${stderr}\\n${stdout}`;\n if (DUPLICATE_PATTERN.test(combined)) {\n return { outcome: \"exists\", detail: stderr.trim() || stdout.trim() || \"agent already exists\" };\n }\n return {\n outcome: \"error\",\n detail: stderr.trim() || stdout.trim() || `openclaw exited with status ${status}`,\n };\n}\n\n/** Runs `openclaw agents add ...` for real. Injectable so tests never shell out. */\nexport type OpenclawRunner = (agentName: string, repoPath: string) => OpenclawAddResult;\n\n// A non-interactive \"register an agent\" call should never legitimately run\n// for minutes, but stays bounded so a hang (e.g. openclaw waiting on a stuck\n// interactive prompt) can't freeze the whole per-repo agent-add loop forever\n// — a real risk when this runs unattended from cron on the VPS.\nconst OPENCLAW_ADD_TIMEOUT_MS = 120_000; // 120s, per spec\n\nexport const runOpenclawAgentAdd: OpenclawRunner = (agentName, repoPath) => {\n const result = spawnSync(OPENCLAW_BINARY, buildOpenclawAddArgs(agentName, repoPath), {\n encoding: \"utf-8\",\n stdio: [\"ignore\", \"pipe\", \"pipe\"],\n timeout: OPENCLAW_ADD_TIMEOUT_MS,\n });\n if (result.error) {\n // spawnSync sets result.error with this code when the timeout fires.\n if ((result.error as NodeJS.ErrnoException).code === \"ETIMEDOUT\") {\n return {\n outcome: \"error\",\n detail:\n `Timed out adding agent '${agentName}' after ${OPENCLAW_ADD_TIMEOUT_MS / 1000}s. ` +\n `openclaw may be waiting for interactive input — check for a stuck prompt.`,\n };\n }\n return { outcome: \"error\", detail: (result.error as Error).message };\n }\n return classifyOpenclawAddResult(result.status, result.stdout ?? \"\", result.stderr ?? \"\");\n};\n\nexport type WorkspaceAgentStatus = \"created\" | \"would-create\" | \"exists\" | \"missing\" | \"error\";\n\nexport interface WorkspaceAgentRow {\n name: string;\n status: WorkspaceAgentStatus;\n detail: string;\n}\n\nexport interface PlanWorkspaceAgentsOptions {\n /** true = preview only, never calls the runner. */\n preview: boolean;\n prefix?: string;\n /** Injectable for tests; defaults to runOpenclawAgentAdd. */\n runner?: OpenclawRunner;\n /** Injectable for tests; defaults to node:fs existsSync. */\n pathExists?: (path: string) => boolean;\n}\n\n/**\n * Build the per-repo plan for `workspace agents`: one OpenClaw agent per\n * registered repo. Preview mode never shells out — it only formats the\n * command that would run. Apply mode calls the runner per repo and keeps\n * going on a per-repo failure (e.g. the agent already exists), since one\n * bad repo must not abort the rest of the workspace.\n */\nexport function planWorkspaceAgents(\n repos: Array<{ name: string; path: string }>,\n options: PlanWorkspaceAgentsOptions,\n): WorkspaceAgentRow[] {\n const { preview, prefix = \"\", runner = runOpenclawAgentAdd, pathExists = existsSync } = options;\n return repos.map((repo) => {\n const agentName = buildAgentName(repo.name, prefix);\n if (!pathExists(repo.path)) {\n return { name: repo.name, status: \"missing\", detail: repo.path };\n }\n if (preview) {\n return {\n name: repo.name,\n status: \"would-create\",\n detail: formatOpenclawAddCommand(agentName, repo.path),\n };\n }\n const result = runner(agentName, repo.path);\n return { name: repo.name, status: result.outcome, detail: result.detail };\n });\n}\n"],"names":["existsSync","spawnSync","OPENCLAW_BINARY","buildAgentName","repoName","prefix","buildOpenclawAddArgs","agentName","repoPath","formatOpenclawAddCommand","join","DUPLICATE_PATTERN","classifyOpenclawAddResult","status","stdout","stderr","outcome","detail","trim","combined","test","OPENCLAW_ADD_TIMEOUT_MS","runOpenclawAgentAdd","result","encoding","stdio","timeout","error","code","message","planWorkspaceAgents","repos","options","preview","runner","pathExists","map","repo","name","path"],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolve the `@argos/cli` package root (the directory holding both
|
|
3
|
+
* `package.json` and `assets/`) relative to the currently running module.
|
|
4
|
+
*
|
|
5
|
+
* Walks up from the calling module's directory. This works from compiled
|
|
6
|
+
* `dist/**` (package root is the parent of `dist/`) and from `src/**` during
|
|
7
|
+
* ts-node/vitest dev runs, without depending on a fixed nesting depth or a
|
|
8
|
+
* build-time asset-copy step.
|
|
9
|
+
*/
|
|
10
|
+
export declare function resolvePackageRoot(fromUrl?: string): string;
|
|
11
|
+
//# sourceMappingURL=package-root.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"package-root.d.ts","sourceRoot":"","sources":["../../src/lib/package-root.ts"],"names":[],"mappings":"AAIA;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,GAAE,MAAwB,GAAG,MAAM,CAS5E"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
import { dirname, join } from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
/**
|
|
5
|
+
* Resolve the `@argos/cli` package root (the directory holding both
|
|
6
|
+
* `package.json` and `assets/`) relative to the currently running module.
|
|
7
|
+
*
|
|
8
|
+
* Walks up from the calling module's directory. This works from compiled
|
|
9
|
+
* `dist/**` (package root is the parent of `dist/`) and from `src/**` during
|
|
10
|
+
* ts-node/vitest dev runs, without depending on a fixed nesting depth or a
|
|
11
|
+
* build-time asset-copy step.
|
|
12
|
+
*/ export function resolvePackageRoot(fromUrl = import.meta.url) {
|
|
13
|
+
let dir = dirname(fileURLToPath(fromUrl));
|
|
14
|
+
for(let i = 0; i < 8; i++){
|
|
15
|
+
if (existsSync(join(dir, "package.json")) && existsSync(join(dir, "assets"))) return dir;
|
|
16
|
+
const parent = dirname(dir);
|
|
17
|
+
if (parent === dir) break;
|
|
18
|
+
dir = parent;
|
|
19
|
+
}
|
|
20
|
+
throw new Error(`Could not resolve the @argos/cli package root from ${fromUrl}`);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
//# sourceMappingURL=package-root.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/lib/package-root.ts"],"sourcesContent":["import { existsSync } from \"node:fs\";\nimport { dirname, join } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\n\n/**\n * Resolve the `@argos/cli` package root (the directory holding both\n * `package.json` and `assets/`) relative to the currently running module.\n *\n * Walks up from the calling module's directory. This works from compiled\n * `dist/**` (package root is the parent of `dist/`) and from `src/**` during\n * ts-node/vitest dev runs, without depending on a fixed nesting depth or a\n * build-time asset-copy step.\n */\nexport function resolvePackageRoot(fromUrl: string = import.meta.url): string {\n let dir = dirname(fileURLToPath(fromUrl));\n for (let i = 0; i < 8; i++) {\n if (existsSync(join(dir, \"package.json\")) && existsSync(join(dir, \"assets\"))) return dir;\n const parent = dirname(dir);\n if (parent === dir) break;\n dir = parent;\n }\n throw new Error(`Could not resolve the @argos/cli package root from ${fromUrl}`);\n}\n"],"names":["existsSync","dirname","join","fileURLToPath","resolvePackageRoot","fromUrl","url","dir","i","parent","Error"],"mappings":"AAAA,SAASA,UAAU,QAAQ,UAAU;AACrC,SAASC,OAAO,EAAEC,IAAI,QAAQ,YAAY;AAC1C,SAASC,aAAa,QAAQ,WAAW;AAEzC;;;;;;;;CAQC,GACD,OAAO,SAASC,mBAAmBC,UAAkB,YAAYC,GAAG;IAClE,IAAIC,MAAMN,QAAQE,cAAcE;IAChC,IAAK,IAAIG,IAAI,GAAGA,IAAI,GAAGA,IAAK;QAC1B,IAAIR,WAAWE,KAAKK,KAAK,oBAAoBP,WAAWE,KAAKK,KAAK,YAAY,OAAOA;QACrF,MAAME,SAASR,QAAQM;QACvB,IAAIE,WAAWF,KAAK;QACpBA,MAAME;IACR;IACA,MAAM,IAAIC,MAAM,CAAC,mDAAmD,EAAEL,SAAS;AACjF"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolve the Argos state directory (`~/.argos` by default).
|
|
3
|
+
* Overridable via `ARGOS_HOME` so tests never touch the real home directory.
|
|
4
|
+
*/
|
|
5
|
+
export declare function resolveArgosHome(): string;
|
|
6
|
+
/**
|
|
7
|
+
* Resolve the Claude Code config directory (`~/.claude` by default).
|
|
8
|
+
* Overridable via `CLAUDE_CONFIG_DIR` so tests never touch the real home directory.
|
|
9
|
+
*/
|
|
10
|
+
export declare function resolveClaudeDir(): string;
|
|
11
|
+
//# sourceMappingURL=paths.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"paths.d.ts","sourceRoot":"","sources":["../../src/lib/paths.ts"],"names":[],"mappings":"AAGA;;;GAGG;AACH,wBAAgB,gBAAgB,IAAI,MAAM,CAEzC;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,IAAI,MAAM,CAEzC"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { homedir } from "node:os";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
/**
|
|
4
|
+
* Resolve the Argos state directory (`~/.argos` by default).
|
|
5
|
+
* Overridable via `ARGOS_HOME` so tests never touch the real home directory.
|
|
6
|
+
*/ export function resolveArgosHome() {
|
|
7
|
+
return process.env.ARGOS_HOME || join(homedir(), ".argos");
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Resolve the Claude Code config directory (`~/.claude` by default).
|
|
11
|
+
* Overridable via `CLAUDE_CONFIG_DIR` so tests never touch the real home directory.
|
|
12
|
+
*/ export function resolveClaudeDir() {
|
|
13
|
+
return process.env.CLAUDE_CONFIG_DIR || join(homedir(), ".claude");
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
//# sourceMappingURL=paths.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/lib/paths.ts"],"sourcesContent":["import { homedir } from \"node:os\";\nimport { join } from \"node:path\";\n\n/**\n * Resolve the Argos state directory (`~/.argos` by default).\n * Overridable via `ARGOS_HOME` so tests never touch the real home directory.\n */\nexport function resolveArgosHome(): string {\n return process.env.ARGOS_HOME || join(homedir(), \".argos\");\n}\n\n/**\n * Resolve the Claude Code config directory (`~/.claude` by default).\n * Overridable via `CLAUDE_CONFIG_DIR` so tests never touch the real home directory.\n */\nexport function resolveClaudeDir(): string {\n return process.env.CLAUDE_CONFIG_DIR || join(homedir(), \".claude\");\n}\n"],"names":["homedir","join","resolveArgosHome","process","env","ARGOS_HOME","resolveClaudeDir","CLAUDE_CONFIG_DIR"],"mappings":"AAAA,SAASA,OAAO,QAAQ,UAAU;AAClC,SAASC,IAAI,QAAQ,YAAY;AAEjC;;;CAGC,GACD,OAAO,SAASC;IACd,OAAOC,QAAQC,GAAG,CAACC,UAAU,IAAIJ,KAAKD,WAAW;AACnD;AAEA;;;CAGC,GACD,OAAO,SAASM;IACd,OAAOH,QAAQC,GAAG,CAACG,iBAAiB,IAAIN,KAAKD,WAAW;AAC1D"}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* True when a hook `command` string points at an Argos-owned script (`.../hooks/argos-*`).
|
|
3
|
+
*
|
|
4
|
+
* This is a bare substring match — good enough for the non-destructive
|
|
5
|
+
* `doctor` diagnostic that uses it, but NOT anchored to any real directory.
|
|
6
|
+
* Destructive operations (deleting hook entries from the user's
|
|
7
|
+
* settings.json) must use `isArgosOwnedHookCommand` below instead, which
|
|
8
|
+
* requires the script to actually resolve inside the managed hooks
|
|
9
|
+
* directory.
|
|
10
|
+
*/
|
|
11
|
+
export declare function isArgosHookCommand(command: unknown): command is string;
|
|
12
|
+
export interface ArgosHookSpec {
|
|
13
|
+
/** Absolute path to the installed hook script, e.g. `<claudeDir>/hooks/argos-guard-destructive.sh`. */
|
|
14
|
+
scriptPath: string;
|
|
15
|
+
/** Claude Code hook matcher, e.g. "Bash". */
|
|
16
|
+
matcher: string;
|
|
17
|
+
/** Hook timeout in seconds (Claude Code's own outer bound on the hook process). */
|
|
18
|
+
timeout?: number;
|
|
19
|
+
statusMessage?: string;
|
|
20
|
+
}
|
|
21
|
+
export type SettingsMergeStatus = "created" | "updated" | "unchanged" | "error";
|
|
22
|
+
export interface SettingsMergeResult {
|
|
23
|
+
status: SettingsMergeStatus;
|
|
24
|
+
detail?: string;
|
|
25
|
+
}
|
|
26
|
+
export interface MergeHooksOptions {
|
|
27
|
+
/**
|
|
28
|
+
* Script paths whose entries should be stripped out of `hooks.PreToolUse`
|
|
29
|
+
* entirely (not overwritten with a new `specs` entry — REMOVED), wherever
|
|
30
|
+
* they're found. Used by `argos init` for a hook whose script write just
|
|
31
|
+
* failed: an entry pointing at a script that doesn't exist (or is broken)
|
|
32
|
+
* is a dangling PreToolUse entry that hard-blocks every Bash call, whether
|
|
33
|
+
* it was never written this run or used to work and just broke.
|
|
34
|
+
*/
|
|
35
|
+
removeScriptPaths?: string[];
|
|
36
|
+
/**
|
|
37
|
+
* Test-only seam: invoked immediately before the pre-write mtime re-check
|
|
38
|
+
* (see the concurrency guard below), so a test can simulate a concurrent
|
|
39
|
+
* writer racing this merge — mutate `settingsPath` from inside the
|
|
40
|
+
* callback, then let the merge proceed and observe it refuse. Never used
|
|
41
|
+
* outside tests.
|
|
42
|
+
*/
|
|
43
|
+
onBeforeWrite?: () => void;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Merge `specs` into `settingsPath` (normally `resolveClaudeDir()/settings.json`).
|
|
47
|
+
*
|
|
48
|
+
* - Missing file → starts from `{}` (created).
|
|
49
|
+
* - An existing hook entry whose command already targets a spec's
|
|
50
|
+
* `scriptPath` → overwritten in place, at its current array index (never
|
|
51
|
+
* moved, never removed).
|
|
52
|
+
* - No existing entry for a spec → appended into the first `PreToolUse`
|
|
53
|
+
* bucket whose `matcher` equals the spec's matcher, or a new bucket is
|
|
54
|
+
* pushed at the end of `PreToolUse` if none matches. Every other bucket,
|
|
55
|
+
* hook, and top-level key is left exactly as found.
|
|
56
|
+
* - `options.removeScriptPaths` → any existing entry whose command targets
|
|
57
|
+
* one of those paths is stripped out entirely (not replaced) — for a hook
|
|
58
|
+
* whose script write just failed, so it never leaves a dangling entry.
|
|
59
|
+
* - Corrupt JSON (unparsable, not an object, `hooks` present but not an
|
|
60
|
+
* object, or `hooks.PreToolUse` present but not an array) → returns
|
|
61
|
+
* `status: "error"` and writes NOTHING. Never clobber a file the caller
|
|
62
|
+
* can't safely reason about, and never silently discard a foreign `hooks`
|
|
63
|
+
* value just because its shape is unexpected.
|
|
64
|
+
* - Concurrency guard: the file's mtime is captured at read time and
|
|
65
|
+
* re-checked immediately before the write; if it moved, someone else wrote
|
|
66
|
+
* settings.json in the meantime and this call refuses with `status:
|
|
67
|
+
* "error"` rather than silently clobbering that write (last-writer-wins).
|
|
68
|
+
* - The write itself is atomic (temp file + rename, see
|
|
69
|
+
* lib/atomic-write.ts) — a crash mid-write can never leave settings.json
|
|
70
|
+
* torn, which would otherwise hard-block every subsequent Bash call.
|
|
71
|
+
*
|
|
72
|
+
* The file is always re-serialized with 2-space indentation on a write —
|
|
73
|
+
* this normalizes the user's original formatting/whitespace, but every key
|
|
74
|
+
* and value it did not own is preserved (structurally byte-identical
|
|
75
|
+
* content, not necessarily byte-identical bytes).
|
|
76
|
+
*/
|
|
77
|
+
export declare function mergeHooksIntoSettings(settingsPath: string, specs: ArgosHookSpec[], options?: MergeHooksOptions): SettingsMergeResult;
|
|
78
|
+
export type RemoveHooksStatus = "removed" | "unchanged" | "error";
|
|
79
|
+
export interface RemoveHooksResult {
|
|
80
|
+
status: RemoveHooksStatus;
|
|
81
|
+
detail?: string;
|
|
82
|
+
/** Count of Argos-owned hook entries actually removed (0 for "unchanged"/"error"). */
|
|
83
|
+
removedCount: number;
|
|
84
|
+
}
|
|
85
|
+
export interface RemoveHooksOptions {
|
|
86
|
+
/**
|
|
87
|
+
* Compute what WOULD be removed without writing anything — `argos remove`'s
|
|
88
|
+
* preview mode. Corrupt-JSON refusal still applies identically in dry-run:
|
|
89
|
+
* a caller must not be told "removed" for a file it can't safely reason
|
|
90
|
+
* about.
|
|
91
|
+
*/
|
|
92
|
+
dryRun?: boolean;
|
|
93
|
+
/** Same test-only seam as `MergeHooksOptions.onBeforeWrite` above. */
|
|
94
|
+
onBeforeWrite?: () => void;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Symmetric counterpart to `mergeHooksIntoSettings`: strips every Argos-owned
|
|
98
|
+
* hook entry (any command matched by `isArgosOwnedHookCommand` against
|
|
99
|
+
* `hooksDir`, not just the current `HOOK_IDS`) out of `hooks.PreToolUse`,
|
|
100
|
+
* wherever it's found. Foreign hooks, buckets, and every other top-level key
|
|
101
|
+
* are left exactly as found — same ownership/refusal contract as the merge
|
|
102
|
+
* path:
|
|
103
|
+
* - `hooksDir` is the resolved `<claudeDir>/hooks` directory this install
|
|
104
|
+
* manages (normally `join(resolveClaudeDir(), "hooks")`). A hook is only
|
|
105
|
+
* ever treated as Argos-owned — and therefore deletable — when its script
|
|
106
|
+
* path actually resolves inside `hooksDir` AND its basename starts with
|
|
107
|
+
* `argos-`; this is deliberately NOT the same as `isArgosHookCommand`'s
|
|
108
|
+
* bare `/hooks/argos-` substring match, which would also match (and
|
|
109
|
+
* delete) an unrelated user hook at a path like
|
|
110
|
+
* `/Users/x/my/hooks/argos-custom.sh`.
|
|
111
|
+
* - Missing file, or no `hooks`/`hooks.PreToolUse` at all → "unchanged",
|
|
112
|
+
* nothing to remove.
|
|
113
|
+
* - Corrupt JSON (unparsable, not an object, `hooks` present but not an
|
|
114
|
+
* object, or `hooks.PreToolUse` present but not an array) → `status:
|
|
115
|
+
* "error"`, writes NOTHING. Never guess at a shape it can't verify.
|
|
116
|
+
* - A `PreToolUse` bucket that becomes empty after removing Argos's own
|
|
117
|
+
* entries is dropped entirely (it only ever existed to hold them — see
|
|
118
|
+
* `mergeHooksIntoSettings`, which creates a fresh bucket per matcher on
|
|
119
|
+
* demand); a bucket that still has foreign hooks left (including one with
|
|
120
|
+
* ZERO Argos-owned hooks in it) keeps its matcher, its hooks, and its
|
|
121
|
+
* position untouched.
|
|
122
|
+
* - Same mtime concurrency guard and atomic write as the merge path.
|
|
123
|
+
*/
|
|
124
|
+
export declare function removeAllArgosHooksFromSettings(settingsPath: string, hooksDir: string, options?: RemoveHooksOptions): RemoveHooksResult;
|
|
125
|
+
//# sourceMappingURL=settings-merge.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"settings-merge.d.ts","sourceRoot":"","sources":["../../src/lib/settings-merge.ts"],"names":[],"mappings":"AAsBA;;;;;;;;;GASG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,IAAI,MAAM,CAEtE;AAqCD,MAAM,WAAW,aAAa;IAC5B,uGAAuG;IACvG,UAAU,EAAE,MAAM,CAAC;IACnB,6CAA6C;IAC7C,OAAO,EAAE,MAAM,CAAC;IAChB,mFAAmF;IACnF,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,MAAM,mBAAmB,GAAG,SAAS,GAAG,SAAS,GAAG,WAAW,GAAG,OAAO,CAAC;AAEhF,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,mBAAmB,CAAC;IAC5B,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,iBAAiB;IAChC;;;;;;;OAOG;IACH,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC7B;;;;;;OAMG;IACH,aAAa,CAAC,EAAE,MAAM,IAAI,CAAC;CAC5B;AAiBD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,wBAAgB,sBAAsB,CACpC,YAAY,EAAE,MAAM,EACpB,KAAK,EAAE,aAAa,EAAE,EACtB,OAAO,GAAE,iBAAsB,GAC9B,mBAAmB,CA8HrB;AAED,MAAM,MAAM,iBAAiB,GAAG,SAAS,GAAG,WAAW,GAAG,OAAO,CAAC;AAElE,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,iBAAiB,CAAC;IAC1B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,sFAAsF;IACtF,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,kBAAkB;IACjC;;;;;OAKG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,sEAAsE;IACtE,aAAa,CAAC,EAAE,MAAM,IAAI,CAAC;CAC5B;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,wBAAgB,+BAA+B,CAC7C,YAAY,EAAE,MAAM,EACpB,QAAQ,EAAE,MAAM,EAChB,OAAO,GAAE,kBAAuB,GAC/B,iBAAiB,CAmGnB"}
|