@shinaps/skills 0.44.0 → 0.45.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/dist/lib/managed-session.js +40 -17
- package/dist/lib/managed-snapshot.js +175 -0
- package/dist/lib/managed.js +42 -6
- package/docs/install.md +1 -1
- package/package.json +1 -1
- package/plugins/shinaps-claude/.claude-plugin/plugin.json +1 -1
- package/plugins/shinaps-claude/managed-agents/skills/dev-managed-cc/references/launch.md +4 -0
- package/plugins/shinaps-claude/references/linear/configuration.md +1 -1
- package/plugins/shinaps-claude/references/lint.md +1 -1
- package/plugins/shinaps-claude/references/managed/configuration.md +1 -1
- package/plugins/shinaps-claude/references/managed/configuration.schema.json +1 -1
- package/plugins/shinaps-claude/references/managed/execute.md +26 -2
- package/plugins/shinaps-claude/references/rule-writing.md +1 -1
- package/plugins/shinaps-claude/references/rules.md +16 -3
- package/plugins/shinaps-claude/skills/explore/SKILL.md +1 -1
- package/plugins/shinaps-claude/skills/extract-rules/SKILL.md +2 -2
- package/plugins/shinaps-claude/skills/managed-execute/SKILL.md +8 -2
- package/plugins/shinaps-claude/skills/refine/SKILL.md +1 -1
- package/plugins/shinaps-codex/.codex-plugin/plugin.json +1 -1
- package/plugins/shinaps-codex/managed-agents/skills/dev-managed-cc/references/launch.md +4 -0
- package/plugins/shinaps-codex/references/linear/configuration.md +1 -1
- package/plugins/shinaps-codex/references/lint.md +1 -1
- package/plugins/shinaps-codex/references/managed/configuration.md +1 -1
- package/plugins/shinaps-codex/references/managed/configuration.schema.json +1 -1
- package/plugins/shinaps-codex/references/managed/execute.md +26 -2
- package/plugins/shinaps-codex/references/rule-writing.md +1 -1
- package/plugins/shinaps-codex/references/rules.md +16 -3
- package/plugins/shinaps-codex/skills/extract-rules/SKILL.md +1 -1
- package/plugins/shinaps-codex/skills/managed-execute/SKILL.md +8 -2
|
@@ -4,6 +4,7 @@ import { join, resolve } from 'node:path';
|
|
|
4
4
|
import { readJson, readJsonIfExists, writeFileAtomic } from "./fs.js";
|
|
5
5
|
import { withLock } from "./lock.js";
|
|
6
6
|
import { filesBeta, managedBeta } from "./managed-ant.js";
|
|
7
|
+
import { importSnapshot } from "./managed-snapshot.js";
|
|
7
8
|
export const staffings = ['LITE', 'BASIC', 'ULTRA'];
|
|
8
9
|
export const permissions = ['commit', 'push', 'draft_pr'];
|
|
9
10
|
const leadKey = './agents/shinaps-dev-managed-cc.md';
|
|
@@ -64,27 +65,28 @@ export async function runSession(account, request, options) {
|
|
|
64
65
|
};
|
|
65
66
|
// 作成より前に記録する。作成後に記録できずに終わっても、次の実行は止まり、重複して作らない。
|
|
66
67
|
await writeFileAtomic(recordPath, JSON.stringify(record, null, 2) + '\n');
|
|
67
|
-
const resources =
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
68
|
+
const resources = request.snapshot
|
|
69
|
+
? []
|
|
70
|
+
: [
|
|
71
|
+
{
|
|
72
|
+
type: 'github_repository',
|
|
73
|
+
url: request.repository.url,
|
|
74
|
+
mount_path: request.repository.mount_path,
|
|
75
|
+
...(options.githubToken ? { authorization_token: options.githubToken } : {}),
|
|
76
|
+
checkout: { type: 'commit', sha: request.base_sha },
|
|
77
|
+
},
|
|
78
|
+
];
|
|
79
|
+
// upload は記録の後に行う。記録の前だと、途中で止まった実行の再実行のたびに Files が増える。
|
|
80
|
+
const upload = async (file, mountPath) => {
|
|
79
81
|
try {
|
|
80
|
-
fileId = await readId(ant({
|
|
82
|
+
const fileId = await readId(ant({
|
|
81
83
|
// セッションの Files と同じ beta の Files API に上げる。パスは絶対パスにする (ant は `-` を
|
|
82
84
|
// stdin と解釈する)。
|
|
83
85
|
argv: [
|
|
84
86
|
'beta:files',
|
|
85
87
|
'upload',
|
|
86
88
|
'--file',
|
|
87
|
-
resolve(
|
|
89
|
+
resolve(file),
|
|
88
90
|
...filesBeta,
|
|
89
91
|
'--transform',
|
|
90
92
|
'id',
|
|
@@ -92,14 +94,18 @@ export async function runSession(account, request, options) {
|
|
|
92
94
|
],
|
|
93
95
|
apiKey: account.apiKey,
|
|
94
96
|
}), 'ant files upload');
|
|
97
|
+
resources.push({ type: 'file', file_id: fileId, mount_path: mountPath });
|
|
95
98
|
}
|
|
96
99
|
catch (error) {
|
|
97
100
|
// セッションはまだ作っていないので、記録を残すと同じ依頼の再実行が不要に止まる。
|
|
98
101
|
await rm(recordPath, { force: true });
|
|
99
102
|
throw error;
|
|
100
103
|
}
|
|
101
|
-
|
|
102
|
-
|
|
104
|
+
};
|
|
105
|
+
if (request.snapshot)
|
|
106
|
+
await upload(request.snapshot.bundle, '/source.bundle');
|
|
107
|
+
if (options.claudeMdPath)
|
|
108
|
+
await upload(options.claudeMdPath, '/CLAUDE.md');
|
|
103
109
|
const body = {
|
|
104
110
|
agent,
|
|
105
111
|
environment_id: environment,
|
|
@@ -140,6 +146,16 @@ function initialMessage(request) {
|
|
|
140
146
|
`必須検証(任意): ${request.verify ?? '指定なし。リポジトリの規約に従う'}`,
|
|
141
147
|
`author(任意): ${request.author ?? '指定なし'}`,
|
|
142
148
|
...(request.claude_md === null ? [] : ['利用者 CLAUDE.md: /mnt/session/uploads/CLAUDE.md']),
|
|
149
|
+
...(request.snapshot
|
|
150
|
+
? [
|
|
151
|
+
'ローカルコードの受け渡し: GitHubのcheckoutの代わりに /mnt/session/uploads/source.bundle を使用する。',
|
|
152
|
+
`作業前に git clone /mnt/session/uploads/source.bundle ${JSON.stringify(request.repository.mount_path)} を実行する。`,
|
|
153
|
+
`比較元は送信時点のスナップショット ${request.snapshot.id}。このSHAからheadブランチを作り作業する。`,
|
|
154
|
+
'このスナップショットには元のGit履歴・remote・資格情報は含まれない。pushとPRは行わない。',
|
|
155
|
+
`完了時はlaunch.mdの一時indexによる出力手順に従い、新規ファイルを含む ${request.snapshot.id} からの全差分を /mnt/session/outputs/changes.patch に書く。`,
|
|
156
|
+
'変更内容・検証結果・未完了事項を /mnt/session/outputs/report.md に書き、ローカルで再開できるようにする。',
|
|
157
|
+
]
|
|
158
|
+
: []),
|
|
143
159
|
].join('\n');
|
|
144
160
|
}
|
|
145
161
|
/**
|
|
@@ -152,10 +168,17 @@ async function requireRecordedSession(account, sessionId) {
|
|
|
152
168
|
for (const name of names.filter((entry) => entry.endsWith('.json'))) {
|
|
153
169
|
const record = await readJson(join(directory, name));
|
|
154
170
|
if (record.session_id === sessionId)
|
|
155
|
-
return;
|
|
171
|
+
return record;
|
|
156
172
|
}
|
|
157
173
|
throw new Error(`Session ${sessionId} was not created by managed run for account ${account.name}.`);
|
|
158
174
|
}
|
|
175
|
+
/** 回収済みのローカル snapshot のセッションを、APIキーを使わずに新しいディレクトリへ復元する。 */
|
|
176
|
+
export async function importSession(account, sessionId, destination) {
|
|
177
|
+
const record = await requireRecordedSession(account, sessionId);
|
|
178
|
+
if (!record.request.snapshot)
|
|
179
|
+
throw new Error('This session did not use a local snapshot.');
|
|
180
|
+
return importSnapshot(record.request.snapshot, join(account.dir, 'sessions', sessionId, 'outputs', 'changes.patch'), destination);
|
|
181
|
+
}
|
|
159
182
|
/** セッションの状態と、lead の最後のメッセージ (1 行目が `状態: 完了|質問|未完了`) を返す。 */
|
|
160
183
|
export async function sessionStatus(account, sessionId, ant) {
|
|
161
184
|
await requireRecordedSession(account, sessionId);
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
import { execFile } from 'node:child_process';
|
|
2
|
+
import { promisify } from 'node:util';
|
|
3
|
+
import { copyFile, lstat, mkdir, mkdtemp, readFile, readlink, realpath, rename, rm, symlink, chmod, } from 'node:fs/promises';
|
|
4
|
+
import { dirname, isAbsolute, join, relative, resolve, sep } from 'node:path';
|
|
5
|
+
import { readJson, writeFileAtomic } from "./fs.js";
|
|
6
|
+
const execute = promisify(execFile);
|
|
7
|
+
// 呼び出し元の index の上書き・hooks・filter・署名を snapshot に持ち込まない。
|
|
8
|
+
async function git(cwd, args, configuration = 'snapshot') {
|
|
9
|
+
const env = Object.fromEntries(Object.entries(process.env).filter(([key]) => !key.startsWith('GIT_') || (configuration === 'source' && key.startsWith('GIT_CONFIG_'))));
|
|
10
|
+
const { stdout } = await execute('git', ['-c', 'core.hooksPath=/dev/null', '-c', 'core.autocrlf=false', ...args], {
|
|
11
|
+
cwd,
|
|
12
|
+
maxBuffer: 32 * 1024 * 1024,
|
|
13
|
+
env: {
|
|
14
|
+
...env,
|
|
15
|
+
...(configuration === 'snapshot'
|
|
16
|
+
? { GIT_CONFIG_NOSYSTEM: '1', GIT_CONFIG_GLOBAL: '/dev/null' }
|
|
17
|
+
: {}),
|
|
18
|
+
GIT_AUTHOR_NAME: 'Managed Snapshot',
|
|
19
|
+
GIT_AUTHOR_EMAIL: 'snapshot@example.invalid',
|
|
20
|
+
GIT_COMMITTER_NAME: 'Managed Snapshot',
|
|
21
|
+
GIT_COMMITTER_EMAIL: 'snapshot@example.invalid',
|
|
22
|
+
GIT_AUTHOR_DATE: '2000-01-01T00:00:00Z',
|
|
23
|
+
GIT_COMMITTER_DATE: '2000-01-01T00:00:00Z',
|
|
24
|
+
},
|
|
25
|
+
});
|
|
26
|
+
return stdout;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* 元の index を変えずに、現在のソースのファイルを1コミットだけの独立したリポジトリへ固定する。
|
|
30
|
+
* @param source Git の checkout のルート。未コミットの変更と、無視されていない新規ファイルを含めて送る。
|
|
31
|
+
* @param root checkout の外にある、snapshot の非公開の保存先。
|
|
32
|
+
*/
|
|
33
|
+
export async function prepareSnapshot(source, root) {
|
|
34
|
+
source = await realpath(source);
|
|
35
|
+
const top = (await git(source, ['rev-parse', '--show-toplevel'], 'source')).trim();
|
|
36
|
+
if ((await realpath(top)) !== source)
|
|
37
|
+
throw new Error('Snapshot source must be the Git checkout root.');
|
|
38
|
+
await mkdir(root, { recursive: true, mode: 0o700 });
|
|
39
|
+
root = await realpath(root);
|
|
40
|
+
if (root === source || root.startsWith(source + sep))
|
|
41
|
+
throw new Error('Snapshot storage must be outside the source checkout.');
|
|
42
|
+
const temporary = await mkdtemp(join(root, '.snapshot-'));
|
|
43
|
+
try {
|
|
44
|
+
const repository = join(temporary, 'repository');
|
|
45
|
+
await mkdir(repository);
|
|
46
|
+
// 方式を固定する。git の既定が変わっても、readSnapshot が受け付ける 40 桁の id にする。
|
|
47
|
+
await git(repository, ['init', '--object-format=sha1', '--initial-branch=snapshot']);
|
|
48
|
+
const candidates = [
|
|
49
|
+
...new Set((await git(source, ['ls-files', '-z', '--cached', '--others', '--exclude-standard'], 'source'))
|
|
50
|
+
.split('\0')
|
|
51
|
+
.filter(Boolean)),
|
|
52
|
+
].sort();
|
|
53
|
+
const files = [];
|
|
54
|
+
for (const name of candidates) {
|
|
55
|
+
// ローカルの運用状態・資格情報・Git の内部・生成された依存物は送らない。
|
|
56
|
+
const parts = name.split('/');
|
|
57
|
+
if (parts.some((part) => ['.git', '.shinaps', 'node_modules', '.local'].includes(part)))
|
|
58
|
+
continue;
|
|
59
|
+
if (parts.some((part) => /^\.env(?:\.|$)/.test(part) && !/\.(example|sample|template)$/.test(part)) ||
|
|
60
|
+
/\.(pem|p12|pfx|key)$/i.test(name)) {
|
|
61
|
+
throw new Error(`Remove credential file from the snapshot source before sending: ${name}`);
|
|
62
|
+
}
|
|
63
|
+
const input = join(source, name);
|
|
64
|
+
let info;
|
|
65
|
+
try {
|
|
66
|
+
info = await lstat(input);
|
|
67
|
+
}
|
|
68
|
+
catch (error) {
|
|
69
|
+
if (error.code === 'ENOENT')
|
|
70
|
+
continue;
|
|
71
|
+
throw error;
|
|
72
|
+
}
|
|
73
|
+
if (!info.isFile() && !info.isSymbolicLink())
|
|
74
|
+
throw new Error(`Snapshot does not support directories or submodules as files: ${name}`);
|
|
75
|
+
// 親ディレクトリも確かめる。途中のディレクトリを差し替えると、checkout 外のファイルを読めてしまう。
|
|
76
|
+
const parent = await realpath(dirname(input));
|
|
77
|
+
if (parent !== source && !parent.startsWith(source + sep))
|
|
78
|
+
throw new Error(`Snapshot path leaves the source checkout: ${name}`);
|
|
79
|
+
const output = join(repository, name);
|
|
80
|
+
await mkdir(dirname(output), { recursive: true });
|
|
81
|
+
if (info.isSymbolicLink()) {
|
|
82
|
+
const target = await readlink(input);
|
|
83
|
+
const destination = resolve(dirname(input), target);
|
|
84
|
+
if (target.startsWith('/') ||
|
|
85
|
+
(destination !== source && !destination.startsWith(source + sep)))
|
|
86
|
+
throw new Error(`Snapshot symlink leaves the source checkout: ${name}`);
|
|
87
|
+
await symlink(target, output);
|
|
88
|
+
}
|
|
89
|
+
else {
|
|
90
|
+
await copyFile(input, output);
|
|
91
|
+
await chmod(output, info.mode & 0o111 ? 0o755 : 0o644);
|
|
92
|
+
}
|
|
93
|
+
files.push(name);
|
|
94
|
+
}
|
|
95
|
+
await git(repository, ['add', '--all', '--force']);
|
|
96
|
+
await git(repository, [
|
|
97
|
+
'-c',
|
|
98
|
+
'commit.gpgsign=false',
|
|
99
|
+
'commit',
|
|
100
|
+
'--allow-empty',
|
|
101
|
+
'-m',
|
|
102
|
+
'Local source snapshot',
|
|
103
|
+
]);
|
|
104
|
+
const id = (await git(repository, ['rev-parse', 'HEAD'])).trim();
|
|
105
|
+
const directory = join(root, id);
|
|
106
|
+
await git(repository, ['bundle', 'create', join(temporary, 'source.bundle'), 'HEAD']);
|
|
107
|
+
const snapshot = {
|
|
108
|
+
id,
|
|
109
|
+
source,
|
|
110
|
+
directory,
|
|
111
|
+
bundle: join(directory, 'source.bundle'),
|
|
112
|
+
files,
|
|
113
|
+
};
|
|
114
|
+
await writeFileAtomic(join(temporary, 'snapshot.json'), JSON.stringify(snapshot, null, 2) + '\n');
|
|
115
|
+
try {
|
|
116
|
+
await rename(temporary, directory);
|
|
117
|
+
}
|
|
118
|
+
catch (error) {
|
|
119
|
+
if (!['EEXIST', 'ENOTEMPTY'].includes(error.code ?? ''))
|
|
120
|
+
throw error;
|
|
121
|
+
// 同じ内容は別の checkout からも作られうるが、比較元としては同じ tree である。
|
|
122
|
+
return readSnapshot(join(directory, 'snapshot.json'));
|
|
123
|
+
}
|
|
124
|
+
return snapshot;
|
|
125
|
+
}
|
|
126
|
+
finally {
|
|
127
|
+
await rm(temporary, { recursive: true, force: true });
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* prepare が書いた manifest だけを受け付ける。id は git の引数と lead への指示文に、bundle は upload に
|
|
132
|
+
* 使うので、別のファイルを渡された場合にそのまま使わない。
|
|
133
|
+
* @param manifest prepare が出力した `snapshot.json` のパス。
|
|
134
|
+
*/
|
|
135
|
+
export async function readSnapshot(manifest) {
|
|
136
|
+
const { id, source, directory, bundle, files } = (await readJson(manifest)) ?? {};
|
|
137
|
+
if (typeof id !== 'string' ||
|
|
138
|
+
!/^[0-9a-f]{40}$/.test(id) ||
|
|
139
|
+
typeof source !== 'string' ||
|
|
140
|
+
typeof directory !== 'string' ||
|
|
141
|
+
!isAbsolute(directory) ||
|
|
142
|
+
bundle !== join(directory, 'source.bundle') ||
|
|
143
|
+
!Array.isArray(files) ||
|
|
144
|
+
!files.every((file) => typeof file === 'string'))
|
|
145
|
+
throw new Error(`Not a snapshot manifest written by managed prepare: ${manifest}`);
|
|
146
|
+
return { id, source, directory, bundle, files };
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* 比較元と差分を新しいディレクトリに復元する。元の checkout は変更しない。
|
|
150
|
+
* @param snapshot リモートのセッションが使った、保存済みの比較元。
|
|
151
|
+
* @param patch 回収した lead の差分。比較元からの全体の差分。
|
|
152
|
+
* @param destination 復元先の新しいディレクトリ。既に存在してはいけない。
|
|
153
|
+
*/
|
|
154
|
+
export async function importSnapshot(snapshot, patch, destination) {
|
|
155
|
+
destination = resolve(destination);
|
|
156
|
+
await mkdir(destination, { mode: 0o700 });
|
|
157
|
+
try {
|
|
158
|
+
await git(destination, [
|
|
159
|
+
'clone',
|
|
160
|
+
'--no-local',
|
|
161
|
+
'--',
|
|
162
|
+
join(snapshot.directory, 'repository'),
|
|
163
|
+
'.',
|
|
164
|
+
]);
|
|
165
|
+
await git(destination, ['checkout', '--detach', snapshot.id]);
|
|
166
|
+
const content = await readFile(patch);
|
|
167
|
+
if (content.length)
|
|
168
|
+
await git(destination, ['apply', '--index', '--', resolve(patch)]);
|
|
169
|
+
return destination;
|
|
170
|
+
}
|
|
171
|
+
catch (error) {
|
|
172
|
+
await rm(destination, { recursive: true, force: true });
|
|
173
|
+
throw error;
|
|
174
|
+
}
|
|
175
|
+
}
|
package/dist/lib/managed.js
CHANGED
|
@@ -1,21 +1,26 @@
|
|
|
1
1
|
import { readFile } from 'node:fs/promises';
|
|
2
2
|
import { homedir } from 'node:os';
|
|
3
|
+
import { join } from 'node:path';
|
|
3
4
|
import { errorMessage } from "./fs.js";
|
|
4
5
|
import { managedAccountDir, managedRoot, packageRoot } from "./layout.js";
|
|
5
6
|
import { antCommand } from "./managed-ant.js";
|
|
6
7
|
import { validateManagedConfig } from "./managed-config.js";
|
|
7
|
-
import { collectOutputs, permissions, replySession, runSession, sessionStatus, staffings, } from "./managed-session.js";
|
|
8
|
+
import { collectOutputs, importSession, permissions, replySession, runSession, sessionStatus, staffings, } from "./managed-session.js";
|
|
8
9
|
import { setupAccount } from "./managed-setup.js";
|
|
10
|
+
import { prepareSnapshot, readSnapshot } from "./managed-snapshot.js";
|
|
9
11
|
const managedUsage = [
|
|
10
12
|
'Usage: shinaps-skills managed setup --account <name> [--account <name> ...] [--apply]',
|
|
11
|
-
' shinaps-skills managed run <managed.json> --account <name> --base <branch> --base-sha <sha> --head <branch> --staffing <LITE|BASIC|ULTRA> --allow <none|commit,push,draft_pr> --budget-cents <n> --task-file <file> [--verify <command>] [--author "<name> <email>"] [--title <title>] [--claude-md <file>]',
|
|
13
|
+
' shinaps-skills managed run <managed.json> --account <name> --base <branch> (--base-sha <sha> | --snapshot <manifest>) --head <branch> --staffing <LITE|BASIC|ULTRA> --allow <none|commit,push,draft_pr> --budget-cents <n> --task-file <file> [--verify <command>] [--author "<name> <email>"] [--title <title>] [--claude-md <file>]',
|
|
12
14
|
' shinaps-skills managed status --account <name> --session <id>',
|
|
13
15
|
' shinaps-skills managed reply --account <name> --session <id> --message-file <file>',
|
|
14
16
|
' shinaps-skills managed collect --account <name> --session <id>',
|
|
17
|
+
' shinaps-skills managed prepare --source <local-checkout-root>',
|
|
18
|
+
' shinaps-skills managed import --account <name> --session <id> --destination <new-directory>',
|
|
15
19
|
'API keys: --api-key <name>=<key>, or SHINAPS_MANAGED_API_KEY_<NAME> (prefer the environment variable; arguments are visible to other processes).',
|
|
16
20
|
'GitHub token for the mounted repository: SHINAPS_MANAGED_GITHUB_TOKEN.',
|
|
21
|
+
'Local source: prepare lists the frozen files and manifest; pass --snapshot <manifest> to run instead of --base-sha. Local snapshots do not push or create PRs.',
|
|
17
22
|
].join('\n');
|
|
18
|
-
const accountName = /^[a-z0-9]
|
|
23
|
+
const accountName = /^[a-z0-9][a-z0-9-]*$/;
|
|
19
24
|
const githubTokenVariable = 'SHINAPS_MANAGED_GITHUB_TOKEN';
|
|
20
25
|
const valueFlags = new Set([
|
|
21
26
|
'account',
|
|
@@ -33,12 +38,15 @@ const valueFlags = new Set([
|
|
|
33
38
|
'claude-md',
|
|
34
39
|
'session',
|
|
35
40
|
'message-file',
|
|
41
|
+
'source',
|
|
42
|
+
'snapshot',
|
|
43
|
+
'destination',
|
|
36
44
|
]);
|
|
37
45
|
const repeatableFlags = new Set(['account', 'api-key']);
|
|
38
46
|
/** アカウント名ごとのキーの環境変数名。名前に `_` を許さないので、名前と一対一に対応する。 */
|
|
39
47
|
const apiKeyVariable = (account) => `SHINAPS_MANAGED_API_KEY_${account.toUpperCase().replaceAll('-', '_')}`;
|
|
40
48
|
/**
|
|
41
|
-
* `shinaps-skills managed <setup|run|status|reply|collect>` を実行する。出力とエラー文から、渡された
|
|
49
|
+
* `shinaps-skills managed <setup|run|status|reply|collect|prepare|import>` を実行する。出力とエラー文から、渡された
|
|
42
50
|
* APIキーと GitHub token の値を伏字にする。
|
|
43
51
|
*/
|
|
44
52
|
export async function managed(args, options = {}) {
|
|
@@ -74,6 +82,25 @@ async function dispatch(args, context) {
|
|
|
74
82
|
const home = context.home ?? homedir();
|
|
75
83
|
const ant = context.ant ?? antCommand(context.env);
|
|
76
84
|
const accounts = flags.get('account') ?? [];
|
|
85
|
+
if (command === 'prepare') {
|
|
86
|
+
if (apply || positional.length || accounts.length)
|
|
87
|
+
throw usageError();
|
|
88
|
+
const snapshot = await prepareSnapshot(required(flags, 'source'), join(managedRoot(home), 'snapshots'));
|
|
89
|
+
context.log(JSON.stringify({ ...snapshot, manifest: join(snapshot.directory, 'snapshot.json') }, null, 2));
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
if (command === 'import') {
|
|
93
|
+
if (apply || positional.length || accounts.length !== 1 || !accountName.test(accounts[0]))
|
|
94
|
+
throw usageError();
|
|
95
|
+
const account = { name: accounts[0], dir: managedAccountDir(managedRoot(home), accounts[0]) };
|
|
96
|
+
const sessionId = required(flags, 'session');
|
|
97
|
+
context.log(JSON.stringify({
|
|
98
|
+
account: account.name,
|
|
99
|
+
session_id: sessionId,
|
|
100
|
+
directory: await importSession(account, sessionId, required(flags, 'destination')),
|
|
101
|
+
}, null, 2));
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
77
104
|
if (command === 'setup') {
|
|
78
105
|
if (positional.length || !accounts.length)
|
|
79
106
|
throw usageError();
|
|
@@ -164,12 +191,19 @@ async function run(configPath, account, flags, context, ant) {
|
|
|
164
191
|
}
|
|
165
192
|
const optional = (flag) => flags.get(flag)?.[0] ?? null;
|
|
166
193
|
const claudeMdPath = optional('claude-md');
|
|
194
|
+
const snapshotPath = optional('snapshot');
|
|
195
|
+
if (snapshotPath && flags.has('base-sha'))
|
|
196
|
+
throw new Error('Use either --base-sha or --snapshot.');
|
|
197
|
+
const snapshot = snapshotPath ? await readSnapshot(snapshotPath) : undefined;
|
|
198
|
+
if (snapshot && (allow.includes('push') || allow.includes('draft_pr'))) {
|
|
199
|
+
throw new Error('Local snapshots support local result recovery; push and draft_pr are not available.');
|
|
200
|
+
}
|
|
167
201
|
const request = {
|
|
168
202
|
account: account.name,
|
|
169
203
|
repository: config.repository,
|
|
170
204
|
vault_id: settings.vault_id,
|
|
171
205
|
base: required(flags, 'base'),
|
|
172
|
-
base_sha: required(flags, 'base-sha'),
|
|
206
|
+
base_sha: snapshot?.id ?? required(flags, 'base-sha'),
|
|
173
207
|
head: required(flags, 'head'),
|
|
174
208
|
staffing: staffing,
|
|
175
209
|
allow,
|
|
@@ -179,6 +213,7 @@ async function run(configPath, account, flags, context, ant) {
|
|
|
179
213
|
author: optional('author'),
|
|
180
214
|
title: optional('title'),
|
|
181
215
|
claude_md: claudeMdPath === null ? null : await readFile(claudeMdPath, 'utf8'),
|
|
216
|
+
...(snapshot ? { snapshot } : {}),
|
|
182
217
|
};
|
|
183
218
|
const sessionId = await runSession(account, request, {
|
|
184
219
|
ant,
|
|
@@ -188,6 +223,7 @@ async function run(configPath, account, flags, context, ant) {
|
|
|
188
223
|
now: context.now ?? (() => new Date()),
|
|
189
224
|
});
|
|
190
225
|
context.log(`Observe: shinaps-skills managed status --account ${account.name} --session ${sessionId}`);
|
|
226
|
+
context.log(JSON.stringify({ account: account.name, session_id: sessionId }));
|
|
191
227
|
}
|
|
192
228
|
function resolveAccounts(names, flags, env, home) {
|
|
193
229
|
const keys = new Map();
|
|
@@ -200,7 +236,7 @@ function resolveAccounts(names, flags, env, home) {
|
|
|
200
236
|
const unique = [...new Set(names)];
|
|
201
237
|
const invalid = unique.filter((name) => !accountName.test(name));
|
|
202
238
|
if (invalid.length) {
|
|
203
|
-
throw new Error(`Invalid account name: ${invalid.join(', ')}. Use lowercase letters, digits and
|
|
239
|
+
throw new Error(`Invalid account name: ${invalid.join(', ')}. Use lowercase letters, digits and hyphens, starting with a letter or digit.`);
|
|
204
240
|
}
|
|
205
241
|
const missing = unique.filter((name) => !(keys.get(name) || env[apiKeyVariable(name)]));
|
|
206
242
|
if (missing.length) {
|
package/docs/install.md
CHANGED
|
@@ -26,7 +26,7 @@ npx @shinaps/skills@latest recover codex
|
|
|
26
26
|
設定の検証(`validate-linear-config`・`validate-managed-config`)、Managed Agents の操作(`managed`)、規約の機械検査(`lint`、[規約の機械検査](lint.md))には、配布 CLI `shinaps-skills` を使います。CLI は plugin に含まれないため、利用中のスキルと同じ版を導入します。
|
|
27
27
|
|
|
28
28
|
```sh
|
|
29
|
-
npm install -g @shinaps/skills@0.
|
|
29
|
+
npm install -g @shinaps/skills@0.45.0
|
|
30
30
|
shinaps-skills --version
|
|
31
31
|
```
|
|
32
32
|
|
package/package.json
CHANGED
|
@@ -71,3 +71,7 @@ PR: <draft PR の URL。無ければ なし>
|
|
|
71
71
|
| hooks | 未対応 |
|
|
72
72
|
| permission mode | 定義の `permission_policy` で固定 |
|
|
73
73
|
| セッション予算 | セッション作成時の `max_list_cost` で付ける |
|
|
74
|
+
|
|
75
|
+
## ローカルsnapshotで始める場合
|
|
76
|
+
|
|
77
|
+
初回メッセージにsource.bundleが指定されている場合は、通常のHEAD確認より先に `/mnt/session/uploads/source.bundle` を指定された作業先へcloneする。比較元は初回メッセージのsnapshot SHAとし、GitHubへのアクセスや元の履歴を前提にしない。成果物は上記の一時indexで比較元からの全差分を出力する手順に従い、変更内容・検証結果・未完了事項を `/mnt/session/outputs/report.md` にも残す。
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
## 検証CLIと契約版
|
|
6
6
|
|
|
7
|
-
利用中のskillとCLIの版を確認し、0.
|
|
7
|
+
利用中のskillとCLIの版を確認し、0.45.0のskillには同じ版のnpm CLIを導入する(`npm install -g @shinaps/skills@0.45.0`)。pluginだけにはCLIは含まれない。導入・更新が必要ならその権限を確認し、検証のたびに `npx latest` を取得しない。
|
|
8
8
|
|
|
9
9
|
0.35.0から通常設定の `review_flow.mode` に `ask` を受理する。`config_version` は2のままで、既存のauto・manualの設定は変更不要。0.33.0〜0.34.xのCLIと `$schema` のURLは `ask` を拒否するため、`ask` を使う設定では `$schema` も0.35.0以降のURLにする。
|
|
10
10
|
|
|
@@ -31,4 +31,4 @@ Shinaps AIの規約は、機械検査(`checks`)を持てる。検査は配
|
|
|
31
31
|
- **使えるルール**: ESLint core、`@typescript-eslint/*`(型情報を使うものを含む)、`vue/*`、同梱の `shinaps/*`(`docs/lint.md` の一覧)。版によって違うので、同じ版のCLIで確かめる。使えるルールが無い規約には検査を付けない(AI照合のまま)。同梱ルールの追加は `docs/lint.md` の手順で別の作業として提案する。
|
|
32
32
|
- **coverageの判断**: 規約の全文(scope・labelsを含み、更新では更新後の全文)と、検査の `rule`・`options`・`files` を照らす。`files` の範囲で、規約が求めることへの違反をこのルールが全て報告すると言えるときだけ `full`、見逃しが一つでもありうれば `partial`。lintが規約より広く報告する(例外も報告する)ことは `full` の妨げにならない(指摘はメインが確認する)。例で通ることは根拠にならない。
|
|
33
33
|
- **補助の確認**: 規約のgood・badの例を一時ディレクトリにファイルとして書き、同じ入口で実行して、badで報告され、goodで報告されないことを確かめる。対象プロジェクトのコードを写さない。
|
|
34
|
-
- **書き込み**: `record_rule`・`update_rule` の `checks` に渡す。一括の `record_rules` は `checks` を受け取らない(付けると呼出し全体が入力エラーになる)ので、検査は登録後に `update_rule` で付ける。`update_rule` は配列ごと置換し、`[]`
|
|
34
|
+
- **書き込み**: `record_rule`・`update_rule` の `checks` に渡す。一括の `record_rules` は `checks` を受け取らない(付けると呼出し全体が入力エラーになる)ので、検査は登録後に `update_rule` で付ける。`update_rule` は配列ごと置換し、`[]` で外す。検査を持つ規約の内容(出典とrepoの表示名以外。適用先はscope、repo_ownerのowner、repoのrepoIdで比べる)を変える更新では、更新後の全文で検査を見直し、同じ要求で `checks` を渡す。渡さないと `CHECKS_REQUIRED` で保存されない。見直した結果が同じなら同じ配列を渡す。
|
|
@@ -4,7 +4,7 @@ Managed Agents は配布CLI `shinaps-skills managed` で導入・依頼・観測
|
|
|
4
4
|
|
|
5
5
|
## アカウント
|
|
6
6
|
|
|
7
|
-
アカウントは、APIキー1つが属する組織・workspace
|
|
7
|
+
アカウントは、APIキー1つが属する組織・workspace に利用者が付ける名前で、先頭は英小文字または数字、以降は英小文字・数字とハイフン(`^[a-z0-9][a-z0-9-]*$`)にする。同じマシンでは、同じ名前は常に同じ組織・workspace を表すようにする。lock がアカウント名で引かれるので、別の組織に同じ名前を使うと別の組織の lock で適用しようとする(`ant apply` は lock と違う組織・workspace の資格情報を拒否して止まる)。
|
|
8
8
|
|
|
9
9
|
### APIキー
|
|
10
10
|
|
|
@@ -63,7 +63,7 @@ shinaps-skills managed collect --account <name> --session <ID>
|
|
|
63
63
|
```
|
|
64
64
|
|
|
65
65
|
- セッションに紐づく Files(`changes.patch` と作業記録の tar)を、そのアカウントの `sessions/<ID>/outputs/` へ保存し、保存したパスを表示する。同じ名前のファイルは最新のものだけを保存する(lead は毎ターン `changes.patch` を比較元からの全体の差分で置き換える)。名前がファイル名だけでないものは保存せずに止まる。保存したサイズは一覧の値と比べる。
|
|
66
|
-
-
|
|
66
|
+
- collect は差分を適用せず、保存だけを行い、自分では push しない。ローカルsnapshotのprepare・importはGitを使い、importは新しいディレクトリにだけ差分を適用する。既存の作業ツリーへは適用しない。push と draft PR の作成は、許可した場合にセッション内の lead が行う。`changes.patch` は合意した base の SHA のチェックアウトで `git apply --check` してから、範囲と受入条件に対する差分を確かめる。適用するかは利用者と決める。
|
|
67
67
|
- `コミット:` と push 先がある場合は `git fetch origin <head>` で head ブランチを取得し、base の SHA からのコミットと差分を確かめる。
|
|
68
68
|
- `PR:` がある場合は `gh pr view <PR の URL> --json isDraft,baseRefName,headRefName` または GitHub MCP の読み取りで、draft であること・base と head が合意どおりであることを確かめる。
|
|
69
69
|
|
|
@@ -74,9 +74,33 @@ shinaps-skills managed collect --account <name> --session <ID>
|
|
|
74
74
|
## 未検証の境界
|
|
75
75
|
|
|
76
76
|
- `managed setup`・`run`・`status`・`reply`・`collect` のコマンド・フラグ・stdin の本文・出力の形は ant 2.4.0 の公式ソースで照合し、偽の `ant` を使うテストで確かめた。実際の `ant` とクラウドではまだ実行していない。
|
|
77
|
-
- API の側で決まり、ant のソースでは確かめられないこと:
|
|
77
|
+
- API の側で決まり、ant のソースでは確かめられないこと: `mount_path: /CLAUDE.md` が `/mnt/session/uploads/CLAUDE.md` に置かれるか、Files の upload・一覧・取得に `files-api-2025-04-14` と `managed-agents-2026-04-01` の両方の beta を付ければよいか(ant の `--beta` は既定の beta を置き換えるので、全ての呼び出しで明示している)、Files の応答の `filename`・`size_bytes`・`created_at`、events の一覧に役割のスレッドの `agent.message` が混ざるか。lock が無い状態での skill の重複は、同じ setup で作る lead の同名確認で止まる。
|
|
78
78
|
- マウントしたリポジトリの remote へ `git push` できるか(`authorization_token` が push に使われるか)。最初のセッションで最優先に確かめ、できなければ GitHub MCP の push 用ツール(`create_branch`・`push_files`)を有効にするかを利用者と相談する。
|
|
79
79
|
- commit をチェックアウトしてマウントした作業先から、origin の head ブランチを取得できるか(clone の深さと fetch の資格情報)。
|
|
80
80
|
- GitHub MCP で draft PR を作れるか。`draft: true` は lead への指示でしか担保されない。
|
|
81
81
|
- vault に資格情報を登録できたことは、上の3点の確認にならない。
|
|
82
82
|
- 完了したスレッドが同時実行の枠(25本)を空けるか。ULTRAでは、ラウンドごとに新しく起動する担当を含めて、同時に保つスレッドが多い。
|
|
83
|
+
|
|
84
|
+
## Routerとローカルコードの往復
|
|
85
|
+
|
|
86
|
+
Claude Account Routerを使う場合は、アプリでAPIキーとクラウド用アカウントを登録し、アプリ内CLIの `cloud managed` 経由で同じmanagedコマンドを呼ぶ。setupとrunはRouterの選択アカウントを使える。status・reply・collect・importは作成結果のaccountとsession_idを明示し、途中の選択変更を引き継がない。キーを取得して会話や設定へ書き出さない。
|
|
87
|
+
|
|
88
|
+
GitHubへpushしていないコードも渡す場合は、先にローカルで固定する。
|
|
89
|
+
|
|
90
|
+
```sh
|
|
91
|
+
shinaps-skills managed prepare --source /path/to/repository
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
戻り値のfilesとmanifestを確認する。追跡中の現在の内容と、Gitで無視されていない新規ファイルを含む。履歴・Git設定・node_modules・.local・.shinapsは含まない。資格情報ファイルやcheckout外へのシンボリックリンクは拒否する。内容の秘密検出は行わないので、送信対象の確認は必要。submoduleはこの方式では扱わない。
|
|
95
|
+
|
|
96
|
+
合意済みのrunに `--snapshot <manifest>` を付ける。比較元のSHAは保存済みsnapshotから決まり、`--base-sha` は不要。体制・許可・予算・範囲は通常どおり明示する。ローカル方式はpush・draft_prを受理しない。snapshotはFiles APIで送り、GitHubリポジトリのマウントやGitHub tokenを使わない。元のGit履歴は含まれない。
|
|
97
|
+
|
|
98
|
+
collectの後、保存されたセッションを新しいローカルcheckoutへ戻せる。
|
|
99
|
+
|
|
100
|
+
```sh
|
|
101
|
+
shinaps-skills managed import --account personal --session SESSION_ID --destination /path/to/new-checkout
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
importはAPIキー不要。保存された送信時点のコード(`~/.local/share/shinaps-ai/skills/snapshots/<SHA>/`。アカウントに属さない。prepareごとに複製とbundleが残るので、importと確認が済んだら削除してよい)を復元し、そのセッションのchanges.patchをindexへ適用する。既存のdestinationや元の作業ツリーは上書きしない。適用失敗時は作成したディレクトリを削除してエラーにする。復元先で差分とreport.mdの報告を読み、ローカルの検証を行って作業を再開する。元のブランチへの統合は別途差分を確認して行う。
|
|
105
|
+
|
|
106
|
+
実クラウドへの送信・実行・回収は未検証。ローカルの代替antと実Gitでの往復テストは、クラウド環境でのgit利用や成果物生成の保証ではない。予算amountのセント単位は[公式仕様](https://platform.claude.com/docs/en/managed-agents/budgets)で確認済みで、リクエスト途中では停止しないため厳密な請求上限とは異なる。
|
|
@@ -60,7 +60,7 @@ coding の規約は、使えるlintのルールで検査できるかを判断し
|
|
|
60
60
|
|
|
61
61
|
## scope
|
|
62
62
|
|
|
63
|
-
全リポジトリに当てはまる判断だけ `scope: user` にして、他のプロジェクトでも通じる表現で書く。同じownerの配下に共通する判断は `scope: repo_owner`、特定のリポジトリの判断は `scope: repo`
|
|
63
|
+
全リポジトリに当てはまる判断だけ `scope: user` にして、他のプロジェクトでも通じる表現で書く。同じownerの配下に共通する判断は `scope: repo_owner`、特定のリポジトリの判断は `scope: repo` にして具体的に書く。`scope: repo` で書くには、[Shinaps規約の取得](rules.md)の「リポジトリを特定する」でrepositoryの `id` を得る。
|
|
64
64
|
|
|
65
65
|
## 目安と上限
|
|
66
66
|
|
|
@@ -25,12 +25,25 @@ Shinaps AIのMCPから規約を取得する手順。取得は呼出し元(メ
|
|
|
25
25
|
|
|
26
26
|
対象が確定していない工程(devの入口・`spec`・`explore`・`debug`)は、論点の技術を語彙から推定してよい。`filterUserRulesByLabels` は指定しない。
|
|
27
27
|
|
|
28
|
+
## リポジトリを特定する
|
|
29
|
+
|
|
30
|
+
`scope: repo` の規約はGitHubのrepositoryの`id`で識別し、owner・名前は表示にだけ使う。名前は改名で変わり、旧名は別のrepositoryやforkが使えるので、名前の一致を同じrepositoryの根拠にしない。
|
|
31
|
+
|
|
32
|
+
1. 対象のownerと名前を決める。既定は現在のGit workspaceの `git remote get-url origin`(`github.com/<owner>/<repo>(.git)?` の形)。登録先が別のrepositoryなら、その値にする。この形に当たらなければ(GitHub以外のremoteなど)、`repoOwner`・`repoName`・`repoId` のどれも渡さない。
|
|
33
|
+
2. `gh api repos/<owner>/<repo> --jq '{id, full_name}'` を1回呼ぶ。改名前の名前でもGitHubは改名後のrepositoryを返すので、`id` を `repoId`、`full_name` の `/` の前後を `repoOwner`・`repoName` にする。
|
|
34
|
+
3. 呼出しが失敗したら(`gh` が無い、認証が無い、404など。理由は区別しない)、`repoId`・`repoName` は渡さない。1で決めたownerだけを `repoOwner` にする。
|
|
35
|
+
|
|
36
|
+
- **取得**: `search_rules` には `repoOwner` と `repoId` を渡し、`repoName` は渡さない(入力エラーになる)。`repoId` が無いときは `scope: repo` の規約が返らないので、「リポジトリを特定できず、このrepositoryの規約は取得していない」と1文で報告する。
|
|
37
|
+
- **新規の書込み**: `scope: repo` の `record_rule`・`record_rules` には `repoOwner`・`repoName`・`repoId` をそろえて渡す。
|
|
38
|
+
- **更新**: `update_rule` で `scope: "repo"` と3つをそろえて渡すのは、適用先を repo にするとき、別のrepositoryに変えるとき、表示名を現在の名前にするとき(2の `full_name` が規約の `repoOwner`・`repoName` と違うとき)だけ。内容だけの更新では適用先の項目を渡さない(`scope` なしで渡すと入力エラーになる)。
|
|
39
|
+
- **`repoId` が得られないとき**: `scope: repo` では書き込まず、`scope: user`・`scope: repo_owner` に変えるか中止するかを利用者に確認する。範囲を変えるときは、[Shinaps規約の書き方](rule-writing.md)の比較をやり直す。
|
|
40
|
+
|
|
28
41
|
## 一覧を取る
|
|
29
42
|
|
|
30
|
-
`search_rules` を `fields: "summary"` で呼ぶ。`repoOwner`・`
|
|
43
|
+
`search_rules` を `fields: "summary"` で呼ぶ。`repoOwner`・`repoId` は「リポジトリを特定する」で決めた値を渡す。対象が確定した照合では `filterUserRulesByLabels: true` を渡す。
|
|
31
44
|
|
|
32
45
|
```json
|
|
33
|
-
{ "fields": "summary", "repoOwner": "...", "
|
|
46
|
+
{ "fields": "summary", "repoOwner": "...", "repoId": 123456789, "languages": ["typescript", "javascript"], "platforms": ["cloudflare-workers"], "frameworks": ["hono"], "filterUserRulesByLabels": true }
|
|
34
47
|
```
|
|
35
48
|
|
|
36
49
|
- サーバーは同じ軸をOR、指定した軸の間をANDで判定する。
|
|
@@ -60,7 +73,7 @@ Shinaps AIのMCPから規約を取得する手順。取得は呼出し元(メ
|
|
|
60
73
|
|
|
61
74
|
[Shinaps規約の書き方](rule-writing.md)の比較に使う規約は、次の条件で取得する。
|
|
62
75
|
|
|
63
|
-
- `search_rules` は `fields: "summary"` で全ページ取得し、`filterUserRulesByLabels` と `kind` を渡さない。`repoOwner`・`
|
|
76
|
+
- `search_rules` は `fields: "summary"` で全ページ取得し、`filterUserRulesByLabels` と `kind` を渡さない。`repoOwner`・`repoId` は「リポジトリを特定する」で決めた値にする。
|
|
64
77
|
- 公式規約も一覧に載るよう、`list_search_facets` の語彙が8件以内の技術軸(`languages` など)を一つ選び、その全slugを渡す。技術軸はその軸のlabelが無い公式規約にも一致する。`topics` は一致必須で別のtopicの公式規約が返らなくなるので渡さない。渡せる軸が無ければ公式規約の取得は未完了とする。
|
|
65
78
|
- `projectVersions` には確定した版だけを渡す。渡しても渡さなくても、条件に合わない条件付きの公式規約は返らない。
|
|
66
79
|
- 一覧のtitleとlabelから同じ場面を扱う可能性を否定できない規約を選び、「全文を取る」で全文を取得する。文字列の一致で選ばない(公式規約のtitleは英語)。「全文を取る規約を選ぶ」は対象ファイルとの照合の基準で、ここには適用しない。
|
|
@@ -76,7 +76,7 @@ Claude Codeの `Skill`・`Agent`・`AskUserQuestion`・`TaskCreate` を利用で
|
|
|
76
76
|
### Phase 1: セッション初期化
|
|
77
77
|
|
|
78
78
|
1. 最初の質問を受領する。引数が空、またはファイルパスだけなら、理解したいことを `AskUserQuestion` で 1 問だけ確認する
|
|
79
|
-
2.
|
|
79
|
+
2. [Shinaps規約の取得](../../references/rules.md) の「リポジトリを特定する」で `repoOwner` / `repoId` を解決する。今回の依頼に関連する既存の spec・調査レポート・Git の変更履歴を確認し、引き継ぐ決定と根拠を要約する。既存成果物がなければ追加取得は不要。
|
|
80
80
|
3. [Shinaps規約の取得](../../references/rules.md) に従い、最初の質問に対応する推定 facet で一覧を取り、必要な規約全文を取得する。論点が別領域へ移ったら、その時点で facet を取り直す
|
|
81
81
|
4. リポジトリの `CLAUDE.md` / `AGENTS.md` / その他の指示ファイルを読む
|
|
82
82
|
5. `git status --short --branch` と HEAD を記録する。調査対象の revision は未コミット変更を含む現在の worktree をデフォルトにし、production / PR / commit が明示された場合は対象 revision を特定して別に記録する。reset / stash / checkout / clean は行わない
|
|
@@ -28,7 +28,7 @@ Claude Codeの `Skill`・`Agent`・`AskUserQuestion`・`TaskCreate` を利用で
|
|
|
28
28
|
2. 失敗したら `git remote get-url origin` を parse (`github.com/<owner>/<repo>(.git)?` 形式)
|
|
29
29
|
3. それでも決まらないなら **ユーザーにチャットで確認** (「対象は `<owner>/<repo>` でいいですか?」)
|
|
30
30
|
|
|
31
|
-
解決した `<owner>/<repo>` をユーザーに **必ず提示** し、誤りなら別の repo
|
|
31
|
+
解決した `<owner>/<repo>` をユーザーに **必ず提示** し、誤りなら別の repo を入力してもらう。確定した repo を [Shinaps規約の取得](../../references/rules.md) の「リポジトリを特定する」で特定し、`repoId` と現在の `repoOwner`・`repoName` を得る。`repoId` が得られなければ `scope: repo` では投入できないので、ここで止めて利用者に確認する。
|
|
32
32
|
|
|
33
33
|
## Step 1. since (期間) をチャットで確認
|
|
34
34
|
|
|
@@ -87,7 +87,7 @@ Good/Bad サンプルコード (examples) の付け方:
|
|
|
87
87
|
|
|
88
88
|
投入手順:
|
|
89
89
|
|
|
90
|
-
- 候補を確定してから、[Shinaps規約の書き方](../../references/rule-writing.md) の「書き込む前に既存の規約と比べる」で候補ごとに結果を決める。既存の規約への**更新**は下の差分確認へ進める。**提示** はブロックの終わりにまとめて利用者へ示し、残りの投入は止めない。**新規**候補だけを集めて `record_rules({ scope: 'repo', repoOwner, repoName, rules: [{ title, why, recommend?, avoid?, kind?, labels?, examples?, sourceUrls? }, ...] })` で **1 call bulk 投入** する。上限は MCP ツールの入力の説明に従う。`labels` は language / platform / framework / topic の slug 配列 (`list_search_facets` が返す公式カタログの slug のみ、language/platform/framework は 1 rule に各 1 つまで) (1 call 最大 100 件、超えたら分割。同一 title の重複は入力の schema 検証で拒否される)。
|
|
90
|
+
- 候補を確定してから、[Shinaps規約の書き方](../../references/rule-writing.md) の「書き込む前に既存の規約と比べる」で候補ごとに結果を決める。既存の規約への**更新**は下の差分確認へ進める。**提示** はブロックの終わりにまとめて利用者へ示し、残りの投入は止めない。**新規**候補だけを集めて `record_rules({ scope: 'repo', repoOwner, repoName, repoId, rules: [{ title, why, recommend?, avoid?, kind?, labels?, examples?, sourceUrls? }, ...] })` で **1 call bulk 投入** する。上限は MCP ツールの入力の説明に従う。`labels` は language / platform / framework / topic の slug 配列 (`list_search_facets` が返す公式カタログの slug のみ、language/platform/framework は 1 rule に各 1 つまで) (1 call 最大 100 件、超えたら分割。同一 title の重複は入力の schema 検証で拒否される)。
|
|
91
91
|
- extract-rules は対象 repo の PR レビューから抽出する性質上 scope=repo (当該 repo 限定) がデフォルト。横断ルールとして残したい場合だけユーザー判断で `scope: 'user'` に格上げし、書き直した全文で比べ直す。**格上げするときは why / recommend / avoid / examples を当該 repo の固有文脈 (内部 API 名・モジュール構成・ドメイン固有の識別子) から切り離し、他プロジェクトでも適用場面を想定できる汎用的な表現・汎用サンプルに解釈し直してから登録する** (scope=repo のままなら repo 固有の具体的な記述・コード片で良い)
|
|
92
92
|
- `RULE_TOO_LARGE` が返ったら、その呼出し分は全体として保存されていない。details の limit・actual・title を確かめ、該当候補の本文・例・出典を短くしてから同じ呼出しをやり直す。`update_rule` の `RULE_UPDATE_CONFLICT` は全文を読み直してから再試行する。
|
|
93
93
|
- `RULE_LIMIT_REACHED` が返ったら投入を止める。個人ルールの保存数には上限があり、残りの枠が足りない `record_rules` はその呼出し分が全体として失敗し、一部も保存されない (件数を減らして一部だけ登録する手順へ自動では切り替えない)。それより前の呼出しで投入した分は保存されている。details の `limit` と `actual` から残りの枠を示し、投入する件数を絞るか既存ルールを整理するかのどちらにするかを利用者に確認する。以降のブロックの抽出・投入にも進まず、利用者の選択を待つ。
|
|
@@ -9,10 +9,16 @@ description: Claude Managed Agents の lead へ、アカウントを選んで de
|
|
|
9
9
|
|
|
10
10
|
# Managed Agents へ依頼する
|
|
11
11
|
|
|
12
|
-
[設定とアカウント](../../references/managed/configuration.md)と[依頼と観測の手順](../../references/managed/execute.md)を読んで進める。操作は配布CLIの `managed run`・`status`・`reply`・`collect` で行う。
|
|
12
|
+
[設定とアカウント](../../references/managed/configuration.md)と[依頼と観測の手順](../../references/managed/execute.md)を読んで進める。操作は配布CLIの `managed prepare`・`run`・`status`・`reply`・`collect`・`import` で行う。
|
|
13
13
|
|
|
14
14
|
- 呼び出しのたびにアカウント・base ブランチと SHA・head ブランチ・範囲と受入条件・体制(LITE・BASIC・ULTRA)・許可・予算を利用者と合意し、引数で明示する。設定・lock・過去の作業から推定しない。
|
|
15
15
|
- 設定(`.shinaps/managed.json`)が無いか旧版なら、手順の「設定を用意する」に従って一問ずつ確認して作る。設定から読むのは `repository` とアカウントの `vault_id` だけで、lead と環境の ID はアカウントの lock から読む。lock が無ければ止め、導入は managed-setup で行う。
|
|
16
16
|
- 計画・実装・レビューの工程と追加の担当は lead に任せ、ここでは指示しない。
|
|
17
|
-
-
|
|
17
|
+
- 回収した差分を既存の作業ツリーへ適用しない(importは新しいディレクトリにだけ復元する)。確認と統合の判断は利用者と行う。push・merge・deploy・release・Issue の更新・セッションのアーカイブは行わない。Linear の進行とは独立した入口である。
|
|
18
18
|
- キーと token は利用者が環境変数に設定する。受け取らない・表示しない・記録しない。
|
|
19
|
+
|
|
20
|
+
## Routerとローカルでの再開
|
|
21
|
+
|
|
22
|
+
利用者がClaude Account Routerとの連携を選んだ場合は、アプリ内CLIの `cloud managed` を使う。APIキーを読み出して会話へ返さない。作成結果のaccountとsession_idを保存し、後続操作でその組を使う。
|
|
23
|
+
|
|
24
|
+
未コミットのローカルコードを渡す依頼では、[ローカルコードの往復](../../references/managed/execute.md#routerとローカルコードの往復)に従ってprepareし、送信する一覧・内容を確認してからsnapshotを指定する。比較元はsnapshotのSHA。collect後、ローカルでの再開が許可されていればimportで新規checkoutへ復元し、差分と報告を確認してQAへ進む。元の作業ツリーには自動適用しない。
|
|
@@ -177,7 +177,7 @@ lint 由来の指摘は auto-apply にしない (ルールは構文で判定す
|
|
|
177
177
|
|
|
178
178
|
1. **修正する** — どう直すかを 1-2 文で提案 (agent が返した `detail` を活用)。承認されたら `Edit` / `Write` で実行
|
|
179
179
|
2. **このまま許容する** — 妥当な理由があるとき。理由を今回のローカル成果物(implementation.md / 調査・レビューレポート)に残す (後から「なぜ違反のままにしたか」を辿れるように)
|
|
180
|
-
3. **ルールの方を修正する** — 規約自体が現実と乖離している場合。[Shinaps規約の書き方](../../references/rule-writing.md) に従って書き込む全文を作る。新規のときは scope (user / repo_owner / repo) も選ばせる (横断的なら user、この repo 限定なら repo)。同じ文書の「書き込む前に既存の規約と比べる」で比べ (Step 1 の結果はファイルの技術で絞っているので使わない)、結果と全文の差分を示して同意を受けてから shinaps-ai MCP server の `record_rule` (新規) or `update_rule` (既存修正) を実行
|
|
180
|
+
3. **ルールの方を修正する** — 規約自体が現実と乖離している場合。[Shinaps規約の書き方](../../references/rule-writing.md) に従って書き込む全文を作る。新規のときは scope (user / repo_owner / repo) も選ばせる (横断的なら user、この repo 限定なら repo。repo は [Shinaps規約の取得](../../references/rules.md) の「リポジトリを特定する」で得た `repoId` を添える)。同じ文書の「書き込む前に既存の規約と比べる」で比べ (Step 1 の結果はファイルの技術で絞っているので使わない)、結果と全文の差分を示して同意を受けてから shinaps-ai MCP server の `record_rule` (新規) or `update_rule` (既存修正) を実行
|
|
181
181
|
4. **後回し** — 別タスクとして扱う。`TaskCreate` で TODO 化し、後の作業セッションで着手できるようにする
|
|
182
182
|
|
|
183
183
|
**提示順は `changed` を先、`pre-existing` を後**。それぞれの中では Violation → Improvement → Question の順。各 finding には `pre-existing` かどうかを明示して提示する (ユーザーが「今回の commit に混ぜるか」を判断する材料になるため)。件数が少なければ 1 回の `AskUserQuestion` に複数 question をまとめてよい (各 finding には前提説明を添える)。
|
|
@@ -71,3 +71,7 @@ PR: <draft PR の URL。無ければ なし>
|
|
|
71
71
|
| hooks | 未対応 |
|
|
72
72
|
| permission mode | 定義の `permission_policy` で固定 |
|
|
73
73
|
| セッション予算 | セッション作成時の `max_list_cost` で付ける |
|
|
74
|
+
|
|
75
|
+
## ローカルsnapshotで始める場合
|
|
76
|
+
|
|
77
|
+
初回メッセージにsource.bundleが指定されている場合は、通常のHEAD確認より先に `/mnt/session/uploads/source.bundle` を指定された作業先へcloneする。比較元は初回メッセージのsnapshot SHAとし、GitHubへのアクセスや元の履歴を前提にしない。成果物は上記の一時indexで比較元からの全差分を出力する手順に従い、変更内容・検証結果・未完了事項を `/mnt/session/outputs/report.md` にも残す。
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
## 検証CLIと契約版
|
|
6
6
|
|
|
7
|
-
利用中のskillとCLIの版を確認し、0.
|
|
7
|
+
利用中のskillとCLIの版を確認し、0.45.0のskillには同じ版のnpm CLIを導入する(`npm install -g @shinaps/skills@0.45.0`)。pluginだけにはCLIは含まれない。導入・更新が必要ならその権限を確認し、検証のたびに `npx latest` を取得しない。
|
|
8
8
|
|
|
9
9
|
0.35.0から通常設定の `review_flow.mode` に `ask` を受理する。`config_version` は2のままで、既存のauto・manualの設定は変更不要。0.33.0〜0.34.xのCLIと `$schema` のURLは `ask` を拒否するため、`ask` を使う設定では `$schema` も0.35.0以降のURLにする。
|
|
10
10
|
|
|
@@ -31,4 +31,4 @@ Shinaps AIの規約は、機械検査(`checks`)を持てる。検査は配
|
|
|
31
31
|
- **使えるルール**: ESLint core、`@typescript-eslint/*`(型情報を使うものを含む)、`vue/*`、同梱の `shinaps/*`(`docs/lint.md` の一覧)。版によって違うので、同じ版のCLIで確かめる。使えるルールが無い規約には検査を付けない(AI照合のまま)。同梱ルールの追加は `docs/lint.md` の手順で別の作業として提案する。
|
|
32
32
|
- **coverageの判断**: 規約の全文(scope・labelsを含み、更新では更新後の全文)と、検査の `rule`・`options`・`files` を照らす。`files` の範囲で、規約が求めることへの違反をこのルールが全て報告すると言えるときだけ `full`、見逃しが一つでもありうれば `partial`。lintが規約より広く報告する(例外も報告する)ことは `full` の妨げにならない(指摘はメインが確認する)。例で通ることは根拠にならない。
|
|
33
33
|
- **補助の確認**: 規約のgood・badの例を一時ディレクトリにファイルとして書き、同じ入口で実行して、badで報告され、goodで報告されないことを確かめる。対象プロジェクトのコードを写さない。
|
|
34
|
-
- **書き込み**: `record_rule`・`update_rule` の `checks` に渡す。一括の `record_rules` は `checks` を受け取らない(付けると呼出し全体が入力エラーになる)ので、検査は登録後に `update_rule` で付ける。`update_rule` は配列ごと置換し、`[]`
|
|
34
|
+
- **書き込み**: `record_rule`・`update_rule` の `checks` に渡す。一括の `record_rules` は `checks` を受け取らない(付けると呼出し全体が入力エラーになる)ので、検査は登録後に `update_rule` で付ける。`update_rule` は配列ごと置換し、`[]` で外す。検査を持つ規約の内容(出典とrepoの表示名以外。適用先はscope、repo_ownerのowner、repoのrepoIdで比べる)を変える更新では、更新後の全文で検査を見直し、同じ要求で `checks` を渡す。渡さないと `CHECKS_REQUIRED` で保存されない。見直した結果が同じなら同じ配列を渡す。
|
|
@@ -4,7 +4,7 @@ Managed Agents は配布CLI `shinaps-skills managed` で導入・依頼・観測
|
|
|
4
4
|
|
|
5
5
|
## アカウント
|
|
6
6
|
|
|
7
|
-
アカウントは、APIキー1つが属する組織・workspace
|
|
7
|
+
アカウントは、APIキー1つが属する組織・workspace に利用者が付ける名前で、先頭は英小文字または数字、以降は英小文字・数字とハイフン(`^[a-z0-9][a-z0-9-]*$`)にする。同じマシンでは、同じ名前は常に同じ組織・workspace を表すようにする。lock がアカウント名で引かれるので、別の組織に同じ名前を使うと別の組織の lock で適用しようとする(`ant apply` は lock と違う組織・workspace の資格情報を拒否して止まる)。
|
|
8
8
|
|
|
9
9
|
### APIキー
|
|
10
10
|
|
|
@@ -63,7 +63,7 @@ shinaps-skills managed collect --account <name> --session <ID>
|
|
|
63
63
|
```
|
|
64
64
|
|
|
65
65
|
- セッションに紐づく Files(`changes.patch` と作業記録の tar)を、そのアカウントの `sessions/<ID>/outputs/` へ保存し、保存したパスを表示する。同じ名前のファイルは最新のものだけを保存する(lead は毎ターン `changes.patch` を比較元からの全体の差分で置き換える)。名前がファイル名だけでないものは保存せずに止まる。保存したサイズは一覧の値と比べる。
|
|
66
|
-
-
|
|
66
|
+
- collect は差分を適用せず、保存だけを行い、自分では push しない。ローカルsnapshotのprepare・importはGitを使い、importは新しいディレクトリにだけ差分を適用する。既存の作業ツリーへは適用しない。push と draft PR の作成は、許可した場合にセッション内の lead が行う。`changes.patch` は合意した base の SHA のチェックアウトで `git apply --check` してから、範囲と受入条件に対する差分を確かめる。適用するかは利用者と決める。
|
|
67
67
|
- `コミット:` と push 先がある場合は `git fetch origin <head>` で head ブランチを取得し、base の SHA からのコミットと差分を確かめる。
|
|
68
68
|
- `PR:` がある場合は `gh pr view <PR の URL> --json isDraft,baseRefName,headRefName` または GitHub MCP の読み取りで、draft であること・base と head が合意どおりであることを確かめる。
|
|
69
69
|
|
|
@@ -74,9 +74,33 @@ shinaps-skills managed collect --account <name> --session <ID>
|
|
|
74
74
|
## 未検証の境界
|
|
75
75
|
|
|
76
76
|
- `managed setup`・`run`・`status`・`reply`・`collect` のコマンド・フラグ・stdin の本文・出力の形は ant 2.4.0 の公式ソースで照合し、偽の `ant` を使うテストで確かめた。実際の `ant` とクラウドではまだ実行していない。
|
|
77
|
-
- API の側で決まり、ant のソースでは確かめられないこと:
|
|
77
|
+
- API の側で決まり、ant のソースでは確かめられないこと: `mount_path: /CLAUDE.md` が `/mnt/session/uploads/CLAUDE.md` に置かれるか、Files の upload・一覧・取得に `files-api-2025-04-14` と `managed-agents-2026-04-01` の両方の beta を付ければよいか(ant の `--beta` は既定の beta を置き換えるので、全ての呼び出しで明示している)、Files の応答の `filename`・`size_bytes`・`created_at`、events の一覧に役割のスレッドの `agent.message` が混ざるか。lock が無い状態での skill の重複は、同じ setup で作る lead の同名確認で止まる。
|
|
78
78
|
- マウントしたリポジトリの remote へ `git push` できるか(`authorization_token` が push に使われるか)。最初のセッションで最優先に確かめ、できなければ GitHub MCP の push 用ツール(`create_branch`・`push_files`)を有効にするかを利用者と相談する。
|
|
79
79
|
- commit をチェックアウトしてマウントした作業先から、origin の head ブランチを取得できるか(clone の深さと fetch の資格情報)。
|
|
80
80
|
- GitHub MCP で draft PR を作れるか。`draft: true` は lead への指示でしか担保されない。
|
|
81
81
|
- vault に資格情報を登録できたことは、上の3点の確認にならない。
|
|
82
82
|
- 完了したスレッドが同時実行の枠(25本)を空けるか。ULTRAでは、ラウンドごとに新しく起動する担当を含めて、同時に保つスレッドが多い。
|
|
83
|
+
|
|
84
|
+
## Routerとローカルコードの往復
|
|
85
|
+
|
|
86
|
+
Claude Account Routerを使う場合は、アプリでAPIキーとクラウド用アカウントを登録し、アプリ内CLIの `cloud managed` 経由で同じmanagedコマンドを呼ぶ。setupとrunはRouterの選択アカウントを使える。status・reply・collect・importは作成結果のaccountとsession_idを明示し、途中の選択変更を引き継がない。キーを取得して会話や設定へ書き出さない。
|
|
87
|
+
|
|
88
|
+
GitHubへpushしていないコードも渡す場合は、先にローカルで固定する。
|
|
89
|
+
|
|
90
|
+
```sh
|
|
91
|
+
shinaps-skills managed prepare --source /path/to/repository
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
戻り値のfilesとmanifestを確認する。追跡中の現在の内容と、Gitで無視されていない新規ファイルを含む。履歴・Git設定・node_modules・.local・.shinapsは含まない。資格情報ファイルやcheckout外へのシンボリックリンクは拒否する。内容の秘密検出は行わないので、送信対象の確認は必要。submoduleはこの方式では扱わない。
|
|
95
|
+
|
|
96
|
+
合意済みのrunに `--snapshot <manifest>` を付ける。比較元のSHAは保存済みsnapshotから決まり、`--base-sha` は不要。体制・許可・予算・範囲は通常どおり明示する。ローカル方式はpush・draft_prを受理しない。snapshotはFiles APIで送り、GitHubリポジトリのマウントやGitHub tokenを使わない。元のGit履歴は含まれない。
|
|
97
|
+
|
|
98
|
+
collectの後、保存されたセッションを新しいローカルcheckoutへ戻せる。
|
|
99
|
+
|
|
100
|
+
```sh
|
|
101
|
+
shinaps-skills managed import --account personal --session SESSION_ID --destination /path/to/new-checkout
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
importはAPIキー不要。保存された送信時点のコード(`~/.local/share/shinaps-ai/skills/snapshots/<SHA>/`。アカウントに属さない。prepareごとに複製とbundleが残るので、importと確認が済んだら削除してよい)を復元し、そのセッションのchanges.patchをindexへ適用する。既存のdestinationや元の作業ツリーは上書きしない。適用失敗時は作成したディレクトリを削除してエラーにする。復元先で差分とreport.mdの報告を読み、ローカルの検証を行って作業を再開する。元のブランチへの統合は別途差分を確認して行う。
|
|
105
|
+
|
|
106
|
+
実クラウドへの送信・実行・回収は未検証。ローカルの代替antと実Gitでの往復テストは、クラウド環境でのgit利用や成果物生成の保証ではない。予算amountのセント単位は[公式仕様](https://platform.claude.com/docs/en/managed-agents/budgets)で確認済みで、リクエスト途中では停止しないため厳密な請求上限とは異なる。
|
|
@@ -60,7 +60,7 @@ coding の規約は、使えるlintのルールで検査できるかを判断し
|
|
|
60
60
|
|
|
61
61
|
## scope
|
|
62
62
|
|
|
63
|
-
全リポジトリに当てはまる判断だけ `scope: user` にして、他のプロジェクトでも通じる表現で書く。同じownerの配下に共通する判断は `scope: repo_owner`、特定のリポジトリの判断は `scope: repo`
|
|
63
|
+
全リポジトリに当てはまる判断だけ `scope: user` にして、他のプロジェクトでも通じる表現で書く。同じownerの配下に共通する判断は `scope: repo_owner`、特定のリポジトリの判断は `scope: repo` にして具体的に書く。`scope: repo` で書くには、[Shinaps規約の取得](rules.md)の「リポジトリを特定する」でrepositoryの `id` を得る。
|
|
64
64
|
|
|
65
65
|
## 目安と上限
|
|
66
66
|
|
|
@@ -25,12 +25,25 @@ Shinaps AIのMCPから規約を取得する手順。取得は呼出し元(メ
|
|
|
25
25
|
|
|
26
26
|
対象が確定していない工程(devの入口・`spec`・`explore`・`debug`)は、論点の技術を語彙から推定してよい。`filterUserRulesByLabels` は指定しない。
|
|
27
27
|
|
|
28
|
+
## リポジトリを特定する
|
|
29
|
+
|
|
30
|
+
`scope: repo` の規約はGitHubのrepositoryの`id`で識別し、owner・名前は表示にだけ使う。名前は改名で変わり、旧名は別のrepositoryやforkが使えるので、名前の一致を同じrepositoryの根拠にしない。
|
|
31
|
+
|
|
32
|
+
1. 対象のownerと名前を決める。既定は現在のGit workspaceの `git remote get-url origin`(`github.com/<owner>/<repo>(.git)?` の形)。登録先が別のrepositoryなら、その値にする。この形に当たらなければ(GitHub以外のremoteなど)、`repoOwner`・`repoName`・`repoId` のどれも渡さない。
|
|
33
|
+
2. `gh api repos/<owner>/<repo> --jq '{id, full_name}'` を1回呼ぶ。改名前の名前でもGitHubは改名後のrepositoryを返すので、`id` を `repoId`、`full_name` の `/` の前後を `repoOwner`・`repoName` にする。
|
|
34
|
+
3. 呼出しが失敗したら(`gh` が無い、認証が無い、404など。理由は区別しない)、`repoId`・`repoName` は渡さない。1で決めたownerだけを `repoOwner` にする。
|
|
35
|
+
|
|
36
|
+
- **取得**: `search_rules` には `repoOwner` と `repoId` を渡し、`repoName` は渡さない(入力エラーになる)。`repoId` が無いときは `scope: repo` の規約が返らないので、「リポジトリを特定できず、このrepositoryの規約は取得していない」と1文で報告する。
|
|
37
|
+
- **新規の書込み**: `scope: repo` の `record_rule`・`record_rules` には `repoOwner`・`repoName`・`repoId` をそろえて渡す。
|
|
38
|
+
- **更新**: `update_rule` で `scope: "repo"` と3つをそろえて渡すのは、適用先を repo にするとき、別のrepositoryに変えるとき、表示名を現在の名前にするとき(2の `full_name` が規約の `repoOwner`・`repoName` と違うとき)だけ。内容だけの更新では適用先の項目を渡さない(`scope` なしで渡すと入力エラーになる)。
|
|
39
|
+
- **`repoId` が得られないとき**: `scope: repo` では書き込まず、`scope: user`・`scope: repo_owner` に変えるか中止するかを利用者に確認する。範囲を変えるときは、[Shinaps規約の書き方](rule-writing.md)の比較をやり直す。
|
|
40
|
+
|
|
28
41
|
## 一覧を取る
|
|
29
42
|
|
|
30
|
-
`search_rules` を `fields: "summary"` で呼ぶ。`repoOwner`・`
|
|
43
|
+
`search_rules` を `fields: "summary"` で呼ぶ。`repoOwner`・`repoId` は「リポジトリを特定する」で決めた値を渡す。対象が確定した照合では `filterUserRulesByLabels: true` を渡す。
|
|
31
44
|
|
|
32
45
|
```json
|
|
33
|
-
{ "fields": "summary", "repoOwner": "...", "
|
|
46
|
+
{ "fields": "summary", "repoOwner": "...", "repoId": 123456789, "languages": ["typescript", "javascript"], "platforms": ["cloudflare-workers"], "frameworks": ["hono"], "filterUserRulesByLabels": true }
|
|
34
47
|
```
|
|
35
48
|
|
|
36
49
|
- サーバーは同じ軸をOR、指定した軸の間をANDで判定する。
|
|
@@ -60,7 +73,7 @@ Shinaps AIのMCPから規約を取得する手順。取得は呼出し元(メ
|
|
|
60
73
|
|
|
61
74
|
[Shinaps規約の書き方](rule-writing.md)の比較に使う規約は、次の条件で取得する。
|
|
62
75
|
|
|
63
|
-
- `search_rules` は `fields: "summary"` で全ページ取得し、`filterUserRulesByLabels` と `kind` を渡さない。`repoOwner`・`
|
|
76
|
+
- `search_rules` は `fields: "summary"` で全ページ取得し、`filterUserRulesByLabels` と `kind` を渡さない。`repoOwner`・`repoId` は「リポジトリを特定する」で決めた値にする。
|
|
64
77
|
- 公式規約も一覧に載るよう、`list_search_facets` の語彙が8件以内の技術軸(`languages` など)を一つ選び、その全slugを渡す。技術軸はその軸のlabelが無い公式規約にも一致する。`topics` は一致必須で別のtopicの公式規約が返らなくなるので渡さない。渡せる軸が無ければ公式規約の取得は未完了とする。
|
|
65
78
|
- `projectVersions` には確定した版だけを渡す。渡しても渡さなくても、条件に合わない条件付きの公式規約は返らない。
|
|
66
79
|
- 一覧のtitleとlabelから同じ場面を扱う可能性を否定できない規約を選び、「全文を取る」で全文を取得する。文字列の一致で選ばない(公式規約のtitleは英語)。「全文を取る規約を選ぶ」は対象ファイルとの照合の基準で、ここには適用しない。
|
|
@@ -10,7 +10,7 @@ description: GitHubの過去PRレビューから繰り返し適用できる規
|
|
|
10
10
|
1. `gh`で期間内のマージ済みPRを取得する。ページングし、並び順と日付フィルターの関係を守る。更新日時順の一覧で古いマージ日時を一件見つけても、それ以降の取得を打ち切らない。
|
|
11
11
|
2. PRのreview本文・inline comment・conversation commentを取得する。PR descriptionはレビュー指摘として扱わない。APIのBot種別は除外し、判断が必要な投稿者は確認する。認証情報やリポジトリ全体をサービスへ送信しない。
|
|
12
12
|
3. 複数のレビューで繰り返され、適用条件とWHYが説明できる規約を候補にする。根拠のない例・URLを作らず、レビューコメントの出典を付ける。
|
|
13
|
-
4. 候補を[Shinaps規約の書き方](../../references/rule-writing.md)に従って書き、同じ文書の「書き込む前に既存の規約と比べる」で候補ごとに結果を決める。提示の候補はまとまりの終わりに示し、残りの投入は止めない。新規の候補だけを対象repoのスコープへ、実際のMCP
|
|
13
|
+
4. 候補を[Shinaps規約の書き方](../../references/rule-writing.md)に従って書き、同じ文書の「書き込む前に既存の規約と比べる」で候補ごとに結果を決める。提示の候補はまとまりの終わりに示し、残りの投入は止めない。新規の候補だけを対象repoのスコープへ、実際のMCPツールのスキーマに従って登録する。対象repoは[Shinaps規約の取得](../../references/rules.md)の「リポジトリを特定する」でrepositoryの`id`を得て指定し、得られなければrepoのスコープでは登録せず利用者に確認する。応答は入力の位置とidだけなので、重複して投入されなかった候補は入力からtitleを得て、上書きせず提示へ進める。全プロジェクト共通へ広げる場合は利用者の判断を確認し、書き直した全文で比べ直す。既存の規約への更新の候補は差分を提示して許可を得る。`RULE_TOO_LARGE`はその呼出し分が全体として保存されなかったことを示すので、details の limit・actual・titleを確かめ、該当候補を短くしてやり直す。更新の`RULE_UPDATE_CONFLICT`は全文を読み直して再試行する。`RULE_LIMIT_REACHED`は残りの枠が足りずその呼出し分が全体として保存されなかったことを示すので、一部だけを登録する手順へ切り替えずに止め、details の limit・actualを示して、件数を絞るか既存ルールを整理するかを利用者に確認する。それより前の呼出しで投入した分は保存されている。以降のまとまりの抽出・投入にも進まず、利用者の選択を待つ。
|
|
14
14
|
5. 大量処理は100 PR程度のまとまりで結果を報告し、継続範囲を確認する。結果が不明な保存失敗では再投入の前に読み戻し、重複を避ける。`RATE_LIMITED`は制限の時間枠が切り替わるまで待ってから1回だけ再試行し、続けて拒否されたら中断して報告する。
|
|
15
15
|
|
|
16
16
|
終了時は処理したPR・コメント数、新規登録・更新・書かない・提示・除外・未処理の件数と根拠を示す。取得・保存が部分的ならその範囲を明記する。記録する場合は.shinaps/work/を使い、[作業記録の共通手順](../../references/work-records.md)に従って初期化・保全する。
|
|
@@ -9,10 +9,16 @@ description: Claude Managed Agents の lead へ、アカウントを選んで de
|
|
|
9
9
|
|
|
10
10
|
# Managed Agents へ依頼する
|
|
11
11
|
|
|
12
|
-
[設定とアカウント](../../references/managed/configuration.md)と[依頼と観測の手順](../../references/managed/execute.md)を読んで進める。操作は配布CLIの `managed run`・`status`・`reply`・`collect` で行う。
|
|
12
|
+
[設定とアカウント](../../references/managed/configuration.md)と[依頼と観測の手順](../../references/managed/execute.md)を読んで進める。操作は配布CLIの `managed prepare`・`run`・`status`・`reply`・`collect`・`import` で行う。
|
|
13
13
|
|
|
14
14
|
- 呼び出しのたびにアカウント・base ブランチと SHA・head ブランチ・範囲と受入条件・体制(LITE・BASIC・ULTRA)・許可・予算を利用者と合意し、引数で明示する。設定・lock・過去の作業から推定しない。
|
|
15
15
|
- 設定(`.shinaps/managed.json`)が無いか旧版なら、手順の「設定を用意する」に従って一問ずつ確認して作る。設定から読むのは `repository` とアカウントの `vault_id` だけで、lead と環境の ID はアカウントの lock から読む。lock が無ければ止め、導入は managed-setup で行う。
|
|
16
16
|
- 計画・実装・レビューの工程と追加の担当は lead に任せ、ここでは指示しない。
|
|
17
|
-
-
|
|
17
|
+
- 回収した差分を既存の作業ツリーへ適用しない(importは新しいディレクトリにだけ復元する)。確認と統合の判断は利用者と行う。push・merge・deploy・release・Issue の更新・セッションのアーカイブは行わない。Linear の進行とは独立した入口である。
|
|
18
18
|
- キーと token は利用者が環境変数に設定する。受け取らない・表示しない・記録しない。
|
|
19
|
+
|
|
20
|
+
## Routerとローカルでの再開
|
|
21
|
+
|
|
22
|
+
利用者がClaude Account Routerとの連携を選んだ場合は、アプリ内CLIの `cloud managed` を使う。APIキーを読み出して会話へ返さない。作成結果のaccountとsession_idを保存し、後続操作でその組を使う。
|
|
23
|
+
|
|
24
|
+
未コミットのローカルコードを渡す依頼では、[ローカルコードの往復](../../references/managed/execute.md#routerとローカルコードの往復)に従ってprepareし、送信する一覧・内容を確認してからsnapshotを指定する。比較元はsnapshotのSHA。collect後、ローカルでの再開が許可されていればimportで新規checkoutへ復元し、差分と報告を確認してQAへ進む。元の作業ツリーには自動適用しない。
|